[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.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
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.hasStableJsName
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.jsFunctionSignature
|
import org.jetbrains.kotlin.ir.backend.js.utils.jsFunctionSignature
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
|
|
||||||
class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<JsIrBackendContext>(context) {
|
class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<JsIrBackendContext>(context) {
|
||||||
override fun getFunctionSignature(function: IrSimpleFunction): Signature =
|
override fun getFunctionSignature(function: IrSimpleFunction): String =
|
||||||
jsFunctionSignature(function, context)
|
jsFunctionSignature(function, context)
|
||||||
|
|
||||||
override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin =
|
override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin =
|
||||||
|
|||||||
+6
-2
@@ -6,8 +6,8 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
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.IrConstructor
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsFunction
|
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> {
|
class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
|
||||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
|
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
|
||||||
val funcName = if (declaration.dispatchReceiverParameter == null) {
|
val funcName = if (declaration.dispatchReceiverParameter == null) {
|
||||||
context.getNameForStaticFunction(declaration)
|
if (declaration.parent is IrFunction) {
|
||||||
|
context.getNameForValueDeclaration(declaration)
|
||||||
|
} else {
|
||||||
|
context.getNameForStaticFunction(declaration)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
context.getNameForMemberFunction(declaration)
|
context.getNameForMemberFunction(declaration)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -127,16 +127,18 @@ class IrModuleToJsTransformer(
|
|||||||
): String {
|
): String {
|
||||||
|
|
||||||
val nameGenerator = refInfo.withReferenceTracking(
|
val nameGenerator = refInfo.withReferenceTracking(
|
||||||
IrNamerImpl(newNameTables = namer),
|
IrNamerImpl(newNameTables = namer, backendContext),
|
||||||
modules
|
modules
|
||||||
)
|
)
|
||||||
val staticContext = JsStaticContext(
|
val staticContext = JsStaticContext(
|
||||||
backendContext = backendContext,
|
backendContext = backendContext,
|
||||||
irNamer = nameGenerator
|
irNamer = nameGenerator,
|
||||||
|
globalNameScope = namer.globalNames
|
||||||
)
|
)
|
||||||
val rootContext = JsGenerationContext(
|
val rootContext = JsGenerationContext(
|
||||||
currentFunction = null,
|
currentFunction = null,
|
||||||
staticContext = staticContext
|
staticContext = staticContext,
|
||||||
|
localNames = LocalNameGenerator(NameScope.EmptyScope)
|
||||||
)
|
)
|
||||||
|
|
||||||
val (importStatements, importedJsModules) =
|
val (importStatements, importedJsModules) =
|
||||||
|
|||||||
+5
-4
@@ -5,12 +5,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
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.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
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 className = context.getNameForClass(irClass)
|
||||||
private val classNameRef = className.makeRef()
|
private val classNameRef = className.makeRef()
|
||||||
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
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 classPrototypeRef = prototypeOf(classNameRef)
|
||||||
private val classBlock = JsGlobalBlock()
|
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.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
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.js.backend.ast.*
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
@@ -50,7 +52,14 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
|
|||||||
return function
|
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 functionParams = declaration.valueParameters.map { functionContext.getNameForValueDeclaration(it) }
|
||||||
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
|
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
|
||||||
|
|
||||||
|
|||||||
@@ -6,21 +6,81 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.JsName
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||||
|
|
||||||
interface IrNamer {
|
interface IrNamer {
|
||||||
fun getNameForConstructor(constructor: IrConstructor): JsName
|
|
||||||
fun getNameForMemberFunction(function: IrSimpleFunction): JsName
|
fun getNameForMemberFunction(function: IrSimpleFunction): JsName
|
||||||
fun getNameForMemberField(field: IrField): 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 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 getRefForExternalClass(klass: IrClass): JsNameRef
|
||||||
fun getNameForLoop(loop: IrLoop): JsName?
|
fun getNameForProperty(property: IrProperty): JsName
|
||||||
fun getAssociatedObjectKey(irClass: IrClass): Int?
|
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
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
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.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 =
|
override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName =
|
||||||
newNameTables.getNameForStaticDeclaration(declaration).toJsName()
|
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 {
|
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||||
require(function.dispatchReceiverParameter != null)
|
require(function.dispatchReceiverParameter != null)
|
||||||
return newNameTables.getNameForMemberFunction(function).toJsName()
|
val signature = jsFunctionSignature(function, context)
|
||||||
|
return signature.toJsName()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getNameForMemberField(field: IrField): JsName {
|
override fun getNameForMemberField(field: IrField): JsName {
|
||||||
require(!field.isStatic)
|
require(!field.isStatic)
|
||||||
return newNameTables.getNameForMemberField(field).toJsName()
|
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
|
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.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
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.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||||
@@ -23,12 +25,14 @@ val emptyScope: JsScope
|
|||||||
|
|
||||||
class JsGenerationContext(
|
class JsGenerationContext(
|
||||||
val currentFunction: IrFunction?,
|
val currentFunction: IrFunction?,
|
||||||
val staticContext: JsStaticContext
|
val staticContext: JsStaticContext,
|
||||||
|
val localNames: LocalNameGenerator? = null
|
||||||
): IrNamer by staticContext {
|
): IrNamer by staticContext {
|
||||||
fun newDeclaration(func: IrFunction? = null): JsGenerationContext {
|
fun newDeclaration(func: IrFunction? = null, localNames: LocalNameGenerator? = null): JsGenerationContext {
|
||||||
return JsGenerationContext(
|
return JsGenerationContext(
|
||||||
currentFunction = func,
|
currentFunction = func,
|
||||||
staticContext = staticContext
|
staticContext = staticContext,
|
||||||
|
localNames = localNames,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +43,21 @@ class JsGenerationContext(
|
|||||||
if (currentFunction!!.isSuspend) {
|
if (currentFunction!!.isSuspend) {
|
||||||
JsNameRef(Namer.CONTINUATION)
|
JsNameRef(Namer.CONTINUATION)
|
||||||
} else {
|
} 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 {
|
private fun isCoroutineDoResume(): Boolean {
|
||||||
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
|
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
|
||||||
return overriddenSymbols.any {
|
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.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
|
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.backend.js.transformers.irToJs.JsIrClassModel
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
|
||||||
|
|
||||||
|
|
||||||
class JsStaticContext(
|
class JsStaticContext(
|
||||||
val backendContext: JsIrBackendContext,
|
val backendContext: JsIrBackendContext,
|
||||||
private val irNamer: IrNamer
|
private val irNamer: IrNamer,
|
||||||
|
val globalNameScope: NameScope
|
||||||
) : IrNamer by irNamer {
|
) : IrNamer by irNamer {
|
||||||
|
|
||||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||||
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
|
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.types.isUnit
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
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.util.render
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
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.isES5IdentifierPart
|
||||||
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
|
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
|
||||||
import org.jetbrains.kotlin.js.naming.isValidES5Identifier
|
import org.jetbrains.kotlin.js.naming.isValidES5Identifier
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||||
import java.util.*
|
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.
|
// 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 {
|
private fun <T> mapToKey(declaration: T): String {
|
||||||
return with(JsManglerIr) {
|
return with(JsManglerIr) {
|
||||||
if (declaration is IrDeclaration) {
|
if (declaration is IrDeclaration) {
|
||||||
declaration.hashedMangle.toString()
|
declaration.hashedMangle.toString()
|
||||||
} else if (declaration is Signature) {
|
} else if (declaration is String) {
|
||||||
declaration.toString().hashMangle.toString()
|
declaration.hashMangle.toString()
|
||||||
} else {
|
} else {
|
||||||
error("Key is not generated for " + declaration?.let { it::class.simpleName })
|
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>(
|
class NameTable<T>(
|
||||||
val parent: NameTable<*>? = null,
|
val parent: NameScope = EmptyScope,
|
||||||
val reserved: MutableSet<String> = mutableSetOf(),
|
val reserved: MutableSet<String> = mutableSetOf(),
|
||||||
val mappedNames: MutableMap<String, String>? = null
|
val mappedNames: MutableMap<String, String>? = null
|
||||||
) {
|
) : NameScope() {
|
||||||
var finished = false
|
|
||||||
val names = mutableMapOf<T, String>()
|
val names = mutableMapOf<T, String>()
|
||||||
|
|
||||||
private fun isReserved(name: String): Boolean {
|
private val suggestedNameLastIdx = mutableMapOf<String, Int>()
|
||||||
if (parent != null && parent.isReserved(name))
|
|
||||||
return true
|
override fun isReserved(name: String): Boolean {
|
||||||
return name in reserved
|
return parent.isReserved(name) || name in reserved
|
||||||
}
|
}
|
||||||
|
|
||||||
fun declareStableName(declaration: T, name: String) {
|
fun declareStableName(declaration: T, name: String) {
|
||||||
if (parent != null) assert(parent.finished)
|
|
||||||
assert(!finished)
|
|
||||||
names[declaration] = name
|
names[declaration] = name
|
||||||
reserved.add(name)
|
reserved.add(name)
|
||||||
mappedNames?.set(mapToKey(declaration), name)
|
mappedNames?.set(mapToKey(declaration), name)
|
||||||
@@ -74,7 +79,7 @@ class NameTable<T>(
|
|||||||
if (!isReserved(suggestedName))
|
if (!isReserved(suggestedName))
|
||||||
return suggestedName
|
return suggestedName
|
||||||
|
|
||||||
var i = 0
|
var i = suggestedNameLastIdx[suggestedName] ?: 0
|
||||||
|
|
||||||
fun freshName() =
|
fun freshName() =
|
||||||
suggestedName + "_" + i
|
suggestedName + "_" + i
|
||||||
@@ -82,6 +87,9 @@ class NameTable<T>(
|
|||||||
while (isReserved(freshName())) {
|
while (isReserved(freshName())) {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suggestedNameLastIdx[suggestedName] = i
|
||||||
|
|
||||||
return freshName()
|
return freshName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,33 +101,25 @@ fun NameTable<IrDeclaration>.dump(): String =
|
|||||||
"--- $declRef => $name"
|
"--- $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 {
|
private const val RESERVED_MEMBER_NAME_SUFFIX = "_k$"
|
||||||
if (field.isEffectivelyExternal()) {
|
|
||||||
return StableNameSignature(field.name.identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
return BackingFieldSignature(field)
|
fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): String {
|
||||||
}
|
|
||||||
|
|
||||||
fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): Signature {
|
|
||||||
require(!declaration.isStaticMethodOfClass)
|
require(!declaration.isStaticMethodOfClass)
|
||||||
require(declaration.dispatchReceiverParameter != null)
|
require(declaration.dispatchReceiverParameter != null)
|
||||||
|
|
||||||
val declarationName = declaration.getJsNameOrKotlinName().asString()
|
val declarationName = declaration.getJsNameOrKotlinName().asString()
|
||||||
|
|
||||||
if (declaration.hasStableJsName(context)) {
|
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()
|
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?
|
// TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters?
|
||||||
declaration.typeParameters.ifNotEmpty {
|
declaration.typeParameters.ifNotEmpty {
|
||||||
nameBuilder.append("_\$t")
|
nameBuilder.append("_\$t")
|
||||||
@@ -141,20 +141,19 @@ fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?):
|
|||||||
|
|
||||||
val signature = nameBuilder.toString()
|
val signature = nameBuilder.toString()
|
||||||
|
|
||||||
// TODO: Check reserved names
|
// TODO: Use better hashCode
|
||||||
return ParameterTypeBasedSignature(signature, declarationName)
|
return sanitizeName(declarationName) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX
|
||||||
}
|
}
|
||||||
|
|
||||||
class NameTables(
|
class NameTables(
|
||||||
packages: List<IrPackageFragment>,
|
packages: Iterable<IrPackageFragment>,
|
||||||
reservedForGlobal: MutableSet<String> = mutableSetOf(),
|
reservedForGlobal: MutableSet<String> = mutableSetOf(),
|
||||||
reservedForMember: MutableSet<String> = mutableSetOf(),
|
reservedForMember: MutableSet<String> = mutableSetOf(),
|
||||||
val mappedNames: MutableMap<String, String>? = null,
|
val mappedNames: MutableMap<String, String>? = null,
|
||||||
private val context: JsIrBackendContext? = null
|
private val context: JsIrBackendContext? = null
|
||||||
) {
|
) {
|
||||||
val globalNames: NameTable<IrDeclaration>
|
val globalNames: NameTable<IrDeclaration>
|
||||||
private val memberNames: NameTable<Signature>
|
private val memberNames: NameTable<IrField>
|
||||||
private val localNames = mutableMapOf<IrDeclaration, NameTable<IrDeclaration>>()
|
|
||||||
private val loopNames = mutableMapOf<IrLoop, String>()
|
private val loopNames = mutableMapOf<IrLoop, String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -171,74 +170,48 @@ class NameTables(
|
|||||||
mappedNames = mappedNames
|
mappedNames = mappedNames
|
||||||
)
|
)
|
||||||
|
|
||||||
val classDeclaration = mutableListOf<IrClass>()
|
|
||||||
for (p in packages) {
|
for (p in packages) {
|
||||||
for (declaration in p.declarations) {
|
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) {
|
if (declaration is IrScript) {
|
||||||
for (memberDecl in declaration.statements) {
|
for (memberDecl in declaration.statements) {
|
||||||
if (memberDecl is IrDeclaration) {
|
if (memberDecl is IrDeclaration) {
|
||||||
generateNamesForTopLevelDecl(memberDecl)
|
processTopLevelLocalDecl(memberDecl)
|
||||||
if (memberDecl is IrClass) {
|
if (memberDecl is IrClass) {
|
||||||
classDeclaration += memberDecl
|
processNonTopLevelLocalDecl(memberDecl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
globalNames.finished = true
|
private fun acceptNonExternalClass(declaration: IrClass) {
|
||||||
|
for (memberDecl in declaration.declarations) {
|
||||||
for (declaration in classDeclaration) {
|
when (memberDecl) {
|
||||||
acceptDeclaration(declaration)
|
is IrField ->
|
||||||
}
|
generateNameForMemberField(memberDecl)
|
||||||
|
|
||||||
for (p in packages) {
|
|
||||||
for (declaration in p.declarations) {
|
|
||||||
acceptDeclaration(declaration)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun acceptDeclaration(declaration: IrDeclaration) {
|
private fun processNonTopLevelLocalDecl(declaration: IrDeclaration) {
|
||||||
val localNameGenerator = LocalNameGenerator(declaration)
|
|
||||||
|
|
||||||
if (declaration is IrClass) {
|
if (declaration is IrClass) {
|
||||||
if (declaration.isEffectivelyExternal()) {
|
if (!declaration.isEffectivelyExternal()) {
|
||||||
declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
|
acceptNonExternalClass(declaration)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
declaration.acceptChildrenVoid(localNameGenerator)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +235,6 @@ class NameTables(
|
|||||||
|
|
||||||
globalNames.names.addAllIfAbsent(table.globalNames.names)
|
globalNames.names.addAllIfAbsent(table.globalNames.names)
|
||||||
memberNames.names.addAllIfAbsent(table.memberNames.names)
|
memberNames.names.addAllIfAbsent(table.memberNames.names)
|
||||||
localNames.addAllIfAbsent(table.localNames)
|
|
||||||
loopNames.addAllIfAbsent(table.loopNames)
|
loopNames.addAllIfAbsent(table.loopNames)
|
||||||
|
|
||||||
globalNames.reserved.addAll(table.globalNames.reserved)
|
globalNames.reserved.addAll(table.globalNames.reserved)
|
||||||
@@ -272,49 +244,23 @@ class NameTables(
|
|||||||
private fun generateNameForMemberField(field: IrField) {
|
private fun generateNameForMemberField(field: IrField) {
|
||||||
require(!field.isTopLevel)
|
require(!field.isTopLevel)
|
||||||
require(!field.isStatic)
|
require(!field.isStatic)
|
||||||
val signature = fieldSignature(field)
|
|
||||||
|
|
||||||
if (field.isEffectivelyExternal()) {
|
if (field.isEffectivelyExternal()) {
|
||||||
memberNames.declareStableName(signature, field.name.identifier)
|
memberNames.declareStableName(field, field.name.identifier)
|
||||||
} else {
|
} else {
|
||||||
memberNames.declareFreshName(signature, "_" + sanitizeName(field.name.asString()))
|
memberNames.declareFreshName(field, "_" + 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun dump(): String {
|
fun dump(): String {
|
||||||
val local = localNames.toList().joinToString("\n") { (decl, table) ->
|
return "Global names:\n${globalNames.dump()}"
|
||||||
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"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): String {
|
fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): String {
|
||||||
val global: String? = globalNames.names[declaration]
|
val global: String? = globalNames.names[declaration]
|
||||||
if (global != null) return global
|
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 {
|
mappedNames?.get(mapToKey(declaration))?.let {
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
@@ -323,8 +269,7 @@ class NameTables(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getNameForMemberField(field: IrField): String {
|
fun getNameForMemberField(field: IrField): String {
|
||||||
val signature = fieldSignature(field)
|
val name = memberNames.names[field] ?: mappedNames?.get(mapToKey(field))
|
||||||
val name = memberNames.names[signature] ?: mappedNames?.get(mapToKey(signature))
|
|
||||||
|
|
||||||
// TODO investigate
|
// TODO investigate
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
@@ -334,24 +279,12 @@ class NameTables(
|
|||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNameForMemberFunction(function: IrSimpleFunction): String {
|
private fun processTopLevelLocalDecl(declaration: IrDeclaration) {
|
||||||
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) {
|
|
||||||
when {
|
when {
|
||||||
declaration !is IrDeclarationWithName ->
|
declaration !is IrDeclarationWithName ->
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// TODO: Handle JsQualifier
|
||||||
declaration.isEffectivelyExternal() && (declaration.getJsModule() == null || declaration.isJsNonModule()) ->
|
declaration.isEffectivelyExternal() && (declaration.getJsModule() == null || declaration.isJsNonModule()) ->
|
||||||
globalNames.declareStableName(declaration, declaration.getJsNameOrKotlinName().identifier)
|
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 {
|
private val breakableDeque: Deque<IrExpression> = LinkedList()
|
||||||
localNames[parentDeclaration] = table
|
|
||||||
}
|
|
||||||
|
|
||||||
private val localLoopNames = NameTable<IrLoop>()
|
override fun visitElement(element: IrElement) {
|
||||||
override fun visitElement(element: IrElement) {
|
element.acceptChildrenVoid(this)
|
||||||
element.acceptChildrenVoid(this)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||||
if (declaration is IrDeclarationWithName) {
|
super.visitDeclaration(declaration)
|
||||||
table.declareFreshName(declaration, declaration.name.asString())
|
if (declaration is IrDeclarationWithName) {
|
||||||
}
|
variableNames.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]!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getNameForLoop(loop: IrLoop): String? =
|
override fun visitBreak(jump: IrBreak) {
|
||||||
loopNames[loop]
|
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.isValidES5Identifier()) return name
|
||||||
if (name.isEmpty()) return "_"
|
if (name.isEmpty()) return "_"
|
||||||
|
|
||||||
|
val builder = StringBuilder()
|
||||||
|
|
||||||
val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
|
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"
|
private const val SYNTHETIC_LOOP_LABEL = "\$l\$break"
|
||||||
|
|||||||
@@ -399,7 +399,6 @@ fun Char.isES5IdentifierPart(): Boolean {
|
|||||||
Character.CONNECTOR_PUNCTUATION -> true
|
Character.CONNECTOR_PUNCTUATION -> true
|
||||||
else -> false
|
else -> false
|
||||||
} ||
|
} ||
|
||||||
// Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec
|
|
||||||
this == '\u200C' || // Zero-width non-joiner
|
this == '\u200C' || // Zero-width non-joiner
|
||||||
this == '\u200D' // Zero-width joiner
|
this == '\u200D' // Zero-width joiner
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user