[JS IR] Namer improvements
- Compute and store local names locally when translating a body. It is a step towards separate JS generation and hopefully reduces memory usage. - Use stable mangled names for member names. Needed for separate JS generation. - Add `abstract class IrNamerBase` with just 3 abstract methods to simplify creating new IrNamer implementations. - Fix O(N^2) of findFreshName when it is called wtih the same name suggestion a lot of times. - Refactor NameTables initialisation: factor out some functions and use descriptive names. - Use StringBuilder in sanitizeName
This commit is contained in:
+1
-2
@@ -7,14 +7,13 @@ package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Signature
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.jsFunctionSignature
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
|
||||
class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<JsIrBackendContext>(context) {
|
||||
override fun getFunctionSignature(function: IrSimpleFunction): Signature =
|
||||
override fun getFunctionSignature(function: IrSimpleFunction): String =
|
||||
jsFunctionSignature(function, context)
|
||||
|
||||
override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin =
|
||||
|
||||
+6
-2
@@ -6,8 +6,8 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||
|
||||
@@ -15,7 +15,11 @@ import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
||||
class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
|
||||
val funcName = if (declaration.dispatchReceiverParameter == null) {
|
||||
context.getNameForStaticFunction(declaration)
|
||||
if (declaration.parent is IrFunction) {
|
||||
context.getNameForValueDeclaration(declaration)
|
||||
} else {
|
||||
context.getNameForStaticFunction(declaration)
|
||||
}
|
||||
} else {
|
||||
context.getNameForMemberFunction(declaration)
|
||||
}
|
||||
|
||||
+5
-3
@@ -127,16 +127,18 @@ class IrModuleToJsTransformer(
|
||||
): String {
|
||||
|
||||
val nameGenerator = refInfo.withReferenceTracking(
|
||||
IrNamerImpl(newNameTables = namer),
|
||||
IrNamerImpl(newNameTables = namer, backendContext),
|
||||
modules
|
||||
)
|
||||
val staticContext = JsStaticContext(
|
||||
backendContext = backendContext,
|
||||
irNamer = nameGenerator
|
||||
irNamer = nameGenerator,
|
||||
globalNameScope = namer.globalNames
|
||||
)
|
||||
val rootContext = JsGenerationContext(
|
||||
currentFunction = null,
|
||||
staticContext = staticContext
|
||||
staticContext = staticContext,
|
||||
localNames = LocalNameGenerator(NameScope.EmptyScope)
|
||||
)
|
||||
|
||||
val (importStatements, importedJsModules) =
|
||||
|
||||
+5
-4
@@ -5,12 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -26,8 +24,11 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
private val className = context.getNameForClass(irClass)
|
||||
private val classNameRef = className.makeRef()
|
||||
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
||||
private val baseClassName = baseClass?.let {
|
||||
context.getNameForClass(baseClass.classifierOrFail.owner as IrClass)
|
||||
|
||||
private val baseClassName by lazy { // Lazy in case was not collected by namer during JsClassGenerator construction
|
||||
baseClass?.let {
|
||||
context.getNameForClass(baseClass.classifierOrFail.owner as IrClass)
|
||||
}
|
||||
}
|
||||
private val classPrototypeRef = prototypeOf(classNameRef)
|
||||
private val classBlock = JsGlobalBlock()
|
||||
|
||||
+10
-1
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -50,7 +52,14 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
|
||||
return function
|
||||
}
|
||||
|
||||
val functionContext = context.newDeclaration(declaration)
|
||||
val localNameGenerator = context.localNames
|
||||
?: LocalNameGenerator(context.staticContext.globalNameScope).also {
|
||||
declaration.acceptChildrenVoid(it)
|
||||
declaration.parentClassOrNull?.thisReceiver?.acceptVoid(it)
|
||||
}
|
||||
|
||||
val functionContext = context.newDeclaration(declaration, localNameGenerator)
|
||||
|
||||
val functionParams = declaration.valueParameters.map { functionContext.getNameForValueDeclaration(it) }
|
||||
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
|
||||
|
||||
|
||||
@@ -6,21 +6,81 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
|
||||
interface IrNamer {
|
||||
fun getNameForConstructor(constructor: IrConstructor): JsName
|
||||
fun getNameForMemberFunction(function: IrSimpleFunction): JsName
|
||||
fun getNameForMemberField(field: IrField): JsName
|
||||
fun getNameForField(field: IrField): JsName
|
||||
fun getNameForValueDeclaration(declaration: IrValueDeclaration): JsName
|
||||
fun getNameForClass(klass: IrClass): JsName
|
||||
fun getNameForStaticFunction(function: IrSimpleFunction): JsName
|
||||
fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName
|
||||
fun getNameForProperty(property: IrProperty): JsName
|
||||
fun getNameForStaticFunction(function: IrSimpleFunction): JsName
|
||||
fun getNameForField(field: IrField): JsName
|
||||
fun getNameForConstructor(constructor: IrConstructor): JsName
|
||||
fun getNameForClass(klass: IrClass): JsName
|
||||
fun getRefForExternalClass(klass: IrClass): JsNameRef
|
||||
fun getNameForLoop(loop: IrLoop): JsName?
|
||||
fun getNameForProperty(property: IrProperty): JsName
|
||||
fun getAssociatedObjectKey(irClass: IrClass): Int?
|
||||
}
|
||||
|
||||
abstract class IrNamerBase : IrNamer {
|
||||
abstract override fun getNameForMemberFunction(function: IrSimpleFunction): JsName
|
||||
abstract override fun getNameForMemberField(field: IrField): JsName
|
||||
abstract override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName
|
||||
|
||||
protected fun String.toJsName() = JsName(this)
|
||||
|
||||
override fun getNameForStaticFunction(function: IrSimpleFunction): JsName =
|
||||
getNameForStaticDeclaration(function)
|
||||
|
||||
override fun getNameForField(field: IrField): JsName {
|
||||
return if (field.isStatic || field.parent is IrScript) {
|
||||
getNameForStaticDeclaration(field)
|
||||
} else {
|
||||
getNameForMemberField(field)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNameForConstructor(constructor: IrConstructor): JsName =
|
||||
getNameForStaticDeclaration(constructor.parentAsClass)
|
||||
|
||||
override fun getNameForClass(klass: IrClass): JsName =
|
||||
getNameForStaticDeclaration(klass)
|
||||
|
||||
override fun getRefForExternalClass(klass: IrClass): JsNameRef {
|
||||
val parent = klass.parent
|
||||
if (klass.isCompanion)
|
||||
return getRefForExternalClass(parent as IrClass)
|
||||
|
||||
val currentClassName = klass.getJsNameOrKotlinName().identifier
|
||||
return when (parent) {
|
||||
is IrClass ->
|
||||
JsNameRef(currentClassName, getRefForExternalClass(parent))
|
||||
|
||||
is IrPackageFragment -> {
|
||||
getNameForStaticDeclaration(klass).makeRef()
|
||||
}
|
||||
|
||||
else ->
|
||||
error("Unsupported external class parent $parent")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNameForProperty(property: IrProperty): JsName {
|
||||
return if (property.parent is IrClass) {
|
||||
property.getJsNameOrKotlinName().asString().toJsName()
|
||||
} else {
|
||||
getNameForStaticDeclaration(property)
|
||||
}
|
||||
}
|
||||
|
||||
private val associatedObjectKeyMap = mutableMapOf<IrClass, Int>()
|
||||
|
||||
override fun getAssociatedObjectKey(irClass: IrClass): Int? {
|
||||
if (irClass.isAssociatedObjectAnnotatedAnnotation) {
|
||||
|
||||
return associatedObjectKeyMap.getOrPut(irClass) { associatedObjectKeyMap.size }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
+10
-65
@@ -5,82 +5,27 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
|
||||
|
||||
class IrNamerImpl(private val newNameTables: NameTables) : IrNamer {
|
||||
|
||||
private fun String.toJsName() = JsName(this)
|
||||
|
||||
class IrNamerImpl(
|
||||
private val newNameTables: NameTables,
|
||||
private val context: JsIrBackendContext,
|
||||
) : IrNamerBase() {
|
||||
override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName =
|
||||
newNameTables.getNameForStaticDeclaration(declaration).toJsName()
|
||||
|
||||
override fun getNameForLoop(loop: IrLoop): JsName? =
|
||||
newNameTables.getNameForLoop(loop)?.toJsName()
|
||||
|
||||
override fun getNameForConstructor(constructor: IrConstructor): JsName {
|
||||
return getNameForStaticDeclaration(constructor.parentAsClass)
|
||||
}
|
||||
|
||||
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||
require(function.dispatchReceiverParameter != null)
|
||||
return newNameTables.getNameForMemberFunction(function).toJsName()
|
||||
val signature = jsFunctionSignature(function, context)
|
||||
return signature.toJsName()
|
||||
}
|
||||
|
||||
override fun getNameForMemberField(field: IrField): JsName {
|
||||
require(!field.isStatic)
|
||||
return newNameTables.getNameForMemberField(field).toJsName()
|
||||
}
|
||||
|
||||
override fun getNameForField(field: IrField): JsName {
|
||||
return if (field.isStatic || field.parent is IrScript) {
|
||||
getNameForStaticDeclaration(field)
|
||||
} else {
|
||||
getNameForMemberField(field)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNameForValueDeclaration(declaration: IrValueDeclaration): JsName =
|
||||
getNameForStaticDeclaration(declaration)
|
||||
|
||||
override fun getNameForClass(klass: IrClass): JsName =
|
||||
getNameForStaticDeclaration(klass)
|
||||
|
||||
override fun getNameForStaticFunction(function: IrSimpleFunction): JsName =
|
||||
getNameForStaticDeclaration(function)
|
||||
|
||||
override fun getNameForProperty(property: IrProperty): JsName =
|
||||
property.getJsNameOrKotlinName().asString().toJsName()
|
||||
|
||||
override fun getRefForExternalClass(klass: IrClass): JsNameRef {
|
||||
val parent = klass.parent
|
||||
if (klass.isCompanion)
|
||||
return getRefForExternalClass(parent as IrClass)
|
||||
|
||||
val currentClassName = klass.getJsNameOrKotlinName().identifier
|
||||
return when (parent) {
|
||||
is IrClass ->
|
||||
JsNameRef(currentClassName, getRefForExternalClass(parent))
|
||||
|
||||
is IrPackageFragment ->
|
||||
JsNameRef(currentClassName)
|
||||
|
||||
else ->
|
||||
error("Unsupported external class parent $parent")
|
||||
}
|
||||
}
|
||||
|
||||
private val associatedObjectKeyMap = mutableMapOf<IrClass, Int>()
|
||||
|
||||
override fun getAssociatedObjectKey(irClass: IrClass): Int? {
|
||||
if (irClass.isAssociatedObjectAnnotatedAnnotation) {
|
||||
|
||||
return associatedObjectKeyMap.getOrPut(irClass) { associatedObjectKeyMap.size }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
+19
-4
@@ -5,8 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
@@ -23,12 +25,14 @@ val emptyScope: JsScope
|
||||
|
||||
class JsGenerationContext(
|
||||
val currentFunction: IrFunction?,
|
||||
val staticContext: JsStaticContext
|
||||
val staticContext: JsStaticContext,
|
||||
val localNames: LocalNameGenerator? = null
|
||||
): IrNamer by staticContext {
|
||||
fun newDeclaration(func: IrFunction? = null): JsGenerationContext {
|
||||
fun newDeclaration(func: IrFunction? = null, localNames: LocalNameGenerator? = null): JsGenerationContext {
|
||||
return JsGenerationContext(
|
||||
currentFunction = func,
|
||||
staticContext = staticContext
|
||||
staticContext = staticContext,
|
||||
localNames = localNames,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -39,10 +43,21 @@ class JsGenerationContext(
|
||||
if (currentFunction!!.isSuspend) {
|
||||
JsNameRef(Namer.CONTINUATION)
|
||||
} else {
|
||||
getNameForValueDeclaration(currentFunction.valueParameters.last()).makeRef()
|
||||
JsNameRef(this.getNameForValueDeclaration(currentFunction.valueParameters.last()))
|
||||
}
|
||||
}
|
||||
|
||||
fun getNameForValueDeclaration(declaration: IrDeclarationWithName): JsName {
|
||||
val name = localNames!!.variableNames.names[declaration]
|
||||
?: error("Variable name is not found ${declaration.name}")
|
||||
return JsName(name)
|
||||
}
|
||||
|
||||
fun getNameForLoop(loop: IrLoop): JsName? {
|
||||
val name = localNames!!.localLoopNames.names[loop] ?: return null
|
||||
return JsName(name)
|
||||
}
|
||||
|
||||
private fun isCoroutineDoResume(): Boolean {
|
||||
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
|
||||
return overriddenSymbols.any {
|
||||
|
||||
+2
-3
@@ -8,16 +8,15 @@ package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
||||
|
||||
|
||||
class JsStaticContext(
|
||||
val backendContext: JsIrBackendContext,
|
||||
private val irNamer: IrNamer
|
||||
private val irNamer: IrNamer,
|
||||
val globalNameScope: NameScope
|
||||
) : IrNamer by irNamer {
|
||||
|
||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
||||
|
||||
+118
-183
@@ -17,48 +17,53 @@ import org.jetbrains.kotlin.ir.expressions.IrWhen
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isEnumClass
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.js.naming.isES5IdentifierPart
|
||||
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
|
||||
import org.jetbrains.kotlin.js.naming.isValidES5Identifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
import java.util.*
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.abs
|
||||
|
||||
// TODO remove direct usages of [mapToKey] from [NameTable] & co and move it to scripting & REPL infrastructure. Review usages.
|
||||
private fun <T> mapToKey(declaration: T): String {
|
||||
return with(JsManglerIr) {
|
||||
if (declaration is IrDeclaration) {
|
||||
declaration.hashedMangle.toString()
|
||||
} else if (declaration is Signature) {
|
||||
declaration.toString().hashMangle.toString()
|
||||
} else if (declaration is String) {
|
||||
declaration.hashMangle.toString()
|
||||
} else {
|
||||
error("Key is not generated for " + declaration?.let { it::class.simpleName })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NameScope {
|
||||
abstract fun isReserved(name: String): Boolean
|
||||
|
||||
object EmptyScope : NameScope() {
|
||||
override fun isReserved(name: String): Boolean = false
|
||||
}
|
||||
}
|
||||
|
||||
class NameTable<T>(
|
||||
val parent: NameTable<*>? = null,
|
||||
val parent: NameScope = EmptyScope,
|
||||
val reserved: MutableSet<String> = mutableSetOf(),
|
||||
val mappedNames: MutableMap<String, String>? = null
|
||||
) {
|
||||
var finished = false
|
||||
) : NameScope() {
|
||||
val names = mutableMapOf<T, String>()
|
||||
|
||||
private fun isReserved(name: String): Boolean {
|
||||
if (parent != null && parent.isReserved(name))
|
||||
return true
|
||||
return name in reserved
|
||||
private val suggestedNameLastIdx = mutableMapOf<String, Int>()
|
||||
|
||||
override fun isReserved(name: String): Boolean {
|
||||
return parent.isReserved(name) || name in reserved
|
||||
}
|
||||
|
||||
fun declareStableName(declaration: T, name: String) {
|
||||
if (parent != null) assert(parent.finished)
|
||||
assert(!finished)
|
||||
names[declaration] = name
|
||||
reserved.add(name)
|
||||
mappedNames?.set(mapToKey(declaration), name)
|
||||
@@ -74,7 +79,7 @@ class NameTable<T>(
|
||||
if (!isReserved(suggestedName))
|
||||
return suggestedName
|
||||
|
||||
var i = 0
|
||||
var i = suggestedNameLastIdx[suggestedName] ?: 0
|
||||
|
||||
fun freshName() =
|
||||
suggestedName + "_" + i
|
||||
@@ -82,6 +87,9 @@ class NameTable<T>(
|
||||
while (isReserved(freshName())) {
|
||||
i++
|
||||
}
|
||||
|
||||
suggestedNameLastIdx[suggestedName] = i
|
||||
|
||||
return freshName()
|
||||
}
|
||||
}
|
||||
@@ -93,33 +101,25 @@ fun NameTable<IrDeclaration>.dump(): String =
|
||||
"--- $declRef => $name"
|
||||
}
|
||||
|
||||
sealed class Signature
|
||||
data class StableNameSignature(val name: String) : Signature()
|
||||
data class BackingFieldSignature(val field: IrField) : Signature()
|
||||
data class ParameterTypeBasedSignature(val mangledName: String, val suggestedName: String) : Signature()
|
||||
|
||||
fun fieldSignature(field: IrField): Signature {
|
||||
if (field.isEffectivelyExternal()) {
|
||||
return StableNameSignature(field.name.identifier)
|
||||
}
|
||||
private const val RESERVED_MEMBER_NAME_SUFFIX = "_k$"
|
||||
|
||||
return BackingFieldSignature(field)
|
||||
}
|
||||
|
||||
fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): Signature {
|
||||
fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): String {
|
||||
require(!declaration.isStaticMethodOfClass)
|
||||
require(declaration.dispatchReceiverParameter != null)
|
||||
|
||||
val declarationName = declaration.getJsNameOrKotlinName().asString()
|
||||
|
||||
if (declaration.hasStableJsName(context)) {
|
||||
return StableNameSignature(declarationName)
|
||||
// TODO: Handle reserved suffix in FE
|
||||
require(!declarationName.endsWith(RESERVED_MEMBER_NAME_SUFFIX)) {
|
||||
"Function ${declaration.fqNameWhenAvailable} uses reserved name suffix \"$RESERVED_MEMBER_NAME_SUFFIX\""
|
||||
}
|
||||
return declarationName
|
||||
}
|
||||
|
||||
val nameBuilder = StringBuilder()
|
||||
|
||||
nameBuilder.append(declarationName)
|
||||
|
||||
// TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters?
|
||||
declaration.typeParameters.ifNotEmpty {
|
||||
nameBuilder.append("_\$t")
|
||||
@@ -141,20 +141,19 @@ fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?):
|
||||
|
||||
val signature = nameBuilder.toString()
|
||||
|
||||
// TODO: Check reserved names
|
||||
return ParameterTypeBasedSignature(signature, declarationName)
|
||||
// TODO: Use better hashCode
|
||||
return sanitizeName(declarationName) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX
|
||||
}
|
||||
|
||||
class NameTables(
|
||||
packages: List<IrPackageFragment>,
|
||||
packages: Iterable<IrPackageFragment>,
|
||||
reservedForGlobal: MutableSet<String> = mutableSetOf(),
|
||||
reservedForMember: MutableSet<String> = mutableSetOf(),
|
||||
val mappedNames: MutableMap<String, String>? = null,
|
||||
private val context: JsIrBackendContext? = null
|
||||
) {
|
||||
val globalNames: NameTable<IrDeclaration>
|
||||
private val memberNames: NameTable<Signature>
|
||||
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
|
||||
private val memberNames: NameTable<IrField>
|
||||
private val loopNames = mutableMapOf<IrLoop, String>()
|
||||
|
||||
init {
|
||||
@@ -171,74 +170,48 @@ class NameTables(
|
||||
mappedNames = mappedNames
|
||||
)
|
||||
|
||||
val classDeclaration = mutableListOf<IrClass>()
|
||||
for (p in packages) {
|
||||
for (declaration in p.declarations) {
|
||||
generateNamesForTopLevelDecl(declaration)
|
||||
processTopLevelLocalDecl(declaration)
|
||||
processNonTopLevelLocalDecl(declaration)
|
||||
declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
processNonTopLevelLocalDecl(declaration)
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
})
|
||||
if (declaration is IrScript) {
|
||||
for (memberDecl in declaration.statements) {
|
||||
if (memberDecl is IrDeclaration) {
|
||||
generateNamesForTopLevelDecl(memberDecl)
|
||||
processTopLevelLocalDecl(memberDecl)
|
||||
if (memberDecl is IrClass) {
|
||||
classDeclaration += memberDecl
|
||||
processNonTopLevelLocalDecl(memberDecl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
globalNames.finished = true
|
||||
|
||||
for (declaration in classDeclaration) {
|
||||
acceptDeclaration(declaration)
|
||||
}
|
||||
|
||||
for (p in packages) {
|
||||
for (declaration in p.declarations) {
|
||||
acceptDeclaration(declaration)
|
||||
private fun acceptNonExternalClass(declaration: IrClass) {
|
||||
for (memberDecl in declaration.declarations) {
|
||||
when (memberDecl) {
|
||||
is IrField ->
|
||||
generateNameForMemberField(memberDecl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun acceptDeclaration(declaration: IrDeclaration) {
|
||||
val localNameGenerator = LocalNameGenerator(declaration)
|
||||
|
||||
private fun processNonTopLevelLocalDecl(declaration: IrDeclaration) {
|
||||
if (declaration is IrClass) {
|
||||
if (declaration.isEffectivelyExternal()) {
|
||||
declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||
val parent = declaration.parent
|
||||
if (parent is IrClass && !parent.isEnumClass) {
|
||||
generateNameForMemberFunction(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
val parent = declaration.parent
|
||||
if (parent is IrClass && !parent.isEnumClass) {
|
||||
generateNameForMemberField(declaration)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
declaration.thisReceiver!!.acceptVoid(localNameGenerator)
|
||||
for (memberDecl in declaration.declarations) {
|
||||
memberDecl.acceptChildrenVoid(LocalNameGenerator(memberDecl))
|
||||
when (memberDecl) {
|
||||
is IrSimpleFunction ->
|
||||
generateNameForMemberFunction(memberDecl)
|
||||
is IrField ->
|
||||
generateNameForMemberField(memberDecl)
|
||||
}
|
||||
}
|
||||
if (!declaration.isEffectivelyExternal()) {
|
||||
acceptNonExternalClass(declaration)
|
||||
}
|
||||
} else {
|
||||
declaration.acceptChildrenVoid(localNameGenerator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +235,6 @@ class NameTables(
|
||||
|
||||
globalNames.names.addAllIfAbsent(table.globalNames.names)
|
||||
memberNames.names.addAllIfAbsent(table.memberNames.names)
|
||||
localNames.addAllIfAbsent(table.localNames)
|
||||
loopNames.addAllIfAbsent(table.loopNames)
|
||||
|
||||
globalNames.reserved.addAll(table.globalNames.reserved)
|
||||
@@ -272,49 +244,23 @@ class NameTables(
|
||||
private fun generateNameForMemberField(field: IrField) {
|
||||
require(!field.isTopLevel)
|
||||
require(!field.isStatic)
|
||||
val signature = fieldSignature(field)
|
||||
|
||||
if (field.isEffectivelyExternal()) {
|
||||
memberNames.declareStableName(signature, field.name.identifier)
|
||||
memberNames.declareStableName(field, field.name.identifier)
|
||||
} else {
|
||||
memberNames.declareFreshName(signature, "_" + sanitizeName(field.name.asString()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateNameForMemberFunction(declaration: IrSimpleFunction) {
|
||||
when (val signature = jsFunctionSignature(declaration, context)) {
|
||||
is StableNameSignature -> memberNames.declareStableName(signature, signature.name)
|
||||
is ParameterTypeBasedSignature -> memberNames.declareFreshName(signature, signature.suggestedName)
|
||||
memberNames.declareFreshName(field, "_" + sanitizeName(field.name.asString()))
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun dump(): String {
|
||||
val local = localNames.toList().joinToString("\n") { (decl, table) ->
|
||||
val declRef = (decl as? IrDeclarationWithName)?.fqNameWhenAvailable ?: decl
|
||||
"\nLocal names for $declRef:\n${table.dump()}\n"
|
||||
}
|
||||
return "Global names:\n${globalNames.dump()}" +
|
||||
// "\nMember names:\n${memberNames.dump()}" +
|
||||
"\nLocal names:\n$local\n"
|
||||
return "Global names:\n${globalNames.dump()}"
|
||||
}
|
||||
|
||||
fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): String {
|
||||
val global: String? = globalNames.names[declaration]
|
||||
if (global != null) return global
|
||||
|
||||
var parent: IrDeclarationParent = declaration.parent
|
||||
while (parent is IrDeclaration) {
|
||||
val parentLocalNames = localNames[parent]
|
||||
if (parentLocalNames != null) {
|
||||
val localName = parentLocalNames.names[declaration]
|
||||
if (localName != null)
|
||||
return localName
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
|
||||
|
||||
mappedNames?.get(mapToKey(declaration))?.let {
|
||||
return it
|
||||
}
|
||||
@@ -323,8 +269,7 @@ class NameTables(
|
||||
}
|
||||
|
||||
fun getNameForMemberField(field: IrField): String {
|
||||
val signature = fieldSignature(field)
|
||||
val name = memberNames.names[signature] ?: mappedNames?.get(mapToKey(signature))
|
||||
val name = memberNames.names[field] ?: mappedNames?.get(mapToKey(field))
|
||||
|
||||
// TODO investigate
|
||||
if (name == null) {
|
||||
@@ -334,24 +279,12 @@ class NameTables(
|
||||
return name
|
||||
}
|
||||
|
||||
fun getNameForMemberFunction(function: IrSimpleFunction): String {
|
||||
val signature = jsFunctionSignature(function, context)
|
||||
val name = memberNames.names[signature] ?: mappedNames?.get(mapToKey(signature))
|
||||
|
||||
// TODO Add a compiler flag, which enables this behaviour
|
||||
// TODO remove in DCE
|
||||
if (name == null) {
|
||||
return sanitizeName(function.name.asString()) + "__error" // TODO one case is a virtual method of an abstract class with no implementation
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
private fun generateNamesForTopLevelDecl(declaration: IrDeclaration) {
|
||||
private fun processTopLevelLocalDecl(declaration: IrDeclaration) {
|
||||
when {
|
||||
declaration !is IrDeclarationWithName ->
|
||||
return
|
||||
|
||||
// TODO: Handle JsQualifier
|
||||
declaration.isEffectivelyExternal() && (declaration.getJsModule() == null || declaration.isJsNonModule()) ->
|
||||
globalNames.declareStableName(declaration, declaration.getJsNameOrKotlinName().identifier)
|
||||
|
||||
@@ -360,66 +293,59 @@ class NameTables(
|
||||
}
|
||||
}
|
||||
|
||||
inner class LocalNameGenerator(parentDeclaration: IrDeclaration) : IrElementVisitorVoid {
|
||||
val table = NameTable<IrDeclaration>(globalNames)
|
||||
}
|
||||
|
||||
private val breakableDeque: Deque<IrExpression> = LinkedList()
|
||||
class LocalNameGenerator(parentScope: NameScope) : IrElementVisitorVoid {
|
||||
val variableNames = NameTable<IrDeclarationWithName>(parentScope)
|
||||
val localLoopNames = NameTable<IrLoop>()
|
||||
|
||||
init {
|
||||
localNames[parentDeclaration] = table
|
||||
}
|
||||
private val breakableDeque: Deque<IrExpression> = LinkedList()
|
||||
|
||||
private val localLoopNames = NameTable<IrLoop>()
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
if (declaration is IrDeclarationWithName) {
|
||||
table.declareFreshName(declaration, declaration.name.asString())
|
||||
}
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
|
||||
override fun visitBreak(jump: IrBreak) {
|
||||
val loop = jump.loop
|
||||
if (loop.label == null && loop != breakableDeque.firstOrNull()) {
|
||||
persistLoopName(SYNTHETIC_LOOP_LABEL, loop)
|
||||
}
|
||||
|
||||
super.visitBreak(jump)
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen) {
|
||||
breakableDeque.push(expression)
|
||||
|
||||
super.visitWhen(expression)
|
||||
|
||||
breakableDeque.pop()
|
||||
}
|
||||
|
||||
override fun visitLoop(loop: IrLoop) {
|
||||
breakableDeque.push(loop)
|
||||
|
||||
super.visitLoop(loop)
|
||||
|
||||
breakableDeque.pop()
|
||||
|
||||
val label = loop.label
|
||||
|
||||
if (label != null) {
|
||||
persistLoopName(label, loop)
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistLoopName(label: String, loop: IrLoop) {
|
||||
localLoopNames.declareFreshName(loop, label)
|
||||
loopNames[loop] = localLoopNames.names[loop]!!
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
super.visitDeclaration(declaration)
|
||||
if (declaration is IrDeclarationWithName) {
|
||||
variableNames.declareFreshName(declaration, declaration.name.asString())
|
||||
}
|
||||
}
|
||||
|
||||
fun getNameForLoop(loop: IrLoop): String? =
|
||||
loopNames[loop]
|
||||
override fun visitBreak(jump: IrBreak) {
|
||||
val loop = jump.loop
|
||||
if (loop.label == null && loop != breakableDeque.firstOrNull()) {
|
||||
persistLoopName(SYNTHETIC_LOOP_LABEL, loop)
|
||||
}
|
||||
|
||||
super.visitBreak(jump)
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen) {
|
||||
breakableDeque.push(expression)
|
||||
|
||||
super.visitWhen(expression)
|
||||
|
||||
breakableDeque.pop()
|
||||
}
|
||||
|
||||
override fun visitLoop(loop: IrLoop) {
|
||||
breakableDeque.push(loop)
|
||||
|
||||
super.visitLoop(loop)
|
||||
|
||||
breakableDeque.pop()
|
||||
|
||||
val label = loop.label
|
||||
|
||||
if (label != null) {
|
||||
persistLoopName(label, loop)
|
||||
}
|
||||
}
|
||||
|
||||
private fun persistLoopName(label: String, loop: IrLoop) {
|
||||
localLoopNames.declareFreshName(loop, label)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -427,8 +353,17 @@ fun sanitizeName(name: String): String {
|
||||
if (name.isValidES5Identifier()) return name
|
||||
if (name.isEmpty()) return "_"
|
||||
|
||||
val builder = StringBuilder()
|
||||
|
||||
val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
|
||||
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
|
||||
builder.append(first)
|
||||
|
||||
for (idx in 1..name.lastIndex) {
|
||||
val c = name[idx]
|
||||
builder.append(if (c.isES5IdentifierPart()) c else '_')
|
||||
}
|
||||
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
private const val SYNTHETIC_LOOP_LABEL = "\$l\$break"
|
||||
|
||||
@@ -399,7 +399,6 @@ fun Char.isES5IdentifierPart(): Boolean {
|
||||
Character.CONNECTOR_PUNCTUATION -> true
|
||||
else -> false
|
||||
} ||
|
||||
// Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec
|
||||
this == '\u200C' || // Zero-width non-joiner
|
||||
this == '\u200D' // Zero-width joiner
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user