[K/N] KT-53069: Guard against recursive generic type argument constraints during objc export
Merge-request: KT-MR-7872 Merged-by: Gleb Lukianets <Gleb.Lukianets@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
96e1ba045c
commit
a3e396e7e4
+23
-45
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.DataClassResolver
|
||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
|
||||
@@ -261,7 +260,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
|
||||
val name = referenceClass(classDescriptor).objCName
|
||||
val members = buildMembers {
|
||||
translatePlainMembers(declarations, ObjCNoneExportScope)
|
||||
translatePlainMembers(declarations, ObjCRootExportScope)
|
||||
}
|
||||
return ObjCInterfaceImpl(name, categoryName = "Extensions", members = members)
|
||||
}
|
||||
@@ -271,7 +270,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
|
||||
// TODO: stop inheriting KotlinBase.
|
||||
val members = buildMembers {
|
||||
translatePlainMembers(declarations, ObjCNoneExportScope)
|
||||
translatePlainMembers(declarations, ObjCRootExportScope)
|
||||
}
|
||||
return objCInterface(
|
||||
name,
|
||||
@@ -342,7 +341,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
?.forEach {
|
||||
val selector = getSelector(it)
|
||||
if (selector !in presentConstructors) {
|
||||
add { buildMethod(it, it, ObjCNoneExportScope, unavailable = true) }
|
||||
add { buildMethod(it, it, ObjCRootExportScope, unavailable = true) }
|
||||
|
||||
if (selector == "init") {
|
||||
add { ObjCMethod(null, false, ObjCInstanceType, listOf("new"), emptyList(), listOf("unavailable")) }
|
||||
@@ -383,7 +382,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
}
|
||||
ClassKind.ENUM_CLASS -> {
|
||||
val type = mapType(descriptor.defaultType, ReferenceBridge, ObjCNoneExportScope)
|
||||
val type = mapType(descriptor.defaultType, ReferenceBridge, ObjCRootExportScope)
|
||||
|
||||
descriptor.enumEntries.forEach {
|
||||
val entryName = namer.getEnumEntrySelector(it)
|
||||
@@ -434,9 +433,9 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
|
||||
internal fun createGenericExportScope(descriptor: ClassDescriptor): ObjCExportScope = if (objcGenerics) {
|
||||
ObjCClassExportScope(descriptor, namer)
|
||||
ObjCRootExportScope.deriveForClass(descriptor, namer)
|
||||
} else {
|
||||
ObjCNoneExportScope
|
||||
ObjCRootExportScope
|
||||
}
|
||||
|
||||
private fun buildThrowableAsErrorMethod(): ObjCMethod {
|
||||
@@ -560,7 +559,7 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
}
|
||||
|
||||
translatePlainMembers(methods, properties, ObjCNoneExportScope)
|
||||
translatePlainMembers(methods, properties, ObjCRootExportScope)
|
||||
}
|
||||
|
||||
private fun StubBuilder<Stub<*>>.translatePlainMembers(members: List<CallableMemberDescriptor>, objCExportScope: ObjCExportScope) {
|
||||
@@ -922,12 +921,18 @@ internal class ObjCExportTranslatorImpl(
|
||||
}
|
||||
|
||||
mostSpecificMatches.firstOrNull()?.let {
|
||||
return it.mapper.mapType(it.type, this, objCExportScope)
|
||||
try {
|
||||
return it.mapper.mapType(it.type, this, objCExportScope.deriveForType(it.type))
|
||||
} catch (e: ObjCExportScope.RecursionBreachException) {
|
||||
return ObjCIdType
|
||||
}
|
||||
}
|
||||
|
||||
if(objcGenerics && kotlinType.isTypeParameter()){
|
||||
val genericTypeUsage = objCExportScope.getGenericTypeUsage(TypeUtils.getTypeParameterDescriptorOrNull(kotlinType))
|
||||
if(genericTypeUsage != null)
|
||||
if (objcGenerics && kotlinType.isTypeParameter()) {
|
||||
val genericTypeUsage = objCExportScope
|
||||
.nearestScopeOfType<ObjCClassExportScope>()
|
||||
?.getGenericTypeUsage(TypeUtils.getTypeParameterDescriptorOrNull(kotlinType))
|
||||
if (genericTypeUsage != null)
|
||||
return genericTypeUsage
|
||||
}
|
||||
|
||||
@@ -1014,7 +1019,9 @@ internal class ObjCExportTranslatorImpl(
|
||||
} else {
|
||||
mapReferenceType(functionType.getReturnTypeFromFunctionType(), objCExportScope)
|
||||
},
|
||||
parameterTypes.map { mapReferenceType(it, objCExportScope) }
|
||||
parameterTypes.map {
|
||||
mapReferenceType(it, objCExportScope)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1369,36 +1376,8 @@ internal 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 getGenericTypeUsage(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeUsage?
|
||||
}
|
||||
|
||||
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 getGenericTypeUsage(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeUsage? {
|
||||
val localTypeParam = typeNames.firstOrNull {
|
||||
typeParameterDescriptor != null &&
|
||||
(it == typeParameterDescriptor || (it.isCapturedFromOuterDeclaration && it.original == typeParameterDescriptor))
|
||||
}
|
||||
|
||||
return if(localTypeParam == null) {
|
||||
null
|
||||
} else {
|
||||
ObjCGenericTypeParameterUsage(localTypeParam, namer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ObjCNoneExportScope: ObjCExportScope{
|
||||
override fun getGenericTypeUsage(typeParameterDescriptor: TypeParameterDescriptor?): ObjCGenericTypeUsage? = null
|
||||
}
|
||||
|
||||
private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? = descriptor.typeConstructor.supertypes.filter { !it.isInterface() }.firstOrNull()
|
||||
private fun computeSuperClassType(descriptor: ClassDescriptor): KotlinType? =
|
||||
descriptor.typeConstructor.supertypes.firstOrNull { !it.isInterface() }
|
||||
|
||||
internal const val OBJC_SUBCLASSING_RESTRICTED = "objc_subclassing_restricted"
|
||||
|
||||
@@ -1415,7 +1394,6 @@ internal fun ClassDescriptor.needCompanionObjectProperty(namer: ObjCExportNamer,
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun DeprecationInfo.toDeprecationAttribute(): String {
|
||||
val attribute = when (deprecationLevel) {
|
||||
DeprecationLevelValue.WARNING -> "deprecated"
|
||||
@@ -1457,4 +1435,4 @@ private fun quoteAsCStringLiteral(str: String): String = buildString {
|
||||
}
|
||||
}
|
||||
append('"')
|
||||
}
|
||||
}
|
||||
+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 = this.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
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ class ObjCExportTranslatorMobile internal constructor(private val delegate: ObjC
|
||||
|
||||
fun translateBaseFunction(descriptor: FunctionDescriptor): ObjCMethod {
|
||||
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor
|
||||
val scope = classDescriptor?.let { delegate.createGenericExportScope(it) } ?: ObjCNoneExportScope
|
||||
val scope = classDescriptor?.let { delegate.createGenericExportScope(it) } ?: ObjCRootExportScope
|
||||
return delegate.buildMethod(descriptor, descriptor, scope)
|
||||
}
|
||||
}
|
||||
@@ -1462,6 +1462,20 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
|
||||
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecList")))
|
||||
@interface KtRecList<T> : KtBase
|
||||
- (instancetype)initWithValue:(NSArray<id> *)value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) NSArray<id> *value __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecFunc")))
|
||||
@interface KtRecFunc<T> : KtBase
|
||||
- (instancetype)initWithValue:(id (^)(void))value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) id (^value)(void) __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((swift_name("RefinedClassA")))
|
||||
@interface KtRefinedClassA : KtBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
|
||||
@@ -1397,6 +1397,20 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
|
||||
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecList")))
|
||||
@interface KtRecList<T> : KtBase
|
||||
- (instancetype)initWithValue:(NSArray<id> *)value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) NSArray<id> *value __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecFunc")))
|
||||
@interface KtRecFunc<T> : KtBase
|
||||
- (instancetype)initWithValue:(id (^)(void))value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) id (^value)(void) __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((swift_name("RefinedClassA")))
|
||||
@interface KtRefinedClassA : KtBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
|
||||
@@ -1397,6 +1397,20 @@ __attribute__((swift_name("OverrideMethodsOfAnyKt")))
|
||||
+ (BOOL)testObj:(id)obj other:(id)other swift:(BOOL)swift error:(NSError * _Nullable * _Nullable)error __attribute__((swift_name("test(obj:other:swift:)")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecList")))
|
||||
@interface KtRecList : KtBase
|
||||
- (instancetype)initWithValue:(NSArray<id> *)value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) NSArray<id> *value __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("RecFunc")))
|
||||
@interface KtRecFunc : KtBase
|
||||
- (instancetype)initWithValue:(id (^)(void))value __attribute__((swift_name("init(value:)"))) __attribute__((objc_designated_initializer));
|
||||
@property (readonly) id (^value)(void) __attribute__((swift_name("value")));
|
||||
@end
|
||||
|
||||
__attribute__((swift_name("RefinedClassA")))
|
||||
@interface KtRefinedClassA : KtBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 recursiveGenericArguments
|
||||
|
||||
class RecList<T: List<T>>(val value: T)
|
||||
|
||||
class RecFunc<T : () -> T>(val value: T)
|
||||
@@ -0,0 +1,17 @@
|
||||
import Kt
|
||||
|
||||
#if !NO_GENERICS
|
||||
private func testRecursiveGenericArguments() throws {
|
||||
try assertTrue(type(of: RecList<NSArray>(value: []).value) == [Any].self)
|
||||
}
|
||||
#endif
|
||||
|
||||
class RecursiveGenericArgumentsTests : SimpleTestProvider {
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
#if !NO_GENERICS
|
||||
test("TestRecursiveGenericArguments", testRecursiveGenericArguments)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user