Support importing non-conventional parameterless Objective-C init

Thus fix #2522 by supporting -[NSOutputStream initToMemory].
This commit is contained in:
Svyatoslav Scherbina
2019-08-05 15:03:42 +03:00
committed by SvyatoslavScherbina
parent e2bef1070f
commit 7efab59bc1
5 changed files with 95 additions and 7 deletions
@@ -68,6 +68,13 @@ private fun ObjCMethod.getKotlinParameters(
stubIrBuilder: StubsBuildingContext,
forConstructorOrFactory: Boolean
): List<FunctionParameterStub> {
if (this.isInit && this.parameters.isEmpty() && this.selector != "init") {
// Create synthetic Unit parameter, just like Swift does in this case:
val parameterName = this.selector.removePrefix("init").removePrefix("With").decapitalize()
return listOf(FunctionParameterStub(parameterName, WrapperStubType(KotlinTypes.unit)))
// Note: this parameter is explicitly handled in compiler.
}
val names = getKotlinParameterNames(forConstructorOrFactory) // TODO: consider refactoring.
val result = mutableListOf<FunctionParameterStub>()
@@ -87,7 +87,7 @@ private fun KotlinToCCallBuilder.addArgument(
variadic: Boolean,
parameter: IrValueParameter?
) {
val argumentPassing = mapParameter(type, variadic, parameter, argument)
val argumentPassing = mapCalleeFunctionParameter(type, variadic, parameter, argument)
addArgument(argument, argumentPassing, variadic)
}
@@ -96,7 +96,7 @@ private fun KotlinToCCallBuilder.addArgument(
argumentPassing: KotlinToCArgumentPassing,
variadic: Boolean
) {
val cArgument = with(argumentPassing) { passValue(argument) }
val cArgument = with(argumentPassing) { passValue(argument) } ?: return
cCallBuilder.arguments += cArgument.expression
if (!variadic) cFunctionBuilder.addParameter(cArgument.type)
}
@@ -413,7 +413,7 @@ private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParamete
})
}
val valuePassing = stubs.mapType(
val valuePassing = stubs.mapFunctionParameterType(
it.type,
retained = it.isConsumed(),
variadic = false,
@@ -615,7 +615,7 @@ private fun cBoolType(target: KonanTarget): CType? = when (target.family) {
else -> CTypes.signedChar
}
private fun KotlinToCCallBuilder.mapParameter(
private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
type: IrType,
variadic: Boolean,
parameter: IrValueParameter?,
@@ -638,7 +638,7 @@ private fun KotlinToCCallBuilder.mapParameter(
classifier == symbols.string && parameter?.isWCStringParameter() == true ->
WCStringArgumentPassing()
else -> stubs.mapType(
else -> stubs.mapFunctionParameterType(
type,
retained = parameter?.isConsumed() ?: false,
variadic = variadic,
@@ -647,6 +647,16 @@ private fun KotlinToCCallBuilder.mapParameter(
}
}
private fun KotlinStubs.mapFunctionParameterType(
type: IrType,
retained: Boolean,
variadic: Boolean,
location: TypeLocation
): ArgumentPassing = when {
type.isUnit() && !variadic -> IgnoredUnitArgumentPassing
else -> mapType(type, retained = retained, variadic = variadic, location = location)
}
private sealed class TypeLocation(val element: IrElement) {
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
class FunctionCallResult(val call: IrFunctionAccessExpression) : TypeLocation(call)
@@ -797,7 +807,7 @@ private fun KotlinStubs.isObjCReferenceType(type: IrType): Boolean {
private class CExpression(val expression: String, val type: CType)
private interface KotlinToCArgumentPassing {
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression?
}
private interface ValueReturning {
@@ -807,10 +817,12 @@ private interface ValueReturning {
fun CCallbackBuilder.returnValue(expression: IrExpression)
}
private interface ValuePassing : KotlinToCArgumentPassing, ValueReturning {
private interface ArgumentPassing : KotlinToCArgumentPassing {
fun CCallbackBuilder.receiveValue(): IrExpression
}
private interface ValuePassing : ArgumentPassing, ValueReturning
private abstract class SimpleValuePassing : ValuePassing {
abstract val kotlinBridgeType: IrType
abstract val cBridgeType: CType
@@ -1410,6 +1422,23 @@ private object VoidReturning : ValueReturning {
}
}
private object IgnoredUnitArgumentPassing : ArgumentPassing {
override fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression? {
// Note: it is not correct to just drop the expression (due to possible side effects),
// so (in lack of other options) evaluate the expression and pass ignored value to the bridge:
val bridgeArgument = irBuilder.irBlock {
+expression
+irInt(0)
}
passThroughBridge(bridgeArgument, irBuilder.context.irBuiltIns.intType, CTypes.int).name
return null
}
override fun CCallbackBuilder.receiveValue(): IrExpression {
return bridgeBuilder.kotlinIrBuilder.irGetObject(irBuiltIns.unitClass)
}
}
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
@@ -213,3 +213,10 @@ id getPrinterProtocolRaw() {
Protocol* getPrinterProtocol() {
return @protocol(Printer);
}
@interface TestInitWithCustomSelector : NSObject
-(instancetype)initCustom;
@property BOOL custom;
+(instancetype _Nonnull)createCustom;
@end;
@@ -25,6 +25,7 @@ fun run() {
testOverrideInit()
testMultipleInheritanceClash()
testClashingWithAny()
testInitWithCustomSelector()
assertEquals(2, ForwardDeclaredEnum.TWO.value)
@@ -374,6 +375,28 @@ fun testClashingWithAny() {
assertTrue(TestClashingWithAny3().equals())
}
fun testInitWithCustomSelector() {
assertFalse(TestInitWithCustomSelector().custom)
assertTrue(TestInitWithCustomSelector(custom = Unit).custom)
val customSubclass: TestInitWithCustomSelector = TestInitWithCustomSelectorSubclass.createCustom()
assertTrue(customSubclass is TestInitWithCustomSelectorSubclass)
assertTrue(customSubclass.custom)
// Test side effect:
var ok = false
assertTrue(TestInitWithCustomSelector(run { ok = true }).custom)
assertTrue(ok)
}
private class TestInitWithCustomSelectorSubclass : TestInitWithCustomSelector {
@OverrideInit constructor(custom: Unit) : super(custom) {
assertSame(Unit, custom)
}
companion object : TestInitWithCustomSelectorMeta()
}
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
elements.forEach {
this.addObject(it as ObjCObject)
+22
View File
@@ -254,3 +254,25 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
return YES;
}
@end;
@implementation TestInitWithCustomSelector
-(instancetype)initCustom {
if (self = [super init]) {
self.custom = YES;
}
return self;
}
-(instancetype)init {
if (self = [super init]) {
self.custom = NO;
}
return self;
}
+(instancetype)createCustom {
return [[self alloc] initCustom];
}
@end;