[K/JS] Move ES modules logic to a new transformer with IC
This commit is contained in:
+1
-1
@@ -123,7 +123,7 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
@Argument(
|
||||
value = "-module-kind",
|
||||
valueDescription = "{plain|amd|commonjs|umd}",
|
||||
valueDescription = "{plain|amd|commonjs|umd|es}",
|
||||
description = "Kind of the JS module generated by the compiler"
|
||||
)
|
||||
var moduleKind: String? by NullableStringFreezableVar(K2JsArgumentConstants.MODULE_PLAIN)
|
||||
|
||||
@@ -551,7 +551,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
var moduleKind: ModuleKind? = if (moduleKindName != null) moduleKindMap[moduleKindName] else ModuleKind.PLAIN
|
||||
if (moduleKind == null) {
|
||||
messageCollector.report(
|
||||
ERROR, "Unknown module kind: $moduleKindName. Valid values are: plain, amd, commonjs, umd", null
|
||||
ERROR, "Unknown module kind: $moduleKindName. Valid values are: plain, amd, commonjs, umd, es", null
|
||||
)
|
||||
moduleKind = ModuleKind.PLAIN
|
||||
}
|
||||
|
||||
@@ -1,459 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.LoweredIr
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsCodeOutliningLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrFileToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.processClassModels
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.hasInterfaceParent
|
||||
import org.jetbrains.kotlin.ir.util.isInterface
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import kotlin.math.abs
|
||||
|
||||
interface CompilerOutputSink {
|
||||
fun write(module: String, path: String, content: String)
|
||||
}
|
||||
|
||||
class JsGenerationOptions(
|
||||
val jsExtension: String = "js",
|
||||
val generatePackageJson: Boolean = false,
|
||||
val generateTypeScriptDefinitions: Boolean = false,
|
||||
)
|
||||
|
||||
class IrToJs(
|
||||
private val backendContext: JsIrBackendContext,
|
||||
private val guid: (IrDeclaration) -> String,
|
||||
private val outputSink: CompilerOutputSink,
|
||||
private val mainArguments: List<String>?,
|
||||
private val granularity: JsGenerationGranularity,
|
||||
private val mainModuleName: String,
|
||||
private val options: JsGenerationOptions,
|
||||
) {
|
||||
val indexFileName = "index.${options.jsExtension}"
|
||||
|
||||
val FileUnit.initFunctionName
|
||||
get() = "KotlinInit$" + sanitizeName(pathToJsModule(file))
|
||||
|
||||
sealed class CodegenUnitReference
|
||||
object ThisUnitReference : CodegenUnitReference()
|
||||
inner class OtherUnitReference(
|
||||
module: IrModuleFragment,
|
||||
) : CodegenUnitReference() {
|
||||
// Path to entry point of other module from "top-level", e.g. directory which contains all other modules
|
||||
val importPath = "./" + module.jsModuleName + "/" + indexFileName
|
||||
}
|
||||
|
||||
abstract class CodegenUnit {
|
||||
abstract val packageFragments: Iterable<IrPackageFragment>
|
||||
abstract val externalPackageFragments: Iterable<IrPackageFragment>
|
||||
abstract fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference
|
||||
abstract val pathToKotlinModulesRoot: String
|
||||
}
|
||||
|
||||
inner class FileUnit(val file: IrFile, val externalFile: IrFile?) : CodegenUnit() {
|
||||
override val packageFragments =
|
||||
listOf(file)
|
||||
|
||||
override val externalPackageFragments =
|
||||
listOfNotNull(externalFile)
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
when (val declarationFile = declaration.file) {
|
||||
file -> ThisUnitReference
|
||||
else -> OtherUnitReference(declarationFile.module)
|
||||
}
|
||||
|
||||
override val pathToKotlinModulesRoot: String by lazy {
|
||||
"../".repeat(file.fqName.pathSegments().size + 1)
|
||||
}
|
||||
}
|
||||
|
||||
inner class ModuleUnit(val module: IrModuleFragment) : CodegenUnit() {
|
||||
override val packageFragments: Iterable<IrPackageFragment> =
|
||||
module.files
|
||||
|
||||
override val externalPackageFragments: Iterable<IrPackageFragment> =
|
||||
packageFragments.mapNotNull { backendContext.externalPackageFragment[it.symbol] }
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
when (val declarationModule = declaration.file.module) {
|
||||
module -> ThisUnitReference
|
||||
else -> OtherUnitReference(declarationModule)
|
||||
}
|
||||
|
||||
override val pathToKotlinModulesRoot: String = "../"
|
||||
}
|
||||
|
||||
class WholeProgramUnit(
|
||||
val modules: Iterable<IrModuleFragment>,
|
||||
val externalModules: Iterable<IrPackageFragment>
|
||||
) : CodegenUnit() {
|
||||
override val packageFragments: Iterable<IrPackageFragment> =
|
||||
modules.flatMap { it.files }
|
||||
|
||||
override val externalPackageFragments: Iterable<IrPackageFragment>
|
||||
get() = externalModules
|
||||
|
||||
override fun referenceCodegenUnitOfDeclaration(declaration: IrDeclaration): CodegenUnitReference =
|
||||
ThisUnitReference
|
||||
|
||||
override val pathToKotlinModulesRoot: String
|
||||
get() = "../"
|
||||
}
|
||||
|
||||
private fun pathToJsModule(file: IrFile): String =
|
||||
"${fileJsRootModuleName(file)}/${fileJsSubModulePath(file)}"
|
||||
|
||||
private fun fileJsRootModuleName(file: IrFile): String =
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM -> mainModuleName
|
||||
PER_MODULE, PER_FILE -> file.module.jsModuleName
|
||||
}
|
||||
|
||||
private fun fileJsSubModulePath(file: IrFile): String =
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM, PER_MODULE -> indexFileName
|
||||
|
||||
PER_FILE -> {
|
||||
val maybeSingleOpenClass = (file.declarations.singleOrNull() as? IrClass)?.takeIf {
|
||||
it.modality == Modality.ABSTRACT || it.modality == Modality.OPEN
|
||||
}
|
||||
|
||||
val hash = abs((maybeSingleOpenClass?.let { guid(it) } ?: file.path).hashCode())
|
||||
val filePrefix = maybeSingleOpenClass?.name?.asString()?.let { sanitizeName(it) + ".class" } ?: file.name
|
||||
val fileName = "${filePrefix}_$hash.${options.jsExtension}"
|
||||
val packagePath = file.fqName.pathSegments().joinToString("") { it.identifier + "/" }
|
||||
"$packagePath$fileName"
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedUnit(
|
||||
val jsStatements: List<JsStatement>,
|
||||
val exportedDeclarations: List<ExportedDeclaration>,
|
||||
)
|
||||
|
||||
fun generateUnit(unit: CodegenUnit): GeneratedUnit {
|
||||
val exportedDeclarations: List<ExportedDeclaration> =
|
||||
with(ExportModelGenerator(backendContext, generateNamespacesForPackages = false)) {
|
||||
(unit.externalPackageFragments + unit.packageFragments).flatMap { packageFragment ->
|
||||
generateExport(packageFragment)
|
||||
}
|
||||
}
|
||||
|
||||
val stableNames: Set<String> = collectStableNames(unit)
|
||||
val nameGenerator = NewNamerImpl(backendContext, unit, guid, stableNames)
|
||||
|
||||
val staticContext = JsStaticContext(
|
||||
backendContext = backendContext,
|
||||
irNamer = nameGenerator,
|
||||
globalNameScope = nameGenerator.staticNames
|
||||
)
|
||||
|
||||
val declarationStatements: List<JsStatement> = unit.packageFragments.flatMap {
|
||||
StaticMembersLowering(backendContext).lower(it as IrFile)
|
||||
it.accept(IrFileToJsTransformer(), staticContext).statements
|
||||
}
|
||||
|
||||
val preDeclarationBlock = JsCompositeBlock()
|
||||
val postDeclarationBlock = JsCompositeBlock()
|
||||
processClassModels(staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
statements += nameGenerator.internalImports.values
|
||||
statements += preDeclarationBlock
|
||||
statements += declarationStatements
|
||||
statements += postDeclarationBlock
|
||||
|
||||
// Generate module initialization
|
||||
|
||||
val initializerBlock = staticContext.initializerBlock
|
||||
when (unit) {
|
||||
is WholeProgramUnit, is ModuleUnit -> {
|
||||
// Run initialization during ES module initialization
|
||||
statements += initializerBlock
|
||||
}
|
||||
|
||||
is FileUnit -> {
|
||||
// Postpone initialization by putting it into a separate function
|
||||
// Will be called later in proper order after class model is initialized
|
||||
val initFunction = JsFunction(emptyScope, JsBlock(initializerBlock.statements), "init fun")
|
||||
initFunction.name = JsName(unit.initFunctionName, false)
|
||||
statements += initFunction.makeStmt()
|
||||
statements += JsExport(initFunction.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate internal export
|
||||
|
||||
val internalExports = mutableListOf<JsExport.Element>()
|
||||
fun export(declaration: IrDeclarationWithName) {
|
||||
internalExports += JsExport.Element(nameGenerator.getNameForStaticDeclaration(declaration), JsName(guid(declaration), false))
|
||||
}
|
||||
|
||||
for (fragment in unit.packageFragments) {
|
||||
for (declaration in fragment.declarations) {
|
||||
if (declaration is IrDeclarationWithName) {
|
||||
if (declaration.origin == JsCodeOutliningLowering.OUTLINED_JS_CODE_ORIGIN) continue
|
||||
export(declaration)
|
||||
}
|
||||
|
||||
// Default implementations of interface methods are nested under interface declarations in IR at this point,
|
||||
// but they are effectively used as a static declaration and can be directly referenced by other codegen unit,
|
||||
// thus requiring internal export
|
||||
declaration.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
|
||||
if (declaration.hasInterfaceParent() && declaration.body != null) {
|
||||
export(declaration)
|
||||
}
|
||||
super.visitSimpleFunction(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
statements += JsExport(JsExport.Subject.Elements(internalExports), null)
|
||||
|
||||
// Generate external export
|
||||
|
||||
val globalNames = NameTable<String>(nameGenerator.staticNames)
|
||||
val exporter = ExportModelToJsStatements(
|
||||
staticContext,
|
||||
declareNewNamespace = { globalNames.declareFreshName(it, it) }
|
||||
)
|
||||
exportedDeclarations.forEach {
|
||||
statements += exporter.generateDeclarationExport(
|
||||
it,
|
||||
null,
|
||||
esModules = true
|
||||
)
|
||||
}
|
||||
|
||||
return GeneratedUnit(statements, exportedDeclarations)
|
||||
}
|
||||
|
||||
private fun collectStableNames(unit: CodegenUnit): Set<String> {
|
||||
val newStableStaticNamesCollectorVisitor =
|
||||
NewStableStaticNamesCollectorVisitor(needToCollectReferences = granularity != WHOLE_PROGRAM)
|
||||
unit.packageFragments.forEach { it.acceptVoid(newStableStaticNamesCollectorVisitor) }
|
||||
unit.externalPackageFragments.forEach { it.acceptVoid(newStableStaticNamesCollectorVisitor) }
|
||||
|
||||
return newStableStaticNamesCollectorVisitor.collectedStableNames
|
||||
}
|
||||
|
||||
// Returns import statement and call expression
|
||||
private fun invokeFunctionFromEntryJsFile(
|
||||
function: IrFunction,
|
||||
args: List<JsExpression> = emptyList()
|
||||
): Pair<JsStatement, JsExpression> {
|
||||
val name = guid(function)
|
||||
val importPath = if (granularity == WHOLE_PROGRAM) "./$indexFileName" else "../" + pathToJsModule(function.file)
|
||||
return Pair(
|
||||
JsImport(importPath, mutableListOf(JsImport.Element(name, null))),
|
||||
JsInvocation(JsNameRef(name), args)
|
||||
)
|
||||
}
|
||||
|
||||
private fun invokeFunctionFromEntryJsFileAsStatements(
|
||||
function: IrFunction,
|
||||
args: List<JsExpression> = emptyList()
|
||||
): List<JsStatement> =
|
||||
invokeFunctionFromEntryJsFile(function, args)
|
||||
.let { listOf(it.first, it.second.makeStmt()) }
|
||||
|
||||
fun generateModules(
|
||||
mainModule: IrModuleFragment,
|
||||
allModules: List<IrModuleFragment>
|
||||
) {
|
||||
when (granularity) {
|
||||
WHOLE_PROGRAM ->
|
||||
generateModule(mainModule, allModules)
|
||||
|
||||
PER_MODULE,
|
||||
PER_FILE ->
|
||||
allModules.forEach { module ->
|
||||
generateModule(mainModule = module, allModules = emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateModuleLevelCode(module: IrModuleFragment, statements: MutableList<JsStatement>) {
|
||||
if (mainArguments != null) {
|
||||
val mainFunction = JsMainFunctionDetector(backendContext).getMainFunctionOrNull(module)
|
||||
if (mainFunction != null) {
|
||||
val generateArgv = mainFunction.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
||||
val generateContinuation = mainFunction.isLoweredSuspendFunction(backendContext)
|
||||
|
||||
val mainArgumentsArray =
|
||||
if (generateArgv)
|
||||
JsArrayLiteral(mainArguments.map { JsStringLiteral(it) })
|
||||
else
|
||||
null
|
||||
|
||||
val continuation =
|
||||
if (generateContinuation) {
|
||||
val (import, invoke) = invokeFunctionFromEntryJsFile(backendContext.coroutineEmptyContinuation.owner.getter!!)
|
||||
statements += import
|
||||
invoke
|
||||
} else
|
||||
null
|
||||
|
||||
statements += invokeFunctionFromEntryJsFileAsStatements(
|
||||
mainFunction, listOfNotNull(mainArgumentsArray, continuation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: tests
|
||||
// backendContext.testRoots[module]?.let { testContainer ->
|
||||
// statements += invokeFunctionFromEntryJsFileAsStatements(testContainer)
|
||||
// }
|
||||
}
|
||||
|
||||
fun generateModule(
|
||||
mainModule: IrModuleFragment,
|
||||
allModules: List<IrModuleFragment>,
|
||||
) {
|
||||
val moduleName = mainModule.jsModuleName
|
||||
val indexJsStatements = mutableListOf<JsStatement>()
|
||||
val exportedDeclarations = mutableListOf<ExportedDeclaration>()
|
||||
|
||||
when (granularity) {
|
||||
PER_FILE -> {
|
||||
for (file in mainModule.files.sortedBy(::fileInitOrder)) {
|
||||
if (file.declarations.isEmpty()) continue
|
||||
|
||||
val pathToSubModule = fileJsSubModulePath(file)
|
||||
indexJsStatements += JsExport(JsExport.Subject.All, fromModule = "./$pathToSubModule")
|
||||
|
||||
val unit = FileUnit(file, backendContext.externalPackageFragment[file.symbol])
|
||||
val generatedUnit = generateUnit(unit)
|
||||
|
||||
val importElements = JsImport.Element(unit.initFunctionName, null)
|
||||
indexJsStatements += JsImport("./$pathToSubModule", mutableListOf(importElements))
|
||||
indexJsStatements += JsInvocation(JsNameRef(JsName(unit.initFunctionName, false))).makeStmt()
|
||||
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
|
||||
outputSink.write(
|
||||
file.module.jsModuleName,
|
||||
pathToSubModule,
|
||||
"// Kotlin file: ${file.path}\n" + generatedUnit.jsStatements.toJsCodeString()
|
||||
)
|
||||
}
|
||||
generateModuleLevelCode(mainModule, indexJsStatements)
|
||||
}
|
||||
|
||||
PER_MODULE -> {
|
||||
val generatedUnit = generateUnit(ModuleUnit(mainModule))
|
||||
indexJsStatements += generatedUnit.jsStatements
|
||||
generateModuleLevelCode(mainModule, indexJsStatements)
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
}
|
||||
|
||||
WHOLE_PROGRAM -> {
|
||||
val generatedUnit = generateUnit(WholeProgramUnit(allModules, backendContext.externalPackageFragment.values))
|
||||
indexJsStatements += generatedUnit.jsStatements
|
||||
allModules.forEach {
|
||||
generateModuleLevelCode(it, indexJsStatements)
|
||||
}
|
||||
exportedDeclarations += generatedUnit.exportedDeclarations
|
||||
}
|
||||
}
|
||||
|
||||
outputSink.write(moduleName, indexFileName, indexJsStatements.toJsCodeString())
|
||||
|
||||
if (options.generatePackageJson) {
|
||||
outputSink.write(moduleName, "package.json", """{ "main": "$indexFileName", "type": "module" }""")
|
||||
}
|
||||
|
||||
if (options.generateTypeScriptDefinitions && exportedDeclarations.isNotEmpty()) {
|
||||
val dts = ExportedModule(moduleName, moduleKind = ModuleKind.ES, exportedDeclarations).toTypeScript()
|
||||
outputSink.write(moduleName, "index.d.ts", dts)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fileInitOrder(file: IrFile): Int =
|
||||
when (val singleDeclaration = file.declarations.singleOrNull()) {
|
||||
// Initialize parent classes before child classes
|
||||
// TODO: Comment about open classes in separate files
|
||||
is IrClass -> singleDeclaration.getInheritanceChainLength()
|
||||
// Initialize regular files after all open classes
|
||||
else -> Int.MAX_VALUE
|
||||
}
|
||||
|
||||
private fun IrClass.getInheritanceChainLength(): Int {
|
||||
if (symbol == backendContext.irBuiltIns.anyClass)
|
||||
return 0
|
||||
|
||||
// FIXME: Filter out interfaces
|
||||
superTypes.forEach { superType ->
|
||||
val superClass: IrClass? = superType.classOrNull?.owner
|
||||
if (superClass != null && /* !!! */ !superClass.isInterface)
|
||||
return superClass.getInheritanceChainLength() + 1
|
||||
}
|
||||
|
||||
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
private val IrModuleFragment.jsModuleName: String
|
||||
get() = name.asString()
|
||||
.replace("[.:@]".toRegex(), "_")
|
||||
.dropWhile { it == '<' }
|
||||
.dropLastWhile { it == '>' }
|
||||
|
||||
private fun List<JsStatement>.toJsCodeString(): String =
|
||||
JsCompositeBlock(this).toString()
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
PER_FILE
|
||||
}
|
||||
|
||||
fun generateEsModules(
|
||||
ir: LoweredIr,
|
||||
outputSink: CompilerOutputSink,
|
||||
mainArguments: List<String>?,
|
||||
granularity: JsGenerationGranularity,
|
||||
options: JsGenerationOptions,
|
||||
) {
|
||||
// Declaration numeration to create temporary GUID
|
||||
// TODO: Replace with an actual GUID
|
||||
val numerator = StaticDeclarationNumerator()
|
||||
ir.allModules.forEach { numerator.add(it) }
|
||||
|
||||
fun guid(declaration: IrDeclaration): String {
|
||||
val name = sanitizeName((declaration as IrDeclarationWithName).name.toString())
|
||||
val number = numerator.numeration[declaration]
|
||||
?: error("Can't find number for declaration ${declaration.fqNameWhenAvailable}")
|
||||
// TODO: Use shorter names in release mode
|
||||
return "${name}_GUID_${number}"
|
||||
}
|
||||
|
||||
val ir2js = IrToJs(ir.context, ::guid, outputSink, mainArguments, granularity, ir.mainModule.jsModuleName, options)
|
||||
ir2js.generateModules(ir.mainModule, ir.allModules)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.codegen
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
PER_FILE
|
||||
}
|
||||
@@ -48,7 +48,7 @@ data class ExportedConstructSignature(
|
||||
val returnType: ExportedType,
|
||||
) : ExportedDeclaration()
|
||||
|
||||
class ExportedProperty(
|
||||
data class ExportedProperty(
|
||||
val name: String,
|
||||
val type: ExportedType,
|
||||
val mutable: Boolean = true,
|
||||
@@ -94,7 +94,7 @@ data class ExportedObject(
|
||||
override val members: List<ExportedDeclaration>,
|
||||
override val nestedClasses: List<ExportedClass>,
|
||||
override val ir: IrClass,
|
||||
val irGetter: IrFunction
|
||||
val irGetter: IrSimpleFunction
|
||||
) : ExportedClass()
|
||||
|
||||
class ExportedParameter(
|
||||
|
||||
+82
-36
@@ -5,8 +5,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.prototypeOf
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
|
||||
@@ -17,8 +25,14 @@ class ExportModelToJsStatements(
|
||||
) {
|
||||
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
|
||||
|
||||
fun generateModuleExport(module: ExportedModule, internalModuleName: JsName): List<JsStatement> {
|
||||
return module.declarations.flatMap { generateDeclarationExport(it, JsNameRef(internalModuleName), esModules = false) }
|
||||
fun generateModuleExport(
|
||||
module: ExportedModule,
|
||||
internalModuleName: JsName?,
|
||||
esModules: Boolean
|
||||
): List<JsStatement> {
|
||||
return module.declarations.flatMap {
|
||||
generateDeclarationExport(it, internalModuleName?.makeRef(), esModules)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateDeclarationExport(
|
||||
@@ -60,17 +74,12 @@ class ExportModelToJsStatements(
|
||||
|
||||
is ExportedFunction -> {
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
if (esModules) {
|
||||
listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||
} else {
|
||||
if (namespace != null) {
|
||||
listOf(
|
||||
jsAssignment(
|
||||
jsElementAccess(declaration.name, namespace),
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
)
|
||||
} else emptyList()
|
||||
when {
|
||||
namespace != null ->
|
||||
listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt())
|
||||
|
||||
esModules -> listOf(JsExport(name, alias = JsName(declaration.name, false)))
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,39 +87,76 @@ class ExportModelToJsStatements(
|
||||
is ExportedConstructSignature -> emptyList()
|
||||
|
||||
is ExportedProperty -> {
|
||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
||||
val getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
val setter = declaration.irSetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, setter, namer).makeStmt())
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
val getter = declaration.irGetter?.let { namer.getNameForStaticDeclaration(it) }
|
||||
val setter = declaration.irSetter?.let { namer.getNameForStaticDeclaration(it) }
|
||||
if (namespace == null) {
|
||||
val property = JsVars.JsVar(
|
||||
JsName(declaration.name, false),
|
||||
JsObjectLiteral(false).apply {
|
||||
getter?.let {
|
||||
val fieldName = when (declaration.irGetter.origin) {
|
||||
JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION -> "getInstance"
|
||||
else -> "get"
|
||||
}
|
||||
propertyInitializers += JsPropertyInitializer(JsStringLiteral(fieldName), it.makeRef())
|
||||
}
|
||||
setter?.let { propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), it.makeRef()) }
|
||||
}
|
||||
)
|
||||
listOf(
|
||||
JsVars(property),
|
||||
JsExport(property.name, JsName(declaration.name, false))
|
||||
)
|
||||
} else {
|
||||
listOf(defineProperty(namespace, declaration.name, getter?.makeRef(), setter?.makeRef(), namer).makeStmt())
|
||||
}
|
||||
}
|
||||
|
||||
is ErrorDeclaration -> emptyList()
|
||||
|
||||
is ExportedObject -> {
|
||||
require(namespace != null) { "Only namespaced properties are allowed" }
|
||||
val newNameSpace = jsElementAccess(declaration.name, namespace)
|
||||
val getter = JsNameRef(namer.getNameForStaticDeclaration(declaration.irGetter))
|
||||
require(namespace != null || esModules) { "Only namespaced properties are allowed" }
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
else ->
|
||||
jsElementAccess(Namer.PROTOTYPE_NAME, namer.getNameForClass(declaration.ir).makeRef())
|
||||
}
|
||||
val staticsExport = declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
|
||||
listOf(defineProperty(namespace, declaration.name, getter, null, namer).makeStmt()) + staticsExport
|
||||
|
||||
val objectExport = when (namespace) {
|
||||
null -> generateDeclarationExport(
|
||||
ExportedProperty(declaration.name, ExportedType.Primitive.Any, irGetter = declaration.irGetter),
|
||||
namespace,
|
||||
esModules
|
||||
)
|
||||
|
||||
else -> listOf(
|
||||
defineProperty(
|
||||
namespace,
|
||||
declaration.name,
|
||||
namer.getNameForStaticDeclaration(declaration.irGetter).makeRef(),
|
||||
null,
|
||||
namer
|
||||
).makeStmt()
|
||||
)
|
||||
}
|
||||
|
||||
objectExport + staticsExport
|
||||
}
|
||||
|
||||
is ExportedRegularClass -> {
|
||||
if (declaration.isInterface) return emptyList()
|
||||
val newNameSpace = if (namespace != null)
|
||||
jsElementAccess(declaration.name, namespace)
|
||||
else
|
||||
prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer)
|
||||
val name = namer.getNameForStaticDeclaration(declaration.ir)
|
||||
val klassExport =
|
||||
if (esModules) {
|
||||
JsExport(name, alias = JsName(declaration.name, false))
|
||||
} else {
|
||||
if (namespace != null) {
|
||||
jsAssignment(
|
||||
newNameSpace,
|
||||
JsNameRef(name)
|
||||
).makeStmt()
|
||||
} else null
|
||||
val newNameSpace = when {
|
||||
namespace != null -> jsElementAccess(declaration.name, namespace)
|
||||
esModules -> name.makeRef()
|
||||
else -> prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer)
|
||||
}
|
||||
val klassExport = when {
|
||||
namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
|
||||
esModules -> JsExport(name, alias = JsName(declaration.name, false))
|
||||
else -> null
|
||||
}
|
||||
|
||||
// These are only used when exporting secondary constructors annotated with @JsName
|
||||
|
||||
+77
-49
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getFqNameWithJsNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||
@@ -15,11 +16,14 @@ import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import javax.lang.model.type.IntersectionType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
private const val Nullable = "Nullable"
|
||||
private const val objects = "_objects_"
|
||||
private const val declare = "declare "
|
||||
private const val declareExorted = "export $declare"
|
||||
|
||||
private const val NonExistent = "__NonExistent"
|
||||
private const val syntheticObjectNameSeparator = '$'
|
||||
|
||||
fun ExportedModule.toTypeScript(): String {
|
||||
@@ -61,32 +65,36 @@ class ExportModelToTsDeclarations {
|
||||
return joinToString("\n") {
|
||||
it.toTypeScript(
|
||||
indent = moduleKind.indent,
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else "export "
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else declareExorted,
|
||||
esModules = moduleKind == ModuleKind.ES
|
||||
)
|
||||
} + generateObjectsNamespaceIfNeeded(
|
||||
indent = moduleKind.indent,
|
||||
prefix = if (moduleKind == ModuleKind.PLAIN) "" else declare,
|
||||
)
|
||||
} + generateObjectsNamespaceIfNeeded(moduleKind.indent)
|
||||
}
|
||||
|
||||
private fun generateObjectsNamespaceIfNeeded(indent: String): String {
|
||||
private fun generateObjectsNamespaceIfNeeded(indent: String, prefix: String): String {
|
||||
return if (objectsSyntheticProperties.isEmpty()) {
|
||||
""
|
||||
} else {
|
||||
"\n" + ExportedNamespace(objects, objectsSyntheticProperties).toTypeScript(indent, "")
|
||||
"\n" + ExportedNamespace(objects, objectsSyntheticProperties).toTypeScript(indent, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||
|
||||
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String =
|
||||
private fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = "", esModules: Boolean = false): String =
|
||||
indent + when (this) {
|
||||
is ErrorDeclaration -> generateTypeScriptString()
|
||||
is ExportedNamespace -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedFunction -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedConstructor -> generateTypeScriptString(indent)
|
||||
is ExportedConstructSignature -> generateTypeScriptString(indent)
|
||||
is ExportedProperty -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedObject -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedNamespace -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedFunction -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedRegularClass -> generateTypeScriptString(indent, prefix)
|
||||
is ExportedProperty -> generateTypeScriptString(indent, prefix, esModules)
|
||||
is ExportedObject -> generateTypeScriptString(indent, prefix, esModules)
|
||||
}
|
||||
|
||||
private fun ErrorDeclaration.generateTypeScriptString(): String {
|
||||
@@ -107,34 +115,45 @@ class ExportModelToTsDeclarations {
|
||||
return "new($renderedParameters): ${returnType.toTypeScript(indent)};"
|
||||
}
|
||||
|
||||
private fun ExportedProperty.generateTypeScriptString(indent: String, prefix: String): String {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
val possibleStatic = if (isMember && isStatic) "static " else ""
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val memberName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
val typeToTypeScript = type.toTypeScript(indent)
|
||||
|
||||
return if (isMember && !isField) {
|
||||
val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
|
||||
if (!mutable) {
|
||||
getter
|
||||
} else {
|
||||
getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
|
||||
}
|
||||
} else {
|
||||
if (!isMember && containsUnresolvedChar) {
|
||||
""
|
||||
} else {
|
||||
val readonly = if (isMember && !mutable) "readonly " else ""
|
||||
private fun ExportedProperty.generateTypeScriptString(indent: String, prefix: String, esModules: Boolean = false): String {
|
||||
val extraIndent = "$indent "
|
||||
val optional = if (isOptional) "?" else ""
|
||||
"$prefix$visibility$possibleStatic$keyword$readonly$memberName$optional: $typeToTypeScript;"
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val memberName = if (containsUnresolvedChar) "\"$name\"" else name
|
||||
val isObjectGetter = irGetter?.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION
|
||||
|
||||
val typeToTypeScript = type.toTypeScript(if (!isMember && esModules && isObjectGetter) extraIndent else indent)
|
||||
|
||||
return if (isMember) {
|
||||
val static = if (isStatic) "static " else ""
|
||||
val abstract = if (isAbstract) "abstract " else ""
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
|
||||
if (isField) {
|
||||
val readonly = if (!mutable) "readonly " else ""
|
||||
"$prefix$visibility$static$abstract$readonly$memberName$optional: $typeToTypeScript;"
|
||||
} else {
|
||||
val getter = "$prefix$visibility$static${abstract}get $memberName(): $typeToTypeScript;"
|
||||
val setter = runIf(mutable) { "\n$indent$prefix$visibility$static${abstract}set $memberName(value: $typeToTypeScript);" }
|
||||
getter + setter.orEmpty()
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
containsUnresolvedChar -> ""
|
||||
esModules -> {
|
||||
if (isObjectGetter) {
|
||||
"${prefix}const $name: {\n${extraIndent}getInstance(): $typeToTypeScript;\n};"
|
||||
} else {
|
||||
val getter = "get(): $typeToTypeScript;"
|
||||
val setter = runIf(mutable) { " set(value: $typeToTypeScript): void;" }
|
||||
"${prefix}const $name: { $getter${setter.orEmpty()} };"
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val keyword = if (mutable) "let " else "const "
|
||||
"$prefix$keyword$memberName$optional: $typeToTypeScript;"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,14 +190,19 @@ class ExportModelToTsDeclarations {
|
||||
return if (!isMember && containsUnresolvedChar) {
|
||||
""
|
||||
} else {
|
||||
"${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
"$prefix$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExportedObject.generateTypeScriptString(indent: String, prefix: String): String {
|
||||
private fun ExportedObject.generateTypeScriptString(indent: String, prefix: String, esModules: Boolean = false): String {
|
||||
val shouldRenderSeparatedAbstractClass = !couldBeProperty()
|
||||
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members)
|
||||
val extraMembers = nestedClasses
|
||||
.takeIf { !shouldRenderSeparatedAbstractClass }
|
||||
?.map { it as ExportedObject }
|
||||
.orEmpty()
|
||||
|
||||
var t: ExportedType = ExportedType.InlineInterfaceType(members + extraMembers)
|
||||
|
||||
for (superInterface in superClasses + superInterfaces) {
|
||||
t = ExportedType.IntersectionType(t, superInterface)
|
||||
@@ -208,13 +232,14 @@ class ExportModelToTsDeclarations {
|
||||
)
|
||||
|
||||
return if (!shouldRenderSeparatedAbstractClass) {
|
||||
property.generateTypeScriptString(indent, prefix)
|
||||
property.generateTypeScriptString(indent, prefix, esModules)
|
||||
} else {
|
||||
val className = NonExistent.takeIf { esModules }.orEmpty() + name
|
||||
val propertyRef = "$objects.$propertyName"
|
||||
val shouldCreateExtraProperty = members.isNotEmpty() || superInterfaces.isNotEmpty() || superClasses.isNotEmpty()
|
||||
val newSuperClass = ExportedType.ClassType(propertyRef, emptyList(), ir).takeIf { shouldCreateExtraProperty }
|
||||
ExportedRegularClass(
|
||||
name = name,
|
||||
val classForRender = ExportedRegularClass(
|
||||
name = className,
|
||||
isInterface = false,
|
||||
isAbstract = true,
|
||||
superClasses = listOfNotNull(newSuperClass),
|
||||
@@ -224,8 +249,14 @@ class ExportModelToTsDeclarations {
|
||||
nestedClasses = nestedClasses,
|
||||
ir = ir
|
||||
)
|
||||
.generateTypeScriptString(indent, prefix)
|
||||
.also { if (shouldCreateExtraProperty) objectsSyntheticProperties.add(property) }
|
||||
|
||||
if (esModules && !property.isMember) {
|
||||
property.copy(type = ExportedType.TypeOf(className), name = name)
|
||||
.generateTypeScriptString(indent, prefix, esModules) + "\n${classForRender.generateTypeScriptString(indent, declare)}"
|
||||
} else {
|
||||
classForRender.generateTypeScriptString(indent, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +303,7 @@ class ExportModelToTsDeclarations {
|
||||
val klassExport =
|
||||
"$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
||||
val staticsExport =
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(
|
||||
indent,
|
||||
prefix
|
||||
) else ""
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
|
||||
|
||||
return if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class JsExecutableProducer(
|
||||
val jsMultiModuleCache = JsMultiModuleCache(caches)
|
||||
val cachedProgram = jsMultiModuleCache.loadProgramHeadersFromCache()
|
||||
|
||||
val resolver = CrossModuleDependenciesResolver(cachedProgram.map { it.jsIrHeader })
|
||||
val resolver = CrossModuleDependenciesResolver(moduleKind, cachedProgram.map { it.jsIrHeader })
|
||||
val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
|
||||
|
||||
jsMultiModuleCache.loadRequiredJsIrModules(crossModuleReferences)
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ class IrModuleToJsTransformer(
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val globalNames = NameTable<String>(namer.globalNames)
|
||||
val exportStatements = ExportModelToJsStatements(staticContext) { globalNames.declareFreshName(it, it) }
|
||||
.generateModuleExport(exportedModule, internalModuleName)
|
||||
.generateModuleExport(exportedModule, internalModuleName, false)
|
||||
|
||||
val (crossModuleImports, importedKotlinModules) = generateCrossModuleImports(nameGenerator, modules, dependencies, { JsName(sanitizeName(it), false) })
|
||||
val crossModuleExports = generateCrossModuleExports(modules, refInfo, internalModuleName)
|
||||
|
||||
+7
-5
@@ -94,6 +94,7 @@ class IrModuleToJsTransformerTmp(
|
||||
|
||||
private val mainModuleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
|
||||
|
||||
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<Pair<IrFile, List<ExportedDeclaration>>>)
|
||||
@@ -103,7 +104,7 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
private fun associateIrAndExport(modules: Iterable<IrModuleFragment>): List<IrAndExportedDeclarations> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||
|
||||
return modules.map { module ->
|
||||
val files = module.files.map { file ->
|
||||
@@ -166,7 +167,7 @@ class IrModuleToJsTransformerTmp(
|
||||
}
|
||||
|
||||
fun generateBinaryAst(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<JsIrFragmentAndBinaryAst> {
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = true)
|
||||
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
|
||||
|
||||
val exportData = files.map { it to exportModelGenerator.generateExportWithExternals(it) }
|
||||
|
||||
@@ -235,12 +236,13 @@ class IrModuleToJsTransformerTmp(
|
||||
polyfills.statements += backendContext.polyfills.getAllPolyfillsFor(file)
|
||||
}
|
||||
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules }
|
||||
val globalNames = NameTable<String>(globalNameScope)
|
||||
val exportStatements =
|
||||
ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport(
|
||||
ExportedModule(mainModuleName, moduleKind, exports),
|
||||
internalModuleName,
|
||||
isEsModules
|
||||
)
|
||||
|
||||
result.exports.statements += exportStatements
|
||||
@@ -379,7 +381,7 @@ private fun generateWrappedModuleBody(
|
||||
// mutable container allows explicitly remove elements from itself,
|
||||
// so we are able to help GC to free heavy JsIrModule objects
|
||||
// TODO: It makes sense to invent something better, because this logic can be easily broken
|
||||
val moduleToRef = program.asCrossModuleDependencies(relativeRequirePath).toMutableList()
|
||||
val moduleToRef = program.asCrossModuleDependencies(moduleKind, relativeRequirePath).toMutableList()
|
||||
val mainModule = moduleToRef.removeLast().let { (main, mainRef) ->
|
||||
generateSingleWrappedModuleBody(
|
||||
mainModuleName,
|
||||
@@ -433,7 +435,7 @@ fun generateSingleWrappedModuleBody(
|
||||
sourceMapsInfo: SourceMapsInfo?,
|
||||
generateScriptModule: Boolean,
|
||||
generateCallToMain: Boolean,
|
||||
crossModuleReferences: CrossModuleReferences = CrossModuleReferences.Empty,
|
||||
crossModuleReferences: CrossModuleReferences = CrossModuleReferences.Empty(moduleKind),
|
||||
outJsProgram: Boolean = true
|
||||
): CompilationOutputs {
|
||||
val program = Merger(
|
||||
|
||||
+32
-8
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
class JsIrProgramFragment(val packageFqn: String) {
|
||||
val nameBindings = mutableMapOf<String, JsName>()
|
||||
@@ -57,8 +58,8 @@ class JsIrModuleHeader(
|
||||
}
|
||||
|
||||
class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
fun asCrossModuleDependencies(relativeRequirePath: Boolean): List<Pair<JsIrModule, CrossModuleReferences>> {
|
||||
val resolver = CrossModuleDependenciesResolver(modules.map { it.makeModuleHeader() })
|
||||
fun asCrossModuleDependencies(moduleKind: ModuleKind, relativeRequirePath: Boolean): List<Pair<JsIrModule, CrossModuleReferences>> {
|
||||
val resolver = CrossModuleDependenciesResolver(moduleKind, modules.map { it.makeModuleHeader() })
|
||||
modules = emptyList()
|
||||
val crossModuleReferences = resolver.resolveCrossModuleDependencies(relativeRequirePath)
|
||||
return crossModuleReferences.entries.map {
|
||||
@@ -75,9 +76,12 @@ class JsIrProgram(private var modules: List<JsIrModule>) {
|
||||
}
|
||||
}
|
||||
|
||||
class CrossModuleDependenciesResolver(private val headers: List<JsIrModuleHeader>) {
|
||||
class CrossModuleDependenciesResolver(
|
||||
private val moduleKind: ModuleKind,
|
||||
private val headers: List<JsIrModuleHeader>
|
||||
) {
|
||||
fun resolveCrossModuleDependencies(relativeRequirePath: Boolean): Map<JsIrModuleHeader, CrossModuleReferences> {
|
||||
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferecenceBuilder(it, relativeRequirePath) }
|
||||
val headerToBuilder = headers.associateWith { JsIrModuleCrossModuleReferecenceBuilder(moduleKind, it, relativeRequirePath) }
|
||||
val definitionModule = mutableMapOf<String, JsIrModuleCrossModuleReferecenceBuilder>()
|
||||
|
||||
val mainModuleHeader = headers.last()
|
||||
@@ -110,7 +114,11 @@ private fun String.prettyTag() = takeWhile { c -> c != '|' }
|
||||
|
||||
private class CrossModuleRef(val module: JsIrModuleCrossModuleReferecenceBuilder, val tag: String)
|
||||
|
||||
private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHeader, val relativeRequirePath: Boolean) {
|
||||
private class JsIrModuleCrossModuleReferecenceBuilder(
|
||||
val moduleKind: ModuleKind,
|
||||
val header: JsIrModuleHeader,
|
||||
val relativeRequirePath: Boolean
|
||||
) {
|
||||
val imports = mutableListOf<CrossModuleRef>()
|
||||
val exports = mutableSetOf<String>()
|
||||
var transitiveJsExportFrom = emptyList<JsIrModuleHeader>()
|
||||
@@ -155,7 +163,13 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHead
|
||||
val transitiveExport = transitiveJsExportFrom.mapNotNull {
|
||||
if (it.hasJsExports) import(it) else null
|
||||
}
|
||||
return CrossModuleReferences(importedModules.values.toList(), transitiveExport, exportNames, resultImports)
|
||||
return CrossModuleReferences(
|
||||
moduleKind,
|
||||
importedModules.values.toList(),
|
||||
transitiveExport,
|
||||
exportNames,
|
||||
resultImports
|
||||
)
|
||||
}
|
||||
|
||||
private fun relativeRequirePath(moduleHeader: JsIrModuleHeader): String? {
|
||||
@@ -177,6 +191,7 @@ private class JsIrModuleCrossModuleReferecenceBuilder(val header: JsIrModuleHead
|
||||
class CrossModuleImport(val exportedAs: String, val moduleExporter: JsName)
|
||||
|
||||
class CrossModuleReferences(
|
||||
val moduleKind: ModuleKind,
|
||||
val importedModules: List<JsImportedModule>, // additional Kotlin imported modules
|
||||
val transitiveJsExportFrom: List<JsName>, // the list of modules which provide their js exports for transitive export
|
||||
val exports: Map<String, String>, // tag -> index
|
||||
@@ -190,12 +205,21 @@ class CrossModuleReferences(
|
||||
val tagToName = module.fragments.flatMap { it.nameBindings.entries }.associate { it.key to it.value }
|
||||
jsImports = imports.entries.associate {
|
||||
val importedAs = tagToName[it.key] ?: error("Internal error: cannot find imported name for symbol ${it.key.prettyTag()}")
|
||||
val exportRef = JsNameRef(it.value.exportedAs, ReservedJsNames.makeCrossModuleNameRef(it.value.moduleExporter))
|
||||
val exportRef = JsNameRef(
|
||||
it.value.exportedAs,
|
||||
it.value.moduleExporter.let {
|
||||
if (moduleKind == ModuleKind.ES) {
|
||||
it.makeRef()
|
||||
} else {
|
||||
ReservedJsNames.makeCrossModuleNameRef(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
it.key to JsVars.JsVar(importedAs, exportRef)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Empty = CrossModuleReferences(listOf(), emptyList(), emptyMap(), emptyMap())
|
||||
fun Empty(moduleKind: ModuleKind) = CrossModuleReferences(moduleKind, listOf(), emptyList(), emptyMap(), emptyMap())
|
||||
}
|
||||
}
|
||||
|
||||
+28
-4
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.partitionIsInstance
|
||||
|
||||
class Merger(
|
||||
private val moduleName: String,
|
||||
@@ -20,6 +21,7 @@ class Merger(
|
||||
private val generateCallToMain: Boolean,
|
||||
) {
|
||||
|
||||
private val isEsModules = moduleKind == ModuleKind.ES
|
||||
private val importStatements = mutableMapOf<String, JsStatement>()
|
||||
private val importedModulesMap = mutableMapOf<JsImportedModuleKey, JsImportedModule>()
|
||||
|
||||
@@ -65,6 +67,14 @@ class Merger(
|
||||
if (crossModuleReferences.exports.isNotEmpty()) {
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
|
||||
if (isEsModules) {
|
||||
val exportedElements = crossModuleReferences.exports.entries.map { (tag, hash) ->
|
||||
val internalName = nameMap[tag] ?: error("Missing name for declaration '$tag'")
|
||||
JsExport.Element(internalName, JsName(hash, false))
|
||||
}
|
||||
|
||||
additionalExports += JsExport(JsExport.Subject.Elements(exportedElements))
|
||||
} else {
|
||||
val createExportBlock = jsAssignment(
|
||||
ReservedJsNames.makeCrossModuleNameRef(internalModuleName),
|
||||
JsAstUtils.or(ReservedJsNames.makeCrossModuleNameRef(internalModuleName), JsObjectLiteral())
|
||||
@@ -78,6 +88,7 @@ class Merger(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsIrProgramFragment.buildRenames(nameMap: MutableMap<String, JsName>): Map<JsName, JsName> {
|
||||
val result = mutableMapOf<JsName, JsName>()
|
||||
@@ -125,6 +136,21 @@ class Merger(
|
||||
}
|
||||
|
||||
private fun declareAndCallJsExporter(): List<JsStatement> {
|
||||
if (isEsModules) {
|
||||
val allExportRelatedStatements = fragments.flatMap { it.exports.statements }
|
||||
val (allExportStatements, restStatements) = allExportRelatedStatements.partitionIsInstance<JsStatement, JsExport>()
|
||||
val (currentModuleExportStatements, restExportStatements) = allExportStatements.partition { it.fromModule == null }
|
||||
val exportedElements = currentModuleExportStatements.takeIf { it.isNotEmpty() }
|
||||
?.asSequence()
|
||||
?.flatMap { (it.subject as JsExport.Subject.Elements).elements }
|
||||
?.distinctBy { (it.alias ?: it.name).ident }
|
||||
?.map { if (it.name.ident == it.alias?.ident) JsExport.Element(it.name, null) else it }
|
||||
?.toList()
|
||||
|
||||
val oneLargeExportStatement = exportedElements?.let { JsExport(JsExport.Subject.Elements(it)) }
|
||||
|
||||
return restStatements + listOfNotNull(oneLargeExportStatement) + restExportStatements
|
||||
} else {
|
||||
val exportBody = JsBlock(fragments.flatMap { it.exports.statements })
|
||||
if (exportBody.isEmpty) {
|
||||
return emptyList()
|
||||
@@ -145,6 +171,7 @@ class Merger(
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun transitiveJsExport(): List<JsStatement> {
|
||||
val internalModuleName = ReservedJsNames.makeInternalModuleName()
|
||||
@@ -214,9 +241,6 @@ class Merger(
|
||||
|
||||
if (generateScriptModule) {
|
||||
with(program.globalBlock) {
|
||||
if (!generateScriptModule) {
|
||||
statements += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
statements.addWithComment("block: polyfills", polyfillDeclarationBlock.statements)
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
statements += moduleBody
|
||||
@@ -228,7 +252,7 @@ class Merger(
|
||||
parameters += JsParameter(internalModuleName)
|
||||
parameters += (importedJsModules).map { JsParameter(it.internalName) }
|
||||
with(body) {
|
||||
if (!generateScriptModule) {
|
||||
if (!isEsModules) {
|
||||
statements += JsStringLiteral("use strict").makeStmt()
|
||||
}
|
||||
statements.addWithComment("block: imports", importStatements)
|
||||
|
||||
+17
-2
@@ -17,7 +17,7 @@ object ModuleWrapperTranslation {
|
||||
}
|
||||
|
||||
fun wrap(
|
||||
moduleId: String, function: JsExpression, importedModules: List<JsImportedModule>,
|
||||
moduleId: String, function: JsFunction, importedModules: List<JsImportedModule>,
|
||||
program: JsProgram, kind: ModuleKind
|
||||
): List<JsStatement> {
|
||||
return when (kind) {
|
||||
@@ -25,7 +25,7 @@ object ModuleWrapperTranslation {
|
||||
ModuleKind.COMMON_JS -> wrapCommonJs(function, importedModules, program)
|
||||
ModuleKind.UMD -> wrapUmd(moduleId, function, importedModules, program)
|
||||
ModuleKind.PLAIN -> wrapPlain(moduleId, function, importedModules, program)
|
||||
ModuleKind.ES -> error("ES modules are not supported in legacy wrapper")
|
||||
ModuleKind.ES -> wrapEsModule(function, importedModules)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,21 @@ object ModuleWrapperTranslation {
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapEsModule(function: JsFunction, importedModules: List<JsImportedModule>): List<JsStatement> {
|
||||
val importStatements = importedModules.zip(function.parameters.drop(1)).map {
|
||||
JsImport(
|
||||
it.first.externalName,
|
||||
if (it.first.plainReference == null) {
|
||||
JsImport.Target.All(alias = it.second.name)
|
||||
} else {
|
||||
JsImport.Target.Default(name = it.second.name)
|
||||
}
|
||||
)
|
||||
}
|
||||
return importStatements + function.body.statements.dropLast(1)
|
||||
}
|
||||
|
||||
|
||||
private fun wrapPlain(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<JsImportedModule>, program: JsProgram
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.IrToJs
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrDeclarationReference
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsImport
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsName
|
||||
|
||||
class StaticDeclarationNumerator {
|
||||
var currentNumber = 0
|
||||
val numeration = mutableMapOf<IrDeclaration, Int>()
|
||||
|
||||
fun add(moduleFragment: IrModuleFragment) {
|
||||
moduleFragment.files.forEach { add(it) }
|
||||
}
|
||||
|
||||
fun add(declaration: IrDeclaration) {
|
||||
// TODO: We should not visit declarations multiple times.
|
||||
// Investigate enum tests in dce-driven mode.
|
||||
if (declaration !in numeration) {
|
||||
numeration[declaration] = currentNumber
|
||||
currentNumber++
|
||||
}
|
||||
}
|
||||
|
||||
fun add(packageFragment: IrPackageFragment) {
|
||||
packageFragment.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
if (declaration !is IrVariable) {
|
||||
add(declaration)
|
||||
}
|
||||
super.visitDeclaration(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class NewStableStaticNamesCollectorVisitor(val needToCollectReferences: Boolean) : IrElementVisitorVoid {
|
||||
val collectedStableNames = mutableSetOf<String>()
|
||||
|
||||
init {
|
||||
collectedStableNames.addAll(RESERVED_IDENTIFIERS)
|
||||
collectedStableNames.add(Namer.IMPLICIT_RECEIVER_NAME)
|
||||
}
|
||||
|
||||
private fun IrDeclaration.collectStableName() {
|
||||
collectedStableNames += stableNameForExternalDeclaration(this) ?: return
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||
super.visitDeclaration(declaration)
|
||||
declaration.collectStableName()
|
||||
}
|
||||
|
||||
override fun visitDeclarationReference(expression: IrDeclarationReference) {
|
||||
super.visitDeclarationReference(expression)
|
||||
if (needToCollectReferences) {
|
||||
val declaration = expression.symbol.owner as? IrDeclaration
|
||||
declaration?.collectStableName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NewNamerImpl(
|
||||
val context: JsIrBackendContext,
|
||||
val unit: IrToJs.CodegenUnit,
|
||||
val exportId: (IrDeclarationWithName) -> String,
|
||||
stableNames: Set<String>,
|
||||
) : IrNamerBase() {
|
||||
val staticNames = NameTable<IrDeclaration>(
|
||||
reserved = stableNames.toMutableSet()
|
||||
)
|
||||
val internalImports = mutableMapOf<String, JsImport>()
|
||||
|
||||
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
|
||||
require(function.dispatchReceiverParameter != null)
|
||||
val name = jsFunctionSignature(function, context)
|
||||
return name.toJsName()
|
||||
}
|
||||
|
||||
override fun getNameForMemberField(field: IrField): JsName {
|
||||
val fieldName = sanitizeName(
|
||||
try {
|
||||
exportId(field)
|
||||
} catch (e: IllegalStateException) {
|
||||
// TODO: Fix DCE with inline classes and remove this hack
|
||||
field.name.asString() + "_LIKELY_ELIMINATED_BY_DCE"
|
||||
}
|
||||
)
|
||||
// TODO: Webpack not minimize member names, it is long name, which is not minimized, so it affects final JS bundle size
|
||||
// Use shorter names
|
||||
return JsName("f_$fieldName", false)
|
||||
}
|
||||
|
||||
override fun getNameForStaticDeclaration(declaration: IrDeclarationWithName): JsName {
|
||||
staticNames.names[declaration]?.let { return JsName(it, false) }
|
||||
|
||||
fun registerImport(moduleId: String, importedName: String) {
|
||||
val fullModuleId = if (moduleId.startsWith(".")) {
|
||||
unit.pathToKotlinModulesRoot + moduleId
|
||||
} else {
|
||||
// TODO: Do we cover this path in tests?
|
||||
moduleId
|
||||
}
|
||||
|
||||
val import = internalImports.getOrPut(fullModuleId) {
|
||||
JsImport(fullModuleId)
|
||||
}
|
||||
import.elements += JsImport.Element(importedName, staticNames.names[declaration]!!)
|
||||
}
|
||||
|
||||
if (declaration.isEffectivelyExternal()) {
|
||||
val jsModule: String? = declaration.getJsModule()
|
||||
val maybeParentFile: IrFile? = declaration.parent as? IrFile
|
||||
val fileJsModule: String? = maybeParentFile?.getJsModule()
|
||||
val jsQualifier: String? = maybeParentFile?.getJsQualifier()
|
||||
|
||||
when {
|
||||
jsModule != null -> {
|
||||
// TODO: Support jsQualifier
|
||||
staticNames.declareFreshName(declaration, declaration.name.asString())
|
||||
registerImport(jsModule, "default")
|
||||
}
|
||||
|
||||
fileJsModule != null -> {
|
||||
// TODO: Support jsQualifier
|
||||
staticNames.declareFreshName(declaration, declaration.name.asString())
|
||||
registerImport(fileJsModule, declaration.getJsNameOrKotlinName().identifier)
|
||||
}
|
||||
|
||||
else -> {
|
||||
var name = declaration.getJsNameOrKotlinName().identifier
|
||||
if (jsQualifier != null)
|
||||
name = "$jsQualifier.$name"
|
||||
|
||||
staticNames.declareStableName(declaration, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else { // Non-external declaration
|
||||
val name = declaration.nameIfPropertyAccessor() ?: declaration.name.asString()
|
||||
staticNames.declareFreshName(declaration, name)
|
||||
val unitReference = unit.referenceCodegenUnitOfDeclaration(declaration)
|
||||
if (unitReference is IrToJs.OtherUnitReference) {
|
||||
registerImport(unitReference.importPath, exportId(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
return JsName(staticNames.names[declaration]!!, false)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Cache?
|
||||
private fun stableNameForExternalDeclaration(declaration: IrDeclaration): String? {
|
||||
if (declaration !is IrDeclarationWithName ||
|
||||
!declaration.hasStaticDispatch() ||
|
||||
!declaration.isEffectivelyExternal() ||
|
||||
declaration.isPropertyAccessor ||
|
||||
declaration.isPropertyField
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (declaration is IrConstructor) {
|
||||
return stableNameForExternalDeclaration(declaration.parentAsClass)
|
||||
}
|
||||
|
||||
val importedFromModuleOnly =
|
||||
declaration.getJsModule() != null && !declaration.isJsNonModule()
|
||||
|
||||
val jsName = declaration.getJsName()
|
||||
|
||||
val jsQualifier = declaration.fileOrNull?.getJsQualifier()
|
||||
|
||||
return when {
|
||||
importedFromModuleOnly ->
|
||||
null
|
||||
|
||||
jsQualifier != null ->
|
||||
jsQualifier.split('1')[0]
|
||||
|
||||
jsName != null ->
|
||||
jsName
|
||||
|
||||
else ->
|
||||
declaration.name.identifier
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ where possible options include:
|
||||
-libraries <path> Paths to Kotlin libraries with .meta.js and .kjsm files, separated by system path separator
|
||||
-main {call|noCall} Define whether the `main` function should be called upon execution
|
||||
-meta-info Generate .meta.js and .kjsm files with metadata. Use to create a library
|
||||
-module-kind {plain|amd|commonjs|umd}
|
||||
-module-kind {plain|amd|commonjs|umd|es}
|
||||
Kind of the JS module generated by the compiler
|
||||
-no-stdlib Don't automatically include the default Kotlin/JS stdlib into compilation dependencies
|
||||
-output <filepath> Destination *.js file for the compilation result
|
||||
|
||||
@@ -34,8 +34,6 @@ object BinaryArtifacts {
|
||||
|
||||
class JsIrArtifact(override val outputFile: File, val compilerResult: CompilerResult, val icCache: Map<String, ByteArray>? = null) : Js()
|
||||
|
||||
class JsEsArtifact(override val outputFile: File, val outputDceFile: File?) : Js()
|
||||
|
||||
data class IncrementalJsArtifact(val originalArtifact: Js, val recompiledArtifact: Js) : Js() {
|
||||
override val outputFile: File
|
||||
get() = unwrap().outputFile
|
||||
|
||||
@@ -1329,15 +1329,27 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
|
||||
@Override
|
||||
public void visitImport(@NotNull JsImport jsImport) {
|
||||
p.print("import {");
|
||||
boolean isMultiline = jsImport.getElements().size() > 1;
|
||||
JsImport.Target target = jsImport.getTarget();
|
||||
|
||||
p.print("import ");
|
||||
|
||||
if (target instanceof JsImport.Target.Default) {
|
||||
nameDef(((JsImport.Target.Default) target).getName());
|
||||
} else if (target instanceof JsImport.Target.All) {
|
||||
p.print("* as ");
|
||||
nameDef(((JsImport.Target.All) target).getAlias());
|
||||
} else if (target instanceof JsImport.Target.Elements) {
|
||||
List<JsImport.Element> elements = ((JsImport.Target.Elements) target).getElements();
|
||||
|
||||
p.print("{");
|
||||
boolean isMultiline = elements.size() > 1;
|
||||
p.indentIn();
|
||||
if (isMultiline)
|
||||
newlineOpt();
|
||||
else
|
||||
space();
|
||||
|
||||
for (JsImport.Element element : jsImport.getElements()) {
|
||||
for (JsImport.Element element : elements) {
|
||||
nameDef(element.getName());
|
||||
JsName alias = element.getAlias();
|
||||
if (alias != null) {
|
||||
@@ -1348,12 +1360,16 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
if (isMultiline) {
|
||||
p.print(',');
|
||||
newlineOpt();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
space();
|
||||
}
|
||||
}
|
||||
p.indentOut();
|
||||
p.print("} from ");
|
||||
p.print("}");
|
||||
}
|
||||
|
||||
p.print(" from ");
|
||||
p.print(javaScriptString(jsImport.getModule()));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,23 @@ package org.jetbrains.kotlin.js.backend.ast
|
||||
|
||||
class JsImport(
|
||||
val module: String,
|
||||
val elements: MutableList<Element> = mutableListOf(),
|
||||
val target: Target,
|
||||
) : SourceInfoAwareJsNode(), JsStatement {
|
||||
constructor(module: String, elements: MutableList<Element> = mutableListOf()) : this(module, Target.Elements(elements))
|
||||
|
||||
val elements: MutableList<Element>
|
||||
get() = (target as Target.Elements).elements
|
||||
|
||||
sealed class Target {
|
||||
class Elements(val elements: MutableList<Element>) : Target()
|
||||
class Default(val name: JsName) : Target() {
|
||||
constructor(name: String) : this(JsName(name, false))
|
||||
}
|
||||
|
||||
class All(val alias: JsName) : Target() {
|
||||
constructor(alias: String) : this(JsName(alias, false))
|
||||
}
|
||||
}
|
||||
|
||||
class Element(
|
||||
val name: JsName,
|
||||
@@ -25,7 +40,7 @@ class JsImport(
|
||||
}
|
||||
|
||||
override fun deepCopy(): JsStatement =
|
||||
JsImport(module, elements.map { it }.toMutableList())
|
||||
JsImport(module, target)
|
||||
|
||||
override fun traverse(v: JsVisitorWithContext, ctx: JsContext<*>) {
|
||||
v.visit(this, ctx)
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.text.StringUtil
|
||||
|
||||
private val LINE_SEPARATOR = System.getProperty("line.separator")!!
|
||||
private val END_MARKER = "<END>$LINE_SEPARATOR"
|
||||
private val ESM_EXTENSION = ".mjs"
|
||||
|
||||
abstract class ProcessBasedScriptEngine(
|
||||
private val executablePath: String
|
||||
@@ -58,6 +59,7 @@ abstract class ProcessBasedScriptEngine(
|
||||
}
|
||||
|
||||
override fun loadFile(path: String) {
|
||||
if (path.endsWith(ESM_EXTENSION)) return
|
||||
eval("load('${path.replace('\\', '/')}');")
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,8 @@ function restoreGlobalState() {
|
||||
resetRealm();
|
||||
|
||||
// noinspection InfiniteLoopJS
|
||||
while (true) {
|
||||
async function loop() {
|
||||
while (true) {
|
||||
let code = readline().replace(/\\n/g, '\n');
|
||||
|
||||
try {
|
||||
@@ -90,7 +91,7 @@ while (true) {
|
||||
restoreGlobalState();
|
||||
break;
|
||||
default:
|
||||
print(Realm.eval(currentRealmIndex, code));
|
||||
print(await Realm.eval(currentRealmIndex, code));
|
||||
}
|
||||
} catch(e) {
|
||||
printErr(e.stack != null ? e.stack : e.toString());
|
||||
@@ -98,4 +99,7 @@ while (true) {
|
||||
}
|
||||
|
||||
print('<END>');
|
||||
}
|
||||
}
|
||||
|
||||
loop()
|
||||
@@ -23,4 +23,7 @@ object JavaScript {
|
||||
|
||||
const val EXTENSION = "js"
|
||||
const val DOT_EXTENSION = "." + EXTENSION
|
||||
|
||||
const val MODULE_EXTENSION = "mjs"
|
||||
const val DOT_MODULE_EXTENSION = "." + MODULE_EXTENSION
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class AntTaskJsTest extends AbstractAntTaskTest {
|
||||
|
||||
List<String> filePaths = CollectionsKt.map(fileNames, s -> getOutputFileByName(s).getAbsolutePath());
|
||||
|
||||
(useNashorn ? NashornJsTestChecker.INSTANCE : V8JsTestChecker.INSTANCE).check(filePaths, "out", "foo", "box", "OK", withModuleSystem);
|
||||
(useNashorn ? NashornJsTestChecker.INSTANCE : V8JsTestChecker.INSTANCE).check(filePaths, "out", "foo", "box", "OK", withModuleSystem, null);
|
||||
}
|
||||
|
||||
private void doJsAntTestForPostfixPrefix(@Nullable String prefix, @Nullable String postfix) throws Exception {
|
||||
|
||||
@@ -51,9 +51,7 @@ class ClassicJsBackendFacade(
|
||||
"$KOTLIN_TEST_INTERNAL.setModuleId(\"$escapedModuleId\"); }\n" +
|
||||
"$content\n"
|
||||
|
||||
ModuleKind.PLAIN -> content
|
||||
|
||||
ModuleKind.ES -> error("Module emulation markers are not supported for ES modules")
|
||||
ModuleKind.PLAIN, ModuleKind.ES -> content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,36 +12,29 @@ import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDe
|
||||
import org.jetbrains.kotlin.cli.common.isWindows
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.CompilerOutputSink
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationOptions
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.generateEsModules
|
||||
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
|
||||
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.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformerTmp
|
||||
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.irMessageLogger
|
||||
import org.jetbrains.kotlin.js.config.ErrorTolerancePolicy
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION
|
||||
import org.jetbrains.kotlin.js.test.utils.esModulesSubDir
|
||||
import org.jetbrains.kotlin.js.test.utils.extractTestPackage
|
||||
import org.jetbrains.kotlin.js.test.utils.jsIrIncrementalDataProvider
|
||||
import org.jetbrains.kotlin.library.uniqueName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.DebugMode
|
||||
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.frontend.classic.moduleDescriptorProvider
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
@@ -49,6 +42,9 @@ import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurato
|
||||
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
|
||||
import java.io.File
|
||||
|
||||
const val REGULAR_EXTENSION = ".js"
|
||||
const val ESM_EXTENSION = ".mjs"
|
||||
|
||||
class JsIrBackendFacade(
|
||||
val testServices: TestServices,
|
||||
private val firstTimeCompilation: Boolean
|
||||
@@ -91,16 +87,16 @@ class JsIrBackendFacade(
|
||||
else -> JsGenerationGranularity.WHOLE_PROGRAM
|
||||
}
|
||||
|
||||
val testPackage = extractTestPackage(testServices)
|
||||
val testPackage = extractTestPackage(testServices, ignoreEsModules = false)
|
||||
val skipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE in module.directives
|
||||
|
||||
if (skipRegularMode) return null
|
||||
|
||||
if (JsEnvironmentConfigurator.incrementalEnabled(testServices)) {
|
||||
val outputFile = if (firstTimeCompilation) {
|
||||
File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name) + ".js")
|
||||
File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name) + module.kind.extension)
|
||||
} else {
|
||||
File(JsEnvironmentConfigurator.getRecompiledJsModuleArtifactPath(testServices, module.name) + ".js")
|
||||
File(JsEnvironmentConfigurator.getRecompiledJsModuleArtifactPath(testServices, module.name) + module.kind.extension)
|
||||
}
|
||||
|
||||
val compiledModule = CompilerResult(
|
||||
@@ -136,11 +132,12 @@ class JsIrBackendFacade(
|
||||
PhaseConfig(jsPhases)
|
||||
}
|
||||
|
||||
|
||||
val loweredIr = compileIr(
|
||||
irModuleFragment,
|
||||
irModuleFragment.apply { resolveTestPathes() },
|
||||
MainModule.Klib(inputArtifact.outputFile.absolutePath),
|
||||
configuration,
|
||||
dependencyModules,
|
||||
dependencyModules.apply { forEach { it.resolveTestPathes() } },
|
||||
emptyMap(),
|
||||
irModuleFragment.irBuiltins,
|
||||
symbolTable,
|
||||
@@ -164,22 +161,27 @@ class JsIrBackendFacade(
|
||||
module: TestModule,
|
||||
loweredIr: LoweredIr,
|
||||
granularity: JsGenerationGranularity,
|
||||
): BinaryArtifacts.Js? {
|
||||
val generateDts = JsEnvironmentConfigurationDirectives.GENERATE_DTS in module.directives
|
||||
): BinaryArtifacts.Js {
|
||||
val mainArguments = JsEnvironmentConfigurator.getMainCallParametersForModule(module)
|
||||
.run { if (shouldBeGenerated()) arguments() else null }
|
||||
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
|
||||
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in module.directives
|
||||
val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives
|
||||
val runNewIr2Js = JsEnvironmentConfigurationDirectives.RUN_NEW_IR_2_JS in module.directives
|
||||
val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives
|
||||
val isEsModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives ||
|
||||
module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND].contains(ModuleKind.ES)
|
||||
|
||||
val outputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL) + module.kind.extension)
|
||||
|
||||
val outputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL) + ".js")
|
||||
val dceOutputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL_DCE_MINIMIZED_NAMES) + ".js")
|
||||
if (!esModules) {
|
||||
if (runNewIr2Js) {
|
||||
val transformer = IrModuleToJsTransformerTmp(loweredIr.context, mainArguments)
|
||||
|
||||
val transformer = IrModuleToJsTransformerTmp(
|
||||
loweredIr.context,
|
||||
mainArguments,
|
||||
moduleToName = JsIrModuleToPath(
|
||||
testServices,
|
||||
isEsModules && granularity != JsGenerationGranularity.WHOLE_PROGRAM
|
||||
)
|
||||
)
|
||||
// If runIrDce then include DCE results
|
||||
// If perModuleOnly then skip whole program
|
||||
// (it.dce => runIrDce) && (perModuleOnly => it.perModule)
|
||||
@@ -203,15 +205,10 @@ class JsIrBackendFacade(
|
||||
}
|
||||
}
|
||||
|
||||
val options = JsGenerationOptions(generatePackageJson = true, generateTypeScriptDefinitions = generateDts)
|
||||
generateEsModules(loweredIr, jsOutputSink(outputFile.parentFile.esModulesSubDir), mainArguments, granularity, options)
|
||||
|
||||
if (runIrDce) {
|
||||
eliminateDeadDeclarations(loweredIr.allModules, loweredIr.context)
|
||||
generateEsModules(loweredIr, jsOutputSink(dceOutputFile.parentFile.esModulesSubDir), mainArguments, granularity, options)
|
||||
return BinaryArtifacts.Js.JsEsArtifact(outputFile, dceOutputFile).dump(module)
|
||||
private fun IrModuleFragment.resolveTestPathes() {
|
||||
JsIrPathReplacer(testServices).let {
|
||||
files.forEach(it::lower)
|
||||
}
|
||||
return BinaryArtifacts.Js.JsEsArtifact(outputFile, null).dump(module)
|
||||
}
|
||||
|
||||
private fun loadIrFromKlib(module: TestModule, configuration: CompilerConfiguration): IrModuleInfo {
|
||||
@@ -238,55 +235,10 @@ class JsIrBackendFacade(
|
||||
) { if (it == mainModuleLib) moduleDescriptor else testServices.jsLibraryProvider.getDescriptorByCompiledLibrary(it) }
|
||||
}
|
||||
|
||||
private fun loadIrFromSources(
|
||||
private fun BinaryArtifacts.Js.JsIrArtifact.dump(
|
||||
module: TestModule,
|
||||
configuration: CompilerConfiguration,
|
||||
inputArtifact: ClassicFrontendOutputArtifact
|
||||
): IrModuleInfo {
|
||||
val errorPolicy = configuration.get(JSConfigurationKeys.ERROR_TOLERANCE_POLICY) ?: ErrorTolerancePolicy.DEFAULT
|
||||
val messageLogger = configuration.irMessageLogger
|
||||
val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImplForJsIC(WholeWorldStageController()),)
|
||||
val verifySignatures = JsEnvironmentConfigurationDirectives.SKIP_MANGLE_VERIFICATION !in module.directives
|
||||
|
||||
val psi2Ir = Psi2IrTranslator(
|
||||
configuration.languageVersionSettings,
|
||||
Psi2IrConfiguration(errorPolicy.allowErrors),
|
||||
messageLogger::checkNoUnboundSymbols
|
||||
)
|
||||
val psi2IrContext = psi2Ir.createGeneratorContext(
|
||||
inputArtifact.analysisResult.moduleDescriptor,
|
||||
inputArtifact.analysisResult.bindingContext,
|
||||
symbolTable
|
||||
)
|
||||
|
||||
return getIrModuleInfoForSourceFiles(
|
||||
psi2IrContext,
|
||||
inputArtifact.project,
|
||||
configuration,
|
||||
inputArtifact.allKtFiles.values.toList(),
|
||||
sortDependencies(JsEnvironmentConfigurator.getAllRecursiveLibrariesFor(module, testServices)),
|
||||
emptyMap(),
|
||||
symbolTable,
|
||||
messageLogger,
|
||||
loadFunctionInterfacesIntoStdlib = true,
|
||||
verifySignatures,
|
||||
) { testServices.jsLibraryProvider.getDescriptorByCompiledLibrary(it) }
|
||||
}
|
||||
|
||||
private fun jsOutputSink(perFileOutputDir: File): CompilerOutputSink {
|
||||
perFileOutputDir.deleteRecursively()
|
||||
perFileOutputDir.mkdirs()
|
||||
|
||||
return object : CompilerOutputSink {
|
||||
override fun write(module: String, path: String, content: String) {
|
||||
val file = File(File(perFileOutputDir, module), path)
|
||||
file.parentFile.mkdirs()
|
||||
file.writeText(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryArtifacts.Js.JsIrArtifact.dump(module: TestModule, firstTimeCompilation: Boolean = true): BinaryArtifacts.Js.JsIrArtifact {
|
||||
firstTimeCompilation: Boolean = true
|
||||
): BinaryArtifacts.Js.JsIrArtifact {
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val moduleId = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)
|
||||
val moduleKind = configuration.get(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
|
||||
@@ -297,9 +249,15 @@ class JsIrBackendFacade(
|
||||
if (dontSkipRegularMode) {
|
||||
for ((mode, output) in compilerResult.outputs.entries) {
|
||||
val outputFile = if (firstTimeCompilation) {
|
||||
File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + ".js")
|
||||
File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + moduleKind.extension)
|
||||
} else {
|
||||
File(JsEnvironmentConfigurator.getRecompiledJsModuleArtifactPath(testServices, module.name, mode) + ".js")
|
||||
File(
|
||||
JsEnvironmentConfigurator.getRecompiledJsModuleArtifactPath(
|
||||
testServices,
|
||||
module.name,
|
||||
mode
|
||||
) + moduleKind.extension
|
||||
)
|
||||
}
|
||||
output.writeTo(outputFile, moduleId, moduleKind)
|
||||
}
|
||||
@@ -307,24 +265,13 @@ class JsIrBackendFacade(
|
||||
|
||||
if (generateDts) {
|
||||
outputFile
|
||||
.withReplacedExtensionOrNull("_v5.js", ".d.ts")!!
|
||||
.withReplacedExtensionOrNull("_v5${moduleKind.extension}", ".d.ts")!!
|
||||
.write(compilerResult.tsDefinitions ?: error("No ts definitions"))
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun BinaryArtifacts.Js.JsEsArtifact.dump(module: TestModule): BinaryArtifacts.Js.JsEsArtifact {
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val moduleName = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)
|
||||
val esmTestFile = outputFile.parentFile.esModulesSubDir.resolve("test.mjs")
|
||||
createEsTestFile(esmTestFile, moduleName)
|
||||
|
||||
val dceEsmTestFile = outputDceFile?.parentFile?.esModulesSubDir?.resolve("test.mjs") ?: return this
|
||||
createEsTestFile(dceEsmTestFile, moduleName)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun CompilationOutputs.writeTo(outputFile: File, moduleId: String, moduleKind: ModuleKind) {
|
||||
val wrappedCode = ClassicJsBackendFacade.wrapWithModuleEmulationMarkers(jsCode, moduleId = moduleId, moduleKind = moduleKind)
|
||||
outputFile.write(wrappedCode)
|
||||
@@ -344,32 +291,37 @@ class JsIrBackendFacade(
|
||||
writeText(text)
|
||||
}
|
||||
|
||||
private fun createEsTestFile(file: File, moduleName: String) {
|
||||
val customTestModule = testServices.moduleStructure.modules
|
||||
.flatMap { it.files }
|
||||
.singleOrNull { JsEnvironmentConfigurationDirectives.ENTRY_ES_MODULE in it.directives }
|
||||
val customTestModuleText = customTestModule?.let { testServices.sourceFileProvider.getContentOfSourceFile(it) }
|
||||
|
||||
val defaultTestModule =
|
||||
"""
|
||||
import { box } from './${moduleName}/index.js';
|
||||
let res = box();
|
||||
if (res !== "OK") {
|
||||
throw "Wrong result: " + String(res);
|
||||
}
|
||||
""".trimIndent()
|
||||
file.writeText(customTestModuleText ?: defaultTestModule)
|
||||
}
|
||||
|
||||
override fun shouldRunAnalysis(module: TestModule): Boolean {
|
||||
return JsEnvironmentConfigurator.isMainModule(module, testServices)
|
||||
}
|
||||
}
|
||||
|
||||
val ModuleKind.extension: String
|
||||
get() = when (this) {
|
||||
ModuleKind.ES -> ESM_EXTENSION
|
||||
else -> REGULAR_EXTENSION
|
||||
}
|
||||
|
||||
val RegisteredDirectives.moduleKind: ModuleKind
|
||||
get() = get(JsEnvironmentConfigurationDirectives.MODULE_KIND).singleOrNull()
|
||||
?: if (contains(JsEnvironmentConfigurationDirectives.ES_MODULES)) ModuleKind.ES else ModuleKind.PLAIN
|
||||
|
||||
val TestModule.kind: ModuleKind
|
||||
get() = directives.moduleKind
|
||||
|
||||
fun String.augmentWithModuleName(moduleName: String): String {
|
||||
check(endsWith("_v5.js"))
|
||||
return if (moduleName.isPath()) {
|
||||
replaceAfterLast(File.separator, moduleName.replace("./", ""))
|
||||
} else {
|
||||
val suffix = when {
|
||||
endsWith(ESM_EXTENSION) -> ESM_EXTENSION
|
||||
endsWith(REGULAR_EXTENSION) -> REGULAR_EXTENSION
|
||||
else -> error("Unexpected file '$this' extension")
|
||||
}
|
||||
val normalizedName = moduleName.run { if (isWindows) minify() else this }
|
||||
return removeSuffix("_v5.js") + "-${normalizedName}_v5.js"
|
||||
|
||||
return removeSuffix("_v5$suffix") + "-${normalizedName}_v5$suffix"
|
||||
}
|
||||
}
|
||||
|
||||
// D8 ignores Windows settings related to extending of maximum path symbols count
|
||||
@@ -380,4 +332,6 @@ fun String.minify(): String {
|
||||
.replace("_minimal_for_test", "_min")
|
||||
}
|
||||
|
||||
private fun String.isPath(): Boolean = contains("/")
|
||||
|
||||
fun File.augmentWithModuleName(moduleName: String): File = File(absolutePath.augmentWithModuleName(moduleName))
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.js.test.converters
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator.Companion.getJsArtifactSimpleName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
private typealias K = IrModuleFragment
|
||||
private typealias V = String
|
||||
|
||||
class JsIrModuleToPath(val testServices: TestServices, shouldProvidePaths: Boolean) : Map<K, V> {
|
||||
override val size = if (!shouldProvidePaths) 0 else 1
|
||||
override val entries = emptySet<Map.Entry<K, V>>()
|
||||
override val keys = emptySet<K>()
|
||||
override val values = emptyList<V>()
|
||||
|
||||
override fun isEmpty() = size == 0
|
||||
override fun containsKey(key: K): Boolean = !isEmpty()
|
||||
override fun containsValue(value: V): Boolean = !isEmpty()
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
return runIf(!isEmpty()) {
|
||||
"./${getJsArtifactSimpleName(testServices, key.safeName)}_v5.mjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.js.test.converters
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.js.test.utils.*
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.isJsFile
|
||||
import org.jetbrains.kotlin.test.services.isMjsFile
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
|
||||
class JsIrPathReplacer(testServices: TestServices) : DeclarationTransformer {
|
||||
private val replacements = testServices.collectReplacementsMap()
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
super.lower(irFile)
|
||||
irFile.replaceJsModulePath()
|
||||
}
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
return null.also {
|
||||
declaration.replaceJsModulePath()
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrAnnotationContainer.replaceJsModulePath() {
|
||||
val jsModuleAnnotation = getAnnotation(JsAnnotations.jsModuleFqn) ?: return
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val stringLiteral = jsModuleAnnotation.getValueArgument(0) as IrConst<String>
|
||||
val pathReplacement = stringLiteral.getReplacement() ?: return
|
||||
|
||||
jsModuleAnnotation.putValueArgument(0, pathReplacement)
|
||||
}
|
||||
|
||||
private fun IrConst<String>.getReplacement(): IrConst<String>? {
|
||||
val replacement = replacements[value] ?: replacements[value.replace("./", "")] ?: return null
|
||||
return IrConstImpl.string(startOffset, endOffset, type, "./" + replacement.replace("./", ""))
|
||||
}
|
||||
|
||||
private fun TestServices.collectReplacementsMap(): Map<String, String> {
|
||||
return moduleStructure.modules.asSequence()
|
||||
.map { module -> module to module.files.filter { it.isJsFile || it.isMjsFile } }
|
||||
.filter { (_, files) -> files.isNotEmpty() }
|
||||
.flatMap { (module, files) -> files.map { it.relativePath to module.getNameFor(it, this) } }
|
||||
.plus(getAdditionalFiles(this).map { it.name to it.name })
|
||||
.plus(getAdditionalMainFiles(this).map { it.name to it.name })
|
||||
.toMap()
|
||||
}
|
||||
}
|
||||
@@ -5,25 +5,15 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.test.handlers
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.js.testOld.engines.ExternalTool
|
||||
import org.jetbrains.kotlin.js.test.utils.*
|
||||
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.defaultsProvider
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import java.io.File
|
||||
|
||||
private val v8tool by lazy { ExternalTool(System.getProperty("javascript.engine.path.V8")) }
|
||||
|
||||
class JsBoxRunner(testServices: TestServices) : AbstractJsArtifactsCollector(testServices) {
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (someAssertionWasFailed) return
|
||||
|
||||
if (JsEnvironmentConfigurationDirectives.ES_MODULES in testServices.moduleStructure.allDirectives) {
|
||||
runEsCode()
|
||||
} else {
|
||||
if (!someAssertionWasFailed) {
|
||||
runJsCode()
|
||||
}
|
||||
}
|
||||
@@ -43,37 +33,30 @@ class JsBoxRunner(testServices: TestServices) : AbstractJsArtifactsCollector(tes
|
||||
|
||||
val dontSkipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE !in globalDirectives
|
||||
if (dontSkipRegularMode) {
|
||||
for (jsFiles in allJsFiles.values) {
|
||||
runGeneratedCode(jsFiles, testModuleName, testPackage, withModuleSystem)
|
||||
for ((mode, jsFiles) in allJsFiles) {
|
||||
val entryModulePath = extractEntryModulePath(mode, testServices)
|
||||
runGeneratedCode(entryModulePath, jsFiles, testModuleName, testPackage, withModuleSystem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runEsCode() {
|
||||
val globalDirectives = testServices.moduleStructure.allDirectives
|
||||
|
||||
val esmOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices).esModulesSubDir
|
||||
val esmDceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_DCE_MINIMIZED_NAMES).esModulesSubDir
|
||||
|
||||
val dontSkipRegularMode = JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE !in globalDirectives
|
||||
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in globalDirectives
|
||||
if (dontSkipRegularMode) {
|
||||
singleRunEsCode(esmOutputDir)
|
||||
if (runIrDce) {
|
||||
singleRunEsCode(esmDceOutputDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun singleRunEsCode(esmOutputDir: File) {
|
||||
val perFileEsModuleFile = "$esmOutputDir/test.mjs"
|
||||
val (allNonEsModuleFiles, inputJsFilesAfter) = extractAllFilesForEsRunner(testServices, esmOutputDir)
|
||||
v8tool.run(*allNonEsModuleFiles.toTypedArray(), perFileEsModuleFile, *inputJsFilesAfter.toTypedArray())
|
||||
}
|
||||
|
||||
private fun runGeneratedCode(jsFiles: List<String>, testModuleName: String?, testPackage: String?, withModuleSystem: Boolean) {
|
||||
private fun runGeneratedCode(
|
||||
entryModulePath: String?,
|
||||
jsFiles: List<String>,
|
||||
testModuleName: String?,
|
||||
testPackage: String?,
|
||||
withModuleSystem: Boolean
|
||||
) {
|
||||
getTestChecker(testServices)
|
||||
.check(jsFiles, testModuleName, testPackage, TEST_FUNCTION, DEFAULT_EXPECTED_RESULT, withModuleSystem)
|
||||
.check(
|
||||
jsFiles,
|
||||
testModuleName,
|
||||
testPackage,
|
||||
TEST_FUNCTION,
|
||||
DEFAULT_EXPECTED_RESULT,
|
||||
withModuleSystem,
|
||||
entryModulePath,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -24,6 +24,7 @@ class JsDtsHandler(testServices: TestServices) : JsBinaryArtifactHandler(testSer
|
||||
val referenceDtsFile = module.files.first().originalFile.withReplacedExtensionOrNull(".kt", ".d.ts")
|
||||
?: error("Can't find reference .d.ts file")
|
||||
val generatedDtsFile = info.outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts")
|
||||
?: info.outputFile.withReplacedExtensionOrNull("_v5.mjs", ".d.ts")
|
||||
?: error("Can't find generated .d.ts file")
|
||||
|
||||
val generatedDts = generatedDtsFile.readText()
|
||||
|
||||
@@ -10,9 +10,10 @@ import org.jetbrains.kotlin.js.JavaScript
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.test.JsAdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.js.test.converters.augmentWithModuleName
|
||||
import org.jetbrains.kotlin.js.test.converters.extension
|
||||
import org.jetbrains.kotlin.js.test.converters.kind
|
||||
import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION
|
||||
import org.jetbrains.kotlin.js.testOld.*
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
@@ -30,14 +31,23 @@ import java.io.File
|
||||
|
||||
private const val MODULE_EMULATION_FILE = "${JsEnvironmentConfigurator.TEST_DATA_DIR_PATH}/moduleEmulation.js"
|
||||
|
||||
val File.esModulesSubDir: File
|
||||
get() = File(absolutePath + "_esm")
|
||||
fun TestModule.getNameFor(filePath: String, testServices: TestServices): String {
|
||||
return JsEnvironmentConfigurator.getJsArtifactSimpleName(testServices, name) + "-js-" + filePath
|
||||
}
|
||||
|
||||
private fun extractJsFiles(testServices: TestServices, modules: List<TestModule>): Pair<List<String>, List<String>> {
|
||||
val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices)
|
||||
fun TestModule.getNameFor(file: TestFile, testServices: TestServices): String {
|
||||
return getNameFor(file.name, testServices)
|
||||
}
|
||||
|
||||
private fun extractJsFiles(
|
||||
testServices: TestServices,
|
||||
modules: List<TestModule>,
|
||||
mode: TranslationMode = TranslationMode.FULL,
|
||||
): Pair<List<String>, List<String>> {
|
||||
val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, mode)
|
||||
|
||||
fun copyInputJsFile(module: TestModule, inputJsFile: TestFile): String {
|
||||
val newName = JsEnvironmentConfigurator.getJsArtifactSimpleName(testServices, module.name) + "-js-" + inputJsFile.name
|
||||
val newName = module.getNameFor(inputJsFile, testServices)
|
||||
val targetFile = File(outputDir, newName)
|
||||
targetFile.writeText(inputJsFile.originalContent)
|
||||
return targetFile.absolutePath
|
||||
@@ -45,49 +55,74 @@ private fun extractJsFiles(testServices: TestServices, modules: List<TestModule>
|
||||
|
||||
val inputJsFiles = modules
|
||||
.flatMap { module -> module.files.map { module to it } }
|
||||
.filter { it.second.isJsFile }
|
||||
|
||||
.filter { it.second.isJsFile || it.second.isMjsFile }
|
||||
|
||||
val after = inputJsFiles
|
||||
.filter { (_, inputJsFile) -> inputJsFile.name.endsWith("__after.js") }
|
||||
.filter { (module, inputJsFile) -> inputJsFile.name.endsWith("__after${module.kind.extension}") }
|
||||
.map { (module, inputJsFile) -> copyInputJsFile(module, inputJsFile) }
|
||||
val before = inputJsFiles
|
||||
.filterNot { (_, inputJsFile) -> inputJsFile.name.endsWith("__after.js") }
|
||||
.filterNot { (module, inputJsFile) -> inputJsFile.name.endsWith("__after${module.kind.extension}") }
|
||||
.map { (module, inputJsFile) -> copyInputJsFile(module, inputJsFile) }
|
||||
|
||||
return before to after
|
||||
}
|
||||
|
||||
private fun getAdditionalFiles(testServices: TestServices): List<String> {
|
||||
fun getAdditionalFilePathes(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL): List<String> {
|
||||
return getAdditionalFiles(testServices, mode, true).map { it.absolutePath }
|
||||
}
|
||||
|
||||
fun getAdditionalFiles(
|
||||
testServices: TestServices,
|
||||
mode: TranslationMode = TranslationMode.FULL,
|
||||
shouldCopyFiles: Boolean = false
|
||||
): List<File> {
|
||||
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
|
||||
val withModuleSystem = testWithModuleSystem(testServices)
|
||||
|
||||
val additionalFiles = mutableListOf<String>()
|
||||
if (withModuleSystem) additionalFiles += File(MODULE_EMULATION_FILE).absolutePath
|
||||
val additionalFiles = mutableListOf<File>()
|
||||
if (withModuleSystem) additionalFiles += File(MODULE_EMULATION_FILE)
|
||||
|
||||
originalFile.parentFile.resolve(originalFile.nameWithoutExtension + JavaScript.DOT_EXTENSION)
|
||||
.takeIf { it.exists() }
|
||||
?.let { additionalFiles += it.absolutePath }
|
||||
?.let { additionalFiles += it }
|
||||
|
||||
originalFile.parentFile.resolve(originalFile.nameWithoutExtension + JavaScript.DOT_MODULE_EXTENSION)
|
||||
.takeIf { it.exists() }
|
||||
?.let {
|
||||
File(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, mode), it.name).apply {
|
||||
if (shouldCopyFiles) it.copyTo(this, true)
|
||||
}
|
||||
}
|
||||
?.let { additionalFiles += it }
|
||||
|
||||
return additionalFiles
|
||||
}
|
||||
|
||||
private fun getAdditionalMjsFiles(testServices: TestServices): List<String> {
|
||||
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
|
||||
return originalFile.parentFile.resolve(originalFile.nameWithoutExtension + ".mjs")
|
||||
.takeIf { it.exists() }
|
||||
?.let { listOf(it.absolutePath) } ?: emptyList()
|
||||
fun getAdditionalMainFilePathes(testServices: TestServices, mode: TranslationMode = TranslationMode.FULL): List<String> {
|
||||
return getAdditionalMainFiles(testServices, mode, shouldCopyFiles = true).map { it.absolutePath }
|
||||
}
|
||||
|
||||
private fun getAdditionalMainFiles(testServices: TestServices): List<String> {
|
||||
fun getAdditionalMainFiles(
|
||||
testServices: TestServices,
|
||||
mode: TranslationMode = TranslationMode.FULL,
|
||||
shouldCopyFiles: Boolean = false
|
||||
): List<File> {
|
||||
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
val additionalFiles = mutableListOf<String>()
|
||||
val additionalFiles = mutableListOf<File>()
|
||||
|
||||
originalFile.parentFile.resolve(originalFile.nameWithoutExtension + "__main.js")
|
||||
.takeIf { it.exists() }
|
||||
?.let { additionalFiles += it.absolutePath }
|
||||
?.let { additionalFiles += it }
|
||||
|
||||
originalFile.parentFile.resolve(originalFile.nameWithoutExtension + "__main.mjs")
|
||||
.takeIf { it.exists() }
|
||||
?.let {
|
||||
File(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, mode), it.name).apply {
|
||||
if (shouldCopyFiles) it.copyTo(this, true)
|
||||
}
|
||||
}
|
||||
?.let { additionalFiles += it }
|
||||
|
||||
return additionalFiles
|
||||
}
|
||||
@@ -99,26 +134,31 @@ fun testWithModuleSystem(testServices: TestServices): Boolean {
|
||||
return mainModuleKind != ModuleKind.PLAIN && NO_JS_MODULE_SYSTEM !in globalDirectives
|
||||
}
|
||||
|
||||
fun getModeOutputFilePath(testServices: TestServices, module: TestModule, mode: TranslationMode): String {
|
||||
return JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + module.kind.extension
|
||||
}
|
||||
|
||||
fun getAllFilesForRunner(
|
||||
testServices: TestServices, modulesToArtifact: Map<TestModule, BinaryArtifacts.Js>
|
||||
): Map<TranslationMode, List<String>> {
|
||||
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
|
||||
val commonFiles = JsAdditionalSourceProvider.getAdditionalJsFiles(originalFile.parent).map { it.absolutePath }
|
||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules)
|
||||
val additionalFiles = getAdditionalFiles(testServices)
|
||||
val additionalMainFiles = getAdditionalMainFiles(testServices)
|
||||
|
||||
if (modulesToArtifact.values.any { it is BinaryArtifacts.Js.JsIrArtifact }) {
|
||||
// JS IR
|
||||
val (module, compilerResult) = modulesToArtifact.entries.mapNotNull { (m, c) -> (c as? BinaryArtifacts.Js.JsIrArtifact)?.let { m to c.compilerResult } }.single()
|
||||
|
||||
val (module, compilerResult) = modulesToArtifact.entries.mapNotNull { (m, c) -> (c as? BinaryArtifacts.Js.JsIrArtifact)?.let { m to c.compilerResult } }
|
||||
.single()
|
||||
val result = mutableMapOf<TranslationMode, List<String>>()
|
||||
|
||||
compilerResult.outputs.entries.forEach { (mode, outputs) ->
|
||||
val paths = mutableListOf<String>()
|
||||
|
||||
val outputFile = JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, mode) + ".js"
|
||||
val outputFile = getModeOutputFilePath(testServices, module, mode)
|
||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules, mode)
|
||||
val additionalFiles = getAdditionalFilePathes(testServices, mode)
|
||||
val additionalMainFiles = getAdditionalMainFilePathes(testServices, mode)
|
||||
|
||||
outputs.dependencies.forEach { (moduleId, _) ->
|
||||
paths += outputFile.augmentWithModuleName(moduleId)
|
||||
}
|
||||
@@ -129,12 +169,15 @@ fun getAllFilesForRunner(
|
||||
|
||||
return result
|
||||
} else {
|
||||
// Old BE and ES modules
|
||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, testServices.moduleStructure.modules)
|
||||
val additionalFiles = getAdditionalFilePathes(testServices)
|
||||
val additionalMainFiles = getAdditionalMainFilePathes(testServices)
|
||||
// Old BE
|
||||
val outputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices)
|
||||
val dceOutputDir = JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, TranslationMode.FULL_DCE_MINIMIZED_NAMES)
|
||||
|
||||
val artifactsPaths = modulesToArtifact.values.map { it.outputFile.absolutePath }.filter { !File(it).isDirectory }
|
||||
val allJsFiles = additionalFiles + inputJsFilesBefore +artifactsPaths + commonFiles + additionalMainFiles + inputJsFilesAfter
|
||||
val allJsFiles = additionalFiles + inputJsFilesBefore + artifactsPaths + commonFiles + additionalMainFiles + inputJsFilesAfter
|
||||
|
||||
val result = mutableMapOf<TranslationMode, List<String>>()
|
||||
|
||||
@@ -154,38 +197,6 @@ fun getAllFilesForRunner(
|
||||
}
|
||||
}
|
||||
|
||||
fun extractAllFilesForEsRunner(testServices: TestServices, esmOutputDir: File): Pair<List<String>, List<String>> {
|
||||
val modules = testServices.moduleStructure.modules
|
||||
val originalFile = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
|
||||
val commonFiles = JsAdditionalSourceProvider.getAdditionalJsFiles(originalFile.parent).map { it.absolutePath }
|
||||
val (inputJsFilesBefore, inputJsFilesAfter) = extractJsFiles(testServices, modules)
|
||||
val additionalFiles = getAdditionalFiles(testServices)
|
||||
val additionalMjsFiles = getAdditionalMjsFiles(testServices)
|
||||
val additionalMainFiles = getAdditionalMainFiles(testServices)
|
||||
|
||||
val allNonEsModuleFiles = additionalFiles + inputJsFilesBefore + commonFiles
|
||||
|
||||
// Copy __main file if present
|
||||
if (additionalMainFiles.isNotEmpty()) {
|
||||
val newFileName = File(esmOutputDir, "test.mjs")
|
||||
newFileName.delete()
|
||||
File(additionalMainFiles.first()).copyTo(newFileName)
|
||||
}
|
||||
|
||||
// Copy all .mjs files into generated directory
|
||||
modules.flatMap { it.files }
|
||||
.filter { it.isMjsFile }
|
||||
.map { File(esmOutputDir, it.name).writeText(it.originalContent) }
|
||||
|
||||
additionalMjsFiles.forEach { mjsFile ->
|
||||
val outFile = File(esmOutputDir, File(mjsFile).name)
|
||||
File(mjsFile).copyTo(outFile, overwrite = true)
|
||||
}
|
||||
|
||||
return Pair(allNonEsModuleFiles, inputJsFilesAfter)
|
||||
}
|
||||
|
||||
fun getOnlyJsFilesForRunner(testServices: TestServices, modulesToArtifact: Map<TestModule, BinaryArtifacts.Js>): List<String> {
|
||||
return getAllFilesForRunner(testServices, modulesToArtifact).let {
|
||||
it[TranslationMode.FULL] ?: it[TranslationMode.PER_MODULE]!!
|
||||
@@ -215,8 +226,49 @@ fun getBoxFunction(testServices: TestServices): KtNamedFunction? {
|
||||
}.singleOrNull()
|
||||
}
|
||||
|
||||
fun extractTestPackage(testServices: TestServices): String? =
|
||||
getBoxFunction(testServices)?.containingKtFile?.packageFqName?.asString()?.takeIf { it.isNotEmpty() }
|
||||
fun extractTestPackage(testServices: TestServices, ignoreEsModules: Boolean = true): String? {
|
||||
val runPlainBoxFunction = RUN_PLAIN_BOX_FUNCTION in testServices.moduleStructure.allDirectives
|
||||
if (runPlainBoxFunction) return null
|
||||
|
||||
val ktFiles = testServices.moduleStructure.modules.flatMap { module ->
|
||||
module.files
|
||||
.filter { it.isKtFile }
|
||||
.map {
|
||||
val project = testServices.compilerConfigurationProvider.getProject(module)
|
||||
module to testServices.sourceFileProvider.getKtFileForSourceFile(it, project)
|
||||
}
|
||||
}
|
||||
|
||||
val fileWithBoxFunction = ktFiles.find { (module, ktFile) ->
|
||||
(!ignoreEsModules || module.kind != ModuleKind.ES) &&
|
||||
ktFile.declarations.find { it is KtNamedFunction && it.name == TEST_FUNCTION } != null
|
||||
} ?: return null
|
||||
|
||||
return fileWithBoxFunction.second.packageFqName.asString().takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun extractEntryModulePath(
|
||||
mode: TranslationMode,
|
||||
testServices: TestServices,
|
||||
): String? =
|
||||
if (getBoxFunction(testServices) == null) {
|
||||
testServices.moduleStructure.modules
|
||||
.find { JsEnvironmentConfigurator.isMainModule(it, testServices) }
|
||||
?.run {
|
||||
files
|
||||
.find { it.isMjsFile && JsEnvironmentConfigurationDirectives.ENTRY_ES_MODULE in it.directives }
|
||||
?.let {
|
||||
JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices, mode).absolutePath +
|
||||
File.separator + getNameFor(it, testServices)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
testServices.moduleStructure.modules
|
||||
.find { JsEnvironmentConfigurator.isMainModule(it, testServices) }
|
||||
?.let { getModeOutputFilePath(testServices, it, mode) }
|
||||
}
|
||||
|
||||
|
||||
fun getTestChecker(testServices: TestServices): AbstractJsTestChecker {
|
||||
val runTestInNashorn = java.lang.Boolean.getBoolean("kotlin.js.useNashorn")
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.js.engine.loadFiles
|
||||
import org.junit.Assert
|
||||
|
||||
private const val DIST_DIR_JS_PATH = "dist/js/"
|
||||
private const val ESM_EXTENSION = ".mjs"
|
||||
|
||||
fun createScriptEngine(): ScriptEngine {
|
||||
return if (java.lang.Boolean.getBoolean("kotlin.js.useNashorn")) ScriptEngineNashorn() else ScriptEngineV8()
|
||||
@@ -22,14 +23,26 @@ fun ScriptEngine.overrideAsserter() {
|
||||
eval("this['kotlin-test'].kotlin.test.overrideAsserter_wbnzx$(this['kotlin-test'].kotlin.test.DefaultAsserter);")
|
||||
}
|
||||
|
||||
private fun String.escapePath(): String {
|
||||
return replace("\\", "/")
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun ScriptEngine.runTestFunction(
|
||||
testModuleName: String?,
|
||||
testPackageName: String?,
|
||||
testFunctionName: String,
|
||||
withModuleSystem: Boolean,
|
||||
testFunctionArgs: String = "",
|
||||
entryModulePath: String? = null,
|
||||
): String {
|
||||
if (withModuleSystem && testModuleName == null && entryModulePath == null) {
|
||||
error("Entry point was not found. Please specify ENTRY_ES_MODULE directive near js file, if this is ES Modules test.")
|
||||
}
|
||||
var script = when {
|
||||
entryModulePath != null && entryModulePath.endsWith(ESM_EXTENSION) -> "globalThis".also {
|
||||
eval("import('${entryModulePath.escapePath()}').then(module => Object.assign(globalThis, module)).catch(console.error)")
|
||||
}
|
||||
withModuleSystem -> "\$kotlin_test_internal\$.require('" + testModuleName!! + "')"
|
||||
testModuleName === null -> "this"
|
||||
else -> testModuleName
|
||||
@@ -51,9 +64,10 @@ abstract class AbstractJsTestChecker {
|
||||
testPackageName: String?,
|
||||
testFunctionName: String,
|
||||
expectedResult: String,
|
||||
withModuleSystem: Boolean
|
||||
withModuleSystem: Boolean,
|
||||
entryModulePath: String? = null,
|
||||
) {
|
||||
val actualResult = run(files, testModuleName, testPackageName, testFunctionName, "", withModuleSystem)
|
||||
val actualResult = run(files, testModuleName, testPackageName, testFunctionName, "", withModuleSystem, entryModulePath)
|
||||
Assert.assertEquals(expectedResult, actualResult.normalize())
|
||||
}
|
||||
|
||||
@@ -76,9 +90,10 @@ abstract class AbstractJsTestChecker {
|
||||
testPackageName: String?,
|
||||
testFunctionName: String,
|
||||
testFunctionArgs: String,
|
||||
withModuleSystem: Boolean
|
||||
withModuleSystem: Boolean,
|
||||
entryModulePath: String? = null,
|
||||
) = run(files) {
|
||||
runTestFunction(testModuleName, testPackageName, testFunctionName, withModuleSystem, testFunctionArgs)
|
||||
runTestFunction(testModuleName, testPackageName, testFunctionName, withModuleSystem, testFunctionArgs, entryModulePath)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1906,18 +1906,6 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
public void testAllFilesPresentInExport() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameStableNameMethod.kt")
|
||||
public void testOverriddenExternalMethodWithSameStableNameMethod() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethod.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reservedModuleName.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+138
@@ -2217,35 +2217,173 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("bridgeSavingAfterExport.kt")
|
||||
public void testBridgeSavingAfterExport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/bridgeSavingAfterExport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("bridgeSavingAfterExportInExportedFile.kt")
|
||||
public void testBridgeSavingAfterExportInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/bridgeSavingAfterExportInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("defaultInlineClassConstructorParam.kt")
|
||||
public void testDefaultInlineClassConstructorParam() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/defaultInlineClassConstructorParam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("defaultInlineClassConstructorParamInExportedFile.kt")
|
||||
public void testDefaultInlineClassConstructorParamInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/defaultInlineClassConstructorParamInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportAllFile.kt")
|
||||
public void testExportAllFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportAllFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportEnumClass.kt")
|
||||
public void testExportEnumClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithEnumClass.kt")
|
||||
public void testExportFileWithEnumClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithInterface.kt")
|
||||
public void testExportFileWithInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithNestedClass.kt")
|
||||
public void testExportFileWithNestedClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithNestedObject.kt")
|
||||
public void testExportFileWithNestedObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithNestedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithProtectedMembers.kt")
|
||||
public void testExportFileWithProtectedMembers() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithProtectedMembers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithTopLevelProperty.kt")
|
||||
public void testExportFileWithTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithTopLevelProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInnerClass.kt")
|
||||
public void testExportInnerClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportInnerClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInterface.kt")
|
||||
public void testExportInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedClass.kt")
|
||||
public void testExportNestedClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedObject.kt")
|
||||
public void testExportNestedObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportNestedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportProtectedMembers.kt")
|
||||
public void testExportProtectedMembers() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportProtectedMembers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportTopLevelProperty.kt")
|
||||
public void testExportTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonIndetifierModuleName.kt")
|
||||
public void testNonIndetifierModuleName() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/nonIndetifierModuleName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonIndetifierModuleNameInExportedFile.kt")
|
||||
public void testNonIndetifierModuleNameInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/nonIndetifierModuleNameInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenChainNonExportIntermediate.kt")
|
||||
public void testOverriddenChainNonExportIntermediate() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenChainNonExportIntermediate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenChainNonExportIntermediateInExportedFile.kt")
|
||||
public void testOverriddenChainNonExportIntermediateInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenChainNonExportIntermediateInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameNameMethod.kt")
|
||||
public void testOverriddenExternalMethodWithSameNameMethod() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameNameMethod.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameStableNameMethod.kt")
|
||||
public void testOverriddenExternalMethodWithSameStableNameMethod() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethod.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt")
|
||||
public void testOverriddenExternalMethodWithSameStableNameMethodInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reservedModuleName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleNameInExportedFile.kt")
|
||||
public void testReservedModuleNameInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reservedModuleNameInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("vararg.kt")
|
||||
public void testVararg() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/vararg.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+138
@@ -2217,35 +2217,173 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/esModules/export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("bridgeSavingAfterExport.kt")
|
||||
public void testBridgeSavingAfterExport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/bridgeSavingAfterExport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("bridgeSavingAfterExportInExportedFile.kt")
|
||||
public void testBridgeSavingAfterExportInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/bridgeSavingAfterExportInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("defaultInlineClassConstructorParam.kt")
|
||||
public void testDefaultInlineClassConstructorParam() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/defaultInlineClassConstructorParam.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("defaultInlineClassConstructorParamInExportedFile.kt")
|
||||
public void testDefaultInlineClassConstructorParamInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/defaultInlineClassConstructorParamInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportAllFile.kt")
|
||||
public void testExportAllFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportAllFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportEnumClass.kt")
|
||||
public void testExportEnumClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithEnumClass.kt")
|
||||
public void testExportFileWithEnumClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithEnumClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithInterface.kt")
|
||||
public void testExportFileWithInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithNestedClass.kt")
|
||||
public void testExportFileWithNestedClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithNestedObject.kt")
|
||||
public void testExportFileWithNestedObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithNestedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithProtectedMembers.kt")
|
||||
public void testExportFileWithProtectedMembers() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithProtectedMembers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportFileWithTopLevelProperty.kt")
|
||||
public void testExportFileWithTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportFileWithTopLevelProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInnerClass.kt")
|
||||
public void testExportInnerClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportInnerClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportInterface.kt")
|
||||
public void testExportInterface() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportInterface.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedClass.kt")
|
||||
public void testExportNestedClass() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportNestedObject.kt")
|
||||
public void testExportNestedObject() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportNestedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportProtectedMembers.kt")
|
||||
public void testExportProtectedMembers() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportProtectedMembers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("exportTopLevelProperty.kt")
|
||||
public void testExportTopLevelProperty() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/exportTopLevelProperty.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonIndetifierModuleName.kt")
|
||||
public void testNonIndetifierModuleName() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/nonIndetifierModuleName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nonIndetifierModuleNameInExportedFile.kt")
|
||||
public void testNonIndetifierModuleNameInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/nonIndetifierModuleNameInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenChainNonExportIntermediate.kt")
|
||||
public void testOverriddenChainNonExportIntermediate() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenChainNonExportIntermediate.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenChainNonExportIntermediateInExportedFile.kt")
|
||||
public void testOverriddenChainNonExportIntermediateInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenChainNonExportIntermediateInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameNameMethod.kt")
|
||||
public void testOverriddenExternalMethodWithSameNameMethod() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameNameMethod.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameStableNameMethod.kt")
|
||||
public void testOverriddenExternalMethodWithSameStableNameMethod() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethod.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt")
|
||||
public void testOverriddenExternalMethodWithSameStableNameMethodInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reservedModuleName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleNameInExportedFile.kt")
|
||||
public void testReservedModuleNameInExportedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reservedModuleNameInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("vararg.kt")
|
||||
public void testVararg() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/vararg.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
Generated
+12
@@ -456,6 +456,12 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
||||
runTest("js/js.translator/testData/typescript-export/module-systems/commonjs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("esm.kt")
|
||||
public void testEsm() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/module-systems/esm.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("plain.kt")
|
||||
public void testPlain() throws Exception {
|
||||
@@ -484,6 +490,12 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
||||
runTest("js/js.translator/testData/typescript-export/module-systems-in-exported-file/commonjs.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("esm.kt")
|
||||
public void testEsm() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/module-systems-in-exported-file/esm.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("plain.kt")
|
||||
public void testPlain() throws Exception {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: bridge_saving_after_export
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
open class A<T> {
|
||||
open fun foo(value: T): T = value
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class B: A<String>() {
|
||||
override fun foo(value: String): String = value
|
||||
}
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { A, B } from "./bridgeSavingAfterExport-bridge_saving_after_export_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
var a = new A()
|
||||
var aFoo = a.foo("ok")
|
||||
if (aFoo != "ok") return "fail 1"
|
||||
|
||||
var b = new B()
|
||||
var bFoo = b.foo("ok")
|
||||
if (bFoo != "ok") return "fail 2"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: bridge_saving_after_export
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
open class A<T> {
|
||||
open fun foo(value: T): T = value
|
||||
}
|
||||
|
||||
class B: A<String>() {
|
||||
override fun foo(value: String): String = value
|
||||
}
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { A, B } from "./bridgeSavingAfterExportInExportedFile-bridge_saving_after_export_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
var a = new A()
|
||||
var aFoo = a.foo("ok")
|
||||
if (aFoo != "ok") return "fail 1"
|
||||
|
||||
var b = new B()
|
||||
var bFoo = b.foo("ok")
|
||||
if (bFoo != "ok") return "fail 2"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// IGNORE_FIR
|
||||
// KT-49225
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// SPLIT_PER_MODULE
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
value class Koo(val koo: String = "OK")
|
||||
|
||||
@JsExport
|
||||
class Bar(val koo: Koo = Koo())
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import { Bar } from "./defaultInlineClassConstructorParam-kotlin_lib_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return new Bar().koo;
|
||||
}
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
// IGNORE_FIR
|
||||
// KT-49225
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SPLIT_PER_MODULE
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: koo.kt
|
||||
value class Koo(val koo: String = "OK")
|
||||
|
||||
// FILE: bar.kt
|
||||
@file:JsExport
|
||||
|
||||
class Bar(val koo: Koo = Koo())
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import { Bar } from "./defaultInlineClassConstructorParamInExportedFile-kotlin_lib_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return new Bar().koo;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// INFER_MAIN_MODULE
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: export_all_file
|
||||
@@ -19,5 +18,9 @@ class B : A() {
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { B } from "./export_all_file/index.js";
|
||||
console.assert(new B().foo("K") == "OK");
|
||||
|
||||
import { B } from "./exportAllFile-export_all_file_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return new B().foo("K")
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: export_enum_class
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
enum class Foo(val constructorParameter: String) {
|
||||
A("aConstructorParameter"),
|
||||
B("bConstructorParameter");
|
||||
|
||||
val foo = ordinal
|
||||
|
||||
fun bar(value: String) = value
|
||||
|
||||
fun bay() = name
|
||||
|
||||
companion object {
|
||||
val baz = "baz"
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
enum class Bar {
|
||||
A,
|
||||
B {
|
||||
var d = "d"
|
||||
init {
|
||||
d = "d2"
|
||||
}
|
||||
fun huh() = "huh"
|
||||
};
|
||||
|
||||
val foo = ordinal
|
||||
|
||||
fun bar(value: String) = value
|
||||
|
||||
fun bay() = name
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class OuterClass {
|
||||
enum class NestedEnum {
|
||||
A,
|
||||
B;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Foo, Bar, OuterClass } from "./exportEnumClass-export_enum_class_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (Foo.A !== Foo.A) return "fail1"
|
||||
if (Foo.B !== Foo.B) return "fail2"
|
||||
|
||||
if (Foo.Companion.baz !== "baz") return "fail3"
|
||||
|
||||
if (Foo.A.foo !== 0) return "fail4"
|
||||
if (Foo.B.foo !== 1) return "fail5"
|
||||
|
||||
if (Foo.A.bar("A") !== "A") return "fail6"
|
||||
if (Foo.B.bar("B") !== "B") return "fail7"
|
||||
|
||||
if (Foo.A.bay() !== "A") return "fail8"
|
||||
if (Foo.B.bay() !== "B") return "fail9"
|
||||
|
||||
if (Foo.A.constructorParameter !== "aConstructorParameter") return "fail10"
|
||||
if (Foo.B.constructorParameter !== "bConstructorParameter") return "fail11"
|
||||
|
||||
if (Bar.A.foo !== 0) return "fail12"
|
||||
if (Bar.B.foo !== 1) return "fail13"
|
||||
|
||||
if (Bar.A.bar("A") !== "A") return "fail14"
|
||||
if (Bar.B.bar("B") !== "B") return "fail15"
|
||||
|
||||
if (Bar.A.bay() !== "A") return "fail15"
|
||||
if (Bar.B.bay() !== "B") return "fail16"
|
||||
|
||||
if (Bar.B.constructor.prototype.hasOwnProperty('d')) return "fail17"
|
||||
if (Bar.B.constructor.prototype.hasOwnProperty('huh')) return "fail18"
|
||||
|
||||
if (Foo.valueOf("A") !== Foo.A) return "fail19"
|
||||
if (Foo.valueOf("B") !== Foo.B) return "fail20"
|
||||
|
||||
if (Foo.values().indexOf(Foo.A) === -1) return "fail21"
|
||||
if (Foo.values().indexOf(Foo.B) === -1) return "fail22"
|
||||
|
||||
if (Foo.A.name !== "A") return "fail23"
|
||||
if (Foo.B.name !== "B") return "fail24"
|
||||
|
||||
if (Foo.A.ordinal !== 0) return "fail25"
|
||||
if (Foo.B.ordinal !== 1) return "fail26"
|
||||
|
||||
if (OuterClass.NestedEnum.A.name !== "A") return "fail27"
|
||||
if (OuterClass.NestedEnum.B.name !== "B") return "fail28"
|
||||
|
||||
if (OuterClass.NestedEnum.A.ordinal !== 0) return "fail29"
|
||||
if (OuterClass.NestedEnum.B.ordinal !== 1) return "fail30"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: export_enum_class
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
enum class Foo(val constructorParameter: String) {
|
||||
A("aConstructorParameter"),
|
||||
B("bConstructorParameter");
|
||||
|
||||
val foo = ordinal
|
||||
|
||||
fun bar(value: String) = value
|
||||
|
||||
fun bay() = name
|
||||
|
||||
companion object {
|
||||
val baz = "baz"
|
||||
}
|
||||
}
|
||||
|
||||
enum class Bar {
|
||||
A,
|
||||
B {
|
||||
var d = "d"
|
||||
init {
|
||||
d = "d2"
|
||||
}
|
||||
fun huh() = "huh"
|
||||
};
|
||||
|
||||
val foo = ordinal
|
||||
|
||||
fun bar(value: String) = value
|
||||
|
||||
fun bay() = name
|
||||
}
|
||||
|
||||
class OuterClass {
|
||||
enum class NestedEnum {
|
||||
A,
|
||||
B;
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Foo, Bar, OuterClass } from "./exportFileWithEnumClass-export_enum_class_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (Foo.A !== Foo.A) return "fail1"
|
||||
if (Foo.B !== Foo.B) return "fail2"
|
||||
|
||||
if (Foo.Companion.baz !== "baz") return "fail3"
|
||||
|
||||
if (Foo.A.foo !== 0) return "fail4"
|
||||
if (Foo.B.foo !== 1) return "fail5"
|
||||
|
||||
if (Foo.A.bar("A") !== "A") return "fail6"
|
||||
if (Foo.B.bar("B") !== "B") return "fail7"
|
||||
|
||||
if (Foo.A.bay() !== "A") return "fail8"
|
||||
if (Foo.B.bay() !== "B") return "fail9"
|
||||
|
||||
if (Foo.A.constructorParameter !== "aConstructorParameter") return "fail10"
|
||||
if (Foo.B.constructorParameter !== "bConstructorParameter") return "fail11"
|
||||
|
||||
if (Bar.A.foo !== 0) return "fail12"
|
||||
if (Bar.B.foo !== 1) return "fail13"
|
||||
|
||||
if (Bar.A.bar("A") !== "A") return "fail14"
|
||||
if (Bar.B.bar("B") !== "B") return "fail15"
|
||||
|
||||
if (Bar.A.bay() !== "A") return "fail15"
|
||||
if (Bar.B.bay() !== "B") return "fail16"
|
||||
|
||||
if (Bar.B.constructor.prototype.hasOwnProperty('d')) return "fail17"
|
||||
if (Bar.B.constructor.prototype.hasOwnProperty('huh')) return "fail18"
|
||||
|
||||
if (Foo.valueOf("A") !== Foo.A) return "fail19"
|
||||
if (Foo.valueOf("B") !== Foo.B) return "fail20"
|
||||
|
||||
if (Foo.values().indexOf(Foo.A) === -1) return "fail21"
|
||||
if (Foo.values().indexOf(Foo.B) === -1) return "fail22"
|
||||
|
||||
if (Foo.A.name !== "A") return "fail23"
|
||||
if (Foo.B.name !== "B") return "fail24"
|
||||
|
||||
if (Foo.A.ordinal !== 0) return "fail25"
|
||||
if (Foo.B.ordinal !== 1) return "fail26"
|
||||
|
||||
if (OuterClass.NestedEnum.A.name !== "A") return "fail27"
|
||||
if (OuterClass.NestedEnum.B.name !== "B") return "fail28"
|
||||
|
||||
if (OuterClass.NestedEnum.A.ordinal !== 0) return "fail29"
|
||||
if (OuterClass.NestedEnum.B.ordinal !== 1) return "fail30"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: export_interface
|
||||
// FILE: not_exported.kt
|
||||
|
||||
interface ParentI {
|
||||
val str: String
|
||||
}
|
||||
|
||||
interface ExtendedI: I {
|
||||
fun bar(): Int
|
||||
}
|
||||
|
||||
open class NotExportedClass(override var value: Int) : ExtendedI {
|
||||
override var variable: Int = value
|
||||
override open fun foo(): String = "Not Exported"
|
||||
override val str: String = "test 1"
|
||||
override open fun bar(): Int = 42
|
||||
}
|
||||
|
||||
|
||||
// FILE: exportes.kt
|
||||
@file:JsExport
|
||||
|
||||
interface I : ParentI {
|
||||
val value: Int
|
||||
var variable: Int
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
class ExportedClass(override val value: Int) : ExtendedI {
|
||||
override var variable: Int = value
|
||||
override fun foo(): String = "Exported"
|
||||
override val str: String = "test 2"
|
||||
override open fun bar(): Int = 43
|
||||
}
|
||||
|
||||
class AnotherOne : NotExportedClass(42) {
|
||||
override fun foo(): String = "Another One Exported"
|
||||
}
|
||||
|
||||
fun generateNotExported(value: Int): NotExportedClass {
|
||||
return NotExportedClass(value)
|
||||
}
|
||||
|
||||
fun consume(i: I): String {
|
||||
return "Value is ${i.value}, variable is ${i.variable} and result is '${i.foo()}'"
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import * as pkg from "./exportFileWithInterface-export_interface_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
const { I, ExportedClass, AnotherOne, generateNotExported, consume } = pkg
|
||||
if (I !== undefined) return "Fail: module should not export interface in runtime"
|
||||
|
||||
const exported = new ExportedClass(1)
|
||||
const another = new AnotherOne()
|
||||
const notExported = generateNotExported (3)
|
||||
|
||||
if (exported.foo() !== "Exported") return "Fail: foo function was not generated for ExportedClass"
|
||||
if (another.foo() !== "Another One Exported") return "Fail: foo function was not generated for AnotherOne"
|
||||
if (notExported.foo() !== "Not Exported") return "Fail: foo function was not generated for NotExportedClass"
|
||||
|
||||
if (exported.value !== 1) return "Fail: value getter was not generated for ExportedClass"
|
||||
if (another.value !== 42) return "Fail: value getter was not generated for AnotherOne"
|
||||
if (notExported.value !== 3) return "Fail: value getter was not generated for NotExportedClass"
|
||||
|
||||
if (exported.variable !== 1) return "Fail: variable getter was not generated for ExportedClass"
|
||||
if (another.variable !== 42) return "Fail: variable getter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 3) return "Fail: variable getter was not generated for NotExportedClass"
|
||||
|
||||
exported.variable = 101
|
||||
another.variable = 102
|
||||
notExported.variable = 103
|
||||
|
||||
if (exported.variable !== 101) return "Fail: variable setter was not generated for ExportedClass"
|
||||
if (another.variable !== 102) return "Fail: variable setter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 103) return "Fail: variable setter was not generated for NotExportedClass"
|
||||
|
||||
try {
|
||||
notExported.value = 42
|
||||
} catch(e) {}
|
||||
if (notExported.value !== 3) return "Fail: value setter was generated for NotExportedClass, but it shouldn't"
|
||||
|
||||
if (consume(exported) !== "Value is 1, variable is 101 and result is 'Exported'") return "Fail: methods or fields of ExportedClass was mangled"
|
||||
if (consume(another) !== "Value is 42, variable is 102 and result is 'Another One Exported'") return "Fail: methods or fields of AnotherOne was mangled"
|
||||
if (consume(notExported) !== "Value is 3, variable is 103 and result is 'Not Exported'") return "Fail: methods or fields of NotExported was mangled"
|
||||
|
||||
if (notExported.str !== undefined) return "Fail: str should not exist inside NotExportedClass"
|
||||
if (exported.str !== undefined) return "Fail: str should not exist inside ExportedClass"
|
||||
|
||||
if (notExported.bar !== undefined) return "Fail: bar should not exist inside NotExportedClass"
|
||||
if (exported.bar !== undefined) return "Fail: bar should not exist inside ExportedClass"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: export_nested_class
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
abstract class A {
|
||||
abstract fun foo(k: String): String
|
||||
}
|
||||
|
||||
class B {
|
||||
class Foo : A() {
|
||||
override fun foo(k: String): String {
|
||||
return "O" + k
|
||||
}
|
||||
|
||||
fun bar(k: String): String {
|
||||
return foo(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MyObject {
|
||||
class A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
class B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
class C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: test.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { B, MyObject } from "./exportFileWithNestedClass-export_nested_class_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (new B.Foo().bar("K") != "OK") return "fail 1";
|
||||
|
||||
const myObject = MyObject.getInstance()
|
||||
if (new myObject.A().valueA() != "OK") return "fail 2";
|
||||
if (new myObject.B().valueB() != "OK") return "fail 3";
|
||||
if (new myObject.C().valueC() != "OK") return "fail 4";
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1265
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_DCE_DRIVEN
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// See KT-43783
|
||||
|
||||
// MODULE: nestedObjectExport
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
class Abc {
|
||||
companion object AbcCompanion {
|
||||
fun xyz(): String = "Companion object method OK"
|
||||
|
||||
val prop: String
|
||||
get() = "Companion object property OK"
|
||||
}
|
||||
}
|
||||
|
||||
class Foo {
|
||||
companion object {
|
||||
fun xyz(): String = "Companion object method OK"
|
||||
|
||||
val prop: String
|
||||
get() = "Companion object property OK"
|
||||
}
|
||||
}
|
||||
|
||||
sealed class MyEnum(val name: String) {
|
||||
object A: MyEnum("A")
|
||||
object B: MyEnum("B")
|
||||
object C: MyEnum("C")
|
||||
}
|
||||
|
||||
object MyObject {
|
||||
object A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
object B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
object C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Foo, Abc, MyEnum, MyObject } from "./exportFileWithNestedObject-nestedObjectExport_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (Abc.AbcCompanion.xyz() != 'Companion object method OK') return 'companion object function failure';
|
||||
if (Abc.AbcCompanion.prop != 'Companion object property OK') return 'companion object property failure';
|
||||
|
||||
if (Foo.Companion.xyz() != 'Companion object method OK') return 'companion object function failure';
|
||||
if (Foo.Companion.prop != 'Companion object property OK') return 'companion object property failure';
|
||||
|
||||
if (MyEnum.A.name != 'A') return 'MyEnum.A failure';
|
||||
if (MyEnum.B.name != 'B') return 'MyEnum.B failure';
|
||||
if (MyEnum.C.name != 'C') return 'MyEnum.C failure';
|
||||
|
||||
if (MyObject.getInstance().A.valueA() != "OK") return 'MyObject.A failure';
|
||||
if (MyObject.getInstance().B.valueB() != "OK") return 'MyObject.B failure';
|
||||
if (MyObject.getInstance().C.valueC() != "OK") return 'MyObject.C failure';
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1265
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: exportProtectedMembers
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
open class Foo protected constructor() {
|
||||
protected fun bar(): String = "protected method"
|
||||
|
||||
private var _baz: String = "baz"
|
||||
|
||||
protected var baz: String
|
||||
get() = _baz
|
||||
set(value) {
|
||||
_baz = value
|
||||
}
|
||||
|
||||
protected val bazReadOnly: String
|
||||
get() = _baz
|
||||
|
||||
protected val quux: String = "quux"
|
||||
|
||||
protected var quuz: String = "quuz"
|
||||
|
||||
protected class NestedClass {
|
||||
val prop: String = "nested class property"
|
||||
}
|
||||
protected object NestedObject {
|
||||
val prop: String = "nested object property"
|
||||
}
|
||||
|
||||
protected companion object {
|
||||
val prop: String = "companion object property"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Foo } from "./exportFileWithProtectedMembers-exportProtectedMembers_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
var foo = new Foo();
|
||||
|
||||
if (foo.bar() != 'protected method') return 'failed to call protected method';
|
||||
if (foo.baz != 'baz') return 'failed to read `baz`';
|
||||
if (foo.bazReadOnly != 'baz') return 'failed to read `bazReadOnly`';
|
||||
foo.baz = 'beer';
|
||||
if (foo.baz != 'beer') return 'failed to write protected var';
|
||||
if (foo.bazReadOnly != 'beer') return 'unexpected value of `bazReadOnly` after modifying `baz`';
|
||||
if (foo.quux != 'quux') return 'failed to read `quux`';
|
||||
if (foo.quuz != 'quuz') return 'failed to read `quuz`';
|
||||
foo.quuz = 'ale';
|
||||
if (foo.quuz != 'ale') return 'failed to write `quuz`';
|
||||
|
||||
var nestedClass = new Foo.NestedClass()
|
||||
if (nestedClass.prop != 'nested class property')
|
||||
return 'failed to read protected class property'
|
||||
if (Foo.NestedObject.prop != 'nested object property')
|
||||
return 'failed to read protected nested object property'
|
||||
if (Foo.Companion.prop != 'companion object property')
|
||||
return 'failed to read protected companion object property'
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// INFER_MAIN_MODULE
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: exported_properites
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
val regularValueProperty: String = "regularValueProperty"
|
||||
|
||||
val regularPropertyGetter: String
|
||||
get() = "regularPropertyGetter"
|
||||
|
||||
var regularVariableProperty: String = "regularVariableProperty"
|
||||
|
||||
var regularVariableGetterWithSetter: String = "regularVariableGetterWithSetter"
|
||||
get() = "$field by custom getter"
|
||||
set(value) { field = "$value set by custom setter" }
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import {
|
||||
regularValueProperty,
|
||||
regularPropertyGetter,
|
||||
regularVariableProperty,
|
||||
regularVariableGetterWithSetter
|
||||
} from "./exportFileWithTopLevelProperty-exported_properites_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
if (typeof regularValueProperty.get !== "function" || regularValueProperty.get() !== "regularValueProperty") {
|
||||
return "Fail: wrongly exported getter for regular `val` property"
|
||||
}
|
||||
if (typeof regularValueProperty.set !== "undefined") {
|
||||
return "Fail: wrongly exported setter for regular `val` property"
|
||||
}
|
||||
if (typeof regularPropertyGetter.get !== "function" || regularPropertyGetter.get() !== "regularPropertyGetter") {
|
||||
return "Fail: wrongly exported getter for a `val` property with custom getter"
|
||||
}
|
||||
if (typeof regularPropertyGetter.set !== "undefined") {
|
||||
return "Fail: wrongly exported setter for a `val` property with custom getter"
|
||||
}
|
||||
if (typeof regularVariableProperty.get !== "function" || regularVariableProperty.get() !== "regularVariableProperty") {
|
||||
return "Fail: wrongly exported getter for regular `var` property"
|
||||
}
|
||||
if (typeof regularVariableProperty.set !== "function" || (regularVariableProperty.set("test1"), regularVariableProperty.get()) !== "test1") {
|
||||
return "Fail: wrongly exported setter for regular `var` property"
|
||||
}
|
||||
if (typeof regularVariableGetterWithSetter.get !== "function" || regularVariableGetterWithSetter.get() !== "regularVariableGetterWithSetter by custom getter") {
|
||||
return "Fail: wrongly exported getter for a `var` property with custom getter and setter"
|
||||
}
|
||||
if (typeof regularVariableGetterWithSetter.set !== "function" || (regularVariableGetterWithSetter.set("test1"), regularVariableGetterWithSetter.get()) !== "test1 set by custom setter by custom getter") {
|
||||
return "Fail: wrongly exported setter for a `var` property with custom getter and setter"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// IGNORE_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
|
||||
// MODULE: export_inner_class
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
class RegularParent(val value: String) {
|
||||
inner class RegularInner(val message: String, val anotherValue: Int) {
|
||||
fun getResult() = value + message
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JsExport
|
||||
class ParentForSecondary {
|
||||
inner class InnerWithSecondaryConstructor(val value: String) {
|
||||
@JsName("innerSuccess")
|
||||
constructor(): this("OK")
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class ParentWithSecondary(val value: String) {
|
||||
@JsName("createO")
|
||||
constructor(): this("O")
|
||||
|
||||
inner class InnerWithSecondaryConstructor(val anotherValue: String) {
|
||||
@JsName("createK")
|
||||
constructor(): this("K")
|
||||
|
||||
fun getResult() = value + anotherValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { RegularParent, ParentForSecondary, ParentWithSecondary } from "./exportInnerClass-export_inner_class_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
var regularParent = new RegularParent("O")
|
||||
var regularInner = new regularParent.RegularInner("K", 42)
|
||||
|
||||
if (regularInner.anotherValue !== 42) return "Fail: second parameter of the RegularInner primary constructor was ignored"
|
||||
if (regularInner.getResult() !== "OK") return "Fail: something is going wrong with the outer this capturing logic"
|
||||
|
||||
var parentForSecondary = new ParentForSecondary()
|
||||
var innerWithSecondary = new parentForSecondary.InnerWithSecondaryConstructor("OK")
|
||||
|
||||
if (innerWithSecondary.value !== "OK") return "Fail: something is going wrong with primary constructor when a secondary one exists"
|
||||
|
||||
var fromSecondary = parentForSecondary.InnerWithSecondaryConstructor.innerSuccess()
|
||||
|
||||
if (fromSecondary.value !== "OK") return "Fail: something is going wrong with secondary constructor inside the inner class"
|
||||
|
||||
var parentFromSecondary = ParentWithSecondary.createO()
|
||||
var innerFromSecondary = parentFromSecondary.InnerWithSecondaryConstructor.createK()
|
||||
|
||||
if (innerFromSecondary.getResult() !== "OK") return "Fail: there is a problem when both parent and inner class have secondary constructors"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: export_interface
|
||||
// FILE: lib.kt
|
||||
|
||||
interface ParentI {
|
||||
val str: String
|
||||
}
|
||||
|
||||
@JsExport
|
||||
interface I : ParentI {
|
||||
val value: Int
|
||||
var variable: Int
|
||||
fun foo(): String
|
||||
}
|
||||
|
||||
interface ExtendedI: I {
|
||||
fun bar(): Int
|
||||
}
|
||||
|
||||
open class NotExportedClass(override var value: Int) : ExtendedI {
|
||||
override var variable: Int = value
|
||||
override open fun foo(): String = "Not Exported"
|
||||
override val str: String = "test 1"
|
||||
override open fun bar(): Int = 42
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class ExportedClass(override val value: Int) : ExtendedI {
|
||||
override var variable: Int = value
|
||||
override fun foo(): String = "Exported"
|
||||
override val str: String = "test 2"
|
||||
override open fun bar(): Int = 43
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class AnotherOne : NotExportedClass(42) {
|
||||
override fun foo(): String = "Another One Exported"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun generateNotExported(value: Int): NotExportedClass {
|
||||
return NotExportedClass(value)
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun consume(i: I): String {
|
||||
return "Value is ${i.value}, variable is ${i.variable} and result is '${i.foo()}'"
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import * as pkg from "./exportInterface-export_interface_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
const { I, ExportedClass, AnotherOne, generateNotExported, consume } = pkg
|
||||
|
||||
if (I !== undefined) return "Fail: module should not export interface in runtime"
|
||||
|
||||
const exported = new ExportedClass(1)
|
||||
const another = new AnotherOne()
|
||||
const notExported = generateNotExported (3)
|
||||
|
||||
if (exported.foo() !== "Exported") return "Fail: foo function was not generated for ExportedClass"
|
||||
if (another.foo() !== "Another One Exported") return "Fail: foo function was not generated for AnotherOne"
|
||||
if (notExported.foo() !== "Not Exported") return "Fail: foo function was not generated for NotExportedClass"
|
||||
|
||||
if (exported.value !== 1) return "Fail: value getter was not generated for ExportedClass"
|
||||
if (another.value !== 42) return "Fail: value getter was not generated for AnotherOne"
|
||||
if (notExported.value !== 3) return "Fail: value getter was not generated for NotExportedClass"
|
||||
|
||||
if (exported.variable !== 1) return "Fail: variable getter was not generated for ExportedClass"
|
||||
if (another.variable !== 42) return "Fail: variable getter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 3) return "Fail: variable getter was not generated for NotExportedClass"
|
||||
|
||||
exported.variable = 101
|
||||
another.variable = 102
|
||||
notExported.variable = 103
|
||||
|
||||
if (exported.variable !== 101) return "Fail: variable setter was not generated for ExportedClass"
|
||||
if (another.variable !== 102) return "Fail: variable setter was not generated for AnotherOne"
|
||||
if (notExported.variable !== 103) return "Fail: variable setter was not generated for NotExportedClass"
|
||||
|
||||
try {
|
||||
notExported.value = 42
|
||||
} catch(e) {}
|
||||
|
||||
if (notExported.value !== 3) return "Fail: value setter was generated for NotExportedClass, but it shouldn't"
|
||||
|
||||
if (consume(exported) !== "Value is 1, variable is 101 and result is 'Exported'") return "Fail: methods or fields of ExportedClass was mangled"
|
||||
if (consume(another) !== "Value is 42, variable is 102 and result is 'Another One Exported'") return "Fail: methods or fields of AnotherOne was mangled"
|
||||
if (consume(notExported) !== "Value is 3, variable is 103 and result is 'Not Exported'") return "Fail: methods or fields of NotExported was mangled"
|
||||
|
||||
if (notExported.str !== undefined) return "Fail: str should not exist inside NotExportedClass"
|
||||
if (exported.str !== undefined) return "Fail: str should not exist inside ExportedClass"
|
||||
|
||||
if (notExported.bar !== undefined) return "Fail: bar should not exist inside NotExportedClass"
|
||||
if (exported.bar !== undefined) return "Fail: bar should not exist inside ExportedClass"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// IGNORE_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: export_nested_class
|
||||
// FILE: lib.kt
|
||||
|
||||
abstract class A {
|
||||
abstract fun foo(k: String): String
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class B {
|
||||
class Foo : A() {
|
||||
override fun foo(k: String): String {
|
||||
return "O" + k
|
||||
}
|
||||
|
||||
fun bar(k: String): String {
|
||||
return foo(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object MyObject {
|
||||
class A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
class B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
class C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { B, MyObject } from "./exportNestedClass-export_nested_class_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (new B.Foo().bar("K") != "OK") return "fail 1";
|
||||
const myObject = MyObject.getInstance()
|
||||
if (new myObject.A().valueA() != "OK") return "fail 2";
|
||||
if (new myObject.B().valueB() != "OK") return "fail 3";
|
||||
if (new myObject.C().valueC() != "OK") return "fail 4";
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1265
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_DCE_DRIVEN
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// See KT-43783
|
||||
|
||||
// MODULE: nestedObjectExport
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
class Abc {
|
||||
companion object AbcCompanion {
|
||||
fun xyz(): String = "Companion object method OK"
|
||||
|
||||
val prop: String
|
||||
get() = "Companion object property OK"
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
class Foo {
|
||||
companion object {
|
||||
fun xyz(): String = "Companion object method OK"
|
||||
|
||||
val prop: String
|
||||
get() = "Companion object property OK"
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
sealed class MyEnum(val name: String) {
|
||||
object A: MyEnum("A")
|
||||
object B: MyEnum("B")
|
||||
object C: MyEnum("C")
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object MyObject {
|
||||
object A {
|
||||
fun valueA() = "OK"
|
||||
}
|
||||
object B {
|
||||
fun valueB() = "OK"
|
||||
}
|
||||
object C {
|
||||
fun valueC() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Abc, Foo, MyEnum, MyObject } from "./exportNestedObject-nestedObjectExport_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
if (Abc.AbcCompanion.xyz() != 'Companion object method OK') return 'companion object function failure';
|
||||
if (Abc.AbcCompanion.prop != 'Companion object property OK') return 'companion object property failure';
|
||||
|
||||
if (Foo.Companion.xyz() != 'Companion object method OK') return 'companion object function failure';
|
||||
if (Foo.Companion.prop != 'Companion object property OK') return 'companion object property failure';
|
||||
|
||||
if (MyEnum.A.name != 'A') return 'MyEnum.A failure';
|
||||
if (MyEnum.B.name != 'B') return 'MyEnum.B failure';
|
||||
if (MyEnum.C.name != 'C') return 'MyEnum.C failure';
|
||||
|
||||
if (MyObject.getInstance().A.valueA() != "OK") return 'MyObject.A failure';
|
||||
if (MyObject.getInstance().B.valueB() != "OK") return 'MyObject.B failure';
|
||||
if (MyObject.getInstance().C.valueC() != "OK") return 'MyObject.C failure';
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1265
|
||||
// ES_MODULES
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: exportProtectedMembers
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
open class Foo protected constructor() {
|
||||
protected fun bar(): String = "protected method"
|
||||
|
||||
private var _baz: String = "baz"
|
||||
|
||||
protected var baz: String
|
||||
get() = _baz
|
||||
set(value) {
|
||||
_baz = value
|
||||
}
|
||||
|
||||
protected val bazReadOnly: String
|
||||
get() = _baz
|
||||
|
||||
protected val quux: String = "quux"
|
||||
|
||||
protected var quuz: String = "quuz"
|
||||
|
||||
protected class NestedClass {
|
||||
val prop: String = "nested class property"
|
||||
}
|
||||
protected object NestedObject {
|
||||
val prop: String = "nested object property"
|
||||
}
|
||||
|
||||
protected companion object {
|
||||
val prop: String = "companion object property"
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Foo } from "./exportProtectedMembers-exportProtectedMembers_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
var foo = new Foo();
|
||||
|
||||
if (foo.bar() != 'protected method') return 'failed to call protected method';
|
||||
if (foo.baz != 'baz') return 'failed to read `baz`';
|
||||
if (foo.bazReadOnly != 'baz') return 'failed to read `bazReadOnly`';
|
||||
foo.baz = 'beer';
|
||||
if (foo.baz != 'beer') return 'failed to write protected var';
|
||||
if (foo.bazReadOnly != 'beer') return 'unexpected value of `bazReadOnly` after modifying `baz`';
|
||||
if (foo.quux != 'quux') return 'failed to read `quux`';
|
||||
if (foo.quuz != 'quuz') return 'failed to read `quuz`';
|
||||
foo.quuz = 'ale';
|
||||
if (foo.quuz != 'ale') return 'failed to write `quuz`';
|
||||
|
||||
var nestedClass = new Foo.NestedClass()
|
||||
if (nestedClass.prop != 'nested class property')
|
||||
return 'failed to read protected class property'
|
||||
if (Foo.NestedObject.prop != 'nested object property')
|
||||
return 'failed to read protected nested object property'
|
||||
if (Foo.Companion.prop != 'companion object property')
|
||||
return 'failed to read protected companion object property'
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: exported_properites
|
||||
// FILE: lib.kt
|
||||
|
||||
@JsExport
|
||||
val regularValueProperty: String = "regularValueProperty"
|
||||
|
||||
@JsExport
|
||||
val regularPropertyGetter: String
|
||||
get() = "regularPropertyGetter"
|
||||
|
||||
@JsExport
|
||||
var regularVariableProperty: String = "regularVariableProperty"
|
||||
|
||||
@JsExport
|
||||
var regularVariableGetterWithSetter: String = "regularVariableGetterWithSetter"
|
||||
get() = "$field by custom getter"
|
||||
set(value) { field = "$value set by custom setter" }
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import {
|
||||
regularValueProperty,
|
||||
regularPropertyGetter,
|
||||
regularVariableProperty,
|
||||
regularVariableGetterWithSetter
|
||||
} from "./exportTopLevelProperty-exported_properites_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
if (typeof regularValueProperty.get !== "function" || regularValueProperty.get() !== "regularValueProperty") {
|
||||
return "Fail: wrongly exported getter for regular `val` property"
|
||||
}
|
||||
if (typeof regularValueProperty.set !== "undefined") {
|
||||
return "Fail: wrongly exported setter for regular `val` property"
|
||||
}
|
||||
if (typeof regularPropertyGetter.get !== "function" || regularPropertyGetter.get() !== "regularPropertyGetter") {
|
||||
return "Fail: wrongly exported getter for a `val` property with custom getter"
|
||||
}
|
||||
if (typeof regularPropertyGetter.set !== "undefined") {
|
||||
return "Fail: wrongly exported setter for a `val` property with custom getter"
|
||||
}
|
||||
if (typeof regularVariableProperty.get !== "function" || regularVariableProperty.get() !== "regularVariableProperty") {
|
||||
return "Fail: wrongly exported getter for regular `var` property"
|
||||
}
|
||||
if (typeof regularVariableProperty.set !== "function" || (regularVariableProperty.set("test1"), regularVariableProperty.get()) !== "test1") {
|
||||
return "Fail: wrongly exported setter for regular `var` property"
|
||||
}
|
||||
if (typeof regularVariableGetterWithSetter.get !== "function" || regularVariableGetterWithSetter.get() !== "regularVariableGetterWithSetter by custom getter") {
|
||||
return "Fail: wrongly exported getter for a `var` property with custom getter and setter"
|
||||
}
|
||||
if (typeof regularVariableGetterWithSetter.set !== "function" || (regularVariableGetterWithSetter.set("test1"), regularVariableGetterWithSetter.get()) !== "test1 set by custom setter by custom getter") {
|
||||
return "Fail: wrongly exported setter for a `var` property with custom getter and setter"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// SKIP_MINIFICATION
|
||||
// INFER_MAIN_MODULE
|
||||
// SKIP_NODE_JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: non_identifier_module_name
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
@JsName("foo")
|
||||
@JsExport
|
||||
@@ -12,5 +11,8 @@ public fun foo(k: String): String = "O$k"
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { foo } from "./non_identifier_module_name/index.js";
|
||||
console.assert(foo("K") == "OK");
|
||||
import { foo } from "./nonIndetifierModuleName-lib_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return foo("K")
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1270
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
@JsName("foo")
|
||||
public fun foo(k: String): String = "O$k"
|
||||
|
||||
// FILE: enty.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import { foo } from "./nonIndetifierModuleNameInExportedFile-lib_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return foo("K")
|
||||
}
|
||||
+13
-7
@@ -1,6 +1,5 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// INFER_MAIN_MODULE
|
||||
|
||||
// ES_MODULES
|
||||
// MODULE: overriden_chain_non_export_intermediate
|
||||
@@ -28,12 +27,19 @@ class C : B() {
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { C } from "./overriden_chain_non_export_intermediate/index.js";
|
||||
import { C } from "./overriddenChainNonExportIntermediate-overriden_chain_non_export_intermediate_v5.mjs";
|
||||
|
||||
function test(c) {
|
||||
if (c.foo() === "foo" && c.bar() === "bar" && c.bay() == "bay") return "OK"
|
||||
export function box() {
|
||||
const c = new C()
|
||||
|
||||
return "fail"
|
||||
const foo = c.foo()
|
||||
if (foo !== "foo") return `Fail: expect c.foo() to return 'foo' but it returns ${foo}`
|
||||
|
||||
const bar = c.bar()
|
||||
if (bar !== "bar") return `Fail: expect c.bar() to return 'bar' but it returns ${bar}`
|
||||
|
||||
const bay = c.bay()
|
||||
if (bay !== "bay") return `Fail: expect c.bay() to return 'bay' but it returns ${bay}`
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
console.assert(test(new C()) == "OK");
|
||||
js/js.translator/testData/box/esModules/export/overriddenChainNonExportIntermediateInExportedFile.kt
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: overriden_chain_non_export_intermediate
|
||||
// FILE: not_exported.kt
|
||||
abstract class B : A() {
|
||||
abstract fun baz(): String
|
||||
|
||||
override fun foo(): String = "foo"
|
||||
}
|
||||
|
||||
|
||||
// FILE: exported.kt
|
||||
@file:JsExport
|
||||
|
||||
abstract class A {
|
||||
abstract fun foo(): String
|
||||
|
||||
abstract fun bar(): String
|
||||
}
|
||||
|
||||
class C : B() {
|
||||
override fun bar(): String = "bar"
|
||||
override fun baz(): String = "baz"
|
||||
|
||||
fun bay(): String = "bay"
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { C } from "./overriddenChainNonExportIntermediateInExportedFile-overriden_chain_non_export_intermediate_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
var c = new C();
|
||||
if (c.foo() === "foo" && c.bar() === "bar" && c.bay() == "bay") return "OK"
|
||||
|
||||
return "fail"
|
||||
}
|
||||
Vendored
+4
-6
@@ -1,9 +1,8 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// INFER_MAIN_MODULE
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: overriden_external_method_with_same_name_method
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
external abstract class Foo {
|
||||
abstract fun o(): String
|
||||
@@ -32,10 +31,9 @@ Foo.prototype.k = function() {
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Baz } from "./overriden_external_method_with_same_name_method/index.js";
|
||||
import { Baz } from "./overriddenExternalMethodWithSameNameMethod-lib_v5.mjs";
|
||||
|
||||
function test(foo) {
|
||||
export function box() {
|
||||
const foo = new Baz()
|
||||
return foo.o() + foo.k()
|
||||
}
|
||||
|
||||
console.assert(test(new Baz()) == "OK");
|
||||
Vendored
+5
-10
@@ -1,12 +1,8 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// IGNORE_BACKEND: JS
|
||||
// INFER_MAIN_MODULE
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// TODO: Fix tests on Windows
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
// MODULE: overriden_external_method_with_same_stable_name_method
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
external abstract class Foo {
|
||||
abstract fun o(): String
|
||||
@@ -36,12 +32,11 @@ Foo.prototype.k = function() {
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Baz } from "./overriden-external-method-with-same-stable-name-method/index.js";
|
||||
import { Baz } from "./overriddenExternalMethodWithSameStableNameMethod-lib_v5.mjs";
|
||||
|
||||
function test(foo) {
|
||||
export function box() {
|
||||
const foo = new Baz()
|
||||
const oStable = foo.oStable("OK")
|
||||
if (oStable !== "OK") return "false: " + oStable
|
||||
return foo.o() + foo.k()
|
||||
}
|
||||
|
||||
console.assert(test(new Baz()) == "OK");
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1252
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: lib
|
||||
// FILE: not_exported.kt
|
||||
|
||||
external abstract class Foo {
|
||||
abstract fun o(): String
|
||||
}
|
||||
|
||||
abstract class Bar : Foo() {
|
||||
@JsName("oStable")
|
||||
abstract fun String.o(): String
|
||||
|
||||
override fun o(): String {
|
||||
return "O".o()
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: exported.kt
|
||||
@file:JsExport
|
||||
|
||||
class Baz : Bar() {
|
||||
override fun String.o(): String {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: foo.js
|
||||
function Foo() {}
|
||||
Foo.prototype.k = function() {
|
||||
return "K"
|
||||
}
|
||||
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { Baz } from "./overriddenExternalMethodWithSameStableNameMethodInExportedFile-lib_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
return test(new Baz());
|
||||
}
|
||||
|
||||
function test(foo) {
|
||||
const oStable = foo.oStable("OK")
|
||||
if (oStable !== "OK") return "false: " + oStable
|
||||
return foo.o() + foo.k()
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1270
|
||||
// SKIP_MINIFICATION
|
||||
// INFER_MAIN_MODULE
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: if
|
||||
@@ -12,6 +11,8 @@ public fun foo(k: String): String = "O$k"
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { foo } from "./if/index.js";
|
||||
import { foo } from "./reservedModuleName-if_v5.mjs";
|
||||
|
||||
console.assert(foo("K") == "OK");
|
||||
export function box() {
|
||||
return foo("K")
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1270
|
||||
// SKIP_MINIFICATION
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: if
|
||||
// FILE: lib.kt
|
||||
@file:JsExport
|
||||
|
||||
@JsName("foo")
|
||||
public fun foo(k: String): String = "O$k"
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { foo } from "./reservedModuleNameInExportedFile-if_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
return foo("K")
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// EXPECTED_REACHABLE_NODES: 1270
|
||||
// SKIP_MINIFICATION
|
||||
// ES_MODULES
|
||||
|
||||
// MODULE: vararg
|
||||
// FILE: lib.kt
|
||||
@JsExport
|
||||
fun uintVararg(vararg uints: UInt): String {
|
||||
for (u in uints) {
|
||||
if (u == 0u) return "Failed"
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun uint(a: Int): UInt {
|
||||
return a.toUInt()
|
||||
}
|
||||
|
||||
// FILE: main.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { uint, uintVararg } from "./vararg-vararg_v5.mjs"
|
||||
|
||||
export function box() {
|
||||
return uintVararg([uint(1), uint(2), uint(3)])
|
||||
}
|
||||
+4
-2
@@ -30,6 +30,8 @@ fun testOk(ok: Any): String {
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { convolutedOk, testOk } from "./main/index.js";
|
||||
import { convolutedOk, testOk } from "./inlinedObjectLiteralIsCheck_v5.mjs";
|
||||
|
||||
console.assert(testOk(convolutedOk()) == "OK");
|
||||
export function box() {
|
||||
return testOk(convolutedOk())
|
||||
}
|
||||
|
||||
@@ -48,8 +48,5 @@ fun box(): String {
|
||||
return "Fail6: ${res.component2}"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Point } from "./main/index.js";
|
||||
import { Point } from "./dataClass_v5.mjs";
|
||||
|
||||
export default function() {
|
||||
var p = new Point(3, 7);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as api from "./main/index.js";
|
||||
import * as api from "./exportedDefaultStub_v5.mjs";
|
||||
|
||||
export default function() {
|
||||
var ping = api.ping;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { A, B } from "./main/index.js";
|
||||
import { A, B } from "./jsExportInClass_v5.mjs";
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ping, Something } from "./main/index.js"
|
||||
import { ping, Something } from "./recursiveExport_v5.mjs"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
|
||||
@@ -24,7 +24,7 @@ open class B {
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
|
||||
import { A, B } from "./main/index.js";
|
||||
import { A, B } from "./inheritanceInNativeClass_v5.mjs";
|
||||
|
||||
function createA() {
|
||||
function ADerived() {
|
||||
@@ -46,7 +46,7 @@ function createB() {
|
||||
return new BDerived();
|
||||
}
|
||||
|
||||
function box() {
|
||||
export function box() {
|
||||
let a = createA();
|
||||
if (a.bar(0) != 124) return "fail1";
|
||||
|
||||
@@ -55,5 +55,3 @@ function box() {
|
||||
|
||||
return "OK";
|
||||
}
|
||||
|
||||
console.assert(box() == "OK");
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export namespace foo {
|
||||
export declare namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
constructor(x: number);
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare const value: { get(): number; };
|
||||
export declare const variable: { get(): number; set(value: number): void; };
|
||||
export declare class C {
|
||||
constructor(x: number);
|
||||
get x(): number;
|
||||
doubleX(): number;
|
||||
}
|
||||
export declare const O: {
|
||||
getInstance(): {
|
||||
get value(): number;
|
||||
};
|
||||
};
|
||||
export declare const Parent: {
|
||||
getInstance(): typeof __NonExistentParent;
|
||||
};
|
||||
declare abstract class __NonExistentParent extends _objects_.foo$Parent {
|
||||
private constructor();
|
||||
}
|
||||
declare namespace __NonExistentParent {
|
||||
class Nested {
|
||||
constructor();
|
||||
get value(): number;
|
||||
}
|
||||
}
|
||||
export declare function box(): string;
|
||||
declare namespace _objects_ {
|
||||
const foo$Parent: {
|
||||
get value(): number;
|
||||
} & {
|
||||
new(): any;
|
||||
};
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/** This file is generated by {@link :js:js.test:generateJsExportOnFileTestFilesForTS} task. DO NOT MODIFY MANUALLY */
|
||||
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// INFER_MAIN_MODULE
|
||||
// MODULE: JS_TESTS
|
||||
// MODULE_KIND: ES
|
||||
// FILE: esm.kt
|
||||
|
||||
@file:JsExport
|
||||
|
||||
package foo
|
||||
|
||||
|
||||
val value = 10
|
||||
|
||||
|
||||
var variable = 10
|
||||
|
||||
|
||||
class C(val x: Int) {
|
||||
fun doubleX() = x * 2
|
||||
}
|
||||
|
||||
|
||||
object O {
|
||||
val value = 10
|
||||
}
|
||||
|
||||
|
||||
object Parent {
|
||||
val value = 10
|
||||
class Nested {
|
||||
val value = 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun box(): String = "OK"
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export namespace foo {
|
||||
export declare namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
constructor(x: number);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export namespace foo {
|
||||
export declare namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
constructor(x: number);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare const value: { get(): number; };
|
||||
export declare const variable: { get(): number; set(value: number): void; };
|
||||
export declare class C {
|
||||
constructor(x: number);
|
||||
get x(): number;
|
||||
doubleX(): number;
|
||||
}
|
||||
export declare const O: {
|
||||
getInstance(): {
|
||||
get value(): number;
|
||||
};
|
||||
};
|
||||
export declare const Parent: {
|
||||
getInstance(): typeof __NonExistentParent;
|
||||
};
|
||||
declare abstract class __NonExistentParent extends _objects_.foo$Parent {
|
||||
private constructor();
|
||||
}
|
||||
declare namespace __NonExistentParent {
|
||||
class Nested {
|
||||
constructor();
|
||||
get value(): number;
|
||||
}
|
||||
}
|
||||
export declare function box(): string;
|
||||
declare namespace _objects_ {
|
||||
const foo$Parent: {
|
||||
get value(): number;
|
||||
} & {
|
||||
new(): any;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// INFER_MAIN_MODULE
|
||||
// MODULE: JS_TESTS
|
||||
// MODULE_KIND: ES
|
||||
// FILE: esm.kt
|
||||
|
||||
package foo
|
||||
|
||||
@JsExport
|
||||
val value = 10
|
||||
|
||||
@JsExport
|
||||
var variable = 10
|
||||
|
||||
@JsExport
|
||||
class C(val x: Int) {
|
||||
fun doubleX() = x * 2
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object O {
|
||||
val value = 10
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object Parent {
|
||||
val value = 10
|
||||
class Nested {
|
||||
val value = 10
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun box(): String = "OK"
|
||||
@@ -1,5 +1,5 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export namespace foo {
|
||||
export declare namespace foo {
|
||||
const prop: number;
|
||||
class C {
|
||||
constructor(x: number);
|
||||
|
||||
+6
@@ -8,6 +8,12 @@ declare namespace JS_TESTS {
|
||||
foo(): number;
|
||||
};
|
||||
function takesO(o: typeof foo.O): number;
|
||||
const WithSimpleObjectInside: {
|
||||
get value(): string;
|
||||
get SimpleObject(): {
|
||||
get value(): string;
|
||||
};
|
||||
};
|
||||
abstract class Parent {
|
||||
private constructor();
|
||||
}
|
||||
|
||||
+8
@@ -26,6 +26,14 @@ fun takesO(o: O): Int =
|
||||
O.x + O.foo()
|
||||
|
||||
|
||||
object WithSimpleObjectInside {
|
||||
val value: String = "WithSimpleObjectInside"
|
||||
object SimpleObject {
|
||||
val value: String = "SimpleObject"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object Parent {
|
||||
object Nested1 {
|
||||
val value: String = "Nested1"
|
||||
|
||||
+3
@@ -7,6 +7,7 @@ var getParent = JS_TESTS.foo.getParent;
|
||||
var createNested1 = JS_TESTS.foo.createNested1;
|
||||
var createNested2 = JS_TESTS.foo.createNested2;
|
||||
var createNested3 = JS_TESTS.foo.createNested3;
|
||||
var WithSimpleObjectInside = JS_TESTS.foo.WithSimpleObjectInside;
|
||||
function assert(condition) {
|
||||
if (!condition) {
|
||||
throw "Assertion failed";
|
||||
@@ -27,5 +28,7 @@ function box() {
|
||||
assert(createNested1() === nested1);
|
||||
assert(createNested2() !== nested2 && createNested2() instanceof Parent.Nested1.Nested2);
|
||||
assert(createNested3() !== nested3 && createNested3() instanceof Parent.Nested1.Nested2.Companion.Nested3);
|
||||
assert(WithSimpleObjectInside.value === "WithSimpleObjectInside");
|
||||
assert(WithSimpleObjectInside.SimpleObject.value === "SimpleObject");
|
||||
return "OK";
|
||||
}
|
||||
|
||||
+4
@@ -6,6 +6,7 @@ import getParent = JS_TESTS.foo.getParent;
|
||||
import createNested1 = JS_TESTS.foo.createNested1;
|
||||
import createNested2 = JS_TESTS.foo.createNested2;
|
||||
import createNested3 = JS_TESTS.foo.createNested3;
|
||||
import WithSimpleObjectInside = JS_TESTS.foo.WithSimpleObjectInside;
|
||||
|
||||
function assert(condition: boolean) {
|
||||
if (!condition) {
|
||||
@@ -30,5 +31,8 @@ function box(): string {
|
||||
assert(createNested1() === nested1)
|
||||
assert(createNested2() !== nested2 && createNested2() instanceof Parent.Nested1.Nested2)
|
||||
assert(createNested3() !== nested3 && createNested3() instanceof Parent.Nested1.Nested2.Companion.Nested3)
|
||||
|
||||
assert(WithSimpleObjectInside.value === "WithSimpleObjectInside");
|
||||
assert(WithSimpleObjectInside.SimpleObject.value === "SimpleObject");
|
||||
return "OK";
|
||||
}
|
||||
@@ -8,6 +8,12 @@ declare namespace JS_TESTS {
|
||||
foo(): number;
|
||||
};
|
||||
function takesO(o: typeof foo.O): number;
|
||||
const WithSimpleObjectInside: {
|
||||
get value(): string;
|
||||
get SimpleObject(): {
|
||||
get value(): string;
|
||||
};
|
||||
};
|
||||
abstract class Parent {
|
||||
private constructor();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@ object O {
|
||||
fun takesO(o: O): Int =
|
||||
O.x + O.foo()
|
||||
|
||||
@JsExport
|
||||
object WithSimpleObjectInside {
|
||||
val value: String = "WithSimpleObjectInside"
|
||||
object SimpleObject {
|
||||
val value: String = "SimpleObject"
|
||||
}
|
||||
}
|
||||
|
||||
@JsExport
|
||||
object Parent {
|
||||
object Nested1 {
|
||||
|
||||
@@ -7,6 +7,7 @@ var getParent = JS_TESTS.foo.getParent;
|
||||
var createNested1 = JS_TESTS.foo.createNested1;
|
||||
var createNested2 = JS_TESTS.foo.createNested2;
|
||||
var createNested3 = JS_TESTS.foo.createNested3;
|
||||
var WithSimpleObjectInside = JS_TESTS.foo.WithSimpleObjectInside;
|
||||
function assert(condition) {
|
||||
if (!condition) {
|
||||
throw "Assertion failed";
|
||||
@@ -27,5 +28,7 @@ function box() {
|
||||
assert(createNested1() === nested1);
|
||||
assert(createNested2() !== nested2 && createNested2() instanceof Parent.Nested1.Nested2);
|
||||
assert(createNested3() !== nested3 && createNested3() instanceof Parent.Nested1.Nested2.Companion.Nested3);
|
||||
assert(WithSimpleObjectInside.value === "WithSimpleObjectInside");
|
||||
assert(WithSimpleObjectInside.SimpleObject.value === "SimpleObject");
|
||||
return "OK";
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import getParent = JS_TESTS.foo.getParent;
|
||||
import createNested1 = JS_TESTS.foo.createNested1;
|
||||
import createNested2 = JS_TESTS.foo.createNested2;
|
||||
import createNested3 = JS_TESTS.foo.createNested3;
|
||||
import WithSimpleObjectInside = JS_TESTS.foo.WithSimpleObjectInside;
|
||||
|
||||
function assert(condition: boolean) {
|
||||
if (!condition) {
|
||||
@@ -30,5 +31,8 @@ function box(): string {
|
||||
assert(createNested1() === nested1)
|
||||
assert(createNested2() !== nested2 && createNested2() instanceof Parent.Nested1.Nested2)
|
||||
assert(createNested3() !== nested3 && createNested3() instanceof Parent.Nested1.Nested2.Companion.Nested3)
|
||||
|
||||
assert(WithSimpleObjectInside.value === "WithSimpleObjectInside");
|
||||
assert(WithSimpleObjectInside.SimpleObject.value === "SimpleObject");
|
||||
return "OK";
|
||||
}
|
||||
+4
@@ -183,4 +183,8 @@ constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun useEsModules() {
|
||||
error("ES modules are not supported in legacy JS compiler. Please, use IR one instead.")
|
||||
}
|
||||
}
|
||||
+1
@@ -53,6 +53,7 @@ interface KotlinJsTargetDsl : KotlinTarget {
|
||||
}
|
||||
|
||||
fun useCommonJs()
|
||||
fun useEsModules()
|
||||
|
||||
val binaries: KotlinJsBinaryContainer
|
||||
|
||||
|
||||
+21
@@ -358,9 +358,30 @@ constructor(
|
||||
legacyTarget?.useCommonJs()
|
||||
}
|
||||
|
||||
override fun useEsModules() {
|
||||
compilations.all {
|
||||
it.kotlinOptions.configureEsModulesOptions()
|
||||
|
||||
binaries
|
||||
.withType(JsIrBinary::class.java)
|
||||
.all {
|
||||
it.linkTask.configure { linkTask ->
|
||||
linkTask.kotlinOptions.configureEsModulesOptions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun KotlinJsOptions.configureCommonJsOptions() {
|
||||
moduleKind = "commonjs"
|
||||
sourceMap = true
|
||||
sourceMapEmbedSources = "never"
|
||||
}
|
||||
|
||||
private fun KotlinJsOptions.configureEsModulesOptions() {
|
||||
moduleKind = "es"
|
||||
sourceMap = true
|
||||
sourceMapEmbedSources = "never"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user