rra/ilgonmic/reducing-size
[JS IR] Fix review remarks - Fix translation mode from flags calculation - comment why not top level could be safely inlined [JS IR] Add test on exportness of internal val Revert "[Gradle, JS] Disable minimizing names in production by default" This reverts commit 700ff8634a73f155a2f0eaf963778216d6877075. [JS IR] Adopt reserved names Revert "[JS IR] Reserve interop names" This reverts commit 63f30becaf1a45750ff85aea3753aae9a85985f4. Revert "[JS IR] Clear minimized namer states" This reverts commit ef7f19fa8a928021e8bdfbee7fbb5285fc74b7ab. [JS IR] Clear minimized namer states [JS IR] Reserve interop names [JS IR] Separate arguments for users [JS IR] Minimized member names in tests [Gradle, JS] Disable minimizing names in production by default [JS IR] Fix extra helps [JS IR] Move mangling of signatures to linking namer [JS IR] Add key and minimized name generator [JS IR] cross module dependencies minimized [JS IR] Minimize names [JS IR] Cross module by index [JS IR] Fix warning [JS IR] Fix inlining accessors [JS IR] Bridges for property accessors [JS IR] Special case of jsFunctionSignature with property accessors [JS IR] Inline property accessor for non top level properties [JS IR] Reduce crossModule size Merge-request: KT-MR-5785 Merged-by: Ilya Goncharov <Ilya.Goncharov@jetbrains.com> ^KT-51735 fixed ^KT-50504 fixed
This commit is contained in:
@@ -13,6 +13,7 @@ object JsLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object JS_CLOSURE_BOX_CLASS_DECLARATION : IrDeclarationOriginImpl("JS_CLOSURE_BOX_CLASS_DECLARATION")
|
||||
object BRIDGE_WITH_STABLE_NAME : IrDeclarationOriginImpl("BRIDGE_WITH_STABLE_NAME")
|
||||
object BRIDGE_WITHOUT_STABLE_NAME : IrDeclarationOriginImpl("BRIDGE_WITHOUT_STABLE_NAME")
|
||||
object BRIDGE_PROPERTY_ACCESSOR : IrDeclarationOriginImpl("BRIDGE_PROPERTY_ACCESSOR")
|
||||
object OBJECT_GET_INSTANCE_FUNCTION : IrDeclarationOriginImpl("OBJECT_GET_INSTANCE_FUNCTION")
|
||||
object JS_SHADOWED_EXPORT : IrDeclarationOriginImpl("JS_SHADOWED_EXPORT")
|
||||
object JS_SHADOWED_DEFAULT_PARAMETER : IrDeclarationOriginImpl("JS_SHADOWED_DEFAULT_PARAMETER")
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsPolyfills
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsInlineClassesUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.MinimizedNameGenerator
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -72,6 +73,11 @@ class JsIrBackendContext(
|
||||
val localClassNames: MutableMap<IrClass, String> = mutableMapOf()
|
||||
val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
|
||||
|
||||
val minimizedNameGenerator: MinimizedNameGenerator =
|
||||
MinimizedNameGenerator()
|
||||
|
||||
val fieldDataCache = mutableMapOf<IrClass, Map<IrField, String>>()
|
||||
|
||||
override val builtIns = module.builtIns
|
||||
|
||||
override val typeSystem: IrTypeSystemContext = IrTypeSystemContextImpl(irBuiltIns)
|
||||
|
||||
+1
@@ -549,6 +549,7 @@ class ExportModelGenerator(
|
||||
if (function.isFakeOverride && !function.isAllowedFakeOverriddenDeclaration(context))
|
||||
return Exportability.NotNeeded
|
||||
if (function.origin == JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME ||
|
||||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR ||
|
||||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME ||
|
||||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
function.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION ||
|
||||
|
||||
+5
-4
@@ -52,10 +52,11 @@ class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<J
|
||||
}
|
||||
|
||||
override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin =
|
||||
if (bridge.hasStableJsName(context))
|
||||
JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME
|
||||
else
|
||||
JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME
|
||||
when {
|
||||
bridge.hasStableJsName(context) -> JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME
|
||||
bridge.correspondingPropertySymbol != null -> JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR
|
||||
else -> JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME
|
||||
}
|
||||
|
||||
override fun extractValueParameters(
|
||||
blockBodyBuilder: IrBlockBodyBuilder,
|
||||
|
||||
+4
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
|
||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
@@ -19,6 +20,9 @@ class JsPropertyAccessorInlineLowering(
|
||||
if (!isSafeToInlineInClosedWorld())
|
||||
return false
|
||||
|
||||
// Member properties could be safely inlined, because initialization processed via parent declaration
|
||||
if (!isTopLevel) return true
|
||||
|
||||
// TODO: teach the deserializer to load constant property initializers
|
||||
if (context.icCompatibleIr2Js) {
|
||||
val accessFile = accessContainer.fileOrNull ?: return false
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ class IrModuleToJsTransformer(
|
||||
|
||||
val code = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java)
|
||||
|
||||
if (fullJs) code[TranslationMode.fromFlags(false, multiModule)] = generateWrappedModuleBody(modules, exportedModule, namer)
|
||||
if (fullJs) code[TranslationMode.fromFlags(false, multiModule, false)] = generateWrappedModuleBody(modules, exportedModule, namer)
|
||||
|
||||
if (dceJs) {
|
||||
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
|
||||
@@ -76,7 +76,7 @@ class IrModuleToJsTransformer(
|
||||
// TODO: is this mode relevant for scripting? If yes, refactor so that the external name tables are used here when needed.
|
||||
val namer = NameTables(emptyList(), context = backendContext)
|
||||
namer.merge(modules.flatMap { it.files }, additionalPackages)
|
||||
code[TranslationMode.fromFlags(true, multiModule)] = generateWrappedModuleBody(modules, exportedModule, namer)
|
||||
code[TranslationMode.fromFlags(true, multiModule, false)] = generateWrappedModuleBody(modules, exportedModule, namer)
|
||||
}
|
||||
|
||||
return CompilerResult(code, dts)
|
||||
|
||||
+37
-16
@@ -33,18 +33,30 @@ import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
enum class TranslationMode(val dce: Boolean, val perModule: Boolean) {
|
||||
FULL(dce = false, perModule = false),
|
||||
FULL_DCE(dce = true, perModule = false),
|
||||
PER_MODULE(dce = false, perModule = true),
|
||||
PER_MODULE_DCE(dce = true, perModule = true);
|
||||
enum class TranslationMode(
|
||||
val dce: Boolean,
|
||||
val perModule: Boolean,
|
||||
val minimizedMemberNames: Boolean,
|
||||
) {
|
||||
FULL(dce = false, perModule = false, minimizedMemberNames = false),
|
||||
FULL_DCE(dce = true, perModule = false, minimizedMemberNames = false),
|
||||
FULL_DCE_MINIMIZED_NAMES(dce = true, perModule = false, minimizedMemberNames = true),
|
||||
PER_MODULE(dce = false, perModule = true, minimizedMemberNames = false),
|
||||
PER_MODULE_DCE(dce = true, perModule = true, minimizedMemberNames = false),
|
||||
PER_MODULE_DCE_MINIMIZED_NAMES(dce = true, perModule = true, minimizedMemberNames = true);
|
||||
|
||||
companion object {
|
||||
fun fromFlags(dce: Boolean, perModule: Boolean): TranslationMode {
|
||||
fun fromFlags(dce: Boolean, perModule: Boolean, minimizedMemberNames: Boolean): TranslationMode {
|
||||
return if (perModule) {
|
||||
if (dce) PER_MODULE_DCE else PER_MODULE
|
||||
if (dce) {
|
||||
if (minimizedMemberNames) PER_MODULE_DCE_MINIMIZED_NAMES
|
||||
else PER_MODULE_DCE
|
||||
} else PER_MODULE
|
||||
} else {
|
||||
if (dce) FULL_DCE else FULL
|
||||
if (dce) {
|
||||
if (minimizedMemberNames) FULL_DCE_MINIMIZED_NAMES
|
||||
else FULL_DCE
|
||||
} else FULL
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,11 +90,11 @@ class IrModuleToJsTransformerTmp(
|
||||
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||
}
|
||||
|
||||
fun compilationOutput(multiModule: Boolean) = generateWrappedModuleBody(
|
||||
fun compilationOutput(multiModule: Boolean, minimizedMemberNames: Boolean) = generateWrappedModuleBody(
|
||||
multiModule,
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
generateProgramFragments(modules, exportData),
|
||||
generateProgramFragments(modules, exportData, minimizedMemberNames),
|
||||
SourceMapsInfo.from(backendContext.configuration),
|
||||
relativeRequirePath,
|
||||
generateScriptModule,
|
||||
@@ -91,7 +103,11 @@ class IrModuleToJsTransformerTmp(
|
||||
val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java)
|
||||
|
||||
modes.filter { !it.dce }.forEach {
|
||||
result[it] = compilationOutput(it.perModule)
|
||||
if (it.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
result[it] = compilationOutput(it.perModule, it.minimizedMemberNames)
|
||||
}
|
||||
|
||||
if (modes.any { it.dce }) {
|
||||
@@ -99,7 +115,11 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
modes.filter { it.dce }.forEach {
|
||||
result[it] = compilationOutput(it.perModule)
|
||||
if (it.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
result[it] = compilationOutput(it.perModule, it.minimizedMemberNames)
|
||||
}
|
||||
|
||||
return CompilerResult(result, dts)
|
||||
@@ -123,7 +143,7 @@ class IrModuleToJsTransformerTmp(
|
||||
val result = mutableMapOf<String, ByteArray>()
|
||||
files.forEach { f ->
|
||||
val exports = exportData[f]!! // TODO
|
||||
val fragment = generateProgramFragment(f, exports)
|
||||
val fragment = generateProgramFragment(f, exports, minimizedMemberNames = false)
|
||||
val output = ByteArrayOutputStream()
|
||||
serializer.serialize(fragment, output)
|
||||
val binaryAst = output.toByteArray()
|
||||
@@ -146,6 +166,7 @@ class IrModuleToJsTransformerTmp(
|
||||
private fun generateProgramFragments(
|
||||
modules: Iterable<IrModuleFragment>,
|
||||
exportData: Map<IrModuleFragment, Map<IrFile, List<ExportedDeclaration>>>,
|
||||
minimizedMemberNames: Boolean
|
||||
): JsIrProgram {
|
||||
return JsIrProgram(
|
||||
modules.map { m ->
|
||||
@@ -154,7 +175,7 @@ class IrModuleToJsTransformerTmp(
|
||||
m.externalModuleName(),
|
||||
m.files.map {
|
||||
val exports = exportData[m]!![it]!!
|
||||
generateProgramFragment(it, exports)
|
||||
generateProgramFragment(it, exports, minimizedMemberNames)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -164,8 +185,8 @@ class IrModuleToJsTransformerTmp(
|
||||
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||
|
||||
private fun generateProgramFragment(file: IrFile, exports: List<ExportedDeclaration>): JsIrProgramFragment {
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext)
|
||||
private fun generateProgramFragment(file: IrFile, exports: List<ExportedDeclaration>, minimizedMemberNames: Boolean): JsIrProgramFragment {
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames)
|
||||
|
||||
val globalNameScope = NameTable<IrDeclaration>()
|
||||
|
||||
|
||||
+6
-10
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
class JsIrProgramFragment(val packageFqn: String) {
|
||||
@@ -75,19 +75,15 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val module: JsIrModule, va
|
||||
val exports = mutableSetOf<String>()
|
||||
var transitiveJsExportFrom = emptyList<JsIrModule>()
|
||||
|
||||
private lateinit var exportNames: Map<String, String> // tag -> name
|
||||
private lateinit var exportNames: Map<String, String> // tag -> index
|
||||
|
||||
private fun buildUniqueNames() {
|
||||
val names = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to sanitizeName(it.value.ident) }
|
||||
val nameToCnt = mutableMapOf<String, Int>()
|
||||
|
||||
val result = mutableMapOf<String, String>()
|
||||
|
||||
var index = 0
|
||||
|
||||
exports.sorted().forEach { tag ->
|
||||
val suggestedName = names[tag] ?: error("Name not found for tag $tag")
|
||||
val suffix = nameToCnt[suggestedName]?.let { "_$it" } ?: ""
|
||||
nameToCnt[suggestedName] = nameToCnt.getOrDefault(suggestedName, 0) + 1
|
||||
result[tag] = suggestedName + suffix
|
||||
result[tag] = index++.toJsIdentifier()
|
||||
}
|
||||
|
||||
exportNames = result
|
||||
@@ -132,7 +128,7 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val module: JsIrModule, va
|
||||
class CrossModuleReferences(
|
||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
||||
val imports: Map<String, JsVars.JsVar>, // tag -> import statement
|
||||
val exports: Map<String, String>, // tag -> name
|
||||
val exports: Map<String, String>, // tag -> index
|
||||
val transitiveJsExportFrom: List<JsName> // the list of modules which provide their js exports for transitive export
|
||||
) {
|
||||
companion object {
|
||||
|
||||
+39
-8
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
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.symbols.IrClassSymbol
|
||||
@@ -17,13 +18,15 @@ import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class JsNameLinkingNamer(private val context: JsIrBackendContext) : IrNamerBase() {
|
||||
class JsNameLinkingNamer(private val context: JsIrBackendContext, private val minimizedMemberNames: Boolean) : IrNamerBase() {
|
||||
|
||||
val nameMap = mutableMapOf<IrDeclaration, JsName>()
|
||||
|
||||
private fun IrDeclarationWithName.getName(prefix: String = ""): JsName {
|
||||
return nameMap.getOrPut(this) {
|
||||
val name = (this as? IrClass)?.let { context.localClassNames[this] } ?: getJsNameOrKotlinName().asString()
|
||||
val name = (this as? IrClass)?.let { context.localClassNames[this] } ?: let {
|
||||
this.nameIfPropertyAccessor() ?: getJsNameOrKotlinName().asString()
|
||||
}
|
||||
JsName(sanitizeName(prefix + name), true)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +85,11 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext) : IrNamerBase(
|
||||
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||
require(function.dispatchReceiverParameter != null)
|
||||
val signature = jsFunctionSignature(function, context)
|
||||
return signature.toJsName()
|
||||
val result = if (minimizedMemberNames && !function.hasStableJsName(context)) {
|
||||
function.parentAsClass.fieldData()
|
||||
context.minimizedNameGenerator.nameBySignature(signature)
|
||||
} else signature
|
||||
return result.toJsName()
|
||||
}
|
||||
|
||||
override fun getNameForMemberField(field: IrField): JsName {
|
||||
@@ -91,10 +98,8 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext) : IrNamerBase(
|
||||
return JsName(field.parentAsClass.fieldData()[field]!!, false)
|
||||
}
|
||||
|
||||
private val fieldDataCache = mutableMapOf<IrClass, Map<IrField, String>>()
|
||||
|
||||
private fun IrClass.fieldData(): Map<IrField, String> {
|
||||
return fieldDataCache.getOrPut(this) {
|
||||
return context.fieldDataCache.getOrPut(this) {
|
||||
val nameCnt = mutableMapOf<String, Int>()
|
||||
|
||||
val allClasses = DFS.topologicalOrder(listOf(this)) { node ->
|
||||
@@ -105,11 +110,37 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext) : IrNamerBase(
|
||||
|
||||
val result = mutableMapOf<IrField, String>()
|
||||
|
||||
if (minimizedMemberNames) {
|
||||
allClasses.reversed().forEach {
|
||||
it.declarations.forEach { declaration ->
|
||||
when {
|
||||
declaration is IrFunction && declaration.dispatchReceiverParameter != null -> {
|
||||
val property = (declaration as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
|
||||
if (property?.isExported(context) == true || property?.isEffectivelyExternal() == true) {
|
||||
context.minimizedNameGenerator.reserveName(property.name.asString())
|
||||
}
|
||||
if (declaration.hasStableJsName(context)) {
|
||||
val signature = jsFunctionSignature(declaration, context)
|
||||
context.minimizedNameGenerator.reserveName(signature)
|
||||
}
|
||||
}
|
||||
declaration is IrProperty -> {
|
||||
if (declaration.isExported(context)) {
|
||||
context.minimizedNameGenerator.reserveName(declaration.name.asString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allClasses.reversed().forEach {
|
||||
it.declarations.forEach {
|
||||
when {
|
||||
it is IrField -> {
|
||||
val safeName = it.safeName()
|
||||
val safeName = if (minimizedMemberNames) {
|
||||
context.minimizedNameGenerator.generateNextName()
|
||||
} else it.safeName()
|
||||
val suffix = nameCnt.getOrDefault(safeName, 0) + 1
|
||||
nameCnt[safeName] = suffix
|
||||
result[it] = safeName + "_$suffix"
|
||||
@@ -121,7 +152,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext) : IrNamerBase(
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -71,10 +71,10 @@ class Merger(
|
||||
).makeStmt()
|
||||
additionalExports += createExportBlock
|
||||
|
||||
crossModuleReferences.exports.entries.forEach { (tag, name) ->
|
||||
crossModuleReferences.exports.entries.forEach { (tag, hash) ->
|
||||
val internalName = nameMap[tag] ?: error("Missing name for declaration '$tag'")
|
||||
val crossModuleRef = ReservedJsNames.makeCrossModuleNameRef(ReservedJsNames.makeInternalModuleName())
|
||||
additionalExports += jsAssignment(JsNameRef(name, crossModuleRef), JsNameRef(internalName)).makeStmt()
|
||||
additionalExports += jsAssignment(JsNameRef(hash, crossModuleRef), JsNameRef(internalName)).makeStmt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,6 +12,6 @@ class ReservedJsNames {
|
||||
companion object {
|
||||
fun makeInternalModuleName() = JsName("_", false)
|
||||
fun makeJsExporterName() = JsName("\$jsExportAll\$", false)
|
||||
fun makeCrossModuleNameRef(moduleName: JsName) = JsNameRef("\$crossModule\$", moduleName.makeRef())
|
||||
fun makeCrossModuleNameRef(moduleName: JsName) = JsNameRef("\$_\$", moduleName.makeRef())
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
class MinimizedNameGenerator {
|
||||
private var index = 0
|
||||
private val functionSignatureToName = mutableMapOf<String, String>()
|
||||
private val reservedNames = mutableSetOf<String>()
|
||||
|
||||
fun generateNextName(): String {
|
||||
var candidate = index++.toJsIdentifier()
|
||||
while (candidate in reservedNames) {
|
||||
candidate = index++.toJsIdentifier()
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
fun nameBySignature(signature: String): String {
|
||||
return functionSignatureToName.getOrPut(signature) {
|
||||
generateNextName()
|
||||
}
|
||||
}
|
||||
|
||||
fun reserveName(signature: String) {
|
||||
reservedNames.add(signature)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
index = 0
|
||||
functionSignatureToName.clear()
|
||||
reservedNames.clear()
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -105,11 +106,21 @@ fun NameTable<IrDeclaration>.dump(): String =
|
||||
|
||||
private const val RESERVED_MEMBER_NAME_SUFFIX = "_k$"
|
||||
|
||||
fun Int.toJsIdentifier(): String {
|
||||
val first = ('a'.code + (this % 26)).toChar().toString()
|
||||
val other = this / 26
|
||||
return if (other == 0) {
|
||||
first
|
||||
} else {
|
||||
first + other.toString(Character.MAX_RADIX)
|
||||
}
|
||||
}
|
||||
|
||||
fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext): String {
|
||||
require(!declaration.isStaticMethodOfClass)
|
||||
require(declaration.dispatchReceiverParameter != null)
|
||||
|
||||
val declarationName = declaration.getJsNameOrKotlinName().asString()
|
||||
var declarationName = declaration.getJsNameOrKotlinName().asString()
|
||||
|
||||
if (declaration.hasStableJsName(context)) {
|
||||
// TODO: Handle reserved suffix in FE
|
||||
@@ -119,6 +130,10 @@ fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext): S
|
||||
return declarationName
|
||||
}
|
||||
|
||||
declaration.nameIfPropertyAccessor()?.let {
|
||||
declarationName = it
|
||||
}
|
||||
|
||||
val nameBuilder = StringBuilder()
|
||||
nameBuilder.append(declarationName)
|
||||
|
||||
@@ -139,7 +154,10 @@ fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext): S
|
||||
val signature = nameBuilder.toString()
|
||||
|
||||
// TODO: Use better hashCode
|
||||
return sanitizeName(declarationName) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX
|
||||
return sanitizeName(
|
||||
declarationName,
|
||||
withHash = false
|
||||
) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX
|
||||
}
|
||||
|
||||
class NameTables(
|
||||
@@ -295,8 +313,10 @@ class NameTables(
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
globalNames.declareFreshName(declaration, declaration.name.asString())
|
||||
else -> {
|
||||
val name = declaration.nameIfPropertyAccessor() ?: declaration.name.asString()
|
||||
globalNames.declareFreshName(declaration, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,12 +413,38 @@ fun sanitizeName(name: String, withHash: Boolean = true): String {
|
||||
}
|
||||
|
||||
return if (withHash) {
|
||||
"${builder}_${name.hashCode().toUInt()}"
|
||||
"${builder}_${abs(name.hashCode()).toString(Character.MAX_RADIX)}"
|
||||
} else {
|
||||
builder.toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun IrDeclarationWithName.nameIfPropertyAccessor(): String? {
|
||||
if (this is IrSimpleFunction) {
|
||||
return when {
|
||||
this.correspondingPropertySymbol != null -> {
|
||||
val property = this.correspondingPropertySymbol!!.owner
|
||||
val name = property.getJsNameOrKotlinName().asString()
|
||||
val prefix = when (this) {
|
||||
property.getter -> "get_"
|
||||
property.setter -> "set_"
|
||||
else -> error("")
|
||||
}
|
||||
prefix + name
|
||||
}
|
||||
this.origin == JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR -> {
|
||||
this.getJsNameOrKotlinName().asString()
|
||||
.removePrefix("<")
|
||||
.removeSuffix(">")
|
||||
.replaceFirst("get-", "get_")
|
||||
.replaceFirst("set-", "set_")
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private inline fun Char.mangleIfNot(predicate: Char.() -> Boolean) =
|
||||
if (predicate()) this else '_'
|
||||
|
||||
|
||||
@@ -157,7 +157,8 @@ class NewNamerImpl(
|
||||
|
||||
|
||||
} else { // Non-external declaration
|
||||
staticNames.declareFreshName(declaration, declaration.name.asString())
|
||||
val name = declaration.nameIfPropertyAccessor() ?: declaration.name.asString()
|
||||
staticNames.declareFreshName(declaration, name)
|
||||
val unitReference = unit.referenceCodegenUnitOfDeclaration(declaration)
|
||||
if (unitReference is IrToJs.OtherUnitReference) {
|
||||
registerImport(unitReference.importPath, exportId(declaration))
|
||||
|
||||
@@ -8,17 +8,16 @@ package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
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.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.isNullableAny
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
@@ -38,7 +37,8 @@ fun IrFunction.hasStableJsName(context: JsIrBackendContext): Boolean {
|
||||
if (
|
||||
origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT ||
|
||||
origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
|
||||
origin == JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME
|
||||
origin == JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME ||
|
||||
origin == JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user