Improve receiver class handling and enhance resolution logic

This commit is contained in:
Sergey Chernov 2026-02-17 12:35:31 +03:00
parent df19524177
commit 69156893d4
2 changed files with 118 additions and 24 deletions

View File

@ -2992,13 +2992,27 @@ class Compiler(
paramKnownClasses[param.name] = cls paramKnownClasses[param.name] = cls
} }
val returnLabels = label?.let { setOf(it) } ?: emptySet() val returnLabels = label?.let { setOf(it) } ?: emptySet()
val extraKnownNames = if (compileClassInfos.isEmpty()) {
paramKnownClasses
} else {
val merged = LinkedHashMap<String, ObjClass>()
for (className in compileClassInfos.keys) {
merged[className] = if (objectDeclNames.contains(className)) {
ObjDynamic.type
} else {
ObjClassType
}
}
merged.putAll(paramKnownClasses)
merged
}
val fnStatements = if (compileBytecode) { val fnStatements = if (compileBytecode) {
returnLabelStack.addLast(returnLabels) returnLabelStack.addLast(returnLabels)
try { try {
wrapFunctionBytecode( wrapFunctionBytecode(
body, body,
"<lambda>", "<lambda>",
paramKnownClasses, extraKnownNames,
forcedLocalSlots = paramSlotPlanSnapshot, forcedLocalSlots = paramSlotPlanSnapshot,
forcedLocalScopeId = paramSlotPlan.id forcedLocalScopeId = paramSlotPlan.id
) )

View File

@ -1142,7 +1142,11 @@ class BytecodeCompiler(
): CompiledValue? { ): CompiledValue? {
val memberName = operatorMemberName(op) ?: return null val memberName = operatorMemberName(op) ?: return null
val receiverClass = resolveReceiverClass(leftRef) val receiverClass = resolveReceiverClass(leftRef)
if (receiverClass == null) { if (receiverClass == null ||
receiverClass.className == "Delegate" ||
receiverClass.className == "LazyDelegate" ||
receiverClass.implementingNames.contains("Delegate")
) {
val objOpcode = when (op) { val objOpcode = when (op) {
BinOp.PLUS -> Opcode.ADD_OBJ BinOp.PLUS -> Opcode.ADD_OBJ
BinOp.MINUS -> Opcode.SUB_OBJ BinOp.MINUS -> Opcode.SUB_OBJ
@ -2517,7 +2521,9 @@ class BytecodeCompiler(
} }
return value return value
} }
if (isKnownClassReceiver(target.target)) { if ((isKnownClassReceiver(target.target) || isClassNameRef(target.target, receiverClass)) &&
(isClassSlot(receiver.slot) || receiverClass == ObjClassType)
) {
val nameId = builder.addConst(BytecodeConst.StringVal(target.name)) val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
if (!target.isOptional) { if (!target.isOptional) {
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot) builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot)
@ -2866,7 +2872,7 @@ class BytecodeCompiler(
} }
val fieldId = if (resolvedMember != null) receiverClass.instanceFieldIdMap()[fieldTarget.name] else null val fieldId = if (resolvedMember != null) receiverClass.instanceFieldIdMap()[fieldTarget.name] else null
val methodId = if (resolvedMember != null) receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name] else null val methodId = if (resolvedMember != null) receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name] else null
if (fieldId == null && methodId == null && isKnownClassReceiver(fieldTarget.target)) { if (fieldId == null && methodId == null && (receiverClass == ObjClassType || isKnownClassReceiver(fieldTarget.target) || isClassNameRef(fieldTarget.target, receiverClass))) {
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name)) val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
if (!fieldTarget.isOptional) { if (!fieldTarget.isOptional) {
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current) builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current)
@ -3259,6 +3265,9 @@ class BytecodeCompiler(
} }
private fun compileFieldRef(ref: FieldRef): CompiledValue? { private fun compileFieldRef(ref: FieldRef): CompiledValue? {
if (ref.name.isBlank()) {
return compileRefWithFallback(ref.target, null, Pos.builtIn)
}
val receiverClass = resolveReceiverClass(ref.target) ?: ObjDynamic.type val receiverClass = resolveReceiverClass(ref.target) ?: ObjDynamic.type
if (receiverClass == ObjDynamic.type) { if (receiverClass == ObjDynamic.type) {
val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null val receiver = compileRefWithFallback(ref.target, null, Pos.builtIn) ?: return null
@ -3345,7 +3354,9 @@ 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)) { if (fieldId == null && methodId == null && (isKnownClassReceiver(ref.target) || isClassNameRef(ref.target, receiverClass)) &&
(isClassSlot(receiver.slot) || receiverClass == ObjClassType)
) {
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name)) val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
if (!ref.isOptional) { if (!ref.isOptional) {
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst) builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst)
@ -3939,8 +3950,8 @@ class BytecodeCompiler(
"Member access requires compile-time receiver type: ${fieldTarget.name}", "Member access 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
if (receiverClass == ObjDynamic.type) {
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name)) val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
val resultSlot = allocSlot() val resultSlot = allocSlot()
if (fieldTarget.isOptional) { if (fieldTarget.isOptional) {
@ -4005,8 +4016,9 @@ class BytecodeCompiler(
} }
val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name] val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name]
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name] val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name]
if (fieldId == null && methodId == null && isKnownClassReceiver(fieldTarget.target)) { if (fieldId == null && methodId == null && (isKnownClassReceiver(fieldTarget.target) || isClassNameRef(fieldTarget.target, receiverClass)) &&
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null (isClassSlot(receiver.slot) || receiverClass == ObjClassType)
) {
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name)) val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
val resultSlot = allocSlot() val resultSlot = allocSlot()
if (fieldTarget.isOptional) { if (fieldTarget.isOptional) {
@ -4070,7 +4082,6 @@ class BytecodeCompiler(
return CompiledValue(resultSlot, SlotType.OBJ) return CompiledValue(resultSlot, SlotType.OBJ)
} }
if (fieldId == null && methodId == null) return null if (fieldId == null && methodId == null) return null
val receiver = compileRefWithFallback(fieldTarget.target, null, Pos.builtIn) ?: return null
val resultSlot = allocSlot() val resultSlot = allocSlot()
if (fieldTarget.isOptional) { if (fieldTarget.isOptional) {
val nullSlot = allocSlot() val nullSlot = allocSlot()
@ -5107,6 +5118,7 @@ class BytecodeCompiler(
} ?: allocSlot() } ?: allocSlot()
builder.emit(Opcode.DECL_CLASS, constId, dst) builder.emit(Opcode.DECL_CLASS, constId, dst)
updateSlotType(dst, SlotType.OBJ) updateSlotType(dst, SlotType.OBJ)
slotObjClass[dst] = if (stmt.spec.isObject) ObjDynamic.type else ObjClassType
stmt.spec.declaredName?.let { updateNameObjClassFromSlot(it, dst) } stmt.spec.declaredName?.let { updateNameObjClassFromSlot(it, dst) }
return CompiledValue(dst, SlotType.OBJ) return CompiledValue(dst, SlotType.OBJ)
} }
@ -7065,26 +7077,53 @@ class BytecodeCompiler(
return when (ref) { return when (ref) {
is LocalVarRef -> { is LocalVarRef -> {
val name = ref.name val name = ref.name
if (nameObjClass[name] == ObjClassType) return true
val directSlot = resolveDirectNameSlot(name)?.slot
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
if (localSlotIndexByName.containsKey(name)) return false if (localSlotIndexByName.containsKey(name)) return false
if (localSlotInfoMap.values.any { it.name == name }) return false if (localSlotInfoMap.values.any { it.name == name }) return false
knownClassNames.contains(name) && !knownObjectNames.contains(name) (knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
!knownObjectNames.contains(name)
} }
is LocalSlotRef -> { is LocalSlotRef -> {
val name = ref.name val name = ref.name
if (slotObjClass[ref.slot] == ObjClassType) return true
if (nameObjClass[name] == ObjClassType) return true
val directSlot = resolveDirectNameSlot(name)?.slot
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
if (localSlotIndexByName.containsKey(name)) return false if (localSlotIndexByName.containsKey(name)) return false
if (localSlotInfoMap.values.any { it.name == name }) return false if (localSlotInfoMap.values.any { it.name == name }) return false
knownClassNames.contains(name) && !knownObjectNames.contains(name) (knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
!knownObjectNames.contains(name)
} }
is FastLocalVarRef -> { is FastLocalVarRef -> {
val name = ref.name val name = ref.name
if (nameObjClass[name] == ObjClassType) return true
val directSlot = resolveDirectNameSlot(name)?.slot
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
if (localSlotIndexByName.containsKey(name)) return false if (localSlotIndexByName.containsKey(name)) return false
if (localSlotInfoMap.values.any { it.name == name }) return false if (localSlotInfoMap.values.any { it.name == name }) return false
knownClassNames.contains(name) && !knownObjectNames.contains(name) (knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
!knownObjectNames.contains(name)
} }
else -> false else -> false
} }
} }
private fun isClassNameRef(ref: ObjRef, receiverClass: ObjClass): Boolean {
if (receiverClass !is ObjInstanceClass) return false
val name = when (ref) {
is LocalVarRef -> ref.name
is LocalSlotRef -> ref.name
is FastLocalVarRef -> ref.name
else -> return false
}
return name == receiverClass.className
}
private fun isClassSlot(slot: Int): Boolean = slotObjClass[slot] == ObjClassType
private fun isThisReceiver(ref: ObjRef): Boolean { private fun isThisReceiver(ref: ObjRef): Boolean {
return when (ref) { return when (ref) {
is LocalSlotRef -> ref.name == "this" is LocalSlotRef -> ref.name == "this"
@ -7193,13 +7232,33 @@ class BytecodeCompiler(
return when (ref) { return when (ref) {
is ConstRef -> ref.constValue as? ObjClass is ConstRef -> ref.constValue as? ObjClass
is TypeDeclRef -> when (val decl = ref.decl()) { is TypeDeclRef -> when (val decl = ref.decl()) {
is TypeDecl.Simple -> resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name] is TypeDecl.Simple -> {
is TypeDecl.Generic -> resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name] resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name]?.let { cls ->
if (cls == ObjClassType) ObjDynamic.type else cls
}
}
is TypeDecl.Generic -> {
resolveTypeNameClass(decl.name) ?: nameObjClass[decl.name]?.let { cls ->
if (cls == ObjClassType) ObjDynamic.type else cls
}
}
else -> null else -> null
} }
is LocalSlotRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name] is LocalSlotRef -> {
is LocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name] resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]?.let { cls ->
is FastLocalVarRef -> resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name] if (cls == ObjClassType) ObjDynamic.type else cls
}
}
is LocalVarRef -> {
resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]?.let { cls ->
if (cls == ObjClassType) ObjDynamic.type else cls
}
}
is FastLocalVarRef -> {
resolveTypeNameClass(ref.name) ?: nameObjClass[ref.name]?.let { cls ->
if (cls == ObjClassType) ObjDynamic.type else cls
}
}
is QualifiedThisRef -> resolveTypeNameClass(ref.typeName) is QualifiedThisRef -> resolveTypeNameClass(ref.typeName)
else -> null else -> null
} }
@ -7242,12 +7301,28 @@ 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 -> callableReturnTypeByScopeId[target.scopeId]?.get(target.slot) is LocalSlotRef -> {
?: nameObjClass[target.name] callableReturnTypeByScopeId[target.scopeId]?.get(target.slot)
?: resolveTypeNameClass(target.name) ?: run {
is LocalVarRef -> callableReturnTypeByName[target.name] val nameClass = nameObjClass[target.name]
?: nameObjClass[target.name] if (nameClass == ObjClassType) {
?: resolveTypeNameClass(target.name) resolveTypeNameClass(target.name) ?: ObjDynamic.type
} else {
nameClass ?: resolveTypeNameClass(target.name)
}
}
}
is LocalVarRef -> {
callableReturnTypeByName[target.name]
?: run {
val nameClass = nameObjClass[target.name]
if (nameClass == ObjClassType) {
resolveTypeNameClass(target.name) ?: ObjDynamic.type
} else {
nameClass ?: resolveTypeNameClass(target.name)
}
}
}
is ConstRef -> target.constValue as? ObjClass is ConstRef -> target.constValue as? ObjClass
else -> null else -> null
} }
@ -7311,7 +7386,12 @@ 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 } classFieldTypesByName[targetClass.className]?.get(name)?.let { cls ->
if (cls.className == "Delegate" || cls.className == "LazyDelegate" || cls.implementingNames.contains("Delegate")) {
return null
}
return cls
}
enumEntriesByName[targetClass.className]?.let { entries -> enumEntriesByName[targetClass.className]?.let { entries ->
return when { return when {
name == "entries" -> ObjList.type name == "entries" -> ObjList.type