diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt index 1c540748bb9..e9caad78314 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt @@ -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(context) { - override fun getFunctionSignature(function: IrSimpleFunction): Signature = + override fun getFunctionSignature(function: IrSimpleFunction): String = jsFunctionSignature(function, context) override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt index 0b3e3370e1a..2a7b77b6a41 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt @@ -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 { 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) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 269a63319ff..968bc4fbc43 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -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) = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index 843b9838f75..13ff53bbc25 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index a4b64bb3fa6..7f3400e2886 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt index d21f466dc29..f401d082059 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamer.kt @@ -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() + + override fun getAssociatedObjectKey(irClass: IrClass): Int? { + if (irClass.isAssociatedObjectAnnotatedAnnotation) { + + return associatedObjectKeyMap.getOrPut(irClass) { associatedObjectKeyMap.size } + } + return null + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt index b7fe32e4bcd..2674710bf3b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrNamerImpl.kt @@ -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() - - override fun getAssociatedObjectKey(irClass: IrClass): Int? { - if (irClass.isAssociatedObjectAnnotatedAnnotation) { - - return associatedObjectKeyMap.getOrPut(irClass) { associatedObjectKeyMap.size } - } - return null - } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt index 652269727ae..0ce3066d03f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt @@ -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 { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index 72e84ac97a6..4c6dfbf502a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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() val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt index 42fc6779169..9986206a876 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -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 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( - val parent: NameTable<*>? = null, + val parent: NameScope = EmptyScope, val reserved: MutableSet = mutableSetOf(), val mappedNames: MutableMap? = null -) { - var finished = false +) : NameScope() { val names = mutableMapOf() - private fun isReserved(name: String): Boolean { - if (parent != null && parent.isReserved(name)) - return true - return name in reserved + private val suggestedNameLastIdx = mutableMapOf() + + 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( if (!isReserved(suggestedName)) return suggestedName - var i = 0 + var i = suggestedNameLastIdx[suggestedName] ?: 0 fun freshName() = suggestedName + "_" + i @@ -82,6 +87,9 @@ class NameTable( while (isReserved(freshName())) { i++ } + + suggestedNameLastIdx[suggestedName] = i + return freshName() } } @@ -93,33 +101,25 @@ fun NameTable.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, + packages: Iterable, reservedForGlobal: MutableSet = mutableSetOf(), reservedForMember: MutableSet = mutableSetOf(), val mappedNames: MutableMap? = null, private val context: JsIrBackendContext? = null ) { val globalNames: NameTable - private val memberNames: NameTable - private val localNames = mutableMapOf>() + private val memberNames: NameTable private val loopNames = mutableMapOf() init { @@ -171,74 +170,48 @@ class NameTables( mappedNames = mappedNames ) - val classDeclaration = mutableListOf() 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(globalNames) +} - private val breakableDeque: Deque = LinkedList() +class LocalNameGenerator(parentScope: NameScope) : IrElementVisitorVoid { + val variableNames = NameTable(parentScope) + val localLoopNames = NameTable() - init { - localNames[parentDeclaration] = table - } + private val breakableDeque: Deque = LinkedList() - private val localLoopNames = NameTable() - 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" diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt index d5d37886a28..cb9abc35d09 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt @@ -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 }