[ObjCExport] Support translation of enum classes
^KT-64953 Fixed
This commit is contained in:
committed by
Space Team
parent
2fb6815d7f
commit
a2d76d739c
+1
-1
@@ -25,7 +25,7 @@ context(KtAnalysisSession)
|
|||||||
internal fun KtNamedClassOrObjectSymbol.getInlineTargetTypeOrNull(): KtType? {
|
internal fun KtNamedClassOrObjectSymbol.getInlineTargetTypeOrNull(): KtType? {
|
||||||
if (!isInlineIncludingKotlinNativeSpecialClasses()) return null
|
if (!isInlineIncludingKotlinNativeSpecialClasses()) return null
|
||||||
|
|
||||||
val constructor = getMemberScope().getConstructors()
|
val constructor = getDeclaredMemberScope().getConstructors()
|
||||||
.find { constructor -> constructor.isPrimary && constructor.valueParameters.size == 1 }
|
.find { constructor -> constructor.isPrimary && constructor.valueParameters.size == 1 }
|
||||||
?: return null
|
?: return null
|
||||||
|
|
||||||
|
|||||||
+5
-11
@@ -1,10 +1,7 @@
|
|||||||
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionLikeSymbol
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType
|
import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType
|
||||||
import org.jetbrains.kotlin.analysis.api.types.KtType
|
import org.jetbrains.kotlin.analysis.api.types.KtType
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanPrimitiveType
|
import org.jetbrains.kotlin.backend.konan.KonanPrimitiveType
|
||||||
@@ -35,7 +32,7 @@ internal fun KtFunctionLikeSymbol.getFunctionMethodBridge(): MethodBridge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtPropertySymbol.getPropertyMethodBridge(): MethodBridge {
|
internal fun KtVariableLikeSymbol.getPropertyMethodBridge(): MethodBridge {
|
||||||
return MethodBridge(
|
return MethodBridge(
|
||||||
bridgeReturnType(),
|
bridgeReturnType(),
|
||||||
receiverType,
|
receiverType,
|
||||||
@@ -143,12 +140,9 @@ private fun KtCallableSymbol.bridgeReturnType(): MethodBridge.ReturnValue {
|
|||||||
return MethodBridge.ReturnValue.Suspend
|
return MethodBridge.ReturnValue.Suspend
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: handle hashCode
|
if (isHashCode) {
|
||||||
// descriptor.containingDeclaration.let { it is ClassDescriptor && KotlinBuiltIns.isAny(it) } &&
|
return MethodBridge.ReturnValue.HashCode
|
||||||
// descriptor.name.asString() == "hashCode" -> {
|
}
|
||||||
// assert(!convertExceptionsToErrors)
|
|
||||||
// MethodBridge.ReturnValue.HashCode
|
|
||||||
// }
|
|
||||||
|
|
||||||
//TODO: handle getter
|
//TODO: handle getter
|
||||||
// descriptor is PropertyGetterDescriptor -> {
|
// descriptor is PropertyGetterDescriptor -> {
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return `true` when the declaration is considered a `fake override`.
|
||||||
|
* K2 will differentiate fake-overrides into 'intersection override' and 'substitution override'
|
||||||
|
* `false` if the symbol is not a fake override
|
||||||
|
*
|
||||||
|
* See:
|
||||||
|
* - [org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal]
|
||||||
|
* - [KtSymbolOrigin.INTERSECTION_OVERRIDE]
|
||||||
|
* - [KtSymbolOrigin.SUBSTITUTION_OVERRIDE]
|
||||||
|
*/
|
||||||
|
internal val KtSymbol.isFakeOverride: Boolean
|
||||||
|
get() {
|
||||||
|
val origin = this.origin
|
||||||
|
if (origin == KtSymbolOrigin.INTERSECTION_OVERRIDE) return true
|
||||||
|
if (origin == KtSymbolOrigin.SUBSTITUTION_OVERRIDE) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.objcexport.analysisApiUtils
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.name.CallableId
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
|
||||||
|
private val hashCodeCallableId = CallableId(StandardClassIds.Any, Name.identifier("hashCode"))
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
internal val KtCallableSymbol.isHashCode: Boolean
|
||||||
|
get() = this.callableIdIfNonLocal == hashCodeCallableId ||
|
||||||
|
getAllOverriddenSymbols().any { overriddenSymbol -> overriddenSymbol.callableIdIfNonLocal == hashCodeCallableId }
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
package org.jetbrains.kotlin.objcexport.analysisApiUtils
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
|
||||||
|
|
||||||
context(KtAnalysisSession)
|
|
||||||
internal fun KtClassOrObjectSymbol.getAllMembers(): List<KtSymbol> {
|
|
||||||
return getMemberScope()
|
|
||||||
.getAllSymbols()
|
|
||||||
.sortedBy { sortMembers(it) }
|
|
||||||
.filter { member -> member.isVisibleInObjC() }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns members explicitly defined in the symbol. All super methods are excluded.
|
|
||||||
*
|
|
||||||
* Also handles edge case with covariant method overwrite:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* interface A {
|
|
||||||
* fun hello(): Any
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* interface B: A {
|
|
||||||
* override fun hello(): String
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* B.getBaseMembers().isEmpty() == true
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* More context is around [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapperKt.isBaseMethod]
|
|
||||||
*/
|
|
||||||
context(KtAnalysisSession)
|
|
||||||
internal fun KtClassOrObjectSymbol.getDeclaredMembers(): List<KtSymbol> {
|
|
||||||
return getDeclaredMemberScope()
|
|
||||||
.getAllSymbols()
|
|
||||||
.sortedBy { sortMembers(it) }
|
|
||||||
.filter { member ->
|
|
||||||
member.isVisibleInObjC() && (member !is KtCallableSymbol || member.getAllOverriddenSymbols().isEmpty())
|
|
||||||
}
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temp workaround of [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorKt.makeMethodsOrderStable]
|
|
||||||
*/
|
|
||||||
private fun sortMembers(it: KtDeclarationSymbol) = when (it) {
|
|
||||||
is KtConstructorSymbol -> 0
|
|
||||||
is KtFunctionSymbol -> 1
|
|
||||||
else -> 2
|
|
||||||
}
|
|
||||||
+3
-3
@@ -6,12 +6,12 @@
|
|||||||
package org.jetbrains.kotlin.objcexport
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtVariableLikeSymbol
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportPropertyName
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportPropertyName
|
||||||
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession)
|
||||||
fun KtPropertySymbol.getObjCPropertyName(): ObjCExportPropertyName {
|
fun KtVariableLikeSymbol.getObjCPropertyName(): ObjCExportPropertyName {
|
||||||
val resolveObjCNameAnnotation = resolveObjCNameAnnotation()
|
val resolveObjCNameAnnotation = resolveObjCNameAnnotation()
|
||||||
|
|
||||||
return ObjCExportPropertyName(
|
return ObjCExportPropertyName(
|
||||||
|
|||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtEnumEntrySymbol
|
||||||
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
internal fun KtClassOrObjectSymbol.translateEnumMembers(): List<ObjCExportStub> {
|
||||||
|
if (classKind != KtClassKind.ENUM_CLASS) return emptyList()
|
||||||
|
return getEnumEntries() + listOf(getEnumValuesMethod(), getEnumEntriesProperty())
|
||||||
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
private fun KtClassOrObjectSymbol.getEnumEntries(): List<ObjCProperty> {
|
||||||
|
val staticMembers = this.getStaticDeclaredMemberScope().getCallableSymbols().toList()
|
||||||
|
return staticMembers.filterIsInstance<KtEnumEntrySymbol>().map { entry ->
|
||||||
|
|
||||||
|
val entryName = entry.getEnumEntryName(false)
|
||||||
|
val swiftName = entry.getEnumEntryName(true)
|
||||||
|
ObjCProperty(
|
||||||
|
name = entryName,
|
||||||
|
comment = null,
|
||||||
|
origin = null,
|
||||||
|
type = entry.returnType.mapToReferenceTypeIgnoringNullability(),
|
||||||
|
propertyAttributes = listOf("class", "readonly"),
|
||||||
|
declarationAttributes = listOf(swiftNameAttribute(swiftName))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.buildEnumValuesMethod]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
private fun KtClassOrObjectSymbol.getEnumValuesMethod(): ObjCMethod {
|
||||||
|
val valuesFunctionSymbol = getStaticMemberScope().getCallableSymbols(Name.identifier("values")).firstOrNull()
|
||||||
|
return ObjCMethod(
|
||||||
|
comment = null,
|
||||||
|
isInstanceMethod = false,
|
||||||
|
returnType = valuesFunctionSymbol?.returnType?.translateToObjCReferenceType() ?: ObjCIdType,
|
||||||
|
selectors = listOf("values"),
|
||||||
|
parameters = emptyList(),
|
||||||
|
attributes = listOf(swiftNameAttribute("values()")),
|
||||||
|
origin = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.buildEnumEntriesProperty]
|
||||||
|
*/
|
||||||
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
|
private fun KtClassOrObjectSymbol.getEnumEntriesProperty(): ObjCProperty {
|
||||||
|
val entriesSymbol = getStaticMemberScope().getCallableSymbols(Name.identifier("entries")).firstOrNull()
|
||||||
|
|
||||||
|
return ObjCProperty(
|
||||||
|
name = "entries",
|
||||||
|
comment = null,
|
||||||
|
type = entriesSymbol?.returnType?.translateToObjCReferenceType() ?: ObjCIdType,
|
||||||
|
propertyAttributes = listOf("class", "readonly"),
|
||||||
|
declarationAttributes = listOf(swiftNameAttribute("entries")),
|
||||||
|
origin = null,
|
||||||
|
setterName = null,
|
||||||
|
getterName = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
context(KtAnalysisSession)
|
||||||
|
private fun KtEnumEntrySymbol.getEnumEntryName(forSwift: Boolean): String {
|
||||||
|
|
||||||
|
val propertyName: String = getObjCPropertyName().run {
|
||||||
|
when {
|
||||||
|
forSwift -> this.swiftName
|
||||||
|
else -> this.objCName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOO_BAR_BAZ -> fooBarBaz:
|
||||||
|
val name = propertyName.split('_').mapIndexed { index, s ->
|
||||||
|
val lower = s.lowercase()
|
||||||
|
if (index == 0) lower else lower.replaceFirstChar(Char::uppercaseChar)
|
||||||
|
}.joinToString("").toIdentifier()
|
||||||
|
return name
|
||||||
|
}
|
||||||
+30
-9
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.nameOrAnonymous
|
|||||||
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasExportForCompilerAnnotation
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
@@ -27,14 +27,27 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
|||||||
|
|
||||||
val superClass = translateSuperClass()
|
val superClass = translateSuperClass()
|
||||||
val superProtocols: List<String> = superProtocols()
|
val superProtocols: List<String> = superProtocols()
|
||||||
val constructors = getMemberScope().getConstructors().filter { !it.hasExportForCompilerAnnotation }
|
|
||||||
|
|
||||||
val members = getMemberScope().getCallableSymbols().plus(constructors)
|
val members = buildList<ObjCExportStub> {
|
||||||
.sortedWith(StableCallableOrder)
|
/* The order of members tries to replicate the K1 implementation explicitly */
|
||||||
.flatMap { it.translateToObjCExportStubs() }
|
this += translateToObjCConstructors()
|
||||||
.toMutableList()
|
|
||||||
|
|
||||||
if (needsCompanionProperty) members.add(buildCompanionProperty())
|
if (needsCompanionProperty) {
|
||||||
|
this += buildCompanionProperty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special case so far: Just for 'Enum' we actually want to add this clone method to match K1 */
|
||||||
|
if (classIdIfNonLocal == StandardClassIds.Enum) {
|
||||||
|
this += cloneMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
this += getDeclaredMemberScope().getCallableSymbols().sortedWith(StableCallableOrder)
|
||||||
|
.mapNotNull { it.translateToObjCExportStub() }
|
||||||
|
|
||||||
|
if (classKind == KtClassKind.ENUM_CLASS) {
|
||||||
|
this += translateEnumMembers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val categoryName: String? = null
|
val categoryName: String? = null
|
||||||
|
|
||||||
@@ -59,10 +72,18 @@ fun KtClassOrObjectSymbol.translateToObjCClass(): ObjCClass? {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtNonErrorClassType.getSuperClassName(): ObjCExportClassOrProtocolName? {
|
internal fun KtNonErrorClassType.getSuperClassName(): ObjCExportClassOrProtocolName? {
|
||||||
val classSymbol = expandedClassSymbol ?: return null
|
val classSymbol = expandedClassSymbol ?: return null
|
||||||
return classSymbol.getObjCClassOrProtocolName()
|
return classSymbol.getObjCClassOrProtocolName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val cloneMethod = ObjCMethod(
|
||||||
|
selectors = listOf("clone"),
|
||||||
|
comment = ObjCComment(contentLines = listOf("@note This method has protected visibility in Kotlin source and is intended only for use by subclasses.")),
|
||||||
|
origin = null,
|
||||||
|
returnType = ObjCIdType,
|
||||||
|
parameters = emptyList(),
|
||||||
|
isInstanceMethod = true,
|
||||||
|
attributes = listOf(swiftNameAttribute("clone()"))
|
||||||
|
)
|
||||||
|
|||||||
+25
-7
@@ -1,23 +1,31 @@
|
|||||||
package org.jetbrains.kotlin.objcexport
|
package org.jetbrains.kotlin.objcexport
|
||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.arrayTypes
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInstanceType
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCInstanceType
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCMethod
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCMethod
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCParameter
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCParameter
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCRawType
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCRawType
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isArrayConstructor
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.getSuperClassSymbolNotAny
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.hasExportForCompilerAnnotation
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isVisibleInObjC
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtConstructorSymbol.translateToObjCConstructors(): List<ObjCMethod> {
|
fun KtClassOrObjectSymbol.translateToObjCConstructors(): List<ObjCMethod> {
|
||||||
if (!isVisibleInObjC()) return emptyList()
|
|
||||||
val result = mutableListOf<ObjCMethod>()
|
val result = mutableListOf<ObjCMethod>()
|
||||||
|
|
||||||
result.add(buildObjCMethod())
|
/* Translate declared constructors */
|
||||||
|
result += getDeclaredMemberScope().getConstructors()
|
||||||
if (isArrayConstructor) {
|
.filter { !it.hasExportForCompilerAnnotation }
|
||||||
|
.filter { it.isVisibleInObjC() }
|
||||||
|
.map { it.buildObjCMethod() }
|
||||||
|
|
||||||
|
/* Create special 'alloc' constructors */
|
||||||
|
if (this.classIdIfNonLocal?.asFqNameString() in arrayTypes ||
|
||||||
|
classKind.isObject || classKind == KtClassKind.ENUM_CLASS
|
||||||
|
) {
|
||||||
result.add(
|
result.add(
|
||||||
ObjCMethod(
|
ObjCMethod(
|
||||||
comment = null,
|
comment = null,
|
||||||
@@ -43,6 +51,16 @@ fun KtConstructorSymbol.translateToObjCConstructors(): List<ObjCMethod> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide "unimplemented" super constructors:
|
||||||
|
getSuperClassSymbolNotAny()?.getMemberScope()?.getConstructors().orEmpty()
|
||||||
|
.filter { it.isVisibleInObjC() }
|
||||||
|
.forEach { superClassConstructor ->
|
||||||
|
val translatedSuperClassConstructor = superClassConstructor.buildObjCMethod(unavailable = true)
|
||||||
|
if (result.none { it.name == translatedSuperClassConstructor.name }) {
|
||||||
|
result.add(translatedSuperClassConstructor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result.size == 1 && result.first().name == "init") {
|
if (result.size == 1 && result.first().name == "init") {
|
||||||
result.add(
|
result.add(
|
||||||
ObjCMethod(
|
ObjCMethod(
|
||||||
|
|||||||
+4
-6
@@ -2,17 +2,15 @@ package org.jetbrains.kotlin.objcexport
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtConstructorSymbol
|
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
import org.jetbrains.kotlin.analysis.api.symbols.KtPropertySymbol
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportStub
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtCallableSymbol.translateToObjCExportStubs(): List<ObjCExportStub> {
|
internal fun KtCallableSymbol.translateToObjCExportStub(): ObjCExportStub? {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is KtConstructorSymbol -> translateToObjCConstructors()
|
is KtPropertySymbol -> translateToObjCProperty()
|
||||||
is KtPropertySymbol -> listOfNotNull(translateToObjCProperty())
|
is KtFunctionSymbol -> translateToObjCMethod()
|
||||||
is KtFunctionSymbol -> listOfNotNull(translateToObjCMethod())
|
else -> null
|
||||||
else -> emptyList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -61,7 +61,7 @@ fun translateToObjCHeader(files: List<KtFile>): ObjCHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
symbol.getSuperClassSymbolNotAny()?.let { superClassSymbol ->
|
symbol.getSuperClassSymbolNotAny()?.let { superClassSymbol ->
|
||||||
process(superClassSymbol, true)
|
result.addAll(process(superClassSymbol, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
result.add(translatedObjCClassOrProtocol)
|
result.add(translatedObjCClassOrProtocol)
|
||||||
|
|||||||
+12
-15
@@ -6,12 +6,14 @@ import com.intellij.openapi.util.io.FileUtil
|
|||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos
|
import org.jetbrains.kotlin.analysis.api.annotations.annotationInfos
|
||||||
import org.jetbrains.kotlin.analysis.api.symbols.*
|
import org.jetbrains.kotlin.analysis.api.symbols.*
|
||||||
|
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
|
||||||
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
import org.jetbrains.kotlin.backend.konan.InternalKotlinNativeApi
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.NameUtils
|
import org.jetbrains.kotlin.name.NameUtils
|
||||||
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors
|
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSelectors
|
||||||
|
import org.jetbrains.kotlin.objcexport.Predefined.anyMethodSwiftNames
|
||||||
import org.jetbrains.kotlin.objcexport.analysisApiUtils.*
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
@@ -19,12 +21,10 @@ internal val KtCallableSymbol.isConstructor: Boolean
|
|||||||
get() = this is KtConstructorSymbol
|
get() = this is KtConstructorSymbol
|
||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtFunctionSymbol.translateToObjCMethod(
|
fun KtFunctionSymbol.translateToObjCMethod(): ObjCMethod? {
|
||||||
): ObjCMethod? {
|
|
||||||
if (!isVisibleInObjC()) return null
|
if (!isVisibleInObjC()) return null
|
||||||
if (anyMethodSelectors.containsKey(this.name)) return null //temp, find replacement for org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.isReal
|
if (isFakeOverride) return null
|
||||||
if (isClone) return null
|
if (isClone) return null
|
||||||
|
|
||||||
return buildObjCMethod()
|
return buildObjCMethod()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +110,10 @@ internal fun KtCallableSymbol.isRefinedInSwift(): Boolean = when {
|
|||||||
|
|
||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): String {
|
internal fun KtFunctionLikeSymbol.getSwiftName(methodBridge: MethodBridge): String {
|
||||||
|
|
||||||
//assert(mapper.isBaseMethod(method)) //TODO: implement isBaseMethod
|
//assert(mapper.isBaseMethod(method)) //TODO: implement isBaseMethod
|
||||||
getPredefined(this, Predefined.anyMethodSwiftNames)?.let { return it }
|
if (this is KtNamedSymbol) {
|
||||||
|
anyMethodSwiftNames[name]?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
val parameters = methodBridge.valueParametersAssociated(this)
|
val parameters = methodBridge.valueParametersAssociated(this)
|
||||||
val method = this
|
val method = this
|
||||||
@@ -172,12 +173,6 @@ private fun splitSelector(selector: String): List<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Not implemented [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getPredefined]
|
|
||||||
*/
|
|
||||||
private fun <T : Any> getPredefined(method: KtFunctionLikeSymbol, predefinedForAny: Map<Name, T>): T? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getSelector]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportNamerImpl.getSelector]
|
||||||
@@ -185,7 +180,9 @@ private fun <T : Any> getPredefined(method: KtFunctionLikeSymbol, predefinedForA
|
|||||||
context(KtAnalysisSession, KtObjCExportSession)
|
context(KtAnalysisSession, KtObjCExportSession)
|
||||||
fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
fun KtFunctionLikeSymbol.getSelector(methodBridge: MethodBridge): String {
|
||||||
|
|
||||||
getPredefined(this, anyMethodSelectors)?.let { return it }
|
if (this is KtNamedSymbol) {
|
||||||
|
anyMethodSelectors[this.name]?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
val parameters = methodBridge.valueParametersAssociated(this)
|
val parameters = methodBridge.valueParametersAssociated(this)
|
||||||
|
|
||||||
@@ -275,7 +272,7 @@ fun MethodBridge.valueParametersAssociated(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
private fun String.startsWithWords(words: String) = this.startsWith(words) &&
|
||||||
(this.length == words.length || !this[words.length].isLowerCase())
|
(this.length == words.length || !this[words.length].isLowerCase())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReturnType]
|
* [org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportTranslatorImpl.mapReturnType]
|
||||||
@@ -295,7 +292,7 @@ fun KtFunctionLikeSymbol.mapReturnType(returnBridge: MethodBridge.ReturnValue):
|
|||||||
if (!returnBridge.successMayBeZero) {
|
if (!returnBridge.successMayBeZero) {
|
||||||
check(
|
check(
|
||||||
successReturnType is ObjCNonNullReferenceType
|
successReturnType is ObjCNonNullReferenceType
|
||||||
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
|| (successReturnType is ObjCPointerType && !successReturnType.nullable)
|
||||||
) {
|
) {
|
||||||
"Unexpected return type: $successReturnType in $this"
|
"Unexpected return type: $successReturnType in $this"
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-14
@@ -23,14 +23,14 @@ fun KtClassOrObjectSymbol.translateToObjCObject(): ObjCClass? {
|
|||||||
val superProtocols: List<String> = superProtocols()
|
val superProtocols: List<String> = superProtocols()
|
||||||
val categoryName: String? = null
|
val categoryName: String? = null
|
||||||
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
val generics: List<ObjCGenericTypeDeclaration> = emptyList()
|
||||||
val objectMembers = getDefaultMembers().toMutableList()
|
|
||||||
|
|
||||||
val superClass = translateSuperClass()
|
val superClass = translateSuperClass()
|
||||||
|
|
||||||
getMemberScope().getCallableSymbols()
|
val objectMembers = mutableListOf<ObjCExportStub>()
|
||||||
|
objectMembers += translateToObjCConstructors()
|
||||||
|
objectMembers += getDefaultMembers()
|
||||||
|
objectMembers += getDeclaredMemberScope().getCallableSymbols()
|
||||||
.sortedWith(StableCallableOrder)
|
.sortedWith(StableCallableOrder)
|
||||||
.flatMap { it.translateToObjCExportStubs() }
|
.mapNotNull { it.translateToObjCExportStub() }
|
||||||
.forEach { objectMembers.add(it) }
|
|
||||||
|
|
||||||
return ObjCInterfaceImpl(
|
return ObjCInterfaceImpl(
|
||||||
name = name.objCName,
|
name = name.objCName,
|
||||||
@@ -50,15 +50,6 @@ context(KtAnalysisSession, KtObjCExportSession)
|
|||||||
private fun KtClassOrObjectSymbol.getDefaultMembers(): List<ObjCExportStub> {
|
private fun KtClassOrObjectSymbol.getDefaultMembers(): List<ObjCExportStub> {
|
||||||
|
|
||||||
val result = mutableListOf<ObjCExportStub>()
|
val result = mutableListOf<ObjCExportStub>()
|
||||||
val allocWithZoneParameter = ObjCParameter("zone", null, ObjCRawType("struct _NSZone *"), null)
|
|
||||||
|
|
||||||
result.add(
|
|
||||||
ObjCMethod(null, null, false, ObjCInstanceType, listOf("alloc"), emptyList(), listOf("unavailable"))
|
|
||||||
)
|
|
||||||
|
|
||||||
result.add(
|
|
||||||
ObjCMethod(null, null, false, ObjCInstanceType, listOf("allocWithZone:"), listOf(allocWithZoneParameter), listOf("unavailable"))
|
|
||||||
)
|
|
||||||
|
|
||||||
result.add(
|
result.add(
|
||||||
ObjCMethod(
|
ObjCMethod(
|
||||||
|
|||||||
+2
-2
@@ -24,9 +24,9 @@ fun KtClassOrObjectSymbol.translateToObjCProtocol(): ObjCProtocol? {
|
|||||||
// TODO: Check error type!
|
// TODO: Check error type!
|
||||||
val name = getObjCClassOrProtocolName()
|
val name = getObjCClassOrProtocolName()
|
||||||
|
|
||||||
val members = getMemberScope().getCallableSymbols()
|
val members = getDeclaredMemberScope().getCallableSymbols()
|
||||||
.sortedWith(StableCallableOrder)
|
.sortedWith(StableCallableOrder)
|
||||||
.flatMap { it.translateToObjCExportStubs() }
|
.mapNotNull { it.translateToObjCExportStub() }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
val comment: ObjCComment? = annotationsList.translateToObjCComment()
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ context(KtAnalysisSession, KtObjCExportSession)
|
|||||||
fun KtFileSymbol.translateToObjCTopLevelInterfaceFileFacade(): ObjCInterface? {
|
fun KtFileSymbol.translateToObjCTopLevelInterfaceFileFacade(): ObjCInterface? {
|
||||||
val topLevelCallableStubs = getFileScope().getCallableSymbols()
|
val topLevelCallableStubs = getFileScope().getCallableSymbols()
|
||||||
.sortedWith(StableCallableOrder)
|
.sortedWith(StableCallableOrder)
|
||||||
.flatMap { callableSymbol -> callableSymbol.translateToObjCExportStubs() }
|
.mapNotNull { callableSymbol -> callableSymbol.translateToObjCExportStub() }
|
||||||
.toList()
|
.toList()
|
||||||
/* If there are no top level functions or properties, we do not need to export a file facade */
|
/* If there are no top level functions or properties, we do not need to export a file facade */
|
||||||
.ifEmpty { return null }
|
.ifEmpty { return null }
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.objcexport.tests
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.analysis.api.analyze
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
import org.jetbrains.kotlin.objcexport.analysisApiUtils.isHashCode
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.InlineSourceCodeAnalysis
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getClassOrFail
|
||||||
|
import org.jetbrains.kotlin.objcexport.testUtils.getFunctionOrFail
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class IsHashCodeTest(
|
||||||
|
private val inlineSourceCodeAnalysis: InlineSourceCodeAnalysis,
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - Any - hashCode`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile("")
|
||||||
|
analyze(file) {
|
||||||
|
val anySymbol = getClassOrObjectSymbolByClassId(StandardClassIds.Any) ?: error("Missing kotlin.Any")
|
||||||
|
val hashCodeSymbol = anySymbol.getFunctionOrFail("hashCode")
|
||||||
|
assertTrue(hashCodeSymbol.isHashCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - data class hashCode`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile("data class Foo(val x: Int)")
|
||||||
|
analyze(file) {
|
||||||
|
val fooSymbol = file.getClassOrFail("Foo")
|
||||||
|
val hashCodeSymbol = fooSymbol.getFunctionOrFail("hashCode")
|
||||||
|
assertTrue(hashCodeSymbol.isHashCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - overridden hashCode`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile(
|
||||||
|
"""
|
||||||
|
class Foo {
|
||||||
|
override fun hashCode() = 42
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
analyze(file) {
|
||||||
|
val hashCodeSymbol = file.getClassOrFail("Foo").getFunctionOrFail("hashCode")
|
||||||
|
assertTrue(hashCodeSymbol.isHashCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test - Any equals`() {
|
||||||
|
val file = inlineSourceCodeAnalysis.createKtFile("")
|
||||||
|
analyze(file) {
|
||||||
|
val anySymbol = getClassOrObjectSymbolByClassId(StandardClassIds.Any) ?: error("Missing kotlin.Any")
|
||||||
|
val hashCodeSymbol = anySymbol.getFunctionOrFail("equals")
|
||||||
|
assertFalse(hashCodeSymbol.isHashCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-1
@@ -44,7 +44,6 @@ class ObjCExportHeaderGeneratorTest(private val generator: HeaderGenerator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TodoAnalysisApi
|
|
||||||
fun `test - simpleEnumClass`() {
|
fun `test - simpleEnumClass`() {
|
||||||
doTest(headersTestDataDir.resolve("simpleEnumClass"))
|
doTest(headersTestDataDir.resolve("simpleEnumClass"))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user