diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIr.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIr.kt index d013e474d2f..cda499df01f 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIr.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIr.kt @@ -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 accept(visitor: StubIrVisitor, 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") } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt index 726dd175b09..e04bb834548 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt @@ -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() 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) } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrMetadataEmitter.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrMetadataEmitter.kt new file mode 100644 index 00000000000..77485057256 --- /dev/null +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrMetadataEmitter.kt @@ -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() + val fragments = emitModuleFragments() + return KlibModuleMetadata(moduleName, fragments, annotations) + } + + private fun emitModuleFragments(): List = + 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 { + + private val uniqIds = StubIrUniqIdProvider() + + private val classes = mutableListOf() + private val properties = mutableListOf() + private val typeAliases = mutableListOf() + private val functions = mutableListOf() + + 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() \ No newline at end of file diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrTextEmitter.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrTextEmitter.kt index 2d9c408f4fe..723225ae747 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrTextEmitter.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrTextEmitter.kt @@ -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" + } } } diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrType.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrType.kt index e8f9241d91a..23c38706d01 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrType.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrType.kt @@ -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 } /** * Wrapper over [Classifier]. - * @property underlyingType is not null if this type is an typealias. */ class ClassifierStubType( val classifier: Classifier, - val typeArguments: List = emptyList(), - val underlyingType: StubType? = null, + override val typeArguments: List = 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, + 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, + val parameterTypes: List, // 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 by lazy { + listOf(*parameterTypes.toTypedArray(), returnType).map { TypeArgumentStub(it) } + } } class TypeParameterType( val name: String, override val nullable: Boolean -) : StubType() +) : StubType() { + override val typeArguments: List = 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, 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): 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 } -} +} \ No newline at end of file diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrUniqIdProvider.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrUniqIdProvider.kt new file mode 100644 index 00000000000..0e7131e2af8 --- /dev/null +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrUniqIdProvider.kt @@ -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()) +} \ No newline at end of file