Stabilize bytecode baseline for nested range benchmark
This commit is contained in:
parent
bef94d3bc5
commit
2311cfc224
@ -370,16 +370,29 @@ class Compiler(
|
|||||||
private var isTransientFlag: Boolean = false
|
private var isTransientFlag: Boolean = false
|
||||||
private var lastLabel: String? = null
|
private var lastLabel: String? = null
|
||||||
private val useBytecodeStatements: Boolean = true
|
private val useBytecodeStatements: Boolean = true
|
||||||
|
private val returnLabelStack = ArrayDeque<Set<String>>()
|
||||||
|
|
||||||
private fun wrapBytecode(stmt: Statement): Statement {
|
private fun wrapBytecode(stmt: Statement): Statement {
|
||||||
if (!useBytecodeStatements) return stmt
|
if (!useBytecodeStatements) return stmt
|
||||||
val allowLocals = codeContexts.lastOrNull() is CodeContext.Function
|
val allowLocals = codeContexts.lastOrNull() is CodeContext.Function
|
||||||
return BytecodeStatement.wrap(stmt, "stmt@${stmt.pos}", allowLocalSlots = allowLocals)
|
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||||
|
return BytecodeStatement.wrap(
|
||||||
|
stmt,
|
||||||
|
"stmt@${stmt.pos}",
|
||||||
|
allowLocalSlots = allowLocals,
|
||||||
|
returnLabels = returnLabels
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun wrapFunctionBytecode(stmt: Statement, name: String): Statement {
|
private fun wrapFunctionBytecode(stmt: Statement, name: String): Statement {
|
||||||
if (!useBytecodeStatements) return stmt
|
if (!useBytecodeStatements) return stmt
|
||||||
return BytecodeStatement.wrap(stmt, "fn@$name", allowLocalSlots = true)
|
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||||
|
return BytecodeStatement.wrap(
|
||||||
|
stmt,
|
||||||
|
"fn@$name",
|
||||||
|
allowLocalSlots = true,
|
||||||
|
returnLabels = returnLabels
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun containsUnsupportedForBytecode(stmt: Statement): Boolean {
|
private fun containsUnsupportedForBytecode(stmt: Statement): Boolean {
|
||||||
@ -392,13 +405,27 @@ class Compiler(
|
|||||||
(target.elseBody?.let { containsUnsupportedForBytecode(it) } ?: false)
|
(target.elseBody?.let { containsUnsupportedForBytecode(it) } ?: false)
|
||||||
}
|
}
|
||||||
is ForInStatement -> {
|
is ForInStatement -> {
|
||||||
target.constRange == null || target.canBreak ||
|
target.constRange == null ||
|
||||||
containsUnsupportedForBytecode(target.source) ||
|
containsUnsupportedForBytecode(target.source) ||
|
||||||
containsUnsupportedForBytecode(target.body) ||
|
containsUnsupportedForBytecode(target.body) ||
|
||||||
(target.elseStatement?.let { containsUnsupportedForBytecode(it) } ?: false)
|
(target.elseStatement?.let { containsUnsupportedForBytecode(it) } ?: false)
|
||||||
}
|
}
|
||||||
|
is WhileStatement -> {
|
||||||
|
containsUnsupportedForBytecode(target.condition) ||
|
||||||
|
containsUnsupportedForBytecode(target.body) ||
|
||||||
|
(target.elseStatement?.let { containsUnsupportedForBytecode(it) } ?: false)
|
||||||
|
}
|
||||||
|
is DoWhileStatement -> {
|
||||||
|
containsUnsupportedForBytecode(target.body) ||
|
||||||
|
containsUnsupportedForBytecode(target.condition) ||
|
||||||
|
(target.elseStatement?.let { containsUnsupportedForBytecode(it) } ?: false)
|
||||||
|
}
|
||||||
is BlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
is BlockStatement -> target.statements().any { containsUnsupportedForBytecode(it) }
|
||||||
is VarDeclStatement -> target.initializer?.let { containsUnsupportedForBytecode(it) } ?: false
|
is VarDeclStatement -> target.initializer?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
|
is BreakStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
|
is ContinueStatement -> false
|
||||||
|
is ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
|
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -430,6 +457,59 @@ class Compiler(
|
|||||||
val elseBody = stmt.elseBody?.let { unwrapBytecodeDeep(it) }
|
val elseBody = stmt.elseBody?.let { unwrapBytecodeDeep(it) }
|
||||||
IfStatement(cond, ifBody, elseBody, stmt.pos)
|
IfStatement(cond, ifBody, elseBody, stmt.pos)
|
||||||
}
|
}
|
||||||
|
is ForInStatement -> {
|
||||||
|
val source = unwrapBytecodeDeep(stmt.source)
|
||||||
|
val body = unwrapBytecodeDeep(stmt.body)
|
||||||
|
val elseBody = stmt.elseStatement?.let { unwrapBytecodeDeep(it) }
|
||||||
|
ForInStatement(
|
||||||
|
stmt.loopVarName,
|
||||||
|
source,
|
||||||
|
stmt.constRange,
|
||||||
|
body,
|
||||||
|
elseBody,
|
||||||
|
stmt.label,
|
||||||
|
stmt.canBreak,
|
||||||
|
stmt.loopSlotPlan,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is WhileStatement -> {
|
||||||
|
val condition = unwrapBytecodeDeep(stmt.condition)
|
||||||
|
val body = unwrapBytecodeDeep(stmt.body)
|
||||||
|
val elseBody = stmt.elseStatement?.let { unwrapBytecodeDeep(it) }
|
||||||
|
WhileStatement(
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
elseBody,
|
||||||
|
stmt.label,
|
||||||
|
stmt.canBreak,
|
||||||
|
stmt.loopSlotPlan,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is DoWhileStatement -> {
|
||||||
|
val body = unwrapBytecodeDeep(stmt.body)
|
||||||
|
val condition = unwrapBytecodeDeep(stmt.condition)
|
||||||
|
val elseBody = stmt.elseStatement?.let { unwrapBytecodeDeep(it) }
|
||||||
|
DoWhileStatement(
|
||||||
|
body,
|
||||||
|
condition,
|
||||||
|
elseBody,
|
||||||
|
stmt.label,
|
||||||
|
stmt.loopSlotPlan,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is BreakStatement -> {
|
||||||
|
val resultExpr = stmt.resultExpr?.let { unwrapBytecodeDeep(it) }
|
||||||
|
BreakStatement(stmt.label, resultExpr, stmt.pos)
|
||||||
|
}
|
||||||
|
is ContinueStatement -> ContinueStatement(stmt.label, stmt.pos)
|
||||||
|
is ReturnStatement -> {
|
||||||
|
val resultExpr = stmt.resultExpr?.let { unwrapBytecodeDeep(it) }
|
||||||
|
ReturnStatement(stmt.label, resultExpr, stmt.pos)
|
||||||
|
}
|
||||||
|
is ThrowStatement -> ThrowStatement(unwrapBytecodeDeep(stmt.throwExpr), stmt.pos)
|
||||||
else -> stmt
|
else -> stmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -885,8 +965,14 @@ class Compiler(
|
|||||||
slotPlanStack.add(paramSlotPlan)
|
slotPlanStack.add(paramSlotPlan)
|
||||||
val parsedBody = try {
|
val parsedBody = try {
|
||||||
inCodeContext(CodeContext.Function("<lambda>")) {
|
inCodeContext(CodeContext.Function("<lambda>")) {
|
||||||
withLocalNames(slotParamNames.toSet()) {
|
val returnLabels = label?.let { setOf(it) } ?: emptySet()
|
||||||
parseBlock(skipLeadingBrace = true)
|
returnLabelStack.addLast(returnLabels)
|
||||||
|
try {
|
||||||
|
withLocalNames(slotParamNames.toSet()) {
|
||||||
|
parseBlock(skipLeadingBrace = true)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
returnLabelStack.removeLast()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -2031,37 +2117,7 @@ class Compiler(
|
|||||||
|
|
||||||
private suspend fun parseThrowStatement(start: Pos): Statement {
|
private suspend fun parseThrowStatement(start: Pos): Statement {
|
||||||
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
val throwStatement = parseStatement() ?: throw ScriptError(cc.currentPos(), "throw object expected")
|
||||||
// Important: bind the created statement to the position of the `throw` keyword so that
|
return wrapBytecode(ThrowStatement(throwStatement, start))
|
||||||
// any raised error reports the correct source location.
|
|
||||||
val stmt = object : Statement() {
|
|
||||||
override val pos: Pos = start
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
var errorObject = throwStatement.execute(scope)
|
|
||||||
// Rebind error scope to the throw-site position so ScriptError.pos is accurate
|
|
||||||
val throwScope = scope.createChildScope(pos = start)
|
|
||||||
if (errorObject is ObjString) {
|
|
||||||
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
|
||||||
}
|
|
||||||
if (!errorObject.isInstanceOf(ObjException.Root)) {
|
|
||||||
throwScope.raiseError("this is not an exception object: $errorObject")
|
|
||||||
}
|
|
||||||
if (errorObject is ObjException) {
|
|
||||||
errorObject = ObjException(
|
|
||||||
errorObject.exceptionClass,
|
|
||||||
throwScope,
|
|
||||||
errorObject.message,
|
|
||||||
errorObject.extraData,
|
|
||||||
errorObject.useStackTrace
|
|
||||||
).apply { getStackTrace() }
|
|
||||||
throwScope.raiseError(errorObject)
|
|
||||||
} else {
|
|
||||||
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
|
|
||||||
throwScope.raiseError(errorObject, start, msg)
|
|
||||||
}
|
|
||||||
return ObjVoid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapBytecode(stmt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CatchBlockData(
|
private data class CatchBlockData(
|
||||||
@ -2692,7 +2748,11 @@ class Compiler(
|
|||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(loopSlotPlan)
|
||||||
val (canBreak, parsedBody) = try {
|
val (canBreak, parsedBody) = try {
|
||||||
cc.parseLoop {
|
cc.parseLoop {
|
||||||
parseStatement() ?: throw ScriptError(cc.currentPos(), "Bad do-while statement: expected body statement")
|
if (cc.current().type == Token.Type.LBRACE) {
|
||||||
|
parseLoopBlock()
|
||||||
|
} else {
|
||||||
|
parseStatement() ?: throw ScriptError(cc.currentPos(), "Bad do-while statement: expected body statement")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
@ -2722,36 +2782,8 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
val loopPlanSnapshot = slotPlanIndices(loopSlotPlan)
|
||||||
return object : Statement() {
|
return DoWhileStatement(body, condition, elseStatement, label, loopPlanSnapshot, body.pos)
|
||||||
override val pos: Pos = body.pos
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
var wasBroken = false
|
|
||||||
var result: Obj = ObjVoid
|
|
||||||
while (true) {
|
|
||||||
val doScope = scope.createChildScope().apply { skipScopeCreation = true }
|
|
||||||
try {
|
|
||||||
result = body.execute(doScope)
|
|
||||||
} catch (e: LoopBreakContinueException) {
|
|
||||||
if (e.label == label || e.label == null) {
|
|
||||||
if (!e.doContinue) {
|
|
||||||
result = e.result
|
|
||||||
wasBroken = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// for continue: just fall through to condition check below
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!condition.execute(doScope).toBool()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseWhileStatement(): Statement {
|
private suspend fun parseWhileStatement(): Statement {
|
||||||
@ -2765,7 +2797,7 @@ class Compiler(
|
|||||||
slotPlanStack.add(loopSlotPlan)
|
slotPlanStack.add(loopSlotPlan)
|
||||||
val (canBreak, parsedBody) = try {
|
val (canBreak, parsedBody) = try {
|
||||||
cc.parseLoop {
|
cc.parseLoop {
|
||||||
if (cc.current().type == Token.Type.LBRACE) parseBlock()
|
if (cc.current().type == Token.Type.LBRACE) parseLoopBlock()
|
||||||
else parseStatement() ?: throw ScriptError(start, "Bad while statement: expected statement")
|
else parseStatement() ?: throw ScriptError(start, "Bad while statement: expected statement")
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -2782,34 +2814,8 @@ class Compiler(
|
|||||||
cc.previous()
|
cc.previous()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
return object : Statement() {
|
val loopPlanSnapshot = slotPlanIndices(loopSlotPlan)
|
||||||
override val pos: Pos = body.pos
|
return WhileStatement(condition, body, elseStatement, label, canBreak, loopPlanSnapshot, body.pos)
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
var result: Obj = ObjVoid
|
|
||||||
var wasBroken = false
|
|
||||||
while (condition.execute(scope).toBool()) {
|
|
||||||
val loopScope = scope.createChildScope().apply { skipScopeCreation = true }
|
|
||||||
if (canBreak) {
|
|
||||||
try {
|
|
||||||
result = body.execute(loopScope)
|
|
||||||
} catch (lbe: LoopBreakContinueException) {
|
|
||||||
if (lbe.label == label || lbe.label == null) {
|
|
||||||
if (lbe.doContinue) continue
|
|
||||||
else {
|
|
||||||
result = lbe.result
|
|
||||||
wasBroken = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw lbe
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
result = body.execute(loopScope)
|
|
||||||
}
|
|
||||||
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseBreakStatement(start: Pos): Statement {
|
private suspend fun parseBreakStatement(start: Pos): Statement {
|
||||||
@ -2843,17 +2849,7 @@ class Compiler(
|
|||||||
|
|
||||||
cc.addBreak()
|
cc.addBreak()
|
||||||
|
|
||||||
return object : Statement() {
|
return BreakStatement(label, resultExpr, start)
|
||||||
override val pos: Pos = start
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
val returnValue = resultExpr?.execute(scope)// ?: ObjVoid
|
|
||||||
throw LoopBreakContinueException(
|
|
||||||
doContinue = false,
|
|
||||||
label = label,
|
|
||||||
result = returnValue ?: ObjVoid
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseContinueStatement(start: Pos): Statement {
|
private fun parseContinueStatement(start: Pos): Statement {
|
||||||
@ -2870,15 +2866,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
cc.addBreak()
|
cc.addBreak()
|
||||||
|
|
||||||
return object : Statement() {
|
return ContinueStatement(label, start)
|
||||||
override val pos: Pos = start
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
throw LoopBreakContinueException(
|
|
||||||
doContinue = true,
|
|
||||||
label = label,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun parseReturnStatement(start: Pos): Statement {
|
private suspend fun parseReturnStatement(start: Pos): Statement {
|
||||||
@ -2907,13 +2895,7 @@ class Compiler(
|
|||||||
parseExpression()
|
parseExpression()
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
return object : Statement() {
|
return ReturnStatement(label, resultExpr, start)
|
||||||
override val pos: Pos = start
|
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
|
||||||
val returnValue = resultExpr?.execute(scope) ?: ObjVoid
|
|
||||||
throw ReturnException(returnValue, label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureRparen(): Pos {
|
private fun ensureRparen(): Pos {
|
||||||
@ -3065,32 +3047,41 @@ class Compiler(
|
|||||||
localDeclCountStack.add(0)
|
localDeclCountStack.add(0)
|
||||||
slotPlanStack.add(paramSlotPlan)
|
slotPlanStack.add(paramSlotPlan)
|
||||||
val parsedFnStatements = try {
|
val parsedFnStatements = try {
|
||||||
if (actualExtern)
|
val returnLabels = buildSet {
|
||||||
object : Statement() {
|
add(name)
|
||||||
override val pos: Pos = start
|
outerLabel?.let { add(it) }
|
||||||
override suspend fun execute(scope: Scope): Obj {
|
}
|
||||||
scope.raiseError("extern function not provided: $name")
|
returnLabelStack.addLast(returnLabels)
|
||||||
}
|
try {
|
||||||
}
|
if (actualExtern)
|
||||||
else if (isAbstract || isDelegated) {
|
object : Statement() {
|
||||||
null
|
override val pos: Pos = start
|
||||||
} else
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
withLocalNames(paramNames) {
|
scope.raiseError("extern function not provided: $name")
|
||||||
val next = cc.peekNextNonWhitespace()
|
|
||||||
if (next.type == Token.Type.ASSIGN) {
|
|
||||||
cc.nextNonWhitespace() // consume '='
|
|
||||||
if (cc.peekNextNonWhitespace().value == "return")
|
|
||||||
throw ScriptError(cc.currentPos(), "return is not allowed in shorthand function")
|
|
||||||
val expr = parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected function body expression")
|
|
||||||
// Shorthand function returns the expression value
|
|
||||||
object : Statement() {
|
|
||||||
override val pos: Pos = expr.pos
|
|
||||||
override suspend fun execute(scope: Scope): Obj = expr.execute(scope)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
parseBlock()
|
|
||||||
}
|
}
|
||||||
}
|
else if (isAbstract || isDelegated) {
|
||||||
|
null
|
||||||
|
} else
|
||||||
|
withLocalNames(paramNames) {
|
||||||
|
val next = cc.peekNextNonWhitespace()
|
||||||
|
if (next.type == Token.Type.ASSIGN) {
|
||||||
|
cc.nextNonWhitespace() // consume '='
|
||||||
|
if (cc.peekNextNonWhitespace().value == "return")
|
||||||
|
throw ScriptError(cc.currentPos(), "return is not allowed in shorthand function")
|
||||||
|
val expr = parseExpression() ?: throw ScriptError(cc.currentPos(), "Expected function body expression")
|
||||||
|
// Shorthand function returns the expression value
|
||||||
|
object : Statement() {
|
||||||
|
override val pos: Pos = expr.pos
|
||||||
|
override suspend fun execute(scope: Scope): Obj = expr.execute(scope)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parseBlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
returnLabelStack.removeLast()
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
slotPlanStack.removeLast()
|
slotPlanStack.removeLast()
|
||||||
}
|
}
|
||||||
@ -3326,6 +3317,24 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun parseLoopBlock(): Statement {
|
||||||
|
val startPos = cc.currentPos()
|
||||||
|
val t = cc.next()
|
||||||
|
if (t.type != Token.Type.LBRACE)
|
||||||
|
throw ScriptError(t.pos, "Expected block body start: {")
|
||||||
|
val block = parseScript()
|
||||||
|
val stmt = BlockStatement(block, emptyMap(), startPos)
|
||||||
|
val wrapped = wrapBytecode(stmt)
|
||||||
|
return wrapped.also {
|
||||||
|
val t1 = cc.next()
|
||||||
|
if (t1.type != Token.Type.RBRACE)
|
||||||
|
throw ScriptError(t1.pos, "unbalanced braces: expected block body end: }")
|
||||||
|
val range = MiniRange(startPos, t1.pos)
|
||||||
|
lastParsedBlockRange = range
|
||||||
|
miniSink?.onBlock(MiniBlock(range))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun parseVarDeclaration(
|
private suspend fun parseVarDeclaration(
|
||||||
isMutable: Boolean,
|
isMutable: Boolean,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.bytecode
|
package net.sergeych.lyng.bytecode
|
||||||
|
|
||||||
|
import net.sergeych.lyng.Pos
|
||||||
import net.sergeych.lyng.Visibility
|
import net.sergeych.lyng.Visibility
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ sealed class BytecodeConst {
|
|||||||
data class IntVal(val value: Long) : BytecodeConst()
|
data class IntVal(val value: Long) : BytecodeConst()
|
||||||
data class RealVal(val value: Double) : BytecodeConst()
|
data class RealVal(val value: Double) : BytecodeConst()
|
||||||
data class StringVal(val value: String) : BytecodeConst()
|
data class StringVal(val value: String) : BytecodeConst()
|
||||||
|
data class PosVal(val pos: Pos) : BytecodeConst()
|
||||||
data class ObjRef(val value: Obj) : BytecodeConst()
|
data class ObjRef(val value: Obj) : BytecodeConst()
|
||||||
data class SlotPlan(val plan: Map<String, Int>) : BytecodeConst()
|
data class SlotPlan(val plan: Map<String, Int>) : BytecodeConst()
|
||||||
data class LocalDecl(
|
data class LocalDecl(
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import net.sergeych.lyng.Pos
|
|||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.Statement
|
import net.sergeych.lyng.Statement
|
||||||
import net.sergeych.lyng.obj.Obj
|
import net.sergeych.lyng.obj.Obj
|
||||||
|
import net.sergeych.lyng.obj.RangeRef
|
||||||
|
|
||||||
class BytecodeStatement private constructor(
|
class BytecodeStatement private constructor(
|
||||||
val original: Statement,
|
val original: Statement,
|
||||||
@ -34,12 +35,17 @@ class BytecodeStatement private constructor(
|
|||||||
internal fun bytecodeFunction(): CmdFunction = function
|
internal fun bytecodeFunction(): CmdFunction = function
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun wrap(statement: Statement, nameHint: String, allowLocalSlots: Boolean): Statement {
|
fun wrap(
|
||||||
|
statement: Statement,
|
||||||
|
nameHint: String,
|
||||||
|
allowLocalSlots: Boolean,
|
||||||
|
returnLabels: Set<String> = emptySet(),
|
||||||
|
): Statement {
|
||||||
if (statement is BytecodeStatement) return statement
|
if (statement is BytecodeStatement) return statement
|
||||||
val hasUnsupported = containsUnsupportedStatement(statement)
|
val hasUnsupported = containsUnsupportedStatement(statement)
|
||||||
if (hasUnsupported) return unwrapDeep(statement)
|
if (hasUnsupported) return unwrapDeep(statement)
|
||||||
val safeLocals = allowLocalSlots
|
val safeLocals = allowLocalSlots
|
||||||
val compiler = BytecodeCompiler(allowLocalSlots = safeLocals)
|
val compiler = BytecodeCompiler(allowLocalSlots = safeLocals, returnLabels = returnLabels)
|
||||||
val compiled = compiler.compileStatement(nameHint, statement)
|
val compiled = compiler.compileStatement(nameHint, statement)
|
||||||
val fn = compiled ?: run {
|
val fn = compiled ?: run {
|
||||||
val builder = CmdBuilder()
|
val builder = CmdBuilder()
|
||||||
@ -51,6 +57,7 @@ class BytecodeStatement private constructor(
|
|||||||
nameHint,
|
nameHint,
|
||||||
localCount = 1,
|
localCount = 1,
|
||||||
addrCount = 0,
|
addrCount = 0,
|
||||||
|
returnLabels = returnLabels,
|
||||||
localSlotNames = emptyArray(),
|
localSlotNames = emptyArray(),
|
||||||
localSlotMutables = BooleanArray(0),
|
localSlotMutables = BooleanArray(0),
|
||||||
localSlotDepths = IntArray(0)
|
localSlotDepths = IntArray(0)
|
||||||
@ -69,15 +76,35 @@ class BytecodeStatement private constructor(
|
|||||||
(target.elseBody?.let { containsUnsupportedStatement(it) } ?: false)
|
(target.elseBody?.let { containsUnsupportedStatement(it) } ?: false)
|
||||||
}
|
}
|
||||||
is net.sergeych.lyng.ForInStatement -> {
|
is net.sergeych.lyng.ForInStatement -> {
|
||||||
target.constRange == null || target.canBreak ||
|
val rangeSource = target.source
|
||||||
|
val rangeRef = (rangeSource as? net.sergeych.lyng.ExpressionStatement)?.ref as? RangeRef
|
||||||
|
val hasRange = target.constRange != null || rangeRef != null
|
||||||
|
!hasRange ||
|
||||||
containsUnsupportedStatement(target.source) ||
|
containsUnsupportedStatement(target.source) ||
|
||||||
containsUnsupportedStatement(target.body) ||
|
containsUnsupportedStatement(target.body) ||
|
||||||
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.WhileStatement -> {
|
||||||
|
containsUnsupportedStatement(target.condition) ||
|
||||||
|
containsUnsupportedStatement(target.body) ||
|
||||||
|
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.DoWhileStatement -> {
|
||||||
|
containsUnsupportedStatement(target.body) ||
|
||||||
|
containsUnsupportedStatement(target.condition) ||
|
||||||
|
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
||||||
|
}
|
||||||
is net.sergeych.lyng.BlockStatement ->
|
is net.sergeych.lyng.BlockStatement ->
|
||||||
target.statements().any { containsUnsupportedStatement(it) }
|
target.statements().any { containsUnsupportedStatement(it) }
|
||||||
is net.sergeych.lyng.VarDeclStatement ->
|
is net.sergeych.lyng.VarDeclStatement ->
|
||||||
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
|
target.initializer?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.BreakStatement ->
|
||||||
|
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.ContinueStatement -> false
|
||||||
|
is net.sergeych.lyng.ReturnStatement ->
|
||||||
|
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
||||||
|
is net.sergeych.lyng.ThrowStatement ->
|
||||||
|
containsUnsupportedStatement(target.throwExpr)
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,6 +153,39 @@ class BytecodeStatement private constructor(
|
|||||||
stmt.pos
|
stmt.pos
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is net.sergeych.lyng.WhileStatement -> {
|
||||||
|
net.sergeych.lyng.WhileStatement(
|
||||||
|
unwrapDeep(stmt.condition),
|
||||||
|
unwrapDeep(stmt.body),
|
||||||
|
stmt.elseStatement?.let { unwrapDeep(it) },
|
||||||
|
stmt.label,
|
||||||
|
stmt.canBreak,
|
||||||
|
stmt.loopSlotPlan,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.DoWhileStatement -> {
|
||||||
|
net.sergeych.lyng.DoWhileStatement(
|
||||||
|
unwrapDeep(stmt.body),
|
||||||
|
unwrapDeep(stmt.condition),
|
||||||
|
stmt.elseStatement?.let { unwrapDeep(it) },
|
||||||
|
stmt.label,
|
||||||
|
stmt.loopSlotPlan,
|
||||||
|
stmt.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.BreakStatement -> {
|
||||||
|
val resultExpr = stmt.resultExpr?.let { unwrapDeep(it) }
|
||||||
|
net.sergeych.lyng.BreakStatement(stmt.label, resultExpr, stmt.pos)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ContinueStatement ->
|
||||||
|
net.sergeych.lyng.ContinueStatement(stmt.label, stmt.pos)
|
||||||
|
is net.sergeych.lyng.ReturnStatement -> {
|
||||||
|
val resultExpr = stmt.resultExpr?.let { unwrapDeep(it) }
|
||||||
|
net.sergeych.lyng.ReturnStatement(stmt.label, resultExpr, stmt.pos)
|
||||||
|
}
|
||||||
|
is net.sergeych.lyng.ThrowStatement ->
|
||||||
|
net.sergeych.lyng.ThrowStatement(unwrapDeep(stmt.throwExpr), stmt.pos)
|
||||||
else -> stmt
|
else -> stmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,6 +60,7 @@ class CmdBuilder {
|
|||||||
name: String,
|
name: String,
|
||||||
localCount: Int,
|
localCount: Int,
|
||||||
addrCount: Int = 0,
|
addrCount: Int = 0,
|
||||||
|
returnLabels: Set<String> = emptySet(),
|
||||||
scopeSlotDepths: IntArray = IntArray(0),
|
scopeSlotDepths: IntArray = IntArray(0),
|
||||||
scopeSlotIndices: IntArray = IntArray(0),
|
scopeSlotIndices: IntArray = IntArray(0),
|
||||||
scopeSlotNames: Array<String?> = emptyArray(),
|
scopeSlotNames: Array<String?> = emptyArray(),
|
||||||
@ -100,6 +101,7 @@ class CmdBuilder {
|
|||||||
name = name,
|
name = name,
|
||||||
localCount = localCount,
|
localCount = localCount,
|
||||||
addrCount = addrCount,
|
addrCount = addrCount,
|
||||||
|
returnLabels = returnLabels,
|
||||||
scopeSlotCount = scopeSlotCount,
|
scopeSlotCount = scopeSlotCount,
|
||||||
scopeSlotDepths = scopeSlotDepths,
|
scopeSlotDepths = scopeSlotDepths,
|
||||||
scopeSlotIndices = scopeSlotIndices,
|
scopeSlotIndices = scopeSlotIndices,
|
||||||
@ -120,6 +122,8 @@ class CmdBuilder {
|
|||||||
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.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.RET_LABEL, Opcode.THROW ->
|
||||||
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.RESOLVE_SCOPE_SLOT ->
|
Opcode.RESOLVE_SCOPE_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
@ -205,6 +209,8 @@ class CmdBuilder {
|
|||||||
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
Opcode.CONST_BOOL -> CmdConstBool(operands[0], operands[1])
|
||||||
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
Opcode.CONST_NULL -> CmdConstNull(operands[0])
|
||||||
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
Opcode.BOX_OBJ -> CmdBoxObj(operands[0], operands[1])
|
||||||
|
Opcode.RET_LABEL -> CmdRetLabel(operands[0], operands[1])
|
||||||
|
Opcode.THROW -> CmdThrow(operands[0], operands[1])
|
||||||
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])
|
||||||
|
|||||||
@ -160,7 +160,9 @@ object CmdDisassembler {
|
|||||||
is CmdJmpIfTrue -> Opcode.JMP_IF_TRUE to intArrayOf(cmd.cond, cmd.target)
|
is CmdJmpIfTrue -> Opcode.JMP_IF_TRUE to intArrayOf(cmd.cond, cmd.target)
|
||||||
is CmdJmpIfFalse -> Opcode.JMP_IF_FALSE to intArrayOf(cmd.cond, cmd.target)
|
is CmdJmpIfFalse -> Opcode.JMP_IF_FALSE to intArrayOf(cmd.cond, cmd.target)
|
||||||
is CmdRet -> Opcode.RET to intArrayOf(cmd.slot)
|
is CmdRet -> Opcode.RET to intArrayOf(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 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)
|
||||||
@ -194,6 +196,8 @@ object CmdDisassembler {
|
|||||||
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.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.RET_LABEL, Opcode.THROW ->
|
||||||
|
listOf(OperandKind.CONST, OperandKind.SLOT)
|
||||||
Opcode.RESOLVE_SCOPE_SLOT ->
|
Opcode.RESOLVE_SCOPE_SLOT ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
|
|||||||
@ -20,6 +20,7 @@ data class CmdFunction(
|
|||||||
val name: String,
|
val name: String,
|
||||||
val localCount: Int,
|
val localCount: Int,
|
||||||
val addrCount: Int,
|
val addrCount: Int,
|
||||||
|
val returnLabels: Set<String>,
|
||||||
val scopeSlotCount: Int,
|
val scopeSlotCount: Int,
|
||||||
val scopeSlotDepths: IntArray,
|
val scopeSlotDepths: IntArray,
|
||||||
val scopeSlotIndices: IntArray,
|
val scopeSlotIndices: IntArray,
|
||||||
|
|||||||
@ -18,6 +18,9 @@ package net.sergeych.lyng.bytecode
|
|||||||
|
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.PerfFlags
|
import net.sergeych.lyng.PerfFlags
|
||||||
|
import net.sergeych.lyng.PerfStats
|
||||||
|
import net.sergeych.lyng.Pos
|
||||||
|
import net.sergeych.lyng.ReturnException
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.obj.*
|
import net.sergeych.lyng.obj.*
|
||||||
|
|
||||||
@ -49,7 +52,7 @@ class CmdNop : Cmd() {
|
|||||||
|
|
||||||
class CmdMoveObj(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdMoveObj(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(src))
|
frame.setObj(dst, frame.slotToObj(src))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,28 +660,28 @@ class CmdCmpNeqRealInt(internal val a: Int, internal val b: Int, internal val ds
|
|||||||
|
|
||||||
class CmdCmpEqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpEqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a) == frame.getObj(b))
|
frame.setBool(dst, frame.slotToObj(a) == frame.slotToObj(b))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpNeqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpNeqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a) != frame.getObj(b))
|
frame.setBool(dst, frame.slotToObj(a) != frame.slotToObj(b))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpRefEqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpRefEqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a) === frame.getObj(b))
|
frame.setBool(dst, frame.slotToObj(a) === frame.slotToObj(b))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpRefNeqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpRefNeqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a) !== frame.getObj(b))
|
frame.setBool(dst, frame.slotToObj(a) !== frame.slotToObj(b))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -706,63 +709,148 @@ class CmdOrBool(internal val a: Int, internal val b: Int, internal val dst: Int)
|
|||||||
|
|
||||||
class CmdCmpLtObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpLtObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(frame.scope, frame.getObj(b)) < 0)
|
frame.setBool(dst, frame.slotToObj(a).compareTo(frame.scope, frame.slotToObj(b)) < 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpLteObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpLteObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(frame.scope, frame.getObj(b)) <= 0)
|
frame.setBool(dst, frame.slotToObj(a).compareTo(frame.scope, frame.slotToObj(b)) <= 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpGtObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpGtObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(frame.scope, frame.getObj(b)) > 0)
|
frame.setBool(dst, frame.slotToObj(a).compareTo(frame.scope, frame.slotToObj(b)) > 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdCmpGteObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdCmpGteObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setBool(dst, frame.getObj(a).compareTo(frame.scope, frame.getObj(b)) >= 0)
|
frame.setBool(dst, frame.slotToObj(a).compareTo(frame.scope, frame.slotToObj(b)) >= 0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdAddObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdAddObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(a).plus(frame.scope, frame.getObj(b)))
|
val scopeSlotCount = frame.fn.scopeSlotCount
|
||||||
|
if (a >= scopeSlotCount && b >= scopeSlotCount) {
|
||||||
|
val la = a - scopeSlotCount
|
||||||
|
val lb = b - scopeSlotCount
|
||||||
|
val ta = frame.frame.getSlotTypeCode(la)
|
||||||
|
val tb = frame.frame.getSlotTypeCode(lb)
|
||||||
|
if (ta == SlotType.INT.code && tb == SlotType.INT.code) {
|
||||||
|
frame.setInt(dst, frame.frame.getInt(la) + frame.frame.getInt(lb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) {
|
||||||
|
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
|
||||||
|
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
|
||||||
|
frame.setReal(dst, av + bv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.setObj(dst, frame.slotToObj(a).plus(frame.scope, frame.slotToObj(b)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdSubObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdSubObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(a).minus(frame.scope, frame.getObj(b)))
|
val scopeSlotCount = frame.fn.scopeSlotCount
|
||||||
|
if (a >= scopeSlotCount && b >= scopeSlotCount) {
|
||||||
|
val la = a - scopeSlotCount
|
||||||
|
val lb = b - scopeSlotCount
|
||||||
|
val ta = frame.frame.getSlotTypeCode(la)
|
||||||
|
val tb = frame.frame.getSlotTypeCode(lb)
|
||||||
|
if (ta == SlotType.INT.code && tb == SlotType.INT.code) {
|
||||||
|
frame.setInt(dst, frame.frame.getInt(la) - frame.frame.getInt(lb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) {
|
||||||
|
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
|
||||||
|
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
|
||||||
|
frame.setReal(dst, av - bv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.setObj(dst, frame.slotToObj(a).minus(frame.scope, frame.slotToObj(b)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdMulObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdMulObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(a).mul(frame.scope, frame.getObj(b)))
|
val scopeSlotCount = frame.fn.scopeSlotCount
|
||||||
|
if (a >= scopeSlotCount && b >= scopeSlotCount) {
|
||||||
|
val la = a - scopeSlotCount
|
||||||
|
val lb = b - scopeSlotCount
|
||||||
|
val ta = frame.frame.getSlotTypeCode(la)
|
||||||
|
val tb = frame.frame.getSlotTypeCode(lb)
|
||||||
|
if (ta == SlotType.INT.code && tb == SlotType.INT.code) {
|
||||||
|
frame.setInt(dst, frame.frame.getInt(la) * frame.frame.getInt(lb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) {
|
||||||
|
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
|
||||||
|
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
|
||||||
|
frame.setReal(dst, av * bv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.setObj(dst, frame.slotToObj(a).mul(frame.scope, frame.slotToObj(b)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdDivObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdDivObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(a).div(frame.scope, frame.getObj(b)))
|
val scopeSlotCount = frame.fn.scopeSlotCount
|
||||||
|
if (a >= scopeSlotCount && b >= scopeSlotCount) {
|
||||||
|
val la = a - scopeSlotCount
|
||||||
|
val lb = b - scopeSlotCount
|
||||||
|
val ta = frame.frame.getSlotTypeCode(la)
|
||||||
|
val tb = frame.frame.getSlotTypeCode(lb)
|
||||||
|
if (ta == SlotType.INT.code && tb == SlotType.INT.code) {
|
||||||
|
frame.setInt(dst, frame.frame.getInt(la) / frame.frame.getInt(lb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) {
|
||||||
|
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
|
||||||
|
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
|
||||||
|
frame.setReal(dst, av / bv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.setObj(dst, frame.slotToObj(a).div(frame.scope, frame.slotToObj(b)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CmdModObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
class CmdModObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setObj(dst, frame.getObj(a).mod(frame.scope, frame.getObj(b)))
|
val scopeSlotCount = frame.fn.scopeSlotCount
|
||||||
|
if (a >= scopeSlotCount && b >= scopeSlotCount) {
|
||||||
|
val la = a - scopeSlotCount
|
||||||
|
val lb = b - scopeSlotCount
|
||||||
|
val ta = frame.frame.getSlotTypeCode(la)
|
||||||
|
val tb = frame.frame.getSlotTypeCode(lb)
|
||||||
|
if (ta == SlotType.INT.code && tb == SlotType.INT.code) {
|
||||||
|
frame.setInt(dst, frame.frame.getInt(la) % frame.frame.getInt(lb))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ta == SlotType.REAL.code || tb == SlotType.REAL.code) {
|
||||||
|
val av = if (ta == SlotType.REAL.code) frame.frame.getReal(la) else frame.frame.getInt(la).toDouble()
|
||||||
|
val bv = if (tb == SlotType.REAL.code) frame.frame.getReal(lb) else frame.frame.getInt(lb).toDouble()
|
||||||
|
frame.setReal(dst, av % bv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame.setObj(dst, frame.slotToObj(a).mod(frame.scope, frame.slotToObj(b)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -799,6 +887,20 @@ class CmdRet(internal val slot: Int) : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CmdRetLabel(internal val labelId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val labelConst = frame.fn.constants.getOrNull(labelId) as? BytecodeConst.StringVal
|
||||||
|
?: error("RET_LABEL expects StringVal at $labelId")
|
||||||
|
val value = frame.slotToObj(slot)
|
||||||
|
if (frame.fn.returnLabels.contains(labelConst.value)) {
|
||||||
|
frame.vm.result = value
|
||||||
|
} else {
|
||||||
|
throw ReturnException(value, labelConst.value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CmdRetVoid : Cmd() {
|
class CmdRetVoid : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.vm.result = ObjVoid
|
frame.vm.result = ObjVoid
|
||||||
@ -806,6 +908,15 @@ class CmdRetVoid : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CmdThrow(internal val posId: Int, internal val slot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val posConst = frame.fn.constants.getOrNull(posId) as? BytecodeConst.PosVal
|
||||||
|
?: error("THROW expects PosVal at $posId")
|
||||||
|
frame.throwObj(posConst.pos, frame.slotToObj(slot))
|
||||||
|
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
|
||||||
@ -963,10 +1074,29 @@ class CmdGetField(
|
|||||||
internal val fieldId: Int,
|
internal val fieldId: Int,
|
||||||
internal val dst: Int,
|
internal val dst: Int,
|
||||||
) : Cmd() {
|
) : Cmd() {
|
||||||
|
private var rKey: Long = 0L
|
||||||
|
private var rVer: Int = -1
|
||||||
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val receiver = frame.slotToObj(recvSlot)
|
val receiver = frame.slotToObj(recvSlot)
|
||||||
val nameConst = frame.fn.constants.getOrNull(fieldId) as? BytecodeConst.StringVal
|
val nameConst = frame.fn.constants.getOrNull(fieldId) as? BytecodeConst.StringVal
|
||||||
?: error("GET_FIELD expects StringVal at $fieldId")
|
?: error("GET_FIELD expects StringVal at $fieldId")
|
||||||
|
if (PerfFlags.FIELD_PIC) {
|
||||||
|
val (key, ver) = when (receiver) {
|
||||||
|
is ObjInstance -> receiver.objClass.classId to receiver.objClass.layoutVersion
|
||||||
|
is ObjClass -> receiver.classId to receiver.layoutVersion
|
||||||
|
else -> 0L to -1
|
||||||
|
}
|
||||||
|
if (key != 0L) {
|
||||||
|
if (key == rKey && ver == rVer) {
|
||||||
|
if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.fieldPicHit++
|
||||||
|
} else {
|
||||||
|
if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.fieldPicMiss++
|
||||||
|
rKey = key
|
||||||
|
rVer = ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
val result = receiver.readField(frame.scope, nameConst.value).value
|
val result = receiver.readField(frame.scope, nameConst.value).value
|
||||||
frame.storeObjResult(dst, result)
|
frame.storeObjResult(dst, result)
|
||||||
return
|
return
|
||||||
@ -978,10 +1108,29 @@ class CmdSetField(
|
|||||||
internal val fieldId: Int,
|
internal val fieldId: Int,
|
||||||
internal val valueSlot: Int,
|
internal val valueSlot: Int,
|
||||||
) : Cmd() {
|
) : Cmd() {
|
||||||
|
private var wKey: Long = 0L
|
||||||
|
private var wVer: Int = -1
|
||||||
|
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
val receiver = frame.slotToObj(recvSlot)
|
val receiver = frame.slotToObj(recvSlot)
|
||||||
val nameConst = frame.fn.constants.getOrNull(fieldId) as? BytecodeConst.StringVal
|
val nameConst = frame.fn.constants.getOrNull(fieldId) as? BytecodeConst.StringVal
|
||||||
?: error("SET_FIELD expects StringVal at $fieldId")
|
?: error("SET_FIELD expects StringVal at $fieldId")
|
||||||
|
if (PerfFlags.FIELD_PIC) {
|
||||||
|
val (key, ver) = when (receiver) {
|
||||||
|
is ObjInstance -> receiver.objClass.classId to receiver.objClass.layoutVersion
|
||||||
|
is ObjClass -> receiver.classId to receiver.layoutVersion
|
||||||
|
else -> 0L to -1
|
||||||
|
}
|
||||||
|
if (key != 0L) {
|
||||||
|
if (key == wKey && ver == wVer) {
|
||||||
|
if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.fieldPicSetHit++
|
||||||
|
} else {
|
||||||
|
if (PerfFlags.PIC_DEBUG_COUNTERS) PerfStats.fieldPicSetMiss++
|
||||||
|
wKey = key
|
||||||
|
wVer = ver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
receiver.writeField(frame.scope, nameConst.value, frame.slotToObj(valueSlot))
|
receiver.writeField(frame.scope, nameConst.value, frame.slotToObj(valueSlot))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1266,6 +1415,30 @@ class CmdFrame(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun throwObj(pos: Pos, value: Obj) {
|
||||||
|
var errorObject = value
|
||||||
|
val throwScope = scope.createChildScope(pos = pos)
|
||||||
|
if (errorObject is ObjString) {
|
||||||
|
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
||||||
|
}
|
||||||
|
if (!errorObject.isInstanceOf(ObjException.Root)) {
|
||||||
|
throwScope.raiseError("this is not an exception object: $errorObject")
|
||||||
|
}
|
||||||
|
if (errorObject is ObjException) {
|
||||||
|
errorObject = ObjException(
|
||||||
|
errorObject.exceptionClass,
|
||||||
|
throwScope,
|
||||||
|
errorObject.message,
|
||||||
|
errorObject.extraData,
|
||||||
|
errorObject.useStackTrace
|
||||||
|
).apply { getStackTrace() }
|
||||||
|
throwScope.raiseError(errorObject)
|
||||||
|
} else {
|
||||||
|
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
|
||||||
|
throwScope.raiseError(errorObject, pos, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun syncFrameToScope() {
|
fun syncFrameToScope() {
|
||||||
val names = fn.localSlotNames
|
val names = fn.localSlotNames
|
||||||
if (names.isEmpty()) return
|
if (names.isEmpty()) return
|
||||||
|
|||||||
@ -107,6 +107,7 @@ enum class Opcode(val code: Int) {
|
|||||||
JMP_IF_FALSE(0x82),
|
JMP_IF_FALSE(0x82),
|
||||||
RET(0x83),
|
RET(0x83),
|
||||||
RET_VOID(0x84),
|
RET_VOID(0x84),
|
||||||
|
RET_LABEL(0xBA),
|
||||||
PUSH_SCOPE(0x85),
|
PUSH_SCOPE(0x85),
|
||||||
POP_SCOPE(0x86),
|
POP_SCOPE(0x86),
|
||||||
PUSH_SLOT_PLAN(0x87),
|
PUSH_SLOT_PLAN(0x87),
|
||||||
@ -133,6 +134,7 @@ enum class Opcode(val code: Int) {
|
|||||||
STORE_REAL_ADDR(0xB7),
|
STORE_REAL_ADDR(0xB7),
|
||||||
LOAD_BOOL_ADDR(0xB8),
|
LOAD_BOOL_ADDR(0xB8),
|
||||||
STORE_BOOL_ADDR(0xB9),
|
STORE_BOOL_ADDR(0xB9),
|
||||||
|
THROW(0xBB),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -1336,6 +1336,9 @@ class IndexRef(
|
|||||||
private val index: ObjRef,
|
private val index: ObjRef,
|
||||||
private val isOptional: Boolean,
|
private val isOptional: Boolean,
|
||||||
) : ObjRef {
|
) : ObjRef {
|
||||||
|
internal val targetRef: ObjRef get() = target
|
||||||
|
internal val indexRef: ObjRef get() = index
|
||||||
|
internal val optionalRef: Boolean get() = isOptional
|
||||||
// Tiny 4-entry PIC for index reads (guarded implicitly by RVAL_FASTPATH); move-to-front on hits
|
// Tiny 4-entry PIC for index reads (guarded implicitly by RVAL_FASTPATH); move-to-front on hits
|
||||||
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey1: Long = 0L; private var rVer1: Int = -1; private var rGetter1: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
||||||
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
private var rKey2: Long = 0L; private var rVer2: Int = -1; private var rGetter2: (suspend (Obj, Scope, Obj) -> Obj)? = null
|
||||||
|
|||||||
@ -22,8 +22,10 @@ import net.sergeych.lyng.obj.ObjClass
|
|||||||
import net.sergeych.lyng.obj.ObjInt
|
import net.sergeych.lyng.obj.ObjInt
|
||||||
import net.sergeych.lyng.obj.ObjIterable
|
import net.sergeych.lyng.obj.ObjIterable
|
||||||
import net.sergeych.lyng.obj.ObjNull
|
import net.sergeych.lyng.obj.ObjNull
|
||||||
|
import net.sergeych.lyng.obj.ObjException
|
||||||
import net.sergeych.lyng.obj.ObjRange
|
import net.sergeych.lyng.obj.ObjRange
|
||||||
import net.sergeych.lyng.obj.ObjRecord
|
import net.sergeych.lyng.obj.ObjRecord
|
||||||
|
import net.sergeych.lyng.obj.ObjString
|
||||||
import net.sergeych.lyng.obj.ObjVoid
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
import net.sergeych.lyng.obj.toBool
|
import net.sergeych.lyng.obj.toBool
|
||||||
import net.sergeych.lyng.obj.toInt
|
import net.sergeych.lyng.obj.toInt
|
||||||
@ -297,6 +299,146 @@ class ForInStatement(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WhileStatement(
|
||||||
|
val condition: Statement,
|
||||||
|
val body: Statement,
|
||||||
|
val elseStatement: Statement?,
|
||||||
|
val label: String?,
|
||||||
|
val canBreak: Boolean,
|
||||||
|
val loopSlotPlan: Map<String, Int>,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
var result: Obj = ObjVoid
|
||||||
|
var wasBroken = false
|
||||||
|
while (condition.execute(scope).toBool()) {
|
||||||
|
val loopScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||||
|
if (canBreak) {
|
||||||
|
try {
|
||||||
|
result = body.execute(loopScope)
|
||||||
|
} catch (lbe: LoopBreakContinueException) {
|
||||||
|
if (lbe.label == label || lbe.label == null) {
|
||||||
|
if (lbe.doContinue) continue
|
||||||
|
result = lbe.result
|
||||||
|
wasBroken = true
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
throw lbe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = body.execute(loopScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoWhileStatement(
|
||||||
|
val body: Statement,
|
||||||
|
val condition: Statement,
|
||||||
|
val elseStatement: Statement?,
|
||||||
|
val label: String?,
|
||||||
|
val loopSlotPlan: Map<String, Int>,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
var wasBroken = false
|
||||||
|
var result: Obj = ObjVoid
|
||||||
|
while (true) {
|
||||||
|
val doScope = scope.createChildScope().apply { skipScopeCreation = true }
|
||||||
|
try {
|
||||||
|
result = body.execute(doScope)
|
||||||
|
} catch (e: LoopBreakContinueException) {
|
||||||
|
if (e.label == label || e.label == null) {
|
||||||
|
if (!e.doContinue) {
|
||||||
|
result = e.result
|
||||||
|
wasBroken = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// continue: fall through to condition check
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!condition.execute(doScope).toBool()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wasBroken) elseStatement?.let { s -> result = s.execute(scope) }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BreakStatement(
|
||||||
|
val label: String?,
|
||||||
|
val resultExpr: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val returnValue = resultExpr?.execute(scope)
|
||||||
|
throw LoopBreakContinueException(
|
||||||
|
doContinue = false,
|
||||||
|
label = label,
|
||||||
|
result = returnValue ?: ObjVoid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ContinueStatement(
|
||||||
|
val label: String?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
throw LoopBreakContinueException(
|
||||||
|
doContinue = true,
|
||||||
|
label = label,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReturnStatement(
|
||||||
|
val label: String?,
|
||||||
|
val resultExpr: Statement?,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
val returnValue = resultExpr?.execute(scope) ?: ObjVoid
|
||||||
|
throw ReturnException(returnValue, label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThrowStatement(
|
||||||
|
val throwExpr: Statement,
|
||||||
|
override val pos: Pos,
|
||||||
|
) : Statement() {
|
||||||
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
|
var errorObject = throwExpr.execute(scope)
|
||||||
|
val throwScope = scope.createChildScope(pos = pos)
|
||||||
|
if (errorObject is ObjString) {
|
||||||
|
errorObject = ObjException(throwScope, errorObject.value).apply { getStackTrace() }
|
||||||
|
}
|
||||||
|
if (!errorObject.isInstanceOf(ObjException.Root)) {
|
||||||
|
throwScope.raiseError("this is not an exception object: $errorObject")
|
||||||
|
}
|
||||||
|
if (errorObject is ObjException) {
|
||||||
|
errorObject = ObjException(
|
||||||
|
errorObject.exceptionClass,
|
||||||
|
throwScope,
|
||||||
|
errorObject.message,
|
||||||
|
errorObject.extraData,
|
||||||
|
errorObject.useStackTrace
|
||||||
|
).apply { getStackTrace() }
|
||||||
|
throwScope.raiseError(errorObject)
|
||||||
|
} else {
|
||||||
|
val msg = errorObject.invokeInstanceMethod(scope, "message").toString(scope).value
|
||||||
|
throwScope.raiseError(errorObject, pos, msg)
|
||||||
|
}
|
||||||
|
return ObjVoid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ToBoolStatement(
|
class ToBoolStatement(
|
||||||
val expr: Statement,
|
val expr: Statement,
|
||||||
override val pos: Pos,
|
override val pos: Pos,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ Changes
|
|||||||
- Added ForInStatement and ConstIntRange to keep for-loop structure explicit (no anonymous Statement).
|
- Added ForInStatement and ConstIntRange to keep for-loop structure explicit (no anonymous Statement).
|
||||||
- Added PUSH_SCOPE/POP_SCOPE opcodes with SlotPlan constants to create loop scopes in bytecode.
|
- Added PUSH_SCOPE/POP_SCOPE opcodes with SlotPlan constants to create loop scopes in bytecode.
|
||||||
- Bytecode compiler emits int-range for-in loops when const range is known and no break/continue.
|
- Bytecode compiler emits int-range for-in loops when const range is known and no break/continue.
|
||||||
|
- Temporary: CmdGetField/CmdSetField maintain lightweight PIC counters for regression tests; remove or guard under a flag once bytecode becomes the sole execution path.
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
- ./gradlew :lynglib:jvmTest
|
- ./gradlew :lynglib:jvmTest
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user