Fix do-while scoping and module pseudo-symbol
This commit is contained in:
parent
3210205061
commit
d6e1e74b48
@ -223,6 +223,11 @@ class Compiler(
|
||||
}
|
||||
|
||||
private fun resolveIdentifierRef(name: String, pos: Pos): ObjRef {
|
||||
if (name == "__PACKAGE__") {
|
||||
resolutionSink?.reference(name, pos)
|
||||
val value = ObjString(packageName ?: "unknown").asReadonly
|
||||
return ConstRef(value)
|
||||
}
|
||||
if (name == "this") {
|
||||
resolutionSink?.reference(name, pos)
|
||||
return LocalVarRef(name, pos)
|
||||
@ -3176,10 +3181,13 @@ class Compiler(
|
||||
val label = getLabel()?.also { cc.labels += it }
|
||||
val loopSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||
slotPlanStack.add(loopSlotPlan)
|
||||
var conditionSlotPlan: SlotPlan = loopSlotPlan
|
||||
val (canBreak, parsedBody) = try {
|
||||
cc.parseLoop {
|
||||
if (cc.current().type == Token.Type.LBRACE) {
|
||||
parseLoopBlock()
|
||||
val (blockStmt, blockPlan) = parseLoopBlockWithPlan()
|
||||
conditionSlotPlan = blockPlan
|
||||
blockStmt
|
||||
} else {
|
||||
parseStatement() ?: throw ScriptError(cc.currentPos(), "Bad do-while statement: expected body statement")
|
||||
}
|
||||
@ -3196,7 +3204,7 @@ class Compiler(
|
||||
throw ScriptError(tWhile.pos, "Expected 'while' after do body")
|
||||
|
||||
ensureLparen()
|
||||
slotPlanStack.add(loopSlotPlan)
|
||||
slotPlanStack.add(conditionSlotPlan)
|
||||
val condition = try {
|
||||
parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected condition after 'while'")
|
||||
} finally {
|
||||
@ -3212,7 +3220,7 @@ class Compiler(
|
||||
cc.previous()
|
||||
null
|
||||
}
|
||||
val loopPlanSnapshot = slotPlanIndices(loopSlotPlan)
|
||||
val loopPlanSnapshot = slotPlanIndices(conditionSlotPlan)
|
||||
return DoWhileStatement(body, condition, elseStatement, label, loopPlanSnapshot, body.pos)
|
||||
}
|
||||
|
||||
@ -3819,6 +3827,35 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun parseLoopBlockWithPlan(): Pair<Statement, SlotPlan> {
|
||||
val startPos = cc.currentPos()
|
||||
val t = cc.next()
|
||||
if (t.type != Token.Type.LBRACE)
|
||||
throw ScriptError(t.pos, "Expected block body start: {")
|
||||
resolutionSink?.enterScope(ScopeKind.BLOCK, startPos, null)
|
||||
val blockSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||
slotPlanStack.add(blockSlotPlan)
|
||||
val capturePlan = CapturePlan(blockSlotPlan)
|
||||
capturePlanStack.add(capturePlan)
|
||||
val block = try {
|
||||
parseScript()
|
||||
} finally {
|
||||
capturePlanStack.removeLast()
|
||||
slotPlanStack.removeLast()
|
||||
}
|
||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
||||
val wrapped = wrapBytecode(stmt)
|
||||
val t1 = cc.next()
|
||||
if (t1.type != Token.Type.RBRACE)
|
||||
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
||||
val range = MiniRange(startPos, t1.pos)
|
||||
lastParsedBlockRange = range
|
||||
miniSink?.onBlock(MiniBlock(range))
|
||||
resolutionSink?.exitScope(t1.pos)
|
||||
return wrapped to blockSlotPlan
|
||||
}
|
||||
|
||||
private suspend fun parseVarDeclaration(
|
||||
isMutable: Boolean,
|
||||
visibility: Visibility,
|
||||
|
||||
@ -347,6 +347,13 @@ open class Scope(
|
||||
|
||||
open operator fun get(name: String): ObjRecord? {
|
||||
if (name == "this") return thisObj.asReadonly
|
||||
if (name == "__PACKAGE__") {
|
||||
var s: Scope? = this
|
||||
while (s != null) {
|
||||
if (s is ModuleScope) return s.packageNameObj
|
||||
s = s.parent
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Prefer direct locals/bindings declared in this frame
|
||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
||||
|
||||
@ -175,6 +175,9 @@ class BytecodeCompiler(
|
||||
return when (ref) {
|
||||
is ConstRef -> compileConst(ref.constValue)
|
||||
is LocalSlotRef -> {
|
||||
if (ref.name == "__PACKAGE__") {
|
||||
return compileNameLookup(ref.name)
|
||||
}
|
||||
if (!allowLocalSlots) return null
|
||||
if (ref.isDelegated) return null
|
||||
if (ref.name.isEmpty()) return null
|
||||
@ -201,6 +204,9 @@ class BytecodeCompiler(
|
||||
CompiledValue(mapped, resolved)
|
||||
}
|
||||
is LocalVarRef -> {
|
||||
if (ref.name == "__PACKAGE__") {
|
||||
return compileNameLookup(ref.name)
|
||||
}
|
||||
if (allowLocalSlots) {
|
||||
if (!forceScopeSlots) {
|
||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||
@ -2775,18 +2781,34 @@ class BytecodeCompiler(
|
||||
val loopLabel = builder.label()
|
||||
val continueLabel = builder.label()
|
||||
val endLabel = builder.label()
|
||||
val useLoopScope = stmt.loopSlotPlan.isNotEmpty()
|
||||
val breakLabel = if (useLoopScope) builder.label() else endLabel
|
||||
val planId = if (useLoopScope) {
|
||||
builder.addConst(BytecodeConst.SlotPlan(stmt.loopSlotPlan, emptyList()))
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
builder.mark(loopLabel)
|
||||
if (useLoopScope) {
|
||||
builder.emit(Opcode.PUSH_SCOPE, planId)
|
||||
resetAddrCache()
|
||||
}
|
||||
loopStack.addLast(
|
||||
LoopContext(
|
||||
stmt.label,
|
||||
endLabel,
|
||||
breakLabel,
|
||||
continueLabel,
|
||||
breakFlagSlot,
|
||||
if (wantResult) resultSlot else null,
|
||||
hasIterator = false
|
||||
)
|
||||
)
|
||||
val bodyValue = compileStatementValueOrFallback(stmt.body, wantResult) ?: return null
|
||||
val bodyTarget = if (stmt.body is BytecodeStatement) stmt.body.original else stmt.body
|
||||
val bodyValue = if (useLoopScope && bodyTarget is BlockStatement) {
|
||||
emitInlineBlock(bodyTarget, wantResult)
|
||||
} else {
|
||||
compileStatementValueOrFallback(stmt.body, wantResult)
|
||||
} ?: return null
|
||||
loopStack.removeLast()
|
||||
if (wantResult) {
|
||||
val bodyObj = ensureObjSlot(bodyValue)
|
||||
@ -2795,10 +2817,20 @@ class BytecodeCompiler(
|
||||
builder.mark(continueLabel)
|
||||
val condition = compileCondition(stmt.condition, stmt.pos) ?: return null
|
||||
if (condition.type != SlotType.BOOL) return null
|
||||
if (useLoopScope) {
|
||||
builder.emit(Opcode.POP_SCOPE)
|
||||
resetAddrCache()
|
||||
}
|
||||
builder.emit(
|
||||
Opcode.JMP_IF_TRUE,
|
||||
listOf(CmdBuilder.Operand.IntVal(condition.slot), CmdBuilder.Operand.LabelRef(loopLabel))
|
||||
)
|
||||
if (useLoopScope) {
|
||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||
builder.mark(breakLabel)
|
||||
builder.emit(Opcode.POP_SCOPE)
|
||||
resetAddrCache()
|
||||
}
|
||||
|
||||
builder.mark(endLabel)
|
||||
if (stmt.elseStatement != null) {
|
||||
|
||||
@ -2008,7 +2008,6 @@ class ScriptTest {
|
||||
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testMethodCallLastBlockWithEllipsis() = runTest {
|
||||
eval(
|
||||
@ -2027,7 +2026,6 @@ class ScriptTest {
|
||||
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun nationalCharsTest() = runTest {
|
||||
eval(
|
||||
@ -2051,7 +2049,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun doWhileSimpleTest() = runTest {
|
||||
eval(
|
||||
@ -2066,7 +2063,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testFailDoWhileSample1() = runTest {
|
||||
eval(
|
||||
@ -2081,7 +2077,6 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("incremental enable")
|
||||
@Test
|
||||
fun testForContinue() = runTest {
|
||||
eval(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user