diff --git a/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java index 265f2e1b54c..d9e58df7cae 100644 --- a/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java +++ b/core/metadata/src/org/jetbrains/kotlin/metadata/deserialization/Flags.java @@ -238,8 +238,8 @@ public class Flags { return new BooleanFlagField(offset); } - protected final int offset; - protected final int bitWidth; + public final int offset; + public final int bitWidth; private FlagField(int offset, int bitWidth) { this.offset = offset; diff --git a/libraries/kotlinx-metadata/build.gradle.kts b/libraries/kotlinx-metadata/build.gradle.kts new file mode 100644 index 00000000000..b02daac8671 --- /dev/null +++ b/libraries/kotlinx-metadata/build.gradle.kts @@ -0,0 +1,16 @@ +description = "Kotlin metadata manipulation library" + +plugins { + kotlin("jvm") +} + +sourceSets { + "main" { projectDefault() } + "test" { } +} + +dependencies { + compile(project(":kotlin-stdlib")) + compileOnly(project(":core:metadata")) + compileOnly(protobufLite()) +} diff --git a/libraries/kotlinx-metadata/jvm/build.gradle.kts b/libraries/kotlinx-metadata/jvm/build.gradle.kts new file mode 100644 index 00000000000..bf808b31c5b --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/build.gradle.kts @@ -0,0 +1,63 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +description = "Kotlin JVM metadata manipulation library" + +plugins { + kotlin("jvm") +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +val shadows by configurations.creating { + isTransitive = false +} +configurations.getByName("compileOnly").extendsFrom(shadows) +configurations.getByName("testCompile").extendsFrom(shadows) + +dependencies { + compile(project(":kotlin-stdlib")) + shadows(project(":kotlinx-metadata")) + shadows(project(":core:metadata")) + shadows(project(":core:metadata.jvm")) + shadows(protobufLite()) + testCompile(commonDep("junit:junit")) +} + +noDefaultJar() + +val shadowJar = task("shadowJar") { + callGroovy("manifestAttributes", manifest, project) + manifest.attributes["Implementation-Version"] = version + + from(the().sourceSets["main"].output) + exclude("**/*.proto") + configurations = listOf(shadows) + + val artifactRef = outputs.files.singleFile + runtimeJarArtifactBy(this, artifactRef) + addArtifact("runtime", this, artifactRef) +} + +sourcesJar { + for (dependency in shadows.dependencies) { + if (dependency is ProjectDependency) { + val javaPlugin = dependency.dependencyProject.convention.findPlugin(JavaPluginConvention::class.java) + if (javaPlugin != null) { + from(javaPlugin.sourceSets["main"].allSource) + } + } + } +} + +javadocJar() + +// Change this version before publishing +version = "0.1-SNAPSHOT" +// publish() + +projectTest { + workingDir = rootDir +} diff --git a/libraries/kotlinx-metadata/jvm/src/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions b/libraries/kotlinx-metadata/jvm/src/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions new file mode 100644 index 00000000000..7ac2235c4e0 --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions @@ -0,0 +1 @@ +kotlinx.metadata.jvm.impl.JvmMetadataExtensions diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt new file mode 100644 index 00000000000..f7d2feda357 --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm + +/** + * A mirror to the [Metadata] annotation on a JVM class file, containing the metadata of Kotlin declarations declared in the class file. + * Properties of this class correspond to the properties of [Metadata], but the names are not shortened because there's no restriction + * on the bytecode size here. + * + * Note that [Metadata] is not yet public (see https://youtrack.jetbrains.com/issue/KT-23602). In order to create an instance of + * [KotlinClassHeader] from the [Metadata] annotation instance obtained reflectively, one could use Java code to workaround + * the internal visibility error. + * + * @param kind see [kind] + * @param metadataVersion see [metadataVersion] + * @param bytecodeVersion see [bytecodeVersion] + * @param data1 see [data1] + * @param data2 see [data2] + * @param extraString see [extraString] + * @param packageName see [packageName] + * @param extraInt see [extraInt] + */ +class KotlinClassHeader( + kind: Int?, + metadataVersion: IntArray?, + bytecodeVersion: IntArray?, + data1: Array?, + data2: Array?, + extraString: String?, + packageName: String?, + extraInt: Int? +) { + /** + * A kind of the metadata this header encodes. + * + * @see Metadata.k + * @see CLASS_KIND + * @see FILE_FACADE_KIND + * @see SYNTHETIC_CLASS_KIND + * @see MULTI_FILE_CLASS_FACADE_KIND + * @see MULTI_FILE_CLASS_PART_KIND + */ + val kind: Int = kind ?: 1 + + /** + * The version of the metadata provided in other properties of this header. + * + * @see Metadata.mv + * @see COMPATIBLE_METADATA_VERSION + */ + val metadataVersion: IntArray = metadataVersion ?: intArrayOf() + + /** + * The version of the bytecode interface (naming conventions, signatures) of the corresponding class file. + * + * @see Metadata.bv + * @see COMPATIBLE_BYTECODE_VERSION + */ + val bytecodeVersion: IntArray = bytecodeVersion ?: intArrayOf() + + /** + * The first array of strings used to encode the metadata. + * + * @see Metadata.d1 + */ + val data1: Array = data1 ?: emptyArray() + + /** + * The second array of strings used to encode the metadata. + * + * @see Metadata.d2 + */ + val data2: Array = data2 ?: emptyArray() + + /** + * An extra string field for the metadata. + * + * @see Metadata.xs + */ + val extraString: String = extraString ?: "" + + /** + * Fully qualified name of the Kotlin package of the corresponding class, in case [JvmPackageName] was used. + * + * @see Metadata.pn + */ + val packageName: String = packageName ?: "" + + /** + * An extra int field for the metadata. + * + * @see Metadata.xi + */ + val extraInt: Int = extraInt ?: 0 + + companion object { + /** + * A class file kind signifying that the corresponding class file contains a declaration of a Kotlin class. + * + * @see kind + */ + const val CLASS_KIND = 1 + + /** + * A class file kind signifying that the corresponding class file is a compiled Kotlin file facade. + * + * @see kind + */ + const val FILE_FACADE_KIND = 2 + + /** + * A class file kind signifying that the corresponding class file is synthetic, e.g. it's a class for lambda, `$DefaultImpls` class + * for interface method implementations, `$WhenMappings` class for optimized `when` over enums, etc. + * + * @see kind + */ + const val SYNTHETIC_CLASS_KIND = 3 + + /** + * A class file kind signifying that the corresponding class file is a compiled multi-file class facade. + * + * @see kind + * @see JvmMultifileClass + */ + const val MULTI_FILE_CLASS_FACADE_KIND = 4 + + /** + * A class file kind signifying that the corresponding class file is a compiled multi-file class part, i.e. an internal class + * with method bodies and their metadata, accessed only from the corresponding facade. + * + * @see kind + * @see JvmMultifileClass + */ + const val MULTI_FILE_CLASS_PART_KIND = 5 + + /** + * The latest metadata version supported by this version of the library. + * + * @see metadataVersion + */ + @JvmField + val COMPATIBLE_METADATA_VERSION = intArrayOf(1, 1, 10) + + /** + * The latest bytecode version supported by this version of the library. + * + * @see bytecodeVersion + */ + @JvmField + val COMPATIBLE_BYTECODE_VERSION = intArrayOf(1, 0, 2) + } +} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt new file mode 100644 index 00000000000..5c2f8a5e24a --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm + +import kotlinx.metadata.InconsistentKotlinMetadataException +import kotlinx.metadata.KmClassVisitor +import kotlinx.metadata.KmLambdaVisitor +import kotlinx.metadata.KmPackageVisitor +import kotlinx.metadata.impl.accept +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import kotlin.LazyThreadSafetyMode.PUBLICATION + +/** + * Represents the parsed metadata of a Kotlin JVM class file. + * + * To create an instance of [KotlinClassMetadata], first obtain a [KotlinClassHeader] instance by loading the contents + * of the [Metadata] annotation on a class file, and then call [KotlinClassMetadata.read]. + */ +sealed class KotlinClassMetadata(val header: KotlinClassHeader) { + /** + * Represents metadata of a class file containing a declaration of a Kotlin class. + */ + class Class internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + private val classData by lazy(PUBLICATION) { + val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) + ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) + JvmProtoBufUtil.readClassDataFrom(data1, header.data2) + } + + /** + * Makes the given visitor visit metadata of this class. + * + * @param v the visitor that must visit this class + */ + fun accept(v: KmClassVisitor) { + val (strings, proto) = classData + proto.accept(v, strings) + } + } + + /** + * Represents metadata of a class file containing a compiled Kotlin file facade. + */ + class FileFacade internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + private val packageData by lazy(PUBLICATION) { + val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) + ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) + JvmProtoBufUtil.readPackageDataFrom(data1, header.data2) + } + + /** + * Makes the given visitor visit metadata of this file facade. + * + * @param v the visitor that must visit this file facade + */ + fun accept(v: KmPackageVisitor) { + val (strings, proto) = packageData + proto.accept(v, strings) + } + } + + /** + * Represents metadata of a class file containing a synthetic class, e.g. a class for lambda, `$DefaultImpls` class for interface + * method implementations, `$WhenMappings` class for optimized `when` over enums, etc. + */ + class SyntheticClass internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + private val functionData by lazy(PUBLICATION) { + header.data1.takeIf(Array<*>::isNotEmpty)?.let { data1 -> + JvmProtoBufUtil.readFunctionDataFrom(data1, header.data2) + } + } + + /** + * Returns `true` if this synthetic class is a class file compiled for a Kotlin lambda. + */ + val isLambda: Boolean + get() = header.data1.isNotEmpty() + + /** + * Makes the given visitor visit metadata of this file facade, if this synthetic class represents a Kotlin lambda + * (`isLambda` == true). + * + * Throws [IllegalStateException] if this synthetic class does not represent a Kotlin lambda. + * + * @param v the visitor that must visit this lambda + */ + fun accept(v: KmLambdaVisitor) { + if (!isLambda) throw IllegalStateException( + "accept(KmLambdaVisitor) is only possible for synthetic classes which are lambdas (isLambda = true)" + ) + + val (strings, proto) = functionData!! + proto.accept(v, strings) + } + } + + /** + * Represents metadata of a class file containing a compiled multi-file class facade. + * + * @see JvmMultifileClass + */ + class MultiFileClassFacade internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + /** + * JVM internal names of the part classes which this multi-file class combines. + */ + val partClassNames: List = header.data1.asList() + } + + /** + * Represents metadata of a class file containing a compiled multi-file class part, i.e. an internal class with method bodies + * and their metadata, accessed only from the corresponding facade. + * + * @see JvmMultifileClass + */ + class MultiFileClassPart internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + private val packageData by lazy(PUBLICATION) { + val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) + ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) + JvmProtoBufUtil.readPackageDataFrom(data1, header.data2) + } + + /** + * JVM internal name of the corresponding multi-file class facade. + */ + val facadeClassName: String + get() = header.extraString + + /** + * Makes the given visitor visit metadata of this multi-file class part. + * + * @param v the visitor that must visit this multi-file class part + */ + fun accept(v: KmPackageVisitor) { + val (strings, proto) = packageData + proto.accept(v, strings) + } + } + + /** + * Represents metadata of an unknown class file. This class is used if an old version of this library is used against a new kind + * of class files generated by the Kotlin compiler, unsupported by this library. + */ + class Unknown internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) + + companion object { + /** + * Reads and parses the given header of a Kotlin JVM class file and returns the correct type of [KotlinClassMetadata] encoded by + * this header, or `null` if this header encodes an unsupported kind of Kotlin classes or has an unsupported metadata version. + * + * Throws [InconsistentKotlinMetadataException] if the metadata has inconsistencies which signal that it may have been + * modified by a separate tool. + * + * @param header the header of a Kotlin JVM class file to be parsed + */ + @JvmStatic + fun read(header: KotlinClassHeader): KotlinClassMetadata? { + // We only support metadata of version 1.1.* (this is Kotlin from 1.0 until today) + val version = header.metadataVersion + if (version.getOrNull(0) != 1 || version.getOrNull(1) != 1) return null + + return try { + when (header.kind) { + KotlinClassHeader.CLASS_KIND -> KotlinClassMetadata.Class(header) + KotlinClassHeader.FILE_FACADE_KIND -> KotlinClassMetadata.FileFacade(header) + KotlinClassHeader.SYNTHETIC_CLASS_KIND -> KotlinClassMetadata.SyntheticClass(header) + KotlinClassHeader.MULTI_FILE_CLASS_FACADE_KIND -> KotlinClassMetadata.MultiFileClassFacade(header) + KotlinClassHeader.MULTI_FILE_CLASS_PART_KIND -> KotlinClassMetadata.MultiFileClassPart(header) + else -> KotlinClassMetadata.Unknown(header) + } + } catch (e: InconsistentKotlinMetadataException) { + throw e + } catch (e: Throwable) { + throw InconsistentKotlinMetadataException("Exception occurred when reading Kotlin metadata", e) + } + } + } +} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt new file mode 100644 index 00000000000..7752f81ffd2 --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm.impl + +import kotlinx.metadata.* +import kotlinx.metadata.impl.extensions.MetadataExtensions +import kotlinx.metadata.impl.readAnnotation +import kotlinx.metadata.jvm.* +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver +import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull +import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil + +internal class JvmMetadataExtensions : MetadataExtensions { + override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, strings: NameResolver, types: TypeTable) { + val ext = v.visitExtensions(JvmFunctionExtensionVisitor.TYPE) as? JvmFunctionExtensionVisitor ?: return + ext.visit(JvmProtoBufUtil.getJvmMethodSignature(proto, strings, types)) + } + + override fun readPropertyExtensions(v: KmPropertyVisitor, proto: ProtoBuf.Property, strings: NameResolver, types: TypeTable) { + val ext = v.visitExtensions(JvmPropertyExtensionVisitor.TYPE) as? JvmPropertyExtensionVisitor ?: return + val fieldSignature = JvmProtoBufUtil.getJvmFieldSignature(proto, strings, types) + val propertySignature = proto.getExtensionOrNull(JvmProtoBuf.propertySignature) + val getterSignature = + if (propertySignature != null && propertySignature.hasGetter()) propertySignature.getter else null + val setterSignature = + if (propertySignature != null && propertySignature.hasSetter()) propertySignature.setter else null + ext.visit( + fieldSignature?.name, + fieldSignature?.desc, + getterSignature?.run { strings.getString(name) + strings.getString(desc) }, + setterSignature?.run { strings.getString(name) + strings.getString(desc) } + ) + + val syntheticMethod = + if (propertySignature != null && propertySignature.hasSyntheticMethod()) propertySignature.syntheticMethod else null + ext.visitSyntheticMethodForAnnotations(syntheticMethod?.run { strings.getString(name) + strings.getString(desc) }) + + ext.visitEnd() + } + + override fun readConstructorExtensions(v: KmConstructorVisitor, proto: ProtoBuf.Constructor, strings: NameResolver, types: TypeTable) { + val ext = v.visitExtensions(JvmConstructorExtensionVisitor.TYPE) as? JvmConstructorExtensionVisitor ?: return + ext.visit(JvmProtoBufUtil.getJvmConstructorSignature(proto, strings, types)) + } + + override fun readTypeParameterExtensions(v: KmTypeParameterVisitor, proto: ProtoBuf.TypeParameter, strings: NameResolver) { + val ext = v.visitExtensions(JvmTypeParameterExtensionVisitor.TYPE) as? JvmTypeParameterExtensionVisitor ?: return + for (annotation in proto.getExtension(JvmProtoBuf.typeParameterAnnotation)) { + ext.visitAnnotation(annotation.readAnnotation(strings)) + } + ext.visitEnd() + } + + override fun readTypeExtensions(v: KmTypeVisitor, proto: ProtoBuf.Type, strings: NameResolver) { + val ext = v.visitExtensions(JvmTypeExtensionVisitor.TYPE) as? JvmTypeExtensionVisitor ?: return + ext.visit(proto.getExtension(JvmProtoBuf.isRaw)) + for (annotation in proto.getExtension(JvmProtoBuf.typeAnnotation)) { + ext.visitAnnotation(annotation.readAnnotation(strings)) + } + ext.visitEnd() + } +} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmExtensions.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmExtensions.kt new file mode 100644 index 00000000000..ddada1eff4f --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmExtensions.kt @@ -0,0 +1,193 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm + +import kotlinx.metadata.* +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil + +/** + * A visitor to visit JVM extensions for a function. + */ +open class JvmFunctionExtensionVisitor @JvmOverloads constructor( + private val delegate: JvmFunctionExtensionVisitor? = null +) : KmFunctionExtensionVisitor { + /** + * Visits the JVM signature of the function, in the JVM-based format: `"equals(Ljava/lang/Object;)Z"`, + * or null if the JVM signature of this function is unknown. + * + * @param desc the signature of the function + */ + open fun visit(desc: String?) { + delegate?.visit(desc) + } + + companion object { + /** + * The type of this extension visitor. + * + * @see KmExtensionType + */ + @JvmField + val TYPE: KmExtensionType = KmExtensionType(JvmFunctionExtensionVisitor::class) + } +} + +/** + * A visitor to visit JVM extensions for a property. + */ +open class JvmPropertyExtensionVisitor @JvmOverloads constructor( + private val delegate: JvmPropertyExtensionVisitor? = null +) : KmPropertyExtensionVisitor { + /** + * Visits JVM signatures of field and accessors generated for the property. + * + * @param fieldName the name of the field, or `null` if this property has no field + * @param fieldTypeDesc the type of the field in the JVM-based format (`"Ljava/lang/Object;"`), or `null` if this property has no field + * @param getterDesc the signature of the property getter in the JVM-based format: `"getX()Ljava/lang/Object;"`, + * or `null` if this property has no getter or its signature is unknown + * @param setterDesc the signature of the property setter in the JVM-based format: `"setX(Ljava/lang/Object;)V"`, + * or `null` if this property has no setter or its signature is unknown + */ + open fun visit(fieldName: String?, fieldTypeDesc: String?, getterDesc: String?, setterDesc: String?) { + delegate?.visit(fieldName, fieldTypeDesc, getterDesc, setterDesc) + } + + /** + * Visits the JVM signature of a synthetic method which is generated to store annotations on a property in the bytecode, + * in the JVM-based format: `"getX$annotations()V"` + * + * @param desc the signature of the synthetic method + */ + open fun visitSyntheticMethodForAnnotations(desc: String?) { + delegate?.visitSyntheticMethodForAnnotations(desc) + } + + /** + * Visits the end of JVM extensions for the property. + */ + open fun visitEnd() { + delegate?.visitEnd() + } + + companion object { + /** + * The type of this extension visitor. + * + * @see KmExtensionType + */ + @JvmField + val TYPE: KmExtensionType = KmExtensionType(JvmPropertyExtensionVisitor::class) + } +} + +/** + * A visitor to visit JVM extensions for a constructor. + */ +open class JvmConstructorExtensionVisitor @JvmOverloads constructor( + private val delegate: JvmConstructorExtensionVisitor? = null +) : KmConstructorExtensionVisitor { + /** + * Visits the JVM signature of the constructor, in the JVM-based format: `"(Ljava/lang/Object;)V"`, + * or null if the JVM signature of this constructor is unknown. + * + * @param desc the signature of the constructor + */ + open fun visit(desc: String?) { + delegate?.visit(desc) + } + + companion object { + /** + * The type of this extension visitor. + * + * @see KmExtensionType + */ + @JvmField + val TYPE: KmExtensionType = KmExtensionType(JvmConstructorExtensionVisitor::class) + } +} + +/** + * A visitor to visit JVM extensions for a type parameter. + */ +open class JvmTypeParameterExtensionVisitor @JvmOverloads constructor( + private val delegate: JvmTypeParameterExtensionVisitor? = null +) : KmTypeParameterExtensionVisitor { + /** + * Visits an annotation on the type parameter. + * + * @param annotation the annotation on the type parameter + */ + open fun visitAnnotation(annotation: KmAnnotation) { + delegate?.visitAnnotation(annotation) + } + + /** + * Visits the end of JVM extensions for the type parameter. + */ + open fun visitEnd() { + delegate?.visitEnd() + } + + companion object { + /** + * The type of this extension visitor. + * + * @see KmExtensionType + */ + @JvmField + val TYPE: KmExtensionType = KmExtensionType(JvmTypeParameterExtensionVisitor::class) + } +} + +/** + * A visitor to visit JVM extensions for a type. + */ +open class JvmTypeExtensionVisitor @JvmOverloads constructor( + private val delegate: JvmTypeExtensionVisitor? = null +) : KmTypeExtensionVisitor { + /** + * Visits the JVM-specific flags of a type. + * + * @param isRaw whether the type is seen as a raw type in Java + */ + open fun visit(isRaw: Boolean) { + delegate?.visit(isRaw) + } + + /** + * Visits an annotation on the type. + * + * @param annotation the annotation on the type + */ + open fun visitAnnotation(annotation: KmAnnotation) { + delegate?.visitAnnotation(annotation) + } + + /** + * Visits the end of JVM extensions for the type parameter. + */ + open fun visitEnd() { + delegate?.visitEnd() + } + + companion object { + /** + * The type of this extension visitor. + * + * @see KmExtensionType + */ + @JvmField + val TYPE: KmExtensionType = KmExtensionType(JvmTypeExtensionVisitor::class) + + /** + * The type flexibility id, signifying that the visited type is a JVM platform type. + * + * @see KmTypeVisitor.visitFlexibleTypeUpperBound + */ + const val PLATFORM_TYPE_ID = JvmProtoBufUtil.PLATFORM_TYPE_ID + } +} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt new file mode 100644 index 00000000000..415de408f51 --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm + +import kotlinx.metadata.ClassName +import kotlinx.metadata.isLocal + +/** + * JVM internal name of the class, where package names are separated by '/', and class names are separated by '$', + * for example: `"org/foo/bar/Baz$Nested"` + */ +val ClassName.jvmInternalName: String + get() = + if (this.isLocal) substring(1) + else replace('/', '$') diff --git a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt new file mode 100644 index 00000000000..adbb3e26bc8 --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.test + +import kotlinx.metadata.* +import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor +import kotlinx.metadata.jvm.KotlinClassHeader +import kotlinx.metadata.jvm.KotlinClassMetadata +import org.junit.Assert.assertEquals +import org.junit.Test + +class MetadataSmokeTest { + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + private fun Class<*>.readMetadata(): KotlinClassHeader { + return getAnnotation(Metadata::class.java).run { + KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi) + } + } + + @Test + fun listInlineFunctions() { + @Suppress("unused") + class L { + val x: Int inline get() = 42 + inline fun foo(f: () -> String) = f() + fun bar() {} + } + + val inlineFunctions = mutableListOf() + + val klass = KotlinClassMetadata.read(L::class.java.readMetadata()) as KotlinClassMetadata.Class + klass.accept(object : KmClassVisitor() { + override fun visitFunction(flags: Int, name: String): KmFunctionVisitor? { + return object : KmFunctionVisitor() { + override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? { + if (type != JvmFunctionExtensionVisitor.TYPE) return null + + return object : JvmFunctionExtensionVisitor() { + override fun visit(desc: String?) { + if (Flags.Function.IS_INLINE(flags) && desc != null) { + inlineFunctions += desc + } + } + } + } + } + } + }) + + assertEquals( + listOf("foo(Lkotlin/jvm/functions/Function0;)Ljava/lang/String;"), + inlineFunctions + ) + } +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/ClassName.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/ClassName.kt new file mode 100644 index 00000000000..a38532df960 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/ClassName.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +/** + * A fully qualified name of a classifier from the Kotlin's point of view. May differ from the JVM name of the class + * which is the runtime representation of this Kotlin classifier (for example, Kotlin class "kotlin/Int" -> JVM class "java/lang/Integer") + * + * Package names in this name are separated by '/', and class names are separated by '.', for example: `"org/foo/bar/Baz.Nested"`. + * + * If this name starts with '.', it represents a local class or an anonymous object. This is used by the Kotlin compiler + * to prevent lookup of this name in the resolution. + */ +// TODO: use inline class in 1.3 +typealias ClassName = String + +val ClassName.isLocal: Boolean + get() = this.startsWith(".") diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/Flags.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/Flags.kt new file mode 100644 index 00000000000..e529dcc47e9 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/Flags.kt @@ -0,0 +1,481 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +import org.jetbrains.kotlin.metadata.ProtoBuf.* +import org.jetbrains.kotlin.metadata.deserialization.Flags as F +import org.jetbrains.kotlin.metadata.ProtoBuf.Class.Kind as ClassKind + +/** + * A container of all flags applicable to all Kotlin declarations. A "flag" is a boolean trait that is either present or not + * in a declaration. To check whether the flag is present in the bitmask, call [MetadataFlag.invoke] on the flag, passing the bitmask + * as the argument: + * + * override fun visitFunction(flags: Int, name: String): KmFunctionVisitor? { + * if (Flags.Function.IS_INLINE(flags)) { + * ... + * } + * } + * + * To construct a bitmask out of several flags, call [Flags.invoke] on the needed flags: + * + * v.visitFunction(Flags(Flags.Function.IS_DECLARATION, Flags.Function.IS_INLINE), "foo") + * + * Flags common to multiple kinds of Kotlin declarations ("common flags") are declared directly in the [Flags] object. + * Flags applicable to specific kinds of declarations ("declaration-specific flags") are declared in nested objects of the [Flags] object. + * + * Some flags are mutually exclusive, i.e. there are "flag groups" such that no more than one flag from each group can be present + * in the same bitmask. Among common flags, there are the following flag groups: + * * visibility flags: [IS_INTERNAL], [IS_PRIVATE], [IS_PROTECTED], [IS_PUBLIC], [IS_PRIVATE_TO_THIS], [IS_LOCAL] + * * modality flags: [IS_FINAL], [IS_OPEN], [IS_ABSTRACT], [IS_SEALED] + * + * Some declaration-specific flags form other flag groups, see the documentation of the corresponding containers for more information. + */ +object Flags { + /** + * Combines several flags into an integer bitmask. Note that in case several mutually exclusive flags are passed (for example, + * several visibility flags), the resulting bitmask will hold the value of the latest flag. + * + * For example, `Flags(Flags.IS_PRIVATE, Flags.IS_PUBLIC, Flags.IS_INTERNAL)` is the same as `Flags(Flags.IS_INTERNAL)` + */ + operator fun invoke(vararg flags: MetadataFlag): Int = + flags.fold(0) { acc, flag -> flag + acc } + + /** + * Signifies that the corresponding declaration has at least one annotation. + * + * This flag is useful for reading Kotlin metadata on JVM efficiently. On JVM, most of the annotations are written not to the Kotlin + * metadata, but directly on the corresponding declarations in the class file. This flag can be used as an optimization to avoid + * reading annotations from the class file (which can be slow) in case when a declaration has no annotations. + */ + @JvmField + val HAS_ANNOTATIONS = MetadataFlag(F.HAS_ANNOTATIONS) + + + /** + * A visibility flag, signifying that the corresponding declaration is `internal`. + */ + @JvmField + val IS_INTERNAL = MetadataFlag(F.VISIBILITY, Visibility.INTERNAL_VALUE) + + /** + * A visibility flag, signifying that the corresponding declaration is `private`. + */ + @JvmField + val IS_PRIVATE = MetadataFlag(F.VISIBILITY, Visibility.PRIVATE_VALUE) + + /** + * A visibility flag, signifying that the corresponding declaration is `protected`. + */ + @JvmField + val IS_PROTECTED = MetadataFlag(F.VISIBILITY, Visibility.PROTECTED_VALUE) + + /** + * A visibility flag, signifying that the corresponding declaration is `public`. + */ + @JvmField + val IS_PUBLIC = MetadataFlag(F.VISIBILITY, Visibility.PUBLIC_VALUE) + + /** + * A visibility flag, signifying that the corresponding declaration is "private-to-this", which is a non-denotable visibility of + * private members in Kotlin which are callable only on the same instance of the declaring class. + */ + @JvmField + val IS_PRIVATE_TO_THIS = MetadataFlag(F.VISIBILITY, Visibility.PRIVATE_TO_THIS_VALUE) + + /** + * A visibility flag, signifying that the corresponding declaration is local, i.e. declared inside a code block + * and not visible from the outside. + */ + @JvmField + val IS_LOCAL = MetadataFlag(F.VISIBILITY, Visibility.LOCAL_VALUE) + + + /** + * A modality flag, signifying that the corresponding declaration is `final`. + */ + @JvmField + val IS_FINAL = MetadataFlag(F.MODALITY, Modality.FINAL_VALUE) + + /** + * A modality flag, signifying that the corresponding declaration is `open`. + */ + @JvmField + val IS_OPEN = MetadataFlag(F.MODALITY, Modality.OPEN_VALUE) + + /** + * A modality flag, signifying that the corresponding declaration is `abstract`. + */ + @JvmField + val IS_ABSTRACT = MetadataFlag(F.MODALITY, Modality.ABSTRACT_VALUE) + + /** + * A modality flag, signifying that the corresponding declaration is `sealed`. + */ + @JvmField + val IS_SEALED = MetadataFlag(F.MODALITY, Modality.SEALED_VALUE) + + + /** + * A container of flags applicable to Kotlin classes, including interfaces, objects, enum classes and annotation classes. + * + * In addition to the common flag groups, the following flag groups exist for class flags: + * * class kind flags: [IS_CLASS], [IS_INTERFACE], [IS_ENUM_CLASS], [IS_ENUM_ENTRY], [IS_ANNOTATION_CLASS], [IS_OBJECT], + * [IS_COMPANION_OBJECT] + */ + object Class { + /** + * A class kind flag, signifying that the corresponding class is a usual `class`. + */ + @JvmField + val IS_CLASS = MetadataFlag(F.CLASS_KIND, ClassKind.CLASS_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is an `interface`. + */ + @JvmField + val IS_INTERFACE = MetadataFlag(F.CLASS_KIND, ClassKind.INTERFACE_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is an `enum class`. + */ + @JvmField + val IS_ENUM_CLASS = MetadataFlag(F.CLASS_KIND, ClassKind.ENUM_CLASS_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is an enum entry. + */ + @JvmField + val IS_ENUM_ENTRY = MetadataFlag(F.CLASS_KIND, ClassKind.ENUM_ENTRY_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is an `annotation class`. + */ + @JvmField + val IS_ANNOTATION_CLASS = MetadataFlag(F.CLASS_KIND, ClassKind.ANNOTATION_CLASS_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is a non-companion `object`. + */ + @JvmField + val IS_OBJECT = MetadataFlag(F.CLASS_KIND, ClassKind.OBJECT_VALUE) + + /** + * A class kind flag, signifying that the corresponding class is a `companion object`. + */ + @JvmField + val IS_COMPANION_OBJECT = MetadataFlag(F.CLASS_KIND, ClassKind.COMPANION_OBJECT_VALUE) + + + /** + * Signifies that the corresponding class is `inner`. + */ + @JvmField + val IS_INNER = MetadataFlag(F.IS_INNER) + + /** + * Signifies that the corresponding class is `data`. + */ + @JvmField + val IS_DATA = MetadataFlag(F.IS_DATA) + + /** + * Signifies that the corresponding class is `external`. + */ + @JvmField + val IS_EXTERNAL = MetadataFlag(F.IS_EXTERNAL_CLASS) + + /** + * Signifies that the corresponding class is `expect`. + */ + @JvmField + val IS_EXPECT = MetadataFlag(F.IS_EXPECT_CLASS) + + /** + * Signifies that the corresponding class is `inline`. + */ + @JvmField + val IS_INLINE = MetadataFlag(F.IS_INLINE_CLASS) + } + + /** + * A container of flags applicable to Kotlin constructors. + */ + object Constructor { + /** + * Signifies that the corresponding constructor is primary, i.e. declared in the class header, not in the class body. + */ + @JvmField + val IS_PRIMARY = MetadataFlag(F.IS_SECONDARY, 0) + } + + /** + * A container of flags applicable to Kotlin functions. + * + * In addition to the common flag groups, the following flag groups exist for function flags: + * * member kind flags: [IS_DECLARATION], [IS_FAKE_OVERRIDE], [IS_DELEGATION], [IS_SYNTHESIZED] + */ + object Function { + /** + * A member kind flag, signifying that the corresponding function is explicitly declared in the containing class. + */ + @JvmField + val IS_DECLARATION = MetadataFlag(F.MEMBER_KIND, MemberKind.DECLARATION_VALUE) + + /** + * A member kind flag, signifying that the corresponding function exists in the containing class because a function with a suitable + * signature exists in a supertype. This flag is not written by the Kotlin compiler and its effects are unspecified. + */ + @JvmField + val IS_FAKE_OVERRIDE = MetadataFlag(F.MEMBER_KIND, MemberKind.FAKE_OVERRIDE_VALUE) + + /** + * A member kind flag, signifying that the corresponding function exists in the containing class because it has been produced + * by interface delegation (delegation "by"). + */ + @JvmField + val IS_DELEGATION = MetadataFlag(F.MEMBER_KIND, MemberKind.DELEGATION_VALUE) + + /** + * A member kind flag, signifying that the corresponding function exists in the containing class because it has been synthesized + * by the compiler and has no declaration in the source code. + */ + @JvmField + val IS_SYNTHESIZED = MetadataFlag(F.MEMBER_KIND, MemberKind.SYNTHESIZED_VALUE) + + + /** + * Signifies that the corresponding function is `operator`. + */ + @JvmField + val IS_OPERATOR = MetadataFlag(F.IS_OPERATOR) + + /** + * Signifies that the corresponding function is `infix`. + */ + @JvmField + val IS_INFIX = MetadataFlag(F.IS_INFIX) + + /** + * Signifies that the corresponding function is `inline`. + */ + @JvmField + val IS_INLINE = MetadataFlag(F.IS_INLINE) + + /** + * Signifies that the corresponding function is `tailrec`. + */ + @JvmField + val IS_TAILREC = MetadataFlag(F.IS_TAILREC) + + /** + * Signifies that the corresponding function is `external`. + */ + @JvmField + val IS_EXTERNAL = MetadataFlag(F.IS_EXTERNAL_FUNCTION) + + /** + * Signifies that the corresponding function is `suspend`. + */ + @JvmField + val IS_SUSPEND = MetadataFlag(F.IS_SUSPEND) + + /** + * Signifies that the corresponding function is `expect`. + */ + @JvmField + val IS_EXPECT = MetadataFlag(F.IS_EXPECT_FUNCTION) + } + + /** + * A container of flags applicable to Kotlin properties. + * + * In addition to the common flag groups, the following flag groups exist for property flags: + * * member kind flags: [IS_DECLARATION], [IS_FAKE_OVERRIDE], [IS_DELEGATION], [IS_SYNTHESIZED] + */ + object Property { + /** + * A member kind flag, signifying that the corresponding property is explicitly declared in the containing class. + */ + @JvmField + val IS_DECLARATION = MetadataFlag(F.MEMBER_KIND, MemberKind.DECLARATION_VALUE) + + /** + * A member kind flag, signifying that the corresponding property exists in the containing class because a property with a suitable + * signature exists in a supertype. This flag is not written by the Kotlin compiler and its effects are unspecified. + */ + @JvmField + val IS_FAKE_OVERRIDE = MetadataFlag(F.MEMBER_KIND, MemberKind.FAKE_OVERRIDE_VALUE) + + /** + * A member kind flag, signifying that the corresponding property exists in the containing class because it has been produced + * by interface delegation (delegation "by"). + */ + @JvmField + val IS_DELEGATION = MetadataFlag(F.MEMBER_KIND, MemberKind.DELEGATION_VALUE) + + /** + * A member kind flag, signifying that the corresponding property exists in the containing class because it has been synthesized + * by the compiler and has no declaration in the source code. + */ + @JvmField + val IS_SYNTHESIZED = MetadataFlag(F.MEMBER_KIND, MemberKind.SYNTHESIZED_VALUE) + + + /** + * Signifies that the corresponding property is `var`. + */ + @JvmField + val IS_VAR = MetadataFlag(F.IS_VAR) + + /** + * Signifies that the corresponding property has a getter. + */ + @JvmField + val HAS_GETTER = MetadataFlag(F.HAS_GETTER) + + /** + * Signifies that the corresponding property has a setter. + */ + @JvmField + val HAS_SETTER = MetadataFlag(F.HAS_SETTER) + + /** + * Signifies that the corresponding property is `const`. + */ + @JvmField + val IS_CONST = MetadataFlag(F.IS_CONST) + + /** + * Signifies that the corresponding property is `lateinit`. + */ + @JvmField + val IS_LATEINIT = MetadataFlag(F.IS_LATEINIT) + + /** + * Signifies that the corresponding property has a constant value. On JVM, this flag allows an optimization similarly to + * [F.HAS_ANNOTATIONS]: constant values of properties are written to the bytecode directly, and this flag can be used to avoid + * reading the value from the bytecode in case there isn't one. + */ + @JvmField + val HAS_CONSTANT = MetadataFlag(F.HAS_CONSTANT) + + /** + * Signifies that the corresponding property is `external`. + */ + @JvmField + val IS_EXTERNAL = MetadataFlag(F.IS_EXTERNAL_PROPERTY) + + /** + * Signifies that the corresponding property is a delegated property. + */ + @JvmField + val IS_DELEGATED = MetadataFlag(F.IS_DELEGATED) + + /** + * Signifies that the corresponding property is `expect`. + */ + @JvmField + val IS_EXPECT = MetadataFlag(F.IS_EXPECT_PROPERTY) + } + + /** + * A container of flags applicable to Kotlin property getters and setters. + */ + object PropertyAccessor { + /** + * Signifies that the corresponding property accessor is not default, i.e. it has a body and/or annotations in the source code. + */ + @JvmField + val IS_NOT_DEFAULT = MetadataFlag(F.IS_NOT_DEFAULT) + + /** + * Signifies that the corresponding property accessor is `external`. + */ + @JvmField + val IS_EXTERNAL = MetadataFlag(F.IS_EXTERNAL_ACCESSOR) + + /** + * Signifies that the corresponding property accessor is `inline`. + */ + @JvmField + val IS_INLINE = MetadataFlag(F.IS_INLINE_ACCESSOR) + } + + /** + * A container of flags applicable to Kotlin types. + */ + object Type { + /** + * Signifies that the corresponding type is marked as nullable, i.e. has a question mark at the end of its notation. + */ + @JvmField + val IS_NULLABLE = MetadataFlag(0, 1, 1) + + /** + * Signifies that the corresponding type is `suspend`. + */ + @JvmField + val IS_SUSPEND = MetadataFlag(F.SUSPEND_TYPE.offset + 1, F.SUSPEND_TYPE.bitWidth, 1) + } + + /** + * A container of flags applicable to Kotlin type parameters. + */ + object TypeParameter { + /** + * Signifies that the corresponding type parameter is `reified`. + */ + @JvmField + val IS_REIFIED = MetadataFlag(0, 1, 1) + } + + /** + * A container of flags applicable to Kotlin value parameters. + */ + object ValueParameter { + /** + * Signifies that the corresponding value parameter declares a default value. Note that the default value itself can be a complex + * expression and is not available via metadata. Also note that in case of an override of a parameter with default value, the + * parameter in the derived method does _not_ declare the default value ([DECLARES_DEFAULT_VALUE] == false), but the parameter is + * still optional at the call site because the default value from the base method is used. + */ + @JvmField + val DECLARES_DEFAULT_VALUE = MetadataFlag(F.DECLARES_DEFAULT_VALUE) + + /** + * Signifies that the corresponding value parameter is `crossinline`. + */ + @JvmField + val IS_CROSSINLINE = MetadataFlag(F.IS_CROSSINLINE) + + /** + * Signifies that the corresponding value parameter is `noinline`. + */ + @JvmField + val IS_NOINLINE = MetadataFlag(F.IS_NOINLINE) + } + + /** + * A container of flags applicable to Kotlin effect expressions. + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + */ + object EffectExpression { + /** + * Signifies that the corresponding effect expression should be negated to compute the proposition or the conclusion of an effect. + */ + @JvmField + val IS_NEGATED = MetadataFlag(F.IS_NEGATED) + + /** + * Signifies that the corresponding effect expression checks whether a value of some variable is `null`. + */ + @JvmField + val IS_NULL_CHECK_PREDICATE = MetadataFlag(F.IS_NULL_CHECK_PREDICATE) + } +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/InconsistentKotlinMetadataException.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/InconsistentKotlinMetadataException.kt new file mode 100644 index 00000000000..5f19f222ad3 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/InconsistentKotlinMetadataException.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +class InconsistentKotlinMetadataException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/MetadataFlag.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/MetadataFlag.kt new file mode 100644 index 00000000000..782ed012988 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/MetadataFlag.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +import org.jetbrains.kotlin.metadata.deserialization.Flags.BooleanFlagField +import org.jetbrains.kotlin.metadata.deserialization.Flags.FlagField + +/** + * Represents a boolean flag that is either present or not in a Kotlin declaration. + * + * @see Flags + */ +class MetadataFlag internal constructor( + private val offset: Int, + private val bitWidth: Int, + private val value: Int +) { + internal constructor(field: FlagField<*>, value: Int) : this(field.offset, field.bitWidth, value) + + internal constructor(field: BooleanFlagField) : this(field, 1) + + internal operator fun plus(flags: Int): Int = + (flags and (((1 shl bitWidth) - 1) shl offset).inv()) + (value shl offset) + + /** + * Checks whether the flag is present in the given bitmask. + */ + operator fun invoke(flags: Int): Boolean = + (flags ushr offset) and ((1 shl bitWidth) - 1) == value +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/annotations.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/annotations.kt new file mode 100644 index 00000000000..e0147cf4619 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/annotations.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +/** + * Represents an annotation, written to the Kotlin metadata. Note that not all annotations are written to metadata on all platforms. + * For example, on JVM most of the annotations are written directly on the corresponding declarations in the class file, + * and entries in the metadata only have a flag ([Flags.HAS_ANNOTATIONS]) to signal if they do have annotations in the bytecode. + * On JVM, only annotations on type parameters and types are serialized to the Kotlin metadata. + * + * @param className the fully qualified name of the annotation class + * @param arguments explicitly specified arguments to the annotation; does not include default values for annotation parameters + * (specified in the annotation class declaration) + */ +data class KmAnnotation(val className: ClassName, val arguments: Map>) + +/** + * Represents an argument to the annotation. + * + * @param T the type of the value of this argument + */ +sealed class KmAnnotationArgument { + /** + * The value of this argument. + */ + abstract val value: T + + data class ByteValue(override val value: Byte) : KmAnnotationArgument() + data class CharValue(override val value: Char) : KmAnnotationArgument() + data class ShortValue(override val value: Short) : KmAnnotationArgument() + data class IntValue(override val value: Int) : KmAnnotationArgument() + data class LongValue(override val value: Long) : KmAnnotationArgument() + data class FloatValue(override val value: Float) : KmAnnotationArgument() + data class DoubleValue(override val value: Double) : KmAnnotationArgument() + data class BooleanValue(override val value: Boolean) : KmAnnotationArgument() + + data class StringValue(override val value: String) : KmAnnotationArgument() + data class KClassValue(override val value: ClassName) : KmAnnotationArgument() + data class EnumValue(val enumClassName: ClassName, val enumEntryName: String) : KmAnnotationArgument() { + override val value: String = "$enumClassName.$enumEntryName" + } + + data class AnnotationValue(override val value: KmAnnotation) : KmAnnotationArgument() + data class ArrayValue(override val value: List>) : KmAnnotationArgument>>() +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt new file mode 100644 index 00000000000..a10ad3a48b3 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +import kotlin.reflect.KClass + +/** + * A type of the extension visitor expected by the code that uses the visitor API. + * + * Each declaration which can have platform-specific extensions in the metadata has a method `visitExtensions` in its visitor, e.g.: + * + * open fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? + * + * The client code is supposed to return the extension visitor corresponding to the given type, or to return `null` if the type is + * of no interest to that code. Each platform-specific extension visitor has a [KmExtensionType] instance declared in the `TYPE` property + * its companion object. For example, to load JVM extensions on a function, one could do: + * + * override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? { + * if (type != JvmFunctionExtensionVisitor.TYPE) return null + * + * return object : JvmFunctionExtensionVisitor() { + * ... + * } + * } + * + * In case an extension visitor of an unrelated type is returned, the code using the visitor API must ignore that visitor. + */ +data class KmExtensionType(val klass: KClass) + +/** + * A base interface for all extension visitors. + */ +interface KmExtensionVisitor + +/** + * A visitor to visit platform-specific extensions for a function. + */ +interface KmFunctionExtensionVisitor : KmExtensionVisitor + +/** + * A visitor to visit platform-specific extensions for a property. + */ +interface KmPropertyExtensionVisitor : KmExtensionVisitor + +/** + * A visitor to visit platform-specific extensions for a constructor. + */ +interface KmConstructorExtensionVisitor : KmExtensionVisitor + +/** + * A visitor to visit platform-specific extensions for a type parameter. + */ +interface KmTypeParameterExtensionVisitor : KmExtensionVisitor + +/** + * A visitor to visit platform-specific extensions for a type. + */ +interface KmTypeExtensionVisitor : KmExtensionVisitor diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt new file mode 100644 index 00000000000..a8862038895 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.impl.extensions + +import kotlinx.metadata.* +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver +import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import java.util.* + +interface MetadataExtensions { + fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, strings: NameResolver, types: TypeTable) + + fun readPropertyExtensions(v: KmPropertyVisitor, proto: ProtoBuf.Property, strings: NameResolver, types: TypeTable) + + fun readConstructorExtensions(v: KmConstructorVisitor, proto: ProtoBuf.Constructor, strings: NameResolver, types: TypeTable) + + fun readTypeParameterExtensions(v: KmTypeParameterVisitor, proto: ProtoBuf.TypeParameter, strings: NameResolver) + + fun readTypeExtensions(v: KmTypeVisitor, proto: ProtoBuf.Type, strings: NameResolver) + + companion object { + val INSTANCE: MetadataExtensions by lazy { + ServiceLoader.load(MetadataExtensions::class.java).toList().firstOrNull() + ?: error( + "No MetadataExtensions instances found in the classpath. Please ensure that the META-INF/services/ " + + "is not stripped from your application and that the Java virtual machine is not running " + + "under a security manager" + ) + } + } +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt new file mode 100644 index 00000000000..49e5bf7285a --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.impl + +import kotlinx.metadata.ClassName +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmAnnotationArgument +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf.Annotation.Argument.Value.Type.* +import org.jetbrains.kotlin.metadata.deserialization.NameResolver + +fun ProtoBuf.Annotation.readAnnotation(strings: NameResolver): KmAnnotation = + KmAnnotation( + strings.getClassName(id), + argumentList.mapNotNull { argument -> + argument.value.readAnnotationArgument(strings)?.let { value -> + strings.getString(argument.nameId) to value + } + }.toMap() + ) + +private fun ProtoBuf.Annotation.Argument.Value.readAnnotationArgument(strings: NameResolver): KmAnnotationArgument<*>? = + when (type) { + BYTE -> KmAnnotationArgument.ByteValue(intValue.toByte()) + CHAR -> KmAnnotationArgument.CharValue(intValue.toChar()) + SHORT -> KmAnnotationArgument.ShortValue(intValue.toShort()) + INT -> KmAnnotationArgument.IntValue(intValue.toInt()) + LONG -> KmAnnotationArgument.LongValue(intValue) + FLOAT -> KmAnnotationArgument.FloatValue(floatValue) + DOUBLE -> KmAnnotationArgument.DoubleValue(doubleValue) + BOOLEAN -> KmAnnotationArgument.BooleanValue(intValue != 0L) + STRING -> KmAnnotationArgument.StringValue(strings.getString(stringValue)) + CLASS -> KmAnnotationArgument.KClassValue(strings.getClassName(classId)) + ENUM -> KmAnnotationArgument.EnumValue(strings.getClassName(classId), strings.getString(enumValueId)) + ANNOTATION -> KmAnnotationArgument.AnnotationValue(annotation.readAnnotation(strings)) + ARRAY -> KmAnnotationArgument.ArrayValue(arrayElementList.mapNotNull { it.readAnnotationArgument(strings) }) + null -> null + } + +internal fun NameResolver.getClassName(index: Int): ClassName { + val name = getQualifiedClassName(index) + return if (isLocalClassName(index)) ".$name" else name +} diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt new file mode 100644 index 00000000000..aeb80e7c398 --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt @@ -0,0 +1,413 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.impl + +import kotlinx.metadata.* +import kotlinx.metadata.impl.extensions.MetadataExtensions +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.* +import org.jetbrains.kotlin.metadata.deserialization.Flags +import org.jetbrains.kotlin.protobuf.MessageLite + +class ReadContext( + internal val strings: NameResolver, + internal val types: TypeTable, + internal val versionRequirements: VersionRequirementTable, + private val parent: ReadContext? = null +) { + internal val extensions = MetadataExtensions.INSTANCE + private val typeParameterNameToId = mutableMapOf() + + operator fun get(index: Int): String = + strings.getString(index) + + fun className(index: Int): ClassName = + strings.getClassName(index) + + fun getTypeParameterId(name: Int): Int? = + typeParameterNameToId[name] ?: parent?.getTypeParameterId(name) + + fun withTypeParameters(typeParameters: List): ReadContext = + ReadContext(strings, types, versionRequirements, this).apply { + for (typeParameter in typeParameters) { + typeParameterNameToId[typeParameter.name] = typeParameter.id + } + } +} + +fun ProtoBuf.Class.accept(v: KmClassVisitor, strings: NameResolver) { + val c = ReadContext(strings, TypeTable(typeTable), VersionRequirementTable.create(versionRequirementTable)) + .withTypeParameters(typeParameterList) + + v.visit(flags, c.className(fqName)) + + for (typeParameter in typeParameterList) { + typeParameter.accept(v::visitTypeParameter, c) + } + + for (supertype in supertypes(c.types)) { + v.visitSupertype(supertype.typeFlags)?.let { supertype.accept(it, c) } + } + + for (constructor in constructorList) { + v.visitConstructor(constructor.flags)?.let { constructor.accept(it, c) } + } + + v.visitDeclarations(functionList, propertyList, typeAliasList, c) + + if (hasCompanionObjectName()) { + v.visitCompanionObject(c[companionObjectName]) + } + + for (nestedClassName in nestedClassNameList) { + v.visitNestedClass(c[nestedClassName]) + } + + for (enumEntry in enumEntryList) { + if (!enumEntry.hasName()) { + throw InconsistentKotlinMetadataException("No name for EnumEntry") + } + v.visitEnumEntry(c[enumEntry.name]) + } + + for (sealedSubclassFqName in sealedSubclassFqNameList) { + v.visitSealedSubclass(c.className(sealedSubclassFqName)) + } + + if (hasVersionRequirement()) { + v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } + } + + v.visitEnd() +} + +fun ProtoBuf.Package.accept(v: KmPackageVisitor, strings: NameResolver) { + val c = ReadContext(strings, TypeTable(typeTable), VersionRequirementTable.create(versionRequirementTable)) + + v.visitDeclarations(functionList, propertyList, typeAliasList, c) + + v.visitEnd() +} + +private fun KmDeclarationContainerVisitor.visitDeclarations( + functions: List, + properties: List, + typeAliases: List, + c: ReadContext +) { + for (function in functions) { + visitFunction(function.flags, c[function.name])?.let { function.accept(it, c) } + } + + for (property in properties) { + val flags = property.flags + val defaultAccessorFlags = Flags.getAccessorFlags( + Flags.HAS_ANNOTATIONS.get(flags), Flags.VISIBILITY.get(flags), Flags.MODALITY.get(flags), false, false, false + ) + visitProperty( + flags, + c[property.name], + if (property.hasGetterFlags()) property.getterFlags else defaultAccessorFlags, + if (property.hasSetterFlags()) property.setterFlags else defaultAccessorFlags + )?.let { property.accept(it, c) } + } + + for (typeAlias in typeAliases) { + visitTypeAlias(typeAlias.flags, c[typeAlias.name])?.let { typeAlias.accept(it, c) } + } +} + +fun ProtoBuf.Function.accept(v: KmLambdaVisitor, strings: NameResolver) { + val c = ReadContext(strings, TypeTable(typeTable), VersionRequirementTable.EMPTY) + + v.visitFunction(flags, c[name])?.let { accept(it, c) } + + v.visitEnd() +} + +private fun ProtoBuf.Constructor.accept(v: KmConstructorVisitor, c: ReadContext) { + for (parameter in valueParameterList) { + v.visitValueParameter(parameter.flags, c[parameter.name])?.let { parameter.accept(it, c) } + } + + if (hasVersionRequirement()) { + v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } + } + + c.extensions.readConstructorExtensions(v, this, c.strings, c.types) + + v.visitEnd() +} + +private fun ProtoBuf.Function.accept(v: KmFunctionVisitor, outer: ReadContext) { + val c = outer.withTypeParameters(typeParameterList) + + for (typeParameter in typeParameterList) { + typeParameter.accept(v::visitTypeParameter, c) + } + + receiverType(c.types)?.let { receiverType -> + v.visitReceiverParameterType(receiverType.typeFlags)?.let { receiverType.accept(it, c) } + } + + for (parameter in valueParameterList) { + v.visitValueParameter(parameter.flags, c[parameter.name])?.let { parameter.accept(it, c) } + } + + returnType(c.types).let { returnType -> + v.visitReturnType(returnType.typeFlags)?.let { returnType.accept(it, c) } + } + + if (hasContract()) { + v.visitContract()?.let { contract.accept(it, c) } + } + + if (hasVersionRequirement()) { + v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } + } + + c.extensions.readFunctionExtensions(v, this, c.strings, c.types) + + v.visitEnd() +} + +private fun ProtoBuf.Property.accept(v: KmPropertyVisitor, outer: ReadContext) { + val c = outer.withTypeParameters(typeParameterList) + + for (typeParameter in typeParameterList) { + typeParameter.accept(v::visitTypeParameter, c) + } + + receiverType(c.types)?.let { receiverType -> + v.visitReceiverParameterType(receiverType.typeFlags)?.let { receiverType.accept(it, c) } + } + + if (hasSetterValueParameter()) { + val parameter = setterValueParameter + v.visitSetterParameter(parameter.flags, c[parameter.name])?.let { parameter.accept(it, c) } + } + + returnType(c.types).let { returnType -> + v.visitReturnType(returnType.typeFlags)?.let { returnType.accept(it, c) } + } + + if (hasVersionRequirement()) { + v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } + } + + c.extensions.readPropertyExtensions(v, this, c.strings, c.types) + + v.visitEnd() +} + +private fun ProtoBuf.TypeAlias.accept(v: KmTypeAliasVisitor, outer: ReadContext) { + val c = outer.withTypeParameters(typeParameterList) + + for (typeParameter in typeParameterList) { + typeParameter.accept(v::visitTypeParameter, c) + } + + underlyingType(c.types).let { underlyingType -> + v.visitUnderlyingType(underlyingType.typeFlags)?.let { underlyingType.accept(it, c) } + } + + expandedType(c.types).let { expandedType -> + v.visitExpandedType(expandedType.typeFlags)?.let { expandedType.accept(it, c) } + } + + for (annotation in annotationList) { + v.visitAnnotation(annotation.readAnnotation(c.strings)) + } + + if (hasVersionRequirement()) { + v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } + } + + v.visitEnd() +} + +private fun ProtoBuf.ValueParameter.accept(v: KmValueParameterVisitor, c: ReadContext) { + type(c.types).let { type -> + v.visitType(type.typeFlags)?.let { type.accept(it, c) } + } + + varargElementType(c.types)?.let { varargElementType -> + v.visitVarargElementType(varargElementType.typeFlags)?.let { varargElementType.accept(it, c) } + } + + v.visitEnd() +} + +private inline fun ProtoBuf.TypeParameter.accept( + visit: (flags: Int, name: String, id: Int, variance: KmVariance) -> KmTypeParameterVisitor?, + c: ReadContext +) { + val variance = when (variance!!) { + ProtoBuf.TypeParameter.Variance.IN -> KmVariance.IN + ProtoBuf.TypeParameter.Variance.OUT -> KmVariance.OUT + ProtoBuf.TypeParameter.Variance.INV -> KmVariance.INVARIANT + } + + visit(typeParameterFlags, c[name], id, variance)?.let { accept(it, c) } +} + +private fun ProtoBuf.TypeParameter.accept(v: KmTypeParameterVisitor, c: ReadContext) { + for (upperBound in upperBounds(c.types)) { + v.visitUpperBound(upperBound.typeFlags)?.let { upperBound.accept(it, c) } + } + + c.extensions.readTypeParameterExtensions(v, this, c.strings) + + v.visitEnd() +} + +private fun ProtoBuf.Type.accept(v: KmTypeVisitor, c: ReadContext) { + when { + hasClassName() -> v.visitClass(c.className(className)) + hasTypeAliasName() -> v.visitTypeAlias(c.className(typeAliasName)) + hasTypeParameter() -> v.visitTypeParameter(typeParameter) + hasTypeParameterName() -> { + val id = c.getTypeParameterId(typeParameterName) + ?: throw InconsistentKotlinMetadataException("No type parameter id for ${c[typeParameterName]}") + v.visitTypeParameter(id) + } + else -> { + throw InconsistentKotlinMetadataException("No classifier (class, type alias or type parameter) recorded for Type") + } + } + + for (argument in argumentList) { + val variance = when (argument.projection!!) { + ProtoBuf.Type.Argument.Projection.IN -> KmVariance.IN + ProtoBuf.Type.Argument.Projection.OUT -> KmVariance.OUT + ProtoBuf.Type.Argument.Projection.INV -> KmVariance.INVARIANT + ProtoBuf.Type.Argument.Projection.STAR -> null + } + + if (variance != null) { + val argumentType = argument.type(c.types) + ?: throw InconsistentKotlinMetadataException("No type argument for non-STAR projection in Type") + v.visitArgument(argumentType.typeFlags, variance)?.let { argumentType.accept(it, c) } + } else { + v.visitStarProjection() + } + } + + abbreviatedType(c.types)?.let { abbreviatedType -> + v.visitAbbreviatedType(abbreviatedType.typeFlags)?.let { abbreviatedType.accept(it, c) } + } + + outerType(c.types)?.let { outerType -> + v.visitOuterType(outerType.typeFlags)?.let { outerType.accept(it, c) } + } + + flexibleUpperBound(c.types)?.let { upperBound -> + v.visitFlexibleTypeUpperBound( + upperBound.typeFlags, + if (hasFlexibleTypeCapabilitiesId()) c[flexibleTypeCapabilitiesId] else null + )?.let { upperBound.accept(it, c) } + } + + c.extensions.readTypeExtensions(v, this, c.strings) + + v.visitEnd() +} + +private fun MessageLite.acceptVersionRequirementVisitor(v: KmVersionRequirementVisitor, c: ReadContext) { + val message = VersionRequirement.create(this, c.strings, c.versionRequirements) + ?: throw InconsistentKotlinMetadataException("No VersionRequirement with the given id in the table") + + val kind = when (message.kind) { + ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION -> KmVersionRequirementVersionKind.LANGUAGE_VERSION + ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION -> KmVersionRequirementVersionKind.COMPILER_VERSION + ProtoBuf.VersionRequirement.VersionKind.API_VERSION -> KmVersionRequirementVersionKind.API_VERSION + } + + val level = when (message.level) { + DeprecationLevel.WARNING -> KmVersionRequirementLevel.WARNING + DeprecationLevel.ERROR -> KmVersionRequirementLevel.ERROR + DeprecationLevel.HIDDEN -> KmVersionRequirementLevel.HIDDEN + } + + v.visit(kind, level, message.errorCode, message.message) + + val (major, minor, patch) = message.version + v.visitVersion(major, minor, patch) + + v.visitEnd() +} + +private fun ProtoBuf.Contract.accept(v: KmContractVisitor, c: ReadContext) { + for (effect in effectList) { + if (!effect.hasEffectType()) continue + + val effectType = when (effect.effectType!!) { + ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> KmEffectType.RETURNS_CONSTANT + ProtoBuf.Effect.EffectType.CALLS -> KmEffectType.CALLS + ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> KmEffectType.RETURNS_NOT_NULL + } + + val effectKind = if (!effect.hasKind()) null else when (effect.kind!!) { + ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE -> KmEffectInvocationKind.AT_MOST_ONCE + ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> KmEffectInvocationKind.EXACTLY_ONCE + ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> KmEffectInvocationKind.AT_LEAST_ONCE + } + + v.visitEffect(effectType, effectKind)?.let { effect.accept(it, c) } + } + + v.visitEnd() +} + +private fun ProtoBuf.Effect.accept(v: KmEffectVisitor, c: ReadContext) { + for (constructorArgument in effectConstructorArgumentList) { + v.visitConstructorArgument()?.let { constructorArgument.accept(it, c) } + } + + if (hasConclusionOfConditionalEffect()) { + v.visitConclusionOfConditionalEffect()?.let { conclusionOfConditionalEffect.accept(it, c) } + } + + v.visitEnd() +} + +private fun ProtoBuf.Expression.accept(v: KmEffectExpressionVisitor, c: ReadContext) { + v.visit( + flags, + if (hasValueParameterReference()) valueParameterReference else null + ) + + if (hasConstantValue()) { + v.visitConstantValue( + when (constantValue!!) { + ProtoBuf.Expression.ConstantValue.TRUE -> true + ProtoBuf.Expression.ConstantValue.FALSE -> false + ProtoBuf.Expression.ConstantValue.NULL -> null + } + ) + } + + isInstanceType(c.types)?.let { type -> + v.visitIsInstanceType(type.typeFlags)?.let { type.accept(it, c) } + } + + for (andArgument in andArgumentList) { + v.visitAndArgument()?.let { andArgument.accept(it, c) } + } + + for (orArgument in orArgumentList) { + v.visitOrArgument()?.let { orArgument.accept(it, c) } + } + + v.visitEnd() +} + +private val ProtoBuf.Type.typeFlags: Int + get() = (if (nullable) 1 shl 0 else 0) + + (flags shl 1) + +private val ProtoBuf.TypeParameter.typeParameterFlags: Int + get() = if (reified) 1 else 0 diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt new file mode 100644 index 00000000000..0d0e8e85aaf --- /dev/null +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt @@ -0,0 +1,811 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata + +/** + * A visitor to visit Kotlin declarations, which are containers of other declarations: functions, properties and type aliases. + */ +abstract class KmDeclarationContainerVisitor @JvmOverloads constructor(protected open val delegate: KmDeclarationContainerVisitor? = null) { + /** + * Visits a function in the container. + * + * @param flags function flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag and [Flags.Function] flags + * @param name the name of the function + */ + open fun visitFunction(flags: Int, name: String): KmFunctionVisitor? = + delegate?.visitFunction(flags, name) + + /** + * Visits a property in the container. + * + * @param flags property flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag and [Flags.Property] flags + * @param name the name of the property + * @param getterFlags property accessor flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag + * and [Flags.PropertyAccessor] flags + * @param setterFlags property accessor flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag + * and [Flags.PropertyAccessor] flags + */ + open fun visitProperty(flags: Int, name: String, getterFlags: Int, setterFlags: Int): KmPropertyVisitor? = + delegate?.visitProperty(flags, name, getterFlags, setterFlags) + + /** + * Visits a type alias in the container. + * + * @param flags type alias flags, consisting of [Flags.HAS_ANNOTATIONS] and visibility flag + * @param name the name of the type alias + */ + open fun visitTypeAlias(flags: Int, name: String): KmTypeAliasVisitor? = + delegate?.visitTypeAlias(flags, name) +} + +/** + * A visitor to visit Kotlin classes, including interfaces, objects, enum classes and annotation classes. + * + * When using this class, [visit] must be called first, followed by zero or more [visitTypeParameter] calls, followed by zero or more calls + * to other visit* methods, followed by [visitEnd]. + */ +abstract class KmClassVisitor @JvmOverloads constructor(delegate: KmClassVisitor? = null) : KmDeclarationContainerVisitor(delegate) { + override val delegate: KmClassVisitor? + get() = super.delegate as KmClassVisitor? + + /** + * Visits the basic information about the class. + * + * @param flags class flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag and [Flags.Class] flags + * @param name the name of the class + */ + open fun visit(flags: Int, name: ClassName) { + delegate?.visit(flags, name) + } + + /** + * Visits a type parameter of the class. + * + * @param flags type parameter flags, consisting of [Flags.TypeParameter] flags + * @param name the name of the type parameter + * @param id the id of the type parameter, useful to be able to uniquely identify the type parameter in different contexts where + * the name isn't enough (e.g. `class A { fun foo(t: T) }`) + * @param variance the declaration-site variance of the type parameter + */ + open fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? = + delegate?.visitTypeParameter(flags, name, id, variance) + + /** + * Visits a supertype of the class. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitSupertype(flags: Int): KmTypeVisitor? = + delegate?.visitSupertype(flags) + + /** + * Visits a constructor of the class. + * + * @param flags constructor flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag and [Flags.Constructor] flags + */ + open fun visitConstructor(flags: Int): KmConstructorVisitor? = + delegate?.visitConstructor(flags) + + /** + * Visits the name of the companion object of this class, if it has one. + * + * @param name the name of the companion object + */ + open fun visitCompanionObject(name: String) { + delegate?.visitCompanionObject(name) + } + + /** + * Visits the name of a nested class of this class. + * + * @param name the name of a nested class + */ + open fun visitNestedClass(name: String) { + delegate?.visitNestedClass(name) + } + + /** + * Visits the name of an enum entry, if this class is an enum class. + * + * @param name the name of an enum entry + */ + open fun visitEnumEntry(name: String) { + delegate?.visitEnumEntry(name) + } + + /** + * Visits the name of a direct subclass of this class, if this class is `sealed`. + * + * @param name the name of a direct subclass + */ + open fun visitSealedSubclass(name: ClassName) { + delegate?.visitSealedSubclass(name) + } + + /** + * Visits the version requirement on this class. + */ + open fun visitVersionRequirement(): KmVersionRequirementVisitor? = + delegate?.visitVersionRequirement() + + /** + * Visits the end of the class. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit Kotlin package fragments, including single file facades and multi-file class parts. + * + * When using this class, [visitEnd] must be called exactly once and after calls to all other visit* methods. + */ +abstract class KmPackageVisitor @JvmOverloads constructor(delegate: KmPackageVisitor? = null) : KmDeclarationContainerVisitor(delegate) { + override val delegate: KmPackageVisitor? + get() = super.delegate as KmPackageVisitor? + + /** + * Visits the end of the package fragment. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit the metadata of a synthetic class generated for a Kotlin lambda. + * + * When using this class, [visitFunction] must be called first, followed by [visitEnd]. + */ +abstract class KmLambdaVisitor @JvmOverloads constructor(private val delegate: KmLambdaVisitor? = null) { + /** + * Visits the signature of a synthetic anonymous function, representing the lambda. + * + * @param flags function flags, consisting of [Flags.HAS_ANNOTATIONS], visibility flag, modality flag and [Flags.Function] flags + * @param name the name of the function (usually `""` or `""` for lambdas emitted by the Kotlin compiler) + */ + open fun visitFunction(flags: Int, name: String): KmFunctionVisitor? = + delegate?.visitFunction(flags, name) + + /** + * Visits the end of the lambda. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a constructor of a Kotlin class. + * + * When using this class, [visitEnd] must be called exactly once and after calls to all other visit* methods. + */ +abstract class KmConstructorVisitor @JvmOverloads constructor(private val delegate: KmConstructorVisitor? = null) { + /** + * Visits a value parameter of the constructor. + * + * @param flags value parameter flags, consisting of [Flags.ValueParameter] flags + * @param name the name of the value parameter + */ + open fun visitValueParameter(flags: Int, name: String): KmValueParameterVisitor? = + delegate?.visitValueParameter(flags, name) + + /** + * Visits the version requirement on this constructor. + */ + open fun visitVersionRequirement(): KmVersionRequirementVisitor? = + delegate?.visitVersionRequirement() + + /** + * Visits the extensions of the given type on the constructor. + * + * @param type the type of extension visitor to be returned + */ + open fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the constructor. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a Kotlin function declaration. + * + * When using this class, zero or more calls to [visitTypeParameter] must be done first, followed by zero or more calls + * to other visit* methods, followed by [visitEnd]. + */ +abstract class KmFunctionVisitor @JvmOverloads constructor(private val delegate: KmFunctionVisitor? = null) { + /** + * Visits a type parameter of the function. + * + * @param flags type parameter flags, consisting of [Flags.TypeParameter] flags + * @param name the name of the type parameter + * @param id the id of the type parameter, useful to be able to uniquely identify the type parameter in different contexts where + * the name isn't enough (e.g. `class A { fun foo(t: T) }`) + * @param variance the declaration-site variance of the type parameter + */ + open fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? = + delegate?.visitTypeParameter(flags, name, id, variance) + + /** + * Visits the type of the receiver of the function, if this is an extension function. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitReceiverParameterType(flags: Int): KmTypeVisitor? = + delegate?.visitReceiverParameterType(flags) + + /** + * Visits a value parameter of the function. + * + * @param flags value parameter flags, consisting of [Flags.ValueParameter] flags + * @param name the name of the value parameter + */ + open fun visitValueParameter(flags: Int, name: String): KmValueParameterVisitor? = + delegate?.visitValueParameter(flags, name) + + /** + * Visits the return type of the function. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitReturnType(flags: Int): KmTypeVisitor? = + delegate?.visitReturnType(flags) + + /** + * Visits the version requirement on this function. + */ + open fun visitVersionRequirement(): KmVersionRequirementVisitor? = + delegate?.visitVersionRequirement() + + /** + * Visits the contract of the function. + */ + open fun visitContract(): KmContractVisitor? = + delegate?.visitContract() + + /** + * Visits the extensions of the given type on the function. + * + * @param type the type of extension visitor to be returned + */ + open fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the function. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a Kotlin property declaration. + * + * When using this class, zero or more calls to [visitTypeParameter] must be done first, followed by zero or more calls + * to other visit* methods, followed by [visitEnd]. + */ +abstract class KmPropertyVisitor @JvmOverloads constructor(private val delegate: KmPropertyVisitor? = null) { + /** + * Visits a type parameter of the property. + * + * @param flags type parameter flags, consisting of [Flags.TypeParameter] flags + * @param name the name of the type parameter + * @param id the id of the type parameter, useful to be able to uniquely identify the type parameter in different contexts where + * the name isn't enough (e.g. `class A { fun foo(t: T) }`) + * @param variance the declaration-site variance of the type parameter + */ + open fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? = + delegate?.visitTypeParameter(flags, name, id, variance) + + /** + * Visits the type of the receiver of the property, if this is an extension property. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitReceiverParameterType(flags: Int): KmTypeVisitor? = + delegate?.visitReceiverParameterType(flags) + + /** + * Visits a value parameter of the setter of this property, if this is a `var` property. + * + * @param flags value parameter flags, consisting of [Flags.ValueParameter] flags + * @param name the name of the value parameter (`""` for properties emitted by the Kotlin compiler) + */ + open fun visitSetterParameter(flags: Int, name: String): KmValueParameterVisitor? = + delegate?.visitSetterParameter(flags, name) + + /** + * Visits the type of the property. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitReturnType(flags: Int): KmTypeVisitor? = + delegate?.visitReturnType(flags) + + /** + * Visits the version requirement on this property. + */ + open fun visitVersionRequirement(): KmVersionRequirementVisitor? = + delegate?.visitVersionRequirement() + + /** + * Visits the extensions of the given type on the property. + * + * @param type the type of extension visitor to be returned + */ + open fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the property. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a Kotlin type alias declaration. + * + * When using this class, zero or more calls to [visitTypeParameter] must be done first, followed by zero or more calls + * to other visit* methods, followed by [visitEnd]. + */ +abstract class KmTypeAliasVisitor @JvmOverloads constructor(private val delegate: KmTypeAliasVisitor? = null) { + /** + * Visits a type parameter of the type alias. + * + * @param flags type parameter flags, consisting of [Flags.TypeParameter] flags + * @param name the name of the type parameter + * @param id the id of the type parameter, useful to be able to uniquely identify the type parameter in different contexts where + * the name isn't enough (e.g. `class A { fun foo(t: T) }`) + * @param variance the declaration-site variance of the type parameter + */ + open fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? = + delegate?.visitTypeParameter(flags, name, id, variance) + + /** + * Visits the underlying type of the type alias, i.e. the type in the right-hand side of the type alias declaration. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitUnderlyingType(flags: Int): KmTypeVisitor? = + delegate?.visitUnderlyingType(flags) + + /** + * Visits the expanded type of the type alias, i.e. the full expansion of the underlying type, where all type aliases are substituted + * with their expanded types. If not type aliases are used in the underlying type, expanded type is equal to the underlying type. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitExpandedType(flags: Int): KmTypeVisitor? = + delegate?.visitExpandedType(flags) + + /** + * Visits the annotation on the type alias. + * + * @param annotation annotation on the type alias + */ + open fun visitAnnotation(annotation: KmAnnotation) { + delegate?.visitAnnotation(annotation) + } + + /** + * Visits the version requirement on this type alias. + */ + open fun visitVersionRequirement(): KmVersionRequirementVisitor? = + delegate?.visitVersionRequirement() + + /** + * Visits the end of the type alias. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a value parameter of a Kotlin constructor, function or property setter. + * + * When using this class, either [visitType] or [visitVarargElementType] must be called first (depending on whether the value parameter + * is `vararg` or not), followed by [visitEnd]. + */ +abstract class KmValueParameterVisitor @JvmOverloads constructor(private val delegate: KmValueParameterVisitor? = null) { + /** + * Visits the type of the value parameter, if this is **not** a `vararg` parameter. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitType(flags: Int): KmTypeVisitor? = + delegate?.visitType(flags) + + /** + * Visits the type of the value parameter, if this is a `vararg` parameter. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitVarargElementType(flags: Int): KmTypeVisitor? = + delegate?.visitVarargElementType(flags) + + /** + * Visits the end of the value parameter. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a type parameter of a Kotlin class, function or property. + * + * When using this class, zero or more [visitUpperBound] calls must be done first, followed by [visitEnd]. + */ +abstract class KmTypeParameterVisitor @JvmOverloads constructor(private val delegate: KmTypeParameterVisitor? = null) { + /** + * Visits the upper bound of the type parameter. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitUpperBound(flags: Int): KmTypeVisitor? = + delegate?.visitUpperBound(flags) + + /** + * Visits the extensions of the given type on the type parameter. + * + * @param type the type of extension visitor to be returned + */ + open fun visitExtensions(type: KmExtensionType): KmTypeParameterExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the type parameter. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit a type. The type must have a classifier which is one of: a class [visitClass], type parameter [visitTypeParameter] + * or type alias [visitTypeAlias]. If the type's classifier is a class or a type alias, it can have type arguments ([visitArgument] and + * [visitStarProjection]). If the type's classifier is an inner class, it can have the outer type ([visitOuterType]), which captures + * the generic type arguments of the outer class. Also, each type can have an abbreviation ([visitAbbreviatedType]) in case a type alias + * was used originally at this site in the declaration (all types are expanded by default for metadata produced by the Kotlin compiler). + * If [visitFlexibleTypeUpperBound] is called, this type is regarded as a flexible type, and its contents represent the lower bound, + * and the result of the call represents the upper bound. + * + * When using this class, [visitEnd] must be called exactly once and after calls to all other visit* methods. + */ +abstract class KmTypeVisitor @JvmOverloads constructor(private val delegate: KmTypeVisitor? = null) { + /** + * Visits the name of the class, if this type's classifier is a class. + * + * @param name the name of the class + */ + open fun visitClass(name: ClassName) { + delegate?.visitClass(name) + } + + /** + * Visits the name of the type alias, if this type's classifier is a type alias. Note that all types are expanded for metadata produced + * by the Kotlin compiler, so the the type with a type alias classifier may only appear in a call to [visitAbbreviatedType]. + * + * @param name the name of the type alias + */ + open fun visitTypeAlias(name: ClassName) { + delegate?.visitTypeAlias(name) + } + + /** + * Visits the id of the type parameter, if this type's classifier is a type parameter. + * + * @param id id of the type parameter + */ + open fun visitTypeParameter(id: Int) { + delegate?.visitTypeParameter(id) + } + + /** + * Visits the type projection used in a type argument of the type based on a class or on a type alias. + * For example, in `MutableMap`, `in String?` is the type projection which is the first type argument of the type. + * + * @param flags type flags, consisting of [Flags.Type] flags + * @param variance the variance of the type projection + */ + open fun visitArgument(flags: Int, variance: KmVariance): KmTypeVisitor? = + delegate?.visitArgument(flags, variance) + + /** + * Visits the star (`*`) projection used in a type argument of the type based on a class or on a type alias. + * For example, in `MutableMap`, `*` is the star projection which is the second type argument of the type. + */ + open fun visitStarProjection() { + delegate?.visitStarProjection() + } + + /** + * Visits the abbreviation of this type. Note that all types are expanded for metadata produced by the Kotlin compiler. For example: + * + * typealias A = MutableList + * + * fun foo(a: A) {} + * + * The type of the `foo`'s parameter in the metadata is actually `MutableList`, and its abbreviation is `A`. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitAbbreviatedType(flags: Int): KmTypeVisitor? = + delegate?.visitAbbreviatedType(flags) + + /** + * Visits the outer type, if this type's classifier is an inner class. For example: + * + * class A { inner class B } + * + * fun foo(a: A<*>.B) {} + * + * The type of the `foo`'s parameter in the metadata is `B` (a type whose classifier is class `B`, and it has one type argument, + * type `Byte?`), and its outer type is `A<*>` (a type whose classifier is class `A`, and it has one type argument, star projection). + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitOuterType(flags: Int): KmTypeVisitor? = + delegate?.visitOuterType(flags) + + /** + * Visits the upper bound of the type, marking it as flexible and its contents as the lower bound. Flexible types in Kotlin include + * platform types in Kotlin/JVM and `dynamic` type in Kotlin/JS. + * + * @param flags type flags, consisting of [Flags.Type] flags + * @param typeFlexibilityId id of the kind of flexibility this type has. For example, "kotlin.jvm.PlatformType" for JVM platform types, + * or "kotlin.DynamicType" for JS dynamic type + */ + open fun visitFlexibleTypeUpperBound(flags: Int, typeFlexibilityId: String?): KmTypeVisitor? = + delegate?.visitFlexibleTypeUpperBound(flags, typeFlexibilityId) + + /** + * Visits the extensions of the given type on the type. + * + * @param type the type of extension visitor to be returned + */ + open fun visitExtensions(type: KmExtensionType): KmTypeExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the type. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit the contents of a version requirement on a Kotlin declaration. + * + * Version requirement is an internal feature of the Kotlin compiler and the standard Kotlin library, + * enabled for example with the internal [kotlin.internal.RequireKotlin] annotation. + * + * When using this class, [visit] must be called first, followed by [visitVersion], followed by [visitEnd]. + */ +abstract class KmVersionRequirementVisitor @JvmOverloads constructor(private val delegate: KmVersionRequirementVisitor? = null) { + /** + * Visits the description of this version requirement. + * + * @param kind the kind of the version that this declaration requires: compiler, language or API version + * @param level the level of the diagnostic that must be reported on the usages of the declaration in case + * the version requirement is not satisfied + * @param errorCode optional error code to be displayed in the diagnostic + * @param message optional message to be displayed in the diagnostic + */ + open fun visit(kind: KmVersionRequirementVersionKind, level: KmVersionRequirementLevel, errorCode: Int?, message: String?) { + delegate?.visit(kind, level, errorCode, message) + } + + /** + * Visits the version required by this requirement. + * + * @param major the major component of the version (e.g. "1" in "1.2.3") + * @param minor the minor component of the version (e.g. "2" in "1.2.3") + * @param patch the patch component of the version (e.g. "3" in "1.2.3") + */ + open fun visitVersion(major: Int, minor: Int, patch: Int) { + delegate?.visitVersion(major, minor, patch) + } + + /** + * Visits the end of the version requirement. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit the contents of the contract of a Kotlin function. + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + * + * When using this class, zero or more calls to [visitEffect] must be done first, followed by [visitEnd]. + */ +abstract class KmContractVisitor @JvmOverloads constructor(private val delegate: KmContractVisitor? = null) { + /** + * Visits an effect of this contract. + * + * @param type type of the effect + * @param invocationKind optional number of invocations of the lambda parameter of this function, + * specified further in the effect expression + */ + open fun visitEffect(type: KmEffectType, invocationKind: KmEffectInvocationKind?): KmEffectVisitor? = + delegate?.visitEffect(type, invocationKind) + + /** + * Visits the end of the contract. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit an effect (a part of the contract of a Kotlin function). + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + * + * When using this class, zero or more calls to [visitConstructorArgument] or [visitConclusionOfConditionalEffect] must be done first, + * followed by [visitEnd]. + */ +abstract class KmEffectVisitor @JvmOverloads constructor(private val delegate: KmEffectVisitor? = null) { + /** + * Visits the optional argument of the effect constructor, i.e. the constant value for the [KmEffectType.RETURNS_CONSTANT] effect, + * or the parameter reference for the [KmEffectType.CALLS] effect. + */ + open fun visitConstructorArgument(): KmEffectExpressionVisitor? = + delegate?.visitConstructorArgument() + + /** + * Visits the optional conclusion of the effect. If this method is called, the effect represents an implication with the + * right-hand side handled by the returned visitor. + */ + open fun visitConclusionOfConditionalEffect(): KmEffectExpressionVisitor? = + delegate?.visitConclusionOfConditionalEffect() + + /** + * Visits the end of the effect. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * A visitor to visit the effect expression, the contents of an effect (a part of the contract of a Kotlin function). + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + * + * When using this class, [visit] must be called first, followed by zero or more calls to other visit* methods, followed by [visitEnd]. + */ +abstract class KmEffectExpressionVisitor @JvmOverloads constructor(private val delegate: KmEffectExpressionVisitor? = null) { + /** + * Visits the basic information of the effect expression. + * + * @param flags effect expression flags, consisting of [Flags.EffectExpression] flags + * @param parameterIndex optional 1-based index of the value parameter of the function, for effects which assert something about + * the function parameters. The index 0 means the extension receiver parameter + */ + open fun visit(flags: Int, parameterIndex: Int?) { + delegate?.visit(flags, parameterIndex) + } + + /** + * Visits the constant value used in the effect expression. May be `true`, `false` or `null`. + * + * @param value the constant value + */ + open fun visitConstantValue(value: Any?) { + delegate?.visitConstantValue(value) + } + + /** + * Visits the type used as the target of an `is`-expression in the effect expression. + * + * @param flags type flags, consisting of [Flags.Type] flags + */ + open fun visitIsInstanceType(flags: Int): KmTypeVisitor? = + delegate?.visitIsInstanceType(flags) + + /** + * Visits the argument of an `&&`-expression. If this method is called, the expression represents the left-hand side and + * the returned visitor handles the right-hand side. + */ + open fun visitAndArgument(): KmEffectExpressionVisitor? = + delegate?.visitAndArgument() + + /** + * Visits the argument of an `||`-expression. If this method is called, the expression represents the left-hand side and + * the returned visitor handles the right-hand side. + */ + open fun visitOrArgument(): KmEffectExpressionVisitor? = + delegate?.visitOrArgument() + + /** + * Visits the end of the effect expression. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + +/** + * Variance applied to a type parameter on the declaration site (*declaration-site variance*), + * or to a type in a projection (*use-site variance*). + */ +enum class KmVariance { + /** + * The affected type parameter or type is *invariant*, which means it has no variance applied to it. + */ + INVARIANT, + + /** + * The affected type parameter or type is *contravariant*. Denoted by the `in` modifier in the source code. + */ + IN, + + /** + * The affected type parameter or type is *covariant*. Denoted by the `out` modifier in the source code. + */ + OUT, +} + +/** + * Type of an effect (a part of the contract of a Kotlin function). + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + */ +enum class KmEffectType { + RETURNS_CONSTANT, + CALLS, + RETURNS_NOT_NULL, +} + +/** + * Number of invocations of a lambda parameter specified by an effect (a part of the contract of a Kotlin function). + * + * Contracts are an internal feature of the standard Kotlin library, and their behavior and/or binary format + * may change in a subsequent release. + */ +enum class KmEffectInvocationKind { + AT_MOST_ONCE, + EXACTLY_ONCE, + AT_LEAST_ONCE, +} + +/** + * Severity of the diagnostic reported by the compiler when a version requirement is not satisfied. + */ +enum class KmVersionRequirementLevel { + WARNING, + ERROR, + HIDDEN, +} + +/** + * The kind of the version that is required by a version requirement. + */ +enum class KmVersionRequirementVersionKind { + LANGUAGE_VERSION, + COMPILER_VERSION, + API_VERSION, +} diff --git a/settings.gradle b/settings.gradle index d3be70ab375..08f701816a7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -113,6 +113,8 @@ include ":kotlin-build-common", ":kotlin-stdlib-jdk8", ":kotlin-stdlib:samples", ":kotlin-stdlib:jvm-minimal-for-test", + ":kotlinx-metadata", + ":kotlinx-metadata-jvm", ":prepare:build.version", ":kotlin-build-common", ":prepare:formatter", @@ -204,6 +206,8 @@ project(':kotlin-stdlib:samples').projectDir = "$rootDir/libraries/stdlib/sample project(":kotlin-stdlib:jvm-minimal-for-test").projectDir = "$rootDir/libraries/stdlib/jvm-minimal-for-test" as File project(':kotlin-reflect').projectDir = "$rootDir/libraries/reflect" as File project(':kotlin-reflect-api').projectDir = "$rootDir/libraries/reflect/api" as File +project(':kotlinx-metadata').projectDir = "$rootDir/libraries/kotlinx-metadata" as File +project(':kotlinx-metadata-jvm').projectDir = "$rootDir/libraries/kotlinx-metadata/jvm" as File project(':kotlin-compiler').projectDir = "$rootDir/prepare/compiler" as File project(':kotlin-compiler-embeddable').projectDir = "$rootDir/prepare/compiler-embeddable" as File project(':kotlin-compiler-client-embeddable').projectDir = "$rootDir/prepare/compiler-client-embeddable" as File