Rework Objective-C constructors and support 'initBy' intrinsic

This commit is contained in:
Svyatoslav Scherbina
2017-08-29 13:33:01 +03:00
committed by SvyatoslavScherbina
parent 5a7e3e20b4
commit 07f26dc1df
6 changed files with 65 additions and 7 deletions
@@ -51,8 +51,17 @@ class ObjCPointerHolder(inline val rawPtr: NativePtr) {
@konan.internal.ExportForCompiler @konan.internal.ExportForCompiler
private external fun ObjCObject.initFromPtr(ptr: NativePtr) private external fun ObjCObject.initFromPtr(ptr: NativePtr)
@konan.internal.Intrinsic
external fun <T : ObjCObjectBase> T.initBy(constructorCall: T): T
@konan.internal.ExportForCompiler @konan.internal.ExportForCompiler
private fun ObjCObject.initFrom(other: ObjCObject?) = this.initFromPtr(other!!.rawPtr) private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
if (superInitCallResult == null)
throw RuntimeException("Super initialization failed")
if (superInitCallResult.rawPtr != this.rawPtr)
throw UnsupportedOperationException("Super initializer has replaced object")
}
fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T) // TODO: make private
@@ -421,7 +421,7 @@ val ObjCClassOrProtocol.kotlinName: String get() = when (this) {
} }
private val ObjCClass.baseClassName: String private val ObjCClass.baseClassName: String
get() = baseClass?.name ?: "ObjCObject" get() = baseClass?.name ?: "ObjCObjectBase"
private fun Parameter.getTypeStringRepresentation() = private fun Parameter.getTypeStringRepresentation() =
(if (this.nsConsumed) "__attribute__((ns_consumed)) " else "") + type.getStringRepresentation() (if (this.nsConsumed) "__attribute__((ns_consumed)) " else "") + type.getStringRepresentation()
@@ -135,7 +135,6 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
.getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single() .getContributedDescriptors().filterIsInstance<PropertyDescriptor>().single()
val objCObjectInitFromPtr = packageScope.getContributedFunctions("initFromPtr").single() val objCObjectInitFromPtr = packageScope.getContributedFunctions("initFromPtr").single()
val objCObjectInitFrom = packageScope.getContributedFunctions("initFrom").single()
val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single() val allocObjCObject = packageScope.getContributedFunctions("allocObjCObject").single()
@@ -155,6 +154,9 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single() val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single()
val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single() val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single()
val objCObjectSuperInitCheck = packageScope.getContributedFunctions("superInitCheck").single()
val objCObjectInitBy = packageScope.getContributedFunctions("initBy").single()
val objCAction = packageScope.getContributedClassifier("ObjCAction") as ClassDescriptor val objCAction = packageScope.getContributedClassifier("ObjCAction") as ClassDescriptor
val objCOutlet = packageScope.getContributedClassifier("ObjCOutlet") as ClassDescriptor val objCOutlet = packageScope.getContributedClassifier("ObjCOutlet") as ClassDescriptor
@@ -56,7 +56,8 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
val interopObjCObjectInitFromPtr = val interopObjCObjectInitFromPtr =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFromPtr) symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFromPtr)
val interopObjCObjectInitFrom = symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectInitFrom) val interopObjCObjectSuperInitCheck =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectSuperInitCheck)
val interopObjCObjectRawValueGetter = val interopObjCObjectRawValueGetter =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr.getter!!) symbolTable.referenceSimpleFunction(context.interopBuiltIns.objCObjectRawPtr.getter!!)
@@ -1607,10 +1607,30 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return null return null
} }
private fun evaluateSpecialIntrinsicCall(expression: IrFunctionAccessExpression): LLVMValueRef? {
if (expression.descriptor.isIntrinsic) {
when (expression.descriptor.original) {
context.interopBuiltIns.objCObjectInitBy -> {
val receiver = evaluateExpression(expression.extensionReceiver!!)
val irConstructorCall = expression.getValueArgument(0) as IrCall
val constructorDescriptor = irConstructorCall.descriptor as ClassConstructorDescriptor
val constructorArgs = evaluateExplicitArgs(irConstructorCall)
val args = listOf(receiver) + constructorArgs
callDirect(constructorDescriptor, args, Lifetime.IRRELEVANT)
return receiver
}
}
}
return null
}
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun evaluateCall(value: IrMemberAccessExpression): LLVMValueRef { private fun evaluateCall(value: IrFunctionAccessExpression): LLVMValueRef {
context.log{"evaluateCall : ${ir2string(value)}"} context.log{"evaluateCall : ${ir2string(value)}"}
evaluateSpecialIntrinsicCall(value)?.let { return it }
val args = evaluateExplicitArgs(value) val args = evaluateExplicitArgs(value)
compileTimeEvaluate(value, args)?.let { return it } compileTimeEvaluate(value, args)?.let { return it }
@@ -1925,6 +1945,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
assert(args.isEmpty()) assert(args.isEmpty())
functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero, functionGenerationContext.allocArray(codegen.typeInfoValue(constructedClass), count = kImmZero,
lifetime = resultLifetime(callee)) lifetime = resultLifetime(callee))
} else if (constructedClass.isKotlinObjCClass()) {
callDirect(context.interopBuiltIns.allocObjCObject, listOf(genGetObjCClass(constructedClass)),
resultLifetime(callee))
} else { } else {
functionGenerationContext.allocInstance(typeInfoForAllocation(constructedClass), resultLifetime(callee)) functionGenerationContext.allocInstance(typeInfoForAllocation(constructedClass), resultLifetime(callee))
} }
@@ -388,7 +388,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
val initCall = builder.genLoweredObjCMethodCall( val initCall = builder.genLoweredObjCMethodCall(
initMethodInfo, initMethodInfo,
superQualifier = symbolTable.referenceClass(expression.descriptor.constructedClass), superQualifier = symbolTable.referenceClass(expression.descriptor.constructedClass),
receiver = builder.callAlloc(constructedClass.symbol), receiver = builder.irGet(constructedClass.thisReceiver!!.symbol),
arguments = initMethod.valueParameters.map { expression.getValueArgument(it)!! } arguments = initMethod.valueParameters.map { expression.getValueArgument(it)!! }
) )
@@ -400,7 +400,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
// Required for the IR to be valid, will be ignored in codegen: // Required for the IR to be valid, will be ignored in codegen:
+IrDelegatingConstructorCallImpl(startOffset, endOffset, superConstructor, superConstructor.descriptor) +IrDelegatingConstructorCallImpl(startOffset, endOffset, superConstructor, superConstructor.descriptor)
+irCall(symbols.interopObjCObjectInitFrom).apply { +irCall(symbols.interopObjCObjectSuperInitCheck).apply {
extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol) extensionReceiver = irGet(constructedClass.thisReceiver!!.symbol)
putValueArgument(0, initCall) putValueArgument(0, initCall)
} }
@@ -729,6 +729,29 @@ private class InteropTransformer(val context: Context, val irFile: IrFile) : IrB
} }
} }
interop.objCObjectInitBy -> {
val intrinsic = interop.objCObjectInitBy.name
val argument = expression.getValueArgument(0)!!
val constructedClass =
((argument as? IrCall)?.descriptor as? ClassConstructorDescriptor)?.constructedClass
if (constructedClass == null) {
context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
irFile, argument)
}
val extensionReceiver = expression.extensionReceiver!!
if (extensionReceiver !is IrGetValue ||
extensionReceiver.descriptor != constructedClass.thisAsReceiverParameter) {
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
irFile, extensionReceiver)
}
expression
}
else -> expression else -> expression
} }
} }