diff --git a/bytecode_migration_plan.md b/bytecode_migration_plan.md index 07e5649..1791c5f 100644 --- a/bytecode_migration_plan.md +++ b/bytecode_migration_plan.md @@ -128,6 +128,7 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te - [ ] Step 27: Remove interpreter opcodes and constants from bytecode runtime. - [ ] Delete `BytecodeConst.ValueFn`, `CmdMakeValueFn`, and `MAKE_VALUE_FN` (blocked: some lambdas still fall back to non-bytecode bodies). - [x] Delete `BytecodeConst.StatementVal`, `CmdEvalStmt`, and `EVAL_STMT`. + - [x] Add bytecode-backed `::class` via `ClassOperatorRef` + `GET_OBJ_CLASS` to avoid ValueFn for class operator. - [ ] Remove `emitStatementCall`/`emitStatementEval` once unused. - [ ] Step 28: Scope as facade only. - [ ] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls. diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index b695b98..5c80a57 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -2044,6 +2044,7 @@ class Compiler( is QualifiedThisMethodSlotCallRef -> false is QualifiedThisFieldSlotRef -> false is ClassScopeMemberRef -> false + is ClassOperatorRef -> containsUnsupportedRef(ref.target) else -> false } } @@ -3118,9 +3119,7 @@ class Compiler( if (t.type != Token.Type.ID) throw ScriptError(t.pos, "Expecting ID after ::") return when (t.value) { "class" -> { - val ref = ValueFnRef { scope -> - operand.evalValue(scope).objClass.asReadonly - } + val ref = ClassOperatorRef(operand, t.pos) lambdaReturnTypeByRef[ref] = ObjClassType ref } @@ -7247,6 +7246,7 @@ class Compiler( decl?.let { resolveClassByName(it.typeName) } } is ValueFnRef -> lambdaReturnTypeByRef[directRef] + is ClassOperatorRef -> lambdaReturnTypeByRef[directRef] is CastRef -> resolveTypeRefClass(directRef.castTypeRef()) is BinaryOpRef -> inferBinaryOpReturnClass(directRef) is ImplicitThisMethodCallRef -> { 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 f516ae6..c332425 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -420,6 +420,14 @@ class BytecodeCompiler( CompiledValue(slot, resolved) } is ValueFnRef -> compileValueFnRef(ref) + is ClassOperatorRef -> { + val target = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null + val dst = allocSlot() + builder.emit(Opcode.GET_OBJ_CLASS, target.slot, dst) + updateSlotType(dst, SlotType.OBJ) + slotObjClass[dst] = ObjClassType + CompiledValue(dst, SlotType.OBJ) + } is ListLiteralRef -> compileListLiteral(ref) is MapLiteralRef -> compileMapLiteral(ref) is ThisMethodSlotCallRef -> compileThisMethodSlotCall(ref) 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 5034b5e..718368a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt @@ -129,7 +129,7 @@ class CmdBuilder { Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList() Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ, Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL, - Opcode.OBJ_TO_BOOL, + Opcode.OBJ_TO_BOOL, Opcode.GET_OBJ_CLASS, Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT, Opcode.ASSERT_IS -> listOf(OperandKind.SLOT, OperandKind.SLOT) @@ -258,6 +258,7 @@ class CmdBuilder { Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1]) Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1]) Opcode.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1]) + Opcode.GET_OBJ_CLASS -> CmdGetObjClass(operands[0], operands[1]) Opcode.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3]) Opcode.LOAD_THIS -> CmdLoadThis(operands[0]) Opcode.LOAD_THIS_VARIANT -> CmdLoadThisVariant(operands[0], operands[1]) 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 f380838..6534f7e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt @@ -90,6 +90,7 @@ object CmdDisassembler { is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst) is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst) is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst) + is CmdGetObjClass -> Opcode.GET_OBJ_CLASS to intArrayOf(cmd.src, cmd.dst) is CmdCheckIs -> Opcode.CHECK_IS to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst) is CmdAssertIs -> Opcode.ASSERT_IS to intArrayOf(cmd.objSlot, cmd.typeSlot) is CmdMakeQualifiedView -> Opcode.MAKE_QUALIFIED_VIEW to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst) @@ -256,7 +257,7 @@ object CmdDisassembler { Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL, Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT -> listOf(OperandKind.SLOT, OperandKind.SLOT) - Opcode.OBJ_TO_BOOL, Opcode.ASSERT_IS -> + Opcode.OBJ_TO_BOOL, Opcode.GET_OBJ_CLASS, Opcode.ASSERT_IS -> listOf(OperandKind.SLOT, OperandKind.SLOT) Opcode.CHECK_IS, Opcode.MAKE_QUALIFIED_VIEW -> listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) 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 5c3e2a3..925cd60 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -214,6 +214,14 @@ class CmdObjToBool(internal val src: Int, internal val dst: Int) : Cmd() { } } +class CmdGetObjClass(internal val src: Int, internal val dst: Int) : Cmd() { + override suspend fun perform(frame: CmdFrame) { + val cls = frame.slotToObj(src).objClass + frame.setObj(dst, cls) + return + } +} + class CmdCheckIs(internal val objSlot: Int, internal val typeSlot: Int, internal val dst: Int) : Cmd() { override suspend fun perform(frame: CmdFrame) { val obj = frame.slotToObj(objSlot) 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 35913d1..fbb879e 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt @@ -43,6 +43,7 @@ enum class Opcode(val code: Int) { ASSERT_IS(0x16), MAKE_QUALIFIED_VIEW(0x17), MAKE_LAMBDA_FN(0x18), + GET_OBJ_CLASS(0x19), ADD_INT(0x20), SUB_INT(0x21), diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt index bdc9e9b..ac946a2 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt @@ -78,6 +78,13 @@ open class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef { override suspend fun get(scope: Scope): ObjRecord = fn(scope) } +/** Compile-time supported ::class operator reference. */ +class ClassOperatorRef(val target: ObjRef, val pos: Pos) : ObjRef { + override suspend fun get(scope: Scope): ObjRecord { + return target.evalValue(scope).objClass.asReadonly + } +} + /** Unary operations supported by ObjRef. */ enum class UnaryOp { NOT, NEGATE, BITNOT }