-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Roger Johansson edited this page Jan 14, 2026
·
11 revisions
A lightweight JavaScript interpreter written in C# with an IR-based execution model.
| Topic | Description |
|---|---|
| Architecture Overview | Execution model, AST vs IR, function variants |
| JsValue System | Value representation and the evaluator overload pattern |
| JsEnvironment & Slots | Variable storage: named, slot, and flat slot access |
| JsObject & Properties | Property descriptors, prototype chains, extensibility |
| Scope Analysis | Hoisting, TDZ, slot assignment, closure capture |
| Topic | Description |
|---|---|
| Execution Tiers | AST, IR, and planned expression bytecode/IL |
| IR Execution | Instruction types, dispatch table, ExecutionPlanRunner |
| Generators & Async | How generators, async functions, and async generators work |
| Promise & Microtasks | Promise states, microtask queue, await handling |
| ES Modules | Import/export, module loading, live bindings |
| Type | Page | Status |
|---|---|---|
| Full Index | All types overview | ~95% |
| Object | keys, values, assign, freeze, etc. | 100% |
| Array | map, filter, reduce, sort, etc. | 100% |
| String | split, replace, trim, pad, etc. | 100% |
| Number / Math | parseInt, isNaN, sin, cos, etc. | 100% |
| Date | getTime, setMonth, toISOString, etc. | 100% |
| Promise | then, catch, all, race, etc. | 100% |
| Map / Set | get, set, has, delete, etc. | 100% |
| RegExp | exec, test, match, replace, etc. | 100% |
| TypedArray | Int8Array, Float64Array, DataView, etc. | 100% |
| Function / Error / JSON | call, bind, parse, stringify, etc. | 75-100% |
| Reflect / Proxy | get, set, has, traps, etc. | 69-100% |
| Global / Console | eval, parseInt, log, error, etc. | 28-100% |
| Topic | Description |
|---|---|
| Host Integration API | Embedding guide: SetGlobalValue, module loaders, .NET/JS bridging |
| Standard Library Architecture | Attribute-based prototypes, constructor patterns |
| Source Generator Architecture | PrototypeSourceGenerator, stdlib-compat.json, automatic stubs |
| Performance Patterns | Object pooling, fast/slow path split, caching |
| Debugging | Logger assertions, pool guards, AST-free execution checks |
| Testing | IR plan logging, AST node access, cache verification, test helpers |
| Topic | Description |
|---|---|
| Error Signaling | ThrowSignal vs CompletionSignal, control flow patterns |
| Proxy & Reflect | 13 trap handlers, revocable proxies, Reflect operations |
| RegExp Implementation | JS-to-.NET pattern translation, Unicode mode, surrogate handling |
| Symbol System | JsSymbol vs Symbol atoms, global registry, well-known symbols |
| Temporal API | TC39 Temporal proposal, nanosecond precision, timezone support |
| Pooling Deep Dive | BucketedArrayPool, IsCaptured pattern, lock-free algorithms |
| BigInt Implementation | Arbitrary precision integers, BigInteger wrapper, typed arrays |
| WeakCollections | WeakMap, WeakSet, WeakRef - ConditionalWeakTable-based weak refs |
| Iterator Protocol | Iterators, for-of lowering, TC39 iterator helpers |
The engine has two execution paths:
- IR Execution (primary) - AST is lowered to a flat instruction sequence with explicit jumps
-
AST Walking (fallback) - Direct recursive evaluation for
with/evalfunctions
flowchart LR
JS[/"JS Source"/] --> Parser((Parser))
Parser --> AST((AST))
AST --> Analysis((Scope Analysis))
Analysis --> Decision{with/eval?}
Decision -->|No| IR[["IR Execution"]]
Decision -->|Yes| Walk[["AST Walking"]]
IR --> Result[/"JsValue"/]
Walk --> Result
| Type | Location | Purpose |
|---|---|---|
JsValue |
JsValue.cs |
Tagged union for JS values (undefined, null, bool, number, string, symbol, object) |
JsEnvironment |
JsEnvironment.cs |
Lexical scope with variable slots |
ExecutionPlan |
Execution/ExecutionPlan.cs |
Lowered IR for a function/script |
ExecutionInstruction |
Execution/Instructions/ |
Base for all IR instructions |
ExecutionPlanRunner |
TypedAstEvaluator.ExecutionPlanRunner.cs |
The IR interpreter |
EvaluationContext |
EvaluationContext.cs |
Per-execution state (realm, signals, cancellation) |
From slowest to fastest:
| Level | Mechanism | Use Case |
|---|---|---|
| Named | Walk scope chain, linear scan by Symbol |
eval, with, unoptimized |
| Slot | Direct index into scope's slot array | Known scope, JsVariable
|
| Flat Slot | Single flat array across all scopes | IR hot paths, O(1) regardless of depth |
flowchart LR
subgraph AST["AST"]
IF((if)) --> C((x))
IF --> T((then))
IF --> E((else))
end
subgraph IR["IR"]
I0["0: Branch"] --> I1["1: a()"]
I0 -.-> I3["3: b()"]
I1 --> I2["2: Jump"]
I2 -.-> I4["4: ..."]
I3 --> I4
end
AST ==> IR
Located in Execution/Emitters/:
| Emitter | Handles |
|---|---|
LoopEmitter |
for/while/do-while |
ForOfEmitter |
for-of/for-in iteration |
TryEmitter |
try/catch/finally |
SwitchEmitter |
switch statements |
YieldEmitter |
yield/yield* |
BlockEmitter |
block scopes |
Handlers indexed by InstructionKind enum for O(1) dispatch:
private static readonly InstructionHandler[] _dispatchTable = new InstructionHandler[43];See: IR Execution
| Type | File | Description |
|---|---|---|
| Sync | SyncFunctionInvoker.cs |
Normal functions, uses IR with AST fallback |
| Generator | SyncGeneratorInvoker.cs |
Pause/resume via yield, returns iterator |
| Async | AsyncFunctionInvoker.cs |
Reuses generator IR, driven internally |
| Async Generator | AsyncGeneratorInvoker.cs |
Iterator where .next() returns Promise |
-
YieldInstructionpauses execution, returns{ value, done: false } -
StoreResumeValuehandles.next(value),.return(),.throw() -
YieldStarInstructiondelegates to inner iterator
-
awaiton unresolved promise setsIsPendingAwait -
HandlePendingStepattaches.then()handlers - Callbacks resume via
DriveToCompletion()
See: Generators & Async
Pooled types (implement IRentable):
-
JsEnvironment- execution scopes -
IteratorDriverState- for-of loop state -
ForInDriverState- for-in loop state - Various enumerators
Hot handlers use inlined fast path + non-inlined slow path:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static InstructionResult HandleIncrementSlot(...)
{
if (flatSlotId >= 0 && _flatSlots is not null)
return FastPath(...); // inline
return HandleIncrementSlotSlow(...); // no-inline
}Control flow modeled as typed signals (not exceptions):
ReturnCompletionSignalThrowFlowCompletionSignal-
BreakCompletionSignal/ContinueCompletionSignal YieldCompletionSignalPendingAwaitCompletionSignal
See: Performance Patterns
Two-tier system to catch pooling bugs:
| Tier | File | Scope |
|---|---|---|
PoolDebug |
PoolDebug.cs |
DEBUG-only, zero RELEASE overhead |
PoolGuard |
PoolGuard.cs |
Runtime, env var JSENGINE_DEBUG_POOL_GUARDS=true
|
#if DEBUG
EvaluationContext.AssertNoAstEvaluation = true;
// Throws if IR execution falls back to AST
#endifUse FakeLogger with DebugMode = true to capture slot hits/misses.
See: Debugging
| Aspect | Jint | JsEngine |
|---|---|---|
| ExecutionContext | Immutable struct | Sealed class |
| Promise/Async | Sync blocking | Microtask queue (spec-compliant) |
| Property Storage | HybridDictionary | Standard Dictionary |
| Control Flow | Exceptions | Completion Signals |
| Generators | AST replay | IR with pause/resume |
See: Jint vs JsEngine Comparison
dotnet build
dotnet test tests/Asynkron.JsEngine.Testsroslynator fix src/Asynkron.JsEngine-
dotnet test tests/Asynkron.JsEngine.Tests(all pass) -
quickdup --path src/Asynkron.JsEngine --ext .cs(no new duplications) dotnet format src/Asynkron.JsEngine
Scripts in scripts/:
-
profile.sh- CPU profiling with dotnet-trace - Heap and exception profiling available
| Guide | Description |
|---|---|
| Architecture | Full execution model deep-dive |
| JsValue Usage | Evaluator overload pattern |
| Debugging | Logger assertions, pool guards |
| Coding Standards | InvariantCulture, style rules |
| Development Rules | Thread safety, timeouts |
| Build & Test | Commands, demos |
| Profiling | CPU/memory profiling |
| Test Bombs | Systematic debugging methodology |
| Layered Tests | Stage-by-stage verification |
| Doc | Description |
|---|---|
| Jint Comparison | Architectural trade-offs |
| Instruction Size Audit | IR instruction optimization |
- Language features: 100% Test262 compliant
- Built-ins: ~95% complete (see Standard Library Index)
See: Test262 README