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 {
|
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") {
|
if (name == "this") {
|
||||||
resolutionSink?.reference(name, pos)
|
resolutionSink?.reference(name, pos)
|
||||||
return LocalVarRef(name, pos)
|
return LocalVarRef(name, pos)
|
||||||
@ -3176,10 +3181,13 @@ class Compiler(
|
|||||||
val label = getLabel()?.also { cc.labels += it }
|
val label = getLabel()?.also { cc.labels += it }
|
||||||
val loopSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
val loopSlotPlan = SlotPlan(mutableMapOf(), 0, nextScopeId++)
|
||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(loopSlotPlan)
|
||||||
|
var conditionSlotPlan: SlotPlan = loopSlotPlan
|
||||||
val (canBreak, parsedBody) = try {
|
val (canBreak, parsedBody) = try {
|
||||||
cc.parseLoop {
|
cc.parseLoop {
|
||||||
if (cc.current().type == Token.Type.LBRACE) {
|
if (cc.current().type == Token.Type.LBRACE) {
|
||||||
parseLoopBlock()
|
val (blockStmt, blockPlan) = parseLoopBlockWithPlan()
|
||||||
|
conditionSlotPlan = blockPlan
|
||||||
|
blockStmt
|
||||||
} else {
|
} else {
|
||||||
parseStatement() ?: throw ScriptError(cc.currentPos(), "Bad do-while statement: expected body statement")
|
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")
|
throw ScriptError(tWhile.pos, "Expected 'while' after do body")
|
||||||
|
|
||||||
ensureLparen()
|
ensureLparen()
|
||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(conditionSlotPlan)
|
||||||
val condition = try {
|
val condition = try {
|
||||||
parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected condition after 'while'")
|
parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected condition after 'while'")
|
||||||
} finally {
|
} finally {
|
||||||
@ -3212,7 +3220,7 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val loopPlanSnapshot = slotPlanIndices(loopSlotPlan)
|
val loopPlanSnapshot = slotPlanIndices(conditionSlotPlan)
|
||||||
return DoWhileStatement(body, condition, elseStatement, label, loopPlanSnapshot, body.pos)
|
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(
|
private suspend fun parseVarDeclaration(
|
||||||
isMutable: Boolean,
|
isMutable: Boolean,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
|
|||||||
@ -347,6 +347,13 @@ open class Scope(
|
|||||||
|
|
||||||
open operator fun get(name: String): ObjRecord? {
|
open operator fun get(name: String): ObjRecord? {
|
||||||
if (name == "this") return thisObj.asReadonly
|
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
|
// 1. Prefer direct locals/bindings declared in this frame
|
||||||
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
tryGetLocalRecord(this, name, currentClassCtx)?.let { return it }
|
||||||
|
|||||||
@ -175,6 +175,9 @@ class BytecodeCompiler(
|
|||||||
return when (ref) {
|
return when (ref) {
|
||||||
is ConstRef -> compileConst(ref.constValue)
|
is ConstRef -> compileConst(ref.constValue)
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
|
if (ref.name == "__PACKAGE__") {
|
||||||
|
return compileNameLookup(ref.name)
|
||||||
|
}
|
||||||
if (!allowLocalSlots) return null
|
if (!allowLocalSlots) return null
|
||||||
if (ref.isDelegated) return null
|
if (ref.isDelegated) return null
|
||||||
if (ref.name.isEmpty()) return null
|
if (ref.name.isEmpty()) return null
|
||||||
@ -201,6 +204,9 @@ class BytecodeCompiler(
|
|||||||
CompiledValue(mapped, resolved)
|
CompiledValue(mapped, resolved)
|
||||||
}
|
}
|
||||||
is LocalVarRef -> {
|
is LocalVarRef -> {
|
||||||
|
if (ref.name == "__PACKAGE__") {
|
||||||
|
return compileNameLookup(ref.name)
|
||||||
|
}
|
||||||
if (allowLocalSlots) {
|
if (allowLocalSlots) {
|
||||||
if (!forceScopeSlots) {
|
if (!forceScopeSlots) {
|
||||||
scopeSlotIndexByName[ref.name]?.let { slot ->
|
scopeSlotIndexByName[ref.name]?.let { slot ->
|
||||||
@ -2775,18 +2781,34 @@ class BytecodeCompiler(
|
|||||||
val loopLabel = builder.label()
|
val loopLabel = builder.label()
|
||||||
val continueLabel = builder.label()
|
val continueLabel = builder.label()
|
||||||
val endLabel = 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)
|
builder.mark(loopLabel)
|
||||||
|
if (useLoopScope) {
|
||||||
|
builder.emit(Opcode.PUSH_SCOPE, planId)
|
||||||
|
resetAddrCache()
|
||||||
|
}
|
||||||
loopStack.addLast(
|
loopStack.addLast(
|
||||||
LoopContext(
|
LoopContext(
|
||||||
stmt.label,
|
stmt.label,
|
||||||
endLabel,
|
breakLabel,
|
||||||
continueLabel,
|
continueLabel,
|
||||||
breakFlagSlot,
|
breakFlagSlot,
|
||||||
if (wantResult) resultSlot else null,
|
if (wantResult) resultSlot else null,
|
||||||
hasIterator = false
|
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()
|
loopStack.removeLast()
|
||||||
if (wantResult) {
|
if (wantResult) {
|
||||||
val bodyObj = ensureObjSlot(bodyValue)
|
val bodyObj = ensureObjSlot(bodyValue)
|
||||||
@ -2795,10 +2817,20 @@ class BytecodeCompiler(
|
|||||||
builder.mark(continueLabel)
|
builder.mark(continueLabel)
|
||||||
val condition = compileCondition(stmt.condition, stmt.pos) ?: return null
|
val condition = compileCondition(stmt.condition, stmt.pos) ?: return null
|
||||||
if (condition.type != SlotType.BOOL) return null
|
if (condition.type != SlotType.BOOL) return null
|
||||||
|
if (useLoopScope) {
|
||||||
|
builder.emit(Opcode.POP_SCOPE)
|
||||||
|
resetAddrCache()
|
||||||
|
}
|
||||||
builder.emit(
|
builder.emit(
|
||||||
Opcode.JMP_IF_TRUE,
|
Opcode.JMP_IF_TRUE,
|
||||||
listOf(CmdBuilder.Operand.IntVal(condition.slot), CmdBuilder.Operand.LabelRef(loopLabel))
|
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)
|
builder.mark(endLabel)
|
||||||
if (stmt.elseStatement != null) {
|
if (stmt.elseStatement != null) {
|
||||||
|
|||||||
@ -2008,7 +2008,6 @@ class ScriptTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testMethodCallLastBlockWithEllipsis() = runTest {
|
fun testMethodCallLastBlockWithEllipsis() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2027,7 +2026,6 @@ class ScriptTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun nationalCharsTest() = runTest {
|
fun nationalCharsTest() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2051,7 +2049,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun doWhileSimpleTest() = runTest {
|
fun doWhileSimpleTest() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2066,7 +2063,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testFailDoWhileSample1() = runTest {
|
fun testFailDoWhileSample1() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2081,7 +2077,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testForContinue() = runTest {
|
fun testForContinue() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user