Fix bytecode bool conversion and object equality

This commit is contained in:
sergeych committed 2026-01-28 16:45:29 +03:00
1 parent 7b3d92beb9
commit 63bcb91504
6 files changed
+197 -81

No files matched your search

@@ -1182,9 +1182,6 @@ class BytecodeCompiler(
}
private fun compileCall(ref: CallRef): CompiledValue? {
if (ref.target is LocalVarRef || ref.target is FastLocalVarRef || ref.target is BoundLocalVarRef) {
return null
}
val fieldTarget = ref.target as? FieldRef
if (fieldTarget != null) {
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
@@ -1195,7 +1192,7 @@ class BytecodeCompiler(
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
builder.emit(Opcode.CALL_VIRTUAL, receiver.slot, methodId, args.base, encodedCount, dst)
return CompiledValue(dst, SlotType.UNKNOWN)
return CompiledValue(dst, SlotType.OBJ)
}
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
@@ -1222,7 +1219,7 @@ class BytecodeCompiler(
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
builder.emit(Opcode.CALL_SLOT, callee.slot, args.base, encodedCount, dst)
return CompiledValue(dst, SlotType.UNKNOWN)
return CompiledValue(dst, SlotType.OBJ)
}
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
@@ -1253,7 +1250,7 @@ class BytecodeCompiler(
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
builder.emit(Opcode.CALL_VIRTUAL, receiver.slot, methodId, args.base, encodedCount, dst)
return CompiledValue(dst, SlotType.UNKNOWN)
return CompiledValue(dst, SlotType.OBJ)
}
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
@@ -2186,6 +2183,24 @@ class BytecodeCompiler(
if (compiled != null) {
if (forceType == null) return compiled
if (compiled.type == forceType) return compiled
if (forceType == SlotType.BOOL) {
val converted = when (compiled.type) {
SlotType.INT -> {
val dst = allocSlot()
builder.emit(Opcode.INT_TO_BOOL, compiled.slot, dst)
updateSlotType(dst, SlotType.BOOL)
CompiledValue(dst, SlotType.BOOL)
}
SlotType.OBJ -> {
val dst = allocSlot()
builder.emit(Opcode.OBJ_TO_BOOL, compiled.slot, dst)
updateSlotType(dst, SlotType.BOOL)
CompiledValue(dst, SlotType.BOOL)
}
else -> null
}
if (converted != null) return converted
}
if (compiled.type == SlotType.UNKNOWN) {
compiled = null
}
@@ -22,6 +22,7 @@ import net.sergeych.lyng.PerfStats
import net.sergeych.lyng.Pos
import net.sergeych.lyng.ReturnException
import net.sergeych.lyng.Scope
import net.sergeych.lyng.Statement
import net.sergeych.lyng.obj.*
class CmdVm {
@@ -713,14 +714,18 @@ 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() {
override suspend fun perform(frame: CmdFrame) {
frame.setBool(dst, frame.slotToObj(a) == frame.slotToObj(b))
val left = frame.slotToObj(a)
val right = frame.slotToObj(b)
frame.setBool(dst, left.equals(frame.scope, right))
return
}
}
class CmdCmpNeqObj(internal val a: Int, internal val b: Int, internal val dst: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.setBool(dst, frame.slotToObj(a) != frame.slotToObj(b))
val left = frame.slotToObj(a)
val right = frame.slotToObj(b)
frame.setBool(dst, !left.equals(frame.scope, right))
return
}
}
@@ -1109,9 +1114,11 @@ class CmdCallSlot(
}
val callee = frame.slotToObj(calleeSlot)
val args = frame.buildArguments(argBase, argCount)
val result = if (PerfFlags.SCOPE_POOL) {
val canPool = PerfFlags.SCOPE_POOL && callee !is Statement
val result = if (canPool) {
frame.scope.withChildFrame(args) { child -> callee.callOn(child) }
} else {
// Pooling for Statement-based callables (lambdas) can still alter closure semantics; keep safe path for now.
callee.callOn(frame.scope.createChildScope(frame.scope.pos, args = args))
}
if (frame.fn.localSlotNames.isNotEmpty()) {
@@ -1588,15 +1588,16 @@ class CallRef(
internal val isOptionalInvoke: Boolean,
) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord {
val usePool = PerfFlags.SCOPE_POOL
val callee = target.evalValue(scope)
if (callee == ObjNull && isOptionalInvoke) return ObjNull.asReadonly
val callArgs = args.toArguments(scope, tailBlock)
val usePool = PerfFlags.SCOPE_POOL && callee !is Statement
val result: Obj = if (usePool) {
scope.withChildFrame(callArgs) { child ->
callee.callOn(child)
}
} else {
// Pooling for Statement callables (lambdas) can still perturb closure semantics; keep safe path for now.
callee.callOn(scope.createChildScope(scope.pos, callArgs))
}
return result.asReadonly