Support importing non-conventional parameterless Objective-C init
Thus fix #2522 by supporting -[NSOutputStream initToMemory].
This commit is contained in:
committed by
SvyatoslavScherbina
parent
e2bef1070f
commit
7efab59bc1
+7
@@ -68,6 +68,13 @@ private fun ObjCMethod.getKotlinParameters(
|
|||||||
stubIrBuilder: StubsBuildingContext,
|
stubIrBuilder: StubsBuildingContext,
|
||||||
forConstructorOrFactory: Boolean
|
forConstructorOrFactory: Boolean
|
||||||
): List<FunctionParameterStub> {
|
): 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 names = getKotlinParameterNames(forConstructorOrFactory) // TODO: consider refactoring.
|
||||||
val result = mutableListOf<FunctionParameterStub>()
|
val result = mutableListOf<FunctionParameterStub>()
|
||||||
|
|
||||||
|
|||||||
+36
-7
@@ -87,7 +87,7 @@ private fun KotlinToCCallBuilder.addArgument(
|
|||||||
variadic: Boolean,
|
variadic: Boolean,
|
||||||
parameter: IrValueParameter?
|
parameter: IrValueParameter?
|
||||||
) {
|
) {
|
||||||
val argumentPassing = mapParameter(type, variadic, parameter, argument)
|
val argumentPassing = mapCalleeFunctionParameter(type, variadic, parameter, argument)
|
||||||
addArgument(argument, argumentPassing, variadic)
|
addArgument(argument, argumentPassing, variadic)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ private fun KotlinToCCallBuilder.addArgument(
|
|||||||
argumentPassing: KotlinToCArgumentPassing,
|
argumentPassing: KotlinToCArgumentPassing,
|
||||||
variadic: Boolean
|
variadic: Boolean
|
||||||
) {
|
) {
|
||||||
val cArgument = with(argumentPassing) { passValue(argument) }
|
val cArgument = with(argumentPassing) { passValue(argument) } ?: return
|
||||||
cCallBuilder.arguments += cArgument.expression
|
cCallBuilder.arguments += cArgument.expression
|
||||||
if (!variadic) cFunctionBuilder.addParameter(cArgument.type)
|
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,
|
it.type,
|
||||||
retained = it.isConsumed(),
|
retained = it.isConsumed(),
|
||||||
variadic = false,
|
variadic = false,
|
||||||
@@ -615,7 +615,7 @@ private fun cBoolType(target: KonanTarget): CType? = when (target.family) {
|
|||||||
else -> CTypes.signedChar
|
else -> CTypes.signedChar
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinToCCallBuilder.mapParameter(
|
private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
variadic: Boolean,
|
variadic: Boolean,
|
||||||
parameter: IrValueParameter?,
|
parameter: IrValueParameter?,
|
||||||
@@ -638,7 +638,7 @@ private fun KotlinToCCallBuilder.mapParameter(
|
|||||||
classifier == symbols.string && parameter?.isWCStringParameter() == true ->
|
classifier == symbols.string && parameter?.isWCStringParameter() == true ->
|
||||||
WCStringArgumentPassing()
|
WCStringArgumentPassing()
|
||||||
|
|
||||||
else -> stubs.mapType(
|
else -> stubs.mapFunctionParameterType(
|
||||||
type,
|
type,
|
||||||
retained = parameter?.isConsumed() ?: false,
|
retained = parameter?.isConsumed() ?: false,
|
||||||
variadic = variadic,
|
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) {
|
private sealed class TypeLocation(val element: IrElement) {
|
||||||
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
|
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
|
||||||
class FunctionCallResult(val call: IrFunctionAccessExpression) : TypeLocation(call)
|
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 class CExpression(val expression: String, val type: CType)
|
||||||
|
|
||||||
private interface KotlinToCArgumentPassing {
|
private interface KotlinToCArgumentPassing {
|
||||||
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression
|
fun KotlinToCCallBuilder.passValue(expression: IrExpression): CExpression?
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ValueReturning {
|
private interface ValueReturning {
|
||||||
@@ -807,10 +817,12 @@ private interface ValueReturning {
|
|||||||
fun CCallbackBuilder.returnValue(expression: IrExpression)
|
fun CCallbackBuilder.returnValue(expression: IrExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ValuePassing : KotlinToCArgumentPassing, ValueReturning {
|
private interface ArgumentPassing : KotlinToCArgumentPassing {
|
||||||
fun CCallbackBuilder.receiveValue(): IrExpression
|
fun CCallbackBuilder.receiveValue(): IrExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ValuePassing : ArgumentPassing, ValueReturning
|
||||||
|
|
||||||
private abstract class SimpleValuePassing : ValuePassing {
|
private abstract class SimpleValuePassing : ValuePassing {
|
||||||
abstract val kotlinBridgeType: IrType
|
abstract val kotlinBridgeType: IrType
|
||||||
abstract val cBridgeType: CType
|
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)"
|
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
|
||||||
|
|
||||||
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
|
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
|
||||||
|
|||||||
@@ -213,3 +213,10 @@ id getPrinterProtocolRaw() {
|
|||||||
Protocol* getPrinterProtocol() {
|
Protocol* getPrinterProtocol() {
|
||||||
return @protocol(Printer);
|
return @protocol(Printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface TestInitWithCustomSelector : NSObject
|
||||||
|
-(instancetype)initCustom;
|
||||||
|
@property BOOL custom;
|
||||||
|
|
||||||
|
+(instancetype _Nonnull)createCustom;
|
||||||
|
@end;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ fun run() {
|
|||||||
testOverrideInit()
|
testOverrideInit()
|
||||||
testMultipleInheritanceClash()
|
testMultipleInheritanceClash()
|
||||||
testClashingWithAny()
|
testClashingWithAny()
|
||||||
|
testInitWithCustomSelector()
|
||||||
|
|
||||||
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
assertEquals(2, ForwardDeclaredEnum.TWO.value)
|
||||||
|
|
||||||
@@ -374,6 +375,28 @@ fun testClashingWithAny() {
|
|||||||
assertTrue(TestClashingWithAny3().equals())
|
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 {
|
fun nsArrayOf(vararg elements: Any): NSArray = NSMutableArray().apply {
|
||||||
elements.forEach {
|
elements.forEach {
|
||||||
this.addObject(it as ObjCObject)
|
this.addObject(it as ObjCObject)
|
||||||
|
|||||||
@@ -254,3 +254,25 @@ static CustomRetainMethodsImpl* retainedCustomRetainMethodsImpl;
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@end;
|
@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;
|
||||||
|
|||||||
Reference in New Issue
Block a user