From 1aa2e26344e0e79c6a8cc10ab244b7ee09b4992b Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Mon, 15 Jan 2018 20:16:30 +0300 Subject: [PATCH] Fix enum entries processing in C reverse interop. (#1232) --- .../kotlin/backend/konan/CAdapterGenerator.kt | 51 +++++++++++++++++-- backend.native/tests/build.gradle | 4 +- .../tests/produce_dynamic/simple/hello.kt | 16 ++++++ .../tests/produce_dynamic/simple/main.c | 10 ++++ 4 files changed, 77 insertions(+), 4 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 bd7c4436d7e..adfbe029563 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.backend.konan +import kotlinx.cinterop.cValuesOf import java.io.PrintWriter import llvm.* import org.jetbrains.kotlin.backend.common.descriptors.allParameters @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.name.isChildOf import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.types.KotlinType private enum class ScopeKind { TOP, @@ -183,7 +185,6 @@ private class ExportedElement(val kind: ElementKind, val result = call(callee, args, exceptionHandler = ExceptionHandler.Caller, verbatim = true) ret(result) } - } else { LLVMAddAlias(context.llvmModule, llvmFunction.type, llvmFunction, cname)!! } @@ -199,13 +200,23 @@ private class ExportedElement(val kind: ElementKind, LLVMBuildRet(builder, (declaration as ClassDescriptor).typeInfoPtr.llvm) LLVMDisposeBuilder(builder) } + isEnumEntry -> { + // Produce entry getter. + cname = "_konan_function_${owner.nextFunctionIndex()}" + generateFunction(owner.codegen, owner.kGetObjectFuncType, cname) { + val value = getEnumEntry(declaration as ClassDescriptor, ExceptionHandler.Caller) + ret(value) + } + } } } fun uniqueName(descriptor: DeclarationDescriptor) = scope.scopeUniqueName(descriptor) val isFunction = declaration is FunctionDescriptor - val isClass = declaration is ClassDescriptor + val isClass = declaration is ClassDescriptor && declaration.kind != ClassKind.ENUM_ENTRY + val isEnumClass = declaration is ClassDescriptor && declaration.kind == ClassKind.ENUM_CLASS + val isEnumEntry = declaration is ClassDescriptor && declaration.kind == ClassKind.ENUM_ENTRY fun makeCFunctionSignature(): List> { if (!isFunction) { @@ -215,7 +226,7 @@ private class ExportedElement(val kind: ElementKind, val original = descriptor.original as FunctionDescriptor val returned = when { original is ConstructorDescriptor -> uniqueName(original) to original.constructedClass - // Suspend functions actually return Any?. + // Suspend functions actually return 'Any?'. original.isSuspend -> uniqueName(original) to TypeUtils.getClassDescriptor(owner.context.builtIns.nullableAnyType)!! else -> uniqueName(original) to TypeUtils.getClassDescriptor(original.returnType!!)!! @@ -271,6 +282,21 @@ private class ExportedElement(val kind: ElementKind, return "extern \"C\" ${owner.prefix}_KType* $cname(void);" } + fun makeEnumEntryDeclaration(): String { + assert(isEnumEntry) + val enumClass = declaration.containingDeclaration as ClassDescriptor + val enumClassC = owner.translateType(enumClass) + + return """ + |extern "C" KObjHeader* $cname(KObjHeader**); + |static $enumClassC ${cname}_impl(void) { + | KObjHolder result_holder; + | KObjHeader* result = $cname(result_holder.slot()); + | return $enumClassC { .pinned = CreateStablePointer(result)}; + |} + """.trimMargin() + } + private fun translateArgument(name: String, clazz: ClassDescriptor, direction: Direction, builder: StringBuilder): String { val fqName = clazz.fqNameSafe.asString() @@ -509,6 +535,14 @@ internal class CAdapterGenerator( fun generateBindings() { scopes.push(ExportedElementScope(ScopeKind.TOP, "kotlin")) context.moduleDescriptor.accept(this, null) + // TODO: add few predefined types. + listOf( + // context.builtIns.anyType, + // context.builtIns.getPrimitiveArrayKotlinType(PrimitiveType.INT) + ).forEach { + TypeUtils.getClassDescriptor(it)!!.accept(this@CAdapterGenerator, null) + } + val top = scopes.pop() assert(scopes.isEmpty() && top.kind == ScopeKind.TOP) @@ -532,6 +566,10 @@ internal class CAdapterGenerator( output(element.makeFunctionPointerString(), indent) element.isClass -> output("${prefix}_KType* (*_type)(void);", indent) + element.isEnumEntry -> { + val enumClass = element.declaration.containingDeclaration as ClassDescriptor + output("${translateType(enumClass)} (*get)(); /* enum entry for ${element.name}. */", indent) + } // TODO: handle properties. } } @@ -542,6 +580,8 @@ internal class CAdapterGenerator( output(element.makeFunctionDeclaration(), 0) element.isClass -> output(element.makeClassDeclaration(), 0) + element.isEnumEntry -> + output(element.makeEnumEntryDeclaration(), 0) // TODO: handle properties. } } @@ -552,6 +592,8 @@ internal class CAdapterGenerator( output("/* ${element.name} = */ ${element.cname}_impl, ", indent) element.isClass -> output("/* Type for ${element.name} = */ ${element.cname}, ", indent) + element.isEnumEntry -> + output("/* enum entry getter ${element.name} = */ ${element.cname}_impl", indent) // TODO: handle properties. } } @@ -775,4 +817,7 @@ internal class CAdapterGenerator( internal val kGetTypeFuncType = LLVMFunctionType(codegen.kTypeInfoPtr, null, 0, 0)!! + // Abstraction leak for slot :(. + internal val kGetObjectFuncType = + LLVMFunctionType(codegen.kObjHeaderPtr, cValuesOf(codegen.kObjHeaderPtrPtr), 1, 0)!! } diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index bea36c4942a..d5520602ce6 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2361,7 +2361,9 @@ task produce_dynamic(type: DynamicKonanTest) { "Impl2.I: e 5 Impl2\n" + "String is Kotlin/Native\n" + "RO property is 42\n" + - "RW property is 239\n" + "RW property is 239\n" + + "enum100 = 100\n" + + "object = 42\n" } task buildKonanStdlibTests(type: BuildKonanTest) { diff --git a/backend.native/tests/produce_dynamic/simple/hello.kt b/backend.native/tests/produce_dynamic/simple/hello.kt index fcfb6943645..b3b088b216c 100644 --- a/backend.native/tests/produce_dynamic/simple/hello.kt +++ b/backend.native/tests/produce_dynamic/simple/hello.kt @@ -14,6 +14,22 @@ open class Base { } +// Enum. +enum class Enum(val code: Int) { + ONE(1), + TWO(2), + HUNDRED(100) +} + +// Object. +interface Codeable { + fun asCode(): Int +} + +val an_object = object : Codeable { + override fun asCode() = 42 +} + class Child : Base() { override fun fooParam(arg0: String, arg1: Int) = println("Child.fooParam: $arg0 $arg1") diff --git a/backend.native/tests/produce_dynamic/simple/main.c b/backend.native/tests/produce_dynamic/simple/main.c index 01790ec28a7..ee440211778 100644 --- a/backend.native/tests/produce_dynamic/simple/main.c +++ b/backend.native/tests/produce_dynamic/simple/main.c @@ -13,8 +13,12 @@ int main(void) { T_(Base) casted_child = { .pinned = child.pinned }; T_(I) casted_impl1 = { .pinned = impl1.pinned }; T_(I) casted_impl2 = { .pinned = impl2.pinned }; + T_(Enum) enum1 = __ kotlin.root.Enum.HUNDRED.get(); + T_(Codeable) object1 = __ kotlin.root.get_an_object(); + const char* string = __ kotlin.root.getString(); + __ kotlin.root.hello(); __ kotlin.root.Base.foo(base); __ kotlin.root.Base.fooParam(base, "a", 1); @@ -29,11 +33,17 @@ int main(void) { __ kotlin.root.Child.set_rwProperty(child, 238); printf("RW property is %d\n", __ kotlin.root.Child.get_rwProperty(child)); + printf("enum100 = %d\n", __ kotlin.root.Enum.get_code(enum1)); + + printf("object = %d\n", __ kotlin.root.Codeable.asCode(object1)); + __ DisposeString(string); __ DisposeStablePointer(base.pinned); __ DisposeStablePointer(child.pinned); __ DisposeStablePointer(impl1.pinned); __ DisposeStablePointer(impl2.pinned); + __ DisposeStablePointer(enum1.pinned); + __ DisposeStablePointer(object1.pinned); return 0; }