diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index 20ac529..491143c 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -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(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, tailBlock: Boolean): CallArgs? { diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt index fd2e795..f593315 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/obj/ObjRef.kt @@ -1865,6 +1865,11 @@ class ThisMethodSlotCallRef( private val tailBlock: Boolean, private val isOptional: Boolean ) : ObjRef { + internal fun methodName(): String = name + internal fun arguments(): List = 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) : ObjRef { + internal fun entries(): List = 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