[JS IR BE] Initial support for mudule wrapper generation
This commit is contained in:
@@ -48,7 +48,8 @@ class JsIrBackendContext(
|
||||
val symbolTable: SymbolTable,
|
||||
irModuleFragment: IrModuleFragment,
|
||||
override val configuration: CompilerConfiguration,
|
||||
val dependencies: List<IrModuleFragment>
|
||||
val dependencies: List<CompiledModule>,
|
||||
val moduleType: ModuleType
|
||||
) : CommonBackendContext {
|
||||
|
||||
override val builtIns = module.builtIns
|
||||
@@ -56,6 +57,9 @@ class JsIrBackendContext(
|
||||
val phaseConfig = PhaseConfig(jsPhases, configuration)
|
||||
override var inVerbosePhase: Boolean = false
|
||||
|
||||
val packageLevelJsModules = mutableListOf<IrFile>()
|
||||
val declarationLevelJsModules = mutableListOf<IrDeclaration>()
|
||||
|
||||
val internalPackageFragmentDescriptor = KnownPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal"))
|
||||
val implicitDeclarationFile by lazy {
|
||||
IrFileImpl(object : SourceManager.FileEntry {
|
||||
@@ -176,9 +180,6 @@ class JsIrBackendContext(
|
||||
|
||||
val originalModuleIndex = ModuleIndex(irModuleFragment)
|
||||
|
||||
lateinit var moduleFragmentCopy: IrModuleFragment
|
||||
lateinit var jsProgram: JsNode
|
||||
|
||||
fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type)
|
||||
|
||||
override val ir = object : Ir<CommonBackendContext>(this, irModuleFragment) {
|
||||
@@ -291,4 +292,4 @@ class JsIrBackendContext(
|
||||
/*TODO*/
|
||||
print(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,13 @@ import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.CoroutineIntrinsicLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.FunctionInlining
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.replaceUnboundSymbols
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
|
||||
private fun DeclarationContainerLoweringPass.runOnFilesPostfix(files: Iterable<IrFile>) = files.forEach { runOnFilePostfix(it) }
|
||||
@@ -68,8 +65,10 @@ private fun makeCustomJsModulePhase(
|
||||
}
|
||||
)
|
||||
|
||||
private val moveBodilessDeclarationsToSeparatePlacePhase = makeJsModulePhase(
|
||||
::MoveBodilessDeclarationsToSeparatePlace,
|
||||
private val moveBodilessDeclarationsToSeparatePlacePhase = makeCustomJsModulePhase(
|
||||
{ context, module ->
|
||||
moveBodilessDeclarationsToSeparatePlace(context, module)
|
||||
},
|
||||
name = "MoveBodilessDeclarationsToSeparatePlace",
|
||||
description = "Move `external` and `built-in` declarations into separate place to make the following lowerings do not care about them"
|
||||
)
|
||||
@@ -80,12 +79,6 @@ private val expectDeclarationsRemovingPhase = makeJsModulePhase(
|
||||
description = "Remove expect declaration from module fragment"
|
||||
)
|
||||
|
||||
private val coroutineIntrinsicLoweringPhase = makeJsModulePhase(
|
||||
::CoroutineIntrinsicLowering,
|
||||
name = "CoroutineIntrinsicLowering",
|
||||
description = "Replace common coroutine intrinsics with platform specific ones"
|
||||
)
|
||||
|
||||
private val arrayInlineConstructorLoweringPhase = makeJsModulePhase(
|
||||
::ArrayInlineConstructorLowering,
|
||||
name = "ArrayInlineConstructorLowering",
|
||||
@@ -98,13 +91,6 @@ private val lateinitLoweringPhase = makeJsModulePhase(
|
||||
description = "Insert checks for lateinit field references"
|
||||
)
|
||||
|
||||
private val moduleCopyingPhase = makeCustomJsModulePhase(
|
||||
{ context, module -> context.moduleFragmentCopy = module.deepCopyWithSymbols() },
|
||||
name = "ModuleCopying",
|
||||
description = "<Supposed to be removed> Copy current module to make it accessible from different one",
|
||||
prerequisite = setOf(lateinitLoweringPhase)
|
||||
)
|
||||
|
||||
private val functionInliningPhase = makeCustomJsModulePhase(
|
||||
{ context, module ->
|
||||
FunctionInlining(context).inline(module)
|
||||
@@ -113,7 +99,7 @@ private val functionInliningPhase = makeCustomJsModulePhase(
|
||||
},
|
||||
name = "FunctionInliningPhase",
|
||||
description = "Perform function inlining",
|
||||
prerequisite = setOf(moduleCopyingPhase, lateinitLoweringPhase, arrayInlineConstructorLoweringPhase, coroutineIntrinsicLoweringPhase)
|
||||
prerequisite = setOf(lateinitLoweringPhase, arrayInlineConstructorLoweringPhase)
|
||||
)
|
||||
|
||||
private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase(
|
||||
@@ -196,7 +182,7 @@ private val suspendFunctionsLoweringPhase = makeJsModulePhase(
|
||||
::SuspendFunctionsLowering,
|
||||
name = "SuspendFunctionsLowering",
|
||||
description = "Transform suspend functions into CoroutineImpl instance and build state machine",
|
||||
prerequisite = setOf(unitMaterializationLoweringPhase, coroutineIntrinsicLoweringPhase)
|
||||
prerequisite = setOf(unitMaterializationLoweringPhase)
|
||||
)
|
||||
|
||||
private val privateMembersLoweringPhase = makeJsModulePhase(
|
||||
@@ -347,21 +333,13 @@ private val callsLoweringPhase = makeJsModulePhase(
|
||||
description = "Handle intrinsics"
|
||||
)
|
||||
|
||||
private val irToJsPhase = makeCustomJsModulePhase(
|
||||
{ context, module -> context.jsProgram = IrModuleToJsTransformer(context).let { module.accept(it, null) } },
|
||||
name = "IrModuleToJsTransformer",
|
||||
description = "Generate JsAst from IrTree"
|
||||
)
|
||||
|
||||
val jsPhases = namedIrModulePhase(
|
||||
name = "IrModuleLowering",
|
||||
description = "IR module lowering",
|
||||
lower = moveBodilessDeclarationsToSeparatePlacePhase then
|
||||
expectDeclarationsRemovingPhase then
|
||||
coroutineIntrinsicLoweringPhase then
|
||||
arrayInlineConstructorLoweringPhase then
|
||||
lateinitLoweringPhase then
|
||||
moduleCopyingPhase then
|
||||
functionInliningPhase then
|
||||
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
|
||||
throwableSuccessorsLoweringPhase then
|
||||
@@ -396,6 +374,5 @@ val jsPhases = namedIrModulePhase(
|
||||
blockDecomposerLoweringPhase then
|
||||
primitiveCompanionLoweringPhase then
|
||||
constLoweringPhase then
|
||||
callsLoweringPhase then
|
||||
irToJsPhase
|
||||
)
|
||||
callsLoweringPhase
|
||||
)
|
||||
|
||||
@@ -7,38 +7,57 @@ package org.jetbrains.kotlin.ir.backend.js
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.CoroutineIntrinsicLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
|
||||
data class Result(val moduleDescriptor: ModuleDescriptor, val generatedCode: String, val moduleFragment: IrModuleFragment)
|
||||
enum class ModuleType {
|
||||
TEST_RUNTIME,
|
||||
SECONDARY,
|
||||
MAIN
|
||||
}
|
||||
|
||||
class CompiledModule(
|
||||
val moduleName: String,
|
||||
val generatedCode: String?,
|
||||
val moduleFragment: IrModuleFragment?,
|
||||
val moduleType: ModuleType,
|
||||
val dependencies: List<CompiledModule>
|
||||
) {
|
||||
val descriptor
|
||||
get() = moduleFragment!!.descriptor as ModuleDescriptorImpl
|
||||
}
|
||||
|
||||
fun compile(
|
||||
project: Project,
|
||||
files: List<KtFile>,
|
||||
configuration: CompilerConfiguration,
|
||||
export: FqName? = null,
|
||||
dependencies: List<ModuleDescriptor> = listOf(),
|
||||
irDependencyModules: List<IrModuleFragment> = listOf(),
|
||||
builtInsModule: ModuleDescriptorImpl? = null
|
||||
): Result {
|
||||
export: List<FqName> = emptyList(),
|
||||
dependencies: List<CompiledModule> = emptyList(),
|
||||
builtInsModule: CompiledModule? = null,
|
||||
moduleType: ModuleType
|
||||
): CompiledModule {
|
||||
val analysisResult =
|
||||
TopDownAnalyzerFacadeForJS.analyzeFiles(
|
||||
files,
|
||||
project,
|
||||
configuration,
|
||||
dependencies.mapNotNull { it as? ModuleDescriptorImpl },
|
||||
dependencies.map { it.descriptor },
|
||||
emptyList(),
|
||||
thisIsBuiltInsModule = builtInsModule == null,
|
||||
customBuiltInsModule = builtInsModule
|
||||
customBuiltInsModule = builtInsModule?.descriptor
|
||||
)
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
@@ -46,7 +65,7 @@ fun compile(
|
||||
TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext)
|
||||
|
||||
val symbolTable = SymbolTable()
|
||||
irDependencyModules.forEach { symbolTable.loadModule(it)}
|
||||
dependencies.forEach { symbolTable.loadModule(it.moduleFragment!!) }
|
||||
|
||||
val psi2IrTranslator = Psi2IrTranslator(configuration.languageVersionSettings)
|
||||
val psi2IrContext = psi2IrTranslator.createGeneratorContext(analysisResult.moduleDescriptor, analysisResult.bindingContext, symbolTable)
|
||||
@@ -59,10 +78,43 @@ fun compile(
|
||||
psi2IrContext.symbolTable,
|
||||
moduleFragment,
|
||||
configuration,
|
||||
irDependencyModules
|
||||
dependencies,
|
||||
moduleType
|
||||
)
|
||||
|
||||
// CoroutineIntrinsicLowering currently have to be applied to dependencies
|
||||
// before running inline pass for current module.
|
||||
// Therefore we run it here before producing ir for current module.
|
||||
// TODO: Find a better way to implement coroutine intrinsics
|
||||
val coroutineIntrinsicLowering = CoroutineIntrinsicLowering(context)
|
||||
moduleFragment.files.forEach { file -> coroutineIntrinsicLowering.lower(file) }
|
||||
|
||||
// TODO: Split compilation into two steps: kt -> ir, ir -> js
|
||||
val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
var moduleFragmentCopy: IrModuleFragment? = null
|
||||
when (moduleType) {
|
||||
ModuleType.MAIN -> {
|
||||
val moduleDependencies: List<CompiledModule> =
|
||||
DFS.topologicalOrder(dependencies, CompiledModule::dependencies)
|
||||
.filter { it.moduleType == ModuleType.SECONDARY }
|
||||
|
||||
val fileDependencies = moduleDependencies.flatMap { it.moduleFragment!!.files }
|
||||
|
||||
moduleFragment.files.addAll(0, fileDependencies)
|
||||
}
|
||||
|
||||
ModuleType.SECONDARY -> {
|
||||
return CompiledModule(moduleName, null, moduleFragment, moduleType, dependencies)
|
||||
}
|
||||
|
||||
ModuleType.TEST_RUNTIME -> {
|
||||
moduleFragmentCopy = moduleFragment.deepCopyWithSymbols()
|
||||
}
|
||||
}
|
||||
|
||||
jsPhases.invokeToplevel(context.phaseConfig, context, moduleFragment)
|
||||
|
||||
return Result(analysisResult.moduleDescriptor, context.jsProgram.toString(), context.moduleFragmentCopy)
|
||||
}
|
||||
val jsProgram = moduleFragment.accept(IrModuleToJsTransformer(context), null)
|
||||
|
||||
return CompiledModule(moduleName, jsProgram.toString(), moduleFragmentCopy, moduleType, dependencies)
|
||||
}
|
||||
|
||||
+25
-13
@@ -5,25 +5,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.addChild
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class MoveBodilessDeclarationsToSeparatePlace() : FileLoweringPass {
|
||||
fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module: IrModuleFragment) {
|
||||
|
||||
constructor(context: JsIrBackendContext) : this()
|
||||
|
||||
private val builtInClasses = listOf(
|
||||
val builtInClasses = listOf(
|
||||
"String",
|
||||
"Nothing",
|
||||
"Array",
|
||||
@@ -44,10 +41,10 @@ class MoveBodilessDeclarationsToSeparatePlace() : FileLoweringPass {
|
||||
"Double"
|
||||
).map { Name.identifier(it) }.toSet()
|
||||
|
||||
private fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||
declaration is IrClass && declaration.name in builtInClasses
|
||||
|
||||
private val packageFragment = IrExternalPackageFragmentImpl(object : IrExternalPackageFragmentSymbol {
|
||||
val packageFragment = IrExternalPackageFragmentImpl(object : IrExternalPackageFragmentSymbol {
|
||||
override val descriptor: PackageFragmentDescriptor
|
||||
get() = error("Operation is unsupported")
|
||||
|
||||
@@ -61,16 +58,31 @@ class MoveBodilessDeclarationsToSeparatePlace() : FileLoweringPass {
|
||||
}
|
||||
}, FqName.ROOT)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
|
||||
fun lowerFile(irFile: IrFile): IrFile? {
|
||||
if (irFile.getJsModule() != null || irFile.getJsQualifier() != null) {
|
||||
context.packageLevelJsModules.add(irFile)
|
||||
return null
|
||||
}
|
||||
|
||||
val it = irFile.declarations.iterator()
|
||||
|
||||
while (it.hasNext()) {
|
||||
val d = it.next()
|
||||
|
||||
if (d.isEffectivelyExternal() || isBuiltInClass(d)) {
|
||||
|
||||
if (d.getJsModule() != null)
|
||||
context.declarationLevelJsModules.add(d)
|
||||
|
||||
it.remove()
|
||||
packageFragment.addChild(d)
|
||||
}
|
||||
}
|
||||
return irFile
|
||||
}
|
||||
}
|
||||
|
||||
module.files.transformFlat { irFile ->
|
||||
listOfNotNull(lowerFile(irFile))
|
||||
}
|
||||
}
|
||||
|
||||
+144
-17
@@ -5,47 +5,174 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ModuleType
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithVisibility
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
|
||||
class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
|
||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
|
||||
val program = JsProgram()
|
||||
val rootContext = JsGenerationContext(JsRootScope(program), backendContext)
|
||||
val moduleName = backendContext.configuration[CommonConfigurationKeys.MODULE_NAME]!!
|
||||
private val moduleKind = backendContext.configuration[JSConfigurationKeys.MODULE_KIND]!!
|
||||
|
||||
private fun generateModuleBody(module: IrModuleFragment, context: JsGenerationContext): List<JsStatement> {
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
|
||||
// TODO: fix it up with new name generator
|
||||
val anyName = rootContext.getNameForSymbol(backendContext.irBuiltIns.anyClass)
|
||||
val throwableName = rootContext.getNameForSymbol(backendContext.irBuiltIns.throwableClass)
|
||||
val anyName = context.getNameForSymbol(backendContext.irBuiltIns.anyClass)
|
||||
val throwableName = context.getNameForSymbol(backendContext.irBuiltIns.throwableClass)
|
||||
|
||||
program.globalBlock.statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT))
|
||||
program.globalBlock.statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR))
|
||||
statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT))
|
||||
statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR))
|
||||
|
||||
val preDeclarationBlock = JsGlobalBlock()
|
||||
val postDeclarationBlock = JsGlobalBlock()
|
||||
val preDeclarationBlock = JsBlock()
|
||||
val postDeclarationBlock = JsBlock()
|
||||
|
||||
program.globalBlock.statements += preDeclarationBlock
|
||||
statements += preDeclarationBlock
|
||||
|
||||
declaration.files.forEach {
|
||||
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
|
||||
module.files.forEach {
|
||||
statements.add(it.accept(IrFileToJsTransformer(), context))
|
||||
}
|
||||
|
||||
// sort member forwarding code
|
||||
processClassModels(rootContext.staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
processClassModels(context.staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||
|
||||
program.globalBlock.statements += postDeclarationBlock
|
||||
program.globalBlock.statements += rootContext.staticContext.initializerBlock
|
||||
statements += postDeclarationBlock
|
||||
statements += context.staticContext.initializerBlock
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun findModuleExports(module: IrModuleFragment, context: JsGenerationContext): Map<JsName, JsName> {
|
||||
val publicDeclarations = module.files
|
||||
.flatMap { it.declarations }
|
||||
.filterIsInstance<IrDeclarationWithVisibility>()
|
||||
.filter { it.visibility == Visibilities.PUBLIC }
|
||||
.filter { !it.isEffectivelyExternal() }
|
||||
.filterIsInstance<IrSymbolOwner>()
|
||||
|
||||
val publicDeclarationNames = publicDeclarations.map {
|
||||
context.getNameForSymbol(it.symbol)
|
||||
}
|
||||
|
||||
return publicDeclarationNames.associate { it to it }
|
||||
}
|
||||
|
||||
private fun generateModuleInGlobalScope(module: IrModuleFragment): JsProgram {
|
||||
val program = JsProgram()
|
||||
val rootContext = JsGenerationContext(JsRootScope(program), backendContext)
|
||||
|
||||
val moduleBody = generateModuleBody(module, rootContext)
|
||||
program.globalBlock.statements += moduleBody
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
private fun generateModule(module: IrModuleFragment): JsProgram {
|
||||
val program = JsProgram()
|
||||
val rootContext = JsGenerationContext(JsRootScope(program), backendContext)
|
||||
|
||||
val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function")
|
||||
val internalModuleName = rootFunction.scope.declareName("_")
|
||||
|
||||
rootFunction.parameters += JsParameter(internalModuleName)
|
||||
|
||||
val declarationLevelJsModules =
|
||||
backendContext.declarationLevelJsModules.map { externalDeclaration ->
|
||||
val jsModule = externalDeclaration.getJsModule()!!
|
||||
val name = rootContext.getNameForDeclaration(externalDeclaration)
|
||||
JsImportedModule(jsModule, name, name.makeRef())
|
||||
}
|
||||
|
||||
val packageLevelJsModules = mutableListOf<JsImportedModule>()
|
||||
val importStatements = mutableListOf<JsStatement>()
|
||||
|
||||
for (file in backendContext.packageLevelJsModules) {
|
||||
val jsModule = file.getJsModule()
|
||||
val jsQualifier = file.getJsQualifier()
|
||||
|
||||
assert(jsModule != null || jsQualifier != null)
|
||||
|
||||
val qualifiedReference: JsNameRef
|
||||
|
||||
if (jsModule != null) {
|
||||
val internalName = rootFunction.scope.declareFreshName("moduleImport")
|
||||
packageLevelJsModules += JsImportedModule(jsModule, internalName, null)
|
||||
|
||||
qualifiedReference =
|
||||
if (jsQualifier == null)
|
||||
internalName.makeRef()
|
||||
else
|
||||
JsNameRef(jsQualifier, internalName.makeRef())
|
||||
} else {
|
||||
qualifiedReference = JsNameRef(jsQualifier!!)
|
||||
}
|
||||
|
||||
file.declarations.forEach { declaration ->
|
||||
val declName = rootContext.getNameForDeclaration(declaration)
|
||||
importStatements.add(
|
||||
JsExpressionStatement(
|
||||
jsAssignment(
|
||||
declName.makeRef(),
|
||||
JsNameRef(declName, qualifiedReference)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val importedJsModules = declarationLevelJsModules + packageLevelJsModules
|
||||
rootFunction.parameters += importedJsModules.map { JsParameter(it.internalName) }
|
||||
|
||||
rootFunction.body.statements += importStatements
|
||||
|
||||
val moduleBody = generateModuleBody(module, rootContext)
|
||||
|
||||
val moduleExports = findModuleExports(module, rootContext)
|
||||
|
||||
val exportStatements = moduleExports.map { (from, to) ->
|
||||
JsExpressionStatement(jsAssignment(JsNameRef(from, internalModuleName.makeRef()), to.makeRef()))
|
||||
}
|
||||
|
||||
rootFunction.body.statements += moduleBody
|
||||
|
||||
rootFunction.body.statements += exportStatements
|
||||
|
||||
rootFunction.body.statements += JsReturn(internalModuleName.makeRef())
|
||||
|
||||
program.globalBlock.statements += ModuleWrapperTranslation.wrap(
|
||||
moduleName,
|
||||
rootFunction,
|
||||
importedJsModules,
|
||||
program,
|
||||
kind = moduleKind
|
||||
)
|
||||
|
||||
return program
|
||||
}
|
||||
|
||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode =
|
||||
when (backendContext.moduleType) {
|
||||
ModuleType.MAIN -> generateModule(declaration)
|
||||
ModuleType.SECONDARY -> JsEmpty
|
||||
ModuleType.TEST_RUNTIME -> generateModuleInGlobalScope(declaration)
|
||||
}
|
||||
|
||||
|
||||
private fun processClassModels(
|
||||
classModelMap: Map<JsName, JsClassModel>,
|
||||
preDeclarationBlock: JsGlobalBlock,
|
||||
postDeclarationBlock: JsGlobalBlock
|
||||
preDeclarationBlock: JsBlock,
|
||||
postDeclarationBlock: JsBlock
|
||||
) {
|
||||
val declarationHandler = object : DFS.AbstractNodeHandler<JsName, Unit>() {
|
||||
override fun result() {}
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
object ModuleWrapperTranslation {
|
||||
object Namer {
|
||||
fun requiresEscaping(name: String) = false
|
||||
}
|
||||
|
||||
fun wrap(
|
||||
moduleId: String, function: JsExpression, importedModules: List<JsImportedModule>,
|
||||
program: JsProgram, kind: ModuleKind
|
||||
): List<JsStatement> {
|
||||
return when (kind) {
|
||||
ModuleKind.AMD -> wrapAmd(function, importedModules, program)
|
||||
ModuleKind.COMMON_JS -> wrapCommonJs(function, importedModules, program)
|
||||
ModuleKind.UMD -> wrapUmd(moduleId, function, importedModules, program)
|
||||
ModuleKind.PLAIN -> wrapPlain(moduleId, function, importedModules, program)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapUmd(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<JsImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val defineName = scope.declareName("define")
|
||||
val exportsName = scope.declareName("exports")
|
||||
|
||||
val adapterBody = JsBlock()
|
||||
val adapter = JsFunction(program.scope, adapterBody, "Adapter")
|
||||
val rootName = adapter.scope.declareName("root")
|
||||
val factoryName = adapter.scope.declareName("factory")
|
||||
adapter.parameters += JsParameter(rootName)
|
||||
adapter.parameters += JsParameter(factoryName)
|
||||
|
||||
val amdTest = JsAstUtils.and(JsAstUtils.typeOfIs(defineName.makeRef(), JsStringLiteral("function")),
|
||||
JsNameRef("amd", defineName.makeRef()))
|
||||
val commonJsTest = JsAstUtils.typeOfIs(exportsName.makeRef(), JsStringLiteral("object"))
|
||||
|
||||
val amdBody = JsBlock(wrapAmd(factoryName.makeRef(), importedModules, program))
|
||||
val commonJsBody = JsBlock(wrapCommonJs(factoryName.makeRef(), importedModules, program))
|
||||
val plainInvocation = makePlainInvocation(moduleId, factoryName.makeRef(), importedModules, program)
|
||||
|
||||
val lhs: JsExpression = if (Namer.requiresEscaping(moduleId)) {
|
||||
JsArrayAccess(rootName.makeRef(), JsStringLiteral(moduleId))
|
||||
}
|
||||
else {
|
||||
JsNameRef(scope.declareName(moduleId), rootName.makeRef())
|
||||
}
|
||||
|
||||
val plainBlock = JsBlock()
|
||||
for (importedModule in importedModules) {
|
||||
plainBlock.statements += addModuleValidation(moduleId, program, importedModule)
|
||||
}
|
||||
plainBlock.statements += JsAstUtils.assignment(lhs, plainInvocation).makeStmt()
|
||||
|
||||
val selector = JsAstUtils.newJsIf(amdTest, amdBody, JsAstUtils.newJsIf(commonJsTest, commonJsBody, plainBlock))
|
||||
adapterBody.statements += selector
|
||||
|
||||
return listOf(JsInvocation(adapter, JsThisRef(), function).makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapAmd(
|
||||
function: JsExpression,
|
||||
importedModules: List<JsImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val defineName = scope.declareName("define")
|
||||
val invocationArgs = listOf(
|
||||
JsArrayLiteral(listOf(JsStringLiteral("exports")) + importedModules.map { JsStringLiteral(it.externalName) }),
|
||||
function
|
||||
)
|
||||
|
||||
val invocation = JsInvocation(defineName.makeRef(), invocationArgs)
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapCommonJs(
|
||||
function: JsExpression,
|
||||
importedModules: List<JsImportedModule>,
|
||||
program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val scope = program.scope
|
||||
val moduleName = scope.declareName("module")
|
||||
val requireName = scope.declareName("require")
|
||||
|
||||
val invocationArgs = importedModules.map { JsInvocation(requireName.makeRef(), JsStringLiteral(it.externalName)) }
|
||||
val invocation = JsInvocation(function, listOf(JsNameRef("exports", moduleName.makeRef())) + invocationArgs)
|
||||
return listOf(invocation.makeStmt())
|
||||
}
|
||||
|
||||
private fun wrapPlain(
|
||||
moduleId: String, function: JsExpression,
|
||||
importedModules: List<JsImportedModule>, program: JsProgram
|
||||
): List<JsStatement> {
|
||||
val invocation = makePlainInvocation(moduleId, function, importedModules, program)
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
|
||||
for (importedModule in importedModules) {
|
||||
statements += addModuleValidation(moduleId, program, importedModule)
|
||||
}
|
||||
|
||||
statements += if (Namer.requiresEscaping(moduleId)) {
|
||||
JsAstUtils.assignment(makePlainModuleRef(moduleId, program), invocation).makeStmt()
|
||||
}
|
||||
else {
|
||||
JsAstUtils.newVar(program.rootScope.declareName(moduleId), invocation)
|
||||
}
|
||||
|
||||
return statements
|
||||
}
|
||||
|
||||
private fun addModuleValidation(
|
||||
currentModuleId: String,
|
||||
program: JsProgram,
|
||||
module: JsImportedModule
|
||||
): JsStatement {
|
||||
val moduleRef = makePlainModuleRef(module, program)
|
||||
val moduleExistsCond = JsAstUtils.typeOfIs(moduleRef, JsStringLiteral("undefined"))
|
||||
val moduleNotFoundMessage = JsStringLiteral(
|
||||
"Error loading module '" + currentModuleId + "'. Its dependency '" + module.externalName + "' was not found. " +
|
||||
"Please, check whether '" + module.externalName + "' is loaded prior to '" + currentModuleId + "'.")
|
||||
val moduleNotFoundThrow = JsThrow(JsNew(JsNameRef("Error"), listOf<JsExpression>(moduleNotFoundMessage)))
|
||||
return JsIf(moduleExistsCond, JsBlock(moduleNotFoundThrow))
|
||||
}
|
||||
|
||||
private fun makePlainInvocation(
|
||||
moduleId: String,
|
||||
function: JsExpression,
|
||||
importedModules: List<JsImportedModule>,
|
||||
program: JsProgram
|
||||
): JsInvocation {
|
||||
val invocationArgs = importedModules.map { makePlainModuleRef(it, program) }
|
||||
val moduleRef = makePlainModuleRef(moduleId, program)
|
||||
val testModuleDefined = JsAstUtils.typeOfIs(moduleRef, JsStringLiteral("undefined"))
|
||||
val selfArg = JsConditional(testModuleDefined, JsObjectLiteral(false), moduleRef.deepCopy())
|
||||
|
||||
return JsInvocation(function, listOf(selfArg) + invocationArgs)
|
||||
}
|
||||
|
||||
private fun makePlainModuleRef(module: JsImportedModule, program: JsProgram): JsExpression {
|
||||
return module.plainReference ?: makePlainModuleRef(module.externalName, program)
|
||||
}
|
||||
|
||||
private fun makePlainModuleRef(moduleId: String, program: JsProgram): JsExpression {
|
||||
// TODO: we could use `this.moduleName` syntax. However, this does not work for `kotlin` module in Rhino, since
|
||||
// we run kotlin.js in a parent scope. Consider better solution
|
||||
return if (Namer.requiresEscaping(moduleId)) {
|
||||
JsArrayAccess(JsThisRef(), JsStringLiteral(moduleId))
|
||||
}
|
||||
else {
|
||||
program.scope.declareName(moduleId).makeRef()
|
||||
}
|
||||
}
|
||||
}
|
||||
+104
@@ -87,3 +87,107 @@ fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpressi
|
||||
val objectDefineProperty = JsNameRef("defineProperty", Namer.JS_OBJECT)
|
||||
return JsInvocation(objectDefineProperty, receiver, JsStringLiteral(name), value())
|
||||
}
|
||||
|
||||
// Partially copied from org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
object JsAstUtils {
|
||||
private fun deBlockIfPossible(statement: JsStatement): JsStatement {
|
||||
return if (statement is JsBlock && statement.statements.size == 1) {
|
||||
statement.statements[0]
|
||||
} else {
|
||||
statement
|
||||
}
|
||||
}
|
||||
|
||||
fun newJsIf(
|
||||
ifExpression: JsExpression,
|
||||
thenStatement: JsStatement,
|
||||
elseStatement: JsStatement? = null
|
||||
): JsIf {
|
||||
var elseStatement = elseStatement
|
||||
elseStatement = if (elseStatement != null) deBlockIfPossible(elseStatement) else null
|
||||
return JsIf(ifExpression, deBlockIfPossible(thenStatement), elseStatement)
|
||||
}
|
||||
|
||||
fun and(op1: JsExpression, op2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.AND, op1, op2)
|
||||
}
|
||||
|
||||
fun or(op1: JsExpression, op2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.OR, op1, op2)
|
||||
}
|
||||
|
||||
fun equality(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.REF_EQ, arg1, arg2)
|
||||
}
|
||||
|
||||
fun inequality(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.REF_NEQ, arg1, arg2)
|
||||
}
|
||||
|
||||
fun lessThanEq(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.LTE, arg1, arg2)
|
||||
}
|
||||
|
||||
fun lessThan(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.LT, arg1, arg2)
|
||||
}
|
||||
|
||||
fun greaterThan(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.GT, arg1, arg2)
|
||||
}
|
||||
|
||||
fun greaterThanEq(arg1: JsExpression, arg2: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.GTE, arg1, arg2)
|
||||
}
|
||||
|
||||
fun assignment(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.ASG, left, right)
|
||||
}
|
||||
|
||||
fun assignmentToThisField(fieldName: String, right: JsExpression): JsStatement {
|
||||
return assignment(JsNameRef(fieldName, JsThisRef()), right).source(right.source).makeStmt()
|
||||
}
|
||||
|
||||
fun decomposeAssignment(expr: JsExpression): Pair<JsExpression, JsExpression>? {
|
||||
if (expr !is JsBinaryOperation) return null
|
||||
|
||||
return if (expr.operator != JsBinaryOperator.ASG) null else Pair(expr.arg1, expr.arg2)
|
||||
|
||||
}
|
||||
|
||||
fun sum(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.ADD, left, right)
|
||||
}
|
||||
|
||||
fun addAssign(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.ASG_ADD, left, right)
|
||||
}
|
||||
|
||||
fun subtract(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.SUB, left, right)
|
||||
}
|
||||
|
||||
fun mul(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.MUL, left, right)
|
||||
}
|
||||
|
||||
fun div(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.DIV, left, right)
|
||||
}
|
||||
|
||||
fun mod(left: JsExpression, right: JsExpression): JsBinaryOperation {
|
||||
return JsBinaryOperation(JsBinaryOperator.MOD, left, right)
|
||||
}
|
||||
|
||||
fun not(expression: JsExpression): JsPrefixOperation {
|
||||
return JsPrefixOperation(JsUnaryOperator.NOT, expression)
|
||||
}
|
||||
|
||||
fun typeOfIs(expression: JsExpression, string: JsStringLiteral): JsBinaryOperation {
|
||||
return equality(JsPrefixOperation(JsUnaryOperator.TYPEOF, expression), string)
|
||||
}
|
||||
|
||||
fun newVar(name: JsName, expr: JsExpression?): JsVars {
|
||||
return JsVars(JsVars.JsVar(name, expr))
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
object JsAnnotations {
|
||||
val jsModuleFqn = FqName("kotlin.js.JsModule")
|
||||
val jsNonModuleFqn = FqName("kotlin.js.JsNonModule")
|
||||
val jsNameFqn = FqName("kotlin.js.JsName")
|
||||
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
||||
}
|
||||
|
||||
private fun IrCall.getSingleConstStringArgument() =
|
||||
(getValueArgument(0) as IrConst<String>).value
|
||||
|
||||
fun IrAnnotationContainer.getJsModule(): String? =
|
||||
getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument()
|
||||
|
||||
fun IrAnnotationContainer.isJsNonModule(): Boolean =
|
||||
hasAnnotation(JsAnnotations.jsNonModuleFqn)
|
||||
|
||||
fun IrAnnotationContainer.getJsQualifier(): String? =
|
||||
getAnnotation(JsAnnotations.jsQualifierFqn)?.getSingleConstStringArgument()
|
||||
|
||||
fun IrAnnotationContainer.getJsName(): String? =
|
||||
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
||||
+7
-2
@@ -6,8 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
@@ -44,6 +43,12 @@ class JsGenerationContext {
|
||||
this.currentFunction = func
|
||||
}
|
||||
|
||||
fun getNameForDeclaration(declaration: IrDeclaration): JsName = when (declaration) {
|
||||
is IrSymbolOwner -> getNameForSymbol(declaration.symbol)
|
||||
is IrProperty -> currentScope.declareName(declaration.name.identifier)
|
||||
else -> error("Unsupported")
|
||||
}
|
||||
|
||||
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, this)
|
||||
fun getNameForType(type: IrType): JsName = staticContext.getNameForType(type, this)
|
||||
fun getNameForLoop(loop: IrLoop): JsName? = staticContext.getNameForLoop(loop, this)
|
||||
|
||||
+5
-2
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isStatic
|
||||
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -13,7 +12,6 @@ import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.isDynamic
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.isInlined
|
||||
@@ -102,6 +100,11 @@ class SimpleNameGenerator : NameGenerator {
|
||||
return@getOrPut nameDeclarator(declaration.descriptor.name.asString())
|
||||
}
|
||||
|
||||
val jsName = declaration.getJsName()
|
||||
if (jsName != null) {
|
||||
return@getOrPut context.currentScope.declareName(jsName)
|
||||
}
|
||||
|
||||
if (declaration.isEffectivelyExternal()) {
|
||||
// TODO: descriptors are still used here due to the corresponding declaration doesn't have enough information yet
|
||||
val descriptorForName = when (descriptor) {
|
||||
|
||||
@@ -379,6 +379,11 @@ tailrec fun IrElement.getPackageFragment(): IrPackageFragment? {
|
||||
}
|
||||
}
|
||||
|
||||
fun IrAnnotationContainer.getAnnotation(name: FqName) =
|
||||
annotations.find {
|
||||
it.symbol.owner.parentAsClass.descriptor.fqNameSafe == name
|
||||
}
|
||||
|
||||
fun IrAnnotationContainer.hasAnnotation(name: FqName) =
|
||||
annotations.any {
|
||||
it.symbol.owner.parentAsClass.descriptor.fqNameSafe == name
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
class IntRange {
|
||||
operator fun contains(a: Int) = (1..2).contains(a)
|
||||
}
|
||||
|
||||
@@ -126,6 +126,9 @@ abstract class BasicBoxTest(
|
||||
val testPackage = testFactory.testPackage
|
||||
val testFunction = TEST_FUNCTION
|
||||
|
||||
val mainModuleName = if (TEST_MODULE in modules) TEST_MODULE else DEFAULT_MODULE
|
||||
val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"")
|
||||
|
||||
val generatedJsFiles = orderedModules.asReversed().mapNotNull { module ->
|
||||
val dependencies = module.dependencies.map { modules[it]?.outputFileName(outputDir) + ".meta.js" }
|
||||
val friends = module.friends.map { modules[it]?.outputFileName(outputDir) + ".meta.js" }
|
||||
@@ -135,15 +138,12 @@ abstract class BasicBoxTest(
|
||||
file.parent, module, outputFileName, dependencies, friends, modules.size > 1,
|
||||
!SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(),
|
||||
outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction,
|
||||
runtimeType
|
||||
runtimeType, isMainModule = (mainModuleName == module.name)
|
||||
)
|
||||
|
||||
if (!module.name.endsWith(OLD_MODULE_SUFFIX)) Pair(outputFileName, module) else null
|
||||
}
|
||||
|
||||
val mainModuleName = if (TEST_MODULE in modules) TEST_MODULE else DEFAULT_MODULE
|
||||
val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"")
|
||||
|
||||
val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension(COMMON_FILES_DIR_PATH, JavaScript.EXTENSION)
|
||||
val localCommonFile = file.parent + "/" + COMMON_FILES_NAME + JavaScript.DOT_EXTENSION
|
||||
val localCommonFiles = if (File(localCommonFile).exists()) listOf(localCommonFile) else emptyList()
|
||||
@@ -315,7 +315,8 @@ abstract class BasicBoxTest(
|
||||
mainCallParameters: MainCallParameters,
|
||||
testPackage: String?,
|
||||
testFunction: String,
|
||||
runtime: JsIrTestRuntime
|
||||
runtime: JsIrTestRuntime,
|
||||
isMainModule: Boolean
|
||||
) {
|
||||
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
|
||||
val testFiles = kotlinFiles.map { it.fileName }
|
||||
@@ -337,7 +338,7 @@ abstract class BasicBoxTest(
|
||||
val incrementalData = IncrementalData()
|
||||
translateFiles(
|
||||
psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime, isMainModule
|
||||
)
|
||||
|
||||
if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) {
|
||||
@@ -385,7 +386,7 @@ abstract class BasicBoxTest(
|
||||
|
||||
translateFiles(
|
||||
translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime
|
||||
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime, false
|
||||
)
|
||||
|
||||
val originalOutput = FileUtil.loadFile(outputFile)
|
||||
@@ -450,7 +451,8 @@ abstract class BasicBoxTest(
|
||||
remap: Boolean,
|
||||
testPackage: String?,
|
||||
testFunction: String,
|
||||
runtime: JsIrTestRuntime
|
||||
runtime: JsIrTestRuntime,
|
||||
isMainModule: Boolean
|
||||
) {
|
||||
val translator = K2JSTranslator(config)
|
||||
val translationResult = translator.translateUnits(ExceptionThrowingReporter, units, mainCallParameters)
|
||||
@@ -467,18 +469,9 @@ abstract class BasicBoxTest(
|
||||
val outputDir = outputFile.parentFile ?: error("Parent file for output file should not be null, outputFilePath: " + outputFile.path)
|
||||
outputFiles.writeAllTo(outputDir)
|
||||
|
||||
if (config.moduleKind == ModuleKind.COMMON_JS) {
|
||||
if (config.moduleKind != ModuleKind.PLAIN) {
|
||||
val content = FileUtil.loadFile(outputFile, true)
|
||||
val wrappedContent = "$KOTLIN_TEST_INTERNAL.beginModule();\n" +
|
||||
"$content\n" +
|
||||
"$KOTLIN_TEST_INTERNAL.endModule(\"${StringUtil.escapeStringCharacters(config.moduleId)}\");"
|
||||
FileUtil.writeToFile(outputFile, wrappedContent)
|
||||
}
|
||||
else if (config.moduleKind == ModuleKind.AMD || config.moduleKind == ModuleKind.UMD) {
|
||||
val content = FileUtil.loadFile(outputFile, true)
|
||||
val wrappedContent = "if (typeof $KOTLIN_TEST_INTERNAL !== \"undefined\") { " +
|
||||
"$KOTLIN_TEST_INTERNAL.setModuleId(\"${StringUtil.escapeStringCharacters(config.moduleId)}\"); }\n" +
|
||||
"$content\n"
|
||||
val wrappedContent = wrapWithModuleEmulationMarkers(content, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
FileUtil.writeToFile(outputFile, wrappedContent)
|
||||
}
|
||||
|
||||
@@ -496,6 +489,28 @@ abstract class BasicBoxTest(
|
||||
checkSourceMap(outputFile, translationResult.program, remap)
|
||||
}
|
||||
|
||||
protected fun wrapWithModuleEmulationMarkers(
|
||||
content: String,
|
||||
moduleKind: ModuleKind,
|
||||
moduleId: String
|
||||
): String {
|
||||
val escapedModuleId = StringUtil.escapeStringCharacters(moduleId)
|
||||
|
||||
return when (moduleKind) {
|
||||
|
||||
ModuleKind.COMMON_JS -> "$KOTLIN_TEST_INTERNAL.beginModule();\n" +
|
||||
"$content\n" +
|
||||
"$KOTLIN_TEST_INTERNAL.endModule(\"$escapedModuleId\");"
|
||||
|
||||
ModuleKind.AMD, ModuleKind.UMD ->
|
||||
"if (typeof $KOTLIN_TEST_INTERNAL !== \"undefined\") { " +
|
||||
"$KOTLIN_TEST_INTERNAL.setModuleId(\"$escapedModuleId\"); }\n" +
|
||||
"$content\n"
|
||||
|
||||
ModuleKind.PLAIN -> content
|
||||
}
|
||||
}
|
||||
|
||||
private fun processJsProgram(program: JsProgram, psiFiles: List<KtFile>) {
|
||||
psiFiles.asSequence()
|
||||
.map { it.text }
|
||||
|
||||
@@ -5,20 +5,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.test
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.backend.js.Result
|
||||
import org.jetbrains.kotlin.ir.backend.js.ModuleType
|
||||
import org.jetbrains.kotlin.ir.backend.js.CompiledModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.compile
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.JsConfig
|
||||
import org.jetbrains.kotlin.js.facade.MainCallParameters
|
||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import java.io.File
|
||||
|
||||
private var runtimeResults = mutableMapOf<JsIrTestRuntime, Result>()
|
||||
private var runtimeResults = mutableMapOf<JsIrTestRuntime, CompiledModule>()
|
||||
|
||||
abstract class BasicIrBoxTest(
|
||||
pathToTestDir: String,
|
||||
@@ -41,7 +42,7 @@ abstract class BasicIrBoxTest(
|
||||
// TODO Design incremental compilation for IR and add test support
|
||||
override val incrementalCompilationChecksEnabled = false
|
||||
|
||||
private val compilationCache = mutableMapOf<String, Result>()
|
||||
private val compilationCache = mutableMapOf<String, CompiledModule>()
|
||||
|
||||
override fun doTest(filePath: String, expectedResult: String, mainCallParameters: MainCallParameters, coroutinesPackage: String) {
|
||||
compilationCache.clear()
|
||||
@@ -59,7 +60,8 @@ abstract class BasicIrBoxTest(
|
||||
remap: Boolean,
|
||||
testPackage: String?,
|
||||
testFunction: String,
|
||||
runtime: JsIrTestRuntime
|
||||
runtime: JsIrTestRuntime,
|
||||
isMainModule: Boolean
|
||||
) {
|
||||
val filesToCompile = units
|
||||
.map { (it as TranslationUnit.SourceFile).file }
|
||||
@@ -94,14 +96,15 @@ abstract class BasicIrBoxTest(
|
||||
val runtimeFile = File(runtime.path)
|
||||
val runtimeResult = runtimeResults.getOrPut(runtime) {
|
||||
runtimeConfiguration.put(CommonConfigurationKeys.MODULE_NAME, "JS_IR_RUNTIME")
|
||||
val result = compile(config.project, runtime.sources.map(::createPsiFile), runtimeConfiguration)
|
||||
runtimeFile.write(result.generatedCode)
|
||||
val result = compile(config.project, runtime.sources.map(::createPsiFile), runtimeConfiguration, moduleType = ModuleType.TEST_RUNTIME)
|
||||
runtimeFile.write(result.generatedCode!!)
|
||||
result
|
||||
}
|
||||
|
||||
val dependencyNames = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name }
|
||||
val dependencies = listOf(runtimeResult.moduleDescriptor) + dependencyNames.mapNotNull { compilationCache[it]?.moduleDescriptor }
|
||||
val irDependencies = listOf(runtimeResult.moduleFragment) + compilationCache.values.map { it.moduleFragment }
|
||||
val dependencies = listOf(runtimeResult) + dependencyNames.mapNotNull {
|
||||
compilationCache[it]
|
||||
}
|
||||
|
||||
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE, setOf("UnitMaterializationLowering"))
|
||||
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE_BEFORE, setOf("ReturnableBlockLowering"))
|
||||
@@ -112,18 +115,21 @@ abstract class BasicIrBoxTest(
|
||||
config.project,
|
||||
filesToCompile,
|
||||
config.configuration,
|
||||
FqName((testPackage?.let { "$it." } ?: "") + testFunction),
|
||||
listOf(FqName((testPackage?.let { "$it." } ?: "") + testFunction)),
|
||||
dependencies,
|
||||
irDependencies,
|
||||
runtimeResult.moduleDescriptor as ModuleDescriptorImpl
|
||||
runtimeResult,
|
||||
moduleType = if (isMainModule) ModuleType.MAIN else ModuleType.SECONDARY
|
||||
)
|
||||
|
||||
compilationCache[outputFile.name.replace(".js", ".meta.js")] = result
|
||||
|
||||
// Prefix to help node.js runner find runtime
|
||||
val runtimePrefix = "// RUNTIME: [\"${runtimeFile.path}\"]\n"
|
||||
|
||||
outputFile.write(runtimePrefix + result.generatedCode)
|
||||
val generatedCode = result.generatedCode
|
||||
if (generatedCode != null) {
|
||||
// Prefix to help node.js runner find runtime
|
||||
val runtimePrefix = "// RUNTIME: [\"${runtimeFile.path}\"]\n"
|
||||
val wrappedCode = wrapWithModuleEmulationMarkers(generatedCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||
outputFile.write(runtimePrefix + wrappedCode)
|
||||
}
|
||||
}
|
||||
|
||||
override fun runGeneratedCode(
|
||||
@@ -137,7 +143,7 @@ abstract class BasicIrBoxTest(
|
||||
) {
|
||||
// TODO: should we do anything special for module systems?
|
||||
// TODO: return list of js from translateFiles and provide then to this function with other js files
|
||||
nashornIrJsTestCheckers[runtime]!!.check(jsFiles, null, null, testFunction, expectedResult, false)
|
||||
nashornIrJsTestCheckers[runtime]!!.check(jsFiles, testModuleName, null, testFunction, expectedResult, withModuleSystem)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1316
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.js
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1316
|
||||
// MODULE: lib-1
|
||||
// FILE: lib-1.js
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.js
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
|
||||
// MODULE: lib
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE_KIND: AMD
|
||||
package foo
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE_KIND: AMD
|
||||
package foo
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
// MODULE_KIND: UMD
|
||||
package foo
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1288
|
||||
// MODULE_KIND: UMD
|
||||
// NO_JS_MODULE_SYSTEM
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
// MODULE_KIND: AMD
|
||||
// FILE: lib.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
// FILE: a.kt
|
||||
// MODULE_KIND: UMD
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE_KIND: AMD
|
||||
package foo
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
// MODULE_KIND: COMMON_JS
|
||||
// FUNCTION_CALLED_TIMES: require count=2
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1282
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1282
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1289
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: module-1
|
||||
// FILE: bar.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: module1
|
||||
// FILE: bar.kt
|
||||
|
||||
js/js.translator/testData/box/multiModuleWrappers/common_js/inlineFromModuleWithNonIdentifierName.kt
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
// MODULE: #my-libr@ry
|
||||
// FILE: bar.kt
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1285
|
||||
// MODULE: module-1
|
||||
// FILE: bar.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1285
|
||||
// MODULE: module1
|
||||
// FILE: bar.kt
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
// MODULE: module-1
|
||||
// FILE: bar.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1286
|
||||
// MODULE: module1
|
||||
// FILE: bar.kt
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1284
|
||||
package foo
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1288
|
||||
package foo
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
|
||||
var kotlin;
|
||||
var emulatedModules = { kotlin: kotlin };
|
||||
var module = { exports: {} };
|
||||
var currentModuleId;
|
||||
|
||||
+2
-1
@@ -59,4 +59,5 @@ allFiles.forEach(function(path) {
|
||||
})
|
||||
});
|
||||
|
||||
console.log(vm.runInContext("box()", sandbox));
|
||||
// TODO: Support multimodule tests
|
||||
console.log(vm.runInContext("JS_TESTS.box()", sandbox));
|
||||
|
||||
Reference in New Issue
Block a user