Bytecode for iterable for-in loops
This commit is contained in:
parent
37a8831fd7
commit
8dfdbaa0a0
@ -422,8 +422,7 @@ class Compiler(
|
|||||||
(target.elseBody?.let { containsUnsupportedForBytecode(it) } ?: false)
|
(target.elseBody?.let { containsUnsupportedForBytecode(it) } ?: false)
|
||||||
}
|
}
|
||||||
is ForInStatement -> {
|
is ForInStatement -> {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -2079,8 +2078,8 @@ class Compiler(
|
|||||||
cc.currentPos(),
|
cc.currentPos(),
|
||||||
"when else block already defined"
|
"when else block already defined"
|
||||||
)
|
)
|
||||||
elseCase =
|
elseCase = parseStatement()?.let { unwrapBytecodeDeep(it) }
|
||||||
parseStatement() ?: throw ScriptError(
|
?: throw ScriptError(
|
||||||
cc.currentPos(),
|
cc.currentPos(),
|
||||||
"when else block expected"
|
"when else block expected"
|
||||||
)
|
)
|
||||||
@ -2102,7 +2101,8 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
// parsed conditions?
|
// parsed conditions?
|
||||||
if (!skipParseBody) {
|
if (!skipParseBody) {
|
||||||
val block = parseStatement() ?: throw ScriptError(cc.currentPos(), "when case block expected")
|
val block = parseStatement()?.let { unwrapBytecodeDeep(it) }
|
||||||
|
?: throw ScriptError(cc.currentPos(), "when case block expected")
|
||||||
for (c in currentCondition) cases += WhenCase(c, block)
|
for (c in currentCondition) cases += WhenCase(c, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1679,10 +1679,81 @@ class BytecodeCompiler(
|
|||||||
rangeRef = extractRangeFromLocal(stmt.source)
|
rangeRef = extractRangeFromLocal(stmt.source)
|
||||||
}
|
}
|
||||||
val typedRangeLocal = if (range == null && rangeRef == null) extractTypedRangeLocal(stmt.source) else null
|
val typedRangeLocal = if (range == null && rangeRef == null) extractTypedRangeLocal(stmt.source) else null
|
||||||
if (range == null && rangeRef == null && typedRangeLocal == null) return null
|
|
||||||
val loopLocalIndex = localSlotIndexByName[stmt.loopVarName] ?: return null
|
val loopLocalIndex = localSlotIndexByName[stmt.loopVarName] ?: return null
|
||||||
val loopSlotId = scopeSlotCount + loopLocalIndex
|
val loopSlotId = scopeSlotCount + loopLocalIndex
|
||||||
|
|
||||||
|
if (range == null && rangeRef == null && typedRangeLocal == null) {
|
||||||
|
val sourceValue = compileStatementValueOrFallback(stmt.source) ?: return null
|
||||||
|
val sourceObj = ensureObjSlot(sourceValue)
|
||||||
|
val typeId = builder.addConst(BytecodeConst.ObjRef(ObjIterable))
|
||||||
|
val typeSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_OBJ, typeId, typeSlot)
|
||||||
|
builder.emit(Opcode.ASSERT_IS, sourceObj.slot, typeSlot)
|
||||||
|
|
||||||
|
val iterSlot = allocSlot()
|
||||||
|
val iteratorId = builder.addConst(BytecodeConst.StringVal("iterator"))
|
||||||
|
builder.emit(Opcode.CALL_VIRTUAL, sourceObj.slot, iteratorId, 0, 0, iterSlot)
|
||||||
|
|
||||||
|
val breakFlagSlot = allocSlot()
|
||||||
|
val falseId = builder.addConst(BytecodeConst.Bool(false))
|
||||||
|
builder.emit(Opcode.CONST_BOOL, falseId, breakFlagSlot)
|
||||||
|
|
||||||
|
val resultSlot = allocSlot()
|
||||||
|
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
||||||
|
builder.emit(Opcode.CONST_OBJ, voidId, resultSlot)
|
||||||
|
|
||||||
|
val loopLabel = builder.label()
|
||||||
|
val continueLabel = builder.label()
|
||||||
|
val endLabel = builder.label()
|
||||||
|
builder.mark(loopLabel)
|
||||||
|
|
||||||
|
val hasNextSlot = allocSlot()
|
||||||
|
val hasNextId = builder.addConst(BytecodeConst.StringVal("hasNext"))
|
||||||
|
builder.emit(Opcode.CALL_VIRTUAL, iterSlot, hasNextId, 0, 0, hasNextSlot)
|
||||||
|
val condSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.OBJ_TO_BOOL, hasNextSlot, condSlot)
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_FALSE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(condSlot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||||
|
)
|
||||||
|
|
||||||
|
val nextSlot = allocSlot()
|
||||||
|
val nextId = builder.addConst(BytecodeConst.StringVal("next"))
|
||||||
|
builder.emit(Opcode.CALL_VIRTUAL, iterSlot, nextId, 0, 0, nextSlot)
|
||||||
|
val nextObj = ensureObjSlot(CompiledValue(nextSlot, SlotType.UNKNOWN))
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, nextObj.slot, loopSlotId)
|
||||||
|
updateSlotType(loopSlotId, SlotType.OBJ)
|
||||||
|
updateSlotTypeByName(stmt.loopVarName, SlotType.OBJ)
|
||||||
|
|
||||||
|
loopStack.addLast(
|
||||||
|
LoopContext(stmt.label, endLabel, continueLabel, breakFlagSlot, if (wantResult) resultSlot else null)
|
||||||
|
)
|
||||||
|
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
|
||||||
|
loopStack.removeLast()
|
||||||
|
if (wantResult) {
|
||||||
|
val bodyObj = ensureObjSlot(bodyValue)
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, bodyObj.slot, resultSlot)
|
||||||
|
}
|
||||||
|
builder.mark(continueLabel)
|
||||||
|
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel)))
|
||||||
|
|
||||||
|
builder.mark(endLabel)
|
||||||
|
if (stmt.elseStatement != null) {
|
||||||
|
val afterElse = builder.label()
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_TRUE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(breakFlagSlot), CmdBuilder.Operand.LabelRef(afterElse))
|
||||||
|
)
|
||||||
|
val elseValue = compileStatementValueOrFallback(stmt.elseStatement, wantResult) ?: return null
|
||||||
|
if (wantResult) {
|
||||||
|
val elseObj = ensureObjSlot(elseValue)
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, elseObj.slot, resultSlot)
|
||||||
|
}
|
||||||
|
builder.mark(afterElse)
|
||||||
|
}
|
||||||
|
return resultSlot
|
||||||
|
}
|
||||||
|
|
||||||
val iSlot = allocSlot()
|
val iSlot = allocSlot()
|
||||||
val endSlot = allocSlot()
|
val endSlot = allocSlot()
|
||||||
if (range != null) {
|
if (range != null) {
|
||||||
|
|||||||
@ -81,14 +81,7 @@ 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 -> {
|
||||||
val rangeSource = target.source
|
val unsupported = containsUnsupportedStatement(target.source) ||
|
||||||
val rangeRef = (rangeSource as? net.sergeych.lyng.ExpressionStatement)?.ref as? RangeRef
|
|
||||||
val sourceRef = (rangeSource as? net.sergeych.lyng.ExpressionStatement)?.ref
|
|
||||||
val hasRange = target.constRange != null ||
|
|
||||||
rangeRef != null ||
|
|
||||||
(sourceRef is net.sergeych.lyng.obj.LocalSlotRef)
|
|
||||||
val unsupported = !hasRange ||
|
|
||||||
containsUnsupportedStatement(target.source) ||
|
|
||||||
containsUnsupportedStatement(target.body) ||
|
containsUnsupportedStatement(target.body) ||
|
||||||
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
(target.elseStatement?.let { containsUnsupportedStatement(it) } ?: false)
|
||||||
unsupported
|
unsupported
|
||||||
|
|||||||
@ -120,8 +120,12 @@ class CmdBuilder {
|
|||||||
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 -> 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.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT ->
|
Opcode.OBJ_TO_BOOL,
|
||||||
|
Opcode.NEG_INT, Opcode.NEG_REAL, Opcode.NOT_BOOL, Opcode.INV_INT,
|
||||||
|
Opcode.ASSERT_IS ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.CHECK_IS ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.RANGE_INT_BOUNDS ->
|
Opcode.RANGE_INT_BOUNDS ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.RET_LABEL, Opcode.THROW ->
|
Opcode.RET_LABEL, Opcode.THROW ->
|
||||||
@ -211,7 +215,10 @@ 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.OBJ_TO_BOOL -> CmdObjToBool(operands[0], operands[1])
|
||||||
Opcode.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3])
|
Opcode.RANGE_INT_BOUNDS -> CmdRangeIntBounds(operands[0], operands[1], operands[2], operands[3])
|
||||||
|
Opcode.CHECK_IS -> CmdCheckIs(operands[0], operands[1], operands[2])
|
||||||
|
Opcode.ASSERT_IS -> CmdAssertIs(operands[0], operands[1])
|
||||||
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.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
|
||||||
|
|||||||
@ -68,6 +68,9 @@ object CmdDisassembler {
|
|||||||
is CmdConstBool -> Opcode.CONST_BOOL to intArrayOf(cmd.constId, cmd.dst)
|
is CmdConstBool -> Opcode.CONST_BOOL to intArrayOf(cmd.constId, cmd.dst)
|
||||||
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
is CmdConstNull -> Opcode.CONST_NULL to intArrayOf(cmd.dst)
|
||||||
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
is CmdBoxObj -> Opcode.BOX_OBJ to intArrayOf(cmd.src, cmd.dst)
|
||||||
|
is CmdObjToBool -> Opcode.OBJ_TO_BOOL to intArrayOf(cmd.src, cmd.dst)
|
||||||
|
is CmdCheckIs -> Opcode.CHECK_IS to intArrayOf(cmd.objSlot, cmd.typeSlot, cmd.dst)
|
||||||
|
is CmdAssertIs -> Opcode.ASSERT_IS to intArrayOf(cmd.objSlot, cmd.typeSlot)
|
||||||
is CmdRangeIntBounds -> Opcode.RANGE_INT_BOUNDS to intArrayOf(cmd.src, cmd.startSlot, cmd.endSlot, cmd.okSlot)
|
is CmdRangeIntBounds -> Opcode.RANGE_INT_BOUNDS to intArrayOf(cmd.src, cmd.startSlot, cmd.endSlot, cmd.okSlot)
|
||||||
is CmdResolveScopeSlot -> Opcode.RESOLVE_SCOPE_SLOT to intArrayOf(cmd.scopeSlot, cmd.addrSlot)
|
is CmdResolveScopeSlot -> Opcode.RESOLVE_SCOPE_SLOT to intArrayOf(cmd.scopeSlot, cmd.addrSlot)
|
||||||
is CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
is CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||||
@ -197,6 +200,10 @@ 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.OBJ_TO_BOOL, Opcode.ASSERT_IS ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||||
|
Opcode.CHECK_IS ->
|
||||||
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.RANGE_INT_BOUNDS ->
|
Opcode.RANGE_INT_BOUNDS ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.RET_LABEL, Opcode.THROW ->
|
Opcode.RET_LABEL, Opcode.THROW ->
|
||||||
|
|||||||
@ -154,6 +154,37 @@ class CmdBoxObj(internal val src: Int, internal val dst: Int) : Cmd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CmdObjToBool(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
frame.setBool(dst, frame.slotToObj(src).toBool())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdCheckIs(internal val objSlot: Int, internal val typeSlot: Int, internal val dst: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val obj = frame.slotToObj(objSlot)
|
||||||
|
val typeObj = frame.slotToObj(typeSlot)
|
||||||
|
val clazz = typeObj as? ObjClass
|
||||||
|
frame.setBool(dst, clazz != null && obj.isInstanceOf(clazz))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd() {
|
||||||
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
|
val obj = frame.slotToObj(objSlot)
|
||||||
|
val typeObj = frame.slotToObj(typeSlot)
|
||||||
|
val clazz = typeObj as? ObjClass ?: frame.scope.raiseClassCastError(
|
||||||
|
"${typeObj.inspect(frame.scope)} is not the class instance"
|
||||||
|
)
|
||||||
|
if (!obj.isInstanceOf(clazz)) {
|
||||||
|
frame.scope.raiseClassCastError("expected ${clazz.className}, got ${obj.objClass.className}")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CmdRangeIntBounds(
|
class CmdRangeIntBounds(
|
||||||
internal val src: Int,
|
internal val src: Int,
|
||||||
internal val startSlot: Int,
|
internal val startSlot: Int,
|
||||||
|
|||||||
@ -34,6 +34,9 @@ enum class Opcode(val code: Int) {
|
|||||||
REAL_TO_INT(0x11),
|
REAL_TO_INT(0x11),
|
||||||
BOOL_TO_INT(0x12),
|
BOOL_TO_INT(0x12),
|
||||||
INT_TO_BOOL(0x13),
|
INT_TO_BOOL(0x13),
|
||||||
|
OBJ_TO_BOOL(0x14),
|
||||||
|
CHECK_IS(0x15),
|
||||||
|
ASSERT_IS(0x16),
|
||||||
|
|
||||||
ADD_INT(0x20),
|
ADD_INT(0x20),
|
||||||
SUB_INT(0x21),
|
SUB_INT(0x21),
|
||||||
|
|||||||
@ -5044,4 +5044,62 @@ class ScriptTest {
|
|||||||
assertEquals( [1], t(1) )
|
assertEquals( [1], t(1) )
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testForInIterableDisasm() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
scope.eval("""
|
||||||
|
fun type(x) {
|
||||||
|
when(x) {
|
||||||
|
"42", 42 -> "answer to the great question"
|
||||||
|
is Real, is Int -> "number"
|
||||||
|
is String -> {
|
||||||
|
for( d in x ) {
|
||||||
|
if( d !in '0'..'9' )
|
||||||
|
break "unknown"
|
||||||
|
}
|
||||||
|
else "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
println("[DEBUG_LOG] type disasm:\n${scope.disassembleSymbol("type")}")
|
||||||
|
val r1 = scope.eval("""type("12%")""")
|
||||||
|
val r2 = scope.eval("""type("153")""")
|
||||||
|
println("[DEBUG_LOG] type(\"12%\")=${r1.inspect(scope)}")
|
||||||
|
println("[DEBUG_LOG] type(\"153\")=${r2.inspect(scope)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testForInIterableBytecode() = runTest {
|
||||||
|
val result = eval("""
|
||||||
|
fun sumAll(x) {
|
||||||
|
var s = 0
|
||||||
|
for (i in x) s += i
|
||||||
|
s
|
||||||
|
}
|
||||||
|
sumAll([1,2,3]) + sumAll(0..3)
|
||||||
|
""".trimIndent())
|
||||||
|
assertEquals(ObjInt(12), result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testForInIterableUnknownTypeDisasm() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
scope.eval("""
|
||||||
|
fun countAll(x) {
|
||||||
|
var c = 0
|
||||||
|
for (i in x) c++
|
||||||
|
c
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
val disasm = scope.disassembleSymbol("countAll")
|
||||||
|
println("[DEBUG_LOG] countAll disasm:\n$disasm")
|
||||||
|
assertFalse(disasm.contains("not a compiled body"))
|
||||||
|
assertFalse(disasm.contains("EVAL_FALLBACK"))
|
||||||
|
val r1 = scope.eval("countAll([1,2,3])")
|
||||||
|
val r2 = scope.eval("countAll(0..3)")
|
||||||
|
assertEquals(ObjInt(3), r1)
|
||||||
|
assertEquals(ObjInt(4), r2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user