Expose enum .values function when generating Obj-C header

This commit is contained in:
Svyatoslav Scherbina
2020-10-30 10:49:51 +03:00
committed by Stanislav Erokhin
parent 740d50ca09
commit 58855b9eff
8 changed files with 195 additions and 9 deletions
@@ -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)
@@ -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(
@@ -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()
@@ -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)
}
@@ -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;