Disable inheriting Objective-C initializers by Kotlin classes
Unoverridden inherited Objective-C initializer can lead to creating uninitialized Kotlin instances. Throw exception when such an initializer gets called.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
324f54ba1e
commit
12df524138
+10
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
@@ -163,6 +164,15 @@ class ObjCOverridabilityCondition : ExternalOverridabilityCondition {
|
||||
|
||||
}
|
||||
|
||||
fun ConstructorDescriptor.getObjCInitMethod(): FunctionDescriptor? {
|
||||
return this.annotations.findAnnotation(FqName("kotlinx.cinterop.ObjCConstructor"))?.let {
|
||||
val initSelector = it.getStringValue("initSelector")
|
||||
this.constructedClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence()
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
.single { it.getExternalObjCMethodInfo()?.selector == initSelector }
|
||||
}
|
||||
}
|
||||
|
||||
fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.valueParameters.isEmpty()) {
|
||||
descriptor.name.asString()
|
||||
} else {
|
||||
|
||||
+1
@@ -328,6 +328,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") }
|
||||
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
|
||||
val missingInitImp by lazy { importRtFunction("MissingInitImp") }
|
||||
|
||||
private val personalityFunctionName = when (context.config.targetManager.target) {
|
||||
KonanTarget.MINGW -> "__gxx_personality_seh0"
|
||||
|
||||
+35
-17
@@ -17,12 +17,11 @@
|
||||
package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.LLVMStoreSizeOfType
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.ObjCMethodInfo
|
||||
import llvm.LLVMValueRef
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValueOrNull
|
||||
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isFinalClass
|
||||
@@ -39,7 +38,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
|
||||
|
||||
val objCLLvmDeclarations = context.llvmDeclarations.forClass(descriptor).objCDeclarations!!
|
||||
|
||||
val instanceMethods = descriptor.generateOverridingMethodDescs() + irClass.generateImpMethodDescs()
|
||||
val instanceMethods = generateInstanceMethodDescs(irClass)
|
||||
|
||||
val companionObjectDescriptor = descriptor.companionObjectDescriptor
|
||||
val classMethods = companionObjectDescriptor?.generateOverridingMethodDescs() ?: emptyList()
|
||||
@@ -80,6 +79,25 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
|
||||
objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0))
|
||||
}
|
||||
|
||||
private fun generateInstanceMethodDescs(
|
||||
irClass: IrClass
|
||||
): List<ObjCMethodDesc> = mutableListOf<ObjCMethodDesc>().apply {
|
||||
val descriptor = irClass.descriptor
|
||||
addAll(descriptor.generateOverridingMethodDescs())
|
||||
addAll(irClass.generateImpMethodDescs())
|
||||
val allImplementedSelectors = this.map { it.selector }.toSet()
|
||||
|
||||
assert(descriptor.getSuperClassNotAny()!!.isExternalObjCClass())
|
||||
val allInitMethodsInfo = descriptor.getSuperClassNotAny()!!.constructors
|
||||
.mapNotNull { it.getObjCInitMethod()?.getExternalObjCMethodInfo() }
|
||||
.filter { it.selector !in allImplementedSelectors }
|
||||
.distinctBy { it.selector }
|
||||
|
||||
allInitMethodsInfo.mapTo(this) {
|
||||
ObjCMethodDesc(it.selector, it.encoding, context.llvm.missingInitImp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectClassName(descriptor: ClassDescriptor): String? {
|
||||
val exportObjCClassAnnotation =
|
||||
descriptor.annotations.findAnnotation(context.interopBuiltIns.exportObjCClass.fqNameSafe)
|
||||
@@ -93,37 +111,37 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMethodDesc(selector: String, encoding: String, imp: ConstPointer) = Struct(
|
||||
private inner class ObjCMethodDesc(
|
||||
val selector: String, val encoding: String, val impFunction: LLVMValueRef
|
||||
) : Struct(
|
||||
runtime.objCMethodDescription,
|
||||
staticData.cStringLiteral(selector),
|
||||
staticData.cStringLiteral(encoding),
|
||||
imp
|
||||
constPointer(impFunction).bitcast(int8TypePtr)
|
||||
)
|
||||
|
||||
private fun generateMethodDesc(info: ObjCMethodInfo): ConstValue {
|
||||
return createMethodDesc(
|
||||
info.selector,
|
||||
info.encoding,
|
||||
constPointer(context.llvm.externalFunction(info.imp, functionType(voidType))).bitcast(int8TypePtr)
|
||||
)
|
||||
}
|
||||
private fun generateMethodDesc(info: ObjCMethodInfo) = ObjCMethodDesc(
|
||||
info.selector,
|
||||
info.encoding,
|
||||
context.llvm.externalFunction(info.imp, functionType(voidType))
|
||||
)
|
||||
|
||||
private fun ClassDescriptor.generateOverridingMethodDescs(): List<ConstValue> =
|
||||
private fun ClassDescriptor.generateOverridingMethodDescs(): List<ObjCMethodDesc> =
|
||||
this.unsubstitutedMemberScope.contributedMethods.filter {
|
||||
it.kind.isReal && it !is ConstructorDescriptor
|
||||
}.mapNotNull { it.getObjCMethodInfo() }.map { generateMethodDesc(it) }
|
||||
|
||||
private fun IrClass.generateImpMethodDescs(): List<ConstValue> = this.declarations
|
||||
private fun IrClass.generateImpMethodDescs(): List<ObjCMethodDesc> = this.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.mapNotNull {
|
||||
val annotation =
|
||||
it.descriptor.annotations.findAnnotation(context.interopBuiltIns.objCMethodImp.fqNameSafe) ?:
|
||||
return@mapNotNull null
|
||||
|
||||
createMethodDesc(
|
||||
ObjCMethodDesc(
|
||||
annotation.getStringValue("selector"),
|
||||
annotation.getStringValue("encoding"),
|
||||
it.descriptor.entryPointAddress
|
||||
it.descriptor.llvmFunction
|
||||
)
|
||||
}
|
||||
}
|
||||
+2
-11
@@ -389,7 +389,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
|
||||
assert(constructedClassDescriptor.getSuperClassNotAny() == expression.descriptor.constructedClass)
|
||||
|
||||
val initMethod = getObjCInitMethod(expression.descriptor)!!
|
||||
val initMethod = expression.descriptor.getObjCInitMethod()!!
|
||||
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||
|
||||
assert(expression.dispatchReceiver == null)
|
||||
@@ -438,22 +438,13 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
|
||||
}
|
||||
}
|
||||
|
||||
private fun getObjCInitMethod(descriptor: ConstructorDescriptor): FunctionDescriptor? {
|
||||
return descriptor.annotations.findAnnotation(FqName("kotlinx.cinterop.ObjCConstructor"))?.let {
|
||||
val initSelector = it.getStringValue("initSelector")
|
||||
descriptor.constructedClass.unsubstitutedMemberScope.getContributedDescriptors().asSequence()
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
.single { it.getExternalObjCMethodInfo()?.selector == initSelector }
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
|
||||
val descriptor = expression.descriptor.original
|
||||
|
||||
if (descriptor is ConstructorDescriptor) {
|
||||
val initMethod = getObjCInitMethod(descriptor)
|
||||
val initMethod = descriptor.getObjCInitMethod()
|
||||
|
||||
if (initMethod != null) {
|
||||
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#if KONAN_OBJC_INTEROP
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import <Foundation/NSString.h>
|
||||
|
||||
namespace {
|
||||
@@ -34,6 +35,11 @@ namespace {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Note: using @"foo" string literals leads to linkage dependency on frameworks.
|
||||
NSString* cStringToNS(const char* str) {
|
||||
return [getNSStringClass() stringWithCString:str encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -67,6 +73,24 @@ OBJ_GETTER(CreateKStringFromNSString, NSString* str) {
|
||||
RETURN_OBJ(result->obj());
|
||||
}
|
||||
|
||||
// Note: this body is used for init methods with signatures differing from this;
|
||||
// it is correct on arm64 and x86_64, because the body uses only the first two arguments which are fixed,
|
||||
// and returns pointers.
|
||||
id MissingInitImp(id self, SEL _cmd) {
|
||||
const char* className = object_getClassName(self);
|
||||
[self release]; // Since init methods receive ownership on the receiver.
|
||||
|
||||
// Lookup dynamically to avoid direct reference to Foundation:
|
||||
Class nsExceptionClass = objc_getClass("NSException");
|
||||
RuntimeAssert(nsExceptionClass != nullptr, "NSException class not found");
|
||||
|
||||
[nsExceptionClass raise:cStringToNS("Initializer is not implemented")
|
||||
format:cStringToNS("%s is not implemented in %s"),
|
||||
sel_getName(_cmd), className];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#else // KONAN_OBJC_INTEROP
|
||||
|
||||
Reference in New Issue
Block a user