[Interop] Generate IR for CEnum inheritors
Since there is non-trivial logic inside code that we generate for interop enums, we generate it inside special IrProvider.
This commit is contained in:
committed by
Sergey Bogolepov
parent
8de328e269
commit
44552393ad
+8
-3
@@ -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<DeclarationDescriptor, IrSymbol>()
|
||||
@@ -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",
|
||||
|
||||
+113
@@ -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 <reified T: DeclarationDescriptor> ClassDescriptor.findDeclarationByName(name: String): T? =
|
||||
unsubstitutedMemberScope
|
||||
.getContributedDescriptors()
|
||||
.filterIsInstance<T>()
|
||||
.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, <this> 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<IrDeclaration> {
|
||||
val fakeOverrides = classDescriptor.unsubstitutedMemberScope
|
||||
.getContributedDescriptors()
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
.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<ClassDescriptor>()
|
||||
.firstOrNull { it.implementsCEnum(interopBuiltIns) }
|
||||
+97
@@ -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<PackageFragmentDescriptor, IrFile>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -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<FunctionDescriptor>("byValue")!!
|
||||
val byValueIrFunction = createFunction(byValueFunctionDescriptor)
|
||||
val irValueParameter = byValueIrFunction.valueParameters.first()
|
||||
// val values: Array<E> = 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
|
||||
}
|
||||
}
|
||||
+166
@@ -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<PropertyDescriptor>("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<T> has a single type parameter.
|
||||
).apply {
|
||||
putTypeArgument(0, descriptor.defaultType.toIrType())
|
||||
}
|
||||
+irInstanceInitializer(symbolTable.referenceClass(descriptor))
|
||||
}
|
||||
return irConstructor
|
||||
}
|
||||
}
|
||||
+95
@@ -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<PropertyDescriptor>()
|
||||
.filter { it.annotations.hasAnnotation(cEnumEntryAliasAnnonation) }
|
||||
|
||||
private fun fundCorrespondingEnumEntrySymbol(aliasDescriptor: PropertyDescriptor, irClass: IrClass): IrEnumEntrySymbol {
|
||||
val enumEntryName = aliasDescriptor.annotations
|
||||
.findAnnotation(cEnumEntryAliasAnnonation)!!
|
||||
.getArgumentValueOrNull<String>("entryName")
|
||||
return irClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||
.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -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<Int>("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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user