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:
@@ -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
|
||||
|
||||
+3
@@ -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)
|
||||
|
||||
|
||||
@@ -228,13 +228,14 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
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")!!
|
||||
|
||||
@@ -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<IrFunctionSymbol, IrSimpleFunctionSymbol>
|
||||
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 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()
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPa
|
||||
|
||||
for ((local, newContainer) in extractedLocalClasses) {
|
||||
newContainer.addChild(local)
|
||||
context.extractedLocalClasses += local
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IrDeclaration> {
|
||||
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<IrDeclaration>, context: JsIrBackendConte
|
||||
val constructedClasses = hashSetOf<IrClass>()
|
||||
|
||||
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<IrFunction>().single { it.name.asString() == "toString" }
|
||||
@@ -141,6 +143,8 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, 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<IrDeclaration>, 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<IrDeclaration>, 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<IrDeclaration>, 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ class JsIrBackendContext(
|
||||
override val lateinitNullableFields
|
||||
get() = error("Use Mapping.lateInitFieldToNullableField instead")
|
||||
|
||||
override val extractedLocalClasses: MutableSet<IrClass> = 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<IrFileSymbol, IrFile>()
|
||||
val externalDeclarations = hashSetOf<IrDeclaration>()
|
||||
val bodilessBuiltInsPackageFragment: IrPackageFragment = run {
|
||||
|
||||
class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol {
|
||||
|
||||
+144
-71
@@ -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(
|
||||
{ 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<CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>> = listOf(
|
||||
val loweringList = listOf<Lowering>(
|
||||
scriptRemoveReceiverLowering,
|
||||
validateIrBeforeLowering,
|
||||
testGenerationPhase,
|
||||
@@ -571,9 +641,12 @@ val phaseList: List<CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModule
|
||||
validateIrAfterLowering
|
||||
)
|
||||
|
||||
val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase
|
||||
|
||||
val jsPhases = namedIrModulePhase(
|
||||
name = "IrModuleLowering",
|
||||
description = "IR module lowering",
|
||||
lower = phaseList.drop(1).fold(phaseList[0]) { acc: CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>, phase ->
|
||||
acc.then(phase)
|
||||
})
|
||||
lower = loweringList.drop(1).fold(loweringList[0].modulePhase) { acc: CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>, lowering ->
|
||||
acc.then(lowering.modulePhase)
|
||||
}
|
||||
)
|
||||
@@ -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<IrValueDeclaration, IrValueParameter>()
|
||||
val enumEntryToCorrespondingField = newMapping<IrEnumEntry, IrField>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+145
-50
@@ -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<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 {
|
||||
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
|
||||
// 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 fun <T> restrictionImpl(declaration: IrDeclaration?, fn: () -> T): T {
|
||||
private inline fun <T> (() -> 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 <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.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<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(
|
||||
val jsCode: String?,
|
||||
@@ -55,7 +45,8 @@ fun compile(
|
||||
mainArguments: List<String>?,
|
||||
exportedDeclarations: Set<FqName> = 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<IrModuleFragment>()
|
||||
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(
|
||||
|
||||
-6
@@ -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 {
|
||||
|
||||
+22
@@ -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)
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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<IrSimpleFunction>()
|
||||
.single { it.name == function.name }
|
||||
}
|
||||
|
||||
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||
|
||||
+1
-7
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class IrFileToJsTransformer : BaseIrElementToJsNodeTransformer<JsBlock, JsGenera
|
||||
val fileContext = data.newDeclaration()
|
||||
val block = JsGlobalBlock()
|
||||
|
||||
declaration.declarations.forEach {
|
||||
ArrayList(declaration.declarations).forEach {
|
||||
block.statements.add(it.accept(IrDeclarationToJsTransformer(), fileContext))
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -50,17 +50,21 @@ class IrModuleToJsTransformer(
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
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 nameGenerator = IrNamerImpl(
|
||||
|
||||
@@ -199,7 +199,8 @@ class NameTables(
|
||||
}
|
||||
|
||||
for (p in packages) {
|
||||
for (declaration in p.declarations) {
|
||||
// TODO
|
||||
for (declaration in ArrayList(p.declarations)) {
|
||||
acceptDeclaration(declaration)
|
||||
}
|
||||
}
|
||||
@@ -332,6 +333,10 @@ class NameTables(
|
||||
val signature = fieldSignature(field)
|
||||
val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)]
|
||||
|
||||
if (name == null) {
|
||||
return sanitizeName(field.name.asString()) + "__error"
|
||||
}
|
||||
|
||||
require(name != null) {
|
||||
"Can't find name for member field ${field.render()}"
|
||||
}
|
||||
@@ -346,6 +351,11 @@ class NameTables(
|
||||
// of `invoke` functions in FunctionN interfaces
|
||||
if (name == null && signature is ParameterTypeBasedSignature && signature.suggestedName.startsWith("invoke"))
|
||||
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) {
|
||||
"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.impl.IrConstructorImpl
|
||||
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.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
@@ -60,6 +59,9 @@ class JvmBackendContext(
|
||||
) : CommonBackendContext {
|
||||
override val transformedFunction: MutableMap<IrFunctionSymbol, IrSimpleFunctionSymbol>
|
||||
get() = TODO("not implemented")
|
||||
|
||||
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
|
||||
|
||||
override val scriptMode: Boolean = false
|
||||
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
||||
|
||||
|
||||
+2
-1
@@ -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<IrFunctionSymbol, IrSimpleFunctionSymbol>()
|
||||
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
|
||||
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
|
||||
|
||||
// Place to store declarations excluded from code generation
|
||||
val excludedDeclarations: IrPackageFragment by lazy {
|
||||
|
||||
@@ -25,8 +25,6 @@ interface StageController {
|
||||
|
||||
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> bodyLowering(fn: () -> T): T = fn()
|
||||
@@ -38,6 +36,7 @@ interface StageController {
|
||||
fun canAccessDeclarationsOf(irClass: IrClass): Boolean = true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> withInitialIr(noinline fn: () -> T): T {
|
||||
return stageController.withInitialIr(fn)
|
||||
}
|
||||
+1
@@ -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=<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-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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String>,
|
||||
allDependencies: 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 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<TranslationUnit>,
|
||||
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")
|
||||
|
||||
@@ -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<TranslationUnit>,
|
||||
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,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// SKIP_DCE_DRIVEN
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
|
||||
|
||||
Reference in New Issue
Block a user