[IR] Implement IdSignature API

- Basic implementation of IdSignature classes and interafces
 - Abstract and platform Ir/Descriptor based signature builders
This commit is contained in:
Roman Artemev
2020-01-27 15:31:45 +03:00
committed by romanart
parent 45f036548b
commit bb04eae93e
6 changed files with 500 additions and 0 deletions
@@ -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 = "<no description>") : 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
}
@@ -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<D> {
protected var packageFqn: FqName = FqName.ROOT
protected val classFanSegments = mutableListOf<String>()
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()
}
}
@@ -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<DeclarationDescriptor>(),
DeclarationDescriptorVisitor<Unit, Nothing?> {
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
}
}
@@ -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)
}
@@ -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<IrDeclaration>(), 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)
}
}
}
}
@@ -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)
}