Fix bytecode loop locals and class member resolution
This commit is contained in:
parent
2e9e0921bf
commit
ffb22d0875
@ -23,5 +23,6 @@ sealed class CodeContext {
|
||||
class ClassBody(val name: String, val isExtern: Boolean = false): CodeContext() {
|
||||
val pendingInitializations = mutableMapOf<String, Pos>()
|
||||
val declaredMembers = mutableSetOf<String>()
|
||||
var slotPlanId: Int? = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<ParsedArgument>?)
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user