[K/JS] Add file-to-file compilation ^KT-6168 Fixed
This commit is contained in:
+7
@@ -657,6 +657,13 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
|||||||
collector.deprecationWarn(irBaseClassInMetadata, false, "-Xir-base-class-in-metadata")
|
collector.deprecationWarn(irBaseClassInMetadata, false, "-Xir-base-class-in-metadata")
|
||||||
collector.deprecationWarn(irNewIr2Js, true, "-Xir-new-ir2js")
|
collector.deprecationWarn(irNewIr2Js, true, "-Xir-new-ir2js")
|
||||||
|
|
||||||
|
if (irPerFile && moduleKind != MODULE_ES) {
|
||||||
|
collector.report(
|
||||||
|
CompilerMessageSeverity.ERROR,
|
||||||
|
"Per-file compilation can't be used with any `moduleKind` except `es` (ECMAScript Modules)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return super.configureAnalysisFlags(collector, languageVersion).also {
|
return super.configureAnalysisFlags(collector, languageVersion).also {
|
||||||
it[allowFullyQualifiedNameInKClass] = wasm && wasmKClassFqn //Only enabled WASM BE supports this flag
|
it[allowFullyQualifiedNameInKClass] = wasm && wasmKClassFqn //Only enabled WASM BE supports this flag
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -26,6 +26,10 @@ public interface K2JsArgumentConstants {
|
|||||||
String MODULE_UMD = "umd";
|
String MODULE_UMD = "umd";
|
||||||
String MODULE_ES = "es";
|
String MODULE_ES = "es";
|
||||||
|
|
||||||
|
String GRANULARITY_WHOLE_PROGRAM = "whole-program";
|
||||||
|
String GRANULARITY_PER_MODULE = "per-module";
|
||||||
|
String GRANULARITY_PER_FILE = "per-file";
|
||||||
|
|
||||||
String SOURCE_MAP_SOURCE_CONTENT_ALWAYS = "always";
|
String SOURCE_MAP_SOURCE_CONTENT_ALWAYS = "always";
|
||||||
String SOURCE_MAP_SOURCE_CONTENT_NEVER = "never";
|
String SOURCE_MAP_SOURCE_CONTENT_NEVER = "never";
|
||||||
String SOURCE_MAP_SOURCE_CONTENT_INLINING = "inlining";
|
String SOURCE_MAP_SOURCE_CONTENT_INLINING = "inlining";
|
||||||
|
|||||||
@@ -41,13 +41,9 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
|||||||
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
||||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.dce.dumpDeclarationIrSizesIfNeed
|
import org.jetbrains.kotlin.ir.backend.js.dce.dumpDeclarationIrSizesIfNeed
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputsBuilt
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsCodeGenerator
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||||
import org.jetbrains.kotlin.ir.linkage.partial.setupPartialLinkageConfig
|
import org.jetbrains.kotlin.ir.linkage.partial.setupPartialLinkageConfig
|
||||||
@@ -71,8 +67,8 @@ import java.io.IOException
|
|||||||
|
|
||||||
private val K2JSCompilerArguments.granularity: JsGenerationGranularity
|
private val K2JSCompilerArguments.granularity: JsGenerationGranularity
|
||||||
get() = when {
|
get() = when {
|
||||||
this.irPerModule -> JsGenerationGranularity.PER_MODULE
|
|
||||||
this.irPerFile -> JsGenerationGranularity.PER_FILE
|
this.irPerFile -> JsGenerationGranularity.PER_FILE
|
||||||
|
this.irPerModule -> JsGenerationGranularity.PER_MODULE
|
||||||
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +125,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
|||||||
val ir = lowerIr()
|
val ir = lowerIr()
|
||||||
val transformer = IrModuleToJsTransformer(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName)
|
val transformer = IrModuleToJsTransformer(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName)
|
||||||
|
|
||||||
val mode = TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule, arguments.irMinimizedMemberNames)
|
val mode = TranslationMode.fromFlags(arguments.irDce, arguments.granularity, arguments.irMinimizedMemberNames)
|
||||||
return transformer.makeJsCodeGenerator(ir.allModules, mode)
|
return transformer.makeJsCodeGenerator(ir.allModules, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +299,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
|||||||
relativeRequirePath = true
|
relativeRequirePath = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val (outputs, rebuiltModules) = jsExecutableProducer.buildExecutable(arguments.irPerModule, outJsProgram = false)
|
val (outputs, rebuiltModules) = jsExecutableProducer.buildExecutable(arguments.granularity, outJsProgram = false)
|
||||||
outputs.writeAll(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
|
outputs.writeAll(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
|
||||||
|
|
||||||
icCaches.cacheGuard.release()
|
icCaches.cacheGuard.release()
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
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.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
|
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.JsPolyfills
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.translateJsCodeIntoStatementList
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.translateJsCodeIntoStatementList
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
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.loops.ForLoopsLowering
|
||||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering
|
||||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.cleanup.CleanupLowering
|
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.JsSuspendArityStoreLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
|
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.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.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
|
||||||
@@ -840,15 +840,6 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Clean up IR before codegen"
|
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(
|
private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase(
|
||||||
::JsSuspendArityStoreLowering,
|
::JsSuspendArityStoreLowering,
|
||||||
name = "JsSuspendArityStoreLowering",
|
name = "JsSuspendArityStoreLowering",
|
||||||
@@ -962,7 +953,6 @@ val loweringList = listOf<Lowering>(
|
|||||||
cleanupLoweringPhase,
|
cleanupLoweringPhase,
|
||||||
// Currently broken due to static members lowering making single-open-class
|
// Currently broken due to static members lowering making single-open-class
|
||||||
// files non-recognizable as single-class files
|
// files non-recognizable as single-class files
|
||||||
// moveOpenClassesToSeparatePlaceLowering,
|
|
||||||
validateIrAfterLowering,
|
validateIrAfterLowering,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory
|
|||||||
import org.jetbrains.kotlin.backend.common.DefaultMapping
|
import org.jetbrains.kotlin.backend.common.DefaultMapping
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
|
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
class JsMapping : DefaultMapping() {
|
class JsMapping : DefaultMapping() {
|
||||||
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
|
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
|
||||||
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<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 outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
|
||||||
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
|
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
|
||||||
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, 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.backend.common.phaser.invokeToplevel
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
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.collectNativeImplementations
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
|
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.moveBodilessDeclarationsToSeparatePlace
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
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.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.backend.js.transformers.irToJs.TranslationMode
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
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.backend.common.phaser.PhaserState
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
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.ic.JsIrCompilerICInterface
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
|
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.generateJsTests
|
||||||
@@ -53,7 +52,7 @@ class JsIrCompilerWithIC(
|
|||||||
allModules: Collection<IrModuleFragment>,
|
allModules: Collection<IrModuleFragment>,
|
||||||
dirtyFiles: Collection<IrFile>,
|
dirtyFiles: Collection<IrFile>,
|
||||||
mainArguments: List<String>?
|
mainArguments: List<String>?
|
||||||
): List<() -> JsIrProgramFragment> {
|
): List<() -> List<JsIrProgramFragment>> {
|
||||||
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
val shouldGeneratePolyfills = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
||||||
|
|
||||||
allModules.forEach {
|
allModules.forEach {
|
||||||
|
|||||||
+1
-3
@@ -159,7 +159,7 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "interface default implementation")
|
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")
|
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")
|
irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.es6mode && isEsModules) return
|
|
||||||
|
|
||||||
val property = irFunction.correspondingPropertySymbol?.owner ?: return
|
val property = irFunction.correspondingPropertySymbol?.owner ?: return
|
||||||
|
|
||||||
if (property.isExported(context) || property.getJsName() != null || property.isOverriddenExternal()) {
|
if (property.isExported(context) || property.getJsName() != null || property.isOverriddenExternal()) {
|
||||||
|
|||||||
+29
-70
@@ -93,16 +93,11 @@ class ExportModelToJsStatements(
|
|||||||
|
|
||||||
is ExportedProperty -> {
|
is ExportedProperty -> {
|
||||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||||
when {
|
when (namespace) {
|
||||||
namespace == null -> {
|
null -> {
|
||||||
val property = declaration.generateTopLevelGetters()
|
val property = declaration.generateTopLevelGetters()
|
||||||
listOf(JsVars(property), JsExport(property.name.makeRef(), JsName(declaration.name, false)))
|
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 -> {
|
else -> {
|
||||||
val getter = declaration.irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
val getter = declaration.irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||||
val setter = declaration.irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
val setter = declaration.irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
||||||
@@ -123,10 +118,11 @@ class ExportModelToJsStatements(
|
|||||||
|
|
||||||
is ExportedObject -> {
|
is ExportedObject -> {
|
||||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||||
|
val (name, objectClassInitialization) = declaration.getNameAndInitialization()
|
||||||
val newNameSpace = when {
|
val newNameSpace = when {
|
||||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||||
else ->
|
else ->
|
||||||
jsElementAccess(Namer.PROTOTYPE_NAME, staticContext.getNameForClass(declaration.ir).makeRef())
|
jsElementAccess(Namer.PROTOTYPE_NAME, name.makeRef())
|
||||||
}
|
}
|
||||||
val staticsExport =
|
val staticsExport =
|
||||||
declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
|
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 -> {
|
is ExportedRegularClass -> {
|
||||||
if (declaration.isInterface) return emptyList()
|
if (declaration.isInterface) return emptyList()
|
||||||
val name = staticContext.getNameForStaticDeclaration(declaration.ir)
|
val (name, classInitialization) = declaration.getNameAndInitialization()
|
||||||
val newNameSpace = when {
|
val newNameSpace = when {
|
||||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||||
esModules -> name.makeRef()
|
esModules -> name.makeRef()
|
||||||
else -> prototypeOf(staticContext.getNameForClass(declaration.ir).makeRef(), staticContext)
|
else -> prototypeOf(name.makeRef(), staticContext)
|
||||||
}
|
}
|
||||||
val klassExport = when {
|
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))
|
esModules -> JsExport(name.makeRef(), alias = JsName(declaration.name, false))
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -181,12 +177,12 @@ class ExportModelToJsStatements(
|
|||||||
|
|
||||||
val innerClassesAssignments = declaration.nestedClasses
|
val innerClassesAssignments = declaration.nestedClasses
|
||||||
.filter { it.ir.isInner }
|
.filter { it.ir.isInner }
|
||||||
.map { it.generateInnerClassAssignment(declaration) }
|
.map { it.generateInnerClassAssignment(name) }
|
||||||
|
|
||||||
val staticsExport = (staticFunctions + enumEntries + declaration.nestedClasses)
|
val staticsExport = (staticFunctions + enumEntries + declaration.nestedClasses)
|
||||||
.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
|
.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> {
|
private fun ExportedClass.generateInnerClassAssignment(outerClassName: JsName): JsStatement {
|
||||||
val getter = irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
val innerClassRef = ir.getClassRef(staticContext)
|
||||||
val setter = irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
|
val outerClassRef = outerClassName.makeRef()
|
||||||
|
|
||||||
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()
|
|
||||||
val companionObject = ir.companionObject()
|
val companionObject = ir.companionObject()
|
||||||
val secondaryConstructors = members.filterIsInstanceAnd<ExportedFunction> { it.isStatic }
|
val secondaryConstructors = members.filterIsInstanceAnd<ExportedFunction> { it.isStatic }
|
||||||
val bindConstructor = JsName("__bind_constructor_", false)
|
val bindConstructor = JsName("__bind_constructor_", false)
|
||||||
@@ -283,30 +245,27 @@ class ExportModelToJsStatements(
|
|||||||
blockStatements.add(JsReturn(bindConstructor.makeRef()))
|
blockStatements.add(JsReturn(bindConstructor.makeRef()))
|
||||||
val innerClassGetter = JsFunction(emptyScope, JsBlock(*blockStatements.toTypedArray()), "inner class '$name' getter")
|
val innerClassGetter = JsFunction(emptyScope, JsBlock(*blockStatements.toTypedArray()), "inner class '$name' getter")
|
||||||
|
|
||||||
return if (es6mode) {
|
return defineProperty(
|
||||||
outerClass.ir.getCorrespondingJsClass().members += innerClassGetter.also {
|
prototypeOf(outerClassRef, staticContext),
|
||||||
it.name = JsName(name, false)
|
name,
|
||||||
it.modifiers.add(JsFunction.Modifier.GET)
|
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 -> classRef.name!! to null
|
||||||
} else {
|
|
||||||
defineProperty(
|
|
||||||
prototypeOf(outerClassRef, staticContext),
|
|
||||||
name,
|
|
||||||
innerClassGetter,
|
|
||||||
null,
|
|
||||||
staticContext
|
|
||||||
).makeStmt()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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())
|
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.backend.common.serialization.cityHash64
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
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.backend.js.transformers.irToJs.JsIrProgramFragment
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
@@ -33,7 +33,7 @@ fun interface JsIrCompilerICInterface {
|
|||||||
allModules: Collection<IrModuleFragment>,
|
allModules: Collection<IrModuleFragment>,
|
||||||
dirtyFiles: Collection<IrFile>,
|
dirtyFiles: Collection<IrFile>,
|
||||||
mainArguments: List<String>?
|
mainArguments: List<String>?
|
||||||
): List<() -> JsIrProgramFragment>
|
): List<() -> List<JsIrProgramFragment>>
|
||||||
}
|
}
|
||||||
|
|
||||||
fun interface JsIrCompilerICInterfaceFactory {
|
fun interface JsIrCompilerICInterfaceFactory {
|
||||||
@@ -616,7 +616,7 @@ class CacheUpdater(
|
|||||||
private fun commitCacheAndBuildModuleArtifacts(
|
private fun commitCacheAndBuildModuleArtifacts(
|
||||||
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
||||||
moduleNames: Map<KotlinLibraryFile, String>,
|
moduleNames: Map<KotlinLibraryFile, String>,
|
||||||
rebuiltFileFragments: KotlinSourceFileMap<JsIrProgramFragment>
|
rebuiltFileFragments: KotlinSourceFileMap<List<JsIrProgramFragment>>
|
||||||
): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") {
|
): List<ModuleArtifact> = stopwatch.measure("Incremental cache - committing artifacts") {
|
||||||
incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) ->
|
incrementalCacheArtifacts.map { (libFile, incrementalCacheArtifact) ->
|
||||||
incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
|
incrementalCacheArtifact.buildModuleArtifactAndCommitCache(
|
||||||
@@ -630,7 +630,7 @@ class CacheUpdater(
|
|||||||
compilerForIC: JsIrCompilerICInterface,
|
compilerForIC: JsIrCompilerICInterface,
|
||||||
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
|
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
|
||||||
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
|
dirtyFiles: Map<KotlinLibraryFile, Set<KotlinSourceFile>>
|
||||||
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>> =
|
): MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>> =
|
||||||
stopwatch.measure("Processing IR - lowering") {
|
stopwatch.measure("Processing IR - lowering") {
|
||||||
val dirtyFilesForCompiling = mutableListOf<IrFile>()
|
val dirtyFilesForCompiling = mutableListOf<IrFile>()
|
||||||
val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>()
|
val dirtyFilesForRestoring = mutableListOf<Pair<KotlinLibraryFile, KotlinSourceFile>>()
|
||||||
@@ -737,7 +737,7 @@ class CacheUpdater(
|
|||||||
private data class FragmentGenerators(
|
private data class FragmentGenerators(
|
||||||
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
val incrementalCacheArtifacts: Map<KotlinLibraryFile, IncrementalCacheArtifact>,
|
||||||
val moduleNames: Map<KotlinLibraryFile, String>,
|
val moduleNames: Map<KotlinLibraryFile, String>,
|
||||||
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>>
|
val generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators {
|
private fun loadIrAndMakeIrFragmentGenerators(): FragmentGenerators {
|
||||||
@@ -751,9 +751,9 @@ class CacheUpdater(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateIrFragments(
|
private fun generateIrFragments(
|
||||||
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> JsIrProgramFragment>>
|
generators: MutableList<Triple<KotlinLibraryFile, KotlinSourceFile, () -> List<JsIrProgramFragment>>>
|
||||||
): KotlinSourceFileMap<JsIrProgramFragment> = stopwatch.measure("Processing IR - generating program fragments") {
|
): KotlinSourceFileMap<List<JsIrProgramFragment>> = stopwatch.measure("Processing IR - generating program fragments") {
|
||||||
val rebuiltFragments = KotlinSourceFileMutableMap<JsIrProgramFragment>()
|
val rebuiltFragments = KotlinSourceFileMutableMap<List<JsIrProgramFragment>>()
|
||||||
while (generators.isNotEmpty()) {
|
while (generators.isNotEmpty()) {
|
||||||
val (libFile, srcFile, fragmentGenerator) = generators.removeFirst()
|
val (libFile, srcFile, fragmentGenerator) = generators.removeFirst()
|
||||||
rebuiltFragments[libFile, srcFile] = fragmentGenerator()
|
rebuiltFragments[libFile, srcFile] = fragmentGenerator()
|
||||||
@@ -783,7 +783,7 @@ fun rebuildCacheForDirtyFiles(
|
|||||||
exportedDeclarations: Set<FqName>,
|
exportedDeclarations: Set<FqName>,
|
||||||
mainArguments: List<String>?,
|
mainArguments: List<String>?,
|
||||||
es6mode: Boolean
|
es6mode: Boolean
|
||||||
): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragment>>> {
|
): Pair<IrModuleFragment, List<Pair<IrFile, List<JsIrProgramFragment>>>> {
|
||||||
val internationService = IrInterningService()
|
val internationService = IrInterningService()
|
||||||
val emptyMetadata = object : KotlinSourceFileExports() {
|
val emptyMetadata = object : KotlinSourceFileExports() {
|
||||||
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
|
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
|
||||||
|
|||||||
+1
-1
@@ -211,7 +211,7 @@ internal fun CrossModuleReferences.crossModuleReferencesHashForIC() = HashCalcul
|
|||||||
update(importedModule.relativeRequirePath ?: "")
|
update(importedModule.relativeRequirePath ?: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
updateForEach(transitiveJsExportFrom) { transitiveExport ->
|
updateForEach(transitiveExportFrom) { transitiveExport ->
|
||||||
update(transitiveExport.internalName.toString())
|
update(transitiveExport.internalName.toString())
|
||||||
update(transitiveExport.externalName)
|
update(transitiveExport.externalName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ internal inline fun File.useCodedOutput(f: CodedOutputStream.() -> Unit) {
|
|||||||
outputStream().useCodedOutput(f)
|
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 {
|
internal fun icError(what: String, libFile: KotlinLibraryFile? = null, srcFile: KotlinSourceFile? = null): Nothing {
|
||||||
val filePath = listOfNotNull(libFile?.path, srcFile?.path).joinToString(":") { File(it).name }
|
val filePath = listOfNotNull(libFile?.path, srcFile?.path).joinToString(":") { File(it).name }
|
||||||
val msg = if (filePath.isEmpty()) what else "$what for $filePath"
|
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) {
|
internal sealed class SourceFileCacheArtifact(val srcFile: KotlinSourceFile, val binaryAstFile: File) {
|
||||||
abstract fun commitMetadata()
|
abstract fun commitMetadata()
|
||||||
|
|
||||||
fun commitBinaryAst(fragment: JsIrProgramFragment) {
|
fun commitBinaryAst(fragments: List<JsIrProgramFragment>) {
|
||||||
binaryAstFile.parentFile?.mkdirs()
|
binaryAstFile.parentFile?.mkdirs()
|
||||||
BufferedOutputStream(binaryAstFile.outputStream()).use {
|
BufferedOutputStream(binaryAstFile.outputStream()).use {
|
||||||
fragment.serializeTo(it)
|
fragments.serializeTo(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ internal class IncrementalCacheArtifact(
|
|||||||
|
|
||||||
fun buildModuleArtifactAndCommitCache(
|
fun buildModuleArtifactAndCommitCache(
|
||||||
moduleName: String,
|
moduleName: String,
|
||||||
rebuiltFileFragments: Map<KotlinSourceFile, JsIrProgramFragment>,
|
rebuiltFileFragments: Map<KotlinSourceFile, List<JsIrProgramFragment>>,
|
||||||
): ModuleArtifact {
|
): ModuleArtifact {
|
||||||
val fileArtifacts = srcCacheActions.map { srcFileAction ->
|
val fileArtifacts = srcCacheActions.map { srcFileAction ->
|
||||||
val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile]
|
val rebuiltFileFragment = rebuiltFileFragments[srcFileAction.srcFile]
|
||||||
@@ -67,7 +67,7 @@ internal class IncrementalCacheArtifact(
|
|||||||
srcFileAction.commitBinaryAst(rebuiltFileFragment)
|
srcFileAction.commitBinaryAst(rebuiltFileFragment)
|
||||||
}
|
}
|
||||||
srcFileAction.commitMetadata()
|
srcFileAction.commitMetadata()
|
||||||
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment, srcFileAction.binaryAstFile)
|
SrcFileArtifact(srcFileAction.srcFile.path, rebuiltFileFragment ?: emptyList(), srcFileAction.binaryAstFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName)
|
return ModuleArtifact(moduleName, fileArtifacts, artifactsDir, forceRebuildJs, externalModuleName)
|
||||||
|
|||||||
+5
-5
@@ -26,11 +26,11 @@ class JsExecutableProducer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildExecutable(multiModule: Boolean, outJsProgram: Boolean) = if (multiModule) {
|
fun buildExecutable(granularity: JsGenerationGranularity, outJsProgram: Boolean) =
|
||||||
buildMultiModuleExecutable(outJsProgram)
|
when (granularity) {
|
||||||
} else {
|
JsGenerationGranularity.WHOLE_PROGRAM -> buildSingleModuleExecutable(outJsProgram)
|
||||||
buildSingleModuleExecutable(outJsProgram)
|
JsGenerationGranularity.PER_MODULE, JsGenerationGranularity.PER_FILE -> buildMultiModuleExecutable(outJsProgram)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildSingleModuleExecutable(outJsProgram: Boolean): BuildResult {
|
private fun buildSingleModuleExecutable(outJsProgram: Boolean): BuildResult {
|
||||||
val modules = caches.map { cacheArtifact -> cacheArtifact.loadJsIrModule() }
|
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 optionalCrossModuleImports = hashSetOf<String>()
|
||||||
|
|
||||||
val crossModuleReferencesHash = ICHash.fromProtoStream(this)
|
val crossModuleReferencesHash = ICHash.fromProtoStream(this)
|
||||||
val hasJsExports = readBool()
|
val reexportedInModuleWithName = ifTrue { readString() }
|
||||||
|
|
||||||
repeat(readInt32()) {
|
repeat(readInt32()) {
|
||||||
val tag = readString()
|
val tag = readString()
|
||||||
val mask = readInt32()
|
val mask = readInt32()
|
||||||
@@ -44,6 +45,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
|||||||
nameBindings[tag] = readString()
|
nameBindings[tag] = readString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedModuleInfo(
|
CachedModuleInfo(
|
||||||
artifact = this@fetchModuleInfo,
|
artifact = this@fetchModuleInfo,
|
||||||
jsIrHeader = JsIrModuleHeader(
|
jsIrHeader = JsIrModuleHeader(
|
||||||
@@ -52,7 +54,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
|||||||
definitions = definitions,
|
definitions = definitions,
|
||||||
nameBindings = nameBindings,
|
nameBindings = nameBindings,
|
||||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||||
hasJsExports = hasJsExports,
|
reexportedInModuleWithName = reexportedInModuleWithName,
|
||||||
associatedModule = null
|
associatedModule = null
|
||||||
),
|
),
|
||||||
crossModuleReferencesHash = crossModuleReferencesHash
|
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
|
names[tag] = ((maskAndName?.first ?: 0) or NameType.DEFINITIONS.typeMask) to maskAndName?.second
|
||||||
}
|
}
|
||||||
crossModuleReferencesHash.toProtoStream(this)
|
crossModuleReferencesHash.toProtoStream(this)
|
||||||
writeBoolNoTag(jsIrHeader.hasJsExports)
|
|
||||||
|
ifNotNull(jsIrHeader.reexportedInModuleWithName) {
|
||||||
|
writeStringNoTag(it)
|
||||||
|
}
|
||||||
|
|
||||||
writeInt32NoTag(names.size)
|
writeInt32NoTag(names.size)
|
||||||
|
|
||||||
for ((tag, maskAndName) in names) {
|
for ((tag, maskAndName) in names) {
|
||||||
writeStringNoTag(tag)
|
writeStringNoTag(tag)
|
||||||
writeInt32NoTag(maskAndName.first)
|
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 org.jetbrains.kotlin.ir.backend.js.utils.serialization.deserializeJsIrProgramFragment
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SrcFileArtifact(val srcFilePath: String, private val fragment: JsIrProgramFragment?, private val astArtifact: File? = null) {
|
class SrcFileArtifact(val srcFilePath: String, private val fragments: List<JsIrProgramFragment>, private val astArtifact: File? = null) {
|
||||||
fun loadJsIrFragment(): JsIrProgramFragment? {
|
fun loadJsIrFragments(): List<JsIrProgramFragment> {
|
||||||
if (fragment != null) {
|
if (fragments.isNotEmpty()) {
|
||||||
return fragment
|
return fragments
|
||||||
}
|
}
|
||||||
return astArtifact?.ifExists { readBytes() }?.let {
|
return astArtifact?.ifExists { readBytes() }?.let {
|
||||||
deserializeJsIrProgramFragment(it)
|
deserializeJsIrProgramFragment(it)
|
||||||
}
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isModified() = fragment != null
|
fun isModified() = fragments.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ModuleArtifact(
|
class ModuleArtifact(
|
||||||
@@ -35,7 +35,7 @@ class ModuleArtifact(
|
|||||||
val moduleExternalName = externalModuleName ?: moduleSafeName
|
val moduleExternalName = externalModuleName ?: moduleSafeName
|
||||||
|
|
||||||
fun loadJsIrModule(): JsIrModule {
|
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)
|
return JsIrModule(moduleSafeName, moduleExternalName, fragments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -226,7 +226,7 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
|||||||
constructor.isEffectivelyExternal() ->
|
constructor.isEffectivelyExternal() ->
|
||||||
JsIrBuilder.buildCall(context.intrinsics.jsCreateExternalThisSymbol)
|
JsIrBuilder.buildCall(context.intrinsics.jsCreateExternalThisSymbol)
|
||||||
.apply {
|
.apply {
|
||||||
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
|
putValueArgument(0, getCurrentConstructorReference(constructorReplacement))
|
||||||
putValueArgument(1, expression.symbol.owner.parentAsClass.jsConstructorReference(context))
|
putValueArgument(1, expression.symbol.owner.parentAsClass.jsConstructorReference(context))
|
||||||
putValueArgument(2, irAnyArray(expression.valueArguments.memoryOptimizedMap { it ?: context.getVoid() }))
|
putValueArgument(2, irAnyArray(expression.valueArguments.memoryOptimizedMap { it ?: context.getVoid() }))
|
||||||
putValueArgument(3, boxParameterGetter)
|
putValueArgument(3, boxParameterGetter)
|
||||||
@@ -234,7 +234,7 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
|||||||
constructor.parentAsClass.symbol == context.irBuiltIns.anyClass ->
|
constructor.parentAsClass.symbol == context.irBuiltIns.anyClass ->
|
||||||
JsIrBuilder.buildCall(context.intrinsics.jsCreateThisSymbol)
|
JsIrBuilder.buildCall(context.intrinsics.jsCreateThisSymbol)
|
||||||
.apply {
|
.apply {
|
||||||
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
|
putValueArgument(0, getCurrentConstructorReference(constructorReplacement))
|
||||||
putValueArgument(1, boxParameterGetter)
|
putValueArgument(1, boxParameterGetter)
|
||||||
}
|
}
|
||||||
else ->
|
else ->
|
||||||
@@ -263,12 +263,8 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrClass.getCurrentConstructorReference(currentFactoryFunction: IrSimpleFunction): IrExpression {
|
private fun getCurrentConstructorReference(currentFactoryFunction: IrSimpleFunction): IrExpression {
|
||||||
return if (isFinalClass) {
|
return JsIrBuilder.buildGetValue(currentFactoryFunction.dispatchReceiverParameter!!.symbol)
|
||||||
jsConstructorReference(context)
|
|
||||||
} else {
|
|
||||||
JsIrBuilder.buildGetValue(currentFactoryFunction.dispatchReceiverParameter!!.symbol)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrDeclaration.excludeFromExport() {
|
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.backend.common.lower.optimizations.PropertyAccessorInlineLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
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.IrDeclaration
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
import org.jetbrains.kotlin.ir.util.fileOrNull
|
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.backend.common.compilationException
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.types.isString
|
import org.jetbrains.kotlin.ir.types.isString
|
||||||
@@ -169,7 +167,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
|
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 callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
|
||||||
val fromPrimary = context.currentFunction is IrConstructor
|
val fromPrimary = context.currentFunction is IrConstructor
|
||||||
val thisRef =
|
val thisRef =
|
||||||
@@ -202,7 +200,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
|
|
||||||
return when {
|
return when {
|
||||||
klass.isEffectivelyExternal() -> {
|
klass.isEffectivelyExternal() -> {
|
||||||
val refForExternalClass = context.getRefForExternalClass(klass)
|
val refForExternalClass = klass.getClassRef(context.staticContext)
|
||||||
val varargParameterIndex = expression.symbol.owner.varargParameterIndex()
|
val varargParameterIndex = expression.symbol.owner.varargParameterIndex()
|
||||||
if (varargParameterIndex == -1) {
|
if (varargParameterIndex == -1) {
|
||||||
JsNew(refForExternalClass, arguments)
|
JsNew(refForExternalClass, arguments)
|
||||||
@@ -225,8 +223,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val ref = context.getNameForClass(klass).makeRef()
|
JsNew(klass.getClassRef(context.staticContext), arguments)
|
||||||
JsNew(ref, arguments)
|
|
||||||
}
|
}
|
||||||
}.withSource(expression, context)
|
}.withSource(expression, context)
|
||||||
}
|
}
|
||||||
@@ -320,14 +317,14 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
|
|
||||||
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: JsGenerationContext): JsExpression {
|
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: JsGenerationContext): JsExpression {
|
||||||
val name = when (val function = expression.symbol.owner) {
|
val name = when (val function = expression.symbol.owner) {
|
||||||
is IrConstructor -> data.getNameForConstructor(function)
|
is IrConstructor -> function.getConstructorRef(data.staticContext)
|
||||||
is IrSimpleFunction -> data.getNameForStaticFunction(function)
|
is IrSimpleFunction -> data.getNameForStaticFunction(function).makeRef()
|
||||||
else -> compilationException(
|
else -> compilationException(
|
||||||
"Unexpected function kind",
|
"Unexpected function kind",
|
||||||
expression
|
expression
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return JsNameRef(name).withSource(expression, data)
|
return name.withSource(expression, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prefixOperation(operator: JsUnaryOperator, expression: IrDynamicOperatorExpression, data: JsGenerationContext) =
|
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.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
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.StaticMembersLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
|
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
@@ -45,34 +46,51 @@ val String.safeModuleName: String
|
|||||||
val IrModuleFragment.safeName: String
|
val IrModuleFragment.safeName: String
|
||||||
get() = name.asString().safeModuleName
|
get() = name.asString().safeModuleName
|
||||||
|
|
||||||
|
enum class JsGenerationGranularity {
|
||||||
|
WHOLE_PROGRAM,
|
||||||
|
PER_MODULE,
|
||||||
|
PER_FILE
|
||||||
|
}
|
||||||
|
|
||||||
enum class TranslationMode(
|
enum class TranslationMode(
|
||||||
val production: Boolean,
|
val production: Boolean,
|
||||||
val perModule: Boolean,
|
val granularity: JsGenerationGranularity,
|
||||||
val minimizedMemberNames: Boolean,
|
val minimizedMemberNames: Boolean,
|
||||||
) {
|
) {
|
||||||
FULL_DEV(production = false, perModule = false, minimizedMemberNames = false),
|
FULL_DEV(production = false, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = false),
|
||||||
FULL_PROD(production = true, perModule = false, minimizedMemberNames = false),
|
FULL_PROD(production = true, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = false),
|
||||||
FULL_PROD_MINIMIZED_NAMES(production = true, perModule = false, minimizedMemberNames = true),
|
FULL_PROD_MINIMIZED_NAMES(production = true, granularity = JsGenerationGranularity.WHOLE_PROGRAM, minimizedMemberNames = true),
|
||||||
PER_MODULE_DEV(production = false, perModule = true, minimizedMemberNames = false),
|
PER_MODULE_DEV(production = false, granularity = JsGenerationGranularity.PER_MODULE, minimizedMemberNames = false),
|
||||||
PER_MODULE_PROD(production = true, perModule = true, minimizedMemberNames = false),
|
PER_MODULE_PROD(production = true, granularity = JsGenerationGranularity.PER_MODULE, minimizedMemberNames = false),
|
||||||
PER_MODULE_PROD_MINIMIZED_NAMES(production = true, perModule = true, minimizedMemberNames = true);
|
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 {
|
companion object {
|
||||||
fun fromFlags(
|
fun fromFlags(
|
||||||
production: Boolean,
|
production: Boolean,
|
||||||
perModule: Boolean,
|
granularity: JsGenerationGranularity,
|
||||||
minimizedMemberNames: Boolean
|
minimizedMemberNames: Boolean
|
||||||
): TranslationMode {
|
): TranslationMode {
|
||||||
return if (perModule) {
|
return when (granularity) {
|
||||||
if (production) {
|
JsGenerationGranularity.PER_MODULE ->
|
||||||
if (minimizedMemberNames) PER_MODULE_PROD_MINIMIZED_NAMES
|
if (production) {
|
||||||
else PER_MODULE_PROD
|
if (minimizedMemberNames) PER_MODULE_PROD_MINIMIZED_NAMES
|
||||||
} else PER_MODULE_DEV
|
else PER_MODULE_PROD
|
||||||
} else {
|
} else PER_MODULE_DEV
|
||||||
if (production) {
|
|
||||||
if (minimizedMemberNames) FULL_PROD_MINIMIZED_NAMES
|
JsGenerationGranularity.PER_FILE ->
|
||||||
else FULL_PROD
|
if (production) {
|
||||||
} else FULL_DEV
|
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(
|
class JsCodeGenerator(
|
||||||
private val program: JsIrProgram,
|
private val program: JsIrProgram,
|
||||||
private val multiModule: Boolean,
|
private val granularity: JsGenerationGranularity,
|
||||||
private val mainModuleName: String,
|
private val mainModuleName: String,
|
||||||
private val moduleKind: ModuleKind,
|
private val moduleKind: ModuleKind,
|
||||||
private val sourceMapsInfo: SourceMapsInfo?
|
private val sourceMapsInfo: SourceMapsInfo?
|
||||||
) {
|
) {
|
||||||
fun generateJsCode(relativeRequirePath: Boolean, outJsProgram: Boolean): CompilationOutputsBuilt {
|
fun generateJsCode(relativeRequirePath: Boolean, outJsProgram: Boolean): CompilationOutputsBuilt {
|
||||||
return generateWrappedModuleBody(
|
return generateWrappedModuleBody(
|
||||||
multiModule,
|
granularity,
|
||||||
mainModuleName,
|
mainModuleName,
|
||||||
moduleKind,
|
moduleKind,
|
||||||
program,
|
program,
|
||||||
@@ -101,7 +119,7 @@ class JsCodeGenerator(
|
|||||||
class IrModuleToJsTransformer(
|
class IrModuleToJsTransformer(
|
||||||
private val backendContext: JsIrBackendContext,
|
private val backendContext: JsIrBackendContext,
|
||||||
private val mainArguments: List<String>?,
|
private val mainArguments: List<String>?,
|
||||||
private val moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
moduleToName: Map<IrModuleFragment, String> = emptyMap(),
|
||||||
private val removeUnusedAssociatedObjects: Boolean = true,
|
private val removeUnusedAssociatedObjects: Boolean = true,
|
||||||
) {
|
) {
|
||||||
private val shouldGeneratePolyfills = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
private val shouldGeneratePolyfills = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_POLYFILLS)
|
||||||
@@ -113,7 +131,14 @@ class IrModuleToJsTransformer(
|
|||||||
private val isEsModules = moduleKind == ModuleKind.ES
|
private val isEsModules = moduleKind == ModuleKind.ES
|
||||||
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
|
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>)
|
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>)
|
||||||
|
|
||||||
@@ -172,15 +197,22 @@ class IrModuleToJsTransformer(
|
|||||||
return makeJsCodeGeneratorFromIr(exportData, mode)
|
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 exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||||
val exportData = exportModelGenerator.generateExportWithExternals(files)
|
val exportData = exportModelGenerator.generateExportWithExternals(files)
|
||||||
|
val mode = TranslationMode.fromFlags(production = false, backendContext.granularity, minimizedMemberNames = false)
|
||||||
|
|
||||||
doStaticMembersLowering(allModules)
|
doStaticMembersLowering(allModules)
|
||||||
|
|
||||||
return exportData.map {
|
return exportData
|
||||||
{ generateProgramFragment(it, minimizedMemberNames = false) }
|
.groupBy { it.generatedFrom ?: it.file }
|
||||||
}
|
.map {
|
||||||
|
{ it.value.flatMap { generateProgramFragment(it, mode) } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> {
|
private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> {
|
||||||
@@ -191,47 +223,123 @@ class IrModuleToJsTransformer(
|
|||||||
val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) {
|
val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) {
|
||||||
allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) }
|
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 {
|
private fun makeJsCodeGeneratorFromIr(exportData: List<IrAndExportedDeclarations>, mode: TranslationMode): JsCodeGenerator {
|
||||||
if (mode.minimizedMemberNames) {
|
if (mode.minimizedMemberNames) {
|
||||||
backendContext.fieldDataCache.clear()
|
backendContext.fieldDataCache.clear()
|
||||||
backendContext.minimizedNameGenerator.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 ->
|
exportData.map { data ->
|
||||||
JsIrModule(
|
JsIrModule(
|
||||||
data.fragment.safeName,
|
data.fragment.safeName,
|
||||||
data.fragment.externalModuleName(),
|
moduleFragmentToNameMapper.getExternalNameFor(data.fragment),
|
||||||
data.files.map { generateProgramFragment(it, mode.minimizedMemberNames) }
|
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 generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
|
||||||
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
|
||||||
private val optimizeGeneratedJs = backendContext.configuration.get(JSConfigurationKeys.OPTIMIZE_GENERATED_JS, true)
|
private val optimizeGeneratedJs = backendContext.configuration.get(JSConfigurationKeys.OPTIMIZE_GENERATED_JS, true)
|
||||||
|
|
||||||
private fun generateProgramFragment(fileExports: IrFileExports, minimizedMemberNames: Boolean): JsIrProgramFragment {
|
private fun IrFileExports.generateProgramFragmentForExport(
|
||||||
val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames, isEsModules)
|
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 globalNameScope = NameTable<IrDeclaration>()
|
||||||
|
val nameGenerator = JsNameLinkingNamer(backendContext, mode.minimizedMemberNames, isEsModules)
|
||||||
val staticContext = JsStaticContext(
|
val staticContext = JsStaticContext(backendContext, nameGenerator, globalNameScope, mode)
|
||||||
backendContext = backendContext,
|
|
||||||
irNamer = nameGenerator,
|
|
||||||
globalNameScope = globalNameScope
|
|
||||||
)
|
|
||||||
|
|
||||||
val result = JsIrProgramFragment(fileExports.file.packageFqName.asString()).apply {
|
val result = JsIrProgramFragment(fileExports.file.packageFqName.asString()).apply {
|
||||||
if (shouldGeneratePolyfills) {
|
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 statements = result.declarations.statements
|
||||||
val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
|
val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
|
||||||
|
val exportFragment = fileExports.generateProgramFragmentForExport(mode, globalNameScope)
|
||||||
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
|
|
||||||
|
|
||||||
if (fileStatements.isNotEmpty()) {
|
if (fileStatements.isNotEmpty()) {
|
||||||
var startComment = ""
|
var startComment = ""
|
||||||
@@ -310,50 +406,69 @@ class IrModuleToJsTransformer(
|
|||||||
|
|
||||||
val definitionSet = fileExports.file.declarations.toSet()
|
val definitionSet = fileExports.file.declarations.toSet()
|
||||||
|
|
||||||
fun computeTag(declaration: IrDeclaration): String? {
|
result.computeAndSaveNameBindings(definitionSet, nameGenerator)
|
||||||
val tag = (backendContext.irFactory as IdSignatureRetriever).declarationSignature(declaration)?.toString()
|
result.computeAndSaveImports(definitionSet, nameGenerator)
|
||||||
|
result.computeAndSaveDefinitions(definitionSet, fileExports)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optimizeGeneratedJs) {
|
if (optimizeGeneratedJs) {
|
||||||
optimizeFragmentByJsAst(result)
|
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(
|
private fun generateMainArguments(
|
||||||
@@ -374,10 +489,12 @@ class IrModuleToJsTransformer(
|
|||||||
|
|
||||||
return listOfNotNull(mainArgumentsArray, continuation)
|
return listOfNotNull(mainArgumentsArray, continuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun IrFile.couldBeSkipped(): Boolean = declarations.all { it.origin == JsCodeOutliningLowering.OUTLINED_JS_CODE_ORIGIN }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateWrappedModuleBody(
|
private fun generateWrappedModuleBody(
|
||||||
multiModule: Boolean,
|
granularity: JsGenerationGranularity,
|
||||||
mainModuleName: String,
|
mainModuleName: String,
|
||||||
moduleKind: ModuleKind,
|
moduleKind: ModuleKind,
|
||||||
program: JsIrProgram,
|
program: JsIrProgram,
|
||||||
@@ -385,44 +502,8 @@ private fun generateWrappedModuleBody(
|
|||||||
relativeRequirePath: Boolean,
|
relativeRequirePath: Boolean,
|
||||||
outJsProgram: Boolean
|
outJsProgram: Boolean
|
||||||
): CompilationOutputsBuilt {
|
): CompilationOutputsBuilt {
|
||||||
if (multiModule) {
|
return when (granularity) {
|
||||||
// mutable container allows explicitly remove elements from itself,
|
JsGenerationGranularity.WHOLE_PROGRAM -> generateSingleWrappedModuleBody(
|
||||||
// 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(
|
|
||||||
mainModuleName,
|
mainModuleName,
|
||||||
moduleKind,
|
moduleKind,
|
||||||
program.asFragments(),
|
program.asFragments(),
|
||||||
@@ -430,9 +511,64 @@ private fun generateWrappedModuleBody(
|
|||||||
generateCallToMain = true,
|
generateCallToMain = true,
|
||||||
outJsProgram = outJsProgram
|
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(
|
fun generateSingleWrappedModuleBody(
|
||||||
moduleName: String,
|
moduleName: String,
|
||||||
moduleKind: ModuleKind,
|
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.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
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.js.common.isValidES5Identifier
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||||
import org.jetbrains.kotlin.utils.toSmartList
|
import org.jetbrains.kotlin.utils.toSmartList
|
||||||
|
|
||||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
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 className = context.getNameForClass(irClass)
|
||||||
private val classNameRef = className.makeRef()
|
|
||||||
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
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 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
|
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 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 {
|
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)
|
assert(!irClass.isExpect)
|
||||||
|
|
||||||
if (!es6mode) maybeGeneratePrimaryConstructor()
|
if (!es6mode) maybeGeneratePrimaryConstructor()
|
||||||
|
|
||||||
val transformer = IrDeclarationToJsTransformer()
|
|
||||||
|
|
||||||
// Properties might be lowered out of classes
|
// Properties might be lowered out of classes
|
||||||
// We'll use IrSimpleFunction::correspondingProperty to collect them into set
|
// We'll use IrSimpleFunction::correspondingProperty to collect them into set
|
||||||
val properties = mutableSetOf<IrProperty>()
|
val properties = mutableSetOf<IrProperty>()
|
||||||
|
|
||||||
val jsClass = JsClass(name = className, baseClass = baseClassRef)
|
val jsClass = JsClass(name = classNameUsedInsideDeclarationStatements, baseClass = baseClassRef)
|
||||||
|
|
||||||
if (baseClass != null && !baseClass.isAny()) {
|
if (baseClass != null && !baseClass.isAny()) {
|
||||||
jsClass.baseClass = baseClassRef
|
jsClass.baseClass = baseClassRef
|
||||||
}
|
}
|
||||||
|
|
||||||
if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt()
|
if (es6mode) {
|
||||||
|
classModel.preDeclarationBlock.statements += jsClass.makeStmt()
|
||||||
|
}
|
||||||
|
|
||||||
for (declaration in irClass.declarations) {
|
for (declaration in irClass.declarations) {
|
||||||
when (declaration) {
|
when (declaration) {
|
||||||
is IrConstructor -> {
|
is IrConstructor -> {
|
||||||
|
val constructor = declaration.accept(IrFunctionToJsTransformer(), context)
|
||||||
if (es6mode) {
|
if (es6mode) {
|
||||||
declaration.accept(IrFunctionToJsTransformer(), context).let { fn ->
|
jsClass.constructor = constructor.apply { name = null }
|
||||||
if (fn.body.statements.any { it !is JsEmpty && !it.isSimpleSuperCall(fn) }) {
|
|
||||||
jsClass.constructor = fn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
classBlock.statements += declaration.accept(transformer, context)
|
classBlock.statements += constructor.apply { name = classNameUsedInsideDeclarationStatements }.makeStmt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IrSimpleFunction -> {
|
is IrSimpleFunction -> {
|
||||||
@@ -275,7 +316,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
assert(!declaration.isStaticMethodOfClass)
|
assert(!declaration.isStaticMethodOfClass)
|
||||||
|
|
||||||
if (irClass.isInterface) {
|
if (irClass.isInterface) {
|
||||||
classModel.preDeclarationBlock.statements += translatedFunction.makeStmt()
|
interfaceDefaultsBlock.statements += translatedFunction.makeStmt()
|
||||||
return Pair(memberName, null)
|
return Pair(memberName, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +364,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
private fun maybeGeneratePrimaryConstructor() {
|
private fun maybeGeneratePrimaryConstructor() {
|
||||||
if (!irClass.declarations.any { it is IrConstructor }) {
|
if (!irClass.declarations.any { it is IrConstructor }) {
|
||||||
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
|
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
|
||||||
func.name = className
|
func.name = classNameUsedInsideDeclarationStatements
|
||||||
classBlock.statements += func.makeStmt()
|
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 {
|
val ownerSymbol = classOrNull?.takeIf {
|
||||||
!isAny() && !isFunctionType() && !it.owner.isEffectivelyExternal()
|
!isAny() && !isFunctionType() && !it.owner.isEffectivelyExternal()
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
return JsNameRef(context.getNameForClass(ownerSymbol.owner))
|
return ownerSymbol.owner.getClassRef(context.staticContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
|
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.backend.common.compilationException
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
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.*
|
||||||
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.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
@@ -91,13 +88,13 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
|||||||
|
|
||||||
add(intrinsics.jsObjectCreateSymbol) { call, context ->
|
add(intrinsics.jsObjectCreateSymbol) { call, context ->
|
||||||
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||||
val className = context.getNameForClass(classToCreate)
|
val className = classToCreate.getClassRef(context.staticContext)
|
||||||
objectCreate(prototypeOf(className.makeRef(), context.staticContext), context.staticContext)
|
objectCreate(prototypeOf(className, context.staticContext), context.staticContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
add(intrinsics.jsClass) { call, context ->
|
add(intrinsics.jsClass) { call, context ->
|
||||||
val typeArgument = call.getTypeArgument(0)
|
val typeArgument = call.getTypeArgument(0)
|
||||||
typeArgument?.getClassRef(context)
|
typeArgument?.getClassRef(context.staticContext)
|
||||||
?: compilationException(
|
?: compilationException(
|
||||||
"Type argument of jsClass must be statically known class",
|
"Type argument of jsClass must be statically known class",
|
||||||
typeArgument
|
typeArgument
|
||||||
@@ -180,7 +177,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
|||||||
val arg = translateCallArguments(call, context).single()
|
val arg = translateCallArguments(call, context).single()
|
||||||
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
|
val inlineClass = icUtils.getInlinedClass(call.getTypeArgument(0)!!)!!
|
||||||
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
|
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 }
|
.apply { isInlineClassBoxing = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +207,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
|||||||
is IrFunctionReference -> {
|
is IrFunctionReference -> {
|
||||||
val superClass = call.superQualifierSymbol!!
|
val superClass = call.superQualifierSymbol!!
|
||||||
val functionName = context.getNameForMemberFunction(target.symbol.owner as IrSimpleFunction)
|
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))
|
JsNameRef(functionName, prototypeOf(superName, context.staticContext))
|
||||||
}
|
}
|
||||||
is IrFunctionExpression -> target.accept(IrElementToJsExpressionTransformer(), context)
|
is IrFunctionExpression -> target.accept(IrElementToJsExpressionTransformer(), context)
|
||||||
|
|||||||
+31
-32
@@ -31,15 +31,16 @@ class JsIrProgramFragment(val packageFqn: String) {
|
|||||||
class JsIrModule(
|
class JsIrModule(
|
||||||
val moduleName: String,
|
val moduleName: String,
|
||||||
val externalModuleName: String,
|
val externalModuleName: String,
|
||||||
val fragments: List<JsIrProgramFragment>
|
val fragments: List<JsIrProgramFragment>,
|
||||||
|
val reexportedInModuleWithName: String? = null,
|
||||||
) {
|
) {
|
||||||
fun makeModuleHeader(): JsIrModuleHeader {
|
fun makeModuleHeader(): JsIrModuleHeader {
|
||||||
val nameBindings = mutableMapOf<String, String>()
|
val nameBindings = mutableMapOf<String, String>()
|
||||||
val definitions = mutableSetOf<String>()
|
val definitions = mutableSetOf<String>()
|
||||||
val optionalCrossModuleImports = hashSetOf<String>()
|
val optionalCrossModuleImports = hashSetOf<String>()
|
||||||
var hasJsExports = false
|
var hasDeclarationsToReexport = false
|
||||||
for (fragment in fragments) {
|
for (fragment in fragments) {
|
||||||
hasJsExports = hasJsExports || !fragment.exports.isEmpty
|
hasDeclarationsToReexport = hasDeclarationsToReexport || !fragment.exports.isEmpty
|
||||||
for ((tag, name) in fragment.nameBindings.entries) {
|
for ((tag, name) in fragment.nameBindings.entries) {
|
||||||
nameBindings[tag] = name.toString()
|
nameBindings[tag] = name.toString()
|
||||||
}
|
}
|
||||||
@@ -52,7 +53,7 @@ class JsIrModule(
|
|||||||
definitions = definitions,
|
definitions = definitions,
|
||||||
nameBindings = nameBindings,
|
nameBindings = nameBindings,
|
||||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||||
hasJsExports = hasJsExports,
|
reexportedInModuleWithName = reexportedInModuleWithName.takeIf { hasDeclarationsToReexport },
|
||||||
associatedModule = this
|
associatedModule = this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -64,8 +65,8 @@ class JsIrModuleHeader(
|
|||||||
val definitions: Set<String>,
|
val definitions: Set<String>,
|
||||||
val nameBindings: Map<String, String>,
|
val nameBindings: Map<String, String>,
|
||||||
val optionalCrossModuleImports: Set<String>,
|
val optionalCrossModuleImports: Set<String>,
|
||||||
val hasJsExports: Boolean,
|
val reexportedInModuleWithName: String? = null,
|
||||||
var associatedModule: JsIrModule?
|
var associatedModule: JsIrModule?,
|
||||||
) {
|
) {
|
||||||
val externalNames: Set<String> by lazy(LazyThreadSafetyMode.NONE) { nameBindings.keys - definitions }
|
val externalNames: Set<String> by lazy(LazyThreadSafetyMode.NONE) { nameBindings.keys - definitions }
|
||||||
}
|
}
|
||||||
@@ -89,19 +90,18 @@ class JsIrProgram(private var modules: List<JsIrModule>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CrossModuleDependenciesResolver(
|
class CrossModuleDependenciesResolver(private val moduleKind: ModuleKind, private val headers: List<JsIrModuleHeader>) {
|
||||||
private val moduleKind: ModuleKind,
|
|
||||||
private val headers: List<JsIrModuleHeader>
|
|
||||||
) {
|
|
||||||
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
|
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
|
||||||
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferenceBuilder(moduleKind, it, relativeRequirePath) }
|
val reexportModuleToHeader = headers.groupBy { it.reexportedInModuleWithName }
|
||||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
|
val headerToBuilder = headers.associateWith {
|
||||||
|
JsIrModuleCrossModuleReferenceBuilder(
|
||||||
if (moduleKind != ModuleKind.ES) {
|
moduleKind,
|
||||||
val mainModuleHeader = headers.last()
|
it,
|
||||||
val otherModuleHeaders = headers.dropLast(1)
|
relativeRequirePath,
|
||||||
headerToBuilder[mainModuleHeader]!!.transitiveJsExportFrom = otherModuleHeaders
|
reexportModuleToHeader[it.moduleName] ?: emptyList(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferenceBuilder>()
|
||||||
|
|
||||||
for (header in headers) {
|
for (header in headers) {
|
||||||
val builder = headerToBuilder[header]!!
|
val builder = headerToBuilder[header]!!
|
||||||
@@ -115,6 +115,7 @@ class CrossModuleDependenciesResolver(
|
|||||||
val builder = headerToBuilder[header]!!
|
val builder = headerToBuilder[header]!!
|
||||||
for (tag in header.externalNames) {
|
for (tag in header.externalNames) {
|
||||||
val fromModuleBuilder = definitionModule[tag]
|
val fromModuleBuilder = definitionModule[tag]
|
||||||
|
|
||||||
if (fromModuleBuilder == null) {
|
if (fromModuleBuilder == null) {
|
||||||
if (tag in header.optionalCrossModuleImports) {
|
if (tag in header.optionalCrossModuleImports) {
|
||||||
continue
|
continue
|
||||||
@@ -128,6 +129,8 @@ class CrossModuleDependenciesResolver(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headerToBuilder.forEach { it.value.buildExportNames() }
|
||||||
|
|
||||||
return headers.associateWith { headerToBuilder[it]!!.buildCrossModuleRefs() }
|
return headers.associateWith { headerToBuilder[it]!!.buildCrossModuleRefs() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,21 +140,20 @@ private class CrossModuleRef(val module: JsIrModuleCrossModuleReferenceBuilder,
|
|||||||
private class JsIrModuleCrossModuleReferenceBuilder(
|
private class JsIrModuleCrossModuleReferenceBuilder(
|
||||||
val moduleKind: ModuleKind,
|
val moduleKind: ModuleKind,
|
||||||
val header: JsIrModuleHeader,
|
val header: JsIrModuleHeader,
|
||||||
val relativeRequirePath: Boolean
|
val relativeRequirePath: Boolean,
|
||||||
|
val transitiveExportFrom: List<JsIrModuleHeader>,
|
||||||
) {
|
) {
|
||||||
val imports = mutableListOf<CrossModuleRef>()
|
val imports = mutableListOf<CrossModuleRef>()
|
||||||
val exports = mutableSetOf<String>()
|
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() {
|
fun buildExportNames(startIndex: Int = 0) {
|
||||||
var index = 0
|
var index = startIndex
|
||||||
exportNames = exports.sorted().associateWith { index++.toJsIdentifier() }
|
exportNames = exports.sorted().associateWith { index++.toJsIdentifier() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildCrossModuleRefs(): CrossModuleReferences {
|
fun buildCrossModuleRefs(): CrossModuleReferences {
|
||||||
buildExportNames()
|
|
||||||
val isImportOptional = moduleKind == ModuleKind.ES
|
val isImportOptional = moduleKind == ModuleKind.ES
|
||||||
val importedModules = mutableMapOf<JsIrModuleHeader, JsImportedModule>()
|
val importedModules = mutableMapOf<JsIrModuleHeader, JsImportedModule>()
|
||||||
|
|
||||||
@@ -165,18 +167,16 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
|||||||
|
|
||||||
val resultImports = imports.associate { crossModuleRef ->
|
val resultImports = imports.associate { crossModuleRef ->
|
||||||
val tag = crossModuleRef.tag
|
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 exportedAs = crossModuleRef.module.exportNames[tag]!!
|
||||||
val importedModule = import(crossModuleRef.module.header)
|
val importedModule = import(crossModuleRef.module.header)
|
||||||
|
|
||||||
tag to CrossModuleImport(exportedAs, importedModule)
|
tag to CrossModuleImport(exportedAs, importedModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
val transitiveExport = transitiveJsExportFrom.mapNotNull {
|
val transitiveExport = transitiveExportFrom.mapNotNull {
|
||||||
if (!it.hasJsExports) null else CrossModuleTransitiveExport(import(it).internalName, it.externalModuleName)
|
it.reexportedInModuleWithName?.run {
|
||||||
|
CrossModuleTransitiveExport(import(it).internalName, relativeRequirePath(it) ?: it.externalModuleName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return CrossModuleReferences(
|
return CrossModuleReferences(
|
||||||
moduleKind,
|
moduleKind,
|
||||||
@@ -208,8 +208,7 @@ private class JsIrModuleCrossModuleReferenceBuilder(
|
|||||||
.toRelativeString(parentMain)
|
.toRelativeString(parentMain)
|
||||||
.replace(File.separator, "/")
|
.replace(File.separator, "/")
|
||||||
|
|
||||||
return relativePath.takeIf { it.startsWith("../") }
|
return relativePath.takeIf { it.startsWith("../") } ?: "./$relativePath"
|
||||||
?: "./$relativePath"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +221,7 @@ fun CrossModuleTransitiveExport.getRequireEsmName() = "$externalName$ESM_EXTENSI
|
|||||||
class CrossModuleReferences(
|
class CrossModuleReferences(
|
||||||
val moduleKind: ModuleKind,
|
val moduleKind: ModuleKind,
|
||||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
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 exports: Map<String, String>, // tag -> index
|
||||||
val imports: Map<String, CrossModuleImport>, // tag -> import statement
|
val imports: Map<String, CrossModuleImport>, // tag -> import statement
|
||||||
) {
|
) {
|
||||||
|
|||||||
+2
-3
@@ -148,7 +148,6 @@ class Merger(
|
|||||||
?.asSequence()
|
?.asSequence()
|
||||||
?.flatMap { (it.subject as JsExport.Subject.Elements).elements }
|
?.flatMap { (it.subject as JsExport.Subject.Elements).elements }
|
||||||
?.distinctBy { it.alias?.ident ?: it.name.ident }
|
?.distinctBy { it.alias?.ident ?: it.name.ident }
|
||||||
?.map { if (it.name.ident == it.alias?.ident) JsExport.Element(it.name, null) else it }
|
|
||||||
?.toList()
|
?.toList()
|
||||||
|
|
||||||
val oneLargeExportStatement = exportedElements?.let { JsExport(JsExport.Subject.Elements(it)) }
|
val oneLargeExportStatement = exportedElements?.let { JsExport(JsExport.Subject.Elements(it)) }
|
||||||
@@ -179,14 +178,14 @@ class Merger(
|
|||||||
|
|
||||||
private fun transitiveJsExport(): List<JsStatement> {
|
private fun transitiveJsExport(): List<JsStatement> {
|
||||||
return if (isEsModules) {
|
return if (isEsModules) {
|
||||||
crossModuleReferences.transitiveJsExportFrom.map {
|
crossModuleReferences.transitiveExportFrom.map {
|
||||||
JsExport(JsExport.Subject.All, it.getRequireEsmName())
|
JsExport(JsExport.Subject.All, it.getRequireEsmName())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||||
val exporterName = ReservedJsNames.makeJsExporterName()
|
val exporterName = ReservedJsNames.makeJsExporterName()
|
||||||
|
|
||||||
crossModuleReferences.transitiveJsExportFrom.map {
|
crossModuleReferences.transitiveExportFrom.map {
|
||||||
JsInvocation(
|
JsInvocation(
|
||||||
JsNameRef(exporterName, it.internalName.makeRef()),
|
JsNameRef(exporterName, it.internalName.makeRef()),
|
||||||
internalModuleName.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.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrFileEntry
|
import org.jetbrains.kotlin.ir.IrFileEntry
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
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.JsLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
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.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
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.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
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.js.config.SourceMapSourceEmbedding
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
@@ -38,8 +37,8 @@ import java.io.IOException
|
|||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
fun jsUndefined(context: IrNamer, backendContext: JsIrBackendContext): JsExpression {
|
fun jsUndefined(context: JsStaticContext): JsExpression {
|
||||||
return when (val void = backendContext.getVoid()) {
|
return when (val void = context.backendContext.getVoid()) {
|
||||||
is IrGetField -> context.getNameForField(void.symbol.owner).makeRef()
|
is IrGetField -> context.getNameForField(void.symbol.owner).makeRef()
|
||||||
else -> JsNullLiteral()
|
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 {
|
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(
|
return JsInvocation(
|
||||||
context
|
context
|
||||||
.getNameForStaticFunction(context.backendContext.intrinsics.jsDefinePropertySymbol.owner)
|
.getNameForStaticFunction(context.backendContext.intrinsics.jsDefinePropertySymbol.owner)
|
||||||
.makeRef(),
|
.makeRef(),
|
||||||
obj,
|
listOfNotNull(obj, JsStringLiteral(name), getter ?: runIf(setter != null) { jsUndefined(context) }, setter)
|
||||||
JsStringLiteral(name),
|
|
||||||
getter ?: undefined,
|
|
||||||
setter ?: undefined
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +208,7 @@ fun translateCall(
|
|||||||
val nameForStaticDeclaration = context.getNameForStaticDeclaration(target)
|
val nameForStaticDeclaration = context.getNameForStaticDeclaration(target)
|
||||||
JsNameRef(Namer.CALL_FUNCTION, JsNameRef(nameForStaticDeclaration))
|
JsNameRef(Namer.CALL_FUNCTION, JsNameRef(nameForStaticDeclaration))
|
||||||
} else {
|
} else {
|
||||||
val qualifierName = context.getNameForClass(klass).makeRef()
|
val qualifierName = klass.getClassRef(context.staticContext)
|
||||||
val targetName = context.getNameForMemberFunction(target)
|
val targetName = context.getNameForMemberFunction(target)
|
||||||
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName, context.staticContext))
|
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName, context.staticContext))
|
||||||
JsNameRef(Namer.CALL_FUNCTION, qPrototype)
|
JsNameRef(Namer.CALL_FUNCTION, qPrototype)
|
||||||
@@ -399,7 +394,7 @@ fun translateCallArguments(
|
|||||||
val varargParameterIndex = function.realOverrideTarget.varargParameterIndex()
|
val varargParameterIndex = function.realOverrideTarget.varargParameterIndex()
|
||||||
|
|
||||||
val validWithNullArgs = expression.validWithNullArgs()
|
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)
|
val arguments = (0 until size)
|
||||||
.mapIndexedTo(ArrayList(size)) { i, _ ->
|
.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 }) {
|
for (temporaryName in scope.declaredNames.asSequence().filter { it.isTemporary }) {
|
||||||
var resolvedName = temporaryName.ident
|
var resolvedName = temporaryName.ident
|
||||||
var suffix = nextSuffix.getOrDefault(temporaryName.ident, 0)
|
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++}"
|
resolvedName = "${temporaryName.ident}_${suffix++}"
|
||||||
}
|
}
|
||||||
nextSuffix[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
|
// We need it to not rename methods and fields inside class body
|
||||||
// Because if they are in clash with something, it means overriding
|
// Because if they are in clash with something, it means overriding
|
||||||
x.constructor?.accept(this)
|
x.constructor?.accept(this)
|
||||||
|
x.baseClass?.accept(this)
|
||||||
x.members.forEach { visitFunction(it, shouldReserveName = false) }
|
x.members.forEach { visitFunction(it, shouldReserveName = false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
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.declarations.IrDeclarationWithName
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
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.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
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.ir.util.unexpectedSymbolKind
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||||
|
|
||||||
fun IrType.asString(): String = when (this) {
|
fun IrType.asString(): String = when (this) {
|
||||||
// TODO: should each IrErrorType have own string representation?
|
// 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
|
is IrScriptSymbol -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrType.getClassRef(context: JsGenerationContext): JsNameRef =
|
fun IrType.getClassRef(context: JsStaticContext): JsExpression {
|
||||||
when (val klass = classifierOrFail.owner) {
|
return when (val klass = classifierOrFail.owner) {
|
||||||
is IrClass ->
|
is IrClass -> klass.getClassRef(context)
|
||||||
if (klass.isEffectivelyExternal())
|
|
||||||
context.getRefForExternalClass(klass)
|
|
||||||
else
|
|
||||||
context.getNameForClass(klass).makeRef()
|
|
||||||
|
|
||||||
else -> context.getNameForStaticDeclaration(klass as IrDeclarationWithName).makeRef()
|
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
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
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.JsIntrinsicTransformers
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrClassModel
|
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.declarations.IrDeclaration
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.js.backend.ast.JsCompositeBlock
|
import org.jetbrains.kotlin.js.backend.ast.JsCompositeBlock
|
||||||
@@ -17,9 +19,12 @@ class JsStaticContext(
|
|||||||
val backendContext: JsIrBackendContext,
|
val backendContext: JsIrBackendContext,
|
||||||
private val irNamer: IrNamer,
|
private val irNamer: IrNamer,
|
||||||
val globalNameScope: NameTable<IrDeclaration>,
|
val globalNameScope: NameTable<IrDeclaration>,
|
||||||
|
val mode: TranslationMode,
|
||||||
) : IrNamer by irNamer {
|
) : IrNamer by irNamer {
|
||||||
val intrinsics = JsIntrinsicTransformers(backendContext)
|
val intrinsics = JsIntrinsicTransformers(backendContext)
|
||||||
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
val classModels = mutableMapOf<IrClassSymbol, JsIrClassModel>()
|
||||||
|
|
||||||
val initializerBlock = JsCompositeBlock()
|
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()
|
private val keptDeclarations: MutableSet<IrDeclaration> = mutableSetOf()
|
||||||
|
|
||||||
fun shouldKeep(declaration: IrDeclaration): Boolean {
|
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) {
|
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.nio.ByteBuffer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun deserializeJsIrProgramFragment(input: ByteArray): JsIrProgramFragment {
|
fun deserializeJsIrProgramFragment(input: ByteArray): List<JsIrProgramFragment> {
|
||||||
return JsIrAstDeserializer(input).readFragment()
|
return JsIrAstDeserializer(input).readFragments()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class JsIrAstDeserializer(private val source: ByteArray) {
|
private class JsIrAstDeserializer(private val source: ByteArray) {
|
||||||
@@ -76,6 +76,10 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
|||||||
return if (readBoolean()) then() else null
|
return if (readBoolean()) then() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readFragments(): List<JsIrProgramFragment> {
|
||||||
|
return readList { readFragment() }
|
||||||
|
}
|
||||||
|
|
||||||
fun readFragment(): JsIrProgramFragment {
|
fun readFragment(): JsIrProgramFragment {
|
||||||
return JsIrProgramFragment(readString()).apply {
|
return JsIrProgramFragment(readString()).apply {
|
||||||
readRepeated {
|
readRepeated {
|
||||||
@@ -320,7 +324,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
|
|||||||
CLASS -> {
|
CLASS -> {
|
||||||
JsClass(
|
JsClass(
|
||||||
ifTrue { nameTable[readInt()] },
|
ifTrue { nameTable[readInt()] },
|
||||||
ifTrue { nameTable[readInt()].makeRef() },
|
ifTrue { readExpression() },
|
||||||
ifTrue { readFunction() },
|
ifTrue { readFunction() },
|
||||||
).apply {
|
).apply {
|
||||||
readRepeated { members += readFunction() }
|
readRepeated { members += readFunction() }
|
||||||
|
|||||||
+10
-5
@@ -15,8 +15,8 @@ import java.io.DataOutputStream
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun JsIrProgramFragment.serializeTo(output: OutputStream) {
|
fun List<JsIrProgramFragment>.serializeTo(output: OutputStream) {
|
||||||
JsIrAstSerializer().append(this).saveTo(output)
|
JsIrAstSerializer().appendAll(this).saveTo(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataWriter {
|
private class DataWriter {
|
||||||
@@ -89,6 +89,12 @@ private class JsIrAstSerializer {
|
|||||||
private val fileStack: Deque<String> = ArrayDeque()
|
private val fileStack: Deque<String> = ArrayDeque()
|
||||||
private val importedNames = mutableSetOf<JsName>()
|
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 {
|
fun append(fragment: JsIrProgramFragment): JsIrAstSerializer {
|
||||||
importedNames += fragment.imports.map { fragment.nameBindings[it.key]!! }
|
importedNames += fragment.imports.map { fragment.nameBindings[it.key]!! }
|
||||||
fragmentSerializer.writeFragment(fragment)
|
fragmentSerializer.writeFragment(fragment)
|
||||||
@@ -437,9 +443,8 @@ private class JsIrAstSerializer {
|
|||||||
ifNotNull(x.name) {
|
ifNotNull(x.name) {
|
||||||
writeInt(internalizeName(it))
|
writeInt(internalizeName(it))
|
||||||
}
|
}
|
||||||
// TODO: add more complex JsNameRef parsing in future when we will support `class` expressions inside a `js` call
|
ifNotNull(x.baseClass) {
|
||||||
ifNotNull(x.baseClass?.name) {
|
writeExpression(it)
|
||||||
writeInt(internalizeName(it))
|
|
||||||
}
|
}
|
||||||
ifNotNull(x.constructor) {
|
ifNotNull(x.constructor) {
|
||||||
writeFunction(it)
|
writeFunction(it)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
$TESTDATA_DIR$/simple2js.kt
|
||||||
|
-Xir-only
|
||||||
|
-ir-output-dir
|
||||||
|
$TEMP_DIR$
|
||||||
|
-ir-output-name
|
||||||
|
out
|
||||||
|
-module-kind
|
||||||
|
es
|
||||||
|
-Xir-per-file
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
$TESTDATA_DIR$/simple2js.kt
|
||||||
|
-Xir-only
|
||||||
|
-ir-output-dir
|
||||||
|
$TEMP_DIR$
|
||||||
|
-ir-output-name
|
||||||
|
out
|
||||||
|
-module-kind
|
||||||
|
commonjs
|
||||||
|
-Xir-per-file
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
error: per-file compilation can't be used with any `moduleKind` except `es` (ECMAScript Modules)
|
||||||
|
COMPILATION_ERROR
|
||||||
+9
-3
@@ -58,7 +58,9 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu
|
|||||||
TranslationMode.FULL_DEV to "out",
|
TranslationMode.FULL_DEV to "out",
|
||||||
TranslationMode.FULL_PROD_MINIMIZED_NAMES to "outMin",
|
TranslationMode.FULL_PROD_MINIMIZED_NAMES to "outMin",
|
||||||
TranslationMode.PER_MODULE_DEV to "outPm",
|
TranslationMode.PER_MODULE_DEV to "outPm",
|
||||||
TranslationMode.PER_MODULE_PROD_MINIMIZED_NAMES to "outPmMin"
|
TranslationMode.PER_MODULE_PROD_MINIMIZED_NAMES to "outPmMin",
|
||||||
|
TranslationMode.PER_FILE_DEV to "outPf",
|
||||||
|
TranslationMode.PER_FILE_PROD_MINIMIZED_NAMES to "outPfMin"
|
||||||
)
|
)
|
||||||
|
|
||||||
private const val OUTPUT_KLIB_DIR_NAME = "outputKlibDir"
|
private const val OUTPUT_KLIB_DIR_NAME = "outputKlibDir"
|
||||||
@@ -86,11 +88,15 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL_DEV): String {
|
fun getJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL_DEV): String {
|
||||||
return getJsArtifactsOutputDir(testServices, translationMode).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5"
|
return getJsArtifactsOutputDir(testServices, translationMode).absolutePath + File.separator + getJsModuleArtifactName(testServices, moduleName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRecompiledJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL_DEV): String {
|
fun getRecompiledJsModuleArtifactPath(testServices: TestServices, moduleName: String, translationMode: TranslationMode = TranslationMode.FULL_DEV): String {
|
||||||
return getJsArtifactsRecompiledOutputDir(testServices, translationMode).absolutePath + File.separator + getJsArtifactSimpleName(testServices, moduleName) + "_v5"
|
return getJsArtifactsRecompiledOutputDir(testServices, translationMode).absolutePath + File.separator + getJsModuleArtifactName(testServices, moduleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getJsModuleArtifactName(testServices: TestServices, moduleName: String): String {
|
||||||
|
return getJsArtifactSimpleName(testServices, moduleName) + "_v5"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getJsKlibArtifactPath(testServices: TestServices, moduleName: String): String {
|
fun getJsKlibArtifactPath(testServices: TestServices, moduleName: String): String {
|
||||||
|
|||||||
+1
-2
@@ -26,10 +26,9 @@ class TemporaryDirectoryManagerImpl(testServices: TestServices) : TemporaryDirec
|
|||||||
|
|
||||||
// This code will simplify directory name for windows. This is needed because there can occur errors due to long name
|
// This code will simplify directory name for windows. This is needed because there can occur errors due to long name
|
||||||
val lastDot = className.lastIndexOf('.')
|
val lastDot = className.lastIndexOf('.')
|
||||||
val packageName = className.substring(0, lastDot + 1)
|
|
||||||
val simplifiedClassName = className.substring(lastDot + 1).getOnlyUpperCaseSymbols()
|
val simplifiedClassName = className.substring(lastDot + 1).getOnlyUpperCaseSymbols()
|
||||||
val simplifiedMethodName = methodName.getOnlyUpperCaseSymbols()
|
val simplifiedMethodName = methodName.getOnlyUpperCaseSymbols()
|
||||||
KtTestUtil.tmpDirForTest(packageName + simplifiedClassName, "test$simplifiedMethodName")
|
KtTestUtil.tmpDirForTest(simplifiedClassName, simplifiedMethodName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val rootDir: File
|
override val rootDir: File
|
||||||
|
|||||||
@@ -1403,6 +1403,16 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
runTest("compiler/testData/cli/js/outputPrefixFileNotFound.args");
|
runTest("compiler/testData/cli/js/outputPrefixFileNotFound.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("perFileWithValidModuleKind.args")
|
||||||
|
public void testPerFileWithValidModuleKind() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/js/perFileWithValidModuleKind.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("perFileWithWrongModuleKind.args")
|
||||||
|
public void testPerFileWithWrongModuleKind() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/js/perFileWithWrongModuleKind.args");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("reifiedIntersectionType.args")
|
@TestMetadata("reifiedIntersectionType.args")
|
||||||
public void testReifiedIntersectionType() throws Exception {
|
public void testReifiedIntersectionType() throws Exception {
|
||||||
runTest("compiler/testData/cli/js/reifiedIntersectionType.args");
|
runTest("compiler/testData/cli/js/reifiedIntersectionType.args");
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ inline fun <T, R> Iterable<T>.same(extractor: (T) -> R): Boolean {
|
|||||||
inline fun <R> runIf(condition: Boolean, block: () -> R): R? = if (condition) block() else null
|
inline fun <R> runIf(condition: Boolean, block: () -> R): R? = if (condition) block() else null
|
||||||
inline fun <R> runUnless(condition: Boolean, block: () -> R): R? = if (condition) null else block()
|
inline fun <R> runUnless(condition: Boolean, block: () -> R): R? = if (condition) null else block()
|
||||||
|
|
||||||
|
inline fun <A : B, B> A.butIf(condition: Boolean, block: (A) -> B): B = if (condition) block(this) else this
|
||||||
|
|
||||||
inline fun <T, R> Collection<T>.foldMap(transform: (T) -> R, operation: (R, R) -> R): R {
|
inline fun <T, R> Collection<T>.foldMap(transform: (T) -> R, operation: (R, R) -> R): R {
|
||||||
val iterator = iterator()
|
val iterator = iterator()
|
||||||
var result = transform(iterator.next())
|
var result = transform(iterator.next())
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.js.common.Symbol
|
|||||||
|
|
||||||
class JsClass(
|
class JsClass(
|
||||||
private var name: JsName? = null,
|
private var name: JsName? = null,
|
||||||
var baseClass: JsNameRef? = null,
|
var baseClass: JsExpression? = null,
|
||||||
var constructor: JsFunction? = null,
|
var constructor: JsFunction? = null,
|
||||||
val members: MutableList<JsFunction> = mutableListOf()
|
val members: MutableList<JsFunction> = mutableListOf()
|
||||||
) : JsLiteral(), HasName {
|
) : JsLiteral(), HasName {
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ class JsExport(
|
|||||||
object All : Subject()
|
object All : Subject()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Element(
|
class Element(val name: JsNameRef, val alias: JsName? = null)
|
||||||
val name: JsNameRef,
|
|
||||||
val alias: JsName?
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun accept(visitor: JsVisitor) {
|
override fun accept(visitor: JsVisitor) {
|
||||||
visitor.visitExport(this)
|
visitor.visitExport(this)
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrCompilerWithIC
|
import org.jetbrains.kotlin.ir.backend.js.JsIrCompilerWithIC
|
||||||
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.backend.js.WholeWorldStageController
|
import org.jetbrains.kotlin.ir.backend.js.WholeWorldStageController
|
||||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
import org.jetbrains.kotlin.ir.backend.js.ic.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.jsPhases
|
import org.jetbrains.kotlin.ir.backend.js.jsPhases
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.extension
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.extension
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||||
@@ -361,7 +361,7 @@ abstract class AbstractInvalidationTest(private val targetBackend: TargetBackend
|
|||||||
relativeRequirePath = true
|
relativeRequirePath = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true)
|
val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(granularity = JsGenerationGranularity.PER_MODULE, outJsProgram = true)
|
||||||
val writtenFiles = writeJsCode(projStep.id, mainModuleName, jsOutput)
|
val writtenFiles = writeJsCode(projStep.id, mainModuleName, jsOutput)
|
||||||
|
|
||||||
verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS)
|
verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS)
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import org.jetbrains.kotlin.cli.common.isWindows
|
|||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig
|
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageConfig
|
||||||
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageLogLevel
|
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageLogLevel
|
||||||
@@ -54,10 +54,10 @@ class JsIrBackendFacade(
|
|||||||
val testServices: TestServices,
|
val testServices: TestServices,
|
||||||
private val firstTimeCompilation: Boolean
|
private val firstTimeCompilation: Boolean
|
||||||
) : AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Js>() {
|
) : AbstractTestFacade<BinaryArtifacts.KLib, BinaryArtifacts.Js>() {
|
||||||
override val inputKind: ArtifactKinds.KLib
|
override val inputKind: ArtifactKinds.KLib get() = ArtifactKinds.KLib
|
||||||
get() = ArtifactKinds.KLib
|
override val outputKind: ArtifactKinds.Js get() = ArtifactKinds.Js
|
||||||
override val outputKind: ArtifactKinds.Js
|
|
||||||
get() = ArtifactKinds.Js
|
private val jsIrPathReplacer by lazy { JsIrPathReplacer(testServices) }
|
||||||
|
|
||||||
constructor(testServices: TestServices) : this(testServices, firstTimeCompilation = true)
|
constructor(testServices: TestServices) : this(testServices, firstTimeCompilation = true)
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ class JsIrBackendFacade(
|
|||||||
|
|
||||||
val granularity = when {
|
val granularity = when {
|
||||||
!firstTimeCompilation -> JsGenerationGranularity.WHOLE_PROGRAM
|
!firstTimeCompilation -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||||
|
splitPerFile || module.kind == ModuleKind.ES -> JsGenerationGranularity.PER_FILE
|
||||||
splitPerModule || perModule -> JsGenerationGranularity.PER_MODULE
|
splitPerModule || perModule -> JsGenerationGranularity.PER_MODULE
|
||||||
splitPerFile -> JsGenerationGranularity.PER_FILE
|
|
||||||
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ class JsIrBackendFacade(
|
|||||||
caches = testServices.jsIrIncrementalDataProvider.getCaches(),
|
caches = testServices.jsIrIncrementalDataProvider.getCaches(),
|
||||||
relativeRequirePath = false
|
relativeRequirePath = false
|
||||||
)
|
)
|
||||||
jsExecutableProducer.buildExecutable(it.perModule, true).compilationOut
|
jsExecutableProducer.buildExecutable(it.granularity, true).compilationOut
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return BinaryArtifacts.Js.JsIrArtifact(
|
return BinaryArtifacts.Js.JsIrArtifact(
|
||||||
@@ -168,6 +168,7 @@ class JsIrBackendFacade(
|
|||||||
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
|
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
|
||||||
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in module.directives
|
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in module.directives
|
||||||
val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives
|
val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives
|
||||||
|
val perFileOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_FILE in module.directives
|
||||||
val isEsModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives ||
|
val isEsModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives ||
|
||||||
module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND].contains(ModuleKind.ES)
|
module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND].contains(ModuleKind.ES)
|
||||||
|
|
||||||
@@ -182,7 +183,7 @@ class JsIrBackendFacade(
|
|||||||
mainArguments,
|
mainArguments,
|
||||||
moduleToName = runIf(isEsModules) {
|
moduleToName = runIf(isEsModules) {
|
||||||
loweredIr.allModules.associateWith {
|
loweredIr.allModules.associateWith {
|
||||||
"./${getJsArtifactSimpleName(testServices, it.safeName)}_v5.mjs".minifyIfNeed()
|
"./${getJsArtifactSimpleName(testServices, it.safeName)}_v5".minifyIfNeed()
|
||||||
}
|
}
|
||||||
} ?: emptyMap()
|
} ?: emptyMap()
|
||||||
)
|
)
|
||||||
@@ -190,17 +191,21 @@ class JsIrBackendFacade(
|
|||||||
// If perModuleOnly then skip whole program
|
// If perModuleOnly then skip whole program
|
||||||
// (it.dce => runIrDce) && (perModuleOnly => it.perModule)
|
// (it.dce => runIrDce) && (perModuleOnly => it.perModule)
|
||||||
val translationModes = TranslationMode.values()
|
val translationModes = TranslationMode.values()
|
||||||
.filter { (it.production || !onlyIrDce) && (!it.production || runIrDce) && (!perModuleOnly || it.perModule) }
|
.filter {
|
||||||
|
(it.production || !onlyIrDce) &&
|
||||||
|
(!it.production || runIrDce) &&
|
||||||
|
(!perModuleOnly || it.granularity == JsGenerationGranularity.PER_MODULE) &&
|
||||||
|
(!perFileOnly || it.granularity == JsGenerationGranularity.PER_FILE)
|
||||||
|
}
|
||||||
.filter { it.production == it.minimizedMemberNames }
|
.filter { it.production == it.minimizedMemberNames }
|
||||||
|
.filter { isEsModules || it.granularity != JsGenerationGranularity.PER_FILE }
|
||||||
.toSet()
|
.toSet()
|
||||||
val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, false)
|
val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, isEsModules)
|
||||||
return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module)
|
return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrModuleFragment.resolveTestPaths() {
|
private fun IrModuleFragment.resolveTestPaths() {
|
||||||
JsIrPathReplacer(testServices).let {
|
files.forEach(jsIrPathReplacer::lower)
|
||||||
files.forEach(it::lower)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadIrFromKlib(module: TestModule, configuration: CompilerConfiguration): IrModuleInfo {
|
private fun loadIrFromKlib(module: TestModule, configuration: CompilerConfiguration): IrModuleInfo {
|
||||||
@@ -268,8 +273,9 @@ class JsIrBackendFacade(
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun File.fixJsFile(newJsTarget: File, moduleId: String, moduleKind: ModuleKind) {
|
fun File.fixJsFile(rootDir: File, newJsTarget: File, moduleId: String, moduleKind: ModuleKind) {
|
||||||
val newJsCode = ClassicJsBackendFacade.wrapWithModuleEmulationMarkers(readText(), moduleKind, moduleId)
|
val newJsCode = ClassicJsBackendFacade.wrapWithModuleEmulationMarkers(readText(), moduleKind, moduleId)
|
||||||
|
val jsCodeWithCorrectImportPath = jsIrPathReplacer.replacePathTokensWithRealPath(newJsCode, newJsTarget, rootDir)
|
||||||
|
|
||||||
val oldJsMap = File("$absolutePath.map")
|
val oldJsMap = File("$absolutePath.map")
|
||||||
val jsCodeMap = (moduleKind == ModuleKind.PLAIN && oldJsMap.exists()).ifTrue { oldJsMap.readText() }
|
val jsCodeMap = (moduleKind == ModuleKind.PLAIN && oldJsMap.exists()).ifTrue { oldJsMap.readText() }
|
||||||
@@ -277,12 +283,13 @@ class JsIrBackendFacade(
|
|||||||
this.delete()
|
this.delete()
|
||||||
oldJsMap.delete()
|
oldJsMap.delete()
|
||||||
|
|
||||||
newJsTarget.write(newJsCode)
|
newJsTarget.write(jsCodeWithCorrectImportPath)
|
||||||
jsCodeMap?.let { File("${newJsTarget.absolutePath}.map").write(it) }
|
jsCodeMap?.let { File("${newJsTarget.absolutePath}.map").write(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CompilationOutputs.writeTo(outputFile: File, moduleId: String, moduleKind: ModuleKind) {
|
private fun CompilationOutputs.writeTo(outputFile: File, moduleId: String, moduleKind: ModuleKind) {
|
||||||
val tmpBuildDir = outputFile.parentFile.resolve("tmp-build")
|
val rootDir = outputFile.parentFile
|
||||||
|
val tmpBuildDir = rootDir.resolve("tmp-build")
|
||||||
// CompilationOutputs keeps the `outputDir` clean by removing all outdated JS and other unknown files.
|
// CompilationOutputs keeps the `outputDir` clean by removing all outdated JS and other unknown files.
|
||||||
// To ensure that useful files around `outputFile`, such as irdump, are not removed, use `tmpBuildDir` instead.
|
// To ensure that useful files around `outputFile`, such as irdump, are not removed, use `tmpBuildDir` instead.
|
||||||
val allJsFiles = writeAll(tmpBuildDir, outputFile.nameWithoutExtension, false, moduleId, moduleKind).filter {
|
val allJsFiles = writeAll(tmpBuildDir, outputFile.nameWithoutExtension, false, moduleId, moduleKind).filter {
|
||||||
@@ -290,11 +297,11 @@ class JsIrBackendFacade(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mainModuleFile = allJsFiles.last()
|
val mainModuleFile = allJsFiles.last()
|
||||||
mainModuleFile.fixJsFile(outputFile, moduleId, moduleKind)
|
mainModuleFile.fixJsFile(rootDir, outputFile, moduleId, moduleKind)
|
||||||
|
|
||||||
dependencies.map { it.first }.zip(allJsFiles.dropLast(1)).forEach { (depModuleId, builtJsFilePath) ->
|
dependencies.map { it.first }.zip(allJsFiles.dropLast(1)).forEach { (depModuleId, builtJsFilePath) ->
|
||||||
val newFile = outputFile.augmentWithModuleName(depModuleId)
|
val newFile = outputFile.augmentWithModuleName(depModuleId)
|
||||||
builtJsFilePath.fixJsFile(newFile, depModuleId, moduleKind)
|
builtJsFilePath.fixJsFile(rootDir, newFile, depModuleId, moduleKind)
|
||||||
}
|
}
|
||||||
tmpBuildDir.deleteRecursively()
|
tmpBuildDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
@@ -347,8 +354,9 @@ fun String.minifyIfNeed(): String {
|
|||||||
if (fileName.length <= 80) return this
|
if (fileName.length <= 80) return this
|
||||||
|
|
||||||
val fileExtension = fileFullName.substringAfterLast('.')
|
val fileExtension = fileFullName.substringAfterLast('.')
|
||||||
|
val extensionPart = if (fileExtension.isEmpty()) "" else ".$fileExtension"
|
||||||
|
|
||||||
return "$directoryPath$delimiter${fileName.cityHash64().toULong().toString(16)}.$fileExtension"
|
return "$directoryPath$delimiter${fileName.cityHash64().toULong().toString(16)}$extensionPart"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun File.augmentWithModuleName(moduleName: String): File = File(absolutePath.augmentWithModuleName(moduleName))
|
fun File.augmentWithModuleName(moduleName: String): File = File(absolutePath.augmentWithModuleName(moduleName))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,6 +18,10 @@ import org.jetbrains.kotlin.test.services.TestServices
|
|||||||
import org.jetbrains.kotlin.test.services.isJsFile
|
import org.jetbrains.kotlin.test.services.isJsFile
|
||||||
import org.jetbrains.kotlin.test.services.isMjsFile
|
import org.jetbrains.kotlin.test.services.isMjsFile
|
||||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.io.invariantSeparatorsPath
|
||||||
|
|
||||||
|
private const val PATH_TO_ROOT_TOKEN = "@PATH_TO_ROOT"
|
||||||
|
|
||||||
class JsIrPathReplacer(testServices: TestServices) : DeclarationTransformer {
|
class JsIrPathReplacer(testServices: TestServices) : DeclarationTransformer {
|
||||||
private val replacements = testServices.collectReplacementsMap()
|
private val replacements = testServices.collectReplacementsMap()
|
||||||
@@ -35,6 +39,7 @@ class JsIrPathReplacer(testServices: TestServices) : DeclarationTransformer {
|
|||||||
|
|
||||||
private fun IrAnnotationContainer.replaceJsModulePath() {
|
private fun IrAnnotationContainer.replaceJsModulePath() {
|
||||||
val jsModuleAnnotation = getAnnotation(JsAnnotations.jsModuleFqn) ?: return
|
val jsModuleAnnotation = getAnnotation(JsAnnotations.jsModuleFqn) ?: return
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val stringLiteral = jsModuleAnnotation.getValueArgument(0) as IrConst<String>
|
val stringLiteral = jsModuleAnnotation.getValueArgument(0) as IrConst<String>
|
||||||
val pathReplacement = stringLiteral.getReplacement() ?: return
|
val pathReplacement = stringLiteral.getReplacement() ?: return
|
||||||
@@ -43,17 +48,24 @@ class JsIrPathReplacer(testServices: TestServices) : DeclarationTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun IrConst<String>.getReplacement(): IrConst<String>? {
|
private fun IrConst<String>.getReplacement(): IrConst<String>? {
|
||||||
val replacement = replacements[value] ?: replacements[value.replace("./", "")] ?: return null
|
return IrConstImpl.string(startOffset, endOffset, type, replacements[value] ?: return null)
|
||||||
return IrConstImpl.string(startOffset, endOffset, type, "./" + replacement.replace("./", ""))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestServices.collectReplacementsMap(): Map<String, String> {
|
private fun TestServices.collectReplacementsMap(): Map<String, String> {
|
||||||
return moduleStructure.modules.asSequence()
|
return moduleStructure.modules.asSequence()
|
||||||
.map { module -> module to module.files.filter { it.isJsFile || it.isMjsFile } }
|
.map { module -> module to module.files.filter { it.isJsFile || it.isMjsFile } }
|
||||||
.filter { (_, files) -> files.isNotEmpty() }
|
.filter { (_, files) -> files.isNotEmpty() }
|
||||||
.flatMap { (module, files) -> files.map { it.relativePath to module.getNameFor(it, this) } }
|
.flatMap { (module, files) -> files.map { "./${it.relativePath}" to "$PATH_TO_ROOT_TOKEN/${module.getNameFor(it, this)}" } }
|
||||||
.plus(getAdditionalFiles(this).map { it.name to it.name })
|
.plus(getAdditionalFiles(this).map { "./${it.name}" to "$PATH_TO_ROOT_TOKEN/${it.name}" })
|
||||||
.plus(getAdditionalMainFiles(this).map { it.name to it.name })
|
.plus(getAdditionalMainFiles(this).map { "./${it.name}" to "$PATH_TO_ROOT_TOKEN/${it.name}" })
|
||||||
.toMap()
|
.toMap()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun replacePathTokensWithRealPath(content: String, file: File, rootDir: File): String {
|
||||||
|
val relativePathToRootDir = rootDir.relativeTo(file.parentFile).invariantSeparatorsPath.ifEmpty { "." }
|
||||||
|
return content.replace("from '$PATH_TO_ROOT_TOKEN/(.+?)';${'$'}".toRegex(RegexOption.MULTILINE)) { result ->
|
||||||
|
val importPath = result.groups[1]?.value ?: error("Unexpected import path")
|
||||||
|
"from '$relativePathToRootDir/$importPath'\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,17 +31,23 @@ class JsArtifactsDumpHandler(testServices: TestServices) : AfterAnalysisChecker(
|
|||||||
val testGroupOutputDirForMinification = File(pathToRootOutputDir + "out-min/" + testGroupOutputDirPrefix)
|
val testGroupOutputDirForMinification = File(pathToRootOutputDir + "out-min/" + testGroupOutputDirPrefix)
|
||||||
val testGroupOutputDirForPerModuleCompilation = File(pathToRootOutputDir + "out-per-module/" + testGroupOutputDirPrefix)
|
val testGroupOutputDirForPerModuleCompilation = File(pathToRootOutputDir + "out-per-module/" + testGroupOutputDirPrefix)
|
||||||
val testGroupOutputDirForPerModuleMinification = File(pathToRootOutputDir + "out-per-module-min/" + testGroupOutputDirPrefix)
|
val testGroupOutputDirForPerModuleMinification = File(pathToRootOutputDir + "out-per-module-min/" + testGroupOutputDirPrefix)
|
||||||
|
val testGroupOutputDirForPerFileCompilation = File(pathToRootOutputDir + "out-per-file/" + testGroupOutputDirPrefix)
|
||||||
|
val testGroupOutputDirForPerFileMinification = File(pathToRootOutputDir + "out-per-file-min/" + testGroupOutputDirPrefix)
|
||||||
|
|
||||||
val outputDir = getOutputDir(originalFile, testGroupOutputDirForCompilation, stopFile)
|
val outputDir = getOutputDir(originalFile, testGroupOutputDirForCompilation, stopFile)
|
||||||
val dceOutputDir = getOutputDir(originalFile, testGroupOutputDirForMinification, stopFile)
|
val dceOutputDir = getOutputDir(originalFile, testGroupOutputDirForMinification, stopFile)
|
||||||
val perModuleOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleCompilation, stopFile)
|
val perModuleOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleCompilation, stopFile)
|
||||||
val preModuleDceOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleMinification, stopFile)
|
val perModuleDceOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerModuleMinification, stopFile)
|
||||||
|
val perFileOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerFileCompilation, stopFile)
|
||||||
|
val perFileDceOutputDir = getOutputDir(originalFile, testGroupOutputDirForPerFileMinification, stopFile)
|
||||||
val minOutputDir = File(dceOutputDir, originalFile.nameWithoutExtension)
|
val minOutputDir = File(dceOutputDir, originalFile.nameWithoutExtension)
|
||||||
|
|
||||||
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices), outputDir)
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices), outputDir)
|
||||||
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_PROD_MINIMIZED_NAMES), dceOutputDir)
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_PROD_MINIMIZED_NAMES), dceOutputDir)
|
||||||
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE_DEV), perModuleOutputDir)
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE_DEV), perModuleOutputDir)
|
||||||
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE_PROD_MINIMIZED_NAMES), preModuleDceOutputDir)
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_MODULE_PROD_MINIMIZED_NAMES), perModuleDceOutputDir)
|
||||||
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_FILE_DEV), perFileOutputDir)
|
||||||
|
copy(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.PER_FILE_PROD_MINIMIZED_NAMES), perFileDceOutputDir)
|
||||||
copy(JsEnvironmentConfigurator.getMinificationJsArtifactsOutputDir(testServices), minOutputDir)
|
copy(JsEnvironmentConfigurator.getMinificationJsArtifactsOutputDir(testServices), minOutputDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -23,11 +23,11 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
|
|||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
||||||
import org.jetbrains.kotlin.ir.backend.js.*
|
import org.jetbrains.kotlin.ir.backend.js.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.CacheUpdater
|
import org.jetbrains.kotlin.ir.backend.js.ic.CacheUpdater
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
||||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
|
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.backend.js.transformers.irToJs.TranslationMode
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||||
@@ -346,7 +346,7 @@ abstract class AbstractJsPartialLinkageTestCase(val compilerType: CompilerType)
|
|||||||
relativeRequirePath = true
|
relativeRequirePath = true
|
||||||
)
|
)
|
||||||
|
|
||||||
return jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true).compilationOut
|
return jsExecutableProducer.buildExecutable(granularity = JsGenerationGranularity.PER_MODULE, outJsProgram = true).compilationOut
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrModuleFragment.exportName get() = "kotlin_${name.asStringStripSpecialMarkers()}"
|
private val IrModuleFragment.exportName get() = "kotlin_${name.asStringStripSpecialMarkers()}"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ private class TestArtifactCache(val moduleName: String, val binaryAsts: MutableM
|
|||||||
// TODO: It will be better to use saved fragments, but it doesn't work
|
// TODO: It will be better to use saved fragments, but it doesn't work
|
||||||
// Merger.merge() + JsNode.resolveTemporaryNames() modify fragments,
|
// Merger.merge() + JsNode.resolveTemporaryNames() modify fragments,
|
||||||
// therefore the sequential calls produce different results
|
// therefore the sequential calls produce different results
|
||||||
fragment = deserializeJsIrProgramFragment(it.value)
|
fragments = deserializeJsIrProgramFragment(it.value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ private fun extractJsFiles(
|
|||||||
fun copyInputJsFile(module: TestModule, inputJsFile: TestFile): String {
|
fun copyInputJsFile(module: TestModule, inputJsFile: TestFile): String {
|
||||||
val newName = module.getNameFor(inputJsFile, testServices)
|
val newName = module.getNameFor(inputJsFile, testServices)
|
||||||
val targetFile = File(outputDir, newName)
|
val targetFile = File(outputDir, newName)
|
||||||
targetFile.writeText(inputJsFile.originalContent)
|
targetFile.writeText(inputJsFile.originalContent.trim())
|
||||||
return targetFile.absolutePath
|
return targetFile.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ private fun extractJsFiles(
|
|||||||
return before to after
|
return before to after
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAdditionalFilePathes(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL_DEV): List<String> {
|
fun getAdditionalFilePaths(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL_DEV): List<String> {
|
||||||
return getAdditionalFiles(testServices, mode, true).map { it.absolutePath }
|
return getAdditionalFiles(testServices, mode, true).map { it.absolutePath }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ fun getAdditionalFiles(
|
|||||||
return additionalFiles
|
return additionalFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAdditionalMainFilePathes(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL_DEV): List<String> {
|
fun getAdditionalMainFilePaths(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL_DEV): List<String> {
|
||||||
return getAdditionalMainFiles(testServices, mode, shouldCopyFiles = true).map { it.absolutePath }
|
return getAdditionalMainFiles(testServices, mode, shouldCopyFiles = true).map { it.absolutePath }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,12 +157,13 @@ fun getAllFilesForRunner(
|
|||||||
|
|
||||||
val outputFile = getModeOutputFilePath(testServices, module, mode)
|
val outputFile = getModeOutputFilePath(testServices, module, mode)
|
||||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules, mode)
|
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules, mode)
|
||||||
val additionalFiles = getAdditionalFilePathes(testServices, mode)
|
val additionalFiles = getAdditionalFilePaths(testServices, mode)
|
||||||
val additionalMainFiles = getAdditionalMainFilePathes(testServices, mode)
|
val additionalMainFiles = getAdditionalMainFilePaths(testServices, mode)
|
||||||
|
|
||||||
outputs.dependencies.forEach { (moduleId, _) ->
|
outputs.dependencies.forEach { (moduleId, _) ->
|
||||||
paths += outputFile.augmentWithModuleName(moduleId)
|
paths += outputFile.augmentWithModuleName(moduleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
paths += outputFile
|
paths += outputFile
|
||||||
|
|
||||||
result[mode] = additionalFiles + inputJsFilesBefore + paths + commonFiles + additionalMainFiles + inputJsFilesAfter
|
result[mode] = additionalFiles + inputJsFilesBefore + paths + commonFiles + additionalMainFiles + inputJsFilesAfter
|
||||||
@@ -171,8 +172,8 @@ fun getAllFilesForRunner(
|
|||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules)
|
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules)
|
||||||
val additionalFiles = getAdditionalFilePathes(testServices)
|
val additionalFiles = getAdditionalFilePaths(testServices)
|
||||||
val additionalMainFiles = getAdditionalMainFilePathes(testServices)
|
val additionalMainFiles = getAdditionalMainFilePaths(testServices)
|
||||||
// Old BE
|
// Old BE
|
||||||
val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices)
|
val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices)
|
||||||
val dceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_PROD_MINIMIZED_NAMES)
|
val dceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_PROD_MINIMIZED_NAMES)
|
||||||
|
|||||||
+6
@@ -2421,6 +2421,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
|||||||
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("perFileExportedApi.kt")
|
||||||
|
public void testPerFileExportedApi() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/esModules/jsExport/perFileExportedApi.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("recursiveExport.kt")
|
@TestMetadata("recursiveExport.kt")
|
||||||
public void testRecursiveExport() throws Exception {
|
public void testRecursiveExport() throws Exception {
|
||||||
|
|||||||
+6
@@ -2527,6 +2527,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
|||||||
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("perFileExportedApi.kt")
|
||||||
|
public void testPerFileExportedApi() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/esModules/jsExport/perFileExportedApi.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("recursiveExport.kt")
|
@TestMetadata("recursiveExport.kt")
|
||||||
public void testRecursiveExport() throws Exception {
|
public void testRecursiveExport() throws Exception {
|
||||||
|
|||||||
+6
@@ -2421,6 +2421,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
runTest("js/js.translator/testData/box/esModules/jsExport/jsExportInClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("perFileExportedApi.kt")
|
||||||
|
public void testPerFileExportedApi() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/esModules/jsExport/perFileExportedApi.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("recursiveExport.kt")
|
@TestMetadata("recursiveExport.kt")
|
||||||
public void testRecursiveExport() throws Exception {
|
public void testRecursiveExport() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// DONT_TARGET_EXACT_BACKEND: JS
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// ES_MODULES
|
||||||
|
// SPLIT_PER_FILE
|
||||||
|
|
||||||
|
// FILE: base.kt
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
open class Base() {
|
||||||
|
var bar = "bar"
|
||||||
|
fun foo() = "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: a.kt
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
class A : Base() {
|
||||||
|
val pong = "pong"
|
||||||
|
fun ping() = "ping"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: function.kt
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
fun create() = A()
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
@JsModule("./perFileExportedApi.mjs")
|
||||||
|
external fun jsBox(): String
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val res = jsBox()
|
||||||
|
|
||||||
|
if (res != "bar¬ bar&foo&ping&pong") {
|
||||||
|
return "Fail: ${res}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { create } from "./perFileExportedApi-kotlin_main_v5/function.export.mjs";
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
let result = ""
|
||||||
|
const a = create()
|
||||||
|
|
||||||
|
result += a.bar
|
||||||
|
a.bar = "not bar"
|
||||||
|
result += `&${a.bar}`
|
||||||
|
result += `&${a.foo()}`
|
||||||
|
result += `&${a.ping()}`
|
||||||
|
result += `&${a.pong}`
|
||||||
|
|
||||||
|
return result
|
||||||
|
};
|
||||||
+5
-2
@@ -1,8 +1,11 @@
|
|||||||
MODULES: lib1, lib2, main
|
MODULES: lib1, lib2, main
|
||||||
|
|
||||||
STEP 0..1:
|
STEP 0:
|
||||||
libs: lib1, lib2, main
|
libs: lib1, lib2, main
|
||||||
dirty js: lib1, lib2, main
|
dirty js: lib1, lib2, main
|
||||||
|
STEP 1:
|
||||||
|
libs: lib1, lib2, main
|
||||||
|
dirty js: lib1, lib2
|
||||||
STEP 2:
|
STEP 2:
|
||||||
libs: lib1, lib2, main
|
libs: lib1, lib2, main
|
||||||
dirty js: lib1, lib2
|
dirty js: lib1, lib2
|
||||||
@@ -11,7 +14,7 @@ STEP 3:
|
|||||||
dirty js: lib2, main
|
dirty js: lib2, main
|
||||||
STEP 4:
|
STEP 4:
|
||||||
libs: lib1, lib2, main
|
libs: lib1, lib2, main
|
||||||
dirty js: lib1, lib2, main
|
dirty js: lib1, lib2
|
||||||
STEP 5..6:
|
STEP 5..6:
|
||||||
libs: lib1, lib2, main
|
libs: lib1, lib2, main
|
||||||
dirty js: lib2, main
|
dirty js: lib2, main
|
||||||
|
|||||||
@@ -210,6 +210,13 @@ public final annotation class ExperimentalJsExport : kotlin.Annotation {
|
|||||||
public constructor ExperimentalJsExport()
|
public constructor ExperimentalJsExport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.RequiresOptIn(level = Level.WARNING)
|
||||||
|
@kotlin.annotation.MustBeDocumented
|
||||||
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
|
public final annotation class ExperimentalJsFileName : kotlin.Annotation {
|
||||||
|
public constructor ExperimentalJsFileName()
|
||||||
|
}
|
||||||
|
|
||||||
public external object JSON {
|
public external object JSON {
|
||||||
public final fun <T> parse(text: kotlin.String): T
|
public final fun <T> parse(text: kotlin.String): T
|
||||||
|
|
||||||
@@ -266,6 +273,14 @@ public final annotation class JsExternalInheritorsOnly : kotlin.Annotation {
|
|||||||
public constructor JsExternalInheritorsOnly()
|
public constructor JsExternalInheritorsOnly()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE})
|
||||||
|
public final annotation class JsFileName : kotlin.Annotation {
|
||||||
|
public constructor JsFileName(name: kotlin.String)
|
||||||
|
|
||||||
|
public final val name: kotlin.String { get; }
|
||||||
|
}
|
||||||
|
|
||||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
|
||||||
public final annotation class JsModule : kotlin.Annotation {
|
public final annotation class JsModule : kotlin.Annotation {
|
||||||
@@ -275,7 +290,7 @@ public final annotation class JsModule : kotlin.Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER})
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER})
|
||||||
public final annotation class JsName : kotlin.Annotation {
|
public final annotation class JsName : kotlin.Annotation {
|
||||||
public constructor JsName(name: kotlin.String)
|
public constructor JsName(name: kotlin.String)
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,13 @@ public final annotation class ExperimentalJsExport : kotlin.Annotation {
|
|||||||
public constructor ExperimentalJsExport()
|
public constructor ExperimentalJsExport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.RequiresOptIn(level = Level.WARNING)
|
||||||
|
@kotlin.annotation.MustBeDocumented
|
||||||
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
|
public final annotation class ExperimentalJsFileName : kotlin.Annotation {
|
||||||
|
public constructor ExperimentalJsFileName()
|
||||||
|
}
|
||||||
|
|
||||||
public external object JSON {
|
public external object JSON {
|
||||||
public final fun <T> parse(text: kotlin.String): T
|
public final fun <T> parse(text: kotlin.String): T
|
||||||
|
|
||||||
@@ -265,6 +272,14 @@ public final annotation class JsExternalInheritorsOnly : kotlin.Annotation {
|
|||||||
public constructor JsExternalInheritorsOnly()
|
public constructor JsExternalInheritorsOnly()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE})
|
||||||
|
public final annotation class JsFileName : kotlin.Annotation {
|
||||||
|
public constructor JsFileName(name: kotlin.String)
|
||||||
|
|
||||||
|
public final val name: kotlin.String { get; }
|
||||||
|
}
|
||||||
|
|
||||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.FILE})
|
||||||
public final annotation class JsModule : kotlin.Annotation {
|
public final annotation class JsModule : kotlin.Annotation {
|
||||||
@@ -274,7 +289,7 @@ public final annotation class JsModule : kotlin.Annotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER})
|
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER})
|
||||||
public final annotation class JsName : kotlin.Annotation {
|
public final annotation class JsName : kotlin.Annotation {
|
||||||
public constructor JsName(name: kotlin.String)
|
public constructor JsName(name: kotlin.String)
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,34 @@ import kotlin.annotation.AnnotationTarget.*
|
|||||||
/**
|
/**
|
||||||
* Gives a declaration (a function, a property or a class) specific name in JavaScript.
|
* Gives a declaration (a function, a property or a class) specific name in JavaScript.
|
||||||
*/
|
*/
|
||||||
@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
@Target(FILE, CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
||||||
@OptionalExpectation
|
@OptionalExpectation
|
||||||
public expect annotation class JsName(val name: String)
|
public expect annotation class JsName(val name: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks experimental [JsFileName] annotation.
|
||||||
|
*
|
||||||
|
* Note that behavior of these annotations will likely be changed in the future.
|
||||||
|
*
|
||||||
|
* Usages of such annotations will be reported as warnings unless an explicit opt-in with
|
||||||
|
* the [OptIn] annotation, e.g. `@OptIn(ExperimentalJsFileName::class)`,
|
||||||
|
* or with the `-opt-in=kotlin.js.ExperimentalJsFileName` compiler option is given.
|
||||||
|
*/
|
||||||
|
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||||
|
@MustBeDocumented
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
public annotation class ExperimentalJsFileName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the name of the compiled file produced from the annotated source file instead of the default one.
|
||||||
|
*
|
||||||
|
* This annotation can be applied only to files and only when the compilation granularity is `PER_FILE`.
|
||||||
|
*/
|
||||||
|
@Target(FILE)
|
||||||
|
@OptionalExpectation
|
||||||
|
@ExperimentalJsFileName
|
||||||
|
public expect annotation class JsFileName(val name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks experimental JS export annotations.
|
* Marks experimental JS export annotations.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ val jsMainSources by task<Sync> {
|
|||||||
listOf(
|
listOf(
|
||||||
"libraries/stdlib/js/src/org.w3c/**",
|
"libraries/stdlib/js/src/org.w3c/**",
|
||||||
"libraries/stdlib/js/src/kotlin/char.kt",
|
"libraries/stdlib/js/src/kotlin/char.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/collections.kt",
|
"libraries/stdlib/js/src/kotlin/collectionJs.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/collections/**",
|
"libraries/stdlib/js/src/kotlin/collections/**",
|
||||||
"libraries/stdlib/js/src/kotlin/time/**",
|
"libraries/stdlib/js/src/kotlin/time/**",
|
||||||
"libraries/stdlib/js/src/kotlin/console.kt",
|
"libraries/stdlib/js/src/kotlin/console.kt",
|
||||||
@@ -90,7 +90,7 @@ val jsMainSources by task<Sync> {
|
|||||||
"libraries/stdlib/js/src/kotlin/json.kt",
|
"libraries/stdlib/js/src/kotlin/json.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/promise.kt",
|
"libraries/stdlib/js/src/kotlin/promise.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/regexp.kt",
|
"libraries/stdlib/js/src/kotlin/regexp.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/sequence.kt",
|
"libraries/stdlib/js/src/kotlin/sequenceJs.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/throwableExtensions.kt",
|
"libraries/stdlib/js/src/kotlin/throwableExtensions.kt",
|
||||||
"libraries/stdlib/js/src/kotlin/text/**",
|
"libraries/stdlib/js/src/kotlin/text/**",
|
||||||
"libraries/stdlib/js/src/kotlin/reflect/KTypeHelpers.kt",
|
"libraries/stdlib/js/src/kotlin/reflect/KTypeHelpers.kt",
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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 kotlin.js
|
||||||
|
|
||||||
|
internal fun setMetadataFor(
|
||||||
|
ctor: Ctor,
|
||||||
|
name: String?,
|
||||||
|
metadataConstructor: (name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?) -> Metadata,
|
||||||
|
parent: Ctor?,
|
||||||
|
interfaces: Array<dynamic>?,
|
||||||
|
associatedObjectKey: Number?,
|
||||||
|
associatedObjects: dynamic,
|
||||||
|
suspendArity: Array<Int>?
|
||||||
|
) {
|
||||||
|
if (parent != null) {
|
||||||
|
js("""
|
||||||
|
ctor.prototype = Object.create(parent.prototype)
|
||||||
|
ctor.prototype.constructor = ctor;
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
val metadata = metadataConstructor(name, associatedObjectKey, associatedObjects, suspendArity ?: js("[]"))
|
||||||
|
ctor.`$metadata$` = metadata
|
||||||
|
|
||||||
|
if (interfaces != null) {
|
||||||
|
val receiver = if (metadata.iid != null) ctor else ctor.prototype
|
||||||
|
receiver.`$imask$` = implement(interfaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There was a problem with per-module compilation (KT-55758) when the top-level state (iid) was reinitialized during stdlib module initialization
|
||||||
|
// As a result we miss already incremented iid and had the same iids in two different modules
|
||||||
|
// So, to keep the state consistent it was moved into the variable without initializer and function
|
||||||
|
@Suppress("MUST_BE_INITIALIZED")
|
||||||
|
private var iid: dynamic
|
||||||
|
|
||||||
|
private fun generateInterfaceId(): Int {
|
||||||
|
if (iid === VOID) {
|
||||||
|
iid = 0
|
||||||
|
}
|
||||||
|
iid = iid.unsafeCast<Int>() + 1
|
||||||
|
return iid.unsafeCast<Int>()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun interfaceMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||||
|
return createMetadata("interface", name, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun objectMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||||
|
return createMetadata("object", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||||
|
return createMetadata("class", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seems like we need to disable this check if variables are used inside js annotation
|
||||||
|
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")
|
||||||
|
private fun createMetadata(
|
||||||
|
kind: String,
|
||||||
|
name: String?,
|
||||||
|
associatedObjectKey: Number?,
|
||||||
|
associatedObjects: dynamic,
|
||||||
|
suspendArity: Array<Int>?,
|
||||||
|
iid: Int?
|
||||||
|
): Metadata {
|
||||||
|
val undef = VOID
|
||||||
|
return js("""({
|
||||||
|
kind: kind,
|
||||||
|
simpleName: name,
|
||||||
|
associatedObjectKey: associatedObjectKey,
|
||||||
|
associatedObjects: associatedObjects,
|
||||||
|
suspendArity: suspendArity,
|
||||||
|
${'$'}kClass$: undef,
|
||||||
|
iid: iid
|
||||||
|
})""")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal external interface Metadata {
|
||||||
|
val kind: String
|
||||||
|
// This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf())
|
||||||
|
// Can be pre-initialized or lazy initialized and then should be immutable
|
||||||
|
val simpleName: String?
|
||||||
|
val associatedObjectKey: Number?
|
||||||
|
val associatedObjects: dynamic
|
||||||
|
val suspendArity: Array<Int>?
|
||||||
|
val iid: Int?
|
||||||
|
|
||||||
|
var `$kClass$`: dynamic
|
||||||
|
|
||||||
|
var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2
|
||||||
|
}
|
||||||
@@ -5,95 +5,6 @@
|
|||||||
|
|
||||||
package kotlin.js
|
package kotlin.js
|
||||||
|
|
||||||
internal fun setMetadataFor(
|
|
||||||
ctor: Ctor,
|
|
||||||
name: String?,
|
|
||||||
metadataConstructor: (name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?) -> Metadata,
|
|
||||||
parent: Ctor?,
|
|
||||||
interfaces: Array<dynamic>?,
|
|
||||||
associatedObjectKey: Number?,
|
|
||||||
associatedObjects: dynamic,
|
|
||||||
suspendArity: Array<Int>?
|
|
||||||
) {
|
|
||||||
if (parent != null) {
|
|
||||||
js("""
|
|
||||||
ctor.prototype = Object.create(parent.prototype)
|
|
||||||
ctor.prototype.constructor = ctor;
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
|
|
||||||
val metadata = metadataConstructor(name, associatedObjectKey, associatedObjects, suspendArity ?: js("[]"))
|
|
||||||
ctor.`$metadata$` = metadata
|
|
||||||
|
|
||||||
if (interfaces != null) {
|
|
||||||
val receiver = if (metadata.iid != null) ctor else ctor.prototype
|
|
||||||
receiver.`$imask$` = implement(interfaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There was a problem with per-module compilation (KT-55758) when the top-level state (iid) was reinitialized during stdlib module initialization
|
|
||||||
// As a result we miss already incremented iid and had the same iids in two different modules
|
|
||||||
// So, to keep the state consistent it was moved into the next lateinit variable and function
|
|
||||||
private lateinit var iid: Any
|
|
||||||
|
|
||||||
private fun generateInterfaceId(): Int {
|
|
||||||
if (!::iid.isInitialized) {
|
|
||||||
iid = 0
|
|
||||||
}
|
|
||||||
iid = iid.unsafeCast<Int>() + 1
|
|
||||||
return iid.unsafeCast<Int>()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun interfaceMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
|
||||||
return createMetadata("interface", name, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun objectMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
|
||||||
return createMetadata("object", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
|
||||||
return createMetadata("class", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seems like we need to disable this check if variables are used inside js annotation
|
|
||||||
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")
|
|
||||||
private fun createMetadata(
|
|
||||||
kind: String,
|
|
||||||
name: String?,
|
|
||||||
associatedObjectKey: Number?,
|
|
||||||
associatedObjects: dynamic,
|
|
||||||
suspendArity: Array<Int>?,
|
|
||||||
iid: Int?
|
|
||||||
): Metadata {
|
|
||||||
val undef = VOID
|
|
||||||
return js("""({
|
|
||||||
kind: kind,
|
|
||||||
simpleName: name,
|
|
||||||
associatedObjectKey: associatedObjectKey,
|
|
||||||
associatedObjects: associatedObjects,
|
|
||||||
suspendArity: suspendArity,
|
|
||||||
${'$'}kClass$: undef,
|
|
||||||
iid: iid
|
|
||||||
})""")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal external interface Metadata {
|
|
||||||
val kind: String
|
|
||||||
// This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf())
|
|
||||||
// Can be pre-initialized or lazy initialized and then should be immutable
|
|
||||||
val simpleName: String?
|
|
||||||
val associatedObjectKey: Number?
|
|
||||||
val associatedObjects: dynamic
|
|
||||||
val suspendArity: Array<Int>?
|
|
||||||
val iid: Int?
|
|
||||||
|
|
||||||
var `$kClass$`: dynamic
|
|
||||||
|
|
||||||
var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2
|
|
||||||
}
|
|
||||||
|
|
||||||
internal external interface Ctor {
|
internal external interface Ctor {
|
||||||
var `$imask$`: BitMask?
|
var `$imask$`: BitMask?
|
||||||
var `$metadata$`: Metadata
|
var `$metadata$`: Metadata
|
||||||
@@ -128,22 +39,6 @@ internal fun calculateErrorInfo(proto: dynamic): Int {
|
|||||||
|
|
||||||
private fun getPrototypeOf(obj: dynamic) = JsObject.getPrototypeOf(obj)
|
private fun getPrototypeOf(obj: dynamic) = JsObject.getPrototypeOf(obj)
|
||||||
|
|
||||||
private fun searchForMetadata(obj: dynamic): Metadata? {
|
|
||||||
if (obj == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var metadata: Metadata? = obj.`$metadata$`
|
|
||||||
var currentObject = getPrototypeOf(obj)
|
|
||||||
|
|
||||||
while (metadata == null && currentObject != null) {
|
|
||||||
val currentConstructor = currentObject.constructor
|
|
||||||
metadata = currentConstructor.`$metadata$`
|
|
||||||
currentObject = getPrototypeOf(currentObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isInterfaceImpl(obj: dynamic, iface: Int): Boolean {
|
private fun isInterfaceImpl(obj: dynamic, iface: Int): Boolean {
|
||||||
val mask: BitMask = obj.`$imask$`.unsafeCast<BitMask?>() ?: return false
|
val mask: BitMask = obj.`$imask$`.unsafeCast<BitMask?>() ?: return false
|
||||||
return mask.isBitSet(iface)
|
return mask.isBitSet(iface)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package kotlin.js
|
|||||||
import kotlin.annotation.AnnotationTarget.*
|
import kotlin.annotation.AnnotationTarget.*
|
||||||
|
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
@Target(FILE, CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
||||||
internal external annotation class JsName(val name: String)
|
internal external annotation class JsName(val name: String)
|
||||||
|
|
||||||
internal external annotation class native
|
internal external annotation class native
|
||||||
|
|||||||
@@ -58,9 +58,18 @@ internal annotation class marker
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
@Target(FILE, CLASS, FUNCTION, PROPERTY, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER)
|
||||||
public actual annotation class JsName(actual val name: String)
|
public actual annotation class JsName(actual val name: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the name of the compiled file produced from the annotated source file instead of the default one.
|
||||||
|
*
|
||||||
|
* This annotation can be applied only to files and only when the compilation granularity is `PER_FILE`.
|
||||||
|
*/
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@Target(FILE)
|
||||||
|
public actual annotation class JsFileName(actual val name: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denotes an `external` declaration that must be imported from native JavaScript library.
|
* Denotes an `external` declaration that must be imported from native JavaScript library.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: Suppress will be until the next bootstrapping, after the bootstrapping the annotation should be replaced with [kotlin.js.JsFileName]
|
||||||
|
@file:Suppress("ANNOTATION_IS_NOT_APPLICABLE_TO_MULTIFILE_CLASSES")
|
||||||
|
@file:kotlin.js.JsName("CollectionsKt")
|
||||||
@file:kotlin.jvm.JvmMultifileClass
|
@file:kotlin.jvm.JvmMultifileClass
|
||||||
@file:kotlin.jvm.JvmName("CollectionsKt")
|
@file:kotlin.jvm.JvmName("CollectionsKt")
|
||||||
@file:OptIn(kotlin.experimental.ExperimentalTypeInference::class)
|
@file:OptIn(kotlin.experimental.ExperimentalTypeInference::class)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public actual annotation class JsExport {
|
|||||||
*/
|
*/
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@Target(
|
@Target(
|
||||||
|
AnnotationTarget.FILE,
|
||||||
AnnotationTarget.CLASS,
|
AnnotationTarget.CLASS,
|
||||||
AnnotationTarget.FUNCTION,
|
AnnotationTarget.FUNCTION,
|
||||||
AnnotationTarget.PROPERTY,
|
AnnotationTarget.PROPERTY,
|
||||||
|
|||||||
+3
@@ -3384,6 +3384,9 @@ public final class kotlin/jdk7/AutoCloseableKt {
|
|||||||
public abstract interface annotation class kotlin/js/ExperimentalJsExport : java/lang/annotation/Annotation {
|
public abstract interface annotation class kotlin/js/ExperimentalJsExport : java/lang/annotation/Annotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class kotlin/js/ExperimentalJsFileName : java/lang/annotation/Annotation {
|
||||||
|
}
|
||||||
|
|
||||||
public final class kotlin/jvm/JvmClassMappingKt {
|
public final class kotlin/jvm/JvmClassMappingKt {
|
||||||
public static final fun getAnnotationClass (Ljava/lang/annotation/Annotation;)Lkotlin/reflect/KClass;
|
public static final fun getAnnotationClass (Ljava/lang/annotation/Annotation;)Lkotlin/reflect/KClass;
|
||||||
public static final fun getJavaClass (Ljava/lang/Object;)Ljava/lang/Class;
|
public static final fun getJavaClass (Ljava/lang/Object;)Ljava/lang/Class;
|
||||||
|
|||||||
Reference in New Issue
Block a user