[WIP] Swift export: introduce MixedAST instead of String code representation
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/SirBridgeProviderImpl.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/SirBridgeProviderImpl.kt index feff9e9..4f59500 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/SirBridgeProviderImpl.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/SirBridgeProviderImpl.kt
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.sir.* import org.jetbrains.kotlin.sir.providers.* +import org.jetbrains.kotlin.sir.providers.impl.tree.MixedAST +import org.jetbrains.kotlin.sir.providers.impl.tree.ast +import org.jetbrains.kotlin.sir.providers.impl.tree.invoke import org.jetbrains.kotlin.sir.providers.source.kaSymbolOrNull import org.jetbrains.kotlin.sir.providers.utils.KotlinCoroutineSupportModule import org.jetbrains.kotlin.sir.providers.utils.KotlinRuntimeSupportModule @@ -234,9 +237,11 @@ } else if (errorParameter != null) { add("var ${errorParameter.name}: UnsafeMutableRawPointer? = nil") add("let _result = ".takeIf { resultTransformer != null }.orEmpty() + descriptor.swiftInvocationLineForCBridge(typeNamer)) - val error = errorParameter.bridge.inSwiftSources.kotlinToSwift(typeNamer, errorParameter.name) + val error = errorParameter.bridge.inSwiftSources.kotlinToSwift(typeNamer, errorParameter.name.ast()) add("guard ${errorParameter.name} == nil else { throw KotlinError(wrapped: $error) }") - resultTransformer?.let { add(it(descriptor.returnType.inSwiftSources.kotlinToSwift(typeNamer, "_result"))) } + resultTransformer?.let { + add(it(descriptor.returnType.inSwiftSources.kotlinToSwift(typeNamer, "_result".ast()).toString())) + } } else { val swiftCallAndTransformationLines = descriptor.swiftLinesForCBridgeCallAndTransformation(typeNamer) addAll(swiftCallAndTransformationLines.dropLast(1)) @@ -295,7 +300,7 @@ allParameters.forEach { val parameterName = "__${it.name}".kotlinIdentifier - add("${indent}val $parameterName = ${it.bridge.inKotlinSources.swiftToKotlin(typeNamer, it.name.kotlinIdentifier)}") + add("${indent}val $parameterName = ${it.bridge.inKotlinSources.swiftToKotlin(typeNamer, it.name.kotlinIdentifier.ast())}") } val callSite = buildCallSite() val resultName = "_result" @@ -326,7 +331,7 @@ """ try { val $resultName = $callSite - return ${returnType.inKotlinSources.kotlinToSwift(typeNamer, resultName)} + return ${returnType.inKotlinSources.kotlinToSwift(typeNamer, resultName.ast())} } catch (error: Throwable) { __${errorParameter.name}.value = StableRef.create(error).asCPointer() return $defaultValue @@ -335,28 +340,28 @@ ) } else { add("${indent}val $resultName = $callSite") - add("${indent}return ${returnType.inKotlinSources.kotlinToSwift(typeNamer, resultName)}") + add("${indent}return ${returnType.inKotlinSources.kotlinToSwift(typeNamer, resultName.ast())}") } } add("}") } -private fun BridgeFunctionDescriptor.swiftInvocationLineForCBridge(typeNamer: SirTypeNamer): String { - val parameters = allParameters.filter { it.isRenderable }.joinToString { +private fun BridgeFunctionDescriptor.swiftInvocationLineForCBridge(typeNamer: SirTypeNamer): MixedAST { + val parameters = allParameters.filter { it.isRenderable }.map { // We fix ugly `self` escaping here. This is the only place we'd otherwise need full support for swift's contextual keywords - it.bridge.inSwiftSources.swiftToKotlin(typeNamer, it.name.takeIf { it == "self" } ?: it.name.swiftIdentifier) + it.bridge.inSwiftSources.swiftToKotlin(typeNamer, (it.name.takeIf { it == "self" } ?: it.name.swiftIdentifier).ast()) } - return "$cBridgeName($parameters)" + return cBridgeName.ast().invoke(*parameters.toTypedArray()) } private fun BridgeFunctionDescriptor.swiftLinesForCBridgeCallAndTransformation(typeNamer: SirTypeNamer): List<String> { val swiftInvocation = swiftInvocationLineForCBridge(typeNamer) if (returnType.typeList.size <= 1) { - return listOf(returnType.inSwiftSources.kotlinToSwift(typeNamer, swiftInvocation)) + return listOf(returnType.inSwiftSources.kotlinToSwift(typeNamer, swiftInvocation).toString()) } return buildList { add("let _result = $swiftInvocation") - add(returnType.inSwiftSources.kotlinToSwift(typeNamer, "_result")) + add(returnType.inSwiftSources.kotlinToSwift(typeNamer, "_result".ast()).toString()) } } @@ -429,7 +434,7 @@ private val BridgeFunctionDescriptor.safeImportName: String get() = kotlinFqName.pathSegments().joinToString(separator = "_") { it.asString().replace("_", "__") } -private fun String.prependIndentToTrailingLines(indent: String): String = this.lines().let { lines -> +private fun MixedAST.prependIndentToTrailingLines(indent: String): String = toString().lines().let { lines -> lines.singleOrNull() ?: buildString { append(lines.first()) for (line in lines.drop(1)) {
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt index 26c06c0..3d88d6d 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/BridgeProvider/TypeBridging.kt
@@ -9,6 +9,33 @@ import org.jetbrains.kotlin.sir.providers.SirSession import org.jetbrains.kotlin.sir.providers.SirTypeNamer import org.jetbrains.kotlin.sir.providers.impl.BridgeProvider.Bridge.* +import org.jetbrains.kotlin.sir.providers.impl.tree.MixedAST +import org.jetbrains.kotlin.sir.providers.impl.tree.access +import org.jetbrains.kotlin.sir.providers.impl.tree.addr +import org.jetbrains.kotlin.sir.providers.impl.tree.asExcl +import org.jetbrains.kotlin.sir.providers.impl.tree.ast +import org.jetbrains.kotlin.sir.providers.impl.tree.block +import org.jetbrains.kotlin.sir.providers.impl.tree.brackets +import org.jetbrains.kotlin.sir.providers.impl.tree.cond +import org.jetbrains.kotlin.sir.providers.impl.tree.create +import org.jetbrains.kotlin.sir.providers.impl.tree.createClassWrapper +import org.jetbrains.kotlin.sir.providers.impl.tree.createProtocolWrapper +import org.jetbrains.kotlin.sir.providers.impl.tree.createRetainedExternalRCRef +import org.jetbrains.kotlin.sir.providers.impl.tree.dereferenceExternalRCRef +import org.jetbrains.kotlin.sir.providers.impl.tree.eq +import org.jetbrains.kotlin.sir.providers.impl.tree.exclExcl +import org.jetbrains.kotlin.sir.providers.impl.tree.externalRCRef +import org.jetbrains.kotlin.sir.providers.impl.tree.interpretObjCPointer +import org.jetbrains.kotlin.sir.providers.impl.tree.invoke +import org.jetbrains.kotlin.sir.providers.impl.tree.invokeLambda +import org.jetbrains.kotlin.sir.providers.impl.tree.kotlinTypeName +import org.jetbrains.kotlin.sir.providers.impl.tree.named +import org.jetbrains.kotlin.sir.providers.impl.tree.objcPtr +import org.jetbrains.kotlin.sir.providers.impl.tree.op +import org.jetbrains.kotlin.sir.providers.impl.tree.ret +import org.jetbrains.kotlin.sir.providers.impl.tree.safeAccess +import org.jetbrains.kotlin.sir.providers.impl.tree.switch +import org.jetbrains.kotlin.sir.providers.impl.tree.variable import org.jetbrains.kotlin.sir.providers.toBridge import org.jetbrains.kotlin.sir.providers.utils.KotlinRuntimeModule import org.jetbrains.kotlin.sir.providers.utils.KotlinRuntimeSupportModule @@ -160,14 +187,14 @@ * Generate value conversions from Swift to Kotlin. */ internal interface SwiftToKotlinValueConversion { - fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = valueExpression + fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } /** * Generate value conversions from Kotlin to Swift. */ internal interface KotlinToSwiftValueConversion { - fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = valueExpression + fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } /** @@ -176,13 +203,16 @@ internal interface ValueConversion : SwiftToKotlinValueConversion, KotlinToSwiftValueConversion internal object IdentityValueConversion : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = valueExpression - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = valueExpression + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } -private fun String.mapSwift(temporalName: String = "it", transform: (String) -> String): String { - val adapter = transform(temporalName).takeIf { it != temporalName } - return this + (adapter?.let { ".map { $temporalName in $it }" } ?: "") +private fun MixedAST.mapSwift(temporalName: String = "it", transform: (MixedAST) -> MixedAST): MixedAST { + val adapter = transform(temporalName.ast()).takeIf { it.toString() != temporalName } + if (adapter == null) return this + return access("map").invokeLambda { + temporalName.ast().op(MixedAST.Operator.IN, adapter) + } } internal sealed interface AnyBridge { @@ -275,9 +305,9 @@ object AsVoid : WithSingleType(SirNominalType(SirSwiftModule.void), KotlinType.Unit, CType.Void) { override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = "Unit" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = MixedAST.Unit - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = valueExpression + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } override val inSwiftSources = IdentityValueConversion @@ -289,11 +319,14 @@ CType.Int32 ) { override val inKotlinSources = object : SwiftToKotlinValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = valueExpression + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } override val inSwiftSources = object : SwiftToKotlinValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = "{ $valueExpression; return 0 }()" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = block { + +valueExpression + +"0".ast().ret() + }.invoke() } } @@ -315,27 +348,28 @@ class AsObject(swiftType: SirNominalType, kotlinType: KotlinType, cType: CType) : WithSingleType(swiftType, kotlinType, cType) { override val inKotlinSources = object : ValueConversion { // nulls are handled by AsOptionalWrapper, so safe to cast from nullable to non-nullable - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.dereferenceExternalRCRef($valueExpression) as ${ + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.dereferenceExternalRCRef().op( + MixedAST.Operator.AS, typeNamer.kotlinFqName( swiftType, SirTypeNamer.KotlinNameType.PARAMETRIZED - ) - }" + ).ast() + ) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.createRetainedExternalRCRef($valueExpression)" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.createRetainedExternalRCRef() } override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = "${valueExpression}.__externalRCRef()" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.externalRCRef() - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { - val swiftFqName = typeNamer.swiftFqName(swiftType) + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { return if (swiftType.isValueType) { - "$swiftFqName(__externalRCRefUnsafe: $valueExpression, options: .asBestFittingWrapper)" + swiftType.create(typeNamer, valueExpression) } else { - "$swiftFqName.__createClassWrapper(externalRCRef: $valueExpression)" + swiftType.createClassWrapper(typeNamer, valueExpression) } } } @@ -359,23 +393,23 @@ */ object AsAnyBridgeable : WithSingleType(KotlinRuntimeSupportModule.kotlinBridgeableType, KotlinType.KotlinObject, CType.Object) { override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.dereferenceExternalRCRef($valueExpression) as kotlin.Any" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.dereferenceExternalRCRef().op(MixedAST.Operator.AS, MixedAST.KotlinAny) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.createRetainedExternalRCRef($valueExpression)" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.createRetainedExternalRCRef() } override val inSwiftSources: ValueConversion = object : ValueConversion { override fun swiftToKotlin( typeNamer: SirTypeNamer, - valueExpression: String, - ): String { - return "$valueExpression.__externalRCRef()" - } + valueExpression: MixedAST, + ): MixedAST = valueExpression.externalRCRef() - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "${typeNamer.swiftFqName(SirNominalType(KotlinRuntimeModule.kotlinBase))}.__createProtocolWrapper(externalRCRef: $valueExpression) as! ${typeNamer.swiftFqName(swiftType)}" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + SirNominalType(KotlinRuntimeModule.kotlinBase) + .createProtocolWrapper(typeNamer, valueExpression) + .asExcl(typeNamer, swiftType) } } @@ -394,34 +428,38 @@ */ class AsExistential(swiftType: SirExistentialType, kotlinType: KotlinType, cType: CType) : WithSingleType(swiftType, kotlinType, cType) { override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.dereferenceExternalRCRef($valueExpression) as ${ + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.dereferenceExternalRCRef().op( + MixedAST.Operator.AS, typeNamer.kotlinFqName( swiftType, SirTypeNamer.KotlinNameType.PARAMETRIZED - ) - }" + ).ast() + ) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.createRetainedExternalRCRef($valueExpression)" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.createRetainedExternalRCRef() } override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = "${valueExpression}.__externalRCRef()" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.externalRCRef() - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "${typeNamer.swiftFqName(SirNominalType(KotlinRuntimeModule.kotlinBase))}.__createProtocolWrapper(externalRCRef: $valueExpression) as! ${typeNamer.swiftFqName(swiftType)}" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + SirNominalType(KotlinRuntimeModule.kotlinBase) + .createProtocolWrapper(typeNamer, valueExpression) + .asExcl(typeNamer, swiftType) } } class AsOpaqueObject(swiftType: SirType, kotlinType: KotlinType, cType: CType) : WithSingleType(swiftType, kotlinType, cType) { override val inKotlinSources = object : ValueConversion { // nulls are handled by AsOptionalWrapper, so safe to cast from nullable to non-nullable - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.dereferenceExternalRCRef($valueExpression)!!" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.dereferenceExternalRCRef().exclExcl() - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "kotlin.native.internal.ref.createRetainedExternalRCRef($valueExpression)" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.createRetainedExternalRCRef() } override val inSwiftSources = IdentityValueConversion @@ -432,11 +470,11 @@ cType: CType, ) : WithSingleType(swiftType, KotlinType.ObjCObjectUnretained, cType) { override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = - "interpretObjCPointer<${typeNamer.kotlinFqName(swiftType, SirTypeNamer.KotlinNameType.PARAMETRIZED)}>($valueExpression)" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.interpretObjCPointer(swiftType, typeNamer) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "$valueExpression.objcPtr()" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.objcPtr() } override val inSwiftSources: ValueConversion = IdentityValueConversion @@ -448,11 +486,11 @@ ) : AsObjCBridged(swiftType, CType.id) { override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { - return "$valueExpression as! NSObject? ?? NSNull()" - } + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.op(MixedAST.Operator.AS_EXCL, "NSObject?".ast()) + .op(MixedAST.Operator.SWIFT_ELVIS, MixedAST.NsNull.invoke()) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = valueExpression + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = valueExpression } } @@ -465,11 +503,11 @@ // promotes `UInt16` (which `UTF16.CodeUnit` is) to the closest fitting signed integer storage (C int). // -[NSNumber toKotlin] then produces Int box when crossing bridge, // which doesn't pass strict cast checks near interpretObjCPointer - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = - "interpretObjCPointer<Int>($valueExpression).toChar()" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.interpretObjCPointer("Int").access("toChar") - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "$valueExpression.objcPtr()" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.objcPtr() } } else { super.inKotlinSources @@ -477,10 +515,10 @@ override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = - "NSNumber(value: $valueExpression)" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + "NSNumber".ast().invoke(valueExpression.named("value")) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { require(swiftType is SirNominalType) val fromNSNumberValue = when (swiftType.typeDeclaration) { SirSwiftModule.bool -> "boolValue" @@ -500,7 +538,7 @@ else -> error("Attempt to get ${swiftType.typeDeclaration} from NSNumber") } - return "$valueExpression.$fromNSNumberValue" + return valueExpression.access(fromNSNumberValue) } } } @@ -511,8 +549,8 @@ } override val inKotlinSources = object : ValueConversion by super.inKotlinSources { - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = - super@OptionalChar.inKotlinSources.kotlinToSwift(typeNamer, "${valueExpression}?.code") + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + super@OptionalChar.inKotlinSources.kotlinToSwift(typeNamer, valueExpression.safeAccess("code")) } } @@ -520,8 +558,8 @@ swiftType: SirNominalType, ) : AsObjCBridged(swiftType, CType.NSObject) { override val inSwiftSources: ValueConversion = object : ValueConversion { - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = - "$valueExpression as! ${typeNamer.swiftFqName(swiftType)}" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.asExcl(typeNamer, swiftType) } } @@ -530,17 +568,16 @@ cType: CType, ) : AsObjCBridged(swiftType, cType) { abstract inner class InSwiftSources : ValueConversion { - abstract override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String + abstract override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { - return "$valueExpression as! ${typeNamer.swiftFqName(swiftType)}" - } + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.asExcl(typeNamer, swiftType) } } open class AsNSArray(swiftType: SirNominalType, elementBridge: WithSingleType) : AsNSCollection(swiftType, CType.NSArray(elementBridge.cType)) { override val inSwiftSources = object : InSwiftSources() { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { return valueExpression.mapSwift { elementBridge.inSwiftSources.swiftToKotlin(typeNamer, it) } } } @@ -548,26 +585,21 @@ class AsNSArrayForVariadic(swiftType: SirNominalType, elementBridge: WithSingleType) : AsNSArray(swiftType, elementBridge) { override val inKotlinSources: ValueConversion = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { val arrayKind = typeNamer.kotlinPrimitiveFqNameIfAny(swiftType.typeArguments.single()) ?: "Typed" - return "interpretObjCPointer<${ - typeNamer.kotlinFqName( - swiftType, - SirTypeNamer.KotlinNameType.PARAMETRIZED - ) - }>($valueExpression).to${arrayKind}Array()" + return valueExpression.interpretObjCPointer(swiftType, typeNamer).access("to${arrayKind}Array").invoke() } - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "$valueExpression.objcPtr()" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.objcPtr() } } class AsNSSet(swiftType: SirNominalType, elementBridge: WithSingleType) : AsNSCollection(swiftType, CType.NSSet(elementBridge.cType)) { override val inSwiftSources = object : InSwiftSources() { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { val transformedElements = valueExpression.mapSwift { elementBridge.inSwiftSources.swiftToKotlin(typeNamer, it) } - return if (transformedElements == valueExpression) valueExpression else "Set($transformedElements)" + return if (transformedElements == valueExpression) valueExpression else "Set".ast().invoke(transformedElements) } } } @@ -576,16 +608,25 @@ AsNSCollection(swiftType, CType.NSDictionary(keyBridge.cType, valueBridge.cType)) { override val inSwiftSources = object : InSwiftSources() { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { - val keyAdapter = keyBridge.inSwiftSources.swiftToKotlin(typeNamer, "key") - val valueAdapter = valueBridge.inSwiftSources.swiftToKotlin(typeNamer, "value") - return if (keyAdapter == "key" && valueAdapter == "value") { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { + val key = "key".ast() + val value = "value".ast() + val keyAdapter = keyBridge.inSwiftSources.swiftToKotlin(typeNamer, key) + val valueAdapter = valueBridge.inSwiftSources.swiftToKotlin(typeNamer, value) + return if (keyAdapter.toString() == "key" && valueAdapter.toString() == "value") { valueExpression } else { - "Dictionary(uniqueKeysWithValues: $valueExpression.map { key, value in (" + - "${keyBridge.inSwiftSources.swiftToKotlin(typeNamer, "key")}, " + - "${valueBridge.inSwiftSources.swiftToKotlin(typeNamer, "value")} " + - ")})" + val argument = valueExpression.access("map").invokeLambda { + +key.op(MixedAST.Operator.COMMA, value) + .op( + MixedAST.Operator.IN, + brackets( + keyBridge.inSwiftSources.swiftToKotlin(typeNamer, key), + valueBridge.inSwiftSources.swiftToKotlin(typeNamer, value) + ) + ) + } + "Dictionary".ast().invoke(argument.named("uniqueKeysWithValues")) } } } @@ -597,14 +638,16 @@ CType.Void ) { override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = "null" - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = "Unit" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = MixedAST.Null + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = MixedAST.Unit } override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String) = error("unrepresentable") - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String) = - "{ ${valueExpression}; return nil; }()" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = error("unrepresentable") + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = block { + +valueExpression + +MixedAST.Nil.ret() + }.invoke() } } @@ -617,40 +660,46 @@ override val inKotlinSources: ValueConversion get() = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { require(wrappedObject is SwiftToKotlinBridge) - return "if ($valueExpression == kotlin.native.internal.NativePtr.NULL) null else ${ + return valueExpression.eq(MixedAST.NativeNull).cond( + MixedAST.Null, wrappedObject.inKotlinSources.swiftToKotlin(typeNamer, valueExpression) - }" + ) } - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { require(wrappedObject is KotlinToSwiftBridge) - return "if ($valueExpression == null) kotlin.native.internal.NativePtr.NULL else ${ + return valueExpression.eq(MixedAST.NativeNull).cond( + MixedAST.Null, wrappedObject.inKotlinSources.kotlinToSwift(typeNamer, valueExpression) - }" + ) } } override val inSwiftSources: ValueConversion = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { require( wrappedObject is AsObjCBridged || wrappedObject is AsObject || wrappedObject is AsExistential || wrappedObject is AsAnyBridgeable || wrappedObject is AsBlock || wrappedObject is SirCustomTypeTranslatorImpl.RangeBridge ) - return valueExpression.mapSwift { wrappedObject.inSwiftSources.swiftToKotlin(typeNamer, it) } + - " ?? ${wrappedObject.renderNil()}" + return valueExpression.mapSwift { + wrappedObject.inSwiftSources.swiftToKotlin(typeNamer, it) + }.op(MixedAST.Operator.SWIFT_ELVIS, wrappedObject.nil()) } - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { return when (wrappedObject) { is AsObjCBridged -> valueExpression.mapSwift { wrappedObject.inSwiftSources.kotlinToSwift(typeNamer, it) } is AsObject, is AsExistential, is AsAnyBridgeable, is SirCustomTypeTranslatorImpl.RangeBridge -> - "{ switch $valueExpression { case ${wrappedObject.renderNil()}: .none; case let res: ${ - wrappedObject.inSwiftSources.kotlinToSwift(typeNamer, "res") - }; } }()" + block { + +valueExpression.switch { + case(wrappedObject.nil(), MixedAST.None) + case("res".variable(), wrappedObject.inSwiftSources.kotlinToSwift(typeNamer, "res".ast())) + } + }.invoke() is AsBlock, is AsIs, is AsVoid, @@ -711,50 +760,46 @@ override val inKotlinSources: SwiftToKotlinValueConversion get() = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { val argsInClosure = parameters .mapIndexed { idx, el -> "arg${idx}" to el }.takeIf { it.isNotEmpty() } - val defineArgs = argsInClosure - ?.let { - " ${ - it.joinToString { - "${it.first}: ${ - typeNamer.kotlinFqName( - it.second.swiftType, - SirTypeNamer.KotlinNameType.PARAMETRIZED - ) - }" - } - } ->" + val defineArgs = argsInClosure?.map { (name, bridge) -> + name.ast().op(MixedAST.Operator.COLON, bridge.swiftType.kotlinTypeName(typeNamer)) + } + val callArgs = argsInClosure?.map { (name, bridge) -> + bridge.inKotlinSources.kotlinToSwift(typeNamer, name.ast()) + }.orEmpty() + return "run".ast().invokeLambda { + +"kotlinFun".variable().op( + MixedAST.Operator.ASSIGN, + "convertBlockPtrToKotlinFunction<$kotlinFunctionTypeRendered>".ast().invoke(valueExpression) + ) + +block(defineArgs?.let { MixedAST.LambdaParameters(it) }) { + +"_result".variable().op(MixedAST.Operator.ASSIGN, "kotlinFun".ast().invoke(*callArgs.toTypedArray())) + +returnType.inKotlinSources.swiftToKotlin(typeNamer, "_result".ast()) } - val callArgs = argsInClosure - ?.let { it.joinToString { it.second.inKotlinSources.kotlinToSwift(typeNamer, it.first) } } ?: "" - return """run { - | val kotlinFun = convertBlockPtrToKotlinFunction<$kotlinFunctionTypeRendered>($valueExpression); - | {${defineArgs ?: ""} - | val _result = kotlinFun($callArgs) - | ${returnType.inKotlinSources.swiftToKotlin(typeNamer, "_result")} - | } - |}""".replaceIndentByMargin(" ") + } } } override val inSwiftSources = object : SwiftToKotlinValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { val argsInClosure = parameters .mapIndexed { idx, el -> "arg${idx}" to el }.takeIf { it.isNotEmpty() } - val defineArgs = argsInClosure - ?.let { " ${it.joinToString { it.first }} in" } ?: "" - val callArgs = argsInClosure - ?.let { - it.joinToString { param -> - param.second.inSwiftSources.kotlinToSwift(typeNamer, param.first) - } - } ?: "" - return """{ - | let originalBlock = $valueExpression - | return {$defineArgs ${"return ${returnType.inSwiftSources.swiftToKotlin(typeNamer, "originalBlock($callArgs)")}"} } - |}()""".trimMargin() + val defineArgs = argsInClosure?.map { (name, bridge) -> + name.ast().op(MixedAST.Operator.IN, "".ast()) + } + val callArgs = argsInClosure?.map { (name, bridge) -> + bridge.inSwiftSources.kotlinToSwift(typeNamer, name.ast()) + }.orEmpty() + return block { + +"originalBlock".variable().op(MixedAST.Operator.ASSIGN, valueExpression) + +block(defineArgs?.let { MixedAST.LambdaParameters(it) }) { + returnType.inSwiftSources.swiftToKotlin( + typeNamer, "originalBlock".ast().invoke(*callArgs.toTypedArray()) + ).ret() + }.ret() + } } } } @@ -765,15 +810,15 @@ override val inSwiftSources: ValueConversion get() = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { - return "&$valueExpression" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { + return valueExpression.addr() } - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { - return "${typeNamer.swiftFqName(SirNominalType(KotlinRuntimeModule.kotlinBase))}.__createClassWrapper(externalRCRef: $valueExpression)" - } + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + SirNominalType(KotlinRuntimeModule.kotlinBase) + .createClassWrapper(typeNamer, valueExpression) } } } -private fun AnyBridge.renderNil(): String = if (this is AsObjCBridgedOptional) "NSNull()" else "nil" +private fun AnyBridge.nil(): MixedAST = if (this is AsObjCBridgedOptional) MixedAST.NsNull else MixedAST.Nil
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/SirCustomTypeTranslatorImpl.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/SirCustomTypeTranslatorImpl.kt index 1e6907f..c63a42b 100644 --- a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/SirCustomTypeTranslatorImpl.kt +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/SirCustomTypeTranslatorImpl.kt
@@ -52,6 +52,13 @@ import org.jetbrains.kotlin.sir.providers.impl.BridgeProvider.Bridge.AsNSSet import org.jetbrains.kotlin.sir.providers.impl.BridgeProvider.Bridge.AsObjCBridged import org.jetbrains.kotlin.sir.providers.impl.SirTypeProviderImpl.TypeTranslationCtx +import org.jetbrains.kotlin.sir.providers.impl.tree.MixedAST +import org.jetbrains.kotlin.sir.providers.impl.tree.access +import org.jetbrains.kotlin.sir.providers.impl.tree.ast +import org.jetbrains.kotlin.sir.providers.impl.tree.createRetainedExternalRCRef +import org.jetbrains.kotlin.sir.providers.impl.tree.invoke +import org.jetbrains.kotlin.sir.providers.impl.tree.op +import org.jetbrains.kotlin.sir.providers.impl.tree.parameterX import org.jetbrains.kotlin.sir.util.SirSwiftModule public class SirCustomTypeTranslatorImpl( @@ -214,24 +221,25 @@ get() = typeList.first().cType override val inKotlinSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String { - val operator = if (inclusive) ".." else "..<" - return "${valueExpression}_1 $operator ${valueExpression}_2" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { + val operator = if (inclusive) MixedAST.Operator.RANGE_KOTLIN else MixedAST.Operator.RANGE_UNTIL + return valueExpression.parameterX(1).op(operator, valueExpression.parameterX(2)) } - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String = - "kotlin.native.internal.ref.createRetainedExternalRCRef($valueExpression)" + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.createRetainedExternalRCRef() } override val inSwiftSources = object : ValueConversion { - override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: String): String = - "$valueExpression.lowerBound, $valueExpression.upperBound" + override fun swiftToKotlin(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST = + valueExpression.access("lowerBound").op(MixedAST.Operator.COMMA, valueExpression.access("upperBound")) - override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: String): String { + override fun kotlinToSwift(typeNamer: SirTypeNamer, valueExpression: MixedAST): MixedAST { val startBridge = nativePointerToMultipleObjCBridge(0) val endBridge = nativePointerToMultipleObjCBridge(1) - val operator = if (inclusive) "..." else "..<" - return "${startBridge.name}($valueExpression) $operator ${endBridge.name}($valueExpression)" + val operator = if (inclusive) MixedAST.Operator.RANGE_SWIFT else MixedAST.Operator.RANGE_UNTIL + return startBridge.name.ast().invoke(valueExpression) + .op(operator, endBridge.name.ast().invoke(valueExpression)) } }
diff --git a/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt new file mode 100644 index 0000000..6dabd68 --- /dev/null +++ b/native/swift/sir-providers/src/org/jetbrains/kotlin/sir/providers/impl/tree/MixedAST.kt
@@ -0,0 +1,248 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.sir.providers.impl.tree + +import org.jetbrains.kotlin.sir.SirType +import org.jetbrains.kotlin.sir.providers.SirTypeNamer + +/** + * This class represents simple AST, that is used in TypeBridging.kt + * for Swift & Kotlin generated code representation + */ +internal sealed class MixedAST { + class Direct(val expression: String) : MixedAST() { + override fun toString(): String = expression + } + + class ExternalRcRef(val argument: MixedAST) : MixedAST() { + override fun toString(): String = "$argument.__externalRCRef()" + } + + class BinaryOp(val left: MixedAST, val operator: Operator, val right: MixedAST) : MixedAST() { + override fun toString(): String = "$left$operator$right" + + fun replace(operator: Operator = this.operator, transform: (MixedAST) -> MixedAST) = + BinaryOp(transform(left), operator, transform(right)) + } + + class Conditional(val condition: MixedAST, val ifTrue: MixedAST, val ifFalse: MixedAST) : MixedAST() { + override fun toString(): String = "if ($condition) $ifTrue else $ifFalse" + } + + class Variable(val identifier: MixedAST) : MixedAST() { + override fun toString(): String = "let $identifier" + } + + class Case(val branch: MixedAST, val result: MixedAST) : MixedAST() { + override fun toString(): String = "case $branch: $result;" + } + + class Switch(val subject: MixedAST, val cases: List<Case>) : MixedAST() { + override fun toString(): String = buildString { + append("switch $subject { ") + append(cases.joinToString(separator = " ")) + append(" }") + } + } + + class Block(val lambdaParameters: LambdaParameters?, val argument: MixedAST) : MixedAST() { + override fun toString(): String = + if (lambdaParameters == null) "{ $argument }" + else "{ $lambdaParameters $argument }" + } + + class Invoke(val callee: MixedAST, val argument: MixedAST? = null) : MixedAST() { + override fun toString(): String = if (argument == null) "$callee()" else "$callee($argument)" + } + + class InvokeLambda(val callee: MixedAST, val argument: Block) : MixedAST() { + override fun toString(): String = "$callee $argument" + } + + class Return(val argument: MixedAST) : MixedAST() { + override fun toString(): String = "return $argument" + } + + class ExclExcl(val argument: MixedAST) : MixedAST() { + override fun toString(): String = "$argument!!" + } + + class Addr(val argument: MixedAST) : MixedAST() { + override fun toString(): String = "&$argument" + } + + class LambdaParameters(val parameters: List<MixedAST>) : MixedAST() { + override fun toString(): String = parameters.joinToString() + " -> " + } + + object Unit : MixedAST() { + override fun toString(): String = "Unit" + } + + object Null : MixedAST() { + override fun toString(): String = "null" + } + + object Nil : MixedAST() { + override fun toString(): String = "nil" + } + + object NativeNull : MixedAST() { + override fun toString(): String = "kotlin.native.internal.NativePtr.NULL" + } + + object NsNull : MixedAST() { + override fun toString(): String = "NSNull" + } + + object None : MixedAST() { + override fun toString(): String = ".none" + } + + object KotlinAny : MixedAST() { + override fun toString(): String = "kotlin.Any" + } + + enum class Operator(private val representation: String) { + COMMA(", "), + SEMICOLON("; "), + COLON(": "), + ACCESS("."), + SAFE_ACCESS("?."), + AS(" as "), + AS_EXCL(" as! "), + ASSIGN(" = "), + EQUALS(" == "), + IN(" in "), + SWIFT_ELVIS(" ?? "), + RANGE_KOTLIN(" .. "), + RANGE_SWIFT(" ... "), + RANGE_UNTIL(" ..< "), + ; + + override fun toString(): String = representation + } +} + +internal fun String.ast() = MixedAST.Direct(this) + +internal fun MixedAST.parameterX(index: Int): MixedAST.Direct { + assert(this is MixedAST.Direct) + return MixedAST.Direct("${this}_$index") +} + +internal fun MixedAST.access(name: String) = MixedAST.BinaryOp(this, MixedAST.Operator.ACCESS, name.ast()) + +internal fun MixedAST.safeAccess(name: String) = MixedAST.BinaryOp(this, MixedAST.Operator.SAFE_ACCESS, name.ast()) + +internal fun MixedAST.externalRCRef() = access("__externalRCRef").invoke() + +internal fun MixedAST.dereferenceExternalRCRef() = access("kotlin.native.internal.ref.dereferenceExternalRCRef").invoke() + +internal fun MixedAST.createRetainedExternalRCRef() = access("kotlin.native.internal.ref.createRetainedExternalRCRef").invoke() + +internal fun MixedAST.objcPtr() = access("objcPtr").invoke() + +internal fun MixedAST.interpretObjCPointer(type: SirType, namer: SirTypeNamer) = + interpretObjCPointer(type.kotlinTypeName(namer).toString()) + +internal fun MixedAST.interpretObjCPointer(typeName: String) = + "__interpretObjCPointer<$typeName>".ast().invoke(this) + +internal fun MixedAST.invoke(vararg arguments: MixedAST): MixedAST.Invoke { + if (arguments.isEmpty()) return MixedAST.Invoke(this) + var argument: MixedAST = arguments.first() + for (i in 1 until arguments.size) { + argument = argument.op(MixedAST.Operator.COMMA, arguments[i]) + } + return MixedAST.Invoke(this, argument) +} + +internal fun brackets(vararg arguments: MixedAST): MixedAST = + "".ast().invoke(*arguments) + +internal fun MixedAST.invokeLambda(f: BlockBuilder.() -> Unit) = + MixedAST.InvokeLambda(this, block(f)) + +internal fun String.variable(): MixedAST.Variable = MixedAST.Variable(ast()) + +internal fun MixedAST.named(name: String): MixedAST = name.ast().op(MixedAST.Operator.COLON, this) + +internal fun asBestFittingWrapper() = ".asBestFittingWrapper".ast().named("options") + +internal fun MixedAST.ret() = MixedAST.Return(this) + +internal fun MixedAST.exclExcl() = MixedAST.ExclExcl(this) + +internal fun MixedAST.asExcl(namer: SirTypeNamer, type: SirType) = + op(MixedAST.Operator.AS_EXCL, type.swiftTypeName(namer)) + +internal fun MixedAST.addr() = MixedAST.Addr(this) + +internal fun MixedAST.eq(right: MixedAST) = op(MixedAST.Operator.EQUALS, right) + +internal fun MixedAST.op(op: MixedAST.Operator, right: MixedAST) = MixedAST.BinaryOp(this, op, right) + +internal fun MixedAST.cond(ifTrue: MixedAST, ifFalse: MixedAST) = MixedAST.Conditional(this, ifTrue, ifFalse) + +internal fun SirType.kotlinTypeName(namer: SirTypeNamer): MixedAST = + namer.kotlinFqName(this, SirTypeNamer.KotlinNameType.PARAMETRIZED).ast() + +internal fun SirType.swiftTypeName(namer: SirTypeNamer): MixedAST = + namer.swiftFqName(this).ast() + +internal fun SirType.createProtocolWrapper(namer: SirTypeNamer, expression: MixedAST): MixedAST = + swiftTypeName(namer).access("__createProtocolWrapper").invoke(expression.named("externalRCRef")) + +internal fun SirType.createClassWrapper(namer: SirTypeNamer, expression: MixedAST): MixedAST = + swiftTypeName(namer).access("__createClassWrapper").invoke(expression.named("externalRCRef")) + +internal fun SirType.create(namer: SirTypeNamer, expression: MixedAST): MixedAST = + swiftTypeName(namer).invoke(expression.named("__externalRCRefUnsafe"), asBestFittingWrapper()) + +internal fun block(lambdaParameters: MixedAST.LambdaParameters? = null, f: BlockBuilder.() -> Unit): MixedAST.Block { + return BlockBuilder(lambdaParameters).apply { f() }.block +} + +internal fun MixedAST.switch(f: SwitchBuilder.() -> Unit): MixedAST.Switch { + return SwitchBuilder(this).apply { f() }.switch +} + +internal class BlockBuilder(val lambdaParameters: MixedAST.LambdaParameters?) { + private val statements: MutableList<MixedAST> = mutableListOf() + + operator fun MixedAST.unaryPlus() { + add(this) + } + + fun add(statement: MixedAST) { + statements += statement + } + + fun add(s: String) { + add(s.ast()) + } + + val block: MixedAST.Block + get() { + var ast = statements.first() + for (i in 1 until statements.size) { + ast = ast.op(MixedAST.Operator.SEMICOLON, statements[i]) + } + return MixedAST.Block(lambdaParameters, ast) + } +} + +internal class SwitchBuilder(val subject: MixedAST) { + private val cases: MutableList<MixedAST.Case> = mutableListOf() + + fun case(branch: MixedAST, result: MixedAST) { + cases.add(MixedAST.Case(branch, result)) + } + + val switch: MixedAST.Switch + get() = MixedAST.Switch(subject, cases) +}