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