Rework C bindings generator to pure descriptor-based system. (#1193)

This commit is contained in:
Nikolay Igotti
2017-12-27 13:30:12 +03:00
committed by GitHub
parent 6315dff4a8
commit c2d067924d
7 changed files with 162 additions and 53 deletions
@@ -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<String>()
private val scopeNamesMap = mutableMapOf<DeclarationDescriptor, String>()
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<FqName> {
val result = mutableSetOf<FqName>()
fun getSubPackages(fqName: FqName) {
result.add(fqName)
module.getSubPackagesOf(fqName) { true }.forEach { getSubPackages(it) }
}
getSubPackages(FqName.ROOT)
return result
}
private fun ModuleDescriptor.getPackageFragments(): List<PackageFragmentDescriptor> =
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<Boolean, Void?> {
private val scopes = mutableListOf<ExportedElementScope>()
internal val prefix = context.config.moduleId
private lateinit var outputStreamWriter: PrintWriter
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
private fun visitChildren(descriptors: Collection<DeclarationDescriptor>) {
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("<anonymous>"))
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<PackageFragmentDescriptor>()
private var currentPackageFragments: List<PackageFragmentDescriptor> = 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(
"<this>" to "thiz"
"<this>" to "thiz",
"<set-?>" 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",
@@ -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<Boolean, Int>): DeepVisitor< Int>(worker) {
class DeepPrintVisitor(worker: DeclarationDescriptorVisitor<Boolean, Int>): DeepVisitor<Int>(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<DeclarationDescriptor>, data: Int): Boolean {
override fun visitChildren(descriptors: Collection<DeclarationDescriptor>, data: Int): Boolean {
return super.visitChildren(descriptors, data+1)
}
}
public class PrintVisitor: DeclarationDescriptorVisitor<Boolean, Int> {
class PrintVisitor: DeclarationDescriptorVisitor<Boolean, Int> {
fun printDescriptor(descriptor: DeclarationDescriptor, amount: Int): Boolean {
println("${nTabs(amount)} ${descriptor.toString()}")
@@ -280,8 +280,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
}
private fun appendCAdapters(declaration: IrModuleFragment) {
declaration.acceptVoid(CAdapterGenerator(context, codegen))
private fun appendCAdapters() {
CAdapterGenerator(context, codegen).generateBindings()
}
//-------------------------------------------------------------------------//
@@ -304,7 +304,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
appendStaticInitializers(context.llvm.staticInitializers)
appendEntryPointSelector(findMainEntryPoint(context))
if (context.isDynamicLibrary) {
appendCAdapters(declaration)
appendCAdapters()
}
}
@@ -58,7 +58,7 @@ internal class DeserializerDriver(val context: Context) {
}
inner class InlineBodiesPrinterVisitor(worker: EmptyDescriptorVisitorVoid): DeepVisitor<Unit>(worker) { }
inner class InlineBodiesPrinterVisitor(worker: EmptyDescriptorVisitorVoid): DeepVisitor<Unit>(worker)
inner class InlineBodyPrinter: EmptyDescriptorVisitorVoid() {
+3 -1
View File
@@ -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) {
@@ -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.
@@ -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);