diff --git a/bytecode_migration_plan.md b/bytecode_migration_plan.md index 684f3e1..c0f348d 100644 --- a/bytecode_migration_plan.md +++ b/bytecode_migration_plan.md @@ -114,10 +114,10 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te - [ ] Step 25: Replace Statement-based declaration calls in bytecode. - [x] Add bytecode const/op for enum declarations (no `Statement` objects in constants). - - [ ] Add bytecode const/op for class declarations (no `Statement` objects in constants). + - [x] Add bytecode const/op for class declarations (no `Statement` objects in constants). - [x] Add bytecode const/op for function declarations (no `Statement` objects in constants). - [x] Replace `emitStatementCall` usage for `EnumDeclStatement`. - - [ ] Replace `emitStatementCall` usage for `ClassDeclStatement`. + - [x] Replace `emitStatementCall` usage for `ClassDeclStatement`. - [x] Replace `emitStatementCall` usage for `FunctionDeclStatement`. - [ ] Add JVM disasm coverage to ensure module init has no `CALL_SLOT` to `Callable@...` for declarations. - [ ] Step 26: Bytecode-backed lambdas (remove `ValueFnRef` runtime execution). diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt index 28b153c..82d42ca 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/ClassDeclStatement.kt @@ -17,20 +17,147 @@ package net.sergeych.lyng import net.sergeych.lyng.obj.Obj +import net.sergeych.lyng.obj.ObjClass +import net.sergeych.lyng.obj.ObjException +import net.sergeych.lyng.obj.ObjInstance +import net.sergeych.lyng.obj.ObjInstanceClass +import net.sergeych.lyng.obj.ObjNull +import net.sergeych.lyng.obj.ObjRecord + +data class ClassDeclBaseSpec( + val name: String, + val args: List? +) + +data class ClassDeclSpec( + val declaredName: String?, + val className: String, + val typeName: String, + val startPos: Pos, + val isExtern: Boolean, + val isAbstract: Boolean, + val isObject: Boolean, + val isAnonymous: Boolean, + val baseSpecs: List, + val constructorArgs: ArgsDeclaration?, + val constructorFieldIds: Map?, + val bodyInit: Statement?, + val initScope: List, +) + +internal suspend fun executeClassDecl(scope: Scope, spec: ClassDeclSpec): Obj { + if (spec.isObject) { + val parentClasses = spec.baseSpecs.map { baseSpec -> + val rec = scope[baseSpec.name] ?: throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}") + (rec.value as? ObjClass) ?: throw ScriptError(spec.startPos, "${baseSpec.name} is not a class") + } + + val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray()) + newClass.isAnonymous = spec.isAnonymous + newClass.constructorMeta = ArgsDeclaration(emptyList(), Token.Type.RPAREN) + for (i in parentClasses.indices) { + val argsList = spec.baseSpecs[i].args + if (argsList != null) newClass.directParentArgs[parentClasses[i]] = argsList + } + + val classScope = scope.createChildScope(newThisObj = newClass) + classScope.currentClassCtx = newClass + newClass.classScope = classScope + classScope.addConst("object", newClass) + + spec.bodyInit?.execute(classScope) + + val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY)) + if (spec.declaredName != null) { + scope.addItem(spec.declaredName, false, instance) + } + return instance + } + + if (spec.isExtern) { + val rec = scope[spec.className] + val existing = rec?.value as? ObjClass + val resolved = if (existing != null) { + existing + } else if (spec.className.contains('.')) { + scope.resolveQualifiedIdentifier(spec.className) as? ObjClass + } else { + null + } + val stub = resolved ?: ObjInstanceClass(spec.className).apply { this.isAbstract = true } + spec.declaredName?.let { scope.addItem(it, false, stub) } + return stub + } + + val parentClasses = spec.baseSpecs.map { baseSpec -> + val rec = scope[baseSpec.name] + val cls = rec?.value as? ObjClass + if (cls != null) return@map cls + if (baseSpec.name == "Exception") return@map ObjException.Root + if (rec == null) throw ScriptError(spec.startPos, "unknown base class: ${baseSpec.name}") + throw ScriptError(spec.startPos, "${baseSpec.name} is not a class") + } + + val constructorCode = object : Statement() { + override val pos: Pos = spec.startPos + override suspend fun execute(scope: Scope): Obj { + val instance = scope.thisObj as ObjInstance + return instance + } + } + + val newClass = ObjInstanceClass(spec.className, *parentClasses.toTypedArray()).also { + it.isAbstract = spec.isAbstract + it.instanceConstructor = constructorCode + it.constructorMeta = spec.constructorArgs + for (i in parentClasses.indices) { + val argsList = spec.baseSpecs[i].args + if (argsList != null) it.directParentArgs[parentClasses[i]] = argsList + } + spec.constructorArgs?.params?.forEach { p -> + if (p.accessType != null) { + it.createField( + p.name, + ObjNull, + isMutable = p.accessType == AccessType.Var, + visibility = p.visibility ?: Visibility.Public, + declaringClass = it, + pos = Pos.builtIn, + isTransient = p.isTransient, + type = ObjRecord.Type.ConstructorField, + fieldId = spec.constructorFieldIds?.get(p.name) + ) + } + } + } + + spec.declaredName?.let { scope.addItem(it, false, newClass) } + val classScope = scope.createChildScope(newThisObj = newClass) + classScope.currentClassCtx = newClass + newClass.classScope = classScope + spec.bodyInit?.execute(classScope) + if (spec.initScope.isNotEmpty()) { + for (s in spec.initScope) { + s.execute(classScope) + } + } + newClass.checkAbstractSatisfaction(spec.startPos) + return newClass +} class ClassDeclStatement( - val executable: DeclExecutable, - private val startPos: Pos, - val declaredName: String?, + val spec: ClassDeclSpec, ) : Statement() { - override val pos: Pos = startPos + override val pos: Pos = spec.startPos + val declaredName: String? get() = spec.declaredName + val typeName: String get() = spec.typeName override suspend fun execute(scope: Scope): Obj { - return executable.execute(scope) + return executeClassDecl(scope, spec) } override suspend fun callOn(scope: Scope): Obj { val target = scope.parent ?: scope - return executable.execute(target) + return executeClassDecl(target, spec) } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index 6a9d784..2d5ac81 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -5959,38 +5959,22 @@ class Compiler( val initScope = popInitScope() - val declStatement = object : Statement() { - override val pos: Pos = startPos - override suspend fun execute(scope: Scope): Obj { - val parentClasses = baseSpecs.map { baseSpec -> - val rec = scope[baseSpec.name] ?: throw ScriptError(startPos, "unknown base class: ${baseSpec.name}") - (rec.value as? ObjClass) ?: throw ScriptError(startPos, "${baseSpec.name} is not a class") - } - - val newClass = ObjInstanceClass(className, *parentClasses.toTypedArray()) - newClass.isAnonymous = nameToken == null - newClass.constructorMeta = ArgsDeclaration(emptyList(), Token.Type.RPAREN) - for (i in parentClasses.indices) { - val argsList = baseSpecs[i].args - // In object, we evaluate parent args once at creation time - if (argsList != null) newClass.directParentArgs[parentClasses[i]] = argsList - } - - val classScope = scope.createChildScope(newThisObj = newClass) - classScope.currentClassCtx = newClass - newClass.classScope = classScope - classScope.addConst("object", newClass) - - bodyInit?.execute(classScope) - - // Create instance (singleton) - val instance = newClass.callOn(scope.createChildScope(Arguments.EMPTY)) - if (declaredName != null) - scope.addItem(declaredName, false, instance) - return instance - } - } - return ClassDeclStatement(StatementDeclExecutable(declStatement), startPos, className) + val spec = ClassDeclSpec( + declaredName = declaredName, + className = className, + typeName = className, + startPos = startPos, + isExtern = false, + isAbstract = false, + isObject = true, + isAnonymous = nameToken == null, + baseSpecs = baseSpecs.map { ClassDeclBaseSpec(it.name, it.args) }, + constructorArgs = null, + constructorFieldIds = null, + bodyInit = bodyInit, + initScope = emptyList() + ) + return ClassDeclStatement(spec) } private suspend fun parseClassDeclaration(isAbstract: Boolean = false, isExtern: Boolean = false): Statement { @@ -6313,91 +6297,23 @@ class Compiler( // create instance constructor // create custom objClass with all fields and instance constructor - val constructorCode = object : Statement() { - override val pos: Pos = startPos - override suspend fun execute(scope: Scope): Obj { - // constructor code is registered with class instance and is called over - // new `thisObj` already set by class to ObjInstance.instanceContext - val instance = scope.thisObj as ObjInstance - // Constructor parameters have been assigned to instance scope by ObjClass.callOn before - // invoking parent/child constructors. - // IMPORTANT: do not execute class body here; class body was executed once in the class scope - // to register methods and prepare initializers. Instance constructor should be empty unless - // we later add explicit constructor body syntax. - return instance - } - } val classInfo = compileClassInfos[className] - val classDeclStatement = object : Statement() { - override val pos: Pos = startPos - override suspend fun execute(scope: Scope): Obj { - // the main statement should create custom ObjClass instance with field - // accessors, constructor registration, etc. - if (isExtern) { - val rec = scope[className] - val existing = rec?.value as? ObjClass - val resolved = existing ?: resolveClassByName(className) - val stub = resolved ?: ObjInstanceClass(className).apply { this.isAbstract = true } - scope.addItem(declaredName, false, stub) - return stub - } - // Resolve parent classes by name at execution time - val parentClasses = baseSpecs.map { baseSpec -> - val rec = scope[baseSpec.name] - val cls = rec?.value as? ObjClass - if (cls != null) return@map cls - if (baseSpec.name == "Exception") return@map ObjException.Root - if (rec == null) throw ScriptError(nameToken.pos, "unknown base class: ${baseSpec.name}") - throw ScriptError(nameToken.pos, "${baseSpec.name} is not a class") - } - - val newClass = ObjInstanceClass(className, *parentClasses.toTypedArray()).also { - it.isAbstract = isAbstract - it.instanceConstructor = constructorCode - it.constructorMeta = constructorArgsDeclaration - // Attach per-parent constructor args (thunks) if provided - for (i in parentClasses.indices) { - val argsList = baseSpecs[i].args - if (argsList != null) it.directParentArgs[parentClasses[i]] = argsList - } - // Register constructor fields in the class members - constructorArgsDeclaration?.params?.forEach { p -> - if (p.accessType != null) { - it.createField( - p.name, ObjNull, - isMutable = p.accessType == AccessType.Var, - visibility = p.visibility ?: Visibility.Public, - declaringClass = it, - // Constructor fields are not currently supporting override/closed in parser - // but we should pass Pos.builtIn to skip validation for now if needed, - // or p.pos to allow it. - pos = Pos.builtIn, - isTransient = p.isTransient, - type = ObjRecord.Type.ConstructorField, - fieldId = classInfo?.fieldIds?.get(p.name) - ) - } - } - } - - scope.addItem(declaredName, false, newClass) - // Prepare class scope for class-scope members (static) and future registrations - val classScope = scope.createChildScope(newThisObj = newClass) - // Set lexical class context for visibility tagging inside class body - classScope.currentClassCtx = newClass - newClass.classScope = classScope - // Execute class body once in class scope to register instance methods and prepare instance field initializers - bodyInit?.execute(classScope) - if (initScope.isNotEmpty()) { - for (s in initScope) - s.execute(classScope) - } - newClass.checkAbstractSatisfaction(nameToken.pos) - // Debug summary: list registered instance methods and class-scope functions for this class - return newClass - } - } - ClassDeclStatement(StatementDeclExecutable(classDeclStatement), startPos, qualifiedName) + val spec = ClassDeclSpec( + declaredName = declaredName, + className = className, + typeName = className, + startPos = startPos, + isExtern = isExtern, + isAbstract = isAbstract, + isObject = false, + isAnonymous = false, + baseSpecs = baseSpecs.map { ClassDeclBaseSpec(it.name, it.args) }, + constructorArgs = constructorArgsDeclaration, + constructorFieldIds = classInfo?.fieldIds, + bodyInit = bodyInit, + initScope = initScope + ) + ClassDeclStatement(spec) } } @@ -7224,7 +7140,7 @@ class Compiler( } } if (unwrapped is ClassDeclStatement) { - unwrapped.declaredName?.let { return resolveClassByName(it) } + return resolveClassByName(unwrapped.typeName) } val directRef = unwrapDirectRef(unwrapped) return when (directRef) { @@ -7233,7 +7149,7 @@ class Compiler( is RangeRef -> ObjRange.type is StatementRef -> { val decl = directRef.statement as? ClassDeclStatement - decl?.declaredName?.let { resolveClassByName(it) } + decl?.let { resolveClassByName(it.typeName) } } is ValueFnRef -> lambdaReturnTypeByRef[directRef] is CastRef -> resolveTypeRefClass(directRef.castTypeRef()) 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 c5a3345..76d2085 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -161,7 +161,7 @@ class BytecodeCompiler( ) } is net.sergeych.lyng.ClassDeclStatement -> { - val value = emitStatementCall(stmt) + val value = emitDeclClass(stmt) builder.emit(Opcode.RET, value.slot) val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount builder.build( @@ -4108,7 +4108,6 @@ class BytecodeCompiler( private fun emitDeclExec(stmt: Statement): CompiledValue { val executable = when (stmt) { - is net.sergeych.lyng.ClassDeclStatement -> stmt.executable else -> throw BytecodeCompileException( "Bytecode compile error: unsupported declaration ${stmt::class.simpleName}", stmt.pos @@ -4144,6 +4143,14 @@ class BytecodeCompiler( return CompiledValue(dst, SlotType.OBJ) } + private fun emitDeclClass(stmt: net.sergeych.lyng.ClassDeclStatement): CompiledValue { + val constId = builder.addConst(BytecodeConst.ClassDecl(stmt.spec)) + val dst = allocSlot() + builder.emit(Opcode.DECL_CLASS, constId, dst) + updateSlotType(dst, SlotType.OBJ) + return CompiledValue(dst, SlotType.OBJ) + } + private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? { val target = if (stmt is BytecodeStatement) stmt.original else stmt setPos(target.pos) @@ -4177,7 +4184,7 @@ class BytecodeCompiler( is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target) is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target) is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target) - is net.sergeych.lyng.ClassDeclStatement -> emitDeclExec(target) + is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target) is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target) is net.sergeych.lyng.EnumDeclStatement -> emitDeclEnum(target) is net.sergeych.lyng.TryStatement -> emitTry(target, true) @@ -4210,7 +4217,7 @@ class BytecodeCompiler( is VarDeclStatement -> emitVarDecl(target) is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target) is IfStatement -> compileIfStatement(target) - is net.sergeych.lyng.ClassDeclStatement -> emitDeclExec(target) + is net.sergeych.lyng.ClassDeclStatement -> emitDeclClass(target) is net.sergeych.lyng.FunctionDeclStatement -> emitDeclFunction(target) is net.sergeych.lyng.EnumDeclStatement -> emitDeclEnum(target) is net.sergeych.lyng.ForInStatement -> { 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 1c216c9..32f13d2 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeConst.kt @@ -47,6 +47,9 @@ sealed class BytecodeConst { data class FunctionDecl( val spec: net.sergeych.lyng.FunctionDeclSpec, ) : BytecodeConst() + data class ClassDecl( + val spec: net.sergeych.lyng.ClassDeclSpec, + ) : BytecodeConst() data class SlotPlan(val plan: Map, val captures: List = emptyList()) : BytecodeConst() data class CaptureTable(val entries: List) : BytecodeConst() data class ExtensionPropertyDecl( 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 0fe0391..b39c466 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdBuilder.kt @@ -157,7 +157,7 @@ class CmdBuilder { Opcode.PUSH_TRY -> listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP) Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE, - Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, + Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.ASSIGN_DESTRUCTURE -> listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, @@ -413,6 +413,7 @@ class CmdBuilder { Opcode.DECL_EXEC -> CmdDeclExec(operands[0], operands[1]) Opcode.DECL_ENUM -> CmdDeclEnum(operands[0], operands[1]) Opcode.DECL_FUNCTION -> CmdDeclFunction(operands[0], operands[1]) + Opcode.DECL_CLASS -> CmdDeclClass(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.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(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 bd82f0b..b35328a 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdDisassembler.kt @@ -214,6 +214,7 @@ object CmdDisassembler { is CmdDeclExec -> Opcode.DECL_EXEC to intArrayOf(cmd.constId, cmd.slot) is CmdDeclEnum -> Opcode.DECL_ENUM to intArrayOf(cmd.constId, cmd.slot) is CmdDeclFunction -> Opcode.DECL_FUNCTION to intArrayOf(cmd.constId, cmd.slot) + is CmdDeclClass -> Opcode.DECL_CLASS 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 CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot) @@ -285,7 +286,7 @@ object CmdDisassembler { Opcode.PUSH_TRY -> listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP) Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE, - Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, + Opcode.DECL_EXEC, Opcode.DECL_ENUM, Opcode.DECL_FUNCTION, Opcode.DECL_CLASS, Opcode.ASSIGN_DESTRUCTURE -> listOf(OperandKind.CONST, OperandKind.SLOT) Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT, 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 1aec0f5..cb10310 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/CmdRuntime.kt @@ -1354,6 +1354,16 @@ class CmdDeclFunction(internal val constId: Int, internal val slot: Int) : Cmd() } } +class CmdDeclClass(internal val constId: Int, internal val slot: Int) : Cmd() { + override suspend fun perform(frame: CmdFrame) { + val decl = frame.fn.constants[constId] as? BytecodeConst.ClassDecl + ?: error("DECL_CLASS expects ClassDecl at $constId") + val result = executeClassDecl(frame.ensureScope(), decl.spec) + frame.storeObjResult(slot, result) + return + } +} + class CmdDeclDestructure(internal val constId: Int, internal val slot: Int) : Cmd() { override suspend fun perform(frame: CmdFrame) { val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureDecl 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 371321f..25e8434 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/Opcode.kt @@ -167,6 +167,7 @@ enum class Opcode(val code: Int) { DELEGATED_SET_LOCAL(0xC3), BIND_DELEGATE_LOCAL(0xC4), DECL_FUNCTION(0xC5), + DECL_CLASS(0xC6), ; companion object { diff --git a/lynglib/src/commonTest/kotlin/BytecodeRecentOpsTest.kt b/lynglib/src/commonTest/kotlin/BytecodeRecentOpsTest.kt index b3e3a18..612c888 100644 --- a/lynglib/src/commonTest/kotlin/BytecodeRecentOpsTest.kt +++ b/lynglib/src/commonTest/kotlin/BytecodeRecentOpsTest.kt @@ -260,7 +260,9 @@ class BytecodeRecentOpsTest { assertNotNull(moduleFn, "module bytecode missing") val disasm = CmdDisassembler.disassemble(moduleFn) assertTrue(!disasm.contains("CALL_SLOT"), disasm) - assertTrue(disasm.contains("DECL_EXEC"), disasm) + assertTrue(disasm.contains("DECL_CLASS"), disasm) + assertTrue(disasm.contains("DECL_FUNCTION"), disasm) + assertTrue(disasm.contains("DECL_ENUM"), disasm) } @Test