From bb04eae93e79e1963436ecb9208912af1a192c44 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Mon, 27 Jan 2020 15:31:45 +0300 Subject: [PATCH] [IR] Implement IdSignature API - Basic implementation of IdSignature classes and interafces - Abstract and platform Ir/Descriptor based signature builders --- .../jetbrains/kotlin/ir/util/IdSignature.kt | 154 ++++++++++++++++++ .../signature/IdSignatureBuilder.kt | 64 ++++++++ .../signature/IdSignatureDescriptor.kt | 126 ++++++++++++++ .../signature/IdSignatureFlags.kt | 14 ++ .../signature/IdSignatureSerializer.kt | 119 ++++++++++++++ .../serialization/JvmIdSignatureDescriptor.kt | 23 +++ 6 files changed, 500 insertions(+) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureBuilder.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureDescriptor.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureFlags.kt create mode 100644 compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureSerializer.kt create mode 100644 compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/JvmIdSignatureDescriptor.kt diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt new file mode 100644 index 00000000000..9c33e69eecb --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IdSignature.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.util + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.name.FqName + +sealed class IdSignature { + + abstract val isPublic: Boolean + + open fun isPackageSignature() = false + + abstract fun topLevelSignature(): IdSignature + abstract fun nearestPublicSig(): IdSignature + + abstract fun packageFqName(): FqName + + abstract fun render(): String + + open val hasTopLevel: Boolean get() = !isPackageSignature() + + val isLocal: Boolean get() = !isPublic + + override fun toString(): String { + return "${if (isPublic) "public" else "private"} ${render()}" + } + + data class PublicSignature(val packageFqn: FqName, val classFqn: FqName, val id: Long?, val mask: Long) : IdSignature() { + override val isPublic = true + + override fun packageFqName() = packageFqn + + private fun adaptMask(old: Long): Long { + // TODO: design the way flags are being mutated up to declaration tree + return old + } + + override fun topLevelSignature(): IdSignature { + if (classFqn.isRoot) { + assert(id == null) + // package signature + return this + } + + val pathSegments = classFqn.pathSegments() + + if (pathSegments.size == 1) return this + + return PublicSignature(packageFqn, FqName(pathSegments.first().asString()), null, adaptMask(mask)) + } + + override fun isPackageSignature(): Boolean = id == null && classFqn.isRoot + + override fun nearestPublicSig(): PublicSignature = this + + override fun render(): String = "${packageFqn.asString()}/${classFqn.asString()}|$id[${mask.toString(2)}]" + + override fun toString() = super.toString() + } + + class AccessorSignature(val propertySignature: IdSignature, val accessorSignature: PublicSignature) : IdSignature() { + override val isPublic: Boolean = true + + override fun topLevelSignature() = propertySignature.topLevelSignature() + + override fun nearestPublicSig() = this + + override fun packageFqName() = propertySignature.packageFqName() + + override fun render(): String = accessorSignature.render() + + override fun equals(other: Any?): Boolean { + if (other is AccessorSignature) return accessorSignature == other.accessorSignature + return accessorSignature == other + } + + override fun hashCode(): Int = accessorSignature.hashCode() + } + + data class FileLocalSignature(val container: IdSignature, val id: Long) : IdSignature() { + override val isPublic = false + + override fun packageFqName(): FqName = container.packageFqName() + + override fun topLevelSignature(): IdSignature { + val topLevelContainer = container.topLevelSignature() + if (topLevelContainer === container) { + if (topLevelContainer is PublicSignature && topLevelContainer.classFqn.isRoot) { + // private top level + return this + } + } + return topLevelContainer + } + + override fun nearestPublicSig(): IdSignature = container.nearestPublicSig() + + override fun render(): String = "${container.render()}:$id" + + override fun toString() = super.toString() + } + + class BuiltInSignature(val mangle: String, val id: Long) : IdSignature() { + constructor(id: Long) : this("", id) + + override fun topLevelSignature(): IdSignature = this // built ins are always top level + override fun nearestPublicSig(): IdSignature = this + + override fun packageFqName(): FqName = IrBuiltIns.KOTLIN_INTERNAL_IR_FQN + + override val isPublic: Boolean = true + override fun render(): String = "<ß|$mangle>" + + override fun equals(other: Any?): Boolean { + return this === other || other is BuiltInSignature && id == other.id + } + + override fun hashCode(): Int = id.toInt() xor (id shr 32).toInt() + } + + // Used to reference local variable and value parameters in function + class ScopeLocalDeclaration(val id: Int, val description: String = "") : IdSignature() { + override val isPublic: Boolean = false + + override val hasTopLevel: Boolean = false + + override fun topLevelSignature(): IdSignature = error("Is not supported for Local ID") + + override fun nearestPublicSig(): IdSignature = error("Is not supported for Local ID") + + override fun packageFqName(): FqName = error("Is not supported for Local ID") + + override fun render(): String = "#$id" + + override fun equals(other: Any?): Boolean { + return other is ScopeLocalDeclaration && id == other.id + } + + override fun hashCode(): Int = id + } +} + +interface IdSignatureComposer { + fun composeSignature(descriptor: DeclarationDescriptor): IdSignature? + fun composeEnumEntrySignature(descriptor: ClassDescriptor): IdSignature? + + fun string2Hash(s: String): Long +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureBuilder.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureBuilder.kt new file mode 100644 index 00000000000..65273712231 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureBuilder.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.signature + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.name.FqName + +abstract class IdSignatureBuilder { + protected var packageFqn: FqName = FqName.ROOT + protected val classFanSegments = mutableListOf() + protected var hashId: Long? = null + protected var hashIdAcc: Long? = null + protected var mask = 0L + + protected abstract fun accept(d: D) + + protected fun reset() { + this.packageFqn = FqName.ROOT + this.classFanSegments.clear() + this.hashId = null + this.mask = 0L + } + + protected fun build(): IdSignature { + return if (hashIdAcc == null) { + IdSignature.PublicSignature(packageFqn, FqName.fromSegments(classFanSegments), hashId, mask) + } else { + val accessorSignature = IdSignature.PublicSignature(packageFqn, FqName.fromSegments(classFanSegments), hashIdAcc, mask) + hashIdAcc = null + classFanSegments.run { removeAt(lastIndex) } + val propertySignature = build() + IdSignature.AccessorSignature(propertySignature, accessorSignature) + } + } + + + protected fun setExpected(f: Boolean) { + mask = mask or IdSignatureFlags.IS_EXPECT.encode(f) + } + + protected fun setSpecialJavaProperty(f: Boolean) { + mask = mask or IdSignatureFlags.IS_JAVA_FOR_KOTLIN_OVERRIDE_PPROPERTY.encode(f) + } + + protected open fun platformSpecificProperty(descriptor: PropertyDescriptor) {} + protected open fun platformSpecificGetter(descriptor: PropertyGetterDescriptor) {} + protected open fun platformSpecificSetter(descriptor: PropertySetterDescriptor) {} + protected open fun platformSpecificFunction(descriptor: FunctionDescriptor) {} + protected open fun platformSpecificConstructor(descriptor: ConstructorDescriptor) {} + protected open fun platformSpecificClass(descriptor: ClassDescriptor) {} + protected open fun platformSpecificAlias(descriptor: TypeAliasDescriptor) {} + + fun buildSignature(declaration: D): IdSignature { + reset() + + accept(declaration) + + return build() + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureDescriptor.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureDescriptor.kt new file mode 100644 index 00000000000..60ad4cfaa80 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureDescriptor.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.signature + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.descriptors.WrappedDeclarationDescriptor +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.util.IdSignatureComposer +import org.jetbrains.kotlin.ir.util.KotlinMangler + +open class IdSignatureDescriptor(private val mangler: KotlinMangler.DescriptorMangler) : IdSignatureComposer { + + override fun string2Hash(s: String): Long { + return mangler.run { s.hashMangle } + } + + protected open fun createSignatureBuilder(): DescriptorBasedSignatureBuilder = DescriptorBasedSignatureBuilder(mangler) + + protected open class DescriptorBasedSignatureBuilder(private val mangler: KotlinMangler.DescriptorMangler) : + IdSignatureBuilder(), + DeclarationDescriptorVisitor { + + override fun accept(d: DeclarationDescriptor) { + d.accept(this, null) + } + + private fun reportUnexpectedDescriptor(descriptor: DeclarationDescriptor) { + error("Unexpected descriptor $descriptor") + } + + private fun collectFqNames(descriptor: DeclarationDescriptorNonRoot) { + descriptor.containingDeclaration.accept(this, null) + classFanSegments.add(descriptor.name.asString()) + } + + override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Nothing?) { + packageFqn = descriptor.fqName + } + + override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: Nothing?) { + packageFqn = descriptor.fqName + } + + override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: Nothing?) = reportUnexpectedDescriptor(descriptor) + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Nothing?) { + hashId = mangler.run { descriptor.hashedMangle } + collectFqNames(descriptor) + setExpected(descriptor.isExpect) + platformSpecificFunction(descriptor) + } + + override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(descriptor) + + override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Nothing?) { + collectFqNames(descriptor) + setExpected(descriptor.isExpect) + platformSpecificClass(descriptor) + } + + override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Nothing?) { + collectFqNames(descriptor) + setExpected(descriptor.isExpect) + platformSpecificAlias(descriptor) + } + + override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Nothing?) = reportUnexpectedDescriptor(descriptor) + + override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, data: Nothing?) { + hashId = mangler.run { constructorDescriptor.hashedMangle } + collectFqNames(constructorDescriptor) + platformSpecificConstructor(constructorDescriptor) + } + + override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(scriptDescriptor) + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Nothing?) { + hashId = mangler.run { descriptor.hashedMangle } + collectFqNames(descriptor) + setExpected(descriptor.isExpect) + platformSpecificProperty(descriptor) + } + + override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(descriptor) + + override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: Nothing?) { + hashIdAcc = mangler.run { descriptor.hashedMangle } + descriptor.correspondingProperty.accept(this, null) + classFanSegments.add(descriptor.name.asString()) + setExpected(descriptor.isExpect) + platformSpecificGetter(descriptor) + } + + override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: Nothing?) { + hashIdAcc = mangler.run { descriptor.hashedMangle } + descriptor.correspondingProperty.accept(this, null) + classFanSegments.add(descriptor.name.asString()) + setExpected(descriptor.isExpect) + platformSpecificSetter(descriptor) + } + + override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: Nothing?) = + reportUnexpectedDescriptor(descriptor) + } + + private val composer by lazy { createSignatureBuilder() } + + override fun composeSignature(descriptor: DeclarationDescriptor): IdSignature? { + if (descriptor is WrappedDeclarationDescriptor<*>) return null + return if (mangler.run { descriptor.isExported() }) { + composer.buildSignature(descriptor) + } else null + } + + override fun composeEnumEntrySignature(descriptor: ClassDescriptor): IdSignature? { + return if (mangler.run { descriptor.isExportEnumEntry() }) { + composer.buildSignature(descriptor) + } else null + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureFlags.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureFlags.kt new file mode 100644 index 00000000000..281a4bf30c7 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureFlags.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.signature + +enum class IdSignatureFlags { + IS_EXPECT, + IS_JAVA_FOR_KOTLIN_OVERRIDE_PPROPERTY; + + fun encode(isSet: Boolean): Long = if (isSet) 1L shl ordinal else 0L + fun decode(flags: Long): Boolean = (flags and (1L shl ordinal) != 0L) +} diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureSerializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureSerializer.kt new file mode 100644 index 00000000000..44e4ba952a2 --- /dev/null +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/signature/IdSignatureSerializer.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.serialization.signature + +import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.IdSignature +import org.jetbrains.kotlin.ir.util.KotlinMangler +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.FqName + +open class IdSignatureSerializer(val mangler: KotlinMangler.IrMangler, startIndex: Long) { + private fun composeSignatureForDeclaration(declaration: IrDeclaration): IdSignature { + return if (mangler.run { declaration.isExported() }) { + composePublicIdSignature(declaration) + } else composeFileLocalIdSignature(declaration) + } + + private var localIndex: Long = startIndex + private var scopeIndex: Int = 0 + lateinit var table: DeclarationTable + + private inner class PublicIdSigBuilder : IdSignatureBuilder(), IrElementVisitorVoid { + + override fun accept(d: IrDeclaration) { + d.acceptVoid(this) + } + + private fun collectFqNames(declaration: IrDeclarationWithName) { + declaration.parent.acceptVoid(this) + classFanSegments.add(declaration.name.asString()) + } + + override fun visitElement(element: IrElement) = error("Unexpected element ${element.render()}") + + override fun visitPackageFragment(declaration: IrPackageFragment) { + packageFqn = declaration.fqName + } + + override fun visitClass(declaration: IrClass) { + collectFqNames(declaration) + setExpected(declaration.isExpect) + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction) { + val property = declaration.correspondingPropertySymbol + if (property != null) { + hashIdAcc = mangler.run { declaration.hashedMangle } + property.owner.acceptVoid(this) + classFanSegments.add(declaration.name.asString()) + } else { + hashId = mangler.run { declaration.hashedMangle } + collectFqNames(declaration) + } + setExpected(declaration.isExpect) + } + + override fun visitConstructor(declaration: IrConstructor) { + hashId = mangler.run { declaration.hashedMangle } + collectFqNames(declaration) + setExpected(declaration.isExpect) + } + + override fun visitProperty(declaration: IrProperty) { + hashId = mangler.run { declaration.hashedMangle } + collectFqNames(declaration) + setExpected(declaration.isExpect) + } + + override fun visitTypeAlias(declaration: IrTypeAlias) { + collectFqNames(declaration) + } + + override fun visitEnumEntry(declaration: IrEnumEntry) { + collectFqNames(declaration) + } + } + + private val publicSignatureBuilder = PublicIdSigBuilder() + + private fun composeContainerIdSignature(container: IrDeclarationParent): IdSignature { + if (container is IrPackageFragment) return IdSignature.PublicSignature(container.fqName, FqName.ROOT, null, 0) + if (container is IrDeclaration) return table.signatureByDeclaration(container) + error("Unexpected container ${container.render()}") + } + + fun composePublicIdSignature(declaration: IrDeclaration): IdSignature { + assert(mangler.run { declaration.isExported() }) + + return publicSignatureBuilder.buildSignature(declaration) + } + + fun composeFileLocalIdSignature(declaration: IrDeclaration): IdSignature { + assert(!mangler.run { declaration.isExported() }) + + return table.privateDeclarationSignature(declaration) { + when (declaration) { + is IrValueDeclaration -> IdSignature.ScopeLocalDeclaration(scopeIndex++, declaration.name.asString()) + is IrField -> { + val p = declaration.correspondingPropertySymbol?.let { composeSignatureForDeclaration(it.owner) } + ?: composeContainerIdSignature(declaration.parent) + IdSignature.FileLocalSignature(p, ++localIndex) + } + is IrSimpleFunction -> { + val p = declaration.correspondingPropertySymbol?.let { composeSignatureForDeclaration(it.owner) } + ?: composeContainerIdSignature(declaration.parent) + IdSignature.FileLocalSignature(p, ++localIndex) + } + else -> IdSignature.FileLocalSignature(composeContainerIdSignature(declaration.parent), ++localIndex) + } + } + } +} \ No newline at end of file diff --git a/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/JvmIdSignatureDescriptor.kt b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/JvmIdSignatureDescriptor.kt new file mode 100644 index 00000000000..a133a6659f2 --- /dev/null +++ b/compiler/ir/serialization.jvm/src/org/jetbrains/kotlin/backend/jvm/serialization/JvmIdSignatureDescriptor.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.serialization + +import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.ir.util.KotlinMangler +import org.jetbrains.kotlin.load.java.descriptors.JavaForKotlinOverridePropertyDescriptor + +class JvmIdSignatureDescriptor(private val mangler: KotlinMangler.DescriptorMangler) : IdSignatureDescriptor(mangler) { + + private class JvmDescriptorBasedSignatureBuilder(mangler: KotlinMangler.DescriptorMangler) : DescriptorBasedSignatureBuilder(mangler) { + override fun platformSpecificProperty(descriptor: PropertyDescriptor) { + // See KT-31646 + setSpecialJavaProperty(descriptor is JavaForKotlinOverridePropertyDescriptor) + } + } + + override fun createSignatureBuilder(): DescriptorBasedSignatureBuilder = JvmDescriptorBasedSignatureBuilder(mangler) +} \ No newline at end of file