[K/N] Modularise :kotlin-native:native.backend to extract objc header generation (1/2)

^KT-63905
This commit is contained in:
Sebastian Sellmair
2023-11-29 11:27:55 +01:00
committed by Space Team
parent 4294c7bab7
commit ae9f3d66c2
69 changed files with 0 additions and 0 deletions
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
sealed class BinaryType<out T> {
class Primitive(val type: PrimitiveBinaryType) : BinaryType<Nothing>()
class Reference<T>(val types: Sequence<T>, val nullable: Boolean) : BinaryType<T>()
}
fun BinaryType<*>.primitiveBinaryTypeOrNull(): PrimitiveBinaryType? = when (this) {
is BinaryType.Primitive -> this.type
is BinaryType.Reference -> null
}
enum class PrimitiveBinaryType {
BOOLEAN, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, POINTER, VECTOR128
}
@@ -0,0 +1,316 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.utils.atMostOne
import org.jetbrains.kotlin.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
fun IrType.getInlinedClassNative(): IrClass? = IrTypeInlineClassesSupport.getInlinedClass(this)
fun IrType.isInlinedNative(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isInlined(): Boolean = IrTypeInlineClassesSupport.isInlined(this)
fun IrClass.isNativePrimitiveType() = IrTypeInlineClassesSupport.isTopLevelClass(this) &&
KonanPrimitiveType.byFqNameParts[packageFqName]?.get(name) != null
fun KotlinType.getInlinedClass(): ClassDescriptor? = KotlinTypeInlineClassesSupport.getInlinedClass(this)
fun ClassDescriptor.isInlined(): Boolean = KotlinTypeInlineClassesSupport.isInlined(this)
fun KotlinType.binaryRepresentationIsNullable() = KotlinTypeInlineClassesSupport.representationIsNullable(this)
internal inline fun <R> KotlinType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: ClassDescriptor, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: KotlinType) -> R
): R = KotlinTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
internal inline fun <R> IrType.unwrapToPrimitiveOrReference(
eachInlinedClass: (inlinedClass: IrClass, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: IrType) -> R
): R = IrTypeInlineClassesSupport.unwrapToPrimitiveOrReference(this, eachInlinedClass, ifPrimitive, ifReference)
// TODO: consider renaming to `isReference`.
fun KotlinType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun IrType.binaryTypeIsReference(): Boolean = this.computePrimitiveBinaryTypeOrNull() == null
fun KotlinType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun KotlinType.computeBinaryType(): BinaryType<ClassDescriptor> = KotlinTypeInlineClassesSupport.computeBinaryType(this)
fun IrType.computePrimitiveBinaryTypeOrNull(): PrimitiveBinaryType? =
this.computeBinaryType().primitiveBinaryTypeOrNull()
fun IrType.computeBinaryType(): BinaryType<IrClass> = IrTypeInlineClassesSupport.computeBinaryType(this)
fun IrClass.inlinedClassIsNullable(): Boolean = this.defaultType.makeNullable().getInlinedClassNative() == this // TODO: optimize
fun IrClass.isUsedAsBoxClass(): Boolean = IrTypeInlineClassesSupport.isUsedAsBoxClass(this)
/**
* Most "underlying" user-visible non-reference type.
* It is visible as inlined to compiler for simplicity.
*/
enum class KonanPrimitiveType(val classId: ClassId, val binaryType: BinaryType.Primitive) {
BOOLEAN(PrimitiveType.BOOLEAN, PrimitiveBinaryType.BOOLEAN),
CHAR(PrimitiveType.CHAR, PrimitiveBinaryType.SHORT),
BYTE(PrimitiveType.BYTE, PrimitiveBinaryType.BYTE),
SHORT(PrimitiveType.SHORT, PrimitiveBinaryType.SHORT),
INT(PrimitiveType.INT, PrimitiveBinaryType.INT),
LONG(PrimitiveType.LONG, PrimitiveBinaryType.LONG),
FLOAT(PrimitiveType.FLOAT, PrimitiveBinaryType.FLOAT),
DOUBLE(PrimitiveType.DOUBLE, PrimitiveBinaryType.DOUBLE),
NON_NULL_NATIVE_PTR(ClassId.topLevel(KonanFqNames.nonNullNativePtr.toSafe()), PrimitiveBinaryType.POINTER),
VECTOR128(ClassId.topLevel(KonanFqNames.Vector128), PrimitiveBinaryType.VECTOR128)
;
constructor(primitiveType: PrimitiveType, primitiveBinaryType: PrimitiveBinaryType)
: this(ClassId.topLevel(primitiveType.typeFqName), primitiveBinaryType)
constructor(classId: ClassId, primitiveBinaryType: PrimitiveBinaryType)
: this(classId, BinaryType.Primitive(primitiveBinaryType))
val fqName: FqNameUnsafe get() = this.classId.asSingleFqName().toUnsafe()
companion object {
val byFqNameParts = KonanPrimitiveType.values().groupingBy {
assert(!it.classId.isNestedClass)
it.classId.packageFqName
}.fold({ _, _ -> mutableMapOf<Name, KonanPrimitiveType>() },
{ _, accumulator, element ->
accumulator.also { it[element.classId.shortClassName] = element }
})
}
}
internal abstract class InlineClassesSupport<Class : Any, Type : Any> {
protected abstract fun isNullable(type: Type): Boolean
protected abstract fun makeNullable(type: Type): Type
protected abstract fun erase(type: Type): Class
protected abstract fun computeFullErasure(type: Type): Sequence<Class>
protected abstract fun hasInlineModifier(clazz: Class): Boolean
protected abstract fun getNativePointedSuperclass(clazz: Class): Class?
protected abstract fun getInlinedClassUnderlyingType(clazz: Class): Type
protected abstract fun getPackageFqName(clazz: Class): FqName?
protected abstract fun getName(clazz: Class): Name?
abstract fun isTopLevelClass(clazz: Class): Boolean
@JvmName("classIsInlined")
fun isInlined(clazz: Class): Boolean = getInlinedClass(clazz) != null
fun isInlined(type: Type): Boolean = getInlinedClass(type) != null
fun isUsedAsBoxClass(clazz: Class) = getInlinedClass(clazz) == clazz // To handle NativePointed subclasses.
fun getInlinedClass(type: Type): Class? =
getInlinedClass(erase(type), isNullable(type))
protected fun getKonanPrimitiveType(clazz: Class): KonanPrimitiveType? =
if (isTopLevelClass(clazz))
KonanPrimitiveType.byFqNameParts[getPackageFqName(clazz)]?.get(getName(clazz))
else null
protected fun isImplicitInlineClass(clazz: Class): Boolean =
isTopLevelClass(clazz) && (getKonanPrimitiveType(clazz) != null ||
getName(clazz) == KonanFqNames.nativePtr.shortName() && getPackageFqName(clazz) == KonanFqNames.internalPackageName ||
getName(clazz) == InteropFqNames.cPointer.shortName() && getPackageFqName(clazz) == InteropFqNames.cPointer.parent().toSafe())
private fun getInlinedClass(erased: Class, isNullable: Boolean): Class? {
val inlinedClass = getInlinedClass(erased) ?: return null
return if (!isNullable || representationIsNonNullReferenceOrPointer(inlinedClass)) {
inlinedClass
} else {
null
}
}
tailrec fun representationIsNonNullReferenceOrPointer(clazz: Class): Boolean {
val konanPrimitiveType = getKonanPrimitiveType(clazz)
if (konanPrimitiveType != null) {
return konanPrimitiveType == KonanPrimitiveType.NON_NULL_NATIVE_PTR
}
val inlinedClass = getInlinedClass(clazz) ?: return true
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
return if (isNullable(underlyingType)) {
false
} else {
representationIsNonNullReferenceOrPointer(erase(underlyingType))
}
}
@JvmName("classGetInlinedClass")
private fun getInlinedClass(clazz: Class): Class? =
if (hasInlineModifier(clazz) || isImplicitInlineClass(clazz)) {
clazz
} else {
getNativePointedSuperclass(clazz)
}
inline fun <R> unwrapToPrimitiveOrReference(
type: Type,
eachInlinedClass: (inlinedClass: Class, nullable: Boolean) -> Unit,
ifPrimitive: (primitiveType: KonanPrimitiveType, nullable: Boolean) -> R,
ifReference: (type: Type) -> R
): R {
var currentType: Type = type
while (true) {
val inlinedClass = getInlinedClass(currentType)
if (inlinedClass == null) {
return ifReference(currentType)
}
val nullable = isNullable(currentType)
getKonanPrimitiveType(inlinedClass)?.let { primitiveType ->
return ifPrimitive(primitiveType, nullable)
}
eachInlinedClass(inlinedClass, nullable)
val underlyingType = getInlinedClassUnderlyingType(inlinedClass)
currentType = if (nullable) makeNullable(underlyingType) else underlyingType
}
}
fun representationIsNullable(type: Type): Boolean {
unwrapToPrimitiveOrReference(
type,
eachInlinedClass = { _, nullable -> if (nullable) return true },
ifPrimitive = { _, nullable -> return nullable },
ifReference = { return isNullable(it) }
)
}
// TODO: optimize.
fun computeBinaryType(type: Type): BinaryType<Class> {
val erased = erase(type)
val inlinedClass = getInlinedClass(erased, isNullable(type)) ?: return createReferenceBinaryType(type)
getKonanPrimitiveType(inlinedClass)?.let {
return it.binaryType
}
val underlyingBinaryType = computeBinaryType(getInlinedClassUnderlyingType(inlinedClass))
return if (isNullable(type) && underlyingBinaryType is BinaryType.Reference) {
BinaryType.Reference(underlyingBinaryType.types, true)
} else {
underlyingBinaryType
}
}
private fun createReferenceBinaryType(type: Type): BinaryType.Reference<Class> =
BinaryType.Reference(computeFullErasure(type), true)
}
internal object KotlinTypeInlineClassesSupport : InlineClassesSupport<ClassDescriptor, KotlinType>() {
override fun isNullable(type: KotlinType): Boolean = type.isNullable()
override fun makeNullable(type: KotlinType): KotlinType = type.makeNullable()
override tailrec fun erase(type: KotlinType): ClassDescriptor {
val descriptor = type.constructor.declarationDescriptor
return if (descriptor is ClassDescriptor) {
descriptor
} else {
erase(type.constructor.supertypes.first())
}
}
override fun computeFullErasure(type: KotlinType): Sequence<ClassDescriptor> {
val classifier = type.constructor.declarationDescriptor
return if (classifier is ClassDescriptor) sequenceOf(classifier)
else type.constructor.supertypes.asSequence().flatMap { computeFullErasure(it) }
}
override fun hasInlineModifier(clazz: ClassDescriptor): Boolean = clazz.isInlineClass()
override fun getNativePointedSuperclass(clazz: ClassDescriptor): ClassDescriptor? = clazz.getAllSuperClassifiers()
.firstOrNull { it.fqNameUnsafe == InteropFqNames.nativePointed } as ClassDescriptor?
override fun getInlinedClassUnderlyingType(clazz: ClassDescriptor): KotlinType =
clazz.unsubstitutedPrimaryConstructor!!.valueParameters.single().type
override fun getPackageFqName(clazz: ClassDescriptor) =
clazz.findPackage().fqName
override fun getName(clazz: ClassDescriptor) =
clazz.name
override fun isTopLevelClass(clazz: ClassDescriptor): Boolean = clazz.containingDeclaration is PackageFragmentDescriptor
}
internal object IrTypeInlineClassesSupport : InlineClassesSupport<IrClass, IrType>() {
override fun isNullable(type: IrType): Boolean = type.isNullable()
override fun makeNullable(type: IrType): IrType = type.makeNullable()
override tailrec fun erase(type: IrType): IrClass {
val classifier = type.classifierOrFail
return when (classifier) {
is IrClassSymbol -> classifier.owner
is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
else -> error(classifier)
}
}
override fun computeFullErasure(type: IrType): Sequence<IrClass> = when (val classifier = type.classifierOrFail) {
is IrClassSymbol -> sequenceOf(classifier.owner)
is IrTypeParameterSymbol -> classifier.owner.superTypes.asSequence().flatMap { computeFullErasure(it) }
is IrScriptSymbol -> classifier.unexpectedSymbolKind<IrClassifierSymbol>()
}
override fun hasInlineModifier(clazz: IrClass): Boolean = clazz.isSingleFieldValueClass
override fun getNativePointedSuperclass(clazz: IrClass): IrClass? {
var superClass: IrClass? = clazz
while (superClass != null && InteropFqNames.nativePointed.toSafe() != superClass.fqNameWhenAvailable)
superClass = superClass.getSuperClassNotAny()
return superClass
}
override fun getInlinedClassUnderlyingType(clazz: IrClass): IrType =
clazz.constructors.firstOrNull { it.isPrimary }?.valueParameters?.single()?.type
?: clazz.declarations.filterIsInstance<IrProperty>().atMostOne { it.backingField?.takeUnless { it.isStatic } != null }?.backingField?.type
?: clazz.inlineClassRepresentation!!.underlyingType
override fun getPackageFqName(clazz: IrClass) =
clazz.packageFqName
override fun getName(clazz: IrClass): Name? =
clazz.name
override fun isTopLevelClass(clazz: IrClass): Boolean = clazz.isTopLevel
}
@@ -0,0 +1,155 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
object InteropFqNames {
const val cPointerName = "CPointer"
const val nativePointedName = "NativePointed"
const val objCObjectBaseName = "ObjCObjectBase"
const val objCOverrideInitName = "OverrideInit"
const val objCActionName = "ObjCAction"
const val objCOutletName = "ObjCOutlet"
const val objCMethodImpName = "ObjCMethodImp"
const val exportObjCClassName = "ExportObjCClass"
const val nativeHeapName = "nativeHeap"
const val cValueName = "CValue"
const val cValuesName = "CValues"
const val cValuesRefName = "CValuesRef"
const val cEnumName = "CEnum"
const val cStructVarName = "CStructVar"
const val cEnumVarName = "CEnumVar"
const val cPrimitiveVarName = "CPrimitiveVar"
const val cPointedName = "CPointed"
const val interopStubsName = "InteropStubs"
const val managedTypeName = "ManagedType"
const val memScopeName = "MemScope"
const val foreignObjCObjectName = "ForeignObjCObject"
const val cOpaqueName = "COpaque"
const val objCObjectName = "ObjCObject"
const val objCObjectBaseMetaName = "ObjCObjectBaseMeta"
const val objCClassName = "ObjCClass"
const val objCClassOfName = "ObjCClassOf"
const val objCProtocolName = "ObjCProtocol"
const val nativeMemUtilsName = "nativeMemUtils"
const val cPlusPlusClassName = "CPlusPlusClass"
const val skiaRefCntName = "SkiaRefCnt"
const val TypeName = "Type"
const val cstrPropertyName = "cstr"
const val wcstrPropertyName = "wcstr"
const val nativePointedRawPtrPropertyName = "rawPtr"
const val cPointerRawValuePropertyName = "rawValue"
const val getObjCClassFunName = "getObjCClass"
const val objCObjectSuperInitCheckFunName = "superInitCheck"
const val allocObjCObjectFunName = "allocObjCObject"
const val typeOfFunName = "typeOf"
const val objCObjectInitByFunName = "initBy"
const val objCObjectRawPtrFunName = "objcPtr"
const val interpretObjCPointerFunName = "interpretObjCPointer"
const val interpretObjCPointerOrNullFunName = "interpretObjCPointerOrNull"
const val interpretNullablePointedFunName = "interpretNullablePointed"
const val interpretCPointerFunName = "interpretCPointer"
const val nativePointedGetRawPointerFunName = "getRawPointer"
const val cPointerGetRawValueFunName = "getRawValue"
const val cValueWriteFunName = "write"
const val cValueReadFunName = "readValue"
const val allocTypeFunName = "alloc"
val packageName = FqName("kotlinx.cinterop")
val cPointer = packageName.child(cPointerName).toUnsafe()
val nativePointed = packageName.child(nativePointedName).toUnsafe()
val objCObjectBase = packageName.child(objCObjectBaseName)
val objCOverrideInit = objCObjectBase.child(objCOverrideInitName)
val objCAction = packageName.child(objCActionName)
val objCOutlet = packageName.child(objCOutletName)
val objCMethodImp = packageName.child(objCMethodImpName)
val exportObjCClass = packageName.child(exportObjCClassName)
val cValue = packageName.child(cValueName)
val cValues = packageName.child(cValuesName)
val cValuesRef = packageName.child(cValuesRefName)
val cEnum = packageName.child(cEnumName)
val cStructVar = packageName.child(cStructVarName)
val cPointed = packageName.child(cPointedName)
val interopStubs = packageName.child(interopStubsName)
val managedType = packageName.child(managedTypeName)
}
private fun FqName.child(nameIdent: String) = child(Name.identifier(nameIdent))
internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
private val packageScope = builtIns.builtInsModule.getPackage(InteropFqNames.packageName).memberScope
internal fun getContributedVariables(name: String) = packageScope.getContributedVariables(name)
internal fun getContributedFunctions(name: String) = packageScope.getContributedFunctions(name)
internal fun getContributedClass(name: String) = packageScope.getContributedClass(name)
}
private fun MemberScope.getContributedVariables(name: String) =
this.getContributedVariables(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
internal fun MemberScope.getContributedClass(name: String): ClassDescriptor =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) as ClassDescriptor
private fun MemberScope.getContributedFunctions(name: String) =
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
internal val cKeywords = setOf(
// Actual C keywords.
"auto", "break", "case",
"char", "const", "continue",
"default", "do", "double",
"else", "enum", "extern",
"float", "for", "goto",
"if", "int", "long",
"register", "return",
"short", "signed", "sizeof", "static", "struct", "switch",
"typedef", "union", "unsigned",
"void", "volatile", "while",
// C99-specific.
"_Bool", "_Complex", "_Imaginary", "inline", "restrict",
// C11-specific.
"_Alignas", "_Alignof", "_Atomic", "_Generic", "_Noreturn", "_Static_assert", "_Thread_local",
// Not exactly keywords, but reserved or standard-defined.
"and", "not", "or", "xor",
"bool", "complex", "imaginary",
// C++ keywords not listed above.
"alignas", "alignof", "and_eq", "asm",
"bitand", "bitor", "bool",
"catch", "char16_t", "char32_t", "class", "compl", "constexpr", "const_cast",
"decltype", "delete", "dynamic_cast",
"explicit", "export",
"false", "friend",
"inline",
"mutable",
"namespace", "new", "noexcept", "not_eq", "nullptr",
"operator", "or_eq",
"private", "protected", "public",
"reinterpret_cast",
"static_assert",
"template", "this", "thread_local", "throw", "true", "try", "typeid", "typename",
"using",
"virtual",
"wchar_t",
"xor_eq"
)
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NativeRuntimeNames
internal const val NATIVE_PTR_NAME = "NativePtr"
internal const val NON_NULL_NATIVE_PTR_NAME = "NonNullNativePtr"
internal const val IMMUTABLE_BLOB_OF = "immutableBlobOf"
object KonanFqNames {
val function = FqName("kotlin.Function")
val kFunction = FqName("kotlin.reflect.KFunction")
val packageName = FqName("kotlin.native")
val internalPackageName = FqName("kotlin.native.internal")
val nativePtr = internalPackageName.child(Name.identifier(NATIVE_PTR_NAME)).toUnsafe()
val nonNullNativePtr = internalPackageName.child(Name.identifier(NON_NULL_NATIVE_PTR_NAME)).toUnsafe()
val Vector128 = FqName("kotlinx.cinterop.Vector128")
val throws = FqName("kotlin.Throws")
val cancellationException = FqName("kotlin.coroutines.cancellation.CancellationException")
val threadLocal = FqName("kotlin.native.concurrent.ThreadLocal")
val sharedImmutable = FqName("kotlin.native.concurrent.SharedImmutable")
val volatile = FqName("kotlin.concurrent.Volatile")
val frozen = FqName("kotlin.native.internal.Frozen")
val frozenLegacyMM = FqName("kotlin.native.internal.FrozenLegacyMM")
val leakDetectorCandidate = FqName("kotlin.native.internal.LeakDetectorCandidate")
val canBePrecreated = FqName("kotlin.native.internal.CanBePrecreated")
val typedIntrinsic = FqName("kotlin.native.internal.TypedIntrinsic")
val constantConstructorIntrinsic = FqName("kotlin.native.internal.ConstantConstructorIntrinsic")
val objCMethod = FqName("kotlinx.cinterop.ObjCMethod")
val hasFinalizer = FqName("kotlin.native.internal.HasFinalizer")
val hasFreezeHook = FqName("kotlin.native.internal.HasFreezeHook")
val gcUnsafeCall = NativeRuntimeNames.Annotations.gcUnsafeCallClassId.asSingleFqName()
val eagerInitialization = FqName("kotlin.native.EagerInitialization")
val noReorderFields = FqName("kotlin.native.internal.NoReorderFields")
val objCName = FqName("kotlin.native.ObjCName")
val hidesFromObjC = FqName("kotlin.native.HidesFromObjC")
val refinesInSwift = FqName("kotlin.native.RefinesInSwift")
val shouldRefineInSwift = FqName("kotlin.native.ShouldRefineInSwift")
val reflectionPackageName = FqName("kotlin.native.internal.ReflectionPackageName")
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2022 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.backend.konan
enum class ObjCExportSuspendFunctionLaunchThreadRestriction {
/**
* In this mode, suspend functions called from ObjC/Swift may only be called from the main thread
*/
MAIN,
/**
* In this mode, suspend functions called from ObjC/Swift may be called from any thread
*/
NONE,
;
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 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.backend.konan
enum class UnitSuspendFunctionObjCExport {
/**
* In this mode suspend functions and methods with [Unit] return type are exported to Objective-C with an additional argument,
* continuation callback, with the following Objective-C signature: `(^)(KtKotlinUnit * _Nullable, NSError * _Nullable)`.
*/
LEGACY,
/**
* In this mode suspend functions and methods with [Unit] return type are exported to Objective-C with an additional argument,
* continuation callback, with the following Objective-C signature: `(^)(NSError * _Nullable)`.
*
* Methods overriding superclass methods or implementing interface requirements narrowing down a more general return type
* to [Unit] are exported like in [LEGACY] mode.
*
* Note that in Swift 5.5 and higher suspend functions exported this way are transparently mapped to
* async functions with `Void` return type
*/
PROPER,
;
companion object {
val DEFAULT = PROPER
}
}
@@ -0,0 +1,124 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
/**
* Implementation of given method.
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(allowAbstract: Boolean = false): T {
if (this.kind.isReal) {
return this
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
// TODO: is it correct to take first?
@Suppress("UNCHECKED_CAST")
return filtered.first { allowAbstract || it.modality != Modality.ABSTRACT } as T
}
}
internal val ClassDescriptor.isArray: Boolean
get() = this.fqNameSafe.asString() in arrayTypes
internal val ClassDescriptor.isInterface: Boolean
get() = (this.kind == ClassKind.INTERFACE)
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
internal fun ClassDescriptor.isNothing() = this.defaultType.isNothing()
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
get() {
val result = mutableListOf<T>()
fun traverse(descriptor: T) {
result.add(descriptor)
@Suppress("UNCHECKED_CAST")
descriptor.overriddenDescriptors.forEach { traverse(it as T) }
}
traverse(this)
return result
}
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
get () = unsubstitutedMemberScope.contributedMethods
internal val MemberScope.contributedMethods: List<FunctionDescriptor>
get () {
val contributedDescriptors = this.getContributedDescriptors()
val functions = contributedDescriptors.filterIsInstance<FunctionDescriptor>()
val properties = contributedDescriptors.filterIsInstance<PropertyDescriptor>()
val getters = properties.mapNotNull { it.getter }
val setters = properties.mapNotNull { it.setter }
return functions + getters + setters
}
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
internal val FunctionDescriptor.target: FunctionDescriptor
get() = (if (modality == Modality.ABSTRACT) this else resolveFakeOverride()).original
internal fun DeclarationDescriptor.findPackageView(): PackageViewDescriptor {
val packageFragment = this.findPackage()
return packageFragment.module.getPackage(packageFragment.fqName)
}
internal fun DeclarationDescriptor.allContainingDeclarations(): List<DeclarationDescriptor> {
var list = mutableListOf<DeclarationDescriptor>()
var current = this.containingDeclaration
while (current != null) {
list.add(current)
current = current.containingDeclaration
}
return list
}
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
val result = mutableSetOf<FqName>()
val packageFragmentProvider = (module as? ModuleDescriptorImpl)?.packageFragmentProviderForModuleContentWithoutDependencies
fun getSubPackages(fqName: FqName) {
result.add(fqName)
val subPackages = packageFragmentProvider?.getSubPackagesOf(fqName) { true }
?: module.getSubPackagesOf(fqName) { true }
subPackages.forEach { getSubPackages(it) }
}
getSubPackages(FqName.ROOT)
return result
}
fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescriptor> =
getPackagesFqNames(this).flatMap {
getPackage(it).fragments.filter { it.module == this }.toSet()
}
val ClassDescriptor.enumEntries: List<ClassDescriptor>
get() {
assert(this.kind == ClassKind.ENUM_CLASS)
return this.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<ClassDescriptor>()
.filter { it.kind == ClassKind.ENUM_ENTRY }
}
internal val DeclarationDescriptor.isExpectMember: Boolean
get() = this is MemberDescriptor && this.isExpect
@@ -0,0 +1,149 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
import org.jetbrains.kotlin.builtins.getFunctionTypeKind
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
internal fun ClassDescriptor.isMappedFunctionClass() =
this.getFunctionTypeKind() == FunctionTypeKind.Function &&
// Type parameters include return type.
declaredTypeParameters.size - 1 < CustomTypeMappers.functionTypeMappersArityLimit
internal interface CustomTypeMapper {
val mappedClassId: ClassId
fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType
}
internal object CustomTypeMappers {
/**
* Custom type mappers.
*
* Don't forget to update [hiddenTypes] after adding new one.
*/
private val predefined: Map<ClassId, CustomTypeMapper> = with(StandardNames.FqNames) {
val result = mutableListOf<CustomTypeMapper>()
result += Collection(list, "NSArray")
result += Collection(mutableList, "NSMutableArray")
result += Collection(set, "NSSet")
result += Collection(mutableSet, { namer.mutableSetName.objCName })
result += Collection(map, "NSDictionary")
result += Collection(mutableMap, { namer.mutableMapName.objCName })
NSNumberKind.values().forEach {
// TODO: NSNumber seem to have different equality semantics.
val classId = it.mappedKotlinClassId
if (classId != null) {
result += Simple(classId, { namer.numberBoxName(classId).objCName })
}
}
result += Simple(ClassId.topLevel(string.toSafe()), "NSString")
result.associateBy { it.mappedClassId }
}
internal val functionTypeMappersArityLimit = 33 // not including, i.e. [0..33)
fun hasMapper(descriptor: ClassDescriptor): Boolean {
// Should be equivalent to `getMapper(descriptor) != null`.
if (descriptor.classId in predefined) return true
if (descriptor.isMappedFunctionClass()) return true
return false
}
fun getMapper(descriptor: ClassDescriptor): CustomTypeMapper? {
val classId = descriptor.classId
predefined[classId]?.let { return it }
if (descriptor.isMappedFunctionClass()) {
// TODO: somewhat hacky, consider using FunctionClassDescriptor.arity later.
val arity = descriptor.declaredTypeParameters.size - 1 // Type parameters include return type.
assert(classId == StandardNames.getFunctionClassId(arity))
return Function(arity)
}
return null
}
/**
* Types to be "hidden" during mapping, i.e. represented as `id`.
*
* Currently contains super types of classes handled by custom type mappers.
* Note: can be generated programmatically, but requires stdlib in this case.
*/
val hiddenTypes: Set<ClassId> = listOf(
"kotlin.Any",
"kotlin.CharSequence",
"kotlin.Comparable",
"kotlin.Function",
"kotlin.Number",
"kotlin.collections.Collection",
"kotlin.collections.Iterable",
"kotlin.collections.MutableCollection",
"kotlin.collections.MutableIterable"
).map { ClassId.topLevel(FqName(it)) }.toSet()
private class Simple(
override val mappedClassId: ClassId,
private val getObjCClassName: ObjCExportTranslatorImpl.() -> String
) : CustomTypeMapper {
constructor(
mappedClassId: ClassId,
objCClassName: String
) : this(mappedClassId, { objCClassName })
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType =
ObjCClassType(translator.getObjCClassName())
}
private class Collection(
mappedClassFqName: FqName,
private val getObjCClassName: ObjCExportTranslatorImpl.() -> String
) : CustomTypeMapper {
constructor(
mappedClassFqName: FqName,
objCClassName: String
) : this(mappedClassFqName, { objCClassName })
override val mappedClassId = ClassId.topLevel(mappedClassFqName)
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
val typeArguments = mappedSuperType.arguments.map {
val argument = it.type
if (TypeUtils.isNullableType(argument)) {
// Kotlin `null` keys and values are represented as `NSNull` singleton.
ObjCIdType
} else {
translator.mapReferenceTypeIgnoringNullability(argument, objCExportScope)
}
}
return ObjCClassType(translator.getObjCClassName(), typeArguments)
}
}
private class Function(private val parameterCount: Int) : CustomTypeMapper {
override val mappedClassId: ClassId
get() = StandardNames.getFunctionClassId(parameterCount)
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
return translator.mapFunctionTypeIgnoringNullability(mappedSuperType, objCExportScope, returnsVoid = false)
}
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2022 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.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isSubpackageOf
/**
* Tries to infer a main package name that can be used
* for bundle ID of a framework.
*/
internal class MainPackageGuesser {
fun guess(
moduleDescriptor: ModuleDescriptor,
includedLibraryDescriptors: List<ModuleDescriptor>,
exportedDependencies: List<ModuleDescriptor>,
): FqName {
// Consider exported libraries only if we cannot infer the package from sources or included libs.
return guessMainPackage(includedLibraryDescriptors + moduleDescriptor)
?: guessMainPackage(exportedDependencies)
?: FqName.ROOT
}
private fun guessMainPackage(modules: List<ModuleDescriptor>): FqName? {
if (modules.isEmpty()) {
return null
}
val allPackages = modules.flatMap {
it.getPackageFragments() // Includes also all parent packages, e.g. the root one.
}
val nonEmptyPackages = allPackages
.filter { it.getMemberScope().getContributedDescriptors().isNotEmpty() }
.map { it.fqName }.distinct()
return allPackages.map { it.fqName }.distinct()
.filter { candidate -> nonEmptyPackages.all { it.isSubpackageOf(candidate) } }
// Now there are all common ancestors of non-empty packages. Longest of them is the least common accessor:
.maxByOrNull { it.asString().length }
}
}
@@ -0,0 +1,122 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.util.allParameters
internal sealed class TypeBridge
internal object ReferenceBridge : TypeBridge()
internal data class BlockPointerBridge(
val numberOfParameters: Int,
val returnsVoid: Boolean
) : TypeBridge()
internal data class ValueTypeBridge(val objCValueType: ObjCValueType) : TypeBridge()
internal sealed class MethodBridgeParameter
internal sealed class MethodBridgeReceiver : MethodBridgeParameter() {
object Static : MethodBridgeReceiver()
object Factory : MethodBridgeReceiver()
object Instance : MethodBridgeReceiver()
}
internal object MethodBridgeSelector : MethodBridgeParameter()
internal sealed class MethodBridgeValueParameter : MethodBridgeParameter() {
data class Mapped(val bridge: TypeBridge) : MethodBridgeValueParameter()
object ErrorOutParameter : MethodBridgeValueParameter()
data class SuspendCompletion(val useUnitCompletion: Boolean) : MethodBridgeValueParameter()
}
internal data class MethodBridge(
val returnBridge: ReturnValue,
val receiver: MethodBridgeReceiver,
val valueParameters: List<MethodBridgeValueParameter>
) {
sealed class ReturnValue {
object Void : ReturnValue()
object HashCode : ReturnValue()
data class Mapped(val bridge: TypeBridge) : ReturnValue()
sealed class Instance : ReturnValue() {
object InitResult : Instance()
object FactoryResult : Instance()
}
sealed class WithError : ReturnValue() {
object Success : WithError()
data class ZeroForError(val successBridge: ReturnValue, val successMayBeZero: Boolean) : WithError()
}
object Suspend : ReturnValue()
}
val paramBridges: List<MethodBridgeParameter> =
listOf(receiver) + MethodBridgeSelector + valueParameters
// TODO: it is not exactly true in potential future cases.
val isInstance: Boolean get() = when (receiver) {
MethodBridgeReceiver.Static,
MethodBridgeReceiver.Factory -> false
MethodBridgeReceiver.Instance -> true
}
val returnsError: Boolean
get() = returnBridge is ReturnValue.WithError
}
internal fun MethodBridge.valueParametersAssociated(
descriptor: FunctionDescriptor
): List<Pair<MethodBridgeValueParameter, ParameterDescriptor?>> {
val kotlinParameters = descriptor.allParameters.iterator()
val skipFirstKotlinParameter = when (this.receiver) {
MethodBridgeReceiver.Static -> false
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
}
if (skipFirstKotlinParameter) {
kotlinParameters.next()
}
return this.valueParameters.map {
when (it) {
is MethodBridgeValueParameter.Mapped -> it to kotlinParameters.next()
is MethodBridgeValueParameter.SuspendCompletion,
is MethodBridgeValueParameter.ErrorOutParameter -> it to null
}
}.also { assert(!kotlinParameters.hasNext()) }
}
internal fun MethodBridge.parametersAssociated(
irFunction: IrFunction
): List<Pair<MethodBridgeParameter, IrValueParameter?>> {
val kotlinParameters = irFunction.allParameters.iterator()
return this.paramBridges.map {
when (it) {
is MethodBridgeValueParameter.Mapped,
MethodBridgeReceiver.Instance,
is MethodBridgeValueParameter.SuspendCompletion ->
it to kotlinParameters.next()
MethodBridgeReceiver.Static, MethodBridgeSelector, MethodBridgeValueParameter.ErrorOutParameter ->
it to null
MethodBridgeReceiver.Factory -> {
kotlinParameters.next()
it to null
}
}
}.also { assert(!kotlinParameters.hasNext()) }
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2022 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.backend.konan.objcexport
/**
* Creates a module map for a framework.
*
* Reference: https://clang.llvm.org/docs/Modules.html
*/
class ModuleMapBuilder {
fun build(frameworkName: String, moduleDependencies: Set<String>): String = buildString {
appendLine("framework module $frameworkName {")
appendLine(" umbrella header \"$frameworkName.h\"")
appendLine()
appendLine(" export *")
appendLine(" module * { export * }")
appendLine()
moduleDependencies.forEach {
appendLine(" use $it")
}
appendLine("}")
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageUtil
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.source.getPsi
internal class ObjCExportHeaderGeneratorImpl(
val context: PhaseContext,
moduleDescriptors: List<ModuleDescriptor>,
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
problemCollector: ObjCExportProblemCollector,
objcGenerics: Boolean
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, problemCollector) {
override val shouldExportKDoc = context.shouldExportKDoc()
internal class ProblemCollector(val context: PhaseContext) : ObjCExportProblemCollector {
override fun reportWarning(text: String) {
context.reportCompilationWarning(text)
}
override fun reportWarning(declaration: DeclarationDescriptor, text: String) {
val psi = (declaration as? DeclarationDescriptorWithSource)?.source?.getPsi()
?: return reportWarning(
"$text\n (at ${DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declaration)})"
)
val location = MessageUtil.psiElementToMessageLocation(psi)
context.messageCollector.report(CompilerMessageSeverity.WARNING, text, location)
}
override fun reportException(throwable: Throwable) {
throw throwable
}
}
override fun getAdditionalImports(): List<String> =
context.config.configuration.getNotNull(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS)
}
@@ -0,0 +1,480 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierTypeOrDefault
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.resolve.DescriptorResolver
import org.jetbrains.kotlin.resolve.TypeResolver
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope
import org.jetbrains.kotlin.resolve.scopes.LocalRedeclarationChecker
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.types.error.ErrorUtils
interface ObjCExportLazy {
interface Configuration {
val frameworkName: String
fun isIncluded(moduleInfo: ModuleInfo): Boolean
fun getCompilerModuleName(moduleInfo: ModuleInfo): String
val objcGenerics: Boolean
val disableSwiftMemberNameMangling: Boolean
get() = false
val unitSuspendFunctionExport: UnitSuspendFunctionObjCExport
val ignoreInterfaceMethodCollisions: Boolean
get() = false
}
fun generateBase(): List<ObjCTopLevel<*>>
fun translate(file: KtFile): List<ObjCTopLevel<*>>
}
@JvmOverloads
fun createObjCExportLazy(
configuration: ObjCExportLazy.Configuration,
problemCollector: ObjCExportProblemCollector,
codeAnalyzer: KotlinCodeAnalyzer,
typeResolver: TypeResolver,
descriptorResolver: DescriptorResolver,
fileScopeProvider: FileScopeProvider,
builtIns: KotlinBuiltIns,
deprecationResolver: DeprecationResolver? = null
): ObjCExportLazy = ObjCExportLazyImpl(
configuration,
problemCollector,
codeAnalyzer,
typeResolver,
descriptorResolver,
fileScopeProvider,
builtIns,
deprecationResolver
)
internal class ObjCExportLazyImpl(
private val configuration: ObjCExportLazy.Configuration,
problemCollector: ObjCExportProblemCollector,
private val codeAnalyzer: KotlinCodeAnalyzer,
private val typeResolver: TypeResolver,
private val descriptorResolver: DescriptorResolver,
private val fileScopeProvider: FileScopeProvider,
builtIns: KotlinBuiltIns,
deprecationResolver: DeprecationResolver?
) : ObjCExportLazy {
private val namerConfiguration = createNamerConfiguration(configuration)
private val nameTranslator: ObjCExportNameTranslator = ObjCExportNameTranslatorImpl(namerConfiguration)
private val mapper = ObjCExportMapper(deprecationResolver, local = true, configuration.unitSuspendFunctionExport)
private val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, problemCollector, local = true)
private val translator: ObjCExportTranslator = ObjCExportTranslatorImpl(
null,
mapper,
namer,
problemCollector,
configuration.objcGenerics
)
private val isValid: Boolean
get() = codeAnalyzer.moduleDescriptor.isValid
override fun generateBase() = translator.generateBaseDeclarations()
override fun translate(file: KtFile): List<ObjCTopLevel<*>> =
translateClasses(file) + translateTopLevels(file)
private fun translateClasses(container: KtDeclarationContainer): List<ObjCClass<*>> {
val result = mutableListOf<ObjCClass<*>>()
container.declarations.forEach { declaration ->
// Supposed to be true if ObjCExportMapper.shouldBeVisible is true.
if (declaration is KtClassOrObject && declaration.isPublic && declaration !is KtEnumEntry
&& !declaration.hasExpectModifier()) {
if (!declaration.isAnnotation() && !declaration.hasModifier(KtTokens.INLINE_KEYWORD)) {
result += translateClass(declaration)
}
declaration.body?.let {
result += translateClasses(it)
}
}
}
return result
}
private fun translateClass(ktClassOrObject: KtClassOrObject): ObjCClass<*> {
val name = nameTranslator.getClassOrProtocolName(ktClassOrObject)
// Note: some attributes may be missing (e.g. "unavailable" for unexposed classes).
return if (ktClassOrObject.isInterface) {
LazyObjCProtocolImpl(name, ktClassOrObject, this)
} else {
val isFinal = ktClassOrObject.modalityModifier() == null ||
ktClassOrObject.hasModifier(KtTokens.FINAL_KEYWORD)
val attributes = if (isFinal) {
listOf(OBJC_SUBCLASSING_RESTRICTED)
} else {
emptyList()
}
LazyObjCInterfaceImpl(name,
attributes,
generics = translateGenerics(ktClassOrObject),
psi = ktClassOrObject,
lazy = this)
}
}
private fun translateGenerics(ktClassOrObject: KtClassOrObject): List<ObjCGenericTypeDeclaration> = if (configuration.objcGenerics) {
ktClassOrObject.typeParametersWithOuter
.map {
ObjCGenericTypeRawDeclaration(
nameTranslator.getTypeParameterName(it),
ObjCVariance.fromKotlinVariance(it.variance)
)
}
.toList()
} else {
emptyList()
}
private fun translateTopLevels(file: KtFile): List<ObjCInterface> {
val extensions =
mutableMapOf<ClassDescriptor, MutableList<KtCallableDeclaration>>()
val topLevel = mutableListOf<KtCallableDeclaration>()
file.children.filterIsInstance<KtCallableDeclaration>().forEach {
// Supposed to be similar to ObjCExportMapper.shouldBeVisible.
if ((it is KtFunction || it is KtProperty) && it.isPublic && !it.hasExpectModifier()) {
val classDescriptor = getClassIfExtension(it)
if (classDescriptor != null) {
// If a class is hidden from Objective-C API then it is meaningless
// to export its extensions.
if (!classDescriptor.isHiddenFromObjC()) {
extensions.getOrPut(classDescriptor, { mutableListOf() }) += it
}
} else {
topLevel += it
}
}
}
val result = mutableListOf<ObjCInterface>()
extensions.mapTo(result) { (classDescriptor, declarations) ->
translateExtensions(file, classDescriptor, declarations)
}
if (topLevel.isNotEmpty()) result += translateFileClass(file, topLevel)
return result
}
private fun translateFileClass(file: KtFile, declarations: List<KtCallableDeclaration>): ObjCInterface {
val name = nameTranslator.getFileClassName(file)
return LazyObjCFileInterface(name, file, declarations, this)
}
private fun translateExtensions(
file: KtFile,
classDescriptor: ClassDescriptor,
declarations: List<KtCallableDeclaration>
): ObjCInterface {
// TODO: consider using file-based categories in compiler too.
val name = if (ErrorUtils.isError(classDescriptor)) {
ObjCExportNamer.ClassOrProtocolName("ERROR", "ERROR")
} else {
namer.getClassOrProtocolName(classDescriptor)
}
return LazyObjCExtensionInterface(name, nameTranslator.getCategoryName(file), classDescriptor, declarations, this)
}
private fun resolveDeclaration(ktDeclaration: KtDeclaration): DeclarationDescriptor =
codeAnalyzer.resolveToDescriptor(ktDeclaration)
private fun resolve(ktClassOrObject: KtClassOrObject) =
resolveDeclaration(ktClassOrObject) as ClassDescriptor
private fun resolve(ktCallableDeclaration: KtCallableDeclaration) =
resolveDeclaration(ktCallableDeclaration) as CallableMemberDescriptor
private fun getClassIfExtension(topLevelDeclaration: KtCallableDeclaration): ClassDescriptor? {
val receiverType = topLevelDeclaration.receiverTypeReference ?: return null
val fileScope = fileScopeProvider.getFileResolutionScope(topLevelDeclaration.containingKtFile)
val trace = BindingTraceContext() // TODO: revise.
val kotlinReceiverType = typeResolver.resolveType(
createHeaderScope(topLevelDeclaration, fileScope, trace),
receiverType,
trace,
checkBounds = false
)
return translator.getClassIfExtension(kotlinReceiverType)
}
private fun createHeaderScope(
declaration: KtCallableDeclaration,
parent: LexicalScope,
trace: BindingTrace
): LexicalScope {
if (declaration.typeParameters.isEmpty()) return parent
val fakeName = Name.special("<fake>")
val sourceElement = SourceElement.NO_SOURCE
val descriptor: CallableMemberDescriptor
val scopeKind: LexicalScopeKind
when (declaration) {
is KtFunction -> {
descriptor = SimpleFunctionDescriptorImpl.create(
parent.ownerDescriptor,
Annotations.EMPTY,
fakeName,
CallableMemberDescriptor.Kind.DECLARATION,
sourceElement
)
scopeKind = LexicalScopeKind.FUNCTION_HEADER
}
is KtProperty -> {
descriptor = PropertyDescriptorImpl.create(
parent.ownerDescriptor,
Annotations.EMPTY,
Modality.FINAL,
DescriptorVisibilities.PUBLIC,
declaration.isVar,
fakeName,
CallableMemberDescriptor.Kind.DECLARATION,
sourceElement,
false,
false,
false,
false,
false,
false
)
scopeKind = LexicalScopeKind.PROPERTY_HEADER
}
else -> TODO("${declaration::class}")
}
val result = LexicalWritableScope(
parent,
descriptor,
false,
LocalRedeclarationChecker.DO_NOTHING,
scopeKind
)
val typeParameters = descriptorResolver.resolveTypeParametersForDescriptor(
descriptor,
result,
result,
declaration.typeParameters,
trace
)
descriptorResolver.resolveGenericBounds(declaration, descriptor, result, typeParameters, trace)
return result
}
private class LazyObjCProtocolImpl(
name: ObjCExportNamer.ClassOrProtocolName,
override val psi: KtClassOrObject,
private val lazy: ObjCExportLazyImpl
) : LazyObjCProtocol(name) {
override val descriptor: ClassDescriptor by lazy { lazy.resolve(psi) }
override val isValid: Boolean
get() = lazy.isValid
override fun computeRealStub(): ObjCProtocol = lazy.translator.translateInterface(descriptor)
}
private class LazyObjCInterfaceImpl(
name: ObjCExportNamer.ClassOrProtocolName,
attributes: List<String>,
generics: List<ObjCGenericTypeDeclaration>,
override val psi: KtClassOrObject,
private val lazy: ObjCExportLazyImpl
) : LazyObjCInterface(name = name, generics = generics, categoryName = null, attributes = attributes) {
override val descriptor: ClassDescriptor by lazy { lazy.resolve(psi) }
override val isValid: Boolean
get() = lazy.isValid
override fun computeRealStub(): ObjCInterface = lazy.translator.translateClass(descriptor)
}
private class LazyObjCFileInterface(
name: ObjCExportNamer.ClassOrProtocolName,
private val file: KtFile,
private val declarations: List<KtCallableDeclaration>,
private val lazy: ObjCExportLazyImpl
) : LazyObjCInterface(name = name, generics = emptyList(), categoryName = null, attributes = listOf(OBJC_SUBCLASSING_RESTRICTED)) {
override val descriptor: ClassDescriptor?
get() = null
override val isValid: Boolean
get() = lazy.isValid
override val psi: PsiElement?
get() = null
override fun computeRealStub(): ObjCInterface = lazy.translator.translateFile(
PsiSourceFile(file),
declarations.mapNotNull { declaration ->
lazy.resolve(declaration).takeIf { descriptor ->
lazy.mapper.shouldBeExposed(descriptor)
}
}
)
}
private class LazyObjCExtensionInterface(
name: ObjCExportNamer.ClassOrProtocolName,
categoryName: String,
private val classDescriptor: ClassDescriptor,
private val declarations: List<KtCallableDeclaration>,
private val lazy: ObjCExportLazyImpl
) : LazyObjCInterface(name = name.objCName, generics = emptyList(), categoryName = categoryName, attributes = emptyList()) {
override val descriptor: ClassDescriptor?
get() = null
override val isValid: Boolean
get() = lazy.isValid
override val psi: PsiElement?
get() = null
override fun computeRealStub(): ObjCInterface = lazy.translator.translateExtensions(
classDescriptor,
declarations.mapNotNull { declaration ->
lazy.resolve(declaration).takeIf { lazy.mapper.shouldBeExposed(it) }
}
)
}
}
private abstract class LazyObjCInterface : ObjCInterface {
constructor(
name: ObjCExportNamer.ClassOrProtocolName,
generics: List<ObjCGenericTypeDeclaration>,
categoryName: String?,
attributes: List<String>
) : super(name.objCName, generics, categoryName, attributes + name.toNameAttributes())
constructor(
name: String,
generics: List<ObjCGenericTypeDeclaration>,
categoryName: String,
attributes: List<String>
) : super(name, generics, categoryName, attributes)
protected abstract fun computeRealStub(): ObjCInterface
private val realStub by lazy { computeRealStub() }
override val members: List<Stub<*>>
get() = realStub.members
override val superProtocols: List<String>
get() = realStub.superProtocols
override val superClass: String?
get() = realStub.superClass
override val superClassGenerics: List<ObjCNonNullReferenceType>
get() = realStub.superClassGenerics
}
private abstract class LazyObjCProtocol(
name: ObjCExportNamer.ClassOrProtocolName
) : ObjCProtocol(name.objCName, name.toNameAttributes()) {
protected abstract fun computeRealStub(): ObjCProtocol
private val realStub by lazy { computeRealStub() }
override val members: List<Stub<*>>
get() = realStub.members
override val superProtocols: List<String>
get() = realStub.superProtocols
}
internal fun createNamerConfiguration(configuration: ObjCExportLazy.Configuration): ObjCExportNamer.Configuration {
return object : ObjCExportNamer.Configuration {
override val topLevelNamePrefix = abbreviate(configuration.frameworkName)
override fun getAdditionalPrefix(module: ModuleDescriptor): String? {
if (module.isNativeStdlib() || module.isCommonStdlibCheckSpecificallyForIDE()) return "Kotlin"
// Note: incorrect for compiler since it doesn't store ModuleInfo to ModuleDescriptor.
val moduleInfo = module.getCapability(ModuleInfo.Capability) ?: return null
if (configuration.isIncluded(moduleInfo)) return null
return abbreviate(configuration.getCompilerModuleName(moduleInfo))
}
override val objcGenerics = configuration.objcGenerics
override val disableSwiftMemberNameMangling = configuration.disableSwiftMemberNameMangling
override val ignoreInterfaceMethodCollisions: Boolean = configuration.ignoreInterfaceMethodCollisions
}
}
private val kotlinSequenceClassId = ClassId.topLevel(FqName("kotlin.sequences.Sequence"))
// This is a special workaround needed for resolve in the IDE.
private fun ModuleDescriptor.isCommonStdlibCheckSpecificallyForIDE() =
findClassAcrossModuleDependencies(kotlinSequenceClassId)?.module == this
private val KtModifierListOwner.isPublic: Boolean
get() = this.visibilityModifierTypeOrDefault() == KtTokens.PUBLIC_KEYWORD
internal val KtPureClassOrObject.isInterface: Boolean
get() = this is KtClass && this.isInterface()
internal val KtClassOrObject.typeParametersWithOuter
get() = generateSequence(this, { if (it is KtClass && it.isInner()) it.containingClassOrObject else null })
.flatMap { it.typeParameters.asSequence() }
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.psi.KtFile
internal fun ObjCExportLazy.dumpObjCHeader(files: Collection<KtFile>, outputFile: String, shouldExportKDoc: Boolean) {
val lines = (this.generateBase() + files.flatMap { this.translate(it) })
.flatMap { StubRenderer.render(it, shouldExportKDoc) + listOf("") }
File(outputFile).writeLines(lines)
}
@@ -0,0 +1,469 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenDescriptors
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.objcinterop.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.DataClassResolver
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal class ObjCExportMapper(
internal val deprecationResolver: DeprecationResolver? = null,
private val local: Boolean = false,
internal val unitSuspendFunctionExport: UnitSuspendFunctionObjCExport
) {
fun getCustomTypeMapper(descriptor: ClassDescriptor): CustomTypeMapper? = CustomTypeMappers.getMapper(descriptor)
val hiddenTypes: Set<ClassId> get() = CustomTypeMappers.hiddenTypes
fun isSpecialMapped(descriptor: ClassDescriptor): Boolean {
// TODO: this method duplicates some of the [ObjCExportTranslatorImpl.mapReferenceType] logic.
return KotlinBuiltIns.isAny(descriptor) ||
descriptor.getAllSuperClassifiers().any { it is ClassDescriptor && CustomTypeMappers.hasMapper(it) }
}
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
fun bridgeMethod(descriptor: FunctionDescriptor): MethodBridge = if (local) {
bridgeMethodImpl(descriptor)
} else {
methodBridgeCache.getOrPut(descriptor) {
bridgeMethodImpl(descriptor)
}
}
}
internal fun ObjCExportMapper.getClassIfCategory(descriptor: CallableMemberDescriptor): ClassDescriptor? {
if (descriptor.dispatchReceiverParameter != null) return null
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null
return getClassIfCategory(extensionReceiverType)
}
internal fun ObjCExportMapper.getClassIfCategory(extensionReceiverType: KotlinType): ClassDescriptor? {
// FIXME: this code must rely on type mapping instead of copying its logic.
if (extensionReceiverType.isObjCObjectType()) return null
val erasedClass = extensionReceiverType.getErasedTypeClass()
return if (!erasedClass.isInterface && !erasedClass.isInlined() && !this.isSpecialMapped(erasedClass)) {
erasedClass
} else {
// E.g. receiver is protocol, or some type with custom mapping.
null
}
}
private fun isSealedClassConstructor(descriptor: ConstructorDescriptor) = descriptor.constructedClass.isSealed()
/**
* Check that given [method] is a synthetic .componentN() method of a data class.
*/
private fun isComponentNMethod(method: CallableMemberDescriptor): Boolean {
if ((method as? FunctionDescriptor)?.isOperator != true) return false
val parent = method.containingDeclaration
if (parent is ClassDescriptor && parent.isData && DataClassResolver.isComponentLike(method.name)) {
// componentN method of data class.
return true
}
return false
}
// Note: partially duplicated in ObjCExportLazyImpl.translateTopLevels.
internal fun ObjCExportMapper.shouldBeExposed(descriptor: CallableMemberDescriptor): Boolean = when {
!descriptor.isEffectivelyPublicApi -> false
descriptor.isExpect -> false
isHiddenByDeprecation(descriptor) -> false
descriptor is ConstructorDescriptor && isSealedClassConstructor(descriptor) -> false
// KT-42641. Don't expose componentN methods of data classes
// because they are useless in Objective-C/Swift.
isComponentNMethod(descriptor) && descriptor.overriddenDescriptors.isEmpty() -> false
descriptor.isHiddenFromObjC() -> false
else -> true
}
private fun CallableMemberDescriptor.isHiddenFromObjC(): Boolean = when {
// Note: the front-end checker requires all overridden descriptors to be either refined or not refined.
overriddenDescriptors.isNotEmpty() -> overriddenDescriptors.first().isHiddenFromObjC()
else -> annotations.any { annotation ->
annotation.annotationClass?.annotations?.any { it.fqName == KonanFqNames.hidesFromObjC } == true
}
}
/**
* Check if the given class or its enclosing declaration is marked as @HiddenFromObjC.
*/
internal fun ClassDescriptor.isHiddenFromObjC(): Boolean = when {
(this.containingDeclaration as? ClassDescriptor)?.isHiddenFromObjC() == true -> true
else -> annotations.any { annotation ->
annotation.annotationClass?.annotations?.any { it.fqName == KonanFqNames.hidesFromObjC } == true
}
}
internal fun ObjCExportMapper.shouldBeExposed(descriptor: ClassDescriptor): Boolean =
shouldBeVisible(descriptor) && !isSpecialMapped(descriptor) && !descriptor.defaultType.isObjCObjectType()
private fun ObjCExportMapper.isHiddenByDeprecation(descriptor: CallableMemberDescriptor): Boolean {
// Note: ObjCExport generally expect overrides of exposed methods to be exposed.
// So don't hide a "deprecated hidden" method which overrides non-hidden one:
if (deprecationResolver != null && deprecationResolver.isDeprecatedHidden(descriptor) &&
descriptor.overriddenDescriptors.all { isHiddenByDeprecation(it) }) {
return true
}
// Note: ObjCExport expects members of unexposed classes to be unexposed too.
// So hide a declaration if it is from a hidden class:
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && isHiddenByDeprecation(containingDeclaration)) {
return true
}
return false
}
internal fun ObjCExportMapper.getDeprecation(descriptor: DeclarationDescriptor): DeprecationInfo? {
deprecationResolver?.getDeprecations(descriptor).orEmpty().maxByOrNull {
when (it.deprecationLevel) {
DeprecationLevelValue.WARNING -> 1
DeprecationLevelValue.ERROR -> 2
DeprecationLevelValue.HIDDEN -> 3
}
}?.let { return it }
(descriptor as? ConstructorDescriptor)?.let {
// Note: a deprecation can't be applied to a class itself when generating header
// since the class can be referred from the header.
// Apply class deprecations to its constructors instead:
return getDeprecation(it.constructedClass)
}
return null
}
private fun ObjCExportMapper.isHiddenByDeprecation(descriptor: ClassDescriptor): Boolean {
if (deprecationResolver == null) return false
if (deprecationResolver.isDeprecatedHidden(descriptor)) return true
// Note: ObjCExport requires super class of exposed class to be exposed.
// So hide a class if its super class is hidden:
val superClass = descriptor.getSuperClassNotAny()
if (superClass != null && isHiddenByDeprecation(superClass)) {
return true
}
// Note: ObjCExport requires enclosing class of exposed class to be exposed.
// Also in Kotlin hidden class members (including other classes) aren't directly accessible.
// So hide a class if its enclosing class is hidden:
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && isHiddenByDeprecation(containingDeclaration)) {
return true
}
return false
}
// Note: the logic is partially duplicated in ObjCExportLazyImpl.translateClasses.
internal fun ObjCExportMapper.shouldBeVisible(descriptor: ClassDescriptor): Boolean =
descriptor.isEffectivelyPublicApi && when (descriptor.kind) {
ClassKind.CLASS, ClassKind.INTERFACE, ClassKind.ENUM_CLASS, ClassKind.OBJECT -> true
ClassKind.ENUM_ENTRY, ClassKind.ANNOTATION_CLASS -> false
} && !descriptor.isExpect && !descriptor.isInlined() && !isHiddenByDeprecation(descriptor) && !descriptor.isHiddenFromObjC()
private fun ObjCExportMapper.isBase(descriptor: CallableMemberDescriptor): Boolean =
descriptor.overriddenDescriptors.all { !shouldBeExposed(it) }
// e.g. it is not `override`, or overrides only unexposed methods.
/**
* Check that given [descriptor] is a so-called "base method", i.e. method
* that doesn't override anything in a generated Objective-C interface.
* Note that it does not mean that it has no "override" keyword.
* Consider example:
* ```kotlin
* private interface I {
* fun f()
* }
*
* class C : I {
* override fun f() {}
* }
* ```
* Interface `I` is not exposed to the generated header, so C#f is considered to be a base method even though it has an "override" keyword.
*/
internal fun ObjCExportMapper.isBaseMethod(descriptor: FunctionDescriptor) =
this.isBase(descriptor)
internal fun ObjCExportMapper.getBaseMethods(descriptor: FunctionDescriptor): List<FunctionDescriptor> =
if (isBaseMethod(descriptor)) {
listOf(descriptor)
} else {
descriptor.overriddenDescriptors.filter { shouldBeExposed(it) }
.flatMap { getBaseMethods(it.original)}
.distinct()
}
internal fun ObjCExportMapper.isBaseProperty(descriptor: PropertyDescriptor) =
isBase(descriptor)
internal fun ObjCExportMapper.getBaseProperties(descriptor: PropertyDescriptor): List<PropertyDescriptor> =
if (isBaseProperty(descriptor)) {
listOf(descriptor)
} else {
descriptor.overriddenDescriptors
.flatMap { getBaseProperties(it.original) }
.distinct()
}
@Suppress("NO_TAIL_CALLS_FOUND", "NON_TAIL_RECURSIVE_CALL") // K2 warning suppression, TODO: KT-62472
internal tailrec fun KotlinType.getErasedTypeClass(): ClassDescriptor =
TypeUtils.getClassDescriptor(this) ?: this.constructor.supertypes.first().getErasedTypeClass()
internal fun ObjCExportMapper.isTopLevel(descriptor: CallableMemberDescriptor): Boolean =
descriptor.containingDeclaration !is ClassDescriptor && this.getClassIfCategory(descriptor) == null
internal fun ObjCExportMapper.isObjCProperty(property: PropertyDescriptor): Boolean =
property.extensionReceiverParameter == null || getClassIfCategory(property) != null
internal fun ClassDescriptor.getEnumValuesFunctionDescriptor(): SimpleFunctionDescriptor? {
require(this.kind == ClassKind.ENUM_CLASS)
return this.staticScope.getContributedFunctions(
StandardNames.ENUM_VALUES,
NoLookupLocation.FROM_BACKEND
).singleOrNull { it.extensionReceiverParameter == null && it.valueParameters.size == 0 }
}
internal fun ClassDescriptor.getEnumEntriesPropertyDescriptor(): PropertyDescriptor? {
require(this.kind == ClassKind.ENUM_CLASS)
return this.staticScope.getContributedVariables(
StandardNames.ENUM_ENTRIES,
NoLookupLocation.FROM_BACKEND
).singleOrNull { it.extensionReceiverParameter == null }
}
internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = method.allOverriddenDescriptors.any {
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanFqNames.throws)
}
private fun ObjCExportMapper.bridgeType(
kotlinType: KotlinType
): TypeBridge = kotlinType.unwrapToPrimitiveOrReference<TypeBridge>(
eachInlinedClass = { inlinedClass, _ ->
when (inlinedClass.classId) {
UnsignedType.UBYTE.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_CHAR)
UnsignedType.USHORT.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_SHORT)
UnsignedType.UINT.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_INT)
UnsignedType.ULONG.classId -> return ValueTypeBridge(ObjCValueType.UNSIGNED_LONG_LONG)
}
},
ifPrimitive = { primitiveType, _ ->
val objCValueType = when (primitiveType) {
KonanPrimitiveType.BOOLEAN -> ObjCValueType.BOOL
KonanPrimitiveType.CHAR -> ObjCValueType.UNICHAR
KonanPrimitiveType.BYTE -> ObjCValueType.CHAR
KonanPrimitiveType.SHORT -> ObjCValueType.SHORT
KonanPrimitiveType.INT -> ObjCValueType.INT
KonanPrimitiveType.LONG -> ObjCValueType.LONG_LONG
KonanPrimitiveType.FLOAT -> ObjCValueType.FLOAT
KonanPrimitiveType.DOUBLE -> ObjCValueType.DOUBLE
KonanPrimitiveType.NON_NULL_NATIVE_PTR -> ObjCValueType.POINTER
KonanPrimitiveType.VECTOR128 -> TODO()
}
ValueTypeBridge(objCValueType)
},
ifReference = {
if (kotlinType.isFunctionType) {
bridgeFunctionType(kotlinType)
} else {
ReferenceBridge
}
}
)
private fun ObjCExportMapper.bridgeFunctionType(kotlinType: KotlinType): TypeBridge {
// kotlinType.arguments include return type: <P1, P2, ..., Pn, R>
val numberOfParameters = kotlinType.arguments.size - 1
val returnType = kotlinType.getReturnTypeFromFunctionType()
val returnsVoid = returnType.isUnit() || returnType.isNothing()
// Note: this is correct because overriding method can't turn this into false
// neither for a parameter nor for a return type.
return BlockPointerBridge(numberOfParameters, returnsVoid)
}
private fun ObjCExportMapper.bridgeParameter(parameter: ParameterDescriptor): MethodBridgeValueParameter =
MethodBridgeValueParameter.Mapped(bridgeType(parameter.type))
private fun ObjCExportMapper.bridgeReturnType(
descriptor: FunctionDescriptor,
convertExceptionsToErrors: Boolean
): MethodBridge.ReturnValue {
val returnType = descriptor.returnType!!
return when {
descriptor.isSuspend -> MethodBridge.ReturnValue.Suspend
descriptor is ConstructorDescriptor -> if (descriptor.constructedClass.isArray) {
MethodBridge.ReturnValue.Instance.FactoryResult
} else {
MethodBridge.ReturnValue.Instance.InitResult
}.let {
if (convertExceptionsToErrors) {
MethodBridge.ReturnValue.WithError.ZeroForError(it, successMayBeZero = false)
} else {
it
}
}
descriptor.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) } &&
descriptor.name.asString() == "hashCode" -> {
assert(!convertExceptionsToErrors)
MethodBridge.ReturnValue.HashCode
}
descriptor is PropertyGetterDescriptor -> {
assert(!convertExceptionsToErrors)
MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
}
returnType.isUnit() || returnType.isNothing() -> if (convertExceptionsToErrors) {
MethodBridge.ReturnValue.WithError.Success
} else {
MethodBridge.ReturnValue.Void
}
else -> {
val returnTypeBridge = bridgeType(returnType)
val successReturnValueBridge = MethodBridge.ReturnValue.Mapped(returnTypeBridge)
if (convertExceptionsToErrors) {
val canReturnZero = !returnTypeBridge.isReferenceOrPointer() || TypeUtils.isNullableType(returnType)
MethodBridge.ReturnValue.WithError.ZeroForError(
successReturnValueBridge,
successMayBeZero = canReturnZero
)
} else {
successReturnValueBridge
}
}
}
}
private fun TypeBridge.isReferenceOrPointer(): Boolean = when (this) {
ReferenceBridge, is BlockPointerBridge -> true
is ValueTypeBridge -> this.objCValueType == ObjCValueType.POINTER
}
private fun ObjCExportMapper.bridgeMethodImpl(descriptor: FunctionDescriptor): MethodBridge {
assert(isBaseMethod(descriptor))
val convertExceptionsToErrors = this.doesThrow(descriptor)
val kotlinParameters = descriptor.allParameters.iterator()
val isTopLevel = isTopLevel(descriptor)
val receiver = if (descriptor is ConstructorDescriptor && descriptor.constructedClass.isArray) {
kotlinParameters.next()
MethodBridgeReceiver.Factory
} else if (isTopLevel) {
MethodBridgeReceiver.Static
} else {
kotlinParameters.next()
MethodBridgeReceiver.Instance
}
val valueParameters = mutableListOf<MethodBridgeValueParameter>()
kotlinParameters.forEach {
valueParameters += bridgeParameter(it)
}
val returnBridge = bridgeReturnType(descriptor, convertExceptionsToErrors)
if (descriptor.isSuspend) {
val useUnitCompletion = (unitSuspendFunctionExport == UnitSuspendFunctionObjCExport.PROPER) && (descriptor.returnType!!.isUnit())
valueParameters += MethodBridgeValueParameter.SuspendCompletion(useUnitCompletion)
} else if (convertExceptionsToErrors) {
// Add error out parameter before tail block parameters. The convention allows this.
// Placing it after would trigger https://bugs.swift.org/browse/SR-12201
// (see also https://github.com/JetBrains/kotlin-native/issues/3825).
val tailBlocksCount = valueParameters.reversed().takeWhile { it.isBlockPointer() }.count()
valueParameters.add(valueParameters.size - tailBlocksCount, MethodBridgeValueParameter.ErrorOutParameter)
}
return MethodBridge(returnBridge, receiver, valueParameters)
}
private fun MethodBridgeValueParameter.isBlockPointer(): Boolean = when (this) {
is MethodBridgeValueParameter.Mapped -> when (this.bridge) {
ReferenceBridge, is ValueTypeBridge -> false
is BlockPointerBridge -> true
}
MethodBridgeValueParameter.ErrorOutParameter -> false
is MethodBridgeValueParameter.SuspendCompletion -> true
}
internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor): TypeBridge {
assert(isBaseProperty(descriptor))
return bridgeType(descriptor.type)
}
internal enum class NSNumberKind(val mappedKotlinClassId: ClassId?, val objCType: ObjCType) {
CHAR(PrimitiveType.BYTE, ObjCPrimitiveType.char),
UNSIGNED_CHAR(UnsignedType.UBYTE, ObjCPrimitiveType.unsigned_char),
SHORT(PrimitiveType.SHORT, ObjCPrimitiveType.short),
UNSIGNED_SHORT(UnsignedType.USHORT, ObjCPrimitiveType.unsigned_short),
INT(PrimitiveType.INT, ObjCPrimitiveType.int),
UNSIGNED_INT(UnsignedType.UINT, ObjCPrimitiveType.unsigned_int),
LONG(ObjCPrimitiveType.long),
UNSIGNED_LONG(ObjCPrimitiveType.unsigned_long),
LONG_LONG(PrimitiveType.LONG, ObjCPrimitiveType.long_long),
UNSIGNED_LONG_LONG(UnsignedType.ULONG, ObjCPrimitiveType.unsigned_long_long),
FLOAT(PrimitiveType.FLOAT, ObjCPrimitiveType.float),
DOUBLE(PrimitiveType.DOUBLE, ObjCPrimitiveType.double),
BOOL(PrimitiveType.BOOLEAN, ObjCPrimitiveType.BOOL),
INTEGER(ObjCPrimitiveType.NSInteger),
UNSIGNED_INTEGER(ObjCPrimitiveType.NSUInteger)
;
// UNSIGNED_SHORT -> unsignedShort
private val kindName = this.name.split('_')
.joinToString("") { it.lowercase().replaceFirstChar(Char::uppercaseChar) }.replaceFirstChar(Char::lowercaseChar)
val valueSelector = kindName // unsignedShort
val initSelector = "initWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // initWithUnsignedShort:
val factorySelector = "numberWith${kindName.replaceFirstChar(Char::uppercaseChar)}:" // numberWithUnsignedShort:
constructor(
primitiveType: PrimitiveType,
objCPrimitiveType: ObjCPrimitiveType
) : this(ClassId.topLevel(primitiveType.typeFqName), objCPrimitiveType)
constructor(
unsignedType: UnsignedType,
objCPrimitiveType: ObjCPrimitiveType
) : this(unsignedType.classId, objCPrimitiveType)
constructor(objCPrimitiveType: ObjCPrimitiveType) : this(null, objCPrimitiveType)
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2022 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.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.KotlinType
interface ObjCExportScope {
class RecursionBreachException : Exception {
constructor(type: KotlinType) : super("$type was already encountered during type mapping process.")
}
val parent: ObjCExportScope?
get() = null
fun deriveForType(kotlinType: KotlinType): ObjCTypeExportScope = ObjCTypeExportScopeImpl(kotlinType, this)
fun deriveForClass(container: DeclarationDescriptor, namer: ObjCExportNamer): ObjCClassExportScope =
ObjCClassExportScopeImpl(container, namer, this)
}
internal inline fun <reified T : ObjCExportScope> ObjCExportScope.nearestScopeOfType(): T? {
var parent: ObjCExportScope? = this
while (parent != null) {
if (parent is T) {
return parent
}
parent = parent.parent
}
return null
}
internal object ObjCRootExportScope : ObjCExportScope
interface ObjCClassExportScope : ObjCExportScope {
fun getGenericTypeUsage(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeUsage?
}
private class ObjCClassExportScopeImpl constructor(
container: DeclarationDescriptor,
val namer: ObjCExportNamer,
override val parent: ObjCExportScope?,
) : ObjCClassExportScope {
private val typeParameterNames: List<TypeParameterDescriptor> =
if (container is ClassDescriptor && !container.isInterface) {
container.typeConstructor.parameters
} else {
emptyList<TypeParameterDescriptor>()
}
override fun getGenericTypeUsage(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeUsage? {
return typeParameterDescriptor?.let { descriptor ->
typeParameterNames.firstOrNull {
it == descriptor || it.isCapturedFromOuterDeclaration && it.original == descriptor
}?.let {
ObjCGenericTypeParameterUsage(it, namer)
}
}
}
}
interface ObjCTypeExportScope : ObjCExportScope {
val kotlinType: KotlinType
}
private class ObjCTypeExportScopeImpl(override val kotlinType: KotlinType, override val parent: ObjCExportScope?) : ObjCTypeExportScope {
init {
var parent = this.parent
while (parent != null && parent is ObjCTypeExportScope) {
if (parent.kotlinType == kotlinType)
throw ObjCExportScope.RecursionBreachException(kotlinType)
parent = parent.parent
}
}
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2021 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.backend.konan.objcexport
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
class ObjCExportTranslatorMobile internal constructor(private val delegate: ObjCExportTranslatorImpl) : ObjCExportTranslator by delegate {
companion object {
fun create(namer: ObjCExportNamer, configuration: ObjCExportLazy.Configuration): ObjCExportTranslatorMobile {
val mapper = ObjCExportMapper(local = true, unitSuspendFunctionExport = configuration.unitSuspendFunctionExport)
return ObjCExportTranslatorMobile(ObjCExportTranslatorImpl(null, mapper, namer, ObjCExportProblemCollector.SILENT, configuration.objcGenerics))
}
}
fun translateBaseFunction(descriptor: FunctionDescriptor): ObjCMethod {
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor
val scope = classDescriptor?.let { delegate.createGenericExportScope(it) } ?: ObjCRootExportScope
return delegate.buildMethod(descriptor, descriptor, scope)
}
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
data class ObjCExportedStubs(
val classForwardDeclarations: Set<ObjCClassForwardDeclaration>,
val protocolForwardDeclarations: Set<String>,
val stubs: List<Stub<*>>
)
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2022 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.backend.konan.objcexport
import org.jetbrains.kotlin.konan.file.File
// For now, the object is pretty dumb.
// Later it will accept an object with ObjC declarations instead of lines.
class ObjCHeaderWriter {
fun write(
headerName: String,
headerLines: List<String>,
headersDirectory: File,
) {
headersDirectory.child(headerName).writeLines(headerLines)
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.module
class ObjcExportHeaderGeneratorMobile internal constructor(
moduleDescriptors: List<ModuleDescriptor>,
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
problemCollector: ObjCExportProblemCollector,
objcGenerics: Boolean,
private val restrictToLocalModules: Boolean
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics, problemCollector) {
companion object {
fun createInstance(
configuration: ObjCExportLazy.Configuration,
problemCollector: ObjCExportProblemCollector,
builtIns: KotlinBuiltIns,
moduleDescriptors: List<ModuleDescriptor>,
deprecationResolver: DeprecationResolver? = null,
local: Boolean = false,
restrictToLocalModules: Boolean = false): ObjCExportHeaderGenerator {
val mapper = ObjCExportMapper(deprecationResolver, local, configuration.unitSuspendFunctionExport)
val namerConfiguration = createNamerConfiguration(configuration)
val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, problemCollector, local)
return ObjcExportHeaderGeneratorMobile(
moduleDescriptors,
mapper,
namer,
problemCollector,
configuration.objcGenerics,
restrictToLocalModules
)
}
}
override fun shouldTranslateExtraClass(descriptor: ClassDescriptor): Boolean =
!restrictToLocalModules || descriptor.module in moduleDescriptors
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
internal class StubBuilder<S : Stub<*>>(private val problemCollector: ObjCExportProblemCollector) {
private val children = mutableListOf<S>()
inline fun add(provider: () -> S) {
try {
children.add(provider())
} catch (t: Throwable) {
problemCollector.reportException(t)
}
}
operator fun plusAssign(set: Collection<S>) {
children += set
}
fun build(): List<S> = children
}
@@ -0,0 +1,252 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.backend.common.serialization.extractSerializedKdocString
import org.jetbrains.kotlin.backend.common.serialization.metadata.findKDocString
object StubRenderer {
fun render(stub: Stub<*>): List<String> = render(stub, false)
private fun findPositionToInsertGeneratedCommentLine(kDoc: List<String>, generatedCommentLine: String): Int {
val generatedWords = generatedCommentLine.trim().split(" ").map { it.trim() }
if (generatedWords.size >= 2 && generatedWords[0] == "@param") {
for (i in kDoc.indices.reversed()) {
val kDocLineWords = kDoc[i].trim().split(" ").map { it.trim() }.filter { it.isNotEmpty() }.filterNot { it == "*" }
if (kDocLineWords.size >= 2 && kDocLineWords[0] == generatedWords[0] && kDocLineWords[1] == generatedWords[1]) {
return i + 1 // position after last `@param` kDoc line, describing same parameter as in generatedCommentLine
}
}
}
return kDoc.size
}
internal fun render(stub: Stub<*>, shouldExportKDoc: Boolean): List<String> = collect {
stub.run {
val (kDocEnding, commentBlockEnding) = if (comment?.contentLines == null) {
Pair("*/", null) // Close kDoc with `*/`, and print nothing after empty comment
} else {
Pair("", "*/") // Don't terminate kDoc, though close comment block with `*/`
}
val kDoc = if (shouldExportKDoc) {
descriptor?.extractKDocString()?.let {
if (it.startsWith("/**") && it.endsWith("*/")) {
// Nested comment is allowed inside of preformatted ``` block in kdoc but not in ObjC
val kdocClean = "/**${it.substring(3, it.length - 2).replace("*/", "**").replace("/*", "**")}$kDocEnding"
kdocClean.lines().map { it.trim().let {
if (it.isNotEmpty() && it[0] == '*') " $it"
else it
}
}
} else null
}
} else null
val kDocAndComment = kDoc?.filterNot { it.isEmpty() }.orEmpty().toMutableList()
comment?.contentLines?.let { commentLine ->
if (!kDoc.isNullOrEmpty()) kDocAndComment.add(" *") // Separator between nonempty kDoc and nonempty comment
commentLine.forEach { kDocAndComment.add(findPositionToInsertGeneratedCommentLine(kDocAndComment, it), " * $it")}
}
if (kDocAndComment.isNotEmpty()) {
+"" // Probably makes the output more readable.
if (kDoc.isNullOrEmpty()) +"/**" // Start comment block, in case kDoc was empty
kDocAndComment.forEach {
+it
}
commentBlockEnding?.let { +it }
}
when (this) {
is ObjCProtocol -> {
attributes.forEach {
+renderAttribute(it)
}
+renderProtocolHeader()
+"@required"
renderMembers(this, shouldExportKDoc)
+"@end"
}
is ObjCInterface -> {
attributes.forEach {
+renderAttribute(it)
}
+renderInterfaceHeader()
renderMembers(this, shouldExportKDoc)
+"@end"
}
is ObjCMethod -> {
+renderMethod(this)
}
is ObjCProperty -> {
+renderProperty(this)
}
else -> throw IllegalArgumentException("unsupported stub: " + stub::class)
}
}
}
private fun renderProperty(property: ObjCProperty): String = buildString {
fun StringBuilder.appendTypeAndName() {
append(' ')
append(property.type.render(property.name))
}
fun ObjCProperty.getAllAttributes(): List<String> {
if (getterName == null && setterName == null) return propertyAttributes
val allAttributes = propertyAttributes.toMutableList()
getterName?.let { allAttributes += "getter=$it" }
setterName?.let { allAttributes += "setter=$it" }
return allAttributes
}
fun StringBuilder.appendAttributes() {
val attributes = property.getAllAttributes()
if (attributes.isNotEmpty()) {
append(' ')
attributes.joinTo(this, prefix = "(", postfix = ")")
}
}
append("@property")
appendAttributes()
appendTypeAndName()
appendPostfixDeclarationAttributes(property.declarationAttributes)
append(';')
}
private fun renderMethod(method: ObjCMethod): String = buildString {
fun appendStaticness() {
if (method.isInstanceMethod) {
append('-')
} else {
append('+')
}
}
fun appendReturnType() {
append(" (")
append(method.returnType.render())
append(')')
}
fun appendParameters() {
assert(method.selectors.size == method.parameters.size ||
method.selectors.size == 1 && method.parameters.size == 0)
if (method.selectors.size == 1 && method.parameters.size == 0) {
append(method.selectors[0])
} else {
for (i in 0 until method.selectors.size) {
if (i > 0) append(' ')
val parameter = method.parameters[i]
val selector = method.selectors[i]
append(selector)
append("(")
append(parameter.type.render())
append(")")
append(parameter.name)
}
}
}
fun appendAttributes() {
appendPostfixDeclarationAttributes(method.attributes)
}
appendStaticness()
appendReturnType()
appendParameters()
appendAttributes()
append(';')
}
private fun Appendable.appendPostfixDeclarationAttributes(attributes: List<kotlin.String>) {
if (attributes.isNotEmpty()) this.append(' ')
attributes.joinTo(this, separator = " ", transform = this@StubRenderer::renderAttribute)
}
private fun ObjCProtocol.renderProtocolHeader() = buildString {
append("@protocol ")
append(name)
appendSuperProtocols(this@renderProtocolHeader)
}
private fun StringBuilder.appendSuperProtocols(clazz: ObjCClass<ClassDescriptor>) {
val protocols = clazz.superProtocols
if (protocols.isNotEmpty()) {
protocols.joinTo(this, separator = ", ", prefix = " <", postfix = ">")
}
}
private fun ObjCInterface.renderInterfaceHeader() = buildString {
fun appendSuperClass() {
if (superClass != null) append(" : $superClass")
formatGenerics(this, superClassGenerics)
}
fun appendGenerics() {
formatGenerics(this, generics)
}
fun appendCategoryName() {
if (categoryName != null) {
append(" (")
append(categoryName)
append(')')
}
}
append("@interface ")
append(name)
appendGenerics()
appendCategoryName()
appendSuperClass()
appendSuperProtocols(this@renderInterfaceHeader)
}
private fun Collector.renderMembers(clazz: ObjCClass<*>, shouldExportKDoc: Boolean) {
clazz.members.forEach {
+render(it, shouldExportKDoc)
}
}
private fun renderAttribute(attribute: String) = "__attribute__(($attribute))"
private fun collect(p: Collector.() -> Unit): List<String> {
val collector = Collector()
collector.p()
return collector.build()
}
private class Collector {
private val collection: MutableList<String> = mutableListOf()
fun build(): List<String> = collection
operator fun String.unaryPlus() {
collection += this
}
operator fun List<String>.unaryPlus() {
collection += this
}
}
}
fun formatGenerics(buffer: Appendable, generics: List<Any>) {
if (generics.isNotEmpty()) {
generics.joinTo(buffer, separator = ", ", prefix = "<", postfix = ">")
}
}
private fun DeclarationDescriptor.extractKDocString(): String? {
return (this as? DeclarationDescriptorWithSource)?.findKDocString()
?: extractSerializedKdocString()
}
@@ -0,0 +1,218 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.llvm.LlvmParameterAttribute
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.types.Variance
sealed class ObjCType {
final override fun toString(): String = this.render()
abstract fun render(attrsAndName: String): String
fun render() = render("")
protected fun String.withAttrsAndName(attrsAndName: String) =
if (attrsAndName.isEmpty()) this else "$this ${attrsAndName.trimStart()}"
}
data class ObjCRawType(
val rawText: String
) : ObjCType() {
override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName)
}
sealed class ObjCReferenceType : ObjCType()
sealed class ObjCNonNullReferenceType : ObjCReferenceType()
data class ObjCNullableReferenceType(
val nonNullType: ObjCNonNullReferenceType,
val isNullableResult: Boolean = false
) : ObjCReferenceType() {
override fun render(attrsAndName: String): String {
val attribute = if (isNullableResult) objcNullableResultAttribute else objcNullableAttribute
return nonNullType.render(" $attribute".withAttrsAndName(attrsAndName))
}
}
data class ObjCClassType(
val className: String,
val typeArguments: List<ObjCNonNullReferenceType> = emptyList()
) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = buildString {
append(className)
if (typeArguments.isNotEmpty()) {
append("<")
typeArguments.joinTo(this) { it.render() }
append(">")
}
append(" *")
append(attrsAndName)
}
}
sealed class ObjCGenericTypeUsage: ObjCNonNullReferenceType() {
abstract val typeName: String
final override fun render(attrsAndName: String): String {
return typeName.withAttrsAndName(attrsAndName)
}
}
data class ObjCGenericTypeRawUsage(override val typeName: String) : ObjCGenericTypeUsage()
data class ObjCGenericTypeParameterUsage(
val typeParameterDescriptor: TypeParameterDescriptor,
val namer: ObjCExportNamer
) : ObjCGenericTypeUsage() {
override val typeName: String
get() = namer.getTypeParameterName(typeParameterDescriptor)
}
data class ObjCProtocolType(
val protocolName: String
) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName)
}
object ObjCIdType : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = "id".withAttrsAndName(attrsAndName)
}
object ObjCInstanceType : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String): String = "instancetype".withAttrsAndName(attrsAndName)
}
data class ObjCBlockPointerType(
val returnType: ObjCType,
val parameterTypes: List<ObjCReferenceType>
) : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String) = returnType.render(buildString {
append("(^")
append(attrsAndName)
append(")(")
if (parameterTypes.isEmpty()) append("void")
parameterTypes.joinTo(this) { it.render() }
append(')')
})
}
object ObjCMetaClassType : ObjCNonNullReferenceType() {
override fun render(attrsAndName: String): String = "Class".withAttrsAndName(attrsAndName)
}
sealed class ObjCPrimitiveType(
val cName: String
) : ObjCType() {
object NSUInteger : ObjCPrimitiveType("NSUInteger")
object BOOL : ObjCPrimitiveType("BOOL")
object unichar : ObjCPrimitiveType("unichar")
object int8_t : ObjCPrimitiveType("int8_t")
object int16_t : ObjCPrimitiveType("int16_t")
object int32_t : ObjCPrimitiveType("int32_t")
object int64_t : ObjCPrimitiveType("int64_t")
object uint8_t : ObjCPrimitiveType("uint8_t")
object uint16_t : ObjCPrimitiveType("uint16_t")
object uint32_t : ObjCPrimitiveType("uint32_t")
object uint64_t : ObjCPrimitiveType("uint64_t")
object float : ObjCPrimitiveType("float")
object double : ObjCPrimitiveType("double")
object NSInteger : ObjCPrimitiveType("NSInteger")
object char : ObjCPrimitiveType("char")
object unsigned_char: ObjCPrimitiveType("unsigned char")
object unsigned_short: ObjCPrimitiveType("unsigned short")
object int: ObjCPrimitiveType("int")
object unsigned_int: ObjCPrimitiveType("unsigned int")
object long: ObjCPrimitiveType("long")
object unsigned_long: ObjCPrimitiveType("unsigned long")
object long_long: ObjCPrimitiveType("long long")
object unsigned_long_long: ObjCPrimitiveType("unsigned long long")
object short: ObjCPrimitiveType("short")
override fun render(attrsAndName: String) = cName.withAttrsAndName(attrsAndName)
}
data class ObjCPointerType(
val pointee: ObjCType,
val nullable: Boolean = false
) : ObjCType() {
override fun render(attrsAndName: String) =
pointee.render("*${if (nullable) {
" $objcNullableAttribute".withAttrsAndName(attrsAndName)
} else {
attrsAndName
}}")
}
object ObjCVoidType : ObjCType() {
override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName)
}
internal enum class ObjCValueType(val encoding: String, val defaultParameterAttributes: List<LlvmParameterAttribute> = emptyList()) {
BOOL("c", listOf(LlvmParameterAttribute.SignExt)),
UNICHAR("S", listOf(LlvmParameterAttribute.ZeroExt)),
// TODO: Switch to explicit SIGNED_CHAR
CHAR("c", listOf(LlvmParameterAttribute.SignExt)),
SHORT("s", listOf(LlvmParameterAttribute.SignExt)),
INT("i"),
LONG_LONG("q"),
UNSIGNED_CHAR("C", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_SHORT("S", listOf(LlvmParameterAttribute.ZeroExt)),
UNSIGNED_INT("I"),
UNSIGNED_LONG_LONG("Q"),
FLOAT("f"),
DOUBLE("d"),
POINTER("^v")
}
enum class ObjCVariance(internal val declaration: String) {
INVARIANT(""),
COVARIANT("__covariant "),
CONTRAVARIANT("__contravariant ");
companion object {
fun fromKotlinVariance(variance: Variance): ObjCVariance = when (variance) {
Variance.OUT_VARIANCE -> COVARIANT
Variance.IN_VARIANCE -> CONTRAVARIANT
else -> INVARIANT
}
}
}
sealed class ObjCGenericTypeDeclaration {
abstract val typeName: String
abstract val variance: ObjCVariance
final override fun toString(): String = variance.declaration + typeName
}
data class ObjCGenericTypeRawDeclaration(
override val typeName: String,
override val variance: ObjCVariance = ObjCVariance.INVARIANT
) : ObjCGenericTypeDeclaration()
data class ObjCGenericTypeParameterDeclaration(
val typeParameterDescriptor: TypeParameterDescriptor,
val namer: ObjCExportNamer
) : ObjCGenericTypeDeclaration() {
override val typeName: String
get() = namer.getTypeParameterName(typeParameterDescriptor)
override val variance: ObjCVariance
get() = ObjCVariance.fromKotlinVariance(typeParameterDescriptor.variance)
}
internal fun ObjCType.makeNullableIfReferenceOrPointer(): ObjCType = when (this) {
is ObjCPointerType -> ObjCPointerType(this.pointee, nullable = true)
is ObjCNonNullReferenceType -> ObjCNullableReferenceType(this)
is ObjCNullableReferenceType, is ObjCRawType, is ObjCPrimitiveType, ObjCVoidType -> this
}
const val objcNullableAttribute = "_Nullable"
const val objcNullableResultAttribute = "_Nullable_result"
@@ -0,0 +1,106 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
class ObjCComment(val contentLines: List<String>) {
constructor(vararg contentLines: String) : this(contentLines.toList())
}
data class ObjCClassForwardDeclaration(
val className: String,
val typeDeclarations: List<ObjCGenericTypeDeclaration> = emptyList()
)
abstract class Stub<out D : DeclarationDescriptor>(val name: String, val comment: ObjCComment? = null) {
abstract val descriptor: D?
open val psi: PsiElement?
get() = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi
open val isValid: Boolean
get() = descriptor?.module?.isValid ?: true
}
abstract class ObjCTopLevel<out D : DeclarationDescriptor>(name: String, comment: ObjCComment? = null) : Stub<D>(name, comment)
abstract class ObjCClass<out D : DeclarationDescriptor>(name: String,
val attributes: List<String>,
comment: ObjCComment? = null) : ObjCTopLevel<D>(name, comment) {
abstract val superProtocols: List<String>
abstract val members: List<Stub<*>>
}
abstract class ObjCProtocol(name: String,
attributes: List<String>,
comment: ObjCComment? = null) : ObjCClass<ClassDescriptor>(name, attributes, comment)
class ObjCProtocolImpl(
name: String,
override val descriptor: ClassDescriptor,
override val superProtocols: List<String>,
override val members: List<Stub<*>>,
attributes: List<String> = emptyList(),
comment: ObjCComment? = null) : ObjCProtocol(name, attributes, comment)
abstract class ObjCInterface(name: String,
val generics: List<ObjCGenericTypeDeclaration>,
val categoryName: String?,
attributes: List<String>,
comment: ObjCComment? = null) : ObjCClass<ClassDescriptor>(name, attributes, comment) {
abstract val superClass: String?
abstract val superClassGenerics: List<ObjCNonNullReferenceType>
}
class ObjCInterfaceImpl(
name: String,
generics: List<ObjCGenericTypeDeclaration> = emptyList(),
override val descriptor: ClassDescriptor? = null,
override val superClass: String? = null,
override val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList(),
override val superProtocols: List<String> = emptyList(),
categoryName: String? = null,
override val members: List<Stub<*>> = emptyList(),
attributes: List<String> = emptyList(),
comment: ObjCComment? = null
) : ObjCInterface(name, generics, categoryName, attributes, comment)
class ObjCMethod(
override val descriptor: DeclarationDescriptor?,
val isInstanceMethod: Boolean,
val returnType: ObjCType,
val selectors: List<String>,
val parameters: List<ObjCParameter>,
val attributes: List<String>,
comment: ObjCComment? = null
) : Stub<DeclarationDescriptor>(buildMethodName(selectors, parameters), comment)
class ObjCParameter(name: String,
override val descriptor: ParameterDescriptor?,
val type: ObjCType) : Stub<ParameterDescriptor>(name)
class ObjCProperty(name: String,
override val descriptor: DeclarationDescriptorWithSource?,
val type: ObjCType,
val propertyAttributes: List<String>,
val setterName: String? = null,
val getterName: String? = null,
val declarationAttributes: List<String> = emptyList(),
comment: ObjCComment? = null) : Stub<DeclarationDescriptorWithSource>(name, comment) {
@Deprecated("", ReplaceWith("this.propertyAttributes"), DeprecationLevel.WARNING)
val attributes: List<String> get() = propertyAttributes
}
private fun buildMethodName(selectors: List<String>, parameters: List<ObjCParameter>): String =
if (selectors.size == 1 && parameters.size == 0) {
selectors[0]
} else {
assert(selectors.size == parameters.size)
selectors.joinToString(separator = "")
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.After
import java.io.File
import kotlin.test.fail
abstract class AbstractObjCExportHeaderGeneratorTest(
private val generator: ObjCExportHeaderGenerator
) {
fun interface ObjCExportHeaderGenerator {
fun generateHeaders(disposable: Disposable, root: File): String
}
private val testRootDisposable = Disposer.newDisposable()
protected val objCExportTestDataDir = testDataDir.resolve("objcexport")
@After
fun dispose() {
Disposer.dispose(testRootDisposable)
}
protected fun doTest(root: File) {
if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory")
val generatedHeaders = generator.generateHeaders(testRootDisposable, root)
KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), generatedHeaders)
}
}
abstract class AbstractFE10ObjCExportHeaderGeneratorTest : AbstractObjCExportHeaderGeneratorTest(Fe10ObjCExportHeaderGenerator)
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.backend.konan.KlibFactories
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys.Companion.KONAN_HOME
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportHeaderGeneratorImpl
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapper
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments
import org.jetbrains.kotlin.cli.common.createFlexiblePhaseConfig
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.testFramework.MockProjectEx
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.File
object Fe10ObjCExportHeaderGenerator : AbstractObjCExportHeaderGeneratorTest.ObjCExportHeaderGenerator {
override fun generateHeaders(disposable: Disposable, root: File): String {
val headerGenerator = createObjCExportHeaderGenerator(disposable, root)
headerGenerator.translateModuleDeclarations()
return headerGenerator.build().joinToString(System.lineSeparator())
}
private fun createObjCExportHeaderGenerator(disposable: Disposable, root: File): ObjCExportHeaderGenerator {
val mapper = ObjCExportMapper(
unitSuspendFunctionExport = UnitSuspendFunctionObjCExport.DEFAULT
)
val namer = ObjCExportNamerImpl(
mapper = mapper,
builtIns = DefaultBuiltIns.Instance,
local = false,
problemCollector = ObjCExportProblemCollector.SILENT,
configuration = object : ObjCExportNamer.Configuration {
override val topLevelNamePrefix: String get() = ""
override fun getAdditionalPrefix(module: ModuleDescriptor): String? = null
override val objcGenerics: Boolean = true
}
)
val environment: KotlinCoreEnvironment = createKotlinCoreEnvironment(disposable)
val phaseContext = BasicPhaseContext(
KonanConfig(environment.project, environment.configuration)
)
val kotlinFiles = root.walkTopDown().filter { it.isFile }.filter { it.extension == "kt" }.toList()
return ObjCExportHeaderGeneratorImpl(
context = phaseContext,
moduleDescriptors = listOf(createModuleDescriptor(environment, kotlinFiles)),
mapper = mapper,
namer = namer,
problemCollector = ObjCExportProblemCollector.SILENT,
objcGenerics = true
)
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.backend.konan.KlibFactories
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.arguments.K2NativeCompilerArguments
import org.jetbrains.kotlin.cli.common.createFlexiblePhaseConfig
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.library.resolveSingleFileKlib
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.File
fun createModuleDescriptor(
environment: KotlinCoreEnvironment,
tempDir: File,
kotlinSources: List<String>
): ModuleDescriptor {
val testModuleRoot = tempDir.resolve("testModule")
testModuleRoot.mkdirs()
val testSourcesFiles = kotlinSources.mapIndexed { index, kotlinSource ->
testModuleRoot.resolve("TestSources$index.kt").apply {
writeText(kotlinSource)
}
}
return createModuleDescriptor(environment, testSourcesFiles)
}
fun createModuleDescriptor(
environment: KotlinCoreEnvironment,
kotlinFiles: List<File>,
): ModuleDescriptor {
val psiFactory = KtPsiFactory(environment.project)
val kotlinPsiFiles = kotlinFiles.map { file -> psiFactory.createFile(file.name, KtTestUtil.doLoadFile(file)) }
val analysisResult = CommonResolverForModuleFactory.analyzeFiles(
files = kotlinPsiFiles,
moduleName = Name.special("<test_module>"),
dependOnBuiltIns = true,
languageVersionSettings = environment.configuration.languageVersionSettings,
targetPlatform = CommonPlatforms.defaultCommonPlatform,
targetEnvironment = CompilerEnvironment,
dependenciesContainer = DependenciesContainerImpl,
) { content ->
environment.createPackagePartProvider(content.moduleContentScope)
}
return analysisResult.moduleDescriptor
}
fun createKotlinCoreEnvironment(
disposable: Disposable, compilerConfiguration: CompilerConfiguration = createCompilerConfiguration()
): KotlinCoreEnvironment {
return KotlinCoreEnvironment.createForTests(
parentDisposable = disposable,
initialConfiguration = compilerConfiguration,
extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES
)
}
private fun createCompilerConfiguration(): CompilerConfiguration {
val configuration = KotlinTestUtils.newConfiguration()
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, createLanguageVersionSettings())
configuration.put(CLIConfigurationKeys.FLEXIBLE_PHASE_CONFIG, createFlexiblePhaseConfig(K2NativeCompilerArguments()))
configuration.put(KonanConfigKeys.KONAN_HOME, konanHomePath)
configuration.put(KonanConfigKeys.PRODUCE, CompilerOutputKind.FRAMEWORK)
configuration.put(KonanConfigKeys.AUTO_CACHEABLE_FROM, emptyList())
configuration.put(KonanConfigKeys.CACHE_DIRECTORIES, emptyList())
configuration.put(KonanConfigKeys.CACHED_LIBRARIES, emptyMap())
configuration.put(KonanConfigKeys.FRAMEWORK_IMPORT_HEADERS, emptyList())
configuration.put(KonanConfigKeys.EXPORT_KDOC, true)
return configuration
}
private fun createLanguageVersionSettings() = LanguageVersionSettingsImpl(
languageVersion = LanguageVersion.LATEST_STABLE,
apiVersion = ApiVersion.LATEST_STABLE
)
private object DependenciesContainerImpl : CommonDependenciesContainer {
override val moduleInfos: List<ModuleInfo> get() = listOf(DefaultBuiltInsModuleInfo, KotlinNativeStdlibModuleInfo)
override val friendModuleInfos: List<ModuleInfo> get() = emptyList()
override val refinesModuleInfos: List<ModuleInfo> get() = emptyList()
override fun registerDependencyForAllModules(moduleInfo: ModuleInfo, descriptorForModule: ModuleDescriptorImpl) = Unit
override fun packageFragmentProviderForModuleInfo(moduleInfo: ModuleInfo): PackageFragmentProvider? = null
private val stdlibModuleDescriptor = KlibFactories.DefaultDeserializedDescriptorFactory.createDescriptor(
library = resolveSingleFileKlib(org.jetbrains.kotlin.konan.file.File("$konanHomePath/klib/common/stdlib")),
languageVersionSettings = createLanguageVersionSettings(),
builtIns = DefaultBuiltIns.Instance,
storageManager = LockBasedStorageManager.NO_LOCKS,
packageAccessHandler = null
).also { it.setDependencies(it) }
override fun moduleDescriptorForModuleInfo(moduleInfo: ModuleInfo): ModuleDescriptor {
if (moduleInfo == DefaultBuiltInsModuleInfo) return DefaultBuiltIns.Instance.builtInsModule
if (moduleInfo == KotlinNativeStdlibModuleInfo) return stdlibModuleDescriptor
error("Unknown module info $moduleInfo")
}
}
private object DefaultBuiltInsModuleInfo : ModuleInfo {
override val name get() = DefaultBuiltIns.Instance.builtInsModule.name
override fun dependencies() = listOf(this)
override fun dependencyOnBuiltIns() = ModuleInfo.DependencyOnBuiltIns.LAST
override val platform get() = NativePlatforms.unspecifiedNativePlatform
override val analyzerServices get() = NativePlatformAnalyzerServices
}
private object KotlinNativeStdlibModuleInfo : ModuleInfo {
override val analyzerServices: PlatformDependentAnalyzerServices = NativePlatformAnalyzerServices
override val name: Name = Name.special("<stdlib>")
override val platform: TargetPlatform = NativePlatforms.unspecifiedNativePlatform
override fun dependencies(): List<ModuleInfo> = listOf(this)
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import com.intellij.openapi.Disposable
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import java.io.File
/**
* Test Environment that enables quick 'inline' creation of [ModuleDescriptor] instances.
*
* # Examples
* ## Test operating on a single Kotlin Source file:
*
* ```kotlin
* class MyTest : InlineSourceTestEnvironment {
* @Test
* fun `test - my unit`() {
* val moduleDescriptor = createModuleDescriptor("""
* package com.example
* class MyClassUnderTest
* """.trimIndent()
* )
*
* // my test!
* }
* }
* ```
*
* ## Test that requires several source files (e.g. multiple packages are involved)
* ```kotlin
* @Test
* fun `test - my unit`() {
* val moduleDescriptor = createModuleDescriptor {
* source("class Foo")
* source("""
* package com.example
* class Foo
* """)
* }
* }
* ```
*/
interface InlineSourceTestEnvironment {
val kotlinCoreEnvironment: KotlinCoreEnvironment
val testDisposable: Disposable
val testTempDir: File
}
interface InlineSourceCodeCollector {
fun source(@Language("kotlin") sourceCode: String)
}
fun InlineSourceTestEnvironment.createModuleDescriptor(@Language("kotlin") sourceCode: String): ModuleDescriptor =
createModuleDescriptor(kotlinCoreEnvironment, testTempDir, listOf(sourceCode))
fun InlineSourceTestEnvironment.createModuleDescriptor(build: InlineSourceCodeCollector.() -> Unit): ModuleDescriptor {
val sources = mutableListOf<String>()
object : InlineSourceCodeCollector {
override fun source(sourceCode: String) {
sources.add(sourceCode)
}
}.build()
return createModuleDescriptor(kotlinCoreEnvironment, testTempDir, sources.toList())
}
@@ -0,0 +1,52 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapper
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamer
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportProblemCollector
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
internal fun createObjCExportNamerConfiguration(
topLevelNamePrefix: String = "",
additionalPrefix: (moduleName: Name) -> String? = { null }
): ObjCExportNamer.Configuration {
return object : ObjCExportNamer.Configuration {
override val topLevelNamePrefix: String get() = topLevelNamePrefix
override fun getAdditionalPrefix(module: ModuleDescriptor): String? = additionalPrefix(module.name)
override val objcGenerics: Boolean = true
}
}
internal fun createObjCExportMapper(
deprecationResolver: DeprecationResolver? = null,
local: Boolean = false,
unitSuspendFunctionObjCExport: UnitSuspendFunctionObjCExport = UnitSuspendFunctionObjCExport.DEFAULT
): ObjCExportMapper {
return ObjCExportMapper(deprecationResolver, local, unitSuspendFunctionObjCExport)
}
internal fun createObjCExportNamer(
configuration: ObjCExportNamer.Configuration = createObjCExportNamerConfiguration(),
builtIns: KotlinBuiltIns = DefaultBuiltIns.Instance,
local: Boolean = false,
problemCollector: ObjCExportProblemCollector = ObjCExportProblemCollector.SILENT,
mapper: ObjCExportMapper = createObjCExportMapper(local = local),
): ObjCExportNamer {
return ObjCExportNamerImpl(
builtIns = builtIns,
mapper = mapper,
local = local,
problemCollector = problemCollector,
configuration = configuration
)
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
private const val konanHomePropertyKey = "org.jetbrains.kotlin.native.home"
val konanHomePath: String
get() = System.getProperty(konanHomePropertyKey) ?: error("Missing System property: '$konanHomePropertyKey'")
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2023 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.backend.konan.testUtils
import java.io.File
val projectDir = File("kotlin-native/backend.native")
val testDataDir = projectDir.resolve("functionalTest/testData")
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2023 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.backend.konan.tests
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportHeaderGenerator
import org.jetbrains.kotlin.backend.konan.testUtils.AbstractFE10ObjCExportHeaderGeneratorTest
import org.junit.jupiter.api.Test
/**
* ## Test Scope
* This test will cover the generation of 'objc' headers.
* The corresponding class in would be [ObjCExportHeaderGenerator].
*
* The input of the test are Kotlin source files;
* The output is the generated header files;
* The output will be compared to already checked in golden files.
*
* ## How to create a new test
* Every test has a corresponding 'root' directory.
* All directories are found in `backend.native/functionalTest/testData/objcexport`.
*
* 1) Create new root directory (e.g. myTest) in testData/objcexport
* 2) Place kotlin files into the directory e.g. testData/objcexport/myTest/Foo.kt
* 3) Create a test function and call ` doTest(objCExportTestDataDir.resolve("myTest"))`
* 4) The first invocation will fail the test, but generates the header that can be checked in (if sufficient)
*/
class Fe10ObjCExportHeaderGeneratorTest : AbstractFE10ObjCExportHeaderGeneratorTest() {
@Test
fun `test - simpleClass`() {
doTest(objCExportTestDataDir.resolve("simpleClass"))
}
@Test
fun `test - simpleInterface`() {
doTest(objCExportTestDataDir.resolve("simpleInterface"))
}
@Test
fun `test - simpleEnumClass`() {
doTest(objCExportTestDataDir.resolve("simpleEnumClass"))
}
@Test
fun `test - simpleObject`() {
doTest(objCExportTestDataDir.resolve("simpleObject"))
}
@Test
fun `test - topLevelFunction`() {
doTest(objCExportTestDataDir.resolve("topLevelFunction"))
}
@Test
fun `test - topLevelProperty`() {
doTest(objCExportTestDataDir.resolve("topLevelProperty"))
}
@Test
fun `test - sameClassNameInDifferentPackage`() {
doTest(objCExportTestDataDir.resolve("sameClassNameInDifferentPackage"))
}
@Test
fun `test - samePropertyAndFunctionName`() {
doTest(objCExportTestDataDir.resolve("samePropertyAndFunctionName"))
}
@Test
fun `test - classImplementingInterface`() {
doTest(objCExportTestDataDir.resolve("classImplementingInterface"))
}
@Test
fun `test - interfaceImplementingInterface`() {
doTest(objCExportTestDataDir.resolve("interfaceImplementingInterface"))
}
@Test
fun `test - classWithObjCNameAnnotation`() {
doTest(objCExportTestDataDir.resolve("classWithObjCNameAnnotation"))
}
@Test
fun `test - functionWithObjCNameAnnotation`() {
doTest(objCExportTestDataDir.resolve("functionWithObjCNameAnnotation"))
}
@Test
fun `test - classWithKDoc`() {
doTest(objCExportTestDataDir.resolve("classWithKDoc"))
}
@Test
fun `test - classWithHidesFromObjCAnnotation`() {
doTest(objCExportTestDataDir.resolve("classWithHidesFromObjCAnnotation"))
}
@Test
fun `test - functionWithThrowsAnnotation`() {
doTest(objCExportTestDataDir.resolve("functionWithThrowsAnnotation"))
}
@Test
fun `test - functionWithErrorType`() {
doTest(objCExportTestDataDir.resolve("functionWithErrorType"))
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2023 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.backend.konan.tests
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.backend.konan.testUtils.*
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.TypeAttributes
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
/**
* ## Test Scope
* This test shall cover test the [ObjCExportMapper];
* The test will invoke the type mapping a known type (e.g. List<Int>) and check
* the corresponding conversion to [ObjCNonNullReferenceType].
*
* The test also acts as quick entry point for debugging the [ObjCExportMapper]
*/
class ObjCExportMapperTest : InlineSourceTestEnvironment {
override val testDisposable = Disposer.newDisposable()
override val kotlinCoreEnvironment: KotlinCoreEnvironment = createKotlinCoreEnvironment(testDisposable)
@TempDir
override lateinit var testTempDir: File
@AfterEach
fun dispose() {
Disposer.dispose(testDisposable)
}
/**
* No 'type mapper' expected for a simple Kotlin class like 'Foo'.
* Only well known standard types (List, ...) will get mapped to their corresponding
* ObjC counterpart (NSArray, ...)
*/
@Test
fun `test - simple class`() {
val module = createModuleDescriptor("class Foo")
val foo = module.findClassAcrossModuleDependencies(ClassId.fromString("Foo"))!!
assertNull(createObjCExportMapper().getCustomTypeMapper(foo))
}
/**
* Will test ObjC type mapping from List<Int> to NSArray<Int *> *
*/
@Test
fun `test - List of int`() {
/* We are only using built in / stdlib parts, so we can provide empty source code */
val module = createModuleDescriptor("")
val objcExportMapper = createObjCExportMapper()
val objcExportNamer = createObjCExportNamer(mapper = objcExportMapper)
val objcExportTranslator = ObjCExportTranslatorImpl(
generator = ObjCExportHeaderGeneratorImpl(
context = BasicPhaseContext(
KonanConfig(kotlinCoreEnvironment.project, kotlinCoreEnvironment.configuration)
),
moduleDescriptors = listOf(module),
mapper = objcExportMapper,
namer = objcExportNamer,
problemCollector = ObjCExportProblemCollector.SILENT,
objcGenerics = true
),
mapper = objcExportMapper,
namer = objcExportNamer,
problemCollector = ObjCExportProblemCollector.SILENT,
objcGenerics = true
)
val listClassDescriptor = module.findClassAcrossModuleDependencies(ClassId.fromString("kotlin/collections/List"))!!
val intClassDescriptor = module.findClassAcrossModuleDependencies(ClassId.fromString("kotlin/Int"))!!
/* Represents List<Int> */
val listOfIntType = KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, listClassDescriptor, listOf(
TypeProjectionImpl(KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, intClassDescriptor, emptyList()))
))
val typeMapper = assertNotNull(objcExportMapper.getCustomTypeMapper(listClassDescriptor))
assertEquals(ClassId.fromString("kotlin/collections/List"), typeMapper.mappedClassId)
val listOfIntMapped = typeMapper.mapType(listOfIntType, objcExportTranslator, objCExportScope = ObjCRootExportScope)
assertEquals(ObjCClassType("NSArray", typeArguments = listOf(ObjCClassType("Int"))), listOfIntMapped)
assertEquals("NSArray<Int *> *", listOfIntMapped.toString())
}
}
@@ -0,0 +1,192 @@
/*
* Copyright 2010-2023 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.backend.konan.tests
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.backend.konan.descriptors.enumEntries
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamer
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamer.ClassOrProtocolName
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamer.PropertyName
import org.jetbrains.kotlin.backend.konan.testUtils.*
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.findSingleFunction
import org.jetbrains.kotlin.resolve.scopes.findFirstVariable
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import kotlin.test.assertEquals
/**
* ## Test Scope
* This test will cover basic cases of the [ObjCExportNamer]. <br>
*
* The __input__ to this test will be descriptors defined as 'inline source code'.
* (e.g. a String like "class Foo").<br>
*
* The __output__ of this test will be result of the request to the [ObjCExportNamer].
* (e.g. the [ClassOrProtocolName])
*/
class ObjCExportNamerTest : InlineSourceTestEnvironment {
override val testDisposable = Disposer.newDisposable()
override val kotlinCoreEnvironment = createKotlinCoreEnvironment(testDisposable)
@TempDir
override lateinit var testTempDir: File
@AfterEach
fun dispose() {
Disposer.dispose(testDisposable)
}
@Test
fun `test - simple class`() {
val module = createModuleDescriptor("class Foo")
val clazz = module.findClassAcrossModuleDependencies(ClassId.fromString("Foo"))!!
assertEquals(
ClassOrProtocolName("Foo", "Foo"),
createObjCExportNamer().getClassOrProtocolName(clazz)
)
}
@Test
fun `test - simple class - with prefix`() {
val module = createModuleDescriptor("class Foo")
val clazz = module.findClassAcrossModuleDependencies(ClassId.fromString("Foo"))!!
assertEquals(
ClassOrProtocolName("Foo", "XFoo"),
createObjCExportNamer(createObjCExportNamerConfiguration(topLevelNamePrefix = "X")).getClassOrProtocolName(clazz)
)
}
@Test
fun `test - simple function`() {
val module = createModuleDescriptor("fun foo() = 42")
val foo = module.getPackage(FqName.ROOT).memberScope.findSingleFunction(Name.identifier("foo"))
assertEquals("foo()", createObjCExportNamer().getSwiftName(foo))
assertEquals("foo", createObjCExportNamer().getSelector(foo))
}
@Test
fun `test - function with parameters`() {
val module = createModuleDescriptor("fun foo(a: Int, b: Int) = a + b")
val foo = module.getPackage(FqName.ROOT).memberScope.findSingleFunction(Name.identifier("foo"))
assertEquals("foo(a:b:)", createObjCExportNamer().getSwiftName(foo))
assertEquals("fooA:b:", createObjCExportNamer().getSelector(foo))
}
@Test
fun `test - simple property`() {
val module = createModuleDescriptor("val foo = 42")
val foo = module.getPackage(FqName.ROOT).memberScope.findFirstVariable("foo") { true }!!
assertEquals(PropertyName("foo", "foo"), createObjCExportNamer().getPropertyName(foo))
}
@Test
fun `test - simple property - with prefix`() {
val module = createModuleDescriptor("val foo = 42")
val foo = module.getPackage(FqName.ROOT).memberScope.findFirstVariable("foo") { true }!!
assertEquals(
PropertyName("foo", "foo"),
createObjCExportNamer(createObjCExportNamerConfiguration(topLevelNamePrefix = "X")).getPropertyName(foo))
}
@Test
fun `test - function inside class`() {
val module = createModuleDescriptor("""
package bar
class Foo {
fun someFunction(a: Int, b: Int) = a + b
}
""".trimIndent()
)
val fooClass = module.findClassAcrossModuleDependencies(ClassId.fromString("bar/Foo"))!!
val someFunction = fooClass.unsubstitutedMemberScope.findSingleFunction(Name.identifier("someFunction"))
assertEquals("someFunction(a:b:)", createObjCExportNamer().getSwiftName(someFunction))
assertEquals("someFunctionA:b:", createObjCExportNamer().getSelector(someFunction))
}
@Test
fun `test - class with ObjCName annotation`() {
val module = createModuleDescriptor("""
@kotlin.native.ObjCName("ObjCFoo", "SwiftFoo")
class Foo
""".trimIndent())
val fooClass = module.findClassAcrossModuleDependencies(ClassId.fromString("Foo"))!!
assertEquals(
ClassOrProtocolName(swiftName = "SwiftFoo", objCName = "ObjCFoo"),
createObjCExportNamer().getClassOrProtocolName(fooClass)
)
}
@Test
fun `test - simple parameter`() {
val module = createModuleDescriptor("fun foo(a: Int)")
val foo = module.getPackage(FqName.ROOT).memberScope.findSingleFunction(Name.identifier("foo"))
val parameterA = foo.valueParameters.find { it.name == Name.identifier("a") }!!
assertEquals("a", createObjCExportNamer().getParameterName(parameterA))
}
@Test
fun `test - parameter with ObjCName annotation`() {
val module = createModuleDescriptor("""
import kotlin.native.ObjCName
fun foo(@ObjCName("aObjC", "aSwift") a: Int)
""".trimIndent())
val foo = module.getPackage(FqName.ROOT).memberScope.findSingleFunction(Name.identifier("foo"))
val parameterA = foo.valueParameters.find { it.name == Name.identifier("a") }!!
assertEquals("aObjC", createObjCExportNamer().getParameterName(parameterA))
}
@Test
fun `test - class mangling`() {
val module = createModuleDescriptor {
source("""
package a
class Foo
""".trimIndent())
source("""
package b
class Foo
""".trimIndent())
}
val aFoo = module.findClassAcrossModuleDependencies(ClassId.fromString("a/Foo"))!!
val bFoo = module.findClassAcrossModuleDependencies(ClassId.fromString("b/Foo"))!!
val namer = createObjCExportNamer()
assertEquals(ClassOrProtocolName("Foo", "Foo"), namer.getClassOrProtocolName(aFoo))
assertEquals(ClassOrProtocolName("Foo_", "Foo_"), namer.getClassOrProtocolName(bFoo))
/* Check caching in namer: Should return same name again */
assertEquals(ClassOrProtocolName("Foo", "Foo"), namer.getClassOrProtocolName(aFoo))
}
@Test
fun `test - simple enum`() {
val module = createModuleDescriptor("""
enum class Foo {
A, B, C
}
""".trimIndent())
val foo = module.findClassAcrossModuleDependencies(ClassId.fromString("Foo"))!!
val fooA = foo.enumEntries.find { it.name == Name.identifier("A") }!!
val namer = createObjCExportNamer()
assertEquals(ClassOrProtocolName("Foo", "Foo"), namer.getClassOrProtocolName(foo))
assertEquals("a", namer.getEnumEntrySelector(fooA))
assertEquals("a", namer.getEnumEntrySwiftName(fooA))
}
}
@@ -0,0 +1,41 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@protocol Foo;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
@protocol Foo
@required
- (id)someMethod __attribute__((swift_name("someMethod()")));
- (id)someMethodWithCovariantOverwrite __attribute__((swift_name("someMethodWithCovariantOverwrite()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
__attribute__((objc_subclassing_restricted))
@interface Bar : Base <Foo>
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (id)someMethod __attribute__((swift_name("someMethod()")));
- (NSString *)someMethodWithCovariantOverwrite __attribute__((swift_name("someMethodWithCovariantOverwrite()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,11 @@
interface Foo {
val someProperty: Int
fun someMethod(): Any
fun someMethodWithCovariantOverwrite(): Any
}
class Bar : Foo {
override val someProperty: Int = 42
override fun someMethod(): Any = ""
override fun someMethodWithCovariantOverwrite(): String = ""
}
@@ -0,0 +1,29 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface NotHidden : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,14 @@
import kotlin.experimental.ExperimentalObjCRefinement
@ExperimentalObjCRefinement
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
@kotlin.native.HidesFromObjC
annotation class MyHiddenFromObjC
@OptIn(ExperimentalObjCRefinement::class)
@MyHiddenFromObjC
class Hidden
class NotHidden
@@ -0,0 +1,52 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
/**
* This class [Foo] is documented.
*/
__attribute__((objc_subclassing_restricted))
@interface Foo : Base
/**
* This class [Foo] is documented.
*/
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
/**
* This class [Foo] is documented.
*/
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
/**
* This member function is documented
*/
- (void)someMemberFunction __attribute__((swift_name("someMemberFunction()")));
/**
* This member property is documented.
* It will return the 'The Answer to the Ultimate Question of Life, The Universe, and Everything'
*/
@property (readonly) int32_t someMemberProperty __attribute__((swift_name("someMemberProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,17 @@
/**
* This class [Foo] is documented.
*/
class Foo {
/**
* This member function is documented
*/
fun someMemberFunction() {
}
/**
* This member property is documented.
* It will return the 'The Answer to the Ultimate Question of Life, The Universe, and Everything'
*/
val someMemberProperty = 42
}
@@ -0,0 +1,30 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("FooInSwift")))
@interface FooInObjC : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,5 @@
import kotlin.experimental.ExperimentalObjCName
@OptIn(ExperimentalObjCName::class)
@kotlin.native.ObjCName("FooInObjC", "FooInSwift")
class Foo
@@ -0,0 +1,33 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@class ERROR;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
+ (ERROR *)foo __attribute__((swift_name("foo()")));
@end
@interface ERROR : Base
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
fun foo() = Unresolved
@@ -0,0 +1,35 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface Foo : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (void)objcMemberFunction __attribute__((swift_name("swiftMemberFunction()")));
@end
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
+ (NSString *)objcTopLevelFunction __attribute__((swift_name("swiftTopLevelFunction()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,11 @@
@file:OptIn(ExperimentalObjCName::class)
import kotlin.experimental.ExperimentalObjCName
@kotlin.native.ObjCName("objcTopLevelFunction", "swiftTopLevelFunction")
fun someTopLevelFunction() = ""
class Foo {
@kotlin.native.ObjCName("objcMemberFunction", "swiftMemberFunction")
fun someMemberFunction() = Unit
}
@@ -0,0 +1,65 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@class Throwable;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
@interface Throwable : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (instancetype)initWithMessage:(NSString * _Nullable)message __attribute__((swift_name("init(message:)"))) __attribute__((objc_designated_initializer));
- (instancetype)initWithCause:(Throwable * _Nullable)cause __attribute__((swift_name("init(cause:)"))) __attribute__((objc_designated_initializer));
- (instancetype)initWithMessage:(NSString * _Nullable)message cause:(Throwable * _Nullable)cause __attribute__((swift_name("init(message:cause:)"))) __attribute__((objc_designated_initializer));
@property (readonly) Throwable * _Nullable cause __attribute__((swift_name("cause")));
@property (readonly) NSString * _Nullable message __attribute__((swift_name("message")));
- (NSError *)asError __attribute__((swift_name("asError()")));
@end
__attribute__((objc_subclassing_restricted))
@interface MyError : Throwable
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (instancetype)initWithMessage:(NSString * _Nullable)message __attribute__((swift_name("init(message:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
- (instancetype)initWithCause:(Throwable * _Nullable)cause __attribute__((swift_name("init(cause:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
- (instancetype)initWithMessage:(NSString * _Nullable)message cause:(Throwable * _Nullable)cause __attribute__((swift_name("init(message:cause:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
@end
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
/**
* @note This method converts all Kotlin exceptions to errors.
*/
+ (BOOL)barAndReturnError:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("bar()")));
/**
* @note This method converts instances of MyError to errors.
* Other uncaught Kotlin exceptions are fatal.
*/
+ (BOOL)fooAndReturnError:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("foo()")));
/**
* @note This method converts all Kotlin exceptions to errors.
*/
+ (NSString * _Nullable)someStringAndReturnError:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("someString()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,10 @@
class MyError : Throwable()
@Throws(MyError::class)
fun foo() = Unit
@Throws(Throwable::class)
fun bar() = Unit
@Throws(Throwable::class)
fun someString() = ""
@@ -0,0 +1,36 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@protocol Foo;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
@protocol Foo
@required
- (id)someMethod __attribute__((swift_name("someMethod()")));
- (id)someMethodWithCovariantOverwrite __attribute__((swift_name("someMethodWithCovariantOverwrite()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
@protocol Bar <Foo>
@required
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,9 @@
interface Foo {
val someProperty: Int
fun someMethod(): Any
fun someMethodWithCovariantOverwrite(): Any
}
interface Bar : Foo {
override fun someMethodWithCovariantOverwrite(): String = ""
}
@@ -0,0 +1,37 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface Foo : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)someMethodInFooA __attribute__((swift_name("someMethodInFooA()")));
@end
__attribute__((objc_subclassing_restricted))
@interface Foo_ : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)someMethodInFooB __attribute__((swift_name("someMethodInFooB()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,5 @@
package a
class Foo {
fun someMethodInFooA() = ""
}
@@ -0,0 +1,5 @@
package b
class Foo {
fun someMethodInFooB() = ""
}
@@ -0,0 +1,29 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
+ (NSString *)foo __attribute__((swift_name("foo()")));
@property (class, readonly, getter=foo_) int32_t foo __attribute__((swift_name("foo")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,2 @@
fun foo() = ""
val foo = 42
@@ -0,0 +1,31 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface Foo : Base
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
+ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead")));
- (NSString *)someMethod __attribute__((swift_name("someMethod()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,4 @@
class Foo {
val someProperty: Int = 42
fun someMethod(): String = ""
}
@@ -0,0 +1,85 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@class EnumCompanion, Enum<E>, Foo, Array<T>;
@protocol Comparable, Iterator;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
@protocol Comparable
@required
- (int32_t)compareToOther:(id _Nullable)other __attribute__((swift_name("compareTo(other:)")));
@end
@interface Enum<E> : Base <Comparable>
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer));
@property (class, readonly, getter=companion) EnumCompanion *companion __attribute__((swift_name("companion")));
/**
* @note This method has protected visibility in Kotlin source and is intended only for use by subclasses.
*/
- (id)clone __attribute__((swift_name("clone()")));
- (int32_t)compareToOther:(E)other __attribute__((swift_name("compareTo(other:)")));
- (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)")));
- (NSUInteger)hash __attribute__((swift_name("hash()")));
- (NSString *)description __attribute__((swift_name("description()")));
@property (readonly) NSString *name __attribute__((swift_name("name")));
@property (readonly) int32_t ordinal __attribute__((swift_name("ordinal")));
@end
__attribute__((objc_subclassing_restricted))
@interface Foo : Enum<Foo *>
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
@property (class, readonly) Foo *a __attribute__((swift_name("a")));
@property (class, readonly) Foo *b __attribute__((swift_name("b")));
@property (class, readonly) Foo *c __attribute__((swift_name("c")));
+ (Array<Foo *> *)values __attribute__((swift_name("values()")));
@property (class, readonly) NSArray<Foo *> *entries __attribute__((swift_name("entries")));
@end
__attribute__((objc_subclassing_restricted))
@interface EnumCompanion : Base
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
+ (instancetype)companion __attribute__((swift_name("init()")));
@property (class, readonly, getter=shared) EnumCompanion *shared __attribute__((swift_name("shared")));
@end
__attribute__((objc_subclassing_restricted))
@interface Array<T> : Base
+ (instancetype)arrayWithSize:(int32_t)size init:(T _Nullable (^)(Int *))init __attribute__((swift_name("init(size:init:)")));
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
- (T _Nullable)getIndex:(int32_t)index __attribute__((swift_name("get(index:)")));
- (id<Iterator>)iterator __attribute__((swift_name("iterator()")));
- (void)setIndex:(int32_t)index value:(T _Nullable)value __attribute__((swift_name("set(index:value:)")));
@property (readonly) int32_t size __attribute__((swift_name("size")));
@end
@protocol Iterator
@required
- (BOOL)hasNext __attribute__((swift_name("hasNext()")));
- (id _Nullable)next __attribute__((swift_name("next()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,3 @@
enum class Foo {
A, B, C
}
@@ -0,0 +1,29 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
@protocol Foo
@required
- (NSString *)someMethod __attribute__((swift_name("someMethod()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,4 @@
interface Foo {
val someProperty: Int
fun someMethod(): String
}
@@ -0,0 +1,35 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@class Foo;
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface Foo : Base
+ (instancetype)alloc __attribute__((unavailable));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
+ (instancetype)foo __attribute__((swift_name("init()")));
@property (class, readonly, getter=shared) Foo *shared __attribute__((swift_name("shared")));
- (NSString *)someMethod __attribute__((swift_name("someMethod()")));
@property (readonly) int32_t someProperty __attribute__((swift_name("someProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1,4 @@
object Foo {
val someProperty = 42
fun someMethod() = ""
}
@@ -0,0 +1,28 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
+ (NSString *)myTopLevelFunction __attribute__((swift_name("myTopLevelFunction()")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
fun myTopLevelFunction() = ""
@@ -0,0 +1,28 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSError.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
NS_ASSUME_NONNULL_BEGIN
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#pragma clang diagnostic ignored "-Wincompatible-property-type"
#pragma clang diagnostic ignored "-Wnullability"
#pragma push_macro("_Nullable_result")
#if !__has_feature(nullability_nullable_result)
#undef _Nullable_result
#define _Nullable_result _Nullable
#endif
__attribute__((objc_subclassing_restricted))
@interface FooKt : Base
@property (class, readonly) int32_t myTopLevelProperty __attribute__((swift_name("myTopLevelProperty")));
@end
#pragma pop_macro("_Nullable_result")
#pragma clang diagnostic pop
NS_ASSUME_NONNULL_END
@@ -0,0 +1 @@
val myTopLevelProperty = 42