Compare commits
No commits in common. "f5ced0250571af14b4c0f2bf2b87d43b1ef8b3fa" and "7b70a37e90ea1dd524fe757485de97e8cc8a822c" have entirely different histories.
f5ced02505
...
7b70a37e90
@ -1,21 +1,32 @@
|
|||||||
# Bytecode Migration Plan
|
# Bytecode Migration Plan
|
||||||
|
|
||||||
Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM tests green after each step.
|
Goal: migrate :lynglib compiler/runtime so values live in frame slots and bytecode is the default execution path.
|
||||||
|
|
||||||
## Steps
|
## Step 1: Imports as module slots (done)
|
||||||
|
- [x] Seed module slot plans from import bindings (lazy, unused imports do not allocate).
|
||||||
|
- [x] Avoid mutating scopes during compile-time imports; bind slots at runtime instead.
|
||||||
|
- [x] Make runtime member access honor extensions (methods + properties).
|
||||||
|
- [x] Ensure class members (ObjClass instances) resolve by slot id in bytecode runtime.
|
||||||
|
- [x] Expose `Iterator` in root scope so stdlib externs bind at runtime.
|
||||||
|
|
||||||
- [x] Step 1: Module/import slots seeded into module frame; bytecode module resolution works across closures.
|
## Step 2: Class-scope member refs + qualified-this refs (pending)
|
||||||
- [x] Step 2: Allow implicit/qualified `this` member refs to compile to bytecode.
|
- [ ] Bytecode-compile `ClassScopeMemberRef` (currently forced to AST in `Compiler.containsUnsupportedRef`).
|
||||||
- [x] Enable bytecode for `ImplicitThisMethodCallRef`, `QualifiedThisMethodSlotCallRef`, `QualifiedThisFieldSlotRef`.
|
- [ ] Bytecode-compile `QualifiedThisFieldSlotRef` / `QualifiedThisMethodSlotCallRef`.
|
||||||
- [x] Keep unsupported cases blocked: `ClassScopeMemberRef`, dynamic receivers, delegated members.
|
- [ ] Ensure slot resolution uses class member ids, not scope lookup; no fallback opcodes.
|
||||||
- [x] JVM tests must be green before commit.
|
- [ ] Add coverage for class static access + qualified-this access to keep JVM tests green.
|
||||||
- [x] Step 3: Bytecode support for `try/catch/finally`.
|
|
||||||
- [x] Implement bytecode emission for try/catch and finally blocks.
|
|
||||||
- [x] Preserve existing error/stack semantics.
|
|
||||||
- [x] JVM tests must be green before commit.
|
|
||||||
|
|
||||||
## Notes
|
## Step 3: Expand bytecode coverage for control flow + literals (pending)
|
||||||
|
- [ ] Add bytecode support for `TryStatement` (catch/finally) in `Compiler.containsUnsupportedForBytecode` and `BytecodeCompiler`.
|
||||||
|
- [ ] Support `WhenStatement` conditions beyond the current limited set.
|
||||||
|
- [ ] Add map literal spread support (currently throws in `BytecodeCompiler`).
|
||||||
|
- [ ] Remove remaining `BytecodeCompileException` cases for common member access (missing id paths).
|
||||||
|
|
||||||
- Keep imports bound to module frame slots; no scope map writes for imports.
|
## Known bytecode gaps (from current guards)
|
||||||
- Avoid inline suspend lambdas in compiler hot paths; use explicit `object : Statement()`.
|
- [ ] `TryStatement` is always excluded by `Compiler.containsUnsupportedForBytecode`.
|
||||||
- Do not reintroduce bytecode fallback opcodes; all symbol resolution remains compile-time only.
|
- [ ] `ClassScopeMemberRef` and qualified-this refs are excluded by `Compiler.containsUnsupportedRef`.
|
||||||
|
- [ ] `BytecodeCompiler` rejects map literal spreads and some argument expressions.
|
||||||
|
- [ ] Member access still fails when compile-time receiver class cannot be resolved.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
- [ ] `./gradlew :lynglib:jvmTest` (full suite) after each step; if failures pre-exist, run targeted tests tied to the change and record the gap in this file.
|
||||||
|
- [ ] Baseline full suite: currently 46 failures on `:lynglib:jvmTest` (run 2026-02-08); keep targeted tests green until the baseline is addressed.
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import net.sergeych.lyng.obj.Obj
|
|||||||
class BlockStatement(
|
class BlockStatement(
|
||||||
val block: Script,
|
val block: Script,
|
||||||
val slotPlan: Map<String, Int>,
|
val slotPlan: Map<String, Int>,
|
||||||
val scopeId: Int,
|
|
||||||
val captureSlots: List<CaptureSlot> = emptyList(),
|
val captureSlots: List<CaptureSlot> = emptyList(),
|
||||||
private val startPos: Pos,
|
private val startPos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
|
|||||||
@ -1897,11 +1897,7 @@ 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 TryStatement -> {
|
is TryStatement -> true
|
||||||
containsUnsupportedForBytecode(target.body) ||
|
|
||||||
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
|
|
||||||
(target.finallyClause?.let { containsUnsupportedForBytecode(it) } ?: false)
|
|
||||||
}
|
|
||||||
is WhenStatement -> {
|
is WhenStatement -> {
|
||||||
containsUnsupportedForBytecode(target.value) ||
|
containsUnsupportedForBytecode(target.value) ||
|
||||||
target.cases.any { case ->
|
target.cases.any { case ->
|
||||||
@ -1976,9 +1972,9 @@ class Compiler(
|
|||||||
if (!hasMember && !hasExtensionFor(receiverClass.className, ref.name)) return true
|
if (!hasMember && !hasExtensionFor(receiverClass.className, ref.name)) return true
|
||||||
containsUnsupportedRef(ref.receiver) || ref.args.any { containsUnsupportedForBytecode(it.value) }
|
containsUnsupportedRef(ref.receiver) || ref.args.any { containsUnsupportedForBytecode(it.value) }
|
||||||
}
|
}
|
||||||
is ImplicitThisMethodCallRef -> false
|
is ImplicitThisMethodCallRef -> true
|
||||||
is QualifiedThisMethodSlotCallRef -> false
|
is QualifiedThisMethodSlotCallRef -> true
|
||||||
is QualifiedThisFieldSlotRef -> false
|
is QualifiedThisFieldSlotRef -> true
|
||||||
is ClassScopeMemberRef -> true
|
is ClassScopeMemberRef -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@ -2103,7 +2099,7 @@ class Compiler(
|
|||||||
is BlockStatement -> {
|
is BlockStatement -> {
|
||||||
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
||||||
val script = Script(stmt.block.pos, unwrapped)
|
val script = Script(stmt.block.pos, unwrapped)
|
||||||
BlockStatement(script, stmt.slotPlan, stmt.scopeId, stmt.captureSlots, stmt.pos)
|
BlockStatement(script, stmt.slotPlan, stmt.captureSlots, stmt.pos)
|
||||||
}
|
}
|
||||||
is InlineBlockStatement -> {
|
is InlineBlockStatement -> {
|
||||||
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
||||||
@ -5392,12 +5388,19 @@ class Compiler(
|
|||||||
for ((name, idx) in basePlan) {
|
for ((name, idx) in basePlan) {
|
||||||
newPlan[name] = idx + 1
|
newPlan[name] = idx + 1
|
||||||
}
|
}
|
||||||
return BlockStatement(stmt.block, newPlan, stmt.scopeId, stmt.captureSlots, stmt.pos)
|
return BlockStatement(stmt.block, newPlan, stmt.captureSlots, stmt.pos)
|
||||||
}
|
}
|
||||||
fun stripCatchCaptures(block: Statement): Statement {
|
fun stripCatchCaptures(block: Statement): Statement {
|
||||||
val stmt = block as? BlockStatement ?: return block
|
val stmt = block as? BlockStatement ?: return block
|
||||||
if (stmt.captureSlots.isEmpty()) return stmt
|
if (stmt.captureSlots.isEmpty()) return stmt
|
||||||
return BlockStatement(stmt.block, stmt.slotPlan, stmt.scopeId, emptyList(), stmt.pos)
|
return BlockStatement(stmt.block, stmt.slotPlan, emptyList(), stmt.pos)
|
||||||
|
}
|
||||||
|
fun resolveExceptionClass(scope: Scope, name: String): ObjClass {
|
||||||
|
val rec = scope[name]
|
||||||
|
val cls = rec?.value as? ObjClass
|
||||||
|
if (cls != null) return cls
|
||||||
|
if (name == "Exception") return ObjException.Root
|
||||||
|
scope.raiseSymbolNotFound("error class does not exist or is not a class: $name")
|
||||||
}
|
}
|
||||||
fun resolveCatchVarClass(names: List<String>): ObjClass? {
|
fun resolveCatchVarClass(names: List<String>): ObjClass? {
|
||||||
if (names.size == 1) {
|
if (names.size == 1) {
|
||||||
@ -5504,15 +5507,55 @@ class Compiler(
|
|||||||
throw ScriptError(cc.currentPos(), "try block must have either catch or finally clause or both")
|
throw ScriptError(cc.currentPos(), "try block must have either catch or finally clause or both")
|
||||||
|
|
||||||
val stmtPos = body.pos
|
val stmtPos = body.pos
|
||||||
val tryCatches = catches.map { cdata ->
|
val tryStatement = object : Statement() {
|
||||||
TryStatement.CatchBlock(
|
override val pos: Pos = stmtPos
|
||||||
catchVarName = cdata.catchVar.value,
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
catchVarPos = cdata.catchVar.pos,
|
var result: Obj = ObjVoid
|
||||||
classNames = cdata.classNames,
|
try {
|
||||||
block = cdata.block
|
// body is a parsed block, it already has separate context
|
||||||
)
|
result = body.execute(scope)
|
||||||
|
} catch (e: ReturnException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: LoopBreakContinueException) {
|
||||||
|
throw e
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// convert to appropriate exception
|
||||||
|
val caughtObj = when (e) {
|
||||||
|
is ExecutionError -> e.errorObject
|
||||||
|
else -> ObjUnknownException(scope, e.message ?: e.toString())
|
||||||
}
|
}
|
||||||
return TryStatement(body, tryCatches, finallyClause, stmtPos)
|
// let's see if we should catch it:
|
||||||
|
var isCaught = false
|
||||||
|
for (cdata in catches) {
|
||||||
|
var match: Obj? = null
|
||||||
|
for (exceptionClassName in cdata.classNames) {
|
||||||
|
val exObj = resolveExceptionClass(scope, exceptionClassName)
|
||||||
|
if (caughtObj.isInstanceOf(exObj)) {
|
||||||
|
match = caughtObj
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match != null) {
|
||||||
|
val catchContext = scope.createChildScope(pos = cdata.catchVar.pos).apply {
|
||||||
|
skipScopeCreation = true
|
||||||
|
}
|
||||||
|
catchContext.addItem(cdata.catchVar.value, false, caughtObj)
|
||||||
|
result = cdata.block.execute(catchContext)
|
||||||
|
isCaught = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// rethrow if not caught this exception
|
||||||
|
if (!isCaught)
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
// finally clause does not alter result!
|
||||||
|
finallyClause?.execute(scope)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TryStatement(tryStatement, stmtPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseEnumDeclaration(isExtern: Boolean = false): Statement {
|
private fun parseEnumDeclaration(isExtern: Boolean = false): Statement {
|
||||||
@ -7121,7 +7164,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val block = Script(startPos, listOf(expr))
|
val block = Script(startPos, listOf(expr))
|
||||||
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
@ -7437,7 +7480,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
||||||
val wrapped = wrapBytecode(stmt)
|
val wrapped = wrapBytecode(stmt)
|
||||||
return wrapped.also {
|
return wrapped.also {
|
||||||
val t1 = cc.next()
|
val t1 = cc.next()
|
||||||
@ -7468,7 +7511,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
||||||
val wrapped = wrapBytecode(stmt)
|
val wrapped = wrapBytecode(stmt)
|
||||||
return wrapped.also {
|
return wrapped.also {
|
||||||
val t1 = cc.next()
|
val t1 = cc.next()
|
||||||
@ -7498,7 +7541,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
||||||
val wrapped = wrapBytecode(stmt)
|
val wrapped = wrapBytecode(stmt)
|
||||||
val t1 = cc.next()
|
val t1 = cc.next()
|
||||||
if (t1.type != Token.Type.RBRACE)
|
if (t1.type != Token.Type.RBRACE)
|
||||||
|
|||||||
@ -17,71 +17,14 @@
|
|||||||
package net.sergeych.lyng
|
package net.sergeych.lyng
|
||||||
|
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
import net.sergeych.lyng.obj.ObjClass
|
|
||||||
import net.sergeych.lyng.obj.ObjException
|
|
||||||
import net.sergeych.lyng.obj.ObjUnknownException
|
|
||||||
import net.sergeych.lyng.obj.ObjVoid
|
|
||||||
|
|
||||||
class TryStatement(
|
class TryStatement(
|
||||||
val body: Statement,
|
private val delegate: Statement,
|
||||||
val catches: List<CatchBlock>,
|
|
||||||
val finallyClause: Statement?,
|
|
||||||
private val startPos: Pos,
|
private val startPos: Pos,
|
||||||
) : Statement() {
|
) : Statement() {
|
||||||
override val pos: Pos = startPos
|
override val pos: Pos = startPos
|
||||||
|
|
||||||
data class CatchBlock(
|
|
||||||
val catchVarName: String,
|
|
||||||
val catchVarPos: Pos,
|
|
||||||
val classNames: List<String>,
|
|
||||||
val block: Statement
|
|
||||||
)
|
|
||||||
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
var result: Obj = ObjVoid
|
return delegate.execute(scope)
|
||||||
try {
|
|
||||||
result = body.execute(scope)
|
|
||||||
} catch (e: ReturnException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: LoopBreakContinueException) {
|
|
||||||
throw e
|
|
||||||
} catch (e: Exception) {
|
|
||||||
val caughtObj = when (e) {
|
|
||||||
is ExecutionError -> e.errorObject
|
|
||||||
else -> ObjUnknownException(scope, e.message ?: e.toString())
|
|
||||||
}
|
|
||||||
var isCaught = false
|
|
||||||
for (cdata in catches) {
|
|
||||||
var match: Obj? = null
|
|
||||||
for (exceptionClassName in cdata.classNames) {
|
|
||||||
val exObj = resolveExceptionClass(scope, exceptionClassName)
|
|
||||||
if (caughtObj.isInstanceOf(exObj)) {
|
|
||||||
match = caughtObj
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match != null) {
|
|
||||||
val catchContext = scope.createChildScope(pos = cdata.catchVarPos).apply {
|
|
||||||
skipScopeCreation = true
|
|
||||||
}
|
|
||||||
catchContext.addItem(cdata.catchVarName, false, caughtObj)
|
|
||||||
result = cdata.block.execute(catchContext)
|
|
||||||
isCaught = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isCaught) throw e
|
|
||||||
} finally {
|
|
||||||
finallyClause?.execute(scope)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveExceptionClass(scope: Scope, name: String): ObjClass {
|
|
||||||
val rec = scope[name]
|
|
||||||
val cls = rec?.value as? ObjClass
|
|
||||||
if (cls != null) return cls
|
|
||||||
if (name == "Exception") return ObjException.Root
|
|
||||||
scope.raiseSymbolNotFound("error class does not exist or is not a class: $name")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,7 +133,7 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
||||||
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> compileExtensionPropertyDecl(name, stmt)
|
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> compileExtensionPropertyDecl(name, stmt)
|
||||||
is net.sergeych.lyng.TryStatement -> {
|
is net.sergeych.lyng.TryStatement -> {
|
||||||
val value = emitTry(stmt, true) ?: return null
|
val value = emitStatementEval(stmt)
|
||||||
builder.emit(Opcode.RET, value.slot)
|
builder.emit(Opcode.RET, value.slot)
|
||||||
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
|
||||||
builder.build(
|
builder.build(
|
||||||
@ -3314,13 +3314,12 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target)
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target)
|
||||||
is net.sergeych.lyng.EnumDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.EnumDeclStatement -> emitStatementEval(target)
|
||||||
is net.sergeych.lyng.TryStatement -> emitTry(target, true)
|
is net.sergeych.lyng.TryStatement -> emitStatementEval(target)
|
||||||
is net.sergeych.lyng.WhenStatement -> compileWhen(target, true)
|
is net.sergeych.lyng.WhenStatement -> compileWhen(target, true)
|
||||||
is net.sergeych.lyng.BreakStatement -> compileBreak(target)
|
is net.sergeych.lyng.BreakStatement -> compileBreak(target)
|
||||||
is net.sergeych.lyng.ContinueStatement -> compileContinue(target)
|
is net.sergeych.lyng.ContinueStatement -> compileContinue(target)
|
||||||
is net.sergeych.lyng.ReturnStatement -> compileReturn(target)
|
is net.sergeych.lyng.ReturnStatement -> compileReturn(target)
|
||||||
is net.sergeych.lyng.ThrowStatement -> compileThrow(target)
|
is net.sergeych.lyng.ThrowStatement -> compileThrow(target)
|
||||||
is net.sergeych.lyng.TryStatement -> emitTry(target, false)
|
|
||||||
else -> {
|
else -> {
|
||||||
emitFallbackStatement(target)
|
emitFallbackStatement(target)
|
||||||
}
|
}
|
||||||
@ -3363,7 +3362,6 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ContinueStatement -> compileContinue(target)
|
is net.sergeych.lyng.ContinueStatement -> compileContinue(target)
|
||||||
is net.sergeych.lyng.ReturnStatement -> compileReturn(target)
|
is net.sergeych.lyng.ReturnStatement -> compileReturn(target)
|
||||||
is net.sergeych.lyng.ThrowStatement -> compileThrow(target)
|
is net.sergeych.lyng.ThrowStatement -> compileThrow(target)
|
||||||
is net.sergeych.lyng.TryStatement -> emitTry(target, false)
|
|
||||||
is net.sergeych.lyng.WhenStatement -> compileWhen(target, false)
|
is net.sergeych.lyng.WhenStatement -> compileWhen(target, false)
|
||||||
else -> {
|
else -> {
|
||||||
emitFallbackStatement(target)
|
emitFallbackStatement(target)
|
||||||
@ -3425,167 +3423,6 @@ class BytecodeCompiler(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun emitTry(stmt: net.sergeych.lyng.TryStatement, needResult: Boolean): CompiledValue? {
|
|
||||||
val resultSlot = allocSlot()
|
|
||||||
updateSlotType(resultSlot, SlotType.OBJ)
|
|
||||||
val exceptionSlot = allocSlot()
|
|
||||||
updateSlotType(exceptionSlot, SlotType.OBJ)
|
|
||||||
val catchLabel = if (stmt.catches.isNotEmpty()) builder.label() else null
|
|
||||||
val finallyLabel = if (stmt.finallyClause != null) builder.label() else null
|
|
||||||
val endLabel = builder.label()
|
|
||||||
val catchOperand = catchLabel?.let { CmdBuilder.Operand.LabelRef(it) }
|
|
||||||
?: CmdBuilder.Operand.IntVal(-1)
|
|
||||||
val finallyOperand = finallyLabel?.let { CmdBuilder.Operand.LabelRef(it) }
|
|
||||||
?: CmdBuilder.Operand.IntVal(-1)
|
|
||||||
builder.emit(
|
|
||||||
Opcode.PUSH_TRY,
|
|
||||||
listOf(
|
|
||||||
CmdBuilder.Operand.IntVal(exceptionSlot),
|
|
||||||
catchOperand,
|
|
||||||
finallyOperand
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val bodyValue = compileStatementValueOrFallback(stmt.body, needResult) ?: return null
|
|
||||||
if (needResult) {
|
|
||||||
emitMove(bodyValue, resultSlot)
|
|
||||||
}
|
|
||||||
if (finallyLabel != null) {
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(finallyLabel)))
|
|
||||||
} else {
|
|
||||||
builder.emit(Opcode.POP_TRY)
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
|
||||||
}
|
|
||||||
if (catchLabel != null) {
|
|
||||||
builder.mark(catchLabel)
|
|
||||||
val catchBlockLabels = stmt.catches.map { builder.label() }
|
|
||||||
val noMatchLabel = builder.label()
|
|
||||||
for ((index, cdata) in stmt.catches.withIndex()) {
|
|
||||||
val handlerLabel = catchBlockLabels[index]
|
|
||||||
for (className in cdata.classNames) {
|
|
||||||
val classValue = compileCatchClassSlot(className) ?: return null
|
|
||||||
val checkSlot = allocSlot()
|
|
||||||
builder.emit(Opcode.CHECK_IS, exceptionSlot, classValue.slot, checkSlot)
|
|
||||||
builder.emit(
|
|
||||||
Opcode.JMP_IF_TRUE,
|
|
||||||
listOf(CmdBuilder.Operand.IntVal(checkSlot), CmdBuilder.Operand.LabelRef(handlerLabel))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(noMatchLabel)))
|
|
||||||
for ((index, cdata) in stmt.catches.withIndex()) {
|
|
||||||
val handlerLabel = catchBlockLabels[index]
|
|
||||||
builder.mark(handlerLabel)
|
|
||||||
builder.emit(Opcode.CLEAR_PENDING_THROWABLE)
|
|
||||||
val catchValue = emitCatchBlock(cdata.block, cdata.catchVarName, exceptionSlot, needResult)
|
|
||||||
?: return null
|
|
||||||
if (needResult) {
|
|
||||||
emitMove(catchValue, resultSlot)
|
|
||||||
}
|
|
||||||
if (finallyLabel != null) {
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(finallyLabel)))
|
|
||||||
} else {
|
|
||||||
builder.emit(Opcode.POP_TRY)
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.mark(noMatchLabel)
|
|
||||||
if (finallyLabel != null) {
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(finallyLabel)))
|
|
||||||
} else {
|
|
||||||
builder.emit(Opcode.POP_TRY)
|
|
||||||
builder.emit(Opcode.RETHROW_PENDING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (finallyLabel != null) {
|
|
||||||
builder.mark(finallyLabel)
|
|
||||||
builder.emit(Opcode.POP_TRY)
|
|
||||||
stmt.finallyClause?.let { finallyClause ->
|
|
||||||
compileStatementValueOrFallback(finallyClause, false) ?: return null
|
|
||||||
}
|
|
||||||
builder.emit(Opcode.RETHROW_PENDING)
|
|
||||||
}
|
|
||||||
builder.mark(endLabel)
|
|
||||||
if (needResult) return CompiledValue(resultSlot, SlotType.OBJ)
|
|
||||||
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
|
||||||
val voidSlot = allocSlot()
|
|
||||||
builder.emit(Opcode.CONST_OBJ, voidId, voidSlot)
|
|
||||||
return CompiledValue(voidSlot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitCatchBlock(
|
|
||||||
block: Statement,
|
|
||||||
catchVarName: String,
|
|
||||||
exceptionSlot: Int,
|
|
||||||
needResult: Boolean
|
|
||||||
): CompiledValue? {
|
|
||||||
val stmt = block as? BlockStatement
|
|
||||||
if (stmt == null) {
|
|
||||||
val declId = builder.addConst(BytecodeConst.LocalDecl(catchVarName, false, Visibility.Public, false))
|
|
||||||
builder.emit(Opcode.DECL_LOCAL, declId, exceptionSlot)
|
|
||||||
return compileStatementValueOrFallback(block, needResult)
|
|
||||||
}
|
|
||||||
val captureNames = if (stmt.captureSlots.isEmpty()) emptyList() else stmt.captureSlots.map { it.name }
|
|
||||||
val planId = builder.addConst(BytecodeConst.SlotPlan(stmt.slotPlan, captureNames))
|
|
||||||
builder.emit(Opcode.PUSH_SCOPE, planId)
|
|
||||||
resetAddrCache()
|
|
||||||
val declId = builder.addConst(BytecodeConst.LocalDecl(catchVarName, false, Visibility.Public, false))
|
|
||||||
builder.emit(Opcode.DECL_LOCAL, declId, exceptionSlot)
|
|
||||||
val catchSlotIndex = stmt.slotPlan[catchVarName]
|
|
||||||
if (catchSlotIndex != null) {
|
|
||||||
val key = ScopeSlotKey(stmt.scopeId, catchSlotIndex)
|
|
||||||
val localIndex = localSlotIndexByKey[key]
|
|
||||||
if (localIndex != null) {
|
|
||||||
val localSlot = scopeSlotCount + localIndex
|
|
||||||
if (localSlot != exceptionSlot) {
|
|
||||||
emitMove(CompiledValue(exceptionSlot, SlotType.OBJ), localSlot)
|
|
||||||
}
|
|
||||||
updateSlotType(localSlot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val statements = stmt.statements()
|
|
||||||
var lastValue: CompiledValue? = null
|
|
||||||
for ((index, statement) in statements.withIndex()) {
|
|
||||||
val isLast = index == statements.lastIndex
|
|
||||||
val wantResult = needResult && isLast
|
|
||||||
val value = compileStatementValueOrFallback(statement, wantResult) ?: return null
|
|
||||||
if (wantResult) {
|
|
||||||
lastValue = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val result = if (needResult) {
|
|
||||||
lastValue ?: run {
|
|
||||||
val slot = allocSlot()
|
|
||||||
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
|
||||||
builder.emit(Opcode.CONST_OBJ, voidId, slot)
|
|
||||||
CompiledValue(slot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastValue ?: run {
|
|
||||||
val slot = allocSlot()
|
|
||||||
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
|
||||||
builder.emit(Opcode.CONST_OBJ, voidId, slot)
|
|
||||||
CompiledValue(slot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.emit(Opcode.POP_SCOPE)
|
|
||||||
resetAddrCache()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun compileCatchClassSlot(name: String): CompiledValue? {
|
|
||||||
val ref = LocalVarRef(name, Pos.builtIn)
|
|
||||||
val compiled = compileRef(ref)
|
|
||||||
if (compiled != null) {
|
|
||||||
return ensureObjSlot(compiled)
|
|
||||||
}
|
|
||||||
val cls = nameObjClass[name] ?: resolveTypeNameClass(name) ?: return null
|
|
||||||
val id = builder.addConst(BytecodeConst.ObjRef(cls))
|
|
||||||
val slot = allocSlot()
|
|
||||||
builder.emit(Opcode.CONST_OBJ, id, slot)
|
|
||||||
updateSlotType(slot, SlotType.OBJ)
|
|
||||||
return CompiledValue(slot, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitInlineStatements(statements: List<Statement>, needResult: Boolean): CompiledValue? {
|
private fun emitInlineStatements(statements: List<Statement>, needResult: Boolean): CompiledValue? {
|
||||||
var lastValue: CompiledValue? = null
|
var lastValue: CompiledValue? = null
|
||||||
for ((index, statement) in statements.withIndex()) {
|
for ((index, statement) in statements.withIndex()) {
|
||||||
@ -5344,13 +5181,6 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
stmt.elseCase?.let { collectScopeSlots(it) }
|
stmt.elseCase?.let { collectScopeSlots(it) }
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.TryStatement -> {
|
|
||||||
collectScopeSlots(stmt.body)
|
|
||||||
for (catchBlock in stmt.catches) {
|
|
||||||
collectScopeSlots(catchBlock.block)
|
|
||||||
}
|
|
||||||
stmt.finallyClause?.let { collectScopeSlots(it) }
|
|
||||||
}
|
|
||||||
is net.sergeych.lyng.BreakStatement -> {
|
is net.sergeych.lyng.BreakStatement -> {
|
||||||
stmt.resultExpr?.let { collectScopeSlots(it) }
|
stmt.resultExpr?.let { collectScopeSlots(it) }
|
||||||
}
|
}
|
||||||
@ -5532,13 +5362,6 @@ class BytecodeCompiler(
|
|||||||
is net.sergeych.lyng.ThrowStatement -> {
|
is net.sergeych.lyng.ThrowStatement -> {
|
||||||
collectLoopVarNames(stmt.throwExpr)
|
collectLoopVarNames(stmt.throwExpr)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.TryStatement -> {
|
|
||||||
collectLoopVarNames(stmt.body)
|
|
||||||
for (catchBlock in stmt.catches) {
|
|
||||||
collectLoopVarNames(catchBlock.block)
|
|
||||||
}
|
|
||||||
stmt.finallyClause?.let { collectLoopVarNames(it) }
|
|
||||||
}
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,11 +126,7 @@ class BytecodeStatement private constructor(
|
|||||||
is net.sergeych.lyng.ClassDeclStatement -> false
|
is net.sergeych.lyng.ClassDeclStatement -> false
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> false
|
is net.sergeych.lyng.FunctionDeclStatement -> false
|
||||||
is net.sergeych.lyng.EnumDeclStatement -> false
|
is net.sergeych.lyng.EnumDeclStatement -> false
|
||||||
is net.sergeych.lyng.TryStatement -> {
|
is net.sergeych.lyng.TryStatement -> true
|
||||||
containsUnsupportedStatement(target.body) ||
|
|
||||||
target.catches.any { containsUnsupportedStatement(it.block) } ||
|
|
||||||
(target.finallyClause?.let { containsUnsupportedStatement(it) } ?: false)
|
|
||||||
}
|
|
||||||
is net.sergeych.lyng.WhenStatement -> {
|
is net.sergeych.lyng.WhenStatement -> {
|
||||||
containsUnsupportedStatement(target.value) ||
|
containsUnsupportedStatement(target.value) ||
|
||||||
target.cases.any { case ->
|
target.cases.any { case ->
|
||||||
@ -151,7 +147,6 @@ class BytecodeStatement private constructor(
|
|||||||
net.sergeych.lyng.BlockStatement(
|
net.sergeych.lyng.BlockStatement(
|
||||||
net.sergeych.lyng.Script(stmt.pos, unwrapped),
|
net.sergeych.lyng.Script(stmt.pos, unwrapped),
|
||||||
stmt.slotPlan,
|
stmt.slotPlan,
|
||||||
stmt.scopeId,
|
|
||||||
stmt.captureSlots,
|
stmt.captureSlots,
|
||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
|
|||||||
@ -119,8 +119,7 @@ class CmdBuilder {
|
|||||||
|
|
||||||
private fun operandKinds(op: Opcode): List<OperandKind> {
|
private fun operandKinds(op: Opcode): List<OperandKind> {
|
||||||
return when (op) {
|
return when (op) {
|
||||||
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY,
|
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN -> emptyList()
|
||||||
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING -> emptyList()
|
|
||||||
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
||||||
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
||||||
Opcode.OBJ_TO_BOOL,
|
Opcode.OBJ_TO_BOOL,
|
||||||
@ -145,8 +144,6 @@ class CmdBuilder {
|
|||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
Opcode.PUSH_TRY ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
@ -241,7 +238,6 @@ class CmdBuilder {
|
|||||||
Opcode.MAKE_QUALIFIED_VIEW -> CmdMakeQualifiedView(operands[0], operands[1], operands[2])
|
Opcode.MAKE_QUALIFIED_VIEW -> CmdMakeQualifiedView(operands[0], operands[1], operands[2])
|
||||||
Opcode.RET_LABEL -> CmdRetLabel(operands[0], operands[1])
|
Opcode.RET_LABEL -> CmdRetLabel(operands[0], operands[1])
|
||||||
Opcode.THROW -> CmdThrow(operands[0], operands[1])
|
Opcode.THROW -> CmdThrow(operands[0], operands[1])
|
||||||
Opcode.RETHROW_PENDING -> CmdRethrowPending()
|
|
||||||
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
||||||
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
||||||
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
||||||
@ -380,9 +376,6 @@ class CmdBuilder {
|
|||||||
Opcode.POP_SCOPE -> CmdPopScope()
|
Opcode.POP_SCOPE -> CmdPopScope()
|
||||||
Opcode.PUSH_SLOT_PLAN -> CmdPushSlotPlan(operands[0])
|
Opcode.PUSH_SLOT_PLAN -> CmdPushSlotPlan(operands[0])
|
||||||
Opcode.POP_SLOT_PLAN -> CmdPopSlotPlan()
|
Opcode.POP_SLOT_PLAN -> CmdPopSlotPlan()
|
||||||
Opcode.PUSH_TRY -> CmdPushTry(operands[0], operands[1], operands[2])
|
|
||||||
Opcode.POP_TRY -> CmdPopTry()
|
|
||||||
Opcode.CLEAR_PENDING_THROWABLE -> CmdClearPendingThrowable()
|
|
||||||
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
Opcode.DECL_LOCAL -> CmdDeclLocal(operands[0], operands[1])
|
||||||
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
Opcode.DECL_DELEGATED -> CmdDeclDelegated(operands[0], operands[1])
|
||||||
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
|
||||||
|
|||||||
@ -180,14 +180,10 @@ object CmdDisassembler {
|
|||||||
is CmdRetLabel -> Opcode.RET_LABEL to intArrayOf(cmd.labelId, cmd.slot)
|
is CmdRetLabel -> Opcode.RET_LABEL to intArrayOf(cmd.labelId, cmd.slot)
|
||||||
is CmdRetVoid -> Opcode.RET_VOID to intArrayOf()
|
is CmdRetVoid -> Opcode.RET_VOID to intArrayOf()
|
||||||
is CmdThrow -> Opcode.THROW to intArrayOf(cmd.posId, cmd.slot)
|
is CmdThrow -> Opcode.THROW to intArrayOf(cmd.posId, cmd.slot)
|
||||||
is CmdRethrowPending -> Opcode.RETHROW_PENDING to intArrayOf()
|
|
||||||
is CmdPushScope -> Opcode.PUSH_SCOPE to intArrayOf(cmd.planId)
|
is CmdPushScope -> Opcode.PUSH_SCOPE to intArrayOf(cmd.planId)
|
||||||
is CmdPopScope -> Opcode.POP_SCOPE to intArrayOf()
|
is CmdPopScope -> Opcode.POP_SCOPE to intArrayOf()
|
||||||
is CmdPushSlotPlan -> Opcode.PUSH_SLOT_PLAN to intArrayOf(cmd.planId)
|
is CmdPushSlotPlan -> Opcode.PUSH_SLOT_PLAN to intArrayOf(cmd.planId)
|
||||||
is CmdPopSlotPlan -> Opcode.POP_SLOT_PLAN to intArrayOf()
|
is CmdPopSlotPlan -> Opcode.POP_SLOT_PLAN to intArrayOf()
|
||||||
is CmdPushTry -> Opcode.PUSH_TRY to intArrayOf(cmd.exceptionSlot, cmd.catchIp, cmd.finallyIp)
|
|
||||||
is CmdPopTry -> Opcode.POP_TRY to intArrayOf()
|
|
||||||
is CmdClearPendingThrowable -> Opcode.CLEAR_PENDING_THROWABLE to intArrayOf()
|
|
||||||
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclLocal -> Opcode.DECL_LOCAL to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDelegated -> Opcode.DECL_DELEGATED to intArrayOf(cmd.constId, cmd.slot)
|
||||||
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
|
||||||
@ -218,8 +214,7 @@ object CmdDisassembler {
|
|||||||
|
|
||||||
private fun operandKinds(op: Opcode): List<OperandKind> {
|
private fun operandKinds(op: Opcode): List<OperandKind> {
|
||||||
return when (op) {
|
return when (op) {
|
||||||
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY,
|
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN,
|
||||||
Opcode.CLEAR_PENDING_THROWABLE, Opcode.RETHROW_PENDING,
|
|
||||||
Opcode.ITER_POP, Opcode.ITER_CANCEL -> emptyList()
|
Opcode.ITER_POP, Opcode.ITER_CANCEL -> emptyList()
|
||||||
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
Opcode.MOVE_OBJ, Opcode.MOVE_INT, Opcode.MOVE_REAL, Opcode.MOVE_BOOL, Opcode.BOX_OBJ,
|
||||||
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
Opcode.INT_TO_REAL, Opcode.REAL_TO_INT, Opcode.BOOL_TO_INT, Opcode.INT_TO_BOOL,
|
||||||
@ -247,8 +242,6 @@ object CmdDisassembler {
|
|||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
Opcode.PUSH_SCOPE, Opcode.PUSH_SLOT_PLAN ->
|
||||||
listOf(OperandKind.CONST)
|
listOf(OperandKind.CONST)
|
||||||
Opcode.PUSH_TRY ->
|
|
||||||
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
|
|
||||||
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
|
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
|
||||||
listOf(OperandKind.CONST, OperandKind.SLOT)
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
|
||||||
|
|||||||
@ -34,14 +34,7 @@ class CmdVm {
|
|||||||
while (result == null) {
|
while (result == null) {
|
||||||
val cmd = cmds[frame.ip]
|
val cmd = cmds[frame.ip]
|
||||||
frame.ip += 1
|
frame.ip += 1
|
||||||
try {
|
|
||||||
cmd.perform(frame)
|
cmd.perform(frame)
|
||||||
} catch (e: Throwable) {
|
|
||||||
if (!frame.handleException(e)) {
|
|
||||||
frame.cancelIterators()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
frame.cancelIterators()
|
frame.cancelIterators()
|
||||||
@ -1145,13 +1138,6 @@ class CmdThrow(internal val posId: Int, internal val slot: Int) : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdRethrowPending : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
frame.rethrowPending()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdPushScope(internal val planId: Int) : Cmd() {
|
class CmdPushScope(internal val planId: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val planConst = frame.fn.constants[planId] as? BytecodeConst.SlotPlan
|
val planConst = frame.fn.constants[planId] as? BytecodeConst.SlotPlan
|
||||||
@ -1184,27 +1170,6 @@ class CmdPopSlotPlan : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdPushTry(internal val exceptionSlot: Int, internal val catchIp: Int, internal val finallyIp: Int) : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
frame.pushTry(exceptionSlot, catchIp, finallyIp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdPopTry : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
frame.popTry()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdClearPendingThrowable : Cmd() {
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
|
||||||
frame.clearPendingThrowable()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() {
|
class CmdDeclLocal(internal val constId: Int, internal val slot: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val decl = frame.fn.constants[constId] as? BytecodeConst.LocalDecl
|
val decl = frame.fn.constants[constId] as? BytecodeConst.LocalDecl
|
||||||
@ -1662,14 +1627,6 @@ class CmdFrame(
|
|||||||
private var scopeDepth = 0
|
private var scopeDepth = 0
|
||||||
private var virtualDepth = 0
|
private var virtualDepth = 0
|
||||||
private val iterStack = ArrayDeque<Obj>()
|
private val iterStack = ArrayDeque<Obj>()
|
||||||
internal data class TryHandler(
|
|
||||||
val exceptionSlot: Int,
|
|
||||||
val catchIp: Int,
|
|
||||||
val finallyIp: Int,
|
|
||||||
var inCatch: Boolean = false
|
|
||||||
)
|
|
||||||
internal val tryStack = ArrayDeque<TryHandler>()
|
|
||||||
private var pendingThrowable: Throwable? = null
|
|
||||||
|
|
||||||
internal val frame = BytecodeFrame(fn.localCount, args.size)
|
internal val frame = BytecodeFrame(fn.localCount, args.size)
|
||||||
private val addrScopes: Array<Scope?> = arrayOfNulls(fn.addrCount)
|
private val addrScopes: Array<Scope?> = arrayOfNulls(fn.addrCount)
|
||||||
@ -1752,63 +1709,6 @@ class CmdFrame(
|
|||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleException(t: Throwable): Boolean {
|
|
||||||
val handler = tryStack.lastOrNull() ?: return false
|
|
||||||
val finallyIp = handler.finallyIp
|
|
||||||
if (t is ReturnException || t is LoopBreakContinueException) {
|
|
||||||
if (finallyIp >= 0) {
|
|
||||||
pendingThrowable = t
|
|
||||||
ip = finallyIp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (handler.inCatch) {
|
|
||||||
if (finallyIp >= 0) {
|
|
||||||
pendingThrowable = t
|
|
||||||
ip = finallyIp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
handler.inCatch = true
|
|
||||||
pendingThrowable = t
|
|
||||||
if (handler.catchIp >= 0) {
|
|
||||||
val caughtObj = when (t) {
|
|
||||||
is ExecutionError -> t.errorObject
|
|
||||||
else -> ObjUnknownException(ensureScope(), t.message ?: t.toString())
|
|
||||||
}
|
|
||||||
storeObjResult(handler.exceptionSlot, caughtObj)
|
|
||||||
ip = handler.catchIp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (finallyIp >= 0) {
|
|
||||||
ip = finallyIp
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pushTry(exceptionSlot: Int, catchIp: Int, finallyIp: Int) {
|
|
||||||
tryStack.addLast(TryHandler(exceptionSlot, catchIp, finallyIp))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun popTry() {
|
|
||||||
if (tryStack.isNotEmpty()) {
|
|
||||||
tryStack.removeLast()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearPendingThrowable() {
|
|
||||||
pendingThrowable = null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rethrowPending() {
|
|
||||||
val t = pendingThrowable ?: return
|
|
||||||
pendingThrowable = null
|
|
||||||
throw t
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun posForIp(ip: Int): Pos? {
|
private fun posForIp(ip: Int): Pos? {
|
||||||
if (ip < 0) return null
|
if (ip < 0) return null
|
||||||
return fn.posByIp.getOrNull(ip)
|
return fn.posByIp.getOrNull(ip)
|
||||||
|
|||||||
@ -127,9 +127,6 @@ enum class Opcode(val code: Int) {
|
|||||||
DECL_EXT_PROPERTY(0x8A),
|
DECL_EXT_PROPERTY(0x8A),
|
||||||
DECL_DELEGATED(0x8B),
|
DECL_DELEGATED(0x8B),
|
||||||
DECL_DESTRUCTURE(0x8C),
|
DECL_DESTRUCTURE(0x8C),
|
||||||
PUSH_TRY(0x8D),
|
|
||||||
POP_TRY(0x8E),
|
|
||||||
CLEAR_PENDING_THROWABLE(0x8F),
|
|
||||||
|
|
||||||
CALL_DIRECT(0x90),
|
CALL_DIRECT(0x90),
|
||||||
CALL_MEMBER_SLOT(0x92),
|
CALL_MEMBER_SLOT(0x92),
|
||||||
@ -151,7 +148,6 @@ enum class Opcode(val code: Int) {
|
|||||||
LOAD_BOOL_ADDR(0xB8),
|
LOAD_BOOL_ADDR(0xB8),
|
||||||
STORE_BOOL_ADDR(0xB9),
|
STORE_BOOL_ADDR(0xB9),
|
||||||
THROW(0xBB),
|
THROW(0xBB),
|
||||||
RETHROW_PENDING(0xBC),
|
|
||||||
ITER_PUSH(0xBF),
|
ITER_PUSH(0xBF),
|
||||||
ITER_POP(0xC0),
|
ITER_POP(0xC0),
|
||||||
ITER_CANCEL(0xC1),
|
ITER_CANCEL(0xC1),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user