Skip to content

Commit

Permalink
[K/N] Fix: Crashes when casting to an Obj-C class companion
Browse files Browse the repository at this point in the history
When generating section __objc_classrefs, need to consider distinguishing _OBJC_CLASS_$_ and _OBJC_METACLASS_$_

KT-65260
  • Loading branch information
edisongz committed Mar 21, 2024
1 parent 83ddf90 commit 764f903
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
import org.jetbrains.kotlin.ir.descriptors.IrBasedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
Expand Down Expand Up @@ -266,9 +267,14 @@ fun IrClass.getExternalObjCClassBinaryName(): String =
this.getExplicitExternalObjCClassBinaryName()
?: this.name.asString()

fun IrClass.getExternalObjCMetaClassBinaryName(): String =
this.getExplicitExternalObjCClassBinaryName()
?: this.name.asString().removeSuffix("Meta")
fun IrClass.getExternalObjCMetaClassBinaryName(): String {
this.getExplicitExternalObjCClassBinaryName()?.let { return it }
// External ObjC metaclass is named as `Foo.Componion`, Foo is subclass of NSObject or itself.
if (this.isCompanion) {
(this.parent as? IrLazyClass)?.let { return it.name.asString() }
}
return this.name.asString().removeSuffix("Meta")
}

private fun IrClass.getExplicitExternalObjCClassBinaryName() =
this.annotations.findAnnotation(externalObjCClassFqName)!!.getAnnotationValueOrNull<String>("binaryName")
this.annotations.findAnnotation(externalObjCClassFqName)?.getAnnotationValueOrNull<String>("binaryName")
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ internal abstract class FunctionGenerationContext(
generationState.dependenciesTracker.add(irClass)
if (irClass.isObjCMetaClass()) {
val name = irClass.getExternalObjCMetaClassBinaryName()
val objCClass = getObjCClass(name)
val objCClass = getObjCClass(name, isMetaclass = true)

val getClass = llvm.externalNativeRuntimeFunction(
"object_getClass",
Expand Down Expand Up @@ -1364,7 +1364,8 @@ internal abstract class FunctionGenerationContext(
}
}

private fun getObjCClass(binaryName: String) = load(llvm.int8PtrType, codegen.objCDataGenerator!!.genClassRef(binaryName).llvm)
private fun getObjCClass(binaryName: String, isMetaclass: Boolean = false) =
load(llvm.int8PtrType, codegen.objCDataGenerator!!.genClassRef(binaryName, isMetaclass).llvm)

fun getObjCClassFromNativeRuntime(binaryName: String): LLVMValueRef {
generationState.dependenciesTracker.addNativeRuntime()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
global.pointer
}

fun genClassRef(name: String): ConstPointer = classRefs.getOrPut(name) {
val classGlobal = getClassGlobal(name, isMetaclass = false)
fun genClassRef(name: String, isMetaclass: Boolean = false): ConstPointer = classRefs.getOrPut(name) {
// Need to distinguish between _OBJC_CLASS_$_ and _OBJC_METACLASS_$_
val classGlobal = getClassGlobal(name, isMetaclass)
val global = codegen.staticData.placeGlobal("OBJC_CLASSLIST_REFERENCES_\$_", classGlobal).also {
it.setLinkage(LLVMLinkage.LLVMPrivateLinkage)
it.setSection("__DATA,__objc_classrefs,regular,no_dead_strip")
Expand Down
5 changes: 5 additions & 0 deletions kotlin-native/backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,11 @@ if (PlatformInfo.isAppleTarget(project)) {
UtilsKt.dependsOnPlatformLibs(it)
}

standaloneTest("interop_objc_kt65260") {
source = "interop/objc/kt65260.kt"
UtilsKt.dependsOnPlatformLibs(it)
}

standaloneTest("objc_arc_contract") {
def bcFile = project.layout.buildDirectory.file("objc_arc_contract.bc").get().asFile
doBeforeBuild {
Expand Down
16 changes: 16 additions & 0 deletions kotlin-native/backend.native/tests/interop/objc/kt65260.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

import platform.Foundation.NSArray
import platform.Foundation.NSString
import platform.darwin.NSObject

fun main() {
// Test: cast to NSObject Metaclass
Any() as NSObject.Companion
// Test: cast to subclass of NSObject Metaclass
Any() as NSString.Companion
Any() as NSArray.Companion
}

0 comments on commit 764f903

Please sign in to comment.