Expose enum .values function when generating Obj-C header
This commit is contained in:
committed by
Stanislav Erokhin
parent
740d50ca09
commit
58855b9eff
+18
@@ -1229,6 +1229,9 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
||||
is ObjCGetterForKotlinEnumEntry -> {
|
||||
classAdapters += createEnumEntryAdapter(it.irEnumEntrySymbol.owner)
|
||||
}
|
||||
is ObjCClassMethodForKotlinEnumValues -> {
|
||||
classAdapters += createEnumValuesAdapter(it.valuesFunctionSymbol.owner, it.selector)
|
||||
}
|
||||
is ObjCMethodForKotlinMethod -> {} // Handled below.
|
||||
}.let {} // Force exhaustive.
|
||||
}
|
||||
@@ -1473,6 +1476,21 @@ private fun ObjCExportCodeGenerator.createEnumEntryAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun ObjCExportCodeGenerator.createEnumValuesAdapter(
|
||||
valuesFunction: IrFunction,
|
||||
selector: String
|
||||
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
|
||||
val methodBridge = MethodBridge(
|
||||
returnBridge = MethodBridge.ReturnValue.Mapped(ReferenceBridge),
|
||||
receiver = MethodBridgeReceiver.Static,
|
||||
valueParameters = emptyList()
|
||||
)
|
||||
|
||||
val imp = generateObjCImp(valuesFunction, valuesFunction, methodBridge, isVirtual = false)
|
||||
|
||||
return objCToKotlinMethodAdapter(selector, methodBridge, imp)
|
||||
}
|
||||
|
||||
private fun List<CallableMemberDescriptor>.toMethods(): List<FunctionDescriptor> = this.flatMap {
|
||||
when (it) {
|
||||
is PropertyDescriptor -> listOfNotNull(it.getter, it.setter)
|
||||
|
||||
+13
-4
@@ -10,10 +10,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.enumEntries
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
|
||||
@@ -67,6 +64,13 @@ internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): Obj
|
||||
descriptor.enumEntries.mapTo(methods) {
|
||||
ObjCGetterForKotlinEnumEntry(symbolTable.referenceEnumEntry(it))
|
||||
}
|
||||
|
||||
descriptor.getEnumValuesFunctionDescriptor()?.let {
|
||||
methods += ObjCClassMethodForKotlinEnumValues(
|
||||
symbolTable.referenceSimpleFunction(it),
|
||||
namer.getEnumValuesSelector(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val categoryMethods = categoryMembers[descriptor].orEmpty().toObjCMethods()
|
||||
@@ -100,6 +104,11 @@ internal class ObjCFactoryMethodForKotlinArrayConstructor(
|
||||
|
||||
internal class ObjCGetterForKotlinEnumEntry(val irEnumEntrySymbol: IrEnumEntrySymbol) : ObjCMethodSpec()
|
||||
|
||||
internal class ObjCClassMethodForKotlinEnumValues(
|
||||
val valuesFunctionSymbol: IrFunctionSymbol,
|
||||
val selector: String
|
||||
) : ObjCMethodSpec()
|
||||
|
||||
internal sealed class ObjCTypeSpec(val binaryName: String)
|
||||
|
||||
internal sealed class ObjCTypeForKotlinType(
|
||||
|
||||
+23
@@ -365,6 +365,14 @@ internal class ObjCExportTranslatorImpl(
|
||||
+ObjCProperty(entryName, it, type, listOf("class", "readonly"),
|
||||
declarationAttributes = listOf(swiftNameAttribute(entryName)))
|
||||
}
|
||||
|
||||
// Note: it is possible to support this function through a common machinery,
|
||||
// but it is very special (static and synthetic), so apparently it is much easier
|
||||
// to keep this ad hoc here than to add special cases to the most complicated parts
|
||||
// of the machinery.
|
||||
descriptor.getEnumValuesFunctionDescriptor()?.let { enumValues ->
|
||||
+buildEnumValuesMethod(enumValues, genericExportScope)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Nothing special.
|
||||
@@ -404,6 +412,21 @@ internal class ObjCExportTranslatorImpl(
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildEnumValuesMethod(
|
||||
enumValues: SimpleFunctionDescriptor,
|
||||
genericExportScope: ObjCExportScope
|
||||
): ObjCMethod {
|
||||
val selector = namer.getEnumValuesSelector(enumValues)
|
||||
return ObjCMethod(
|
||||
enumValues,
|
||||
isInstanceMethod = false,
|
||||
returnType = mapReferenceType(enumValues.returnType!!, genericExportScope),
|
||||
selectors = splitSelector(selector),
|
||||
parameters = emptyList(),
|
||||
attributes = listOf(swiftNameAttribute("$selector()"))
|
||||
)
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.getExposedMembers(): List<CallableMemberDescriptor> =
|
||||
this.unsubstitutedMemberScope.getContributedDescriptors()
|
||||
.asSequence()
|
||||
|
||||
+10
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.builtins.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.deprecation.Deprecation
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
|
||||
@@ -181,6 +182,15 @@ internal fun ObjCExportMapper.isTopLevel(descriptor: CallableMemberDescriptor):
|
||||
internal fun ObjCExportMapper.isObjCProperty(property: PropertyDescriptor): Boolean =
|
||||
property.extensionReceiverParameter == null || getClassIfCategory(property) != null
|
||||
|
||||
internal fun ClassDescriptor.getEnumValuesFunctionDescriptor(): SimpleFunctionDescriptor? {
|
||||
require(this.kind == ClassKind.ENUM_CLASS)
|
||||
|
||||
return this.staticScope.getContributedFunctions(
|
||||
StandardNames.ENUM_VALUES,
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).singleOrNull { it.extensionReceiverParameter == null && it.valueParameters.size == 0 }
|
||||
}
|
||||
|
||||
internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = method.allOverriddenDescriptors.any {
|
||||
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanFqNames.throws)
|
||||
}
|
||||
|
||||
+20
-5
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isArray
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.konan.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -56,6 +57,7 @@ interface ObjCExportNamer {
|
||||
fun getPropertyName(property: PropertyDescriptor): String
|
||||
fun getObjectInstanceSelector(descriptor: ClassDescriptor): String
|
||||
fun getEnumEntrySelector(descriptor: ClassDescriptor): String
|
||||
fun getEnumValuesSelector(descriptor: FunctionDescriptor): String
|
||||
fun getTypeParameterName(typeParameterDescriptor: TypeParameterDescriptor): String
|
||||
|
||||
fun numberBoxName(classId: ClassId): ClassOrProtocolName
|
||||
@@ -328,7 +330,7 @@ internal class ObjCExportNamerImpl(
|
||||
|
||||
private val genericTypeParameterNameMapping = GenericTypeParameterNameMapping()
|
||||
|
||||
private abstract inner class ClassPropertyNameMapping<T : Any> : Mapping<T, String>() {
|
||||
private abstract inner class ClassSelectorNameMapping<T : Any> : Mapping<T, String>() {
|
||||
|
||||
// Try to avoid clashing with NSObject class methods:
|
||||
|
||||
@@ -343,12 +345,12 @@ internal class ObjCExportNamerImpl(
|
||||
override fun reserved(name: String) = (name in reserved) || (name in cKeywords)
|
||||
}
|
||||
|
||||
private val objectInstanceSelectors = object : ClassPropertyNameMapping<ClassDescriptor>() {
|
||||
private val objectInstanceSelectors = object : ClassSelectorNameMapping<ClassDescriptor>() {
|
||||
override fun conflict(first: ClassDescriptor, second: ClassDescriptor) = false
|
||||
}
|
||||
|
||||
private val enumEntrySelectors = object : ClassPropertyNameMapping<ClassDescriptor>() {
|
||||
override fun conflict(first: ClassDescriptor, second: ClassDescriptor) =
|
||||
private val enumClassSelectors = object : ClassSelectorNameMapping<DeclarationDescriptor>() {
|
||||
override fun conflict(first: DeclarationDescriptor, second: DeclarationDescriptor) =
|
||||
first.containingDeclaration == second.containingDeclaration
|
||||
}
|
||||
|
||||
@@ -558,7 +560,7 @@ internal class ObjCExportNamerImpl(
|
||||
override fun getEnumEntrySelector(descriptor: ClassDescriptor): String {
|
||||
assert(descriptor.kind == ClassKind.ENUM_ENTRY)
|
||||
|
||||
return enumEntrySelectors.getOrPut(descriptor) {
|
||||
return enumClassSelectors.getOrPut(descriptor) {
|
||||
// FOO_BAR_BAZ -> fooBarBaz:
|
||||
val name = descriptor.name.asString().split('_').mapIndexed { index, s ->
|
||||
val lower = s.toLowerCase()
|
||||
@@ -569,6 +571,19 @@ internal class ObjCExportNamerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEnumValuesSelector(descriptor: FunctionDescriptor): String {
|
||||
val containingDeclaration = descriptor.containingDeclaration
|
||||
require(containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.ENUM_CLASS)
|
||||
require(descriptor.name == StandardNames.ENUM_VALUES)
|
||||
require(descriptor.dispatchReceiverParameter == null) { "must be static" }
|
||||
require(descriptor.extensionReceiverParameter == null) { "must be static" }
|
||||
require(descriptor.valueParameters.isEmpty())
|
||||
|
||||
return enumClassSelectors.getOrPut(descriptor) {
|
||||
StringBuilder(descriptor.name.asString()).mangledBySuffixUnderscores()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTypeParameterName(typeParameterDescriptor: TypeParameterDescriptor): String {
|
||||
return genericTypeParameterNameMapping.getOrPut(typeParameterDescriptor) {
|
||||
StringBuilder().apply {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package enumValues
|
||||
|
||||
enum class EnumLeftRightUpDown {
|
||||
LEFT, RIGHT, UP, DOWN
|
||||
}
|
||||
|
||||
enum class EnumOneTwoThreeValues {
|
||||
ONE, TWO, THREE, VALUES
|
||||
}
|
||||
|
||||
enum class EnumValuesValues_ {
|
||||
VALUES, VALUES_
|
||||
}
|
||||
|
||||
enum class EmptyEnum {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import Kt
|
||||
|
||||
private func testEnumValues() throws {
|
||||
let values = EnumLeftRightUpDown.values()
|
||||
|
||||
try assertEquals(actual: values.size, expected: 4)
|
||||
|
||||
try assertSame(actual: values.get(index: 0), expected: EnumLeftRightUpDown.left)
|
||||
try assertSame(actual: values.get(index: 1), expected: EnumLeftRightUpDown.right)
|
||||
try assertSame(actual: values.get(index: 2), expected: EnumLeftRightUpDown.up)
|
||||
try assertSame(actual: values.get(index: 3), expected: EnumLeftRightUpDown.down)
|
||||
}
|
||||
|
||||
private func testEnumValuesMangled() throws {
|
||||
let values = EnumOneTwoThreeValues.values_()
|
||||
|
||||
try assertEquals(actual: values.size, expected: 4)
|
||||
|
||||
try assertSame(actual: values.get(index: 0), expected: EnumOneTwoThreeValues.one)
|
||||
try assertSame(actual: values.get(index: 1), expected: EnumOneTwoThreeValues.two)
|
||||
try assertSame(actual: values.get(index: 2), expected: EnumOneTwoThreeValues.three)
|
||||
try assertSame(actual: values.get(index: 3), expected: EnumOneTwoThreeValues.values)
|
||||
}
|
||||
|
||||
private func testEnumValuesMangledTwice() throws {
|
||||
let values = EnumValuesValues_.values__()
|
||||
|
||||
try assertEquals(actual: values.size, expected: 2)
|
||||
|
||||
try assertSame(actual: values.get(index: 0), expected: EnumValuesValues_.values)
|
||||
try assertSame(actual: values.get(index: 1), expected: EnumValuesValues_.values_)
|
||||
}
|
||||
|
||||
private func testEnumValuesEmpty() throws {
|
||||
try assertEquals(actual: EmptyEnum.values().size, expected: 0)
|
||||
}
|
||||
|
||||
class EnumValuesTests : SimpleTestProvider {
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
test("TestEnumValues", testEnumValues)
|
||||
test("TestEnumValuesMangled", testEnumValuesMangled)
|
||||
test("TestEnumValuesMangledTwice", testEnumValuesMangledTwice)
|
||||
test("TestEnumValuesEmpty", testEnumValuesEmpty)
|
||||
}
|
||||
}
|
||||
@@ -331,6 +331,52 @@ __attribute__((swift_name("DeallocRetainKt")))
|
||||
+ (KtKotlinWeakReference<id> *)createWeakReferenceValue:(id)value __attribute__((swift_name("createWeakReference(value:)")));
|
||||
@end;
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("EnumLeftRightUpDown")))
|
||||
@interface KtEnumLeftRightUpDown : KtKotlinEnum<KtEnumLeftRightUpDown *>
|
||||
+ (instancetype)alloc __attribute__((unavailable));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
|
||||
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
|
||||
@property (class, readonly) KtEnumLeftRightUpDown *left __attribute__((swift_name("left")));
|
||||
@property (class, readonly) KtEnumLeftRightUpDown *right __attribute__((swift_name("right")));
|
||||
@property (class, readonly) KtEnumLeftRightUpDown *up __attribute__((swift_name("up")));
|
||||
@property (class, readonly) KtEnumLeftRightUpDown *down __attribute__((swift_name("down")));
|
||||
+ (KtKotlinArray<KtEnumLeftRightUpDown *> *)values __attribute__((swift_name("values()")));
|
||||
@end;
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("EnumOneTwoThreeValues")))
|
||||
@interface KtEnumOneTwoThreeValues : KtKotlinEnum<KtEnumOneTwoThreeValues *>
|
||||
+ (instancetype)alloc __attribute__((unavailable));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
|
||||
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
|
||||
@property (class, readonly) KtEnumOneTwoThreeValues *one __attribute__((swift_name("one")));
|
||||
@property (class, readonly) KtEnumOneTwoThreeValues *two __attribute__((swift_name("two")));
|
||||
@property (class, readonly) KtEnumOneTwoThreeValues *three __attribute__((swift_name("three")));
|
||||
@property (class, readonly) KtEnumOneTwoThreeValues *values __attribute__((swift_name("values")));
|
||||
+ (KtKotlinArray<KtEnumOneTwoThreeValues *> *)values __attribute__((swift_name("values()")));
|
||||
@end;
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("EnumValuesValues_")))
|
||||
@interface KtEnumValuesValues_ : KtKotlinEnum<KtEnumValuesValues_ *>
|
||||
+ (instancetype)alloc __attribute__((unavailable));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
|
||||
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
|
||||
@property (class, readonly) KtEnumValuesValues_ *values __attribute__((swift_name("values")));
|
||||
@property (class, readonly) KtEnumValuesValues_ *values __attribute__((swift_name("values")));
|
||||
+ (KtKotlinArray<KtEnumValuesValues_ *> *)values __attribute__((swift_name("values()")));
|
||||
@end;
|
||||
|
||||
__attribute__((objc_subclassing_restricted))
|
||||
__attribute__((swift_name("EmptyEnum")))
|
||||
@interface KtEmptyEnum : KtKotlinEnum<KtEmptyEnum *>
|
||||
+ (instancetype)alloc __attribute__((unavailable));
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));
|
||||
- (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)) __attribute__((unavailable));
|
||||
+ (KtKotlinArray<KtEmptyEnum *> *)values __attribute__((swift_name("values()")));
|
||||
@end;
|
||||
|
||||
__attribute__((swift_name("FHolder")))
|
||||
@interface KtFHolder : KtBase
|
||||
- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer));
|
||||
@@ -696,6 +742,7 @@ __attribute__((swift_name("Enumeration")))
|
||||
@property (class, readonly) KtEnumeration *answer __attribute__((swift_name("answer")));
|
||||
@property (class, readonly) KtEnumeration *year __attribute__((swift_name("year")));
|
||||
@property (class, readonly) KtEnumeration *temperature __attribute__((swift_name("temperature")));
|
||||
+ (KtKotlinArray<KtEnumeration *> *)values __attribute__((swift_name("values()")));
|
||||
@property (readonly) int32_t enumValue __attribute__((swift_name("enumValue")));
|
||||
@end;
|
||||
|
||||
@@ -1407,6 +1454,7 @@ __attribute__((swift_name("TestInvalidIdentifiers.E")))
|
||||
@property (class, readonly) KtTestInvalidIdentifiersE *_5_ __attribute__((swift_name("_5_")));
|
||||
@property (class, readonly) KtTestInvalidIdentifiersE *__ __attribute__((swift_name("__")));
|
||||
@property (class, readonly) KtTestInvalidIdentifiersE *__ __attribute__((swift_name("__")));
|
||||
+ (KtKotlinArray<KtTestInvalidIdentifiersE *> *)values __attribute__((swift_name("values()")));
|
||||
@property (readonly) int32_t value __attribute__((swift_name("value")));
|
||||
@end;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user