Step 22: delegated locals in bytecode
This commit is contained in:
parent
9d508e219f
commit
c035b4c34c
@ -73,10 +73,10 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [x] Step 21: Union mismatch path in bytecode.
|
- [x] Step 21: Union mismatch path in bytecode.
|
||||||
- [x] Replace `UnionTypeMismatchStatement` branch with a bytecode-compilable throw path (no custom `StatementRef` that blocks bytecode).
|
- [x] Replace `UnionTypeMismatchStatement` branch with a bytecode-compilable throw path (no custom `StatementRef` that blocks bytecode).
|
||||||
- [x] Add a JVM test that forces the union mismatch at runtime and asserts the error message.
|
- [x] Add a JVM test that forces the union mismatch at runtime and asserts the error message.
|
||||||
- [ ] Step 22: Delegated local slots in bytecode.
|
- [x] Step 22: Delegated local slots in bytecode.
|
||||||
- [ ] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
- [x] Support reads/writes/assign-ops/inc/dec for delegated locals (`LocalSlotRef.isDelegated`) in `BytecodeCompiler`.
|
||||||
- [ ] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
- [x] Remove `containsDelegatedRefs` guard once delegated locals are bytecode-safe.
|
||||||
- [ ] Add JVM tests that use delegated locals inside bytecode-compiled functions.
|
- [x] Add JVM tests that use delegated locals inside bytecode-compiled functions.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@ -1545,8 +1545,7 @@ class Compiler(
|
|||||||
statements.isNotEmpty() &&
|
statements.isNotEmpty() &&
|
||||||
codeContexts.lastOrNull() is CodeContext.Module &&
|
codeContexts.lastOrNull() is CodeContext.Module &&
|
||||||
resolutionScriptDepth == 1 &&
|
resolutionScriptDepth == 1 &&
|
||||||
statements.none { containsUnsupportedForBytecode(it) } &&
|
statements.none { containsUnsupportedForBytecode(it) }
|
||||||
statements.none { containsDelegatedRefs(it) }
|
|
||||||
val (finalStatements, moduleBytecode) = if (wrapScriptBytecode) {
|
val (finalStatements, moduleBytecode) = if (wrapScriptBytecode) {
|
||||||
val unwrapped = statements.map { unwrapBytecodeDeep(it) }
|
val unwrapped = statements.map { unwrapBytecodeDeep(it) }
|
||||||
val block = InlineBlockStatement(unwrapped, start)
|
val block = InlineBlockStatement(unwrapped, start)
|
||||||
@ -1829,9 +1828,6 @@ class Compiler(
|
|||||||
if (codeContexts.any { it is CodeContext.Function }) {
|
if (codeContexts.any { it is CodeContext.Function }) {
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
if (containsDelegatedRefs(stmt)) {
|
|
||||||
return stmt
|
|
||||||
}
|
|
||||||
if (containsUnsupportedForBytecode(stmt)) {
|
if (containsUnsupportedForBytecode(stmt)) {
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
@ -1871,7 +1867,6 @@ class Compiler(
|
|||||||
extraKnownNameObjClass: Map<String, ObjClass> = emptyMap()
|
extraKnownNameObjClass: Map<String, ObjClass> = emptyMap()
|
||||||
): Statement {
|
): Statement {
|
||||||
if (!useBytecodeStatements) return stmt
|
if (!useBytecodeStatements) return stmt
|
||||||
if (containsDelegatedRefs(stmt)) return stmt
|
|
||||||
if (containsUnsupportedForBytecode(stmt)) return stmt
|
if (containsUnsupportedForBytecode(stmt)) return stmt
|
||||||
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||||
val allowedScopeNames = moduleSlotPlan()?.slots?.keys
|
val allowedScopeNames = moduleSlotPlan()?.slots?.keys
|
||||||
@ -6965,8 +6960,7 @@ class Compiler(
|
|||||||
val fnStatements = rawFnStatements?.let { stmt ->
|
val fnStatements = rawFnStatements?.let { stmt ->
|
||||||
if (useBytecodeStatements &&
|
if (useBytecodeStatements &&
|
||||||
parentContext !is CodeContext.ClassBody &&
|
parentContext !is CodeContext.ClassBody &&
|
||||||
!containsUnsupportedForBytecode(stmt) &&
|
!containsUnsupportedForBytecode(stmt)
|
||||||
!containsDelegatedRefs(stmt)
|
|
||||||
) {
|
) {
|
||||||
val paramKnownClasses = mutableMapOf<String, ObjClass>()
|
val paramKnownClasses = mutableMapOf<String, ObjClass>()
|
||||||
for (param in argsDeclaration.params) {
|
for (param in argsDeclaration.params) {
|
||||||
|
|||||||
@ -303,7 +303,18 @@ class BytecodeCompiler(
|
|||||||
return CompiledValue(slot, resolved)
|
return CompiledValue(slot, resolved)
|
||||||
}
|
}
|
||||||
if (!allowLocalSlots) return null
|
if (!allowLocalSlots) return null
|
||||||
if (ref.isDelegated) return compileEvalRef(ref)
|
if (ref.isDelegated) {
|
||||||
|
val mapped = resolveSlot(ref) ?: return null
|
||||||
|
if (mapped < scopeSlotCount) {
|
||||||
|
val addrSlot = ensureScopeAddr(mapped)
|
||||||
|
val local = allocSlot()
|
||||||
|
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, local)
|
||||||
|
updateSlotType(local, SlotType.OBJ)
|
||||||
|
return CompiledValue(local, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
updateSlotType(mapped, SlotType.OBJ)
|
||||||
|
return CompiledValue(mapped, SlotType.OBJ)
|
||||||
|
}
|
||||||
if (ref.name.isEmpty()) return null
|
if (ref.name.isEmpty()) return null
|
||||||
if (ref.captureOwnerScopeId == null && refScopeId(ref) == 0) {
|
if (ref.captureOwnerScopeId == null && refScopeId(ref) == 0) {
|
||||||
val byName = scopeSlotIndexByName[ref.name]
|
val byName = scopeSlotIndexByName[ref.name]
|
||||||
@ -1663,6 +1674,14 @@ class BytecodeCompiler(
|
|||||||
val localTarget = assignTarget(ref)
|
val localTarget = assignTarget(ref)
|
||||||
if (localTarget != null) {
|
if (localTarget != null) {
|
||||||
if (!allowLocalSlots) return null
|
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
|
||||||
|
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}"))
|
||||||
@ -1970,6 +1989,30 @@ class BytecodeCompiler(
|
|||||||
val localTarget = ref.target as? LocalSlotRef
|
val localTarget = ref.target as? LocalSlotRef
|
||||||
if (localTarget != null) {
|
if (localTarget != null) {
|
||||||
if (!allowLocalSlots) return compileEvalRef(ref)
|
if (!allowLocalSlots) return compileEvalRef(ref)
|
||||||
|
if (localTarget.isDelegated) {
|
||||||
|
val slot = resolveSlot(localTarget) ?: return null
|
||||||
|
if (slot >= scopeSlotCount) return null
|
||||||
|
val addrSlot = ensureScopeAddr(slot)
|
||||||
|
val current = allocSlot()
|
||||||
|
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
|
||||||
|
updateSlotType(current, SlotType.OBJ)
|
||||||
|
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||||
|
val rhsObj = ensureObjSlot(rhs)
|
||||||
|
val objOp = when (ref.op) {
|
||||||
|
BinOp.PLUS -> Opcode.ADD_OBJ
|
||||||
|
BinOp.MINUS -> Opcode.SUB_OBJ
|
||||||
|
BinOp.STAR -> Opcode.MUL_OBJ
|
||||||
|
BinOp.SLASH -> Opcode.DIV_OBJ
|
||||||
|
BinOp.PERCENT -> Opcode.MOD_OBJ
|
||||||
|
else -> null
|
||||||
|
} ?: return compileEvalRef(ref)
|
||||||
|
val result = allocSlot()
|
||||||
|
builder.emit(objOp, current, rhsObj.slot, result)
|
||||||
|
updateSlotType(result, SlotType.OBJ)
|
||||||
|
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return CompiledValue(result, SlotType.OBJ)
|
||||||
|
}
|
||||||
if (localTarget.isDelegated) return compileEvalRef(ref)
|
if (localTarget.isDelegated) return compileEvalRef(ref)
|
||||||
val slot = resolveSlot(localTarget) ?: return null
|
val slot = resolveSlot(localTarget) ?: return null
|
||||||
val targetType = slotTypes[slot] ?: SlotType.OBJ
|
val targetType = slotTypes[slot] ?: SlotType.OBJ
|
||||||
@ -2252,7 +2295,32 @@ class BytecodeCompiler(
|
|||||||
builder.emit(Opcode.SET_CLASS_SCOPE, classObj.slot, nameId, newValue.slot)
|
builder.emit(Opcode.SET_CLASS_SCOPE, classObj.slot, nameId, newValue.slot)
|
||||||
}
|
}
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
if (!allowLocalSlots || !target.isMutable || target.isDelegated) return null
|
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 current = allocSlot()
|
||||||
|
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
|
||||||
|
val nullSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_NULL, nullSlot)
|
||||||
|
val cmpSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CMP_REF_EQ_OBJ, current, nullSlot, cmpSlot)
|
||||||
|
val assignLabel = builder.label()
|
||||||
|
val endLabel = builder.label()
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_TRUE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(assignLabel))
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, current, resultSlot)
|
||||||
|
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||||
|
builder.mark(assignLabel)
|
||||||
|
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, newValue.slot)
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, newValue.slot, resultSlot)
|
||||||
|
builder.mark(endLabel)
|
||||||
|
updateSlotType(resultSlot, SlotType.OBJ)
|
||||||
|
return CompiledValue(resultSlot, SlotType.OBJ)
|
||||||
|
}
|
||||||
val slot = resolveSlot(target) ?: return null
|
val slot = resolveSlot(target) ?: return null
|
||||||
if (slot < scopeSlotCount) {
|
if (slot < scopeSlotCount) {
|
||||||
val addrSlot = ensureScopeAddr(slot)
|
val addrSlot = ensureScopeAddr(slot)
|
||||||
@ -2733,7 +2801,30 @@ class BytecodeCompiler(
|
|||||||
val target = ref.target as? LocalSlotRef
|
val target = ref.target as? LocalSlotRef
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
if (!allowLocalSlots) return null
|
if (!allowLocalSlots) return null
|
||||||
if (!target.isMutable || target.isDelegated) return null
|
if (!target.isMutable) return null
|
||||||
|
if (target.isDelegated) {
|
||||||
|
val slot = resolveSlot(target) ?: return null
|
||||||
|
if (slot >= scopeSlotCount) return null
|
||||||
|
val addrSlot = ensureScopeAddr(slot)
|
||||||
|
val current = allocSlot()
|
||||||
|
builder.emit(Opcode.LOAD_OBJ_ADDR, addrSlot, current)
|
||||||
|
updateSlotType(current, SlotType.OBJ)
|
||||||
|
val oneSlot = allocSlot()
|
||||||
|
val oneId = builder.addConst(BytecodeConst.ObjRef(ObjInt.One))
|
||||||
|
builder.emit(Opcode.CONST_OBJ, oneId, oneSlot)
|
||||||
|
updateSlotType(oneSlot, SlotType.OBJ)
|
||||||
|
val result = allocSlot()
|
||||||
|
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
|
||||||
|
builder.emit(op, current, oneSlot, result)
|
||||||
|
updateSlotType(result, SlotType.OBJ)
|
||||||
|
builder.emit(Opcode.ASSIGN_SCOPE_SLOT, slot, result)
|
||||||
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
|
return if (wantResult && ref.isPost) {
|
||||||
|
CompiledValue(current, SlotType.OBJ)
|
||||||
|
} else {
|
||||||
|
CompiledValue(result, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
}
|
||||||
val slot = resolveSlot(target) ?: return null
|
val slot = resolveSlot(target) ?: return null
|
||||||
val slotType = slotTypes[slot] ?: SlotType.UNKNOWN
|
val slotType = slotTypes[slot] ?: SlotType.UNKNOWN
|
||||||
if (slot < scopeSlotCount && slotType != SlotType.UNKNOWN) {
|
if (slot < scopeSlotCount && slotType != SlotType.UNKNOWN) {
|
||||||
@ -6071,6 +6162,9 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
stmt.initializer?.let { collectScopeSlots(it) }
|
stmt.initializer?.let { collectScopeSlots(it) }
|
||||||
}
|
}
|
||||||
|
is DelegatedVarDeclStatement -> {
|
||||||
|
collectScopeSlots(stmt.initializer)
|
||||||
|
}
|
||||||
is IfStatement -> {
|
is IfStatement -> {
|
||||||
collectScopeSlots(stmt.condition)
|
collectScopeSlots(stmt.condition)
|
||||||
collectScopeSlots(stmt.ifBody)
|
collectScopeSlots(stmt.ifBody)
|
||||||
|
|||||||
@ -135,6 +135,8 @@ 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.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||||
@ -254,6 +256,7 @@ 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.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
Opcode.LOAD_OBJ_ADDR -> CmdLoadObjAddr(operands[0], operands[1])
|
||||||
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
Opcode.STORE_OBJ_ADDR -> CmdStoreObjAddr(operands[0], operands[1])
|
||||||
Opcode.LOAD_INT_ADDR -> CmdLoadIntAddr(operands[0], operands[1])
|
Opcode.LOAD_INT_ADDR -> CmdLoadIntAddr(operands[0], operands[1])
|
||||||
|
|||||||
@ -84,6 +84,7 @@ 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 CmdLoadObjAddr -> Opcode.LOAD_OBJ_ADDR to intArrayOf(cmd.addrSlot, 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 CmdStoreObjAddr -> Opcode.STORE_OBJ_ADDR to intArrayOf(cmd.src, cmd.addrSlot)
|
||||||
is CmdLoadIntAddr -> Opcode.LOAD_INT_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
is CmdLoadIntAddr -> Opcode.LOAD_INT_ADDR to intArrayOf(cmd.addrSlot, cmd.dst)
|
||||||
@ -243,6 +244,8 @@ 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.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
Opcode.LOAD_OBJ_ADDR, Opcode.LOAD_INT_ADDR, Opcode.LOAD_REAL_ADDR, Opcode.LOAD_BOOL_ADDR ->
|
||||||
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
listOf(OperandKind.ADDR, OperandKind.SLOT)
|
||||||
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
Opcode.STORE_OBJ_ADDR, Opcode.STORE_INT_ADDR, Opcode.STORE_REAL_ADDR, Opcode.STORE_BOOL_ADDR ->
|
||||||
|
|||||||
@ -358,6 +358,13 @@ 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 CmdIntToReal(internal val src: Int, internal val dst: Int) : Cmd() {
|
class CmdIntToReal(internal val src: Int, internal val dst: Int) : Cmd() {
|
||||||
override suspend fun perform(frame: CmdFrame) {
|
override suspend fun perform(frame: CmdFrame) {
|
||||||
frame.setReal(dst, frame.getReal(src))
|
frame.setReal(dst, frame.getReal(src))
|
||||||
@ -2124,6 +2131,16 @@ 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()
|
||||||
|
|||||||
@ -161,6 +161,7 @@ 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),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@ -199,6 +199,25 @@ class BytecodeRecentOpsTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun delegatedLocalAssignAndIncDec() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
}
|
||||||
|
assertEquals(4, calc())
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun unionMemberDispatchSubtype() = runTest {
|
fun unionMemberDispatchSubtype() = runTest {
|
||||||
eval(
|
eval(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user