JS IR: per-module .js generation support
This commit is contained in:
+3
@@ -135,6 +135,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var irModuleName: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(value = "-Xir-per-module", description = "Splits generated .js per-module")
|
||||
var irPerModule: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xinclude",
|
||||
valueDescription = "<path>",
|
||||
|
||||
@@ -229,14 +229,19 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
mainArguments = mainCallArguments,
|
||||
generateFullJs = !arguments.irDce,
|
||||
generateDceJs = arguments.irDce,
|
||||
dceDriven = arguments.irDceDriven
|
||||
dceDriven = arguments.irDceDriven,
|
||||
multiModule = arguments.irPerModule,
|
||||
relativeRequirePath = true
|
||||
)
|
||||
} catch (e: JsIrCompilationError) {
|
||||
return COMPILATION_ERROR
|
||||
}
|
||||
|
||||
val jsCode = if (arguments.irDce && !arguments.irDceDriven) compiledModule.dceJsCode!! else compiledModule.jsCode!!
|
||||
outputFile.writeText(jsCode)
|
||||
outputFile.writeText(jsCode.mainModule)
|
||||
jsCode.dependencies.forEach { (name, content) ->
|
||||
outputFile.resolveSibling("$name.js").writeText(content)
|
||||
}
|
||||
if (arguments.generateDts) {
|
||||
val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!!
|
||||
dtsFile.writeText(compiledModule.tsDefinitions ?: error("No ts definitions"))
|
||||
|
||||
@@ -8,10 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js
|
||||
import org.jetbrains.kotlin.backend.common.ir.isMemberOfOpenClass
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.associatedObject
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isAssociatedObjectAnnotatedAnnotation
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
@@ -26,11 +23,10 @@ import java.util.*
|
||||
|
||||
fun eliminateDeadDeclarations(
|
||||
modules: Iterable<IrModuleFragment>,
|
||||
context: JsIrBackendContext,
|
||||
mainFunction: IrSimpleFunction?
|
||||
context: JsIrBackendContext
|
||||
) {
|
||||
|
||||
val allRoots = stageController.withInitialIr { buildRoots(modules, context, mainFunction) }
|
||||
val allRoots = stageController.withInitialIr { buildRoots(modules, context) }
|
||||
|
||||
val usefulDeclarations = usefulDeclarations(allRoots, context)
|
||||
|
||||
@@ -43,7 +39,7 @@ private fun IrField.isConstant(): Boolean {
|
||||
return correspondingPropertySymbol?.owner?.isConst ?: false
|
||||
}
|
||||
|
||||
private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable<IrDeclaration> {
|
||||
private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackendContext): Iterable<IrDeclaration> {
|
||||
val rootDeclarations =
|
||||
(modules.flatMap { it.files } + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file ->
|
||||
file.declarations.flatMap { if (it is IrProperty) listOfNotNull(it.backingField, it.getter, it.setter) else listOf(it) }
|
||||
@@ -56,9 +52,9 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackend
|
||||
}.filter { !(it is IrField && it.isConstant() && !it.isExported(context)) }
|
||||
}
|
||||
|
||||
if (context.hasTests) rootDeclarations += context.testContainer
|
||||
rootDeclarations += context.testRoots.values
|
||||
|
||||
if (mainFunction != null) {
|
||||
JsMainFunctionDetector.getMainFunctionOrNull(modules.last())?.let { mainFunction ->
|
||||
rootDeclarations += mainFunction
|
||||
if (mainFunction.isSuspend) {
|
||||
rootDeclarations += context.coroutineEmptyContinuation.owner
|
||||
|
||||
+19
-11
@@ -92,8 +92,12 @@ class JsIrBackendContext(
|
||||
|
||||
private val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal"))
|
||||
val implicitDeclarationFile = run {
|
||||
IrFileImpl(object : SourceManager.FileEntry {
|
||||
override val name = "<implicitDeclarations>"
|
||||
syntheticFile("implicitDeclarations", irModuleFragment)
|
||||
}
|
||||
|
||||
private fun syntheticFile(name: String, module: IrModuleFragment): IrFile {
|
||||
return IrFileImpl(object : SourceManager.FileEntry {
|
||||
override val name = "<$name>"
|
||||
override val maxOffset = UNDEFINED_OFFSET
|
||||
|
||||
override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int) =
|
||||
@@ -110,20 +114,24 @@ class JsIrBackendContext(
|
||||
override fun getLineNumber(offset: Int) = UNDEFINED_OFFSET
|
||||
override fun getColumnNumber(offset: Int) = UNDEFINED_OFFSET
|
||||
}, internalPackageFragmentDescriptor).also {
|
||||
irModuleFragment.files += it
|
||||
module.files += it
|
||||
}
|
||||
}
|
||||
|
||||
private var testContainerField: IrSimpleFunction? = null
|
||||
private val testContainerFuns = mutableMapOf<IrModuleFragment, IrSimpleFunction>()
|
||||
|
||||
val hasTests get() = testContainerField != null
|
||||
|
||||
val testContainer: IrSimpleFunction
|
||||
get() = testContainerField ?: JsIrBuilder.buildFunction("test fun", irBuiltIns.unitType, implicitDeclarationFile).apply {
|
||||
body = JsIrBuilder.buildBlockBody(emptyList())
|
||||
testContainerField = this
|
||||
implicitDeclarationFile.declarations += this
|
||||
fun createTestContainerFun(module: IrModuleFragment): IrSimpleFunction {
|
||||
return testContainerFuns.getOrPut(module) {
|
||||
val file = syntheticFile("tests", module)
|
||||
JsIrBuilder.buildFunction("test fun", irBuiltIns.unitType, file).apply {
|
||||
body = JsIrBuilder.buildBlockBody(emptyList())
|
||||
file.declarations += this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val testRoots: Map<IrModuleFragment, IrSimpleFunction>
|
||||
get() = testContainerFuns
|
||||
|
||||
override val mapping = JsMapping()
|
||||
override val declarationFactory = JsDeclarationFactory(mapping)
|
||||
|
||||
@@ -618,12 +618,6 @@ private val callsLoweringPhase = makeBodyLoweringPhase(
|
||||
description = "Handle intrinsics"
|
||||
)
|
||||
|
||||
private val testGenerationPhase = makeJsModulePhase(
|
||||
::TestGenerator,
|
||||
name = "TestGenerationLowering",
|
||||
description = "Generate invocations to kotlin.test suite and test functions"
|
||||
).toModuleLowering()
|
||||
|
||||
private val staticMembersLoweringPhase = makeDeclarationTransformerPhase(
|
||||
::StaticMembersLowering,
|
||||
name = "StaticMembersLowering",
|
||||
@@ -657,7 +651,6 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
||||
val loweringList = listOf<Lowering>(
|
||||
scriptRemoveReceiverLowering,
|
||||
validateIrBeforeLowering,
|
||||
testGenerationPhase,
|
||||
expectDeclarationsRemovingPhase,
|
||||
stripTypeAliasDeclarationsPhase,
|
||||
arrayConstructorPhase,
|
||||
|
||||
@@ -14,12 +14,10 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.generateTests
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.StageController
|
||||
import org.jetbrains.kotlin.ir.declarations.stageController
|
||||
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
|
||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||
import org.jetbrains.kotlin.ir.util.noUnboundLeft
|
||||
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||
@@ -27,11 +25,13 @@ import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
class CompilerResult(
|
||||
val jsCode: String?,
|
||||
val dceJsCode: String?,
|
||||
val jsCode: JsCode?,
|
||||
val dceJsCode: JsCode?,
|
||||
val tsDefinitions: String? = null
|
||||
)
|
||||
|
||||
class JsCode(val mainModule: String, val dependencies: Iterable<Pair<String, String>> = emptyList())
|
||||
|
||||
fun compile(
|
||||
project: Project,
|
||||
mainModule: MainModule,
|
||||
@@ -45,7 +45,9 @@ fun compile(
|
||||
generateFullJs: Boolean = true,
|
||||
generateDceJs: Boolean = false,
|
||||
dceDriven: Boolean = false,
|
||||
es6mode: Boolean = false
|
||||
es6mode: Boolean = false,
|
||||
multiModule: Boolean = false,
|
||||
relativeRequirePath: Boolean = false
|
||||
): CompilerResult {
|
||||
stageController = object : StageController {}
|
||||
|
||||
@@ -54,19 +56,17 @@ fun compile(
|
||||
|
||||
val moduleDescriptor = moduleFragment.descriptor
|
||||
|
||||
val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(moduleFragment)
|
||||
|
||||
val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration, es6mode = es6mode)
|
||||
|
||||
// Load declarations referenced during `context` initialization
|
||||
val irProviders = listOf(deserializer)
|
||||
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings).generateUnboundSymbolsAsDependencies()
|
||||
|
||||
val allModules = when (mainModule) {
|
||||
is MainModule.SourceFiles -> dependencyModules + listOf(moduleFragment)
|
||||
is MainModule.Klib -> dependencyModules
|
||||
}
|
||||
|
||||
val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, allModules.first(), exportedDeclarations, configuration, es6mode = es6mode)
|
||||
|
||||
// Load declarations referenced during `context` initialization
|
||||
val irProviders = listOf(deserializer)
|
||||
ExternalDependenciesGenerator(symbolTable, irProviders, configuration.languageVersionSettings).generateUnboundSymbolsAsDependencies()
|
||||
|
||||
deserializer.postProcess()
|
||||
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
|
||||
|
||||
@@ -74,32 +74,42 @@ fun compile(
|
||||
moveBodilessDeclarationsToSeparatePlace(context, module)
|
||||
}
|
||||
|
||||
// TODO should be done incrementally
|
||||
generateTests(context, allModules.last())
|
||||
|
||||
if (dceDriven) {
|
||||
|
||||
// TODO we should only generate tests for the current module
|
||||
// TODO should be done incrementally
|
||||
allModules.forEach { module ->
|
||||
generateTests(context, module)
|
||||
}
|
||||
|
||||
val controller = MutableController(context, pirLowerings)
|
||||
stageController = controller
|
||||
|
||||
controller.currentStage = controller.lowerings.size + 1
|
||||
|
||||
eliminateDeadDeclarations(allModules, context, mainFunction)
|
||||
eliminateDeadDeclarations(allModules, context)
|
||||
|
||||
// TODO investigate whether this is needed anymore
|
||||
stageController = object : StageController {
|
||||
override val currentStage: Int = controller.currentStage
|
||||
}
|
||||
|
||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
||||
return transformer.generateModule(allModules, fullJs = true, dceJs = false)
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
context,
|
||||
mainArguments,
|
||||
fullJs = true,
|
||||
dceJs = false,
|
||||
multiModule = multiModule,
|
||||
relativeRequirePath = relativeRequirePath
|
||||
)
|
||||
return transformer.generateModule(allModules)
|
||||
} else {
|
||||
jsPhases.invokeToplevel(phaseConfig, context, allModules)
|
||||
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
|
||||
return transformer.generateModule(allModules, generateFullJs, generateDceJs)
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
context,
|
||||
mainArguments,
|
||||
fullJs = generateFullJs,
|
||||
dceJs = generateDceJs,
|
||||
multiModule = multiModule,
|
||||
relativeRequirePath = relativeRequirePath
|
||||
)
|
||||
return transformer.generateModule(allModules)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +121,6 @@ fun generateJsCode(
|
||||
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
|
||||
jsPhases.invokeToplevel(PhaseConfig(jsPhases), context, listOf(moduleFragment))
|
||||
|
||||
val transformer = IrModuleToJsTransformer(context, null, null, true, nameTables)
|
||||
return transformer.generateModule(listOf(moduleFragment)).jsCode!!
|
||||
val transformer = IrModuleToJsTransformer(context, null, true, nameTables)
|
||||
return transformer.generateModule(listOf(moduleFragment)).jsCode!!.mainModule
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ package org.jetbrains.kotlin.ir.backend.js.export
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
sealed class ExportedDeclaration
|
||||
|
||||
data class ExportedModule(
|
||||
val name: String,
|
||||
val moduleKind: ModuleKind,
|
||||
val declarations: List<ExportedDeclaration>
|
||||
)
|
||||
|
||||
|
||||
+4
-3
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
|
||||
@@ -22,11 +21,12 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
private fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||
val namespaceFqName = file.fqName
|
||||
val exports = file.declarations.flatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
||||
return when {
|
||||
@@ -38,7 +38,8 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
fun generateExport(modules: Iterable<IrModuleFragment>): ExportedModule =
|
||||
ExportedModule(
|
||||
sanitizeName(context.configuration[CommonConfigurationKeys.MODULE_NAME]!!),
|
||||
context.configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
context.configuration[JSConfigurationKeys.MODULE_KIND]!!,
|
||||
(context.externalPackageFragment.values + modules.flatMap { it.files }).flatMap {
|
||||
generateExport(it)
|
||||
}
|
||||
|
||||
+3
-1
@@ -5,12 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
|
||||
// TODO: Support module kinds other than plain
|
||||
|
||||
fun ExportedModule.toTypeScript(): String {
|
||||
val prefix = " type Nullable<T> = T | null | undefined\n"
|
||||
val body = declarations.joinToString("\n") { it.toTypeScript(" ") }
|
||||
return "declare namespace $name {\n$prefix$body\n}\n"
|
||||
return "declare namespace ${sanitizeName(name)} {\n$prefix$body\n}\n"
|
||||
}
|
||||
|
||||
fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||
|
||||
+6
-4
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class PropertyReferenceLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
|
||||
|
||||
private val implicitDeclarationFile = context.implicitDeclarationFile
|
||||
private val referenceBuilderSymbol = context.kpropertyBuilder
|
||||
private val localDelegateBuilderSymbol = context.klocalDelegateBuilder
|
||||
private val jsClassSymbol = context.intrinsics.jsClass
|
||||
@@ -40,7 +40,11 @@ class PropertyReferenceLowering(private val context: JsIrBackendContext) : BodyL
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
newDeclarations.clear()
|
||||
irBody.transformChildrenVoid(PropertyReferenceTransformer())
|
||||
implicitDeclarationFile.declarations.addAll(newDeclarations)
|
||||
if (!newDeclarations.isEmpty()) {
|
||||
val file = container.file
|
||||
newDeclarations.forEach { it.parent = file }
|
||||
file.declarations.addAll(newDeclarations)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class PropertyReferenceTransformer : IrElementTransformerVoid() {
|
||||
@@ -55,8 +59,6 @@ class PropertyReferenceLowering(private val context: JsIrBackendContext) : BodyL
|
||||
name = Name.identifier("${property.name.asString()}\$factory")
|
||||
}
|
||||
|
||||
factoryDeclaration.parent = implicitDeclarationFile
|
||||
|
||||
val boundArguments = listOfNotNull(reference.dispatchReceiver, reference.extensionReceiver)
|
||||
|
||||
val valueParameters = ArrayList<IrValueParameter>(boundArguments.size)
|
||||
|
||||
+4
-4
@@ -22,14 +22,14 @@ import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
fun generateTests(context: JsIrBackendContext, moduleFragment: IrModuleFragment) {
|
||||
val generator = TestGenerator(context)
|
||||
val generator = TestGenerator(context) { context.createTestContainerFun(moduleFragment) }
|
||||
|
||||
moduleFragment.files.forEach {
|
||||
moduleFragment.files.toList().forEach {
|
||||
generator.lower(it)
|
||||
}
|
||||
}
|
||||
|
||||
class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
class TestGenerator(val context: JsIrBackendContext, val testContainerFactory: () -> IrSimpleFunction) : FileLoweringPass {
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
irFile.declarations.forEach {
|
||||
@@ -44,7 +44,7 @@ class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
private val packageSuites = mutableMapOf<FqName, FunctionWithBody>()
|
||||
|
||||
private fun suiteForPackage(fqName: FqName) = packageSuites.getOrPut(fqName) {
|
||||
context.suiteFun!!.createInvocation(fqName.asString(), context.testContainer)
|
||||
context.suiteFun!!.createInvocation(fqName.asString(), testContainerFactory())
|
||||
}
|
||||
|
||||
private data class FunctionWithBody(val function: IrSimpleFunction, val body: IrBlockBody)
|
||||
|
||||
+144
-31
@@ -5,8 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCode
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.eliminateDeadDeclarations
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelGenerator
|
||||
@@ -26,16 +26,17 @@ import org.jetbrains.kotlin.utils.DFS
|
||||
|
||||
class IrModuleToJsTransformer(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val mainFunction: IrSimpleFunction?,
|
||||
private val mainArguments: List<String>?,
|
||||
private val generateScriptModule: Boolean = false,
|
||||
var namer: NameTables = NameTables(emptyList())
|
||||
var namer: NameTables = NameTables(emptyList()),
|
||||
private val fullJs: Boolean = true,
|
||||
private val dceJs: Boolean = false,
|
||||
private val multiModule: Boolean = false,
|
||||
private val relativeRequirePath: Boolean = false
|
||||
) {
|
||||
val moduleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
private val generateRegionComments = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_REGION_COMMENTS)
|
||||
|
||||
fun generateModule(modules: Iterable<IrModuleFragment>, fullJs: Boolean = true, dceJs: Boolean = false): CompilerResult {
|
||||
fun generateModule(modules: Iterable<IrModuleFragment>): CompilerResult {
|
||||
val additionalPackages = with(backendContext) {
|
||||
externalPackageFragment.values + listOf(
|
||||
bodilessBuiltInsPackageFragment,
|
||||
@@ -50,6 +51,10 @@ class IrModuleToJsTransformer(
|
||||
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
|
||||
}
|
||||
|
||||
if (multiModule) {
|
||||
breakCrossModuleFieldAccess(backendContext, modules)
|
||||
}
|
||||
|
||||
modules.forEach { module ->
|
||||
namer.merge(module.files, additionalPackages)
|
||||
}
|
||||
@@ -57,7 +62,7 @@ class IrModuleToJsTransformer(
|
||||
val jsCode = if (fullJs) generateWrappedModuleBody(modules, exportedModule, namer) else null
|
||||
|
||||
val dceJsCode = if (dceJs) {
|
||||
eliminateDeadDeclarations(modules, backendContext, mainFunction)
|
||||
eliminateDeadDeclarations(modules, backendContext)
|
||||
// Use a fresh namer for DCE so that we could compare the result with DCE-driven
|
||||
// TODO: is this mode relevant for scripting? If yes, refactor so that the external name tables are used here when needed.
|
||||
val namer = NameTables(emptyList())
|
||||
@@ -68,11 +73,63 @@ class IrModuleToJsTransformer(
|
||||
return CompilerResult(jsCode, dceJsCode, dts)
|
||||
}
|
||||
|
||||
private fun generateWrappedModuleBody(modules: Iterable<IrModuleFragment>, exportedModule: ExportedModule, namer: NameTables): String {
|
||||
val program = JsProgram()
|
||||
private fun generateWrappedModuleBody(modules: Iterable<IrModuleFragment>, exportedModule: ExportedModule, namer: NameTables): JsCode {
|
||||
if (multiModule) {
|
||||
|
||||
val nameGenerator = IrNamerImpl(
|
||||
newNameTables = namer
|
||||
val refInfo = buildCrossModuleReferenceInfo(modules)
|
||||
|
||||
val rM = modules.reversed()
|
||||
|
||||
val main = rM.first()
|
||||
val others = rM.drop(1)
|
||||
|
||||
val mainModule = generateWrappedModuleBody2(
|
||||
listOf(main),
|
||||
others,
|
||||
exportedModule,
|
||||
namer,
|
||||
refInfo
|
||||
)
|
||||
|
||||
val dependencies = others.mapIndexed { index, module ->
|
||||
val moduleName = sanitizeName(module.safeName)
|
||||
|
||||
val exportedDeclarations = ExportModelGenerator(backendContext).let { module.files.flatMap { file -> it.generateExport(file) } }
|
||||
|
||||
moduleName to generateWrappedModuleBody2(
|
||||
listOf(module),
|
||||
others.drop(index + 1),
|
||||
ExportedModule(moduleName, exportedModule.moduleKind, exportedDeclarations),
|
||||
namer,
|
||||
refInfo
|
||||
)
|
||||
}.reversed()
|
||||
|
||||
return JsCode(mainModule, dependencies)
|
||||
} else {
|
||||
return JsCode(
|
||||
generateWrappedModuleBody2(
|
||||
modules,
|
||||
emptyList(),
|
||||
exportedModule,
|
||||
namer,
|
||||
EmptyCrossModuleReferenceInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateWrappedModuleBody2(
|
||||
modules: Iterable<IrModuleFragment>,
|
||||
dependencies: Iterable<IrModuleFragment>,
|
||||
exportedModule: ExportedModule,
|
||||
namer: NameTables,
|
||||
refInfo: CrossModuleReferenceInfo
|
||||
): String {
|
||||
|
||||
val nameGenerator = refInfo.withReferenceTracking(
|
||||
IrNamerImpl(newNameTables = namer),
|
||||
modules
|
||||
)
|
||||
val staticContext = JsStaticContext(
|
||||
backendContext = backendContext,
|
||||
@@ -83,9 +140,6 @@ class IrModuleToJsTransformer(
|
||||
staticContext = staticContext
|
||||
)
|
||||
|
||||
val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function")
|
||||
val internalModuleName = JsName("_")
|
||||
|
||||
val (importStatements, importedJsModules) =
|
||||
generateImportStatements(
|
||||
getNameForExternalDeclaration = { rootContext.getNameForStaticDeclaration(it) },
|
||||
@@ -93,40 +147,96 @@ class IrModuleToJsTransformer(
|
||||
)
|
||||
|
||||
val moduleBody = generateModuleBody(modules, rootContext)
|
||||
val exportStatements = ExportModelToJsStatements(internalModuleName, namer)
|
||||
.generateModuleExport(exportedModule)
|
||||
|
||||
val internalModuleName = JsName("_")
|
||||
val exportStatements = ExportModelToJsStatements(internalModuleName, namer).generateModuleExport(exportedModule)
|
||||
|
||||
val callToMain = generateCallToMain(modules, rootContext)
|
||||
|
||||
val (crossModuleImports, importedKotlinModules) = generateCrossModuleImports(nameGenerator, modules, dependencies, { JsName(sanitizeName(it)) })
|
||||
val crossModuleExports = generateCrossModuleExports(modules, refInfo, internalModuleName)
|
||||
|
||||
val program = JsProgram()
|
||||
if (generateScriptModule) {
|
||||
with(program.globalBlock) {
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
||||
statements += moduleBody
|
||||
statements.addWithComment("block: exports", exportStatements)
|
||||
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
||||
}
|
||||
} else {
|
||||
with(rootFunction) {
|
||||
val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function").apply {
|
||||
parameters += JsParameter(internalModuleName)
|
||||
parameters += importedJsModules.map { JsParameter(it.internalName) }
|
||||
parameters += (importedJsModules + importedKotlinModules).map { JsParameter(it.internalName) }
|
||||
with(body) {
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
statements.addWithComment("block: imports", importStatements + crossModuleImports)
|
||||
statements += moduleBody
|
||||
statements.addWithComment("block: exports", exportStatements)
|
||||
statements += generateCallToMain(rootContext)
|
||||
statements.addWithComment("block: exports", exportStatements + crossModuleExports)
|
||||
statements += callToMain
|
||||
statements += JsReturn(internalModuleName.makeRef())
|
||||
}
|
||||
}
|
||||
|
||||
program.globalBlock.statements += ModuleWrapperTranslation.wrap(
|
||||
moduleName,
|
||||
exportedModule.name,
|
||||
rootFunction,
|
||||
importedJsModules,
|
||||
importedJsModules + importedKotlinModules,
|
||||
program,
|
||||
kind = moduleKind
|
||||
kind = exportedModule.moduleKind
|
||||
)
|
||||
}
|
||||
|
||||
return program.toString()
|
||||
}
|
||||
|
||||
private fun generateCrossModuleImports(
|
||||
namerWithImports: IrNamerWithImports,
|
||||
currentModules: Iterable<IrModuleFragment>,
|
||||
allowedDependencies: Iterable<IrModuleFragment>,
|
||||
declareFreshGlobal: (String) -> JsName
|
||||
): Pair<MutableList<JsStatement>, List<JsImportedModule>> {
|
||||
val imports = mutableListOf<JsStatement>()
|
||||
val modules = mutableListOf<JsImportedModule>()
|
||||
|
||||
namerWithImports.imports().forEach { (module, names) ->
|
||||
check(module in allowedDependencies) {
|
||||
val deps = if (names.size > 10) "[${names.take(10).joinToString()}, ...]" else "$names"
|
||||
"Module ${currentModules.map { it.name.asString() }} depend on module ${module.name.asString()} via $deps"
|
||||
}
|
||||
|
||||
val moduleName = declareFreshGlobal(module.safeName)
|
||||
modules += JsImportedModule(moduleName.ident, moduleName, moduleName.makeRef(), relativeRequirePath)
|
||||
|
||||
names.forEach {
|
||||
imports += JsVars(JsVars.JsVar(JsName(it), JsNameRef(it, JsNameRef("\$crossModule\$", moduleName.makeRef()))))
|
||||
}
|
||||
}
|
||||
|
||||
return imports to modules
|
||||
}
|
||||
|
||||
private fun generateCrossModuleExports(
|
||||
modules: Iterable<IrModuleFragment>,
|
||||
refInfo: CrossModuleReferenceInfo,
|
||||
internalModuleName: JsName
|
||||
): List<JsStatement> {
|
||||
return modules.flatMap {
|
||||
refInfo.exports(it).map {
|
||||
jsAssignment(
|
||||
JsNameRef(it, JsNameRef("\$crossModule\$", internalModuleName.makeRef())),
|
||||
JsNameRef(it)
|
||||
).makeStmt()
|
||||
}
|
||||
}.let {
|
||||
if (!it.isEmpty()) {
|
||||
val createExportBlock = jsAssignment(
|
||||
JsNameRef("\$crossModule\$", internalModuleName.makeRef()),
|
||||
JsAstUtils.or(JsNameRef("\$crossModule\$", internalModuleName.makeRef()), JsObjectLiteral())
|
||||
).makeStmt()
|
||||
return listOf(createExportBlock) + it
|
||||
} else it
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateModuleBody(modules: Iterable<IrModuleFragment>, context: JsGenerationContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>().also {
|
||||
if (!generateScriptModule) it += JsStringLiteral("use strict").makeStmt()
|
||||
@@ -176,10 +286,12 @@ class IrModuleToJsTransformer(
|
||||
statements.addWithComment("block: post-declaration", postDeclarationBlock.statements)
|
||||
statements.addWithComment("block: init", context.staticContext.initializerBlock.statements)
|
||||
|
||||
if (backendContext.hasTests) {
|
||||
statements.startRegion("block: tests")
|
||||
statements += JsInvocation(context.getNameForStaticFunction(backendContext.testContainer).makeRef()).makeStmt()
|
||||
statements.endRegion()
|
||||
modules.forEach {
|
||||
backendContext.testRoots[it]?.let { testContainer ->
|
||||
statements.startRegion("block: tests")
|
||||
statements += JsInvocation(context.getNameForStaticFunction(testContainer).makeRef()).makeStmt()
|
||||
statements.endRegion()
|
||||
}
|
||||
}
|
||||
|
||||
return statements
|
||||
@@ -198,8 +310,9 @@ class IrModuleToJsTransformer(
|
||||
return listOfNotNull(mainArgumentsArray, continuation)
|
||||
}
|
||||
|
||||
private fun generateCallToMain(rootContext: JsGenerationContext): List<JsStatement> {
|
||||
private fun generateCallToMain(modules: Iterable<IrModuleFragment>, rootContext: JsGenerationContext): List<JsStatement> {
|
||||
if (mainArguments == null) return emptyList() // in case `NO_MAIN` and `main(..)` exists
|
||||
val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(modules.last())
|
||||
return mainFunction?.let {
|
||||
val jsName = rootContext.getNameForStaticFunction(it)
|
||||
listOf(JsInvocation(jsName.makeRef(), generateMainArguments(it, rootContext)).makeStmt())
|
||||
|
||||
+2
-2
@@ -77,7 +77,7 @@ object ModuleWrapperTranslation {
|
||||
val scope = program.scope
|
||||
val defineName = scope.declareName("define")
|
||||
val invocationArgs = listOf(
|
||||
JsArrayLiteral(listOf(JsStringLiteral("exports")) + importedModules.map { JsStringLiteral(it.externalName) }),
|
||||
JsArrayLiteral(listOf(JsStringLiteral("exports")) + importedModules.map { JsStringLiteral(it.requireName) }),
|
||||
function
|
||||
)
|
||||
|
||||
@@ -94,7 +94,7 @@ object ModuleWrapperTranslation {
|
||||
val moduleName = scope.declareName("module")
|
||||
val requireName = scope.declareName("require")
|
||||
|
||||
val invocationArgs = importedModules.map { JsInvocation(requireName.makeRef(), JsStringLiteral(it.externalName)) }
|
||||
val invocationArgs = importedModules.map { JsInvocation(requireName.makeRef(), JsStringLiteral(it.requireName)) }
|
||||
val invocation = JsInvocation(function, listOf(JsNameRef("exports", moduleName.makeRef())) + invocationArgs)
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.IrNamer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetField
|
||||
import org.jetbrains.kotlin.ir.expressions.IrSetField
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface CrossModuleReferenceInfo {
|
||||
fun exports(module: IrModuleFragment): List<String>
|
||||
|
||||
fun withReferenceTracking(namer: IrNamer, excludedModules: Iterable<IrModuleFragment>): IrNamerWithImports
|
||||
}
|
||||
|
||||
interface IrNamerWithImports : IrNamer {
|
||||
fun imports(): List<Pair<IrModuleFragment, List<String>>>
|
||||
}
|
||||
|
||||
object EmptyCrossModuleReferenceInfo : CrossModuleReferenceInfo {
|
||||
override fun exports(module: IrModuleFragment): List<String> = emptyList()
|
||||
|
||||
override fun withReferenceTracking(namer: IrNamer, excludedModules: Iterable<IrModuleFragment>): IrNamerWithImports =
|
||||
object : IrNamerWithImports, IrNamer by namer {
|
||||
override fun imports() = emptyList<Pair<IrModuleFragment, List<String>>>()
|
||||
}
|
||||
}
|
||||
|
||||
private class CrossModuleReferenceInfoImpl(val topLevelDeclarationToModule: Map<IrDeclaration, IrModuleFragment>) :
|
||||
CrossModuleReferenceInfo {
|
||||
|
||||
private val exportedNames: MutableMap<IrModuleFragment, MutableSet<String>> = mutableMapOf()
|
||||
|
||||
override fun exports(module: IrModuleFragment): List<String> {
|
||||
return exportedNames[module]?.toList() ?: emptyList()
|
||||
}
|
||||
|
||||
override fun withReferenceTracking(namer: IrNamer, excludedModules: Iterable<IrModuleFragment>): IrNamerWithImports {
|
||||
val excludedModulesSet = excludedModules.toSet()
|
||||
|
||||
return object : IrNamerWithImports, IrNamer by namer {
|
||||
|
||||
override fun imports(): List<Pair<IrModuleFragment, List<String>>> {
|
||||
return importedNames.entries.map { (module, names) -> module to names.toList() }
|
||||
}
|
||||
|
||||
private val importedNames: MutableMap<IrModuleFragment, MutableSet<String>> = mutableMapOf()
|
||||
|
||||
private fun JsName.track(d: IrDeclaration): JsName {
|
||||
topLevelDeclarationToModule[d]?.let { module ->
|
||||
if (module !in excludedModulesSet) {
|
||||
importedNames.getOrPut(module) { mutableSetOf() } += this.ident
|
||||
exportedNames.getOrPut(module) { mutableSetOf() } += this.ident
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getNameForConstructor(constructor: IrConstructor): JsName {
|
||||
return namer.getNameForConstructor(constructor).track(constructor.constructedClass)
|
||||
}
|
||||
|
||||
override fun getNameForField(field: IrField): JsName {
|
||||
return namer.getNameForField(field).track(field)
|
||||
}
|
||||
|
||||
override fun getNameForClass(klass: IrClass): JsName {
|
||||
return namer.getNameForClass(klass).track(klass)
|
||||
}
|
||||
|
||||
override fun getNameForStaticFunction(function: IrSimpleFunction): JsName {
|
||||
return namer.getNameForStaticFunction(function).track(function)
|
||||
}
|
||||
|
||||
override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName {
|
||||
return namer.getNameForStaticDeclaration(declaration).track(declaration)
|
||||
}
|
||||
|
||||
override fun getNameForProperty(property: IrProperty): JsName {
|
||||
return namer.getNameForProperty(property).track(property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun buildCrossModuleReferenceInfo(modules: Iterable<IrModuleFragment>): CrossModuleReferenceInfo {
|
||||
val map = mutableMapOf<IrDeclaration, IrModuleFragment>()
|
||||
|
||||
modules.forEach { module ->
|
||||
module.files.forEach { file ->
|
||||
file.declarations.forEach { declaration ->
|
||||
map[declaration] = module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CrossModuleReferenceInfoImpl(map)
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun breakCrossModuleFieldAccess(
|
||||
context: JsIrBackendContext,
|
||||
modules: Iterable<IrModuleFragment>
|
||||
) {
|
||||
val fieldToGetter = mutableMapOf<IrField, IrSimpleFunction>()
|
||||
|
||||
fun IrField.getter(): IrSimpleFunction {
|
||||
return fieldToGetter.getOrPut(this) {
|
||||
val fieldName = name
|
||||
val getter = buildFun {
|
||||
name = Name.identifier("get-$fieldName")
|
||||
returnType = type
|
||||
}
|
||||
getter.body = IrBlockBodyImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
|
||||
IrReturnImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, getter.symbol,
|
||||
IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, type)
|
||||
)
|
||||
)
|
||||
)
|
||||
getter.parent = parent
|
||||
(parent as IrDeclarationContainer).declarations += getter
|
||||
|
||||
getter
|
||||
}
|
||||
}
|
||||
|
||||
val fieldToSetter = mutableMapOf<IrField, IrSimpleFunction>()
|
||||
|
||||
fun IrField.setter(): IrSimpleFunction {
|
||||
return fieldToSetter.getOrPut(this) {
|
||||
val fieldName = name
|
||||
val setter = buildFun {
|
||||
name = Name.identifier("set-$fieldName")
|
||||
returnType = context.irBuiltIns.unitType
|
||||
}
|
||||
|
||||
val param = setter.addValueParameter("value", type)
|
||||
|
||||
setter.body = IrBlockBodyImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
|
||||
IrSetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, type).apply {
|
||||
value = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, param.symbol)
|
||||
}
|
||||
)
|
||||
)
|
||||
setter.parent = parent
|
||||
(parent as IrDeclarationContainer).declarations += setter
|
||||
|
||||
setter
|
||||
}
|
||||
}
|
||||
|
||||
modules.reversed().forEach { module ->
|
||||
|
||||
val moduleFields = module.files.flatMap { it.declarations.filterIsInstance<IrField>() }.toSet()
|
||||
|
||||
fun IrField.transformAccess(fn: IrField.() -> IrCall): IrCall? {
|
||||
if (parent !is IrPackageFragment || isEffectivelyExternal() || this in moduleFields) return null
|
||||
return fn()
|
||||
}
|
||||
|
||||
module.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
|
||||
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
return expression.symbol.owner.transformAccess {
|
||||
val getter = getter()
|
||||
IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, getter.returnType, getter.symbol)
|
||||
} ?: expression
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
return expression.symbol.owner.transformAccess {
|
||||
val setter = setter()
|
||||
IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, setter.returnType, setter.symbol).apply {
|
||||
putValueArgument(0, expression.value)
|
||||
}
|
||||
} ?: expression
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
val IrModuleFragment.safeName: String
|
||||
get() {
|
||||
var result = name.asString()
|
||||
|
||||
if (result.startsWith('<')) result = result.substring(1)
|
||||
if (result.endsWith('>')) result = result.substring(0, result.length - 1)
|
||||
|
||||
return sanitizeName("kotlin-$result")
|
||||
}
|
||||
+1
@@ -11,6 +11,7 @@ where advanced options include:
|
||||
Print declarations' reachability info to stdout during performing DCE
|
||||
-Xir-module-name=<name> Specify a compilation module name for IR backend
|
||||
-Xir-only Disables pre-IR backend
|
||||
-Xir-per-module Splits generated .js per-module
|
||||
-Xir-produce-js Generates JS file using IR backend. Also disables pre-IR backend
|
||||
-Xir-produce-klib-dir Generate unpacked KLIB into parent directory of output JS file.
|
||||
In combination with -meta-info generates both IR and pre-IR versions of library.
|
||||
|
||||
@@ -16,8 +16,17 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.backend.ast
|
||||
|
||||
class JsImportedModule(val externalName: String, var internalName: JsName, val plainReference: JsExpression?) {
|
||||
|
||||
class JsImportedModule @JvmOverloads constructor(
|
||||
val externalName: String,
|
||||
var internalName: JsName,
|
||||
val plainReference: JsExpression?,
|
||||
val relativeRequirePath: Boolean = false
|
||||
) {
|
||||
val key = JsImportedModuleKey(externalName, plainReference?.toString())
|
||||
}
|
||||
|
||||
val JsImportedModule.requireName: String
|
||||
get() = if (relativeRequirePath) "./$externalName.js" else externalName
|
||||
|
||||
data class JsImportedModuleKey(val baseName: String, val plainName: String?)
|
||||
@@ -129,6 +129,7 @@ abstract class BasicBoxTest(
|
||||
val expectActualLinker = EXPECT_ACTUAL_LINKER.matcher(fileContent).find()
|
||||
|
||||
val skipDceDriven = SKIP_DCE_DRIVEN.matcher(fileContent).find()
|
||||
val splitPerModule = SPLIT_PER_MODULE.matcher(fileContent).find()
|
||||
|
||||
TestFileFactoryImpl(coroutinesPackage).use { testFactory ->
|
||||
val inputFiles = TestFiles.createTestFiles(
|
||||
@@ -173,7 +174,7 @@ abstract class BasicBoxTest(
|
||||
testFactory.tmpDir,
|
||||
file.parent, module, outputFileName, dceOutputFileName, pirOutputFileName, dependencies, allDependencies, friends, modules.size > 1,
|
||||
!SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile,
|
||||
actualMainCallParameters, testPackage, testFunction, needsFullIrRuntime, isMainModule, expectActualLinker, skipDceDriven
|
||||
actualMainCallParameters, testPackage, testFunction, needsFullIrRuntime, isMainModule, expectActualLinker, skipDceDriven, splitPerModule
|
||||
)
|
||||
|
||||
when {
|
||||
@@ -395,7 +396,8 @@ abstract class BasicBoxTest(
|
||||
needsFullIrRuntime: Boolean,
|
||||
isMainModule: Boolean,
|
||||
expectActualLinker: Boolean,
|
||||
skipDceDriven: Boolean
|
||||
skipDceDriven: Boolean,
|
||||
splitPerModule: Boolean
|
||||
) {
|
||||
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
|
||||
val testFiles = kotlinFiles.map { it.fileName }
|
||||
@@ -419,7 +421,7 @@ abstract class BasicBoxTest(
|
||||
val incrementalData = IncrementalData()
|
||||
translateFiles(
|
||||
psiFiles.map(TranslationUnit::SourceFile), outputFile, dceOutputFile, pirOutputFile, config, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule, skipDceDriven
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule, skipDceDriven, splitPerModule
|
||||
)
|
||||
|
||||
if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) {
|
||||
@@ -471,7 +473,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
translateFiles(
|
||||
translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false, true
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false, true, false
|
||||
)
|
||||
|
||||
val originalOutput = FileUtil.loadFile(outputFile)
|
||||
@@ -544,7 +546,8 @@ abstract class BasicBoxTest(
|
||||
testFunction: String,
|
||||
needsFullIrRuntime: Boolean,
|
||||
isMainModule: Boolean,
|
||||
skipDceDriven: Boolean
|
||||
skipDceDriven: Boolean,
|
||||
splitPerModule: Boolean
|
||||
) {
|
||||
val translator = K2JSTranslator(config, false)
|
||||
val translationResult = translator.translateUnits(ExceptionThrowingReporter, units, mainCallParameters)
|
||||
@@ -929,6 +932,7 @@ abstract class BasicBoxTest(
|
||||
private val KJS_WITH_FULL_RUNTIME = Pattern.compile("^// *KJS_WITH_FULL_RUNTIME *\$", Pattern.MULTILINE)
|
||||
private val EXPECT_ACTUAL_LINKER = Pattern.compile("^// EXPECT_ACTUAL_LINKER *$", Pattern.MULTILINE)
|
||||
private val SKIP_DCE_DRIVEN = Pattern.compile("^// *SKIP_DCE_DRIVEN *$", Pattern.MULTILINE)
|
||||
private val SPLIT_PER_MODULE = Pattern.compile("^// *SPLIT_PER_MODULE *$", Pattern.MULTILINE)
|
||||
|
||||
@JvmStatic
|
||||
protected val runTestInNashorn = getBoolean("kotlin.js.useNashorn")
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.js.facade.MainCallParameters
|
||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parsing.parseBoolean
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
|
||||
import java.io.File
|
||||
@@ -56,6 +57,8 @@ abstract class BasicIrBoxTest(
|
||||
|
||||
val runEs6Mode: Boolean = getBoolean("kotlin.js.ir.es6", false)
|
||||
|
||||
val perModule: Boolean = getBoolean("kotlin.js.ir.perModule")
|
||||
|
||||
private val osName: String = System.getProperty("os.name").toLowerCase()
|
||||
|
||||
// TODO Design incremental compilation for IR and add test support
|
||||
@@ -63,8 +66,11 @@ abstract class BasicIrBoxTest(
|
||||
|
||||
private val compilationCache = mutableMapOf<String, String>()
|
||||
|
||||
private val cachedDependencies = mutableMapOf<String, Collection<String>>()
|
||||
|
||||
override fun doTest(filePath: String, expectedResult: String, mainCallParameters: MainCallParameters, coroutinesPackage: String) {
|
||||
compilationCache.clear()
|
||||
cachedDependencies.clear()
|
||||
super.doTest(filePath, expectedResult, mainCallParameters, coroutinesPackage)
|
||||
}
|
||||
|
||||
@@ -86,7 +92,8 @@ abstract class BasicIrBoxTest(
|
||||
testFunction: String,
|
||||
needsFullIrRuntime: Boolean,
|
||||
isMainModule: Boolean,
|
||||
skipDceDriven: Boolean
|
||||
skipDceDriven: Boolean,
|
||||
splitPerModule: Boolean
|
||||
) {
|
||||
val filesToCompile = units.map { (it as TranslationUnit.SourceFile).file }
|
||||
|
||||
@@ -135,18 +142,13 @@ abstract class BasicIrBoxTest(
|
||||
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))),
|
||||
generateFullJs = true,
|
||||
generateDceJs = runIrDce,
|
||||
es6mode = runEs6Mode
|
||||
es6mode = runEs6Mode,
|
||||
multiModule = splitPerModule || perModule
|
||||
)
|
||||
|
||||
val wrappedCode =
|
||||
wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
outputFile.write(wrappedCode)
|
||||
compiledModule.jsCode!!.writeTo(outputFile, config)
|
||||
|
||||
compiledModule.dceJsCode?.let { dceJsCode ->
|
||||
val dceWrappedCode =
|
||||
wrapWithModuleEmulationMarkers(dceJsCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
dceOutputFile.write(dceWrappedCode)
|
||||
}
|
||||
compiledModule.dceJsCode?.writeTo(dceOutputFile, config)
|
||||
|
||||
if (generateDts) {
|
||||
val dtsFile = outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts")
|
||||
@@ -166,11 +168,9 @@ abstract class BasicIrBoxTest(
|
||||
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null },
|
||||
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))),
|
||||
dceDriven = true,
|
||||
es6mode = runEs6Mode
|
||||
).jsCode!!.let { pirCode ->
|
||||
val pirWrappedCode = wrapWithModuleEmulationMarkers(pirCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
pirOutputFile.write(pirWrappedCode)
|
||||
}
|
||||
es6mode = runEs6Mode,
|
||||
multiModule = splitPerModule || perModule
|
||||
).jsCode!!.writeTo(pirOutputFile, config)
|
||||
}
|
||||
} else {
|
||||
generateKLib(
|
||||
@@ -188,6 +188,23 @@ abstract class BasicIrBoxTest(
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsCode.writeTo(outputFile: File, config: JsConfig) {
|
||||
val wrappedCode =
|
||||
wrapWithModuleEmulationMarkers(mainModule, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
outputFile.write(wrappedCode)
|
||||
|
||||
val dependencyPaths = mutableListOf<String>()
|
||||
|
||||
dependencies.forEach { (moduleId, code) ->
|
||||
val wrappedCode = wrapWithModuleEmulationMarkers(code, config.moduleKind, moduleId)
|
||||
val dependencyPath = outputFile.absolutePath.replace("_v5.js", "-${moduleId}_v5.js")
|
||||
dependencyPaths += dependencyPath
|
||||
File(dependencyPath).write(wrappedCode)
|
||||
}
|
||||
|
||||
cachedDependencies[outputFile.absolutePath] = dependencyPaths
|
||||
}
|
||||
|
||||
override fun dontRunOnSpecificPlatform(targetBackend: TargetBackend): Boolean {
|
||||
if (targetBackend != TargetBackend.JS_IR_ES6) return false
|
||||
if (!runEs6Mode) return false
|
||||
@@ -210,7 +227,8 @@ abstract class BasicIrBoxTest(
|
||||
// TODO: should we do anything special for module systems?
|
||||
// TODO: return list of js from translateFiles and provide then to this function with other js files
|
||||
|
||||
testChecker.check(jsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem)
|
||||
val allFiles = jsFiles.flatMap { file -> cachedDependencies[file]?.let { deps -> deps + file } ?: listOf(file) }
|
||||
testChecker.check(allFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+98
@@ -797,6 +797,104 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/crossModuleRefIR")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CrossModuleRefIR extends AbstractIrBoxJsES6Test {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCrossModuleRefIR() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/crossModuleRefIR"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||
}
|
||||
|
||||
@TestMetadata("callableObjectRef.kt")
|
||||
public void testCallableObjectRef() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/callableObjectRef.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("export.kt")
|
||||
public void testExport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/export.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inheritance.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModule.kt")
|
||||
public void testInlineJsModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModuleNonIdentifier.kt")
|
||||
public void testInlineJsModuleNonIdentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModuleNonIdentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModulePackage.kt")
|
||||
public void testInlineJsModulePackage() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModulePackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModule.kt")
|
||||
public void testInlineModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModuleNonIndentifier.kt")
|
||||
public void testInlineModuleNonIndentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModuleNonIndentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambda.kt")
|
||||
public void testLambda() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/lambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("object.kt")
|
||||
public void testObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/object.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectInInlineClosure.kt")
|
||||
public void testObjectInInlineClosure() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectInInlineClosure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectIsObject.kt")
|
||||
public void testObjectIsObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectIsObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelExtension.kt")
|
||||
public void testTopLevelExtension() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelExtension.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunction.kt")
|
||||
public void testTopLevelFunction() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelMutableProperty.kt")
|
||||
public void testTopLevelMutableProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelMutableProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelProperty.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/dataClass")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+98
@@ -797,6 +797,104 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/crossModuleRefIR")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CrossModuleRefIR extends AbstractIrBoxJsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCrossModuleRefIR() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/crossModuleRefIR"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("callableObjectRef.kt")
|
||||
public void testCallableObjectRef() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/callableObjectRef.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("export.kt")
|
||||
public void testExport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/export.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inheritance.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModule.kt")
|
||||
public void testInlineJsModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModuleNonIdentifier.kt")
|
||||
public void testInlineJsModuleNonIdentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModuleNonIdentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModulePackage.kt")
|
||||
public void testInlineJsModulePackage() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModulePackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModule.kt")
|
||||
public void testInlineModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModuleNonIndentifier.kt")
|
||||
public void testInlineModuleNonIndentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModuleNonIndentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambda.kt")
|
||||
public void testLambda() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/lambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("object.kt")
|
||||
public void testObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/object.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectInInlineClosure.kt")
|
||||
public void testObjectInInlineClosure() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectInInlineClosure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectIsObject.kt")
|
||||
public void testObjectIsObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectIsObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelExtension.kt")
|
||||
public void testTopLevelExtension() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelExtension.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunction.kt")
|
||||
public void testTopLevelFunction() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelMutableProperty.kt")
|
||||
public void testTopLevelMutableProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelMutableProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelProperty.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/dataClass")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+98
@@ -797,6 +797,104 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/crossModuleRefIR")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CrossModuleRefIR extends AbstractBoxJsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCrossModuleRefIR() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/crossModuleRefIR"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("callableObjectRef.kt")
|
||||
public void testCallableObjectRef() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/callableObjectRef.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("export.kt")
|
||||
public void testExport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/export.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inheritance.kt")
|
||||
public void testInheritance() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inheritance.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModule.kt")
|
||||
public void testInlineJsModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModuleNonIdentifier.kt")
|
||||
public void testInlineJsModuleNonIdentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModuleNonIdentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineJsModulePackage.kt")
|
||||
public void testInlineJsModulePackage() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineJsModulePackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModule.kt")
|
||||
public void testInlineModule() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModule.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineModuleNonIndentifier.kt")
|
||||
public void testInlineModuleNonIndentifier() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/inlineModuleNonIndentifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambda.kt")
|
||||
public void testLambda() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/lambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("object.kt")
|
||||
public void testObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/object.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectInInlineClosure.kt")
|
||||
public void testObjectInInlineClosure() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectInInlineClosure.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectIsObject.kt")
|
||||
public void testObjectIsObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/objectIsObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelExtension.kt")
|
||||
public void testTopLevelExtension() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelExtension.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelFunction.kt")
|
||||
public void testTopLevelFunction() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelMutableProperty.kt")
|
||||
public void testTopLevelMutableProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelMutableProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/crossModuleRefIR/topLevelProperty.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/box/dataClass")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1289
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
object O {
|
||||
operator fun invoke() = "OK"
|
||||
}
|
||||
|
||||
inline fun callO() = O()
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
val a = O()
|
||||
if (a != "OK") return "fail: simple: $a"
|
||||
|
||||
val b = callO()
|
||||
if (b != "OK") return "fail: inline: $a"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1291
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
class A(val x: Int) {
|
||||
constructor(a: Int, b: Int) : this(a + b)
|
||||
}
|
||||
|
||||
external class B(x: Int) {
|
||||
constructor(a: Int, b: Int)
|
||||
|
||||
val x: Int
|
||||
}
|
||||
|
||||
// TODO: may be useful after implementing local classes in inline functions
|
||||
/*
|
||||
inline fun foo(p: Int, q: Int, r: Int): Pair<Int, Int> {
|
||||
class C(val x : Int) {
|
||||
constructor(a: Int, b: Int) : this(a + b)
|
||||
}
|
||||
return Pair(C(p).x, C(q, r).x)
|
||||
}
|
||||
*/
|
||||
|
||||
inline fun callPrimaryConstructor(x: Int) = A(x).x
|
||||
|
||||
inline fun callSecondaryConstructor(x: Int, y: Int) = A(x, y).x
|
||||
|
||||
// FILE: lib.js
|
||||
|
||||
function B(x, y) {
|
||||
this.x = x;
|
||||
if (typeof y !== 'undefined') {
|
||||
this.x += y;
|
||||
}
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
val a = A(23).x
|
||||
if (a != 23) return "fail: primary constructor: $a"
|
||||
|
||||
val b = A(40, 2).x
|
||||
if (b != 42) return "fail: secondary constructor: $b"
|
||||
|
||||
val c = B(99).x
|
||||
if (c != 99) return "fail: native primary constructor: $c"
|
||||
|
||||
val d = B(100, 11).x
|
||||
if (d != 111) return "fail: native secondary constructor: $d"
|
||||
|
||||
/*
|
||||
val (e, f) = foo(123, 320, 1)
|
||||
if (e != 123) return "fail: local primary constructor: $e"
|
||||
if (f != 321) return "fail: local secondary constructor: $f"
|
||||
*/
|
||||
|
||||
val g = callPrimaryConstructor(55)
|
||||
if (g != 55) return "fail: primary constructor from inline function: $g"
|
||||
|
||||
val h = callSecondaryConstructor(990, 9)
|
||||
if (h != 999) return "fail: secondary constructor from inline function: $h"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// SPLIT_PER_MODULE
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// EXPECTED_REACHABLE_NODES: 1316
|
||||
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
|
||||
@JsExport
|
||||
fun O(): String = "O"
|
||||
|
||||
// MODULE: lib2(lib1)
|
||||
// FILE: lib2.kt
|
||||
|
||||
@JsExport
|
||||
fun K(): String = "K"
|
||||
|
||||
|
||||
// MODULE: main(lib1, lib2)
|
||||
// FILE: main.kt
|
||||
|
||||
@JsExport
|
||||
fun test() = O() + K()
|
||||
|
||||
// FILE: test.js
|
||||
|
||||
function box() {
|
||||
|
||||
if (main.test() != "OK") return "fail 1";
|
||||
|
||||
return kotlin_lib1.O() + kotlin_lib2.K();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1292
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
package lib
|
||||
|
||||
open class A {
|
||||
fun foo() = 23
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
|
||||
package main
|
||||
|
||||
import lib.A
|
||||
|
||||
class B : A() {
|
||||
fun bar() = foo() + 1
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val result = B().bar()
|
||||
if (result != 24) return "fail: $result"
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1316
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.js
|
||||
define("lib1", [], function() {
|
||||
return function() {
|
||||
return "OK";
|
||||
}
|
||||
})
|
||||
|
||||
// MODULE: lib2(lib1)
|
||||
// FILE: lib2.kt
|
||||
// MODULE_KIND: AMD
|
||||
@JsModule("lib1")
|
||||
external fun foo(): String
|
||||
|
||||
// MODULE: lib3(lib2)
|
||||
// FILE: lib3.kt
|
||||
// MODULE_KIND: AMD
|
||||
inline fun bar() = foo()
|
||||
|
||||
// MODULE: main(lib3)
|
||||
// FILE: main.kt
|
||||
// MODULE_KIND: AMD
|
||||
|
||||
fun box() = bar()
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1316
|
||||
// MODULE: lib-1
|
||||
// FILE: lib-1.js
|
||||
define("lib-1", [], function() {
|
||||
return function() {
|
||||
return "OK";
|
||||
}
|
||||
})
|
||||
|
||||
// MODULE: lib2(lib-1)
|
||||
// FILE: lib2.kt
|
||||
// MODULE_KIND: AMD
|
||||
@JsModule("lib-1")
|
||||
external fun foo(): String
|
||||
|
||||
// MODULE: lib3(lib2)
|
||||
// FILE: lib3.kt
|
||||
// MODULE_KIND: AMD
|
||||
inline fun bar() = foo()
|
||||
|
||||
// MODULE: main(lib3)
|
||||
// FILE: main.kt
|
||||
// MODULE_KIND: AMD
|
||||
|
||||
fun box() = bar()
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.js
|
||||
define("lib1", [], function() {
|
||||
return {
|
||||
foo: function() {
|
||||
return "OK";
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
// MODULE: lib2(lib1)
|
||||
// FILE: lib2.kt
|
||||
// MODULE_KIND: AMD
|
||||
@file:JsModule("lib1")
|
||||
|
||||
external fun foo(): String
|
||||
|
||||
// MODULE: lib3(lib2)
|
||||
// FILE: lib3.kt
|
||||
// MODULE_KIND: AMD
|
||||
inline fun bar() = foo()
|
||||
|
||||
// MODULE: main(lib3)
|
||||
// FILE: main.kt
|
||||
// MODULE_KIND: AMD
|
||||
|
||||
fun box() = bar()
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
|
||||
fun foo() = "OK"
|
||||
|
||||
// MODULE: lib2(lib1)
|
||||
// FILE: lib2.kt
|
||||
|
||||
inline fun bar() = foo()
|
||||
|
||||
// MODULE: main(lib1, lib2)
|
||||
// FILE: main.kt
|
||||
|
||||
fun box() = bar()
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
// MODULE: 1
|
||||
// FILE: lib1.kt
|
||||
|
||||
fun foo() = "OK"
|
||||
|
||||
// MODULE: 2(1)
|
||||
// FILE: lib2.kt
|
||||
|
||||
inline fun bar() = foo()
|
||||
|
||||
// MODULE: main(2)
|
||||
// FILE: main.kt
|
||||
|
||||
fun box() = bar()
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
fun bar(f: () -> String) = f()
|
||||
|
||||
inline fun foo(): String {
|
||||
return bar { "OK" }
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box() = foo()
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1289
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
var log = ""
|
||||
|
||||
object O {
|
||||
init {
|
||||
log += "O.init;"
|
||||
}
|
||||
|
||||
fun result() = "OK"
|
||||
}
|
||||
|
||||
fun getResult(): String {
|
||||
log += "before;"
|
||||
val result = O.result()
|
||||
log += "after;"
|
||||
return result
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
val result = getResult()
|
||||
if (result != "OK") return "fail: unexpected result: $result"
|
||||
|
||||
if (log != "before;O.init;after;") return "fail: wrong evaluation order: $log"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1382
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
object O {
|
||||
val result = "OK"
|
||||
|
||||
inline fun foo(): String {
|
||||
val o = object {
|
||||
fun bar() = O
|
||||
}
|
||||
return fetch(o.bar())
|
||||
}
|
||||
}
|
||||
|
||||
fun fetch(o: O) = o.result
|
||||
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box() = O.foo()
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
object O
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
var o: Any = O
|
||||
if (o !is O) return "fail1"
|
||||
if (!(o is O)) return "fail2"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
package lib
|
||||
|
||||
class A(val x: Int)
|
||||
|
||||
fun A.foo() = 23 + x
|
||||
|
||||
inline fun A.baz() = 99 + x
|
||||
|
||||
inline fun A.callFoo() = foo()
|
||||
|
||||
inline fun A.buzz(): Int {
|
||||
val o = object {
|
||||
fun f() = 111 + x
|
||||
}
|
||||
return o.f()
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
val a = A(1).foo()
|
||||
if (a != 24) return "fail: simple function: $a"
|
||||
|
||||
val c = A(1).baz()
|
||||
if (c != 100) return "fail: inline function: $c"
|
||||
|
||||
val d = A(1).buzz()
|
||||
if (d != 112) return "fail: inline function with object expression: $d"
|
||||
|
||||
val e = A(2).callFoo()
|
||||
if (e != 25) return "fail: inline function calling another function: $e"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1287
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
package lib
|
||||
|
||||
fun foo() = 23
|
||||
|
||||
external fun bar(): Int = definedExternally
|
||||
|
||||
inline fun baz() = 99
|
||||
|
||||
inline fun callFoo() = foo()
|
||||
|
||||
inline fun buzz(): Int {
|
||||
val o = object {
|
||||
fun f() = 111
|
||||
}
|
||||
return o.f()
|
||||
}
|
||||
|
||||
// FILE: lib.js
|
||||
|
||||
function bar() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
|
||||
package main
|
||||
|
||||
fun box(): String {
|
||||
val a = lib.foo()
|
||||
if (a != 23) return "fail: simple function: $a"
|
||||
|
||||
val b = lib.bar()
|
||||
if (b != 42) return "fail: native function: $b"
|
||||
|
||||
val c = lib.baz()
|
||||
if (c != 99) return "fail: inline function: $c"
|
||||
|
||||
val d = lib.buzz()
|
||||
if (d != 111) return "fail: inline function with object expression: $d"
|
||||
|
||||
val e = lib.callFoo()
|
||||
if (e != 23) return "fail: inline function calling another function: $e"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
var foo = 23
|
||||
|
||||
var bar: Int = 42
|
||||
get() = field
|
||||
set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
@JsName("faz") var baz = 99
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: lib.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
if (foo != 23) return "fail: simple property initial value: $foo"
|
||||
foo = 24
|
||||
if (foo != 24) return "fail: simple property new value: $foo"
|
||||
|
||||
if (bar != 42) return "fail: property with accessor initial value: $bar"
|
||||
bar = 43
|
||||
if (bar != 43) return "fail: property with accessor new value: $bar"
|
||||
|
||||
if (baz != 99) return "fail: renamed property initial value: $baz"
|
||||
baz = 100
|
||||
if (baz != 100) return "fail: renamed property new value: $baz"
|
||||
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPLIT_PER_MODULE
|
||||
// EXPECTED_REACHABLE_NODES: 1287
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
package lib
|
||||
|
||||
val foo = 23
|
||||
|
||||
val boo: Int
|
||||
get() = 42
|
||||
|
||||
external val bar: Int = definedExternally
|
||||
|
||||
external val far: Int
|
||||
get() = definedExternally
|
||||
|
||||
// TODO: annotations like this are not serialized properly. Uncomment after KT-14529 gets fixed
|
||||
/*
|
||||
val fuzz: Int
|
||||
@JsName("getBuzz") get() = 55
|
||||
*/
|
||||
|
||||
inline fun fetchFoo() = foo
|
||||
|
||||
@JsName("fee")
|
||||
val tee = 2525
|
||||
|
||||
// FILE: lib.js
|
||||
|
||||
var bar = 99
|
||||
var far = 111
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: lib.kt
|
||||
package main
|
||||
|
||||
import lib.*
|
||||
|
||||
fun box(): String {
|
||||
if (foo != 23) return "fail: simple property: $foo"
|
||||
if (boo != 42) return "fail: property with accessor: $boo"
|
||||
if (bar != 99) return "fail: native property: $bar"
|
||||
if (far != 111) return "fail: native property with accessor: $far"
|
||||
//if (fuzz != 55) return "fail: property with JsName on accessor: $fuzz"
|
||||
if (tee != 2525) return "fail: native property with JsName: $tee"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user