Migrate runtime locals to frame slots
This commit is contained in:
1 parent
34678068ac
commit
cd145f6a96
27 files changed
+1247
-398
No files matched your search
@@ -0,0 +1,94 @@
|
||||
# Plan: Eliminate Scope Slots for Module + Extensions (Frame-Backed Only)
|
||||
|
||||
Goal (1 + 2)
|
||||
- Module-level vars must be frame-backed just like locals; no special scope storage.
|
||||
- Extension wrappers must use frame slots as well; no scope-slot storage.
|
||||
- Scope remains only as a facade for Kotlin extern symbols (reflection/interop), not as primary storage.
|
||||
|
||||
Non-Goals (for now)
|
||||
- Full reflection redesign (set aside).
|
||||
- Removing scope slots entirely (interop/reflection may still need a facade).
|
||||
|
||||
Current Findings (high level)
|
||||
- BytecodeCompiler still builds scope slots for module symbols and for extension wrappers via pendingScopeNameRefs.
|
||||
- CmdRuntime reads/writes scope slots whenever slot < scopeSlotCount.
|
||||
- RESOLVE_SCOPE_SLOT + addr slots are emitted for moving between scope and frame slots.
|
||||
|
||||
Design Direction
|
||||
- Frame slots are the sole storage for module vars and extension wrappers.
|
||||
- Scope slots are reserved for explicit extern/interop names only (not part of this change).
|
||||
- Module symbols are captured like any other (closure capture via FrameSlotRef), no module slot plan or scope slot mapping.
|
||||
- Persist the module frame inside ModuleScope so its lifetime matches the module.
|
||||
|
||||
Key Decisions
|
||||
- Module scope is just another frame in the parent chain; do not seed or persist module vars through scope slots.
|
||||
- Extension wrappers are treated like ordinary locals/globals in frame slots; no pending scope name refs.
|
||||
- Compatibility: when `seedScope` is not a `ModuleScope`, module-level declarations still map to scope slots so `Scope.eval(...)` retains cross-call variables.
|
||||
|
||||
Work Plan
|
||||
|
||||
Phase A: Compiler changes (scope slot creation)
|
||||
1) Stop adding module declarations to scope slots
|
||||
- In `BytecodeCompiler.collectScopeSlots`, when `moduleScopeId` matches a declaration, treat it as a local slot, not a scope slot.
|
||||
- Remove `isModuleDecl` special-casing that pushes into `scopeSlotMap`.
|
||||
|
||||
2) Remove synthetic scope slots for extension wrappers
|
||||
- Gate `pendingScopeNameRefs` (extension callable/getter/setter queueing) behind explicit scope-slot usage.
|
||||
- Ensure extension wrappers are resolved via local slot indices only (frame-backed).
|
||||
|
||||
3) Tighten scope-slot eligibility
|
||||
- `isModuleSlot(...)` should no longer route module names to scope slots.
|
||||
- Only names in `allowedScopeNames` / `scopeSlotNameSet` (extern/interop) should become scope slots.
|
||||
|
||||
4) ResolveSlot prefers frame slots for everything
|
||||
- Update `resolveSlot` to pick local slots for module/extension names.
|
||||
- Only return scopeSlotMap entries for explicit extern/interop names.
|
||||
|
||||
Phase B: Runtime changes (module frame)
|
||||
1) Persist the module frame inside ModuleScope
|
||||
- Add `ModuleScope.ensureModuleFrame(fn)` that creates/reuses a `BytecodeFrame` with `fn.localCount`.
|
||||
- Store local slot metadata on ModuleScope for future interop (names/mutables/delegated).
|
||||
|
||||
2) Use the module frame when executing the module bytecode
|
||||
- `CmdFrame` should reuse the module frame when `scope0 is ModuleScope` and args are empty.
|
||||
- Avoid clobbering module locals with args.
|
||||
|
||||
3) Remove module seed path that depends on scope slots
|
||||
- Remove `moduleSlotPlan` application in `Script.execute`.
|
||||
- Keep `seedImportBindings` and `seedModuleLocals`, but write into the module frame (scopeSlotCount should be 0).
|
||||
|
||||
Phase C: Bytecode + ABI considerations
|
||||
1) No new opcodes required for (1) + (2)
|
||||
- If we later need persistence across module reloads, introduce explicit sync opcodes (requires approval).
|
||||
|
||||
2) Migration safety
|
||||
- Keep existing scope-slot ops for interop only; do not delete opcodes yet.
|
||||
|
||||
Detailed Change List (by file)
|
||||
- `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt`
|
||||
- `collectScopeSlots`: treat module declarations as locals; do not map into `scopeSlotMap`.
|
||||
- Remove/guard `pendingScopeNameRefs` queuing for extension wrappers.
|
||||
- `isModuleSlot`: stop routing by moduleScopeId; only allow for explicit interop names.
|
||||
- `resolveSlot`: prefer local slots; avoid scope slot fallback for module/extension names.
|
||||
|
||||
- `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Script.kt`
|
||||
- Remove module slot plan application that assumes scope slots.
|
||||
- Ensure module vars survive via frame capture rather than scope seeding.
|
||||
|
||||
- `lynglib/src/commonMain/kotlin/net/sergeych/lyng/ModuleScope.kt`
|
||||
- Add module frame storage + metadata and `ensureModuleFrame(fn)`.
|
||||
|
||||
- `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt`
|
||||
- Use module frame when `scope0 is ModuleScope` in `CmdFrame`.
|
||||
|
||||
Testing / Validation
|
||||
- Disassemble compiled modules: verify no `RESOLVE_SCOPE_SLOT` for module vars or extension wrappers.
|
||||
- Run `:lynglib:jvmTest` and at least one module script execution that references module vars across calls.
|
||||
- Verify extension call wrappers still resolve and invoke correctly without scope-slot backing.
|
||||
|
||||
Risks / Open Questions
|
||||
- Module persistence: if module vars were previously “persisted” via scope slots, confirm frame-backed storage matches desired lifetime.
|
||||
- Extension wrapper lookup: ensure name resolution does not rely on scope slot indexing anywhere else.
|
||||
|
||||
Approval Gates
|
||||
- Any new opcode or runtime sync step must be proposed separately and approved before implementation.
|
||||
Reference in new issue
Block a user