Step 24: remove assign scope slot

This commit is contained in:
Sergey Chernov 2026-02-09 20:23:25 +03:00
parent 6aa23e8ef3
commit dab0b9f165
8 changed files with 71 additions and 87 deletions

View File

@ -81,6 +81,9 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
- [x] Add bytecode ops to bind/get/set delegated locals without scope storage. - [x] Add bytecode ops to bind/get/set delegated locals without scope storage.
- [x] Store delegated locals in frame slots and compile get/set/assign ops with new ops. - [x] Store delegated locals in frame slots and compile get/set/assign ops with new ops.
- [x] Preserve reflection facade by syncing delegated locals into scope only when needed. - [x] Preserve reflection facade by syncing delegated locals into scope only when needed.
- [x] Step 24: Remove `ASSIGN_SCOPE_SLOT` now that delegated locals are always frame-backed.
- [x] Force delegated locals into local slots (even module) and avoid scope-slot resolution.
- [x] Drop opcode/runtime support for `ASSIGN_SCOPE_SLOT`.
## Notes ## Notes

View File

@ -1967,7 +1967,7 @@ class Compiler(
is AssignRef -> { is AssignRef -> {
val target = ref.target as? LocalSlotRef val target = ref.target as? LocalSlotRef
if (target != null) { if (target != null) {
(target.isDelegated) || containsUnsupportedRef(ref.value) containsUnsupportedRef(ref.value)
} else { } else {
containsUnsupportedRef(ref.target) || containsUnsupportedRef(ref.value) containsUnsupportedRef(ref.target) || containsUnsupportedRef(ref.value)
} }

View File

@ -317,13 +317,7 @@ class BytecodeCompiler(
if (!allowLocalSlots) return null if (!allowLocalSlots) return null
if (ref.isDelegated) { if (ref.isDelegated) {
val mapped = resolveSlot(ref) ?: return null val mapped = resolveSlot(ref) ?: return null
if (mapped < scopeSlotCount) { if (mapped < scopeSlotCount) return null
val addrSlot = ensureScopeAddr(mapped)
val local = allocSlot()
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, local)
updateSlotType(local, SlotType.OBJ)
return CompiledValue(local, SlotType.OBJ)
}
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name)) val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
val local = allocSlot() val local = allocSlot()
builder.emit(Opcode.DELEGATED_GET_LOCAL, mapped, nameId, local) builder.emit(Opcode.DELEGATED_GET_LOCAL, mapped, nameId, local)
@ -1692,16 +1686,12 @@ class BytecodeCompiler(
if (localTarget.isDelegated) { if (localTarget.isDelegated) {
val slot = resolveSlot(localTarget) ?: return null val slot = resolveSlot(localTarget) ?: return null
val value = compileRef(assignValue(ref)) ?: return null val value = compileRef(assignValue(ref)) ?: return null
if (slot >= scopeSlotCount) { if (slot < scopeSlotCount) return null
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name)) val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, value.slot) builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, value.slot)
updateSlotType(slot, SlotType.OBJ) updateSlotType(slot, SlotType.OBJ)
return value return value
} }
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, value.slot)
updateSlotType(slot, SlotType.OBJ)
return value
}
val value = compileRef(assignValue(ref)) ?: return null val value = compileRef(assignValue(ref)) ?: return null
if (!localTarget.isMutable || localTarget.isDelegated) { if (!localTarget.isMutable || localTarget.isDelegated) {
val msgId = builder.addConst(BytecodeConst.StringVal("can't reassign val ${localTarget.name}")) val msgId = builder.addConst(BytecodeConst.StringVal("can't reassign val ${localTarget.name}"))
@ -2011,14 +2001,10 @@ class BytecodeCompiler(
if (!allowLocalSlots) return compileEvalRef(ref) if (!allowLocalSlots) return compileEvalRef(ref)
if (localTarget.isDelegated) { if (localTarget.isDelegated) {
val slot = resolveSlot(localTarget) ?: return null val slot = resolveSlot(localTarget) ?: return null
if (slot < scopeSlotCount) return null
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name)) val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
val current = allocSlot() val current = allocSlot()
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current) builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current)
} else {
val addrSlot = ensureScopeAddr(slot)
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
}
updateSlotType(current, SlotType.OBJ) updateSlotType(current, SlotType.OBJ)
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref) val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
val rhsObj = ensureObjSlot(rhs) val rhsObj = ensureObjSlot(rhs)
@ -2033,11 +2019,7 @@ class BytecodeCompiler(
val result = allocSlot() val result = allocSlot()
builder.emit(objOp, current, rhsObj.slot, result) builder.emit(objOp, current, rhsObj.slot, result)
updateSlotType(result, SlotType.OBJ) updateSlotType(result, SlotType.OBJ)
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result) builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result)
} else {
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
}
updateSlotType(slot, SlotType.OBJ) updateSlotType(slot, SlotType.OBJ)
return CompiledValue(result, SlotType.OBJ) return CompiledValue(result, SlotType.OBJ)
} }
@ -2326,14 +2308,10 @@ class BytecodeCompiler(
if (!allowLocalSlots || !target.isMutable) return null if (!allowLocalSlots || !target.isMutable) return null
if (target.isDelegated) { if (target.isDelegated) {
val slot = resolveSlot(target) ?: return null val slot = resolveSlot(target) ?: return null
if (slot < scopeSlotCount) return null
val nameId = builder.addConst(BytecodeConst.StringVal(target.name)) val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
val current = allocSlot() val current = allocSlot()
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current) builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current)
} else {
val addrSlot = ensureScopeAddr(slot)
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
}
val nullSlot = allocSlot() val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot) builder.emit(Opcode.CONST_NULL, nullSlot)
val cmpSlot = allocSlot() val cmpSlot = allocSlot()
@ -2347,11 +2325,7 @@ class BytecodeCompiler(
builder.emit(Opcode.MOVE_OBJ, current, resultSlot) builder.emit(Opcode.MOVE_OBJ, current, resultSlot)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel))) builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(assignLabel) builder.mark(assignLabel)
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, newValue.slot) builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, newValue.slot)
} else {
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, newValue.slot)
}
builder.emit(Opcode.MOVE_OBJ, newValue.slot, resultSlot) builder.emit(Opcode.MOVE_OBJ, newValue.slot, resultSlot)
builder.mark(endLabel) builder.mark(endLabel)
updateSlotType(resultSlot, SlotType.OBJ) updateSlotType(resultSlot, SlotType.OBJ)
@ -2840,14 +2814,10 @@ class BytecodeCompiler(
if (!target.isMutable) return null if (!target.isMutable) return null
if (target.isDelegated) { if (target.isDelegated) {
val slot = resolveSlot(target) ?: return null val slot = resolveSlot(target) ?: return null
if (slot < scopeSlotCount) return null
val nameId = builder.addConst(BytecodeConst.StringVal(target.name)) val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
val current = allocSlot() val current = allocSlot()
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current) builder.emit(Opcode.DELEGATED_GET_LOCAL, slot, nameId, current)
} else {
val addrSlot = ensureScopeAddr(slot)
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
}
updateSlotType(current, SlotType.OBJ) updateSlotType(current, SlotType.OBJ)
val oneSlot = allocSlot() val oneSlot = allocSlot()
val oneId = builder.addConst(BytecodeConst.ObjRef(ObjInt.One)) val oneId = builder.addConst(BytecodeConst.ObjRef(ObjInt.One))
@ -2857,11 +2827,7 @@ class BytecodeCompiler(
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
builder.emit(op, current, oneSlot, result) builder.emit(op, current, oneSlot, result)
updateSlotType(result, SlotType.OBJ) updateSlotType(result, SlotType.OBJ)
if (slot >= scopeSlotCount) {
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result) builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result)
} else {
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
}
updateSlotType(slot, SlotType.OBJ) updateSlotType(slot, SlotType.OBJ)
return if (wantResult && ref.isPost) { return if (wantResult && ref.isPost) {
CompiledValue(current, SlotType.OBJ) CompiledValue(current, SlotType.OBJ)
@ -4619,17 +4585,25 @@ class BytecodeCompiler(
val value = compileStatementValueOrFallback(stmt.initializer) ?: return null val value = compileStatementValueOrFallback(stmt.initializer) ?: return null
val slotIndex = stmt.slotIndex val slotIndex = stmt.slotIndex
val scopeId = stmt.scopeId ?: 0 val scopeId = stmt.scopeId ?: 0
val isModuleSlot = isModuleSlot(scopeId, stmt.name)
val localSlot = if (slotIndex != null) { val localSlot = if (slotIndex != null) {
val key = ScopeSlotKey(scopeId, slotIndex) val key = ScopeSlotKey(scopeId, slotIndex)
localSlotIndexByKey[key]?.let { scopeSlotCount + it } localSlotIndexByKey[key]?.let { scopeSlotCount + it }
} else { } else {
null null
} }
if (allowLocalSlots && !isModuleSlot && localSlot != null) { if (allowLocalSlots && localSlot != null) {
val nameId = builder.addConst(BytecodeConst.StringVal(stmt.name)) if (value.slot != localSlot) {
val accessId = builder.addConst(BytecodeConst.StringVal(if (stmt.isMutable) "Var" else "Val")) emitMove(value, localSlot)
builder.emit(Opcode.BIND_DELEGATE_LOCAL, value.slot, nameId, accessId, localSlot) }
val declId = builder.addConst(
BytecodeConst.DelegatedDecl(
stmt.name,
stmt.isMutable,
stmt.visibility,
stmt.isTransient
)
)
builder.emit(Opcode.DECL_DELEGATED, declId, localSlot)
updateSlotType(localSlot, SlotType.OBJ) updateSlotType(localSlot, SlotType.OBJ)
return CompiledValue(localSlot, SlotType.OBJ) return CompiledValue(localSlot, SlotType.OBJ)
} }
@ -6032,7 +6006,7 @@ class BytecodeCompiler(
private fun resolveSlot(ref: LocalSlotRef): Int? { private fun resolveSlot(ref: LocalSlotRef): Int? {
loopSlotOverrides[ref.name]?.let { return it } loopSlotOverrides[ref.name]?.let { return it }
val scopeId = refScopeId(ref) val scopeId = refScopeId(ref)
if (isModuleSlot(scopeId, ref.name)) { if (!ref.isDelegated && isModuleSlot(scopeId, ref.name)) {
val key = ScopeSlotKey(scopeId, refSlot(ref)) val key = ScopeSlotKey(scopeId, refSlot(ref))
scopeSlotMap[key]?.let { return it } scopeSlotMap[key]?.let { return it }
scopeSlotIndexByName[ref.name]?.let { return it } scopeSlotIndexByName[ref.name]?.let { return it }
@ -6050,6 +6024,13 @@ class BytecodeCompiler(
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
return scopeSlotMap[scopeKey] return scopeSlotMap[scopeKey]
} }
if (ref.isDelegated) {
val localKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
val localIndex = localSlotIndexByKey[localKey]
if (localIndex != null) return scopeSlotCount + localIndex
val nameIndex = localSlotIndexByName[ref.name]
if (nameIndex != null) return scopeSlotCount + nameIndex
}
if (forceScopeSlots) { if (forceScopeSlots) {
val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref)) val scopeKey = ScopeSlotKey(refScopeId(ref), refSlot(ref))
return scopeSlotMap[scopeKey] return scopeSlotMap[scopeKey]
@ -6236,8 +6217,7 @@ class BytecodeCompiler(
is DelegatedVarDeclStatement -> { is DelegatedVarDeclStatement -> {
val slotIndex = stmt.slotIndex val slotIndex = stmt.slotIndex
val scopeId = stmt.scopeId ?: 0 val scopeId = stmt.scopeId ?: 0
val isModuleSlot = isModuleSlot(scopeId, stmt.name) if (allowLocalSlots && slotIndex != null) {
if (allowLocalSlots && !forceScopeSlots && slotIndex != null && !isModuleSlot) {
val key = ScopeSlotKey(scopeId, slotIndex) val key = ScopeSlotKey(scopeId, slotIndex)
declaredLocalKeys.add(key) declaredLocalKeys.add(key)
if (!localSlotInfoMap.containsKey(key)) { if (!localSlotInfoMap.containsKey(key)) {
@ -6528,8 +6508,8 @@ class BytecodeCompiler(
} }
return return
} }
val shouldLocalize = !forceScopeSlots || intLoopVarNames.contains(ref.name) val shouldLocalize = ref.isDelegated || !forceScopeSlots || intLoopVarNames.contains(ref.name)
val isModuleSlot = isModuleSlot(scopeId, ref.name) val isModuleSlot = if (ref.isDelegated) false else isModuleSlot(scopeId, ref.name)
if (allowLocalSlots && shouldLocalize && !isModuleSlot) { if (allowLocalSlots && shouldLocalize && !isModuleSlot) {
if (!localSlotInfoMap.containsKey(key)) { if (!localSlotInfoMap.containsKey(key)) {
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated) localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated)
@ -6577,8 +6557,8 @@ class BytecodeCompiler(
scopeSlotNameMap[key] = target.name scopeSlotNameMap[key] = target.name
} }
} else { } else {
val shouldLocalize = !forceScopeSlots || intLoopVarNames.contains(target.name) val shouldLocalize = target.isDelegated || !forceScopeSlots || intLoopVarNames.contains(target.name)
val isModuleSlot = isModuleSlot(scopeId, target.name) val isModuleSlot = if (target.isDelegated) false else isModuleSlot(scopeId, target.name)
if (allowLocalSlots && shouldLocalize && !isModuleSlot) { if (allowLocalSlots && shouldLocalize && !isModuleSlot) {
if (!localSlotInfoMap.containsKey(key)) { if (!localSlotInfoMap.containsKey(key)) {
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated) localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated)

View File

@ -138,8 +138,6 @@ class CmdBuilder {
listOf(OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.RESOLVE_SCOPE_SLOT -> Opcode.RESOLVE_SCOPE_SLOT ->
listOf(OperandKind.SLOT, OperandKind.ADDR) listOf(OperandKind.SLOT, OperandKind.ADDR)
Opcode.ASSIGN_SCOPE_SLOT ->
listOf(OperandKind.SLOT, OperandKind.SLOT)
Opcode.DELEGATED_GET_LOCAL -> Opcode.DELEGATED_GET_LOCAL ->
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
Opcode.DELEGATED_SET_LOCAL -> Opcode.DELEGATED_SET_LOCAL ->
@ -265,7 +263,6 @@ class CmdBuilder {
Opcode.THROW -> CmdThrow(operands[0], operands[1]) Opcode.THROW -> CmdThrow(operands[0], operands[1])
Opcode.RETHROW_PENDING -> CmdRethrowPending() Opcode.RETHROW_PENDING -> CmdRethrowPending()
Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1]) Opcode.RESOLVE_SCOPE_SLOT -> CmdResolveScopeSlot(operands[0], operands[1])
Opcode.ASSIGN_SCOPE_SLOT -> CmdAssignScopeSlot(operands[0], operands[1])
Opcode.DELEGATED_GET_LOCAL -> CmdDelegatedGetLocal(operands[0], operands[1], operands[2]) Opcode.DELEGATED_GET_LOCAL -> CmdDelegatedGetLocal(operands[0], operands[1], operands[2])
Opcode.DELEGATED_SET_LOCAL -> CmdDelegatedSetLocal(operands[0], operands[1], operands[2]) Opcode.DELEGATED_SET_LOCAL -> CmdDelegatedSetLocal(operands[0], operands[1], operands[2])
Opcode.BIND_DELEGATE_LOCAL -> CmdBindDelegateLocal(operands[0], operands[1], operands[2], operands[3]) Opcode.BIND_DELEGATE_LOCAL -> CmdBindDelegateLocal(operands[0], operands[1], operands[2], operands[3])

View File

@ -84,7 +84,6 @@ object CmdDisassembler {
cmd.dst cmd.dst
) )
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 CmdAssignScopeSlot -> Opcode.ASSIGN_SCOPE_SLOT to intArrayOf(cmd.scopeSlot, cmd.valueSlot)
is CmdDelegatedGetLocal -> Opcode.DELEGATED_GET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.dst) is CmdDelegatedGetLocal -> Opcode.DELEGATED_GET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.dst)
is CmdDelegatedSetLocal -> Opcode.DELEGATED_SET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.valueSlot) is CmdDelegatedSetLocal -> Opcode.DELEGATED_SET_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.valueSlot)
is CmdBindDelegateLocal -> Opcode.BIND_DELEGATE_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.accessId, cmd.dst) is CmdBindDelegateLocal -> Opcode.BIND_DELEGATE_LOCAL to intArrayOf(cmd.delegateSlot, cmd.nameId, cmd.accessId, cmd.dst)
@ -247,8 +246,6 @@ object CmdDisassembler {
listOf(OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.RESOLVE_SCOPE_SLOT -> Opcode.RESOLVE_SCOPE_SLOT ->
listOf(OperandKind.SLOT, OperandKind.ADDR) listOf(OperandKind.SLOT, OperandKind.ADDR)
Opcode.ASSIGN_SCOPE_SLOT ->
listOf(OperandKind.SLOT, OperandKind.SLOT)
Opcode.DELEGATED_GET_LOCAL -> Opcode.DELEGATED_GET_LOCAL ->
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT) listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
Opcode.DELEGATED_SET_LOCAL -> Opcode.DELEGATED_SET_LOCAL ->

View File

@ -358,13 +358,6 @@ class CmdStoreBoolAddr(internal val src: Int, internal val addrSlot: Int) : Cmd(
} }
} }
class CmdAssignScopeSlot(internal val scopeSlot: Int, internal val valueSlot: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) {
frame.assignScopeSlot(scopeSlot, frame.slotToObj(valueSlot))
return
}
}
class CmdDelegatedGetLocal( class CmdDelegatedGetLocal(
internal val delegateSlot: Int, internal val delegateSlot: Int,
internal val nameId: Int, internal val nameId: Int,
@ -2192,16 +2185,6 @@ class CmdFrame(
} }
} }
suspend fun assignScopeSlot(scopeSlot: Int, value: Obj) {
ensureScope()
val target = scopeTarget(scopeSlot)
val index = ensureScopeSlot(target, scopeSlot)
val name = fn.scopeSlotNames.getOrNull(scopeSlot)
?: target.raiseSymbolNotFound("slot $scopeSlot")
val record = target.getSlotRecord(index)
target.assign(record, name, value)
}
suspend fun getInt(slot: Int): Long { suspend fun getInt(slot: Int): Long {
return if (slot < fn.scopeSlotCount) { return if (slot < fn.scopeSlotCount) {
getScopeSlotValue(slot).toLong() getScopeSlotValue(slot).toLong()

View File

@ -161,10 +161,9 @@ enum class Opcode(val code: Int) {
ITER_PUSH(0xBF), ITER_PUSH(0xBF),
ITER_POP(0xC0), ITER_POP(0xC0),
ITER_CANCEL(0xC1), ITER_CANCEL(0xC1),
ASSIGN_SCOPE_SLOT(0xC2), DELEGATED_GET_LOCAL(0xC2),
DELEGATED_GET_LOCAL(0xC3), DELEGATED_SET_LOCAL(0xC3),
DELEGATED_SET_LOCAL(0xC4), BIND_DELEGATE_LOCAL(0xC4),
BIND_DELEGATE_LOCAL(0xC5),
; ;
companion object { companion object {

View File

@ -18,10 +18,12 @@
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.Compiler import net.sergeych.lyng.Compiler
import net.sergeych.lyng.ExecutionError import net.sergeych.lyng.ExecutionError
import net.sergeych.lyng.Pos
import net.sergeych.lyng.Script import net.sergeych.lyng.Script
import net.sergeych.lyng.ScriptError import net.sergeych.lyng.ScriptError
import net.sergeych.lyng.Source import net.sergeych.lyng.Source
import net.sergeych.lyng.eval import net.sergeych.lyng.eval
import net.sergeych.lyng.toSource
import net.sergeych.lyng.obj.toInt import net.sergeych.lyng.obj.toInt
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -218,6 +220,29 @@ class BytecodeRecentOpsTest {
) )
} }
@Test
fun delegatedLocalDisasmUsesDelegateOps() = runTest {
val script = """
class BoxDelegate(var v) : Delegate {
override fun getValue(thisRef: Object, name: String): Object = v
override fun setValue(thisRef: Object, name: String, value: Object) { v = value }
}
fun calc() {
var x by BoxDelegate(1)
x += 2
x++
return x
}
""".trimIndent()
val compiled = Compiler.compile(script.toSource(), Script.defaultImportManager)
val scope = Script.defaultImportManager.newModuleAt(Pos.builtIn)
compiled.execute(scope)
val disasm = scope.disassembleSymbol("calc")
assertTrue(disasm.contains("DELEGATED_GET_LOCAL"), disasm)
assertTrue(disasm.contains("DELEGATED_SET_LOCAL"), disasm)
assertTrue(disasm.contains("DECL_DELEGATED"), disasm)
}
@Test @Test
fun unionMemberDispatchSubtype() = runTest { fun unionMemberDispatchSubtype() = runTest {
eval( eval(