Add experimental generics support for produced frameworks (#2850)
Available under `-Xobjc-generics` flag.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
7842e6ca43
commit
e553684598
@@ -217,6 +217,86 @@ foo {
|
||||
|
||||
</div>
|
||||
|
||||
### Generics
|
||||
|
||||
Objective-C supports "lightweight generics" defined on classes, with a relatively limited feature set. Swift can import
|
||||
generics defined on classes to help provide additional type information to the compiler.
|
||||
|
||||
Generic feature support for Objc and Swift differ from Kotlin, so the translation will inevitably lose some information,
|
||||
but the features supported retain meaningful information.
|
||||
|
||||
### To Use
|
||||
|
||||
Generics are currently not enabled by default. To have the framework header written with generics, add an experimental
|
||||
flag to the compiler config:
|
||||
|
||||
```
|
||||
compilations.main {
|
||||
outputKinds("framework")
|
||||
extraOpts "-Xobjc-generics"
|
||||
}
|
||||
```
|
||||
|
||||
#### Limitations
|
||||
|
||||
Objective-C generics do not support all features of either Kotlin or Swift, so there will be some information lost
|
||||
in the translation.
|
||||
|
||||
Generics can only be defined on classes, not on interfaces (protocols in Objc and Swift) or functions.
|
||||
|
||||
#### Nullability
|
||||
|
||||
Kotlin and Swift both define nullability as part of the type specification, while Objc defines nullability on methods
|
||||
and properties of a type. As such, the following:
|
||||
|
||||
```kotlin
|
||||
class Sample<T>(){
|
||||
fun myVal():T
|
||||
}
|
||||
```
|
||||
|
||||
will (logically) look like this:
|
||||
|
||||
```swift
|
||||
class Sample<T>(){
|
||||
fun myVal():T?
|
||||
}
|
||||
```
|
||||
|
||||
In order to support a potentially nullable type, the Objc header needs to define `myVal` with a nullable return value.
|
||||
|
||||
To mitigate this, when defining your generic classes, if the generic type should *never* be null, provide a non-null
|
||||
type constraint:
|
||||
|
||||
```kotlin
|
||||
class Sample<T:Any>(){
|
||||
fun myVal():T
|
||||
}
|
||||
```
|
||||
|
||||
That will force the Objc header to mark `myVal` as non-null.
|
||||
|
||||
#### Variance
|
||||
|
||||
Objective-C allows generics to be declared covariant or contravariant. Swift has no support for variance. Generic classes coming
|
||||
from Objective-C can be force-cast as needed.
|
||||
|
||||
```kotlin
|
||||
data class SomeData(val num:Int = 42):BaseData()
|
||||
class GenVarOut<out T:Any>(val arg:T)
|
||||
```
|
||||
|
||||
```swift
|
||||
let variOut = GenVarOut<SomeData>(arg: sd)
|
||||
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>
|
||||
```
|
||||
|
||||
#### Constraints
|
||||
|
||||
In Kotlin you can provide upper bounds for a generic type. Objective-C also supports this, but that support is unavailable
|
||||
in more complex cases, and is currently not supported in the Kotlin - Objective-C interop. The exception here being a non-null
|
||||
upper bound will make Objective-C methods/properties non-null.
|
||||
|
||||
## Casting between mapped types
|
||||
|
||||
When writing Kotlin code, an object may need to be converted from a Kotlin type
|
||||
|
||||
@@ -201,6 +201,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
||||
put(COVERAGE, arguments.coverage)
|
||||
put(LIBRARIES_TO_COVER, arguments.coveredLibraries.toNonNullList())
|
||||
arguments.coverageFile?.let { put(PROFRAW_PATH, it) }
|
||||
put(OBJC_GENERICS, arguments.objcGenerics)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xcoverage-file", valueDescription = "<path>", description = "Save coverage information to the given file")
|
||||
var coverageFile: String? = null
|
||||
|
||||
@Argument(value = "-Xobjc-generics", description = "Enable experimental generics support for framework header")
|
||||
var objcGenerics: Boolean = false
|
||||
|
||||
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> =
|
||||
super.configureAnalysisFlags(collector).also {
|
||||
val useExperimental = it[AnalysisFlags.useExperimental] as List<*>
|
||||
|
||||
+2
@@ -114,6 +114,8 @@ class KonanConfigKeys {
|
||||
= CompilerConfigurationKey.create<List<String>>("libraries that should be covered")
|
||||
val PROFRAW_PATH: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("path to *.profraw coverage output")
|
||||
val OBJC_GENERICS: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("write objc header with generics support")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
internal interface CustomTypeMapper {
|
||||
val mappedClassId: ClassId
|
||||
fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType
|
||||
fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType
|
||||
}
|
||||
|
||||
internal object CustomTypeMappers {
|
||||
@@ -81,7 +81,7 @@ internal object CustomTypeMappers {
|
||||
objCClassName: String
|
||||
) : this(mappedClassId, { objCClassName })
|
||||
|
||||
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType =
|
||||
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType =
|
||||
ObjCClassType(translator.getObjCClassName())
|
||||
}
|
||||
|
||||
@@ -97,14 +97,14 @@ internal object CustomTypeMappers {
|
||||
|
||||
override val mappedClassId = ClassId.topLevel(mappedClassFqName)
|
||||
|
||||
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType {
|
||||
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)
|
||||
translator.mapReferenceTypeIgnoringNullability(argument, objCExportScope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ internal object CustomTypeMappers {
|
||||
private class Function(parameterCount: Int) : CustomTypeMapper {
|
||||
override val mappedClassId: ClassId = KotlinBuiltIns.getFunctionClassId(parameterCount)
|
||||
|
||||
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl): ObjCNonNullReferenceType {
|
||||
override fun mapType(mappedSuperType: KotlinType, translator: ObjCExportTranslatorImpl, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
|
||||
val functionType = mappedSuperType
|
||||
|
||||
val returnType = functionType.getReturnTypeFromFunctionType()
|
||||
@@ -123,8 +123,8 @@ internal object CustomTypeMappers {
|
||||
functionType.getValueParameterTypesFromFunctionType().map { it.type }
|
||||
|
||||
return ObjCBlockPointerType(
|
||||
translator.mapReferenceType(returnType),
|
||||
parameterTypes.map { translator.mapReferenceType(it) }
|
||||
translator.mapReferenceType(returnType, objCExportScope),
|
||||
parameterTypes.map { translator.mapReferenceType(it, objCExportScope) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.backend.konan.getExportedDependencies
|
||||
@@ -53,14 +54,16 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) {
|
||||
return if (produceFramework) {
|
||||
val mapper = ObjCExportMapper()
|
||||
val moduleDescriptors = listOf(context.moduleDescriptor) + context.getExportedDependencies()
|
||||
val objcGenerics = context.configuration.getBoolean(KonanConfigKeys.OBJC_GENERICS)
|
||||
val namer = ObjCExportNamerImpl(
|
||||
moduleDescriptors.toSet(),
|
||||
context.moduleDescriptor.builtIns,
|
||||
mapper,
|
||||
context.moduleDescriptor.namePrefix,
|
||||
local = false
|
||||
local = false,
|
||||
objcGenerics = objcGenerics
|
||||
)
|
||||
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer)
|
||||
val headerGenerator = ObjCExportHeaderGeneratorImpl(context, moduleDescriptors, mapper, namer, objcGenerics)
|
||||
headerGenerator.translateModule()
|
||||
headerGenerator.buildInterface()
|
||||
} else {
|
||||
|
||||
+144
-35
@@ -18,6 +18,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isInterface
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
@@ -43,7 +46,8 @@ internal class ObjCExportTranslatorImpl(
|
||||
val builtIns: KotlinBuiltIns,
|
||||
val mapper: ObjCExportMapper,
|
||||
val namer: ObjCExportNamer,
|
||||
val warningCollector: ObjCExportWarningCollector
|
||||
val warningCollector: ObjCExportWarningCollector,
|
||||
val objcGenerics: Boolean
|
||||
) : ObjCExportTranslator {
|
||||
|
||||
private val kotlinAnyName = namer.kotlinAnyName
|
||||
@@ -55,13 +59,32 @@ internal class ObjCExportTranslatorImpl(
|
||||
attributes = listOf("unavailable(\"Kotlin subclass of Objective-C class can't be imported\")")
|
||||
)
|
||||
|
||||
private fun genericExportScope(classDescriptor: DeclarationDescriptor): ObjCExportScope {
|
||||
return if(objcGenerics && classDescriptor is ClassDescriptor && !classDescriptor.isInterface) {
|
||||
ObjCClassExportScope(classDescriptor, namer)
|
||||
} else {
|
||||
ObjCNoneExportScope
|
||||
}
|
||||
}
|
||||
|
||||
private fun referenceClass(descriptor: ClassDescriptor): ObjCExportNamer.ClassOrProtocolName {
|
||||
fun forwardDeclarationObjcClassName(objcGenerics: Boolean, descriptor: ClassDescriptor, namer:ObjCExportNamer): String {
|
||||
val className = namer.getClassOrProtocolName(descriptor)
|
||||
val builder = StringBuilder(className.objCName)
|
||||
if (objcGenerics)
|
||||
formatGenerics(builder, descriptor.typeConstructor.parameters.map { typeParameterDescriptor ->
|
||||
"${typeParameterDescriptor.variance.objcDeclaration()}${namer.getTypeParameterName(typeParameterDescriptor)}"
|
||||
})
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
assert(mapper.shouldBeExposed(descriptor))
|
||||
assert(!descriptor.isInterface)
|
||||
generator?.requireClassOrInterface(descriptor)
|
||||
|
||||
return translateClassOrInterfaceName(descriptor).also {
|
||||
generator?.referenceClass(it.objCName, descriptor)
|
||||
val objcName = forwardDeclarationObjcClassName(objcGenerics, descriptor, namer)
|
||||
generator?.referenceClass(objcName, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +131,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
|
||||
val name = referenceClass(classDescriptor).objCName
|
||||
val members = buildMembers {
|
||||
translatePlainMembers(declarations)
|
||||
translatePlainMembers(declarations, ObjCNoneExportScope)
|
||||
}
|
||||
return ObjCInterface(name, categoryName = "Extensions", members = members)
|
||||
}
|
||||
@@ -118,7 +141,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
|
||||
// TODO: stop inheriting KotlinBase.
|
||||
val members = buildMembers {
|
||||
translatePlainMembers(declarations)
|
||||
translatePlainMembers(declarations, ObjCNoneExportScope)
|
||||
}
|
||||
return objCInterface(
|
||||
name,
|
||||
@@ -129,7 +152,20 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
|
||||
override fun translateClass(descriptor: ClassDescriptor): ObjCInterface {
|
||||
val name = translateClassOrInterfaceName(descriptor)
|
||||
|
||||
val genericExportScope = genericExportScope(descriptor)
|
||||
|
||||
fun superClassGenerics(genericExportScope: ObjCExportScope): List<ObjCNonNullReferenceType> {
|
||||
val parentType = computeSuperClassType(descriptor)
|
||||
return if(parentType != null) {
|
||||
parentType.arguments.map { typeProjection ->
|
||||
mapReferenceTypeIgnoringNullability(typeProjection.type, genericExportScope)
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
val superClass = descriptor.getSuperClassNotAny()
|
||||
|
||||
val superName = if (superClass == null) {
|
||||
@@ -150,7 +186,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
val selector = getSelector(it)
|
||||
if (!descriptor.isArray) presentConstructors += selector
|
||||
|
||||
+buildMethod(it, it)
|
||||
+buildMethod(it, it, genericExportScope)
|
||||
if (selector == "init") {
|
||||
+ObjCMethod(it, false, ObjCInstanceType, listOf("new"), emptyList(),
|
||||
listOf("availability(swift, unavailable, message=\"use object initializers instead\")"))
|
||||
@@ -174,7 +210,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
)
|
||||
}
|
||||
ClassKind.ENUM_CLASS -> {
|
||||
val type = mapType(descriptor.defaultType, ReferenceBridge)
|
||||
val type = mapType(descriptor.defaultType, ReferenceBridge, ObjCNoneExportScope)
|
||||
|
||||
descriptor.enumEntries.forEach {
|
||||
val entryName = namer.getEnumEntrySelector(it)
|
||||
@@ -194,7 +230,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
?.forEach {
|
||||
val selector = getSelector(it)
|
||||
if (selector !in presentConstructors) {
|
||||
val c = buildMethod(it, it)
|
||||
val c = buildMethod(it, it, ObjCNoneExportScope)
|
||||
+ObjCMethod(c.descriptor, c.isInstanceMethod, c.returnType, c.selectors, c.parameters, c.attributes + "unavailable")
|
||||
|
||||
if (selector == "init") {
|
||||
@@ -210,10 +246,28 @@ internal class ObjCExportTranslatorImpl(
|
||||
|
||||
val attributes = if (descriptor.isFinalOrEnum) listOf("objc_subclassing_restricted") else emptyList()
|
||||
|
||||
val name = translateClassOrInterfaceName(descriptor)
|
||||
|
||||
val generics = if (objcGenerics) {
|
||||
descriptor.typeConstructor.parameters.map {
|
||||
"${it.variance.objcDeclaration()}${namer.getTypeParameterName(it)}"
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
val superClassGenerics = if (objcGenerics) {
|
||||
superClassGenerics(genericExportScope)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
return objCInterface(
|
||||
name,
|
||||
generics = generics,
|
||||
descriptor = descriptor,
|
||||
superClass = superName.objCName,
|
||||
superClassGenerics = superClassGenerics,
|
||||
superProtocols = superProtocols,
|
||||
members = members,
|
||||
attributes = attributes
|
||||
@@ -303,21 +357,21 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
}
|
||||
|
||||
translatePlainMembers(methods, properties)
|
||||
translatePlainMembers(methods, properties, ObjCNoneExportScope)
|
||||
}
|
||||
|
||||
private fun StubBuilder.translatePlainMembers(members: List<CallableMemberDescriptor>) {
|
||||
private fun StubBuilder.translatePlainMembers(members: List<CallableMemberDescriptor>, objCExportScope: ObjCExportScope) {
|
||||
val methods = mutableListOf<FunctionDescriptor>()
|
||||
val properties = mutableListOf<PropertyDescriptor>()
|
||||
|
||||
members.toObjCMembers(methods, properties)
|
||||
|
||||
translatePlainMembers(methods, properties)
|
||||
translatePlainMembers(methods, properties, objCExportScope)
|
||||
}
|
||||
|
||||
private fun StubBuilder.translatePlainMembers(methods: List<FunctionDescriptor>, properties: List<PropertyDescriptor>) {
|
||||
methods.forEach { +buildMethod(it, it) }
|
||||
properties.forEach { +buildProperty(it, it) }
|
||||
private fun StubBuilder.translatePlainMembers(methods: List<FunctionDescriptor>, properties: List<PropertyDescriptor>, objCExportScope: ObjCExportScope) {
|
||||
methods.forEach { +buildMethod(it, it, objCExportScope) }
|
||||
properties.forEach { +buildProperty(it, it, objCExportScope) }
|
||||
}
|
||||
|
||||
private fun <D : CallableMemberDescriptor, S : Stub<*>> StubBuilder.collectMethodsOrProperties(
|
||||
@@ -355,7 +409,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
mapper.getBaseMethods(method)
|
||||
.asSequence()
|
||||
.distinctBy { namer.getSelector(it) }
|
||||
.map { base -> buildMethod((if (isInterface) base else method), base) }
|
||||
.map { base -> buildMethod((if (isInterface) base else method), base, genericExportScope(method.containingDeclaration)) }
|
||||
.map { method -> RenderedStub(method) }
|
||||
.toSet()
|
||||
}
|
||||
@@ -368,7 +422,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
mapper.getBaseProperties(property)
|
||||
.asSequence()
|
||||
.distinctBy { namer.getPropertyName(it) }
|
||||
.map { base -> buildProperty((if (isInterface) base else property), base) }
|
||||
.map { base -> buildProperty((if (isInterface) base else property), base, genericExportScope(property.containingDeclaration)) }
|
||||
.map { property -> RenderedStub(property) }
|
||||
.toSet()
|
||||
}
|
||||
@@ -379,12 +433,12 @@ internal class ObjCExportTranslatorImpl(
|
||||
return namer.getSelector(method)
|
||||
}
|
||||
|
||||
private fun buildProperty(property: PropertyDescriptor, baseProperty: PropertyDescriptor): ObjCProperty {
|
||||
private fun buildProperty(property: PropertyDescriptor, baseProperty: PropertyDescriptor, objCExportScope: ObjCExportScope): ObjCProperty {
|
||||
assert(mapper.isBaseProperty(baseProperty))
|
||||
assert(mapper.isObjCProperty(baseProperty))
|
||||
|
||||
val getterBridge = mapper.bridgeMethod(baseProperty.getter!!)
|
||||
val type = mapReturnType(getterBridge.returnBridge, property.getter!!)
|
||||
val type = mapReturnType(getterBridge.returnBridge, property.getter!!, objCExportScope)
|
||||
val name = namer.getPropertyName(baseProperty)
|
||||
|
||||
val attributes = mutableListOf<String>()
|
||||
@@ -409,7 +463,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
return ObjCProperty(name, property, type, attributes, setterName, getterName, listOf(swiftNameAttribute(name)))
|
||||
}
|
||||
|
||||
private fun buildMethod(method: FunctionDescriptor, baseMethod: FunctionDescriptor): ObjCMethod {
|
||||
private fun buildMethod(method: FunctionDescriptor, baseMethod: FunctionDescriptor, objCExportScope: ObjCExportScope): ObjCMethod {
|
||||
fun collectParameters(baseMethodBridge: MethodBridge, method: FunctionDescriptor): List<ObjCParameter> {
|
||||
fun unifyName(initialName: String, usedNames: Set<String>): String {
|
||||
var unique = initialName
|
||||
@@ -424,6 +478,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
val parameters = mutableListOf<ObjCParameter>()
|
||||
|
||||
val usedNames = mutableSetOf<String>()
|
||||
|
||||
valueParametersAssociated.forEach { (bridge: MethodBridgeValueParameter, p: ParameterDescriptor?) ->
|
||||
val candidateName: String = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> {
|
||||
@@ -442,12 +497,12 @@ internal class ObjCExportTranslatorImpl(
|
||||
usedNames += uniqueName
|
||||
|
||||
val type = when (bridge) {
|
||||
is MethodBridgeValueParameter.Mapped -> mapType(p!!.type, bridge.bridge)
|
||||
is MethodBridgeValueParameter.Mapped -> mapType(p!!.type, bridge.bridge, objCExportScope)
|
||||
MethodBridgeValueParameter.ErrorOutParameter ->
|
||||
ObjCPointerType(ObjCNullableReferenceType(ObjCClassType("NSError")), nullable = true)
|
||||
|
||||
is MethodBridgeValueParameter.KotlinResultOutParameter ->
|
||||
ObjCPointerType(mapType(method.returnType!!, bridge.bridge), nullable = true)
|
||||
ObjCPointerType(mapType(method.returnType!!, bridge.bridge, objCExportScope), nullable = true)
|
||||
}
|
||||
|
||||
parameters += ObjCParameter(uniqueName, p, type)
|
||||
@@ -462,7 +517,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
exportThrownFromThisAndOverridden(method)
|
||||
|
||||
val isInstanceMethod: Boolean = baseMethodBridge.isInstance
|
||||
val returnType: ObjCType = mapReturnType(baseMethodBridge.returnBridge, method)
|
||||
val returnType: ObjCType = mapReturnType(baseMethodBridge.returnBridge, method, objCExportScope)
|
||||
val parameters = collectParameters(baseMethodBridge, method)
|
||||
val selector = getSelector(baseMethod)
|
||||
val selectorParts: List<String> = splitSelector(selector)
|
||||
@@ -524,13 +579,13 @@ internal class ObjCExportTranslatorImpl(
|
||||
method.allOverriddenDescriptors.forEach { exportThrown(it) }
|
||||
}
|
||||
|
||||
private fun mapReturnType(returnBridge: MethodBridge.ReturnValue, method: FunctionDescriptor): ObjCType = when (returnBridge) {
|
||||
private fun mapReturnType(returnBridge: MethodBridge.ReturnValue, method: FunctionDescriptor, objCExportScope: ObjCExportScope): ObjCType = when (returnBridge) {
|
||||
MethodBridge.ReturnValue.Void -> ObjCVoidType
|
||||
MethodBridge.ReturnValue.HashCode -> ObjCPrimitiveType("NSUInteger")
|
||||
is MethodBridge.ReturnValue.Mapped -> mapType(method.returnType!!, returnBridge.bridge)
|
||||
is MethodBridge.ReturnValue.Mapped -> mapType(method.returnType!!, returnBridge.bridge, objCExportScope)
|
||||
MethodBridge.ReturnValue.WithError.Success -> ObjCPrimitiveType("BOOL")
|
||||
is MethodBridge.ReturnValue.WithError.RefOrNull -> {
|
||||
val successReturnType = mapReturnType(returnBridge.successBridge, method) as? ObjCNonNullReferenceType
|
||||
val successReturnType = mapReturnType(returnBridge.successBridge, method, objCExportScope) as? ObjCNonNullReferenceType
|
||||
?: error("Function is expected to have non-null return type: $method")
|
||||
|
||||
ObjCNullableReferenceType(successReturnType)
|
||||
@@ -540,8 +595,8 @@ internal class ObjCExportTranslatorImpl(
|
||||
MethodBridge.ReturnValue.Instance.FactoryResult -> ObjCInstanceType
|
||||
}
|
||||
|
||||
internal fun mapReferenceType(kotlinType: KotlinType): ObjCReferenceType =
|
||||
mapReferenceTypeIgnoringNullability(kotlinType).let {
|
||||
internal fun mapReferenceType(kotlinType: KotlinType, objCExportScope: ObjCExportScope): ObjCReferenceType =
|
||||
mapReferenceTypeIgnoringNullability(kotlinType, objCExportScope).let {
|
||||
if (kotlinType.binaryRepresentationIsNullable()) {
|
||||
ObjCNullableReferenceType(it)
|
||||
} else {
|
||||
@@ -549,7 +604,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType): ObjCNonNullReferenceType {
|
||||
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType, objCExportScope: ObjCExportScope): ObjCNonNullReferenceType {
|
||||
class TypeMappingMatch(val type: KotlinType, val descriptor: ClassDescriptor, val mapper: CustomTypeMapper)
|
||||
|
||||
val typeMappingMatches = (listOf(kotlinType) + kotlinType.supertypes()).mapNotNull { type ->
|
||||
@@ -580,7 +635,13 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
|
||||
mostSpecificMatches.firstOrNull()?.let {
|
||||
return it.mapper.mapType(it.type, this)
|
||||
return it.mapper.mapType(it.type, this, objCExportScope)
|
||||
}
|
||||
|
||||
if(objcGenerics && kotlinType.isTypeParameter()){
|
||||
val genericTypeDeclaration = objCExportScope.getGenericDeclaration(TypeUtils.getTypeParameterDescriptorOrNull(kotlinType))
|
||||
if(genericTypeDeclaration != null)
|
||||
return genericTypeDeclaration
|
||||
}
|
||||
|
||||
val classDescriptor = kotlinType.getErasedTypeClass()
|
||||
@@ -599,7 +660,14 @@ internal class ObjCExportTranslatorImpl(
|
||||
return if (classDescriptor.isInterface) {
|
||||
ObjCProtocolType(referenceProtocol(classDescriptor).objCName)
|
||||
} else {
|
||||
ObjCClassType(referenceClass(classDescriptor).objCName)
|
||||
val typeArgs = if (objcGenerics) {
|
||||
kotlinType.arguments.map { typeProjection ->
|
||||
mapReferenceTypeIgnoringNullability(typeProjection.type, objCExportScope)
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
ObjCClassType(referenceClass(classDescriptor).objCName, typeArgs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,8 +695,8 @@ internal class ObjCExportTranslatorImpl(
|
||||
return ObjCIdType
|
||||
}
|
||||
|
||||
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge): ObjCType = when (typeBridge) {
|
||||
ReferenceBridge -> mapReferenceType(kotlinType)
|
||||
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge, objCExportScope: ObjCExportScope): ObjCType = when (typeBridge) {
|
||||
ReferenceBridge -> mapReferenceType(kotlinType, objCExportScope)
|
||||
is ValueTypeBridge -> {
|
||||
when (typeBridge.objCValueType) {
|
||||
ObjCValueType.BOOL -> ObjCPrimitiveType("BOOL")
|
||||
@@ -654,7 +722,8 @@ abstract class ObjCExportHeaderGenerator internal constructor(
|
||||
val moduleDescriptors: List<ModuleDescriptor>,
|
||||
val builtIns: KotlinBuiltIns,
|
||||
internal val mapper: ObjCExportMapper,
|
||||
val namer: ObjCExportNamer
|
||||
val namer: ObjCExportNamer,
|
||||
val objcGenerics:Boolean = false
|
||||
) {
|
||||
|
||||
constructor(
|
||||
@@ -701,7 +770,8 @@ abstract class ObjCExportHeaderGenerator internal constructor(
|
||||
|
||||
override fun reportWarning(method: FunctionDescriptor, text: String) =
|
||||
this@ObjCExportHeaderGenerator.reportWarning(method, text)
|
||||
})
|
||||
},
|
||||
objcGenerics)
|
||||
|
||||
private val generatedClasses = mutableSetOf<ClassDescriptor>()
|
||||
private val extensions = mutableMapOf<ClassDescriptor, MutableList<CallableMemberDescriptor>>()
|
||||
@@ -936,7 +1006,7 @@ abstract class ObjCExportHeaderGenerator internal constructor(
|
||||
}
|
||||
|
||||
internal fun referenceClass(objCName: String, descriptor: ClassDescriptor? = null) {
|
||||
if (descriptor !in generatedClasses) classForwardDeclarations += objCName
|
||||
if (objcGenerics || descriptor !in generatedClasses) classForwardDeclarations += objCName
|
||||
}
|
||||
|
||||
internal fun referenceProtocol(objCName: String, descriptor: ClassDescriptor? = null) {
|
||||
@@ -949,6 +1019,7 @@ private fun objCInterface(
|
||||
generics: List<String> = emptyList(),
|
||||
descriptor: ClassDescriptor? = null,
|
||||
superClass: String? = null,
|
||||
superClassGenerics: List<ObjCNonNullReferenceType> = emptyList(),
|
||||
superProtocols: List<String> = emptyList(),
|
||||
members: List<Stub<*>> = emptyList(),
|
||||
attributes: List<String> = emptyList()
|
||||
@@ -957,6 +1028,7 @@ private fun objCInterface(
|
||||
generics,
|
||||
descriptor,
|
||||
superClass,
|
||||
superClassGenerics,
|
||||
superProtocols,
|
||||
null,
|
||||
members,
|
||||
@@ -984,3 +1056,40 @@ private fun ObjCExportNamer.ClassOrProtocolName.toNameAttributes(): List<String>
|
||||
|
||||
private fun swiftNameAttribute(swiftName: String) = "swift_name(\"$swiftName\")"
|
||||
private fun objcRuntimeNameAttribute(name: String) = "objc_runtime_name(\"$name\")"
|
||||
|
||||
interface ObjCExportScope{
|
||||
fun getGenericDeclaration(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeDeclaration?
|
||||
}
|
||||
|
||||
internal class ObjCClassExportScope constructor(container:DeclarationDescriptor, val namer: ObjCExportNamer): ObjCExportScope {
|
||||
private val typeNames = if(container is ClassDescriptor && !container.isInterface) {
|
||||
container.typeConstructor.parameters
|
||||
} else {
|
||||
emptyList<TypeParameterDescriptor>()
|
||||
}
|
||||
|
||||
override fun getGenericDeclaration(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeDeclaration? {
|
||||
val localTypeParam = typeNames.firstOrNull {
|
||||
typeParameterDescriptor != null &&
|
||||
(it == typeParameterDescriptor || (it.isCapturedFromOuterDeclaration && it.original == typeParameterDescriptor))
|
||||
}
|
||||
|
||||
return if(localTypeParam == null) {
|
||||
null
|
||||
} else {
|
||||
ObjCGenericTypeDeclaration(localTypeParam, namer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ObjCNoneExportScope: ObjCExportScope{
|
||||
override fun getGenericDeclaration(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeDeclaration? = null
|
||||
}
|
||||
|
||||
internal fun Variance.objcDeclaration():String = when(this){
|
||||
Variance.OUT_VARIANCE -> "__covariant "
|
||||
Variance.IN_VARIANCE -> "__contravariant "
|
||||
else -> ""
|
||||
}
|
||||
|
||||
private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? = descriptor.typeConstructor.supertypes.filter { !it.isInterface() }.firstOrNull()
|
||||
+3
-2
@@ -20,8 +20,9 @@ internal class ObjCExportHeaderGeneratorImpl(
|
||||
val context: Context,
|
||||
moduleDescriptors: List<ModuleDescriptor>,
|
||||
mapper: ObjCExportMapper,
|
||||
namer: ObjCExportNamer
|
||||
) : ObjCExportHeaderGenerator(moduleDescriptors, context.builtIns, mapper, namer) {
|
||||
namer: ObjCExportNamer,
|
||||
objcGenerics: Boolean
|
||||
) : ObjCExportHeaderGenerator(moduleDescriptors, context.builtIns, mapper, namer, objcGenerics) {
|
||||
|
||||
override fun reportWarning(text: String) {
|
||||
context.reportCompilationWarning(text)
|
||||
|
||||
+95
-5
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.backend.konan.cKeywords
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
@@ -16,7 +15,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
|
||||
@@ -31,6 +30,7 @@ interface ObjCExportNamer {
|
||||
fun getPropertyName(property: PropertyDescriptor): String
|
||||
fun getObjectInstanceSelector(descriptor: ClassDescriptor): String
|
||||
fun getEnumEntrySelector(descriptor: ClassDescriptor): String
|
||||
fun getTypeParameterName(typeParameterDescriptor: TypeParameterDescriptor): String
|
||||
|
||||
fun numberBoxName(classId: ClassId): ClassOrProtocolName
|
||||
|
||||
@@ -61,9 +61,9 @@ internal class ObjCExportNamerImpl(
|
||||
builtIns: KotlinBuiltIns,
|
||||
private val mapper: ObjCExportMapper,
|
||||
private val topLevelNamePrefix: String,
|
||||
private val local: Boolean
|
||||
private val local: Boolean,
|
||||
private val objcGenerics: Boolean = false
|
||||
) : ObjCExportNamer {
|
||||
|
||||
private fun String.toUnmangledClassOrProtocolName(): ObjCExportNamer.ClassOrProtocolName =
|
||||
ObjCExportNamer.ClassOrProtocolName(swiftName = this, objCName = this)
|
||||
|
||||
@@ -122,6 +122,8 @@ internal class ObjCExportNamerImpl(
|
||||
// Classes and protocols share the same namespace in Swift.
|
||||
private val swiftClassAndProtocolNames = GlobalNameMapping<Any, String>()
|
||||
|
||||
private val genericTypeParameterNameMapping = GenericTypeParameterNameMapping()
|
||||
|
||||
private abstract inner class ClassPropertyNameMapping<T : Any> : Mapping<T, String>() {
|
||||
|
||||
// Try to avoid clashing with NSObject class methods:
|
||||
@@ -194,6 +196,8 @@ internal class ObjCExportNamerImpl(
|
||||
append(getClassOrProtocolSwiftName(containingDeclaration))
|
||||
|
||||
val importAsMember = when {
|
||||
objcGenerics && descriptor.hasGenericsInHierarchy() -> false
|
||||
|
||||
descriptor.isInterface || containingDeclaration.isInterface -> {
|
||||
// Swift doesn't support neither nested nor outer protocols.
|
||||
false
|
||||
@@ -221,6 +225,22 @@ internal class ObjCExportNamerImpl(
|
||||
}.mangledBySuffixUnderscores()
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasGenericsInHierarchy(): Boolean {
|
||||
fun ClassDescriptor.hasGenericsChildren(): Boolean =
|
||||
unsubstitutedMemberScope.getContributedDescriptors()
|
||||
.asSequence()
|
||||
.filterIsInstance<ClassDescriptor>()
|
||||
.any {
|
||||
it.typeConstructor.parameters.isNotEmpty() ||
|
||||
it.hasGenericsChildren()
|
||||
}
|
||||
|
||||
val upGenerics = generateSequence(this) { it.containingDeclaration as? ClassDescriptor }
|
||||
.any { it.typeConstructor.parameters.isNotEmpty() }
|
||||
|
||||
return upGenerics || hasGenericsChildren()
|
||||
}
|
||||
|
||||
private fun getClassOrProtocolObjCName(descriptor: ClassDescriptor): String {
|
||||
val objCMapping = if (descriptor.isInterface) objCProtocolNames else objCClassNames
|
||||
return objCMapping.getOrPut(descriptor) {
|
||||
@@ -364,6 +384,16 @@ internal class ObjCExportNamerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTypeParameterName(typeParameterDescriptor: TypeParameterDescriptor): String {
|
||||
return genericTypeParameterNameMapping.getOrPut(typeParameterDescriptor) {
|
||||
StringBuilder().apply {
|
||||
append(typeParameterDescriptor.name.asString())
|
||||
}.mangledSequence {
|
||||
append('_')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val any = builtIns.any
|
||||
|
||||
@@ -424,13 +454,71 @@ internal class ObjCExportNamerImpl(
|
||||
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
||||
(this.length == words.length || !this[words.length].isLowerCase())
|
||||
|
||||
private inner class GenericTypeParameterNameMapping {
|
||||
private val elementToName = mutableMapOf<TypeParameterDescriptor, String>()
|
||||
private val typeParameterNameClassOverrides = mutableMapOf<ClassDescriptor, MutableSet<String>>()
|
||||
|
||||
fun reserved(name: String): Boolean {
|
||||
return name in reservedNames
|
||||
}
|
||||
|
||||
fun getOrPut(element: TypeParameterDescriptor, nameCandidates: () -> Sequence<String>): String {
|
||||
getIfAssigned(element)?.let { return it }
|
||||
|
||||
nameCandidates().forEach {
|
||||
if (tryAssign(element, it)) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
error("name candidates run out")
|
||||
}
|
||||
|
||||
private fun tryAssign(element: TypeParameterDescriptor, name: String): Boolean {
|
||||
if (element in elementToName) error(element)
|
||||
|
||||
if (reserved(name)) return false
|
||||
|
||||
if (!validName(element, name)) return false
|
||||
|
||||
assignName(element, name)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun assignName(element: TypeParameterDescriptor, name: String) {
|
||||
if (!local) {
|
||||
elementToName[element] = name
|
||||
}
|
||||
classNameSet(element).add(name)
|
||||
}
|
||||
|
||||
private fun validName(element: TypeParameterDescriptor, name: String): Boolean {
|
||||
assert(element.containingDeclaration is ClassDescriptor)
|
||||
|
||||
val nameSet = classNameSet(element)
|
||||
return !objCClassNames.nameExists(name) && !objCProtocolNames.nameExists(name) && name !in nameSet
|
||||
}
|
||||
|
||||
private fun classNameSet(element: TypeParameterDescriptor): MutableSet<String> {
|
||||
return typeParameterNameClassOverrides.getOrPut(element.containingDeclaration as ClassDescriptor) {
|
||||
mutableSetOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIfAssigned(element: TypeParameterDescriptor): String? = elementToName[element]
|
||||
|
||||
private val reservedNames = setOf("id", "NSObject", "NSArray", "NSCopying", "NSNumber", "NSInteger",
|
||||
"NSUInteger", "NSString", "NSSet", "NSDictionary", "NSMutableArray", "int", "unsigned", "short",
|
||||
"char", "long", "float", "double", "int32_t", "int64_t", "int16_t", "int8_t", "unichar")
|
||||
}
|
||||
|
||||
private abstract inner class Mapping<in T : Any, N>() {
|
||||
private val elementToName = mutableMapOf<T, N>()
|
||||
private val nameToElements = mutableMapOf<N, MutableList<T>>()
|
||||
|
||||
abstract fun conflict(first: T, second: T): Boolean
|
||||
open fun reserved(name: N) = false
|
||||
|
||||
fun getOrPut(element: T, nameCandidates: () -> Sequence<N>): N {
|
||||
getIfAssigned(element)?.let { return it }
|
||||
|
||||
@@ -443,6 +531,8 @@ internal class ObjCExportNamerImpl(
|
||||
error("name candidates run out")
|
||||
}
|
||||
|
||||
fun nameExists(name: N) = nameToElements.containsKey(name)
|
||||
|
||||
private fun getIfAssigned(element: T): N? = elementToName[element]
|
||||
|
||||
private fun tryAssign(element: T, name: N): Boolean {
|
||||
|
||||
+8
-4
@@ -137,13 +137,11 @@ object StubRenderer {
|
||||
private fun ObjCInterface.renderInterfaceHeader() = buildString {
|
||||
fun appendSuperClass() {
|
||||
if (superClass != null) append(" : $superClass")
|
||||
formatGenerics(this, superClassGenerics.map { it.render() })
|
||||
}
|
||||
|
||||
fun appendGenerics() {
|
||||
val generics = generics
|
||||
if (generics.isNotEmpty()) {
|
||||
generics.joinTo(this, separator = ", ", prefix = "<", postfix = ">")
|
||||
}
|
||||
formatGenerics(this, generics)
|
||||
}
|
||||
|
||||
fun appendCategoryName() {
|
||||
@@ -190,3 +188,9 @@ object StubRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatGenerics(buffer: Appendable, generics:List<String>) {
|
||||
if (generics.isNotEmpty()) {
|
||||
generics.joinTo(buffer, separator = ", ", prefix = "<", postfix = ">")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
|
||||
sealed class ObjCType {
|
||||
final override fun toString(): String = this.render()
|
||||
|
||||
@@ -49,6 +51,15 @@ class ObjCClassType(
|
||||
}
|
||||
}
|
||||
|
||||
class ObjCGenericTypeDeclaration(
|
||||
val typeParameterDescriptor: TypeParameterDescriptor,
|
||||
val namer: ObjCExportNamer
|
||||
) : ObjCNonNullReferenceType() {
|
||||
override fun render(attrsAndName: String): String {
|
||||
return namer.getTypeParameterName(typeParameterDescriptor).withAttrsAndName(attrsAndName)
|
||||
}
|
||||
}
|
||||
|
||||
class ObjCProtocolType(
|
||||
val protocolName: String
|
||||
) : ObjCNonNullReferenceType() {
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ class ObjCInterface(name: String,
|
||||
val generics: List<String> = emptyList(),
|
||||
descriptor: ClassDescriptor? = null,
|
||||
val superClass: String? = null,
|
||||
val superClassGenerics: List<ObjCNonNullReferenceType> = emptyList(),
|
||||
superProtocols: List<String> = emptyList(),
|
||||
val categoryName: String? = null,
|
||||
members: List<Stub<*>> = emptyList(),
|
||||
|
||||
@@ -3353,6 +3353,24 @@ if (isAppleTarget(project)) {
|
||||
swiftSources = ['framework/values/values.swift']
|
||||
}
|
||||
|
||||
task testValuesGenericsFramework(type: FrameworkTest) {
|
||||
frameworkName = 'ValuesGenerics'
|
||||
konanArtifacts {
|
||||
framework(frameworkName, targets: [ target ]) {
|
||||
srcDir 'framework/values'
|
||||
srcDir 'framework/values_generics'
|
||||
baseDir "$testOutputFramework/$frameworkName"
|
||||
|
||||
if (!useCustomDist) {
|
||||
dependsOn ":${target}CrossDistRuntime", ':commonDistRuntime', ':distCompiler'
|
||||
}
|
||||
|
||||
extraOpts "-Xembed-bitcode-marker", "-Xobjc-generics"
|
||||
}
|
||||
}
|
||||
swiftSources = ['framework/values_generics/values.swift']
|
||||
}
|
||||
|
||||
task testStdlibFramework(type: FrameworkTest) {
|
||||
frameworkName = 'Stdlib'
|
||||
fullBitcode = true
|
||||
|
||||
@@ -280,6 +280,14 @@ class Deeply {
|
||||
}
|
||||
}
|
||||
|
||||
class WithGenericDeeply() {
|
||||
class Nested {
|
||||
class Type<T> {
|
||||
val thirtyThree = 33
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CKeywords(val float: Float, val `enum`: Int, var goto: Boolean)
|
||||
|
||||
interface Base1 {
|
||||
|
||||
@@ -494,6 +494,7 @@ func testPureSwiftClasses() throws {
|
||||
func testNames() throws {
|
||||
try assertEquals(actual: ValuesKt.PROPERTY_NAME_MUST_NOT_BE_ALTERED_BY_SWIFT, expected: 111)
|
||||
try assertEquals(actual: Deeply.NestedType().thirtyTwo, expected: 32)
|
||||
try assertEquals(actual: WithGenericDeeply.NestedType().thirtyThree, expected: 33)
|
||||
try assertEquals(actual: CKeywords(float: 1.0, enum : 42, goto: true).goto_, expected: true)
|
||||
}
|
||||
|
||||
@@ -612,4 +613,4 @@ class ValuesTests : TestProvider {
|
||||
TestCase(name: "TestGH2931", method: withAutorelease(testGH2931)),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import ValuesGenerics
|
||||
|
||||
// -------- Tests --------
|
||||
|
||||
func testVararg() throws {
|
||||
let ktArray = KotlinArray<KotlinInt>(size: 3, init: { (_) -> KotlinInt in return KotlinInt(int:42) })
|
||||
let arr: [Int] = ValuesKt.varargToList(args: ktArray as! KotlinArray<AnyObject>) as! [Int]
|
||||
try assertEquals(actual: arr, expected: [42, 42, 42])
|
||||
}
|
||||
|
||||
func testDataClass() throws {
|
||||
let f = "1" as NSString
|
||||
let s = "2" as NSString
|
||||
let t = "3" as NSString
|
||||
|
||||
let tripleVal = TripleVals<NSString>(first: f, second: s, third: t)
|
||||
try assertEquals(actual: tripleVal.first, expected: f, "Data class' value")
|
||||
try assertEquals(actual: tripleVal.first, expected: "1", "Data class' value literal")
|
||||
try assertEquals(actual: tripleVal.component2(), expected: s, "Data class' component")
|
||||
print(tripleVal)
|
||||
try assertEquals(actual: String(describing: tripleVal), expected: "TripleVals(first=\(f), second=\(s), third=\(t))")
|
||||
|
||||
let tripleVar = TripleVars<NSString>(first: f, second: s, third: t)
|
||||
try assertEquals(actual: tripleVar.first, expected: f, "Data class' value")
|
||||
try assertEquals(actual: tripleVar.component2(), expected: s, "Data class' component")
|
||||
print(tripleVar)
|
||||
try assertEquals(actual: String(describing: tripleVar), expected: "[\(f), \(s), \(t)]")
|
||||
|
||||
tripleVar.first = t
|
||||
tripleVar.second = f
|
||||
tripleVar.third = s
|
||||
try assertEquals(actual: tripleVar.component2(), expected: f, "Data class' component")
|
||||
try assertEquals(actual: String(describing: tripleVar), expected: "[\(t), \(f), \(s)]")
|
||||
}
|
||||
|
||||
func testInlineClasses() throws {
|
||||
let ic1: Int32 = 42
|
||||
let ic1N = ValuesKt.box(ic1: 17)
|
||||
let ic2 = "foo"
|
||||
let ic2N = "bar"
|
||||
let ic3 = TripleVals<AnyObject>(first: KotlinInt(int:1), second: KotlinInt(int:2), third: KotlinInt(int:3))
|
||||
let ic3N = ValuesKt.box(ic3: nil)
|
||||
|
||||
try assertEquals(
|
||||
actual: ValuesKt.concatenateInlineClassValues(ic1: ic1, ic1N: ic1N, ic2: ic2, ic2N: ic2N, ic3: ic3, ic3N: ic3N),
|
||||
expected: "42 17 foo bar TripleVals(first=1, second=2, third=3) null"
|
||||
)
|
||||
|
||||
try assertEquals(
|
||||
actual: ValuesKt.concatenateInlineClassValues(ic1: ic1, ic1N: nil, ic2: ic2, ic2N: nil, ic3: nil, ic3N: nil),
|
||||
expected: "42 null foo null null null"
|
||||
)
|
||||
|
||||
try assertEquals(actual: ValuesKt.getValue1(ic1), expected: 42)
|
||||
try assertEquals(actual: ValuesKt.getValueOrNull1(ic1N) as! Int, expected: 17)
|
||||
|
||||
try assertEquals(actual: ValuesKt.getValue2(ic2), expected: "foo")
|
||||
try assertEquals(actual: ValuesKt.getValueOrNull2(ic2N), expected: "bar")
|
||||
|
||||
try assertEquals(actual: ValuesKt.getValue3(ic3), expected: ic3)
|
||||
try assertEquals(actual: ValuesKt.getValueOrNull3(ic3N), expected: nil)
|
||||
}
|
||||
|
||||
func testGeneric() throws {
|
||||
let a = SomeGeneric<SomeData>(t: SomeData(num: 52))
|
||||
let asd : SomeData = a.myVal()!
|
||||
try assertEquals(actual: asd.num, expected: 52)
|
||||
|
||||
let nulls = GenOpen<SomeData>(arg: SomeData(num: 62))
|
||||
let nullssd : SomeData = nulls.arg!
|
||||
try assertEquals(actual: nullssd.num, expected: 62)
|
||||
|
||||
let isnull = GenOpen<SomeData>(arg: nil)
|
||||
try assertEquals(actual: isnull.arg, expected: nil)
|
||||
|
||||
let nonnulls = GenNonNull<SomeData>(arg: SomeData(num: 72))
|
||||
let nonnullssd : SomeData = nonnulls.arg
|
||||
try assertEquals(actual: nonnullssd.num, expected: 72)
|
||||
try assertEquals(actual: (Values_genericsKt.starGeneric(arg: nonnulls as! GenNonNull<AnyObject>) as! SomeData).num, expected: 72)
|
||||
|
||||
let sd = SomeData(num: 33)
|
||||
let nullColl = GenCollectionsNull<SomeData>(arg: sd, coll: [sd])
|
||||
let nonNullColl = GenCollectionsNonNull<SomeData>(arg: sd, coll: [sd])
|
||||
|
||||
try assertEquals(actual: (nullColl.coll[0] as! SomeData).num, expected: 33)
|
||||
let nonNullCollSd : SomeData = nonNullColl.coll[0]
|
||||
try assertEquals(actual: nonNullCollSd.num, expected: 33)
|
||||
try assertEquals(actual: nonNullColl.arg, expected: nonNullCollSd)
|
||||
|
||||
let mixed = GenNullability<SomeData>(arg: sd, nArg: sd)
|
||||
try assertEquals(actual: mixed.asNullable()?.num, expected: 33)
|
||||
try assertEquals(actual: mixed.pAsNullable?.num, expected: 33)
|
||||
let mixedSd : SomeData? = mixed.pAsNullable
|
||||
try assertEquals(actual: mixedSd, expected: mixed.nArg)
|
||||
}
|
||||
|
||||
// Swift ignores the variance and lets you force-cast to whatever you need, for better or worse.
|
||||
// This would *not* work with direct Swift interop.
|
||||
func testGenericVariance() throws {
|
||||
let sd = SomeData(num: 22)
|
||||
|
||||
let variOut = GenVarOut<SomeData>(arg: sd)
|
||||
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>
|
||||
let variOutOther : GenVarOut<SomeOtherData> = variOut as! GenVarOut<SomeOtherData>
|
||||
|
||||
let variOutCheck = "variOut: \(variOut.arg.asString()), variOutAny: \(variOutAny.arg.asString()), variOutOther: \(variOutOther.arg.asString())"
|
||||
try assertEquals(actual: variOutCheck, expected: "variOut: 22, variOutAny: 22, variOutOther: 22")
|
||||
|
||||
let variIn = GenVarIn<SomeData>(tArg: sd)
|
||||
let variInAny : GenVarIn<BaseData> = variIn as! GenVarIn<BaseData>
|
||||
let variInOther : GenVarIn<SomeOtherData> = variIn as! GenVarIn<SomeOtherData>
|
||||
|
||||
let varInCheck = "variIn: \(variIn.valString()), variInAny: \(variInAny.valString()), variInOther: \(variInOther.valString())"
|
||||
try assertEquals(actual: varInCheck, expected: "variIn: SomeData(num=22), variInAny: SomeData(num=22), variInOther: SomeData(num=22)")
|
||||
|
||||
let variCoType:GenVarOut<BaseData> = Values_genericsKt.variCoType()
|
||||
try assertEquals(actual: "890", expected: variCoType.arg.asString())
|
||||
|
||||
let variContraType:GenVarIn<SomeData> = Values_genericsKt.variContraType()
|
||||
try assertEquals(actual: "SomeData(num=1890)", expected: variContraType.valString())
|
||||
}
|
||||
|
||||
// Swift should completely ignore this, as should objc. Really verifying that the header generator
|
||||
// deals with this
|
||||
func testGenericUseSiteVariance() throws {
|
||||
let sd = SomeData(num: 22)
|
||||
|
||||
let varUse = GenVarUse<BaseData>(arg: sd)
|
||||
let varUseArg = GenVarUse<BaseData>(arg: sd)
|
||||
|
||||
varUse.varUse(a: varUseArg, b: GenVarUse<SomeData>(arg: sd) as! GenVarUse<BaseData>)
|
||||
}
|
||||
|
||||
func testGenericInterface() throws {
|
||||
let a: NoGeneric = SomeGeneric<SomeData>(t: SomeData(num: 52))
|
||||
try assertEquals(actual: (a.myVal() as! SomeData).num, expected: 52)
|
||||
}
|
||||
|
||||
func testGenericInheritance() throws {
|
||||
let ge = GenEx<SomeData, SomeOtherData>(myT:SomeOtherData(str:"Hello"), baseT:SomeData(num: 11))
|
||||
let geT : SomeData = ge.t
|
||||
try assertEquals(actual: geT.num, expected: 11)
|
||||
let gemyT : SomeOtherData = ge.myT
|
||||
try assertEquals(actual: gemyT.str, expected: "Hello")
|
||||
let geBase = ge as GenBase<SomeData>
|
||||
let geBaseT : SomeData = geBase.t
|
||||
try assertEquals(actual: geBaseT.num, expected: 11)
|
||||
|
||||
//Similar to above but param names don't match and will dupe property definitions on child class
|
||||
//Functional, but should be fixed
|
||||
let ge2 = GenEx2<SomeData, SomeOtherData>(myT:SomeOtherData(str:"Hello2"), baseT:SomeData(num: 22))
|
||||
let ge2Val : SomeData = ge2.t
|
||||
let ge2SODVal : SomeOtherData = ge2.myT
|
||||
let ge2base : GenBase<SomeData> = ge2 as GenBase<SomeData>
|
||||
let ge2BaseVal : SomeData = ge2base.t
|
||||
try assertEquals(actual: ge2Val, expected: ge2BaseVal)
|
||||
|
||||
let geAny = GenExAny<SomeData, SomeOtherData>(myT:SomeOtherData(str:"Hello"), baseT:SomeData(num: 131))
|
||||
try assertEquals(actual: (geAny.t as! SomeData).num, expected: 131)
|
||||
let geBaseAny = geAny as! GenBase<SomeData>
|
||||
let geBaseAnyT : SomeData = geBaseAny.t
|
||||
try assertEquals(actual: geBaseAnyT.num, expected: 131)
|
||||
}
|
||||
|
||||
func testGenericInnerClass() throws {
|
||||
|
||||
let nestedClass = GenOuterGenNested<SomeData>(b: SomeData(num: 543))
|
||||
let nestedClassB : SomeData = nestedClass.b
|
||||
try assertEquals(actual: nestedClassB.num, expected: 543)
|
||||
|
||||
let innerClass = GenOuterGenInner<SomeData, SomeOtherData>(GenOuter<SomeOtherData>(a: SomeOtherData(str: "ggg")), c: SomeData(num: 66), aInner: SomeOtherData(str: "ttt"))
|
||||
let innerClassC : SomeData = innerClass.c
|
||||
try assertEquals(actual: innerClassC.num, expected: 66)
|
||||
let outerFun : SomeOtherData = innerClass.outerFun()
|
||||
let outerVal : SomeOtherData = innerClass.outerVal
|
||||
try assertEquals(actual: outerFun, expected: outerVal)
|
||||
try assertEquals(actual: outerFun.str, expected: "ggg")
|
||||
|
||||
Values_genericsKt.genInnerFunc(obj: innerClass)
|
||||
Values_genericsKt.genInnerFuncAny(obj: innerClass as! GenOuterGenInner<AnyObject, AnyObject>)
|
||||
|
||||
let innerReturned : GenOuterGenInner<SomeOtherData, SomeData> = Values_genericsKt.genInnerCreate()
|
||||
let innerReturnedInner : SomeOtherData = innerReturned.c
|
||||
try assertEquals(actual: innerReturnedInner.str, expected: "ppp")
|
||||
|
||||
let nestedClassSame = GenOuterSameGenNestedSame<SomeData>(a: SomeData(num: 545))
|
||||
let nestedClassSameA : SomeData = nestedClassSame.a
|
||||
try assertEquals(actual: nestedClassSameA.num, expected: 545)
|
||||
|
||||
let nested = GenOuterSameNestedNoGeneric()
|
||||
|
||||
let innerClassSame = GenOuterSameGenInnerSame<SomeOtherData, SomeData>(GenOuterSame<SomeData>(a: SomeData(num: 44)), a: SomeOtherData(str: "rrr"))
|
||||
let innerClassSameA : SomeOtherData = innerClassSame.a
|
||||
try assertEquals(actual: innerClassSame.a.str, expected: "rrr")
|
||||
|
||||
let gob : GenOuterBlankGenInner<SomeOtherData> = GenOuterBlankGenInner<SomeOtherData>(GenOuterBlank(sd: SomeData(num: 321)), arg: SomeOtherData(str: "aaa"))
|
||||
let gob2 : GenOuterBlank2GenInner<SomeOtherData> = GenOuterBlank2GenInner<SomeOtherData>(GenOuterBlank2(oarg: SomeOtherData(str: "ooo")), arg: SomeOtherData(str: "bbb"))
|
||||
|
||||
let gobsod : SomeOtherData = gob.arg!
|
||||
try assertEquals(actual: gobsod.str, expected: "aaa")
|
||||
|
||||
let gob2arg : SomeOtherData = gob2.arg!
|
||||
let gob2out : SomeOtherData = gob2.fromOuter()!
|
||||
|
||||
try assertEquals(actual: gob2arg.str, expected: "bbb")
|
||||
try assertEquals(actual: gob2out.str, expected: "ooo")
|
||||
|
||||
let inarg = GenOuterDeepGenShallowInner<SomeOtherData>(GenOuterDeep<SomeOtherData>(oarg: SomeOtherData(str: "fff")))
|
||||
let godeep : GenOuterDeepGenShallowInnerGenDeepInner<SomeOtherData> = GenOuterDeepGenShallowInnerGenDeepInner<SomeOtherData>(inarg)
|
||||
let deepval : SomeOtherData = godeep.o()!
|
||||
try assertEquals(actual: deepval.str, expected: "fff")
|
||||
|
||||
let deep2 = GenOuterDeep2()
|
||||
let deep2Before = GenOuterDeep2.Before(deep2)
|
||||
let deep2After = GenOuterDeep2.After(deep2)
|
||||
let deep2soi = GenOuterDeep2GenShallowOuterInner(deep2)
|
||||
let deep2si = GenOuterDeep2GenShallowOuterInnerGenShallowInner<SomeData>(deep2soi)
|
||||
let deep2i = GenOuterDeep2GenShallowOuterInnerGenShallowInnerGenDeepInner<SomeData>(deep2si)
|
||||
|
||||
let gbb : GenBothBlank.GenInner = GenBothBlank.GenInner(GenBothBlank(a: SomeData(num: 22)), b: SomeOtherData(str: "ttt"))
|
||||
try assertEquals(actual: gbb.b.str, expected: "ttt")
|
||||
}
|
||||
|
||||
func testGenericClashing() throws {
|
||||
let gcId = GenClashId<SomeData, SomeOtherData>(arg: SomeData(num: 22), arg2: SomeOtherData(str: "lll"))
|
||||
try assertEquals(actual: gcId.x() as! NSString, expected: "Foo")
|
||||
let gcIdArg : SomeData = gcId.arg
|
||||
try assertEquals(actual: gcIdArg.num, expected: 22)
|
||||
let gcIdArg2 : SomeOtherData = gcId.arg2
|
||||
try assertEquals(actual: gcIdArg2.str, expected: "lll")
|
||||
|
||||
let gcClass = GenClashClass<SomeData, SomeOtherData, NSString>(arg: SomeData(num: 432), arg2: SomeOtherData(str: "lll"), arg3: "Bar")
|
||||
try assertEquals(actual: gcClass.int(), expected: 55)
|
||||
try assertEquals(actual: gcClass.sd().num, expected: 88)
|
||||
try assertEquals(actual: gcClass.list()[1].num, expected: 22)
|
||||
try assertEquals(actual: gcClass.arg.num, expected: 432)
|
||||
try assertEquals(actual: gcClass.clash().str, expected: "aaa")
|
||||
try assertEquals(actual: gcClass.arg2.str, expected: "lll")
|
||||
try assertEquals(actual: gcClass.arg3, expected: "Bar")
|
||||
|
||||
//GenClashNames uses type parameter names that force the Objc class name itself to be mangled. Swift keeps names however
|
||||
let clashNames = GenClashNames<SomeData, SomeData, SomeData, SomeData>()
|
||||
try assertEquals(actual: clashNames.foo().str, expected: "nnn")
|
||||
try assertEquals(actual: clashNames.bar().str, expected: "qqq")
|
||||
try assertTrue(clashNames.baz(arg: ClashnameParam(str: "meh")), "ClashnameParam issue")
|
||||
|
||||
let clashNamesEx = GenClashEx<SomeData>()
|
||||
|
||||
let geClash = GenExClash<SomeOtherData>(myT:SomeOtherData(str:"Hello"))
|
||||
try assertEquals(actual: geClash.t.num, expected: 55)
|
||||
try assertEquals(actual: geClash.myT.str, expected: "Hello")
|
||||
}
|
||||
|
||||
func testGenericExtensions() throws {
|
||||
let gnn = GenNonNull<SomeData>(arg: SomeData(num: 432))
|
||||
try assertEquals(actual: (gnn.foo() as! SomeData).num, expected: 432)
|
||||
}
|
||||
|
||||
// -------- Execution of the test --------
|
||||
|
||||
class ValuesGenericsTests : TestProvider {
|
||||
var tests: [TestCase] = []
|
||||
|
||||
init() {
|
||||
providers.append(self)
|
||||
tests = [
|
||||
TestCase(name: "TestVararg", method: withAutorelease(testVararg)),
|
||||
TestCase(name: "TestDataClass", method: withAutorelease(testDataClass)),
|
||||
TestCase(name: "TestInlineClasses", method: withAutorelease(testInlineClasses)),
|
||||
TestCase(name: "TestGeneric", method: withAutorelease(testGeneric)),
|
||||
TestCase(name: "TestGenericVariance", method: withAutorelease(testGenericVariance)),
|
||||
TestCase(name: "TestGenericUseSiteVariance", method: withAutorelease(testGenericUseSiteVariance)),
|
||||
TestCase(name: "TestGenericInheritance", method: withAutorelease(testGenericInheritance)),
|
||||
TestCase(name: "TestGenericInterface", method: withAutorelease(testGenericInterface)),
|
||||
TestCase(name: "TestGenericInnerClass", method: withAutorelease(testGenericInnerClass)),
|
||||
TestCase(name: "TestGenericClashing", method: withAutorelease(testGenericClashing)),
|
||||
TestCase(name: "TestGenericExtensions", method: withAutorelease(testGenericExtensions)),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// All classes and methods should be used in tests
|
||||
@file:Suppress("UNUSED")
|
||||
|
||||
package conversions
|
||||
|
||||
import kotlin.native.concurrent.isFrozen
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
// Generics
|
||||
abstract class BaseData{
|
||||
abstract fun asString():String
|
||||
}
|
||||
|
||||
data class SomeData(val num:Int = 42):BaseData() {
|
||||
override fun asString(): String = num.toString()
|
||||
}
|
||||
|
||||
data class SomeOtherData(val str:String):BaseData() {
|
||||
fun anotherFun(){}
|
||||
override fun asString(): String = str
|
||||
}
|
||||
|
||||
interface NoGeneric<T> {
|
||||
fun myVal():T
|
||||
}
|
||||
|
||||
data class SomeGeneric<T>(val t:T):NoGeneric<T>{
|
||||
override fun myVal(): T = t
|
||||
}
|
||||
|
||||
class GenOpen<T:Any?>(val arg:T)
|
||||
class GenNonNull<T:Any>(val arg:T)
|
||||
|
||||
class GenCollectionsNull<T>(val arg: T, val coll: List<T>)
|
||||
class GenCollectionsNonNull<T:Any>(val arg: T, val coll: List<T>)
|
||||
|
||||
//Force @class declaration at top of file with Objc variance
|
||||
object ForceUse {
|
||||
val gvo = GenVarOut(SomeData())
|
||||
}
|
||||
|
||||
class GenVarOut<out T:Any>(val arg:T)
|
||||
|
||||
class GenVarIn<in T:Any>(tArg:T){
|
||||
private val t = tArg
|
||||
|
||||
fun valString():String = t.toString()
|
||||
|
||||
fun goIn(t:T){
|
||||
//Just taking a val
|
||||
}
|
||||
}
|
||||
|
||||
class GenVarUse<T:Any>(val arg:T){
|
||||
fun varUse(a:GenVarUse<out T>, b:GenVarUse<in T>){
|
||||
//Should complile but do nothing
|
||||
}
|
||||
}
|
||||
|
||||
fun variCoType():GenVarOut<BaseData>{
|
||||
val compileVarOutSD:GenVarOut<SomeData> = GenVarOut(SomeData(890))
|
||||
val compileVarOut:GenVarOut<BaseData> = compileVarOutSD
|
||||
return compileVarOut
|
||||
}
|
||||
|
||||
fun variContraType():GenVarIn<SomeData>{
|
||||
val compileVariIn:GenVarIn<BaseData> = GenVarIn(SomeData(1890))
|
||||
val compileVariInSD:GenVarIn<SomeData> = compileVariIn
|
||||
return compileVariInSD
|
||||
}
|
||||
|
||||
open class GenBase<T:Any>(val t:T)
|
||||
class GenEx<TT:Any, T:Any>(val myT:T, baseT:TT):GenBase<TT>(baseT)
|
||||
class GenEx2<T:Any, S:Any>(val myT:S, baseT:T):GenBase<T>(baseT)
|
||||
|
||||
class GenExAny<TT:Any, T:Any>(val myT:T, baseT:TT):GenBase<Any>(baseT)
|
||||
|
||||
class GenNullability<T:Any>(val arg: T, val nArg:T?){
|
||||
fun asNullable():T? = arg
|
||||
val pAsNullable:T?
|
||||
get() = arg
|
||||
}
|
||||
|
||||
fun starGeneric(arg: GenNonNull<*>):Any{
|
||||
return arg.arg
|
||||
}
|
||||
|
||||
class GenOuter<A:Any>(val a:A){
|
||||
class GenNested<B:Any>(val b:B)
|
||||
inner class GenInner<C:Any>(val c:C, val aInner:A) {
|
||||
fun outerFun(): A = a
|
||||
val outerVal: A = a
|
||||
}
|
||||
}
|
||||
|
||||
class GenOuterSame<A:Any>(val a:A){
|
||||
class GenNestedSame<A:Any>(val a:A)
|
||||
inner class GenInnerSame<A:Any>(val a:A)
|
||||
class NestedNoGeneric()
|
||||
}
|
||||
|
||||
fun genInnerFunc(obj: GenOuter<SomeOtherData>.GenInner<SomeData>) {}
|
||||
fun <A:Any, C:Any> genInnerFuncAny(obj: GenOuter<A>.GenInner<C>){}
|
||||
|
||||
fun genInnerCreate(): GenOuter<SomeData>.GenInner<SomeOtherData> =
|
||||
GenOuter(SomeData(33)).GenInner(SomeOtherData("ppp"), SomeData(77))
|
||||
|
||||
class GenOuterBlank(val sd: SomeData) {
|
||||
inner class GenInner<T>(val arg: T){
|
||||
fun fromOuter(): SomeData = sd
|
||||
}
|
||||
}
|
||||
|
||||
class GenOuterBlank2<T>(val oarg: T) {
|
||||
inner class GenInner(val arg: T){
|
||||
fun fromOuter(): T = oarg
|
||||
}
|
||||
}
|
||||
|
||||
class GenOuterDeep<T>(val oarg: T) {
|
||||
inner class GenShallowInner(){
|
||||
inner class GenDeepInner(){
|
||||
fun o(): T = oarg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GenOuterDeep2() {
|
||||
inner class Before()
|
||||
inner class GenShallowOuterInner() {
|
||||
inner class GenShallowInner<T>() {
|
||||
inner class GenDeepInner()
|
||||
}
|
||||
}
|
||||
inner class After()
|
||||
}
|
||||
|
||||
class GenBothBlank(val a: SomeData) {
|
||||
inner class GenInner(val b: SomeOtherData)
|
||||
}
|
||||
|
||||
class GenClashId<id : Any, id_ : Any>(val arg: id, val arg2: id_){
|
||||
fun x(): Any = "Foo"
|
||||
}
|
||||
|
||||
class GenClashClass<ValuesGenericsClashingData : Any, NSArray : Any, int32_t : Any>(
|
||||
val arg: ValuesGenericsClashingData, val arg2: NSArray, val arg3: int32_t
|
||||
) {
|
||||
fun sd(): SomeData = SomeData(88)
|
||||
fun list(): List<SomeData> = listOf(SomeData(11), SomeData(22))
|
||||
fun int(): Int = 55
|
||||
fun clash(): ClashingData = ClashingData("aaa")
|
||||
}
|
||||
|
||||
data class ClashingData(val str: String)
|
||||
|
||||
class GenClashNames<ValuesGenericsClashnameClass, ValuesGenericsClashnameProtocol, ValuesGenericsClashnameParam, ValuesGenericsValues_genericsKt>() {
|
||||
fun foo() = ClashnameClass("nnn")
|
||||
|
||||
fun bar(): ClashnameProtocol = object : ClashnameProtocol{
|
||||
override val str = "qqq"
|
||||
}
|
||||
|
||||
fun baz(arg: ClashnameParam): Boolean {
|
||||
return arg.str == "meh"
|
||||
}
|
||||
}
|
||||
|
||||
class GenClashEx<ValuesGenericsClashnameClass>: ClashnameClass("ttt"){
|
||||
fun foo() = ClashnameClass("nnn")
|
||||
}
|
||||
|
||||
open class ClashnameClass(val str: String)
|
||||
interface ClashnameProtocol {
|
||||
val str: String
|
||||
}
|
||||
data class ClashnameParam(val str: String)
|
||||
|
||||
class GenExClash<ValuesGenericsSomeData:Any>(val myT:ValuesGenericsSomeData):GenBase<SomeData>(SomeData(55))
|
||||
|
||||
class SelfRef : GenBasic<SelfRef>()
|
||||
|
||||
open class GenBasic<T>()
|
||||
|
||||
//Extensions
|
||||
fun <T:Any> GenNonNull<T>.foo(): T = arg
|
||||
@@ -139,4 +139,4 @@ open class FrameworkTest : DefaultTask() {
|
||||
check(exitCode == 0, { "Compilation failed" })
|
||||
check(output.toFile().exists(), { "Compiler swiftc hasn't produced an output file: $output" })
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user