Step 27B: bytecode ::class

This commit is contained in:
Sergey Chernov 2026-02-11 23:10:32 +03:00
parent ac680ceb6c
commit 18278794d6
8 changed files with 32 additions and 5 deletions

View File

@ -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. - [ ] 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). - [ ] 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] 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. - [ ] Remove `emitStatementCall`/`emitStatementEval` once unused.
- [ ] Step 28: Scope as facade only. - [ ] Step 28: Scope as facade only.
- [ ] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls. - [ ] Audit bytecode execution paths for `Statement.execute` usage and remove remaining calls.

View File

@ -2044,6 +2044,7 @@ class Compiler(
is QualifiedThisMethodSlotCallRef -> false is QualifiedThisMethodSlotCallRef -> false
is QualifiedThisFieldSlotRef -> false is QualifiedThisFieldSlotRef -> false
is ClassScopeMemberRef -> false is ClassScopeMemberRef -> false
is ClassOperatorRef -> containsUnsupportedRef(ref.target)
else -> false else -> false
} }
} }
@ -3118,9 +3119,7 @@ class Compiler(
if (t.type != Token.Type.ID) throw ScriptError(t.pos, "Expecting ID after ::") if (t.type != Token.Type.ID) throw ScriptError(t.pos, "Expecting ID after ::")
return when (t.value) { return when (t.value) {
"class" -> { "class" -> {
val ref = ValueFnRef { scope -> val ref = ClassOperatorRef(operand, t.pos)
operand.evalValue(scope).objClass.asReadonly
}
lambdaReturnTypeByRef[ref] = ObjClassType lambdaReturnTypeByRef[ref] = ObjClassType
ref ref
} }
@ -7247,6 +7246,7 @@ class Compiler(
decl?.let { resolveClassByName(it.typeName) } decl?.let { resolveClassByName(it.typeName) }
} }
is ValueFnRef -> lambdaReturnTypeByRef[directRef] is ValueFnRef -> lambdaReturnTypeByRef[directRef]
is ClassOperatorRef -> lambdaReturnTypeByRef[directRef]
is CastRef -> resolveTypeRefClass(directRef.castTypeRef()) is CastRef -> resolveTypeRefClass(directRef.castTypeRef())
is BinaryOpRef -> inferBinaryOpReturnClass(directRef) is BinaryOpRef -> inferBinaryOpReturnClass(directRef)
is ImplicitThisMethodCallRef -> { is ImplicitThisMethodCallRef -> {

View File

@ -420,6 +420,14 @@ class BytecodeCompiler(
CompiledValue(slot, resolved) CompiledValue(slot, resolved)
} }
is ValueFnRef -> compileValueFnRef(ref) 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 ListLiteralRef -> compileListLiteral(ref)
is MapLiteralRef -> compileMapLiteral(ref) is MapLiteralRef -> compileMapLiteral(ref)
is ThisMethodSlotCallRef -> compileThisMethodSlotCall(ref) is ThisMethodSlotCallRef -> compileThisMethodSlotCall(ref)

View File

@ -129,7 +129,7 @@ class CmdBuilder {
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList() Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList()
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ, 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.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.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT,
Opcode.ASSERT_IS -> Opcode.ASSERT_IS ->
listOf(OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT)
@ -258,6 +258,7 @@ class CmdBuilder {
Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1]) Opcode.MAKE_LAMBDA_FN -> CmdMakeLambda(operands[0], operands[1])
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1]) Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
Opcode.OBJ_TO_BOOL -> CmdObjToBool(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.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3])
Opcode.LOAD_THIS -> CmdLoadThis(operands[0]) Opcode.LOAD_THIS -> CmdLoadThis(operands[0])
Opcode.LOAD_THIS_VARIANT -> CmdLoadThisVariant(operands[0], operands[1]) Opcode.LOAD_THIS_VARIANT -> CmdLoadThisVariant(operands[0], operands[1])

View File

@ -90,6 +90,7 @@ object CmdDisassembler {
is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst) is CmdMakeLambda -> Opcode.MAKE_LAMBDA_FN to intArrayOf(cmd.id, cmd.dst)
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, 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 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 CmdCheckIs -> Opcode.CHECK_IS to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
is CmdAssertIs -> Opcode.ASSERT_IS to intArrayOf(cmd.objSlot, cmd.typeSlot) 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) 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.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 -> Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
listOf(OperandKind.SLOT, OperandKind.SLOT) 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) listOf(OperandKind.SLOT, OperandKind.SLOT)
Opcode.CHECK_IS, Opcode.MAKE_QUALIFIED_VIEW -> Opcode.CHECK_IS, Opcode.MAKE_QUALIFIED_VIEW ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)

View File

@ -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() { class CmdCheckIs(internal val objSlot: Int, internal val typeSlot: Int, internal val dst: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) { override suspend fun perform(frame: CmdFrame) {
val obj = frame.slotToObj(objSlot) val obj = frame.slotToObj(objSlot)

View File

@ -43,6 +43,7 @@ enum class Opcode(val code: Int) {
ASSERT_IS(0x16), ASSERT_IS(0x16),
MAKE_QUALIFIED_VIEW(0x17), MAKE_QUALIFIED_VIEW(0x17),
MAKE_LAMBDA_FN(0x18), MAKE_LAMBDA_FN(0x18),
GET_OBJ_CLASS(0x19),
ADD_INT(0x20), ADD_INT(0x20),
SUB_INT(0x21), SUB_INT(0x21),

View File

@ -78,6 +78,13 @@ open class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord = fn(scope) 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. */ /** Unary operations supported by ObjRef. */
enum class UnaryOp { NOT, NEGATE, BITNOT } enum class UnaryOp { NOT, NEGATE, BITNOT }