[IR] Refactored Kotlin Mangler API

- Implement abstract Ir-based, Descriptor-based
 mangler computers and export checkers and corresponding platform
 specializations
 - Add runtime verifier which check whether all types of mangler
 produces the same type of data

Note: code migration is not finished yet
This commit is contained in:
Roman Artemev
2020-01-27 15:24:27 +03:00
committed by romanart
parent ba25b0faaf
commit 0254734bb5
18 changed files with 1075 additions and 408 deletions
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerForBE
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.types.isUnit
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
fun <T> mapToKey(declaration: T): String {
return with(JsManglerForBE) {
return with(JsManglerIr) {
if (declaration is IrDeclaration && isPublic(declaration)) {
declaration.hashedMangle.toString()
} else if (declaration is Signature) {
@@ -32,7 +32,7 @@ fun <T> mapToKey(declaration: T): String {
}
}
fun JsManglerForBE.isPublic(declaration: IrDeclaration) =
fun JsManglerIr.isPublic(declaration: IrDeclaration) =
declaration.isExported() && declaration !is IrScript && declaration !is IrVariable && declaration !is IrValueParameter
class NameTable<T>(
@@ -5,22 +5,35 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.name.Name
interface KotlinMangler {
interface KotlinMangler<D : Any> {
val String.hashMangle: Long
val IrDeclaration.hashedMangle: Long
fun IrDeclaration.isExported(): Boolean
val IrFunction.functionName: String
val IrType.isInlined: Boolean
val Long.isSpecial: Boolean
companion object {
private val FUNCTION_PREFIX = "<BUILT-IN-FUNCTION>"
fun functionClassSymbolName(name: Name) = "ktype:$FUNCTION_PREFIX$name"
fun functionInvokeSymbolName(name: Name) = "kfun:$FUNCTION_PREFIX$name.invoke"
fun D.isExported(): Boolean
val D.mangleString: String
val D.hashedMangle: Long get() = mangleString.hashMangle
val manglerName: String
interface DescriptorMangler : KotlinMangler<DeclarationDescriptor> {
override val manglerName: String
get() = "Descriptor"
fun ClassDescriptor.isExportEnumEntry(): Boolean
fun ClassDescriptor.mangleEnumEntryString(): String
fun PropertyDescriptor.isExportField(): Boolean
fun PropertyDescriptor.mangleFieldString(): String
}
interface IrMangler : KotlinMangler<IrDeclaration> {
override val manglerName: String
get() = "Ir"
}
}
@@ -1,362 +0,0 @@
/*
* Copyright 2010-2019 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
import org.jetbrains.kotlin.backend.common.ir.isExpect
import org.jetbrains.kotlin.backend.common.ir.isProperExpect
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
private const val PUBLIC_MANGLE_FLAG = 1L shl 63
abstract class KotlinManglerImpl : KotlinMangler {
override val String.hashMangle get() = (this.cityHash64() % PUBLIC_MANGLE_FLAG) or PUBLIC_MANGLE_FLAG
private fun hashedMangleImpl(declaration: IrDeclaration): Long {
return declaration.uniqSymbolName().hashMangle
}
override val IrDeclaration.hashedMangle: Long
get() = hashedMangleImpl(this)
// We can't call "with (super) { this.isExported() }" in children.
// So provide a hook.
protected open fun IrDeclaration.isPlatformSpecificExported(): Boolean = false
override fun IrDeclaration.isExported(): Boolean = isExportedImpl(this)
/**
* Defines whether the declaration is exported, i.e. visible from other modules.
*
* Exported declarations must have predictable and stable ABI
* that doesn't depend on any internal transformations (e.g. IR lowering),
* and so should be computable from the descriptor itself without checking a backend state.
*/
private tailrec fun isExportedImpl(declaration: IrDeclaration): Boolean {
// TODO: revise
val descriptorAnnotations = declaration.descriptor.annotations
if (declaration.isPlatformSpecificExported()) return true
if (declaration is IrTypeAlias && declaration.parent is IrPackageFragment) {
return true
}
if (descriptorAnnotations.hasAnnotation(publishedApiAnnotation)) {
return true
}
if (declaration.isAnonymousObject)
return false
if (declaration is IrConstructor && declaration.constructedClass.kind.isSingleton) {
// Currently code generator can access the constructor of the singleton,
// so ignore visibility of the constructor itself.
return isExportedImpl(declaration.constructedClass)
}
if (declaration is IrFunction) {
val descriptor = declaration.descriptor
// TODO: this code is required because accessor doesn't have a reference to property.
if (descriptor is PropertyAccessorDescriptor) {
val property = descriptor.correspondingProperty
if (property.annotations.hasAnnotation(publishedApiAnnotation)) return true
}
}
val visibility = when (declaration) {
is IrClass -> declaration.visibility
is IrFunction -> declaration.visibility
is IrProperty -> declaration.visibility
is IrField -> declaration.visibility
is IrTypeAlias -> declaration.visibility
else -> null
}
/**
* note: about INTERNAL - with support of friend modules we let frontend to deal with internal declarations.
*/
if (visibility != null && !visibility.isPublicAPI && visibility != Visibilities.INTERNAL) {
// If the declaration is explicitly marked as non-public,
// then it must not be accessible from other modules.
return false
}
val parent = declaration.parent
if (parent is IrDeclaration) {
return isExportedImpl(parent)
}
return true
}
private fun IrTypeParameter.effectiveParent(): IrDeclaration = when (val irParent = parent) {
is IrClass -> irParent
is IrConstructor -> irParent
is IrSimpleFunction -> irParent.correspondingPropertySymbol?.owner ?: irParent
else -> error("Unexpected type parameter container")
}
private fun collectTypeParameterContainers(element: IrElement): List<IrDeclaration> {
val result = mutableListOf<IrDeclaration>()
tailrec fun collectTypeParameterContainersImpl(element: IrElement) {
when (element) {
is IrConstructor -> result += element
is IrSimpleFunction -> result.add(element.correspondingPropertySymbol?.owner ?: element)
is IrProperty -> result += element
is IrClass -> result += element
else -> return
}
collectTypeParameterContainersImpl((element as IrDeclaration).parent)
}
collectTypeParameterContainersImpl(element)
return result
}
private fun mapTypeParameterContainers(element: IrElement): Map<IrDeclaration, Int> {
return collectTypeParameterContainers(element).mapIndexed { i, d -> d to i }.toMap()
}
private val publishedApiAnnotation = FqName("kotlin.PublishedApi")
protected open fun mangleTypeParameter(typeParameter: IrTypeParameter, typeParameterNamer: (IrTypeParameter) -> String): String {
return typeParameterNamer(typeParameter)
}
protected fun acyclicTypeMangler(type: IrType, typeParameterNamer: (IrTypeParameter) -> String): String {
var hashString = type.classifierOrNull?.let {
when (it) {
is IrClassSymbol -> it.owner.fqNameForIrSerialization.asString()
is IrTypeParameterSymbol -> mangleTypeParameter(it.owner, typeParameterNamer)
else -> error("Unexpected type constructor")
}
} ?: "<dynamic>"
when (type) {
is IrSimpleType -> {
if (!type.arguments.isEmpty()) {
hashString += "<${type.arguments.map {
when (it) {
is IrStarProjection -> "#STAR"
is IrTypeProjection -> {
val variance = it.variance.label
val projection = if (variance == "") "" else "${variance}_"
projection + acyclicTypeMangler(it.type, typeParameterNamer)
}
else -> error(it)
}
}.joinToString(",")}>"
}
if (type.hasQuestionMark) hashString += "?"
}
!is IrDynamicType -> {
error(type)
}
}
return hashString
}
protected fun typeToHashString(type: IrType, typeParameterNamer: (IrTypeParameter) -> String) =
acyclicTypeMangler(type, typeParameterNamer)
fun IrValueParameter.extensionReceiverNamePart(typeParameterNamer: (IrTypeParameter) -> String): String =
"@${typeToHashString(this.type, typeParameterNamer)}."
open fun IrFunction.valueParamsPart(typeParameterNamer: (IrTypeParameter) -> String): String {
return this.valueParameters.map {
"${typeToHashString(it.type, typeParameterNamer)}${if (it.isVararg) "_VarArg" else ""}"
}.joinToString(";")
}
open fun IrFunction.typeParamsPart(typeParameters: List<IrTypeParameter>, typeParameterNamer: (IrTypeParameter) -> String): String {
if (typeParameters.isEmpty()) return ""
fun mangleTypeParameter(index: Int, typeParameter: IrTypeParameter): String {
// We use type parameter index instead of name since changing name is not a binary-incompatible change
return typeParameter.superTypes.joinToString("&", "$index<", ">") {
acyclicTypeMangler(it, typeParameterNamer)
}
}
return typeParameters.withIndex().joinToString(";", "{", "}") { (i, tp) ->
mangleTypeParameter(i, tp)
}
}
open fun IrFunction.signature(typeParameterNamer: (IrTypeParameter) -> String): String {
val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart(typeParameterNamer) ?: ""
val valueParamsPart = this.valueParamsPart(typeParameterNamer)
// Distinguish value types and references - it's needed for calling virtual methods through bridges.
// Also is function has type arguments - frontend allows exactly matching overrides.
val signatureSuffix =
when {
this.typeParameters.isNotEmpty() -> "Generic"
!returnType.isUnitOrNullableUnit() -> typeToHashString(returnType, typeParameterNamer)
else -> ""
}
val typesParamsPart = this.typeParamsPart(typeParameters, typeParameterNamer)
return "$extensionReceiverPart($valueParamsPart)$typesParamsPart$signatureSuffix"
}
open val IrFunction.platformSpecificFunctionName: String? get() = null
// TODO: rename to indicate that it has signature included
override val IrFunction.functionName: String
get() {
// TODO: Again. We can't call super in children, so provide a hook for now.
this.platformSpecificFunctionName?.let { return it }
val typeContainerMap = mapTypeParameterContainers(this)
val typeParameterNamer: (IrTypeParameter) -> String = {
val eParent = it.effectiveParent()
"${typeContainerMap[eParent] ?: error("No parent for ${it.render()}")}:${it.index}"
}
val name = this.name.mangleIfInternal(this.module, this.visibility)
return "$name${signature(typeParameterNamer)}"
}
override val Long.isSpecial: Boolean
get() = specialHashes.contains(this)
fun Name.mangleIfInternal(moduleDescriptor: ModuleDescriptor, visibility: Visibility): String =
if (visibility != Visibilities.INTERNAL) {
this.asString()
} else {
val moduleName = moduleDescriptor.name.asString()
.let { it.substring(1, it.lastIndex) } // Remove < and >.
"$this\$$moduleName"
}
val IrField.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it."
}
return "kfield:$containingDeclarationPart$name"
}
val IrClass.typeInfoSymbolName: String
get() {
assert(isExportedImpl(this))
if (isBuiltInFunction(this))
return KotlinMangler.functionClassSymbolName(name)
return "ktype:" + this.fqNameForIrSerialization.toString()
}
val IrTypeParameter.symbolName: String
get() {
val parentDeclaration = (parent as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: parent
val containingDeclarationPart = when (parentDeclaration) {
is IrDeclaration -> parentDeclaration.uniqSymbolName()
else -> error("Unexpected type parameter parent")
}
return "ktypeparam:$containingDeclarationPart$name@$index"
}
val IrTypeAlias.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it."
}
return "ktypealias:$containingDeclarationPart$name"
}
// This is a little extension over what's used in real mangling
// since some declarations never appear in the bitcode symbols.
internal fun IrDeclaration.uniqSymbolName(): String = when (this) {
is IrFunction -> this.uniqFunctionName
is IrProperty -> this.symbolName
is IrClass -> this.typeInfoSymbolName
is IrField -> this.symbolName
is IrEnumEntry -> this.symbolName
is IrTypeParameter -> this.symbolName
is IrTypeAlias -> this.symbolName
else -> error("Unexpected exported declaration: $this")
} + expectPart
// TODO: need to figure out the proper OptionalExpectation behavior
private val IrDeclaration.expectPart get() = if (this.isProperExpect) "#expect" else ""
private val IrDeclarationParent.fqNameUnique: FqName
get() = when (this) {
is IrPackageFragment -> this.fqName
is IrDeclaration -> this.parent.fqNameUnique.child(this.uniqName)
else -> error(this)
}
private val IrDeclaration.uniqName: Name
get() = when (this) {
is IrSimpleFunction -> Name.special("<${this.uniqFunctionName}>")
else -> this.nameForIrSerialization
}
private val IrProperty.symbolName: String
get() {
val typeContainerMap = mapTypeParameterContainers(this)
val typeParameterNamer: (IrTypeParameter) -> String = {
val eParent = it.effectiveParent()
"${typeContainerMap[eParent] ?: error("No parent for ${it.render()}")}:${it.index}"
}
val extensionReceiver: String = getter?.extensionReceiverParameter?.extensionReceiverNamePart(typeParameterNamer) ?: ""
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it."
}
return "kprop:$containingDeclarationPart$extensionReceiver$name"
}
private val IrEnumEntry.symbolName: String
get() {
val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it."
}
return "kenumentry:$containingDeclarationPart$name"
}
// This is basicly the same as .symbolName, but disambiguates external functions with the same C name.
// In addition functions appearing in fq sequence appear as <full signature>.
private val IrFunction.uniqFunctionName: String
get() {
if (isBuiltInFunction(this))
return KotlinMangler.functionInvokeSymbolName(parentAsClass.name)
val parent = this.parent
val containingDeclarationPart = parent.fqNameUnique.let {
if (it.isRoot) "" else "$it."
}
return "kfun:$containingDeclarationPart#$functionName"
}
private val specialHashes = listOf("Function", "KFunction", "SuspendFunction", "KSuspendFunction")
.flatMap { name ->
(0..255).map { KotlinMangler.functionClassSymbolName(Name.identifier(name + it)) }
}.map { it.hashMangle }
.toSet()
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2019 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.mangle
import org.jetbrains.kotlin.backend.common.serialization.cityHash64
import org.jetbrains.kotlin.ir.util.KotlinMangler
abstract class AbstractKotlinMangler<D : Any> : KotlinMangler<D> {
override val String.hashMangle get() = cityHash64()
protected abstract fun getExportChecker(): KotlinExportChecker<D>
protected abstract fun getMangleComputer(prefix: String): KotlinMangleComputer<D>
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2019 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.mangle
interface KotlinExportChecker<D : Any> {
fun check(declaration: D, type: SpecialDeclarationType): Boolean
fun D.isPlatformSpecificExported(): Boolean
}
@@ -3,10 +3,8 @@
* 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.backend.js.lower.serialization.metadata
package org.jetbrains.kotlin.backend.common.serialization.mangle
class JsKlibMetadataModuleDescriptor<out T>(
val name: String,
val imported: List<String>,
val data: T
)
interface KotlinMangleComputer<D : Any> {
fun computeMangle(declaration: D): String
}
@@ -0,0 +1,46 @@
/*
* 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.mangle
enum class MangleConstant(val prefix: Char, val separator: Char, val suffix: Char) {
VALUE_PARAMETERS('(', ';', ')'),
TYPE_PARAMETERS('{', ';', '}'),
UPPER_BOUNDS('<', '&', '>'),
TYPE_ARGUMENTS('<', ',', '>'),
FLEXIBLE_TYPE('[', '~', ']');
companion object {
const val VAR_ARG_MARK = "..."
const val STAR_MARK = '*'
const val Q_MARK = '?'
const val EXPECT_MARK = "#expect"
const val UNKNOWN_MARK = "<unknown>"
const val DYNAMIC_MARK = "<dynamic>"
const val VARIANCE_SEPARATOR = '|'
const val UPPER_BOUND_SEPARATOR = '§'
const val PREFIX_SEPARATOR = ':'
const val MODULE_SEPARATOR = '$'
const val FQN_SEPARATOR = '.'
const val INDEX_SEPARATOR = ':'
const val PLATFORM_FUNCTION_MARKER = '%'
const val EXTENSION_RECEIVER_PREFIX = '@'
const val FUNCTION_NAME_PREFIX = '#'
const val TYPE_PARAM_INDEX_PREFIX = '@'
const val EMPTY_PREFIX = ""
const val FUN_PREFIX = "kfun"
const val CLASS_PREFIX = "kclass"
const val PROPERTY_PREFIX = "kprop"
const val FIELD_PREFIX = "kfield"
const val ENUM_ENTRY_PREFIX = "kenumentry"
const val TYPE_ALIAS_PREFIX = "ktypealias"
const val TYPE_PARAM_PREFIX = "ktypeparam"
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2019 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.mangle
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
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.acceptChildrenVoid
class ManglerChecker(vararg _manglers: KotlinMangler<IrDeclaration>) : IrElementVisitorVoid {
private val manglers = _manglers.toList()
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun KotlinMangler<IrDeclaration>.isExportCheck(declaration: IrDeclaration) = declaration.isExported()
private fun KotlinMangler<IrDeclaration>.stringMangle(declaration: IrDeclaration) = declaration.mangleString
private fun <T : Any, R> Iterable<T>.checkAllEqual(init: R, op: T.() -> R, onError: (T, R, T, R) -> Unit): R {
var prev: T? = null
var r = init
for (it in this) {
if (prev == null) {
r = it.op()
prev = it
} else {
val tmp = it.op()
if (r != tmp) {
onError(prev, r, it, tmp)
}
prev = it
r = tmp
}
}
return r
}
override fun visitDeclaration(declaration: IrDeclaration) {
val exported = manglers.checkAllEqual(false, { isExportCheck(declaration) }) { m1, r1, m2, r2 ->
println("${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n")
error("${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n")
}
if (!exported) return
manglers.checkAllEqual("", { stringMangle(declaration) }) { m1, r1, m2, r2 ->
println("${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n")
error("${declaration.render()}\n ${m1.manglerName}: $r1\n ${m2.manglerName}: $r2\n")
}
declaration.acceptChildrenVoid(this)
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2019 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.mangle
enum class SpecialDeclarationType {
REGULAR,
ANON_INIT,
BACKING_FIELD,
ENUM_ENTRY;
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2019 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.mangle.descriptor
import org.jetbrains.kotlin.backend.common.serialization.mangle.AbstractKotlinMangler
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.ir.declarations.IrAnonymousInitializer
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.util.KotlinMangler
abstract class DescriptorBasedKotlinManglerImpl : AbstractKotlinMangler<DeclarationDescriptor>(), KotlinMangler.DescriptorMangler {
private fun withPrefix(prefix: String, descriptor: DeclarationDescriptor): String =
getMangleComputer(prefix).computeMangle(descriptor)
override fun ClassDescriptor.mangleEnumEntryString(): String = withPrefix(MangleConstant.ENUM_ENTRY_PREFIX, this)
override fun ClassDescriptor.isExportEnumEntry(): Boolean =
getExportChecker().check(this, SpecialDeclarationType.ENUM_ENTRY)
override fun PropertyDescriptor.isExportField(): Boolean = false
override fun PropertyDescriptor.mangleFieldString(): String = error("Fields supposed to be non-exporting")
override val DeclarationDescriptor.mangleString: String
get() = withPrefix(MangleConstant.EMPTY_PREFIX, this)
override fun DeclarationDescriptor.isExported(): Boolean = getExportChecker().check(this, SpecialDeclarationType.REGULAR)
}
class Ir2DescriptorManglerAdapter(private val delegate: DescriptorBasedKotlinManglerImpl) : AbstractKotlinMangler<IrDeclaration>(),
KotlinMangler.IrMangler {
override val manglerName: String
get() = delegate.manglerName
override fun IrDeclaration.isExported(): Boolean {
return when (this) {
is IrAnonymousInitializer -> false
is IrEnumEntry -> delegate.run { descriptor.isExportEnumEntry() }
is IrField -> delegate.run { descriptor.isExportField() }
else -> delegate.run { descriptor.isExported() }
}
}
override val IrDeclaration.mangleString: String
get() {
return when (this) {
is IrEnumEntry -> delegate.run { descriptor.mangleEnumEntryString() }
is IrField -> delegate.run { descriptor.mangleFieldString() }
else -> delegate.run { descriptor.mangleString }
}
}
override fun getExportChecker() = error("Should not have been reached")
override fun getMangleComputer(prefix: String): KotlinMangleComputer<IrDeclaration> = error("Should not have been reached")
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2019 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.mangle.descriptor
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.backend.common.serialization.mangle.publishedApiAnnotation
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.name.SpecialNames
abstract class DescriptorExportCheckerVisitor : DeclarationDescriptorVisitor<Boolean, SpecialDeclarationType>,
KotlinExportChecker<DeclarationDescriptor> {
override fun check(declaration: DeclarationDescriptor, type: SpecialDeclarationType): Boolean {
return declaration.accept(this, type)
}
private fun reportUnexpectedDescriptor(descriptor: DeclarationDescriptor): Nothing {
error("unexpected descriptor $descriptor")
}
private fun Visibility.isPubliclyVisible(): Boolean = isPublicAPI || this === Visibilities.INTERNAL
private fun DeclarationDescriptorNonRoot.isExported(annotations: Annotations, visibility: Visibility?): Boolean {
if (annotations.hasAnnotation(publishedApiAnnotation)) return true
if (visibility != null && !visibility.isPubliclyVisible()) return false
return containingDeclaration.accept(this@DescriptorExportCheckerVisitor, SpecialDeclarationType.REGULAR) ?: false
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: SpecialDeclarationType) = true
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: SpecialDeclarationType) = true
override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: SpecialDeclarationType) = false
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: SpecialDeclarationType): Boolean {
return descriptor.run { isExported(annotations, visibility) }
}
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: SpecialDeclarationType): Boolean = false
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: SpecialDeclarationType): Boolean {
if (data == SpecialDeclarationType.ANON_INIT) return false
if (descriptor.name == SpecialNames.NO_NAME_PROVIDED) return false
if (descriptor.kind == ClassKind.ENUM_ENTRY && data == SpecialDeclarationType.REGULAR) return false
return descriptor.run { isExported(annotations, visibility) }
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: SpecialDeclarationType) =
if (descriptor.containingDeclaration is PackageFragmentDescriptor) true
else descriptor.run { isExported(annotations, visibility) }
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: SpecialDeclarationType): Boolean {
reportUnexpectedDescriptor(descriptor)
}
override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, data: SpecialDeclarationType): Boolean {
val klass = constructorDescriptor.constructedClass
return if (klass.kind.isSingleton)
klass.accept(this, SpecialDeclarationType.REGULAR)
else constructorDescriptor.run { isExported(annotations, visibility) }
}
override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: SpecialDeclarationType): Boolean {
reportUnexpectedDescriptor(scriptDescriptor)
}
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: SpecialDeclarationType): Boolean {
val visibility = if (data == SpecialDeclarationType.BACKING_FIELD) {
return false
} else descriptor.visibility
return descriptor.run { isExported(annotations, visibility) }
}
override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: SpecialDeclarationType): Boolean {
return false
}
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: SpecialDeclarationType): Boolean {
return descriptor.run { isExported(correspondingProperty.annotations, visibility) }
}
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: SpecialDeclarationType): Boolean {
return descriptor.run { isExported(correspondingProperty.annotations, visibility) }
}
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: SpecialDeclarationType) = false
}
@@ -0,0 +1,255 @@
/*
* Copyright 2010-2019 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.mangle.descriptor
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant
import org.jetbrains.kotlin.backend.common.serialization.mangle.collect
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
abstract class DescriptorMangleComputer(protected val builder: StringBuilder, protected val specialPrefix: String) :
DeclarationDescriptorVisitor<Unit, Boolean>, KotlinMangleComputer<DeclarationDescriptor> {
override fun computeMangle(declaration: DeclarationDescriptor): String {
declaration.accept(this, true)
return builder.toString()
}
protected abstract fun copy(): DescriptorMangleComputer
private val typeParameterContainer = ArrayList<DeclarationDescriptor>(4)
private var isRealExpect = false
private fun addPrefix(prefix: String, addPrefix: Boolean): Int {
if (addPrefix) {
builder.append(prefix)
builder.append(MangleConstant.PREFIX_SEPARATOR)
}
return builder.length
}
private fun DeclarationDescriptor.mangleSimpleDeclaration(prefix: String, addPrefix: Boolean, name: String) {
val prefixLength = addPrefix(prefix, addPrefix)
containingDeclaration?.accept(this@DescriptorMangleComputer, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
builder.append(name)
}
open val FunctionDescriptor.platformSpecificFunctionName: String? get() = null
private fun reportUnexpectedDescriptor(descriptor: DeclarationDescriptor) {
error("unexpected descriptor $descriptor")
}
open fun FunctionDescriptor.platformSpecificSuffix(): String? = null
private fun FunctionDescriptor.mangleFunction(isCtor: Boolean, prefix: Boolean, container: CallableDescriptor) {
isRealExpect = isRealExpect or isExpect
val prefixLength = addPrefix(MangleConstant.FUN_PREFIX, prefix)
typeParameterContainer.add(container)
container.containingDeclaration.accept(this@DescriptorMangleComputer, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
builder.append(MangleConstant.FUNCTION_NAME_PREFIX)
if (visibility != Visibilities.INTERNAL) builder.append(name)
else {
builder.append(name)
builder.append(MangleConstant.MODULE_SEPARATOR)
builder.append(module.name.asString().run { substring(1, lastIndex) })
}
mangleSignature(isCtor, container)
platformSpecificSuffix()?.let {
builder.append(MangleConstant.PLATFORM_FUNCTION_MARKER)
builder.append(it)
}
if (prefix && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
private fun FunctionDescriptor.mangleSignature(isCtor: Boolean, realTypeParameterContainer: CallableDescriptor) {
extensionReceiverParameter?.let {
builder.append(MangleConstant.EXTENSION_RECEIVER_PREFIX)
mangleExtensionReceiverParameter(builder, it)
}
valueParameters.collect(builder, MangleConstant.VALUE_PARAMETERS) { mangleValueParameter(this, it) }
realTypeParameterContainer.typeParameters.filter { it.containingDeclaration == realTypeParameterContainer }
.collect(builder, MangleConstant.TYPE_PARAMETERS) { mangleTypeParameter(this, it) }
returnType?.run {
if (!isCtor && !isUnit()) {
mangleType(builder, this)
}
}
}
private fun mangleExtensionReceiverParameter(vpBuilder: StringBuilder, param: ReceiverParameterDescriptor) {
mangleType(vpBuilder, param.type)
}
private fun mangleValueParameter(vpBuilder: StringBuilder, param: ValueParameterDescriptor) {
mangleType(vpBuilder, param.type)
if (param.varargElementType != null) vpBuilder.append(MangleConstant.VAR_ARG_MARK)
}
private fun mangleTypeParameter(tpBuilder: StringBuilder, param: TypeParameterDescriptor) {
tpBuilder.append(param.index)
tpBuilder.append(MangleConstant.UPPER_BOUND_SEPARATOR)
param.upperBounds.collect(tpBuilder, MangleConstant.UPPER_BOUNDS) { mangleType(this, it) }
}
private fun mangleType(tBuilder: StringBuilder, wtype: KotlinType) {
when (val type = wtype.unwrap()) {
is SimpleType -> {
when (val classifier = type.constructor.declarationDescriptor) {
is ClassDescriptor -> classifier.accept(copy(), false)
is TypeParameterDescriptor -> tBuilder.mangleTypeParameterReference(classifier)
else -> error("Unexpected classifier: $classifier")
}
type.arguments.ifNotEmpty {
collect(tBuilder, MangleConstant.TYPE_ARGUMENTS) { arg ->
if (arg.isStarProjection) {
append(MangleConstant.STAR_MARK)
} else {
if (arg.projectionKind != Variance.INVARIANT) {
append(arg.projectionKind.label)
append(MangleConstant.VARIANCE_SEPARATOR)
}
mangleType(this, arg.type)
}
}
}
if (type.isMarkedNullable) tBuilder.append(MangleConstant.Q_MARK)
}
is DynamicType -> tBuilder.append(MangleConstant.DYNAMIC_MARK)
is FlexibleType -> {
// TODO: is that correct way to mangle flexible type?
with(MangleConstant.FLEXIBLE_TYPE) {
tBuilder.append(prefix)
mangleType(tBuilder, type.lowerBound)
tBuilder.append(separator)
mangleType(tBuilder, type.upperBound)
tBuilder.append(suffix)
}
}
else -> error("Unexpected type $wtype")
}
}
private fun StringBuilder.mangleTypeParameterReference(typeParameter: TypeParameterDescriptor) {
val parent = typeParameter.containingDeclaration
val ci = typeParameterContainer.indexOf(parent)
// TODO: what should we do in this case?
// require(ci >= 0) { "No type container found for ${typeParameter.render()}" }
append(ci)
append(MangleConstant.INDEX_SEPARATOR)
append(typeParameter.index)
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Boolean) {
descriptor.fqName.let { if (!it.isRoot) builder.append(it.asString()) }
}
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: Boolean) = reportUnexpectedDescriptor(descriptor)
override fun visitVariableDescriptor(descriptor: VariableDescriptor, data: Boolean) = reportUnexpectedDescriptor(descriptor)
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Boolean) {
descriptor.platformSpecificFunctionName?.let {
builder.append(it)
return
}
descriptor.mangleFunction(false, data, descriptor)
}
override fun visitTypeParameterDescriptor(descriptor: TypeParameterDescriptor, data: Boolean) {
addPrefix(MangleConstant.TYPE_PARAM_PREFIX, data)
descriptor.containingDeclaration.accept(this, data)
builder.append(MangleConstant.TYPE_PARAM_INDEX_PREFIX)
builder.append(descriptor.index)
}
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Boolean) {
isRealExpect = isRealExpect or descriptor.isExpect
typeParameterContainer.add(descriptor)
val prefix = if (specialPrefix == MangleConstant.ENUM_ENTRY_PREFIX) specialPrefix else MangleConstant.CLASS_PREFIX
descriptor.mangleSimpleDeclaration(prefix, data, descriptor.name.asString())
if (data && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Boolean) {
descriptor.mangleSimpleDeclaration(MangleConstant.TYPE_ALIAS_PREFIX, data, descriptor.name.asString())
}
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Boolean) = reportUnexpectedDescriptor(descriptor)
override fun visitConstructorDescriptor(constructorDescriptor: ConstructorDescriptor, data: Boolean) {
constructorDescriptor.mangleFunction(true, data, constructorDescriptor)
}
override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: Boolean) = reportUnexpectedDescriptor(scriptDescriptor)
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Boolean) {
val extensionReceiver = descriptor.extensionReceiverParameter
val prefix = if (specialPrefix == MangleConstant.FIELD_PREFIX) specialPrefix else MangleConstant.PROPERTY_PREFIX
val prefixLength = addPrefix(prefix, data)
isRealExpect = isRealExpect or descriptor.isExpect
typeParameterContainer.add(descriptor)
descriptor.containingDeclaration.accept(this, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
if (extensionReceiver != null) {
builder.append(MangleConstant.EXTENSION_RECEIVER_PREFIX)
mangleExtensionReceiverParameter(builder, extensionReceiver)
}
builder.append(descriptor.name)
if (data && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
override fun visitValueParameterDescriptor(descriptor: ValueParameterDescriptor, data: Boolean) = reportUnexpectedDescriptor(descriptor)
private fun manglePropertyAccessor(accessor: PropertyAccessorDescriptor, data: Boolean) {
accessor.mangleFunction(false, data, accessor.correspondingProperty)
}
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: Boolean) {
manglePropertyAccessor(descriptor, data)
}
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: Boolean) {
manglePropertyAccessor(descriptor, data)
}
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: Boolean) =
reportUnexpectedDescriptor(descriptor)
}
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2019 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.mangle.ir
import org.jetbrains.kotlin.backend.common.serialization.mangle.AbstractKotlinMangler
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.util.KotlinMangler
abstract class IrBasedKotlinManglerImpl : AbstractKotlinMangler<IrDeclaration>(), KotlinMangler.IrMangler {
override val IrDeclaration.mangleString: String
get() = getMangleComputer("").computeMangle(this)
override fun IrDeclaration.isExported(): Boolean = getExportChecker().check(this, SpecialDeclarationType.REGULAR)
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2019 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.mangle.ir
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.backend.common.serialization.mangle.publishedApiAnnotation
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.SpecialNames
abstract class IrExportCheckerVisitor : IrElementVisitor<Boolean, Nothing?>, KotlinExportChecker<IrDeclaration> {
override fun check(declaration: IrDeclaration, type: SpecialDeclarationType): Boolean {
return declaration.accept(this, null)
}
abstract override fun IrDeclaration.isPlatformSpecificExported(): Boolean
private fun IrDeclaration.isExported(annotations: List<IrConstructorCall>, visibility: Visibility?): Boolean {
if (annotations.hasAnnotation(publishedApiAnnotation)) return true
if (visibility != null && !visibility.isPubliclyVisible()) return false
return parent.accept(this@IrExportCheckerVisitor, null)
}
private fun Visibility.isPubliclyVisible(): Boolean = isPublicAPI || this === Visibilities.INTERNAL
override fun visitElement(element: IrElement, data: Nothing?): Boolean = error("Should bot reach here ${element.render()}")
override fun visitDeclaration(declaration: IrDeclaration, data: Nothing?) = declaration.run { isExported(annotations, null) }
override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer, data: Nothing?) = false
override fun visitValueParameter(declaration: IrValueParameter, data: Nothing?) = false
override fun visitVariable(declaration: IrVariable, data: Nothing?) = false
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty, data: Nothing?) = false
override fun visitTypeParameter(declaration: IrTypeParameter, data: Nothing?) = false
override fun visitField(declaration: IrField, data: Nothing?) = false
override fun visitProperty(declaration: IrProperty, data: Nothing?): Boolean {
return declaration.run { isExported(annotations, visibility) }
}
override fun visitPackageFragment(declaration: IrPackageFragment, data: Nothing?): Boolean = true
override fun visitTypeAlias(declaration: IrTypeAlias, data: Nothing?): Boolean =
if (declaration.parent is IrPackageFragment) true
else declaration.run { isExported(annotations, visibility) }
override fun visitClass(declaration: IrClass, data: Nothing?): Boolean {
if (declaration.name == SpecialNames.NO_NAME_PROVIDED) return false
return declaration.run { isExported(annotations, visibility) }
}
override fun visitConstructor(declaration: IrConstructor, data: Nothing?): Boolean {
val klass = declaration.constructedClass
return if (klass.kind.isSingleton) klass.accept(this, null) else declaration.run { isExported(annotations, visibility) }
}
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Nothing?): Boolean {
val annotations = declaration.run { correspondingPropertySymbol?.owner?.annotations ?: annotations }
return declaration.run { isExported(annotations, visibility) }
}
}
@@ -0,0 +1,230 @@
/*
* Copyright 2010-2019 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.mangle.ir
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant
import org.jetbrains.kotlin.backend.common.serialization.mangle.collect
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isVararg
import org.jetbrains.kotlin.ir.util.module
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
abstract class IrMangleComputer(protected val builder: StringBuilder) : IrElementVisitor<Unit, Boolean>,
KotlinMangleComputer<IrDeclaration> {
private val typeParameterContainer = ArrayList<IrDeclaration>(4)
private var isRealExpect = false
open val IrFunction.platformSpecificFunctionName: String? get() = null
protected abstract fun copy(): IrMangleComputer
override fun computeMangle(declaration: IrDeclaration): String {
declaration.accept(this, true)
return builder.toString()
}
private fun addPrefix(prefix: String, addPrefix: Boolean): Int {
if (addPrefix) {
builder.append(prefix)
builder.append(MangleConstant.PREFIX_SEPARATOR)
}
return builder.length
}
private fun IrDeclaration.mangleSimpleDeclaration(prefix: String, addPrefix: Boolean, name: String) {
val prefixLength = addPrefix(prefix, addPrefix)
parent.accept(this@IrMangleComputer, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
builder.append(name)
}
private fun IrFunction.mangleFunction(isCtor: Boolean, prefix: Boolean, container: IrDeclaration) {
isRealExpect = isRealExpect or isExpect
val prefixLength = addPrefix(MangleConstant.FUN_PREFIX, prefix)
typeParameterContainer.add(container)
container.parent.accept(this@IrMangleComputer, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
builder.append(MangleConstant.FUNCTION_NAME_PREFIX)
if (visibility != Visibilities.INTERNAL) builder.append(name)
else {
builder.append(name)
builder.append(MangleConstant.MODULE_SEPARATOR)
val moduleName = try {
module.name.asString().run { substring(1, lastIndex) }
} catch (e: Throwable) {
MangleConstant.UNKNOWN_MARK
}
builder.append(moduleName)
}
mangleSignature(isCtor)
if (prefix && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
private fun IrFunction.mangleSignature(isCtor: Boolean) {
extensionReceiverParameter?.let {
builder.append(MangleConstant.EXTENSION_RECEIVER_PREFIX)
mangleValueParameter(builder, it)
}
valueParameters.collect(builder, MangleConstant.VALUE_PARAMETERS) { mangleValueParameter(this, it) }
typeParameters.collect(builder, MangleConstant.TYPE_PARAMETERS) { mangleTypeParameter(this, it) }
if (!isCtor && !returnType.isUnit()) {
mangleType(builder, returnType)
}
}
private fun IrTypeParameter.effectiveParent(): IrDeclaration = when (val irParent = parent) {
is IrSimpleFunction -> irParent.correspondingPropertySymbol?.owner ?: irParent
is IrTypeParametersContainer -> irParent
else -> error("Unexpected type parameter container ${irParent.render()} for TP ${render()}")
}
private fun mangleValueParameter(vpBuilder: StringBuilder, param: IrValueParameter) {
mangleType(vpBuilder, param.type)
if (param.isVararg) vpBuilder.append(MangleConstant.VAR_ARG_MARK)
}
private fun mangleTypeParameter(tpBuilder: StringBuilder, param: IrTypeParameter) {
tpBuilder.append(param.index)
tpBuilder.append(MangleConstant.UPPER_BOUND_SEPARATOR)
param.superTypes.collect(tpBuilder, MangleConstant.UPPER_BOUNDS) { mangleType(this, it) }
}
private fun StringBuilder.mangleTypeParameterReference(typeParameter: IrTypeParameter) {
val parent = typeParameter.effectiveParent()
val ci = typeParameterContainer.indexOf(parent)
// TODO: what should we do in this case?
// require(ci >= 0) { "No type container found for ${typeParameter.render()}" }
append(ci)
append(MangleConstant.INDEX_SEPARATOR)
append(typeParameter.index)
}
private fun mangleType(tBuilder: StringBuilder, type: IrType) {
when (type) {
is IrSimpleType -> {
when (val classifier = type.classifier) {
is IrClassSymbol -> classifier.owner.accept(copy(), false)
is IrTypeParameterSymbol -> tBuilder.mangleTypeParameterReference(classifier.owner)
}
type.arguments.ifNotEmpty {
collect(tBuilder, MangleConstant.TYPE_ARGUMENTS) { arg ->
when (arg) {
is IrStarProjection -> append(MangleConstant.STAR_MARK)
is IrTypeProjection -> {
if (arg.variance != Variance.INVARIANT) {
append(arg.variance.label)
append(MangleConstant.VARIANCE_SEPARATOR)
}
mangleType(this, arg.type)
}
}
}
}
if (type.hasQuestionMark) tBuilder.append(MangleConstant.Q_MARK)
}
is IrDynamicType -> tBuilder.append(MangleConstant.DYNAMIC_MARK)
else -> error("Unexpected type $type")
}
}
override fun visitElement(element: IrElement, data: Boolean) = error("unexpected element ${element.render()}")
override fun visitClass(declaration: IrClass, data: Boolean) {
isRealExpect = isRealExpect or declaration.isExpect
typeParameterContainer.add(declaration)
declaration.mangleSimpleDeclaration(MangleConstant.CLASS_PREFIX, data, declaration.name.asString())
if (data && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
override fun visitPackageFragment(declaration: IrPackageFragment, data: Boolean) {
declaration.fqName.let { if (!it.isRoot) builder.append(it.asString()) }
}
override fun visitProperty(declaration: IrProperty, data: Boolean) {
val extensionReceiver = declaration.run { (getter ?: setter)?.extensionReceiverParameter }
val prefixLength = addPrefix(MangleConstant.PROPERTY_PREFIX, data)
isRealExpect = isRealExpect or declaration.isExpect
typeParameterContainer.add(declaration)
declaration.parent.accept(this, false)
if (prefixLength != builder.length) builder.append(MangleConstant.FQN_SEPARATOR)
if (extensionReceiver != null) {
builder.append(MangleConstant.EXTENSION_RECEIVER_PREFIX)
mangleValueParameter(builder, extensionReceiver)
}
builder.append(declaration.name)
if (data && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
override fun visitField(declaration: IrField, data: Boolean) =
declaration.mangleSimpleDeclaration(MangleConstant.FIELD_PREFIX, data, declaration.name.asString())
override fun visitEnumEntry(declaration: IrEnumEntry, data: Boolean) {
declaration.mangleSimpleDeclaration(MangleConstant.ENUM_ENTRY_PREFIX, data, declaration.name.asString())
if (data && isRealExpect) builder.append(MangleConstant.EXPECT_MARK)
}
override fun visitTypeAlias(declaration: IrTypeAlias, data: Boolean) =
declaration.mangleSimpleDeclaration(MangleConstant.TYPE_ALIAS_PREFIX, data, declaration.name.asString())
override fun visitTypeParameter(declaration: IrTypeParameter, data: Boolean) {
addPrefix(MangleConstant.TYPE_PARAM_PREFIX, data)
declaration.effectiveParent().accept(this, data)
builder.append(MangleConstant.TYPE_PARAM_INDEX_PREFIX)
builder.append(declaration.index)
}
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: Boolean) {
isRealExpect = isRealExpect or declaration.isExpect
declaration.platformSpecificFunctionName?.let {
builder.append(it)
return
}
val container = declaration.correspondingPropertySymbol?.owner ?: declaration
declaration.mangleFunction(false, data, container)
}
override fun visitConstructor(declaration: IrConstructor, data: Boolean) = declaration.mangleFunction(true, data, declaration)
}
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2019 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.mangle
import org.jetbrains.kotlin.name.FqName
internal fun <T> Collection<T>.collect(builder: StringBuilder, params: MangleConstant, collect: StringBuilder.(T) -> Unit) {
var first = true
builder.append(params.prefix)
for (e in this) {
if (first) {
first = false
} else {
builder.append(params.separator)
}
builder.collect(e)
}
builder.append(params.suffix)
}
internal val publishedApiAnnotation = FqName("kotlin.PublishedApi")
@@ -5,25 +5,63 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.serialization.KotlinManglerImpl
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.isInlined
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorBasedKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorExportCheckerVisitor
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrBasedKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrExportCheckerVisitor
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrMangleComputer
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
abstract class AbstractJsMangler : KotlinManglerImpl() {
override val IrType.isInlined: Boolean
get() = this.isInlined()
abstract class AbstractJsManglerIr : IrBasedKotlinManglerImpl() {
companion object {
private val exportChecker = JsIrExportChecker()
}
private class JsIrExportChecker : IrExportCheckerVisitor() {
override fun IrDeclaration.isPlatformSpecificExported() = false
}
private class JsIrManglerComputer(builder: StringBuilder) : IrMangleComputer(builder) {
override fun copy(): IrMangleComputer = JsIrManglerComputer(builder)
}
override fun getExportChecker(): KotlinExportChecker<IrDeclaration> = exportChecker
override fun getMangleComputer(prefix: String): KotlinMangleComputer<IrDeclaration> {
return JsIrManglerComputer(StringBuilder(256))
}
}
object JsMangler : AbstractJsMangler()
object JsManglerIr : AbstractJsManglerIr()
abstract class AbstractJsDescriptorMangler : DescriptorBasedKotlinManglerImpl() {
companion object {
private val exportChecker = JsDescriptorExportChecker()
}
private class JsDescriptorExportChecker : DescriptorExportCheckerVisitor() {
override fun DeclarationDescriptor.isPlatformSpecificExported() = false
}
private class JsDescriptorManglerComputer(builder: StringBuilder, prefix: String) : DescriptorMangleComputer(builder, prefix) {
override fun copy(): DescriptorMangleComputer = JsDescriptorManglerComputer(builder, specialPrefix)
}
override fun getExportChecker(): KotlinExportChecker<DeclarationDescriptor> = exportChecker
override fun getMangleComputer(prefix: String): KotlinMangleComputer<DeclarationDescriptor> {
return JsDescriptorManglerComputer(StringBuilder(256), prefix)
}
}
/*
* JsManglerForBE is a special verison of kotlin mangler used in case when IR is not completely correct from Type Parameter perspective.
* I.e. usage of TypeParameter is not in TP's container. It's acceptable if only required to distinguish declaration somehow.
*/
object JsManglerForBE : AbstractJsMangler() {
override fun mangleTypeParameter(typeParameter: IrTypeParameter, typeParameterNamer: (IrTypeParameter) -> String): String =
typeParameter.name.asString()
}
object JsManglerDesc : AbstractJsDescriptorMangler()
@@ -5,13 +5,74 @@
package org.jetbrains.kotlin.ir.backend.jvm.serialization
import org.jetbrains.kotlin.backend.common.serialization.KotlinManglerImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.isInlined
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.KotlinMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicExportChecker
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.classic.ClassicMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorBasedKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorExportCheckerVisitor
import org.jetbrains.kotlin.backend.common.serialization.mangle.descriptor.DescriptorMangleComputer
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrBasedKotlinManglerImpl
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrExportCheckerVisitor
import org.jetbrains.kotlin.backend.common.serialization.mangle.ir.IrMangleComputer
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
// Copied from JsMangler for now
object JvmMangler : KotlinManglerImpl() {
abstract class AbstractJvmManglerIr : IrBasedKotlinManglerImpl() {
override val IrType.isInlined: Boolean
get() = this.isInlined()
companion object {
private val exportChecker = JvmIrExportChecker()
}
private class JvmIrExportChecker : IrExportCheckerVisitor() {
override fun IrDeclaration.isPlatformSpecificExported() = false
}
private class JvmIrManglerComputer(builder: StringBuilder) : IrMangleComputer(builder) {
override fun copy(): IrMangleComputer = JvmIrManglerComputer(builder)
}
override fun getExportChecker(): KotlinExportChecker<IrDeclaration> = exportChecker
override fun getMangleComputer(prefix: String): KotlinMangleComputer<IrDeclaration> {
return JvmIrManglerComputer(StringBuilder(256))
}
}
object JvmManglerIr : AbstractJvmManglerIr()
abstract class AbstractJvmDescriptorMangler(private val mainDetector: MainFunctionDetector?) : DescriptorBasedKotlinManglerImpl() {
companion object {
private val exportChecker = JvmDescriptorExportChecker()
}
private class JvmDescriptorExportChecker : DescriptorExportCheckerVisitor() {
override fun DeclarationDescriptor.isPlatformSpecificExported() = false
}
private class JvmDescriptorManglerComputer(builder: StringBuilder, prefix: String, private val mainDetector: MainFunctionDetector?) :
DescriptorMangleComputer(builder, prefix) {
override fun copy(): DescriptorMangleComputer = JvmDescriptorManglerComputer(builder, specialPrefix, mainDetector)
private fun isMainFunction(descriptor: FunctionDescriptor): Boolean = mainDetector?.isMain(descriptor) ?: false
override fun FunctionDescriptor.platformSpecificSuffix(): String? {
return if (isMainFunction(this)) {
return source.containingFile.name
} else null
}
}
override fun getExportChecker(): KotlinExportChecker<DeclarationDescriptor> = exportChecker
override fun getMangleComputer(prefix: String): KotlinMangleComputer<DeclarationDescriptor> {
return JvmDescriptorManglerComputer(StringBuilder(256), prefix, mainDetector)
}
}
class JvmManglerDesc(mainDetector: MainFunctionDetector? = null) : AbstractJvmDescriptorMangler(mainDetector)