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
/js/js.translator/testData/out/
/js/js.translator/testData/out-min/
/js/js.translator/testData/out-pir/
.gradle/
build/
!**/src/**/build
@@ -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? {
@@ -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 {
@@ -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
}
}
}
}
@@ -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(
@@ -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 {
@@ -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)
}
}
@@ -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) {
@@ -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) {
@@ -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))
}
@@ -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>()
@@ -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
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.
-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.
+6
View 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