From ce0b43e596f7ce1c93aaf7b5b92fc73fa279375f Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Mon, 27 Aug 2018 14:48:29 +0300 Subject: [PATCH] Support singleton objects in C interop. (#1940) --- .../kotlin/backend/konan/CAdapterGenerator.kt | 54 ++++++++++++++----- .../backend/konan/llvm/CodeGenerator.kt | 6 +-- backend.native/tests/build.gradle | 1 + .../tests/produce_dynamic/simple/hello.kt | 4 ++ .../tests/produce_dynamic/simple/main.c | 12 +++-- 5 files changed, 58 insertions(+), 19 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 c189c5acee5..f0e7fdd91d7 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 @@ -28,14 +28,14 @@ import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.annotations.* -import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.name.isChildOf +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.annotations.* import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isUnit private enum class ScopeKind { @@ -232,14 +232,22 @@ private class ExportedElement(val kind: ElementKind, LLVMSetLinkage(bridge, LLVMLinkage.LLVMExternalLinkage) } isClass -> { - // Produce type getter. + val classDescriptor = declaration as ClassDescriptor cname = "_konan_function_${owner.nextFunctionIndex()}" - val getTypeFunction = LLVMAddFunction(context.llvmModule, cname, owner.kGetTypeFuncType)!! + // Produce type getter. + val getTypeFunction = LLVMAddFunction(context.llvmModule, "${cname}_type", owner.kGetTypeFuncType)!! val builder = LLVMCreateBuilder()!! val bb = LLVMAppendBasicBlock(getTypeFunction, "")!! LLVMPositionBuilderAtEnd(builder, bb) - LLVMBuildRet(builder, context.ir.getFromCurrentModule(declaration as ClassDescriptor).typeInfoPtr.llvm) + LLVMBuildRet(builder, context.ir.getFromCurrentModule(classDescriptor).typeInfoPtr.llvm) LLVMDisposeBuilder(builder) + // Produce instance getter if needed. + if (isSingletonObject) { + generateFunction(owner.codegen, owner.kGetObjectFuncType, "${cname}_instance") { + val value = getObjectValue(context.ir.get(classDescriptor), ExceptionHandler.Caller, null) + ret(value) + } + } } isEnumEntry -> { // Produce entry getter. @@ -267,7 +275,7 @@ private class ExportedElement(val kind: ElementKind, } val isClass = declaration is ClassDescriptor && declaration.kind != ClassKind.ENUM_ENTRY val isEnumEntry = declaration is ClassDescriptor && declaration.kind == ClassKind.ENUM_ENTRY - + val isSingletonObject = declaration is ClassDescriptor && DescriptorUtils.isObject(declaration) fun KotlinType.includeToSignature() = !this.isUnit() @@ -279,7 +287,7 @@ private class ExportedElement(val kind: ElementKind, val original = descriptor.original as FunctionDescriptor val returned = when { original is ConstructorDescriptor -> uniqueName(original, shortName) to original.constructedClass - // Suspend functions actually return 'Any?'. + // Suspend functions actually return 'Any?'. original.isSuspend -> uniqueName(original, shortName) to TypeUtils.getClassDescriptor(owner.context.builtIns.nullableAnyType)!! else -> uniqueName(original, shortName) to TypeUtils.getClassDescriptor(original.returnType!!)!! @@ -345,7 +353,20 @@ private class ExportedElement(val kind: ElementKind, fun makeClassDeclaration(): String { assert(isClass) - return "extern \"C\" ${owner.prefix}_KType* $cname(void);" + val typeGetter = "extern \"C\" ${owner.prefix}_KType* ${cname}_type(void);" + val instanceGetter = if (isSingletonObject) { + val objectClassC = owner.translateType(declaration as ClassDescriptor) + """ + | + |extern "C" KObjHeader* ${cname}_instance(KObjHeader**); + |static $objectClassC ${cname}_instance_impl(void) { + | KObjHolder result_holder; + | KObjHeader* result = ${cname}_instance(result_holder.slot()); + | return $objectClassC { .pinned = CreateStablePointer(result)}; + |} + """.trimMargin() + } else "" + return "$typeGetter$instanceGetter" } fun makeEnumEntryDeclaration(): String { @@ -416,7 +437,7 @@ private class ExportedElement(val kind: ElementKind, builder.append(" KObjHolder result_holder;\n") val clazz = scope.elements[0] assert(clazz.kind == ElementKind.TYPE) - builder.append(" KObjHeader* result = AllocInstance((const KTypeInfo*)${clazz.cname}(), result_holder.slot());\n") + builder.append(" KObjHeader* result = AllocInstance((const KTypeInfo*)${clazz.cname}_type(), result_holder.slot());\n") args.add(0, "result") } if (!isVoidReturned && !isConstructor) { @@ -669,8 +690,12 @@ internal class CAdapterGenerator( when { element.isFunction -> output(element.makeFunctionPointerString(), indent) - element.isClass -> + element.isClass -> { output("${prefix}_KType* (*_type)(void);", indent) + if (element.isSingletonObject) { + output("${translateType(element.declaration as ClassDescriptor)} (*_instance)();", indent) + } + } element.isEnumEntry -> { val enumClass = element.declaration.containingDeclaration as ClassDescriptor output("${translateType(enumClass)} (*get)(); /* enum entry for ${element.name}. */", indent) @@ -695,8 +720,11 @@ internal class CAdapterGenerator( when { element.isFunction -> output("/* ${element.name} = */ ${element.cnameImpl}, ", indent) - element.isClass -> - output("/* Type for ${element.name} = */ ${element.cname}, ", indent) + element.isClass -> { + output("/* Type for ${element.name} = */ ${element.cname}_type, ", indent) + if (element.isSingletonObject) + output("/* Instance for ${element.name} = */ ${element.cname}_instance_impl, ", indent) + } element.isEnumEntry -> output("/* enum entry getter ${element.name} = */ ${element.cname}_impl,", indent) // TODO: handle properties. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 0e5d75f9937..68e226c067b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -706,10 +706,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, val shared = descriptor.objectIsShared && context.config.threadsAreAllowed val objectPtr = codegen.getObjectInstanceStorage(descriptor, shared) val bbCurrent = currentBlock - val bbInit= basicBlock("label_init", locationInfo) - val bbExit= basicBlock("label_continue", locationInfo) + val bbInit = basicBlock("label_init", locationInfo) + val bbExit = basicBlock("label_continue", locationInfo) val objectVal = loadSlot(objectPtr, false) - val objectInitialized= icmpUGt(ptrToInt(objectVal, codegen.intPtrType), codegen.immOneIntPtrType) + val objectInitialized = icmpUGt(ptrToInt(objectVal, codegen.intPtrType), codegen.immOneIntPtrType) condBr(objectInitialized, bbExit, bbInit) positionAtEnd(bbInit) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index e1006bcc8ee..13111a00733 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -2896,6 +2896,7 @@ task produce_dynamic(type: DynamicKonanTest) { "RW property is 239\n" + "enum100 = 100\n" + "object = 42\n" + + "singleton = I am single\n" + "topLevel = 777 3\n" } diff --git a/backend.native/tests/produce_dynamic/simple/hello.kt b/backend.native/tests/produce_dynamic/simple/hello.kt index 46c14ce6846..3d12520c937 100644 --- a/backend.native/tests/produce_dynamic/simple/hello.kt +++ b/backend.native/tests/produce_dynamic/simple/hello.kt @@ -45,6 +45,10 @@ val an_object = object : Codeable { override fun asCode() = 42 } +object Singleton { + override fun toString() = "I am single" +} + 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 b81713fac3a..3c6f59d09d2 100644 --- a/backend.native/tests/produce_dynamic/simple/main.c +++ b/backend.native/tests/produce_dynamic/simple/main.c @@ -15,8 +15,10 @@ int main(void) { T_(I) casted_impl2 = { .pinned = impl2.pinned }; T_(Enum) enum1 = __ kotlin.root.Enum.HUNDRED.get(); T_(Codeable) object1 = __ kotlin.root.get_an_object(); + T_(Singleton) singleton = __ kotlin.root.Singleton._instance(); - const char* string = __ kotlin.root.getString(); + const char* string1 = __ kotlin.root.getString(); + const char* string2 = __ kotlin.root.Singleton.toString(singleton); __ kotlin.root.hello(); __ kotlin.root.Base.foo(base); @@ -26,7 +28,7 @@ int main(void) { __ kotlin.root.I.foo(casted_impl1, "d", 4, casted_impl1); __ kotlin.root.I.foo(casted_impl2, "e", 5, casted_impl2); - printf("String is %s\n", string); + printf("String is %s\n", string1); printf("RO property is %d\n", __ kotlin.root.Child.get_roProperty(child)); __ kotlin.root.Child.set_rwProperty(child, 238); @@ -36,13 +38,17 @@ int main(void) { printf("object = %d\n", __ kotlin.root.Codeable.asCode(object1)); + printf("singleton = %s\n", string2); + topLevelFunctionVoidFromC(42, 0); __ kotlin.root.topLevelFunctionVoid(42, 0); printf("topLevel = %d %d\n", topLevelFunctionFromC(780, 3), __ kotlin.root.topLevelFunctionFromCShort(5, 2)); __ kotlin.root.useInlineClasses(42, "bar", base); - __ DisposeString(string); + __ DisposeStablePointer(singleton.pinned); + __ DisposeString(string1); + __ DisposeString(string2); __ DisposeStablePointer(base.pinned); __ DisposeStablePointer(child.pinned); __ DisposeStablePointer(impl1.pinned);