[K/JS] Move ES modules logic to a new transformer with IC
This commit is contained in:
+1
-1
@@ -123,7 +123,7 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
@Argument(
|
||||
value = "-module-kind",
|
||||
valueDescription = "{plain|amd|commonjs|umd}",
|
||||
valueDescription = "{plain|amd|commonjs|umd|es}",
|
||||
description = "Kind of the JS module generated by the compiler"
|
||||
)
|
||||
var moduleKind: String? by NullableStringFreezableVar(K2JsArgumentConstants.MODULE_PLAIN)
|
||||
|
||||
@@ -551,7 +551,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
var moduleKind: ModuleKind? = if (moduleKindName != null) moduleKindMap[moduleKindName] else ModuleKind.PLAIN
|
||||
if (moduleKind == null) {
|
||||
messageCollector.report(
|
||||
ERROR, "Unknown module kind: $moduleKindName. Valid values are: plain, amd, commonjs, umd", null
|
||||
ERROR, "Unknown module kind: $moduleKindName. Valid values are: plain, amd, commonjs, umd, es", null
|
||||
)
|
||||
moduleKind = ModuleKind.PLAIN
|
||||
}
|
||||
|
||||
@@ -1,459 +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
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.LoweredIr
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity.*
|
||||
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.transformers.irToJs.IrFileToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.processClassModels
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.hasInterfaceParent
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import kotlin.math.abs
|
||||
|
||||
interface CompilerOutputSink {
|
||||
fun write(module: String, path: String, content: String)
|
||||
}
|
||||
|
||||
class JsGenerationOptions(
|
||||
val jsExtension: String = "js",
|
||||
val generatePackageJson: Boolean = false,
|
||||
val generateTypeScriptDefinitions: Boolean = false,
|
||||
)
|
||||
|
||||
class IrToJs(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val guid: (IrDeclaration) -> String,
|
||||
private val outputSink: CompilerOutputSink,
|
||||
private val mainArguments: List<String>?,
|
||||
private val granularity: JsGenerationGranularity,
|
||||
private val mainModuleName: String,
|
||||
private val options: JsGenerationOptions,
|
||||
) {
|
||||
val indexFileName = "index.${options.jsExtension}"
|
||||
|
||||
val FileUnit.initFunctionName
|
||||
get() = "KotlinInit$" + sanitizeName(pathToJsModule(file))
|
||||
|
||||
sealed class CodegenUnitReference
|
||||
object ThisUnitReference : CodegenUnitReference()
|
||||
inner class OtherUnitReference(
|
||||
module: IrModuleFragment,
|
||||
) : CodegenUnitReference() {
|
||||
// Path to entry point of other module from "top-level", e.g. directory which contains all other modules
|
||||
val importPath = "./" + module.jsModuleName + "/" + indexFileName
|
||||
}
|
||||
|
||||
abstract class CodegenUnit {
|
||||
abstract val packageFragments: Iterable<IrPackageFragment>
|
||||
abstract val externalPackageFragments: Iterable<IrPackageFragment>
|
||||
abstract fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference
|
||||
abstract val pathToKotlinModulesRoot: String
|
||||
}
|
||||
|
||||
inner class FileUnit(val file: IrFile, val externalFile: IrFile?) : CodegenUnit() {
|
||||
override val packageFragments =
|
||||
listOf(file)
|
||||
|
||||
override val externalPackageFragments =
|
||||
listOfNotNull(externalFile)
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
when (val declarationFile = declaration.file) {
|
||||
file -> ThisUnitReference
|
||||
else -> OtherUnitReference(declarationFile.module)
|
||||
}
|
||||
|
||||
override val pathToKotlinModulesRoot: String by lazy {
|
||||
"../".repeat(file.fqName.pathSegments().size + 1)
|
||||
}
|
||||
}
|
||||
|
||||
inner class ModuleUnit(val module: IrModuleFragment) : CodegenUnit() {
|
||||
override val packageFragments: Iterable<IrPackageFragment> =
|
||||
module.files
|
||||
|
||||
override val externalPackageFragments: Iterable<IrPackageFragment> =
|
||||
packageFragments.mapNotNull { backendContext.externalPackageFragment[it.symbol] }
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
when (val declarationModule = declaration.file.module) {
|
||||
module -> ThisUnitReference
|
||||
else -> OtherUnitReference(declarationModule)
|
||||
}
|
||||
|
||||
override val pathToKotlinModulesRoot: String = "../"
|
||||
}
|
||||
|
||||
class WholeProgramUnit(
|
||||
val modules: Iterable<IrModuleFragment>,
|
||||
val externalModules: Iterable<IrPackageFragment>
|
||||
) : CodegenUnit() {
|
||||
override val packageFragments: Iterable<IrPackageFragment> =
|
||||
modules.flatMap { it.files }
|
||||
|
||||
override val externalPackageFragments: Iterable<IrPackageFragment>
|
||||
get() = externalModules
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
ThisUnitReference
|
||||
|
||||
override val pathToKotlinModulesRoot: String
|
||||
get() = "../"
|
||||
}
|
||||
|
||||
private fun pathToJsModule(file: IrFile): String =
|
||||
"${fileJsRootModuleName(file)}/${fileJsSubModulePath(file)}"
|
||||
|
||||
private fun fileJsRootModuleName(file: IrFile): String =
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM -> mainModuleName
|
||||
PER_MODULE, PER_FILE -> file.module.jsModuleName
|
||||
}
|
||||
|
||||
private fun fileJsSubModulePath(file: IrFile): String =
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM, PER_MODULE -> indexFileName
|
||||
|
||||
PER_FILE -> {
|
||||
val maybeSingleOpenClass = (file.declarations.singleOrNull() as? IrClass)?.takeIf {
|
||||
it.modality == Modality.ABSTRACT || it.modality == Modality.OPEN
|
||||
}
|
||||
|
||||
val hash = abs((maybeSingleOpenClass?.let { guid(it) } ?: file.path).hashCode())
|
||||
val filePrefix = maybeSingleOpenClass?.name?.asString()?.let { sanitizeName(it) + ".class" } ?: file.name
|
||||
val fileName = "${filePrefix}_$hash.${options.jsExtension}"
|
||||
val packagePath = file.fqName.pathSegments().joinToString("") { it.identifier + "/" }
|
||||
"$packagePath$fileName"
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedUnit(
|
||||
val jsStatements: List<JsStatement>,
|
||||
val exportedDeclarations: List<ExportedDeclaration>,
|
||||
)
|
||||
|
||||
fun generateUnit(unit: CodegenUnit): GeneratedUnit {
|
||||
val exportedDeclarations: List<ExportedDeclaration> =
|
||||
with(ExportModelGenerator(backendContext, generateNamespacesForPackages = false)) {
|
||||
(unit.externalPackageFragments + unit.packageFragments).flatMap { packageFragment ->
|
||||
generateExport(packageFragment)
|
||||
}
|
||||
}
|
||||
|
||||
val stableNames: Set<String> = collectStableNames(unit)
|
||||
val nameGenerator = NewNamerImpl(backendContext, unit, guid, stableNames)
|
||||
|
||||
val staticContext = JsStaticContext(
|
||||
backendContext = backendContext,
|
||||
irNamer = nameGenerator,
|
||||
globalNameScope = nameGenerator.staticNames
|
||||
)
|
||||
|
||||
val declarationStatements: List<JsStatement> = unit.packageFragments.flatMap {
|
||||
StaticMembersLowering(backendContext).lower(it as IrFile)
|
||||
it.accept(IrFileToJsTransformer(), staticContext).statements
|
||||
}
|
||||
|
||||
val preDeclarationBlock = JsCompositeBlock()
|
||||
val postDeclarationBlock = JsCompositeBlock()
|
||||
processClassModels(staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
statements += nameGenerator.internalImports.values
|
||||
statements += preDeclarationBlock
|
||||
statements += declarationStatements
|
||||
statements += postDeclarationBlock
|
||||
|
||||
// Generate module initialization
|
||||
|
||||
val initializerBlock = staticContext.initializerBlock
|
||||
when (unit) {
|
||||
is WholeProgramUnit, is ModuleUnit -> {
|
||||
// Run initialization during ES module initialization
|
||||
statements += initializerBlock
|
||||
}
|
||||
|
||||
is FileUnit -> {
|
||||
// Postpone initialization by putting it into a separate function
|
||||
// Will be called later in proper order after class model is initialized
|
||||
val initFunction = JsFunction(emptyScope, JsBlock(initializerBlock.statements), "init fun")
|
||||
initFunction.name = JsName(unit.initFunctionName, false)
|
||||
statements += initFunction.makeStmt()
|
||||
statements += JsExport(initFunction.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate internal export
|
||||
|
||||
val internalExports = mutableListOf<JsExport.Element>()
|
||||
fun export(declaration: IrDeclarationWithName) {
|
||||
internalExports += JsExport.Element(nameGenerator.getNameForStaticDeclaration(declaration), JsName(guid(declaration), false))
|
||||
}
|
||||
|
||||
for (fragment in unit.packageFragments) {
|
||||
for (declaration in fragment.declarations) {
|
||||
if (declaration is IrDeclarationWithName) {
|
||||
if (declaration.origin == JsCodeOutliningLowering.OUTLINED_JS_CODE_ORIGIN) continue
|
||||
export(declaration)
|
||||
}
|
||||
|
||||
// Default implementations of interface methods are nested under interface declarations in IR at this point,
|
||||
// but they are effectively used as a static declaration and can be directly referenced by other codegen unit,
|
||||
// thus requiring internal export
|
||||
declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||
if (declaration.hasInterfaceParent() && declaration.body != null) {
|
||||
export(declaration)
|
||||
}
|
||||
super.visitSimpleFunction(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
statements += JsExport(JsExport.Subject.Elements(internalExports), null)
|
||||
|
||||
// Generate external export
|
||||
|
||||
val globalNames = NameTable<String>(nameGenerator.staticNames)
|
||||
val exporter = ExportModelToJsStatements(
|
||||
staticContext,
|
||||
declareNewNamespace = { globalNames.declareFreshName(it, it) }
|
||||
)
|
||||
exportedDeclarations.forEach {
|
||||
statements += exporter.generateDeclarationExport(
|
||||
it,
|
||||
null,
|
||||
esModules = true
|
||||
)
|
||||
}
|
||||
|
||||
return GeneratedUnit(statements, exportedDeclarations)
|
||||
}
|
||||
|
||||
private fun collectStableNames(unit: CodegenUnit): Set<String> {
|
||||
val newStableStaticNamesCollectorVisitor =
|
||||
NewStableStaticNamesCollectorVisitor(needToCollectReferences = granularity != WHOLE_PROGRAM)
|
||||
unit.packageFragments.forEach { it.acceptVoid(newStableStaticNamesCollectorVisitor) }
|
||||
unit.externalPackageFragments.forEach { it.acceptVoid(newStableStaticNamesCollectorVisitor) }
|
||||
|
||||
return newStableStaticNamesCollectorVisitor.collectedStableNames
|
||||
}
|
||||
|
||||
// Returns import statement and call expression
|
||||
private fun invokeFunctionFromEntryJsFile(
|
||||
function: IrFunction,
|
||||
args: List<JsExpression> = emptyList()
|
||||
): Pair<JsStatement, JsExpression> {
|
||||
val name = guid(function)
|
||||
val importPath = if (granularity == WHOLE_PROGRAM) "./$indexFileName" else "../" + pathToJsModule(function.file)
|
||||
return Pair(
|
||||
JsImport(importPath, mutableListOf(JsImport.Element(name, null))),
|
||||
JsInvocation(JsNameRef(name), args)
|
||||
)
|
||||
}
|
||||
|
||||
private fun invokeFunctionFromEntryJsFileAsStatements(
|
||||
function: IrFunction,
|
||||
args: List<JsExpression> = emptyList()
|
||||
): List<JsStatement> =
|
||||
invokeFunctionFromEntryJsFile(function, args)
|
||||
.let { listOf(it.first, it.second.makeStmt()) }
|
||||
|
||||
fun generateModules(
|
||||
mainModule: IrModuleFragment,
|
||||
allModules: List<IrModuleFragment>
|
||||
) {
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM ->
|
||||
generateModule(mainModule, allModules)
|
||||
|
||||
PER_MODULE,
|
||||
PER_FILE ->
|
||||
allModules.forEach { module ->
|
||||
generateModule(mainModule = module, allModules = emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateModuleLevelCode(module: IrModuleFragment, statements: MutableList<JsStatement>) {
|
||||
if (mainArguments != null) {
|
||||
val mainFunction = JsMainFunctionDetector(backendContext).getMainFunctionOrNull(module)
|
||||
if (mainFunction != null) {
|
||||
val generateArgv = mainFunction.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
||||
val generateContinuation = mainFunction.isLoweredSuspendFunction(backendContext)
|
||||
|
||||
val mainArgumentsArray =
|
||||
if (generateArgv)
|
||||
JsArrayLiteral(mainArguments.map { JsStringLiteral(it) })
|
||||
else
|
||||
null
|
||||
|
||||
val continuation =
|
||||
if (generateContinuation) {
|
||||
val (import, invoke) = invokeFunctionFromEntryJsFile(backendContext.coroutineEmptyContinuation.owner.getter!!)
|
||||
statements += import
|
||||
invoke
|
||||
} else
|
||||
null
|
||||
|
||||
statements += invokeFunctionFromEntryJsFileAsStatements(
|
||||
mainFunction, listOfNotNull(mainArgumentsArray, continuation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: tests
|
||||
// backendContext.testRoots[module]?.let { testContainer ->
|
||||
// statements += invokeFunctionFromEntryJsFileAsStatements(testContainer)
|
||||
// }
|
||||
}
|
||||
|
||||
fun generateModule(
|
||||
mainModule: IrModuleFragment,
|
||||
allModules: List<IrModuleFragment>,
|
||||
) {
|
||||
val moduleName = mainModule.jsModuleName
|
||||
val indexJsStatements = mutableListOf<JsStatement>()
|
||||
val exportedDeclarations = mutableListOf<ExportedDeclaration>()
|
||||
|
||||
when (granularity) {
|
||||
PER_FILE -> {
|
||||
for (file in mainModule.files.sortedBy(::fileInitOrder)) {
|
||||
if (file.declarations.isEmpty()) continue
|
||||
|
||||
val pathToSubModule = fileJsSubModulePath(file)
|
||||
indexJsStatements += JsExport(JsExport.Subject.All, fromModule = "./$pathToSubModule")
|
||||
|
||||
val unit = FileUnit(file, backendContext.externalPackageFragment[file.symbol])
|
||||
val generatedUnit = generateUnit(unit)
|
||||
|
||||
val importElements = JsImport.Element(unit.initFunctionName, null)
|
||||
indexJsStatements += JsImport("./$pathToSubModule", mutableListOf(importElements))
|
||||
indexJsStatements += JsInvocation(JsNameRef(JsName(unit.initFunctionName, false))).makeStmt()
|
||||
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
|
||||
outputSink.write(
|
||||
file.module.jsModuleName,
|
||||
pathToSubModule,
|
||||
"// Kotlin file: ${file.path}\n" + generatedUnit.jsStatements.toJsCodeString()
|
||||
)
|
||||
}
|
||||
generateModuleLevelCode(mainModule, indexJsStatements)
|
||||
}
|
||||
|
||||
PER_MODULE -> {
|
||||
val generatedUnit = generateUnit(ModuleUnit(mainModule))
|
||||
indexJsStatements += generatedUnit.jsStatements
|
||||
generateModuleLevelCode(mainModule, indexJsStatements)
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
}
|
||||
|
||||
WHOLE_PROGRAM -> {
|
||||
val generatedUnit = generateUnit(WholeProgramUnit(allModules, backendContext.externalPackageFragment.values))
|
||||
indexJsStatements += generatedUnit.jsStatements
|
||||
allModules.forEach {
|
||||
generateModuleLevelCode(it, indexJsStatements)
|
||||
}
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
}
|
||||
}
|
||||
|
||||
outputSink.write(moduleName, indexFileName, indexJsStatements.toJsCodeString())
|
||||
|
||||
if (options.generatePackageJson) {
|
||||
outputSink.write(moduleName, "package.json", """{ "main": "$indexFileName", "type": "module" }""")
|
||||
}
|
||||
|
||||
if (options.generateTypeScriptDefinitions && exportedDeclarations.isNotEmpty()) {
|
||||
val dts = ExportedModule(moduleName, moduleKind = ModuleKind.ES, exportedDeclarations).toTypeScript()
|
||||
outputSink.write(moduleName, "index.d.ts", dts)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fileInitOrder(file: IrFile): Int =
|
||||
when (val singleDeclaration = file.declarations.singleOrNull()) {
|
||||
// Initialize parent classes before child classes
|
||||
// TODO: Comment about open classes in separate files
|
||||
is IrClass -> singleDeclaration.getInheritanceChainLength()
|
||||
// Initialize regular files after all open classes
|
||||
else -> Int.MAX_VALUE
|
||||
}
|
||||
|
||||
private fun IrClass.getInheritanceChainLength(): Int {
|
||||
if (symbol == backendContext.irBuiltIns.anyClass)
|
||||
return 0
|
||||
|
||||
// FIXME: Filter out interfaces
|
||||
superTypes.forEach { superType ->
|
||||
val superClass: IrClass? = superType.classOrNull?.owner
|
||||
if (superClass != null && /* !!! */ !superClass.isInterface)
|
||||
return superClass.getInheritanceChainLength() + 1
|
||||
}
|
||||
|
||||
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
private val IrModuleFragment.jsModuleName: String
|
||||
get() = name.asString()
|
||||
.replace("[.:@]".toRegex(), "_")
|
||||
.dropWhile { it == '<' }
|
||||
.dropLastWhile { it == '>' }
|
||||
|
||||
private fun List<JsStatement>.toJsCodeString(): String =
|
||||
JsCompositeBlock(this).toString()
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
PER_FILE
|
||||
}
|
||||
|
||||
fun generateEsModules(
|
||||
ir: LoweredIr,
|
||||
outputSink: CompilerOutputSink,
|
||||
mainArguments: List<String>?,
|
||||
granularity: JsGenerationGranularity,
|
||||
options: JsGenerationOptions,
|
||||
) {
|
||||
// Declaration numeration to create temporary GUID
|
||||
// TODO: Replace with an actual GUID
|
||||
val numerator = StaticDeclarationNumerator()
|
||||
ir.allModules.forEach { numerator.add(it) }
|
||||
|
||||
fun guid(declaration: IrDeclaration): String {
|
||||
val name = sanitizeName((declaration as IrDeclarationWithName).name.toString())
|
||||
val number = numerator.numeration[declaration]
|
||||
?: error("Can't find number for declaration ${declaration.fqNameWhenAvailable}")
|
||||
// TODO: Use shorter names in release mode
|
||||
return "${name}_GUID_${number}"
|
||||
}
|
||||
|
||||
val ir2js = IrToJs(ir.context, ::guid, outputSink, mainArguments, granularity, ir.mainModule.jsModuleName, options)
|
||||
ir2js.generateModules(ir.mainModule, ir.allModules)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
@@ -48,7 +48,7 @@ data class ExportedConstructSignature(
|
||||
val returnType: ExportedType,
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedProperty(
|
||||
data class ExportedProperty(
|
||||
val name: String,
|
||||
val type: ExportedType,
|
||||
val mutable: Boolean = true,
|
||||
@@ -94,7 +94,7 @@ data class ExportedObject(
|
||||
override val members: List<ExportedDeclaration>,
|
||||
override val nestedClasses: List<ExportedClass>,
|
||||
override val ir: IrClass,
|
||||
val irGetter: IrFunction
|
||||
val irGetter: IrSimpleFunction
|
||||
) : ExportedClass()
|
||||
|
||||
class ExportedParameter(
|
||||
|
||||
+83
-37
@@ -5,8 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.prototypeOf
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
|
||||
@@ -17,8 +25,14 @@ class ExportModelToJsStatements(
|
||||
) {
|
||||
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
||||
|
||||
fun generateModuleExport(module: ExportedModule, internalModuleName: JsName): List<JsStatement> {
|
||||
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName), esModules = false) }
|
||||
fun generateModuleExport(
|
||||
module: ExportedModule,
|
||||
internalModuleName: JsName?,
|
||||
esModules: Boolean
|
||||
): List<JsStatement> {
|
||||
return module.declarations.flatMap {
|
||||
generateDeclarationExport(it, internalModuleName?.makeRef(), esModules)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDeclarationExport(
|
||||
@@ -60,17 +74,12 @@ class ExportModelToJsStatements(
|
||||
|
||||
is ExportedFunction -> {
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
if (esModules) {
|
||||
listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||
} else {
|
||||
if (namespace != null) {
|
||||
listOf(
|
||||
jsAssignment(
|
||||
jsElementAccess(declaration.name, namespace),
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
)
|
||||
} else emptyList()
|
||||
when {
|
||||
namespace != null ->
|
||||
listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt())
|
||||
|
||||
esModules -> listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,40 +87,77 @@ class ExportModelToJsStatements(
|
||||
is ExportedConstructSignature -> emptyList()
|
||||
|
||||
is ExportedProperty -> {
|
||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
||||
val getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.irSetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter, namer).makeStmt())
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
val getter = declaration.irGetter?.let { namer.getNameForStaticDeclaration(it) }
|
||||
val setter = declaration.irSetter?.let { namer.getNameForStaticDeclaration(it) }
|
||||
if (namespace == null) {
|
||||
val property = JsVars.JsVar(
|
||||
JsName(declaration.name, false),
|
||||
JsObjectLiteral(false).apply {
|
||||
getter?.let {
|
||||
val fieldName = when (declaration.irGetter.origin) {
|
||||
JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION -> "getInstance"
|
||||
else -> "get"
|
||||
}
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral(fieldName), it.makeRef())
|
||||
}
|
||||
setter?.let { propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), it.makeRef()) }
|
||||
}
|
||||
)
|
||||
listOf(
|
||||
JsVars(property),
|
||||
JsExport(property.name, JsName(declaration.name, false))
|
||||
)
|
||||
} else {
|
||||
listOf(defineProperty(namespace, declaration.name, getter?.makeRef(), setter?.makeRef(), namer).makeStmt())
|
||||
}
|
||||
}
|
||||
|
||||
is ErrorDeclaration -> emptyList()
|
||||
|
||||
is ExportedObject -> {
|
||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
||||
val newNameSpace = jsElementAccess(declaration.name, namespace)
|
||||
val getter = JsNameRef(namer.getNameForStaticDeclaration(declaration.irGetter))
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
else ->
|
||||
jsElementAccess(Namer.PROTOTYPE_NAME, namer.getNameForClass(declaration.ir).makeRef())
|
||||
}
|
||||
val staticsExport = declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, null, namer).makeStmt()) + staticsExport
|
||||
|
||||
val objectExport = when (namespace) {
|
||||
null -> generateDeclarationExport(
|
||||
ExportedProperty(declaration.name, ExportedType.Primitive.Any, irGetter = declaration.irGetter),
|
||||
namespace,
|
||||
esModules
|
||||
)
|
||||
|
||||
else -> listOf(
|
||||
defineProperty(
|
||||
namespace,
|
||||
declaration.name,
|
||||
namer.getNameForStaticDeclaration(declaration.irGetter).makeRef(),
|
||||
null,
|
||||
namer
|
||||
).makeStmt()
|
||||
)
|
||||
}
|
||||
|
||||
objectExport + staticsExport
|
||||
}
|
||||
|
||||
is ExportedRegularClass -> {
|
||||
if (declaration.isInterface) return emptyList()
|
||||
val newNameSpace = if (namespace != null)
|
||||
jsElementAccess(declaration.name, namespace)
|
||||
else
|
||||
prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer)
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
val klassExport =
|
||||
if (esModules) {
|
||||
JsExport(name, alias = JsName(declaration.name, false))
|
||||
} else {
|
||||
if (namespace != null) {
|
||||
jsAssignment(
|
||||
newNameSpace,
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
} else null
|
||||
}
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
esModules -> name.makeRef()
|
||||
else -> prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer)
|
||||
}
|
||||
val klassExport = when {
|
||||
namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
|
||||
esModules -> JsExport(name, alias = JsName(declaration.name, false))
|
||||
else -> null
|
||||
}
|
||||
|
||||
// These are only used when exporting secondary constructors annotated with @JsName
|
||||
val staticFunctions = declaration.members
|
||||
|
||||
+73
-45
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getFqNameWithJsNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
@@ -15,11 +16,14 @@ import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import javax.lang.model.type.IntersectionType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
private const val Nullable = "Nullable"
|
||||
private const val objects = "_objects_"
|
||||
private const val declare = "declare "
|
||||
private const val declareExorted = "export $declare"
|
||||
|
||||
private const val NonExistent = "__NonExistent"
|
||||
private const val syntheticObjectNameSeparator = '$'
|
||||
|
||||
fun ExportedModule.toTypeScript(): String {
|
||||
@@ -61,32 +65,36 @@ class ExportModelToTsDeclarations {
|
||||
return joinToString("\n") {
|
||||
it.toTypeScript(
|
||||
indent = moduleKind.indent,
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else "export "
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else declareExorted,
|
||||
esModules = moduleKind == ModuleKind.ES
|
||||
)
|
||||
} + generateObjectsNamespaceIfNeeded(moduleKind.indent)
|
||||
} + generateObjectsNamespaceIfNeeded(
|
||||
indent = moduleKind.indent,
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else declare,
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateObjectsNamespaceIfNeeded(indent: String): String {
|
||||
private fun generateObjectsNamespaceIfNeeded(indent: String, prefix: String): String {
|
||||
return if (objectsSyntheticProperties.isEmpty()) {
|
||||
""
|
||||
} else {
|
||||
"\n" + ExportedNamespace(objects, objectsSyntheticProperties).toTypeScript(indent, "")
|
||||
"\n" + ExportedNamespace(objects, objectsSyntheticProperties).toTypeScript(indent, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||
|
||||
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String =
|
||||
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = "", esModules: Boolean = false): String =
|
||||
indent + when (this) {
|
||||
is ErrorDeclaration -> generateTypeScriptString()
|
||||
is ExportedNamespace -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedFunction -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedConstructor -> generateTypeScriptString(indent)
|
||||
is ExportedConstructSignature -> generateTypeScriptString(indent)
|
||||
is ExportedProperty -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedObject -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedNamespace -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedFunction -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedRegularClass -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedProperty -> generateTypeScriptString(indent, prefix, esModules)
|
||||
is ExportedObject -> generateTypeScriptString(indent, prefix, esModules)
|
||||
}
|
||||
|
||||
private fun ErrorDeclaration.generateTypeScriptString(): String {
|
||||
@@ -107,34 +115,45 @@ class ExportModelToTsDeclarations {
|
||||
return "new($renderedParameters): ${returnType.toTypeScript(indent)};"
|
||||
}
|
||||
|
||||
private fun ExportedProperty.generateTypeScriptString(indent: String, prefix: String): String {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
val possibleStatic = if (isMember && isStatic) "static " else ""
|
||||
private fun ExportedProperty.generateTypeScriptString(indent: String, prefix: String, esModules: Boolean = false): String {
|
||||
val extraIndent = "$indent "
|
||||
val optional = if (isOptional) "?" else ""
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val memberName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
val typeToTypeScript = type.toTypeScript(indent)
|
||||
val memberName = if (containsUnresolvedChar) "\"$name\"" else name
|
||||
val isObjectGetter = irGetter?.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION
|
||||
|
||||
return if (isMember && !isField) {
|
||||
val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
|
||||
if (!mutable) {
|
||||
getter
|
||||
val typeToTypeScript = type.toTypeScript(if (!isMember && esModules && isObjectGetter) extraIndent else indent)
|
||||
|
||||
return if (isMember) {
|
||||
val static = if (isStatic) "static " else ""
|
||||
val abstract = if (isAbstract) "abstract " else ""
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
|
||||
if (isField) {
|
||||
val readonly = if (!mutable) "readonly " else ""
|
||||
"$prefix$visibility$static$abstract$readonly$memberName$optional: $typeToTypeScript;"
|
||||
} else {
|
||||
getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
|
||||
val getter = "$prefix$visibility$static${abstract}get $memberName(): $typeToTypeScript;"
|
||||
val setter = runIf(mutable) { "\n$indent$prefix$visibility$static${abstract}set $memberName(value: $typeToTypeScript);" }
|
||||
getter + setter.orEmpty()
|
||||
}
|
||||
} else {
|
||||
if (!isMember && containsUnresolvedChar) {
|
||||
""
|
||||
} else {
|
||||
val readonly = if (isMember && !mutable) "readonly " else ""
|
||||
val optional = if (isOptional) "?" else ""
|
||||
"$prefix$visibility$possibleStatic$keyword$readonly$memberName$optional: $typeToTypeScript;"
|
||||
when {
|
||||
containsUnresolvedChar -> ""
|
||||
esModules -> {
|
||||
if (isObjectGetter) {
|
||||
"${prefix}const $name: {\n${extraIndent}getInstance(): $typeToTypeScript;\n};"
|
||||
} else {
|
||||
val getter = "get(): $typeToTypeScript;"
|
||||
val setter = runIf(mutable) { " set(value: $typeToTypeScript): void;" }
|
||||
"${prefix}const $name: { $getter${setter.orEmpty()} };"
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val keyword = if (mutable) "let " else "const "
|
||||
"$prefix$keyword$memberName$optional: $typeToTypeScript;"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,14 +190,19 @@ class ExportModelToTsDeclarations {
|
||||
return if (!isMember && containsUnresolvedChar) {
|
||||
""
|
||||
} else {
|
||||
"${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
"$prefix$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExportedObject.generateTypeScriptString(indent: String, prefix: String): String {
|
||||
private fun ExportedObject.generateTypeScriptString(indent: String, prefix: String, esModules: Boolean = false): String {
|
||||
val shouldRenderSeparatedAbstractClass = !couldBeProperty()
|
||||
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members)
|
||||
val extraMembers = nestedClasses
|
||||
.takeIf { !shouldRenderSeparatedAbstractClass }
|
||||
?.map { it as ExportedObject }
|
||||
.orEmpty()
|
||||
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members + extraMembers)
|
||||
|
||||
for (superInterface in superClasses + superInterfaces) {
|
||||
t = ExportedType.IntersectionType(t, superInterface)
|
||||
@@ -208,13 +232,14 @@ class ExportModelToTsDeclarations {
|
||||
)
|
||||
|
||||
return if (!shouldRenderSeparatedAbstractClass) {
|
||||
property.generateTypeScriptString(indent, prefix)
|
||||
property.generateTypeScriptString(indent, prefix, esModules)
|
||||
} else {
|
||||
val className = NonExistent.takeIf { esModules }.orEmpty() + name
|
||||
val propertyRef = "$objects.$propertyName"
|
||||
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClasses.isNotEmpty()
|
||||
val newSuperClass = ExportedType.ClassType(propertyRef, emptyList(), ir).takeIf { shouldCreateExtraProperty }
|
||||
ExportedRegularClass(
|
||||
name = name,
|
||||
val classForRender = ExportedRegularClass(
|
||||
name = className,
|
||||
isInterface = false,
|
||||
isAbstract = true,
|
||||
superClasses = listOfNotNull(newSuperClass),
|
||||
@@ -224,8 +249,14 @@ class ExportModelToTsDeclarations {
|
||||
nestedClasses = nestedClasses,
|
||||
ir = ir
|
||||
)
|
||||
.generateTypeScriptString(indent, prefix)
|
||||
.also { if (shouldCreateExtraProperty) objectsSyntheticProperties.add(property) }
|
||||
|
||||
if (esModules && !property.isMember) {
|
||||
property.copy(type = ExportedType.TypeOf(className), name = name)
|
||||
.generateTypeScriptString(indent, prefix, esModules) + "\n${classForRender.generateTypeScriptString(indent, declare)}"
|
||||
} else {
|
||||
classForRender.generateTypeScriptString(indent, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +303,7 @@ class ExportModelToTsDeclarations {
|
||||
val klassExport =
|
||||
"$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
||||
val staticsExport =
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(
|
||||
indent,
|
||||
prefix
|
||||
) else ""
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
|
||||
|
||||
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class JsExecutableProducer(
|
||||
val jsMultiModuleCache = JsMultiModuleCache(caches)
|
||||
val cachedProgram = jsMultiModuleCache.loadProgramHeadersFromCache()
|
||||
|
||||
val resolver = CrossModuleDependenciesResolver(cachedProgram.map { it.jsIrHeader })
|
||||
val resolver = CrossModuleDependenciesResolver(moduleKind, cachedProgram.map { it.jsIrHeader })
|
||||
val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
|
||||
|
||||
jsMultiModuleCache.loadRequiredJsIrModules(crossModuleReferences)
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ class IrModuleToJsTransformer(
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val globalNames = NameTable<String>(namer.globalNames)
|
||||
val exportStatements = ExportModelToJsStatements(staticContext) { globalNames.declareFreshName(it, it) }
|
||||
.generateModuleExport(exportedModule, internalModuleName)
|
||||
.generateModuleExport(exportedModule, internalModuleName, false)
|
||||
|
||||
val (crossModuleImports, importedKotlinModules) = generateCrossModuleImports(nameGenerator, modules, dependencies, { JsName(sanitizeName(it), false) })
|
||||
val crossModuleExports = generateCrossModuleExports(modules, refInfo, internalModuleName)
|
||||
|
||||
+7
-5
@@ -94,6 +94,7 @@ class IrModuleToJsTransformerTmp(
|
||||
|
||||
private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
|
||||
|
||||
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<Pair<IrFile, List<ExportedDeclaration>>>)
|
||||
@@ -103,7 +104,7 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
private fun associateIrAndExport(modules: Iterable<IrModuleFragment>): List<IrAndExportedDeclarations> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||
|
||||
return modules.map { module ->
|
||||
val files = module.files.map { file ->
|
||||
@@ -166,7 +167,7 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
fun generateBinaryAst(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<JsIrFragmentAndBinaryAst> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||
|
||||
val exportData = files.map { it to exportModelGenerator.generateExportWithExternals(it) }
|
||||
|
||||
@@ -235,12 +236,13 @@ class IrModuleToJsTransformerTmp(
|
||||
polyfills.statements += backendContext.polyfills.getAllPolyfillsFor(file)
|
||||
}
|
||||
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules }
|
||||
val globalNames = NameTable<String>(globalNameScope)
|
||||
val exportStatements =
|
||||
ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport(
|
||||
ExportedModule(mainModuleName, moduleKind, exports),
|
||||
internalModuleName,
|
||||
isEsModules
|
||||
)
|
||||
|
||||
result.exports.statements += exportStatements
|
||||
@@ -379,7 +381,7 @@ private fun generateWrappedModuleBody(
|
||||
// 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(relativeRequirePath).toMutableList()
|
||||
val moduleToRef = program.asCrossModuleDependencies(moduleKind, relativeRequirePath).toMutableList()
|
||||
val mainModule = moduleToRef.removeLast().let { (main, mainRef) ->
|
||||
generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
@@ -433,7 +435,7 @@ fun generateSingleWrappedModuleBody(
|
||||
sourceMapsInfo: SourceMapsInfo?,
|
||||
generateScriptModule: Boolean,
|
||||
generateCallToMain: Boolean,
|
||||
crossModuleReferences: CrossModuleReferences = CrossModuleReferences.Empty,
|
||||
crossModuleReferences: CrossModuleReferences = CrossModuleReferences.Empty(moduleKind),
|
||||
outJsProgram: Boolean = true
|
||||
): CompilationOutputs {
|
||||
val program = Merger(
|
||||
|
||||
+32
-8
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
class JsIrProgramFragment(val packageFqn: String) {
|
||||
val nameBindings = mutableMapOf<String, JsName>()
|
||||
@@ -57,8 +58,8 @@ class JsIrModuleHeader(
|
||||
}
|
||||
|
||||
class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
fun asCrossModuleDependencies(relativeRequirePath: Boolean): List<Pair<JsIrModule, CrossModuleReferences>> {
|
||||
val resolver = CrossModuleDependenciesResolver(modules.map { it.makeModuleHeader() })
|
||||
fun asCrossModuleDependencies(moduleKind: ModuleKind, relativeRequirePath: Boolean): List<Pair<JsIrModule, CrossModuleReferences>> {
|
||||
val resolver = CrossModuleDependenciesResolver(moduleKind, modules.map { it.makeModuleHeader() })
|
||||
modules = emptyList()
|
||||
val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
|
||||
return crossModuleReferences.entries.map {
|
||||
@@ -75,9 +76,12 @@ class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
}
|
||||
}
|
||||
|
||||
class CrossModuleDependenciesResolver(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 { JsIrModuleCrossModuleReferecenceBuilder(it, relativeRequirePath) }
|
||||
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferecenceBuilder(moduleKind, it, relativeRequirePath) }
|
||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferecenceBuilder>()
|
||||
|
||||
val mainModuleHeader = headers.last()
|
||||
@@ -110,7 +114,11 @@ private fun String.prettyTag() = takeWhile { c -> c != '|' }
|
||||
|
||||
private class CrossModuleRef(val module: JsIrModuleCrossModuleReferecenceBuilder, val tag: String)
|
||||
|
||||
private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHeader, val relativeRequirePath: Boolean) {
|
||||
private class JsIrModuleCrossModuleReferecenceBuilder(
|
||||
val moduleKind: ModuleKind,
|
||||
val header: JsIrModuleHeader,
|
||||
val relativeRequirePath: Boolean
|
||||
) {
|
||||
val imports = mutableListOf<CrossModuleRef>()
|
||||
val exports = mutableSetOf<String>()
|
||||
var transitiveJsExportFrom = emptyList<JsIrModuleHeader>()
|
||||
@@ -155,7 +163,13 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHead
|
||||
val transitiveExport = transitiveJsExportFrom.mapNotNull {
|
||||
if (it.hasJsExports) import(it) else null
|
||||
}
|
||||
return CrossModuleReferences(importedModules.values.toList(), transitiveExport, exportNames, resultImports)
|
||||
return CrossModuleReferences(
|
||||
moduleKind,
|
||||
importedModules.values.toList(),
|
||||
transitiveExport,
|
||||
exportNames,
|
||||
resultImports
|
||||
)
|
||||
}
|
||||
|
||||
private fun relativeRequirePath(moduleHeader: JsIrModuleHeader): String? {
|
||||
@@ -177,6 +191,7 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHead
|
||||
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsName)
|
||||
|
||||
class CrossModuleReferences(
|
||||
val moduleKind: ModuleKind,
|
||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
||||
val transitiveJsExportFrom: List<JsName>, // the list of modules which provide their js exports for transitive export
|
||||
val exports: Map<String, String>, // tag -> index
|
||||
@@ -190,12 +205,21 @@ class CrossModuleReferences(
|
||||
val tagToName = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to it.value }
|
||||
jsImports = imports.entries.associate {
|
||||
val importedAs = tagToName[it.key] ?: error("Internal error: cannot find imported name for symbol ${it.key.prettyTag()}")
|
||||
val exportRef = JsNameRef(it.value.exportedAs, ReservedJsNames.makeCrossModuleNameRef(it.value.moduleExporter))
|
||||
val exportRef = JsNameRef(
|
||||
it.value.exportedAs,
|
||||
it.value.moduleExporter.let {
|
||||
if (moduleKind == ModuleKind.ES) {
|
||||
it.makeRef()
|
||||
} else {
|
||||
ReservedJsNames.makeCrossModuleNameRef(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
it.key to JsVars.JsVar(importedAs, exportRef)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Empty = CrossModuleReferences(listOf(), emptyList(), emptyMap(), emptyMap())
|
||||
fun Empty(moduleKind: ModuleKind) = CrossModuleReferences(moduleKind, listOf(), emptyList(), emptyMap(), emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
+54
-30
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.partitionIsInstance
|
||||
|
||||
class Merger(
|
||||
private val moduleName: String,
|
||||
@@ -20,6 +21,7 @@ class Merger(
|
||||
private val generateCallToMain: Boolean,
|
||||
) {
|
||||
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val importStatements = mutableMapOf<String, JsStatement>()
|
||||
private val importedModulesMap = mutableMapOf<JsImportedModuleKey, JsImportedModule>()
|
||||
|
||||
@@ -65,16 +67,25 @@ class Merger(
|
||||
if (crossModuleReferences.exports.isNotEmpty()) {
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
|
||||
val createExportBlock = jsAssignment(
|
||||
ReservedJsNames.makeCrossModuleNameRef(internalModuleName),
|
||||
JsAstUtils.or(ReservedJsNames.makeCrossModuleNameRef(internalModuleName), JsObjectLiteral())
|
||||
).makeStmt()
|
||||
additionalExports += createExportBlock
|
||||
if (isEsModules) {
|
||||
val exportedElements = crossModuleReferences.exports.entries.map { (tag, hash) ->
|
||||
val internalName = nameMap[tag] ?: error("Missing name for declaration '$tag'")
|
||||
JsExport.Element(internalName, JsName(hash, false))
|
||||
}
|
||||
|
||||
crossModuleReferences.exports.entries.forEach { (tag, hash) ->
|
||||
val internalName = nameMap[tag] ?: error("Missing name for declaration '$tag'")
|
||||
val crossModuleRef = ReservedJsNames.makeCrossModuleNameRef(ReservedJsNames.makeInternalModuleName())
|
||||
additionalExports += jsAssignment(JsNameRef(hash, crossModuleRef), JsNameRef(internalName)).makeStmt()
|
||||
additionalExports += JsExport(JsExport.Subject.Elements(exportedElements))
|
||||
} else {
|
||||
val createExportBlock = jsAssignment(
|
||||
ReservedJsNames.makeCrossModuleNameRef(internalModuleName),
|
||||
JsAstUtils.or(ReservedJsNames.makeCrossModuleNameRef(internalModuleName), JsObjectLiteral())
|
||||
).makeStmt()
|
||||
additionalExports += createExportBlock
|
||||
|
||||
crossModuleReferences.exports.entries.forEach { (tag, hash) ->
|
||||
val internalName = nameMap[tag] ?: error("Missing name for declaration '$tag'")
|
||||
val crossModuleRef = ReservedJsNames.makeCrossModuleNameRef(ReservedJsNames.makeInternalModuleName())
|
||||
additionalExports += jsAssignment(JsNameRef(hash, crossModuleRef), JsNameRef(internalName)).makeStmt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,25 +136,41 @@ class Merger(
|
||||
}
|
||||
|
||||
private fun declareAndCallJsExporter(): List<JsStatement> {
|
||||
val exportBody = JsBlock(fragments.flatMap { it.exports.statements })
|
||||
if (exportBody.isEmpty) {
|
||||
return emptyList()
|
||||
}
|
||||
if (isEsModules) {
|
||||
val allExportRelatedStatements = fragments.flatMap { it.exports.statements }
|
||||
val (allExportStatements, restStatements) = allExportRelatedStatements.partitionIsInstance<JsStatement, JsExport>()
|
||||
val (currentModuleExportStatements, restExportStatements) = allExportStatements.partition { it.fromModule == null }
|
||||
val exportedElements = currentModuleExportStatements.takeIf { it.isNotEmpty() }
|
||||
?.asSequence()
|
||||
?.flatMap { (it.subject as JsExport.Subject.Elements).elements }
|
||||
?.distinctBy { (it.alias ?: it.name).ident }
|
||||
?.map { if (it.name.ident == it.alias?.ident) JsExport.Element(it.name, null) else it }
|
||||
?.toList()
|
||||
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val exporterName = ReservedJsNames.makeJsExporterName()
|
||||
val jsExporterFunction = JsFunction(emptyScope, "js exporter function").apply {
|
||||
body = exportBody
|
||||
name = exporterName
|
||||
parameters.add(JsParameter(internalModuleName))
|
||||
val oneLargeExportStatement = exportedElements?.let { JsExport(JsExport.Subject.Elements(it)) }
|
||||
|
||||
return restStatements + listOfNotNull(oneLargeExportStatement) + restExportStatements
|
||||
} else {
|
||||
val exportBody = JsBlock(fragments.flatMap { it.exports.statements })
|
||||
if (exportBody.isEmpty) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val exporterName = ReservedJsNames.makeJsExporterName()
|
||||
val jsExporterFunction = JsFunction(emptyScope, "js exporter function").apply {
|
||||
body = exportBody
|
||||
name = exporterName
|
||||
parameters.add(JsParameter(internalModuleName))
|
||||
}
|
||||
val jsExporterCall = JsInvocation(exporterName.makeRef(), internalModuleName.makeRef())
|
||||
val result = mutableListOf(jsExporterFunction.makeStmt(), jsExporterCall.makeStmt())
|
||||
if (!generateCallToMain) {
|
||||
val exportExporter = jsAssignment(JsNameRef(exporterName, internalModuleName.makeRef()), exporterName.makeRef())
|
||||
result += exportExporter.makeStmt()
|
||||
}
|
||||
return result
|
||||
}
|
||||
val jsExporterCall = JsInvocation(exporterName.makeRef(), internalModuleName.makeRef())
|
||||
val result = mutableListOf(jsExporterFunction.makeStmt(), jsExporterCall.makeStmt())
|
||||
if (!generateCallToMain) {
|
||||
val exportExporter = jsAssignment(JsNameRef(exporterName, internalModuleName.makeRef()), exporterName.makeRef())
|
||||
result += exportExporter.makeStmt()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun transitiveJsExport(): List<JsStatement> {
|
||||
@@ -214,9 +241,6 @@ class Merger(
|
||||
|
||||
if (generateScriptModule) {
|
||||
with(program.globalBlock) {
|
||||
if (!generateScriptModule) {
|
||||
statements += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
statements.addWithComment("block: polyfills", polyfillDeclarationBlock.statements)
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
statements += moduleBody
|
||||
@@ -228,7 +252,7 @@ class Merger(
|
||||
parameters += JsParameter(internalModuleName)
|
||||
parameters += (importedJsModules).map { JsParameter(it.internalName) }
|
||||
with(body) {
|
||||
if (!generateScriptModule) {
|
||||
if (!isEsModules) {
|
||||
statements += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
|
||||
+17
-2
@@ -17,7 +17,7 @@ object ModuleWrapperTranslation {
|
||||
}
|
||||
|
||||
fun wrap(
|
||||
moduleId: String, function: JsExpression, importedModules: List<JsImportedModule>,
|
||||
moduleId: String, function: JsFunction, importedModules: List<JsImportedModule>,
|
||||
program: JsProgram, kind: ModuleKind
|
||||
): List<JsStatement> {
|
||||
return when (kind) {
|
||||
@@ -25,7 +25,7 @@ object ModuleWrapperTranslation {
|
||||
ModuleKind.COMMON_JS -> wrapCommonJs(function, importedModules, program)
|
||||
ModuleKind.UMD -> wrapUmd(moduleId, function, importedModules, program)
|
||||
ModuleKind.PLAIN -> wrapPlain(moduleId, function, importedModules, program)
|
||||
ModuleKind.ES -> error("ES modules are not supported in legacy wrapper")
|
||||
ModuleKind.ES -> wrapEsModule(function, importedModules)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,21 @@ object ModuleWrapperTranslation {
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapEsModule(function: JsFunction, importedModules: List<JsImportedModule>): List<JsStatement> {
|
||||
val importStatements = importedModules.zip(function.parameters.drop(1)).map {
|
||||
JsImport(
|
||||
it.first.externalName,
|
||||
if (it.first.plainReference == null) {
|
||||
JsImport.Target.All(alias = it.second.name)
|
||||
} else {
|
||||
JsImport.Target.Default(name = it.second.name)
|
||||
}
|
||||
)
|
||||
}
|
||||
return importStatements + function.body.statements.dropLast(1)
|
||||
}
|
||||
|
||||
|
||||
private fun wrapPlain(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<JsImportedModule>, program: JsProgram
|
||||
|
||||
@@ -1,208 +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.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.IrToJs
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsImport
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
|
||||
class StaticDeclarationNumerator {
|
||||
var currentNumber = 0
|
||||
val numeration = mutableMapOf<IrDeclaration, Int>()
|
||||
|
||||
fun add(moduleFragment: IrModuleFragment) {
|
||||
moduleFragment.files.forEach { add(it) }
|
||||
}
|
||||
|
||||
fun add(declaration: IrDeclaration) {
|
||||
// TODO: We should not visit declarations multiple times.
|
||||
// Investigate enum tests in dce-driven mode.
|
||||
if (declaration !in numeration) {
|
||||
numeration[declaration] = currentNumber
|
||||
currentNumber++
|
||||
}
|
||||
}
|
||||
|
||||
fun add(packageFragment: IrPackageFragment) {
|
||||
packageFragment.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
if (declaration !is IrVariable) {
|
||||
add(declaration)
|
||||
}
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class NewStableStaticNamesCollectorVisitor(val needToCollectReferences: Boolean) : IrElementVisitorVoid {
|
||||
val collectedStableNames = mutableSetOf<String>()
|
||||
|
||||
init {
|
||||
collectedStableNames.addAll(RESERVED_IDENTIFIERS)
|
||||
collectedStableNames.add(Namer.IMPLICIT_RECEIVER_NAME)
|
||||
}
|
||||
|
||||
private fun IrDeclaration.collectStableName() {
|
||||
collectedStableNames += stableNameForExternalDeclaration(this) ?: return
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
super.visitDeclaration(declaration)
|
||||
declaration.collectStableName()
|
||||
}
|
||||
|
||||
override fun visitDeclarationReference(expression: IrDeclarationReference) {
|
||||
super.visitDeclarationReference(expression)
|
||||
if (needToCollectReferences) {
|
||||
val declaration = expression.symbol.owner as? IrDeclaration
|
||||
declaration?.collectStableName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NewNamerImpl(
|
||||
val context: JsIrBackendContext,
|
||||
val unit: IrToJs.CodegenUnit,
|
||||
val exportId: (IrDeclarationWithName) -> String,
|
||||
stableNames: Set<String>,
|
||||
) : IrNamerBase() {
|
||||
val staticNames = NameTable<IrDeclaration>(
|
||||
reserved = stableNames.toMutableSet()
|
||||
)
|
||||
val internalImports = mutableMapOf<String, JsImport>()
|
||||
|
||||
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||
require(function.dispatchReceiverParameter != null)
|
||||
val name = jsFunctionSignature(function, context)
|
||||
return name.toJsName()
|
||||
}
|
||||
|
||||
override fun getNameForMemberField(field: IrField): JsName {
|
||||
val fieldName = sanitizeName(
|
||||
try {
|
||||
exportId(field)
|
||||
} catch (e: IllegalStateException) {
|
||||
// TODO: Fix DCE with inline classes and remove this hack
|
||||
field.name.asString() + "_LIKELY_ELIMINATED_BY_DCE"
|
||||
}
|
||||
)
|
||||
// TODO: Webpack not minimize member names, it is long name, which is not minimized, so it affects final JS bundle size
|
||||
// Use shorter names
|
||||
return JsName("f_$fieldName", false)
|
||||
}
|
||||
|
||||
override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName {
|
||||
staticNames.names[declaration]?.let { return JsName(it, false) }
|
||||
|
||||
fun registerImport(moduleId: String, importedName: String) {
|
||||
val fullModuleId = if (moduleId.startsWith(".")) {
|
||||
unit.pathToKotlinModulesRoot + moduleId
|
||||
} else {
|
||||
// TODO: Do we cover this path in tests?
|
||||
moduleId
|
||||
}
|
||||
|
||||
val import = internalImports.getOrPut(fullModuleId) {
|
||||
JsImport(fullModuleId)
|
||||
}
|
||||
import.elements += JsImport.Element(importedName, staticNames.names[declaration]!!)
|
||||
}
|
||||
|
||||
if (declaration.isEffectivelyExternal()) {
|
||||
val jsModule: String? = declaration.getJsModule()
|
||||
val maybeParentFile: IrFile? = declaration.parent as? IrFile
|
||||
val fileJsModule: String? = maybeParentFile?.getJsModule()
|
||||
val jsQualifier: String? = maybeParentFile?.getJsQualifier()
|
||||
|
||||
when {
|
||||
jsModule != null -> {
|
||||
// TODO: Support jsQualifier
|
||||
staticNames.declareFreshName(declaration, declaration.name.asString())
|
||||
registerImport(jsModule, "default")
|
||||
}
|
||||
|
||||
fileJsModule != null -> {
|
||||
// TODO: Support jsQualifier
|
||||
staticNames.declareFreshName(declaration, declaration.name.asString())
|
||||
registerImport(fileJsModule, declaration.getJsNameOrKotlinName().identifier)
|
||||
}
|
||||
|
||||
else -> {
|
||||
var name = declaration.getJsNameOrKotlinName().identifier
|
||||
if (jsQualifier != null)
|
||||
name = "$jsQualifier.$name"
|
||||
|
||||
staticNames.declareStableName(declaration, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else { // Non-external declaration
|
||||
val name = declaration.nameIfPropertyAccessor() ?: declaration.name.asString()
|
||||
staticNames.declareFreshName(declaration, name)
|
||||
val unitReference = unit.referenceCodegenUnitOfDeclaration(declaration)
|
||||
if (unitReference is IrToJs.OtherUnitReference) {
|
||||
registerImport(unitReference.importPath, exportId(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
return JsName(staticNames.names[declaration]!!, false)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Cache?
|
||||
private fun stableNameForExternalDeclaration(declaration: IrDeclaration): String? {
|
||||
if (declaration !is IrDeclarationWithName ||
|
||||
!declaration.hasStaticDispatch() ||
|
||||
!declaration.isEffectivelyExternal() ||
|
||||
declaration.isPropertyAccessor ||
|
||||
declaration.isPropertyField
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (declaration is IrConstructor) {
|
||||
return stableNameForExternalDeclaration(declaration.parentAsClass)
|
||||
}
|
||||
|
||||
val importedFromModuleOnly =
|
||||
declaration.getJsModule() != null && !declaration.isJsNonModule()
|
||||
|
||||
val jsName = declaration.getJsName()
|
||||
|
||||
val jsQualifier = declaration.fileOrNull?.getJsQualifier()
|
||||
|
||||
return when {
|
||||
importedFromModuleOnly ->
|
||||
null
|
||||
|
||||
jsQualifier != null ->
|
||||
jsQualifier.split('1')[0]
|
||||
|
||||
jsName != null ->
|
||||
jsName
|
||||
|
||||
else ->
|
||||
declaration.name.identifier
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ where possible options include:
|
||||
-libraries <path> Paths to Kotlin libraries with .meta.js and .kjsm files, separated by system path separator
|
||||
-main {call|noCall} Define whether the `main` function should be called upon execution
|
||||
-meta-info Generate .meta.js and .kjsm files with metadata. Use to create a library
|
||||
-module-kind {plain|amd|commonjs|umd}
|
||||
-module-kind {plain|amd|commonjs|umd|es}
|
||||
Kind of the JS module generated by the compiler
|
||||
-no-stdlib Don't automatically include the default Kotlin/JS stdlib into compilation dependencies
|
||||
-output <filepath> Destination *.js file for the compilation result
|
||||
|
||||
@@ -34,8 +34,6 @@ object BinaryArtifacts {
|
||||
|
||||
class JsIrArtifact(override val outputFile: File, val compilerResult: CompilerResult, val icCache: Map<String, ByteArray>? = null) : Js()
|
||||
|
||||
class JsEsArtifact(override val outputFile: File, val outputDceFile: File?) : Js()
|
||||
|
||||
data class IncrementalJsArtifact(val originalArtifact: Js, val recompiledArtifact: Js) : Js() {
|
||||
override val outputFile: File
|
||||
get() = unwrap().outputFile
|
||||
|
||||
Reference in New Issue
Block a user