-
Notifications
You must be signed in to change notification settings - Fork 566
Kpgalligan/20190315/generics #2850
Changes from 1 commit
a89b4e2
3027193
f798121
92fbe6c
3f79a03
3363c82
fa5ddee
e71c10c
29e0e40
c030659
9c2b095
41342ac
366d648
56c52bb
1cf5783
f2aed6a
68ae5e4
7b2bc6d
3a0be1c
ea11c37
3f38a28
6a4111e
897d5c0
21458b2
d3d8744
8d3b3fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,7 +46,7 @@ internal class ObjCExportTranslatorImpl( | |
val mapper: ObjCExportMapper, | ||
val namer: ObjCExportNamer, | ||
val warningCollector: ObjCExportWarningCollector, | ||
val objcGenerics:Boolean | ||
val objcGenerics: Boolean | ||
) : ObjCExportTranslator { | ||
|
||
private val kotlinAnyName = namer.kotlinAnyName | ||
|
@@ -186,7 +186,7 @@ internal class ObjCExportTranslatorImpl( | |
) | ||
} | ||
ClassKind.ENUM_CLASS -> { | ||
val type = mapType(descriptor.defaultType, ReferenceBridge, NoneTypeParameterProvider) | ||
val type = mapType(descriptor.defaultType, ReferenceBridge, ObjCNoneExportScope) | ||
|
||
descriptor.enumEntries.forEach { | ||
val entryName = namer.getEnumEntrySelector(it) | ||
|
@@ -548,7 +548,7 @@ internal class ObjCExportTranslatorImpl( | |
method.allOverriddenDescriptors.forEach { exportThrown(it) } | ||
} | ||
|
||
private fun genericTypeParameterFactory(method: FunctionDescriptor) = if(objcGenerics){ClassParameterProvider(method)}else{NoneTypeParameterProvider} | ||
private fun genericTypeParameterFactory(method: FunctionDescriptor) = if(objcGenerics){ObjCClassExportScope(method)}else{ObjCNoneExportScope} | ||
|
||
private fun mapReturnType(returnBridge: MethodBridge.ReturnValue, method: FunctionDescriptor): ObjCType = when (returnBridge) { | ||
MethodBridge.ReturnValue.Void -> ObjCVoidType | ||
|
@@ -566,16 +566,16 @@ internal class ObjCExportTranslatorImpl( | |
MethodBridge.ReturnValue.Instance.FactoryResult -> ObjCInstanceType | ||
} | ||
|
||
internal fun mapReferenceType(kotlinType: KotlinType, typeParameterProvider: TypeParameterProvider): ObjCReferenceType = | ||
mapReferenceTypeIgnoringNullability(kotlinType, typeParameterProvider).let { | ||
internal fun mapReferenceType(kotlinType: KotlinType, objCExportScope: ObjCExportScope): ObjCReferenceType = | ||
mapReferenceTypeIgnoringNullability(kotlinType, objCExportScope).let { | ||
if (kotlinType.binaryRepresentationIsNullable()) { | ||
ObjCNullableReferenceType(it) | ||
} else { | ||
it | ||
} | ||
} | ||
|
||
internal fun mapReferenceTypeIgnoringNullability(kotlinType: KotlinType, typeParameterProvider: TypeParameterProvider): 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 -> | ||
|
@@ -606,11 +606,13 @@ internal class ObjCExportTranslatorImpl( | |
} | ||
|
||
mostSpecificMatches.firstOrNull()?.let { | ||
return it.mapper.mapType(it.type, this, typeParameterProvider) | ||
return it.mapper.mapType(it.type, this, objCExportScope) | ||
} | ||
|
||
if(objcGenerics && kotlinType.isTypeParameter() && typeParameterProvider.typeAvailable(kotlinType)){ | ||
return ObjCGenericType(kotlinType.typeParameterName()) | ||
if(objcGenerics && kotlinType.isTypeParameter()){ | ||
val typeParameterName = objCExportScope.getName(TypeUtils.getTypeParameterDescriptorOrNull(kotlinType)) | ||
if(typeParameterName != null) | ||
return ObjCGenericType(typeParameterName) | ||
} | ||
|
||
val classDescriptor = kotlinType.getErasedTypeClass() | ||
|
@@ -631,7 +633,7 @@ internal class ObjCExportTranslatorImpl( | |
} else { | ||
val typeArgs = if (objcGenerics) { | ||
kotlinType.arguments.map { typeProjection -> | ||
mapReferenceTypeIgnoringNullability(typeProjection.type, typeParameterProvider) | ||
mapReferenceTypeIgnoringNullability(typeProjection.type, objCExportScope) | ||
} | ||
} else { | ||
emptyList() | ||
|
@@ -668,8 +670,8 @@ internal class ObjCExportTranslatorImpl( | |
return ObjCIdType | ||
} | ||
|
||
private fun mapType(kotlinType: KotlinType, typeBridge: TypeBridge, typeParameterProvider: TypeParameterProvider): ObjCType = when (typeBridge) { | ||
ReferenceBridge -> mapReferenceType(kotlinType, typeParameterProvider) | ||
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") | ||
|
@@ -1019,49 +1021,36 @@ 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 TypeParameterProvider{ | ||
fun typeAvailable(kotlinType: KotlinType):Boolean | ||
interface ObjCExportScope{ | ||
fun getName(typeParameterDescriptor: TypeParameterDescriptor?): String? | ||
} | ||
|
||
internal class ClassParameterProvider(container:DeclarationDescriptor): TypeParameterProvider { | ||
constructor(method: FunctionDescriptor) : this(method.containingDeclaration) | ||
private val nameSet:Set<String> | ||
internal class ObjCClassExportScope constructor(method: FunctionDescriptor): ObjCExportScope { | ||
private val types:List<TypeParameterDescriptor> | ||
init { | ||
nameSet = mutableSetOf() | ||
if(container is ClassDescriptor && !container.isInterface) { | ||
container.declaredTypeParameters.forEach { | ||
nameSet.add(it.name.asString()) | ||
} | ||
val container = method.containingDeclaration | ||
types = if(container is ClassDescriptor && !container.isInterface) { | ||
container.declaredTypeParameters | ||
} else { | ||
emptyList() | ||
} | ||
} | ||
|
||
override fun typeAvailable(kotlinType: KotlinType): Boolean = | ||
nameSet.contains(kotlinType.typeParameterName()) | ||
override fun getName(typeParameterDescriptor: TypeParameterDescriptor?): String? = | ||
if(typeParameterDescriptor != null && types.contains(typeParameterDescriptor)){ | ||
typeParameterDescriptor.name.asString() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Somewhat clash-prone. class Clashing<id> {
fun foo(x: Any) {}
} Swift: Clashing<NSString>().foo(x: NSObject()) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, interesting. Xcode seems to be OK with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Sure!
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Went through a couple iterations on 2. I was hoping to delay writing names until the end as it looks like stubs delay rendering, but they can evaluate earlier, so that didn't work. Then I landed on the far easier method of checking type param names and objc class/protocol names, and appending underscores to either. So, in the following: class GenClashNames<ValuesGenericsClashnameClass, ValuesGenericsClashnameProtocol, ValuesGenericsValues_genericsKt>() {
fun foo(): Any = ClashnameClass("nnn")
}
data class ClashnameClass(val str: String)
interface ClashnameProtocol {
val str: String
} @interface ValuesGenericsGenClashNames<ValuesGenericsClashnameClass, The type param name is above, the class name written later is: @interface ValuesGenericsClashnameClass_ : KotlinBase This is rare, obviously, and swift names don't change. Let me know what you think. The alternatives seemed like a lot of work for little gain. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is ok for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, to clarify, "Wanted a more complete implementation". I'm just cleaning up tests and had planned to push last night, but wanted better test coverage and also want a full review that with the experimental flag off that nothing is changed, but the implementation is "complete" assuming I don't find anything with tests and review. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, thank you.
Yes, this is the detail I missed.
That's great! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Btw, what if type parameter is used in properties or methods? class Foo<ValuesGenericsBar> {
fun foo(p: ValuesGenericsBar) {}
fun bar(p: Bar) {}
}
class Bar -> __attribute__((objc_subclassing_restricted))
__attribute__((swift_name("Foo")))
@interface ValuesGenericsFoo<ValuesGenericsBar> : KotlinBase
// ...
- (void)fooP:(ValuesGenericsBar _Nullable)p __attribute__((swift_name("foo(p:)")));
- (void)barP:(ValuesGenericsBar *)p __attribute__((swift_name("bar(p:)")));
@end; I'm still not sure if it makes much sense to fix this now though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I'm not happy that I missed that. My testing got caught up in the outer class definition. Anyway, the RenderedStub will render the name as It is obviously unlikely that names would clash, and if we're expecting significant refactoring before generics would be standard, if ever, then fixing later is OK, as long as it's addressed at some point. Let me know if you want me to deal with it now. I'd be inclined to pass through class members in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it ok to stop at this point. |
||
} else { | ||
null | ||
} | ||
} | ||
|
||
internal object NoneTypeParameterProvider: TypeParameterProvider{ | ||
override fun typeAvailable(kotlinType: KotlinType): Boolean = false | ||
internal object ObjCNoneExportScope: ObjCExportScope{ | ||
override fun getName(typeParameterDescriptor: TypeParameterDescriptor?): String? = null | ||
} | ||
|
||
|
||
private fun Variance.objcDeclaration():String = when(this){ | ||
Variance.OUT_VARIANCE -> "__covariant " | ||
Variance.IN_VARIANCE -> "__contravariant " | ||
else -> "" | ||
} | ||
|
||
//To anybody reviewing this code, I don't know my way | ||
//around the Kotlin parsed type system too well. What | ||
//I'm looking for here is generic type name minus the '?' | ||
//I'm sure there's a much simpler way to get that | ||
internal fun KotlinType.typeParameterName(): String { | ||
return if (this.isMarkedNullable) { | ||
val ts = this.toString() | ||
if (ts.endsWith("?")) | ||
ts.substring(0, ts.length - 1) | ||
else | ||
ts | ||
} else { | ||
this.toString() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, this doesn't match
ObjCClassExportScope
:->
but
Supporting inner classes properly in
ObjCClassExportScope
would fix this.