Implement ECMAScript realm execution contexts#703
Conversation
Add explicit realm and execution-context plumbing across interpreter and bytecode execution, move tagged template caching into realm-owned template maps, and cover repeated engine execution in CLI and native tests. Verification: - ./build/Goccia.Engine.Realm.Test - ./build/GocciaTestRunner tests --no-progress - ./build/GocciaTestRunner tests --no-progress --mode=bytecode - bun run scripts/test-cli-apps.ts - bun run scripts/test-cli.ts - ./format.pas --check
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThis PR centralizes realm ownership and ECMA-262-style execution contexts: introduces TGocciaExecutionContext/stack and RAII scopes, expands TGocciaRealm to own intrinsics/global/env/loaded-module/template maps, adds template-site IDs and realm-scoped template caching, wires realm propagation through engine/interpreter/executor/VM/frame lifecycles, updates tests/docs, and lowers default stack depth. ChangesRealm Isolation and Execution Context Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Suite TimingTest Runner (interpreted: 9,985 passed; bytecode: 9,985 passed)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Test runner worker shutdown frees thread-local heaps in bulk; that shutdown reclamation is not counted as GC collections or collected objects.
Benchmarks (interpreted: 407; bytecode: 407)
MemoryGC rows aggregate the main thread plus all worker thread-local GCs. Benchmark runner performs explicit between-file collections, so collection and collected-object counts can be much higher than the test runner.
Measured on ubuntu-latest x64. |
Benchmark Results407 benchmarks Interpreted: 🟢 25 improved · 🔴 43 regressed · 339 unchanged · avg -0.1% arraybuffer.js — Interp: 🔴 5, 9 unch. · avg -5.1% · Bytecode: 🔴 9, 5 unch. · avg -9.4%
arrays.js — Interp: 🟢 4, 15 unch. · avg +2.0% · Bytecode: 🔴 12, 7 unch. · avg -9.9%
async-await.js — Interp: 🟢 1, 5 unch. · avg +2.6% · Bytecode: 🔴 1, 5 unch. · avg -6.0%
async-generators.js — Interp: 2 unch. · avg +7.5% · Bytecode: 🔴 1, 1 unch. · avg -4.1%
base64.js — Interp: 10 unch. · avg -1.1% · Bytecode: 🔴 2, 8 unch. · avg -1.4%
classes.js — Interp: 🟢 1, 30 unch. · avg -0.5% · Bytecode: 🔴 12, 19 unch. · avg -4.8%
closures.js — Interp: 🔴 2, 9 unch. · avg -3.3% · Bytecode: 🔴 11 · avg -20.0%
collections.js — Interp: 12 unch. · avg -1.5% · Bytecode: 🔴 1, 11 unch. · avg -3.7%
csv.js — Interp: 13 unch. · avg -1.3% · Bytecode: 🔴 3, 10 unch. · avg -2.2%
destructuring.js — Interp: 🔴 1, 21 unch. · avg -2.4% · Bytecode: 🔴 8, 14 unch. · avg -7.7%
fibonacci.js — Interp: 8 unch. · avg -2.6% · Bytecode: 🔴 6, 2 unch. · avg -18.2%
float16array.js — Interp: 🔴 4, 28 unch. · avg -1.7% · Bytecode: 🟢 4, 🔴 11, 17 unch. · avg -3.7%
for-of.js — Interp: 7 unch. · avg -0.3% · Bytecode: 🔴 2, 5 unch. · avg -6.3%
generators.js — Interp: 🟢 1, 🔴 1, 2 unch. · avg -0.3% · Bytecode: 🔴 1, 3 unch. · avg -6.2%
iterators.js — Interp: 🟢 1, 🔴 1, 40 unch. · avg +1.0% · Bytecode: 🟢 4, 🔴 11, 27 unch. · avg -2.8%
json.js — Interp: 🟢 2, 18 unch. · avg +0.8% · Bytecode: 🔴 8, 12 unch. · avg -5.8%
jsx.jsx — Interp: 🔴 1, 20 unch. · avg -0.7% · Bytecode: 🔴 9, 12 unch. · avg -7.4%
modules.js — Interp: 🔴 1, 8 unch. · avg -5.8% · Bytecode: 🔴 7, 2 unch. · avg -34.9%
numbers.js — Interp: 11 unch. · avg -2.6% · Bytecode: 🔴 6, 5 unch. · avg -7.1%
objects.js — Interp: 7 unch. · avg +2.9% · Bytecode: 🔴 4, 3 unch. · avg -13.5%
promises.js — Interp: 🔴 1, 11 unch. · avg -2.5% · Bytecode: 🔴 5, 7 unch. · avg -8.9%
regexp.js — Interp: 11 unch. · avg +0.3% · Bytecode: 🔴 2, 9 unch. · avg -4.8%
strings.js — Interp: 🔴 5, 14 unch. · avg -12.7% · Bytecode: 🔴 11, 8 unch. · avg -17.9%
tsv.js — Interp: 🔴 3, 6 unch. · avg -0.6% · Bytecode: 🔴 3, 6 unch. · avg -9.0%
typed-arrays.js — Interp: 🔴 15, 7 unch. · avg -24.8% · Bytecode: 🟢 3, 🔴 8, 11 unch. · avg -1.8%
uint8array-encoding.js — Interp: 🟢 10, 8 unch. · avg +42.7% · Bytecode: 🟢 7, 🔴 9, 2 unch. · avg +15.2%
weak-collections.js — Interp: 🟢 5, 🔴 3, 7 unch. · avg +16.3% · Bytecode: 🟢 4, 🔴 4, 7 unch. · avg +11.9%
Deterministic profile diffDeterministic profile diff: no significant changes. Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context. |
test262 Conformance
Areas closest to 100%
Per-test deltas (+1 / -0)Newly passing (1):
Steady-state failures are non-blocking; regressions vs the cached main baseline (lower total pass count, or any PASS → non-PASS transition) fail the conformance gate. Measured on ubuntu-latest x64, bytecode mode. Areas grouped by the first two test262 path components; minimum 25 attempted tests, areas already at 100% excluded. Δ vs main compares against the most recent cached |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@source/units/Goccia.ExecutionContext.pas`:
- Around line 123-127: In TGocciaExecutionContextStack.Pop the code currently
zeroes the popped entry with FillChar on
GExecutionContextStack[GExecutionContextStackCount], which bypasses
managed-string finalization for the embedded TGocciaExecutionContext (which has
SourcePath: string); change the cleanup to a managed-safe operation by assigning
GExecutionContextStack[GExecutionContextStackCount] :=
Default(TGocciaExecutionContextStackEntry) (or call Finalize(...) then
Default(...)) instead of FillChar so SourcePath and other managed fields are
finalized properly; update the Pop implementation that references
GExecutionContextStack and GExecutionContextStackCount accordingly.
In `@source/units/Goccia.Executor.Bytecode.pas`:
- Around line 120-128: The code currently overwrites the VM realm on exit by
resetting FVM.Realm to FRealm; instead capture the prior realm before changing
it and restore that saved realm in the finally block. Specifically, save the
existing realm (e.g., SavedRealm := FVM.Realm) before assigning FVM.Realm :=
AContext.Realm in the try section, and in the finally block restore FVM.Realm :=
SavedRealm (leaving the existing FVM.GlobalScope save/restore using
SavedGlobalScope and the AContext.Scope handling unchanged). This ensures nested
ExecuteModule calls (SavedGlobalScope, AContext.Scope, AContext.Realm,
FVM.Realm) correctly restore the outer realm rather than forcing FRealm.
In `@source/units/Goccia.Realm.Test.pas`:
- Around line 378-405: The test resets the global CurrentRealm to nil in the
finally block which can clobber an existing thread realm; modify
TTestRealm.TestExecutionContextStackSetsCurrentRealm to capture the prior realm
into a local variable (e.g., PrevRealm) before calling
SetCurrentRealm(OuterRealm) and then in the finally restore it via
SetCurrentRealm(PrevRealm) instead of SetCurrentRealm(nil); keep the rest of the
teardown (freeing Guard, InnerRealm, OuterRealm) intact and ensure you reference
SetCurrentRealm, CurrentRealm, CreateExecutionContext and
RunningExecutionContext when making the change.
In `@source/units/Goccia.VM.pas`:
- Around line 7715-7720: The code pushes a new execution context using
FCurrentModuleSourcePath (caller module) causing source metadata to attribute to
the caller; change the source-file argument passed into CreateExecutionContext
to prefer the callee template's debug-info source: use
ATemplate.DebugInfo.SourceFile or AClosure.Template.DebugInfo.SourceFile when
available and only fall back to FCurrentModuleSourcePath if debug info is
missing; update the TGocciaExecutionContextStack.Push call in the
APushExecutionContext branch (and the similar block at the OP_IMPORT_META
location) to pass that resolved source-file string instead of
FCurrentModuleSourcePath so contexts reflect the defining module.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: df75f0cd-2eaa-47a4-a89d-a96777344fff
📒 Files selected for processing (28)
CONTEXT.mddocs/architecture.mddocs/build-system.mddocs/bytecode-vm.mddocs/core-patterns.mddocs/embedding.mdscripts/test-cli-apps.tssource/units/Goccia.AST.Expressions.passource/units/Goccia.Bytecode.Chunk.passource/units/Goccia.Engine.Realm.Test.passource/units/Goccia.Engine.passource/units/Goccia.Evaluator.Context.passource/units/Goccia.Evaluator.passource/units/Goccia.ExecutionContext.passource/units/Goccia.Executor.Bytecode.passource/units/Goccia.Executor.Interpreter.passource/units/Goccia.Executor.passource/units/Goccia.Interpreter.passource/units/Goccia.Modules.Loader.passource/units/Goccia.Realm.Test.passource/units/Goccia.Realm.passource/units/Goccia.StackLimit.passource/units/Goccia.VM.CallFrame.passource/units/Goccia.VM.Closure.passource/units/Goccia.VM.passource/units/Goccia.Values.FunctionValue.passource/units/Goccia.Values.GeneratorValue.pastests/language/functions/stack-depth-limit.js
- clear execution-context stack records without FillChar on managed fields - restore the previous bytecode VM realm after nested module execution - restore prior CurrentRealm in realm stack tests - attribute bytecode function contexts to callee debug source paths
Summary
RangeErrorinstead of native stack failure.Testing
Commands run:
./build.pas clean testrunner loader repl bundler benchmarkrunner loaderbare tests./build.pas clean tests testrunner./build/Goccia.Realm.Test./build/Goccia.Engine.Realm.Test./build/GocciaTestRunner tests --no-progress./build/GocciaTestRunner tests --no-progress --mode=bytecodebun run scripts/test-cli-apps.tsbun run scripts/test-cli.ts./format.pas --checkgit diff --check