Compare commits

...

5 Commits

12 changed files with 604 additions and 51 deletions

View File

@ -32,6 +32,17 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
- [x] Step 9: Module-level bytecode execution.
- [x] Compile `Script` bodies to bytecode instead of interpreting at module scope.
- [x] Keep import/module slot seeding in frame-only flow.
- [x] Step 10: Bytecode for declaration statements in module scripts.
- [x] Support `ClassDeclStatement`, `FunctionDeclStatement`, `EnumDeclStatement` in bytecode compilation.
- [x] Keep a mixed execution path for declarations (module bytecode calls statement bodies via `CALL_SLOT`).
- [x] Ensure module object member refs compile as instance access (not class-scope).
- [x] Step 11: Destructuring assignment bytecode.
- [x] Handle `[a, b] = expr` (AssignRef target `ListLiteralRef`) without interpreter fallback.
- [x] Step 12: Optional member assign-ops and inc/dec in bytecode.
- [x] Support `a?.b += 1` and `a?.b++` for `FieldRef` targets.
- [x] Fix post-inc return value for object slots stored in scope frames.
- [x] Handle optional receivers for member assign-ops and inc/dec without evaluating operands on null.
- [x] Support class-scope and index optional inc/dec paths in bytecode.
## Notes

View File

@ -28,4 +28,9 @@ class ClassDeclStatement(
override suspend fun execute(scope: Scope): Obj {
return delegate.execute(scope)
}
override suspend fun callOn(scope: Scope): Obj {
val target = scope.parent ?: scope
return delegate.execute(target)
}
}

View File

@ -165,6 +165,7 @@ class Compiler(
private val classScopeCallableMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
private val objectDeclNames: MutableSet<String> = mutableSetOf()
private fun seedSlotPlanFromScope(scope: Scope, includeParents: Boolean = false) {
val plan = moduleSlotPlan() ?: return
@ -1536,7 +1537,12 @@ class Compiler(
allowedScopeNames = modulePlan.keys,
moduleScopeId = moduleSlotPlan()?.id,
slotTypeByScopeId = slotTypeByScopeId,
knownNameObjClass = knownClassMapForBytecode()
knownNameObjClass = knownClassMapForBytecode(),
knownObjectNames = objectDeclNames,
classFieldTypesByName = classFieldTypesByName,
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName
) as BytecodeStatement
unwrapped to bytecodeStmt.bytecodeFunction()
} else {
@ -1830,7 +1836,12 @@ class Compiler(
allowedScopeNames = allowedScopeNames,
moduleScopeId = moduleSlotPlan()?.id,
slotTypeByScopeId = slotTypeByScopeId,
knownNameObjClass = knownClassMapForBytecode()
knownNameObjClass = knownClassMapForBytecode(),
knownObjectNames = objectDeclNames,
classFieldTypesByName = classFieldTypesByName,
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName
)
}
@ -1861,7 +1872,12 @@ class Compiler(
allowedScopeNames = allowedScopeNames,
moduleScopeId = moduleSlotPlan()?.id,
slotTypeByScopeId = slotTypeByScopeId,
knownNameObjClass = knownNames
knownNameObjClass = knownNames,
knownObjectNames = objectDeclNames,
classFieldTypesByName = classFieldTypesByName,
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName
)
}
@ -1899,6 +1915,9 @@ class Compiler(
is ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
is ExtensionPropertyDeclStatement -> false
is ClassDeclStatement -> false
is FunctionDeclStatement -> false
is EnumDeclStatement -> false
is TryStatement -> {
containsUnsupportedForBytecode(target.body) ||
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
@ -1930,9 +1949,12 @@ class Compiler(
is CastRef -> containsUnsupportedRef(ref.castValueRef()) || containsUnsupportedRef(ref.castTypeRef())
is net.sergeych.lyng.obj.TypeDeclRef -> false
is AssignRef -> {
if (ref.target is ListLiteralRef) return true
val target = ref.target as? LocalSlotRef
(target?.isDelegated == true) || containsUnsupportedRef(ref.value)
if (target != null) {
(target.isDelegated) || containsUnsupportedRef(ref.value)
} else {
containsUnsupportedRef(ref.target) || containsUnsupportedRef(ref.value)
}
}
is AssignOpRef -> containsUnsupportedRef(ref.target) || containsUnsupportedRef(ref.value)
is AssignIfNullRef -> containsUnsupportedRef(ref.target) || containsUnsupportedRef(ref.value)
@ -1967,11 +1989,9 @@ class Compiler(
is LocalSlotRef -> target.name
else -> null
}
if (targetName == "delay") return true
containsUnsupportedRef(ref.target) || ref.args.any { containsUnsupportedForBytecode(it.value) }
}
is MethodCallRef -> {
if (ref.name == "delay") return true
val receiverClass = resolveReceiverClassForMember(ref.receiver) ?: return true
val hasMember = receiverClass.instanceMethodIdMap(includeAbstract = true)[ref.name] != null
if (!hasMember && !hasExtensionFor(receiverClass.className, ref.name)) return true
@ -5662,6 +5682,9 @@ class Compiler(
if (declaredName != null) {
resolutionSink?.declareSymbol(declaredName, SymbolKind.CLASS, isMutable = false, pos = nameToken!!.pos)
declareLocalName(declaredName, isMutable = false)
if (codeContexts.lastOrNull() is CodeContext.Module) {
objectDeclNames.add(declaredName)
}
if (outerClassName != null) {
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
outerCtx?.classScopeMembers?.add(declaredName)
@ -6614,11 +6637,17 @@ class Compiler(
val declKind = if (parentContext is CodeContext.ClassBody) SymbolKind.MEMBER else SymbolKind.FUNCTION
resolutionSink?.declareSymbol(name, declKind, isMutable = false, pos = nameStartPos, isOverride = isOverride)
if (parentContext is CodeContext.ClassBody && extTypeName == null) {
parentContext.declaredMembers.add(name)
if (!parentContext.memberMethodIds.containsKey(name)) {
parentContext.memberMethodIds[name] = parentContext.nextMethodId++
if (isStatic) {
parentContext.classScopeMembers.add(name)
registerClassScopeMember(parentContext.name, name)
registerClassScopeCallableMember(parentContext.name, name)
} else {
parentContext.declaredMembers.add(name)
if (!parentContext.memberMethodIds.containsKey(name)) {
parentContext.memberMethodIds[name] = parentContext.nextMethodId++
}
memberMethodId = parentContext.memberMethodIds[name]
}
memberMethodId = parentContext.memberMethodIds[name]
}
if (declKind != SymbolKind.MEMBER) {
declareLocalName(name, isMutable = false)
@ -7754,7 +7783,10 @@ class Compiler(
}
if (declaringClassNameCaptured != null && extTypeName == null) {
if (isDelegate || isProperty) {
if (isStatic) {
classCtx?.classScopeMembers?.add(name)
registerClassScopeMember(declaringClassNameCaptured, name)
} else if (isDelegate || isProperty) {
if (classCtx != null) {
if (!classCtx.memberMethodIds.containsKey(name)) {
classCtx.memberMethodIds[name] = classCtx.nextMethodId++

View File

@ -27,4 +27,9 @@ class EnumDeclStatement(
override suspend fun execute(scope: Scope): Obj {
return delegate.execute(scope)
}
override suspend fun callOn(scope: Scope): Obj {
val target = scope.parent ?: scope
return delegate.execute(target)
}
}

View File

@ -27,4 +27,9 @@ class FunctionDeclStatement(
override suspend fun execute(scope: Scope): Obj {
return delegate.execute(scope)
}
override suspend fun callOn(scope: Scope): Obj {
val target = scope.parent ?: scope
return delegate.execute(target)
}
}

View File

@ -28,6 +28,11 @@ class BytecodeCompiler(
private val moduleScopeId: Int? = null,
private val slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
private val knownNameObjClass: Map<String, ObjClass> = emptyMap(),
private val knownObjectNames: Set<String> = emptySet(),
private val classFieldTypesByName: Map<String, Map<String, ObjClass>> = emptyMap(),
private val enumEntriesByName: Map<String, List<String>> = emptyMap(),
private val callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
private val callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
) {
private var builder = CmdBuilder()
private var nextSlot = 0
@ -54,7 +59,8 @@ class BytecodeCompiler(
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
private val slotTypes = mutableMapOf<Int, SlotType>()
private val slotObjClass = mutableMapOf<Int, ObjClass>()
private val nameObjClass = mutableMapOf<String, ObjClass>()
private val nameObjClass = knownNameObjClass.toMutableMap()
private val knownClassNames = knownNameObjClass.keys.toSet()
private val slotInitClassByKey = mutableMapOf<ScopeSlotKey, ObjClass>()
private val intLoopVarNames = LinkedHashSet<String>()
private val loopStack = ArrayDeque<LoopContext>()
@ -148,6 +154,54 @@ class BytecodeCompiler(
localSlotMutables
)
}
is net.sergeych.lyng.ClassDeclStatement -> {
val value = emitStatementCall(stmt)
builder.emit(Opcode.RET, value.slot)
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
builder.build(
name,
localCount,
addrCount = nextAddrSlot,
returnLabels = returnLabels,
scopeSlotIndices,
scopeSlotNames,
scopeSlotIsModule,
localSlotNames,
localSlotMutables
)
}
is net.sergeych.lyng.FunctionDeclStatement -> {
val value = emitStatementCall(stmt)
builder.emit(Opcode.RET, value.slot)
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
builder.build(
name,
localCount,
addrCount = nextAddrSlot,
returnLabels = returnLabels,
scopeSlotIndices,
scopeSlotNames,
scopeSlotIsModule,
localSlotNames,
localSlotMutables
)
}
is net.sergeych.lyng.EnumDeclStatement -> {
val value = emitStatementCall(stmt)
builder.emit(Opcode.RET, value.slot)
val localCount = maxOf(nextSlot, value.slot + 1) - scopeSlotCount
builder.build(
name,
localCount,
addrCount = nextAddrSlot,
returnLabels = returnLabels,
scopeSlotIndices,
scopeSlotNames,
scopeSlotIsModule,
localSlotNames,
localSlotMutables
)
}
else -> null
}
}
@ -1646,6 +1700,15 @@ class BytecodeCompiler(
updateNameObjClassFromSlot(nameTarget, slot)
return value
}
val listTarget = ref.target as? ListLiteralRef
if (listTarget != null) {
val value = compileRef(assignValue(ref)) ?: return null
val valueObj = ensureObjSlot(value)
val declId = builder.addConst(BytecodeConst.DestructureAssign(listTarget, callSitePos()))
builder.emit(Opcode.ASSIGN_DESTRUCTURE, declId, valueObj.slot)
updateSlotType(valueObj.slot, SlotType.OBJ)
return CompiledValue(valueObj.slot, SlotType.OBJ)
}
val value = compileRef(assignValue(ref)) ?: return null
val target = ref.target
if (target is ClassScopeMemberRef) {
@ -1689,6 +1752,25 @@ class BytecodeCompiler(
}
return value
}
if (isKnownClassReceiver(target.target)) {
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
if (!target.isOptional) {
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot)
} else {
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
val cmpSlot = allocSlot()
builder.emit(Opcode.CMP_REF_EQ_OBJ, receiver.slot, nullSlot, cmpSlot)
val endLabel = builder.label()
builder.emit(
Opcode.JMP_IF_TRUE,
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(endLabel))
)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot)
builder.mark(endLabel)
}
return value
}
val fieldId = receiverClass.instanceFieldIdMap()[target.name]
val methodId = if (fieldId == null) {
receiverClass.instanceMethodIdMap(includeAbstract = true)[target.name]
@ -1887,28 +1969,102 @@ class BytecodeCompiler(
} ?: return compileEvalRef(ref)
val fieldTarget = ref.target as? FieldRef
if (fieldTarget != null) {
if (fieldTarget.isOptional) return compileEvalRef(ref)
val receiverClass = resolveReceiverClass(fieldTarget.target)
?: throw BytecodeCompileException(
"Member assignment requires compile-time receiver type: ${fieldTarget.name}",
Pos.builtIn
)
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val current = allocSlot()
val result = allocSlot()
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
if (receiverClass == ObjDynamic.type) {
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val current = allocSlot()
val result = allocSlot()
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
if (!fieldTarget.isOptional) {
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
updateSlotType(result, SlotType.OBJ)
return CompiledValue(result, 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))
)
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, result)
builder.mark(endLabel)
updateSlotType(result, SlotType.OBJ)
return CompiledValue(result, SlotType.OBJ)
}
throw BytecodeCompileException(
"Member assignment requires compile-time receiver type: ${fieldTarget.name}",
Pos.builtIn
)
val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name]
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name]
if (fieldId == null && methodId == null && isKnownClassReceiver(fieldTarget.target)) {
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
if (!fieldTarget.isOptional) {
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
} else {
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))
)
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, result)
builder.mark(endLabel)
}
updateSlotType(result, SlotType.OBJ)
return CompiledValue(result, SlotType.OBJ)
}
if (fieldId != null || methodId != null) {
if (!fieldTarget.isOptional) {
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
} else {
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))
)
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, result)
builder.mark(endLabel)
}
updateSlotType(result, SlotType.OBJ)
return CompiledValue(result, SlotType.OBJ)
}
return compileEvalRef(ref)
}
val implicitTarget = ref.target as? ImplicitThisMemberRef
if (implicitTarget != null) {
@ -1991,8 +2147,7 @@ class BytecodeCompiler(
builder.emit(Opcode.SET_INDEX, receiver.slot, index.slot, result)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, current)
builder.emit(objOp, current, rhs.slot, result)
builder.emit(Opcode.CONST_NULL, result)
builder.mark(endLabel)
updateSlotType(result, SlotType.OBJ)
return CompiledValue(result, SlotType.OBJ)
@ -2190,6 +2345,30 @@ class BytecodeCompiler(
val encodedMethodId = encodeMemberId(receiverClass, methodId)
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
val dst = allocSlot()
if (fieldId == null && methodId == null && isKnownClassReceiver(ref.target)) {
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
if (!ref.isOptional) {
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst)
} else {
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))
)
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, dst)
builder.mark(endLabel)
}
updateSlotType(dst, SlotType.OBJ)
return CompiledValue(dst, SlotType.OBJ)
}
if (fieldId != null || methodId != null) {
if (!ref.isOptional) {
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, encodedFieldId ?: -1, encodedMethodId ?: -1, dst)
@ -2507,13 +2686,15 @@ class BytecodeCompiler(
val boxed = allocSlot()
builder.emit(Opcode.BOX_OBJ, current, boxed)
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, boxed, old)
val result = allocSlot()
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
builder.emit(op, boxed, oneSlot, result)
builder.emit(Opcode.MOVE_OBJ, result, boxed)
emitStoreToAddr(boxed, addrSlot, SlotType.OBJ)
updateSlotType(slot, SlotType.OBJ)
CompiledValue(boxed, SlotType.OBJ)
CompiledValue(old, SlotType.OBJ)
} else {
val result = allocSlot()
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
@ -2693,7 +2874,6 @@ class BytecodeCompiler(
val fieldTarget = ref.target as? FieldRef
if (fieldTarget != null) {
if (fieldTarget.isOptional) return null
val receiverClass = resolveReceiverClass(fieldTarget.target)
?: throw BytecodeCompileException(
"Member access requires compile-time receiver type: ${fieldTarget.name}",
@ -2702,6 +2882,45 @@ class BytecodeCompiler(
if (receiverClass == ObjDynamic.type) {
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
val resultSlot = allocSlot()
if (fieldTarget.isOptional) {
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 current = allocSlot()
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, 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
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
} else {
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, resultSlot)
builder.mark(endLabel)
updateSlotType(resultSlot, SlotType.OBJ)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val current = allocSlot()
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, current)
updateSlotType(current, SlotType.OBJ)
@ -2716,16 +2935,121 @@ class BytecodeCompiler(
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
return CompiledValue(old, SlotType.OBJ)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
return CompiledValue(result, SlotType.OBJ)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name]
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name]
if (fieldId == null && methodId == null && isKnownClassReceiver(fieldTarget.target)) {
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
val resultSlot = allocSlot()
if (fieldTarget.isOptional) {
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 current = allocSlot()
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, 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
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
} else {
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, resultSlot)
builder.mark(endLabel)
updateSlotType(resultSlot, SlotType.OBJ)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val current = allocSlot()
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, 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
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, result)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
if (fieldId == null && methodId == null) return null
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val resultSlot = allocSlot()
if (fieldTarget.isOptional) {
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 current = allocSlot()
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, 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
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
} else {
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, resultSlot)
builder.mark(endLabel)
updateSlotType(resultSlot, SlotType.OBJ)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val current = allocSlot()
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, current)
updateSlotType(current, SlotType.OBJ)
@ -2740,16 +3064,55 @@ class BytecodeCompiler(
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
return CompiledValue(old, SlotType.OBJ)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_MEMBER_SLOT, receiver.slot, fieldId ?: -1, methodId ?: -1, result)
return CompiledValue(result, SlotType.OBJ)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val indexTarget = ref.target as? IndexRef ?: return null
if (indexTarget.optionalRef) return null
val receiver = compileRefWithFallback(indexTarget.targetRef, null, Pos.builtIn) ?: return null
if (indexTarget.optionalRef) {
val resultSlot = allocSlot()
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 index = compileRefWithFallback(indexTarget.indexRef, null, Pos.builtIn) ?: return null
val current = allocSlot()
builder.emit(Opcode.GET_INDEX, receiver.slot, index.slot, current)
updateSlotType(current, SlotType.OBJ)
val oneSlot = allocSlot()
val oneId = builder.addConst(BytecodeConst.ObjRef(ObjInt.One))
builder.emit(Opcode.CONST_OBJ, oneId, oneSlot)
val result = allocSlot()
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
if (wantResult && ref.isPost) {
val old = allocSlot()
builder.emit(Opcode.MOVE_OBJ, current, old)
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_INDEX, receiver.slot, index.slot, result)
builder.emit(Opcode.MOVE_OBJ, old, resultSlot)
} else {
builder.emit(op, current, oneSlot, result)
builder.emit(Opcode.SET_INDEX, receiver.slot, index.slot, result)
builder.emit(Opcode.MOVE_OBJ, result, resultSlot)
}
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, resultSlot)
builder.mark(endLabel)
return CompiledValue(resultSlot, SlotType.OBJ)
}
val index = compileRefWithFallback(indexTarget.indexRef, null, Pos.builtIn) ?: return null
val current = allocSlot()
builder.emit(Opcode.GET_INDEX, receiver.slot, index.slot, current)
@ -3092,6 +3455,34 @@ class BytecodeCompiler(
builder.mark(endLabel)
return CompiledValue(dst, SlotType.OBJ)
}
if (isKnownClassReceiver(ref.receiver)) {
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
val memberSlot = allocSlot()
if (!ref.isOptional) {
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
} else {
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))
)
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
builder.mark(nullLabel)
builder.emit(Opcode.CONST_NULL, memberSlot)
builder.mark(endLabel)
}
val args = compileCallArgs(ref.args, ref.tailBlock) ?: return null
val encodedCount = encodeCallArgCount(args) ?: return null
setPos(callPos)
builder.emit(Opcode.CALL_SLOT, memberSlot, args.base, encodedCount, dst)
return CompiledValue(dst, SlotType.OBJ)
}
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
?: throw BytecodeCompileException(
"Unknown member ${ref.name} on ${receiverClass.className}",
@ -3476,6 +3867,17 @@ class BytecodeCompiler(
throw BytecodeCompileException("Unsupported statement in bytecode: $stmtName", stmt.pos)
}
private fun emitStatementCall(stmt: Statement): CompiledValue {
val constId = builder.addConst(BytecodeConst.ObjRef(stmt))
val calleeSlot = allocSlot()
builder.emit(Opcode.CONST_OBJ, constId, calleeSlot)
updateSlotType(calleeSlot, SlotType.OBJ)
val dst = allocSlot()
builder.emit(Opcode.CALL_SLOT, calleeSlot, 0, 0, dst)
updateSlotType(dst, SlotType.OBJ)
return CompiledValue(dst, SlotType.OBJ)
}
private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? {
val target = if (stmt is BytecodeStatement) stmt.original else stmt
setPos(target.pos)
@ -3509,9 +3911,9 @@ class BytecodeCompiler(
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target)
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.EnumDeclStatement -> emitStatementEval(target)
is net.sergeych.lyng.ClassDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.EnumDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.TryStatement -> emitTry(target, true)
is net.sergeych.lyng.WhenStatement -> compileWhen(target, true)
is net.sergeych.lyng.BreakStatement -> compileBreak(target)
@ -3536,6 +3938,9 @@ class BytecodeCompiler(
is VarDeclStatement -> emitVarDecl(target)
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
is IfStatement -> compileIfStatement(target)
is net.sergeych.lyng.ClassDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.EnumDeclStatement -> emitStatementCall(target)
is net.sergeych.lyng.ForInStatement -> {
val resultSlot = emitForIn(target, false) ?: return null
CompiledValue(resultSlot, SlotType.OBJ)
@ -4986,6 +5391,9 @@ class BytecodeCompiler(
private fun resolveReceiverClass(ref: ObjRef): ObjClass? {
return when (ref) {
is LocalSlotRef -> {
if (knownObjectNames.contains(ref.name)) {
return nameObjClass[ref.name] ?: ObjDynamic.type
}
val slot = resolveSlot(ref)
val fromSlot = slot?.let { slotObjClass[it] }
fromSlot
@ -5002,6 +5410,9 @@ class BytecodeCompiler(
}
}
is LocalVarRef -> {
if (knownObjectNames.contains(ref.name)) {
return nameObjClass[ref.name] ?: ObjDynamic.type
}
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
if (fromSlot != null) return fromSlot
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
@ -5046,6 +5457,14 @@ class BytecodeCompiler(
}
}
private fun isKnownClassReceiver(ref: ObjRef): Boolean {
return when (ref) {
is LocalVarRef -> knownClassNames.contains(ref.name) && !knownObjectNames.contains(ref.name)
is LocalSlotRef -> knownClassNames.contains(ref.name) && !knownObjectNames.contains(ref.name)
else -> false
}
}
private fun isAllowedObjectMember(memberName: String): Boolean {
return when (memberName) {
"toString",
@ -5149,8 +5568,12 @@ class BytecodeCompiler(
private fun inferCallReturnClass(ref: CallRef): ObjClass? {
return when (val target = ref.target) {
is LocalSlotRef -> nameObjClass[target.name] ?: resolveTypeNameClass(target.name)
is LocalVarRef -> nameObjClass[target.name] ?: resolveTypeNameClass(target.name)
is LocalSlotRef -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
?: nameObjClass[target.name]
?: resolveTypeNameClass(target.name)
is LocalVarRef -> callableReturnTypeByName[target.name]
?: nameObjClass[target.name]
?: resolveTypeNameClass(target.name)
is ConstRef -> target.constValue as? ObjClass
else -> null
}
@ -5214,6 +5637,16 @@ class BytecodeCompiler(
private fun inferFieldReturnClass(targetClass: ObjClass?, name: String): ObjClass? {
if (targetClass == null) return null
if (targetClass == ObjDynamic.type) return ObjDynamic.type
classFieldTypesByName[targetClass.className]?.get(name)?.let { return it }
enumEntriesByName[targetClass.className]?.let { entries ->
return when {
name == "entries" -> ObjList.type
name == "name" -> ObjString.type
name == "ordinal" -> ObjInt.type
entries.contains(name) -> targetClass
else -> null
}
}
if (targetClass == ObjInstant.type && (name == "distantFuture" || name == "distantPast")) {
return ObjInstant.type
}
@ -6006,7 +6439,11 @@ class BytecodeCompiler(
is CastRef -> containsValueFnRef(ref.castValueRef()) || containsValueFnRef(ref.castTypeRef())
is AssignRef -> {
val target = assignTarget(ref)
(target != null && containsValueFnRef(target)) || containsValueFnRef(assignValue(ref))
if (target != null) {
containsValueFnRef(target) || containsValueFnRef(assignValue(ref))
} else {
containsValueFnRef(ref.target) || containsValueFnRef(assignValue(ref))
}
}
is AssignOpRef -> containsValueFnRef(ref.target) || containsValueFnRef(ref.value)
is AssignIfNullRef -> containsValueFnRef(ref.target) || containsValueFnRef(ref.value)

View File

@ -61,6 +61,10 @@ sealed class BytecodeConst {
val isTransient: Boolean,
val pos: Pos,
) : BytecodeConst()
data class DestructureAssign(
val pattern: ListLiteralRef,
val pos: Pos,
) : BytecodeConst()
data class CallArgsPlan(val tailBlock: Boolean, val specs: List<CallArgSpec>) : BytecodeConst()
data class CallArgSpec(val name: String?, val isSplat: Boolean)
}

View File

@ -45,6 +45,11 @@ class BytecodeStatement private constructor(
moduleScopeId: Int? = null,
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
knownNameObjClass: Map<String, ObjClass> = emptyMap(),
knownObjectNames: Set<String> = emptySet(),
classFieldTypesByName: Map<String, Map<String, ObjClass>> = emptyMap(),
enumEntriesByName: Map<String, List<String>> = emptyMap(),
callableReturnTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
callableReturnTypeByName: Map<String, ObjClass> = emptyMap(),
): Statement {
if (statement is BytecodeStatement) return statement
val hasUnsupported = containsUnsupportedStatement(statement)
@ -63,7 +68,12 @@ class BytecodeStatement private constructor(
allowedScopeNames = allowedScopeNames,
moduleScopeId = moduleScopeId,
slotTypeByScopeId = slotTypeByScopeId,
knownNameObjClass = knownNameObjClass
knownNameObjClass = knownNameObjClass,
knownObjectNames = knownObjectNames,
classFieldTypesByName = classFieldTypesByName,
enumEntriesByName = enumEntriesByName,
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
callableReturnTypeByName = callableReturnTypeByName
)
val compiled = compiler.compileStatement(nameHint, statement)
val fn = compiled ?: throw BytecodeCompileException(

View File

@ -147,7 +147,8 @@ class CmdBuilder {
listOf(OperandKind.CONST)
Opcode.PUSH_TRY ->
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
Opcode.ASSIGN_DESTRUCTURE ->
listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
Opcode.ADD_REAL, Opcode.SUB_REAL, Opcode.MUL_REAL, Opcode.DIV_REAL,
@ -398,6 +399,7 @@ class CmdBuilder {
Opcode.DECL_DESTRUCTURE -> CmdDeclDestructure(operands[0], operands[1])
Opcode.DECL_EXT_PROPERTY -> CmdDeclExtProperty(operands[0], operands[1])
Opcode.CALL_DIRECT -> CmdCallDirect(operands[0], operands[1], operands[2], operands[3])
Opcode.ASSIGN_DESTRUCTURE -> CmdAssignDestructure(operands[0], operands[1])
Opcode.CALL_MEMBER_SLOT -> CmdCallMemberSlot(operands[0], operands[1], operands[2], operands[3], operands[4])
Opcode.CALL_SLOT -> CmdCallSlot(operands[0], operands[1], operands[2], operands[3])
Opcode.CALL_DYNAMIC_MEMBER -> CmdCallDynamicMember(operands[0], operands[1], operands[2], operands[3], operands[4])

View File

@ -193,6 +193,7 @@ object CmdDisassembler {
is CmdDeclDestructure -> Opcode.DECL_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
is CmdDeclExtProperty -> Opcode.DECL_EXT_PROPERTY to intArrayOf(cmd.constId, cmd.slot)
is CmdCallDirect -> Opcode.CALL_DIRECT to intArrayOf(cmd.id, cmd.argBase, cmd.argCount, cmd.dst)
is CmdAssignDestructure -> Opcode.ASSIGN_DESTRUCTURE to intArrayOf(cmd.constId, cmd.slot)
is CmdCallMemberSlot -> Opcode.CALL_MEMBER_SLOT to intArrayOf(cmd.recvSlot, cmd.methodId, cmd.argBase, cmd.argCount, cmd.dst)
is CmdCallSlot -> Opcode.CALL_SLOT to intArrayOf(cmd.calleeSlot, cmd.argBase, cmd.argCount, cmd.dst)
is CmdCallDynamicMember -> Opcode.CALL_DYNAMIC_MEMBER to intArrayOf(cmd.recvSlot, cmd.nameId, cmd.argBase, cmd.argCount, cmd.dst)
@ -254,7 +255,8 @@ object CmdDisassembler {
listOf(OperandKind.CONST)
Opcode.PUSH_TRY ->
listOf(OperandKind.SLOT, OperandKind.IP, OperandKind.IP)
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE ->
Opcode.DECL_LOCAL, Opcode.DECL_EXT_PROPERTY, Opcode.DECL_DELEGATED, Opcode.DECL_DESTRUCTURE,
Opcode.ASSIGN_DESTRUCTURE ->
listOf(OperandKind.CONST, OperandKind.SLOT)
Opcode.ADD_INT, Opcode.SUB_INT, Opcode.MUL_INT, Opcode.DIV_INT, Opcode.MOD_INT,
Opcode.ADD_REAL, Opcode.SUB_REAL, Opcode.MUL_REAL, Opcode.DIV_REAL,

View File

@ -240,7 +240,9 @@ class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd()
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
)
if (!obj.isInstanceOf(clazz)) {
frame.ensureScope().raiseClassCastError("expected ${clazz.className}, got ${obj.objClass.className}")
frame.ensureScope().raiseClassCastError(
"Cannot cast ${obj.objClass.className} to ${clazz.className}"
)
}
return
}
@ -1270,7 +1272,26 @@ class CmdDeclDestructure(internal val constId: Int, internal val slot: Int) : Cm
scope.updateSlotFor(name, immutableRec)
}
}
frame.storeObjResult(slot, ObjVoid)
if (slot >= frame.fn.scopeSlotCount) {
frame.storeObjResult(slot, ObjVoid)
}
return
}
}
class CmdAssignDestructure(internal val constId: Int, internal val slot: Int) : Cmd() {
override suspend fun perform(frame: CmdFrame) {
if (frame.fn.localSlotNames.isNotEmpty()) {
frame.syncFrameToScope(useRefs = true)
}
val decl = frame.fn.constants[constId] as? BytecodeConst.DestructureAssign
?: error("ASSIGN_DESTRUCTURE expects DestructureAssign at $constId")
val value = frame.slotToObj(slot)
decl.pattern.setAt(decl.pos, frame.ensureScope(), value)
if (frame.fn.localSlotNames.isNotEmpty()) {
frame.syncScopeToFrame()
}
frame.storeObjResult(slot, value)
return
}
}
@ -1296,6 +1317,14 @@ class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cm
type = ObjRecord.Type.Property
)
)
val getterName = extensionPropertyGetterName(decl.extTypeName, decl.property.name)
val getterWrapper = ObjExtensionPropertyGetterCallable(decl.property.name, decl.property)
frame.ensureScope().addItem(getterName, false, getterWrapper, decl.visibility, recordType = ObjRecord.Type.Fun)
if (decl.property.setter != null) {
val setterName = extensionPropertySetterName(decl.extTypeName, decl.property.name)
val setterWrapper = ObjExtensionPropertySetterCallable(decl.property.name, decl.property)
frame.ensureScope().addItem(setterName, false, setterWrapper, decl.visibility, recordType = ObjRecord.Type.Fun)
}
frame.setObj(slot, decl.property)
return
}
@ -1449,7 +1478,12 @@ class CmdGetMemberSlot(
}
} else null
} ?: frame.ensureScope().raiseSymbolNotFound("member")
val name = rec.memberName ?: "<member>"
val rawName = rec.memberName ?: "<member>"
val name = if (receiver is ObjInstance && rawName.contains("::")) {
rawName.substringAfterLast("::")
} else {
rawName
}
suspend fun autoCallIfMethod(resolved: ObjRecord, recv: Obj): Obj {
return if (resolved.type == ObjRecord.Type.Fun && !resolved.isAbstract) {
resolved.value.invoke(frame.ensureScope(), resolved.receiver ?: recv, Arguments.EMPTY, resolved.declaringClass)
@ -1498,12 +1532,17 @@ class CmdSetMemberSlot(
}
} else null
} ?: frame.ensureScope().raiseSymbolNotFound("member")
val name = rec.memberName ?: "<member>"
val rawName = rec.memberName ?: "<member>"
val name = if (receiver is ObjInstance && rawName.contains("::")) {
rawName.substringAfterLast("::")
} else {
rawName
}
if (receiver is ObjQualifiedView) {
receiver.writeField(frame.ensureScope(), name, frame.slotToObj(valueSlot))
return
}
frame.ensureScope().assign(rec, name, frame.slotToObj(valueSlot))
receiver.writeField(frame.ensureScope(), name, frame.slotToObj(valueSlot))
return
}
}
@ -2383,10 +2422,10 @@ class CmdFrame(
val direct = record.value
if (direct is FrameSlotRef) return direct.read()
val name = fn.scopeSlotNames[slot]
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
return target.resolve(record, name)
}
if (direct !== ObjUnset) {
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
return target.resolve(record, name)
}
return direct
}
if (name == null) return record.value
@ -2405,10 +2444,10 @@ class CmdFrame(
if (direct is FrameSlotRef) return direct.read()
val slotId = addrScopeSlots[addrSlot]
val name = fn.scopeSlotNames.getOrNull(slotId)
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
return target.resolve(record, name)
}
if (direct !== ObjUnset) {
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
return target.resolve(record, name)
}
return direct
}
if (name == null) return record.value

View File

@ -132,6 +132,7 @@ enum class Opcode(val code: Int) {
CLEAR_PENDING_THROWABLE(0x8F),
CALL_DIRECT(0x90),
ASSIGN_DESTRUCTURE(0x91),
CALL_MEMBER_SLOT(0x92),
CALL_SLOT(0x93),