Fix apply captures, class forward refs, and when bytecode
This commit is contained in:
parent
4b66454bf3
commit
615dc026f7
@ -30,9 +30,15 @@ class BlockStatement(
|
|||||||
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
val target = if (scope.skipScopeCreation) scope else scope.createChildScope(startPos)
|
||||||
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
if (slotPlan.isNotEmpty()) target.applySlotPlan(slotPlan)
|
||||||
if (captureSlots.isNotEmpty()) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
|
val applyScope = scope as? ApplyScope
|
||||||
for (capture in captureSlots) {
|
for (capture in captureSlots) {
|
||||||
val rec = scope.resolveCaptureRecord(capture.name)
|
val rec = if (applyScope != null) {
|
||||||
?: scope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
applyScope.resolveCaptureRecord(capture.name)
|
||||||
|
?: applyScope.callScope.resolveCaptureRecord(capture.name)
|
||||||
|
} else {
|
||||||
|
scope.resolveCaptureRecord(capture.name)
|
||||||
|
} ?: (applyScope?.callScope ?: scope)
|
||||||
|
.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
target.updateSlotFor(capture.name, rec)
|
target.updateSlotFor(capture.name, rec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,7 +71,8 @@ class ClosureScope(val callScope: Scope, val closureScope: Scope) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplyScope(callScope: Scope, val applied: Scope) : Scope(callScope.parent?.parent ?: callScope.parent ?: callScope, thisObj = applied.thisObj) {
|
class ApplyScope(val callScope: Scope, val applied: Scope) :
|
||||||
|
Scope(callScope.parent?.parent ?: callScope.parent ?: callScope, thisObj = applied.thisObj) {
|
||||||
|
|
||||||
override fun get(name: String): ObjRecord? {
|
override fun get(name: String): ObjRecord? {
|
||||||
return applied.get(name) ?: super.get(name)
|
return applied.get(name) ?: super.get(name)
|
||||||
|
|||||||
@ -179,6 +179,63 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun predeclareClassMembers(target: MutableSet<String>) {
|
||||||
|
val saved = cc.savePos()
|
||||||
|
var depth = 0
|
||||||
|
val modifiers = setOf(
|
||||||
|
"public", "private", "protected", "internal",
|
||||||
|
"override", "abstract", "extern", "static", "transient"
|
||||||
|
)
|
||||||
|
fun nextNonWs(): Token {
|
||||||
|
var t = cc.next()
|
||||||
|
while (t.type == Token.Type.NEWLINE || t.type == Token.Type.SINGLE_LINE_COMMENT || t.type == Token.Type.MULTILINE_COMMENT) {
|
||||||
|
t = cc.next()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
while (cc.hasNext()) {
|
||||||
|
var t = cc.next()
|
||||||
|
when (t.type) {
|
||||||
|
Token.Type.LBRACE -> depth++
|
||||||
|
Token.Type.RBRACE -> if (depth == 0) break else depth--
|
||||||
|
Token.Type.ID -> if (depth == 0) {
|
||||||
|
while (t.type == Token.Type.ID && t.value in modifiers) {
|
||||||
|
t = nextNonWs()
|
||||||
|
}
|
||||||
|
when (t.value) {
|
||||||
|
"fun", "fn", "val", "var" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
val afterName = cc.peekNextNonWhitespace()
|
||||||
|
if (afterName.type != Token.Type.DOT) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"class", "object" -> {
|
||||||
|
val nameToken = nextNonWs()
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"enum" -> {
|
||||||
|
val next = nextNonWs()
|
||||||
|
val nameToken = if (next.type == Token.Type.ID && next.value == "class") nextNonWs() else next
|
||||||
|
if (nameToken.type == Token.Type.ID) {
|
||||||
|
target.add(nameToken.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
cc.restorePos(saved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildParamSlotPlan(names: List<String>): SlotPlan {
|
private fun buildParamSlotPlan(names: List<String>): SlotPlan {
|
||||||
val map = mutableMapOf<String, Int>()
|
val map = mutableMapOf<String, Int>()
|
||||||
var idx = 0
|
var idx = 0
|
||||||
@ -228,6 +285,10 @@ class Compiler(
|
|||||||
val value = ObjString(packageName ?: "unknown").asReadonly
|
val value = ObjString(packageName ?: "unknown").asReadonly
|
||||||
return ConstRef(value)
|
return ConstRef(value)
|
||||||
}
|
}
|
||||||
|
if (name == "$~") {
|
||||||
|
resolutionSink?.reference(name, pos)
|
||||||
|
return LocalVarRef(name, pos)
|
||||||
|
}
|
||||||
if (name == "this") {
|
if (name == "this") {
|
||||||
resolutionSink?.reference(name, pos)
|
resolutionSink?.reference(name, pos)
|
||||||
return LocalVarRef(name, pos)
|
return LocalVarRef(name, pos)
|
||||||
@ -1415,8 +1476,20 @@ class Compiler(
|
|||||||
// and the source closure of the lambda which might have other thisObj.
|
// and the source closure of the lambda which might have other thisObj.
|
||||||
val context = scope.applyClosure(closureScope)
|
val context = scope.applyClosure(closureScope)
|
||||||
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
if (paramSlotPlanSnapshot.isNotEmpty()) context.applySlotPlan(paramSlotPlanSnapshot)
|
||||||
if (captureSlots.isNotEmpty() && context !is ApplyScope) {
|
if (captureSlots.isNotEmpty()) {
|
||||||
|
val moduleScope = if (context is ApplyScope) {
|
||||||
|
var s: Scope? = closureScope
|
||||||
|
while (s != null && s !is ModuleScope) {
|
||||||
|
s = s.parent
|
||||||
|
}
|
||||||
|
s as? ModuleScope
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
for (capture in captureSlots) {
|
for (capture in captureSlots) {
|
||||||
|
if (moduleScope != null && moduleScope.getLocalRecordDirect(capture.name) != null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
val rec = closureScope.resolveCaptureRecord(capture.name)
|
val rec = closureScope.resolveCaptureRecord(capture.name)
|
||||||
?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
?: closureScope.raiseSymbolNotFound("symbol ${capture.name} not found")
|
||||||
context.updateSlotFor(capture.name, rec)
|
context.updateSlotFor(capture.name, rec)
|
||||||
@ -2530,6 +2603,11 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
return BlockStatement(stmt.block, newPlan, stmt.captureSlots, stmt.pos)
|
return BlockStatement(stmt.block, newPlan, stmt.captureSlots, stmt.pos)
|
||||||
}
|
}
|
||||||
|
fun stripCatchCaptures(block: Statement): Statement {
|
||||||
|
val stmt = block as? BlockStatement ?: return block
|
||||||
|
if (stmt.captureSlots.isEmpty()) return stmt
|
||||||
|
return BlockStatement(stmt.block, stmt.slotPlan, emptyList(), stmt.pos)
|
||||||
|
}
|
||||||
|
|
||||||
val body = unwrapBytecodeDeep(parseBlock())
|
val body = unwrapBytecodeDeep(parseBlock())
|
||||||
val catches = mutableListOf<CatchBlockData>()
|
val catches = mutableListOf<CatchBlockData>()
|
||||||
@ -2572,7 +2650,12 @@ class Compiler(
|
|||||||
val block = try {
|
val block = try {
|
||||||
resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null)
|
resolutionSink?.enterScope(ScopeKind.BLOCK, catchVar.pos, null)
|
||||||
resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos)
|
resolutionSink?.declareSymbol(catchVar.value, SymbolKind.LOCAL, isMutable = false, pos = catchVar.pos)
|
||||||
withCatchSlot(unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))), catchVar.value)
|
stripCatchCaptures(
|
||||||
|
withCatchSlot(
|
||||||
|
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(catchVar.value to false))),
|
||||||
|
catchVar.value
|
||||||
|
)
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
}
|
}
|
||||||
@ -2586,10 +2669,12 @@ class Compiler(
|
|||||||
val block = try {
|
val block = try {
|
||||||
resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null)
|
resolutionSink?.enterScope(ScopeKind.BLOCK, itToken.pos, null)
|
||||||
resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos)
|
resolutionSink?.declareSymbol(itToken.value, SymbolKind.LOCAL, isMutable = false, pos = itToken.pos)
|
||||||
|
stripCatchCaptures(
|
||||||
withCatchSlot(
|
withCatchSlot(
|
||||||
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)),
|
unwrapBytecodeDeep(parseBlockWithPredeclared(listOf(itToken.value to false), skipLeadingBrace = true)),
|
||||||
itToken.value
|
itToken.value
|
||||||
)
|
)
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
}
|
}
|
||||||
@ -2860,6 +2945,7 @@ class Compiler(
|
|||||||
pendingDeclStart = null
|
pendingDeclStart = null
|
||||||
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
resolutionSink?.declareSymbol(nameToken.value, SymbolKind.CLASS, isMutable = false, pos = nameToken.pos)
|
||||||
return inCodeContext(CodeContext.ClassBody(nameToken.value, isExtern = isExtern)) {
|
return inCodeContext(CodeContext.ClassBody(nameToken.value, isExtern = isExtern)) {
|
||||||
|
val classCtx = codeContexts.lastOrNull() as? CodeContext.ClassBody
|
||||||
val constructorArgsDeclaration =
|
val constructorArgsDeclaration =
|
||||||
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
if (cc.skipTokenOfType(Token.Type.LPAREN, isOptional = true))
|
||||||
parseArgsDeclaration(isClassDeclaration = true)
|
parseArgsDeclaration(isClassDeclaration = true)
|
||||||
@ -2892,6 +2978,11 @@ 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
|
||||||
@ -2945,6 +3036,7 @@ class Compiler(
|
|||||||
resolutionSink?.declareSymbol(param.name, kind, mutable, param.pos)
|
resolutionSink?.declareSymbol(param.name, kind, mutable, param.pos)
|
||||||
}
|
}
|
||||||
val st = try {
|
val st = try {
|
||||||
|
classCtx?.let { predeclareClassMembers(it.declaredMembers) }
|
||||||
withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
withLocalNames(constructorArgsDeclaration?.params?.map { it.name }?.toSet() ?: emptySet()) {
|
||||||
parseScript()
|
parseScript()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -407,6 +407,12 @@ open class Scope(
|
|||||||
|
|
||||||
fun updateSlotFor(name: String, record: ObjRecord) {
|
fun updateSlotFor(name: String, record: ObjRecord) {
|
||||||
nameToSlot[name]?.let { slots[it] = record }
|
nameToSlot[name]?.let { slots[it] = record }
|
||||||
|
if (objects[name] == null) {
|
||||||
|
objects[name] = record
|
||||||
|
}
|
||||||
|
if (localBindings[name] == null) {
|
||||||
|
localBindings[name] = record
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -80,6 +80,22 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ForInStatement -> compileForIn(name, stmt)
|
is net.sergeych.lyng.ForInStatement -> compileForIn(name, stmt)
|
||||||
is net.sergeych.lyng.DoWhileStatement -> compileDoWhile(name, stmt)
|
is net.sergeych.lyng.DoWhileStatement -> compileDoWhile(name, stmt)
|
||||||
is net.sergeych.lyng.WhileStatement -> compileWhile(name, stmt)
|
is net.sergeych.lyng.WhileStatement -> compileWhile(name, stmt)
|
||||||
|
is net.sergeych.lyng.WhenStatement -> {
|
||||||
|
val value = compileWhen(stmt, true) ?: return null
|
||||||
|
builder.emit(Opcode.RET, value.slot)
|
||||||
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
|
builder.build(
|
||||||
|
name,
|
||||||
|
localCount,
|
||||||
|
addrCount = nextAddrSlot,
|
||||||
|
returnLabels = returnLabels,
|
||||||
|
scopeSlotIndices,
|
||||||
|
scopeSlotNames,
|
||||||
|
scopeSlotIsModule,
|
||||||
|
localSlotNames,
|
||||||
|
localSlotMutables
|
||||||
|
)
|
||||||
|
}
|
||||||
is BlockStatement -> compileBlock(name, stmt)
|
is BlockStatement -> compileBlock(name, stmt)
|
||||||
is VarDeclStatement -> compileVarDecl(name, stmt)
|
is VarDeclStatement -> compileVarDecl(name, stmt)
|
||||||
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
||||||
|
|||||||
@ -2184,7 +2184,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testAccessEHData() = runTest {
|
fun testAccessEHData() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2207,7 +2206,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testTryFinally() = runTest {
|
fun testTryFinally() = runTest {
|
||||||
val c = Scope()
|
val c = Scope()
|
||||||
@ -2231,7 +2229,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testThrowFromKotlin() = runTest {
|
fun testThrowFromKotlin() = runTest {
|
||||||
val c = Script.newScope()
|
val c = Script.newScope()
|
||||||
@ -2256,7 +2253,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testReturnValue1() = runTest {
|
fun testReturnValue1() = runTest {
|
||||||
val r = eval(
|
val r = eval(
|
||||||
@ -2278,7 +2274,6 @@ class ScriptTest {
|
|||||||
assertEquals("111", r.toString())
|
assertEquals("111", r.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun doWhileValuesTest() = runTest {
|
fun doWhileValuesTest() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2323,7 +2318,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun doWhileValuesLabelTest() = runTest {
|
fun doWhileValuesLabelTest() = runTest {
|
||||||
withTimeout(5.seconds) {
|
withTimeout(5.seconds) {
|
||||||
@ -2357,7 +2351,6 @@ class ScriptTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testSimpleWhen() = runTest {
|
fun testSimpleWhen() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2382,7 +2375,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testWhenIs() = runTest {
|
fun testWhenIs() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2413,7 +2405,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testWhenIn() = runTest {
|
fun testWhenIn() = runTest {
|
||||||
eval(
|
eval(
|
||||||
@ -2453,7 +2444,6 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testParseSpecialVars() {
|
fun testParseSpecialVars() {
|
||||||
val l = parseLyng("$~".toSource("test$~"))
|
val l = parseLyng("$~".toSource("test$~"))
|
||||||
@ -2462,7 +2452,6 @@ class ScriptTest {
|
|||||||
assertEquals("$~", l[0].value)
|
assertEquals("$~", l[0].value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("incremental enable")
|
|
||||||
@Test
|
@Test
|
||||||
fun testMatchOperator() = runTest {
|
fun testMatchOperator() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user