Step 3: bytecode try/catch/finally
This commit is contained in:
parent
7ea67d4816
commit
f5ced02505
@ -9,10 +9,10 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Enable bytecode for `ImplicitThisMethodCallRef`, `QualifiedThisMethodSlotCallRef`, `QualifiedThisFieldSlotRef`.
|
- [x] Enable bytecode for `ImplicitThisMethodCallRef`, `QualifiedThisMethodSlotCallRef`, `QualifiedThisFieldSlotRef`.
|
||||||
- [x] Keep unsupported cases blocked: `ClassScopeMemberRef`, dynamic receivers, delegated members.
|
- [x] Keep unsupported cases blocked: `ClassScopeMemberRef`, dynamic receivers, delegated members.
|
||||||
- [x] JVM tests must be green before commit.
|
- [x] JVM tests must be green before commit.
|
||||||
- [ ] Step 3: Bytecode support for `try/catch/finally`.
|
- [x] Step 3: Bytecode support for `try/catch/finally`.
|
||||||
- [ ] Implement bytecode emission for try/catch and finally blocks.
|
- [x] Implement bytecode emission for try/catch and finally blocks.
|
||||||
- [ ] Preserve existing error/stack semantics.
|
- [x] Preserve existing error/stack semantics.
|
||||||
- [ ] JVM tests must be green before commit.
|
- [x] JVM tests must be green before commit.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ 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,7 +1897,11 @@ 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 -> true
|
is TryStatement -> {
|
||||||
|
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 ->
|
||||||
@ -2099,7 +2103,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.captureSlots, stmt.pos)
|
BlockStatement(script, stmt.slotPlan, stmt.scopeId, stmt.captureSlots, stmt.pos)
|
||||||
}
|
}
|
||||||
is InlineBlockStatement -> {
|
is InlineBlockStatement -> {
|
||||||
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
val unwrapped = stmt.statements().map { unwrapBytecodeDeep(it) }
|
||||||
@ -5388,19 +5392,12 @@ 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.captureSlots, stmt.pos)
|
return BlockStatement(stmt.block, newPlan, stmt.scopeId, 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, emptyList(), stmt.pos)
|
return BlockStatement(stmt.block, stmt.slotPlan, stmt.scopeId, 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) {
|
||||||
@ -5507,55 +5504,15 @@ 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 tryStatement = object : Statement() {
|
val tryCatches = catches.map { cdata ->
|
||||||
override val pos: Pos = stmtPos
|
TryStatement.CatchBlock(
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
catchVarName = cdata.catchVar.value,
|
||||||
var result: Obj = ObjVoid
|
catchVarPos = cdata.catchVar.pos,
|
||||||
try {
|
classNames = cdata.classNames,
|
||||||
// body is a parsed block, it already has separate context
|
block = cdata.block
|
||||||
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())
|
|
||||||
}
|
|
||||||
// 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)
|
return TryStatement(body, tryCatches, finallyClause, stmtPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseEnumDeclaration(isExtern: Boolean = false): Statement {
|
private fun parseEnumDeclaration(isExtern: Boolean = false): Statement {
|
||||||
@ -7164,7 +7121,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, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, capturePlan.captures.toList(), startPos)
|
||||||
resolutionSink?.exitScope(cc.currentPos())
|
resolutionSink?.exitScope(cc.currentPos())
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
@ -7480,7 +7437,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, 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()
|
||||||
@ -7511,7 +7468,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, 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()
|
||||||
@ -7541,7 +7498,7 @@ class Compiler(
|
|||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
val planSnapshot = slotPlanIndices(blockSlotPlan)
|
||||||
val stmt = BlockStatement(block, planSnapshot, capturePlan.captures.toList(), startPos)
|
val stmt = BlockStatement(block, planSnapshot, blockSlotPlan.id, 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,14 +17,71 @@
|
|||||||
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(
|
||||||
private val delegate: Statement,
|
val body: 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 {
|
||||||
return delegate.execute(scope)
|
var result: Obj = ObjVoid
|
||||||
|
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 = emitStatementEval(stmt)
|
val value = emitTry(stmt, true) ?: return null
|
||||||
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,12 +3314,13 @@ 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 -> emitStatementEval(target)
|
is net.sergeych.lyng.TryStatement -> emitTry(target, true)
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -3362,6 +3363,7 @@ 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)
|
||||||
@ -3423,6 +3425,167 @@ 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()) {
|
||||||
@ -5181,6 +5344,13 @@ 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) }
|
||||||
}
|
}
|
||||||
@ -5362,6 +5532,13 @@ 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,7 +126,11 @@ 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 -> true
|
is net.sergeych.lyng.TryStatement -> {
|
||||||
|
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 ->
|
||||||
@ -147,6 +151,7 @@ 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,7 +119,8 @@ 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 -> emptyList()
|
Opcode.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY,
|
||||||
|
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,
|
||||||
@ -144,6 +145,8 @@ 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,
|
||||||
@ -238,6 +241,7 @@ 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])
|
||||||
@ -376,6 +380,9 @@ 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,10 +180,14 @@ 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)
|
||||||
@ -214,7 +218,8 @@ 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.NOP, Opcode.RET_VOID, Opcode.POP_SCOPE, Opcode.POP_SLOT_PLAN, Opcode.POP_TRY,
|
||||||
|
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,
|
||||||
@ -242,6 +247,8 @@ 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,7 +34,14 @@ class CmdVm {
|
|||||||
while (result == null) {
|
while (result == null) {
|
||||||
val cmd = cmds[frame.ip]
|
val cmd = cmds[frame.ip]
|
||||||
frame.ip += 1
|
frame.ip += 1
|
||||||
cmd.perform(frame)
|
try {
|
||||||
|
cmd.perform(frame)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (!frame.handleException(e)) {
|
||||||
|
frame.cancelIterators()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
frame.cancelIterators()
|
frame.cancelIterators()
|
||||||
@ -1138,6 +1145,13 @@ 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
|
||||||
@ -1170,6 +1184,27 @@ 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
|
||||||
@ -1627,6 +1662,14 @@ 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)
|
||||||
@ -1709,6 +1752,63 @@ 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,6 +127,9 @@ 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),
|
||||||
@ -148,6 +151,7 @@ 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