Step 23: frame-based delegated locals
This commit is contained in:
parent
c035b4c34c
commit
6aa23e8ef3
@ -77,6 +77,10 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
||||
- [x] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
||||
- [x] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
||||
- [x] Add JVM tests that use delegated locals inside bytecode-compiled functions.
|
||||
- [x] Step 23: Refactor delegated locals to keep delegate objects in frame slots.
|
||||
- [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] Preserve reflection facade by syncing delegated locals into scope only when needed.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@ -7985,12 +7985,17 @@ class Compiler(
|
||||
) {
|
||||
if (isDelegate) {
|
||||
val initExpr = initialExpression ?: throw ScriptError(start, "Delegate must be initialized")
|
||||
val slotPlan = slotPlanStack.lastOrNull()
|
||||
val slotIndex = slotPlan?.slots?.get(name)?.index
|
||||
val scopeId = slotPlan?.id
|
||||
return DelegatedVarDeclStatement(
|
||||
name,
|
||||
isMutable,
|
||||
visibility,
|
||||
initExpr,
|
||||
isTransient,
|
||||
slotIndex,
|
||||
scopeId,
|
||||
start
|
||||
)
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ class DelegatedVarDeclStatement(
|
||||
val visibility: Visibility,
|
||||
val initializer: Statement,
|
||||
val isTransient: Boolean,
|
||||
val slotIndex: Int?,
|
||||
val scopeId: Int?,
|
||||
private val startPos: Pos,
|
||||
) : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
|
||||
@ -48,13 +48,14 @@ class BytecodeCompiler(
|
||||
private val scopeSlotIndexByName = LinkedHashMap<String, Int>()
|
||||
private val pendingScopeNameRefs = LinkedHashSet<String>()
|
||||
private val addrSlotByScopeSlot = LinkedHashMap<Int, Int>()
|
||||
private data class LocalSlotInfo(val name: String, val isMutable: Boolean)
|
||||
private data class LocalSlotInfo(val name: String, val isMutable: Boolean, val isDelegated: Boolean)
|
||||
private val localSlotInfoMap = LinkedHashMap<ScopeSlotKey, LocalSlotInfo>()
|
||||
private val localSlotIndexByKey = LinkedHashMap<ScopeSlotKey, Int>()
|
||||
private val localSlotIndexByName = LinkedHashMap<String, Int>()
|
||||
private val loopSlotOverrides = LinkedHashMap<String, Int>()
|
||||
private var localSlotNames = emptyArray<String?>()
|
||||
private var localSlotMutables = BooleanArray(0)
|
||||
private var localSlotDelegated = BooleanArray(0)
|
||||
private val declaredLocalKeys = LinkedHashSet<ScopeSlotKey>()
|
||||
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
|
||||
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||
@ -98,7 +99,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is BlockStatement -> compileBlock(name, stmt)
|
||||
@ -117,7 +119,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is DestructuringVarDeclStatement -> {
|
||||
@ -133,7 +136,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is net.sergeych.lyng.ThrowStatement -> compileThrowStatement(name, stmt)
|
||||
@ -151,7 +155,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is net.sergeych.lyng.ClassDeclStatement -> {
|
||||
@ -167,7 +172,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is net.sergeych.lyng.FunctionDeclStatement -> {
|
||||
@ -183,7 +189,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is net.sergeych.lyng.EnumDeclStatement -> {
|
||||
@ -199,7 +206,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
is net.sergeych.lyng.NopStatement -> {
|
||||
@ -216,7 +224,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
@ -235,7 +244,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -256,7 +266,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -274,7 +285,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -312,8 +324,11 @@ class BytecodeCompiler(
|
||||
updateSlotType(local, SlotType.OBJ)
|
||||
return CompiledValue(local, SlotType.OBJ)
|
||||
}
|
||||
updateSlotType(mapped, SlotType.OBJ)
|
||||
return CompiledValue(mapped, SlotType.OBJ)
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||
val local = allocSlot()
|
||||
builder.emit(Opcode.DELEGATED_GET_LOCAL, mapped, nameId, local)
|
||||
updateSlotType(local, SlotType.OBJ)
|
||||
return CompiledValue(local, SlotType.OBJ)
|
||||
}
|
||||
if (ref.name.isEmpty()) return null
|
||||
if (ref.captureOwnerScopeId == null && refScopeId(ref) == 0) {
|
||||
@ -1676,8 +1691,13 @@ class BytecodeCompiler(
|
||||
if (!allowLocalSlots) return null
|
||||
if (localTarget.isDelegated) {
|
||||
val slot = resolveSlot(localTarget) ?: return null
|
||||
if (slot >= scopeSlotCount) return null
|
||||
val value = compileRef(assignValue(ref)) ?: return null
|
||||
if (slot >= scopeSlotCount) {
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
|
||||
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, value.slot)
|
||||
updateSlotType(slot, SlotType.OBJ)
|
||||
return value
|
||||
}
|
||||
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, value.slot)
|
||||
updateSlotType(slot, SlotType.OBJ)
|
||||
return value
|
||||
@ -1991,10 +2011,14 @@ class BytecodeCompiler(
|
||||
if (!allowLocalSlots) return compileEvalRef(ref)
|
||||
if (localTarget.isDelegated) {
|
||||
val slot = resolveSlot(localTarget) ?: return null
|
||||
if (slot >= scopeSlotCount) return null
|
||||
val addrSlot = ensureScopeAddr(slot)
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(localTarget.name))
|
||||
val current = allocSlot()
|
||||
if (slot >= scopeSlotCount) {
|
||||
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)
|
||||
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||
val rhsObj = ensureObjSlot(rhs)
|
||||
@ -2009,7 +2033,11 @@ class BytecodeCompiler(
|
||||
val result = allocSlot()
|
||||
builder.emit(objOp, current, rhsObj.slot, result)
|
||||
updateSlotType(result, SlotType.OBJ)
|
||||
if (slot >= scopeSlotCount) {
|
||||
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result)
|
||||
} else {
|
||||
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
|
||||
}
|
||||
updateSlotType(slot, SlotType.OBJ)
|
||||
return CompiledValue(result, SlotType.OBJ)
|
||||
}
|
||||
@ -2298,10 +2326,14 @@ class BytecodeCompiler(
|
||||
if (!allowLocalSlots || !target.isMutable) return null
|
||||
if (target.isDelegated) {
|
||||
val slot = resolveSlot(target) ?: return null
|
||||
if (slot >= scopeSlotCount) return null
|
||||
val addrSlot = ensureScopeAddr(slot)
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
|
||||
val current = allocSlot()
|
||||
if (slot >= scopeSlotCount) {
|
||||
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()
|
||||
builder.emit(Opcode.CONST_NULL, nullSlot)
|
||||
val cmpSlot = allocSlot()
|
||||
@ -2315,7 +2347,11 @@ class BytecodeCompiler(
|
||||
builder.emit(Opcode.MOVE_OBJ, current, resultSlot)
|
||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||
builder.mark(assignLabel)
|
||||
if (slot >= scopeSlotCount) {
|
||||
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.mark(endLabel)
|
||||
updateSlotType(resultSlot, SlotType.OBJ)
|
||||
@ -2804,10 +2840,14 @@ class BytecodeCompiler(
|
||||
if (!target.isMutable) return null
|
||||
if (target.isDelegated) {
|
||||
val slot = resolveSlot(target) ?: return null
|
||||
if (slot >= scopeSlotCount) return null
|
||||
val addrSlot = ensureScopeAddr(slot)
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
|
||||
val current = allocSlot()
|
||||
if (slot >= scopeSlotCount) {
|
||||
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)
|
||||
val oneSlot = allocSlot()
|
||||
val oneId = builder.addConst(BytecodeConst.ObjRef(ObjInt.One))
|
||||
@ -2817,7 +2857,11 @@ class BytecodeCompiler(
|
||||
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
|
||||
builder.emit(op, current, oneSlot, result)
|
||||
updateSlotType(result, SlotType.OBJ)
|
||||
if (slot >= scopeSlotCount) {
|
||||
builder.emit(Opcode.DELEGATED_SET_LOCAL, slot, nameId, result)
|
||||
} else {
|
||||
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
|
||||
}
|
||||
updateSlotType(slot, SlotType.OBJ)
|
||||
return if (wantResult && ref.isPost) {
|
||||
CompiledValue(current, SlotType.OBJ)
|
||||
@ -3930,7 +3974,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -3947,7 +3992,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -3965,7 +4011,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -3983,7 +4030,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -4004,7 +4052,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -4021,7 +4070,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -4431,7 +4481,8 @@ class BytecodeCompiler(
|
||||
scopeSlotNames,
|
||||
scopeSlotIsModule,
|
||||
localSlotNames,
|
||||
localSlotMutables
|
||||
localSlotMutables,
|
||||
localSlotDelegated
|
||||
)
|
||||
}
|
||||
|
||||
@ -4566,6 +4617,22 @@ class BytecodeCompiler(
|
||||
|
||||
private fun emitDelegatedVarDecl(stmt: DelegatedVarDeclStatement): CompiledValue? {
|
||||
val value = compileStatementValueOrFallback(stmt.initializer) ?: return null
|
||||
val slotIndex = stmt.slotIndex
|
||||
val scopeId = stmt.scopeId ?: 0
|
||||
val isModuleSlot = isModuleSlot(scopeId, stmt.name)
|
||||
val localSlot = if (slotIndex != null) {
|
||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||
localSlotIndexByKey[key]?.let { scopeSlotCount + it }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (allowLocalSlots && !isModuleSlot && localSlot != null) {
|
||||
val nameId = builder.addConst(BytecodeConst.StringVal(stmt.name))
|
||||
val accessId = builder.addConst(BytecodeConst.StringVal(if (stmt.isMutable) "Var" else "Val"))
|
||||
builder.emit(Opcode.BIND_DELEGATE_LOCAL, value.slot, nameId, accessId, localSlot)
|
||||
updateSlotType(localSlot, SlotType.OBJ)
|
||||
return CompiledValue(localSlot, SlotType.OBJ)
|
||||
}
|
||||
val declId = builder.addConst(
|
||||
BytecodeConst.DelegatedDecl(
|
||||
stmt.name,
|
||||
@ -6026,6 +6093,7 @@ class BytecodeCompiler(
|
||||
pendingScopeNameRefs.clear()
|
||||
localSlotNames = emptyArray()
|
||||
localSlotMutables = BooleanArray(0)
|
||||
localSlotDelegated = BooleanArray(0)
|
||||
declaredLocalKeys.clear()
|
||||
localRangeRefs.clear()
|
||||
intLoopVarNames.clear()
|
||||
@ -6072,6 +6140,7 @@ class BytecodeCompiler(
|
||||
if (allowLocalSlots && localSlotInfoMap.isNotEmpty()) {
|
||||
val names = ArrayList<String?>(localSlotInfoMap.size)
|
||||
val mutables = BooleanArray(localSlotInfoMap.size)
|
||||
val delegated = BooleanArray(localSlotInfoMap.size)
|
||||
var index = 0
|
||||
for ((key, info) in localSlotInfoMap) {
|
||||
localSlotIndexByKey[key] = index
|
||||
@ -6080,10 +6149,12 @@ class BytecodeCompiler(
|
||||
}
|
||||
names.add(info.name)
|
||||
mutables[index] = info.isMutable
|
||||
delegated[index] = info.isDelegated
|
||||
index += 1
|
||||
}
|
||||
localSlotNames = names.toTypedArray()
|
||||
localSlotMutables = mutables
|
||||
localSlotDelegated = delegated
|
||||
}
|
||||
if (scopeSlotCount > 0) {
|
||||
for ((key, index) in scopeSlotMap) {
|
||||
@ -6144,7 +6215,7 @@ class BytecodeCompiler(
|
||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||
declaredLocalKeys.add(key)
|
||||
if (!localSlotInfoMap.containsKey(key)) {
|
||||
localSlotInfoMap[key] = LocalSlotInfo(stmt.name, stmt.isMutable)
|
||||
localSlotInfoMap[key] = LocalSlotInfo(stmt.name, stmt.isMutable, isDelegated = false)
|
||||
}
|
||||
if (!stmt.isMutable) {
|
||||
extractDeclaredRange(stmt.initializer)?.let { range ->
|
||||
@ -6163,6 +6234,24 @@ class BytecodeCompiler(
|
||||
stmt.initializer?.let { collectScopeSlots(it) }
|
||||
}
|
||||
is DelegatedVarDeclStatement -> {
|
||||
val slotIndex = stmt.slotIndex
|
||||
val scopeId = stmt.scopeId ?: 0
|
||||
val isModuleSlot = isModuleSlot(scopeId, stmt.name)
|
||||
if (allowLocalSlots && !forceScopeSlots && slotIndex != null && !isModuleSlot) {
|
||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||
declaredLocalKeys.add(key)
|
||||
if (!localSlotInfoMap.containsKey(key)) {
|
||||
localSlotInfoMap[key] = LocalSlotInfo(stmt.name, stmt.isMutable, isDelegated = true)
|
||||
}
|
||||
} else if (slotIndex != null) {
|
||||
val key = ScopeSlotKey(scopeId, slotIndex)
|
||||
if (!scopeSlotMap.containsKey(key)) {
|
||||
scopeSlotMap[key] = scopeSlotMap.size
|
||||
}
|
||||
if (!scopeSlotNameMap.containsKey(key)) {
|
||||
scopeSlotNameMap[key] = stmt.name
|
||||
}
|
||||
}
|
||||
collectScopeSlots(stmt.initializer)
|
||||
}
|
||||
is IfStatement -> {
|
||||
@ -6441,9 +6530,9 @@ class BytecodeCompiler(
|
||||
}
|
||||
val shouldLocalize = !forceScopeSlots || intLoopVarNames.contains(ref.name)
|
||||
val isModuleSlot = isModuleSlot(scopeId, ref.name)
|
||||
if (allowLocalSlots && !ref.isDelegated && shouldLocalize && !isModuleSlot) {
|
||||
if (allowLocalSlots && shouldLocalize && !isModuleSlot) {
|
||||
if (!localSlotInfoMap.containsKey(key)) {
|
||||
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable)
|
||||
localSlotInfoMap[key] = LocalSlotInfo(ref.name, ref.isMutable, ref.isDelegated)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -6490,9 +6579,9 @@ class BytecodeCompiler(
|
||||
} else {
|
||||
val shouldLocalize = !forceScopeSlots || intLoopVarNames.contains(target.name)
|
||||
val isModuleSlot = isModuleSlot(scopeId, target.name)
|
||||
if (allowLocalSlots && !target.isDelegated && shouldLocalize && !isModuleSlot) {
|
||||
if (allowLocalSlots && shouldLocalize && !isModuleSlot) {
|
||||
if (!localSlotInfoMap.containsKey(key)) {
|
||||
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable)
|
||||
localSlotInfoMap[key] = LocalSlotInfo(target.name, target.isMutable, target.isDelegated)
|
||||
}
|
||||
} else {
|
||||
if (!scopeSlotMap.containsKey(key)) {
|
||||
|
||||
@ -68,7 +68,8 @@ class CmdBuilder {
|
||||
scopeSlotNames: Array<String?> = emptyArray(),
|
||||
scopeSlotIsModule: BooleanArray = BooleanArray(0),
|
||||
localSlotNames: Array<String?> = emptyArray(),
|
||||
localSlotMutables: BooleanArray = BooleanArray(0)
|
||||
localSlotMutables: BooleanArray = BooleanArray(0),
|
||||
localSlotDelegated: BooleanArray = BooleanArray(0)
|
||||
): CmdFunction {
|
||||
val scopeSlotCount = scopeSlotIndices.size
|
||||
require(scopeSlotNames.isEmpty() || scopeSlotNames.size == scopeSlotCount) {
|
||||
@ -78,6 +79,7 @@ class CmdBuilder {
|
||||
"scope slot module mapping size mismatch"
|
||||
}
|
||||
require(localSlotNames.size == localSlotMutables.size) { "local slot metadata size mismatch" }
|
||||
require(localSlotNames.size == localSlotDelegated.size) { "local slot delegation size mismatch" }
|
||||
val labelIps = mutableMapOf<Label, Int>()
|
||||
for ((label, idx) in labelPositions) {
|
||||
labelIps[label] = idx
|
||||
@ -111,6 +113,7 @@ class CmdBuilder {
|
||||
scopeSlotIsModule = if (scopeSlotIsModule.isEmpty()) BooleanArray(scopeSlotCount) else scopeSlotIsModule,
|
||||
localSlotNames = localSlotNames,
|
||||
localSlotMutables = localSlotMutables,
|
||||
localSlotDelegated = localSlotDelegated,
|
||||
constants = constPool.toList(),
|
||||
cmds = cmds.toTypedArray(),
|
||||
posByIp = posByInstr.toTypedArray()
|
||||
@ -137,6 +140,12 @@ class CmdBuilder {
|
||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||
Opcode.ASSIGN_SCOPE_SLOT ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.DELEGATED_GET_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.DELEGATED_SET_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.BIND_DELEGATE_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||
@ -257,6 +266,9 @@ class CmdBuilder {
|
||||
Opcode.RETHROW_PENDING -> CmdRethrowPending()
|
||||
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_SET_LOCAL -> CmdDelegatedSetLocal(operands[0], operands[1], operands[2])
|
||||
Opcode.BIND_DELEGATE_LOCAL -> CmdBindDelegateLocal(operands[0], operands[1], operands[2], operands[3])
|
||||
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
||||
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
||||
Opcode.LOAD_INT_ADDR -> CmdLoadIntAddr(operands[0], operands[1])
|
||||
|
||||
@ -85,6 +85,9 @@ object CmdDisassembler {
|
||||
)
|
||||
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 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 CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||
is CmdStoreObjAddr -> Opcode.STORE_OBJ_ADDR to intArrayOf(cmd.src, cmd.addrSlot)
|
||||
is CmdLoadIntAddr -> Opcode.LOAD_INT_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||
@ -246,6 +249,12 @@ object CmdDisassembler {
|
||||
listOf(OperandKind.SLOT, OperandKind.ADDR)
|
||||
Opcode.ASSIGN_SCOPE_SLOT ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.DELEGATED_GET_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.DELEGATED_SET_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.BIND_DELEGATE_LOCAL ->
|
||||
listOf(OperandKind.SLOT, OperandKind.CONST, OperandKind.CONST, OperandKind.SLOT)
|
||||
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||
|
||||
@ -28,6 +28,7 @@ data class CmdFunction(
|
||||
val scopeSlotIsModule: BooleanArray,
|
||||
val localSlotNames: Array<String?>,
|
||||
val localSlotMutables: BooleanArray,
|
||||
val localSlotDelegated: BooleanArray,
|
||||
val constants: List<BytecodeConst>,
|
||||
val cmds: Array<Cmd>,
|
||||
val posByIp: Array<net.sergeych.lyng.Pos?>,
|
||||
@ -37,6 +38,7 @@ data class CmdFunction(
|
||||
require(scopeSlotNames.size == scopeSlotCount) { "scopeSlotNames size mismatch" }
|
||||
require(scopeSlotIsModule.size == scopeSlotCount) { "scopeSlotIsModule size mismatch" }
|
||||
require(localSlotNames.size == localSlotMutables.size) { "localSlot metadata size mismatch" }
|
||||
require(localSlotNames.size == localSlotDelegated.size) { "localSlot delegation size mismatch" }
|
||||
require(localSlotNames.size <= localCount) { "localSlotNames exceed localCount" }
|
||||
require(addrCount >= 0) { "addrCount must be non-negative" }
|
||||
if (posByIp.isNotEmpty()) {
|
||||
|
||||
@ -365,6 +365,67 @@ class CmdAssignScopeSlot(internal val scopeSlot: Int, internal val valueSlot: In
|
||||
}
|
||||
}
|
||||
|
||||
class CmdDelegatedGetLocal(
|
||||
internal val delegateSlot: Int,
|
||||
internal val nameId: Int,
|
||||
internal val dst: Int,
|
||||
) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val nameConst = frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal
|
||||
?: error("DELEGATED_GET_LOCAL expects StringVal at $nameId")
|
||||
val delegate = frame.slotToObj(delegateSlot)
|
||||
val scope = frame.ensureScope()
|
||||
val rec = ObjRecord(ObjNull, isMutable = false, type = ObjRecord.Type.Delegated)
|
||||
rec.delegate = delegate
|
||||
val resolved = ObjVoid.resolveRecord(scope, rec, nameConst.value, null)
|
||||
frame.storeObjResult(dst, resolved.value)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
class CmdDelegatedSetLocal(
|
||||
internal val delegateSlot: Int,
|
||||
internal val nameId: Int,
|
||||
internal val valueSlot: Int,
|
||||
) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val nameConst = frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal
|
||||
?: error("DELEGATED_SET_LOCAL expects StringVal at $nameId")
|
||||
val delegate = frame.slotToObj(delegateSlot)
|
||||
val scope = frame.ensureScope()
|
||||
val value = frame.slotToObj(valueSlot)
|
||||
delegate.invokeInstanceMethod(scope, "setValue", Arguments(ObjNull, ObjString(nameConst.value), value))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
class CmdBindDelegateLocal(
|
||||
internal val delegateSlot: Int,
|
||||
internal val nameId: Int,
|
||||
internal val accessId: Int,
|
||||
internal val dst: Int,
|
||||
) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val nameConst = frame.fn.constants.getOrNull(nameId) as? BytecodeConst.StringVal
|
||||
?: error("BIND_DELEGATE_LOCAL expects StringVal at $nameId")
|
||||
val accessConst = frame.fn.constants.getOrNull(accessId) as? BytecodeConst.StringVal
|
||||
?: error("BIND_DELEGATE_LOCAL expects StringVal at $accessId")
|
||||
val delegate = frame.slotToObj(delegateSlot)
|
||||
val scope = frame.ensureScope()
|
||||
val bound = try {
|
||||
delegate.invokeInstanceMethod(
|
||||
scope,
|
||||
"bind",
|
||||
Arguments(ObjString(nameConst.value), ObjString(accessConst.value), ObjNull)
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
delegate
|
||||
}
|
||||
frame.storeObjResult(dst, bound)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
class CmdIntToReal(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
frame.setReal(dst, frame.getReal(src))
|
||||
@ -2274,7 +2335,7 @@ class CmdFrame(
|
||||
}
|
||||
val local = slot - fn.scopeSlotCount
|
||||
val localName = fn.localSlotNames.getOrNull(local)
|
||||
if (localName != null) {
|
||||
if (localName != null && fn.localSlotDelegated.getOrNull(local) != true) {
|
||||
val rec = scope.getLocalRecordDirect(localName) ?: scope.localBindings[localName]
|
||||
if (rec != null && (rec.type == ObjRecord.Type.Delegated || rec.type == ObjRecord.Type.Property || rec.value is ObjProperty)) {
|
||||
return scope.resolve(rec, localName)
|
||||
@ -2329,12 +2390,27 @@ class CmdFrame(
|
||||
val name = names[i] ?: continue
|
||||
if (scopeSlotNames.contains(name)) continue
|
||||
val target = resolveLocalScope(i) ?: continue
|
||||
val isDelegated = fn.localSlotDelegated.getOrNull(i) == true
|
||||
val value = if (useRefs) FrameSlotRef(frame, i) else localSlotToObj(i)
|
||||
val rec = target.getLocalRecordDirect(name)
|
||||
if (rec == null) {
|
||||
val isMutable = fn.localSlotMutables.getOrElse(i) { true }
|
||||
target.addItem(name, isMutable, value)
|
||||
if (isDelegated) {
|
||||
val delegatedRec = target.addItem(
|
||||
name,
|
||||
isMutable,
|
||||
ObjNull,
|
||||
recordType = ObjRecord.Type.Delegated
|
||||
)
|
||||
delegatedRec.delegate = localSlotToObj(i)
|
||||
} else {
|
||||
target.addItem(name, isMutable, value)
|
||||
}
|
||||
} else {
|
||||
if (isDelegated && rec.type == ObjRecord.Type.Delegated) {
|
||||
rec.delegate = localSlotToObj(i)
|
||||
continue
|
||||
}
|
||||
val existing = rec.value
|
||||
if (existing is FrameSlotRef && !useRefs) continue
|
||||
rec.value = value
|
||||
@ -2349,6 +2425,11 @@ class CmdFrame(
|
||||
val name = names[i] ?: continue
|
||||
val target = resolveLocalScope(i) ?: continue
|
||||
val rec = target.getLocalRecordDirect(name) ?: continue
|
||||
if (fn.localSlotDelegated.getOrNull(i) == true && rec.type == ObjRecord.Type.Delegated) {
|
||||
val delegate = rec.delegate ?: ObjNull
|
||||
frame.setObj(i, delegate)
|
||||
continue
|
||||
}
|
||||
val value = rec.value
|
||||
if (value is FrameSlotRef) {
|
||||
val resolved = value.read()
|
||||
|
||||
@ -162,6 +162,9 @@ enum class Opcode(val code: Int) {
|
||||
ITER_POP(0xC0),
|
||||
ITER_CANCEL(0xC1),
|
||||
ASSIGN_SCOPE_SLOT(0xC2),
|
||||
DELEGATED_GET_LOCAL(0xC3),
|
||||
DELEGATED_SET_LOCAL(0xC4),
|
||||
BIND_DELEGATE_LOCAL(0xC5),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user