diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 6aa3ad3e455..26ee5a9349b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isForwardDeclarationModule import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForInteropStubs import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols +import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumStubs import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport @@ -201,14 +202,16 @@ internal val psiToIrPhase = konanUnitPhase( val functionIrClassFactory = BuiltInFictitiousFunctionIrClassFactory( symbolTable, generatorContext.irBuiltIns, reflectionTypes) - // TODO: Add special handling for enums and structs. - val irProviderForInteropStubs = IrProviderForInteropStubs { false } val symbols = KonanSymbols(this, symbolTable, symbolTable.lazyWrapper, functionIrClassFactory) val stubGenerator = DeclarationStubGenerator( moduleDescriptor, symbolTable, config.configuration.languageVersionSettings ) - val irProviders = listOf(irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator) + val irProviderForCEnums = IrProviderForCEnumStubs(generatorContext, interopBuiltIns, stubGenerator, symbols) + val irProviderForInteropStubs = IrProviderForInteropStubs { symbol -> + irProviderForCEnums.canHandleSymbol(symbol) + } + val irProviders = listOf(irProviderForCEnums, irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator) stubGenerator.setIrProviders(irProviders) expectDescriptorToSymbol = mutableMapOf() @@ -236,6 +239,8 @@ internal val psiToIrPhase = konanUnitPhase( functionIrClassFactory.module = (listOf(irModule!!) + deserializer.modules.values) .single { it.descriptor.isKonanStdlib() } + + irProviderForCEnums.module = module }, name = "Psi2Ir", description = "Psi to IR conversion", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/DescriptorToIrTranslationUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/DescriptorToIrTranslationUtils.kt new file mode 100644 index 00000000000..111583a6687 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/DescriptorToIrTranslationUtils.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop + +import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations +import org.jetbrains.kotlin.backend.konan.InteropBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.builders.IrBuilder +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.types.KotlinType + +internal inline fun ClassDescriptor.findDeclarationByName(name: String): T? = + unsubstitutedMemberScope + .getContributedDescriptors() + .filterIsInstance() + .firstOrNull { it.name.identifier == name } + +/** + * Provides a set of functions and properties that helps + * to translate descriptor declarations to corresponding IR. + */ +internal interface DescriptorToIrTranslationMixin { + + val symbolTable: SymbolTable + + val irBuiltIns: IrBuiltIns + + val typeTranslator: TypeTranslator + + val stubGenerator: DeclarationStubGenerator + + fun KotlinType.toIrType() = typeTranslator.translateType(this) + + /** + * Declares [IrClass] instance from [descriptor] and populates it with + * supertypes, parameter declaration and fake overrides. + * Additional elements are passed via [builder] callback. + */ + fun createClass(descriptor: ClassDescriptor, builder: (IrClass) -> Unit): IrClass = + symbolTable.declareClass( + startOffset = SYNTHETIC_OFFSET, + endOffset = SYNTHETIC_OFFSET, + origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, + descriptor = descriptor + ).also { irClass -> + symbolTable.withScope(descriptor) { + descriptor.typeConstructor.supertypes.mapTo(irClass.superTypes) { + it.toIrType() + } + irClass.createParameterDeclarations() + builder(irClass) + createFakeOverrides(descriptor).forEach(irClass::addMember) + } + } + + private fun createFakeOverrides(classDescriptor: ClassDescriptor): List { + val fakeOverrides = classDescriptor.unsubstitutedMemberScope + .getContributedDescriptors() + .filterIsInstance() + .filter { it.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE } + return fakeOverrides.map { + when (it) { + is PropertyDescriptor -> createProperty(it) + is FunctionDescriptor -> createFunction(it, IrDeclarationOrigin.FAKE_OVERRIDE) + else -> error("Unexpected fake override descriptor: $it") + } as IrDeclaration // Assistance for type inference. + } + } + + fun createConstructor(constructorDescriptor: ClassConstructorDescriptor): IrConstructor = + stubGenerator.generateMemberStub(constructorDescriptor) as IrConstructor + + fun createProperty(propertyDescriptor: PropertyDescriptor): IrProperty = + stubGenerator.generateMemberStub(propertyDescriptor) as IrProperty + + fun createFunction( + functionDescriptor: FunctionDescriptor, + origin: IrDeclarationOrigin? = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB + ): IrSimpleFunction = + stubGenerator.generateFunctionStub(functionDescriptor, createPropertyIfNeeded = false).also { + if (origin != null) it.origin = origin + } +} + +internal fun IrBuilder.irInstanceInitializer(classSymbol: IrClassSymbol): IrExpression = + IrInstanceInitializerCallImpl( + startOffset, endOffset, + classSymbol, + context.irBuiltIns.unitType + ) + +internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): Boolean = + interopBuiltIns.cEnum in this.getSuperInterfaces() + +/** + * All enums that come from interop library implement CEnum interface. + * This function checks that given symbol located in subtree of + * CEnum inheritor. + */ +internal fun IrSymbol.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? = + descriptor.parentsWithSelf + .filterIsInstance() + .firstOrNull { it.implementsCEnum(interopBuiltIns) } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/IrProviderForCEnumStubs.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/IrProviderForCEnumStubs.kt new file mode 100644 index 00000000000..d841d8a6578 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/IrProviderForCEnumStubs.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop + +import org.jetbrains.kotlin.backend.konan.InteropBuiltIns +import org.jetbrains.kotlin.backend.konan.descriptors.findPackage +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary +import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols +import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumByValueFunctionGenerator +import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumClassGenerator +import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumCompanionGenerator +import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumVarClassGenerator +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.resolve.descriptorUtil.module + +/** + * For the most of descriptors that come from metadata-based interop libraries + * we generate a lazy IR. + * We use a different approach for CEnums and generate IR eagerly. Motivation: + * 1. CEnums are "real" Kotlin enums. Thus, we need apply the same compilation approach + * as we use for usual Kotlin enums. + * Eager generation allows to reuse [EnumClassLowering], [EnumConstructorsLowering] and other + * compiler phases. + * 2. It is an easier and more obvious approach. Since implementation of metadata-based + * libraries generation already took too much time we take an easier approach here. + */ +internal class IrProviderForCEnumStubs( + context: GeneratorContext, + private val interopBuiltIns: InteropBuiltIns, + stubGenerator: DeclarationStubGenerator, + symbols: KonanSymbols +) : IrProvider { + + private val symbolTable: SymbolTable = context.symbolTable + + private val filesMap = mutableMapOf() + + private val cEnumByValueFunctionGenerator = + CEnumByValueFunctionGenerator(context, stubGenerator, symbols) + private val cEnumCompanionGenerator = + CEnumCompanionGenerator(context, stubGenerator, cEnumByValueFunctionGenerator) + private val cEnumVarClassGenerator = + CEnumVarClassGenerator(context, stubGenerator, interopBuiltIns) + private val cEnumClassGenerator = + CEnumClassGenerator(context, stubGenerator, cEnumCompanionGenerator, cEnumVarClassGenerator) + + var module: IrModuleFragment? = null + set(value) { + if (value == null) + error("Provide a valid non-null module") + if (field != null) + error("Module has already been set") + field = value + value.files += filesMap.values + } + + fun canHandleSymbol(symbol: IrSymbol): Boolean { + if (!symbol.descriptor.module.isFromInteropLibrary()) return false + return symbol.findCEnumDescriptor(interopBuiltIns) != null + } + + override fun getDeclaration(symbol: IrSymbol): IrDeclaration? { + if (symbol.isBound) return symbol.owner as IrDeclaration + if (!canHandleSymbol(symbol)) return null + + val enumClassDescriptor = symbol.findCEnumDescriptor(interopBuiltIns)!! + // TODO: This call generates a whole subtree. This a simple but clearly suboptimal solution. + cEnumClassGenerator.findOrGenerateCEnum(enumClassDescriptor, irParentFor(enumClassDescriptor)) + return when (symbol) { + is IrClassSymbol -> symbolTable.referenceClass(symbol.descriptor).owner + is IrEnumEntrySymbol -> symbolTable.referenceEnumEntry(symbol.descriptor).owner + is IrFunctionSymbol -> symbolTable.referenceFunction(symbol.descriptor).owner + is IrPropertySymbol -> symbolTable.referenceProperty(symbol.descriptor).owner + else -> error(symbol) + } + } + + private fun irParentFor(descriptor: ClassDescriptor): IrDeclarationContainer { + val packageFragmentDescriptor = descriptor.findPackage() + return filesMap.getOrPut(packageFragmentDescriptor) { + IrFileImpl(NaiveSourceBasedFileEntryImpl("CEnums"), packageFragmentDescriptor).also { + this@IrProviderForCEnumStubs.module?.files?.add(it) + } + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumByValueFunctionGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumByValueFunctionGenerator.kt new file mode 100644 index 00000000000..9f6bc31a475 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumByValueFunctionGenerator.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop.cenum + +import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols +import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin +import org.jetbrains.kotlin.backend.konan.ir.interop.findDeclarationByName +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.util.irCall +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext + +/** + * Generate IR for function that returns appropriate enum entry for the provided integral value. + */ +internal class CEnumByValueFunctionGenerator( + context: GeneratorContext, + override val stubGenerator: DeclarationStubGenerator, + private val symbols: KonanSymbols +) : DescriptorToIrTranslationMixin { + + override val irBuiltIns: IrBuiltIns = context.irBuiltIns + override val symbolTable: SymbolTable = context.symbolTable + override val typeTranslator: TypeTranslator = context.typeTranslator + + fun generateByValueFunction( + companionIrClass: IrClass, + valuesIrFunctionSymbol: IrSimpleFunctionSymbol + ): IrFunction { + val byValueFunctionDescriptor = companionIrClass.descriptor.findDeclarationByName("byValue")!! + val byValueIrFunction = createFunction(byValueFunctionDescriptor) + val irValueParameter = byValueIrFunction.valueParameters.first() + // val values: Array = values() + // var i: Int = 0 + // val size: Int = values.size + // while (i < size) { + // val entry: E = values[i] + // if (entry.value == arg) { + // return entry + // } + // i++ + // } + // throw NPE + byValueIrFunction.body = irBuilder(irBuiltIns, byValueIrFunction.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +irReturn(irBlock { + val values = irTemporaryVar(irCall(valuesIrFunctionSymbol)) + val inductionVariable = irTemporaryVar(irInt(0)) + val arrayClass = values.type.classOrNull!! + val valuesSize = irCall(symbols.arraySize.getValue(arrayClass)).also { irCall -> + irCall.dispatchReceiver = irGet(values) + } + val getElementFn = symbols.arrayGet.getValue(arrayClass) + val plusFun = symbols.intPlusInt + val lessFunctionSymbol = irBuiltIns.lessFunByOperandType.getValue(irBuiltIns.intClass) + +irWhile().also { loop -> + loop.condition = irCall(lessFunctionSymbol).also { irCall -> + irCall.putValueArgument(0, irGet(inductionVariable)) + irCall.putValueArgument(1, valuesSize) + } + loop.body = irBlock { + val untypedEntry = irCall(getElementFn).also { irCall -> + irCall.dispatchReceiver = irGet(values) + irCall.putValueArgument(0, irGet(inductionVariable)) + } + val entry = irTemporaryVar(irImplicitCast(untypedEntry, byValueIrFunction.returnType)) + val valueGetter = entry.type.getClass()!!.getPropertyGetter("value")!! + val entryValue = irGet(irValueParameter.type, irGet(entry), valueGetter) + +irIfThenElse( + type = irBuiltIns.unitType, + condition = irEquals(entryValue, irGet(irValueParameter)), + thenPart = irReturn(irGet(entry)), + elsePart = irSetVar( + inductionVariable, + irCallOp(plusFun, irBuiltIns.intType, + irGet(inductionVariable), + irInt(1) + ) + ) + ) + } + } + +irCall(symbols.ThrowNullPointerException) + }) + } + return byValueIrFunction + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumClassGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumClassGenerator.kt new file mode 100644 index 00000000000..a58dee29a00 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumClassGenerator.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop.cenum + +import org.jetbrains.kotlin.backend.konan.descriptors.enumEntries +import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin +import org.jetbrains.kotlin.backend.konan.ir.interop.findDeclarationByName +import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrEnumConstructorCallImpl +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi2ir.generators.DeclarationGenerator +import org.jetbrains.kotlin.psi2ir.generators.EnumClassMembersGenerator +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +private fun extractConstantValue(descriptor: DeclarationDescriptor, type: String): ConstantValue<*>? = + descriptor.annotations + .findAnnotation(cEnumEntryValueAnnotationName.child(Name.identifier(type))) + ?.allValueArguments + ?.getValue(Name.identifier("value")) + +private val cEnumEntryValueAnnotationName = FqName("kotlinx.cinterop.internal.ConstantValue") + +private val cEnumEntryValueTypes = setOf( + "Byte", "Short", "Int", "Long", + "UByte", "UShort", "UInt", "ULong" +) + +internal class CEnumClassGenerator( + val context: GeneratorContext, + override val stubGenerator: DeclarationStubGenerator, + private val cEnumCompanionGenerator: CEnumCompanionGenerator, + private val cEnumVarClassGenerator: CEnumVarClassGenerator +) : DescriptorToIrTranslationMixin { + + override val irBuiltIns: IrBuiltIns = context.irBuiltIns + override val symbolTable: SymbolTable = context.symbolTable + override val typeTranslator: TypeTranslator = context.typeTranslator + + private val enumClassMembersGenerator = EnumClassMembersGenerator(DeclarationGenerator(context)) + + /** + * Searches for an IR class for [classDescriptor] in symbol table. + * Generates one if absent. + */ + fun findOrGenerateCEnum(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass { + val irClassSymbol = symbolTable.referenceClass(classDescriptor) + return if (!irClassSymbol.isBound) { + provideIrClassForCEnum(classDescriptor).also { + it.patchDeclarationParents(parent) + parent.declarations += it + } + } else { + irClassSymbol.owner + } + } + + /** + * The main function that for given [descriptor] of the enum generates the whole + * IR tree including entries, CEnumVar class, and companion objects. + */ + private fun provideIrClassForCEnum(descriptor: ClassDescriptor): IrClass = + createClass(descriptor) { enumIrClass -> + enumIrClass.addMember(createEnumPrimaryConstructor(descriptor)) + enumIrClass.addMember(createValueProperty(enumIrClass)) + descriptor.enumEntries.mapTo(enumIrClass.declarations) { entryDescriptor -> + createEnumEntry(descriptor, entryDescriptor) + } + enumClassMembersGenerator.generateSpecialMembers(enumIrClass) + enumIrClass.addChild(cEnumCompanionGenerator.generate(enumIrClass)) + enumIrClass.addChild(cEnumVarClassGenerator.generate(enumIrClass)) + } + + /** + * Creates `value` property that stores integral value of the enum. + */ + private fun createValueProperty(irClass: IrClass): IrProperty { + val propertyDescriptor = irClass.descriptor + .findDeclarationByName("value") + ?: error("No `value` property in ${irClass.name}") + val irProperty = createProperty(propertyDescriptor) + symbolTable.withScope(propertyDescriptor) { + irProperty.backingField = symbolTable.declareField( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, IrDeclarationOrigin.PROPERTY_BACKING_FIELD, + propertyDescriptor, propertyDescriptor.type.toIrType(), Visibilities.PRIVATE + ).also { + it.initializer = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).run { + irExprBody(irGet(irClass.primaryConstructor!!.valueParameters[0])) + } + } + irProperty.getter = createFunction( + propertyDescriptor.getter!!, + IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR + ).also { getter -> + getter.correspondingPropertySymbol = irProperty.symbol + getter.body = irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +irReturn( + irGetField( + irGet(getter.dispatchReceiverParameter!!), + irProperty.backingField!! + ) + ) + } + } + } + return irProperty + } + + private fun createEnumEntry(enumDescriptor: ClassDescriptor, entryDescriptor: ClassDescriptor): IrEnumEntry { + return symbolTable.declareEnumEntry( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, + IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB, entryDescriptor + ).also { enumEntry -> + enumEntry.initializerExpression = IrEnumConstructorCallImpl( + SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, + type = irBuiltIns.unitType, + symbol = symbolTable.referenceConstructor(enumDescriptor.unsubstitutedPrimaryConstructor!!), + typeArgumentsCount = 0 // enums can't be generic + ).also { + it.putValueArgument(0, extractEnumEntryValue(entryDescriptor)) + } + } + } + + /** + * Every enum entry that came from metadata-based interop library is annotated with + * [kotlinx.cinterop.internal.ConstantValue] annotation that holds internal constant value of the + * corresponding entry. + * + * This function extracts value from the annotation. + */ + private fun extractEnumEntryValue(entryDescriptor: ClassDescriptor): IrExpression = + cEnumEntryValueTypes.firstNotNullResult { extractConstantValue(entryDescriptor, it) } ?.let { + context.constantValueGenerator.generateConstantValueAsExpression(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, it) + } ?: error("Enum entry $entryDescriptor has no appropriate @$cEnumEntryValueAnnotationName annotation!") + + private fun createEnumPrimaryConstructor(descriptor: ClassDescriptor): IrConstructor { + val irConstructor = createConstructor(descriptor.unsubstitutedPrimaryConstructor!!) + val enumConstructor = context.builtIns.enum.constructors.single() + irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +IrEnumConstructorCallImpl( + startOffset, endOffset, + context.irBuiltIns.unitType, + symbolTable.referenceConstructor(enumConstructor), + typeArgumentsCount = 1 // kotlin.Enum has a single type parameter. + ).apply { + putTypeArgument(0, descriptor.defaultType.toIrType()) + } + +irInstanceInitializer(symbolTable.referenceClass(descriptor)) + } + return irConstructor + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumCompanionGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumCompanionGenerator.kt new file mode 100644 index 00000000000..9353317761d --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumCompanionGenerator.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop.cenum + +import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull +import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin +import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.builders.irBlockBody +import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl +import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns + +private val cEnumEntryAliasAnnonation = FqName("kotlinx.cinterop.internal.CEnumEntryAlias") + +internal class CEnumCompanionGenerator( + context: GeneratorContext, + override val stubGenerator: DeclarationStubGenerator, + private val cEnumByValueFunctionGenerator: CEnumByValueFunctionGenerator +) : DescriptorToIrTranslationMixin { + + override val irBuiltIns: IrBuiltIns = context.irBuiltIns + override val symbolTable: SymbolTable = context.symbolTable + override val typeTranslator: TypeTranslator = context.typeTranslator + + // Depends on already generated `.values()` irFunction. + fun generate(enumClass: IrClass): IrClass = + createClass(enumClass.descriptor.companionObjectDescriptor!!) { companionIrClass -> + companionIrClass.superTypes += irBuiltIns.anyType + companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor)) + val valuesFunction = enumClass.functions.single { it.name.identifier == "values" }.symbol + val byValueIrFunction = cEnumByValueFunctionGenerator + .generateByValueFunction(companionIrClass, valuesFunction) + companionIrClass.addMember(byValueIrFunction) + findEntryAliases(companionIrClass.descriptor) + .map { declareEntryAliasProperty(it, enumClass) } + .forEach(companionIrClass::addMember) + } + + private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor): IrConstructor { + val anyPrimaryConstructor = companionObjectDescriptor.builtIns.any.unsubstitutedPrimaryConstructor!! + val superConstructorSymbol = symbolTable.referenceConstructor(anyPrimaryConstructor) + return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { + it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +IrDelegatingConstructorCallImpl( + startOffset, endOffset, context.irBuiltIns.unitType, + superConstructorSymbol + ) + +irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor)) + } + } + } + + /** + * Returns all properties in companion object that represent aliases to + * enum entries. + */ + private fun findEntryAliases(companionDescriptor: ClassDescriptor) = + companionDescriptor.defaultType.memberScope.getContributedDescriptors() + .filterIsInstance() + .filter { it.annotations.hasAnnotation(cEnumEntryAliasAnnonation) } + + private fun fundCorrespondingEnumEntrySymbol(aliasDescriptor: PropertyDescriptor, irClass: IrClass): IrEnumEntrySymbol { + val enumEntryName = aliasDescriptor.annotations + .findAnnotation(cEnumEntryAliasAnnonation)!! + .getArgumentValueOrNull("entryName") + return irClass.declarations.filterIsInstance() + .single { it.name.identifier == enumEntryName }.symbol + } + + private fun generateAliasGetterBody(getter: IrSimpleFunction, entrySymbol: IrEnumEntrySymbol): IrBody = + irBuilder(irBuiltIns, getter.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +irReturn( + IrGetEnumValueImpl(startOffset, endOffset, entrySymbol.owner.parentAsClass.defaultType, entrySymbol) + ) + } + + private fun declareEntryAliasProperty(propertyDescriptor: PropertyDescriptor, enumClass: IrClass): IrProperty { + val entrySymbol = fundCorrespondingEnumEntrySymbol(propertyDescriptor, enumClass) + return createProperty(propertyDescriptor).also { + it.getter!!.body = generateAliasGetterBody(it.getter!!, entrySymbol) + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumVarClassGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumVarClassGenerator.kt new file mode 100644 index 00000000000..eafb70f7275 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cenum/CEnumVarClassGenerator.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan.ir.interop.cenum + +import org.jetbrains.kotlin.backend.konan.InteropBuiltIns +import org.jetbrains.kotlin.backend.konan.descriptors.getArgumentValueOrNull +import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin +import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.builders.irBlockBody +import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.builders.irInt +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.addMember +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext + +private val typeSizeAnnotation = FqName("kotlinx.cinterop.internal.CEnumVarTypeSize") + +internal class CEnumVarClassGenerator( + context: GeneratorContext, + override val stubGenerator: DeclarationStubGenerator, + private val interopBuiltIns: InteropBuiltIns +) : DescriptorToIrTranslationMixin { + + override val irBuiltIns: IrBuiltIns = context.irBuiltIns + override val symbolTable: SymbolTable = context.symbolTable + override val typeTranslator: TypeTranslator = context.typeTranslator + + fun generate(enumIrClass: IrClass): IrClass { + val enumVarClassDescriptor = enumIrClass.descriptor.unsubstitutedMemberScope + .getContributedClassifier(Name.identifier("Var"), NoLookupLocation.FROM_BACKEND)!! as ClassDescriptor + return createClass(enumVarClassDescriptor) { enumVarClass -> + enumVarClass.addMember(createPrimaryConstructor(enumVarClass)) + enumVarClass.addMember(createCompanionObject(enumVarClass)) + enumVarClass.addMember(createValueProperty(enumVarClass)) + } + } + + private fun createValueProperty(enumVarClass: IrClass): IrProperty { + val valuePropertyDescriptor = enumVarClass.descriptor.unsubstitutedMemberScope + .getContributedVariables(Name.identifier("value"), NoLookupLocation.FROM_BACKEND).single() + return createProperty(valuePropertyDescriptor) + } + + private fun createPrimaryConstructor(enumVarClass: IrClass): IrConstructor { + val irConstructor = createConstructor(enumVarClass.descriptor.unsubstitutedPrimaryConstructor!!) + val enumVarConstructorSymbol = symbolTable.referenceConstructor( + interopBuiltIns.cEnumVar.unsubstitutedPrimaryConstructor!! + ) + irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +IrDelegatingConstructorCallImpl( + startOffset, endOffset, context.irBuiltIns.unitType, enumVarConstructorSymbol + ).also { + it.putValueArgument(0, irGet(irConstructor.valueParameters[0])) + } + +irInstanceInitializer(symbolTable.referenceClass(enumVarClass.descriptor)) + } + return irConstructor + } + + private fun createCompanionObject(enumVarClass: IrClass): IrClass = + createClass(enumVarClass.descriptor.companionObjectDescriptor!!) { companionIrClass -> + val typeSize = companionIrClass.descriptor.annotations + .findAnnotation(typeSizeAnnotation)!! + .getArgumentValueOrNull("size")!! + companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, typeSize)) + } + + private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, typeSize: Int): IrConstructor { + val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cPrimitiveVarType.unsubstitutedPrimaryConstructor!!) + return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { + it.body = irBuilder(irBuiltIns, it.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +IrDelegatingConstructorCallImpl( + startOffset, endOffset, context.irBuiltIns.unitType, + superConstructorSymbol + ).also { + it.putValueArgument(0, irInt(typeSize)) + } + +irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor)) + } + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt index 8aed75315fb..84a58b2a0a4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt @@ -49,12 +49,17 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import java.lang.reflect.Proxy -internal fun irBuilder(irBuiltIns: IrBuiltIns, scopeOwnerSymbol: IrSymbol): IrBuilderWithScope = +internal fun irBuilder( + irBuiltIns: IrBuiltIns, + scopeOwnerSymbol: IrSymbol, + startOffset: Int = UNDEFINED_OFFSET, + endOffset: Int = UNDEFINED_OFFSET +): IrBuilderWithScope = object : IrBuilderWithScope( IrGeneratorContextBase(irBuiltIns), Scope(scopeOwnerSymbol), - UNDEFINED_OFFSET, - UNDEFINED_OFFSET + startOffset, + endOffset ) {} //TODO: delete file on next kotlin dependency update