Skip to content

Commit 9f94142

Browse files
JSMonkSpace Team
authored and
Space Team
committed
[K/JS] Rework ES modules part with squashed JsImport and right renaming strategy inside import/export statements
1 parent 153d7b9 commit 9f94142

File tree

28 files changed

+382
-257
lines changed

28 files changed

+382
-257
lines changed

compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ protected void addPlatformOptions(@NotNull List<String> $self, @NotNull K2JSComp
9090
moduleKindMap.put(K2JsArgumentConstants.MODULE_COMMONJS, ModuleKind.COMMON_JS);
9191
moduleKindMap.put(K2JsArgumentConstants.MODULE_AMD, ModuleKind.AMD);
9292
moduleKindMap.put(K2JsArgumentConstants.MODULE_UMD, ModuleKind.UMD);
93+
moduleKindMap.put(K2JsArgumentConstants.MODULE_ES, ModuleKind.ES);
9394

9495
sourceMapContentEmbeddingMap.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_ALWAYS, SourceMapSourceEmbedding.ALWAYS);
9596
sourceMapContentEmbeddingMap.put(K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_NEVER, SourceMapSourceEmbedding.NEVER);

compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ interface DelegateFactory {
4444
}
4545

4646
object DefaultDelegateFactory : DelegateFactory {
47+
fun <K : IrDeclaration, V> newDeclarationToValueMapping(): Mapping.Delegate<K, V> = newMappingImpl()
48+
4749
override fun <K : IrDeclaration, V : IrDeclaration> newDeclarationToDeclarationMapping(): Mapping.Delegate<K, V> = newMappingImpl()
4850

4951
override fun <K : IrDeclaration, V : Collection<IrDeclaration>> newDeclarationToDeclarationCollectionMapping(): Mapping.Delegate<K, V> = newMappingImpl()

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
1111
import org.jetbrains.kotlin.ir.declarations.*
1212

1313
class JsMapping : DefaultMapping() {
14-
val esClassWhichNeedBoxParameters = mutableSetOf<IrClass>()
15-
val esClassToPossibilityForOptimization = mutableMapOf<IrClass, MutableReference<Boolean>>()
14+
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
15+
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
1616

1717
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
1818
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class ExportModelToJsStatements(
8383
namespace != null ->
8484
listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt())
8585

86-
esModules -> listOf(JsExport(name, alias = JsName(declaration.name, false)))
86+
esModules -> listOf(JsExport(name.makeRef(), alias = JsName(declaration.name, false)))
8787
else -> emptyList()
8888
}
8989
}
@@ -96,7 +96,7 @@ class ExportModelToJsStatements(
9696
when {
9797
namespace == null -> {
9898
val property = declaration.generateTopLevelGetters()
99-
listOf(JsVars(property), JsExport(property.name, JsName(declaration.name, false)))
99+
listOf(JsVars(property), JsExport(property.name.makeRef(), JsName(declaration.name, false)))
100100
}
101101
es6mode && declaration.isMember -> {
102102
val jsClass = parentClass?.getCorrespondingJsClass() ?: error("Expect to have parentClass at this point")
@@ -168,7 +168,7 @@ class ExportModelToJsStatements(
168168
}
169169
val klassExport = when {
170170
namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
171-
esModules -> JsExport(name, alias = JsName(declaration.name, false))
171+
esModules -> JsExport(name.makeRef(), alias = JsName(declaration.name, false))
172172
else -> null
173173
}
174174

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/HashCalculatorForIC.kt

+9-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.js.config.JSConfigurationKeys
2020
import org.jetbrains.kotlin.library.impl.buffer
2121
import org.jetbrains.kotlin.protobuf.CodedInputStream
2222
import org.jetbrains.kotlin.protobuf.CodedOutputStream
23+
import org.jetbrains.kotlin.serialization.js.ModuleKind
2324
import java.security.MessageDigest
2425

2526
internal fun Hash128Bits.toProtoStream(out: CodedOutputStream) {
@@ -158,6 +159,13 @@ internal fun CrossModuleReferences.crossModuleReferencesHashForIC() = HashCalcul
158159
val import = imports[tag]!!
159160
update(tag)
160161
update(import.exportedAs)
161-
update(import.moduleExporter.toString())
162+
163+
if (moduleKind == ModuleKind.ES) {
164+
update(import.moduleExporter.internalName.toString())
165+
update(import.moduleExporter.externalName)
166+
update(import.moduleExporter.relativeRequirePath ?: "")
167+
} else {
168+
update(import.moduleExporter.internalName.toString())
169+
}
162170
}
163171
}.finalize()

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ES6ConstructorBoxParameterOptimizationLowering.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
2525
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
2626

2727
class ES6ConstructorBoxParameterOptimizationLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
28-
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
28+
private val IrClass.needsOfBoxParameter by context.mapping.esClassWhichNeedBoxParameters
2929

3030
override fun lower(irBody: IrBody, container: IrDeclaration) {
3131
if (!context.es6mode) return
@@ -85,12 +85,12 @@ class ES6ConstructorBoxParameterOptimizationLowering(private val context: JsIrBa
8585
}
8686

8787
private fun IrClass.requiredToHaveBoxParameter(): Boolean {
88-
return esClassWhichNeedBoxParameters.contains(this)
88+
return needsOfBoxParameter == true
8989
}
9090
}
9191

9292
class ES6CollectConstructorsWhichNeedBoxParameters(private val context: JsIrBackendContext) : DeclarationTransformer {
93-
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
93+
private var IrClass.needsOfBoxParameter by context.mapping.esClassWhichNeedBoxParameters
9494

9595
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
9696
if (!context.es6mode || declaration !is IrClass) return null
@@ -134,7 +134,7 @@ class ES6CollectConstructorsWhichNeedBoxParameters(private val context: JsIrBack
134134

135135
private fun IrClass.addToClassListWhichNeedBoxParameter() {
136136
if (isExternal) return
137-
esClassWhichNeedBoxParameters.add(this)
137+
needsOfBoxParameter = true
138138
superClass?.addToClassListWhichNeedBoxParameter()
139139
}
140140
}

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ES6PrimaryConstructorOptimizationLowering.kt

+7-7
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,16 @@ class ES6PrimaryConstructorUsageOptimizationLowering(private val context: JsIrBa
177177
* Otherwise, we can generate a simple ES-class constructor in each class of the hierarchy
178178
*/
179179
class ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
180-
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
181-
private val esClassToPossibilityForOptimization = context.mapping.esClassToPossibilityForOptimization
180+
private val IrClass.needsOfBoxParameter by context.mapping.esClassWhichNeedBoxParameters
181+
private var IrClass.possibilityToOptimizeForEsClass by context.mapping.esClassToPossibilityForOptimization
182182

183183
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
184184
if (
185185
context.es6mode &&
186186
declaration is IrClass &&
187187
!declaration.isExternal &&
188188
!context.inlineClassesUtils.isClassInlineLike(declaration) &&
189-
!esClassToPossibilityForOptimization.contains(declaration)
189+
declaration.possibilityToOptimizeForEsClass == null
190190
) {
191191
declaration.checkIfCanBeOptimized()
192192
}
@@ -199,7 +199,7 @@ class ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering(private val con
199199
var nearestOptimizationDecision: MutableReference<Boolean>? = null
200200

201201
while (currentClass != null && !currentClass.isExternal) {
202-
val currentClassOptimizationDecision = esClassToPossibilityForOptimization[currentClass]
202+
val currentClassOptimizationDecision = currentClass.possibilityToOptimizeForEsClass
203203

204204
if (currentClassOptimizationDecision != null) {
205205
nearestOptimizationDecision = currentClassOptimizationDecision
@@ -214,8 +214,8 @@ class ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering(private val con
214214
}
215215

216216
currentClass = this
217-
while (currentClass != null && !currentClass.isExternal && !esClassToPossibilityForOptimization.contains(currentClass)) {
218-
esClassToPossibilityForOptimization[currentClass] = nearestOptimizationDecision
217+
while (currentClass != null && !currentClass.isExternal && currentClass.possibilityToOptimizeForEsClass == null) {
218+
currentClass.possibilityToOptimizeForEsClass = nearestOptimizationDecision
219219

220220
if (nearestOptimizationDecision.value && !currentClass.canBeOptimized()) {
221221
nearestOptimizationDecision.value = false
@@ -249,7 +249,7 @@ class ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering(private val con
249249
}
250250

251251
private fun IrClass.isSubclassOfExternalClassWithRequiredBoxParameter(): Boolean {
252-
return superClass?.isExternal == true && esClassWhichNeedBoxParameters.contains(this)
252+
return superClass?.isExternal == true && needsOfBoxParameter == true
253253
}
254254
}
255255

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/CompilationOutputs.kt

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
77

88
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
99
import org.jetbrains.kotlin.ir.backend.js.export.toTypeScript
10+
import org.jetbrains.kotlin.js.backend.ast.ESM_EXTENSION
1011
import org.jetbrains.kotlin.js.backend.ast.JsProgram
12+
import org.jetbrains.kotlin.js.backend.ast.REGULAR_EXTENSION
1113
import org.jetbrains.kotlin.serialization.js.ModuleKind
1214
import java.io.File
1315
import java.nio.file.Files
1416

17+
val ModuleKind.extension: String
18+
get() = when (this) {
19+
ModuleKind.ES -> ESM_EXTENSION
20+
else -> REGULAR_EXTENSION
21+
}
22+
1523
abstract class CompilationOutputs {
1624
var dependencies: Collection<Pair<String, CompilationOutputs>> = emptyList()
1725

@@ -35,10 +43,10 @@ abstract class CompilationOutputs {
3543
}
3644

3745
dependencies.forEach { (name, content) ->
38-
outputDir.resolve("$name.js").writeAsJsFile(content)
46+
outputDir.resolve("$name${moduleKind.extension}").writeAsJsFile(content)
3947
}
4048

41-
val outputJsFile = outputDir.resolve("$outputName.js")
49+
val outputJsFile = outputDir.resolve("$outputName${moduleKind.extension}")
4250
outputJsFile.writeAsJsFile(this)
4351

4452
if (genDTS) {

compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIrProgramFragment.kt

+64-38
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@ class CrossModuleDependenciesResolver(
9494
private val headers: List<JsIrModuleHeader>
9595
) {
9696
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
97-
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferecenceBuilder(moduleKind, it, relativeRequirePath) }
98-
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferecenceBuilder>()
97+
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferenceBuilder(moduleKind, it, relativeRequirePath) }
98+
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
9999

100-
val mainModuleHeader = headers.last()
101-
val otherModuleHeaders = headers.dropLast(1)
102-
headerToBuilder[mainModuleHeader]!!.transitiveJsExportFrom = otherModuleHeaders
100+
if (moduleKind != ModuleKind.ES) {
101+
val mainModuleHeader = headers.last()
102+
val otherModuleHeaders = headers.dropLast(1)
103+
headerToBuilder[mainModuleHeader]!!.transitiveJsExportFrom = otherModuleHeaders
104+
}
103105

104106
for (header in headers) {
105107
val builder = headerToBuilder[header]!!
@@ -130,9 +132,9 @@ class CrossModuleDependenciesResolver(
130132
}
131133
}
132134

133-
private class CrossModuleRef(val module: JsIrModuleCrossModuleReferecenceBuilder, val tag: String)
135+
private class CrossModuleRef(val module: JsIrModuleCrossModuleReferenceBuilder, val tag: String)
134136

135-
private class JsIrModuleCrossModuleReferecenceBuilder(
137+
private class JsIrModuleCrossModuleReferenceBuilder(
136138
val moduleKind: ModuleKind,
137139
val header: JsIrModuleHeader,
138140
val relativeRequirePath: Boolean
@@ -150,20 +152,15 @@ private class JsIrModuleCrossModuleReferecenceBuilder(
150152

151153
fun buildCrossModuleRefs(): CrossModuleReferences {
152154
buildExportNames()
155+
val isImportOptional = moduleKind == ModuleKind.ES
153156
val importedModules = mutableMapOf<JsIrModuleHeader, JsImportedModule>()
154157

155-
fun import(moduleHeader: JsIrModuleHeader): JsName {
156-
return importedModules.getOrPut(moduleHeader) {
157-
val jsModuleName = JsName(moduleHeader.moduleName, false)
158-
val relativeRequirePath = relativeRequirePath(moduleHeader)
159-
160-
JsImportedModule(
161-
moduleHeader.externalModuleName,
162-
jsModuleName,
163-
null,
164-
relativeRequirePath
165-
)
166-
}.internalName
158+
fun import(moduleHeader: JsIrModuleHeader): JsImportedModule {
159+
return if (isImportOptional) {
160+
moduleHeader.toJsImportedModule()
161+
} else {
162+
importedModules.getOrPut(moduleHeader) { moduleHeader.toJsImportedModule() }
163+
}
167164
}
168165

169166
val resultImports = imports.associate { crossModuleRef ->
@@ -173,13 +170,13 @@ private class JsIrModuleCrossModuleReferecenceBuilder(
173170
"Cross module dependency resolution failed due to signature '$tag' redefinition"
174171
}
175172
val exportedAs = crossModuleRef.module.exportNames[tag]!!
176-
val moduleName = import(crossModuleRef.module.header)
173+
val importedModule = import(crossModuleRef.module.header)
177174

178-
tag to CrossModuleImport(exportedAs, moduleName)
175+
tag to CrossModuleImport(exportedAs, importedModule)
179176
}
180177

181178
val transitiveExport = transitiveJsExportFrom.mapNotNull {
182-
if (!it.hasJsExports) null else CrossModuleTransitiveExport(import(it), it.externalModuleName)
179+
if (!it.hasJsExports) null else CrossModuleTransitiveExport(import(it).internalName, it.externalModuleName)
183180
}
184181
return CrossModuleReferences(
185182
moduleKind,
@@ -190,12 +187,22 @@ private class JsIrModuleCrossModuleReferecenceBuilder(
190187
)
191188
}
192189

190+
private fun JsIrModuleHeader.toJsImportedModule(): JsImportedModule {
191+
val jsModuleName = JsName(moduleName, false)
192+
val relativeRequirePath = relativeRequirePath(this)
193+
194+
return JsImportedModule(
195+
externalModuleName,
196+
jsModuleName,
197+
null,
198+
relativeRequirePath
199+
)
200+
}
201+
193202
private fun relativeRequirePath(moduleHeader: JsIrModuleHeader): String? {
194203
if (!this.relativeRequirePath) return null
195204

196-
val parentMain = File(header.externalModuleName).parentFile
197-
198-
if (parentMain == null) return "./${moduleHeader.externalModuleName}"
205+
val parentMain = File(header.externalModuleName).parentFile ?: return "./${moduleHeader.externalModuleName}"
199206

200207
val relativePath = File(moduleHeader.externalModuleName)
201208
.toRelativeString(parentMain)
@@ -206,10 +213,12 @@ private class JsIrModuleCrossModuleReferecenceBuilder(
206213
}
207214
}
208215

209-
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsName)
216+
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsImportedModule)
210217

211218
class CrossModuleTransitiveExport(val internalName: JsName, val externalName: String)
212219

220+
fun CrossModuleTransitiveExport.getRequireEsmName() = "$externalName$ESM_EXTENSION"
221+
213222
class CrossModuleReferences(
214223
val moduleKind: ModuleKind,
215224
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
@@ -218,28 +227,45 @@ class CrossModuleReferences(
218227
val imports: Map<String, CrossModuleImport>, // tag -> import statement
219228
) {
220229
// built from imports
221-
var jsImports = emptyMap<String, JsVars.JsVar>() // tag -> import statement
230+
var jsImports = emptyMap<String, JsStatement>() // tag -> import statement
222231
private set
223232

224233
fun initJsImportsForModule(module: JsIrModule) {
225234
val tagToName = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to it.value }
226235
jsImports = imports.entries.associate {
227236
val importedAs = tagToName[it.key] ?: error("Internal error: cannot find imported name for signature ${it.key}")
228-
val exportRef = JsNameRef(
229-
it.value.exportedAs,
230-
it.value.moduleExporter.let {
231-
if (moduleKind == ModuleKind.ES) {
232-
it.makeRef()
233-
} else {
234-
ReservedJsNames.makeCrossModuleNameRef(it)
235-
}
236-
}
237-
)
238-
it.key to JsVars.JsVar(importedAs, exportRef)
237+
it.key to it.value.generateCrossModuleImportStatement(importedAs)
239238
}
240239
}
241240

241+
private fun CrossModuleImport.generateCrossModuleImportStatement(importedAs: JsName): JsStatement {
242+
return when (moduleKind) {
243+
ModuleKind.ES -> generateJsImportStatement(importedAs)
244+
else -> generateImportVariableDeclaration(importedAs)
245+
}
246+
}
247+
248+
private fun CrossModuleImport.generateImportVariableDeclaration(importedAs: JsName): JsStatement {
249+
val exportRef = JsNameRef(exportedAs, ReservedJsNames.makeCrossModuleNameRef(moduleExporter.internalName))
250+
return JsVars(JsVars.JsVar(importedAs, exportRef))
251+
}
252+
253+
private fun CrossModuleImport.generateJsImportStatement(importedAs: JsName): JsStatement {
254+
return JsImport(
255+
moduleExporter.getRequireName(true),
256+
JsImport.Element(JsName(exportedAs, false), importedAs.makeRef())
257+
)
258+
}
259+
242260
companion object {
243261
fun Empty(moduleKind: ModuleKind) = CrossModuleReferences(moduleKind, listOf(), emptyList(), emptyMap(), emptyMap())
244262
}
245263
}
264+
265+
fun JsStatement.renameImportedSymbolInternalName(newName: JsName): JsStatement {
266+
return when (this) {
267+
is JsImport -> JsImport(module, JsImport.Element((target as JsImport.Target.Elements).elements.single().name, newName.makeRef()))
268+
is JsVars -> JsVars(JsVars.JsVar(newName, vars.single().initExpression))
269+
else -> error("Unexpected cross-module import statement ${this::class.qualifiedName}")
270+
}
271+
}

0 commit comments

Comments
 (0)