diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CodeContext.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CodeContext.kt index 1d58c0b..f8fc618 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CodeContext.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/CodeContext.kt @@ -23,5 +23,6 @@ sealed class CodeContext { class ClassBody(val name: String, val isExtern: Boolean = false): CodeContext() { val pendingInitializations = mutableMapOf() val declaredMembers = mutableSetOf() + var slotPlanId: Int? = null } } diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt index e36794a..039bf97 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/Compiler.kt @@ -295,6 +295,14 @@ class Compiler( } val slotLoc = lookupSlotLocation(name, includeModule = false) if (slotLoc != null) { + val classCtx = codeContexts.lastOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody + if (slotLoc.depth > 0 && + classCtx?.slotPlanId == slotLoc.scopeId && + classCtx.declaredMembers.contains(name) + ) { + resolutionSink?.referenceMember(name, pos) + return ImplicitThisMemberRef(name, pos) + } captureLocalRef(name, slotLoc, pos)?.let { ref -> resolutionSink?.reference(name, pos) return ref @@ -868,6 +876,20 @@ class Compiler( is ContinueStatement -> false is ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr) + is WhenStatement -> { + containsUnsupportedForBytecode(target.value) || + target.cases.any { case -> + case.conditions.any { cond -> + when (cond) { + is WhenEqualsCondition -> containsUnsupportedForBytecode(cond.expr) + is WhenInCondition -> containsUnsupportedForBytecode(cond.expr) + is WhenIsCondition -> false + else -> true + } + } || containsUnsupportedForBytecode(case.block) + } || + (target.elseCase?.let { containsUnsupportedForBytecode(it) } ?: false) + } else -> true } } @@ -2962,6 +2984,18 @@ class Compiler( "Bad class declaration: expected ')' at the end of the primary constructor" ) + val classSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++) + classCtx?.slotPlanId = classSlotPlan.id + constructorArgsDeclaration?.params?.forEach { param -> + val mutable = param.accessType?.isMutable ?: false + declareSlotNameIn(classSlotPlan, param.name, mutable, isDelegated = false) + } + constructorArgsDeclaration?.params?.forEach { param -> + if (param.accessType != null) { + classCtx?.declaredMembers?.add(param.name) + } + } + // Optional base list: ":" Base ("," Base)* where Base := ID ( "(" args? ")" )? data class BaseSpec(val name: String, val args: List?) @@ -2983,12 +3017,6 @@ class Compiler( cc.skipTokenOfType(Token.Type.NEWLINE, isOptional = true) pushInitScope() - constructorArgsDeclaration?.params?.forEach { param -> - if (param.accessType != null) { - classCtx?.declaredMembers?.add(param.name) - } - } - // Robust body detection: peek next non-whitespace token; if it's '{', consume and parse the body var classBodyRange: MiniRange? = null val bodyInit: Statement? = run { @@ -3026,12 +3054,7 @@ class Compiler( } // parse body val bodyStart = next.pos - val classSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++) slotPlanStack.add(classSlotPlan) - constructorArgsDeclaration?.params?.forEach { param -> - val mutable = param.accessType?.isMutable ?: false - declareSlotNameIn(classSlotPlan, param.name, mutable, isDelegated = false) - } resolutionSink?.declareClass(nameToken.value, baseSpecs.map { it.name }, startPos) resolutionSink?.enterScope(ScopeKind.CLASS, startPos, nameToken.value, baseSpecs.map { it.name }) constructorArgsDeclaration?.params?.forEach { param -> @@ -3583,7 +3606,8 @@ class Compiler( } miniSink?.onEnterFunction(node) - return inCodeContext(CodeContext.Function(name, implicitThisMembers = extTypeName != null)) { + val implicitThisMembers = extTypeName != null || (parentContext is CodeContext.ClassBody && !isStatic) + return inCodeContext(CodeContext.Function(name, implicitThisMembers = implicitThisMembers)) { cc.labels.add(name) outerLabel?.let { cc.labels.add(it) } @@ -3629,12 +3653,10 @@ class Compiler( cc.nextNonWhitespace() // consume '=' if (cc.peekNextNonWhitespace().value == "return") throw ScriptError(cc.currentPos(), "return is not allowed in shorthand function") - val expr = parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected function body expression") - // Shorthand function returns the expression value - object : Statement() { - override val pos: Pos = expr.pos - override suspend fun execute(scope: Scope): Obj = expr.execute(scope) - } + val exprStmt = parseExpression() + ?: throw ScriptError(cc.currentPos(), "Expected function body expression") + // Shorthand function returns the expression value. + exprStmt } else { parseBlock() } 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 4102d53..3cd87fc 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -26,6 +26,7 @@ import net.sergeych.lyng.Pos import net.sergeych.lyng.Statement import net.sergeych.lyng.ToBoolStatement import net.sergeych.lyng.VarDeclStatement +import net.sergeych.lyng.Visibility import net.sergeych.lyng.WhenCondition import net.sergeych.lyng.WhenEqualsCondition import net.sergeych.lyng.WhenInCondition @@ -1467,6 +1468,13 @@ class BytecodeCompiler( builder.emit(realOp, left, rhs.slot, out) CompiledValue(out, SlotType.REAL) } + SlotType.OBJ -> { + if (objOp == null) return null + val leftObj = allocSlot() + builder.emit(Opcode.BOX_OBJ, out, leftObj) + builder.emit(objOp, leftObj, rhs.slot, out) + CompiledValue(out, SlotType.OBJ) + } else -> null } } @@ -1483,6 +1491,13 @@ class BytecodeCompiler( builder.emit(realOp, out, right, out) CompiledValue(out, SlotType.REAL) } + SlotType.OBJ -> { + if (objOp == null) return null + val leftObj = allocSlot() + builder.emit(Opcode.BOX_OBJ, out, leftObj) + builder.emit(objOp, leftObj, rhs.slot, out) + CompiledValue(out, SlotType.OBJ) + } else -> null } } @@ -2578,6 +2593,18 @@ class BytecodeCompiler( usedOverride = true slot } + val loopDeclId = if (usedOverride) { + builder.addConst( + BytecodeConst.LocalDecl( + stmt.loopVarName, + true, + Visibility.Public, + isTransient = false + ) + ) + } else { + -1 + } try { if (range == null && rangeRef == null && typedRangeLocal == null) { @@ -2623,6 +2650,9 @@ class BytecodeCompiler( builder.emit(Opcode.MOVE_OBJ, nextObj.slot, loopSlotId) updateSlotType(loopSlotId, SlotType.OBJ) updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ) + if (usedOverride) { + builder.emit(Opcode.DECL_LOCAL, loopDeclId, loopSlotId) + } loopStack.addLast( LoopContext( @@ -2719,6 +2749,9 @@ class BytecodeCompiler( builder.emit(Opcode.MOVE_INT, iSlot, loopSlotId) updateSlotType(loopSlotId, SlotType.INT) updateSlotTypeByName(stmt.loopVarName, SlotType.INT) + if (usedOverride) { + builder.emit(Opcode.DECL_LOCAL, loopDeclId, loopSlotId) + } loopStack.addLast( LoopContext( stmt.label, @@ -2785,6 +2818,9 @@ class BytecodeCompiler( builder.emit(Opcode.MOVE_INT, iSlot, loopSlotId) updateSlotType(loopSlotId, SlotType.INT) updateSlotTypeByName(stmt.loopVarName, SlotType.INT) + if (usedOverride) { + builder.emit(Opcode.DECL_LOCAL, loopDeclId, loopSlotId) + } loopStack.addLast( LoopContext( stmt.label, 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 5aa7653..20e5565 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt @@ -2288,6 +2288,13 @@ class ImplicitThisMemberRef( val caller = scope.currentClassCtx val th = scope.thisObj + if (th is ObjClass) { + return th.readField(scope, name) + } + if (th != null && th !is ObjInstance) { + return th.readField(scope, name) + } + // member slots on this instance if (th is ObjInstance) { // private member access for current class context @@ -2333,6 +2340,15 @@ class ImplicitThisMemberRef( val caller = scope.currentClassCtx val th = scope.thisObj + if (th is ObjClass) { + th.writeField(scope, name, newValue) + return + } + if (th != null && th !is ObjInstance) { + th.writeField(scope, name, newValue) + return + } + // member slots on this instance if (th is ObjInstance) { val key = th.objClass.publicMemberResolution[name] ?: name diff --git a/lynglib/src/commonTest/kotlin/ScriptTest.kt b/lynglib/src/commonTest/kotlin/ScriptTest.kt index 6873cf3..cb977a0 100644 --- a/lynglib/src/commonTest/kotlin/ScriptTest.kt +++ b/lynglib/src/commonTest/kotlin/ScriptTest.kt @@ -3725,7 +3725,6 @@ class ScriptTest { ) } - @Ignore("incremental enable") @Test fun testExceptionSerializationPlain() = runTest { eval( @@ -4704,7 +4703,6 @@ class ScriptTest { ) } - @Ignore("incremental enable: expression-body methods not resolved yet") @Test fun testFunMiniDeclaration() = runTest { eval( @@ -4793,7 +4791,6 @@ class ScriptTest { ) } - @Ignore("incremental enable: ctor params in superclass call not resolved yet") @Test fun testExceptionToString() = runTest { eval( @@ -4926,7 +4923,6 @@ class ScriptTest { ) } - @Ignore("incremental enable: capture of static var inside run block not resolved") @Test fun realWorldCaptureProblem() = runTest { eval( @@ -5120,7 +5116,6 @@ class ScriptTest { ) } - @Ignore("incremental enable: for-in over String in disasm sample not yet supported") @Test fun testForInIterableDisasm() = runTest { val scope = Script.newScope() @@ -5148,7 +5143,6 @@ class ScriptTest { println("[DEBUG_LOG] type(\"153\")=${r2.inspect(scope)}") } - @Ignore("incremental enable: for-in bytecode over iterable returns 0") @Test fun testForInIterableBytecode() = runTest { val result = eval(