Support nullable types in reverse C interop. (#3198)

This commit is contained in:
Nikolay Igotti
2019-07-23 15:24:22 +03:00
committed by GitHub
parent 9a54689033
commit 9cf71e8010
5 changed files with 127 additions and 66 deletions
@@ -12,8 +12,6 @@ import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.descriptors.isNothing
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -31,7 +29,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNullable
private enum class ScopeKind { private enum class ScopeKind {
TOP, TOP,
@@ -63,6 +63,13 @@ private operator fun String.times(count: Int): String {
return builder.toString() return builder.toString()
} }
private val KotlinType.shortNameForPredefinedType
get() = this.toString().split('.').last()
private val KotlinType.createNullableNameForPredefinedType
get() = "createNullable${this.shortNameForPredefinedType}"
internal val cKeywords = setOf( internal val cKeywords = setOf(
// Actual C keywords. // Actual C keywords.
"auto", "break", "case", "auto", "break", "case",
@@ -104,10 +111,9 @@ internal val cKeywords = setOf(
"xor_eq" "xor_eq"
) )
private fun org.jetbrains.kotlin.types.KotlinType.isGeneric() = private fun KotlinType.isGeneric() =
constructor.declarationDescriptor is TypeParameterDescriptor constructor.declarationDescriptor is TypeParameterDescriptor
private fun isExportedFunction(descriptor: FunctionDescriptor): Boolean { private fun isExportedFunction(descriptor: FunctionDescriptor): Boolean {
if (!descriptor.isEffectivelyPublicApi || !descriptor.kind.isReal || descriptor.isExpect) if (!descriptor.isEffectivelyPublicApi || !descriptor.kind.isReal || descriptor.isExpect)
return false return false
@@ -147,6 +153,8 @@ private fun functionImplName(descriptor: DeclarationDescriptor, default: String,
return value.takeIf { value != null && value.isNotEmpty() } ?: default return value.takeIf { value != null && value.isNotEmpty() } ?: default
} }
internal data class SignatureElement(val name: String, val type: KotlinType)
private class ExportedElementScope(val kind: ScopeKind, val name: String) { private class ExportedElementScope(val kind: ScopeKind, val name: String) {
val elements = mutableListOf<ExportedElement>() val elements = mutableListOf<ExportedElement>()
val scopes = mutableListOf<ExportedElementScope>() val scopes = mutableListOf<ExportedElementScope>()
@@ -198,7 +206,7 @@ private class ExportedElement(val kind: ElementKind,
lateinit var cname: String lateinit var cname: String
override fun toString(): String { override fun toString(): String {
return "$kind: $name (aliased to $cname)" return "$kind: $name (aliased to ${if (::cname.isInitialized) cname.toString() else "<unknown>"})"
} }
override val context = owner.context override val context = owner.context
@@ -283,22 +291,22 @@ private class ExportedElement(val kind: ElementKind,
fun KotlinType.includeToSignature() = !this.isUnit() fun KotlinType.includeToSignature() = !this.isUnit()
fun makeCFunctionSignature(shortName: Boolean): List<Pair<String, ClassDescriptor>> { fun makeCFunctionSignature(shortName: Boolean): List<SignatureElement> {
if (!isFunction) { if (!isFunction) {
throw Error("only for functions") throw Error("only for functions")
} }
val descriptor = declaration val descriptor = declaration
val original = descriptor.original as FunctionDescriptor val original = descriptor.original as FunctionDescriptor
val returned = when { val returned = when {
original is ConstructorDescriptor -> uniqueName(original, shortName) to original.constructedClass original is ConstructorDescriptor ->
else -> uniqueName(original, shortName) to TypeUtils.getClassDescriptor(original.returnType!!)!! SignatureElement(uniqueName(original, shortName), original.constructedClass.defaultType)
else ->
SignatureElement(uniqueName(original, shortName), original.returnType!!)
} }
val uniqueNames = owner.paramsToUniqueNames(original.explicitParameters) val uniqueNames = owner.paramsToUniqueNames(original.explicitParameters)
val params = ArrayList(original.explicitParameters val params = ArrayList(original.explicitParameters
.filter { it.type.includeToSignature() } .filter { it.type.includeToSignature() }
.map { it -> .map { SignatureElement(uniqueNames[it]!!, it.type) })
uniqueNames[it]!! to TypeUtils.getClassDescriptor(it.type)!!
})
return listOf(returned) + params return listOf(returned) + params
} }
@@ -312,32 +320,30 @@ private class ExportedElement(val kind: ElementKind,
original is ConstructorDescriptor -> owner.context.builtIns.unitType original is ConstructorDescriptor -> owner.context.builtIns.unitType
else -> original.returnType!! else -> original.returnType!!
} }
val returnedClass = TypeUtils.getClassDescriptor(returnedType)!!
val params = ArrayList(original.allParameters val params = ArrayList(original.allParameters
.filter { it.type.includeToSignature() } .filter { it.type.includeToSignature() }
.map { .map {
owner.translateTypeBridge(TypeUtils.getClassDescriptor(it.type)!!) owner.translateTypeBridge(it.type)
}) })
if (owner.isMappedToReference(returnedClass) || owner.isMappedToString(returnedClass)) { if (owner.isMappedToReference(returnedType) || owner.isMappedToString(returnedType)) {
params += "KObjHeader**" params += "KObjHeader**"
} }
return listOf(owner.translateTypeBridge(returnedClass)) + params return listOf(owner.translateTypeBridge(returnedType)) + params
} }
fun makeFunctionPointerString(): String { fun makeFunctionPointerString(): String {
val signature = makeCFunctionSignature(true) val signature = makeCFunctionSignature(true)
return "${owner.translateType(signature[0].second)} (*${signature[0].first})(${signature.drop(1).map { it -> "${owner.translateType(it.second)} ${it.first}" }.joinToString(", ")});" return "${owner.translateType(signature[0])} (*${signature[0].name})(${signature.drop(1).map { it -> "${owner.translateType(it)} ${it.name}" }.joinToString(", ")});"
} }
fun makeTopLevelFunctionString(): Pair<String, String> { fun makeTopLevelFunctionString(): Pair<String, String> {
val signature = makeCFunctionSignature(false) val signature = makeCFunctionSignature(false)
val name = signature[0].first val name = signature[0].name
return (name to return (name to
"extern ${owner.translateType(signature[0].second)} $name(${signature.drop(1).map { it -> "${owner.translateType(it.second)} ${it.first}" }.joinToString(", ")});") "extern ${owner.translateType(signature[0])} $name(${signature.drop(1).map { it -> "${owner.translateType(it)} ${it.name}" }.joinToString(", ")});")
} }
fun makeFunctionDeclaration(): String { fun makeFunctionDeclaration(): String {
assert(isFunction) assert(isFunction)
val bridge = makeBridgeSignature() val bridge = makeBridgeSignature()
@@ -355,7 +361,7 @@ private class ExportedElement(val kind: ElementKind,
assert(isClass) assert(isClass)
val typeGetter = "extern \"C\" ${owner.prefix}_KType* ${cname}_type(void);" val typeGetter = "extern \"C\" ${owner.prefix}_KType* ${cname}_type(void);"
val instanceGetter = if (isSingletonObject) { val instanceGetter = if (isSingletonObject) {
val objectClassC = owner.translateType(declaration as ClassDescriptor) val objectClassC = owner.translateType((declaration as ClassDescriptor).defaultType)
""" """
| |
|extern "C" KObjHeader* ${cname}_instance(KObjHeader**); |extern "C" KObjHeader* ${cname}_instance(KObjHeader**);
@@ -373,7 +379,7 @@ private class ExportedElement(val kind: ElementKind,
fun makeEnumEntryDeclaration(): String { fun makeEnumEntryDeclaration(): String {
assert(isEnumEntry) assert(isEnumEntry)
val enumClass = declaration.containingDeclaration as ClassDescriptor val enumClass = declaration.containingDeclaration as ClassDescriptor
val enumClassC = owner.translateType(enumClass) val enumClassC = owner.translateType(enumClass.defaultType)
return """ return """
|extern "C" KObjHeader* $cname(KObjHeader**); |extern "C" KObjHeader* $cname(KObjHeader**);
@@ -386,26 +392,26 @@ private class ExportedElement(val kind: ElementKind,
""".trimMargin() """.trimMargin()
} }
private fun translateArgument(name: String, clazz: ClassDescriptor, direction: Direction, private fun translateArgument(name: String, signatureElement: SignatureElement,
builder: StringBuilder): String { direction: Direction, builder: StringBuilder): String {
return when { return when {
owner.isMappedToString(clazz) -> owner.isMappedToString(signatureElement.type) ->
if (direction == Direction.C_TO_KOTLIN) { if (direction == Direction.C_TO_KOTLIN) {
builder.append(" KObjHolder ${name}_holder;\n") builder.append(" KObjHolder ${name}_holder;\n")
"CreateStringFromCString($name, ${name}_holder.slot())" "CreateStringFromCString($name, ${name}_holder.slot())"
} else { } else {
"CreateCStringFromString($name)" "CreateCStringFromString($name)"
} }
owner.isMappedToReference(clazz) -> owner.isMappedToReference(signatureElement.type) ->
if (direction == Direction.C_TO_KOTLIN) { if (direction == Direction.C_TO_KOTLIN) {
builder.append(" KObjHolder ${name}_holder2;\n") builder.append(" KObjHolder ${name}_holder2;\n")
"DerefStablePointer(${name}.pinned, ${name}_holder2.slot())" "DerefStablePointer(${name}.pinned, ${name}_holder2.slot())"
} else { } else {
"((${owner.translateType(clazz)}){ .pinned = CreateStablePointer(${name})})" "((${owner.translateType(signatureElement.type)}){ .pinned = CreateStablePointer(${name})})"
} }
else -> { else -> {
assert(!clazz.defaultType.binaryTypeIsReference()) { assert(!signatureElement.type.binaryTypeIsReference()) {
println(clazz.toString()) println(signatureElement.toString())
} }
name name
} }
@@ -418,17 +424,18 @@ private class ExportedElement(val kind: ElementKind,
else else
"${cname}_impl" "${cname}_impl"
private fun translateBody(cfunction: List<Pair<String, ClassDescriptor>>): String { private fun translateBody(cfunction: List<SignatureElement>): String {
val visibility = if (isTopLevelFunction) "RUNTIME_USED extern \"C\"" else "static" val visibility = if (isTopLevelFunction) "RUNTIME_USED extern \"C\"" else "static"
val builder = StringBuilder() val builder = StringBuilder()
builder.append("$visibility ${owner.translateType(cfunction[0].second)} ${cnameImpl}(${cfunction.drop(1).mapIndexed { index, it -> "${owner.translateType(it.second)} arg${index}" }.joinToString(", ")}) {\n") builder.append("$visibility ${owner.translateType(cfunction[0])} ${cnameImpl}(${cfunction.drop(1).
mapIndexed { index, it -> "${owner.translateType(it)} arg${index}" }.joinToString(", ")}) {\n")
val args = ArrayList(cfunction.drop(1).mapIndexed { index, pair -> val args = ArrayList(cfunction.drop(1).mapIndexed { index, pair ->
translateArgument("arg$index", pair.second, Direction.C_TO_KOTLIN, builder) translateArgument("arg$index", pair, Direction.C_TO_KOTLIN, builder)
}) })
val isVoidReturned = owner.isMappedToVoid(cfunction[0].second) val isVoidReturned = owner.isMappedToVoid(cfunction[0].type)
val isConstructor = declaration is ConstructorDescriptor val isConstructor = declaration is ConstructorDescriptor
val isObjectReturned = !isConstructor && owner.isMappedToReference(cfunction[0].second) val isObjectReturned = !isConstructor && owner.isMappedToReference(cfunction[0].type)
val isStringReturned = owner.isMappedToString(cfunction[0].second) val isStringReturned = owner.isMappedToString(cfunction[0].type)
// TODO: do we really need that in every function? // TODO: do we really need that in every function?
builder.append(" Kotlin_initRuntimeIfNeeded();\n") builder.append(" Kotlin_initRuntimeIfNeeded();\n")
builder.append(" try {\n") builder.append(" try {\n")
@@ -452,7 +459,7 @@ private class ExportedElement(val kind: ElementKind,
if (!isVoidReturned) { if (!isVoidReturned) {
val result = translateArgument( val result = translateArgument(
"result", cfunction[0].second, Direction.KOTLIN_TO_C, builder) "result", cfunction[0], Direction.KOTLIN_TO_C, builder)
builder.append(" return $result;\n") builder.append(" return $result;\n")
} }
builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.obj()); } \n") builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.obj()); } \n")
@@ -462,7 +469,7 @@ private class ExportedElement(val kind: ElementKind,
return builder.toString() return builder.toString()
} }
private fun addUsedType(type: org.jetbrains.kotlin.types.KotlinType, set: MutableSet<ClassDescriptor>) { private fun addUsedType(type: KotlinType, set: MutableSet<ClassDescriptor>) {
if (type.constructor.declarationDescriptor is TypeParameterDescriptor) return if (type.constructor.declarationDescriptor is TypeParameterDescriptor) return
val clazz = TypeUtils.getClassDescriptor(type) val clazz = TypeUtils.getClassDescriptor(type)
if (clazz == null) { if (clazz == null) {
@@ -521,6 +528,13 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
private var symbolTableOrNull: SymbolTable? = null private var symbolTableOrNull: SymbolTable? = null
internal val symbolTable get() = symbolTableOrNull!! internal val symbolTable get() = symbolTableOrNull!!
private val predefinedTypes = listOf(
context.builtIns.byteType, context.builtIns.shortType,
context.builtIns.intType, context.builtIns.longType,
context.builtIns.floatType, context.builtIns.doubleType,
context.builtIns.charType, context.builtIns.booleanType,
context.builtIns.unitType)
internal fun paramsToUniqueNames(params: List<ParameterDescriptor>): Map<ParameterDescriptor, String> { internal fun paramsToUniqueNames(params: List<ParameterDescriptor>): Map<ParameterDescriptor, String> {
paramNamesRecorded.clear() paramNamesRecorded.clear()
return params.associate { return params.associate {
@@ -690,14 +704,6 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
}) })
context.moduleDescriptor.getPackage(FqName.ROOT).accept(this, null) context.moduleDescriptor.getPackage(FqName.ROOT).accept(this, null)
// TODO: add few predefined types.
listOf<KotlinType>(
// context.builtIns.anyType,
// context.builtIns.getPrimitiveArrayKotlinType(PrimitiveType.INT)
).forEach {
TypeUtils.getClassDescriptor(it)!!.accept(this@CAdapterGenerator, null)
}
} }
private fun generateBindings() { private fun generateBindings() {
@@ -735,12 +741,12 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
element.isClass -> { element.isClass -> {
output("${prefix}_KType* (*_type)(void);", indent) output("${prefix}_KType* (*_type)(void);", indent)
if (element.isSingletonObject) { if (element.isSingletonObject) {
output("${translateType(element.declaration as ClassDescriptor)} (*_instance)();", indent) output("${translateType((element.declaration as ClassDescriptor).defaultType)} (*_instance)();", indent)
} }
} }
element.isEnumEntry -> { element.isEnumEntry -> {
val enumClass = element.declaration.containingDeclaration as ClassDescriptor val enumClass = element.declaration.containingDeclaration as ClassDescriptor
output("${translateType(enumClass)} (*get)(); /* enum entry for ${element.name}. */", indent) output("${translateType(enumClass.defaultType)} (*get)(); /* enum entry for ${element.name}. */", indent)
} }
// TODO: handle properties. // TODO: handle properties.
} }
@@ -796,11 +802,19 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
private fun defineUsedTypes(scope: ExportedElementScope, indent: Int) { private fun defineUsedTypes(scope: ExportedElementScope, indent: Int) {
val set = mutableSetOf<ClassDescriptor>() val set = mutableSetOf<ClassDescriptor>()
defineUsedTypesImpl(scope, set) defineUsedTypesImpl(scope, set)
// Add nullable primitives.
predefinedTypes.forEach {
val nullableIt = it.makeNullable()
output("typedef struct {", indent)
output("${prefix}_KNativePtr pinned;", indent + 1)
output("} ${translateType(nullableIt)};", indent)
}
set.forEach { set.forEach {
if (isMappedToReference(it) && !it.isInlined()) { val type = it.defaultType
if (isMappedToReference(type) && !it.isInlined()) {
output("typedef struct {", indent) output("typedef struct {", indent)
output("${prefix}_KNativePtr pinned;", indent + 1) output("${prefix}_KNativePtr pinned;", indent + 1)
output("} ${translateType(it)};", indent) output("} ${translateType(type)};", indent)
} }
} }
} }
@@ -855,6 +869,11 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
output("void (*DisposeStablePointer)(${prefix}_KNativePtr ptr);", 1) output("void (*DisposeStablePointer)(${prefix}_KNativePtr ptr);", 1)
output("void (*DisposeString)(const char* string);", 1) output("void (*DisposeString)(const char* string);", 1)
output("${prefix}_KBoolean (*IsInstance)(${prefix}_KNativePtr ref, const ${prefix}_KType* type);", 1) output("${prefix}_KBoolean (*IsInstance)(${prefix}_KNativePtr ref, const ${prefix}_KType* type);", 1)
predefinedTypes.forEach {
val nullableIt = it.makeNullable()
val argument = if (!it.isUnit()) translateType(it) else "void"
output("${translateType(nullableIt)} (*${it.createNullableNameForPredefinedType})($argument);", 1)
}
output("") output("")
output("/* User functions. */", 1) output("/* User functions. */", 1)
@@ -959,11 +978,30 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
| return IsInstance(DerefStablePointer(ref, holder.slot()), (const KTypeInfo*)type); | return IsInstance(DerefStablePointer(ref, holder.slot()), (const KTypeInfo*)type);
|} |}
""".trimMargin()) """.trimMargin())
predefinedTypes.forEach {
assert(!it.isNothing())
val nullableIt = it.makeNullable()
val needArgument = !it.isUnit()
val (parameter, maybeComma) = if (needArgument)
("${translateType(it)} value" to ",") else ("" to "")
val argument = if (needArgument) "value, " else ""
output("extern \"C\" KObjHeader* Kotlin_box${it.shortNameForPredefinedType}($parameter$maybeComma KObjHeader**);")
output("static ${translateType(nullableIt)} ${it.createNullableNameForPredefinedType}Impl($parameter) {")
output("KObjHolder result_holder;", 1)
output("Kotlin_initRuntimeIfNeeded();", 1)
output("KObjHeader* result = Kotlin_box${it.shortNameForPredefinedType}($argument result_holder.slot());", 1)
output("return ${translateType(nullableIt)} { .pinned = CreateStablePointer(result) };", 1)
output("}")
}
makeScopeDefinitions(top, DefinitionKind.C_SOURCE_DECLARATION, 0) makeScopeDefinitions(top, DefinitionKind.C_SOURCE_DECLARATION, 0)
output("static ${prefix}_ExportedSymbols __konan_symbols = {") output("static ${prefix}_ExportedSymbols __konan_symbols = {")
output(".DisposeStablePointer = DisposeStablePointerImpl,", 1) output(".DisposeStablePointer = DisposeStablePointerImpl,", 1)
output(".DisposeString = DisposeStringImpl,", 1) output(".DisposeString = DisposeStringImpl,", 1)
output(".IsInstance = IsInstanceImpl,", 1) output(".IsInstance = IsInstanceImpl,", 1)
predefinedTypes.forEach {
output(".${it.createNullableNameForPredefinedType} = ${it.createNullableNameForPredefinedType}Impl,", 1)
}
makeScopeDefinitions(top, DefinitionKind.C_SOURCE_STRUCT, 1) makeScopeDefinitions(top, DefinitionKind.C_SOURCE_STRUCT, 1)
output("};") output("};")
output("RUNTIME_USED ${prefix}_ExportedSymbols* $exportedSymbol(void) { return &__konan_symbols;}") output("RUNTIME_USED ${prefix}_ExportedSymbols* $exportedSymbol(void) { return &__konan_symbols;}")
@@ -1007,8 +1045,8 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
} }
} }
internal fun isMappedToString(descriptor: ClassDescriptor): Boolean = internal fun isMappedToString(type: KotlinType): Boolean =
isMappedToString(descriptor.defaultType.computeBinaryType()) isMappedToString(type.computeBinaryType())
private fun isMappedToString(binaryType: BinaryType<ClassDescriptor>): Boolean = private fun isMappedToString(binaryType: BinaryType<ClassDescriptor>): Boolean =
when (binaryType) { when (binaryType) {
@@ -1016,12 +1054,12 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
is BinaryType.Reference -> binaryType.types.first() == context.builtIns.string is BinaryType.Reference -> binaryType.types.first() == context.builtIns.string
} }
internal fun isMappedToReference(descriptor: ClassDescriptor) = internal fun isMappedToReference(type: KotlinType) =
!isMappedToVoid(descriptor) && !isMappedToString(descriptor) && !isMappedToVoid(type) && !isMappedToString(type) &&
descriptor.defaultType.binaryTypeIsReference() type.binaryTypeIsReference()
internal fun isMappedToVoid(descriptor: ClassDescriptor): Boolean { internal fun isMappedToVoid(type: KotlinType): Boolean {
return descriptor.isUnit() || descriptor.isNothing() return type.isUnit() || type.isNothing()
} }
fun translateName(name: String): String { fun translateName(name: String): String {
@@ -1032,11 +1070,12 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
} }
} }
private fun translateTypeFull(clazz: ClassDescriptor): Pair<String, String> = if (isMappedToVoid(clazz)) { private fun translateTypeFull(type: KotlinType): Pair<String, String> =
"void" to "void" if (isMappedToVoid(type)) {
} else { "void" to "void"
translateNonVoidTypeFull(clazz.defaultType) } else {
} translateNonVoidTypeFull(type)
}
private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference( private fun translateNonVoidTypeFull(type: KotlinType): Pair<String, String> = type.unwrapToPrimitiveOrReference(
eachInlinedClass = { inlinedClass, _ -> eachInlinedClass = { inlinedClass, _ ->
@@ -1057,9 +1096,13 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
} }
) )
fun translateType(clazz: ClassDescriptor): String = translateTypeFull(clazz).first fun translateType(element: SignatureElement): String =
translateTypeFull(element.type).first
fun translateTypeBridge(clazz: ClassDescriptor): String = translateTypeFull(clazz).second fun translateType(type: KotlinType): String
= translateTypeFull(type).first
fun translateTypeBridge(type: KotlinType): String = translateTypeFull(type).second
fun translateTypeFqName(name: String): String { fun translateTypeFqName(name: String): String {
return name.replace('.', '_') return name.replace('.', '_')
@@ -35,8 +35,10 @@ open class Base {
fun topLevelFunction(x1: Int, x2: Int) = x1 - x2 fun topLevelFunction(x1: Int, x2: Int) = x1 - x2
@CName("topLevelFunctionVoidFromC") @CName("topLevelFunctionVoidFromC")
fun topLevelFunctionVoid(x1: Int, pointer: COpaquePointer?) { fun topLevelFunctionVoid(x1: Int, x2: Int?, x3: Unit?, pointer: COpaquePointer?) {
assert(x1 == 42) assert(x1 == 42)
assert(x2 == 77)
assert(x3 != null)
assert(pointer == null) assert(pointer == null)
} }
@@ -99,6 +101,12 @@ fun useInlineClasses(ic1: IC1, ic2: IC2, ic3: IC3) {
assert(ic2.value == "bar") assert(ic2.value == "bar")
assert(ic3.value is Base) assert(ic3.value is Base)
} }
fun testNullableWithNulls(arg1: Int?, arg2: Unit?) {
assert(arg1 == null)
assert(arg2 == null)
}
fun setCErrorHandler(callback: CPointer<CFunction<(CPointer<ByteVar>) -> Unit>>?) { fun setCErrorHandler(callback: CPointer<CFunction<(CPointer<ByteVar>) -> Unit>>?) {
setUnhandledExceptionHook({ setUnhandledExceptionHook({
throwable: Throwable -> throwable: Throwable ->
@@ -21,6 +21,10 @@ int main(void) {
T_(Enum) enum1 = __ kotlin.root.Enum.HUNDRED.get(); T_(Enum) enum1 = __ kotlin.root.Enum.HUNDRED.get();
T_(Codeable) object1 = __ kotlin.root.get_an_object(); T_(Codeable) object1 = __ kotlin.root.get_an_object();
T_(Data) data = __ kotlin.root.getMutable(); T_(Data) data = __ kotlin.root.getMutable();
T_(kotlin_Int) nullableInt = __ createNullableInt(77);
T_(kotlin_Unit) nullableUnit = __ createNullableUnit();
T_(kotlin_Int) nullableIntNull = { .pinned = 0 };
T_(kotlin_Unit) nullableUnitNull = { .pinned = 0 };
const char* string1 = __ kotlin.root.getString(); const char* string1 = __ kotlin.root.getString();
const char* string2 = __ kotlin.root.Singleton.toString(singleton); const char* string2 = __ kotlin.root.Singleton.toString(singleton);
@@ -51,12 +55,14 @@ int main(void) {
printf("mutable = %s\n", string3); printf("mutable = %s\n", string3);
topLevelFunctionVoidFromC(42, 0); topLevelFunctionVoidFromC(42, nullableInt, nullableUnit, 0);
__ kotlin.root.topLevelFunctionVoid(42, 0); __ kotlin.root.topLevelFunctionVoid(42, nullableInt, nullableUnit, 0);
printf("topLevel = %d %d\n", topLevelFunctionFromC(780, 3), __ kotlin.root.topLevelFunctionFromCShort(5, 2)); printf("topLevel = %d %d\n", topLevelFunctionFromC(780, 3), __ kotlin.root.topLevelFunctionFromCShort(5, 2));
__ kotlin.root.useInlineClasses(42, "bar", base); __ kotlin.root.useInlineClasses(42, "bar", base);
__ kotlin.root.testNullableWithNulls(nullableIntNull, nullableUnitNull);
__ DisposeStablePointer(singleton.pinned); __ DisposeStablePointer(singleton.pinned);
__ DisposeString(string1); __ DisposeString(string1);
__ DisposeString(string2); __ DisposeString(string2);
@@ -69,6 +75,7 @@ int main(void) {
__ DisposeStablePointer(impl2.pinned); __ DisposeStablePointer(impl2.pinned);
__ DisposeStablePointer(enum1.pinned); __ DisposeStablePointer(enum1.pinned);
__ DisposeStablePointer(object1.pinned); __ DisposeStablePointer(object1.pinned);
__ DisposeStablePointer(nullableInt.pinned);
__ kotlin.root.setCErrorHandler(&errorHandler); __ kotlin.root.setCErrorHandler(&errorHandler);
__ kotlin.root.throwException(); __ kotlin.root.throwException();
@@ -71,3 +71,6 @@ fun boxFloat(value: Float): Float? = value
@ExportForCppRuntime("Kotlin_boxDouble") @ExportForCppRuntime("Kotlin_boxDouble")
fun boxDouble(value: Double): Double? = value fun boxDouble(value: Double): Double? = value
@ExportForCppRuntime("Kotlin_boxUnit")
internal fun Kotlin_boxUnit(): Unit? = Unit
@@ -180,4 +180,4 @@ internal fun <T> listOfInternal(vararg elements: T): List<T> {
for (i in 0 until elements.size) for (i in 0 until elements.size)
result.add(elements[i]) result.add(elements[i])
return result return result
} }