diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt index 1e61bcb8c73..8a9ecf9a58b 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/internal/Annotations.kt @@ -2,7 +2,24 @@ package kotlinx.cinterop.internal @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) -annotation class CStruct(val spelling: String) +annotation class CStruct(val spelling: String) { + @Retention(AnnotationRetention.BINARY) + @Target( + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER + ) + annotation class MemberAt(val offset: Long) + + @Retention(AnnotationRetention.BINARY) + @Target( + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER + ) + annotation class BitField(val offset: Long, val size: Int) + + @Retention(AnnotationRetention.BINARY) + annotation class VarType(val size: Long, val align: Int) +} @Target( AnnotationTarget.FUNCTION, diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt index 62f5e8477f4..f7a935eecfe 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt @@ -43,6 +43,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) { val cEnum = this.packageScope.getContributedClass("CEnum") val cEnumVar = this.packageScope.getContributedClass("CEnumVar") + val cStructVar = this.packageScope.getContributedClass("CStructVar") + val cStructVarType = cStructVar.defaultType.memberScope.getContributedClass("Type") val cPrimitiveVar = this.packageScope.getContributedClass("CPrimitiveVar") val cPrimitiveVarType = cPrimitiveVar.defaultType.memberScope.getContributedClass("Type") 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 9f714099edd..eb52db1612b 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 @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary 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.ir.interop.IrProviderForCEnumAndCStructStubs import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport @@ -207,11 +207,15 @@ internal val psiToIrPhase = konanUnitPhase( moduleDescriptor, symbolTable, config.configuration.languageVersionSettings ) - val irProviderForCEnums = IrProviderForCEnumStubs(generatorContext, interopBuiltIns, stubGenerator, symbols) - val irProviderForInteropStubs = IrProviderForInteropStubs { symbol -> - irProviderForCEnums.canHandleSymbol(symbol) - } - val irProviders = listOf(irProviderForCEnums, irProviderForInteropStubs, functionIrClassFactory, deserializer, stubGenerator) + val irProviderForCEnumsAndCStructs = IrProviderForCEnumAndCStructStubs(generatorContext, interopBuiltIns, stubGenerator, symbols) + val irProviderForInteropStubs = IrProviderForInteropStubs(irProviderForCEnumsAndCStructs::canHandleSymbol) + val irProviders = listOf( + irProviderForCEnumsAndCStructs, + irProviderForInteropStubs, + functionIrClassFactory, + deserializer, + stubGenerator + ) stubGenerator.setIrProviders(irProviders) expectDescriptorToSymbol = mutableMapOf() @@ -233,7 +237,7 @@ internal val psiToIrPhase = konanUnitPhase( } modulesWithoutDCE .filter(ModuleDescriptor::isFromInteropLibrary) - .forEach(irProviderForCEnums::buildAllEnumsFrom) + .forEach(irProviderForCEnumsAndCStructs::buildAllEnumsAndStructsFrom) irModule = module irModules = deserializer.modules.filterValues { llvmModuleSpecification.containsModule(it) } @@ -243,7 +247,7 @@ internal val psiToIrPhase = konanUnitPhase( (listOf(irModule!!) + deserializer.modules.values) .single { it.descriptor.isKonanStdlib() } - irProviderForCEnums.module = module + irProviderForCEnumsAndCStructs.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 index 111583a6687..82e18a1bb54 100644 --- 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 @@ -15,6 +15,7 @@ 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.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.types.KotlinType @@ -57,6 +58,9 @@ internal interface DescriptorToIrTranslationMixin { descriptor.typeConstructor.supertypes.mapTo(irClass.superTypes) { it.toIrType() } + descriptor.annotations.mapTo(irClass.annotations) { + typeTranslator.constantValueGenerator.generateAnnotationConstructorCall(it)!! + } irClass.createParameterDeclarations() builder(irClass) createFakeOverrides(descriptor).forEach(irClass::addMember) @@ -102,6 +106,9 @@ internal fun IrBuilder.irInstanceInitializer(classSymbol: IrClassSymbol): IrExpr internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): Boolean = interopBuiltIns.cEnum in this.getSuperInterfaces() +internal fun ClassDescriptor.inheritsFromCStructVar(interopBuiltIns: InteropBuiltIns): Boolean = + interopBuiltIns.cStructVar == this.getSuperClassNotAny() + /** * All enums that come from interop library implement CEnum interface. * This function checks that given symbol located in subtree of @@ -110,4 +117,14 @@ internal fun ClassDescriptor.implementsCEnum(interopBuiltIns: InteropBuiltIns): internal fun IrSymbol.findCEnumDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? = descriptor.parentsWithSelf .filterIsInstance() - .firstOrNull { it.implementsCEnum(interopBuiltIns) } \ No newline at end of file + .firstOrNull { it.implementsCEnum(interopBuiltIns) } + +/** + * All structs that come from interop library inherit from CStructVar class. + * This function checks that given symbol located in subtree of + * CStructVar inheritor. + */ +internal fun IrSymbol.findCStructDescriptor(interopBuiltIns: InteropBuiltIns): ClassDescriptor? = + descriptor.parentsWithSelf + .filterIsInstance() + .firstOrNull { it.inheritsFromCStructVar(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/IrProviderForCEnumAndCStructStubs.kt similarity index 70% rename from backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/IrProviderForCEnumStubs.kt rename to backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/IrProviderForCEnumAndCStructStubs.kt index 761c07ea137..c02cd5c76c0 100644 --- 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/IrProviderForCEnumAndCStructStubs.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.ir.interop.cenum.CEnumByValueFunctionG 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.backend.konan.ir.interop.cstruct.CStructVarClassGenerator import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor @@ -30,7 +31,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter /** * 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: + * We use a different approach for CEnums and CStructVars 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 @@ -38,7 +39,7 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter * 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( +internal class IrProviderForCEnumAndCStructStubs( context: GeneratorContext, private val interopBuiltIns: InteropBuiltIns, stubGenerator: DeclarationStubGenerator, @@ -57,6 +58,8 @@ internal class IrProviderForCEnumStubs( CEnumVarClassGenerator(context, stubGenerator, interopBuiltIns) private val cEnumClassGenerator = CEnumClassGenerator(context, stubGenerator, cEnumCompanionGenerator, cEnumVarClassGenerator) + private val cStructClassGenerator = + CStructVarClassGenerator(context, stubGenerator, interopBuiltIns) var module: IrModuleFragment? = null set(value) { @@ -71,23 +74,36 @@ internal class IrProviderForCEnumStubs( fun canHandleSymbol(symbol: IrSymbol): Boolean { if (!symbol.descriptor.module.isFromInteropLibrary()) return false return symbol.findCEnumDescriptor(interopBuiltIns) != null + || symbol.findCStructDescriptor(interopBuiltIns) != null } - fun buildAllEnumsFrom(interopModule: ModuleDescriptor) { - interopModule.getPackageFragments() - .flatMap { it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) } - .filterIsInstance() - .filter { it.implementsCEnum(interopBuiltIns) } - .forEach { cEnumClassGenerator.findOrGenerateCEnum(it, irParentFor(it)) } + fun buildAllEnumsAndStructsFrom(interopModule: ModuleDescriptor) = interopModule.getPackageFragments() + .flatMap { it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) } + .filterIsInstance() + .forEach { + when { + it.implementsCEnum(interopBuiltIns) -> + cEnumClassGenerator.findOrGenerateCEnum(it, irParentFor(it)) + it.inheritsFromCStructVar(interopBuiltIns) -> + cStructClassGenerator.findOrGenerateCStruct(it, irParentFor(it)) + } + } + + private fun generateIrIfNeeded(symbol: IrSymbol) { + // TODO: These `findOrGenerate` calls generate a whole subtree. + // This a simple but clearly suboptimal solution. + symbol.findCEnumDescriptor(interopBuiltIns)?.let { enumDescriptor -> + cEnumClassGenerator.findOrGenerateCEnum(enumDescriptor, irParentFor(enumDescriptor)) + } + symbol.findCStructDescriptor(interopBuiltIns)?.let { structDescriptor -> + cStructClassGenerator.findOrGenerateCStruct(structDescriptor, irParentFor(structDescriptor)) + } } 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)) + generateIrIfNeeded(symbol) return when (symbol) { is IrClassSymbol -> symbolTable.referenceClass(symbol.descriptor).owner is IrEnumEntrySymbol -> symbolTable.referenceEnumEntry(symbol.descriptor).owner @@ -100,8 +116,8 @@ internal class IrProviderForCEnumStubs( 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) + IrFileImpl(NaiveSourceBasedFileEntryImpl("CTypeDefinitions"), packageFragmentDescriptor).also { + this@IrProviderForCEnumAndCStructStubs.module?.files?.add(it) } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarClassGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarClassGenerator.kt new file mode 100644 index 00000000000..6712917e148 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarClassGenerator.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2020 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.cstruct + +import org.jetbrains.kotlin.backend.konan.InteropBuiltIns +import org.jetbrains.kotlin.backend.konan.ir.interop.DescriptorToIrTranslationMixin +import org.jetbrains.kotlin.backend.konan.ir.interop.irInstanceInitializer +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +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.irGet +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer +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.psi2ir.generators.GeneratorContext + +internal class CStructVarClassGenerator( + 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 + + private val companionGenerator = CStructVarCompanionGenerator(context, stubGenerator, interopBuiltIns) + + fun findOrGenerateCStruct(classDescriptor: ClassDescriptor, parent: IrDeclarationContainer): IrClass { + val irClassSymbol = symbolTable.referenceClass(classDescriptor) + return if (!irClassSymbol.isBound) { + provideIrClassForCStruct(classDescriptor).also { + it.patchDeclarationParents(parent) + parent.declarations += it + } + } else { + irClassSymbol.owner + } + } + + private fun provideIrClassForCStruct(descriptor: ClassDescriptor): IrClass = + createClass(descriptor) { irClass -> + irClass.addMember(createPrimaryConstructor(irClass)) + irClass.addMember(companionGenerator.generate(descriptor)) + descriptor.unsubstitutedMemberScope + .getContributedDescriptors() + .filterIsInstance() + .filter { it.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE } + .map(this::createProperty) + .forEach(irClass::addMember) + } + + private fun createPrimaryConstructor(irClass: IrClass): IrConstructor { + val enumVarConstructorSymbol = symbolTable.referenceConstructor( + interopBuiltIns.cStructVar.unsubstitutedPrimaryConstructor!! + ) + return createConstructor(irClass.descriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor -> + 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(irClass.descriptor)) + } + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarCompanionGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarCompanionGenerator.kt new file mode 100644 index 00000000000..082f5067876 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/interop/cstruct/CStructVarCompanionGenerator.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2020 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.cstruct + +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.ir.builders.irBlockBody +import org.jetbrains.kotlin.ir.builders.irInt +import org.jetbrains.kotlin.ir.builders.irLong +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +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.ir.util.irBuilder +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext + +private val varTypeAnnotationFqName = FqName("kotlinx.cinterop.internal.CStruct.VarType") + +internal class CStructVarCompanionGenerator( + 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(structDescriptor: ClassDescriptor): IrClass = + createClass(structDescriptor.companionObjectDescriptor!!) { companionIrClass -> + val annotation = companionIrClass.descriptor.annotations + .findAnnotation(varTypeAnnotationFqName)!! + val size = annotation.getArgumentValueOrNull("size")!! + val align = annotation.getArgumentValueOrNull("align")!! + companionIrClass.addMember(createCompanionConstructor(companionIrClass.descriptor, size, align)) + } + + private fun createCompanionConstructor(companionObjectDescriptor: ClassDescriptor, size: Long, align: Int): IrConstructor { + val superConstructorSymbol = symbolTable.referenceConstructor(interopBuiltIns.cStructVarType.unsubstitutedPrimaryConstructor!!) + return createConstructor(companionObjectDescriptor.unsubstitutedPrimaryConstructor!!).also { irConstructor -> + irConstructor.body = irBuilder(irBuiltIns, irConstructor.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET).irBlockBody { + +IrDelegatingConstructorCallImpl( + startOffset, endOffset, context.irBuiltIns.unitType, + superConstructorSymbol + ).also { + it.putValueArgument(0, irLong(size)) + it.putValueArgument(1, irInt(align)) + } + +irInstanceInitializer(symbolTable.referenceClass(companionObjectDescriptor)) + } + } + } +} \ No newline at end of file