Add bytecode support for delegated var decls
This commit is contained in:
parent
3391da595f
commit
eb58720365
41
bytecode_migration_plan.md
Normal file
41
bytecode_migration_plan.md
Normal file
@ -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`
|
||||
@ -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) {
|
||||
|
||||
@ -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<CallArgSpec>) : BytecodeConst()
|
||||
data class CallArgSpec(val name: String?, val isSplat: Boolean)
|
||||
}
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user