[StubIR][Interop] Rework StubIR type system to make it usable for metadata-based interop

This commit is contained in:
Sergey Bogolepov
2019-10-07 10:11:11 +03:00
committed by Sergey Bogolepov
parent bd0e0dd2e7
commit 303f6638f4
10 changed files with 298 additions and 111 deletions
@@ -1,3 +1,7 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.native.interop.indexer.FunctionDecl
@@ -78,10 +78,13 @@ data class Classifier(
}
val Classifier.type
get() = KotlinClassifierType(this, arguments = emptyList(), nullable = false)
get() = KotlinClassifierType(this, arguments = emptyList(), nullable = false, underlyingType = null)
fun Classifier.typeWith(vararg arguments: KotlinTypeArgument) =
KotlinClassifierType(this, arguments.toList(), nullable = false)
KotlinClassifierType(this, arguments.toList(), nullable = false, underlyingType = null)
fun Classifier.typeAbbreviation(expandedType: KotlinType) =
KotlinClassifierType(this, arguments = emptyList(), nullable = false, underlyingType = expandedType)
interface KotlinTypeArgument {
/**
@@ -99,10 +102,14 @@ interface KotlinType : KotlinTypeArgument {
fun makeNullableAsSpecified(nullable: Boolean): KotlinType
}
/**
* @property underlyingType is non-null if this type is an alias to another type.
*/
data class KotlinClassifierType(
override val classifier: Classifier,
val arguments: List<KotlinTypeArgument>,
val nullable: Boolean
val nullable: Boolean,
val underlyingType: KotlinType?
) : KotlinType {
override fun makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
@@ -195,6 +202,7 @@ object KotlinTypes {
val cValuesRef by InteropClassifier
val cPointed by InteropClassifier
val cPointer by InteropClassifier
val cPointerVar by InteropClassifier
val cArrayPointer by InteropClassifier
@@ -225,7 +233,6 @@ object KotlinTypes {
private object InteropClassifier : ClassifierAtPackage("kotlinx.cinterop")
private object InteropType : TypeAtPackage("kotlinx.cinterop")
}
abstract class KotlinFile(
@@ -411,9 +411,9 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
!type.def.isAnonymous -> {
val baseTypeMirror = mirror(declarationMapper, type.def.baseType)
TypeMirror.ByValue(
Classifier.topLevel(pkg, kotlinName + "Var").type,
Classifier.topLevel(pkg, kotlinName + "Var").typeAbbreviation(baseTypeMirror.pointedType),
baseTypeMirror.info,
Classifier.topLevel(pkg, kotlinName).type
Classifier.topLevel(pkg, kotlinName).typeAbbreviation(baseTypeMirror.argType)
)
}
else -> mirror(declarationMapper, type.def.baseType)
@@ -463,13 +463,16 @@ fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when
val name = type.def.name
when (baseType) {
is TypeMirror.ByValue -> TypeMirror.ByValue(
Classifier.topLevel(pkg, "${name}Var").type,
Classifier.topLevel(pkg, "${name}Var").typeAbbreviation(baseType.pointedType),
baseType.info,
Classifier.topLevel(pkg, name).type,
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.valueType),
nullable = baseType.nullable
)
is TypeMirror.ByRef -> TypeMirror.ByRef(Classifier.topLevel(pkg, name).type, baseType.info)
is TypeMirror.ByRef -> TypeMirror.ByRef(
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.pointedType),
baseType.info
)
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.native.interop.indexer.*
internal fun ObjCMethod.getKotlinParameterNames(forConstructorOrFactory: Boolean = false): List<String> {
@@ -71,7 +70,7 @@ private fun ObjCMethod.getKotlinParameters(
if (this.isInit && this.parameters.isEmpty() && this.selector != "init") {
// Create synthetic Unit parameter, just like Swift does in this case:
val parameterName = this.selector.removePrefix("init").removePrefix("With").decapitalize()
return listOf(FunctionParameterStub(parameterName, WrapperStubType(KotlinTypes.unit)))
return listOf(FunctionParameterStub(parameterName, KotlinTypes.unit.toStubIrType()))
// Note: this parameter is explicitly handled in compiler.
}
@@ -82,12 +81,12 @@ private fun ObjCMethod.getKotlinParameters(
val kotlinType = stubIrBuilder.mirror(it.type).argType
val name = names[index]
val annotations = if (it.nsConsumed) listOf(AnnotationStub.ObjC.Consumed) else emptyList()
FunctionParameterStub(name, WrapperStubType(kotlinType), isVararg = false, annotations = annotations)
FunctionParameterStub(name, kotlinType.toStubIrType(), isVararg = false, annotations = annotations)
}
if (this.isVariadic) {
result += FunctionParameterStub(
names.last(),
WrapperStubType(KotlinTypes.any.makeNullable()),
KotlinTypes.any.makeNullable().toStubIrType(),
isVararg = true,
annotations = emptyList()
)
@@ -115,10 +114,10 @@ private class ObjCMethodStubBuilder(
val returnType = method.getReturnType(container.classOrProtocol)
isStret = returnType.isStret(context.configuration.target)
stubReturnType = if (returnType.unwrapTypedefs() is VoidType) {
WrapperStubType(KotlinTypes.unit)
KotlinTypes.unit
} else {
WrapperStubType(context.mirror(returnType).argType)
}
context.mirror(returnType).argType
}.toStubIrType()
val methodAnnotation = AnnotationStub.ObjC.Method(
method.selector,
method.encoding,
@@ -190,7 +189,7 @@ private class ObjCMethodStubBuilder(
val annotations = buildObjCMethodAnnotations(factoryAnnotation)
val originalReturnType = method.getReturnType(container.clazz)
val typeParameter = TypeParameterStub("T", WrapperStubType(clazz))
val typeParameter = TypeParameterStub("T", clazz.toStubIrType())
val returnType = if (originalReturnType is ObjCPointer) {
typeParameter.getStubType(originalReturnType.isNullable)
} else {
@@ -414,19 +413,19 @@ internal abstract class ObjCContainerStubBuilder(
} else {
if (isMeta) KotlinTypes.objCObjectBaseMeta else KotlinTypes.objCObjectBase
}
interfaces += WrapperStubType(baseClassifier.type)
interfaces += baseClassifier.type.toStubIrType()
}
container.protocols.forEach {
interfaces += WrapperStubType(context.getKotlinClassFor(it, isMeta).type)
interfaces += context.getKotlinClassFor(it, isMeta).type.toStubIrType()
}
if (interfaces.isEmpty()) {
assert(container is ObjCProtocol)
val classifier = if (isMeta) KotlinTypes.objCObjectMeta else KotlinTypes.objCObject
interfaces += WrapperStubType(classifier.type)
interfaces += classifier.type.toStubIrType()
}
if (!isMeta && container.isProtocolClass()) {
// TODO: map Protocol type to ObjCProtocol instead.
interfaces += WrapperStubType(KotlinTypes.objCProtocol.type)
interfaces += KotlinTypes.objCProtocol.type.toStubIrType()
}
interfaces
}
@@ -491,7 +490,7 @@ internal class ObjCClassStubBuilder(
val objCClassType = KotlinTypes.objCClassOf.typeWith(
context.getKotlinClassFor(clazz, isMeta = false).type
).let { WrapperStubType(it) }
).toStubIrType()
val superClassInit = SuperClassInit(companionSuper)
val companion = ClassStub.Companion(superClassInit, listOf(objCClassType))
@@ -575,7 +574,7 @@ private class ObjCPropertyStubBuilder(
is ObjCClassOrProtocol -> null
is ObjCCategory -> ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass))
}
return listOf(PropertyStub(property.name, WrapperStubType(kotlinType), kind, modality, receiver))
return listOf(PropertyStub(property.name, kotlinType.toStubIrType(), kind, modality, receiver))
}
}
@@ -45,11 +45,6 @@ class SimpleStubContainer(
val StubContainer.children: List<StubIrElement>
get() = (classes as List<StubIrElement>) + properties + functions + typealiases
sealed class StubType {
abstract val nullable: Boolean
}
/**
* Marks that abstract value of such type can be passed as value.
*/
@@ -59,45 +54,25 @@ class TypeParameterStub(
val name: String,
val upperBound: StubType? = null
) {
fun getStubType(nullable: Boolean): StubType =
SymbolicStubType(name, nullable = nullable)
fun getStubType(nullable: Boolean) =
TypeParameterType(name, nullable = nullable)
}
// Add variance if needed
class TypeArgumentStub(val type: StubType)
interface TypeArgument {
object StarProjection : TypeArgument
/**
* Wrapper over [KotlinType].
*/
class WrapperStubType(
val kotlinType: KotlinType
) : StubType() {
override val nullable: Boolean
get() = when (kotlinType) {
is KotlinClassifierType -> kotlinType.nullable
is KotlinFunctionType -> kotlinType.nullable
else -> error("Unknown KotlinType: $kotlinType")
}
enum class Variance {
INVARIANT,
IN,
OUT
}
}
/**
* Wrapper over [Classifier].
*/
class ClassifierStubType(
val classifier: Classifier,
val typeArguments: List<TypeArgumentStub> = emptyList(),
override val nullable: Boolean = false
) : StubType()
/**
* Fallback variant for all cases where we cannot refer to specific [KotlinType] or [Classifier].
*/
class SymbolicStubType(
val name: String,
override val nullable: Boolean = false
) : StubType()
class TypeArgumentStub(
val type: StubType,
val variance: TypeArgument.Variance = TypeArgument.Variance.INVARIANT
) : TypeArgument
/**
* Represents a source of StubIr element.
@@ -344,7 +319,7 @@ sealed class PropertyAccessor : FunctionalStub {
override val annotations: List<AnnotationStub> = emptyList()
}
class InterpretPointed(val cGlobalName:String, pointedType: WrapperStubType) : Getter() {
class InterpretPointed(val cGlobalName:String, pointedType: StubType) : Getter() {
override val annotations: List<AnnotationStub> = emptyList()
val typeParameters: List<StubType> = listOf(pointedType)
}
@@ -416,7 +391,7 @@ class EnumEntryStub(
}
class TypealiasStub(
val alias: ClassifierStubType,
val alias: Classifier,
val aliasee: StubType
) : StubIrElement {
@@ -239,11 +239,9 @@ class StubIrBridgeBuilder(
}
}
private fun isCValuesRef(type: StubType): Boolean {
if (type !is WrapperStubType) return false
return type.kotlinType is KotlinClassifierType && type.kotlinType.classifier == KotlinTypes.cValuesRef
}
private fun isCValuesRef(type: StubType): Boolean =
(type as? ClassifierStubType)?.let { it.classifier == KotlinTypes.cValuesRef }
?: false
private fun generateBridgeBody(function: FunctionStub) {
assert(context.platform == KotlinPlatform.JVM) { "Function ${function.name} was not marked as external." }
@@ -16,7 +16,7 @@ internal class MacroConstantStubBuilder(
val declaration = when (constant) {
is IntegerConstantDef -> {
val literal = context.tryCreateIntegralStub(constant.type, constant.value) ?: return emptyList()
val kotlinType = WrapperStubType(context.mirror(constant.type).argType)
val kotlinType = context.mirror(constant.type).argType.toStubIrType()
when (context.platform) {
KotlinPlatform.NATIVE -> PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Constant(literal))
// No reason to make it const val with backing field on Kotlin/JVM yet:
@@ -28,13 +28,13 @@ internal class MacroConstantStubBuilder(
}
is FloatingConstantDef -> {
val literal = context.tryCreateDoubleStub(constant.type, constant.value) ?: return emptyList()
val kotlinType = WrapperStubType(context.mirror(constant.type).argType)
val kotlinType = context.mirror(constant.type).argType.toStubIrType()
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Val(getter))
}
is StringConstantDef -> {
val literal = StringConstantStub(constant.value.quoteAsKotlinLiteral())
val kotlinType = WrapperStubType(KotlinTypes.string)
val kotlinType = KotlinTypes.string.toStubIrType()
val getter = PropertyAccessor.Getter.SimpleGetter(constant = literal)
PropertyStub(kotlinName, kotlinType, PropertyStub.Kind.Val(getter))
}
@@ -83,16 +83,16 @@ internal class StructStubBuilder(
}
val kind = PropertyStub.Kind.Val(PropertyAccessor.Getter.ArrayMemberAt(offset))
// TODO: Should receiver be added?
PropertyStub(field.name, WrapperStubType(type), kind, annotations = annotations)
PropertyStub(field.name, type.toStubIrType(), kind, annotations = annotations)
} else {
val pointedType = WrapperStubType(fieldRefType.pointedType)
val pointedType = fieldRefType.pointedType.toStubIrType()
val pointedTypeArgument = TypeArgumentStub(pointedType)
if (fieldRefType is TypeMirror.ByValue) {
val kind = PropertyStub.Kind.Var(
PropertyAccessor.Getter.MemberAt(offset, typeArguments = listOf(pointedTypeArgument), hasValueAccessor = true),
PropertyAccessor.Setter.MemberAt(offset, typeArguments = listOf(pointedTypeArgument))
)
PropertyStub(field.name, WrapperStubType(fieldRefType.argType), kind)
PropertyStub(field.name, fieldRefType.argType.toStubIrType(), kind)
} else {
val kind = PropertyStub.Kind.Val(PropertyAccessor.Getter.MemberAt(offset, hasValueAccessor = false))
PropertyStub(field.name, pointedType, kind)
@@ -113,15 +113,15 @@ internal class StructStubBuilder(
context.bridgeComponentsBuilder.getterToBridgeInfo[readBits] = BridgeGenerationInfo("", typeInfo)
context.bridgeComponentsBuilder.setterToBridgeInfo[writeBits] = BridgeGenerationInfo("", typeInfo)
val kind = PropertyStub.Kind.Var(readBits, writeBits)
PropertyStub(field.name, WrapperStubType(kotlinType), kind)
PropertyStub(field.name, kotlinType.toStubIrType(), kind)
}
val superClass = SymbolicStubType("CStructVar")
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", SymbolicStubType("NativePtr"))
val superClass = context.platform.getRuntimeType("CStructVar")
require(superClass is ClassifierStubType)
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", context.platform.getRuntimeType("NativePtr"))
val superClassInit = SuperClassInit(superClass, listOf(GetConstructorParameter(rawPtrConstructorParam)))
// TODO: How we will differ Type and CStructVar.Type?
val companionSuper = SymbolicStubType("Type")
val companionSuper = superClass.nested("Type")
val typeSize = listOf(IntegralConstantStub(def.size, 4, true), IntegralConstantStub(def.align.toLong(), 4, true))
val companionSuperInit = SuperClassInit(companionSuper, typeSize)
val companion = ClassStub.Companion(companionSuperInit)
@@ -170,8 +170,8 @@ internal class StructStubBuilder(
private fun generateForwardStruct(s: StructDecl): List<StubIrElement> = when (context.platform) {
KotlinPlatform.JVM -> {
val classifier = context.getKotlinClassForPointed(s)
val superClass = SymbolicStubType("COpaque")
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", SymbolicStubType("NativePtr"))
val superClass = context.platform.getRuntimeType("COpaque")
val rawPtrConstructorParam = ConstructorParameterStub("rawPtr", context.platform.getRuntimeType("NativePtr"))
val superClassInit = SuperClassInit(superClass, listOf(GetConstructorParameter(rawPtrConstructorParam)))
val origin = StubOrigin.Struct(s)
listOf(ClassStub.Simple(classifier, ClassStubModality.NONE, listOf(rawPtrConstructorParam), superClassInit, origin = origin))
@@ -189,7 +189,7 @@ internal class EnumStubBuilder(
return generateEnumAsConstants(enumDef)
}
val baseTypeMirror = context.mirror(enumDef.baseType)
val baseType = WrapperStubType(baseTypeMirror.argType)
val baseType = baseTypeMirror.argType.toStubIrType()
val clazz = (context.mirror(EnumType(enumDef)) as TypeMirror.ByValue).valueType.classifier
val qualifier = ConstructorParameterStub.Qualifier.VAL(overrides = true)
@@ -216,7 +216,7 @@ internal class EnumStubBuilder(
val enum = ClassStub.Enum(clazz, canonicalEntries,
origin = StubOrigin.Enum(enumDef),
constructorParameters = listOf(valueParamStub),
interfaces = listOf(SymbolicStubType("CEnum"))
interfaces = listOf(context.platform.getRuntimeType("CEnum"))
)
context.bridgeComponentsBuilder.enumToTypeMirror[enum] = baseTypeMirror
@@ -260,8 +260,8 @@ internal class EnumStubBuilder(
val varTypeName = typeMirror.info.constructPointedType(typeMirror.valueType)
val varTypeClassifier = typeMirror.pointedType.classifier
val valueTypeClassifier = typeMirror.valueType.classifier
typealiases += TypealiasStub(ClassifierStubType(varTypeClassifier), WrapperStubType(varTypeName))
typealiases += TypealiasStub(ClassifierStubType(valueTypeClassifier), WrapperStubType(baseKotlinType))
typealiases += TypealiasStub(varTypeClassifier, varTypeName.toStubIrType())
typealiases += TypealiasStub(valueTypeClassifier, baseKotlinType.toStubIrType())
kotlinType = typeMirror.valueType
StubContainerMeta()
@@ -273,7 +273,7 @@ internal class EnumStubBuilder(
val kind = PropertyStub.Kind.Val(getter)
entries += PropertyStub(
constant.name,
WrapperStubType(kotlinType),
kotlinType.toStubIrType(),
kind,
MemberStubModality.FINAL,
null
@@ -313,7 +313,7 @@ internal class FunctionStubBuilder(
KotlinPlatform.JVM -> emptyList()
KotlinPlatform.NATIVE -> listOf(AnnotationStub.CCall.CString)
}
val type = WrapperStubType(KotlinTypes.string.makeNullable())
val type = KotlinTypes.string.makeNullable().toStubIrType()
val functionParameterStub = FunctionParameterStub(parameterName, type, annotations, origin = origin)
context.bridgeComponentsBuilder.cStringParameters += functionParameterStub
functionParameterStub
@@ -323,27 +323,27 @@ internal class FunctionStubBuilder(
KotlinPlatform.JVM -> emptyList()
KotlinPlatform.NATIVE -> listOf(AnnotationStub.CCall.WCString)
}
val type = WrapperStubType(KotlinTypes.string.makeNullable())
val type = KotlinTypes.string.makeNullable().toStubIrType()
val functionParameterStub = FunctionParameterStub(parameterName, type, annotations, origin = origin)
context.bridgeComponentsBuilder.wCStringParameters += functionParameterStub
functionParameterStub
}
representAsValuesRef != null -> {
FunctionParameterStub(parameterName, WrapperStubType(representAsValuesRef), origin = origin)
FunctionParameterStub(parameterName, representAsValuesRef.toStubIrType(), origin = origin)
}
else -> {
val mirror = context.mirror(parameter.type)
val type = WrapperStubType(mirror.argType)
val type = mirror.argType.toStubIrType()
FunctionParameterStub(parameterName, type, origin = origin)
}
}
}
val returnType = WrapperStubType(if (func.returnsVoid()) {
val returnType = if (func.returnsVoid()) {
KotlinTypes.unit
} else {
context.mirror(func.returnType).argType
})
}.toStubIrType()
val annotations: List<AnnotationStub>
@@ -353,7 +353,7 @@ internal class FunctionStubBuilder(
mustBeExternal = false
} else {
if (func.isVararg) {
val type = WrapperStubType(KotlinTypes.any.makeNullable())
val type = KotlinTypes.any.makeNullable().toStubIrType()
parameters += FunctionParameterStub("variadicArguments", type, isVararg = true)
}
annotations = listOf(AnnotationStub.CCall.Symbol("${context.generateNextUniqueId("knifunptr_")}_${func.name}"))
@@ -485,12 +485,12 @@ internal class GlobalStubBuilder(
}
is TypeMirror.ByRef -> {
kotlinType = mirror.pointedType
val getter = PropertyAccessor.Getter.InterpretPointed(global.name, WrapperStubType(kotlinType))
val getter = PropertyAccessor.Getter.InterpretPointed(global.name, kotlinType.toStubIrType())
kind = PropertyStub.Kind.Val(getter)
}
}
}
return listOf(PropertyStub(global.name, WrapperStubType(kotlinType), kind))
return listOf(PropertyStub(global.name, kotlinType.toStubIrType(), kind))
}
}
@@ -509,13 +509,13 @@ internal class TypedefStubBuilder(
val varTypeAliasee = mirror.info.constructPointedType(valueType)
val valueTypeAliasee = baseMirror.valueType
listOf(
TypealiasStub(ClassifierStubType(varType), WrapperStubType(varTypeAliasee)),
TypealiasStub(ClassifierStubType(valueType.classifier), WrapperStubType(valueTypeAliasee))
TypealiasStub(varType, varTypeAliasee.toStubIrType()),
TypealiasStub(valueType.classifier, valueTypeAliasee.toStubIrType())
)
}
is TypeMirror.ByRef -> {
val varTypeAliasee = baseMirror.pointedType
listOf(TypealiasStub(ClassifierStubType(varType), WrapperStubType(varTypeAliasee)))
listOf(TypealiasStub(varType, varTypeAliasee.toStubIrType()))
}
}
}
@@ -42,8 +42,8 @@ fun StubContainer.computeNamesToBeDeclared(pkgName: String): List<String> {
}.onEach { checkPackageCorrectness(it) }.map { it.topLevelName }
val typealiasNames = typealiases
.onEach { checkPackageCorrectness(it.alias.classifier) }
.map { it.alias.classifier.topLevelName }
.onEach { checkPackageCorrectness(it.alias) }
.map { it.alias.topLevelName }
val namesFromNestedContainers = simpleContainers
.flatMap { it.computeNamesToBeDeclared(pkgName) }
@@ -209,7 +209,7 @@ class StubIrTextEmitter(
}
override fun visitTypealias(element: TypealiasStub, owner: StubContainer?) {
val alias = renderClassifierDeclaration(element.alias.classifier) + renderTypeArguments(element.alias.typeArguments)
val alias = renderClassifierDeclaration(element.alias)
val aliasee = renderStubType(element.aliasee)
out("typealias $alias = $aliasee")
}
@@ -464,15 +464,27 @@ class StubIrTextEmitter(
return "${renderStubType(superClassInit.type)}$parameters"
}
private fun renderStubType(stubType: StubType): String = when (stubType) {
is WrapperStubType -> stubType.kotlinType.render(kotlinFile)
is ClassifierStubType -> {
val classifier = kotlinFile.reference(stubType.classifier)
val typeArguments = renderTypeArguments(stubType.typeArguments)
val nullability = if (stubType.nullable) "?" else ""
"$classifier$typeArguments$nullability"
private fun renderStubType(stubType: StubType): String {
val nullable = if (stubType.nullable) "?" else ""
return when (stubType) {
is ClassifierStubType -> {
val classifier = kotlinFile.reference(stubType.classifier)
val typeArguments = renderTypeArguments(stubType.typeArguments)
"$classifier$typeArguments$nullable"
}
is FunctionalType -> buildString {
if (stubType.nullable) append("(")
append('(')
stubType.parameterTypes.joinTo(this) { renderStubType(it) }
append(") -> ")
append(renderStubType(stubType.returnType))
if (stubType.nullable) append(")?")
}
is TypeParameterType -> "${stubType.name}$nullable"
}
is SymbolicStubType -> stubType.name + if (stubType.nullable) "?" else ""
}
private fun renderValueUsage(value: ValueStub): String = when (value) {
@@ -647,12 +659,25 @@ class StubIrTextEmitter(
}
}
private fun renderTypeArguments(typeArguments: List<TypeArgumentStub>) = if (typeArguments.isNotEmpty()) {
typeArguments.joinToString(", ", "<", ">") { renderStubType(it.type) }
private fun renderTypeArguments(typeArguments: List<TypeArgument>) = if (typeArguments.isNotEmpty()) {
typeArguments.joinToString(", ", "<", ">") { renderTypeArgument(it) }
} else {
""
}
private fun renderTypeArgument(typeArgument: TypeArgument) = when (typeArgument) {
is TypeArgumentStub -> {
val variance = when (typeArgument.variance) {
TypeArgument.Variance.INVARIANT -> ""
TypeArgument.Variance.IN -> "in "
TypeArgument.Variance.OUT -> "out "
}
"$variance${renderStubType(typeArgument.type)}"
}
TypeArgument.StarProjection -> "*"
else -> error("Unexpected type argument: $typeArgument")
}
private fun renderTypeParameters(typeParameters: List<TypeParameterStub>) = if (typeParameters.isNotEmpty()) {
typeParameters.joinToString(", ", " <", ">") { renderTypeParameter(it) }
} else {
@@ -0,0 +1,176 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.interop.gen
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
sealed class StubType {
abstract val nullable: Boolean
}
/**
* Wrapper over [Classifier].
* @property underlyingType is not null if this type is an typealias.
*/
class ClassifierStubType(
val classifier: Classifier,
val typeArguments: List<TypeArgument> = emptyList(),
val underlyingType: StubType? = null,
override val nullable: Boolean = false
) : StubType() {
fun nested(name: String): ClassifierStubType {
assert(underlyingType != null) {
"Cannot access nested class `$name` of typealias ${classifier.fqName}"
}
return ClassifierStubType(classifier.nested(name))
}
}
/**
* @return type from kotlinx.cinterop package
*/
fun KotlinPlatform.getRuntimeType(name: String, nullable: Boolean = false): StubType {
val classifier = Classifier.topLevel("kotlinx.cinterop", name)
PredefinedTypesHandler.tryExpandPlatformDependentTypealias(classifier, this, nullable)?.let { return it }
return ClassifierStubType(classifier, nullable = nullable)
}
/**
* Functional type from kotlin package: ([parameterTypes]) -> [returnType]
*/
class FunctionalType(
val parameterTypes: List<StubType>,
val returnType: StubType,
override val nullable: Boolean = false
) : StubType() {
val classifier: Classifier =
Classifier.topLevel("kotlin", "Function${parameterTypes.size}")
}
class TypeParameterType(
val name: String,
override val nullable: Boolean
) : StubType()
fun KotlinType.toStubIrType(): StubType = when (this) {
is KotlinFunctionType -> this.toStubIrType()
is KotlinClassifierType -> this.toStubIrType()
else -> error("Unexpected KotlinType: $this")
}
private fun KotlinFunctionType.toStubIrType(): StubType =
FunctionalType(parameterTypes.map { it.toStubIrType() }, returnType.toStubIrType(), nullable)
private fun KotlinClassifierType.toStubIrType(): StubType {
PredefinedTypesHandler.tryExpandPredefinedTypealias(classifier, nullable)?.let { return it }
val typeArguments = arguments.map { it.toStubIrType() }
val underlyingType = underlyingType?.toStubIrType()
return ClassifierStubType(classifier, typeArguments, underlyingType, nullable)
}
private fun KotlinTypeArgument.toStubIrType(): TypeArgument = when (this) {
is KotlinType -> TypeArgumentStub(this.toStubIrType())
StarProjection -> TypeArgument.StarProjection
else -> error("Unexpected KotlinTypeArgument: $this")
}
/**
* Types that come from kotlinx.cinterop require special handling because we
* don't have explicit information about their structure.
* For example, to be able to produce metadata-based interop library we need to know
* that ByteVar is a typealias to ByteVarOf<Byte>.
*/
private object PredefinedTypesHandler {
private const val cInteropPackage = "kotlinx.cinterop"
private val nativePtrClassifier = Classifier.topLevel(cInteropPackage, "NativePtr")
private val primitives = setOf(
KotlinTypes.boolean,
KotlinTypes.byte, KotlinTypes.short, KotlinTypes.int, KotlinTypes.long,
KotlinTypes.uByte, KotlinTypes.uShort, KotlinTypes.uInt, KotlinTypes.uLong,
KotlinTypes.float, KotlinTypes.double
)
/**
* kotlinx.cinterop.{primitive}Var -> kotlin.{primitive}
*/
private val primitiveVarClassifierToPrimitiveType: Map<Classifier, KotlinClassifierType> =
primitives.associateBy {
val typeVar = "${it.classifier.topLevelName}Var"
Classifier.topLevel(cInteropPackage, typeVar)
}
/**
* @param primitiveType primitive type from kotlin package.
* @return kotlinx.cinterop.[primitiveType]VarOf<[primitiveType]>
*/
private fun getVarOfTypeFor(primitiveType: KotlinClassifierType, nullable: Boolean): ClassifierStubType {
val typeVarOf = "${primitiveType.classifier.topLevelName}VarOf"
val classifier = Classifier.topLevel(cInteropPackage, typeVarOf)
return ClassifierStubType(classifier, listOf(TypeArgumentStub(primitiveType.toStubIrType())), nullable = nullable)
}
private fun expandCOpaquePointerVar(nullable: Boolean): ClassifierStubType {
val typeArgument = TypeArgumentStub(expandCOpaquePointer(nullable=false))
val underlyingType = ClassifierStubType(
KotlinTypes.cPointerVarOf, listOf(typeArgument), nullable = nullable
)
return ClassifierStubType(
KotlinTypes.cOpaquePointerVar.classifier, underlyingType = underlyingType, nullable = nullable
)
}
private fun expandCOpaquePointer(nullable: Boolean): ClassifierStubType {
val typeArgument = TypeArgumentStub(ClassifierStubType(KotlinTypes.cPointed), TypeArgument.Variance.OUT)
val underlyingType = ClassifierStubType(
KotlinTypes.cPointer, listOf(typeArgument), nullable = nullable
)
return ClassifierStubType(
KotlinTypes.cOpaquePointer.classifier, underlyingType = underlyingType, nullable = nullable
)
}
/**
* @param primitiveVarType one of kotlinx.cinterop.{primitive}Var types.
* @return typealias in terms of StubIR types.
*/
private fun expandPrimitiveVarType(primitiveVarClassifier: Classifier, nullable: Boolean): ClassifierStubType {
val primitiveType = primitiveVarClassifierToPrimitiveType.getValue(primitiveVarClassifier)
val underlyingType = getVarOfTypeFor(primitiveType, nullable)
return ClassifierStubType(primitiveVarClassifier, underlyingType = underlyingType, nullable = nullable)
}
private fun expandNativePtr(platform: KotlinPlatform, nullable: Boolean): ClassifierStubType {
val underlyingTypeClassifier = when (platform) {
KotlinPlatform.JVM -> KotlinTypes.long.classifier
KotlinPlatform.NATIVE -> Classifier.topLevel("kotlin.native.internal", "NativePtr")
}
val underlyingType = ClassifierStubType(underlyingTypeClassifier, nullable = nullable)
return ClassifierStubType(nativePtrClassifier, underlyingType = underlyingType, nullable = nullable)
}
/**
* @return [ClassifierStubType] if [classifier] is a typealias from [kotlinx.cinterop] package.
*/
fun tryExpandPredefinedTypealias(classifier: Classifier, nullable: Boolean): ClassifierStubType? =
when (classifier) {
in primitiveVarClassifierToPrimitiveType.keys -> expandPrimitiveVarType(classifier, nullable)
KotlinTypes.cOpaquePointer.classifier -> expandCOpaquePointer(nullable)
KotlinTypes.cOpaquePointerVar.classifier -> expandCOpaquePointerVar(nullable)
else -> null
}
/**
* Variant of [tryExpandPredefinedTypealias] with [platform]-dependent result.
*/
fun tryExpandPlatformDependentTypealias(
classifier: Classifier, platform: KotlinPlatform, nullable: Boolean
): ClassifierStubType? =
when (classifier) {
nativePtrClassifier -> expandNativePtr(platform, nullable)
else -> null
}
}