Fix bytecode object refs and post-inc
This commit is contained in:
parent
5b1a8af4e3
commit
8a7582891a
@ -35,10 +35,12 @@ Goal: migrate the compiler so all values live in frames/bytecode, keeping JVM te
|
|||||||
- [ ] Step 10: Bytecode for declaration statements in module scripts.
|
- [ ] Step 10: Bytecode for declaration statements in module scripts.
|
||||||
- [ ] Support `ClassDeclStatement`, `FunctionDeclStatement`, `EnumDeclStatement` in bytecode compilation.
|
- [ ] Support `ClassDeclStatement`, `FunctionDeclStatement`, `EnumDeclStatement` in bytecode compilation.
|
||||||
- [ ] Decide whether to compile declarations into module bytecode or keep a mixed execution path.
|
- [ ] Decide whether to compile declarations into module bytecode or keep a mixed execution path.
|
||||||
|
- [x] Ensure module object member refs compile as instance access (not class-scope).
|
||||||
- [ ] Step 11: Destructuring assignment bytecode.
|
- [ ] Step 11: Destructuring assignment bytecode.
|
||||||
- [ ] Handle `[a, b] = expr` (AssignRef target `ListLiteralRef`) without interpreter fallback.
|
- [ ] Handle `[a, b] = expr` (AssignRef target `ListLiteralRef`) without interpreter fallback.
|
||||||
- [ ] Step 12: Optional member assign-ops and inc/dec in bytecode.
|
- [ ] Step 12: Optional member assign-ops and inc/dec in bytecode.
|
||||||
- [ ] Support `a?.b += 1` and `a?.b++` for `FieldRef` targets.
|
- [ ] Support `a?.b += 1` and `a?.b++` for `FieldRef` targets.
|
||||||
|
- [x] Fix post-inc return value for object slots stored in scope frames.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@ -28,4 +28,9 @@ class ClassDeclStatement(
|
|||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
return delegate.execute(scope)
|
return delegate.execute(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
|
val target = scope.parent ?: scope
|
||||||
|
return delegate.execute(target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -165,6 +165,7 @@ class Compiler(
|
|||||||
private val classScopeCallableMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
private val classScopeCallableMembersByClassName: MutableMap<String, MutableSet<String>> = mutableMapOf()
|
||||||
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
private val encodedPayloadTypeByScopeId: MutableMap<Int, MutableMap<Int, ObjClass>> = mutableMapOf()
|
||||||
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
private val encodedPayloadTypeByName: MutableMap<String, ObjClass> = mutableMapOf()
|
||||||
|
private val objectDeclNames: MutableSet<String> = mutableSetOf()
|
||||||
|
|
||||||
private fun seedSlotPlanFromScope(scope: Scope, includeParents: Boolean = false) {
|
private fun seedSlotPlanFromScope(scope: Scope, includeParents: Boolean = false) {
|
||||||
val plan = moduleSlotPlan() ?: return
|
val plan = moduleSlotPlan() ?: return
|
||||||
@ -1536,7 +1537,12 @@ class Compiler(
|
|||||||
allowedScopeNames = modulePlan.keys,
|
allowedScopeNames = modulePlan.keys,
|
||||||
moduleScopeId = moduleSlotPlan()?.id,
|
moduleScopeId = moduleSlotPlan()?.id,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownClassMapForBytecode()
|
knownNameObjClass = knownClassMapForBytecode(),
|
||||||
|
knownObjectNames = objectDeclNames,
|
||||||
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
|
enumEntriesByName = enumEntriesByName,
|
||||||
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
|
callableReturnTypeByName = callableReturnTypeByName
|
||||||
) as BytecodeStatement
|
) as BytecodeStatement
|
||||||
unwrapped to bytecodeStmt.bytecodeFunction()
|
unwrapped to bytecodeStmt.bytecodeFunction()
|
||||||
} else {
|
} else {
|
||||||
@ -1830,7 +1836,12 @@ class Compiler(
|
|||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleSlotPlan()?.id,
|
moduleScopeId = moduleSlotPlan()?.id,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownClassMapForBytecode()
|
knownNameObjClass = knownClassMapForBytecode(),
|
||||||
|
knownObjectNames = objectDeclNames,
|
||||||
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
|
enumEntriesByName = enumEntriesByName,
|
||||||
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
|
callableReturnTypeByName = callableReturnTypeByName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1861,7 +1872,12 @@ class Compiler(
|
|||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleSlotPlan()?.id,
|
moduleScopeId = moduleSlotPlan()?.id,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
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 ReturnStatement -> target.resultExpr?.let { containsUnsupportedForBytecode(it) } ?: false
|
||||||
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
|
is ThrowStatement -> containsUnsupportedForBytecode(target.throwExpr)
|
||||||
is ExtensionPropertyDeclStatement -> false
|
is ExtensionPropertyDeclStatement -> false
|
||||||
|
is ClassDeclStatement -> false
|
||||||
|
is FunctionDeclStatement -> false
|
||||||
|
is EnumDeclStatement -> false
|
||||||
is TryStatement -> {
|
is TryStatement -> {
|
||||||
containsUnsupportedForBytecode(target.body) ||
|
containsUnsupportedForBytecode(target.body) ||
|
||||||
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
|
target.catches.any { containsUnsupportedForBytecode(it.block) } ||
|
||||||
@ -5660,6 +5679,9 @@ class Compiler(
|
|||||||
if (declaredName != null) {
|
if (declaredName != null) {
|
||||||
resolutionSink?.declareSymbol(declaredName, SymbolKind.CLASS, isMutable = false, pos = nameToken!!.pos)
|
resolutionSink?.declareSymbol(declaredName, SymbolKind.CLASS, isMutable = false, pos = nameToken!!.pos)
|
||||||
declareLocalName(declaredName, isMutable = false)
|
declareLocalName(declaredName, isMutable = false)
|
||||||
|
if (codeContexts.lastOrNull() is CodeContext.Module) {
|
||||||
|
objectDeclNames.add(declaredName)
|
||||||
|
}
|
||||||
if (outerClassName != null) {
|
if (outerClassName != null) {
|
||||||
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
val outerCtx = codeContexts.asReversed().firstOrNull { it is CodeContext.ClassBody } as? CodeContext.ClassBody
|
||||||
outerCtx?.classScopeMembers?.add(declaredName)
|
outerCtx?.classScopeMembers?.add(declaredName)
|
||||||
@ -6612,12 +6634,18 @@ class Compiler(
|
|||||||
val declKind = if (parentContext is CodeContext.ClassBody) SymbolKind.MEMBER else SymbolKind.FUNCTION
|
val declKind = if (parentContext is CodeContext.ClassBody) SymbolKind.MEMBER else SymbolKind.FUNCTION
|
||||||
resolutionSink?.declareSymbol(name, declKind, isMutable = false, pos = nameStartPos, isOverride = isOverride)
|
resolutionSink?.declareSymbol(name, declKind, isMutable = false, pos = nameStartPos, isOverride = isOverride)
|
||||||
if (parentContext is CodeContext.ClassBody && extTypeName == null) {
|
if (parentContext is CodeContext.ClassBody && extTypeName == null) {
|
||||||
|
if (isStatic) {
|
||||||
|
parentContext.classScopeMembers.add(name)
|
||||||
|
registerClassScopeMember(parentContext.name, name)
|
||||||
|
registerClassScopeCallableMember(parentContext.name, name)
|
||||||
|
} else {
|
||||||
parentContext.declaredMembers.add(name)
|
parentContext.declaredMembers.add(name)
|
||||||
if (!parentContext.memberMethodIds.containsKey(name)) {
|
if (!parentContext.memberMethodIds.containsKey(name)) {
|
||||||
parentContext.memberMethodIds[name] = parentContext.nextMethodId++
|
parentContext.memberMethodIds[name] = parentContext.nextMethodId++
|
||||||
}
|
}
|
||||||
memberMethodId = parentContext.memberMethodIds[name]
|
memberMethodId = parentContext.memberMethodIds[name]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (declKind != SymbolKind.MEMBER) {
|
if (declKind != SymbolKind.MEMBER) {
|
||||||
declareLocalName(name, isMutable = false)
|
declareLocalName(name, isMutable = false)
|
||||||
}
|
}
|
||||||
@ -7752,7 +7780,10 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (declaringClassNameCaptured != null && extTypeName == null) {
|
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 != null) {
|
||||||
if (!classCtx.memberMethodIds.containsKey(name)) {
|
if (!classCtx.memberMethodIds.containsKey(name)) {
|
||||||
classCtx.memberMethodIds[name] = classCtx.nextMethodId++
|
classCtx.memberMethodIds[name] = classCtx.nextMethodId++
|
||||||
|
|||||||
@ -27,4 +27,9 @@ class EnumDeclStatement(
|
|||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
return delegate.execute(scope)
|
return delegate.execute(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
|
val target = scope.parent ?: scope
|
||||||
|
return delegate.execute(target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,4 +27,9 @@ class FunctionDeclStatement(
|
|||||||
override suspend fun execute(scope: Scope): Obj {
|
override suspend fun execute(scope: Scope): Obj {
|
||||||
return delegate.execute(scope)
|
return delegate.execute(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun callOn(scope: Scope): Obj {
|
||||||
|
val target = scope.parent ?: scope
|
||||||
|
return delegate.execute(target)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,11 @@ class BytecodeCompiler(
|
|||||||
private val moduleScopeId: Int? = null,
|
private val moduleScopeId: Int? = null,
|
||||||
private val slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
private val slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
private val knownNameObjClass: Map<String, 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 builder = CmdBuilder()
|
||||||
private var nextSlot = 0
|
private var nextSlot = 0
|
||||||
@ -54,7 +59,8 @@ class BytecodeCompiler(
|
|||||||
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
|
private val localRangeRefs = LinkedHashMap<ScopeSlotKey, RangeRef>()
|
||||||
private val slotTypes = mutableMapOf<Int, SlotType>()
|
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||||
private val slotObjClass = mutableMapOf<Int, ObjClass>()
|
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 slotInitClassByKey = mutableMapOf<ScopeSlotKey, ObjClass>()
|
||||||
private val intLoopVarNames = LinkedHashSet<String>()
|
private val intLoopVarNames = LinkedHashSet<String>()
|
||||||
private val loopStack = ArrayDeque<LoopContext>()
|
private val loopStack = ArrayDeque<LoopContext>()
|
||||||
@ -148,6 +154,54 @@ class BytecodeCompiler(
|
|||||||
localSlotMutables
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1689,6 +1743,25 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
return value
|
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 fieldId = receiverClass.instanceFieldIdMap()[target.name]
|
||||||
val methodId = if (fieldId == null) {
|
val methodId = if (fieldId == null) {
|
||||||
receiverClass.instanceMethodIdMap(includeAbstract = true)[target.name]
|
receiverClass.instanceMethodIdMap(includeAbstract = true)[target.name]
|
||||||
@ -1887,28 +1960,105 @@ class BytecodeCompiler(
|
|||||||
} ?: return compileEvalRef(ref)
|
} ?: return compileEvalRef(ref)
|
||||||
val fieldTarget = ref.target as? FieldRef
|
val fieldTarget = ref.target as? FieldRef
|
||||||
if (fieldTarget != null) {
|
if (fieldTarget != null) {
|
||||||
if (fieldTarget.isOptional) return compileEvalRef(ref)
|
|
||||||
val receiverClass = resolveReceiverClass(fieldTarget.target)
|
val receiverClass = resolveReceiverClass(fieldTarget.target)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
"Member assignment requires compile-time receiver type: ${fieldTarget.name}",
|
"Member assignment requires compile-time receiver type: ${fieldTarget.name}",
|
||||||
Pos.builtIn
|
Pos.builtIn
|
||||||
)
|
)
|
||||||
if (receiverClass == ObjDynamic.type) {
|
|
||||||
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
|
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
|
||||||
val current = allocSlot()
|
val current = allocSlot()
|
||||||
val result = allocSlot()
|
val result = allocSlot()
|
||||||
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
val rhs = compileRef(ref.value) ?: return compileEvalRef(ref)
|
||||||
|
if (receiverClass == ObjDynamic.type) {
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
||||||
|
if (!fieldTarget.isOptional) {
|
||||||
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, current)
|
builder.emit(Opcode.GET_DYNAMIC_MEMBER, receiver.slot, nameId, current)
|
||||||
builder.emit(objOp, current, rhs.slot, result)
|
builder.emit(objOp, current, rhs.slot, result)
|
||||||
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
|
builder.emit(Opcode.SET_DYNAMIC_MEMBER, receiver.slot, nameId, result)
|
||||||
updateSlotType(result, SlotType.OBJ)
|
updateSlotType(result, SlotType.OBJ)
|
||||||
return CompiledValue(result, SlotType.OBJ)
|
return CompiledValue(result, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
throw BytecodeCompileException(
|
val nullSlot = allocSlot()
|
||||||
"Member assignment requires compile-time receiver type: ${fieldTarget.name}",
|
builder.emit(Opcode.CONST_NULL, nullSlot)
|
||||||
Pos.builtIn
|
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, current)
|
||||||
|
builder.emit(objOp, current, rhs.slot, result)
|
||||||
|
builder.mark(endLabel)
|
||||||
|
updateSlotType(result, SlotType.OBJ)
|
||||||
|
return CompiledValue(result, 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 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, current)
|
||||||
|
builder.emit(objOp, current, rhs.slot, 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, current)
|
||||||
|
builder.emit(objOp, current, rhs.slot, result)
|
||||||
|
builder.mark(endLabel)
|
||||||
|
}
|
||||||
|
updateSlotType(result, SlotType.OBJ)
|
||||||
|
return CompiledValue(result, SlotType.OBJ)
|
||||||
|
}
|
||||||
|
return compileEvalRef(ref)
|
||||||
}
|
}
|
||||||
val implicitTarget = ref.target as? ImplicitThisMemberRef
|
val implicitTarget = ref.target as? ImplicitThisMemberRef
|
||||||
if (implicitTarget != null) {
|
if (implicitTarget != null) {
|
||||||
@ -2190,6 +2340,30 @@ class BytecodeCompiler(
|
|||||||
val encodedMethodId = encodeMemberId(receiverClass, methodId)
|
val encodedMethodId = encodeMemberId(receiverClass, methodId)
|
||||||
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
|
||||||
val dst = allocSlot()
|
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 (fieldId != null || methodId != null) {
|
||||||
if (!ref.isOptional) {
|
if (!ref.isOptional) {
|
||||||
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, encodedFieldId ?: -1, encodedMethodId ?: -1, dst)
|
builder.emit(Opcode.GET_MEMBER_SLOT, receiver.slot, encodedFieldId ?: -1, encodedMethodId ?: -1, dst)
|
||||||
@ -2507,13 +2681,15 @@ class BytecodeCompiler(
|
|||||||
val boxed = allocSlot()
|
val boxed = allocSlot()
|
||||||
builder.emit(Opcode.BOX_OBJ, current, boxed)
|
builder.emit(Opcode.BOX_OBJ, current, boxed)
|
||||||
if (wantResult && ref.isPost) {
|
if (wantResult && ref.isPost) {
|
||||||
|
val old = allocSlot()
|
||||||
|
builder.emit(Opcode.MOVE_OBJ, boxed, old)
|
||||||
val result = allocSlot()
|
val result = allocSlot()
|
||||||
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, boxed, oneSlot, result)
|
builder.emit(op, boxed, oneSlot, result)
|
||||||
builder.emit(Opcode.MOVE_OBJ, result, boxed)
|
builder.emit(Opcode.MOVE_OBJ, result, boxed)
|
||||||
emitStoreToAddr(boxed, addrSlot, SlotType.OBJ)
|
emitStoreToAddr(boxed, addrSlot, SlotType.OBJ)
|
||||||
updateSlotType(slot, SlotType.OBJ)
|
updateSlotType(slot, SlotType.OBJ)
|
||||||
CompiledValue(boxed, SlotType.OBJ)
|
CompiledValue(old, SlotType.OBJ)
|
||||||
} else {
|
} else {
|
||||||
val result = allocSlot()
|
val result = allocSlot()
|
||||||
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
|
val op = if (ref.isIncrement) Opcode.ADD_OBJ else Opcode.SUB_OBJ
|
||||||
@ -3092,6 +3268,34 @@ class BytecodeCompiler(
|
|||||||
builder.mark(endLabel)
|
builder.mark(endLabel)
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
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)
|
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
"Unknown member ${ref.name} on ${receiverClass.className}",
|
"Unknown member ${ref.name} on ${receiverClass.className}",
|
||||||
@ -3476,6 +3680,17 @@ class BytecodeCompiler(
|
|||||||
throw BytecodeCompileException("Unsupported statement in bytecode: $stmtName", stmt.pos)
|
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? {
|
private fun compileStatementValueOrFallback(stmt: Statement, needResult: Boolean = true): CompiledValue? {
|
||||||
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||||
setPos(target.pos)
|
setPos(target.pos)
|
||||||
@ -3509,9 +3724,9 @@ class BytecodeCompiler(
|
|||||||
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
||||||
is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target)
|
is DestructuringVarDeclStatement -> emitDestructuringVarDecl(target)
|
||||||
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
|
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> emitExtensionPropertyDecl(target)
|
||||||
is net.sergeych.lyng.ClassDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.ClassDeclStatement -> emitStatementCall(target)
|
||||||
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.FunctionDeclStatement -> emitStatementCall(target)
|
||||||
is net.sergeych.lyng.EnumDeclStatement -> emitStatementEval(target)
|
is net.sergeych.lyng.EnumDeclStatement -> emitStatementCall(target)
|
||||||
is net.sergeych.lyng.TryStatement -> emitTry(target, true)
|
is net.sergeych.lyng.TryStatement -> emitTry(target, true)
|
||||||
is net.sergeych.lyng.WhenStatement -> compileWhen(target, true)
|
is net.sergeych.lyng.WhenStatement -> compileWhen(target, true)
|
||||||
is net.sergeych.lyng.BreakStatement -> compileBreak(target)
|
is net.sergeych.lyng.BreakStatement -> compileBreak(target)
|
||||||
@ -3536,6 +3751,9 @@ class BytecodeCompiler(
|
|||||||
is VarDeclStatement -> emitVarDecl(target)
|
is VarDeclStatement -> emitVarDecl(target)
|
||||||
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
is DelegatedVarDeclStatement -> emitDelegatedVarDecl(target)
|
||||||
is IfStatement -> compileIfStatement(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 -> {
|
is net.sergeych.lyng.ForInStatement -> {
|
||||||
val resultSlot = emitForIn(target, false) ?: return null
|
val resultSlot = emitForIn(target, false) ?: return null
|
||||||
CompiledValue(resultSlot, SlotType.OBJ)
|
CompiledValue(resultSlot, SlotType.OBJ)
|
||||||
@ -4986,6 +5204,9 @@ class BytecodeCompiler(
|
|||||||
private fun resolveReceiverClass(ref: ObjRef): ObjClass? {
|
private fun resolveReceiverClass(ref: ObjRef): ObjClass? {
|
||||||
return when (ref) {
|
return when (ref) {
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
|
if (knownObjectNames.contains(ref.name)) {
|
||||||
|
return nameObjClass[ref.name] ?: ObjDynamic.type
|
||||||
|
}
|
||||||
val slot = resolveSlot(ref)
|
val slot = resolveSlot(ref)
|
||||||
val fromSlot = slot?.let { slotObjClass[it] }
|
val fromSlot = slot?.let { slotObjClass[it] }
|
||||||
fromSlot
|
fromSlot
|
||||||
@ -5002,6 +5223,9 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is LocalVarRef -> {
|
is LocalVarRef -> {
|
||||||
|
if (knownObjectNames.contains(ref.name)) {
|
||||||
|
return nameObjClass[ref.name] ?: ObjDynamic.type
|
||||||
|
}
|
||||||
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
val fromSlot = resolveDirectNameSlot(ref.name)?.let { slotObjClass[it.slot] }
|
||||||
if (fromSlot != null) return fromSlot
|
if (fromSlot != null) return fromSlot
|
||||||
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
val key = localSlotInfoMap.entries.firstOrNull { it.value.name == ref.name }?.key
|
||||||
@ -5046,6 +5270,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 {
|
private fun isAllowedObjectMember(memberName: String): Boolean {
|
||||||
return when (memberName) {
|
return when (memberName) {
|
||||||
"toString",
|
"toString",
|
||||||
@ -5149,8 +5381,12 @@ class BytecodeCompiler(
|
|||||||
|
|
||||||
private fun inferCallReturnClass(ref: CallRef): ObjClass? {
|
private fun inferCallReturnClass(ref: CallRef): ObjClass? {
|
||||||
return when (val target = ref.target) {
|
return when (val target = ref.target) {
|
||||||
is LocalSlotRef -> nameObjClass[target.name] ?: resolveTypeNameClass(target.name)
|
is LocalSlotRef -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
|
||||||
is LocalVarRef -> nameObjClass[target.name] ?: resolveTypeNameClass(target.name)
|
?: nameObjClass[target.name]
|
||||||
|
?: resolveTypeNameClass(target.name)
|
||||||
|
is LocalVarRef -> callableReturnTypeByName[target.name]
|
||||||
|
?: nameObjClass[target.name]
|
||||||
|
?: resolveTypeNameClass(target.name)
|
||||||
is ConstRef -> target.constValue as? ObjClass
|
is ConstRef -> target.constValue as? ObjClass
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@ -5214,6 +5450,16 @@ class BytecodeCompiler(
|
|||||||
private fun inferFieldReturnClass(targetClass: ObjClass?, name: String): ObjClass? {
|
private fun inferFieldReturnClass(targetClass: ObjClass?, name: String): ObjClass? {
|
||||||
if (targetClass == null) return null
|
if (targetClass == null) return null
|
||||||
if (targetClass == ObjDynamic.type) return ObjDynamic.type
|
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")) {
|
if (targetClass == ObjInstant.type && (name == "distantFuture" || name == "distantPast")) {
|
||||||
return ObjInstant.type
|
return ObjInstant.type
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,6 +45,11 @@ class BytecodeStatement private constructor(
|
|||||||
moduleScopeId: Int? = null,
|
moduleScopeId: Int? = null,
|
||||||
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
slotTypeByScopeId: Map<Int, Map<Int, ObjClass>> = emptyMap(),
|
||||||
knownNameObjClass: Map<String, 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 {
|
): Statement {
|
||||||
if (statement is BytecodeStatement) return statement
|
if (statement is BytecodeStatement) return statement
|
||||||
val hasUnsupported = containsUnsupportedStatement(statement)
|
val hasUnsupported = containsUnsupportedStatement(statement)
|
||||||
@ -63,7 +68,12 @@ class BytecodeStatement private constructor(
|
|||||||
allowedScopeNames = allowedScopeNames,
|
allowedScopeNames = allowedScopeNames,
|
||||||
moduleScopeId = moduleScopeId,
|
moduleScopeId = moduleScopeId,
|
||||||
slotTypeByScopeId = slotTypeByScopeId,
|
slotTypeByScopeId = slotTypeByScopeId,
|
||||||
knownNameObjClass = knownNameObjClass
|
knownNameObjClass = knownNameObjClass,
|
||||||
|
knownObjectNames = knownObjectNames,
|
||||||
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
|
enumEntriesByName = enumEntriesByName,
|
||||||
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
|
callableReturnTypeByName = callableReturnTypeByName
|
||||||
)
|
)
|
||||||
val compiled = compiler.compileStatement(nameHint, statement)
|
val compiled = compiler.compileStatement(nameHint, statement)
|
||||||
val fn = compiled ?: throw BytecodeCompileException(
|
val fn = compiled ?: throw BytecodeCompileException(
|
||||||
|
|||||||
@ -240,7 +240,9 @@ class CmdAssertIs(internal val objSlot: Int, internal val typeSlot: Int) : Cmd()
|
|||||||
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
"${typeObj.inspect(frame.ensureScope())} is not the class instance"
|
||||||
)
|
)
|
||||||
if (!obj.isInstanceOf(clazz)) {
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -1296,6 +1298,14 @@ class CmdDeclExtProperty(internal val constId: Int, internal val slot: Int) : Cm
|
|||||||
type = ObjRecord.Type.Property
|
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)
|
frame.setObj(slot, decl.property)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1449,7 +1459,12 @@ class CmdGetMemberSlot(
|
|||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
} ?: frame.ensureScope().raiseSymbolNotFound("member")
|
} ?: 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 {
|
suspend fun autoCallIfMethod(resolved: ObjRecord, recv: Obj): Obj {
|
||||||
return if (resolved.type == ObjRecord.Type.Fun && !resolved.isAbstract) {
|
return if (resolved.type == ObjRecord.Type.Fun && !resolved.isAbstract) {
|
||||||
resolved.value.invoke(frame.ensureScope(), resolved.receiver ?: recv, Arguments.EMPTY, resolved.declaringClass)
|
resolved.value.invoke(frame.ensureScope(), resolved.receiver ?: recv, Arguments.EMPTY, resolved.declaringClass)
|
||||||
@ -1498,12 +1513,17 @@ class CmdSetMemberSlot(
|
|||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
} ?: frame.ensureScope().raiseSymbolNotFound("member")
|
} ?: 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) {
|
if (receiver is ObjQualifiedView) {
|
||||||
receiver.writeField(frame.ensureScope(), name, frame.slotToObj(valueSlot))
|
receiver.writeField(frame.ensureScope(), name, frame.slotToObj(valueSlot))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frame.ensureScope().assign(rec, name, frame.slotToObj(valueSlot))
|
receiver.writeField(frame.ensureScope(), name, frame.slotToObj(valueSlot))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2383,10 +2403,10 @@ class CmdFrame(
|
|||||||
val direct = record.value
|
val direct = record.value
|
||||||
if (direct is FrameSlotRef) return direct.read()
|
if (direct is FrameSlotRef) return direct.read()
|
||||||
val name = fn.scopeSlotNames[slot]
|
val name = fn.scopeSlotNames[slot]
|
||||||
if (direct !== ObjUnset) {
|
|
||||||
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
|
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
|
||||||
return target.resolve(record, name)
|
return target.resolve(record, name)
|
||||||
}
|
}
|
||||||
|
if (direct !== ObjUnset) {
|
||||||
return direct
|
return direct
|
||||||
}
|
}
|
||||||
if (name == null) return record.value
|
if (name == null) return record.value
|
||||||
@ -2405,10 +2425,10 @@ class CmdFrame(
|
|||||||
if (direct is FrameSlotRef) return direct.read()
|
if (direct is FrameSlotRef) return direct.read()
|
||||||
val slotId = addrScopeSlots[addrSlot]
|
val slotId = addrScopeSlots[addrSlot]
|
||||||
val name = fn.scopeSlotNames.getOrNull(slotId)
|
val name = fn.scopeSlotNames.getOrNull(slotId)
|
||||||
if (direct !== ObjUnset) {
|
|
||||||
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
|
if (name != null && (record.type == ObjRecord.Type.Delegated || record.type == ObjRecord.Type.Property || direct is ObjProperty)) {
|
||||||
return target.resolve(record, name)
|
return target.resolve(record, name)
|
||||||
}
|
}
|
||||||
|
if (direct !== ObjUnset) {
|
||||||
return direct
|
return direct
|
||||||
}
|
}
|
||||||
if (name == null) return record.value
|
if (name == null) return record.value
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user