[K/N] Modularise :kotlin-native:native.backend to extract objc header generation (1/2)
^KT-63905
This commit is contained in:
committed by
Space Team
parent
4294c7bab7
commit
ae9f3d66c2
@@ -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")
|
||||
}
|
||||
+19
@@ -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,
|
||||
;
|
||||
}
|
||||
+31
@@ -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
|
||||
}
|
||||
}
|
||||
+124
@@ -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
|
||||
+149
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -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 }
|
||||
}
|
||||
}
|
||||
+122
@@ -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()) }
|
||||
}
|
||||
+26
@@ -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("}")
|
||||
}
|
||||
}
|
||||
+1516
File diff suppressed because it is too large
Load Diff
+54
@@ -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)
|
||||
}
|
||||
+480
@@ -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() }
|
||||
+16
@@ -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)
|
||||
}
|
||||
+469
@@ -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)
|
||||
}
|
||||
+1111
File diff suppressed because it is too large
Load Diff
+80
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -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)
|
||||
}
|
||||
}
|
||||
+12
@@ -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<*>>
|
||||
)
|
||||
+20
@@ -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)
|
||||
}
|
||||
}
|
||||
+44
@@ -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
|
||||
}
|
||||
+24
@@ -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
|
||||
}
|
||||
+252
@@ -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()
|
||||
}
|
||||
+218
@@ -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"
|
||||
+106
@@ -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 = "")
|
||||
}
|
||||
+39
@@ -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)
|
||||
+88
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
+140
@@ -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)
|
||||
}
|
||||
+72
@@ -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())
|
||||
}
|
||||
+52
@@ -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
|
||||
)
|
||||
}
|
||||
+11
@@ -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'")
|
||||
+11
@@ -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")
|
||||
+110
@@ -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"))
|
||||
}
|
||||
}
|
||||
+101
@@ -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())
|
||||
}
|
||||
}
|
||||
+192
@@ -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))
|
||||
}
|
||||
}
|
||||
+41
@@ -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
|
||||
Vendored
+11
@@ -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 = ""
|
||||
}
|
||||
+29
@@ -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
|
||||
+14
@@ -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
|
||||
Vendored
+52
@@ -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
|
||||
+17
@@ -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
|
||||
}
|
||||
+30
@@ -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
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import kotlin.experimental.ExperimentalObjCName
|
||||
|
||||
@OptIn(ExperimentalObjCName::class)
|
||||
@kotlin.native.ObjCName("FooInObjC", "FooInSwift")
|
||||
class Foo
|
||||
+33
@@ -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
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
fun foo() = Unresolved
|
||||
+35
@@ -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
|
||||
+11
@@ -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
|
||||
}
|
||||
+65
@@ -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
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
class MyError : Throwable()
|
||||
|
||||
@Throws(MyError::class)
|
||||
fun foo() = Unit
|
||||
|
||||
@Throws(Throwable::class)
|
||||
fun bar() = Unit
|
||||
|
||||
@Throws(Throwable::class)
|
||||
fun someString() = ""
|
||||
+36
@@ -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
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
interface Foo {
|
||||
val someProperty: Int
|
||||
fun someMethod(): Any
|
||||
fun someMethodWithCovariantOverwrite(): Any
|
||||
}
|
||||
|
||||
interface Bar : Foo {
|
||||
override fun someMethodWithCovariantOverwrite(): String = ""
|
||||
}
|
||||
+37
@@ -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
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package a
|
||||
|
||||
class Foo {
|
||||
fun someMethodInFooA() = ""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package b
|
||||
|
||||
class Foo {
|
||||
fun someMethodInFooB() = ""
|
||||
}
|
||||
+29
@@ -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
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
fun foo() = ""
|
||||
val foo = 42
|
||||
+31
@@ -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
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class Foo {
|
||||
val someProperty: Int = 42
|
||||
fun someMethod(): String = ""
|
||||
}
|
||||
Vendored
+85
@@ -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
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
enum class Foo {
|
||||
A, B, C
|
||||
}
|
||||
Vendored
+29
@@ -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
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
interface Foo {
|
||||
val someProperty: Int
|
||||
fun someMethod(): String
|
||||
}
|
||||
Vendored
+35
@@ -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
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
object Foo {
|
||||
val someProperty = 42
|
||||
fun someMethod() = ""
|
||||
}
|
||||
native/objcexport-header-generator/src/test/testData/objcexport/topLevelFunction/!topLevelFunction.h
Vendored
+28
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
fun myTopLevelFunction() = ""
|
||||
native/objcexport-header-generator/src/test/testData/objcexport/topLevelProperty/!topLevelProperty.h
Vendored
+28
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
val myTopLevelProperty = 42
|
||||
Reference in New Issue
Block a user