diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 16fec701f11..e9be8d49b8d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -262,12 +262,18 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC // TODO move CharSequence-related stiff to IntrinsifyCallsLowering val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence"))) - val charSequenceLengthPropertyGetterSymbol = - charSequenceClassSymbol.owner.declarations.filterIsInstance().first { it.name.asString() == "length" }.getter!!.symbol - val charSequenceGetFunctionSymbol = + val charSequenceLengthPropertyGetterSymbol by lazy { + with(charSequenceClassSymbol.owner.declarations) { + filterIsInstance().firstOrNull { it.name.asString() == "length" }?.getter ?: + filterIsInstance().first { it.name.asString() == "" } + }.symbol + } + val charSequenceGetFunctionSymbol by lazy { charSequenceClassSymbol.owner.declarations.filterIsInstance().single { it.name.asString() == "get" }.symbol - val charSequenceSubSequenceFunctionSymbol = + } + val charSequenceSubSequenceFunctionSymbol by lazy { charSequenceClassSymbol.owner.declarations.filterIsInstance().single { it.name.asString() == "subSequence" }.symbol + } val jsCharSequenceGet = getInternalFunction("charSequenceGet") 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 442428e776f..ae47f94575f 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 @@ -73,7 +73,7 @@ class IrModuleToJsTransformer( module.files.forEach { StaticMembersLowering(backendContext).lower(it) } - val namer = NameTables(module.files + additionalPackages) + namer.merge(module.files, additionalPackages) val program = JsProgram() @@ -102,25 +102,33 @@ class IrModuleToJsTransformer( val exportStatements = ExportModelToJsStatements(internalModuleName, namer) .generateModuleExport(exportedModule) - with(rootFunction) { - parameters += JsParameter(internalModuleName) - parameters += importedJsModules.map { JsParameter(it.internalName) } - with(body) { + if (generateScriptModule) { + with(program.globalBlock) { statements += importStatements statements += moduleBody statements += exportStatements - statements += generateCallToMain(rootContext) - statements += JsReturn(internalModuleName.makeRef()) } - } + } else { + with(rootFunction) { + parameters += JsParameter(internalModuleName) + parameters += importedJsModules.map { JsParameter(it.internalName) } + with(body) { + statements += importStatements + statements += moduleBody + statements += exportStatements + statements += generateCallToMain(rootContext) + statements += JsReturn(internalModuleName.makeRef()) + } + } - program.globalBlock.statements += ModuleWrapperTranslation.wrap( - moduleName, - rootFunction, - importedJsModules, - program, - kind = moduleKind - ) + program.globalBlock.statements += ModuleWrapperTranslation.wrap( + moduleName, + rootFunction, + importedJsModules, + program, + kind = moduleKind + ) + } return CompilerResult(program.toString(), dts) } 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 e40e23e8aeb..f29cc5b6691 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 @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny import org.jetbrains.kotlin.backend.common.ir.isTopLevel import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsMangler import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.types.isUnit @@ -21,10 +22,24 @@ import org.jetbrains.kotlin.js.naming.isES5IdentifierStart import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty +fun mapToKey(declaration: T): String { + return with(JsMangler) { + if (declaration is IrDeclaration && isPublic(declaration)) { + declaration.hashedMangle.toString() + } else if (declaration is Signature) { + declaration.toString().hashMangle.toString() + } else "key_have_not_generated" + } +} + +fun JsMangler.isPublic(declaration: IrDeclaration) = + declaration.isExported() && declaration !is IrScript && declaration !is IrVariable && declaration !is IrValueParameter + class NameTable( val parent: NameTable<*>? = null, - private val reserved: MutableSet = mutableSetOf(), - val sanitizer: (String) -> String = ::sanitizeName + val reserved: MutableSet = mutableSetOf(), + val sanitizer: (String) -> String = ::sanitizeName, + val mappedNames: MutableMap = mutableMapOf() ) { var finished = false val names = mutableMapOf() @@ -40,6 +55,7 @@ class NameTable( assert(!finished) names[declaration] = name reserved.add(name) + mappedNames[mapToKey(declaration)] = name } fun declareFreshName(declaration: T, suggestedName: String): String { @@ -134,7 +150,12 @@ fun functionSignature(declaration: IrFunction): Signature { return ParameterTypeBasedSignature(signature, declarationName) } -class NameTables(packages: List) { +class NameTables( + packages: List, + reservedForGlobal: MutableSet = mutableSetOf(), + reservedForMember: MutableSet = mutableSetOf(), + val mappedNames: MutableMap = mutableMapOf() +) { val globalNames: NameTable private val memberNames: NameTable private val localNames = mutableMapOf>() @@ -144,61 +165,108 @@ class NameTables(packages: List) { val stableNamesCollector = StableNamesCollector() packages.forEach { it.acceptChildrenVoid(stableNamesCollector) } - globalNames = NameTable(reserved = stableNamesCollector.staticNames) - memberNames = NameTable(reserved = stableNamesCollector.memberNames) + globalNames = NameTable( + reserved = (stableNamesCollector.staticNames + reservedForGlobal).toMutableSet(), + mappedNames = mappedNames + ) + memberNames = NameTable( + reserved = (stableNamesCollector.memberNames + reservedForMember).toMutableSet(), + mappedNames = mappedNames + ) + + mappedNames.addAllIfAbsent(mappedNames) + + val classDeclaration = mutableListOf() for (p in packages) { for (declaration in p.declarations) { generateNamesForTopLevelDecl(declaration) + if (declaration is IrScript) { + for (memberDecl in declaration.declarations) { + generateNamesForTopLevelDecl(memberDecl) + if (memberDecl is IrClass) { + classDeclaration += memberDecl + } + } + } } } globalNames.finished = true + for (declaration in classDeclaration) { + acceptDeclaration(declaration) + } + for (p in packages) { for (declaration in p.declarations) { - val localNameGenerator = LocalNameGenerator(declaration) - - 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) - } - } - } - } else { - declaration.acceptChildrenVoid(localNameGenerator) - } + acceptDeclaration(declaration) } } } + private fun acceptDeclaration(declaration: IrDeclaration) { + val localNameGenerator = LocalNameGenerator(declaration) + + 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) + } + } + } + } else { + declaration.acceptChildrenVoid(localNameGenerator) + } + } + + private fun MutableMap.addAllIfAbsent(other: Map) { + this += other.filter { it.key !in this } + } + + private fun packagesAdded() = mappedNames.isEmpty() + + fun merge(files: List, additionalPackages: List) { + val packages = mutableListOf().also { it.addAll(files) } + if (packagesAdded()) packages.addAll(additionalPackages) + + val table = NameTables(packages, globalNames.reserved, memberNames.reserved, mappedNames) + + 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) + memberNames.reserved.addAll(table.memberNames.reserved) + } + private fun generateNameForMemberField(field: IrField) { require(!field.isTopLevel) require(!field.isStatic) @@ -257,12 +325,13 @@ class NameTables(packages: List) { parent = parent.parent } - error("Can't find name for declaration ${declaration.fqNameWhenAvailable}") + return mappedNames[mapToKey(declaration)] ?: error("Can't find name for declaration ${declaration.fqNameWhenAvailable}") } fun getNameForMemberField(field: IrField): String { val signature = fieldSignature(field) - val name = memberNames.names[signature] + val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)] + require(name != null) { "Can't find name for member field $field" } @@ -271,7 +340,7 @@ class NameTables(packages: List) { fun getNameForMemberFunction(function: IrSimpleFunction): String { val signature = functionSignature(function) - val name = memberNames.names[signature] + val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)] // TODO: Fix hack: Coroutines runtime currently relies on stable names // of `invoke` functions in FunctionN interfaces