From c2d067924dd394458db2921bfc2c761cc97836b0 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Wed, 27 Dec 2017 13:30:12 +0300 Subject: [PATCH] Rework C bindings generator to pure descriptor-based system. (#1193) --- .../kotlin/backend/konan/CAdapterGenerator.kt | 180 ++++++++++++++---- .../konan/descriptors/DeepPrintVisitor.kt | 12 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 6 +- .../konan/serialization/DeserializerDriver.kt | 2 +- backend.native/tests/build.gradle | 4 +- .../tests/produce_dynamic/simple/hello.kt | 7 + .../tests/produce_dynamic/simple/main.c | 4 + 7 files changed, 162 insertions(+), 53 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index b3480d44e23..a159dac3ad0 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -25,15 +25,14 @@ import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.descriptors.isUnit import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid -import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.konan.target.* +import org.jetbrains.kotlin.name.isChildOf import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.module private enum class ScopeKind { TOP, @@ -100,11 +99,14 @@ private fun isExportedFunction(descriptor: FunctionDescriptor): Boolean { } private fun isExportedClass(descriptor: ClassDescriptor): Boolean { - if (!descriptor.isEffectivelyPublicApi) - return false + if (!descriptor.isEffectivelyPublicApi) return false + // No sense to export annotations. + if (DescriptorUtils.isAnnotationClass(descriptor)) return false // Do not export types with type parameters. // TODO: is it correct? - return descriptor.declaredTypeParameters.isEmpty() + if (!descriptor.declaredTypeParameters.isEmpty()) return false + + return true } private class ExportedElementScope(val kind: ScopeKind, val name: String) { @@ -113,7 +115,6 @@ private class ExportedElementScope(val kind: ScopeKind, val name: String) { private val scopeNames = mutableSetOf() private val scopeNamesMap = mutableMapOf() - override fun toString(): String { return "$kind: $name ${elements.joinToString(", ")} ${scopes.joinToString("\n")}" } @@ -130,6 +131,7 @@ private class ExportedElementScope(val kind: ScopeKind, val name: String) { fun scopeUniqueName(descriptor: DeclarationDescriptor): String { scopeNamesMap[descriptor]?.apply { return this } var computedName = when (descriptor) { + is ConstructorDescriptor -> "${descriptor.constructedClass.fqNameSafe.shortName().asString()}" is PropertyGetterDescriptor -> "get_${descriptor.correspondingProperty.name.asString()}" is PropertySetterDescriptor -> "set_${descriptor.correspondingProperty.name.asString()}" else -> descriptor.fqNameSafe.shortName().asString() @@ -198,7 +200,7 @@ private class ExportedElement(val kind: ElementKind, } } - fun uniqueName(descriptor: FunctionDescriptor) = scope.scopeUniqueName(descriptor) + fun uniqueName(descriptor: DeclarationDescriptor) = scope.scopeUniqueName(descriptor) val isFunction = declaration is FunctionDescriptor val isClass = declaration is ClassDescriptor @@ -210,8 +212,7 @@ private class ExportedElement(val kind: ElementKind, val descriptor = declaration val original = descriptor.original as FunctionDescriptor val returned = when { - original is ConstructorDescriptor -> - scope.scopeUniqueName(original.constructedClass) to original.constructedClass + original is ConstructorDescriptor -> uniqueName(original) to original.constructedClass // Suspend functions actually return Any?. original.isSuspend -> uniqueName(original) to TypeUtils.getClassDescriptor(owner.context.builtIns.nullableAnyType)!! @@ -334,7 +335,7 @@ private class ExportedElement(val kind: ElementKind, if (type.constructor.declarationDescriptor is TypeParameterDescriptor) return val clazz = TypeUtils.getClassDescriptor(type) if (clazz == null) { - println("cannot get class for $type") + context.reportCompilationWarning("cannot get class for $type") } else { set += clazz } @@ -356,58 +357,156 @@ private class ExportedElement(val kind: ElementKind, } } +private fun getPackagesFqNames(module: ModuleDescriptor): Set { + val result = mutableSetOf() + + fun getSubPackages(fqName: FqName) { + result.add(fqName) + module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) } + } + + getSubPackages(FqName.ROOT) + return result +} + +private fun ModuleDescriptor.getPackageFragments(): List = + getPackagesFqNames(this).flatMap { + getPackage(it).fragments.filter { it.module == this } + } + internal class CAdapterGenerator( - val context: Context, internal val codegen: CodeGenerator) : IrElementVisitorVoid { + val context: Context, internal val codegen: CodeGenerator) : DeclarationDescriptorVisitor { private val scopes = mutableListOf() internal val prefix = context.config.moduleId private lateinit var outputStreamWriter: PrintWriter - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) + private fun visitChildren(descriptors: Collection) { + for (descriptor in descriptors) { + descriptor.accept(this, null) + } } - override fun visitPackageFragment(declaration: IrPackageFragment) { - val fqName = declaration.packageFragmentDescriptor.fqName - val name = if (fqName.isRoot) "root" else fqName.shortName().asString() - val packageScope = ExportedElementScope(ScopeKind.PACKAGE, name) - scopes.last().scopes += packageScope - scopes.push(packageScope) - declaration.acceptChildrenVoid(this) - scopes.pop() + private fun visitChildren(descriptor: DeclarationDescriptor) { + descriptor.accept(this, null) } - override fun visitProperty(declaration: IrProperty) { - val descriptor = declaration.descriptor - if (!descriptor.isEffectivelyPublicApi || !descriptor.kind.isReal) return - ExportedElement(ElementKind.PROPERTY, scopes.last(), declaration.descriptor, this) + override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, ignored: Void?): Boolean { + if (!isExportedFunction(descriptor)) return true + ExportedElement(ElementKind.FUNCTION, scopes.last(), descriptor, this) + return true } - override fun visitFunction(declaration: IrFunction) { - val descriptor = declaration.descriptor - if (!isExportedFunction(descriptor)) return - ExportedElement(ElementKind.FUNCTION, scopes.last(), declaration.descriptor, this) + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, ignored: Void?): Boolean { + if (!isExportedFunction(descriptor)) return true + ExportedElement(ElementKind.FUNCTION, scopes.last(), descriptor, this) + return true } - override fun visitClass(declaration: IrClass) { - val descriptor = declaration.descriptor - if (!isExportedClass(descriptor)) return + override fun visitClassDescriptor(descriptor: ClassDescriptor, ignored: Void?): Boolean { + if (!isExportedClass(descriptor)) return true // TODO: fix me! val shortName = descriptor.fqNameSafe.shortName() if (shortName.isSpecial || shortName.asString().contains("")) - return + return true val classScope = ExportedElementScope(ScopeKind.CLASS, shortName.asString()) scopes.last().scopes += classScope scopes.push(classScope) // Add type getter. ExportedElement(ElementKind.TYPE, scopes.last(), descriptor, this) - declaration.acceptChildrenVoid(this) + visitChildren(descriptor.getConstructors()) + visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().memberScope)) scopes.pop() + return true } - override fun visitModuleFragment(declaration: IrModuleFragment) { + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, ignored: Void?): Boolean { + descriptor.getter?.let { visitChildren(it) } + descriptor.setter?.let { visitChildren(it) } + return true + } + + override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, ignored: Void?): Boolean { + if (!isExportedFunction(descriptor)) return true + ExportedElement(ElementKind.FUNCTION, scopes.last(), descriptor, this) + return true + } + + override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, ignored: Void?): Boolean { + if (!isExportedFunction(descriptor)) return true + ExportedElement(ElementKind.FUNCTION, scopes.last(), descriptor, this) + return true + } + + override fun visitScriptDescriptor(descriptor: ScriptDescriptor, ignored: Void?): Boolean { + context.reportCompilationWarning("visitScriptDescriptor() is ignored") + return true + } + + override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, ignored: Void?): Boolean { + if (descriptor.module != context.moduleDescriptor) return true + val fragments = descriptor.module.getPackage(FqName.ROOT).fragments.filter { it.module == context.moduleDescriptor } + visitChildren(fragments) + return true + } + + override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, ignored: Void?): Boolean { + TODO("visitValueParameterDescriptor() shall not be seen") + } + + override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor?, ignored: Void?): Boolean { + TODO("visitReceiverParameterDescriptor() shall not be seen") + } + + override fun visitVariableDescriptor(descriptor: VariableDescriptor, ignored: Void?): Boolean { + context.reportCompilationWarning("visitVariableDescriptor() is ignored for now") + return true + } + + override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, ignored: Void?): Boolean { + context.reportCompilationWarning("visitTypeParameterDescriptor() is ignored for now") + return true + } + + private val seenPackageFragments = mutableSetOf() + private var currentPackageFragments: List = emptyList() + + override fun visitModuleDeclaration(descriptor: ModuleDescriptor, ignored: Void?): Boolean { + currentPackageFragments = descriptor.getPackageFragments().sortedWith( + Comparator { + o1, o2 -> o1.fqName.toString().compareTo(o2.fqName.toString()) + }) + seenPackageFragments.clear() + descriptor.getPackage(FqName.ROOT).accept(this, null) + return true + } + + override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, ignored: Void?): Boolean { + context.reportCompilationWarning("visitTypeAliasDescriptor() is ignored for now") + return true + } + + override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, ignored: Void?): Boolean { + val fqName = descriptor.fqName + val name = if (fqName.isRoot) "root" else fqName.shortName().asString() + val packageScope = ExportedElementScope(ScopeKind.PACKAGE, name) + scopes.last().scopes += packageScope + scopes.push(packageScope) + visitChildren(DescriptorUtils.getAllDescriptors(descriptor.getMemberScope())) + for (currentPackageFragment in currentPackageFragments) { + if (!seenPackageFragments.contains(currentPackageFragment) && + currentPackageFragment.fqName.isChildOf(descriptor.fqName)) { + seenPackageFragments += currentPackageFragment + visitChildren(currentPackageFragment) + } + } + scopes.pop() + return true + } + + fun generateBindings() { scopes.push(ExportedElementScope(ScopeKind.TOP, "kotlin")) - declaration.acceptChildrenVoid(this) + context.moduleDescriptor.accept(this, null) val top = scopes.pop() assert(scopes.isEmpty() && top.kind == ScopeKind.TOP) @@ -616,12 +715,13 @@ internal class CAdapterGenerator( } private val simpleNameMapping = mapOf( - "" to "thiz" + "" to "thiz", + "" to "set" ) private val primitiveTypeMapping = mapOf( "kotlin.Byte" to "${prefix}_KByte", - "kotlin.Short" to "(${prefix}_KShort", + "kotlin.Short" to "${prefix}_KShort", "kotlin.Int" to "${prefix}_KInt", "kotlin.Long" to "${prefix}_KLong", "kotlin.Float" to "${prefix}_KFloat", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt index 5e8762b6444..15e0687d7b4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DeepPrintVisitor.kt @@ -16,25 +16,21 @@ package org.jetbrains.kotlin.backend.konan.descriptors -import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.util.nTabs -import org.jetbrains.kotlin.renderer.* - import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -public class DeepPrintVisitor(worker: DeclarationDescriptorVisitor): DeepVisitor< Int>(worker) { +class DeepPrintVisitor(worker: DeclarationDescriptorVisitor): DeepVisitor(worker) { - override public fun visitChildren(descriptor: DeclarationDescriptor?, data: Int): Boolean { + override fun visitChildren(descriptor: DeclarationDescriptor?, data: Int): Boolean { return super.visitChildren(descriptor, data+1) } - override public fun visitChildren(descriptors: Collection, data: Int): Boolean { + override fun visitChildren(descriptors: Collection, data: Int): Boolean { return super.visitChildren(descriptors, data+1) } } -public class PrintVisitor: DeclarationDescriptorVisitor { +class PrintVisitor: DeclarationDescriptorVisitor { fun printDescriptor(descriptor: DeclarationDescriptor, amount: Int): Boolean { println("${nTabs(amount)} ${descriptor.toString()}") diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index c168c7d5222..5a9c52afed3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -280,8 +280,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map(worker) { } + inner class InlineBodiesPrinterVisitor(worker: EmptyDescriptorVisitorVoid): DeepVisitor(worker) inner class InlineBodyPrinter: EmptyDescriptorVisitorVoid() { diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 668aabf220a..ff087d86b22 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2352,7 +2352,9 @@ task produce_dynamic(type: DynamicKonanTest) { "Child.fooParam: c 3\n" + "Impl1.I: d 4 Impl1\n" + "Impl2.I: e 5 Impl2\n" + - "String is Kotlin/Native\n" + "String is Kotlin/Native\n" + + "RO property is 42\n" + + "RW property is 239\n" } task buildKonanTests(type: BuildKonanTest) { diff --git a/backend.native/tests/produce_dynamic/simple/hello.kt b/backend.native/tests/produce_dynamic/simple/hello.kt index 301c158bc10..fcfb6943645 100644 --- a/backend.native/tests/produce_dynamic/simple/hello.kt +++ b/backend.native/tests/produce_dynamic/simple/hello.kt @@ -16,6 +16,13 @@ open class Base { class Child : Base() { override fun fooParam(arg0: String, arg1: Int) = println("Child.fooParam: $arg0 $arg1") + + val roProperty: Int + get() = 42 + + var rwProperty: Int = 0 + get() = field + set(value) { field = value + 1 } } // Interface. diff --git a/backend.native/tests/produce_dynamic/simple/main.c b/backend.native/tests/produce_dynamic/simple/main.c index 238db64d757..01790ec28a7 100644 --- a/backend.native/tests/produce_dynamic/simple/main.c +++ b/backend.native/tests/produce_dynamic/simple/main.c @@ -25,6 +25,10 @@ int main(void) { printf("String is %s\n", string); + printf("RO property is %d\n", __ kotlin.root.Child.get_roProperty(child)); + __ kotlin.root.Child.set_rwProperty(child, 238); + printf("RW property is %d\n", __ kotlin.root.Child.get_rwProperty(child)); + __ DisposeString(string); __ DisposeStablePointer(base.pinned); __ DisposeStablePointer(child.pinned);