Implement very basic Objective-C interop

Also refactor StubGenerator
This commit is contained in:
Svyatoslav Scherbina
2017-07-17 14:34:09 +03:00
committed by SvyatoslavScherbina
parent a38835cb46
commit d3185e3e19
57 changed files with 4020 additions and 920 deletions
+3 -1
View File
@@ -61,6 +61,7 @@ sourceSets {
srcDir 'compiler/ir/backend.native/src/'
srcDir "${rootProject.projectDir}/shared/src/main/kotlin"
}
resources.srcDir 'compiler/ir/backend.native/resources/'
}
cli_bc {
java.srcDir 'cli.bc/src'
@@ -213,7 +214,8 @@ task jars(type: Jar) {
from 'build/classes/cli_bc',
'build/classes/compiler',
'build/classes/hashInteropStubs',
'build/classes/llvmInteropStubs'
'build/classes/llvmInteropStubs',
'build/resources/compiler'
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.backend.konan.ObjCOverridabilityCondition
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -100,6 +101,31 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val signExtend = packageScope.getContributedFunctions("signExtend").single()
val narrow = packageScope.getContributedFunctions("narrow").single()
val objCObject = packageScope.getContributedClassifier("ObjCObject") as ClassDescriptor
val objCPointerHolder = packageScope.getContributedClassifier("ObjCPointerHolder") as ClassDescriptor
val objCPointerHolderValue = objCPointerHolder.unsubstitutedMemberScope
.getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single()
val objCObjectInitFromPtr = packageScope.getContributedFunctions("initFromPtr").single()
val objCObjectInitFrom = packageScope.getContributedFunctions("initFrom").single()
val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single()
val getObjCClass = packageScope.getContributedFunctions("getObjCClass").single()
val objCObjectRawPtr = packageScope.getContributedVariables("rawPtr").single {
val extensionReceiverType = it.extensionReceiverParameter?.type
extensionReceiverType != null && !extensionReceiverType.isMarkedNullable &&
TypeUtils.getClassDescriptor(extensionReceiverType) == objCObject
}
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
val getObjCMessenger = packageScope.getContributedFunctions("getMessenger").single()
val getObjCMessengerLU = packageScope.getContributedFunctions("getMessengerLU").single()
}
private fun MemberScope.getContributedVariables(name: String) =
@@ -0,0 +1,156 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.Name
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
import org.jetbrains.kotlin.resolve.descriptorUtil.*
private val interopPackageName = InteropBuiltIns.FqNames.packageName
private val objCObjectFqName = interopPackageName.child(Name.identifier("ObjCObject"))
private val objCClassFqName = interopPackageName.child(Name.identifier("ObjCClass"))
private val externalObjCClassFqName = interopPackageName.child(Name.identifier("ExternalObjCClass"))
private val objCMethodFqName = interopPackageName.child(Name.identifier("ObjCMethod"))
private val objCBridgeFqName = interopPackageName.child(Name.identifier("ObjCBridge"))
fun ClassDescriptor.isObjCClass(): Boolean =
this.getAllSuperClassifiers().any { it.fqNameSafe == objCObjectFqName } &&
this.containingDeclaration.fqNameSafe != interopPackageName
fun ClassDescriptor.isExternalObjCClass(): Boolean = this.isObjCClass() &&
this.parentsWithSelf.filterIsInstance<ClassDescriptor>().any {
it.annotations.findAnnotation(externalObjCClassFqName) != null
}
fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().any {
it.fqNameSafe == objCClassFqName
}
fun FunctionDescriptor.isObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isObjCClass() }
fun FunctionDescriptor.isExternalObjCClassMethod() =
this.containingDeclaration.let { it is ClassDescriptor && it.isExternalObjCClass() }
// Special case: methods from Kotlin Objective-C classes can be called virtually from bridges.
fun FunctionDescriptor.canObjCClassMethodBeCalledVirtually(overriddenDescriptor: FunctionDescriptor) =
overriddenDescriptor.isOverridable && this.kind.isReal && !this.isExternalObjCClassMethod()
fun ClassDescriptor.isKotlinObjCClass(): Boolean = this.isObjCClass() && !this.isExternalObjCClass()
data class ObjCMethodInfo(val descriptor: FunctionDescriptor,
val bridge: FunctionDescriptor,
val selector: String,
val encoding: String,
val imp: String)
private fun CallableDescriptor.getBridgeAnnotation() =
this.annotations.findAnnotation(objCBridgeFqName)
private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? {
assert (this.kind.isReal)
val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null
val packageFragment = this.parents.filterIsInstance<PackageFragmentDescriptor>().single()
val bridgeName = methodAnnotation.getStringValue("bridge")
val bridge = packageFragment.getMemberScope()
.getContributedFunctions(Name.identifier(bridgeName), NoLookupLocation.FROM_BACKEND)
.single()
val bridgeAnnotation = bridge.getBridgeAnnotation()!!
return ObjCMethodInfo(
descriptor = this,
bridge = bridge,
selector = methodAnnotation.getStringValue("selector"),
encoding = bridgeAnnotation.getStringValue("encoding"),
imp = bridgeAnnotation.getStringValue("imp")
)
}
/**
* @param onlyExternal indicates whether to accept overriding methods from Kotlin classes
*/
private fun FunctionDescriptor.getObjCMethodInfo(onlyExternal: Boolean): ObjCMethodInfo? {
if (this.kind.isReal) {
this.decodeObjCMethodAnnotation()?.let { return it }
if (onlyExternal) {
return null
}
}
return this.overriddenDescriptors.asSequence().mapNotNull { it.getObjCMethodInfo(onlyExternal) }.firstOrNull()
}
fun FunctionDescriptor.getExternalObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = true)
fun FunctionDescriptor.getObjCMethodInfo(): ObjCMethodInfo? = this.getObjCMethodInfo(onlyExternal = false)
/**
* Describes method overriding rules for Objective-C methods.
*
* This class is applied at [org.jetbrains.kotlin.resolve.OverridingUtil] as configured with
* `META-INF/services/org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition` resource.
*/
class ObjCOverridabilityCondition : ExternalOverridabilityCondition {
override fun getContract() = ExternalOverridabilityCondition.Contract.BOTH
override fun isOverridable(
superDescriptor: CallableDescriptor,
subDescriptor: CallableDescriptor,
subClassDescriptor: ClassDescriptor?
): ExternalOverridabilityCondition.Result {
assert(superDescriptor.name == subDescriptor.name)
val superClass = superDescriptor.containingDeclaration as? ClassDescriptor
if (superClass == null || !superClass.isObjCClass()) {
return ExternalOverridabilityCondition.Result.UNKNOWN
}
return if (areSelectorsEqual(superDescriptor, subDescriptor)) {
ExternalOverridabilityCondition.Result.OVERRIDABLE
} else {
ExternalOverridabilityCondition.Result.INCOMPATIBLE
}
}
private fun areSelectorsEqual(first: CallableDescriptor, second: CallableDescriptor): Boolean {
// The original Objective-C method selector is represented as
// function name and parameter names (except first).
if (first.valueParameters.size != second.valueParameters.size) {
return false
}
first.valueParameters.forEachIndexed { index, parameter ->
if (index > 0 && parameter.name != second.valueParameters[index].name) {
return false
}
}
return true
}
}
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -35,9 +35,15 @@ internal class OverriddenFunctionDescriptor(val descriptor: FunctionDescriptor,
get() = descriptor.target.bridgeDirectionsTo(overriddenDescriptor)
val canBeCalledVirtually: Boolean
get() {
if (overriddenDescriptor.isObjCClassMethod()) {
return descriptor.canObjCClassMethodBeCalledVirtually(this.overriddenDescriptor)
}
// We check that either method is open, or one of declarations it overrides is open.
get() = overriddenDescriptor.isOverridable
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable }
return (overriddenDescriptor.isOverridable
|| DescriptorUtils.getAllOverriddenDeclarations(overriddenDescriptor).any { it.isOverridable })
}
val inheritsBridge: Boolean
get() = !descriptor.kind.isReal
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.builtins.getFunctionalClassKind
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -308,3 +309,9 @@ internal val FunctionDescriptor.needsInlining: Boolean
internal val FunctionDescriptor.needsSerializedIr: Boolean
get() = (this.needsInlining && this.isExported())
fun AnnotationDescriptor.getStringValue(name: String): String {
val constantValue = this.allValueArguments.entries.single {
it.key.asString() == name
}.value
return constantValue.value as String
}
@@ -49,6 +49,18 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
val interopCPointerGetRawValue = symbolTable.referenceSimpleFunction(context.interopBuiltIns.cPointerGetRawValue)
val interopAllocObjCObject = symbolTable.referenceSimpleFunction(context.interopBuiltIns.allocObjCObject)
val interopGetObjCClass = symbolTable.referenceSimpleFunction(context.interopBuiltIns.getObjCClass)
val interopObjCObjectInitFromPtr =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFromPtr)
val interopObjCObjectInitFrom = symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFrom)
val interopObjCObjectRawValueGetter =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr.getter!!)
val getNativeNullPtr = symbolTable.referenceSimpleFunction(context.builtIns.getNativeNullPtr)
val boxFunctions = ValueType.values().associate {
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
import org.jetbrains.kotlin.backend.konan.isExternalObjCClass
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
@@ -148,8 +151,14 @@ private val FunctionDescriptor.signature: String
// TODO: rename to indicate that it has signature included
internal val FunctionDescriptor.functionName: String
get() = with(this.original) { // basic support for generics
"$name$signature"
get() {
with(this.original) { // basic support for generics
this.getObjCMethodInfo()?.let {
return "objc:${it.selector}"
}
return "$name$signature"
}
}
internal val FunctionDescriptor.symbolName: String
@@ -228,3 +237,6 @@ internal val ClassDescriptor.objectInstanceFieldSymbolName: String
return "kobjref:$fqNameSafe"
}
internal val ClassDescriptor.typeInfoHasVtableAttached: Boolean
get() = !this.isAbstract() && !this.isExternalObjCClass()
@@ -376,6 +376,33 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
return LLVMBuildLandingPad(builder, landingpadType, personalityFunction, numClauses, name)!!
}
inline fun ifThenElse(
condition: LLVMValueRef,
thenValue: LLVMValueRef,
elseBlock: () -> LLVMValueRef
): LLVMValueRef {
val resultType = thenValue.type
val bbExit = basicBlock(locationInfo = position())
val resultPhi = appendingTo(bbExit) {
phi(resultType)
}
val bbElse = basicBlock(locationInfo = position())
condBr(condition, bbExit, bbElse)
assignPhis(resultPhi to thenValue)
appendingTo(bbElse) {
val elseValue = elseBlock()
br(bbExit)
assignPhis(resultPhi to elseValue)
}
positionAtEnd(bbExit)
return resultPhi
}
internal fun debugLocation(locationInfo: LocationInfo): DILocationRef? {
if (!context.shouldContainDebugInfo()) return null
update(currentBlock, locationInfo)
@@ -326,6 +326,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val throwExceptionFunction = importRtFunction("ThrowException")
val appendToInitalizersTail = importRtFunction("AppendToInitializersTail")
val createKotlinObjCClass by lazy { importRtFunction("CreateKotlinObjCClass") }
val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") }
private val personalityFunctionName = when (context.config.targetManager.target) {
KonanTarget.MINGW -> "__gxx_personality_seh0"
else -> "__gxx_personality_v0"
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -156,6 +157,8 @@ internal fun verifyModule(llvmModule: LLVMModuleRef, current: String = "") {
internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
val generator = RTTIGenerator(context)
val kotlinObjCClassInfoGenerator = KotlinObjCClassInfoGenerator(context)
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
@@ -163,12 +166,17 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
if (declaration.descriptor.isIntrinsic) {
val descriptor = declaration.descriptor
if (descriptor.isIntrinsic) {
// do not generate any code for intrinsic classes as they require special handling
return
}
generator.generate(declaration.descriptor)
generator.generate(descriptor)
if (descriptor.isKotlinObjCClass()) {
kotlinObjCClassInfoGenerator.generate(descriptor)
}
}
}
@@ -734,7 +742,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.condBr(condition, bbExit, bbInit)
functionGenerationContext.positionAtEnd(bbInit)
val typeInfo = codegen.typeInfoValue(value.descriptor)
val typeInfo = typeInfoForAllocation(value.descriptor)
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
val ctor = codegen.llvmFunction(initFunction)
val args = listOf(objectPtr, typeInfo, ctor)
@@ -1350,13 +1358,32 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
val fieldInfo = context.llvmDeclarations.forField(value)
val classDescriptor = value.containingDeclaration as ClassDescriptor
val typePtr = pointerType(fieldInfo.classBodyType)
val objectPtr = LLVMBuildGEP(functionGenerationContext.builder, thisPtr, cValuesOf(kImmOne), 1, "")
val typedObjPtr = functionGenerationContext.bitcast(typePtr, objectPtr!!)
val fieldPtr = LLVMBuildStructGEP(functionGenerationContext.builder, typedObjPtr, fieldInfo.index, "")
val bodyPtr = getObjectBodyPtr(classDescriptor, thisPtr)
val typedBodyPtr = functionGenerationContext.bitcast(typePtr, bodyPtr)
val fieldPtr = LLVMBuildStructGEP(functionGenerationContext.builder, typedBodyPtr, fieldInfo.index, "")
return fieldPtr!!
}
private fun getObjectBodyPtr(classDescriptor: ClassDescriptor, objectPtr: LLVMValueRef): LLVMValueRef {
return if (classDescriptor.isObjCClass()) {
assert(classDescriptor.isKotlinObjCClass())
val objCPtr = callDirect(context.interopBuiltIns.objCPointerHolderValue.getter!!,
listOf(objectPtr), Lifetime.IRRELEVANT)
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
val bodyOffset = functionGenerationContext.load(objCDeclarations.bodyOffsetGlobal.llvmGlobal)
functionGenerationContext.gep(objCPtr, bodyOffset)
} else {
LLVMBuildGEP(functionGenerationContext.builder, objectPtr, cValuesOf(kImmOne), 1, "")!!
}
}
//-------------------------------------------------------------------------//
private fun evaluateConst(value: IrConst<*>): LLVMValueRef {
@@ -1871,7 +1898,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
lifetime = resultLifetime(callee))
} else {
functionGenerationContext.allocInstance(codegen.typeInfoValue(constructedClass), resultLifetime(callee))
functionGenerationContext.allocInstance(typeInfoForAllocation(constructedClass), resultLifetime(callee))
}
evaluateSimpleFunctionCall(callee.descriptor,
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
@@ -1879,6 +1906,15 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
}
private fun typeInfoForAllocation(constructedClass: ClassDescriptor): LLVMValueRef {
val descriptorForTypeInfo = if (constructedClass.isObjCClass()) {
context.interopBuiltIns.objCPointerHolder
} else {
constructedClass
}
return codegen.typeInfoValue(descriptorForTypeInfo)
}
//-------------------------------------------------------------------------//
private fun evaluateIntrinsicCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
@@ -1934,10 +1970,122 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
LLVMBuildSExt(functionGenerationContext.builder, intPtrValue, resultType, "")!!
}
}
interop.objCObjectInitFromPtr -> {
genObjCObjectInitFromPtr(args)
}
interop.getObjCReceiverOrSuper -> {
genGetObjCReceiverOrSuper(args)
}
interop.getObjCClass -> {
val typeArgument = callee.getTypeArgument(descriptor.typeParameters.single())
val classDescriptor = TypeUtils.getClassDescriptor(typeArgument!!)!!
genGetObjCClass(classDescriptor)
}
interop.getObjCMessenger -> {
genGetObjCMessenger(args, isLU = false)
}
interop.getObjCMessengerLU -> {
genGetObjCMessenger(args, isLU = true)
}
else -> TODO(callee.descriptor.original.toString())
}
}
private fun genGetObjCClass(classDescriptor: ClassDescriptor): LLVMValueRef {
assert(!classDescriptor.isInterface)
return if (classDescriptor.isExternalObjCClass()) {
val lookUpFunction = context.llvm.externalFunction("objc_lookUpClass",
functionType(int8TypePtr, false, int8TypePtr))
call(lookUpFunction,
listOf(codegen.staticData.cStringLiteral(classDescriptor.name.asString()).llvm))
} else {
val objCDeclarations = context.llvmDeclarations.forClass(classDescriptor).objCDeclarations!!
val classPointerGlobal = objCDeclarations.classPointerGlobal.llvmGlobal
val gen = functionGenerationContext
val storedClass = gen.load(classPointerGlobal)
val storedClassIsNotNull = gen.icmpNe(storedClass, kNullInt8Ptr)
return gen.ifThenElse(storedClassIsNotNull, storedClass) {
val newClass = call(context.llvm.createKotlinObjCClass,
listOf(objCDeclarations.classInfoGlobal.llvmGlobal))
gen.store(newClass, classPointerGlobal)
newClass
}
}
}
private fun genGetObjCMessenger(args: List<LLVMValueRef>, isLU: Boolean): LLVMValueRef {
val gen = functionGenerationContext
// 'LU' means "large or unaligned".
// objc_msgSend*_stret functions must be used when return value is returned through memory
// pointed by implicit argument, which is passed on the register that would otherwise be used for receiver.
// On aarch64 it is never the case, since such implicit argument gets passed on x8.
// On x86_64 it is the case if the return value takes more than 16 bytes or is the structure with
// unaligned fields (there are some complicated exceptions currently ignored). The latter condition
// is "encoded" by stub generator by emitting either `getMessenger` or `getMessengerLU` intrinsic call.
val isStret = when (context.config.targetManager.target) {
KonanTarget.MACBOOK, KonanTarget.IPHONE_SIM -> isLU // x86_64
KonanTarget.IPHONE -> false // aarch64
else -> TODO()
}
val messengerNameSuffix = if (isStret) "_stret" else ""
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
val normalMessenger = context.llvm.externalFunction("objc_msgSend$messengerNameSuffix", functionType)
val superMessenger = context.llvm.externalFunction("objc_msgSendSuper$messengerNameSuffix", functionType)
val superClass = args.single()
val messenger = LLVMBuildSelect(gen.builder,
If = gen.icmpEq(superClass, kNullInt8Ptr),
Then = normalMessenger,
Else = superMessenger,
Name = ""
)!!
return gen.bitcast(int8TypePtr, messenger)
}
private fun genObjCObjectInitFromPtr(args: List<LLVMValueRef>): LLVMValueRef {
return callDirect(context.interopBuiltIns.objCPointerHolder.unsubstitutedPrimaryConstructor!!,
args, Lifetime.IRRELEVANT)
}
private fun genGetObjCReceiverOrSuper(args: List<LLVMValueRef>): LLVMValueRef {
val gen = functionGenerationContext
assert(args.size == 2)
val receiver = args[0]
val superClass = args[1]
val superClassIsNull = gen.icmpEq(superClass, kNullInt8Ptr)
return gen.ifThenElse(superClassIsNull, receiver) {
val structType = structType(kInt8Ptr, kInt8Ptr)
val ptr = gen.alloca(structType)
gen.store(receiver,
LLVMBuildGEP(gen.builder, ptr, cValuesOf(kImmZero, kImmZero), 2, "")!!)
gen.store(superClass,
LLVMBuildGEP(gen.builder, ptr, cValuesOf(kImmZero, kImmOne), 2, "")!!)
gen.bitcast(int8TypePtr, ptr)
}
}
//-------------------------------------------------------------------------//
private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!!
private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!!
@@ -2008,11 +2156,16 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
assert(LLVMTypeOf(args[0]) == codegen.kObjHeaderPtr)
val typeInfoPtrPtr = LLVMBuildStructGEP(functionGenerationContext.builder, args[0], 0 /* type_info */, "")!!
val typeInfoPtr = functionGenerationContext.load(typeInfoPtrPtr)
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
val owner = descriptor.containingDeclaration as ClassDescriptor
val typeInfoPtr: LLVMValueRef = if (owner.isObjCClass()) {
call(context.llvm.getObjCKotlinTypeInfo, listOf(args.first()))
} else {
val typeInfoPtrPtr = LLVMBuildStructGEP(functionGenerationContext.builder, args[0], 0 /* type_info */, "")!!
functionGenerationContext.load(typeInfoPtrPtr)
}
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
val llvmMethod = if (!owner.isInterface) {
// If this is a virtual method of the class - we can call via vtable.
val index = context.getVtableBuilder(owner).vtableIndex(descriptor)
@@ -2068,6 +2221,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val constructedClass = functionGenerationContext.constructedClass!!
val thisPtr = currentCodeContext.genGetValue(constructedClass.thisAsReceiverParameter)
if (constructedClass.isObjCClass()) {
return codegen.theUnitInstanceRef.llvm
}
val thisPtrArgType = codegen.getLLVMType(descriptor.allParameters[0].type)
val thisPtrArg = if (thisPtr.type == thisPtrArgType) {
thisPtr
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 org.jetbrains.kotlin.backend.konan.descriptors.contributedMethods
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
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
internal class KotlinObjCClassInfoGenerator(override val context: Context) : ContextUtils {
fun generate(descriptor: ClassDescriptor) {
assert(descriptor.isFinalClass)
val objCLLvmDeclarations = context.llvmDeclarations.forClass(descriptor).objCDeclarations!!
val instanceMethods = descriptor.generateMethodDescs()
val companionObjectDescriptor = descriptor.companionObjectDescriptor
val classMethods = companionObjectDescriptor?.generateMethodDescs() ?: emptyList()
val superclassName = descriptor.getSuperClassNotAny()!!.name.asString()
val protocolNames = descriptor.getSuperInterfaces().map { it.name.asString().removeSuffix("Protocol") }
val bodySize =
LLVMStoreSizeOfType(llvmTargetData, context.llvmDeclarations.forClass(descriptor).bodyType).toInt()
val className = if (descriptor.isExported()) {
staticData.cStringLiteral(descriptor.fqNameSafe.asString())
} else {
NullPointer(int8Type) // Generate as anonymous.
}
val info = Struct(runtime.kotlinObjCClassInfo,
className,
staticData.cStringLiteral(superclassName),
staticData.placeGlobalConstArray("", int8TypePtr,
protocolNames.map { staticData.cStringLiteral(it) } + NullPointer(int8Type)),
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, instanceMethods),
Int32(instanceMethods.size),
staticData.placeGlobalConstArray("", runtime.objCMethodDescription, classMethods),
Int32(classMethods.size),
Int32(bodySize),
objCLLvmDeclarations.bodyOffsetGlobal.pointer,
descriptor.typeInfoPtr,
companionObjectDescriptor?.typeInfoPtr ?: NullPointer(runtime.typeInfoType),
objCLLvmDeclarations.classPointerGlobal.pointer
)
objCLLvmDeclarations.classInfoGlobal.setInitializer(info)
objCLLvmDeclarations.classPointerGlobal.setInitializer(NullPointer(int8Type))
objCLLvmDeclarations.bodyOffsetGlobal.setInitializer(Int32(0))
}
private fun generateMethodDesc(info: ObjCMethodInfo): ConstValue {
return Struct(runtime.objCMethodDescription,
staticData.cStringLiteral(info.selector),
staticData.cStringLiteral(info.encoding),
constPointer(context.llvm.externalFunction(info.imp, functionType(voidType))).bitcast(int8TypePtr)
)
}
private fun ClassDescriptor.generateMethodDescs(): List<ConstValue> =
this.unsubstitutedMemberScope.contributedMethods.filter {
it.kind.isReal && it !is ConstructorDescriptor
}.mapNotNull { it.getObjCMethodInfo() }.map { generateMethodDesc(it) }
}
@@ -21,6 +21,7 @@ import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.isKotlinObjCClass
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
@@ -75,10 +76,17 @@ internal class ClassLlvmDeclarations(
val fields: List<PropertyDescriptor>, // TODO: it is not an LLVM declaration.
val typeInfoGlobal: StaticData.Global,
val typeInfo: ConstPointer,
val singletonDeclarations: SingletonLlvmDeclarations?)
val singletonDeclarations: SingletonLlvmDeclarations?,
val objCDeclarations: KotlinObjCClassLlvmDeclarations?)
internal class SingletonLlvmDeclarations(val instanceFieldRef: LLVMValueRef)
internal class KotlinObjCClassLlvmDeclarations(
val classPointerGlobal: StaticData.Global,
val classInfoGlobal: StaticData.Global,
val bodyOffsetGlobal: StaticData.Global
)
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
@@ -236,7 +244,7 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
"ktype:$internalName"
}
if (!descriptor.isAbstract()) {
if (descriptor.typeInfoHasVtableAttached) {
// Create the special global consisting of TypeInfo and vtable.
val typeInfoGlobalName = "ktypeglobal:$internalName"
@@ -273,7 +281,14 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
null
}
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr, singletonDeclarations)
val objCDeclarations = if (descriptor.isKotlinObjCClass()) {
createKotlinObjCClassDeclarations(descriptor)
} else {
null
}
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr,
singletonDeclarations, objCDeclarations)
}
private fun createSingletonDeclarations(
@@ -299,6 +314,23 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
return SingletonLlvmDeclarations(instanceFieldRef)
}
private fun createKotlinObjCClassDeclarations(descriptor: ClassDescriptor): KotlinObjCClassLlvmDeclarations {
val internalName = qualifyInternalName(descriptor)
val classPointerGlobal = staticData.createGlobal(int8TypePtr, "kobjcclassptr:$internalName")
val classInfoGlobal = staticData.createGlobal(
context.llvm.runtime.kotlinObjCClassInfo,
"kobjcclassinfo:$internalName"
).apply {
setConstant(true)
}
val bodyOffsetGlobal = staticData.createGlobal(int32Type, "kobjcbodyoffs:$internalName")
return KotlinObjCClassLlvmDeclarations(classPointerGlobal, classInfoGlobal, bodyOffsetGlobal)
}
override fun visitField(declaration: IrField) {
super.visitField(declaration)
@@ -330,6 +362,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
if (!declaration.descriptor.kind.isReal) return
val descriptor = declaration.descriptor
val llvmFunctionType = getLlvmFunctionType(descriptor)
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.isExternalObjCClassMethod
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.FqName
@@ -166,11 +167,18 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
val typeInfoGlobalValue = if (classDesc.isAbstract()) {
val typeInfoGlobalValue = if (!classDesc.typeInfoHasVtableAttached) {
typeInfo
} else {
// TODO: compile-time resolution limits binary compatibility
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map { it.implementation.entryPointAddress }
val vtableEntries = context.getVtableBuilder(classDesc).vtableEntries.map {
val implementation = it.implementation
if (implementation.isExternalObjCClassMethod()) {
NullPointer(int8Type)
} else {
implementation.entryPointAddress
}
}
val vtable = ConstArray(int8TypePtr, vtableEntries)
Struct(typeInfo, vtable)
}
@@ -53,4 +53,7 @@ class Runtime(bitcodeFile: String) {
val dataLayout = LLVMGetDataLayout(llvmModule)!!.toKString()
val targetData = LLVMCreateTargetData(dataLayout)!!
val kotlinObjCClassInfo by lazy { getStructType("KotlinObjCClassInfo") }
val objCMethodDescription by lazy { getStructType("ObjCMethodDescription") }
}
@@ -152,6 +152,10 @@ internal class StaticData(override val context: Context): ContextUtils {
}
private val stringLiterals = mutableMapOf<String, ConstPointer>()
private val cStringLiterals = mutableMapOf<String, ConstPointer>()
fun cStringLiteral(value: String) =
cStringLiterals.getOrPut(value) { placeCStringLiteral(value) }
fun kotlinStringLiteral(type: KotlinType, value: IrConst<String>) =
stringLiterals.getOrPut(value.value) { createKotlinStringLiteral(type, value) }
@@ -41,3 +41,9 @@ internal fun StaticData.createAlias(name: String, aliasee: ConstPointer): ConstP
val alias = LLVMAddAlias(context.llvmModule, aliasee.llvmType, aliasee.llvm, name)!!
return constPointer(alias)
}
internal fun StaticData.placeCStringLiteral(value: String): ConstPointer {
val chars = value.toByteArray(Charsets.UTF_8).map { Int8(it) } + Int8(0)
return placeGlobalConstArray("", int8Type, chars)
}
@@ -20,40 +20,282 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer
import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.ir.builders.IrBuilder
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
internal class InteropLoweringPart1(val context: Context) : IrElementTransformerVoid(), FileLoweringPass {
internal class InteropLoweringPart1(val context: Context) : IrBuildingTransformer(context), FileLoweringPass {
private val symbols get() = context.ir.symbols
private val symbolTable get() = symbols.symbolTable
lateinit var currentFile: IrFile
override fun lower(irFile: IrFile) {
currentFile = irFile
irFile.transformChildrenVoid(this)
}
private fun IrBuilderWithScope.callAlloc(classSymbol: IrClassSymbol): IrExpression {
return irCall(symbols.interopAllocObjCObject, listOf(classSymbol.descriptor.defaultType)).apply {
putValueArgument(0, getObjCClass(classSymbol))
}
}
private fun IrBuilderWithScope.getObjCClass(classSymbol: IrClassSymbol): IrExpression {
val classDescriptor = classSymbol.descriptor
assert(!classDescriptor.isObjCMetaClass())
if (classDescriptor.isExternalObjCClass()) {
val companionObject = classDescriptor.companionObjectDescriptor!!
if (companionObject.unsubstitutedPrimaryConstructor != scope.scopeOwner) {
// Optimization: get class pointer from companion object thus avoiding lookup by name.
return irCall(symbols.interopObjCObjectRawValueGetter).apply {
extensionReceiver = irGetObject(symbolTable.referenceClass(companionObject))
}
}
}
return irCall(symbols.interopGetObjCClass, listOf(classDescriptor.defaultType))
}
private val outerClasses = mutableListOf<IrClass>()
override fun visitClass(declaration: IrClass): IrStatement {
if (declaration.descriptor.isKotlinObjCClass()) {
checkKotlinObjCClass(declaration)
}
outerClasses.push(declaration)
try {
return super.visitClass(declaration)
} finally {
outerClasses.pop()
}
}
private fun checkKotlinObjCClass(irClass: IrClass) {
val kind = irClass.descriptor.kind
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
context.reportCompilationError(
"Only classes are supported as subtypes of Objective-C types",
currentFile, irClass
)
}
if (!irClass.descriptor.isFinalClass) {
context.reportCompilationError(
"Non-final Kotlin subclasses of Objective-C classes are not yet supported",
currentFile, irClass
)
}
var hasObjCClassSupertype = false
irClass.descriptor.defaultType.constructor.supertypes.forEach {
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
if (!descriptor.isObjCClass()) {
context.reportCompilationError(
"Mixing Kotlin and Objective-C supertypes is not supported",
currentFile, irClass
)
}
if (descriptor.kind == ClassKind.CLASS) {
hasObjCClassSupertype = true
}
}
if (!hasObjCClassSupertype) {
context.reportCompilationError(
"Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)",
currentFile, irClass
)
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid()
builder.at(expression)
val constructedClass = outerClasses.peek()!!
val constructedClassDescriptor = constructedClass.descriptor
if (!constructedClassDescriptor.isObjCClass()) {
return expression
}
constructedClassDescriptor.containingDeclaration.let { classContainer ->
if (classContainer is ClassDescriptor && classContainer.isObjCClass() &&
constructedClassDescriptor == classContainer.companionObjectDescriptor) {
val outerConstructedClass = outerClasses[outerClasses.lastIndex - 1]
assert (outerConstructedClass.descriptor == classContainer)
assert(expression.getArguments().isEmpty())
return builder.irBlock(expression) {
+expression // Required for the IR to be valid, will be ignored in codegen.
+irCall(symbols.interopObjCObjectInitFromPtr).apply {
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
putValueArgument(0, getObjCClass(outerConstructedClass.symbol))
}
}
}
}
if (!constructedClassDescriptor.isExternalObjCClass() &&
expression.descriptor.constructedClass.isExternalObjCClass()) {
// Calling super constructor from Kotlin Objective-C class.
assert(constructedClassDescriptor.getSuperClassNotAny() == expression.descriptor.constructedClass)
val initMethod = getObjCInitMethod(expression.descriptor)!!
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
assert(expression.dispatchReceiver == null)
assert(expression.extensionReceiver == null)
val initCall = builder.genLoweredObjCMethodCall(
initMethodInfo,
superQualifier = symbolTable.referenceClass(expression.descriptor.constructedClass),
receiver = builder.callAlloc(constructedClass.symbol),
arguments = initMethod.valueParameters.map { expression.getValueArgument(it)!! }
)
val superConstructor = symbolTable.referenceConstructor(
expression.descriptor.constructedClass.constructors.single { it.valueParameters.size == 0 }
)
return builder.irBlock(expression) {
// Required for the IR to be valid, will be ignored in codegen:
+IrDelegatingConstructorCallImpl(startOffset, endOffset, superConstructor, superConstructor.descriptor)
+irCall(symbols.interopObjCObjectInitFrom).apply {
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
putValueArgument(0, initCall)
}
}
}
return expression
}
private fun IrBuilderWithScope.genLoweredObjCMethodCall(info: ObjCMethodInfo, superQualifier: IrClassSymbol?,
receiver: IrExpression, arguments: List<IrExpression>): IrExpression {
val superClass = superQualifier?.let { getObjCClass(it) } ?:
irCall(symbols.getNativeNullPtr)
val bridge = symbolTable.referenceSimpleFunction(info.bridge)
return irCall(bridge).apply {
putValueArgument(0, superClass)
putValueArgument(1, receiver)
assert(arguments.size + 2 == info.bridge.valueParameters.size)
arguments.forEachIndexed { index, argument ->
putValueArgument(index + 2, argument)
}
}
}
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()
return when (expression.descriptor.original) {
val descriptor = expression.descriptor.original
if (descriptor is ConstructorDescriptor) {
val initMethod = getObjCInitMethod(descriptor)
if (initMethod != null) {
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
assert(expression.extensionReceiver == null)
assert(expression.dispatchReceiver == null)
builder.at(expression)
return builder.genLoweredObjCMethodCall(
initMethod.getExternalObjCMethodInfo()!!,
superQualifier = null,
receiver = builder.callAlloc(symbolTable.referenceClass(descriptor.constructedClass)),
arguments = arguments
)
}
}
descriptor.getExternalObjCMethodInfo()?.let { methodInfo ->
val isInteropStubsFile =
currentFile.fileAnnotations.any { it.fqName == FqName("kotlinx.cinterop.InteropStubs") }
// Special case: bridge from Objective-C method implementation template to Kotlin method;
// handled in CodeGeneratorVisitor.callVirtual.
val useKotlinDispatch = isInteropStubsFile &&
builder.scope.scopeOwner.annotations.hasAnnotation(FqName("konan.internal.ExportForCppRuntime"))
if (!useKotlinDispatch) {
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
assert(expression.extensionReceiver == null)
if (expression.superQualifier?.isObjCMetaClass() == true) {
context.reportCompilationError(
"Super calls to Objective-C meta classes are not supported yet",
currentFile, expression
)
}
if (expression.superQualifier?.isInterface == true) {
context.reportCompilationError(
"Super calls to Objective-C protocols are not allowed",
currentFile, expression
)
}
builder.at(expression)
return builder.genLoweredObjCMethodCall(
methodInfo,
superQualifier = expression.superQualifierSymbol,
receiver = expression.dispatchReceiver!!,
arguments = arguments
)
}
}
return when (descriptor) {
context.interopBuiltIns.typeOf -> {
val typeArgument = expression.getSingleTypeArgument()
val classDescriptor = TypeUtils.getClassDescriptor(typeArgument)
@@ -65,8 +307,8 @@ internal class InteropLoweringPart1(val context: Context) : IrElementTransformer
error("native variable class $classDescriptor must have the companion object")
IrGetObjectValueImpl(
expression.startOffset, expression.endOffset,
companionObjectDescriptor.defaultType, companionObjectDescriptor
expression.startOffset, expression.endOffset, companionObjectDescriptor.defaultType,
symbolTable.referenceClass(companionObjectDescriptor)
)
}
}
+3 -3
View File
@@ -37,7 +37,7 @@ libffiDir.osx = libffi-3.2.1-2-darwin-macos
llvmLtoFlags.osx =
llvmLtoOptFlags.osx = -O3 -function-sections
llvmLtoNooptFlags.osx = -O1
linkerKonanFlags.osx = -lc++
linkerKonanFlags.osx = -lc++ -lobjc
linkerOptimizationFlags.osx = -dead_strip
linkerDebugFlags.osx = -S
osVersionMinFlagLd.osx = -macosx_version_min
@@ -63,7 +63,7 @@ llvmLtoFlags.ios =
llvmLtoOptFlags.ios = -O3 -function-sections
linkerDebugFlags.ios = -S
llvmLtoNooptFlags.ios = -O1
linkerKonanFlags.ios = -lc++
linkerKonanFlags.ios = -lc++ -lobjc
linkerOptimizationFlags.ios = -dead_strip
osVersionMinFlagLd.ios = -iphoneos_version_min
osVersionMinFlagClang.ios = -miphoneos-version-min
@@ -83,7 +83,7 @@ libffiDir.ios_sim = libffi-3.2.1-2-darwin-ios-sim
llvmLtoFlags.ios_sim =
llvmLtoOptFlags.ios_sim = -O3 -function-sections
llvmLtoNooptFlags.ios_sim = -O1
linkerKonanFlags.ios_sim = -lc++
linkerKonanFlags.ios_sim = -lc++ -lobjc
linkerOptimizationFlags.ios_sim = -dead_strip
linkerDebugFlags.ios_sim = -S
osVersionMinFlagLd.ios_sim = -ios_simulator_version_min
+22
View File
@@ -1995,6 +1995,13 @@ kotlinNativeInterop {
linkerOpts '-framework', 'OpenGL', '-framework', 'GLUT'
flavor 'native'
}
objcSmoke {
defFile 'interop/objc/objcSmoke.def'
headers "$projectDir/interop/objc/smoke.h"
linkerOpts "-L$buildDir", "-lobjcsmoke"
flavor 'native'
}
}
}
@@ -2057,4 +2064,19 @@ if (isMac()) {
source = "interop/basics/opengl_teapot.kt"
interop = 'opengl'
}
task interop_objc_smoke(type: RunInteropKonanTest) {
goldValue = "Hello, World!\nKotlin says: Hello, everybody!\nHello from Kotlin\n2, 1\nDeallocated\nDeallocated\n"
source = "interop/objc/smoke.kt"
interop = 'objcSmoke'
doFirst {
execClang {
args "$projectDir/interop/objc/smoke.m"
args "-lobjc", '-fobjc-arc'
args '-fPIC', '-shared', '-o', "$buildDir/libobjcsmoke.dylib"
}
}
}
}
@@ -0,0 +1,2 @@
language = Objective-C
headerFilter = **/smoke.h
+24
View File
@@ -0,0 +1,24 @@
#import <objc/NSObject.h>
@protocol Printer
@required
-(void)print:(const char*)string;
@end;
@interface Foo : NSObject
@property NSString* name;
-(void)hello;
-(void)helloWithPrinter:(id <Printer>)printer;
@end;
@protocol MutablePair
@required
@property (readonly) int first;
@property (readonly) int second;
-(void)update:(int)index add:(int)delta;
-(void)update:(int)index sub:(int)delta;
@end;
void replacePairElements(id <MutablePair> pair, int first, int second);
@@ -0,0 +1,55 @@
import kotlinx.cinterop.*
import objcSmoke.*
fun main(args: Array<String>) {
autoreleasepool {
run()
}
}
fun run() {
val foo = Foo()
foo.hello()
foo.name = "everybody"
foo.helloWithPrinter(object : NSObject(), PrinterProtocol {
override fun print(string: CPointer<ByteVar>?) {
println("Kotlin says: " + string?.toKString())
}
})
Bar().hello()
val pair = MutablePairImpl(42, 17)
replacePairElements(pair, 1, 2)
pair.swap()
println("${pair.first}, ${pair.second}")
}
fun MutablePairProtocol.swap() {
update(0, add = second)
update(1, sub = first)
update(0, add = second)
update(1, sub = second*2)
}
class Bar : Foo() {
override fun helloWithPrinter(printer: PrinterProtocol) = memScoped {
printer.print("Hello from Kotlin".cstr.getPointer(memScope))
}
}
@Suppress("CONFLICTING_OVERLOADS")
class MutablePairImpl(first: Int, second: Int) : NSObject(), MutablePairProtocol {
private var elements = intArrayOf(first, second)
override fun first() = elements.first()
override fun second() = elements.last()
override fun update(index: Int, add: Int) {
elements[index] += add
}
override fun update(index: Int, sub: Int) {
elements[index] -= sub
}
}
+46
View File
@@ -0,0 +1,46 @@
#import <stdio.h>
#import <Foundation/NSString.h>
#import "smoke.h"
@interface CPrinter : NSObject <Printer>
-(void)print:(const char*)string;
@end;
@implementation CPrinter
-(void)print:(const char*)string {
printf("%s\n", string);
fflush(stdout);
}
@end;
@implementation Foo
@synthesize name;
-(instancetype)init {
if (self = [super init]) {
self.name = @"World";
}
return self;
}
-(void)hello {
CPrinter* printer = [[CPrinter alloc] init];
[self helloWithPrinter:printer];
}
-(void)helloWithPrinter:(id <Printer>)printer {
NSString* message = [NSString stringWithFormat:@"Hello, %@!", self.name];
[printer print:message.UTF8String];
}
-(void)dealloc {
printf("Deallocated\n");
}
@end;
void replacePairElements(id <MutablePair> pair, int first, int second) {
[pair update:0 add:(first - pair.first)];
[pair update:1 sub:(pair.second - second)];
}