From 0ff306ad3ada9871ba57ba32be353715a83930ff Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 3 Mar 2017 13:47:05 +0300 Subject: [PATCH] Rewrote enum lowering to support usage from other modules. - Split up enum lowering into two stages: descriptors creation and IR creation. - Added link stage test. - Enum.valueOf() uses binary search instead of linear. --- .../jetbrains/kotlin/backend/konan/Context.kt | 9 + .../konan/EnumSpecialDescriptorsFactory.kt | 149 +++++++++++++ .../kotlin/backend/konan/ir/IrUtils.kt | 15 +- .../backend/konan/lower/EnumClassLowering.kt | 197 ++++++------------ backend.native/tests/build.gradle | 10 +- .../tests/codegen/enum/linkTest_lib.kt | 7 + .../tests/codegen/enum/linkTest_main.kt | 7 + backend.native/tests/codegen/enum/valueOf.kt | 3 + backend.native/tests/codegen/enum/values.kt | 3 + .../kotlin/konan/internal/RuntimeUtils.kt | 21 +- 10 files changed, 280 insertions(+), 141 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt create mode 100644 backend.native/tests/codegen/enum/linkTest_lib.kt create mode 100644 backend.native/tests/codegen/enum/linkTest_main.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index d6b2292a7bb..6ccc4c2a29b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -22,8 +22,10 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import java.lang.System.out internal class SpecialDescriptorsFactory(val context: Context) { + private val enumSpecialDescriptorsFactory by lazy { EnumSpecialDescriptorsFactory(context) } private val outerThisDescriptors = mutableMapOf() private val bridgesDescriptors = mutableMapOf, FunctionDescriptor>() + private val loweredEnums = mutableMapOf() fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor = if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor") @@ -54,6 +56,13 @@ internal class SpecialDescriptorsFactory(val context: Context) { } } + fun getLoweredEnum(enumClassDescriptor: ClassDescriptor): LoweredEnum { + assert(enumClassDescriptor.kind == ClassKind.ENUM_CLASS, { "Expected enum class but was: $enumClassDescriptor" }) + return loweredEnums.getOrPut(enumClassDescriptor) { + enumSpecialDescriptorsFactory.createLoweredEnum(enumClassDescriptor) + } + } + private fun initializeBridgeDescriptor(bridgeDescriptor: SimpleFunctionDescriptorImpl, descriptor: FunctionDescriptor, bridgeDirections: Array) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt new file mode 100644 index 00000000000..943e623d596 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt @@ -0,0 +1,149 @@ +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.backend.jvm.descriptors.initialize +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.createSimpleDelegatingConstructorDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.replace +import org.jetbrains.kotlin.utils.addToStdlib.singletonList + +internal data class LoweredEnum(val implObjectDescriptor: ClassDescriptor, + val valuesProperty: PropertyDescriptor, + val valuesGetter: FunctionDescriptor, + val valuesFunction: FunctionDescriptor, + val valueOfFunction: FunctionDescriptor, + val itemGetter: FunctionDescriptor, + val entriesMap: Map) + +internal class EnumSpecialDescriptorsFactory(val context: Context) { + fun createLoweredEnum(enumClassDescriptor: ClassDescriptor): LoweredEnum { + val implObjectDescriptor = ClassDescriptorImpl(enumClassDescriptor, "OBJECT".synthesizedName, Modality.FINAL, + ClassKind.OBJECT, KonanPlatform.builtIns.anyType.singletonList(), SourceElement.NO_SOURCE, false) + + val valuesProperty = createEnumValuesField(enumClassDescriptor, implObjectDescriptor) + val valuesFunction = createValuesFunctionDescriptor(enumClassDescriptor, implObjectDescriptor) + val valueOfFunction = createValueOfFunctionDescriptor(enumClassDescriptor, implObjectDescriptor) + val valuesGetter = createValuesGetterDescriptor(enumClassDescriptor, implObjectDescriptor) + + val memberScope = MemberScope.Empty + + val constructorOfAny = context.builtIns.any.constructors.first() + val constructorDescriptor = implObjectDescriptor.createSimpleDelegatingConstructorDescriptor(constructorOfAny) + + implObjectDescriptor.initialize(memberScope, setOf(constructorDescriptor), constructorDescriptor) + + return LoweredEnum(implObjectDescriptor, valuesProperty, valuesGetter, valuesFunction, valueOfFunction, + getEnumItemGetter(enumClassDescriptor), createEnumEntriesMap(enumClassDescriptor)) + } + + private fun createValuesFunctionDescriptor(enumClassDescriptor: ClassDescriptor, implObjectDescriptor: ClassDescriptor) + : FunctionDescriptor { + val returnType = genericArrayType.defaultType.replace(listOf(TypeProjectionImpl(enumClassDescriptor.defaultType))) + val result = SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ implObjectDescriptor, + /* annotations = */ Annotations.EMPTY, + /* name = */ "values".synthesizedName, + /* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED, + /* source = */ SourceElement.NO_SOURCE) + result.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameter = */ null, + /* typeParameters = */ listOf(), + /* unsubstitutedValueParameters = */ listOf(), + /* unsubstitutedReturnType = */ returnType, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PUBLIC) + return result + } + + private fun createValuesGetterDescriptor(enumClassDescriptor: ClassDescriptor, implObjectDescriptor: ClassDescriptor) + : FunctionDescriptor { + val returnType = genericArrayType.defaultType.replace(listOf(TypeProjectionImpl(enumClassDescriptor.defaultType))) + val result = SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ implObjectDescriptor, + /* annotations = */ Annotations.EMPTY, + /* name = */ "get-VALUES".synthesizedName, + /* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED, + /* source = */ SourceElement.NO_SOURCE) + result.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameter = */ null, + /* typeParameters = */ listOf(), + /* unsubstitutedValueParameters = */ listOf(), + /* unsubstitutedReturnType = */ returnType, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PUBLIC) + return result + } + + private fun createValueOfFunctionDescriptor(enumClassDescriptor: ClassDescriptor, implObjectDescriptor: ClassDescriptor) + : FunctionDescriptor { + val result = SimpleFunctionDescriptorImpl.create( + /* containingDeclaration = */ implObjectDescriptor, + /* annotations = */ Annotations.EMPTY, + /* name = */ "valueOf".synthesizedName, + /* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED, + /* source = */ SourceElement.NO_SOURCE) + val nameParameterDescriptor = ValueParameterDescriptorImpl( + containingDeclaration = result, + original = null, + index = 0, + annotations = Annotations.EMPTY, + name = Name.identifier("name"), + outType = context.builtIns.stringType, + declaresDefaultValue = false, + isCrossinline = false, + isNoinline = false, + varargElementType = null, + source = SourceElement.NO_SOURCE) + result.initialize( + /* receiverParameterType = */ null, + /* dispatchReceiverParameter = */ null, + /* typeParameters = */ listOf(), + /* unsubstitutedValueParameters = */ listOf(nameParameterDescriptor), + /* unsubstitutedReturnType = */ enumClassDescriptor.defaultType, + /* modality = */ Modality.FINAL, + /* visibility = */ Visibilities.PUBLIC) + return result + } + + private fun createEnumValuesField(enumClassDescriptor: ClassDescriptor, implObjectDescriptor: ClassDescriptor): PropertyDescriptor { + val valuesArrayType = context.builtIns.getArrayType(Variance.INVARIANT, enumClassDescriptor.defaultType) + val receiver = ReceiverParameterDescriptorImpl(implObjectDescriptor, ImplicitClassReceiver(implObjectDescriptor)) + return PropertyDescriptorImpl.create(implObjectDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, + false, "VALUES".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE, + false, false, false, false, false, false).initialize(valuesArrayType, dispatchReceiverParameter = receiver) + } + + private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin")) + private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + private fun getEnumItemGetter(enumClassDescriptor: ClassDescriptor): FunctionDescriptor { + val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single() + + val typeParameterT = genericArrayType.declaredTypeParameters[0] + val enumClassType = enumClassDescriptor.defaultType + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) + return getter.substitute(typeSubstitutor)!! + } + + private fun createEnumEntriesMap(enumClassDescriptor: ClassDescriptor): Map { + val map = mutableMapOf() + enumClassDescriptor.unsubstitutedMemberScope.getContributedDescriptors() + .filter { it is ClassDescriptor && it.kind == ClassKind.ENUM_ENTRY } + .sortedBy { it.name } + .forEachIndexed { index, entry -> map.put(entry.name, index) } + return map + } + +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt index a39f780b7bc..580146f7975 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt @@ -28,8 +28,8 @@ fun ir2stringWhole(ir: IrElement?): String { return strWriter.toString() } -internal fun ClassDescriptor.createSimpleDelegatingConstructor(superConstructorDescriptor: ClassConstructorDescriptor) - : Pair { +internal fun ClassDescriptor.createSimpleDelegatingConstructorDescriptor(superConstructorDescriptor: ClassConstructorDescriptor) + : ClassConstructorDescriptor { val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( this, Annotations.EMPTY, @@ -40,20 +40,23 @@ internal fun ClassDescriptor.createSimpleDelegatingConstructor(superConstructorD } constructorDescriptor.initialize(valueParameters, superConstructorDescriptor.visibility) constructorDescriptor.returnType = superConstructorDescriptor.returnType + return constructorDescriptor +} +internal fun ClassDescriptor.createSimpleDelegatingConstructor(superConstructorDescriptor: ClassConstructorDescriptor, + constructorDescriptor: ClassConstructorDescriptor) + : IrConstructor { val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf( IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, superConstructorDescriptor).apply { - valueParameters.forEachIndexed { idx, parameter -> + constructorDescriptor.valueParameters.forEachIndexed { idx, parameter -> putValueArgument(idx, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter)) } }, IrInstanceInitializerCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, this) ) ) - val constructor = IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body) - - return Pair(constructorDescriptor, constructor) + return IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body) } internal fun Context.createArrayOfExpression(arrayElementType: KotlinType, diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index 9c4c9cb3859..d1fcd7b65bd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -5,20 +5,15 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.SimpleMemberScope import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter -import org.jetbrains.kotlin.backend.jvm.descriptors.initialize import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.KonanPlatform import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.backend.konan.ir.createArrayOfExpression import org.jetbrains.kotlin.backend.konan.ir.createSimpleDelegatingConstructor +import org.jetbrains.kotlin.backend.konan.ir.createSimpleDelegatingConstructorDescriptor import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl -import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* @@ -30,26 +25,14 @@ import org.jetbrains.kotlin.ir.util.transform import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver -import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList -// TODO: cross-module usage. - -internal data class LoweredSyntheticFunction(val functionDescriptor: FunctionDescriptor, val containingClass: ClassDescriptor) - -internal data class LoweredEnumEntry(val implObject: ClassDescriptor, val valuesProperty: PropertyDescriptor, - val itemGetter: FunctionDescriptor, val entriesMap: Map) - -internal class EnumUsageLowering(val context: Context, - val loweredFunctions: Map, - val loweredEnumEntries: Map) +internal class EnumUsageLowering(val context: Context) : IrElementTransformerVoid(), FileLoweringPass { override fun lower(irFile: IrFile) { @@ -58,54 +41,53 @@ internal class EnumUsageLowering(val context: Context, override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression { val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor - enumClassDescriptor.companionObjectDescriptor return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name) } override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { - if(expression.descriptor.kind != ClassKind.ENUM_ENTRY) + if (expression.descriptor.kind != ClassKind.ENUM_ENTRY) return super.visitGetObjectValue(expression) val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name) } private fun loadEnumEntry(startOffset: Int, endOffset: Int, enumClassDescriptor: ClassDescriptor, name: Name): IrExpression { - val loweredEnumEntry = loweredEnumEntries[enumClassDescriptor]!! - val implObject = loweredEnumEntry.implObject - val implObjectGetter = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, implObject.classValueType!!, implObject) - val valuesGetter = IrGetFieldImpl(startOffset, endOffset, loweredEnumEntry.valuesProperty).apply { - receiver = implObjectGetter - } - val ordinal = loweredEnumEntry.entriesMap[name]!! - return IrCallImpl(startOffset, endOffset, loweredEnumEntry.itemGetter).apply { - dispatchReceiver = valuesGetter + val loweredEnum = context.specialDescriptorsFactory.getLoweredEnum(enumClassDescriptor) + val ordinal = loweredEnum.entriesMap[name]!! + return IrCallImpl(startOffset, endOffset, loweredEnum.itemGetter).apply { + dispatchReceiver = IrCallImpl(startOffset, endOffset, loweredEnum.valuesGetter) putValueArgument(0, IrConstImpl.int(startOffset, endOffset, enumClassDescriptor.module.builtIns.intType, ordinal)) } } override fun visitCall(expression: IrCall): IrExpression { - val loweredFunction = loweredFunctions[expression.descriptor] ?: return super.visitCall(expression) - return IrCallImpl(expression.startOffset, expression.endOffset, loweredFunction.functionDescriptor).apply { - val containingClass = loweredFunction.containingClass - dispatchReceiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, containingClass.classValueType!!, containingClass) + expression.transformChildrenVoid(this) + val functionDescriptor = expression.descriptor as? FunctionDescriptor + if (functionDescriptor == null) return expression + if (functionDescriptor.kind != CallableMemberDescriptor.Kind.SYNTHESIZED) return expression + val classDescriptor = functionDescriptor.containingDeclaration as? ClassDescriptor + if (classDescriptor == null || classDescriptor.kind != ClassKind.ENUM_CLASS) return expression + val loweredEnum = context.specialDescriptorsFactory.getLoweredEnum(classDescriptor) + val loweredFunction = when (functionDescriptor.name.asString()) { + "values" -> loweredEnum.valuesFunction + "valueOf" -> loweredEnum.valueOfFunction + else -> throw AssertionError("Unexpected synthetic enum function: $functionDescriptor") + } + return IrCallImpl(expression.startOffset, expression.endOffset, loweredFunction).apply { expression.descriptor.valueParameters.forEach { p -> putValueArgument(p, expression.getValueArgument(p)) } } } } internal class EnumClassLowering(val context: Context) : ClassLoweringPass { - val loweredFunctions = mutableMapOf() - val loweredEnumEntries = mutableMapOf() - fun run(irFile: IrFile) { runOnFilePostfix(irFile) - EnumUsageLowering(context, loweredFunctions, loweredEnumEntries).lower(irFile) + EnumUsageLowering(context).lower(irFile) } override fun lower(irClass: IrClass) { val descriptor = irClass.descriptor - if (descriptor.kind != ClassKind.ENUM_CLASS) - return + if (descriptor.kind != ClassKind.ENUM_CLASS) return EnumClassTransformer(irClass).run() } @@ -115,16 +97,13 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { } private inner class EnumClassTransformer(val irClass: IrClass) { + private val loweredEnum = context.specialDescriptorsFactory.getLoweredEnum(irClass.descriptor) private val enumEntryOrdinals = mutableMapOf() private val loweredEnumConstructors = mutableMapOf() private val descriptorToIrConstructorWithDefaultArguments = mutableMapOf() private val defaultEnumEntryConstructors = mutableMapOf() private val loweredEnumConstructorParameters = mutableMapOf() - private lateinit var valuesFieldDescriptor: PropertyDescriptor - private lateinit var valuesFunctionDescriptor: FunctionDescriptor - private lateinit var valueOfFunctionDescriptor: FunctionDescriptor - fun run() { insertInstanceInitializerCall() assignOrdinalsToEnumEntries() @@ -196,7 +175,8 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { descriptor.constructors.forEach { val loweredEnumConstructor = loweredEnumConstructors[it]!! - val (constructorDescriptor, constructor) = defaultClassDescriptor.createSimpleDelegatingConstructor(loweredEnumConstructor) + val constructorDescriptor = defaultClassDescriptor.createSimpleDelegatingConstructorDescriptor(loweredEnumConstructor) + val constructor = defaultClassDescriptor.createSimpleDelegatingConstructor(loweredEnumConstructor, constructorDescriptor) constructors.add(constructorDescriptor) defaultClass.declarations.add(constructor) defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor) @@ -219,9 +199,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { } private fun createImplObject() { - val descriptor = irClass.descriptor - val implObjectDescriptor = ClassDescriptorImpl(descriptor, "OBJECT".synthesizedName, Modality.FINAL, - ClassKind.OBJECT, KonanPlatform.builtIns.anyType.singletonList(), SourceElement.NO_SOURCE, false) + val implObjectDescriptor = loweredEnum.implObjectDescriptor val implObject = IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, implObjectDescriptor) val enumEntries = mutableListOf() @@ -235,18 +213,8 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { delete = true } is IrFunction -> { - var copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUES) - if(copiedDescriptor != null) - { - valuesFunctionDescriptor = copiedDescriptor + if (declaration.body is IrSyntheticBody) delete = true - } - copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUEOF) - if(copiedDescriptor != null) - { - valueOfFunctionDescriptor = copiedDescriptor - delete = true - } } } if (delete) @@ -255,76 +223,47 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { ++i } - val memberScope = MemberScope.Empty - val constructorOfAny = irClass.descriptor.module.builtIns.any.constructors.first() - val (constructorDescriptor, constructor) = implObjectDescriptor.createSimpleDelegatingConstructor(constructorOfAny) - - implObjectDescriptor.initialize(memberScope, setOf(constructorDescriptor), constructorDescriptor) + val constructor = implObjectDescriptor.createSimpleDelegatingConstructor(constructorOfAny, implObjectDescriptor.constructors.single()) implObject.declarations.add(constructor) - implObject.declarations.add(createSyntheticValuesFieldDeclaration(implObjectDescriptor, enumEntries)) - implObject.declarations.add(createSyntheticValuesMethodDeclaration(implObjectDescriptor)) - implObject.declarations.add(createSyntheticValueOfMethodDeclaration(implObjectDescriptor)) + implObject.declarations.add(createSyntheticValuesPropertyDeclaration(enumEntries)) + implObject.declarations.add(createSyntheticValuesMethodDeclaration()) + implObject.declarations.add(createSyntheticValueOfMethodDeclaration()) irClass.declarations.add(implObject) } - private fun tryCopySyntheticBodyDeclaration(implObjectDescriptor: ClassDescriptor, - declaration: IrFunction, - kind: IrSyntheticBodyKind): FunctionDescriptor? { - if (!declaration.body.let { it is IrSyntheticBody && it.kind == kind }) - return null - val newDescriptor = declaration.descriptor - .newCopyBuilder() - .setOwner(implObjectDescriptor) - .setName(declaration.descriptor.name.identifier.synthesizedName) - .setDispatchReceiverParameter(implObjectDescriptor.thisAsReceiverParameter) - .build()!! - loweredFunctions.put(declaration.descriptor, LoweredSyntheticFunction(newDescriptor, implObjectDescriptor)) - return newDescriptor - } + private fun createSyntheticValuesPropertyDeclaration(enumEntries: List): IrPropertyImpl { + val irValuesInitializer = context.createArrayOfExpression(irClass.descriptor.defaultType, + enumEntries.sortedBy { it.descriptor.name }.map { it.initializerExpression }) - private fun createSyntheticValuesFieldDeclaration(implObjectDescriptor: ClassDescriptor, - enumEntries: List): IrFieldImpl { - val valuesArrayType = context.builtIns.getArrayType(Variance.INVARIANT, irClass.descriptor.defaultType) - valuesFieldDescriptor = createSyntheticValuesFieldDescriptor(implObjectDescriptor, valuesArrayType) - - val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single() - - val typeParameterT = genericArrayType.declaredTypeParameters[0] - val enumClassType = irClass.descriptor.defaultType - val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) - val substitutedValueOf = getter.substitute(typeSubstitutor)!! - - val entriesMap = enumEntries.associateBy({ it -> it.descriptor.name }, { it -> enumEntryOrdinals[it.descriptor]!! }).toMap() - val loweredEnumEntry = LoweredEnumEntry(implObjectDescriptor, valuesFieldDescriptor, substitutedValueOf, entriesMap) - loweredEnumEntries.put(irClass.descriptor, loweredEnumEntry) - - val irValuesInitializer = context.createArrayOfExpression(irClass.descriptor.defaultType, enumEntries.map { it.initializerExpression }) - - val irField = IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, - valuesFieldDescriptor, + val irField = IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_ENUM, + loweredEnum.valuesProperty, IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irValuesInitializer)) - return irField + + val getter = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_ENUM, loweredEnum.valuesGetter) + + val receiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + loweredEnum.implObjectDescriptor.defaultType, loweredEnum.implObjectDescriptor) + val value = IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valuesProperty, receiver) + val returnStatement = IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + loweredEnum.valuesGetter.returnType!!, loweredEnum.valuesGetter, value) + getter.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(returnStatement)) + + val irProperty = IrPropertyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_ENUM, + false, loweredEnum.valuesProperty, irField, getter, null) + return irProperty } - private fun createSyntheticValuesFieldDescriptor(implObjectDescriptor: ClassDescriptor, valuesArrayType: SimpleType): PropertyDescriptorImpl { - val receiver = ReceiverParameterDescriptorImpl(implObjectDescriptor, ImplicitClassReceiver(implObjectDescriptor)) - return PropertyDescriptorImpl.create(implObjectDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE, - false, "VALUES".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, irClass.descriptor.source, - false, false, false, false, false, false).initialize(valuesArrayType, dispatchReceiverParameter = receiver) - } - - private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin")) + private object DECLARATION_ORIGIN_ENUM : + IrDeclarationOriginImpl("ENUM") private val genericValueOfFun = context.builtIns.getKonanInternalFunctions("valueOfForEnum").single() private val genericValuesFun = context.builtIns.getKonanInternalFunctions("valuesForEnum").single() - private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor - - private fun createSyntheticValuesMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction { + private fun createSyntheticValuesMethodDeclaration(): IrFunction { val typeParameterT = genericValuesFun.typeParameters[0] val enumClassType = irClass.descriptor.defaultType val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) @@ -332,18 +271,19 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val irValuesCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType)) .apply { - val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter) - putValueArgument(0, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver)) + val receiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + loweredEnum.implObjectDescriptor.defaultType, loweredEnum.implObjectDescriptor) + putValueArgument(0, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valuesProperty, receiver)) } val body = IrBlockBodyImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, - listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFunctionDescriptor, irValuesCall)) + listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valuesFunction, irValuesCall)) ) - return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valuesFunctionDescriptor, body) + return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_ENUM, loweredEnum.valuesFunction, body) } - private fun createSyntheticValueOfMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction { + private fun createSyntheticValueOfMethodDeclaration(): IrFunction { val typeParameterT = genericValueOfFun.typeParameters[0] val enumClassType = irClass.descriptor.defaultType val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType))) @@ -351,16 +291,17 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val irValueOfCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType)) .apply { - putValueArgument(0, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor.valueParameters[0])) - val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter) - putValueArgument(1, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver)) + putValueArgument(0, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valueOfFunction.valueParameters[0])) + val receiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + loweredEnum.implObjectDescriptor.defaultType, loweredEnum.implObjectDescriptor) + putValueArgument(1, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valuesProperty, receiver)) } val body = IrBlockBodyImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, - listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor, irValueOfCall)) + listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, loweredEnum.valueOfFunction, irValueOfCall)) ) - return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valueOfFunctionDescriptor, body) + return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, DECLARATION_ORIGIN_ENUM, loweredEnum.valueOfFunction, body) } private fun lowerEnumConstructors(irClass: IrClass) { @@ -595,12 +536,12 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { } } -private fun ValueParameterDescriptor.loweredIndex(): Int = index + 2 +private fun ValueParameterDescriptor.loweredIndex(): Int = index + 2 -private class ParameterMapper(val originalDescriptor:FunctionDescriptor): IrElementTransformerVoid() { +private class ParameterMapper(val originalDescriptor: FunctionDescriptor) : IrElementTransformerVoid() { override fun visitGetValue(expression: IrGetValue): IrExpression { val descriptor = expression.descriptor - when(descriptor) { + when (descriptor) { is ValueParameterDescriptor -> { return IrGetValueImpl(expression.startOffset, expression.endOffset, diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 03d7e38eed9..a5fac478d74 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -349,12 +349,12 @@ task enum1(type: RunKonanTest) { } task enum_valueOf(type: RunKonanTest) { - goldValue = "E1\n" + goldValue = "E1\nE2\nE3\n" source = "codegen/enum/valueOf.kt" } task enum_values(type: RunKonanTest) { - goldValue = "E2\n" + goldValue = "E3\nE1\nE2\n" source = "codegen/enum/values.kt" } @@ -383,6 +383,12 @@ task enum_interfaceCallWithEntryClass(type: RunKonanTest) { source = "codegen/enum/interfaceCallWithEntryClass.kt" } +task enum_linkTest(type: LinkKonanTest) { + goldValue = "42\n117\n-1\n" + source = "codegen/enum/linkTest_main.kt" + lib = "codegen/enum/linkTest_lib.kt" +} + task innerClass_simple(type: RunKonanTest) { goldValue = "OK\n" source = "codegen/innerClass/simple.kt" diff --git a/backend.native/tests/codegen/enum/linkTest_lib.kt b/backend.native/tests/codegen/enum/linkTest_lib.kt new file mode 100644 index 00000000000..3653236c9cd --- /dev/null +++ b/backend.native/tests/codegen/enum/linkTest_lib.kt @@ -0,0 +1,7 @@ +package a + +enum class A(val x: Int) { + Z1(42), + Z2(117), + Z3(-1) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/linkTest_main.kt b/backend.native/tests/codegen/enum/linkTest_main.kt new file mode 100644 index 00000000000..cb4be8e436a --- /dev/null +++ b/backend.native/tests/codegen/enum/linkTest_main.kt @@ -0,0 +1,7 @@ +import a.* + +fun main(args: Array) { + println(A.Z1.x) + println(A.valueOf("Z2").x) + println(A.values()[2].x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/valueOf.kt b/backend.native/tests/codegen/enum/valueOf.kt index 9883e06369d..5e8cdb6970b 100644 --- a/backend.native/tests/codegen/enum/valueOf.kt +++ b/backend.native/tests/codegen/enum/valueOf.kt @@ -1,8 +1,11 @@ enum class E { + E3, E1, E2 } fun main(args: Array) { println(E.valueOf("E1").toString()) + println(E.valueOf("E2").toString()) + println(E.valueOf("E3").toString()) } \ No newline at end of file diff --git a/backend.native/tests/codegen/enum/values.kt b/backend.native/tests/codegen/enum/values.kt index aebaa636140..f02f30f1133 100644 --- a/backend.native/tests/codegen/enum/values.kt +++ b/backend.native/tests/codegen/enum/values.kt @@ -1,8 +1,11 @@ enum class E { + E3, E1, E2 } fun main(args: Array) { + println(E.values()[0].toString()) println(E.values()[1].toString()) + println(E.values()[2].toString()) } \ No newline at end of file diff --git a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt index 743da531602..1b9a58df8ef 100644 --- a/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt +++ b/runtime/src/main/kotlin/konan/internal/RuntimeUtils.kt @@ -27,15 +27,26 @@ fun ThrowNoWhenBranchMatchedException(): Nothing { @ExportForCppRuntime internal fun TheEmptyString() = "" -fun > valueOfForEnum(name: String, arr: Array) : T +fun > valueOfForEnum(name: String, values: Array) : T { - for (x in arr) - if (x.name == name) - return x + var left = 0 + var right = values.size - 1 + while (left <= right) { + val middle = (left + right) / 2 + val x = values[middle].name.compareTo(name) + when { + x < 0 -> left = middle + 1 + x > 0 -> right = middle - 1 + else -> return values[middle] + } + } throw Exception("Invalid enum name: $name") } fun > valuesForEnum(values: Array): Array { - return values.clone() as Array + val result = Array(values.size, { null }) + for (value in values) + result[value.ordinal] = value + return result as Array } \ No newline at end of file