diff --git a/.gitignore b/.gitignore index 5e893f3b9fc..076d0870fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ workspace.xml /jps-plugin/testData/kannotator /js/js.translator/testData/out/ /js/js.translator/testData/out-min/ +/js/js.translator/testData/out-pir/ .gradle/ build/ !**/src/**/build diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt index a2096268080..f57da065a4d 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt @@ -125,6 +125,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xir-dce", description = "Perform experimental dead code elimination") var irDce: Boolean by FreezableVar(false) + @Argument(value = "-Xir-dce-driven", description = "Perform a more experimental faster dead code elimination") + var irDceDriven: Boolean by FreezableVar(false) + @Argument(value = "-Xir-only", description = "Disables pre-IR backend") var irOnly: Boolean by FreezableVar(false) diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index 45033d0f85a..05f5e9a7c7b 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -228,13 +228,14 @@ class K2JsIrCompiler : CLICompiler() { friendDependencies = friendDependencies, mainArguments = mainCallArguments, generateFullJs = !arguments.irDce, - generateDceJs = arguments.irDce + generateDceJs = arguments.irDce, + dceDriven = arguments.irDceDriven ) } catch (e: JsIrCompilationError) { return COMPILATION_ERROR } - val jsCode = if (arguments.irDce) compiledModule.dceJsCode!! else compiledModule.jsCode!! + val jsCode = if (arguments.irDce && !arguments.irDceDriven) compiledModule.dceJsCode!! else compiledModule.jsCode!! outputFile.writeText(jsCode) if (arguments.generateDts) { val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!! diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt index 7f9f8433f8c..c86a051beeb 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.backend.common.ir.DeclarationFactory import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol @@ -35,4 +36,5 @@ interface BackendContext { val internalPackageFqn: FqName val transformedFunction: MutableMap val lateinitNullableFields: MutableMap + val extractedLocalClasses: MutableSet } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt index 5dd5bab6b20..a2da4e19cad 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt @@ -37,7 +37,7 @@ open class DefaultMapping : Mapping { override val lateInitFieldToNullableField: Mapping.Delegate = newMapping() override val inlineClassMemberToStatic: Mapping.Delegate = newMapping() - protected fun newMapping() = object : Mapping.Delegate() { + protected open fun newMapping() = object : Mapping.Delegate() { private val map: MutableMap = mutableMapOf() override operator fun get(key: K): V? { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt index 82a57568a33..d44fe7c197d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt @@ -44,6 +44,7 @@ open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPa for ((local, newContainer) in extractedLocalClasses) { newContainer.addChild(local) + context.extractedLocalClasses += local } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt index 1a427cbd3e4..4a2ec20fbd8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Dce.kt @@ -6,14 +6,11 @@ package org.jetbrains.kotlin.ir.backend.js import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName -import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classifierOrFail @@ -28,11 +25,13 @@ fun eliminateDeadDeclarations( mainFunction: IrSimpleFunction? ) { - val allRoots = buildRoots(module, context, mainFunction) + val allRoots = stageController.withInitialIr { buildRoots(module, context, mainFunction) } val usefulDeclarations = usefulDeclarations(allRoots, context) - removeUselessDeclarations(module, usefulDeclarations) + stageController.unrestrictDeclarationListsAccess { + removeUselessDeclarations(module, usefulDeclarations) + } } private fun IrField.isConstant(): Boolean { @@ -42,13 +41,14 @@ private fun IrField.isConstant(): Boolean { private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable { val rootDeclarations = (module.files + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file -> - file.declarations.filter { - it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true - || it.isExported(context) - || it.isEffectivelyExternal() - || it is IrField && it.correspondingPropertySymbol?.owner?.isExported(context) == true - || it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.isExported(context) == true - }.filter { !(it is IrField && it.isConstant() && !it.isExported(context)) } + file.declarations.flatMap { if (it is IrProperty) listOfNotNull(it.backingField, it.getter, it.setter) else listOf(it) } + .filter { + it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true + || it.isExported(context) + || it.isEffectivelyExternal() + || it is IrField && it.correspondingPropertySymbol?.owner?.isExported(context) == true + || it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.isExported(context) == true + }.filter { !(it is IrField && it.isConstant() && !it.isExported(context)) } } if (context.hasTests) rootDeclarations += context.testContainer @@ -121,14 +121,16 @@ fun usefulDeclarations(roots: Iterable, context: JsIrBackendConte val constructedClasses = hashSetOf() fun IrDeclaration.enqueue() { - if (this !in result) { + if ((this !is IrProperty || this.isExternal) && this !in result) { result.add(this) queue.addLast(this) } } // Add roots, including nested declarations - roots.withNested().forEach { it.enqueue() } + stageController.withInitialIr { + roots.withNested().forEach { it.enqueue() } + } val toStringMethod = context.irBuiltIns.anyClass.owner.declarations.filterIsInstance().single { it.name.asString() == "toString" } @@ -141,6 +143,8 @@ fun usefulDeclarations(roots: Iterable, context: JsIrBackendConte while (queue.isNotEmpty()) { val declaration = queue.pollFirst() + stageController.lazyLower(declaration) + if (declaration is IrClass) { declaration.superTypes.forEach { (it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue() @@ -184,6 +188,8 @@ fun usefulDeclarations(roots: Iterable, context: JsIrBackendConte else -> null } + (body as? IrBody)?.let { stageController.lazyLower(it) } + body?.acceptVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) @@ -258,7 +264,8 @@ fun usefulDeclarations(roots: Iterable, context: JsIrBackendConte } for (klass in constructedClasses) { - for (declaration in klass.declarations) { + // TODO a better way to support inverse overrides. + for (declaration in ArrayList(klass.declarations)) { if (declaration in result) continue if (declaration is IrOverridableDeclaration<*> && declaration.overridesUsefulFunction()) { @@ -277,6 +284,32 @@ fun usefulDeclarations(roots: Iterable, context: JsIrBackendConte declaration.enqueue() } } + + for (declaration in ArrayList(klass.declarations)) { + // TODO this is a hack. + if (declaration is IrProperty) { + declaration.getter?.let { if (it.overridesUsefulFunction()) it.enqueue() } + declaration.setter?.let { if (it.overridesUsefulFunction()) it.enqueue() } + } + } + + // Special hack for `IntrinsicsJs.kt` support + if (klass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }) { + ArrayList(klass.declarations).forEach { + if (it is IrSimpleFunction && it.name.asString().startsWith("invoke")) { + it.enqueue() + } + } + } + + // TODO find out how `doResume` gets removed + if (klass.symbol == context.ir.symbols.coroutineImpl) { + ArrayList(klass.declarations).forEach { + if (it is IrSimpleFunction && it.name.asString() == "doResume") { + it.enqueue() + } + } + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 094abe22daa..790dc106f53 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -50,6 +50,8 @@ class JsIrBackendContext( override val lateinitNullableFields get() = error("Use Mapping.lateInitFieldToNullableField instead") + override val extractedLocalClasses: MutableSet = hashSetOf() + override val builtIns = module.builtIns override var inVerbosePhase: Boolean = false @@ -57,6 +59,7 @@ class JsIrBackendContext( val devMode = configuration[JSConfigurationKeys.DEVELOPER_MODE] ?: false val externalPackageFragment = mutableMapOf() + val externalDeclarations = hashSetOf() val bodilessBuiltInsPackageFragment: IrPackageFragment = run { class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 0a1e5d385a0..a70f7ec0a77 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -55,252 +55,322 @@ private fun makeCustomJsModulePhase( } ) +sealed class Lowering(val name: String) { + + abstract val modulePhase: SameTypeNamedPhaseWrapper +} + +class DeclarationLowering( + name: String, + description: String, + prerequisite: Set = emptySet(), + private val factory: (JsIrBackendContext) -> DeclarationTransformer +) : Lowering(name) { + fun declarationTransformer(context: JsIrBackendContext): DeclarationTransformer { + return factory(context) + } + + override val modulePhase = makeJsModulePhase(factory, name, description, prerequisite) +} + +class BodyLowering( + name: String, + description: String, + prerequisite: Set = emptySet(), + private val factory: (JsIrBackendContext) -> BodyLoweringPass +) : Lowering(name) { + fun bodyLowering(context: JsIrBackendContext): BodyLoweringPass { + return factory(context) + } + + override val modulePhase = makeJsModulePhase(factory, name, description, prerequisite) +} + +class ModuleLowering( + name: String, + override val modulePhase: SameTypeNamedPhaseWrapper +) : Lowering(name) + +private fun makeDeclarationTransformerPhase( + lowering: (JsIrBackendContext) -> DeclarationTransformer, + name: String, + description: String, + prerequisite: Set = emptySet() +) = DeclarationLowering(name, description, prerequisite.map { it.modulePhase }.toSet(), lowering) + +private fun makeBodyLoweringPhase( + lowering: (JsIrBackendContext) -> BodyLoweringPass, + name: String, + description: String, + prerequisite: Set = emptySet() +) = BodyLowering(name, description, prerequisite.map { it.modulePhase }.toSet(), lowering) + +fun SameTypeNamedPhaseWrapper.toModuleLowering() = ModuleLowering(this.name, this) + private val validateIrBeforeLowering = makeCustomJsModulePhase( { context, module -> validationCallback(context, module) }, name = "ValidateIrBeforeLowering", description = "Validate IR before lowering" -) +).toModuleLowering() private val validateIrAfterLowering = makeCustomJsModulePhase( { context, module -> validationCallback(context, module) }, name = "ValidateIrAfterLowering", description = "Validate IR after lowering" +).toModuleLowering() + +val scriptRemoveReceiverLowering = makeIrModulePhase( + ::ScriptRemoveReceiverLowering, + name = "ScriptRemoveReceiver", + description = "Remove receivers for declarations in script" +).toModuleLowering() + +val createScriptFunctionsPhase = makeJsModulePhase( + ::CreateScriptFunctionsPhase, + name = "CreateScriptFunctionsPhase", + description = "Create functions for initialize and evaluate script" +).toModuleLowering() + +private val moveBodilessDeclarationsToSeparatePlacePhase = makeDeclarationTransformerPhase( + ::MoveBodilessDeclarationsToSeparatePlaceLowering, + name = "MoveBodilessDeclarationsToSeparatePlaceLowering", + description = "Move bodiless declarations to a separate place" ) -private val expectDeclarationsRemovingPhase = makeJsModulePhase( +private val expectDeclarationsRemovingPhase = makeDeclarationTransformerPhase( ::ExpectDeclarationsRemoveLowering, name = "ExpectDeclarationsRemoving", description = "Remove expect declaration from module fragment" ) -private val lateinitNullableFieldsPhase = makeJsModulePhase( +private val lateinitNullableFieldsPhase = makeDeclarationTransformerPhase( ::NullableFieldsForLateinitCreationLowering, name = "LateinitNullableFields", description = "Create nullable fields for lateinit properties" ) -private val lateinitDeclarationLoweringPhase = makeJsModulePhase( +private val lateinitDeclarationLoweringPhase = makeDeclarationTransformerPhase( ::NullableFieldsDeclarationLowering, name = "LateinitDeclarations", description = "Reference nullable fields from properties and getters + insert checks" ) -private val lateinitUsageLoweringPhase = makeJsModulePhase( +private val lateinitUsageLoweringPhase = makeBodyLoweringPhase( ::LateinitUsageLowering, name = "LateinitUsage", description = "Insert checks for lateinit field references" ) -private val stripTypeAliasDeclarationsPhase = makeJsModulePhase( +private val stripTypeAliasDeclarationsPhase = makeDeclarationTransformerPhase( { StripTypeAliasDeclarationsLowering() }, name = "StripTypeAliasDeclarations", description = "Strip typealias declarations" ) // TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase -private val provisionalFunctionExpressionPhase = makeJsModulePhase( +private val provisionalFunctionExpressionPhase = makeBodyLoweringPhase( { ProvisionalFunctionExpressionLowering() }, name = "FunctionExpression", description = "Transform IrFunctionExpression to a local function reference" ) -private val arrayConstructorPhase = makeJsModulePhase( +private val arrayConstructorPhase = makeBodyLoweringPhase( ::ArrayConstructorLowering, name = "ArrayConstructor", description = "Transform `Array(size) { index -> value }` into a loop" ) -private val functionInliningPhase = makeJsModulePhase( +private val functionInliningPhase = makeBodyLoweringPhase( ::FunctionInlining, name = "FunctionInliningPhase", description = "Perform function inlining", prerequisite = setOf(expectDeclarationsRemovingPhase) ) -private val copyInlineFunctionBodyLoweringPhase = makeJsModulePhase( +private val copyInlineFunctionBodyLoweringPhase = makeDeclarationTransformerPhase( ::CopyInlineFunctionBodyLowering, name = "CopyInlineFunctionBody", description = "Copy inline function body", prerequisite = setOf(functionInliningPhase) ) -private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase( +private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeDeclarationTransformerPhase( { RemoveInlineFunctionsWithReifiedTypeParametersLowering() }, name = "RemoveInlineFunctionsWithReifiedTypeParametersLowering", description = "Remove Inline functions with reified parameters from context", prerequisite = setOf(functionInliningPhase) ) -private val throwableSuccessorsLoweringPhase = makeJsModulePhase( +private val throwableSuccessorsLoweringPhase = makeBodyLoweringPhase( ::ThrowableLowering, name = "ThrowableLowering", description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions" ) -private val tailrecLoweringPhase = makeJsModulePhase( +private val tailrecLoweringPhase = makeBodyLoweringPhase( ::TailrecLowering, name = "TailrecLowering", description = "Replace `tailrec` callsites with equivalent loop" ) -private val enumClassConstructorLoweringPhase = makeJsModulePhase( +private val enumClassConstructorLoweringPhase = makeDeclarationTransformerPhase( ::EnumClassConstructorLowering, name = "EnumClassConstructorLowering", description = "Transform Enum Class into regular Class" ) -private val enumClassConstructorBodyLoweringPhase = makeJsModulePhase( +private val enumClassConstructorBodyLoweringPhase = makeBodyLoweringPhase( ::EnumClassConstructorBodyTransformer, name = "EnumClassConstructorBodyLowering", description = "Transform Enum Class into regular Class" ) -private val enumEntryInstancesLoweringPhase = makeJsModulePhase( +private val enumEntryInstancesLoweringPhase = makeDeclarationTransformerPhase( ::EnumEntryInstancesLowering, name = "EnumEntryInstancesLowering", description = "Create instance variable for each enum entry initialized with `null`", prerequisite = setOf(enumClassConstructorLoweringPhase) ) -private val enumEntryInstancesBodyLoweringPhase = makeJsModulePhase( +private val enumEntryInstancesBodyLoweringPhase = makeBodyLoweringPhase( ::EnumEntryInstancesBodyLowering, name = "EnumEntryInstancesBodyLowering", description = "Insert enum entry field initialization into correxposnding class constructors", prerequisite = setOf(enumEntryInstancesLoweringPhase) ) -private val enumClassCreateInitializerLoweringPhase = makeJsModulePhase( +private val enumClassCreateInitializerLoweringPhase = makeDeclarationTransformerPhase( ::EnumClassCreateInitializerLowering, name = "EnumClassCreateInitializerLowering", description = "Create initializer for enum entries", prerequisite = setOf(enumClassConstructorLoweringPhase) ) -private val enumEntryCreateGetInstancesFunsLoweringPhase = makeJsModulePhase( +private val enumEntryCreateGetInstancesFunsLoweringPhase = makeDeclarationTransformerPhase( ::EnumEntryCreateGetInstancesFunsLowering, name = "EnumEntryCreateGetInstancesFunsLowering", description = "Create enumEntry_getInstance functions", prerequisite = setOf(enumClassConstructorLoweringPhase) ) -private val enumSyntheticFunsLoweringPhase = makeJsModulePhase( +private val enumSyntheticFunsLoweringPhase = makeDeclarationTransformerPhase( ::EnumSyntheticFunctionsLowering, name = "EnumSyntheticFunctionsLowering", description = "Implement `valueOf` and `values`", prerequisite = setOf(enumClassConstructorLoweringPhase) ) -private val enumUsageLoweringPhase = makeJsModulePhase( +private val enumUsageLoweringPhase = makeBodyLoweringPhase( ::EnumUsageLowering, name = "EnumUsageLowering", description = "Replace enum access with invocation of corresponding function", prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase) ) -private val enumEntryRemovalLoweringPhase = makeJsModulePhase( +private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase( ::EnumClassRemoveEntriesLowering, name = "EnumEntryRemovalLowering", description = "Replace enum entry with corresponding class", prerequisite = setOf(enumUsageLoweringPhase) ) -private val sharedVariablesLoweringPhase = makeJsModulePhase( +private val sharedVariablesLoweringPhase = makeBodyLoweringPhase( ::SharedVariablesLowering, name = "SharedVariablesLowering", description = "Box captured mutable variables" ) -private val returnableBlockLoweringPhase = makeJsModulePhase( +private val returnableBlockLoweringPhase = makeBodyLoweringPhase( ::ReturnableBlockLowering, name = "ReturnableBlockLowering", description = "Replace returnable block with do-while loop", prerequisite = setOf(functionInliningPhase) ) -private val forLoopsLoweringPhase = makeJsModulePhase( +private val forLoopsLoweringPhase = makeBodyLoweringPhase( ::ForLoopsLowering, name = "ForLoopsLowering", description = "[Optimization] For loops lowering" ) -private val propertyAccessorInlinerLoweringPhase = makeJsModulePhase( +private val propertyAccessorInlinerLoweringPhase = makeBodyLoweringPhase( ::PropertyAccessorInlineLowering, name = "PropertyAccessorInlineLowering", description = "[Optimization] Inline property accessors" ) -private val foldConstantLoweringPhase = makeJsModulePhase( +private val foldConstantLoweringPhase = makeBodyLoweringPhase( { FoldConstantLowering(it, true) }, name = "FoldConstantLowering", description = "[Optimization] Constant Folding", prerequisite = setOf(propertyAccessorInlinerLoweringPhase) ) -private val localDelegatedPropertiesLoweringPhase = makeJsModulePhase( +private val localDelegatedPropertiesLoweringPhase = makeBodyLoweringPhase( { LocalDelegatedPropertiesLowering() }, name = "LocalDelegatedPropertiesLowering", description = "Transform Local Delegated properties" ) -private val localDeclarationsLoweringPhase = makeJsModulePhase( +private val localDeclarationsLoweringPhase = makeBodyLoweringPhase( ::LocalDeclarationsLowering, name = "LocalDeclarationsLowering", description = "Move local declarations into nearest declaration container", prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase) ) -private val localClassExtractionPhase = makeJsModulePhase( +private val localClassExtractionPhase = makeBodyLoweringPhase( ::LocalClassPopupLowering, name = "LocalClassExtractionPhase", description = "Move local declarations into nearest declaration container", prerequisite = setOf(localDeclarationsLoweringPhase) ) -private val innerClassesLoweringPhase = makeJsModulePhase( +private val innerClassesLoweringPhase = makeDeclarationTransformerPhase( ::InnerClassesLowering, name = "InnerClassesLowering", description = "Capture outer this reference to inner class" ) -private val innerClassesMemberBodyLoweringPhase = makeJsModulePhase( +private val innerClassesMemberBodyLoweringPhase = makeBodyLoweringPhase( ::InnerClassesMemberBodyLowering, name = "InnerClassesMemberBody", description = "Replace `this` with 'outer this' field references", prerequisite = setOf(innerClassesLoweringPhase) ) -private val innerClassConstructorCallsLoweringPhase = makeJsModulePhase( +private val innerClassConstructorCallsLoweringPhase = makeBodyLoweringPhase( ::InnerClassConstructorCallsLowering, name = "InnerClassConstructorCallsLowering", description = "Replace inner class constructor invocation" ) -private val suspendFunctionsLoweringPhase = makeJsModulePhase( +private val suspendFunctionsLoweringPhase = makeBodyLoweringPhase( ::JsSuspendFunctionsLowering, name = "SuspendFunctionsLowering", description = "Transform suspend functions into CoroutineImpl instance and build state machine" ) -private val suspendLambdasRemovalLoweringPhase = makeJsModulePhase( +private val suspendLambdasRemovalLoweringPhase = makeDeclarationTransformerPhase( { RemoveSuspendLambdas() }, name = "RemoveSuspendLambdas", description = "Remove suspend lambdas" ) -private val privateMembersLoweringPhase = makeJsModulePhase( +private val privateMembersLoweringPhase = makeDeclarationTransformerPhase( ::PrivateMembersLowering, name = "PrivateMembersLowering", description = "Extract private members from classes" ) -private val privateMemberUsagesLoweringPhase = makeJsModulePhase( +private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase( ::PrivateMemberBodiesLowering, name = "PrivateMemberUsagesLowering", description = "Rewrite the private member usages" ) -private val callableReferenceLoweringPhase = makeJsModulePhase( +private val callableReferenceLoweringPhase = makeBodyLoweringPhase( ::CallableReferenceLowering, name = "CallableReferenceLowering", description = "Handle callable references", @@ -312,163 +382,163 @@ private val callableReferenceLoweringPhase = makeJsModulePhase( ) ) -private val defaultArgumentStubGeneratorPhase = makeJsModulePhase( +private val defaultArgumentStubGeneratorPhase = makeDeclarationTransformerPhase( ::JsDefaultArgumentStubGenerator, name = "DefaultArgumentStubGenerator", description = "Generate synthetic stubs for functions with default parameter values" ) -private val defaultArgumentPatchOverridesPhase = makeJsModulePhase( +private val defaultArgumentPatchOverridesPhase = makeDeclarationTransformerPhase( ::DefaultParameterPatchOverridenSymbolsLowering, name = "DefaultArgumentsPatchOverrides", description = "Patch overrides for fake override dispatch functions", prerequisite = setOf(defaultArgumentStubGeneratorPhase) ) -private val defaultParameterInjectorPhase = makeJsModulePhase( +private val defaultParameterInjectorPhase = makeBodyLoweringPhase( { context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) }, name = "DefaultParameterInjector", description = "Replace callsite with default parameters with corresponding stub function", prerequisite = setOf(callableReferenceLoweringPhase, innerClassesLoweringPhase) ) -private val defaultParameterCleanerPhase = makeJsModulePhase( +private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase( ::DefaultParameterCleaner, name = "DefaultParameterCleaner", description = "Clean default parameters up" ) -private val jsDefaultCallbackGeneratorPhase = makeJsModulePhase( +private val jsDefaultCallbackGeneratorPhase = makeBodyLoweringPhase( ::JsDefaultCallbackGenerator, name = "JsDefaultCallbackGenerator", description = "Build binding for super calls with default parameters" ) -private val varargLoweringPhase = makeJsModulePhase( +private val varargLoweringPhase = makeBodyLoweringPhase( ::VarargLowering, name = "VarargLowering", description = "Lower vararg arguments", prerequisite = setOf(callableReferenceLoweringPhase) ) -private val propertiesLoweringPhase = makeJsModulePhase( +private val propertiesLoweringPhase = makeDeclarationTransformerPhase( { PropertiesLowering() }, name = "PropertiesLowering", description = "Move fields and accessors out from its property" ) -private val primaryConstructorLoweringPhase = makeJsModulePhase( +private val primaryConstructorLoweringPhase = makeDeclarationTransformerPhase( ::PrimaryConstructorLowering, name = "PrimaryConstructorLowering", description = "Creates primary constructor if it doesn't exist", prerequisite = setOf(enumClassConstructorLoweringPhase) ) -private val delegateToPrimaryConstructorLoweringPhase = makeJsModulePhase( +private val delegateToPrimaryConstructorLoweringPhase = makeBodyLoweringPhase( ::DelegateToSyntheticPrimaryConstructor, name = "DelegateToSyntheticPrimaryConstructor", description = "Delegates to synthetic primary constructor", prerequisite = setOf(primaryConstructorLoweringPhase) ) -private val annotationConstructorLowering = makeJsModulePhase( +private val annotationConstructorLowering = makeDeclarationTransformerPhase( ::AnnotationConstructorLowering, name = "AnnotationConstructorLowering", description = "Generate annotation constructor body" ) -private val initializersLoweringPhase = makeJsModulePhase( +private val initializersLoweringPhase = makeBodyLoweringPhase( ::InitializersLowering, name = "InitializersLowering", description = "Merge init block and field initializers into [primary] constructor", prerequisite = setOf(enumClassConstructorLoweringPhase, primaryConstructorLoweringPhase, annotationConstructorLowering) ) -private val initializersCleanupLoweringPhase = makeJsModulePhase( +private val initializersCleanupLoweringPhase = makeDeclarationTransformerPhase( ::InitializersCleanupLowering, name = "InitializersCleanupLowering", description = "Remove non-static anonymous initializers and field init expressions", prerequisite = setOf(initializersLoweringPhase) ) -private val multipleCatchesLoweringPhase = makeJsModulePhase( +private val multipleCatchesLoweringPhase = makeBodyLoweringPhase( ::MultipleCatchesLowering, name = "MultipleCatchesLowering", description = "Replace multiple catches with single one" ) -private val bridgesConstructionPhase = makeJsModulePhase( +private val bridgesConstructionPhase = makeDeclarationTransformerPhase( ::BridgesConstruction, name = "BridgesConstruction", description = "Generate bridges", prerequisite = setOf(suspendFunctionsLoweringPhase) ) -private val typeOperatorLoweringPhase = makeJsModulePhase( +private val typeOperatorLoweringPhase = makeBodyLoweringPhase( ::TypeOperatorLowering, name = "TypeOperatorLowering", description = "Lower IrTypeOperator with corresponding logic", prerequisite = setOf(bridgesConstructionPhase, removeInlineFunctionsWithReifiedTypeParametersLoweringPhase) ) -private val secondaryConstructorLoweringPhase = makeJsModulePhase( +private val secondaryConstructorLoweringPhase = makeDeclarationTransformerPhase( ::SecondaryConstructorLowering, name = "SecondaryConstructorLoweringPhase", description = "Generate static functions for each secondary constructor", prerequisite = setOf(innerClassesLoweringPhase) ) -private val secondaryFactoryInjectorLoweringPhase = makeJsModulePhase( +private val secondaryFactoryInjectorLoweringPhase = makeBodyLoweringPhase( ::SecondaryFactoryInjectorLowering, name = "SecondaryFactoryInjectorLoweringPhase", description = "Replace usage of secondary constructor with corresponding static function", prerequisite = setOf(innerClassesLoweringPhase) ) -private val inlineClassDeclarationLoweringPhase = makeJsModulePhase( +private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase( { InlineClassLowering(it).inlineClassDeclarationLowering }, name = "InlineClassDeclarationLowering", description = "Handle inline class declarations" ) -private val inlineClassUsageLoweringPhase = makeJsModulePhase( +private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase( { InlineClassLowering(it).inlineClassUsageLowering }, name = "InlineClassUsageLowering", description = "Handle inline class usages" ) -private val autoboxingTransformerPhase = makeJsModulePhase( +private val autoboxingTransformerPhase = makeBodyLoweringPhase( ::AutoboxingTransformer, name = "AutoboxingTransformer", description = "Insert box/unbox intrinsics" ) -private val blockDecomposerLoweringPhase = makeJsModulePhase( +private val blockDecomposerLoweringPhase = makeBodyLoweringPhase( ::JsBlockDecomposerLowering, name = "BlockDecomposerLowering", description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS", prerequisite = setOf(typeOperatorLoweringPhase, suspendFunctionsLoweringPhase) ) -private val classReferenceLoweringPhase = makeJsModulePhase( +private val classReferenceLoweringPhase = makeBodyLoweringPhase( ::ClassReferenceLowering, name = "ClassReferenceLowering", description = "Handle class references" ) -private val primitiveCompanionLoweringPhase = makeJsModulePhase( +private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase( ::PrimitiveCompanionLowering, name = "PrimitiveCompanionLowering", description = "Replace common companion object access with platform one" ) -private val constLoweringPhase = makeJsModulePhase( +private val constLoweringPhase = makeBodyLoweringPhase( ::ConstLowering, name = "ConstLowering", description = "Wrap Long and Char constants into constructor invocation" ) -private val callsLoweringPhase = makeJsModulePhase( +private val callsLoweringPhase = makeBodyLoweringPhase( ::CallsLowering, name = "CallsLowering", description = "Handle intrinsics" @@ -478,27 +548,27 @@ private val testGenerationPhase = makeJsModulePhase( ::TestGenerator, name = "TestGenerationLowering", description = "Generate invocations to kotlin.test suite and test functions" -) +).toModuleLowering() -private val staticMembersLoweringPhase = makeJsModulePhase( +private val staticMembersLoweringPhase = makeDeclarationTransformerPhase( ::StaticMembersLowering, name = "StaticMembersLowering", description = "Move static member declarations to top-level" ) -private val objectDeclarationLoweringPhase = makeJsModulePhase( +private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase( ::ObjectDeclarationLowering, name = "ObjectDeclarationLowering", description = "Create lazy object instance generator functions" ) -private val objectUsageLoweringPhase = makeJsModulePhase( +private val objectUsageLoweringPhase = makeBodyLoweringPhase( ::ObjectUsageLowering, name = "ObjectUsageLowering", description = "Transform IrGetObjectValue into instance generator call" ) -val phaseList: List> = listOf( +val loweringList = listOf( scriptRemoveReceiverLowering, validateIrBeforeLowering, testGenerationPhase, @@ -571,9 +641,12 @@ val phaseList: List, phase -> - acc.then(phase) - }) \ No newline at end of file + lower = loweringList.drop(1).fold(loweringList[0].modulePhase) { acc: CompilerPhase, lowering -> + acc.then(lowering.modulePhase) + } +) \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt index 3845e364e09..f6beb2cd61d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.ir.backend.js import org.jetbrains.kotlin.backend.common.DefaultMapping +import org.jetbrains.kotlin.backend.common.Mapping import org.jetbrains.kotlin.ir.declarations.* class JsMapping : DefaultMapping() { @@ -27,4 +28,23 @@ class JsMapping : DefaultMapping() { val enumConstructorOldToNewValueParameters = newMapping() val enumEntryToCorrespondingField = newMapping() val enumClassToInitEntryInstancesFun = newMapping() + + // Triggers `StageController.lazyLower` on access + override fun newMapping(): Mapping.Delegate = object : Mapping.Delegate() { + private val map: MutableMap = mutableMapOf() + + override operator fun get(key: K): V? { + stageController.lazyLower(key) + return map[key] + } + + override operator fun set(key: K, value: V?) { + stageController.lazyLower(key) + if (value == null) { + map.remove(key) + } else { + map[key] = value + } + } + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/MutableController.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/MutableController.kt index b0087ff255d..8322f489746 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/MutableController.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/MutableController.kt @@ -8,11 +8,124 @@ package org.jetbrains.kotlin.ir.backend.js import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrPersistingElementBase +import org.jetbrains.kotlin.ir.declarations.impl.IrBodyBase +import org.jetbrains.kotlin.ir.declarations.impl.IrDeclarationBase +import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.util.isLocal -open class MutableController(override var currentStage: Int = 0) : StageController { +open class MutableController(val context: JsIrBackendContext, val lowerings: List) : StageController { - override var bodiesEnabled: Boolean = true + override var currentStage: Int = 0 + + override fun lazyLower(declaration: IrDeclaration) { + if (declaration is IrDeclarationBase<*>) { + while (declaration.loweredUpTo + 1 < currentStage) { + val i = declaration.loweredUpTo + 1 + withStage(i) { + // TODO a better way to skip declarations in external package fragments + if (declaration.removedOn > i && declaration !in context.externalDeclarations) { + + when (val lowering = lowerings[i - 1]) { + is DeclarationLowering -> lowering.doApplyLoweringTo(declaration) + is BodyLowering -> { + // Handle local declarations in case they leak through types + if (declaration.isLocal) { + declaration.enclosingBody()?.let { + withStage(i + 1) { lazyLower(it) } + } + } + } + } + } + declaration.loweredUpTo = i + } + } + } + } + + override fun lazyLower(body: IrBody) { + if (body is IrBodyBase<*>) { + for (i in (body.loweredUpTo + 1) until currentStage) { + withStage(i) { + if (body.container !in context.externalDeclarations) { + val lowering = lowerings[i - 1] + + if (lowering is BodyLowering) { + bodyLowering { + lowering.bodyLowering(context).lower(body, body.container) + } + } + } + body.loweredUpTo = i + } + } + } + } + + // Launches a lowering and applies it's results + private fun DeclarationLowering.doApplyLoweringTo(declaration: IrDeclarationBase<*>) { + val parentBefore = declaration.parent + val result = restrictTo(declaration) { this.declarationTransformer(context).transformFlat(declaration) } + if (result != null) { + result.forEach { + // Some of our lowerings rely on transformDeclarationsFlat + it.parent = parentBefore + } + + if (parentBefore is IrDeclarationContainer) { + unrestrictDeclarationListsAccess { + + // Field order matters for top level property initialization + val correspondingProperty = when (declaration) { + is IrSimpleFunction -> declaration.correspondingPropertySymbol?.owner + is IrField -> declaration.correspondingPropertySymbol?.owner + else -> null + } + + var index = -1 + parentBefore.declarations.forEachIndexed { i, v -> + if (v == declaration || index == -1 && v == correspondingProperty) { + index = i + } + } + + if (index != -1 && declaration !is IrProperty) { + if (parentBefore.declarations[index] == declaration) { + parentBefore.declarations.removeAt(index) + } + parentBefore.declarations.addAll(index, result) + } else { + parentBefore.declarations.addAll(result) + } + + if (declaration.parent == parentBefore && declaration !in result) { + declaration.removedOn = currentStage + } + } + } + } + } + + // Finds outermost body, containing the declarations + // Doesn't work in case of local declarations inside default arguments + // That might be fine as those shouldn't leak + private fun IrDeclaration.enclosingBody(): IrBody? { + var lastBodyContainer: IrDeclaration? = null + var parent = this.parent + while (parent is IrDeclaration) { + if (parent !is IrClass) { + lastBodyContainer = parent + } + parent = parent.parent + } + return lastBodyContainer?.run { + when (this) { + is IrFunction -> body // TODO What about local declarations inside default arguments? + is IrField -> initializer + else -> null + } + } + } override fun withStage(stage: Int, fn: () -> T): T { val prevStage = currentStage @@ -24,27 +137,45 @@ open class MutableController(override var currentStage: Int = 0) : StageControll } } - override fun withInitialIr(block: () -> T): T = restrictionImpl(null) { withStage(0, block) } + override fun withInitialIr(block: () -> T): T = { withStage(0, block) }.withRestrictions(newRestrictedToDeclaration = null) - override fun withInitialStateOf(declaration: IrDeclaration, block: () -> T): T = withStage((declaration as? IrPersistingElementBase<*>)?.createdOn ?: 0, block) + override fun restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn.withRestrictions(newRestrictedToDeclaration = declaration) - private var restricted: Boolean = false + override fun bodyLowering(fn: () -> T): T = fn.withRestrictions(newBodiesEnabled = true, newRestricted = true, newDeclarationListsRestricted = true) + + override fun unrestrictDeclarationListsAccess(fn: () -> T): T = fn.withRestrictions(newDeclarationListsRestricted = false) + + override fun canModify(element: IrElement): Boolean { + return true +// return !restricted || restrictedToDeclaration === element || element is IrPersistingElementBase<*> && element.createdOn == currentStage + } + + override fun canAccessDeclarationsOf(irClass: IrClass): Boolean { + return !declarationListsRestricted || irClass.visibility == Visibilities.LOCAL && irClass !in context.extractedLocalClasses + } private var restrictedToDeclaration: IrDeclaration? = null + // TODO flags? + override var bodiesEnabled: Boolean = true + private var restricted: Boolean = false + private var declarationListsRestricted = false - override fun restrictTo(declaration: IrDeclaration, fn: () -> T): T = restrictionImpl(declaration, fn) - - private fun restrictionImpl(declaration: IrDeclaration?, fn: () -> T): T { + private inline fun (() -> T).withRestrictions( + newRestrictedToDeclaration: IrDeclaration? = null, + newBodiesEnabled: Boolean? = null, + newRestricted: Boolean? = null, + newDeclarationListsRestricted: Boolean? = null + ): T { val prev = restrictedToDeclaration - restrictedToDeclaration = declaration + restrictedToDeclaration = newRestrictedToDeclaration val wereBodiesEnabled = bodiesEnabled - bodiesEnabled = false + bodiesEnabled = newBodiesEnabled ?: bodiesEnabled val wasRestricted = restricted - restricted = true + restricted = newRestricted ?: restricted val wereDeclarationListsRestricted = declarationListsRestricted - declarationListsRestricted = true + declarationListsRestricted = newDeclarationListsRestricted ?: declarationListsRestricted try { - return fn() + return this.invoke() } finally { restrictedToDeclaration = prev bodiesEnabled = wereBodiesEnabled @@ -52,40 +183,4 @@ open class MutableController(override var currentStage: Int = 0) : StageControll declarationListsRestricted = wereDeclarationListsRestricted } } - - override fun bodyLowering(fn: () -> T): T { - val wereBodiesEnabled = bodiesEnabled - bodiesEnabled = true - val wasRestricted = restricted - restricted = true - val wereDeclarationListsRestricted = declarationListsRestricted - declarationListsRestricted = true - try { - return fn() - } finally { - bodiesEnabled = wereBodiesEnabled - restricted = wasRestricted - declarationListsRestricted = wereDeclarationListsRestricted - } - } - - override fun canModify(element: IrElement): Boolean { - return !restricted || restrictedToDeclaration === element || element is IrPersistingElementBase<*> && element.createdOn == currentStage - } - - private var declarationListsRestricted = false - - override fun unrestrictDeclarationListsAccess(fn: () -> T): T { - val wereDeclarationListsRestricted = declarationListsRestricted - declarationListsRestricted = false - try { - return fn() - } finally { - declarationListsRestricted = wereDeclarationListsRestricted - } - } - - override fun canAccessDeclarationsOf(irClass: IrClass): Boolean { - return !declarationListsRestricted || irClass.visibility == Visibilities.LOCAL /*|| currentStage == (irClass as? IrPersistingElementBase<*>)?.createdOn ?: 0*/ - } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 5b7fe7ffc58..43d81b2fd1a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaserState import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.ir.backend.js.export.isExported +import org.jetbrains.kotlin.ir.backend.js.lower.generateTests import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector @@ -27,16 +27,6 @@ import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.utils.DFS - -fun sortDependencies(dependencies: Collection): Collection { - val mapping = dependencies.map { it.descriptor to it }.toMap() - - return DFS.topologicalOrder(dependencies) { m -> - val descriptor = m.descriptor - descriptor.allDependencyModules.filter { it != descriptor }.map { mapping[it] } - }.reversed() -} class CompilerResult( val jsCode: String?, @@ -55,7 +45,8 @@ fun compile( mainArguments: List?, exportedDeclarations: Set = emptySet(), generateFullJs: Boolean = true, - generateDceJs: Boolean = false + generateDceJs: Boolean = false, + dceDriven: Boolean = false ): CompilerResult { stageController = object : StageController {} @@ -93,17 +84,30 @@ fun compile( moveBodilessDeclarationsToSeparatePlace(context, moduleFragment) - val controller = MutableController() - stageController = controller + if (dceDriven) { - val phaserState = PhaserState() - phaseList.forEachIndexed { index, phase -> - controller.currentStage = index + 1 - phase.invoke(phaseConfig, phaserState, context, moduleFragment) + // TODO we should only generate tests for the current module + // TODO should be done incrementally + generateTests(context, moduleFragment) + + val controller = MutableController(context, pirLowerings) + stageController = controller + + controller.currentStage = controller.lowerings.size + 1 + + eliminateDeadDeclarations(moduleFragment, context, mainFunction) + + stageController = object : StageController { + override val currentStage: Int = controller.currentStage + } + + val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) + return transformer.generateModule(moduleFragment, fullJs = true, dceJs = false) + } else { + jsPhases.invokeToplevel(phaseConfig, context, moduleFragment) + val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) + return transformer.generateModule(moduleFragment, generateFullJs, generateDceJs) } - - val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments) - return transformer.generateModule(moduleFragment, generateFullJs, generateDceJs) } fun generateJsCode( diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt index 6cde2f70ef1..3aebf1af353 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt @@ -27,12 +27,6 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.name.Name -val createScriptFunctionsPhase = makeIrModulePhase( - ::CreateScriptFunctionsPhase, - name = "CreateScriptFunctionsPhase", - description = "Create functions for initialize and evaluate script" -) - private object SCRIPT_FUNCTION : IrDeclarationOriginImpl("SCRIPT_FUNCTION") class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweringPass { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt index 997a5174d6f..7471dde6d11 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.ir.addChild import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier @@ -17,6 +18,8 @@ import org.jetbrains.kotlin.ir.symbols.IrFileSymbol import org.jetbrains.kotlin.ir.util.UniqId import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.isEffectivelyExternal +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.name.FqName private val BODILESS_BUILTIN_CLASSES = listOf( @@ -89,12 +92,16 @@ class MoveBodilessDeclarationsToSeparatePlaceLowering(private val context: JsIrB context.packageLevelJsModules += externalPackageFragment + declaration.collectAllExternalDeclarations() + return emptyList() } else { val d = declaration as? IrDeclarationWithName ?: return null if (isBuiltInClass(d)) { context.bodilessBuiltInsPackageFragment.addChild(d) + d.collectAllExternalDeclarations() + return emptyList() } else if (d.isEffectivelyExternal()) { if (d.getJsModule() != null) @@ -103,10 +110,25 @@ class MoveBodilessDeclarationsToSeparatePlaceLowering(private val context: JsIrB externalPackageFragment.declarations += d d.parent = externalPackageFragment + d.collectAllExternalDeclarations() + return emptyList() } return null } } + + private fun IrDeclaration.collectAllExternalDeclarations() { + this.accept(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitDeclaration(declaration: IrDeclaration) { + context.externalDeclarations.add(declaration) + super.visitDeclaration(declaration) + } + }, null) + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt index 01e7ae5ced3..c545d4e396e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PrimitiveCompanionLowering.kt @@ -57,7 +57,9 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : BodyLowering p.setter?.let { if (it.name == function.name) return it } } - error("Accessor not found") + return actualCompanion.declarations + .filterIsInstance() + .single { it.name == function.name } } override fun lower(irBody: IrBody, container: IrDeclaration) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt index 80116125415..a3890365153 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt @@ -22,13 +22,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import java.lang.IllegalArgumentException -val scriptRemoveReceiverLowering = makeIrModulePhase( - ::ScriptRemoveReceiverLowering, - name = "ScriptRemoveReceiver", - description = "Remove receivers for declarations in script" -) - -private class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass { +class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass { override fun lower(irFile: IrFile) { if (context.scriptMode) { irFile.declarations.transformFlat { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt index 83eac2eb73c..42834abd5b5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt @@ -22,6 +22,14 @@ import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.name.FqName +fun generateTests(context: JsIrBackendContext, moduleFragment: IrModuleFragment) { + val generator = TestGenerator(context) + + moduleFragment.files.forEach { + generator.lower(it) + } +} + class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass { override fun lower(irFile: IrFile) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt index 38baf4a814a..9b2e305523e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFileToJsTransformer.kt @@ -15,7 +15,7 @@ class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer get() = TODO("not implemented") + + override val extractedLocalClasses: MutableSet = hashSetOf() + override val scriptMode: Boolean = false override val lateinitNullableFields = mutableMapOf() diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt index 85e395cfa40..fd2c894713d 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt @@ -20,9 +20,9 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.UniqId @@ -41,6 +41,7 @@ class WasmBackendContext( override val scriptMode = false override val transformedFunction = mutableMapOf() override val lateinitNullableFields = mutableMapOf() + override val extractedLocalClasses: MutableSet = hashSetOf() // Place to store declarations excluded from code generation val excludedDeclarations: IrPackageFragment by lazy { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/PersistentApi.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/PersistentApi.kt index 0be3c81976c..ea1f4370cf5 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/PersistentApi.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/PersistentApi.kt @@ -25,8 +25,6 @@ interface StageController { fun withInitialIr(block: () -> T): T = block() - fun withInitialStateOf(declaration: IrDeclaration, block: () -> T): T = block() - fun restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn() fun bodyLowering(fn: () -> T): T = fn() @@ -38,6 +36,7 @@ interface StageController { fun canAccessDeclarationsOf(irClass: IrClass): Boolean = true } +@Suppress("NOTHING_TO_INLINE") inline fun withInitialIr(noinline fn: () -> T): T { return stageController.withInitialIr(fn) } \ No newline at end of file diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index d62f0ac5e2f..f4be5a6c6e8 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -6,6 +6,7 @@ where advanced options include: -Xgenerate-dts Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only. -Xinclude= A path to an intermediate library that should be processed in the same manner as source files. -Xir-dce Perform experimental dead code elimination + -Xir-dce-driven Perform a more experimental faster dead code elimination -Xir-only Disables pre-IR backend -Xir-produce-js Generates JS file using IR backend. Also disables pre-IR backend -Xir-produce-klib-dir Generate unpacked KLIB into parent directory of output JS file. diff --git a/js/js.tests/build.gradle.kts b/js/js.tests/build.gradle.kts index 754dacdfa18..93e2c71a663 100644 --- a/js/js.tests/build.gradle.kts +++ b/js/js.tests/build.gradle.kts @@ -133,6 +133,12 @@ projectTest("jsTest", true) { } projectTest("jsIrTest", true) { + systemProperty("kotlin.js.ir.pir", "false") + setUpJsBoxTests(jsEnabled = false, jsIrEnabled = true) +} + +projectTest("jsPirTest", true) { + systemProperty("kotlin.js.ir.skipRegularMode", "true") setUpJsBoxTests(jsEnabled = false, jsIrEnabled = true) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index b3fabbfd638..5269781d2e5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -81,13 +81,17 @@ abstract class BasicBoxTest( private val testGroupOutputDirForCompilation = File(pathToRootOutputDir + "out/" + testGroupOutputDirPrefix) private val testGroupOutputDirForMinification = File(pathToRootOutputDir + "out-min/" + testGroupOutputDirPrefix) + private val testGroupOutputDirForPir = File(pathToRootOutputDir + "out-pir/" + testGroupOutputDirPrefix) protected open fun getOutputPrefixFile(testFilePath: String): File? = null protected open fun getOutputPostfixFile(testFilePath: String): File? = null protected open val runMinifierByDefault: Boolean = false protected open val skipMinification = getBoolean("kotlin.js.skipMinificationTest") + + protected open val skipRegularMode: Boolean = false protected open val runIrDce: Boolean = false + protected open val runIrPir: Boolean = false protected open val incrementalCompilationChecksEnabled = true @@ -105,6 +109,7 @@ abstract class BasicBoxTest( val file = File(filePath) val outputDir = getOutputDir(file) val dceOutputDir = getOutputDir(file, testGroupOutputDirForMinification) + val pirOutputDir = getOutputDir(file, testGroupOutputDirForPir) var fileContent = KotlinTestUtils.doLoadFile(file) if (coroutinesPackage.isNotEmpty()) { fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage) @@ -158,10 +163,11 @@ abstract class BasicBoxTest( val outputFileName = module.outputFileName(outputDir) + ".js" val dceOutputFileName = module.outputFileName(dceOutputDir) + ".js" + val pirOutputFileName = module.outputFileName(pirOutputDir) + ".js" val isMainModule = mainModuleName == module.name generateJavaScriptFile( testFactory.tmpDir, - file.parent, module, outputFileName, dceOutputFileName, dependencies, allDependencies, friends, modules.size > 1, + file.parent, module, outputFileName, dceOutputFileName, pirOutputFileName, dependencies, allDependencies, friends, modules.size > 1, !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction, needsFullIrRuntime, isMainModule, klibBasedMpp ) @@ -219,6 +225,10 @@ abstract class BasicBoxTest( val dceAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first.replace(outputDir.absolutePath, dceOutputDir.absolutePath) } + globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + val pirAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first.replace(outputDir.absolutePath, pirOutputDir.absolutePath) } + + globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + + val dontRunGeneratedCode = InTextDirectivesUtils.dontRunGeneratedCode(targetBackend, file) if (!dontRunGeneratedCode && generateNodeJsRunner && !SKIP_NODE_JS.matcher(fileContent).find()) { @@ -229,10 +239,16 @@ abstract class BasicBoxTest( } if (!dontRunGeneratedCode) { - runGeneratedCode(allJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) + if (!skipRegularMode) { + runGeneratedCode(allJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) - if (runIrDce) { - runGeneratedCode(dceAllJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) + if (runIrDce) { + runGeneratedCode(dceAllJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) + } + } + + if (runIrPir && !SKIP_DCE_DRIVEN.matcher(fileContent).find()) { + runGeneratedCode(pirAllJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) } } @@ -352,6 +368,7 @@ abstract class BasicBoxTest( module: TestModule, outputFileName: String, dceOutputFileName: String, + pirOutputFileName: String, dependencies: List, allDependencies: List, friends: List, @@ -383,10 +400,11 @@ abstract class BasicBoxTest( val config = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, tmpDir, incrementalData = null, klibBasedMpp = klibBasedMpp) val outputFile = File(outputFileName) val dceOutputFile = File(dceOutputFileName) + val pirOutputFile = File(pirOutputFileName) val incrementalData = IncrementalData() translateFiles( - psiFiles.map(TranslationUnit::SourceFile), outputFile, dceOutputFile, config, outputPrefixFile, outputPostfixFile, + psiFiles.map(TranslationUnit::SourceFile), outputFile, dceOutputFile, pirOutputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule ) @@ -438,7 +456,7 @@ abstract class BasicBoxTest( val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js") translateFiles( - translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, + translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false ) @@ -501,6 +519,7 @@ abstract class BasicBoxTest( units: List, outputFile: File, dceOutputFile: File, + pirOutputFile: File, config: JsConfig, outputPrefixFile: File?, outputPostfixFile: File?, @@ -895,6 +914,7 @@ abstract class BasicBoxTest( private val CALL_MAIN_PATTERN = Pattern.compile("^// *CALL_MAIN *$", Pattern.MULTILINE) private val KJS_WITH_FULL_RUNTIME = Pattern.compile("^// *KJS_WITH_FULL_RUNTIME *\$", Pattern.MULTILINE) private val KLIB_BASED_MPP = Pattern.compile("^// KLIB_BASED_MPP *$", Pattern.MULTILINE) + private val SKIP_DCE_DRIVEN = Pattern.compile("^// *SKIP_DCE_DRIVEN *$", Pattern.MULTILINE) @JvmStatic protected val runTestInNashorn = getBoolean("kotlin.js.useNashorn") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index 4ce9a94ddf6..4790552e26b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -44,7 +44,13 @@ abstract class BasicIrBoxTest( override val skipMinification = true - override val runIrDce: Boolean = true + private fun getBoolean(s: String, default: Boolean) = System.getProperty(s)?.let { getBoolean(it) } ?: default + + override val skipRegularMode: Boolean = getBoolean("kotlin.js.ir.skipRegularMode") + + override val runIrDce: Boolean = getBoolean("kotlin.js.ir.dce", true) + + override val runIrPir: Boolean = getBoolean("kotlin.js.ir.pir", true) // TODO Design incremental compilation for IR and add test support override val incrementalCompilationChecksEnabled = false @@ -63,6 +69,7 @@ abstract class BasicIrBoxTest( units: List, outputFile: File, dceOutputFile: File, + pirOutputFile: File, config: JsConfig, outputPrefixFile: File?, outputPostfixFile: File?, @@ -111,33 +118,54 @@ abstract class BasicIrBoxTest( PhaseConfig(jsPhases) } - val compiledModule = compile( - project = config.project, - mainModule = MainModule.SourceFiles(filesToCompile), - analyzer = AnalyzerWithCompilerReport(config.configuration), - configuration = config.configuration, - phaseConfig = phaseConfig, - allDependencies = resolvedLibraries, - friendDependencies = emptyList(), - mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }, - exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))), - generateFullJs = true, - generateDceJs = runIrDce - ) + if (!skipRegularMode) { + val compiledModule = compile( + project = config.project, + mainModule = MainModule.SourceFiles(filesToCompile), + analyzer = AnalyzerWithCompilerReport(config.configuration), + configuration = config.configuration, + phaseConfig = phaseConfig, + allDependencies = resolvedLibraries, + friendDependencies = emptyList(), + mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }, + exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))), + generateFullJs = true, + generateDceJs = runIrDce + ) - val wrappedCode = wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind) - outputFile.write(wrappedCode) + val wrappedCode = + wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind) + outputFile.write(wrappedCode) - compiledModule.dceJsCode?.let { dceJsCode -> - val dceWrappedCode = wrapWithModuleEmulationMarkers(dceJsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) - dceOutputFile.write(dceWrappedCode) + compiledModule.dceJsCode?.let { dceJsCode -> + val dceWrappedCode = + wrapWithModuleEmulationMarkers(dceJsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) + dceOutputFile.write(dceWrappedCode) + } + + if (generateDts) { + val dtsFile = outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts") + dtsFile?.write(compiledModule.tsDefinitions ?: error("No ts definitions")) + } } - if (generateDts) { - val dtsFile = outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts") - dtsFile?.write(compiledModule.tsDefinitions ?: error("No ts definitions")) + if (runIrPir) { + compile( + project = config.project, + mainModule = MainModule.SourceFiles(filesToCompile), + analyzer = AnalyzerWithCompilerReport(config.configuration), + configuration = config.configuration, + phaseConfig = phaseConfig, + allDependencies = resolvedLibraries, + friendDependencies = emptyList(), + mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }, + exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))), + dceDriven = true + ).jsCode!!.let { pirCode -> + val pirWrappedCode = wrapWithModuleEmulationMarkers(pirCode, moduleId = config.moduleId, moduleKind = config.moduleKind) + pirOutputFile.write(pirWrappedCode) + } } - } else { generateKLib( project = config.project, diff --git a/js/js.translator/testData/typescript-export/visibility/visibility.kt b/js/js.translator/testData/typescript-export/visibility/visibility.kt index d03856351e3..bd69c7e224e 100644 --- a/js/js.translator/testData/typescript-export/visibility/visibility.kt +++ b/js/js.translator/testData/typescript-export/visibility/visibility.kt @@ -1,4 +1,5 @@ // TARGET_BACKEND: JS_IR +// SKIP_DCE_DRIVEN // CHECK_TYPESCRIPT_DECLARATIONS // RUN_PLAIN_BOX_FUNCTION