Compile this-slot method calls to bytecode

This commit is contained in:
Sergey Chernov 2026-01-28 22:28:31 +03:00
parent 951ce989a6
commit aebe0890d8
2 changed files with 66 additions and 5 deletions

View File

@ -176,8 +176,8 @@ class BytecodeCompiler(
}
is LocalVarRef -> compileNameLookup(ref.name)
is ValueFnRef -> compileEvalRef(ref)
is ListLiteralRef -> compileEvalRef(ref)
is ThisMethodSlotCallRef -> compileEvalRef(ref)
is ListLiteralRef -> compileListLiteral(ref)
is ThisMethodSlotCallRef -> compileThisMethodSlotCall(ref)
is StatementRef -> {
val constId = builder.addConst(BytecodeConst.StatementVal(ref.statement))
val slot = allocSlot()
@ -246,6 +246,29 @@ class BytecodeCompiler(
return CompiledValue(slot, SlotType.OBJ)
}
private fun compileListLiteral(ref: ListLiteralRef): CompiledValue? {
val entries = ref.entries()
val count = entries.size
val baseSlot = nextSlot
val entrySlots = IntArray(count) { allocSlot() }
val spreads = ArrayList<Boolean>(count)
for ((index, entry) in entries.withIndex()) {
val value = when (entry) {
is net.sergeych.lyng.ListEntry.Element ->
compileRefWithFallback(entry.ref, null, Pos.builtIn)
is net.sergeych.lyng.ListEntry.Spread ->
compileRefWithFallback(entry.ref, null, Pos.builtIn)
} ?: return null
emitMove(value, entrySlots[index])
spreads.add(entry is net.sergeych.lyng.ListEntry.Spread)
}
val planId = builder.addConst(BytecodeConst.ListLiteralPlan(spreads))
val dst = allocSlot()
builder.emit(Opcode.LIST_LITERAL, planId, baseSlot, count, dst)
updateSlotType(dst, SlotType.OBJ)
return CompiledValue(dst, SlotType.OBJ)
}
private fun compileUnary(ref: UnaryOpRef): CompiledValue? {
val a = compileRef(unaryOperand(ref)) ?: return null
val out = allocSlot()
@ -1484,6 +1507,37 @@ class BytecodeCompiler(
return CompiledValue(dst, SlotType.OBJ)
}
private fun compileThisMethodSlotCall(ref: ThisMethodSlotCallRef): CompiledValue? {
val receiver = compileNameLookup("this")
val methodId = builder.addConst(BytecodeConst.StringVal(ref.methodName()))
if (methodId > 0xFFFF) return null
val dst = allocSlot()
if (!ref.optionalInvoke()) {
val args = compileCallArgs(ref.arguments(), ref.hasTailBlock()) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
builder.emit(Opcode.CALL_VIRTUAL, receiver.slot, methodId, args.base, encodedCount, dst)
return CompiledValue(dst, SlotType.OBJ)
}
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
val cmpSlot = allocSlot()
builder.emit(Opcode.CMP_REF_EQ_OBJ, receiver.slot, nullSlot, cmpSlot)
val nullLabel = builder.label()
val endLabel = builder.label()
builder.emit(
Opcode.JMP_IF_TRUE,
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
)
val args = compileCallArgs(ref.arguments(), ref.hasTailBlock()) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
builder.emit(Opcode.CALL_VIRTUAL, receiver.slot, methodId, args.base, encodedCount, dst)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, dst)
builder.mark(endLabel)
return CompiledValue(dst, SlotType.OBJ)
}
private data class CallArgs(val base: Int, val count: Int, val planId: Int?)
private fun compileCallArgs(args: List<ParsedArgument>, tailBlock: Boolean): CallArgs? {

View File

@ -1865,6 +1865,11 @@ class ThisMethodSlotCallRef(
private val tailBlock: Boolean,
private val isOptional: Boolean
) : ObjRef {
internal fun methodName(): String = name
internal fun arguments(): List<ParsedArgument> = args
internal fun hasTailBlock(): Boolean = tailBlock
internal fun optionalInvoke(): Boolean = isOptional
override suspend fun get(scope: Scope): ObjRecord = evalValue(scope).asReadonly
override suspend fun evalValue(scope: Scope): Obj {
@ -2516,6 +2521,8 @@ class LocalSlotRef(
}
class ListLiteralRef(private val entries: List<ListEntry>) : ObjRef {
internal fun entries(): List<ListEntry> = entries
override fun forEachVariable(block: (String) -> Unit) {
for (e in entries) {
when (e) {
@ -2665,9 +2672,9 @@ class RangeRef(
/** Assignment if null op: target ?= value */
class AssignIfNullRef(
private val target: ObjRef,
private val value: ObjRef,
private val atPos: Pos,
internal val target: ObjRef,
internal val value: ObjRef,
internal val atPos: Pos,
) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord {
return evalValue(scope).asReadonly