diff --git a/bytecode_migration_plan.md b/bytecode_migration_plan.md new file mode 100644 index 0000000..0c0319b --- /dev/null +++ b/bytecode_migration_plan.md @@ -0,0 +1,41 @@ +# Bytecode migration plan (compiler -> frames/bytecode) + +This is a step-by-step checklist to track remaining non-bytecode paths in the compiler. +Mark items as you implement them. Priorities are ordered by expected simplicity. + +## Priority 1: Quick wins (local changes) +- [ ] Implement bytecode emission for `DelegatedVarDeclStatement`. + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt:3284`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1667` +- [ ] Implement bytecode emission for `DestructuringVarDeclStatement`. + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt:3285`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1667` +- [ ] Ensure `ExtensionPropertyDeclStatement` is handled in both value and no-value bytecode paths. + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt:3332` + +## Priority 2: Conservative wrapper guards to relax +- [ ] Allow wrapping `BreakStatement` / `ContinueStatement` / `ReturnStatement` where bytecode already supports them. + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1608` +- [ ] Revisit `containsLoopControl` as a hard blocker for wrapping (once label handling is verified). + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1542` + +## Priority 3: Medium complexity statements +- [ ] Implement bytecode support for `TryStatement`. + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1698`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt:3290` +- [ ] Expand `WhenStatement` condition coverage (remove "unsupported condition" paths). + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1699` + +## Priority 4: Ref-level blockers +- [ ] Support dynamic member access in bytecode (`FieldRef` / `MethodCallRef` on `ObjDynamic`). + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1735` +- [ ] Implement bytecode for qualified/captured member refs: + - `QualifiedThisMethodSlotCallRef` + - `QualifiedThisFieldSlotRef` + - `ClassScopeMemberRef` + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1759` + +## Priority 5: Wrapping policy improvements +- [ ] Allow partial script wrapping (wrap supported statements even when others are unsupported). + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt:1333` +- [ ] Re-evaluate tooling paths that disable bytecode: + - `CompileTimeResolution.dryRun` (resolution-only) + - `LyngLanguageTools.analyze` (diagnostics/mini-ast) + - References: `lynglib/src/commonMain/kotlin/net/sergeych/lyng/resolution/CompileTimeResolution.kt:67`, `lynglib/src/commonMain/kotlin/net/sergeych/lyng/tools/LyngLanguageTools.kt:97` diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index 6c4c1dd..43db0a8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -99,7 +99,7 @@ class BytecodeCompiler( is net.sergeych.lyng.InlineBlockStatement -> compileInlineBlock(name, stmt) is VarDeclStatement -> compileVarDecl(name, stmt) is DelegatedVarDeclStatement -> { - val value = emitStatementEval(stmt) + val value = emitDelegatedVarDecl(stmt) ?: return null builder.emit(Opcode.RET, value.slot) val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount builder.build( @@ -3281,7 +3281,7 @@ class BytecodeCompiler( } is BlockStatement -> emitBlock(target, true) is VarDeclStatement -> emitVarDecl(target) - is DelegatedVarDeclStatement -> emitStatementEval(target) + is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target) is DestructuringVarDeclStatement -> emitStatementEval(target) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target) is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target) @@ -3308,7 +3308,7 @@ class BytecodeCompiler( } } is VarDeclStatement -> emitVarDecl(target) - is DelegatedVarDeclStatement -> emitStatementEval(target) + is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target) is IfStatement -> compileIfStatement(target) is net.sergeych.lyng.ForInStatement -> { val resultSlot = emitForIn(target, false) ?: return null @@ -3576,6 +3576,21 @@ class BytecodeCompiler( return value } + private fun emitDelegatedVarDecl(stmt: DelegatedVarDeclStatement): CompiledValue? { + val value = compileStatementValueOrFallback(stmt.initializer) ?: return null + val declId = builder.addConst( + BytecodeConst.DelegatedDecl( + stmt.name, + stmt.isMutable, + stmt.visibility, + stmt.isTransient + ) + ) + builder.emit(Opcode.DECL_DELEGATED, declId, value.slot) + updateSlotType(value.slot, SlotType.OBJ) + return CompiledValue(value.slot, SlotType.OBJ) + } + private fun updateNameObjClass(name: String, initializer: Statement?, initializerObjClass: ObjClass? = null) { val cls = initializerObjClass ?: objClassForInitializer(initializer) if (cls != null) { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt index 599e3d1..21368c9 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt @@ -46,6 +46,12 @@ sealed class BytecodeConst { val visibility: Visibility, val isTransient: Boolean, ) : BytecodeConst() + data class DelegatedDecl( + val name: String, + val isMutable: Boolean, + val visibility: Visibility, + val isTransient: Boolean, + ) : BytecodeConst() data class CallArgsPlan(val tailBlock: Boolean, val specs: List) : BytecodeConst() data class CallArgSpec(val name: String?, val isSplat: Boolean) } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt index 6558a8e..5e14230 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt @@ -144,7 +144,7 @@ class CmdBuilder { listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN -> listOf(OperandKind.CONST) - Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY -> + Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED -> listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, Opcode.ADD_REAL, Opcode.SUB_REAL, Opcode.MUL_REAL, Opcode.DIV_REAL, @@ -377,6 +377,7 @@ class CmdBuilder { Opcode.PUSH_SLOT_PLAN -> CmdPushSlotPlan(operands[0]) Opcode.POP_SLOT_PLAN -> CmdPopSlotPlan() Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1]) + Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1]) Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1]) Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3]) Opcode.CALL_MEMBER_SLOT -> CmdCallMemberSlot(operands[0], operands[1], operands[2], operands[3], operands[4]) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt index 9b30177..16777c1 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt @@ -185,6 +185,7 @@ object CmdDisassembler { is CmdPushSlotPlan -> Opcode.PUSH_SLOT_PLAN to intArrayOf(cmd.planId) is CmdPopSlotPlan -> Opcode.POP_SLOT_PLAN to intArrayOf() is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot) + is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot) is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot) is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst) is CmdCallMemberSlot -> Opcode.CALL_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.methodId, cmd.argBase, cmd.argCount, cmd.dst) @@ -240,7 +241,7 @@ object CmdDisassembler { listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN -> listOf(OperandKind.CONST) - Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY -> + Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED -> listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, Opcode.ADD_REAL, Opcode.SUB_REAL, Opcode.MUL_REAL, Opcode.DIV_REAL, diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt index 56c8d33..0ff55a8 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -1187,6 +1187,35 @@ class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() { } } +class CmdDeclDelegated(internal val constId: Int, internal val slot: Int) : Cmd() { + override suspend fun perform(frame: CmdFrame) { + val decl = frame.fn.constants[constId] as? BytecodeConst.DelegatedDecl + ?: error("DECL_DELEGATED expects DelegatedDecl at $constId") + val initValue = frame.slotToObj(slot) + val accessType = ObjString(if (decl.isMutable) "Var" else "Val") + val finalDelegate = try { + initValue.invokeInstanceMethod( + frame.ensureScope(), + "bind", + Arguments(ObjString(decl.name), accessType, ObjNull) + ) + } catch (_: Exception) { + initValue + } + val rec = frame.ensureScope().addItem( + decl.name, + decl.isMutable, + ObjNull, + decl.visibility, + recordType = ObjRecord.Type.Delegated, + isTransient = decl.isTransient + ) + rec.delegate = finalDelegate + frame.storeObjResult(slot, finalDelegate) + return + } +} + class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cmd() { override suspend fun perform(frame: CmdFrame) { val decl = frame.fn.constants[constId] as? BytecodeConst.ExtensionPropertyDecl diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt index b60a747..a774055 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt @@ -125,6 +125,7 @@ enum class Opcode(val code: Int) { POP_SLOT_PLAN(0x88), DECL_LOCAL(0x89), DECL_EXT_PROPERTY(0x8A), + DECL_DELEGATED(0x8B), CALL_DIRECT(0x90), CALL_MEMBER_SLOT(0x92),