[K/JS] Add file-to-file compilation ^KT-6168 Fixed
This commit is contained in:
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
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.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsPolyfills
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.translateJsCodeIntoStatementList
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLamb
|
||||
import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering
|
||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.cleanup.CleanupLowering
|
||||
@@ -24,6 +23,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToFunc
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendArityStoreLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
|
||||
@@ -840,15 +840,6 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
||||
description = "Clean up IR before codegen"
|
||||
)
|
||||
|
||||
private val moveOpenClassesToSeparatePlaceLowering = makeCustomJsModulePhase(
|
||||
{ context, module ->
|
||||
if (context.granularity == JsGenerationGranularity.PER_FILE)
|
||||
moveOpenClassesToSeparateFiles(module)
|
||||
},
|
||||
name = "MoveOpenClassesToSeparateFiles",
|
||||
description = "Move open classes to separate files"
|
||||
).toModuleLowering()
|
||||
|
||||
private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase(
|
||||
::JsSuspendArityStoreLowering,
|
||||
name = "JsSuspendArityStoreLowering",
|
||||
@@ -962,7 +953,6 @@ val loweringList = listOf<Lowering>(
|
||||
cleanupLoweringPhase,
|
||||
// Currently broken due to static members lowering making single-open-class
|
||||
// files non-recognizable as single-class files
|
||||
// moveOpenClassesToSeparatePlaceLowering,
|
||||
validateIrAfterLowering,
|
||||
)
|
||||
|
||||
|
||||
@@ -9,11 +9,15 @@ import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory
|
||||
import org.jetbrains.kotlin.backend.common.DefaultMapping
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import java.util.WeakHashMap
|
||||
|
||||
class JsMapping : DefaultMapping() {
|
||||
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
|
||||
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
|
||||
|
||||
// Check if we need concurrency here
|
||||
val chunkToOriginalFile = WeakHashMap<IrFile, IrFile>()
|
||||
|
||||
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
||||
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
|
||||
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.codegen
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
PER_FILE
|
||||
}
|
||||
@@ -10,12 +10,12 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaserState
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.JsIrCompilerICInterface
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
|
||||
@@ -53,7 +52,7 @@ class JsIrCompilerWithIC(
|
||||
allModules: Collection<IrModuleFragment>,
|
||||
dirtyFiles: Collection<IrFile>,
|
||||
mainArguments: List<String>?
|
||||
): List<() -> JsIrProgramFragment> {
|
||||
): List<() -> List<JsIrProgramFragment>> {
|
||||
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
||||
|
||||
allModules.forEach {
|
||||
|
||||
+1
-3
@@ -159,7 +159,7 @@ internal class JsUsefulDeclarationProcessor(
|
||||
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "interface default implementation")
|
||||
}
|
||||
|
||||
if ((!context.es6mode || !isEsModules) && (irClass.isInner || irClass.isObject)) {
|
||||
if (irClass.isInner || irClass.isObject) {
|
||||
context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irClass, "object lazy initialization")
|
||||
}
|
||||
|
||||
@@ -185,8 +185,6 @@ internal class JsUsefulDeclarationProcessor(
|
||||
irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used")
|
||||
}
|
||||
|
||||
if (context.es6mode && isEsModules) return
|
||||
|
||||
val property = irFunction.correspondingPropertySymbol?.owner ?: return
|
||||
|
||||
if (property.isExported(context) || property.getJsName() != null || property.isOverriddenExternal()) {
|
||||
|
||||
+29
-70
@@ -93,16 +93,11 @@ class ExportModelToJsStatements(
|
||||
|
||||
is ExportedProperty -> {
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
when {
|
||||
namespace == null -> {
|
||||
when (namespace) {
|
||||
null -> {
|
||||
val property = declaration.generateTopLevelGetters()
|
||||
listOf(JsVars(property), JsExport(property.name.makeRef(), JsName(declaration.name, false)))
|
||||
}
|
||||
es6mode && declaration.isMember -> {
|
||||
val jsClass = parentClass?.getCorrespondingJsClass() ?: error("Expect to have parentClass at this point")
|
||||
jsClass.members += declaration.generateClassMembers()
|
||||
listOf(JsEmpty)
|
||||
}
|
||||
else -> {
|
||||
val getter = declaration.irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||
val setter = declaration.irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||
@@ -123,10 +118,11 @@ class ExportModelToJsStatements(
|
||||
|
||||
is ExportedObject -> {
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
val (name, objectClassInitialization) = declaration.getNameAndInitialization()
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
else ->
|
||||
jsElementAccess(Namer.PROTOTYPE_NAME, staticContext.getNameForClass(declaration.ir).makeRef())
|
||||
jsElementAccess(Namer.PROTOTYPE_NAME, name.makeRef())
|
||||
}
|
||||
val staticsExport =
|
||||
declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
|
||||
@@ -155,19 +151,19 @@ class ExportModelToJsStatements(
|
||||
)
|
||||
}
|
||||
|
||||
objectExport + staticsExport
|
||||
listOfNotNull(objectClassInitialization.takeIf { staticsExport.isNotEmpty() }) + objectExport + staticsExport
|
||||
}
|
||||
|
||||
is ExportedRegularClass -> {
|
||||
if (declaration.isInterface) return emptyList()
|
||||
val name = staticContext.getNameForStaticDeclaration(declaration.ir)
|
||||
val (name, classInitialization) = declaration.getNameAndInitialization()
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
esModules -> name.makeRef()
|
||||
else -> prototypeOf(staticContext.getNameForClass(declaration.ir).makeRef(), staticContext)
|
||||
else -> prototypeOf(name.makeRef(), staticContext)
|
||||
}
|
||||
val klassExport = when {
|
||||
namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
|
||||
namespace != null -> jsAssignment(newNameSpace, name.makeRef()).makeStmt()
|
||||
esModules -> JsExport(name.makeRef(), alias = JsName(declaration.name, false))
|
||||
else -> null
|
||||
}
|
||||
@@ -181,12 +177,12 @@ class ExportModelToJsStatements(
|
||||
|
||||
val innerClassesAssignments = declaration.nestedClasses
|
||||
.filter { it.ir.isInner }
|
||||
.map { it.generateInnerClassAssignment(declaration) }
|
||||
.map { it.generateInnerClassAssignment(name) }
|
||||
|
||||
val staticsExport = (staticFunctions + enumEntries + declaration.nestedClasses)
|
||||
.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
|
||||
|
||||
listOfNotNull(klassExport) + staticsExport + innerClassesAssignments
|
||||
listOfNotNull(classInitialization, klassExport) + staticsExport + innerClassesAssignments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,43 +206,9 @@ class ExportModelToJsStatements(
|
||||
)
|
||||
}
|
||||
|
||||
private fun ExportedProperty.generateClassMembers(): List<JsFunction> {
|
||||
val getter = irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||
val setter = irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||
|
||||
return buildList {
|
||||
if (getter != null) {
|
||||
add(JsFunction(emptyScope, "").also {
|
||||
it.name = JsName(name, false)
|
||||
if (isStatic) {
|
||||
it.modifiers.add(JsFunction.Modifier.STATIC)
|
||||
}
|
||||
it.modifiers.add(JsFunction.Modifier.GET)
|
||||
it.body = JsBlock().apply {
|
||||
statements.add(JsReturn(JsInvocation(getter.makeRef())))
|
||||
}
|
||||
})
|
||||
}
|
||||
if (setter != null) {
|
||||
add(JsFunction(emptyScope, "").also {
|
||||
val value = JsName("value", true)
|
||||
it.name = JsName(name, false)
|
||||
it.parameters.add(JsParameter(value))
|
||||
if (isStatic) {
|
||||
it.modifiers.add(JsFunction.Modifier.STATIC)
|
||||
}
|
||||
it.modifiers.add(JsFunction.Modifier.SET)
|
||||
it.body = JsBlock().apply {
|
||||
statements.add(JsExpressionStatement(JsInvocation(setter.makeRef(), value.makeRef())))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExportedClass.generateInnerClassAssignment(outerClass: ExportedClass): JsStatement {
|
||||
val innerClassRef = staticContext.getNameForStaticDeclaration(ir).makeRef()
|
||||
val outerClassRef = staticContext.getNameForStaticDeclaration(outerClass.ir).makeRef()
|
||||
private fun ExportedClass.generateInnerClassAssignment(outerClassName: JsName): JsStatement {
|
||||
val innerClassRef = ir.getClassRef(staticContext)
|
||||
val outerClassRef = outerClassName.makeRef()
|
||||
val companionObject = ir.companionObject()
|
||||
val secondaryConstructors = members.filterIsInstanceAnd<ExportedFunction> { it.isStatic }
|
||||
val bindConstructor = JsName("__bind_constructor_", false)
|
||||
@@ -283,30 +245,27 @@ class ExportModelToJsStatements(
|
||||
blockStatements.add(JsReturn(bindConstructor.makeRef()))
|
||||
val innerClassGetter = JsFunction(emptyScope, JsBlock(*blockStatements.toTypedArray()), "inner class '$name' getter")
|
||||
|
||||
return if (es6mode) {
|
||||
outerClass.ir.getCorrespondingJsClass().members += innerClassGetter.also {
|
||||
it.name = JsName(name, false)
|
||||
it.modifiers.add(JsFunction.Modifier.GET)
|
||||
return defineProperty(
|
||||
prototypeOf(outerClassRef, staticContext),
|
||||
name,
|
||||
innerClassGetter,
|
||||
null,
|
||||
staticContext
|
||||
).makeStmt()
|
||||
}
|
||||
|
||||
private fun ExportedClass.getNameAndInitialization(): Pair<JsName, JsStatement?> {
|
||||
return when (val classRef = ir.getClassRef(staticContext)) {
|
||||
!is JsNameRef -> {
|
||||
val stableName = JsName(name, true)
|
||||
stableName to JsVars(JsVars.JsVar(stableName, classRef))
|
||||
}
|
||||
JsEmpty
|
||||
} else {
|
||||
defineProperty(
|
||||
prototypeOf(outerClassRef, staticContext),
|
||||
name,
|
||||
innerClassGetter,
|
||||
null,
|
||||
staticContext
|
||||
).makeStmt()
|
||||
else -> classRef.name!! to null
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.getCorrespondingJsClass(): JsClass {
|
||||
val jsClassModel = staticContext.classModels[symbol] ?: error("Class with name '$name' was not found")
|
||||
return (jsClassModel.preDeclarationBlock.statements.first() as? JsExpressionStatement)?.expression as? JsClass
|
||||
?: error("Expect to have JsClass as a first statement inside JsIrClassModel")
|
||||
}
|
||||
|
||||
private fun JsNameRef.bindToThis(bindTo: JsExpression): JsInvocation {
|
||||
private fun JsExpression.bindToThis(bindTo: JsExpression): JsInvocation {
|
||||
return JsInvocation(JsNameRef("bind", this), bindTo, JsThisRef())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.backend.common.serialization.IrInterningService
|
||||
import org.jetbrains.kotlin.backend.common.serialization.cityHash64
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
@@ -33,7 +33,7 @@ fun interface JsIrCompilerICInterface {
|
||||
allModules: Collection<IrModuleFragment>,
|
||||
dirtyFiles: Collection<IrFile>,
|
||||
mainArguments: List<String>?
|
||||
): List<() -> JsIrProgramFragment>
|
||||
): List<() -> List<JsIrProgramFragment>>
|
||||
}
|
||||
|
||||
fun interface JsIrCompilerICInterfaceFactory {
|
||||
@@ -616,7 +616,7 @@ class CacheUpdater(
|
||||
private fun commitCacheAndBuildModuleArtifacts(
|
||||
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
||||
moduleNames: Map<KotlinLibraryFile, String>,
|
||||
rebuiltFileFragments: KotlinSourceFileMap<JsIrProgramFragment>
|
||||
rebuiltFileFragments: KotlinSourceFileMap<List<JsIrProgramFragment>>
|
||||
): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") {
|
||||
incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) ->
|
||||
incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
|
||||
@@ -630,7 +630,7 @@ class CacheUpdater(
|
||||
compilerForIC: JsIrCompilerICInterface,
|
||||
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
|
||||
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
|
||||
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>> =
|
||||
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> =
|
||||
stopwatch.measure("Processing IR - lowering") {
|
||||
val dirtyFilesForCompiling = mutableListOf<IrFile>()
|
||||
val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>()
|
||||
@@ -737,7 +737,7 @@ class CacheUpdater(
|
||||
private data class FragmentGenerators(
|
||||
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
||||
val moduleNames: Map<KotlinLibraryFile, String>,
|
||||
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>>
|
||||
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
|
||||
)
|
||||
|
||||
private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators {
|
||||
@@ -751,9 +751,9 @@ class CacheUpdater(
|
||||
}
|
||||
|
||||
private fun generateIrFragments(
|
||||
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>>
|
||||
): KotlinSourceFileMap<JsIrProgramFragment> = stopwatch.measure("Processing IR - generating program fragments") {
|
||||
val rebuiltFragments = KotlinSourceFileMutableMap<JsIrProgramFragment>()
|
||||
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
|
||||
): KotlinSourceFileMap<List<JsIrProgramFragment>> = stopwatch.measure("Processing IR - generating program fragments") {
|
||||
val rebuiltFragments = KotlinSourceFileMutableMap<List<JsIrProgramFragment>>()
|
||||
while (generators.isNotEmpty()) {
|
||||
val (libFile, srcFile, fragmentGenerator) = generators.removeFirst()
|
||||
rebuiltFragments[libFile, srcFile] = fragmentGenerator()
|
||||
@@ -783,7 +783,7 @@ fun rebuildCacheForDirtyFiles(
|
||||
exportedDeclarations: Set<FqName>,
|
||||
mainArguments: List<String>?,
|
||||
es6mode: Boolean
|
||||
): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragment>>> {
|
||||
): Pair<IrModuleFragment, List<Pair<IrFile, List<JsIrProgramFragment>>>> {
|
||||
val internationService = IrInterningService()
|
||||
val emptyMetadata = object : KotlinSourceFileExports() {
|
||||
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ internal fun CrossModuleReferences.crossModuleReferencesHashForIC() = HashCalcul
|
||||
update(importedModule.relativeRequirePath ?: "")
|
||||
}
|
||||
|
||||
updateForEach(transitiveJsExportFrom) { transitiveExport ->
|
||||
updateForEach(transitiveExportFrom) { transitiveExport ->
|
||||
update(transitiveExport.internalName.toString())
|
||||
update(transitiveExport.externalName)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,17 @@ internal inline fun File.useCodedOutput(f: CodedOutputStream.() -> Unit) {
|
||||
outputStream().useCodedOutput(f)
|
||||
}
|
||||
|
||||
internal inline fun <T> CodedOutputStream.ifNotNull(value: T?, write: (T) -> Unit) {
|
||||
writeBoolNoTag(value != null)
|
||||
if (value != null) {
|
||||
write(value)
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T> CodedInputStream.ifTrue(then: () -> T): T? {
|
||||
return if (readBool()) then() else null
|
||||
}
|
||||
|
||||
internal fun icError(what: String, libFile: KotlinLibraryFile? = null, srcFile: KotlinSourceFile? = null): Nothing {
|
||||
val filePath = listOfNotNull(libFile?.path, srcFile?.path).joinToString(":") { File(it).name }
|
||||
val msg = if (filePath.isEmpty()) what else "$what for $filePath"
|
||||
|
||||
+4
-4
@@ -15,10 +15,10 @@ import java.io.File
|
||||
internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) {
|
||||
abstract fun commitMetadata()
|
||||
|
||||
fun commitBinaryAst(fragment: JsIrProgramFragment) {
|
||||
fun commitBinaryAst(fragments: List<JsIrProgramFragment>) {
|
||||
binaryAstFile.parentFile?.mkdirs()
|
||||
BufferedOutputStream(binaryAstFile.outputStream()).use {
|
||||
fragment.serializeTo(it)
|
||||
fragments.serializeTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class IncrementalCacheArtifact(
|
||||
|
||||
fun buildModuleArtifactAndCommitCache(
|
||||
moduleName: String,
|
||||
rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragment>,
|
||||
rebuiltFileFragments: Map<KotlinSourceFile, List<JsIrProgramFragment>>,
|
||||
): ModuleArtifact {
|
||||
val fileArtifacts = srcCacheActions.map { srcFileAction ->
|
||||
val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile]
|
||||
@@ -67,7 +67,7 @@ internal class IncrementalCacheArtifact(
|
||||
srcFileAction.commitBinaryAst(rebuiltFileFragment)
|
||||
}
|
||||
srcFileAction.commitMetadata()
|
||||
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment, srcFileAction.binaryAstFile)
|
||||
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment ?: emptyList(), srcFileAction.binaryAstFile)
|
||||
}
|
||||
|
||||
return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName)
|
||||
|
||||
+5
-5
@@ -26,11 +26,11 @@ class JsExecutableProducer(
|
||||
}
|
||||
}
|
||||
|
||||
fun buildExecutable(multiModule: Boolean, outJsProgram: Boolean) = if (multiModule) {
|
||||
buildMultiModuleExecutable(outJsProgram)
|
||||
} else {
|
||||
buildSingleModuleExecutable(outJsProgram)
|
||||
}
|
||||
fun buildExecutable(granularity: JsGenerationGranularity, outJsProgram: Boolean) =
|
||||
when (granularity) {
|
||||
JsGenerationGranularity.WHOLE_PROGRAM -> buildSingleModuleExecutable(outJsProgram)
|
||||
JsGenerationGranularity.PER_MODULE, JsGenerationGranularity.PER_FILE -> buildMultiModuleExecutable(outJsProgram)
|
||||
}
|
||||
|
||||
private fun buildSingleModuleExecutable(outJsProgram: Boolean): BuildResult {
|
||||
val modules = caches.map { cacheArtifact -> cacheArtifact.loadJsIrModule() }
|
||||
|
||||
+10
-3
@@ -30,7 +30,8 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
val optionalCrossModuleImports = hashSetOf<String>()
|
||||
|
||||
val crossModuleReferencesHash = ICHash.fromProtoStream(this)
|
||||
val hasJsExports = readBool()
|
||||
val reexportedInModuleWithName = ifTrue { readString() }
|
||||
|
||||
repeat(readInt32()) {
|
||||
val tag = readString()
|
||||
val mask = readInt32()
|
||||
@@ -44,6 +45,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
nameBindings[tag] = readString()
|
||||
}
|
||||
}
|
||||
|
||||
CachedModuleInfo(
|
||||
artifact = this@fetchModuleInfo,
|
||||
jsIrHeader = JsIrModuleHeader(
|
||||
@@ -52,7 +54,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
definitions = definitions,
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
hasJsExports = hasJsExports,
|
||||
reexportedInModuleWithName = reexportedInModuleWithName,
|
||||
associatedModule = null
|
||||
),
|
||||
crossModuleReferencesHash = crossModuleReferencesHash
|
||||
@@ -74,8 +76,13 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
names[tag] = ((maskAndName?.first ?: 0) or NameType.DEFINITIONS.typeMask) to maskAndName?.second
|
||||
}
|
||||
crossModuleReferencesHash.toProtoStream(this)
|
||||
writeBoolNoTag(jsIrHeader.hasJsExports)
|
||||
|
||||
ifNotNull(jsIrHeader.reexportedInModuleWithName) {
|
||||
writeStringNoTag(it)
|
||||
}
|
||||
|
||||
writeInt32NoTag(names.size)
|
||||
|
||||
for ((tag, maskAndName) in names) {
|
||||
writeStringNoTag(tag)
|
||||
writeInt32NoTag(maskAndName.first)
|
||||
|
||||
@@ -11,17 +11,17 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.serialization.deserializeJsIrProgramFragment
|
||||
import java.io.File
|
||||
|
||||
class SrcFileArtifact(val srcFilePath: String, private val fragment: JsIrProgramFragment?, private val astArtifact: File? = null) {
|
||||
fun loadJsIrFragment(): JsIrProgramFragment? {
|
||||
if (fragment != null) {
|
||||
return fragment
|
||||
class SrcFileArtifact(val srcFilePath: String, private val fragments: List<JsIrProgramFragment>, private val astArtifact: File? = null) {
|
||||
fun loadJsIrFragments(): List<JsIrProgramFragment> {
|
||||
if (fragments.isNotEmpty()) {
|
||||
return fragments
|
||||
}
|
||||
return astArtifact?.ifExists { readBytes() }?.let {
|
||||
deserializeJsIrProgramFragment(it)
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
fun isModified() = fragment != null
|
||||
fun isModified() = fragments.isNotEmpty()
|
||||
}
|
||||
|
||||
class ModuleArtifact(
|
||||
@@ -35,7 +35,7 @@ class ModuleArtifact(
|
||||
val moduleExternalName = externalModuleName ?: moduleSafeName
|
||||
|
||||
fun loadJsIrModule(): JsIrModule {
|
||||
val fragments = fileArtifacts.sortedBy { it.srcFilePath }.mapNotNull { it.loadJsIrFragment() }
|
||||
val fragments = fileArtifacts.sortedBy { it.srcFilePath }.flatMap { it.loadJsIrFragments() }
|
||||
return JsIrModule(moduleSafeName, moduleExternalName, fragments)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -226,7 +226,7 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
||||
constructor.isEffectivelyExternal() ->
|
||||
JsIrBuilder.buildCall(context.intrinsics.jsCreateExternalThisSymbol)
|
||||
.apply {
|
||||
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
|
||||
putValueArgument(0, getCurrentConstructorReference(constructorReplacement))
|
||||
putValueArgument(1, expression.symbol.owner.parentAsClass.jsConstructorReference(context))
|
||||
putValueArgument(2, irAnyArray(expression.valueArguments.memoryOptimizedMap { it ?: context.getVoid() }))
|
||||
putValueArgument(3, boxParameterGetter)
|
||||
@@ -234,7 +234,7 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
||||
constructor.parentAsClass.symbol == context.irBuiltIns.anyClass ->
|
||||
JsIrBuilder.buildCall(context.intrinsics.jsCreateThisSymbol)
|
||||
.apply {
|
||||
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
|
||||
putValueArgument(0, getCurrentConstructorReference(constructorReplacement))
|
||||
putValueArgument(1, boxParameterGetter)
|
||||
}
|
||||
else ->
|
||||
@@ -263,12 +263,8 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.getCurrentConstructorReference(currentFactoryFunction: IrSimpleFunction): IrExpression {
|
||||
return if (isFinalClass) {
|
||||
jsConstructorReference(context)
|
||||
} else {
|
||||
JsIrBuilder.buildGetValue(currentFactoryFunction.dispatchReceiverParameter!!.symbol)
|
||||
}
|
||||
private fun getCurrentConstructorReference(currentFactoryFunction: IrSimpleFunction): IrExpression {
|
||||
return JsIrBuilder.buildGetValue(currentFactoryFunction.dispatchReceiverParameter!!.symbol)
|
||||
}
|
||||
|
||||
private fun IrDeclaration.excludeFromExport() {
|
||||
|
||||
-1
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
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
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.util.fileOrNull
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.lower
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFileSymbolImpl
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
|
||||
fun moveOpenClassesToSeparateFiles(moduleFragment: IrModuleFragment) {
|
||||
fun createFile(file: IrFile, klass: IrClass): IrFile =
|
||||
IrFileImpl(fileEntry = file.fileEntry, fqName = file.packageFqName, symbol = IrFileSymbolImpl(), module = file.module).also {
|
||||
it.annotations = it.annotations memoryOptimizedPlus file.annotations
|
||||
it.declarations += klass
|
||||
klass.parent = it
|
||||
}
|
||||
|
||||
moduleFragment.files.transformFlat { file ->
|
||||
// We don't have to split declarations with a single class
|
||||
if (file.declarations.size <= 1)
|
||||
return@transformFlat null
|
||||
|
||||
val openClasses = mutableListOf<IrClass>()
|
||||
fun removeAndCollectOpenClasses(container: IrDeclarationContainer) {
|
||||
container.transformDeclarationsFlat { declaration ->
|
||||
if (declaration is IrDeclarationContainer) {
|
||||
removeAndCollectOpenClasses(declaration)
|
||||
}
|
||||
if (
|
||||
declaration is IrClass &&
|
||||
(declaration.modality == Modality.OPEN || declaration.modality == Modality.ABSTRACT) &&
|
||||
declaration.kind == ClassKind.CLASS &&
|
||||
declaration.visibility != DescriptorVisibilities.PRIVATE &&
|
||||
declaration.visibility != DescriptorVisibilities.LOCAL
|
||||
) {
|
||||
openClasses += declaration
|
||||
emptyList()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
removeAndCollectOpenClasses(file)
|
||||
|
||||
return@transformFlat if (openClasses.isEmpty())
|
||||
null
|
||||
else
|
||||
openClasses.mapTo(mutableListOf(file)) { createFile(file, it) }
|
||||
}
|
||||
}
|
||||
|
||||
+7
-10
@@ -8,9 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.backend.common.compilationException
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.isString
|
||||
@@ -169,7 +167,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
||||
val classNameRef = context.getNameForConstructor(expression.symbol.owner).makeRef()
|
||||
val classNameRef = expression.symbol.owner.getConstructorRef(context.staticContext)
|
||||
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
|
||||
val fromPrimary = context.currentFunction is IrConstructor
|
||||
val thisRef =
|
||||
@@ -202,7 +200,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
return when {
|
||||
klass.isEffectivelyExternal() -> {
|
||||
val refForExternalClass = context.getRefForExternalClass(klass)
|
||||
val refForExternalClass = klass.getClassRef(context.staticContext)
|
||||
val varargParameterIndex = expression.symbol.owner.varargParameterIndex()
|
||||
if (varargParameterIndex == -1) {
|
||||
JsNew(refForExternalClass, arguments)
|
||||
@@ -225,8 +223,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val ref = context.getNameForClass(klass).makeRef()
|
||||
JsNew(ref, arguments)
|
||||
JsNew(klass.getClassRef(context.staticContext), arguments)
|
||||
}
|
||||
}.withSource(expression, context)
|
||||
}
|
||||
@@ -320,14 +317,14 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
||||
|
||||
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: JsGenerationContext): JsExpression {
|
||||
val name = when (val function = expression.symbol.owner) {
|
||||
is IrConstructor -> data.getNameForConstructor(function)
|
||||
is IrSimpleFunction -> data.getNameForStaticFunction(function)
|
||||
is IrConstructor -> function.getConstructorRef(data.staticContext)
|
||||
is IrSimpleFunction -> data.getNameForStaticFunction(function).makeRef()
|
||||
else -> compilationException(
|
||||
"Unexpected function kind",
|
||||
expression
|
||||
)
|
||||
}
|
||||
return JsNameRef(name).withSource(expression, data)
|
||||
return name.withSource(expression, data)
|
||||
}
|
||||
|
||||
private fun prefixOperation(operator: JsUnaryOperator, expression: IrDynamicOperatorExpression, data: JsGenerationContext) =
|
||||
|
||||
+270
-134
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionInterfac
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsCodeOutliningLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
@@ -45,34 +46,51 @@ val String.safeModuleName: String
|
||||
val IrModuleFragment.safeName: String
|
||||
get() = name.asString().safeModuleName
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
PER_FILE
|
||||
}
|
||||
|
||||
enum class TranslationMode(
|
||||
val production: Boolean,
|
||||
val perModule: Boolean,
|
||||
val granularity: JsGenerationGranularity,
|
||||
val minimizedMemberNames: Boolean,
|
||||
) {
|
||||
FULL_DEV(production = false, perModule = false, minimizedMemberNames = false),
|
||||
FULL_PROD(production = true, perModule = false, minimizedMemberNames = false),
|
||||
FULL_PROD_MINIMIZED_NAMES(production = true, perModule = false, minimizedMemberNames = true),
|
||||
PER_MODULE_DEV(production = false, perModule = true, minimizedMemberNames = false),
|
||||
PER_MODULE_PROD(production = true, perModule = true, minimizedMemberNames = false),
|
||||
PER_MODULE_PROD_MINIMIZED_NAMES(production = true, perModule = true, minimizedMemberNames = true);
|
||||
FULL_DEV(production = false, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = false),
|
||||
FULL_PROD(production = true, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = false),
|
||||
FULL_PROD_MINIMIZED_NAMES(production = true, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = true),
|
||||
PER_MODULE_DEV(production = false, granularity = JsGenerationGranularity.PER_MODULE, minimizedMemberNames = false),
|
||||
PER_MODULE_PROD(production = true, granularity = JsGenerationGranularity.PER_MODULE, minimizedMemberNames = false),
|
||||
PER_MODULE_PROD_MINIMIZED_NAMES(production = true, granularity = JsGenerationGranularity.PER_MODULE, minimizedMemberNames = true),
|
||||
PER_FILE_DEV(production = false, granularity = JsGenerationGranularity.PER_FILE, minimizedMemberNames = false),
|
||||
PER_FILE_PROD(production = true, granularity = JsGenerationGranularity.PER_FILE, minimizedMemberNames = false),
|
||||
PER_FILE_PROD_MINIMIZED_NAMES(production = true, granularity = JsGenerationGranularity.PER_FILE, minimizedMemberNames = true);
|
||||
|
||||
companion object {
|
||||
fun fromFlags(
|
||||
production: Boolean,
|
||||
perModule: Boolean,
|
||||
granularity: JsGenerationGranularity,
|
||||
minimizedMemberNames: Boolean
|
||||
): TranslationMode {
|
||||
return if (perModule) {
|
||||
if (production) {
|
||||
if (minimizedMemberNames) PER_MODULE_PROD_MINIMIZED_NAMES
|
||||
else PER_MODULE_PROD
|
||||
} else PER_MODULE_DEV
|
||||
} else {
|
||||
if (production) {
|
||||
if (minimizedMemberNames) FULL_PROD_MINIMIZED_NAMES
|
||||
else FULL_PROD
|
||||
} else FULL_DEV
|
||||
return when (granularity) {
|
||||
JsGenerationGranularity.PER_MODULE ->
|
||||
if (production) {
|
||||
if (minimizedMemberNames) PER_MODULE_PROD_MINIMIZED_NAMES
|
||||
else PER_MODULE_PROD
|
||||
} else PER_MODULE_DEV
|
||||
|
||||
JsGenerationGranularity.PER_FILE ->
|
||||
if (production) {
|
||||
if (minimizedMemberNames) PER_FILE_PROD_MINIMIZED_NAMES
|
||||
else PER_FILE_PROD
|
||||
} else PER_FILE_DEV
|
||||
|
||||
JsGenerationGranularity.WHOLE_PROGRAM ->
|
||||
if (production) {
|
||||
if (minimizedMemberNames) FULL_PROD_MINIMIZED_NAMES
|
||||
else FULL_PROD
|
||||
} else FULL_DEV
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,14 +98,14 @@ enum class TranslationMode(
|
||||
|
||||
class JsCodeGenerator(
|
||||
private val program: JsIrProgram,
|
||||
private val multiModule: Boolean,
|
||||
private val granularity: JsGenerationGranularity,
|
||||
private val mainModuleName: String,
|
||||
private val moduleKind: ModuleKind,
|
||||
private val sourceMapsInfo: SourceMapsInfo?
|
||||
) {
|
||||
fun generateJsCode(relativeRequirePath: Boolean, outJsProgram: Boolean): CompilationOutputsBuilt {
|
||||
return generateWrappedModuleBody(
|
||||
multiModule,
|
||||
granularity,
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
program,
|
||||
@@ -101,7 +119,7 @@ class JsCodeGenerator(
|
||||
class IrModuleToJsTransformer(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val mainArguments: List<String>?,
|
||||
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||
moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||
private val removeUnusedAssociatedObjects: Boolean = true,
|
||||
) {
|
||||
private val shouldGeneratePolyfills = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
||||
@@ -113,7 +131,14 @@ class IrModuleToJsTransformer(
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
|
||||
|
||||
private class IrFileExports(val file: IrFile, val exports: List<ExportedDeclaration>, val tsDeclarations: TypeScriptFragment?)
|
||||
private val moduleFragmentToNameMapper = ModuleFragmentToExternalName(moduleToName)
|
||||
|
||||
private class IrFileExports(
|
||||
val file: IrFile,
|
||||
val exports: List<ExportedDeclaration>,
|
||||
val tsDeclarations: TypeScriptFragment?,
|
||||
val generatedFrom: IrFile? = null
|
||||
)
|
||||
|
||||
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>)
|
||||
|
||||
@@ -172,15 +197,22 @@ class IrModuleToJsTransformer(
|
||||
return makeJsCodeGeneratorFromIr(exportData, mode)
|
||||
}
|
||||
|
||||
fun makeIrFragmentsGenerators(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<() -> JsIrProgramFragment> {
|
||||
fun makeIrFragmentsGenerators(
|
||||
dirtyFiles: Collection<IrFile>,
|
||||
allModules: Collection<IrModuleFragment>
|
||||
): List<() -> List<JsIrProgramFragment>> {
|
||||
val files = dirtyFiles + backendContext.mapping.chunkToOriginalFile.keys
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||
val exportData = exportModelGenerator.generateExportWithExternals(files)
|
||||
val mode = TranslationMode.fromFlags(production = false, backendContext.granularity, minimizedMemberNames = false)
|
||||
|
||||
doStaticMembersLowering(allModules)
|
||||
|
||||
return exportData.map {
|
||||
{ generateProgramFragment(it, minimizedMemberNames = false) }
|
||||
}
|
||||
return exportData
|
||||
.groupBy { it.generatedFrom ?: it.file }
|
||||
.map {
|
||||
{ it.value.flatMap { generateProgramFragment(it, mode) } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> {
|
||||
@@ -191,47 +223,123 @@ class IrModuleToJsTransformer(
|
||||
val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) {
|
||||
allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) }
|
||||
}
|
||||
IrFileExports(irFile, allExports, tsDeclarations)
|
||||
IrFileExports(irFile, allExports, tsDeclarations, context.mapping.chunkToOriginalFile[irFile])
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrModuleFragment.externalModuleName(): String {
|
||||
return moduleToName[this] ?: sanitizeName(safeName)
|
||||
}
|
||||
|
||||
private fun makeJsCodeGeneratorFromIr(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsCodeGenerator {
|
||||
if (mode.minimizedMemberNames) {
|
||||
backendContext.fieldDataCache.clear()
|
||||
backendContext.minimizedNameGenerator.clear()
|
||||
}
|
||||
|
||||
val program = JsIrProgram(
|
||||
val program = when (mode.granularity) {
|
||||
JsGenerationGranularity.WHOLE_PROGRAM, JsGenerationGranularity.PER_MODULE -> generateJsIrProgramPerModule(exportData, mode)
|
||||
JsGenerationGranularity.PER_FILE -> generateJsIrProgramPerFile(exportData, mode)
|
||||
}
|
||||
|
||||
return JsCodeGenerator(program, mode.granularity, mainModuleName, moduleKind, sourceMapInfo)
|
||||
}
|
||||
|
||||
private fun generateJsIrProgramPerModule(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsIrProgram {
|
||||
val mainModule = exportData.last()
|
||||
|
||||
return JsIrProgram(
|
||||
exportData.map { data ->
|
||||
JsIrModule(
|
||||
data.fragment.safeName,
|
||||
data.fragment.externalModuleName(),
|
||||
data.files.map { generateProgramFragment(it, mode.minimizedMemberNames) }
|
||||
moduleFragmentToNameMapper.getExternalNameFor(data.fragment),
|
||||
data.files.flatMap { generateProgramFragment(it, mode) },
|
||||
mainModule.fragment.safeName.takeIf { !isEsModules && data != mainModule }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return JsCodeGenerator(program, mode.perModule, mainModuleName, moduleKind, sourceMapInfo)
|
||||
private fun generateJsIrProgramPerFile(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsIrProgram {
|
||||
val modulesPerFile = buildList {
|
||||
for (module in exportData) {
|
||||
var hasFileWithJsExportedDeclaration = false
|
||||
|
||||
for (fileExports in module.files) {
|
||||
if (fileExports.file.couldBeSkipped()) continue
|
||||
val (programFragment, exportProgramFragment) = generateProgramFragment(fileExports, mode)
|
||||
|
||||
add(fileExports.toJsIrModule(mode, programFragment))
|
||||
|
||||
if (fileExports.exports.isNotEmpty()) {
|
||||
add(fileExports.toJsIrModuleForExport(module, mode, exportProgramFragment))
|
||||
hasFileWithJsExportedDeclaration = true
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFileWithJsExportedDeclaration) {
|
||||
add(module.toJsIrProxyModule())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JsIrProgram(modulesPerFile)
|
||||
}
|
||||
|
||||
private fun IrFileExports.toJsIrModule(mode: TranslationMode, programFragment: JsIrProgramFragment): JsIrModule {
|
||||
return JsIrModule(
|
||||
moduleFragmentToNameMapper.getSafeNameFor(file),
|
||||
moduleFragmentToNameMapper.getExternalNameFor(file, mode.granularity),
|
||||
listOf(programFragment),
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrFileExports.toJsIrModuleForExport(
|
||||
module: IrAndExportedDeclarations,
|
||||
mode: TranslationMode,
|
||||
programFragment: JsIrProgramFragment
|
||||
): JsIrModule {
|
||||
return JsIrModule(
|
||||
moduleFragmentToNameMapper.getSafeNameExporterFor(file),
|
||||
moduleFragmentToNameMapper.getExternalNameForExporterFile(file, mode.granularity),
|
||||
listOf(programFragment),
|
||||
module.fragment.safeName
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrAndExportedDeclarations.toJsIrProxyModule(): JsIrModule {
|
||||
return JsIrModule(
|
||||
fragment.safeName,
|
||||
moduleFragmentToNameMapper.getExternalNameFor(fragment),
|
||||
listOf(JsIrProgramFragment("<proxy-file>"))
|
||||
)
|
||||
}
|
||||
|
||||
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||
private val optimizeGeneratedJs = backendContext.configuration.get(JSConfigurationKeys.OPTIMIZE_GENERATED_JS, true)
|
||||
|
||||
private fun generateProgramFragment(fileExports: IrFileExports, minimizedMemberNames: Boolean): JsIrProgramFragment {
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames, isEsModules)
|
||||
private fun IrFileExports.generateProgramFragmentForExport(
|
||||
mode: TranslationMode,
|
||||
nameScope: NameTable<IrDeclaration>
|
||||
): JsIrProgramFragment {
|
||||
val globalNames = NameTable<String>(nameScope)
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules)
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules }
|
||||
val staticContext = JsStaticContext(backendContext, nameGenerator, nameScope, mode)
|
||||
|
||||
return JsIrProgramFragment(file.packageFqName.asString()).apply {
|
||||
dts = tsDeclarations
|
||||
exports.statements += ExportModelToJsStatements(staticContext, backendContext.es6mode, { globalNames.declareFreshName(it, it) })
|
||||
.generateModuleExport(
|
||||
ExportedModule(mainModuleName, moduleKind, this@generateProgramFragmentForExport.exports),
|
||||
internalModuleName,
|
||||
isEsModules
|
||||
)
|
||||
computeAndSaveNameBindings(emptySet(), nameGenerator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateProgramFragment(fileExports: IrFileExports, mode: TranslationMode): List<JsIrProgramFragment> {
|
||||
val globalNameScope = NameTable<IrDeclaration>()
|
||||
|
||||
val staticContext = JsStaticContext(
|
||||
backendContext = backendContext,
|
||||
irNamer = nameGenerator,
|
||||
globalNameScope = globalNameScope
|
||||
)
|
||||
val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules)
|
||||
val staticContext = JsStaticContext(backendContext, nameGenerator, globalNameScope, mode)
|
||||
|
||||
val result = JsIrProgramFragment(fileExports.file.packageFqName.asString()).apply {
|
||||
if (shouldGeneratePolyfills) {
|
||||
@@ -239,21 +347,9 @@ class IrModuleToJsTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules }
|
||||
val globalNames = NameTable<String>(globalNameScope)
|
||||
|
||||
val statements = result.declarations.statements
|
||||
val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
|
||||
|
||||
val exportStatements =
|
||||
ExportModelToJsStatements(staticContext, backendContext.es6mode, { globalNames.declareFreshName(it, it) }).generateModuleExport(
|
||||
ExportedModule(mainModuleName, moduleKind, fileExports.exports),
|
||||
internalModuleName,
|
||||
isEsModules
|
||||
)
|
||||
|
||||
result.exports.statements += exportStatements
|
||||
result.dts = fileExports.tsDeclarations
|
||||
val exportFragment = fileExports.generateProgramFragmentForExport(mode, globalNameScope)
|
||||
|
||||
if (fileStatements.isNotEmpty()) {
|
||||
var startComment = ""
|
||||
@@ -310,50 +406,69 @@ class IrModuleToJsTransformer(
|
||||
|
||||
val definitionSet = fileExports.file.declarations.toSet()
|
||||
|
||||
fun computeTag(declaration: IrDeclaration): String? {
|
||||
val tag = (backendContext.irFactory as IdSignatureRetriever).declarationSignature(declaration)?.toString()
|
||||
|
||||
if (tag == null && declaration !in definitionSet) {
|
||||
error("signature for ${declaration.render()} not found")
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
nameGenerator.nameMap.entries.forEach { (declaration, name) ->
|
||||
computeTag(declaration)?.let { tag ->
|
||||
result.nameBindings[tag] = name
|
||||
if (isBuiltInClass(declaration) || checkIsFunctionInterface(declaration.symbol.signature)) {
|
||||
result.optionalCrossModuleImports += tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameGenerator.imports.entries.forEach { (declaration, importStatement) ->
|
||||
val tag = computeTag(declaration) ?: error("No tag for imported declaration ${declaration.render()}")
|
||||
result.imports[tag] = importStatement
|
||||
result.optionalCrossModuleImports += tag
|
||||
}
|
||||
|
||||
fileExports.file.declarations.forEach {
|
||||
computeTag(it)?.let { tag ->
|
||||
result.definitions += tag
|
||||
}
|
||||
|
||||
if (it is IrClass && it.isInterface) {
|
||||
it.declarations.forEach {
|
||||
computeTag(it)?.let { tag ->
|
||||
result.definitions += tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.computeAndSaveNameBindings(definitionSet, nameGenerator)
|
||||
result.computeAndSaveImports(definitionSet, nameGenerator)
|
||||
result.computeAndSaveDefinitions(definitionSet, fileExports)
|
||||
|
||||
if (optimizeGeneratedJs) {
|
||||
optimizeFragmentByJsAst(result)
|
||||
}
|
||||
|
||||
return result
|
||||
return listOf(result, exportFragment)
|
||||
}
|
||||
|
||||
private fun Set<IrDeclaration>.computeTag(declaration: IrDeclaration): String? {
|
||||
val tag = (backendContext.irFactory as IdSignatureRetriever).declarationSignature(declaration)?.toString()
|
||||
|
||||
if (tag == null && !contains(declaration)) {
|
||||
error("signature for ${declaration.render()} not found")
|
||||
}
|
||||
|
||||
return tag
|
||||
}
|
||||
|
||||
private fun JsIrProgramFragment.computeAndSaveNameBindings(
|
||||
definitions: Set<IrDeclaration>,
|
||||
nameGenerator: JsNameLinkingNamer
|
||||
) {
|
||||
nameGenerator.nameMap.entries.forEach { (declaration, name) ->
|
||||
definitions.computeTag(declaration)?.let { tag ->
|
||||
nameBindings[tag] = name
|
||||
if (isBuiltInClass(declaration) || checkIsFunctionInterface(declaration.symbol.signature)) {
|
||||
optionalCrossModuleImports += tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsIrProgramFragment.computeAndSaveImports(
|
||||
definitions: Set<IrDeclaration>,
|
||||
nameGenerator: JsNameLinkingNamer
|
||||
) {
|
||||
nameGenerator.imports.entries.forEach { (declaration, importExpression) ->
|
||||
val tag = definitions.computeTag(declaration) ?: error("No tag for imported declaration ${declaration.render()}")
|
||||
imports[tag] = importExpression
|
||||
optionalCrossModuleImports += tag
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsIrProgramFragment.computeAndSaveDefinitions(
|
||||
definitions: Set<IrDeclaration>,
|
||||
fileExports: IrFileExports,
|
||||
) {
|
||||
fileExports.file.declarations.forEach {
|
||||
definitions.computeTag(it)?.let { tag ->
|
||||
this.definitions += tag
|
||||
}
|
||||
|
||||
if (it is IrClass && it.isInterface) {
|
||||
it.declarations.forEach {
|
||||
definitions.computeTag(it)?.let { tag ->
|
||||
this.definitions += tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateMainArguments(
|
||||
@@ -374,10 +489,12 @@ class IrModuleToJsTransformer(
|
||||
|
||||
return listOfNotNull(mainArgumentsArray, continuation)
|
||||
}
|
||||
|
||||
private fun IrFile.couldBeSkipped(): Boolean = declarations.all { it.origin == JsCodeOutliningLowering.OUTLINED_JS_CODE_ORIGIN }
|
||||
}
|
||||
|
||||
private fun generateWrappedModuleBody(
|
||||
multiModule: Boolean,
|
||||
granularity: JsGenerationGranularity,
|
||||
mainModuleName: String,
|
||||
moduleKind: ModuleKind,
|
||||
program: JsIrProgram,
|
||||
@@ -385,44 +502,8 @@ private fun generateWrappedModuleBody(
|
||||
relativeRequirePath: Boolean,
|
||||
outJsProgram: Boolean
|
||||
): CompilationOutputsBuilt {
|
||||
if (multiModule) {
|
||||
// mutable container allows explicitly remove elements from itself,
|
||||
// so we are able to help GC to free heavy JsIrModule objects
|
||||
// TODO: It makes sense to invent something better, because this logic can be easily broken
|
||||
val moduleToRef = program.asCrossModuleDependencies(moduleKind, relativeRequirePath).toMutableList()
|
||||
val mainModule = moduleToRef.removeLast().let { (main, mainRef) ->
|
||||
generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
main.fragments,
|
||||
sourceMapsInfo,
|
||||
generateCallToMain = true,
|
||||
mainRef,
|
||||
outJsProgram
|
||||
)
|
||||
}
|
||||
|
||||
mainModule.dependencies = buildList(moduleToRef.size) {
|
||||
while (moduleToRef.isNotEmpty()) {
|
||||
moduleToRef.removeFirst().let { (module, moduleRef) ->
|
||||
val moduleName = module.externalModuleName
|
||||
val moduleCompilationOutput = generateSingleWrappedModuleBody(
|
||||
moduleName,
|
||||
moduleKind,
|
||||
module.fragments,
|
||||
sourceMapsInfo,
|
||||
generateCallToMain = false,
|
||||
moduleRef,
|
||||
outJsProgram
|
||||
)
|
||||
add(moduleName to moduleCompilationOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainModule
|
||||
} else {
|
||||
return generateSingleWrappedModuleBody(
|
||||
return when (granularity) {
|
||||
JsGenerationGranularity.WHOLE_PROGRAM -> generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
program.asFragments(),
|
||||
@@ -430,9 +511,64 @@ private fun generateWrappedModuleBody(
|
||||
generateCallToMain = true,
|
||||
outJsProgram = outJsProgram
|
||||
)
|
||||
JsGenerationGranularity.PER_FILE,
|
||||
JsGenerationGranularity.PER_MODULE -> generateMultiWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
program,
|
||||
sourceMapsInfo,
|
||||
relativeRequirePath,
|
||||
outJsProgram
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateMultiWrappedModuleBody(
|
||||
mainModuleName: String,
|
||||
moduleKind: ModuleKind,
|
||||
program: JsIrProgram,
|
||||
sourceMapsInfo: SourceMapsInfo?,
|
||||
relativeRequirePath: Boolean,
|
||||
outJsProgram: Boolean
|
||||
): CompilationOutputsBuilt {
|
||||
// mutable container allows explicitly remove elements from itself,
|
||||
// so we are able to help GC to free heavy JsIrModule objects
|
||||
// TODO: It makes sense to invent something better, because this logic can be easily broken
|
||||
val moduleToRef = program.asCrossModuleDependencies(moduleKind, relativeRequirePath).toMutableList()
|
||||
|
||||
val mainModule = moduleToRef.removeLast().let { (main, mainRef) ->
|
||||
generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
moduleKind,
|
||||
main.fragments,
|
||||
sourceMapsInfo,
|
||||
generateCallToMain = true,
|
||||
mainRef,
|
||||
outJsProgram
|
||||
)
|
||||
}
|
||||
|
||||
mainModule.dependencies = buildList(moduleToRef.size) {
|
||||
while (moduleToRef.isNotEmpty()) {
|
||||
moduleToRef.removeFirst().let { (module, moduleRef) ->
|
||||
val moduleName = module.externalModuleName
|
||||
val moduleCompilationOutput = generateSingleWrappedModuleBody(
|
||||
moduleName,
|
||||
moduleKind,
|
||||
module.fragments,
|
||||
sourceMapsInfo,
|
||||
generateCallToMain = false,
|
||||
moduleRef,
|
||||
outJsProgram
|
||||
)
|
||||
add(moduleName to moduleCompilationOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainModule
|
||||
}
|
||||
|
||||
fun generateSingleWrappedModuleBody(
|
||||
moduleName: String,
|
||||
moduleKind: ModuleKind,
|
||||
|
||||
+59
-18
@@ -20,55 +20,96 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsVars.JsVar
|
||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||
import org.jetbrains.kotlin.utils.toSmartList
|
||||
|
||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
||||
private val perFile = context.staticContext.isPerFile
|
||||
private val es6mode = context.staticContext.backendContext.es6mode
|
||||
|
||||
private val className = context.getNameForClass(irClass)
|
||||
private val classNameRef = className.makeRef()
|
||||
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
||||
|
||||
private val classNameUsedInsideDeclarationStatements = when {
|
||||
perFile -> JsName("$", true)
|
||||
else -> className
|
||||
}
|
||||
|
||||
private val classNameRef = classNameUsedInsideDeclarationStatements.makeRef()
|
||||
|
||||
private val classPrototypeRef by lazy(LazyThreadSafetyMode.NONE) { prototypeOf(classNameRef, context.staticContext) }
|
||||
private val baseClassRef by lazy(LazyThreadSafetyMode.NONE) { // Lazy in case was not collected by namer during JsClassGenerator construction
|
||||
if (baseClass != null && !baseClass.isAny()) baseClass.getClassRef(context) else null
|
||||
if (baseClass != null && !baseClass.isAny()) baseClass.getClassRef(context.staticContext) else null
|
||||
}
|
||||
private val classBlock = JsCompositeBlock()
|
||||
private val classModel = JsIrClassModel(irClass)
|
||||
private val classBlock = JsCompositeBlock()
|
||||
|
||||
private val es6mode = context.staticContext.backendContext.es6mode
|
||||
private val interfaceDefaultsBlock = when {
|
||||
perFile -> JsCompositeBlock()
|
||||
else -> classModel.preDeclarationBlock
|
||||
}
|
||||
|
||||
private val jsUndefined by lazy(LazyThreadSafetyMode.NONE) { jsUndefined(context.staticContext) }
|
||||
|
||||
fun generate(): JsStatement {
|
||||
return generateClassBlock().butIf(perFile) { it.wrapInFunction() }
|
||||
}
|
||||
|
||||
private fun JsCompositeBlock.wrapInFunction(): JsStatement {
|
||||
val classHolder = JsVar(JsName("${className.ident}Class", true))
|
||||
val functionWrapper = JsFunction(emptyScope, JsBlock(), "lazy wrapper for classes in per-file").apply {
|
||||
name = className
|
||||
with(body.statements) {
|
||||
add(
|
||||
JsIf(
|
||||
JsAstUtils.equality(classHolder.name.makeRef(), jsUndefined),
|
||||
JsBlock(
|
||||
classModel.preDeclarationBlock.statements + statements + classModel.postDeclarationBlock.statements +
|
||||
JsAstUtils.assignment(classHolder.name.makeRef(), classNameRef).makeStmt()
|
||||
)
|
||||
)
|
||||
)
|
||||
add(JsReturn(classHolder.name.makeRef()))
|
||||
}
|
||||
}
|
||||
|
||||
return JsCompositeBlock(interfaceDefaultsBlock.statements + listOf(JsVars(classHolder), functionWrapper.makeStmt())).also {
|
||||
classModel.preDeclarationBlock.statements.clear()
|
||||
classModel.postDeclarationBlock.statements.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateClassBlock(): JsCompositeBlock {
|
||||
assert(!irClass.isExpect)
|
||||
|
||||
if (!es6mode) maybeGeneratePrimaryConstructor()
|
||||
|
||||
val transformer = IrDeclarationToJsTransformer()
|
||||
|
||||
// Properties might be lowered out of classes
|
||||
// We'll use IrSimpleFunction::correspondingProperty to collect them into set
|
||||
val properties = mutableSetOf<IrProperty>()
|
||||
|
||||
val jsClass = JsClass(name = className, baseClass = baseClassRef)
|
||||
val jsClass = JsClass(name = classNameUsedInsideDeclarationStatements, baseClass = baseClassRef)
|
||||
|
||||
if (baseClass != null && !baseClass.isAny()) {
|
||||
jsClass.baseClass = baseClassRef
|
||||
}
|
||||
|
||||
if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt()
|
||||
if (es6mode) {
|
||||
classModel.preDeclarationBlock.statements += jsClass.makeStmt()
|
||||
}
|
||||
|
||||
for (declaration in irClass.declarations) {
|
||||
when (declaration) {
|
||||
is IrConstructor -> {
|
||||
val constructor = declaration.accept(IrFunctionToJsTransformer(), context)
|
||||
if (es6mode) {
|
||||
declaration.accept(IrFunctionToJsTransformer(), context).let { fn ->
|
||||
if (fn.body.statements.any { it !is JsEmpty && !it.isSimpleSuperCall(fn) }) {
|
||||
jsClass.constructor = fn
|
||||
}
|
||||
}
|
||||
jsClass.constructor = constructor.apply { name = null }
|
||||
} else {
|
||||
classBlock.statements += declaration.accept(transformer, context)
|
||||
classBlock.statements += constructor.apply { name = classNameUsedInsideDeclarationStatements }.makeStmt()
|
||||
}
|
||||
}
|
||||
is IrSimpleFunction -> {
|
||||
@@ -275,7 +316,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
assert(!declaration.isStaticMethodOfClass)
|
||||
|
||||
if (irClass.isInterface) {
|
||||
classModel.preDeclarationBlock.statements += translatedFunction.makeStmt()
|
||||
interfaceDefaultsBlock.statements += translatedFunction.makeStmt()
|
||||
return Pair(memberName, null)
|
||||
}
|
||||
|
||||
@@ -323,7 +364,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
private fun maybeGeneratePrimaryConstructor() {
|
||||
if (!irClass.declarations.any { it is IrConstructor }) {
|
||||
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
|
||||
func.name = className
|
||||
func.name = classNameUsedInsideDeclarationStatements
|
||||
classBlock.statements += func.makeStmt()
|
||||
}
|
||||
}
|
||||
@@ -352,12 +393,12 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
}
|
||||
|
||||
|
||||
private fun IrType.asConstructorRef(): JsNameRef? {
|
||||
private fun IrType.asConstructorRef(): JsExpression? {
|
||||
val ownerSymbol = classOrNull?.takeIf {
|
||||
!isAny() && !isFunctionType() && !it.owner.isEffectivelyExternal()
|
||||
} ?: return null
|
||||
|
||||
return JsNameRef(context.getNameForClass(ownerSymbol.owner))
|
||||
return ownerSymbol.owner.getClassRef(context.staticContext)
|
||||
}
|
||||
|
||||
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
|
||||
|
||||
+7
-9
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.compilationException
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getClassRef
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.invokeFunForLambda
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
@@ -91,13 +88,13 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
||||
|
||||
add(intrinsics.jsObjectCreateSymbol) { call, context ->
|
||||
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||
val className = context.getNameForClass(classToCreate)
|
||||
objectCreate(prototypeOf(className.makeRef(), context.staticContext), context.staticContext)
|
||||
val className = classToCreate.getClassRef(context.staticContext)
|
||||
objectCreate(prototypeOf(className, context.staticContext), context.staticContext)
|
||||
}
|
||||
|
||||
add(intrinsics.jsClass) { call, context ->
|
||||
val typeArgument = call.getTypeArgument(0)
|
||||
typeArgument?.getClassRef(context)
|
||||
typeArgument?.getClassRef(context.staticContext)
|
||||
?: compilationException(
|
||||
"Type argument of jsClass must be statically known class",
|
||||
typeArgument
|
||||
@@ -180,7 +177,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
||||
val arg = translateCallArguments(call, context).single()
|
||||
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
|
||||
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
|
||||
JsNew(context.getNameForConstructor(constructor).makeRef(), listOf(arg))
|
||||
|
||||
JsNew(constructor.getConstructorRef(context.staticContext), listOf(arg))
|
||||
.apply { isInlineClassBoxing = true }
|
||||
}
|
||||
|
||||
@@ -209,7 +207,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
||||
is IrFunctionReference -> {
|
||||
val superClass = call.superQualifierSymbol!!
|
||||
val functionName = context.getNameForMemberFunction(target.symbol.owner as IrSimpleFunction)
|
||||
val superName = context.getNameForClass(superClass.owner).makeRef()
|
||||
val superName = superClass.owner.getClassRef(context.staticContext)
|
||||
JsNameRef(functionName, prototypeOf(superName, context.staticContext))
|
||||
}
|
||||
is IrFunctionExpression -> target.accept(IrElementToJsExpressionTransformer(), context)
|
||||
|
||||
+31
-32
@@ -31,15 +31,16 @@ class JsIrProgramFragment(val packageFqn: String) {
|
||||
class JsIrModule(
|
||||
val moduleName: String,
|
||||
val externalModuleName: String,
|
||||
val fragments: List<JsIrProgramFragment>
|
||||
val fragments: List<JsIrProgramFragment>,
|
||||
val reexportedInModuleWithName: String? = null,
|
||||
) {
|
||||
fun makeModuleHeader(): JsIrModuleHeader {
|
||||
val nameBindings = mutableMapOf<String, String>()
|
||||
val definitions = mutableSetOf<String>()
|
||||
val optionalCrossModuleImports = hashSetOf<String>()
|
||||
var hasJsExports = false
|
||||
var hasDeclarationsToReexport = false
|
||||
for (fragment in fragments) {
|
||||
hasJsExports = hasJsExports || !fragment.exports.isEmpty
|
||||
hasDeclarationsToReexport = hasDeclarationsToReexport || !fragment.exports.isEmpty
|
||||
for ((tag, name) in fragment.nameBindings.entries) {
|
||||
nameBindings[tag] = name.toString()
|
||||
}
|
||||
@@ -52,7 +53,7 @@ class JsIrModule(
|
||||
definitions = definitions,
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
hasJsExports = hasJsExports,
|
||||
reexportedInModuleWithName = reexportedInModuleWithName.takeIf { hasDeclarationsToReexport },
|
||||
associatedModule = this
|
||||
)
|
||||
}
|
||||
@@ -64,8 +65,8 @@ class JsIrModuleHeader(
|
||||
val definitions: Set<String>,
|
||||
val nameBindings: Map<String, String>,
|
||||
val optionalCrossModuleImports: Set<String>,
|
||||
val hasJsExports: Boolean,
|
||||
var associatedModule: JsIrModule?
|
||||
val reexportedInModuleWithName: String? = null,
|
||||
var associatedModule: JsIrModule?,
|
||||
) {
|
||||
val externalNames: Set<String> by lazy(LazyThreadSafetyMode.NONE) { nameBindings.keys - definitions }
|
||||
}
|
||||
@@ -89,19 +90,18 @@ class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
}
|
||||
}
|
||||
|
||||
class CrossModuleDependenciesResolver(
|
||||
private val moduleKind: ModuleKind,
|
||||
private val headers: List<JsIrModuleHeader>
|
||||
) {
|
||||
class CrossModuleDependenciesResolver(private val moduleKind: ModuleKind, private val headers: List<JsIrModuleHeader>) {
|
||||
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
|
||||
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferenceBuilder(moduleKind, it, relativeRequirePath) }
|
||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
|
||||
|
||||
if (moduleKind != ModuleKind.ES) {
|
||||
val mainModuleHeader = headers.last()
|
||||
val otherModuleHeaders = headers.dropLast(1)
|
||||
headerToBuilder[mainModuleHeader]!!.transitiveJsExportFrom = otherModuleHeaders
|
||||
val reexportModuleToHeader = headers.groupBy { it.reexportedInModuleWithName }
|
||||
val headerToBuilder = headers.associateWith {
|
||||
JsIrModuleCrossModuleReferenceBuilder(
|
||||
moduleKind,
|
||||
it,
|
||||
relativeRequirePath,
|
||||
reexportModuleToHeader[it.moduleName] ?: emptyList(),
|
||||
)
|
||||
}
|
||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
|
||||
|
||||
for (header in headers) {
|
||||
val builder = headerToBuilder[header]!!
|
||||
@@ -115,6 +115,7 @@ class CrossModuleDependenciesResolver(
|
||||
val builder = headerToBuilder[header]!!
|
||||
for (tag in header.externalNames) {
|
||||
val fromModuleBuilder = definitionModule[tag]
|
||||
|
||||
if (fromModuleBuilder == null) {
|
||||
if (tag in header.optionalCrossModuleImports) {
|
||||
continue
|
||||
@@ -128,6 +129,8 @@ class CrossModuleDependenciesResolver(
|
||||
}
|
||||
}
|
||||
|
||||
headerToBuilder.forEach { it.value.buildExportNames() }
|
||||
|
||||
return headers.associateWith { headerToBuilder[it]!!.buildCrossModuleRefs() }
|
||||
}
|
||||
}
|
||||
@@ -137,21 +140,20 @@ private class CrossModuleRef(val module: JsIrModuleCrossModuleReferenceBuilder,
|
||||
private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
val moduleKind: ModuleKind,
|
||||
val header: JsIrModuleHeader,
|
||||
val relativeRequirePath: Boolean
|
||||
val relativeRequirePath: Boolean,
|
||||
val transitiveExportFrom: List<JsIrModuleHeader>,
|
||||
) {
|
||||
val imports = mutableListOf<CrossModuleRef>()
|
||||
val exports = mutableSetOf<String>()
|
||||
var transitiveJsExportFrom = emptyList<JsIrModuleHeader>()
|
||||
|
||||
private lateinit var exportNames: Map<String, String> // tag -> index
|
||||
lateinit var exportNames: Map<String, String> // tag -> index
|
||||
|
||||
private fun buildExportNames() {
|
||||
var index = 0
|
||||
fun buildExportNames(startIndex: Int = 0) {
|
||||
var index = startIndex
|
||||
exportNames = exports.sorted().associateWith { index++.toJsIdentifier() }
|
||||
}
|
||||
|
||||
fun buildCrossModuleRefs(): CrossModuleReferences {
|
||||
buildExportNames()
|
||||
val isImportOptional = moduleKind == ModuleKind.ES
|
||||
val importedModules = mutableMapOf<JsIrModuleHeader, JsImportedModule>()
|
||||
|
||||
@@ -165,18 +167,16 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
|
||||
val resultImports = imports.associate { crossModuleRef ->
|
||||
val tag = crossModuleRef.tag
|
||||
require(crossModuleRef.module::exportNames.isInitialized) {
|
||||
// This situation appears in case of a dependent module redefine a symbol (function) from their dependency
|
||||
"Cross module dependency resolution failed due to signature '$tag' redefinition"
|
||||
}
|
||||
val exportedAs = crossModuleRef.module.exportNames[tag]!!
|
||||
val importedModule = import(crossModuleRef.module.header)
|
||||
|
||||
tag to CrossModuleImport(exportedAs, importedModule)
|
||||
}
|
||||
|
||||
val transitiveExport = transitiveJsExportFrom.mapNotNull {
|
||||
if (!it.hasJsExports) null else CrossModuleTransitiveExport(import(it).internalName, it.externalModuleName)
|
||||
val transitiveExport = transitiveExportFrom.mapNotNull {
|
||||
it.reexportedInModuleWithName?.run {
|
||||
CrossModuleTransitiveExport(import(it).internalName, relativeRequirePath(it) ?: it.externalModuleName)
|
||||
}
|
||||
}
|
||||
return CrossModuleReferences(
|
||||
moduleKind,
|
||||
@@ -208,8 +208,7 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
||||
.toRelativeString(parentMain)
|
||||
.replace(File.separator, "/")
|
||||
|
||||
return relativePath.takeIf { it.startsWith("../") }
|
||||
?: "./$relativePath"
|
||||
return relativePath.takeIf { it.startsWith("../") } ?: "./$relativePath"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +221,7 @@ fun CrossModuleTransitiveExport.getRequireEsmName() = "$externalName$ESM_EXTENSI
|
||||
class CrossModuleReferences(
|
||||
val moduleKind: ModuleKind,
|
||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
||||
val transitiveJsExportFrom: List<CrossModuleTransitiveExport>, // the list of modules which provide their js exports for transitive export
|
||||
val transitiveExportFrom: List<CrossModuleTransitiveExport>, // the list of modules which provide their exports for transitive export
|
||||
val exports: Map<String, String>, // tag -> index
|
||||
val imports: Map<String, CrossModuleImport>, // tag -> import statement
|
||||
) {
|
||||
|
||||
+2
-3
@@ -148,7 +148,6 @@ class Merger(
|
||||
?.asSequence()
|
||||
?.flatMap { (it.subject as JsExport.Subject.Elements).elements }
|
||||
?.distinctBy { it.alias?.ident ?: it.name.ident }
|
||||
?.map { if (it.name.ident == it.alias?.ident) JsExport.Element(it.name, null) else it }
|
||||
?.toList()
|
||||
|
||||
val oneLargeExportStatement = exportedElements?.let { JsExport(JsExport.Subject.Elements(it)) }
|
||||
@@ -179,14 +178,14 @@ class Merger(
|
||||
|
||||
private fun transitiveJsExport(): List<JsStatement> {
|
||||
return if (isEsModules) {
|
||||
crossModuleReferences.transitiveJsExportFrom.map {
|
||||
crossModuleReferences.transitiveExportFrom.map {
|
||||
JsExport(JsExport.Subject.All, it.getRequireEsmName())
|
||||
}
|
||||
} else {
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val exporterName = ReservedJsNames.makeJsExporterName()
|
||||
|
||||
crossModuleReferences.transitiveJsExportFrom.map {
|
||||
crossModuleReferences.transitiveExportFrom.map {
|
||||
JsInvocation(
|
||||
JsNameRef(exporterName, it.internalName.makeRef()),
|
||||
internalModuleName.makeRef()
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.ir.declarations.path
|
||||
|
||||
private const val EXPORTER_FILE_POSTFIX = ".export"
|
||||
|
||||
class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map<IrModuleFragment, String>) {
|
||||
fun getExternalNameFor(file: IrFile, granularity: JsGenerationGranularity): String {
|
||||
assert(granularity == JsGenerationGranularity.PER_FILE) { "This method should be used only for PER_FILE granularity" }
|
||||
return file.module.getJsOutputName().getExternalModuleNameForPerFile(file)
|
||||
}
|
||||
|
||||
fun getExternalNameForExporterFile(file: IrFile, granularity: JsGenerationGranularity): String {
|
||||
return "${getExternalNameFor(file, granularity)}$EXPORTER_FILE_POSTFIX"
|
||||
}
|
||||
|
||||
fun getSafeNameFor(file: IrFile): String {
|
||||
return "${file.module.safeName}/${file.stableFileName}"
|
||||
}
|
||||
|
||||
fun getSafeNameExporterFor(file: IrFile): String {
|
||||
return "${getSafeNameFor(file)}$EXPORTER_FILE_POSTFIX"
|
||||
}
|
||||
|
||||
fun getExternalNameFor(module: IrModuleFragment): String {
|
||||
return module.getJsOutputName()
|
||||
}
|
||||
|
||||
private fun IrModuleFragment.getJsOutputName(): String {
|
||||
return jsOutputNamesMapping[this] ?: sanitizeName(safeName)
|
||||
}
|
||||
|
||||
private fun String.getExternalModuleNameForPerFile(file: IrFile) = "$this/${file.stableFileName}"
|
||||
|
||||
private val IrFile.stableFileName: String
|
||||
get() {
|
||||
val prefix = packageFqName.asString().replace('.', '/')
|
||||
val fileName = getJsName() ?: name.substringBefore(".kt")
|
||||
return "$prefix${if (prefix.isNotEmpty()) "/" else ""}$fileName"
|
||||
}
|
||||
}
|
||||
+6
-11
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrFileEntry
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
@@ -19,7 +18,6 @@ import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
@@ -31,6 +29,7 @@ import org.jetbrains.kotlin.js.config.SourceMapNamesPolicy
|
||||
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
import java.io.FileInputStream
|
||||
@@ -38,8 +37,8 @@ import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
fun jsUndefined(context: IrNamer, backendContext: JsIrBackendContext): JsExpression {
|
||||
return when (val void = backendContext.getVoid()) {
|
||||
fun jsUndefined(context: JsStaticContext): JsExpression {
|
||||
return when (val void = context.backendContext.getVoid()) {
|
||||
is IrGetField -> context.getNameForField(void.symbol.owner).makeRef()
|
||||
else -> JsNullLiteral()
|
||||
}
|
||||
@@ -93,15 +92,11 @@ fun objectCreate(prototype: JsExpression, context: JsStaticContext) =
|
||||
)
|
||||
|
||||
fun defineProperty(obj: JsExpression, name: String, getter: JsExpression?, setter: JsExpression?, context: JsStaticContext): JsExpression {
|
||||
val undefined by lazy(LazyThreadSafetyMode.NONE) { jsUndefined(context, context.backendContext) }
|
||||
return JsInvocation(
|
||||
context
|
||||
.getNameForStaticFunction(context.backendContext.intrinsics.jsDefinePropertySymbol.owner)
|
||||
.makeRef(),
|
||||
obj,
|
||||
JsStringLiteral(name),
|
||||
getter ?: undefined,
|
||||
setter ?: undefined
|
||||
listOfNotNull(obj, JsStringLiteral(name), getter ?: runIf(setter != null) { jsUndefined(context) }, setter)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -213,7 +208,7 @@ fun translateCall(
|
||||
val nameForStaticDeclaration = context.getNameForStaticDeclaration(target)
|
||||
JsNameRef(Namer.CALL_FUNCTION, JsNameRef(nameForStaticDeclaration))
|
||||
} else {
|
||||
val qualifierName = context.getNameForClass(klass).makeRef()
|
||||
val qualifierName = klass.getClassRef(context.staticContext)
|
||||
val targetName = context.getNameForMemberFunction(target)
|
||||
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName, context.staticContext))
|
||||
JsNameRef(Namer.CALL_FUNCTION, qPrototype)
|
||||
@@ -399,7 +394,7 @@ fun translateCallArguments(
|
||||
val varargParameterIndex = function.realOverrideTarget.varargParameterIndex()
|
||||
|
||||
val validWithNullArgs = expression.validWithNullArgs()
|
||||
val jsUndefined by lazy(LazyThreadSafetyMode.NONE) { jsUndefined(context, context.staticContext.backendContext) }
|
||||
val jsUndefined by lazy(LazyThreadSafetyMode.NONE) { jsUndefined(context.staticContext) }
|
||||
|
||||
val arguments = (0 until size)
|
||||
.mapIndexedTo(ArrayList(size)) { i, _ ->
|
||||
|
||||
+2
-1
@@ -41,7 +41,7 @@ private fun JsNode.resolveNames(): Map<JsName, JsName> {
|
||||
for (temporaryName in scope.declaredNames.asSequence().filter { it.isTemporary }) {
|
||||
var resolvedName = temporaryName.ident
|
||||
var suffix = nextSuffix.getOrDefault(temporaryName.ident, 0)
|
||||
while (resolvedName in JsDeclarationScope.RESERVED_WORDS || !occupiedNames.add(resolvedName)) {
|
||||
while (resolvedName in JsDeclarationScope.RESERVED_WORDS || resolvedName in occupiedNames) {
|
||||
resolvedName = "${temporaryName.ident}_${suffix++}"
|
||||
}
|
||||
nextSuffix[temporaryName.ident] = suffix
|
||||
@@ -79,6 +79,7 @@ private fun JsNode.computeScopes(): Scope {
|
||||
// We need it to not rename methods and fields inside class body
|
||||
// Because if they are in clash with something, it means overriding
|
||||
x.constructor?.accept(this)
|
||||
x.baseClass?.accept(this)
|
||||
x.members.forEach { visitFunction(it, shouldReserveName = false) }
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
@@ -14,10 +15,12 @@ import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsInvocation
|
||||
import org.jetbrains.kotlin.ir.util.unexpectedSymbolKind
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||
|
||||
fun IrType.asString(): String = when (this) {
|
||||
// TODO: should each IrErrorType have own string representation?
|
||||
@@ -54,13 +57,24 @@ tailrec fun erase(type: IrType): IrClass? = when (val classifier = type.classifi
|
||||
is IrScriptSymbol -> null
|
||||
}
|
||||
|
||||
fun IrType.getClassRef(context: JsGenerationContext): JsNameRef =
|
||||
when (val klass = classifierOrFail.owner) {
|
||||
is IrClass ->
|
||||
if (klass.isEffectivelyExternal())
|
||||
context.getRefForExternalClass(klass)
|
||||
else
|
||||
context.getNameForClass(klass).makeRef()
|
||||
|
||||
fun IrType.getClassRef(context: JsStaticContext): JsExpression {
|
||||
return when (val klass = classifierOrFail.owner) {
|
||||
is IrClass -> klass.getClassRef(context)
|
||||
else -> context.getNameForStaticDeclaration(klass as IrDeclarationWithName).makeRef()
|
||||
}
|
||||
}
|
||||
|
||||
fun IrClass.getClassRef(context: JsStaticContext): JsExpression {
|
||||
return when {
|
||||
isEffectivelyExternal() -> context.getRefForExternalClass(this)
|
||||
else -> context.getNameForClass(this)
|
||||
.makeRef()
|
||||
.butIf(context.isPerFile) { JsInvocation(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun IrConstructor.getConstructorRef(context: JsStaticContext): JsExpression {
|
||||
return context.getNameForConstructor(this)
|
||||
.makeRef()
|
||||
.butIf(context.isPerFile && !isEffectivelyExternal()) { JsInvocation(it) }
|
||||
}
|
||||
@@ -6,8 +6,10 @@
|
||||
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.JsGenerationGranularity
|
||||
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.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsCompositeBlock
|
||||
@@ -17,9 +19,12 @@ class JsStaticContext(
|
||||
val backendContext: JsIrBackendContext,
|
||||
private val irNamer: IrNamer,
|
||||
val globalNameScope: NameTable<IrDeclaration>,
|
||||
val mode: TranslationMode,
|
||||
) : IrNamer by irNamer {
|
||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||
|
||||
val initializerBlock = JsCompositeBlock()
|
||||
|
||||
val isPerFile: Boolean get() = mode.granularity === JsGenerationGranularity.PER_FILE
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ class Keeper(private val keep: Set<String>) : IrElementVisitor<Unit, Keeper.Keep
|
||||
private val keptDeclarations: MutableSet<IrDeclaration> = mutableSetOf()
|
||||
|
||||
fun shouldKeep(declaration: IrDeclaration): Boolean {
|
||||
return declaration in keptDeclarations
|
||||
return declaration in keptDeclarations ||
|
||||
(declaration is IrOverridableDeclaration<*> && declaration.overriddenSymbols.any { shouldKeep(it.owner as IrDeclaration) })
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: KeepData) {
|
||||
|
||||
+7
-3
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
fun deserializeJsIrProgramFragment(input: ByteArray): JsIrProgramFragment {
|
||||
return JsIrAstDeserializer(input).readFragment()
|
||||
fun deserializeJsIrProgramFragment(input: ByteArray): List<JsIrProgramFragment> {
|
||||
return JsIrAstDeserializer(input).readFragments()
|
||||
}
|
||||
|
||||
private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||
@@ -76,6 +76,10 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||
return if (readBoolean()) then() else null
|
||||
}
|
||||
|
||||
fun readFragments(): List<JsIrProgramFragment> {
|
||||
return readList { readFragment() }
|
||||
}
|
||||
|
||||
fun readFragment(): JsIrProgramFragment {
|
||||
return JsIrProgramFragment(readString()).apply {
|
||||
readRepeated {
|
||||
@@ -320,7 +324,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||
CLASS -> {
|
||||
JsClass(
|
||||
ifTrue { nameTable[readInt()] },
|
||||
ifTrue { nameTable[readInt()].makeRef() },
|
||||
ifTrue { readExpression() },
|
||||
ifTrue { readFunction() },
|
||||
).apply {
|
||||
readRepeated { members += readFunction() }
|
||||
|
||||
+10
-5
@@ -15,8 +15,8 @@ import java.io.DataOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
fun JsIrProgramFragment.serializeTo(output: OutputStream) {
|
||||
JsIrAstSerializer().append(this).saveTo(output)
|
||||
fun List<JsIrProgramFragment>.serializeTo(output: OutputStream) {
|
||||
JsIrAstSerializer().appendAll(this).saveTo(output)
|
||||
}
|
||||
|
||||
private class DataWriter {
|
||||
@@ -89,6 +89,12 @@ private class JsIrAstSerializer {
|
||||
private val fileStack: Deque<String> = ArrayDeque()
|
||||
private val importedNames = mutableSetOf<JsName>()
|
||||
|
||||
fun appendAll(fragments: List<JsIrProgramFragment>): JsIrAstSerializer {
|
||||
fragmentSerializer.writeInt(fragments.size)
|
||||
fragments.forEach(::append)
|
||||
return this
|
||||
}
|
||||
|
||||
fun append(fragment: JsIrProgramFragment): JsIrAstSerializer {
|
||||
importedNames += fragment.imports.map { fragment.nameBindings[it.key]!! }
|
||||
fragmentSerializer.writeFragment(fragment)
|
||||
@@ -437,9 +443,8 @@ private class JsIrAstSerializer {
|
||||
ifNotNull(x.name) {
|
||||
writeInt(internalizeName(it))
|
||||
}
|
||||
// TODO: add more complex JsNameRef parsing in future when we will support `class` expressions inside a `js` call
|
||||
ifNotNull(x.baseClass?.name) {
|
||||
writeInt(internalizeName(it))
|
||||
ifNotNull(x.baseClass) {
|
||||
writeExpression(it)
|
||||
}
|
||||
ifNotNull(x.constructor) {
|
||||
writeFunction(it)
|
||||
|
||||
Reference in New Issue
Block a user