[Interop] Started to work on StubIr -> Metadata translator
Basic support for function, global properties, and type aliases. Also added AbbreviatedStubType to make translation to metadata a bit easier.
This commit is contained in:
committed by
Sergey Bogolepov
parent
0712a2ae48
commit
ee672f31df
+12
-4
@@ -6,6 +6,10 @@ package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
// TODO: Replace all usages of these strings with constants.
|
||||
const val cinteropPackage = "kotlinx.cinterop"
|
||||
const val cinteropInternalPackage = "$cinteropPackage.internal"
|
||||
|
||||
interface StubIrElement {
|
||||
fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R
|
||||
}
|
||||
@@ -60,7 +64,10 @@ class TypeParameterStub(
|
||||
}
|
||||
|
||||
interface TypeArgument {
|
||||
object StarProjection : TypeArgument
|
||||
object StarProjection : TypeArgument {
|
||||
override fun toString(): String =
|
||||
"*"
|
||||
}
|
||||
|
||||
enum class Variance {
|
||||
INVARIANT,
|
||||
@@ -72,7 +79,10 @@ interface TypeArgument {
|
||||
class TypeArgumentStub(
|
||||
val type: StubType,
|
||||
val variance: TypeArgument.Variance = TypeArgument.Variance.INVARIANT
|
||||
) : TypeArgument
|
||||
) : TypeArgument {
|
||||
override fun toString(): String =
|
||||
type.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a source of StubIr element.
|
||||
@@ -171,8 +181,6 @@ sealed class AnnotationStub(val classifier: Classifier) {
|
||||
AnnotationStub(Classifier.topLevel("kotlin", "Deprecated"))
|
||||
|
||||
private companion object {
|
||||
const val cinteropInternalPackage = "kotlinx.cinterop.internal"
|
||||
const val cinteropPackage = "kotlinx.cinterop"
|
||||
val cCallClassifier = Classifier.topLevel(cinteropInternalPackage, "CCall")
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -37,12 +37,17 @@ class StubIrContext(
|
||||
}
|
||||
).precompileHeaders()
|
||||
|
||||
// TODO: Used only for JVM.
|
||||
val jvmFileClassName = if (configuration.pkgName.isEmpty()) {
|
||||
libName
|
||||
} else {
|
||||
configuration.pkgName.substringAfterLast('.')
|
||||
}
|
||||
|
||||
val validPackageName = configuration.pkgName.split(".").joinToString(".") {
|
||||
if (it.matches(VALID_PACKAGE_NAME_REGEX)) it else "`$it`"
|
||||
}
|
||||
|
||||
private val anonymousStructKotlinNames = mutableMapOf<StructDecl, String>()
|
||||
|
||||
private val forbiddenStructNames = run {
|
||||
@@ -86,6 +91,10 @@ class StubIrContext(
|
||||
|
||||
// TODO: consider exporting Objective-C class and protocol forward refs.
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VALID_PACKAGE_NAME_REGEX = "[a-zA-Z0-9_.]+".toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
class StubIrDriver(
|
||||
@@ -95,6 +104,7 @@ class StubIrDriver(
|
||||
data class DriverOptions(
|
||||
val mode: GenerationMode,
|
||||
val entryPoint: String?,
|
||||
val moduleName: String,
|
||||
val outCFile: File,
|
||||
val outKtFileCreator: () -> File
|
||||
)
|
||||
@@ -106,7 +116,7 @@ class StubIrDriver(
|
||||
}
|
||||
|
||||
fun run(): Result {
|
||||
val (mode, entryPoint, outCFile, outKtFile) = options
|
||||
val (mode, entryPoint, moduleName, outCFile, outKtFile) = options
|
||||
|
||||
val builderResult = StubIrBuilder(context).build()
|
||||
val bridgeBuilderResult = StubIrBridgeBuilder(context, builderResult).build()
|
||||
@@ -119,7 +129,7 @@ class StubIrDriver(
|
||||
GenerationMode.SOURCE_CODE -> {
|
||||
emitSourceCode(outKtFile(), builderResult, bridgeBuilderResult)
|
||||
}
|
||||
GenerationMode.METADATA -> emitMetadata(builderResult)
|
||||
GenerationMode.METADATA -> emitMetadata(builderResult, moduleName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +142,8 @@ class StubIrDriver(
|
||||
return Result.SourceCode
|
||||
}
|
||||
|
||||
private fun emitMetadata(builderResult: StubIrBuilderResult): Result.Metadata {
|
||||
return Result.Metadata(KlibModuleMetadata("test", emptyList(), emptyList()))
|
||||
}
|
||||
private fun emitMetadata(builderResult: StubIrBuilderResult, moduleName: String) =
|
||||
Result.Metadata(StubIrMetadataEmitter(context, builderResult, moduleName).emit())
|
||||
|
||||
private fun emitCFile(context: StubIrContext, cFile: Appendable, entryPoint: String?, nativeBridges: NativeBridges) {
|
||||
val out = { it: String -> cFile.appendln(it) }
|
||||
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* 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 kotlinx.metadata.*
|
||||
import kotlinx.metadata.klib.*
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class StubIrMetadataEmitter(
|
||||
private val context: StubIrContext,
|
||||
private val builderResult: StubIrBuilderResult,
|
||||
private val moduleName: String
|
||||
) {
|
||||
fun emit(): KlibModuleMetadata {
|
||||
val annotations = emptyList<KmAnnotation>()
|
||||
val fragments = emitModuleFragments()
|
||||
return KlibModuleMetadata(moduleName, fragments, annotations)
|
||||
}
|
||||
|
||||
private fun emitModuleFragments(): List<KmModuleFragment> =
|
||||
ModuleMetadataEmitter(context.configuration.pkgName).let {
|
||||
builderResult.stubs.accept(it, null)
|
||||
listOf(it.writeModule())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates single [StubContainer] to [KmModuleFragment].
|
||||
*/
|
||||
internal class ModuleMetadataEmitter(
|
||||
private val packageFqName: String
|
||||
) : StubIrVisitor<StubContainer?, Unit> {
|
||||
|
||||
private val uniqIds = StubIrUniqIdProvider()
|
||||
|
||||
private val classes = mutableListOf<KmClass>()
|
||||
private val properties = mutableListOf<KmProperty>()
|
||||
private val typeAliases = mutableListOf<KmTypeAlias>()
|
||||
private val functions = mutableListOf<KmFunction>()
|
||||
|
||||
fun writeModule(): KmModuleFragment = KmModuleFragment().also { km ->
|
||||
km.fqName = packageFqName
|
||||
km.classes += classes
|
||||
km.pkg = writePackage()
|
||||
}
|
||||
|
||||
private fun writePackage() = KmPackage().also { km ->
|
||||
km.fqName = packageFqName
|
||||
km.typeAliases += typeAliases
|
||||
km.properties += properties
|
||||
km.functions += functions
|
||||
}
|
||||
|
||||
override fun visitClass(element: ClassStub, data: StubContainer?) {
|
||||
// TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun visitTypealias(element: TypealiasStub, data: StubContainer?) {
|
||||
KmTypeAlias(element.flags, element.alias.topLevelName).apply {
|
||||
uniqId = uniqIds.uniqIdForTypeAlias(element)
|
||||
underlyingType = element.aliasee.map(shouldExpandTypeAliases = false)
|
||||
expandedType = element.aliasee.map()
|
||||
}.let(typeAliases::add)
|
||||
}
|
||||
|
||||
override fun visitFunction(element: FunctionStub, data: StubContainer?) {
|
||||
KmFunction(element.flags, element.name).apply {
|
||||
element.annotations.mapTo(annotations, AnnotationStub::map)
|
||||
returnType = element.returnType.map()
|
||||
element.parameters.mapTo(valueParameters, FunctionParameterStub::map)
|
||||
element.typeParameters.mapTo(typeParameters, TypeParameterStub::map)
|
||||
uniqId = uniqIds.uniqIdForFunction(element)
|
||||
}.let(functions::add)
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, data: StubContainer?) {
|
||||
KmProperty(element.flags, element.name, element.getterFlags, element.setterFlags).apply {
|
||||
element.annotations.mapTo(annotations, AnnotationStub::map)
|
||||
uniqId = uniqIds.uniqIdForProperty(element)
|
||||
returnType = element.type.map()
|
||||
if (element.kind is PropertyStub.Kind.Var) {
|
||||
val setter = element.kind.setter
|
||||
setter.annotations.mapTo(setterAnnotations, AnnotationStub::map)
|
||||
// TODO: Maybe it's better to explicitly add setter parameter in stub.
|
||||
setterParameter = FunctionParameterStub("value", element.type).map()
|
||||
}
|
||||
getterAnnotations += when (element.kind) {
|
||||
is PropertyStub.Kind.Val -> element.kind.getter.annotations.map(AnnotationStub::map)
|
||||
is PropertyStub.Kind.Var -> element.kind.getter.annotations.map(AnnotationStub::map)
|
||||
is PropertyStub.Kind.Constant -> emptyList()
|
||||
}
|
||||
if (element.kind is PropertyStub.Kind.Constant) {
|
||||
compileTimeValue = element.kind.constant.map()
|
||||
}
|
||||
}.let(properties::add)
|
||||
}
|
||||
|
||||
override fun visitConstructor(constructorStub: ConstructorStub, data: StubContainer?) {
|
||||
// TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(propertyAccessor: PropertyAccessor, data: StubContainer?) {
|
||||
// TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, data: StubContainer?) {
|
||||
simpleStubContainer.children.forEach { it.accept(this, simpleStubContainer) }
|
||||
}
|
||||
}
|
||||
|
||||
private val FunctionStub.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.Common.IS_PUBLIC,
|
||||
Flag.Function.IS_EXTERNAL,
|
||||
Flag.HAS_ANNOTATIONS.takeIf { annotations.isNotEmpty() },
|
||||
Flag.IS_FINAL.takeIf { modality == MemberStubModality.FINAL },
|
||||
Flag.IS_OPEN.takeIf { modality == MemberStubModality.OPEN }
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val Classifier.fqNameSerialized: String
|
||||
get() = buildString {
|
||||
if (pkg.isNotEmpty()) {
|
||||
append(pkg.replace('.', '/'))
|
||||
append('/')
|
||||
}
|
||||
// Nested classes should dot-separated.
|
||||
append(relativeFqName)
|
||||
}
|
||||
|
||||
private val PropertyStub.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.IS_PUBLIC,
|
||||
Flag.Property.IS_DECLARATION,
|
||||
Flag.HAS_ANNOTATIONS.takeIf { annotations.isNotEmpty() },
|
||||
Flag.IS_FINAL.takeIf { modality == MemberStubModality.FINAL },
|
||||
when (kind) {
|
||||
is PropertyStub.Kind.Val -> null
|
||||
is PropertyStub.Kind.Var -> Flag.Property.IS_VAR
|
||||
is PropertyStub.Kind.Constant -> Flag.Property.IS_CONST
|
||||
},
|
||||
when (kind) {
|
||||
is PropertyStub.Kind.Constant -> null
|
||||
is PropertyStub.Kind.Val,
|
||||
is PropertyStub.Kind.Var -> Flag.Property.HAS_GETTER
|
||||
},
|
||||
when (kind) {
|
||||
is PropertyStub.Kind.Constant -> null
|
||||
is PropertyStub.Kind.Val -> null
|
||||
is PropertyStub.Kind.Var -> Flag.Property.HAS_SETTER
|
||||
}
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val PropertyStub.getterFlags: Flags
|
||||
get() = when (kind) {
|
||||
is PropertyStub.Kind.Val -> kind.getter.flags
|
||||
is PropertyStub.Kind.Var -> kind.getter.flags
|
||||
is PropertyStub.Kind.Constant -> flagsOf()
|
||||
}
|
||||
|
||||
private val PropertyAccessor.Getter.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.HAS_ANNOTATIONS.takeIf { annotations.isNotEmpty() },
|
||||
Flag.IS_PUBLIC,
|
||||
Flag.IS_FINAL,
|
||||
Flag.PropertyAccessor.IS_EXTERNAL.takeIf { this is PropertyAccessor.Getter.ExternalGetter }
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val PropertyStub.setterFlags: Flags
|
||||
get() = if (kind !is PropertyStub.Kind.Var) flagsOf()
|
||||
else kind.setter.flags
|
||||
|
||||
private val PropertyAccessor.Setter.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.HAS_ANNOTATIONS.takeIf { annotations.isNotEmpty() },
|
||||
Flag.IS_PUBLIC,
|
||||
Flag.IS_FINAL,
|
||||
Flag.PropertyAccessor.IS_EXTERNAL.takeIf { this is PropertyAccessor.Setter.ExternalSetter }
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val StubType.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.Type.IS_NULLABLE.takeIf { nullable }
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val TypealiasStub.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.IS_PUBLIC
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private val FunctionParameterStub.flags: Flags
|
||||
get() = listOfNotNull(
|
||||
Flag.HAS_ANNOTATIONS.takeIf { annotations.isNotEmpty() }
|
||||
).let { flagsOf(*it.toTypedArray()) }
|
||||
|
||||
private fun AnnotationStub.map(): KmAnnotation {
|
||||
val args = when (this) {
|
||||
AnnotationStub.ObjC.ConsumesReceiver -> TODO()
|
||||
AnnotationStub.ObjC.ReturnsRetained -> TODO()
|
||||
is AnnotationStub.ObjC.Method -> TODO()
|
||||
is AnnotationStub.ObjC.Factory -> TODO()
|
||||
AnnotationStub.ObjC.Consumed -> TODO()
|
||||
is AnnotationStub.ObjC.Constructor -> TODO()
|
||||
is AnnotationStub.ObjC.ExternalClass -> TODO()
|
||||
AnnotationStub.CCall.CString -> mapOf()
|
||||
AnnotationStub.CCall.WCString -> mapOf()
|
||||
is AnnotationStub.CCall.Symbol ->
|
||||
mapOf("id" to KmAnnotationArgument.StringValue(symbolName))
|
||||
is AnnotationStub.CStruct -> TODO()
|
||||
is AnnotationStub.CNaturalStruct -> TODO()
|
||||
is AnnotationStub.CLength -> TODO()
|
||||
is AnnotationStub.Deprecated -> TODO()
|
||||
}
|
||||
return KmAnnotation(classifier.fqNameSerialized, args)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shouldExpandTypeAliases describes how should we write type aliases.
|
||||
* If [shouldExpandTypeAliases] is true then type alias-based types are written as
|
||||
* ```
|
||||
* Type {
|
||||
* abbreviatedType = AbbreviatedType.abbreviatedClassifier
|
||||
* classifier = AbbreviatedType.underlyingType
|
||||
* arguments = AbbreviatedType.underlyingType.typeArguments
|
||||
* }
|
||||
* ```
|
||||
* So we basically replacing type alias with underlying class.
|
||||
* Otherwise:
|
||||
* ```
|
||||
* Type {
|
||||
* classifier = AbbreviatedType.abbreviatedClassifier
|
||||
* }
|
||||
* ```
|
||||
* As of 25 Nov 2019, the latter form is used only for KmTypeAlias.underlyingType.
|
||||
*/
|
||||
// TODO: Add caching if needed.
|
||||
private fun StubType.map(shouldExpandTypeAliases: Boolean = true): KmType = when (this) {
|
||||
is AbbreviatedType -> {
|
||||
val typeAliasClassifier = KmClassifier.TypeAlias(abbreviatedClassifier.fqNameSerialized)
|
||||
if (shouldExpandTypeAliases) {
|
||||
// Abbreviated and expanded types have the same nullability.
|
||||
KmType(flags).also { km ->
|
||||
km.abbreviatedType = KmType(flags).also { it.classifier = typeAliasClassifier }
|
||||
val kmUnderlyingType = underlyingType.map(true)
|
||||
km.arguments += kmUnderlyingType.arguments
|
||||
km.classifier = kmUnderlyingType.classifier
|
||||
}
|
||||
} else {
|
||||
KmType(flags).also { km -> km.classifier = typeAliasClassifier }
|
||||
}
|
||||
}
|
||||
is ClassifierStubType -> KmType(flags).also { km ->
|
||||
typeArguments.mapTo(km.arguments) { it.map(shouldExpandTypeAliases) }
|
||||
km.classifier = KmClassifier.Class(classifier.fqNameSerialized)
|
||||
}
|
||||
is FunctionalType -> KmType(flags).also { km ->
|
||||
typeArguments.mapTo(km.arguments) { it.map(shouldExpandTypeAliases) }
|
||||
km.classifier = KmClassifier.Class(classifier.fqNameSerialized)
|
||||
}
|
||||
is TypeParameterType -> KmType(flags).also { km ->
|
||||
km.classifier = KmClassifier.TypeParameter(id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionParameterStub.map(): KmValueParameter =
|
||||
KmValueParameter(flags, name).also { km ->
|
||||
val kmType = type.map()
|
||||
if (isVararg) {
|
||||
km.varargElementType = kmType
|
||||
} else {
|
||||
km.type = kmType
|
||||
}
|
||||
annotations.mapTo(km.annotations, AnnotationStub::map)
|
||||
}
|
||||
|
||||
private fun TypeParameterStub.map(): KmTypeParameter =
|
||||
KmTypeParameter(flagsOf(), name, id, KmVariance.INVARIANT).also { km ->
|
||||
km.upperBounds.addIfNotNull(upperBound?.map())
|
||||
}
|
||||
|
||||
private fun TypeArgument.map(expanded: Boolean=true): KmTypeProjection = when (this) {
|
||||
TypeArgument.StarProjection -> KmTypeProjection.STAR
|
||||
is TypeArgumentStub -> KmTypeProjection(variance.map(), type.map(expanded))
|
||||
else -> error("Unexpected TypeArgument: $this")
|
||||
}
|
||||
|
||||
private fun TypeArgument.Variance.map(): KmVariance = when (this) {
|
||||
TypeArgument.Variance.INVARIANT -> KmVariance.INVARIANT
|
||||
TypeArgument.Variance.IN -> KmVariance.IN
|
||||
TypeArgument.Variance.OUT -> KmVariance.OUT
|
||||
}
|
||||
|
||||
private fun ConstantStub.map(): KmAnnotationArgument<*> = when (this) {
|
||||
is StringConstantStub -> KmAnnotationArgument.StringValue(value)
|
||||
is IntegralConstantStub -> when (size) {
|
||||
1 -> if (isSigned) {
|
||||
KmAnnotationArgument.ByteValue(value.toByte())
|
||||
} else {
|
||||
KmAnnotationArgument.UByteValue(value.toByte())
|
||||
}
|
||||
2 -> if (isSigned) {
|
||||
KmAnnotationArgument.ShortValue(value.toShort())
|
||||
} else {
|
||||
KmAnnotationArgument.UShortValue(value.toShort())
|
||||
}
|
||||
4 -> if (isSigned) {
|
||||
KmAnnotationArgument.IntValue(value.toInt())
|
||||
} else {
|
||||
KmAnnotationArgument.UIntValue(value.toInt())
|
||||
}
|
||||
8 -> if (isSigned) {
|
||||
KmAnnotationArgument.LongValue(value)
|
||||
} else {
|
||||
KmAnnotationArgument.ULongValue(value)
|
||||
}
|
||||
|
||||
else -> error("Integral constant of value $value with unexpected size of $size.")
|
||||
}
|
||||
is DoubleConstantStub -> when (size) {
|
||||
4 -> KmAnnotationArgument.FloatValue(value.toFloat())
|
||||
8 -> KmAnnotationArgument.DoubleValue(value)
|
||||
else -> error("Floating-point constant of value $value with unexpected size of $size.")
|
||||
}
|
||||
}
|
||||
|
||||
private val TypeParameterType.id: Int
|
||||
get() = TODO()
|
||||
|
||||
private val TypeParameterStub.id: Int
|
||||
get() = TODO()
|
||||
+7
-19
@@ -29,19 +29,9 @@ class StubIrTextEmitter(
|
||||
private val pkgName: String
|
||||
get() = context.configuration.pkgName
|
||||
|
||||
private val jvmFileClassName = if (pkgName.isEmpty()) {
|
||||
context.libName
|
||||
} else {
|
||||
pkgName.substringAfterLast('.')
|
||||
}
|
||||
|
||||
private val StubContainer.isTopLevelContainer: Boolean
|
||||
get() = this == builderResult.stubs
|
||||
|
||||
companion object {
|
||||
private val VALID_PACKAGE_NAME_REGEX = "[a-zA-Z0-9_.]+".toRegex()
|
||||
}
|
||||
|
||||
/**
|
||||
* The output currently used by the generator.
|
||||
* Should append line separator after any usage.
|
||||
@@ -97,7 +87,7 @@ class StubIrTextEmitter(
|
||||
|
||||
private fun emitKotlinFileHeader() {
|
||||
if (context.platform == KotlinPlatform.JVM) {
|
||||
out("@file:JvmName(${jvmFileClassName.quoteAsKotlinLiteral()})")
|
||||
out("@file:JvmName(${context.jvmFileClassName.quoteAsKotlinLiteral()})")
|
||||
}
|
||||
if (context.platform == KotlinPlatform.NATIVE) {
|
||||
out("@file:kotlinx.cinterop.InteropStubs")
|
||||
@@ -124,14 +114,7 @@ class StubIrTextEmitter(
|
||||
|
||||
out("@file:Suppress(${suppress.joinToString { it.quoteAsKotlinLiteral() }})")
|
||||
if (pkgName != "") {
|
||||
val packageName = pkgName.split(".").joinToString("."){
|
||||
if(it.matches(VALID_PACKAGE_NAME_REGEX)){
|
||||
it
|
||||
}else{
|
||||
"`$it`"
|
||||
}
|
||||
}
|
||||
out("package $packageName")
|
||||
out("package ${context.validPackageName}")
|
||||
out("")
|
||||
}
|
||||
if (context.platform == KotlinPlatform.NATIVE) {
|
||||
@@ -462,6 +445,11 @@ class StubIrTextEmitter(
|
||||
if (stubType.nullable) append(")?")
|
||||
}
|
||||
is TypeParameterType -> "${stubType.name}$nullable"
|
||||
is AbbreviatedType -> {
|
||||
val classifier = kotlinFile.reference(stubType.abbreviatedClassifier)
|
||||
val typeArguments = renderTypeArguments(stubType.typeArguments)
|
||||
"$classifier$typeArguments$nullable"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-31
@@ -5,34 +5,44 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
|
||||
sealed class StubType {
|
||||
abstract val nullable: Boolean
|
||||
abstract val typeArguments: List<TypeArgument>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 typeArguments: List<TypeArgument> = emptyList(),
|
||||
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))
|
||||
}
|
||||
|
||||
fun nested(name: String): ClassifierStubType =
|
||||
ClassifierStubType(classifier.nested(name))
|
||||
|
||||
override fun toString(): String =
|
||||
"${classifier.topLevelName}${typeArguments.ifNotEmpty { joinToString(prefix = "<", postfix = ">") } ?: ""}"
|
||||
}
|
||||
|
||||
class AbbreviatedType(
|
||||
val underlyingType: StubType,
|
||||
val abbreviatedClassifier: Classifier,
|
||||
override val typeArguments: List<TypeArgument>,
|
||||
override val nullable: Boolean = false
|
||||
) : StubType() {
|
||||
override fun toString(): String =
|
||||
"${abbreviatedClassifier.topLevelName}${typeArguments.ifNotEmpty { joinToString(prefix = "<", postfix = ">") } ?: ""}"
|
||||
}
|
||||
|
||||
/**
|
||||
* @return type from kotlinx.cinterop package
|
||||
*/
|
||||
fun KotlinPlatform.getRuntimeType(name: String, nullable: Boolean = false): StubType {
|
||||
val classifier = Classifier.topLevel("kotlinx.cinterop", name)
|
||||
val classifier = Classifier.topLevel(cinteropPackage, name)
|
||||
PredefinedTypesHandler.tryExpandPlatformDependentTypealias(classifier, this, nullable)?.let { return it }
|
||||
return ClassifierStubType(classifier, nullable = nullable)
|
||||
}
|
||||
@@ -41,18 +51,24 @@ fun KotlinPlatform.getRuntimeType(name: String, nullable: Boolean = false): Stub
|
||||
* Functional type from kotlin package: ([parameterTypes]) -> [returnType]
|
||||
*/
|
||||
class FunctionalType(
|
||||
val parameterTypes: List<StubType>,
|
||||
val parameterTypes: List<StubType>, // TODO: Use TypeArguments.
|
||||
val returnType: StubType,
|
||||
override val nullable: Boolean = false
|
||||
) : StubType() {
|
||||
val classifier: Classifier =
|
||||
Classifier.topLevel("kotlin", "Function${parameterTypes.size}")
|
||||
|
||||
override val typeArguments: List<TypeArgument> by lazy {
|
||||
listOf(*parameterTypes.toTypedArray(), returnType).map { TypeArgumentStub(it) }
|
||||
}
|
||||
}
|
||||
|
||||
class TypeParameterType(
|
||||
val name: String,
|
||||
override val nullable: Boolean
|
||||
) : StubType()
|
||||
) : StubType() {
|
||||
override val typeArguments: List<TypeArgument> = emptyList()
|
||||
}
|
||||
|
||||
fun KotlinType.toStubIrType(): StubType = when (this) {
|
||||
is KotlinFunctionType -> this.toStubIrType()
|
||||
@@ -61,13 +77,16 @@ fun KotlinType.toStubIrType(): StubType = when (this) {
|
||||
}
|
||||
|
||||
private fun KotlinFunctionType.toStubIrType(): StubType =
|
||||
FunctionalType(parameterTypes.map { it.toStubIrType() }, returnType.toStubIrType(), nullable)
|
||||
FunctionalType(parameterTypes.map(KotlinType::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)
|
||||
val typeArguments = arguments.map(KotlinTypeArgument::toStubIrType)
|
||||
PredefinedTypesHandler.tryExpandPredefinedTypealias(classifier, nullable, typeArguments)?.let { return it }
|
||||
return if (underlyingType == null) {
|
||||
ClassifierStubType(classifier, typeArguments, nullable)
|
||||
} else {
|
||||
AbbreviatedType(underlyingType.toStubIrType(), classifier, typeArguments, nullable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinTypeArgument.toStubIrType(): TypeArgument = when (this) {
|
||||
@@ -114,53 +133,60 @@ private object PredefinedTypesHandler {
|
||||
return ClassifierStubType(classifier, listOf(TypeArgumentStub(primitiveType.toStubIrType())), nullable = nullable)
|
||||
}
|
||||
|
||||
private fun expandCOpaquePointerVar(nullable: Boolean): ClassifierStubType {
|
||||
private fun expandCOpaquePointerVar(nullable: Boolean): AbbreviatedType {
|
||||
val typeArgument = TypeArgumentStub(expandCOpaquePointer(nullable=false))
|
||||
val underlyingType = ClassifierStubType(
|
||||
KotlinTypes.cPointerVarOf, listOf(typeArgument), nullable = nullable
|
||||
)
|
||||
return ClassifierStubType(
|
||||
KotlinTypes.cOpaquePointerVar.classifier, underlyingType = underlyingType, nullable = nullable
|
||||
)
|
||||
return AbbreviatedType(underlyingType, KotlinTypes.cOpaquePointerVar.classifier, emptyList(), nullable)
|
||||
}
|
||||
|
||||
private fun expandCOpaquePointer(nullable: Boolean): ClassifierStubType {
|
||||
private fun expandCOpaquePointer(nullable: Boolean): AbbreviatedType {
|
||||
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
|
||||
return AbbreviatedType(underlyingType, KotlinTypes.cOpaquePointer.classifier, emptyList(), nullable)
|
||||
}
|
||||
|
||||
private fun expandCPointerVar(typeArguments: List<TypeArgument>, nullable: Boolean): AbbreviatedType {
|
||||
require(typeArguments.size == 1) { "CPointerVar has only one type argument." }
|
||||
val cPointer = ClassifierStubType(KotlinTypes.cPointer, typeArguments)
|
||||
val cPointerVarOfTypeArgument = TypeArgumentStub(cPointer)
|
||||
val underlyingType = ClassifierStubType(
|
||||
KotlinTypes.cPointerVarOf, listOf(cPointerVarOfTypeArgument), nullable = nullable
|
||||
)
|
||||
return AbbreviatedType(underlyingType, KotlinTypes.cPointerVar, typeArguments, 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 {
|
||||
private fun expandPrimitiveVarType(primitiveVarClassifier: Classifier, nullable: Boolean): AbbreviatedType {
|
||||
val primitiveType = primitiveVarClassifierToPrimitiveType.getValue(primitiveVarClassifier)
|
||||
val underlyingType = getVarOfTypeFor(primitiveType, nullable)
|
||||
return ClassifierStubType(primitiveVarClassifier, underlyingType = underlyingType, nullable = nullable)
|
||||
return AbbreviatedType(underlyingType, primitiveVarClassifier, listOf(), nullable)
|
||||
}
|
||||
|
||||
private fun expandNativePtr(platform: KotlinPlatform, nullable: Boolean): ClassifierStubType {
|
||||
private fun expandNativePtr(platform: KotlinPlatform, nullable: Boolean): StubType {
|
||||
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 AbbreviatedType(underlyingType, nativePtrClassifier, listOf(), nullable)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [ClassifierStubType] if [classifier] is a typealias from [kotlinx.cinterop] package.
|
||||
*/
|
||||
fun tryExpandPredefinedTypealias(classifier: Classifier, nullable: Boolean): ClassifierStubType? =
|
||||
fun tryExpandPredefinedTypealias(classifier: Classifier, nullable: Boolean, typeArguments: List<TypeArgument>): AbbreviatedType? =
|
||||
when (classifier) {
|
||||
in primitiveVarClassifierToPrimitiveType.keys -> expandPrimitiveVarType(classifier, nullable)
|
||||
KotlinTypes.cOpaquePointer.classifier -> expandCOpaquePointer(nullable)
|
||||
KotlinTypes.cOpaquePointerVar.classifier -> expandCOpaquePointerVar(nullable)
|
||||
KotlinTypes.cPointerVar -> expandCPointerVar(typeArguments, nullable)
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -169,9 +195,9 @@ private object PredefinedTypesHandler {
|
||||
*/
|
||||
fun tryExpandPlatformDependentTypealias(
|
||||
classifier: Classifier, platform: KotlinPlatform, nullable: Boolean
|
||||
): ClassifierStubType? =
|
||||
): StubType? =
|
||||
when (classifier) {
|
||||
nativePtrClassifier -> expandNativePtr(platform, nullable)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 kotlinx.metadata.klib.UniqId
|
||||
import org.jetbrains.kotlin.backend.common.serialization.cityHash64
|
||||
|
||||
internal class StubIrUniqIdProvider {
|
||||
private val mangler = KotlinLikeInteropMangler()
|
||||
|
||||
fun uniqIdForFunction(function: FunctionStub): UniqId = with(mangler) {
|
||||
when (function.origin) {
|
||||
is StubOrigin.Function -> function.origin.function.uniqueSymbolName
|
||||
is StubOrigin.ObjCMethod -> {
|
||||
require(this.context is ManglingContext.Entity) {
|
||||
"Unexpected mangling context $context for method ${function.name}."
|
||||
}
|
||||
function.origin.method.uniqueSymbolName
|
||||
}
|
||||
// TODO: What to do with "create" method in Objective-C categories?
|
||||
else -> error("Unexpected origin ${function.origin} for function ${function.name}.")
|
||||
}.toUniqId()
|
||||
}
|
||||
|
||||
fun uniqIdForProperty(property: PropertyStub): UniqId = with (mangler) {
|
||||
when (property.origin) {
|
||||
is StubOrigin.ObjCProperty -> {
|
||||
require(this.context is ManglingContext.Entity) {
|
||||
"Unexpected mangling context $context for property ${property.name}."
|
||||
}
|
||||
property.origin.property.uniqueSymbolName
|
||||
}
|
||||
is StubOrigin.Constant -> property.origin.constantDef.uniqueSymbolName
|
||||
is StubOrigin.Global -> property.origin.global.uniqueSymbolName
|
||||
// TODO: What to do with origin for enum entries and struct fields?
|
||||
else -> error("Unexpected origin ${property.origin} for property ${property.name}.")
|
||||
}.toUniqId()
|
||||
}
|
||||
|
||||
private fun InteropMangler.uniqSymbolNameForTypeAlias(origin: StubOrigin): String? = when (origin) {
|
||||
is StubOrigin.TypeDef -> origin.typedefDef.uniqueSymbolName
|
||||
is StubOrigin.Enum -> origin.enum.uniqueSymbolName
|
||||
is StubOrigin.VarOf -> uniqSymbolNameForTypeAlias(origin.typeOrigin)?.let { "$it#Var" }
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun uniqIdForTypeAlias(typeAlias: TypealiasStub): UniqId = with (mangler) {
|
||||
uniqSymbolNameForTypeAlias(typeAlias.origin)
|
||||
?: error("Unexpected origin ${typeAlias.origin} for typealias ${typeAlias.alias.fqName}.")
|
||||
}.toUniqId()
|
||||
|
||||
private fun String.toUniqId() = UniqId(cityHash64())
|
||||
}
|
||||
Reference in New Issue
Block a user