Bytecode compile is/not is and contains

This commit is contained in:
Sergey Chernov 2026-01-28 18:55:41 +03:00
parent 250220a42f
commit 490faea2ba
6 changed files with 43 additions and 10 deletions

View File

@ -209,15 +209,8 @@ class BytecodeCompiler(
val rightValue = compileRefWithFallback(binaryRight(ref), null, refPos(ref)) ?: return null val rightValue = compileRefWithFallback(binaryRight(ref), null, refPos(ref)) ?: return null
val leftObj = ensureObjSlot(leftValue) val leftObj = ensureObjSlot(leftValue)
val rightObj = ensureObjSlot(rightValue) val rightObj = ensureObjSlot(rightValue)
val methodId = builder.addConst(BytecodeConst.StringVal("contains"))
if (methodId > 0xFFFF) return null
val argSlot = allocSlot()
builder.emit(Opcode.BOX_OBJ, leftObj.slot, argSlot)
updateSlotType(argSlot, SlotType.OBJ)
val callSlot = allocSlot()
builder.emit(Opcode.CALL_VIRTUAL, rightObj.slot, methodId, argSlot, 1, callSlot)
val boolSlot = allocSlot() val boolSlot = allocSlot()
builder.emit(Opcode.OBJ_TO_BOOL, callSlot, boolSlot) builder.emit(Opcode.CONTAINS_OBJ, rightObj.slot, leftObj.slot, boolSlot)
updateSlotType(boolSlot, SlotType.BOOL) updateSlotType(boolSlot, SlotType.BOOL)
if (op == BinOp.NOTIN) { if (op == BinOp.NOTIN) {
val outSlot = allocSlot() val outSlot = allocSlot()
@ -227,6 +220,22 @@ class BytecodeCompiler(
} }
return CompiledValue(boolSlot, SlotType.BOOL) return CompiledValue(boolSlot, SlotType.BOOL)
} }
if (op == BinOp.IS || op == BinOp.NOTIS) {
val objValue = compileRefWithFallback(binaryLeft(ref), null, refPos(ref)) ?: return null
val typeValue = compileRefWithFallback(binaryRight(ref), null, refPos(ref)) ?: return null
val objSlot = ensureObjSlot(objValue)
val typeSlot = ensureObjSlot(typeValue)
val checkSlot = allocSlot()
builder.emit(Opcode.CHECK_IS, objSlot.slot, typeSlot.slot, checkSlot)
updateSlotType(checkSlot, SlotType.BOOL)
if (op == BinOp.NOTIS) {
val outSlot = allocSlot()
builder.emit(Opcode.NOT_BOOL, checkSlot, outSlot)
updateSlotType(outSlot, SlotType.BOOL)
return CompiledValue(outSlot, SlotType.BOOL)
}
return CompiledValue(checkSlot, SlotType.BOOL)
}
val leftRef = binaryLeft(ref) val leftRef = binaryLeft(ref)
val rightRef = binaryRight(ref) val rightRef = binaryRight(ref)
var a = compileRef(leftRef) ?: return null var a = compileRef(leftRef) ?: return null

View File

@ -157,7 +157,7 @@ class CmdBuilder {
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT, Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ, Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ, Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ,
Opcode.AND_BOOL, Opcode.OR_BOOL -> Opcode.AND_BOOL, Opcode.OR_BOOL ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET -> Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
@ -348,6 +348,7 @@ class CmdBuilder {
Opcode.MUL_OBJ -> CmdMulObj(operands[0], operands[1], operands[2]) Opcode.MUL_OBJ -> CmdMulObj(operands[0], operands[1], operands[2])
Opcode.DIV_OBJ -> CmdDivObj(operands[0], operands[1], operands[2]) Opcode.DIV_OBJ -> CmdDivObj(operands[0], operands[1], operands[2])
Opcode.MOD_OBJ -> CmdModObj(operands[0], operands[1], operands[2]) Opcode.MOD_OBJ -> CmdModObj(operands[0], operands[1], operands[2])
Opcode.CONTAINS_OBJ -> CmdContainsObj(operands[0], operands[1], operands[2])
Opcode.JMP -> CmdJmp(operands[0]) Opcode.JMP -> CmdJmp(operands[0])
Opcode.JMP_IF_TRUE -> CmdJmpIfTrue(operands[0], operands[1]) Opcode.JMP_IF_TRUE -> CmdJmpIfTrue(operands[0], operands[1])
Opcode.JMP_IF_FALSE -> CmdJmpIfFalse(operands[0], operands[1]) Opcode.JMP_IF_FALSE -> CmdJmpIfFalse(operands[0], operands[1])

View File

@ -160,6 +160,7 @@ object CmdDisassembler {
is CmdMulObj -> Opcode.MUL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdMulObj -> Opcode.MUL_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdDivObj -> Opcode.DIV_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdDivObj -> Opcode.DIV_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdModObj -> Opcode.MOD_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst) is CmdModObj -> Opcode.MOD_OBJ to intArrayOf(cmd.a, cmd.b, cmd.dst)
is CmdContainsObj -> Opcode.CONTAINS_OBJ to intArrayOf(cmd.target, cmd.value, cmd.dst)
is CmdJmp -> Opcode.JMP to intArrayOf(cmd.target) is CmdJmp -> Opcode.JMP to intArrayOf(cmd.target)
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)
@ -235,7 +236,7 @@ object CmdDisassembler {
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT, Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ, Opcode.CMP_EQ_OBJ, Opcode.CMP_NEQ_OBJ, Opcode.CMP_REF_EQ_OBJ, Opcode.CMP_REF_NEQ_OBJ,
Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ, Opcode.CMP_LT_OBJ, Opcode.CMP_LTE_OBJ, Opcode.CMP_GT_OBJ, Opcode.CMP_GTE_OBJ,
Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.ADD_OBJ, Opcode.SUB_OBJ, Opcode.MUL_OBJ, Opcode.DIV_OBJ, Opcode.MOD_OBJ, Opcode.CONTAINS_OBJ,
Opcode.AND_BOOL, Opcode.OR_BOOL -> Opcode.AND_BOOL, Opcode.OR_BOOL ->
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET -> Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->

View File

@ -913,6 +913,13 @@ class CmdModObj(internal val a: Int, internal val b: Int, internal val dst: Int)
} }
} }
class CmdContainsObj(internal val target: Int, internal val value: Int, internal val dst: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.setBool(dst, frame.slotToObj(target).contains(frame.scope, frame.slotToObj(value)))
return
}
}
class CmdJmp(internal val target: Int) : Cmd() { class CmdJmp(internal val target: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) { override suspend fun perform(frame: CmdFrame) {
frame.ip = target frame.ip = target

View File

@ -105,6 +105,7 @@ enum class Opcode(val code: Int) {
MUL_OBJ(0x79), MUL_OBJ(0x79),
DIV_OBJ(0x7A), DIV_OBJ(0x7A),
MOD_OBJ(0x7B), MOD_OBJ(0x7B),
CONTAINS_OBJ(0x7C),
JMP(0x80), JMP(0x80),
JMP_IF_TRUE(0x81), JMP_IF_TRUE(0x81),

View File

@ -5215,6 +5215,20 @@ class ScriptTest {
assertEquals(ObjFalse, scope.eval("inList(5, [1,2,3])")) assertEquals(ObjFalse, scope.eval("inList(5, [1,2,3])"))
} }
@Test
fun testIsOperatorBytecode() = runTest {
val scope = Script.newScope()
scope.eval(
"""
fun isInt(x) { x is Int }
""".trimIndent()
)
val disasm = scope.disassembleSymbol("isInt")
assertFalse(disasm.contains("not a compiled body"))
assertEquals(ObjTrue, scope.eval("isInt(42)"))
assertEquals(ObjFalse, scope.eval("isInt(\"42\")"))
}
@Test @Test
fun testFilterBug() = runTest { fun testFilterBug() = runTest {
eval( eval(