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,
|
||||
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>()
|
||||
|
||||
|
||||
+36
-7
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user