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