DCE-driven mode

:js:js.tests:jsIrTest works same as before
:js:js.tests:jsPirTest runs tests in DCE-driven mode
This commit is contained in:
Anton Bannykh
2020-01-17 15:27:14 +03:00
parent 571bbba0e3
commit 1e96c3d21e
28 changed files with 540 additions and 212 deletions
+1
View File
@@ -18,6 +18,7 @@ workspace.xml
/jps-plugin/testData/kannotator /jps-plugin/testData/kannotator
/js/js.translator/testData/out/ /js/js.translator/testData/out/
/js/js.translator/testData/out-min/ /js/js.translator/testData/out-min/
/js/js.translator/testData/out-pir/
.gradle/ .gradle/
build/ build/
!**/src/**/build !**/src/**/build
@@ -125,6 +125,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-Xir-dce", description = "Perform experimental dead code elimination") @Argument(value = "-Xir-dce", description = "Perform experimental dead code elimination")
var irDce: Boolean by FreezableVar(false) 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") @Argument(value = "-Xir-only", description = "Disables pre-IR backend")
var irOnly: Boolean by FreezableVar(false) var irOnly: Boolean by FreezableVar(false)
@@ -228,13 +228,14 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
friendDependencies = friendDependencies, friendDependencies = friendDependencies,
mainArguments = mainCallArguments, mainArguments = mainCallArguments,
generateFullJs = !arguments.irDce, generateFullJs = !arguments.irDce,
generateDceJs = arguments.irDce generateDceJs = arguments.irDce,
dceDriven = arguments.irDceDriven
) )
} catch (e: JsIrCompilationError) { } catch (e: JsIrCompilationError) {
return COMPILATION_ERROR 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) outputFile.writeText(jsCode)
if (arguments.generateDts) { if (arguments.generateDts) {
val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!! val dtsFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "d.ts")!!
@@ -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.Ir
import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager
import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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.declarations.IrField
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
@@ -35,4 +36,5 @@ interface BackendContext {
val internalPackageFqn: FqName val internalPackageFqn: FqName
val transformedFunction: MutableMap<IrFunctionSymbol, IrSimpleFunctionSymbol> val transformedFunction: MutableMap<IrFunctionSymbol, IrSimpleFunctionSymbol>
val lateinitNullableFields: MutableMap<IrField, IrField> val lateinitNullableFields: MutableMap<IrField, IrField>
val extractedLocalClasses: MutableSet<IrClass>
} }
@@ -37,7 +37,7 @@ open class DefaultMapping : Mapping {
override val lateInitFieldToNullableField: Mapping.Delegate<IrField, IrField> = newMapping() override val lateInitFieldToNullableField: Mapping.Delegate<IrField, IrField> = newMapping()
override val inlineClassMemberToStatic: Mapping.Delegate<IrFunction, IrSimpleFunction> = newMapping() override val inlineClassMemberToStatic: Mapping.Delegate<IrFunction, IrSimpleFunction> = newMapping()
protected fun <K : IrDeclaration, V> newMapping() = object : Mapping.Delegate<K, V>() { protected open fun <K : IrDeclaration, V> newMapping() = object : Mapping.Delegate<K, V>() {
private val map: MutableMap<K, V> = mutableMapOf() private val map: MutableMap<K, V> = mutableMapOf()
override operator fun get(key: K): V? { override operator fun get(key: K): V? {
@@ -44,6 +44,7 @@ open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPa
for ((local, newContainer) in extractedLocalClasses) { for ((local, newContainer) in extractedLocalClasses) {
newContainer.addChild(local) newContainer.addChild(local)
context.extractedLocalClasses += local
} }
} }
@@ -6,14 +6,11 @@
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.ir.IrElement 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.export.isExported
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName 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.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* 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.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
@@ -28,11 +25,13 @@ fun eliminateDeadDeclarations(
mainFunction: IrSimpleFunction? mainFunction: IrSimpleFunction?
) { ) {
val allRoots = buildRoots(module, context, mainFunction) val allRoots = stageController.withInitialIr { buildRoots(module, context, mainFunction) }
val usefulDeclarations = usefulDeclarations(allRoots, context) val usefulDeclarations = usefulDeclarations(allRoots, context)
removeUselessDeclarations(module, usefulDeclarations) stageController.unrestrictDeclarationListsAccess {
removeUselessDeclarations(module, usefulDeclarations)
}
} }
private fun IrField.isConstant(): Boolean { private fun IrField.isConstant(): Boolean {
@@ -42,13 +41,14 @@ private fun IrField.isConstant(): Boolean {
private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable<IrDeclaration> { private fun buildRoots(module: IrModuleFragment, context: JsIrBackendContext, mainFunction: IrSimpleFunction?): Iterable<IrDeclaration> {
val rootDeclarations = val rootDeclarations =
(module.files + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file -> (module.files + context.packageLevelJsModules + context.externalPackageFragment.values).flatMapTo(mutableListOf()) { file ->
file.declarations.filter { file.declarations.flatMap { if (it is IrProperty) listOfNotNull(it.backingField, it.getter, it.setter) else listOf(it) }
it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true .filter {
|| it.isExported(context) it is IrField && it.initializer != null && it.fqNameWhenAvailable?.asString()?.startsWith("kotlin") != true
|| it.isEffectivelyExternal() || it.isExported(context)
|| it is IrField && it.correspondingPropertySymbol?.owner?.isExported(context) == true || it.isEffectivelyExternal()
|| it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.isExported(context) == true || it is IrField && it.correspondingPropertySymbol?.owner?.isExported(context) == true
}.filter { !(it is IrField && it.isConstant() && !it.isExported(context)) } || it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.isExported(context) == true
}.filter { !(it is IrField && it.isConstant() && !it.isExported(context)) }
} }
if (context.hasTests) rootDeclarations += context.testContainer if (context.hasTests) rootDeclarations += context.testContainer
@@ -121,14 +121,16 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
val constructedClasses = hashSetOf<IrClass>() val constructedClasses = hashSetOf<IrClass>()
fun IrDeclaration.enqueue() { fun IrDeclaration.enqueue() {
if (this !in result) { if ((this !is IrProperty || this.isExternal) && this !in result) {
result.add(this) result.add(this)
queue.addLast(this) queue.addLast(this)
} }
} }
// Add roots, including nested declarations // Add roots, including nested declarations
roots.withNested().forEach { it.enqueue() } stageController.withInitialIr {
roots.withNested().forEach { it.enqueue() }
}
val toStringMethod = val toStringMethod =
context.irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "toString" } context.irBuiltIns.anyClass.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "toString" }
@@ -141,6 +143,8 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
while (queue.isNotEmpty()) { while (queue.isNotEmpty()) {
val declaration = queue.pollFirst() val declaration = queue.pollFirst()
stageController.lazyLower(declaration)
if (declaration is IrClass) { if (declaration is IrClass) {
declaration.superTypes.forEach { declaration.superTypes.forEach {
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue() (it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue()
@@ -184,6 +188,8 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
else -> null else -> null
} }
(body as? IrBody)?.let { stageController.lazyLower(it) }
body?.acceptVoid(object : IrElementVisitorVoid { body?.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
@@ -258,7 +264,8 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
} }
for (klass in constructedClasses) { 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 in result) continue
if (declaration is IrOverridableDeclaration<*> && declaration.overridesUsefulFunction()) { if (declaration is IrOverridableDeclaration<*> && declaration.overridesUsefulFunction()) {
@@ -277,6 +284,32 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
declaration.enqueue() 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()
}
}
}
} }
} }
@@ -50,6 +50,8 @@ class JsIrBackendContext(
override val lateinitNullableFields override val lateinitNullableFields
get() = error("Use Mapping.lateInitFieldToNullableField instead") get() = error("Use Mapping.lateInitFieldToNullableField instead")
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
override val builtIns = module.builtIns override val builtIns = module.builtIns
override var inVerbosePhase: Boolean = false override var inVerbosePhase: Boolean = false
@@ -57,6 +59,7 @@ class JsIrBackendContext(
val devMode = configuration[JSConfigurationKeys.DEVELOPER_MODE] ?: false val devMode = configuration[JSConfigurationKeys.DEVELOPER_MODE] ?: false
val externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>() val externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>()
val externalDeclarations = hashSetOf<IrDeclaration>()
val bodilessBuiltInsPackageFragment: IrPackageFragment = run { val bodilessBuiltInsPackageFragment: IrPackageFragment = run {
class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol { class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol {
@@ -55,252 +55,322 @@ private fun makeCustomJsModulePhase(
} }
) )
sealed class Lowering(val name: String) {
abstract val modulePhase: SameTypeNamedPhaseWrapper<JsIrBackendContext, IrModuleFragment>
}
class DeclarationLowering(
name: String,
description: String,
prerequisite: Set<AnyNamedPhase> = 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<AnyNamedPhase> = 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<JsIrBackendContext, IrModuleFragment>
) : Lowering(name)
private fun makeDeclarationTransformerPhase(
lowering: (JsIrBackendContext) -> DeclarationTransformer,
name: String,
description: String,
prerequisite: Set<Lowering> = emptySet()
) = DeclarationLowering(name, description, prerequisite.map { it.modulePhase }.toSet(), lowering)
private fun makeBodyLoweringPhase(
lowering: (JsIrBackendContext) -> BodyLoweringPass,
name: String,
description: String,
prerequisite: Set<Lowering> = emptySet()
) = BodyLowering(name, description, prerequisite.map { it.modulePhase }.toSet(), lowering)
fun SameTypeNamedPhaseWrapper<JsIrBackendContext, IrModuleFragment>.toModuleLowering() = ModuleLowering(this.name, this)
private val validateIrBeforeLowering = makeCustomJsModulePhase( private val validateIrBeforeLowering = makeCustomJsModulePhase(
{ context, module -> validationCallback(context, module) }, { context, module -> validationCallback(context, module) },
name = "ValidateIrBeforeLowering", name = "ValidateIrBeforeLowering",
description = "Validate IR before lowering" description = "Validate IR before lowering"
) ).toModuleLowering()
private val validateIrAfterLowering = makeCustomJsModulePhase( private val validateIrAfterLowering = makeCustomJsModulePhase(
{ context, module -> validationCallback(context, module) }, { context, module -> validationCallback(context, module) },
name = "ValidateIrAfterLowering", name = "ValidateIrAfterLowering",
description = "Validate IR after lowering" 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, ::ExpectDeclarationsRemoveLowering,
name = "ExpectDeclarationsRemoving", name = "ExpectDeclarationsRemoving",
description = "Remove expect declaration from module fragment" description = "Remove expect declaration from module fragment"
) )
private val lateinitNullableFieldsPhase = makeJsModulePhase( private val lateinitNullableFieldsPhase = makeDeclarationTransformerPhase(
::NullableFieldsForLateinitCreationLowering, ::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields", name = "LateinitNullableFields",
description = "Create nullable fields for lateinit properties" description = "Create nullable fields for lateinit properties"
) )
private val lateinitDeclarationLoweringPhase = makeJsModulePhase( private val lateinitDeclarationLoweringPhase = makeDeclarationTransformerPhase(
::NullableFieldsDeclarationLowering, ::NullableFieldsDeclarationLowering,
name = "LateinitDeclarations", name = "LateinitDeclarations",
description = "Reference nullable fields from properties and getters + insert checks" description = "Reference nullable fields from properties and getters + insert checks"
) )
private val lateinitUsageLoweringPhase = makeJsModulePhase( private val lateinitUsageLoweringPhase = makeBodyLoweringPhase(
::LateinitUsageLowering, ::LateinitUsageLowering,
name = "LateinitUsage", name = "LateinitUsage",
description = "Insert checks for lateinit field references" description = "Insert checks for lateinit field references"
) )
private val stripTypeAliasDeclarationsPhase = makeJsModulePhase( private val stripTypeAliasDeclarationsPhase = makeDeclarationTransformerPhase(
{ StripTypeAliasDeclarationsLowering() }, { StripTypeAliasDeclarationsLowering() },
name = "StripTypeAliasDeclarations", name = "StripTypeAliasDeclarations",
description = "Strip typealias declarations" description = "Strip typealias declarations"
) )
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase // TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
private val provisionalFunctionExpressionPhase = makeJsModulePhase( private val provisionalFunctionExpressionPhase = makeBodyLoweringPhase(
{ ProvisionalFunctionExpressionLowering() }, { ProvisionalFunctionExpressionLowering() },
name = "FunctionExpression", name = "FunctionExpression",
description = "Transform IrFunctionExpression to a local function reference" description = "Transform IrFunctionExpression to a local function reference"
) )
private val arrayConstructorPhase = makeJsModulePhase( private val arrayConstructorPhase = makeBodyLoweringPhase(
::ArrayConstructorLowering, ::ArrayConstructorLowering,
name = "ArrayConstructor", name = "ArrayConstructor",
description = "Transform `Array(size) { index -> value }` into a loop" description = "Transform `Array(size) { index -> value }` into a loop"
) )
private val functionInliningPhase = makeJsModulePhase( private val functionInliningPhase = makeBodyLoweringPhase(
::FunctionInlining, ::FunctionInlining,
name = "FunctionInliningPhase", name = "FunctionInliningPhase",
description = "Perform function inlining", description = "Perform function inlining",
prerequisite = setOf(expectDeclarationsRemovingPhase) prerequisite = setOf(expectDeclarationsRemovingPhase)
) )
private val copyInlineFunctionBodyLoweringPhase = makeJsModulePhase( private val copyInlineFunctionBodyLoweringPhase = makeDeclarationTransformerPhase(
::CopyInlineFunctionBodyLowering, ::CopyInlineFunctionBodyLowering,
name = "CopyInlineFunctionBody", name = "CopyInlineFunctionBody",
description = "Copy inline function body", description = "Copy inline function body",
prerequisite = setOf(functionInliningPhase) prerequisite = setOf(functionInliningPhase)
) )
private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase( private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeDeclarationTransformerPhase(
{ RemoveInlineFunctionsWithReifiedTypeParametersLowering() }, { RemoveInlineFunctionsWithReifiedTypeParametersLowering() },
name = "RemoveInlineFunctionsWithReifiedTypeParametersLowering", name = "RemoveInlineFunctionsWithReifiedTypeParametersLowering",
description = "Remove Inline functions with reified parameters from context", description = "Remove Inline functions with reified parameters from context",
prerequisite = setOf(functionInliningPhase) prerequisite = setOf(functionInliningPhase)
) )
private val throwableSuccessorsLoweringPhase = makeJsModulePhase( private val throwableSuccessorsLoweringPhase = makeBodyLoweringPhase(
::ThrowableLowering, ::ThrowableLowering,
name = "ThrowableLowering", name = "ThrowableLowering",
description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions" 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, ::TailrecLowering,
name = "TailrecLowering", name = "TailrecLowering",
description = "Replace `tailrec` callsites with equivalent loop" description = "Replace `tailrec` callsites with equivalent loop"
) )
private val enumClassConstructorLoweringPhase = makeJsModulePhase( private val enumClassConstructorLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassConstructorLowering, ::EnumClassConstructorLowering,
name = "EnumClassConstructorLowering", name = "EnumClassConstructorLowering",
description = "Transform Enum Class into regular Class" description = "Transform Enum Class into regular Class"
) )
private val enumClassConstructorBodyLoweringPhase = makeJsModulePhase( private val enumClassConstructorBodyLoweringPhase = makeBodyLoweringPhase(
::EnumClassConstructorBodyTransformer, ::EnumClassConstructorBodyTransformer,
name = "EnumClassConstructorBodyLowering", name = "EnumClassConstructorBodyLowering",
description = "Transform Enum Class into regular Class" description = "Transform Enum Class into regular Class"
) )
private val enumEntryInstancesLoweringPhase = makeJsModulePhase( private val enumEntryInstancesLoweringPhase = makeDeclarationTransformerPhase(
::EnumEntryInstancesLowering, ::EnumEntryInstancesLowering,
name = "EnumEntryInstancesLowering", name = "EnumEntryInstancesLowering",
description = "Create instance variable for each enum entry initialized with `null`", description = "Create instance variable for each enum entry initialized with `null`",
prerequisite = setOf(enumClassConstructorLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val enumEntryInstancesBodyLoweringPhase = makeJsModulePhase( private val enumEntryInstancesBodyLoweringPhase = makeBodyLoweringPhase(
::EnumEntryInstancesBodyLowering, ::EnumEntryInstancesBodyLowering,
name = "EnumEntryInstancesBodyLowering", name = "EnumEntryInstancesBodyLowering",
description = "Insert enum entry field initialization into correxposnding class constructors", description = "Insert enum entry field initialization into correxposnding class constructors",
prerequisite = setOf(enumEntryInstancesLoweringPhase) prerequisite = setOf(enumEntryInstancesLoweringPhase)
) )
private val enumClassCreateInitializerLoweringPhase = makeJsModulePhase( private val enumClassCreateInitializerLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassCreateInitializerLowering, ::EnumClassCreateInitializerLowering,
name = "EnumClassCreateInitializerLowering", name = "EnumClassCreateInitializerLowering",
description = "Create initializer for enum entries", description = "Create initializer for enum entries",
prerequisite = setOf(enumClassConstructorLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val enumEntryCreateGetInstancesFunsLoweringPhase = makeJsModulePhase( private val enumEntryCreateGetInstancesFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumEntryCreateGetInstancesFunsLowering, ::EnumEntryCreateGetInstancesFunsLowering,
name = "EnumEntryCreateGetInstancesFunsLowering", name = "EnumEntryCreateGetInstancesFunsLowering",
description = "Create enumEntry_getInstance functions", description = "Create enumEntry_getInstance functions",
prerequisite = setOf(enumClassConstructorLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val enumSyntheticFunsLoweringPhase = makeJsModulePhase( private val enumSyntheticFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumSyntheticFunctionsLowering, ::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering", name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`", description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val enumUsageLoweringPhase = makeJsModulePhase( private val enumUsageLoweringPhase = makeBodyLoweringPhase(
::EnumUsageLowering, ::EnumUsageLowering,
name = "EnumUsageLowering", name = "EnumUsageLowering",
description = "Replace enum access with invocation of corresponding function", description = "Replace enum access with invocation of corresponding function",
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase) prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
) )
private val enumEntryRemovalLoweringPhase = makeJsModulePhase( private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassRemoveEntriesLowering, ::EnumClassRemoveEntriesLowering,
name = "EnumEntryRemovalLowering", name = "EnumEntryRemovalLowering",
description = "Replace enum entry with corresponding class", description = "Replace enum entry with corresponding class",
prerequisite = setOf(enumUsageLoweringPhase) prerequisite = setOf(enumUsageLoweringPhase)
) )
private val sharedVariablesLoweringPhase = makeJsModulePhase( private val sharedVariablesLoweringPhase = makeBodyLoweringPhase(
::SharedVariablesLowering, ::SharedVariablesLowering,
name = "SharedVariablesLowering", name = "SharedVariablesLowering",
description = "Box captured mutable variables" description = "Box captured mutable variables"
) )
private val returnableBlockLoweringPhase = makeJsModulePhase( private val returnableBlockLoweringPhase = makeBodyLoweringPhase(
::ReturnableBlockLowering, ::ReturnableBlockLowering,
name = "ReturnableBlockLowering", name = "ReturnableBlockLowering",
description = "Replace returnable block with do-while loop", description = "Replace returnable block with do-while loop",
prerequisite = setOf(functionInliningPhase) prerequisite = setOf(functionInliningPhase)
) )
private val forLoopsLoweringPhase = makeJsModulePhase( private val forLoopsLoweringPhase = makeBodyLoweringPhase(
::ForLoopsLowering, ::ForLoopsLowering,
name = "ForLoopsLowering", name = "ForLoopsLowering",
description = "[Optimization] For loops lowering" description = "[Optimization] For loops lowering"
) )
private val propertyAccessorInlinerLoweringPhase = makeJsModulePhase( private val propertyAccessorInlinerLoweringPhase = makeBodyLoweringPhase(
::PropertyAccessorInlineLowering, ::PropertyAccessorInlineLowering,
name = "PropertyAccessorInlineLowering", name = "PropertyAccessorInlineLowering",
description = "[Optimization] Inline property accessors" description = "[Optimization] Inline property accessors"
) )
private val foldConstantLoweringPhase = makeJsModulePhase( private val foldConstantLoweringPhase = makeBodyLoweringPhase(
{ FoldConstantLowering(it, true) }, { FoldConstantLowering(it, true) },
name = "FoldConstantLowering", name = "FoldConstantLowering",
description = "[Optimization] Constant Folding", description = "[Optimization] Constant Folding",
prerequisite = setOf(propertyAccessorInlinerLoweringPhase) prerequisite = setOf(propertyAccessorInlinerLoweringPhase)
) )
private val localDelegatedPropertiesLoweringPhase = makeJsModulePhase( private val localDelegatedPropertiesLoweringPhase = makeBodyLoweringPhase(
{ LocalDelegatedPropertiesLowering() }, { LocalDelegatedPropertiesLowering() },
name = "LocalDelegatedPropertiesLowering", name = "LocalDelegatedPropertiesLowering",
description = "Transform Local Delegated properties" description = "Transform Local Delegated properties"
) )
private val localDeclarationsLoweringPhase = makeJsModulePhase( private val localDeclarationsLoweringPhase = makeBodyLoweringPhase(
::LocalDeclarationsLowering, ::LocalDeclarationsLowering,
name = "LocalDeclarationsLowering", name = "LocalDeclarationsLowering",
description = "Move local declarations into nearest declaration container", description = "Move local declarations into nearest declaration container",
prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase) prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase)
) )
private val localClassExtractionPhase = makeJsModulePhase( private val localClassExtractionPhase = makeBodyLoweringPhase(
::LocalClassPopupLowering, ::LocalClassPopupLowering,
name = "LocalClassExtractionPhase", name = "LocalClassExtractionPhase",
description = "Move local declarations into nearest declaration container", description = "Move local declarations into nearest declaration container",
prerequisite = setOf(localDeclarationsLoweringPhase) prerequisite = setOf(localDeclarationsLoweringPhase)
) )
private val innerClassesLoweringPhase = makeJsModulePhase( private val innerClassesLoweringPhase = makeDeclarationTransformerPhase(
::InnerClassesLowering, ::InnerClassesLowering,
name = "InnerClassesLowering", name = "InnerClassesLowering",
description = "Capture outer this reference to inner class" description = "Capture outer this reference to inner class"
) )
private val innerClassesMemberBodyLoweringPhase = makeJsModulePhase( private val innerClassesMemberBodyLoweringPhase = makeBodyLoweringPhase(
::InnerClassesMemberBodyLowering, ::InnerClassesMemberBodyLowering,
name = "InnerClassesMemberBody", name = "InnerClassesMemberBody",
description = "Replace `this` with 'outer this' field references", description = "Replace `this` with 'outer this' field references",
prerequisite = setOf(innerClassesLoweringPhase) prerequisite = setOf(innerClassesLoweringPhase)
) )
private val innerClassConstructorCallsLoweringPhase = makeJsModulePhase( private val innerClassConstructorCallsLoweringPhase = makeBodyLoweringPhase(
::InnerClassConstructorCallsLowering, ::InnerClassConstructorCallsLowering,
name = "InnerClassConstructorCallsLowering", name = "InnerClassConstructorCallsLowering",
description = "Replace inner class constructor invocation" description = "Replace inner class constructor invocation"
) )
private val suspendFunctionsLoweringPhase = makeJsModulePhase( private val suspendFunctionsLoweringPhase = makeBodyLoweringPhase(
::JsSuspendFunctionsLowering, ::JsSuspendFunctionsLowering,
name = "SuspendFunctionsLowering", name = "SuspendFunctionsLowering",
description = "Transform suspend functions into CoroutineImpl instance and build state machine" description = "Transform suspend functions into CoroutineImpl instance and build state machine"
) )
private val suspendLambdasRemovalLoweringPhase = makeJsModulePhase( private val suspendLambdasRemovalLoweringPhase = makeDeclarationTransformerPhase(
{ RemoveSuspendLambdas() }, { RemoveSuspendLambdas() },
name = "RemoveSuspendLambdas", name = "RemoveSuspendLambdas",
description = "Remove suspend lambdas" description = "Remove suspend lambdas"
) )
private val privateMembersLoweringPhase = makeJsModulePhase( private val privateMembersLoweringPhase = makeDeclarationTransformerPhase(
::PrivateMembersLowering, ::PrivateMembersLowering,
name = "PrivateMembersLowering", name = "PrivateMembersLowering",
description = "Extract private members from classes" description = "Extract private members from classes"
) )
private val privateMemberUsagesLoweringPhase = makeJsModulePhase( private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase(
::PrivateMemberBodiesLowering, ::PrivateMemberBodiesLowering,
name = "PrivateMemberUsagesLowering", name = "PrivateMemberUsagesLowering",
description = "Rewrite the private member usages" description = "Rewrite the private member usages"
) )
private val callableReferenceLoweringPhase = makeJsModulePhase( private val callableReferenceLoweringPhase = makeBodyLoweringPhase(
::CallableReferenceLowering, ::CallableReferenceLowering,
name = "CallableReferenceLowering", name = "CallableReferenceLowering",
description = "Handle callable references", description = "Handle callable references",
@@ -312,163 +382,163 @@ private val callableReferenceLoweringPhase = makeJsModulePhase(
) )
) )
private val defaultArgumentStubGeneratorPhase = makeJsModulePhase( private val defaultArgumentStubGeneratorPhase = makeDeclarationTransformerPhase(
::JsDefaultArgumentStubGenerator, ::JsDefaultArgumentStubGenerator,
name = "DefaultArgumentStubGenerator", name = "DefaultArgumentStubGenerator",
description = "Generate synthetic stubs for functions with default parameter values" description = "Generate synthetic stubs for functions with default parameter values"
) )
private val defaultArgumentPatchOverridesPhase = makeJsModulePhase( private val defaultArgumentPatchOverridesPhase = makeDeclarationTransformerPhase(
::DefaultParameterPatchOverridenSymbolsLowering, ::DefaultParameterPatchOverridenSymbolsLowering,
name = "DefaultArgumentsPatchOverrides", name = "DefaultArgumentsPatchOverrides",
description = "Patch overrides for fake override dispatch functions", description = "Patch overrides for fake override dispatch functions",
prerequisite = setOf(defaultArgumentStubGeneratorPhase) prerequisite = setOf(defaultArgumentStubGeneratorPhase)
) )
private val defaultParameterInjectorPhase = makeJsModulePhase( private val defaultParameterInjectorPhase = makeBodyLoweringPhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) }, { context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) },
name = "DefaultParameterInjector", name = "DefaultParameterInjector",
description = "Replace callsite with default parameters with corresponding stub function", description = "Replace callsite with default parameters with corresponding stub function",
prerequisite = setOf(callableReferenceLoweringPhase, innerClassesLoweringPhase) prerequisite = setOf(callableReferenceLoweringPhase, innerClassesLoweringPhase)
) )
private val defaultParameterCleanerPhase = makeJsModulePhase( private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase(
::DefaultParameterCleaner, ::DefaultParameterCleaner,
name = "DefaultParameterCleaner", name = "DefaultParameterCleaner",
description = "Clean default parameters up" description = "Clean default parameters up"
) )
private val jsDefaultCallbackGeneratorPhase = makeJsModulePhase( private val jsDefaultCallbackGeneratorPhase = makeBodyLoweringPhase(
::JsDefaultCallbackGenerator, ::JsDefaultCallbackGenerator,
name = "JsDefaultCallbackGenerator", name = "JsDefaultCallbackGenerator",
description = "Build binding for super calls with default parameters" description = "Build binding for super calls with default parameters"
) )
private val varargLoweringPhase = makeJsModulePhase( private val varargLoweringPhase = makeBodyLoweringPhase(
::VarargLowering, ::VarargLowering,
name = "VarargLowering", name = "VarargLowering",
description = "Lower vararg arguments", description = "Lower vararg arguments",
prerequisite = setOf(callableReferenceLoweringPhase) prerequisite = setOf(callableReferenceLoweringPhase)
) )
private val propertiesLoweringPhase = makeJsModulePhase( private val propertiesLoweringPhase = makeDeclarationTransformerPhase(
{ PropertiesLowering() }, { PropertiesLowering() },
name = "PropertiesLowering", name = "PropertiesLowering",
description = "Move fields and accessors out from its property" description = "Move fields and accessors out from its property"
) )
private val primaryConstructorLoweringPhase = makeJsModulePhase( private val primaryConstructorLoweringPhase = makeDeclarationTransformerPhase(
::PrimaryConstructorLowering, ::PrimaryConstructorLowering,
name = "PrimaryConstructorLowering", name = "PrimaryConstructorLowering",
description = "Creates primary constructor if it doesn't exist", description = "Creates primary constructor if it doesn't exist",
prerequisite = setOf(enumClassConstructorLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val delegateToPrimaryConstructorLoweringPhase = makeJsModulePhase( private val delegateToPrimaryConstructorLoweringPhase = makeBodyLoweringPhase(
::DelegateToSyntheticPrimaryConstructor, ::DelegateToSyntheticPrimaryConstructor,
name = "DelegateToSyntheticPrimaryConstructor", name = "DelegateToSyntheticPrimaryConstructor",
description = "Delegates to synthetic primary constructor", description = "Delegates to synthetic primary constructor",
prerequisite = setOf(primaryConstructorLoweringPhase) prerequisite = setOf(primaryConstructorLoweringPhase)
) )
private val annotationConstructorLowering = makeJsModulePhase( private val annotationConstructorLowering = makeDeclarationTransformerPhase(
::AnnotationConstructorLowering, ::AnnotationConstructorLowering,
name = "AnnotationConstructorLowering", name = "AnnotationConstructorLowering",
description = "Generate annotation constructor body" description = "Generate annotation constructor body"
) )
private val initializersLoweringPhase = makeJsModulePhase( private val initializersLoweringPhase = makeBodyLoweringPhase(
::InitializersLowering, ::InitializersLowering,
name = "InitializersLowering", name = "InitializersLowering",
description = "Merge init block and field initializers into [primary] constructor", description = "Merge init block and field initializers into [primary] constructor",
prerequisite = setOf(enumClassConstructorLoweringPhase, primaryConstructorLoweringPhase, annotationConstructorLowering) prerequisite = setOf(enumClassConstructorLoweringPhase, primaryConstructorLoweringPhase, annotationConstructorLowering)
) )
private val initializersCleanupLoweringPhase = makeJsModulePhase( private val initializersCleanupLoweringPhase = makeDeclarationTransformerPhase(
::InitializersCleanupLowering, ::InitializersCleanupLowering,
name = "InitializersCleanupLowering", name = "InitializersCleanupLowering",
description = "Remove non-static anonymous initializers and field init expressions", description = "Remove non-static anonymous initializers and field init expressions",
prerequisite = setOf(initializersLoweringPhase) prerequisite = setOf(initializersLoweringPhase)
) )
private val multipleCatchesLoweringPhase = makeJsModulePhase( private val multipleCatchesLoweringPhase = makeBodyLoweringPhase(
::MultipleCatchesLowering, ::MultipleCatchesLowering,
name = "MultipleCatchesLowering", name = "MultipleCatchesLowering",
description = "Replace multiple catches with single one" description = "Replace multiple catches with single one"
) )
private val bridgesConstructionPhase = makeJsModulePhase( private val bridgesConstructionPhase = makeDeclarationTransformerPhase(
::BridgesConstruction, ::BridgesConstruction,
name = "BridgesConstruction", name = "BridgesConstruction",
description = "Generate bridges", description = "Generate bridges",
prerequisite = setOf(suspendFunctionsLoweringPhase) prerequisite = setOf(suspendFunctionsLoweringPhase)
) )
private val typeOperatorLoweringPhase = makeJsModulePhase( private val typeOperatorLoweringPhase = makeBodyLoweringPhase(
::TypeOperatorLowering, ::TypeOperatorLowering,
name = "TypeOperatorLowering", name = "TypeOperatorLowering",
description = "Lower IrTypeOperator with corresponding logic", description = "Lower IrTypeOperator with corresponding logic",
prerequisite = setOf(bridgesConstructionPhase, removeInlineFunctionsWithReifiedTypeParametersLoweringPhase) prerequisite = setOf(bridgesConstructionPhase, removeInlineFunctionsWithReifiedTypeParametersLoweringPhase)
) )
private val secondaryConstructorLoweringPhase = makeJsModulePhase( private val secondaryConstructorLoweringPhase = makeDeclarationTransformerPhase(
::SecondaryConstructorLowering, ::SecondaryConstructorLowering,
name = "SecondaryConstructorLoweringPhase", name = "SecondaryConstructorLoweringPhase",
description = "Generate static functions for each secondary constructor", description = "Generate static functions for each secondary constructor",
prerequisite = setOf(innerClassesLoweringPhase) prerequisite = setOf(innerClassesLoweringPhase)
) )
private val secondaryFactoryInjectorLoweringPhase = makeJsModulePhase( private val secondaryFactoryInjectorLoweringPhase = makeBodyLoweringPhase(
::SecondaryFactoryInjectorLowering, ::SecondaryFactoryInjectorLowering,
name = "SecondaryFactoryInjectorLoweringPhase", name = "SecondaryFactoryInjectorLoweringPhase",
description = "Replace usage of secondary constructor with corresponding static function", description = "Replace usage of secondary constructor with corresponding static function",
prerequisite = setOf(innerClassesLoweringPhase) prerequisite = setOf(innerClassesLoweringPhase)
) )
private val inlineClassDeclarationLoweringPhase = makeJsModulePhase( private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase(
{ InlineClassLowering(it).inlineClassDeclarationLowering }, { InlineClassLowering(it).inlineClassDeclarationLowering },
name = "InlineClassDeclarationLowering", name = "InlineClassDeclarationLowering",
description = "Handle inline class declarations" description = "Handle inline class declarations"
) )
private val inlineClassUsageLoweringPhase = makeJsModulePhase( private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase(
{ InlineClassLowering(it).inlineClassUsageLowering }, { InlineClassLowering(it).inlineClassUsageLowering },
name = "InlineClassUsageLowering", name = "InlineClassUsageLowering",
description = "Handle inline class usages" description = "Handle inline class usages"
) )
private val autoboxingTransformerPhase = makeJsModulePhase( private val autoboxingTransformerPhase = makeBodyLoweringPhase(
::AutoboxingTransformer, ::AutoboxingTransformer,
name = "AutoboxingTransformer", name = "AutoboxingTransformer",
description = "Insert box/unbox intrinsics" description = "Insert box/unbox intrinsics"
) )
private val blockDecomposerLoweringPhase = makeJsModulePhase( private val blockDecomposerLoweringPhase = makeBodyLoweringPhase(
::JsBlockDecomposerLowering, ::JsBlockDecomposerLowering,
name = "BlockDecomposerLowering", name = "BlockDecomposerLowering",
description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS", description = "Transform statement-like-expression nodes into pure-statement to make it easily transform into JS",
prerequisite = setOf(typeOperatorLoweringPhase, suspendFunctionsLoweringPhase) prerequisite = setOf(typeOperatorLoweringPhase, suspendFunctionsLoweringPhase)
) )
private val classReferenceLoweringPhase = makeJsModulePhase( private val classReferenceLoweringPhase = makeBodyLoweringPhase(
::ClassReferenceLowering, ::ClassReferenceLowering,
name = "ClassReferenceLowering", name = "ClassReferenceLowering",
description = "Handle class references" description = "Handle class references"
) )
private val primitiveCompanionLoweringPhase = makeJsModulePhase( private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase(
::PrimitiveCompanionLowering, ::PrimitiveCompanionLowering,
name = "PrimitiveCompanionLowering", name = "PrimitiveCompanionLowering",
description = "Replace common companion object access with platform one" description = "Replace common companion object access with platform one"
) )
private val constLoweringPhase = makeJsModulePhase( private val constLoweringPhase = makeBodyLoweringPhase(
::ConstLowering, ::ConstLowering,
name = "ConstLowering", name = "ConstLowering",
description = "Wrap Long and Char constants into constructor invocation" description = "Wrap Long and Char constants into constructor invocation"
) )
private val callsLoweringPhase = makeJsModulePhase( private val callsLoweringPhase = makeBodyLoweringPhase(
::CallsLowering, ::CallsLowering,
name = "CallsLowering", name = "CallsLowering",
description = "Handle intrinsics" description = "Handle intrinsics"
@@ -478,27 +548,27 @@ private val testGenerationPhase = makeJsModulePhase(
::TestGenerator, ::TestGenerator,
name = "TestGenerationLowering", name = "TestGenerationLowering",
description = "Generate invocations to kotlin.test suite and test functions" description = "Generate invocations to kotlin.test suite and test functions"
) ).toModuleLowering()
private val staticMembersLoweringPhase = makeJsModulePhase( private val staticMembersLoweringPhase = makeDeclarationTransformerPhase(
::StaticMembersLowering, ::StaticMembersLowering,
name = "StaticMembersLowering", name = "StaticMembersLowering",
description = "Move static member declarations to top-level" description = "Move static member declarations to top-level"
) )
private val objectDeclarationLoweringPhase = makeJsModulePhase( private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase(
::ObjectDeclarationLowering, ::ObjectDeclarationLowering,
name = "ObjectDeclarationLowering", name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions" description = "Create lazy object instance generator functions"
) )
private val objectUsageLoweringPhase = makeJsModulePhase( private val objectUsageLoweringPhase = makeBodyLoweringPhase(
::ObjectUsageLowering, ::ObjectUsageLowering,
name = "ObjectUsageLowering", name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call" description = "Transform IrGetObjectValue into instance generator call"
) )
val phaseList: List<CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>> = listOf( val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering, scriptRemoveReceiverLowering,
validateIrBeforeLowering, validateIrBeforeLowering,
testGenerationPhase, testGenerationPhase,
@@ -571,9 +641,12 @@ val phaseList: List<CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModule
validateIrAfterLowering validateIrAfterLowering
) )
val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase
val jsPhases = namedIrModulePhase( val jsPhases = namedIrModulePhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = phaseList.drop(1).fold(phaseList[0]) { acc: CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>, phase -> lower = loweringList.drop(1).fold(loweringList[0].modulePhase) { acc: CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>, lowering ->
acc.then(phase) acc.then(lowering.modulePhase)
}) }
)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.DefaultMapping import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
class JsMapping : DefaultMapping() { class JsMapping : DefaultMapping() {
@@ -27,4 +28,23 @@ class JsMapping : DefaultMapping() {
val enumConstructorOldToNewValueParameters = newMapping<IrValueDeclaration, IrValueParameter>() val enumConstructorOldToNewValueParameters = newMapping<IrValueDeclaration, IrValueParameter>()
val enumEntryToCorrespondingField = newMapping<IrEnumEntry, IrField>() val enumEntryToCorrespondingField = newMapping<IrEnumEntry, IrField>()
val enumClassToInitEntryInstancesFun = newMapping<IrClass, IrSimpleFunction>() val enumClassToInitEntryInstancesFun = newMapping<IrClass, IrSimpleFunction>()
// Triggers `StageController.lazyLower` on access
override fun <K : IrDeclaration, V> newMapping(): Mapping.Delegate<K, V> = object : Mapping.Delegate<K, V>() {
private val map: MutableMap<K, V> = 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
}
}
}
} }
@@ -8,11 +8,124 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* 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<Lowering>) : 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 <T> withStage(stage: Int, fn: () -> T): T { override fun <T> withStage(stage: Int, fn: () -> T): T {
val prevStage = currentStage val prevStage = currentStage
@@ -24,27 +137,45 @@ open class MutableController(override var currentStage: Int = 0) : StageControll
} }
} }
override fun <T> withInitialIr(block: () -> T): T = restrictionImpl(null) { withStage(0, block) } override fun <T> withInitialIr(block: () -> T): T = { withStage(0, block) }.withRestrictions(newRestrictedToDeclaration = null)
override fun <T> withInitialStateOf(declaration: IrDeclaration, block: () -> T): T = withStage((declaration as? IrPersistingElementBase<*>)?.createdOn ?: 0, block) override fun <T> restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn.withRestrictions(newRestrictedToDeclaration = declaration)
private var restricted: Boolean = false override fun <T> bodyLowering(fn: () -> T): T = fn.withRestrictions(newBodiesEnabled = true, newRestricted = true, newDeclarationListsRestricted = true)
override fun <T> 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 private var restrictedToDeclaration: IrDeclaration? = null
// TODO flags?
override var bodiesEnabled: Boolean = true
private var restricted: Boolean = false
private var declarationListsRestricted = false
override fun <T> restrictTo(declaration: IrDeclaration, fn: () -> T): T = restrictionImpl(declaration, fn) private inline fun <T> (() -> T).withRestrictions(
newRestrictedToDeclaration: IrDeclaration? = null,
private fun <T> restrictionImpl(declaration: IrDeclaration?, fn: () -> T): T { newBodiesEnabled: Boolean? = null,
newRestricted: Boolean? = null,
newDeclarationListsRestricted: Boolean? = null
): T {
val prev = restrictedToDeclaration val prev = restrictedToDeclaration
restrictedToDeclaration = declaration restrictedToDeclaration = newRestrictedToDeclaration
val wereBodiesEnabled = bodiesEnabled val wereBodiesEnabled = bodiesEnabled
bodiesEnabled = false bodiesEnabled = newBodiesEnabled ?: bodiesEnabled
val wasRestricted = restricted val wasRestricted = restricted
restricted = true restricted = newRestricted ?: restricted
val wereDeclarationListsRestricted = declarationListsRestricted val wereDeclarationListsRestricted = declarationListsRestricted
declarationListsRestricted = true declarationListsRestricted = newDeclarationListsRestricted ?: declarationListsRestricted
try { try {
return fn() return this.invoke()
} finally { } finally {
restrictedToDeclaration = prev restrictedToDeclaration = prev
bodiesEnabled = wereBodiesEnabled bodiesEnabled = wereBodiesEnabled
@@ -52,40 +183,4 @@ open class MutableController(override var currentStage: Int = 0) : StageControll
declarationListsRestricted = wereDeclarationListsRestricted declarationListsRestricted = wereDeclarationListsRestricted
} }
} }
override fun <T> 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 <T> 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*/
}
} }
@@ -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.PhaserState
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.config.CompilerConfiguration 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.lower.moveBodilessDeclarationsToSeparatePlace
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
@@ -27,16 +27,6 @@ import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult import org.jetbrains.kotlin.library.resolver.KotlinLibraryResolveResult
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.DFS
fun sortDependencies(dependencies: Collection<IrModuleFragment>): Collection<IrModuleFragment> {
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( class CompilerResult(
val jsCode: String?, val jsCode: String?,
@@ -55,7 +45,8 @@ fun compile(
mainArguments: List<String>?, mainArguments: List<String>?,
exportedDeclarations: Set<FqName> = emptySet(), exportedDeclarations: Set<FqName> = emptySet(),
generateFullJs: Boolean = true, generateFullJs: Boolean = true,
generateDceJs: Boolean = false generateDceJs: Boolean = false,
dceDriven: Boolean = false
): CompilerResult { ): CompilerResult {
stageController = object : StageController {} stageController = object : StageController {}
@@ -93,17 +84,30 @@ fun compile(
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment) moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
val controller = MutableController() if (dceDriven) {
stageController = controller
val phaserState = PhaserState<IrModuleFragment>() // TODO we should only generate tests for the current module
phaseList.forEachIndexed { index, phase -> // TODO should be done incrementally
controller.currentStage = index + 1 generateTests(context, moduleFragment)
phase.invoke(phaseConfig, phaserState, 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( fun generateJsCode(
@@ -27,12 +27,6 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.name.Name 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") private object SCRIPT_FUNCTION : IrDeclarationOriginImpl("SCRIPT_FUNCTION")
class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweringPass { class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweringPass {
@@ -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.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.ir.addChild import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor 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.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule import org.jetbrains.kotlin.ir.backend.js.utils.getJsModule
import org.jetbrains.kotlin.ir.backend.js.utils.getJsQualifier 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.UniqId
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal 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 import org.jetbrains.kotlin.name.FqName
private val BODILESS_BUILTIN_CLASSES = listOf( private val BODILESS_BUILTIN_CLASSES = listOf(
@@ -89,12 +92,16 @@ class MoveBodilessDeclarationsToSeparatePlaceLowering(private val context: JsIrB
context.packageLevelJsModules += externalPackageFragment context.packageLevelJsModules += externalPackageFragment
declaration.collectAllExternalDeclarations()
return emptyList() return emptyList()
} else { } else {
val d = declaration as? IrDeclarationWithName ?: return null val d = declaration as? IrDeclarationWithName ?: return null
if (isBuiltInClass(d)) { if (isBuiltInClass(d)) {
context.bodilessBuiltInsPackageFragment.addChild(d) context.bodilessBuiltInsPackageFragment.addChild(d)
d.collectAllExternalDeclarations()
return emptyList() return emptyList()
} else if (d.isEffectivelyExternal()) { } else if (d.isEffectivelyExternal()) {
if (d.getJsModule() != null) if (d.getJsModule() != null)
@@ -103,10 +110,25 @@ class MoveBodilessDeclarationsToSeparatePlaceLowering(private val context: JsIrB
externalPackageFragment.declarations += d externalPackageFragment.declarations += d
d.parent = externalPackageFragment d.parent = externalPackageFragment
d.collectAllExternalDeclarations()
return emptyList() return emptyList()
} }
return null 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)
}
} }
@@ -57,7 +57,9 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : BodyLowering
p.setter?.let { if (it.name == function.name) return it } p.setter?.let { if (it.name == function.name) return it }
} }
error("Accessor not found") return actualCompanion.declarations
.filterIsInstance<IrSimpleFunction>()
.single { it.name == function.name }
} }
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
@@ -22,13 +22,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
val scriptRemoveReceiverLowering = makeIrModulePhase( class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass {
::ScriptRemoveReceiverLowering,
name = "ScriptRemoveReceiver",
description = "Remove receivers for declarations in script"
)
private class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
if (context.scriptMode) { if (context.scriptMode) {
irFile.declarations.transformFlat { irFile.declarations.transformFlat {
@@ -22,6 +22,14 @@ import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.name.FqName 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 { class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
@@ -15,7 +15,7 @@ class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsBlock, JsGenera
val fileContext = data.newDeclaration() val fileContext = data.newDeclaration()
val block = JsGlobalBlock() val block = JsGlobalBlock()
declaration.declarations.forEach { ArrayList(declaration.declarations).forEach {
block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext)) block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext))
} }
@@ -50,17 +50,21 @@ class IrModuleToJsTransformer(
namer.merge(module.files, additionalPackages) namer.merge(module.files, additionalPackages)
val jsCode = if (fullJs) generateWrappedModuleBody(module, exportedModule) else null val jsCode = if (fullJs) generateWrappedModuleBody(module, exportedModule, namer) else null
val dceJsCode = if (dceJs) { val dceJsCode = if (dceJs) {
eliminateDeadDeclarations(module, backendContext, mainFunction) eliminateDeadDeclarations(module, backendContext, mainFunction)
generateWrappedModuleBody(module, exportedModule) // Use a fresh namer for DCE so that we could compare the result with DCE-driven
// TODO: is this mode relevant for scripting? If yes, refactor so that the external name tables are used here when needed.
val namer = NameTables(emptyList())
namer.merge(module.files, additionalPackages)
generateWrappedModuleBody(module, exportedModule, namer)
} else null } else null
return CompilerResult(jsCode, dceJsCode, dts) return CompilerResult(jsCode, dceJsCode, dts)
} }
private fun generateWrappedModuleBody(module: IrModuleFragment, exportedModule: ExportedModule): String { private fun generateWrappedModuleBody(module: IrModuleFragment, exportedModule: ExportedModule, namer: NameTables): String {
val program = JsProgram() val program = JsProgram()
val nameGenerator = IrNamerImpl( val nameGenerator = IrNamerImpl(
@@ -199,7 +199,8 @@ class NameTables(
} }
for (p in packages) { for (p in packages) {
for (declaration in p.declarations) { // TODO
for (declaration in ArrayList(p.declarations)) {
acceptDeclaration(declaration) acceptDeclaration(declaration)
} }
} }
@@ -332,6 +333,10 @@ class NameTables(
val signature = fieldSignature(field) val signature = fieldSignature(field)
val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)] val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)]
if (name == null) {
return sanitizeName(field.name.asString()) + "__error"
}
require(name != null) { require(name != null) {
"Can't find name for member field ${field.render()}" "Can't find name for member field ${field.render()}"
} }
@@ -346,6 +351,11 @@ class NameTables(
// of `invoke` functions in FunctionN interfaces // of `invoke` functions in FunctionN interfaces
if (name == null && signature is ParameterTypeBasedSignature && signature.suggestedName.startsWith("invoke")) if (name == null && signature is ParameterTypeBasedSignature && signature.suggestedName.startsWith("invoke"))
return signature.suggestedName return signature.suggestedName
if (name == null) {
return sanitizeName(function.name.asString()) + "__error" // TODO one case is a virtual method of an abstract class with no implementation
}
require(name != null) { require(name != null) {
"Can't find name for member function ${function.render()}" "Can't find name for member function ${function.render()}"
} }
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.ir.builders.irNull
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
@@ -60,6 +59,9 @@ class JvmBackendContext(
) : CommonBackendContext { ) : CommonBackendContext {
override val transformedFunction: MutableMap<IrFunctionSymbol, IrSimpleFunctionSymbol> override val transformedFunction: MutableMap<IrFunctionSymbol, IrSimpleFunctionSymbol>
get() = TODO("not implemented") get() = TODO("not implemented")
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
override val scriptMode: Boolean = false override val scriptMode: Boolean = false
override val lateinitNullableFields = mutableMapOf<IrField, IrField>() override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol 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.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol 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.symbols.impl.IrExternalPackageFragmentSymbolImpl
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.UniqId import org.jetbrains.kotlin.ir.util.UniqId
@@ -41,6 +41,7 @@ class WasmBackendContext(
override val scriptMode = false override val scriptMode = false
override val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>() override val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>()
override val lateinitNullableFields = mutableMapOf<IrField, IrField>() override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
// Place to store declarations excluded from code generation // Place to store declarations excluded from code generation
val excludedDeclarations: IrPackageFragment by lazy { val excludedDeclarations: IrPackageFragment by lazy {
@@ -25,8 +25,6 @@ interface StageController {
fun <T> withInitialIr(block: () -> T): T = block() fun <T> withInitialIr(block: () -> T): T = block()
fun <T> withInitialStateOf(declaration: IrDeclaration, block: () -> T): T = block()
fun <T> restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn() fun <T> restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn()
fun <T> bodyLowering(fn: () -> T): T = fn() fun <T> bodyLowering(fn: () -> T): T = fn()
@@ -38,6 +36,7 @@ interface StageController {
fun canAccessDeclarationsOf(irClass: IrClass): Boolean = true fun canAccessDeclarationsOf(irClass: IrClass): Boolean = true
} }
@Suppress("NOTHING_TO_INLINE")
inline fun <T> withInitialIr(noinline fn: () -> T): T { inline fun <T> withInitialIr(noinline fn: () -> T): T {
return stageController.withInitialIr(fn) return stageController.withInitialIr(fn)
} }
+1
View File
@@ -6,6 +6,7 @@ where advanced options include:
-Xgenerate-dts Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only. -Xgenerate-dts Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only.
-Xinclude=<path> A path to an intermediate library that should be processed in the same manner as source files. -Xinclude=<path> 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 Perform experimental dead code elimination
-Xir-dce-driven Perform a more experimental faster dead code elimination
-Xir-only Disables pre-IR backend -Xir-only Disables pre-IR backend
-Xir-produce-js Generates JS file using IR backend. Also 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. -Xir-produce-klib-dir Generate unpacked KLIB into parent directory of output JS file.
+6
View File
@@ -133,6 +133,12 @@ projectTest("jsTest", true) {
} }
projectTest("jsIrTest", 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) setUpJsBoxTests(jsEnabled = false, jsIrEnabled = true)
} }
@@ -81,13 +81,17 @@ abstract class BasicBoxTest(
private val testGroupOutputDirForCompilation = File(pathToRootOutputDir + "out/" + testGroupOutputDirPrefix) private val testGroupOutputDirForCompilation = File(pathToRootOutputDir + "out/" + testGroupOutputDirPrefix)
private val testGroupOutputDirForMinification = File(pathToRootOutputDir + "out-min/" + 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 getOutputPrefixFile(testFilePath: String): File? = null
protected open fun getOutputPostfixFile(testFilePath: String): File? = null protected open fun getOutputPostfixFile(testFilePath: String): File? = null
protected open val runMinifierByDefault: Boolean = false protected open val runMinifierByDefault: Boolean = false
protected open val skipMinification = getBoolean("kotlin.js.skipMinificationTest") protected open val skipMinification = getBoolean("kotlin.js.skipMinificationTest")
protected open val skipRegularMode: Boolean = false
protected open val runIrDce: Boolean = false protected open val runIrDce: Boolean = false
protected open val runIrPir: Boolean = false
protected open val incrementalCompilationChecksEnabled = true protected open val incrementalCompilationChecksEnabled = true
@@ -105,6 +109,7 @@ abstract class BasicBoxTest(
val file = File(filePath) val file = File(filePath)
val outputDir = getOutputDir(file) val outputDir = getOutputDir(file)
val dceOutputDir = getOutputDir(file, testGroupOutputDirForMinification) val dceOutputDir = getOutputDir(file, testGroupOutputDirForMinification)
val pirOutputDir = getOutputDir(file, testGroupOutputDirForPir)
var fileContent = KotlinTestUtils.doLoadFile(file) var fileContent = KotlinTestUtils.doLoadFile(file)
if (coroutinesPackage.isNotEmpty()) { if (coroutinesPackage.isNotEmpty()) {
fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage) fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage)
@@ -158,10 +163,11 @@ abstract class BasicBoxTest(
val outputFileName = module.outputFileName(outputDir) + ".js" val outputFileName = module.outputFileName(outputDir) + ".js"
val dceOutputFileName = module.outputFileName(dceOutputDir) + ".js" val dceOutputFileName = module.outputFileName(dceOutputDir) + ".js"
val pirOutputFileName = module.outputFileName(pirOutputDir) + ".js"
val isMainModule = mainModuleName == module.name val isMainModule = mainModuleName == module.name
generateJavaScriptFile( generateJavaScriptFile(
testFactory.tmpDir, 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, !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile,
actualMainCallParameters, testPackage, testFunction, needsFullIrRuntime, isMainModule, klibBasedMpp 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) } + val dceAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first.replace(outputDir.absolutePath, dceOutputDir.absolutePath) } +
globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles 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) val dontRunGeneratedCode = InTextDirectivesUtils.dontRunGeneratedCode(targetBackend, file)
if (!dontRunGeneratedCode && generateNodeJsRunner && !SKIP_NODE_JS.matcher(fileContent).find()) { if (!dontRunGeneratedCode && generateNodeJsRunner && !SKIP_NODE_JS.matcher(fileContent).find()) {
@@ -229,10 +239,16 @@ abstract class BasicBoxTest(
} }
if (!dontRunGeneratedCode) { if (!dontRunGeneratedCode) {
runGeneratedCode(allJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) if (!skipRegularMode) {
runGeneratedCode(allJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem)
if (runIrDce) { if (runIrDce) {
runGeneratedCode(dceAllJsFiles, testModuleName, testPackage, testFunction, expectedResult, withModuleSystem) 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, module: TestModule,
outputFileName: String, outputFileName: String,
dceOutputFileName: String, dceOutputFileName: String,
pirOutputFileName: String,
dependencies: List<String>, dependencies: List<String>,
allDependencies: List<String>, allDependencies: List<String>,
friends: List<String>, friends: List<String>,
@@ -383,10 +400,11 @@ abstract class BasicBoxTest(
val config = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, tmpDir, incrementalData = null, klibBasedMpp = klibBasedMpp) val config = createConfig(sourceDirs, module, dependencies, allDependencies, friends, multiModule, tmpDir, incrementalData = null, klibBasedMpp = klibBasedMpp)
val outputFile = File(outputFileName) val outputFile = File(outputFileName)
val dceOutputFile = File(dceOutputFileName) val dceOutputFile = File(dceOutputFileName)
val pirOutputFile = File(pirOutputFileName)
val incrementalData = IncrementalData() val incrementalData = IncrementalData()
translateFiles( 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 mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule
) )
@@ -438,7 +456,7 @@ abstract class BasicBoxTest(
val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js") val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js")
translateFiles( translateFiles(
translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, translationUnits, recompiledOutputFile, recompiledOutputFile, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false
) )
@@ -501,6 +519,7 @@ abstract class BasicBoxTest(
units: List<TranslationUnit>, units: List<TranslationUnit>,
outputFile: File, outputFile: File,
dceOutputFile: File, dceOutputFile: File,
pirOutputFile: File,
config: JsConfig, config: JsConfig,
outputPrefixFile: File?, outputPrefixFile: File?,
outputPostfixFile: File?, outputPostfixFile: File?,
@@ -895,6 +914,7 @@ abstract class BasicBoxTest(
private val CALL_MAIN_PATTERN = Pattern.compile("^// *CALL_MAIN *$", Pattern.MULTILINE) 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 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 KLIB_BASED_MPP = Pattern.compile("^// KLIB_BASED_MPP *$", Pattern.MULTILINE)
private val SKIP_DCE_DRIVEN = Pattern.compile("^// *SKIP_DCE_DRIVEN *$", Pattern.MULTILINE)
@JvmStatic @JvmStatic
protected val runTestInNashorn = getBoolean("kotlin.js.useNashorn") protected val runTestInNashorn = getBoolean("kotlin.js.useNashorn")
@@ -44,7 +44,13 @@ abstract class BasicIrBoxTest(
override val skipMinification = true 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 // TODO Design incremental compilation for IR and add test support
override val incrementalCompilationChecksEnabled = false override val incrementalCompilationChecksEnabled = false
@@ -63,6 +69,7 @@ abstract class BasicIrBoxTest(
units: List<TranslationUnit>, units: List<TranslationUnit>,
outputFile: File, outputFile: File,
dceOutputFile: File, dceOutputFile: File,
pirOutputFile: File,
config: JsConfig, config: JsConfig,
outputPrefixFile: File?, outputPrefixFile: File?,
outputPostfixFile: File?, outputPostfixFile: File?,
@@ -111,33 +118,54 @@ abstract class BasicIrBoxTest(
PhaseConfig(jsPhases) PhaseConfig(jsPhases)
} }
val compiledModule = compile( if (!skipRegularMode) {
project = config.project, val compiledModule = compile(
mainModule = MainModule.SourceFiles(filesToCompile), project = config.project,
analyzer = AnalyzerWithCompilerReport(config.configuration), mainModule = MainModule.SourceFiles(filesToCompile),
configuration = config.configuration, analyzer = AnalyzerWithCompilerReport(config.configuration),
phaseConfig = phaseConfig, configuration = config.configuration,
allDependencies = resolvedLibraries, phaseConfig = phaseConfig,
friendDependencies = emptyList(), allDependencies = resolvedLibraries,
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }, friendDependencies = emptyList(),
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))), mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null },
generateFullJs = true, exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction))),
generateDceJs = runIrDce generateFullJs = true,
) generateDceJs = runIrDce
)
val wrappedCode = wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind) val wrappedCode =
outputFile.write(wrappedCode) wrapWithModuleEmulationMarkers(compiledModule.jsCode!!, moduleId = config.moduleId, moduleKind = config.moduleKind)
outputFile.write(wrappedCode)
compiledModule.dceJsCode?.let { dceJsCode -> compiledModule.dceJsCode?.let { dceJsCode ->
val dceWrappedCode = wrapWithModuleEmulationMarkers(dceJsCode, moduleId = config.moduleId, moduleKind = config.moduleKind) val dceWrappedCode =
dceOutputFile.write(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) { if (runIrPir) {
val dtsFile = outputFile.withReplacedExtensionOrNull("_v5.js", ".d.ts") compile(
dtsFile?.write(compiledModule.tsDefinitions ?: error("No ts definitions")) 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 { } else {
generateKLib( generateKLib(
project = config.project, project = config.project,
@@ -1,4 +1,5 @@
// TARGET_BACKEND: JS_IR // TARGET_BACKEND: JS_IR
// SKIP_DCE_DRIVEN
// CHECK_TYPESCRIPT_DECLARATIONS // CHECK_TYPESCRIPT_DECLARATIONS
// RUN_PLAIN_BOX_FUNCTION // RUN_PLAIN_BOX_FUNCTION