Compare commits

...

54 Commits

Author SHA1 Message Date
Anton Bannykh
bdc0f904f1 Review fix: add TODO comments 2020-02-06 15:37:00 +03:00
Anton Bannykh
242119fba2 Add tests extracted from regressions 2020-02-06 15:37:00 +03:00
Anton Bannykh
d1d9d41825 DCE-driven mode
:js:js.tests:jsIrTest works same as before
:js:js.tests:jsPirTest runs tests in DCE-driven mode
2020-02-06 15:37:00 +03:00
Anton Bannykh
48116532f9 Change DCE a bit to touch less declarations 2020-02-06 15:37:00 +03:00
Anton Bannykh
f6f70b7fb0 Simple stage layers + enforce model 2020-02-06 15:36:59 +03:00
Anton Bannykh
c0470e3cae Persistent IR implementation 2020-02-06 15:36:59 +03:00
Anton Bannykh
c72b563178 Suspend Functions
2 tests muted due to duplicate variables found by the validator
If memory serves:
- there is a `suspend inline fun` and a callable reference to it
- the suspend function doesn't remove the original function in this
  case anymore
- the duplicate `var`'s are inside the function body and the
  callable reference state machine body
2020-02-06 15:36:59 +03:00
Anton Bannykh
78bb5670b2 FoldConstantLowering 2020-02-06 15:36:59 +03:00
Anton Bannykh
3307aecb2b PropertyAccessorInlineLowering 2020-02-06 15:36:59 +03:00
Anton Bannykh
ed086c9114 AnnotationConstructorLowering 2020-02-06 15:36:59 +03:00
Anton Bannykh
355963e324 Enums 2020-02-06 15:36:59 +03:00
Anton Bannykh
c575397b6d PrivateMembersLowering 2020-02-06 15:36:58 +03:00
Anton Bannykh
15aaed0837 PrimaryConstructorLowering 2020-02-06 15:36:58 +03:00
Anton Bannykh
bbdc221a1b ObjectLowering 2020-02-06 15:36:58 +03:00
Anton Bannykh
e958d6b915 MoveBodilessDeclarationsToSeparatePlace 2020-02-06 15:36:58 +03:00
Anton Bannykh
f978aa637a CallableReferenceLowering 2020-02-06 15:36:58 +03:00
Anton Bannykh
0624296f0c BlockDecomposerLowering 2020-02-06 15:36:58 +03:00
Anton Bannykh
4df935a4ac FunctionInlining 2020-02-06 15:36:58 +03:00
Anton Bannykh
13158de050 LocalDelegatedProperties 2020-02-06 15:36:58 +03:00
Anton Bannykh
2ac169fa41 VarargLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
ccdf5eb908 TypeOperatorLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
ab9bb748d0 ThrowableLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
2251b0c681 StaticMembersLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
67548f85b3 SecondaryCtorLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
5e2152e4ec PrimitiveCompanionLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
1c336a2f1f MultipleCatchesLowering 2020-02-06 15:36:57 +03:00
Anton Bannykh
133a3f2592 ConstLowering 2020-02-06 15:36:56 +03:00
Anton Bannykh
c44d37395e ClassReferenceLowering 2020-02-06 15:36:56 +03:00
Anton Bannykh
0c37a5317d BridgesConstruction 2020-02-06 15:36:56 +03:00
Anton Bannykh
dacb4bdb29 AutoboxingTransformer 2020-02-06 15:36:56 +03:00
Anton Bannykh
d75aa32f0b Inline function removal + body copying 2020-02-06 15:36:56 +03:00
Anton Bannykh
db233abd0b CallsLowering 2020-02-06 15:36:56 +03:00
Anton Bannykh
a0d97884f0 Refactor default arguments lowering 2020-02-06 15:36:56 +03:00
Anton Bannykh
7474f771b4 Rewrite inner class lowerings 2020-02-06 15:36:56 +03:00
Anton Bannykh
83cfe9abbd Make ForLoopsLowering a BodyLoweringPass 2020-02-06 15:36:55 +03:00
Anton Bannykh
08d5dfa628 Refactor LateinitLowering into DeclarationTransformer's and a BodyLoweringPass 2020-02-06 15:36:55 +03:00
Anton Bannykh
a5e7a5d019 Make InitializersLowering a BodyLoweringPass and add a InitializersCleanupLowering 2020-02-06 15:36:55 +03:00
Anton Bannykh
dafd78f476 Make ExpectDeclarationsRemoveLowering a DeclarationTransformer 2020-02-06 15:36:55 +03:00
Anton Bannykh
bd1d6db743 Make PropertiesLowering a DeclarationTransformer 2020-02-06 15:36:55 +03:00
Anton Bannykh
8efc6663d7 Make StripTypeAliasDeclarationsLowering a DeclarationTransformer 2020-02-06 15:36:55 +03:00
Anton Bannykh
e9e65272b8 Make InlineClassDeclarationLowering a DeclarationTransformer 2020-02-06 15:36:55 +03:00
Anton Bannykh
76e4bfe814 Make inlineClassUsageLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
3b1bdadfb5 Make LocalDeclarationsLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
9bd8779df1 Make LocalClassPopupLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
f5d7f823c7 Make ArrayConstructorLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
4bcecb53dc Make ProvisionalFunctionExpressionLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
d88dfabec7 Make ReturnableBlockLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
fc114cff41 Make SharedVariablesLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
a65bb5d361 Make TailrecLowering a BodyLoweringPass 2020-02-06 15:36:54 +03:00
Anton Bannykh
103ca9f827 Change BodyLoweringPass API; add DeclarationTransformer + helper methods 2020-02-06 15:36:54 +03:00
Anton Bannykh
712c2e865f IR impl: additional constructors for IrBody implementations 2020-02-06 15:36:53 +03:00
Anton Bannykh
62d39aed66 New mechanism for mappings between old and produced declarations 2020-02-06 15:36:53 +03:00
Anton Bannykh
57bd633be7 IR API: change val ... : MutableList to var ...: List for most lists
All mutable state for IR declarations should be either:
- var (mutable properties)
- or class member list

Mutable properties are straightforward to persist.

The class member list is handled in a special way.
2020-02-06 15:36:53 +03:00
Anton Bannykh
d38318a546 IR API: Make IrEnumEntry.initializerExpression IrExpressionBody
All non-declarations should be inside IrBody's now
2020-02-06 15:36:53 +03:00
196 changed files with 5027 additions and 2293 deletions

1
.gitignore vendored
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

View File

@@ -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)

View File

@@ -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")!!

View File

@@ -587,7 +587,7 @@ class Fir2IrDeclarationStorage(
this.correspondingClass = klass
} else if (irParent != null) {
this.initializerExpression =
IrEnumConstructorCallImpl(startOffset, endOffset, irType, irParent.constructors.first().symbol)
IrExpressionBodyImpl(IrEnumConstructorCallImpl(startOffset, endOffset, irType, irParent.constructors.first().symbol))
}
}
}

View File

@@ -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>
}

View File

@@ -33,4 +33,6 @@ interface CommonBackendContext : BackendContext, LoggingContext {
putValueArgument(0, builder.irString(name))
}
}
val mapping: Mapping
}

View File

@@ -68,6 +68,20 @@ abstract class IrElementTransformerVoidWithContext : IrElementTransformerVoid()
return result
}
final override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement {
scopeStack.push(createScope(declaration))
val result = visitAnonymousInitializerNew(declaration)
scopeStack.pop()
return result
}
final override fun visitValueParameter(declaration: IrValueParameter): IrStatement {
scopeStack.push(createScope(declaration))
val result = visitValueParameterNew(declaration)
scopeStack.pop()
return result
}
final override fun visitScript(declaration: IrScript): IrStatement {
scopeStack.push(createScope(declaration))
val result = visitScriptNew(declaration)
@@ -80,6 +94,8 @@ abstract class IrElementTransformerVoidWithContext : IrElementTransformerVoid()
protected val currentClass get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrClassSymbol }
protected val currentFunction get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrFunctionSymbol }
protected val currentProperty get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrPropertySymbol }
protected val currentAnonymousInitializer get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrAnonymousInitializer }
protected val currentValueParameter get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrValueParameter }
protected val currentScope get() = scopeStack.peek()
protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2]
protected val allScopes get() = scopeStack
@@ -109,6 +125,14 @@ abstract class IrElementTransformerVoidWithContext : IrElementTransformerVoid()
return super.visitField(declaration)
}
open fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer): IrStatement {
return super.visitAnonymousInitializer(declaration)
}
open fun visitValueParameterNew(declaration: IrValueParameter): IrStatement {
return super.visitValueParameter(declaration)
}
open fun visitScriptNew(declaration: IrScript): IrStatement {
return super.visitScript(declaration)
}
@@ -152,10 +176,24 @@ abstract class IrElementVisitorVoidWithContext : IrElementVisitorVoid {
scopeStack.pop()
}
final override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) {
scopeStack.push(createScope(declaration))
visitAnonymousInitializerNew(declaration)
scopeStack.pop()
}
final override fun visitValueParameter(declaration: IrValueParameter) {
scopeStack.push(createScope(declaration))
visitValueParameterNew(declaration)
scopeStack.pop()
}
protected val currentFile get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrFileSymbol }
protected val currentClass get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrClassSymbol }
protected val currentFunction get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrFunctionSymbol }
protected val currentProperty get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrPropertySymbol }
protected val currentAnonymousInitializer get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrAnonymousInitializer }
protected val currentValueParameter get() = scopeStack.lastOrNull { it.scope.scopeOwnerSymbol is IrValueParameter }
protected val currentScope get() = scopeStack.peek()
protected val parentScope get() = if (scopeStack.size < 2) null else scopeStack[scopeStack.size - 2]
protected val allScopes get() = scopeStack
@@ -183,4 +221,12 @@ abstract class IrElementVisitorVoidWithContext : IrElementVisitorVoid {
open fun visitFieldNew(declaration: IrField) {
super.visitField(declaration)
}
open fun visitAnonymousInitializerNew(declaration: IrAnonymousInitializer) {
super.visitAnonymousInitializer(declaration)
}
open fun visitValueParameterNew(declaration: IrValueParameter) {
super.visitValueParameter(declaration)
}
}

View File

@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -58,7 +60,7 @@ interface FunctionLoweringPass : FileLoweringPass {
}
interface BodyLoweringPass : FileLoweringPass {
fun lower(irBody: IrBody)
fun lower(irBody: IrBody, container: IrDeclaration)
override fun lower(irFile: IrFile) = runOnFilePostfix(irFile)
}
@@ -110,17 +112,35 @@ fun DeclarationContainerLoweringPass.runOnFilePostfix(irFile: IrFile) {
this.lower(irFile as IrDeclarationContainer)
}
fun BodyLoweringPass.runOnFilePostfix(irFile: IrFile) {
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
fun BodyLoweringPass.runOnFilePostfix(irFile: IrFile, withLocalDeclarations: Boolean = false, allowDeclarationModification: Boolean = false) {
ArrayList(irFile.declarations).forEach {
it.accept(object : IrElementVisitor<Unit, IrDeclaration?> {
override fun visitElement(element: IrElement, data: IrDeclaration?) {
element.acceptChildren(this, data)
}
override fun visitBody(body: IrBody) {
body.acceptChildrenVoid(this)
lower(body)
}
})
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclaration?) {
declaration.acceptChildren(this, declaration)
}
override fun visitClass(declaration: IrClass, data: IrDeclaration?) {
declaration.thisReceiver?.accept(this, declaration)
declaration.typeParameters.forEach { it.accept(this, declaration) }
ArrayList(declaration.declarations).forEach { it.accept(this, declaration) }
}
override fun visitBody(body: IrBody, data: IrDeclaration?) {
if (withLocalDeclarations) body.acceptChildren(this, null)
if (allowDeclarationModification) {
lower(body, data!!)
} else {
stageController.bodyLowering {
lower(body, data!!)
}
}
}
}, null)
}
}
fun FunctionLoweringPass.runOnFilePostfix(irFile: IrFile) {
@@ -134,4 +154,94 @@ fun FunctionLoweringPass.runOnFilePostfix(irFile: IrFile) {
lower(declaration)
}
})
}
interface DeclarationTransformer: FileLoweringPass {
fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>?
override fun lower(irFile: IrFile) {
runPostfix().toFileLoweringPass().lower(irFile)
}
}
fun DeclarationTransformer.transformFlatRestricted(declaration: IrDeclaration): List<IrDeclaration>? {
return stageController.restrictTo(declaration) {
transformFlat(declaration)
}
}
fun DeclarationTransformer.toFileLoweringPass(): FileLoweringPass {
return object : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.declarations.transformFlat(this@toFileLoweringPass::transformFlat)
}
}
}
fun DeclarationTransformer.runPostfix(withLocalDeclarations: Boolean = false): DeclarationTransformer {
return object : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
declaration.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitBody(body: IrBody) {
if (withLocalDeclarations) {
super.visitBody(body)
}
// else stop
}
override fun visitFunction(declaration: IrFunction) {
declaration.acceptChildrenVoid(this)
for (v in declaration.valueParameters) {
val result = this@runPostfix.transformFlatRestricted(v)
if (result != null) error("Don't know how to add value parameters")
}
}
override fun visitProperty(declaration: IrProperty) {
// TODO This is a hack to allow lowering a getter separately from the enclosing property
val visitor = this
fun IrDeclaration.transform() {
acceptVoid(visitor)
val result = this@runPostfix.transformFlatRestricted(this)
if (result != null) {
(parent as? IrDeclarationContainer)?.let {
var index = it.declarations.indexOf(this)
if (index == -1) {
index = it.declarations.indexOf(declaration)
} else {
it.declarations.removeAt(index)
--index
}
it.declarations.addAll(index + 1, result)
}
}
}
declaration.backingField?.transform()
declaration.getter?.transform()
declaration.setter?.transform()
}
override fun visitClass(declaration: IrClass) {
declaration.thisReceiver?.accept(this, null)
declaration.typeParameters.forEach { it.accept(this, null) }
ArrayList(declaration.declarations).forEach { it.accept(this, null) }
declaration.declarations.transformFlat(this@runPostfix::transformFlatRestricted)
}
})
return this@runPostfix.transformFlatRestricted(declaration)
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.ir.declarations.*
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty
interface Mapping {
val defaultArgumentsDispatchFunction: Delegate<IrFunction, IrFunction>
val defaultArgumentsOriginalFunction: Delegate<IrFunction, IrFunction>
val suspendFunctionToCoroutineConstructor: Delegate<IrFunction, IrConstructor>
val lateInitFieldToNullableField: Delegate<IrField, IrField>
val inlineClassMemberToStatic: Delegate<IrFunction, IrSimpleFunction>
abstract class Delegate<K : IrDeclaration, V> {
abstract operator fun get(key: K): V?
abstract operator fun set(key: K, value: V?)
operator fun getValue(thisRef: K, desc: KProperty<*>): V? = get(thisRef)
operator fun setValue(thisRef: K, desc: KProperty<*>, value: V?) {
set(thisRef, value)
}
}
}
open class DefaultMapping : Mapping {
override val defaultArgumentsDispatchFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
override val defaultArgumentsOriginalFunction: Mapping.Delegate<IrFunction, IrFunction> = newMapping()
override val suspendFunctionToCoroutineConstructor: Mapping.Delegate<IrFunction, IrConstructor> = newMapping()
override val lateInitFieldToNullableField: Mapping.Delegate<IrField, IrField> = newMapping()
override val inlineClassMemberToStatic: Mapping.Delegate<IrFunction, IrSimpleFunction> = newMapping()
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? {
return map[key]
}
override operator fun set(key: K, value: V?) {
if (value == null) {
map.remove(key)
} else {
map[key] = value
}
}
}
}
fun <V : Any> KMutableProperty0<V?>.getOrPut(fn: () -> V) = this.get() ?: fn().also {
this.set(it)
}
fun <K : IrDeclaration, V> Mapping.Delegate<K, V>.getOrPut(key: K, fn: () -> V) = this[key] ?: fn().also {
this[key] = it
}

View File

@@ -14,5 +14,6 @@ interface DeclarationFactory {
fun getFieldForEnumEntry(enumEntry: IrEnumEntry): IrField
fun getOuterThisField(innerClass: IrClass): IrField
fun getInnerClassConstructorWithOuterThisParameter(innerClassConstructor: IrConstructor): IrConstructor
fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor?
fun getFieldForObjectInstance(singleton: IrClass): IrField
}

View File

@@ -78,7 +78,7 @@ fun IrClass.addSimpleDelegatingConstructor(
constructor.parent = this
declarations += constructor
superConstructor.valueParameters.mapIndexedTo(constructor.valueParameters) { index, parameter ->
constructor.valueParameters = superConstructor.valueParameters.mapIndexed { index, parameter ->
parameter.copyTo(constructor, index = index)
}
@@ -154,8 +154,13 @@ fun IrValueParameter.copyTo(
WrappedValueParameterDescriptor(this.descriptor.annotations, this.descriptor.source)
}
val symbol = IrValueParameterSymbolImpl(descriptor)
val defaultValueCopy = defaultValue?.deepCopyWithVariables()
defaultValueCopy?.patchDeclarationParents(irFunction)
val defaultValueCopy = defaultValue?.let { originalDefault ->
IrExpressionBodyImpl(originalDefault.startOffset, originalDefault.endOffset) {
expression = originalDefault.expression.deepCopyWithVariables().also {
it.patchDeclarationParents(irFunction)
}
}
}
return IrValueParameterImpl(
startOffset, endOffset, origin, symbol,
name, index, type, varargElementType, isCrossinline, isNoinline
@@ -163,7 +168,7 @@ fun IrValueParameter.copyTo(
descriptor.bind(it)
it.parent = irFunction
it.defaultValue = defaultValueCopy
it.annotations.addAll(annotations.map { it.deepCopyWithSymbols() })
it.annotations = annotations.map { it.deepCopyWithSymbols() }
}
}
@@ -205,7 +210,7 @@ fun IrFunction.copyValueParametersInsertingContinuationFrom(from: IrFunction, in
insertContinuation()
additionalShift = 1
}
valueParameters.add(it.copyTo(this, index = it.index + shift + additionalShift))
valueParameters += it.copyTo(this, index = it.index + shift + additionalShift)
}
// If there was no default argument mask and handler, the continuation goes last.
if (additionalShift == 0) insertContinuation()
@@ -231,7 +236,7 @@ fun IrTypeParametersContainer.copyTypeParameters(
oldToNewParameterMap[sourceParameter] = it
}
}
typeParameters.addAll(newTypeParameters)
typeParameters += newTypeParameters
srcTypeParameters.zip(newTypeParameters).forEach { (srcParameter, dstParameter) ->
dstParameter.copySuperTypesFrom(srcParameter, oldToNewParameterMap)
}
@@ -273,35 +278,29 @@ fun IrFunction.copyValueParametersToStatic(
target.classIfConstructor
)
target.valueParameters.add(
originalDispatchReceiver.copyTo(
target,
origin = originalDispatchReceiver.origin,
index = shift++,
type = type,
name = Name.identifier("\$this")
)
target.valueParameters += originalDispatchReceiver.copyTo(
target,
origin = originalDispatchReceiver.origin,
index = shift++,
type = type,
name = Name.identifier("\$this")
)
}
source.extensionReceiverParameter?.let { originalExtensionReceiver ->
target.valueParameters.add(
originalExtensionReceiver.copyTo(
target,
origin = originalExtensionReceiver.origin,
index = shift++,
name = Name.identifier("\$receiver")
)
target.valueParameters += originalExtensionReceiver.copyTo(
target,
origin = originalExtensionReceiver.origin,
index = shift++,
name = Name.identifier("\$receiver")
)
}
for (oldValueParameter in source.valueParameters) {
if (oldValueParameter.index >= numValueParametersToCopy) break
target.valueParameters.add(
oldValueParameter.copyTo(
target,
origin = origin,
index = oldValueParameter.index + shift
)
target.valueParameters += oldValueParameter.copyTo(
target,
origin = origin,
index = oldValueParameter.index + shift
)
}
}
@@ -367,7 +366,9 @@ fun IrType.remapTypeParameters(
/* Copied from K/N */
fun IrDeclarationContainer.addChild(declaration: IrDeclaration) {
this.declarations += declaration
stageController.unrestrictDeclarationListsAccess {
this.declarations += declaration
}
declaration.accept(SetDeclarationsParentVisitor, this)
}
@@ -444,6 +445,7 @@ fun IrClass.simpleFunctions() = declarations.flatMap {
}
}
fun IrClass.createParameterDeclarations() {
assert(thisReceiver == null)
@@ -601,7 +603,7 @@ fun createStaticFunctionWithReceivers(
typeParameters.forEach { it.superTypes.replaceAll { remap(it) } }
annotations.addAll(oldFunction.annotations)
annotations = oldFunction.annotations
var offset = 0
val dispatchReceiver = oldFunction.dispatchReceiverParameter?.copyTo(
@@ -618,7 +620,7 @@ fun createStaticFunctionWithReceivers(
origin = IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER,
remapTypeMap = typeParameterMap
)
valueParameters.addAll(listOfNotNull(dispatchReceiver, extensionReceiver) +
valueParameters = listOfNotNull(dispatchReceiver, extensionReceiver) +
oldFunction.valueParameters.map {
it.copyTo(
this,
@@ -626,7 +628,6 @@ fun createStaticFunctionWithReceivers(
remapTypeMap = typeParameterMap
)
}
)
if (copyMetadata) metadata = oldFunction.metadata
}
@@ -670,10 +671,11 @@ private fun IrSimpleFunction.copyAndRenameConflictingTypeParametersFrom(
it.parent = this
}
typeParameters.add(newTypeParameter)
newParameters.add(newTypeParameter)
}
typeParameters = typeParameters + newParameters
return newParameters
}

View File

@@ -38,7 +38,9 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val context: C) : FileLoweringPass {
abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val context: C) : BodyLoweringPass {
private var IrFunction.coroutineConstructor by context.mapping.suspendFunctionToCoroutineConstructor
protected object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL")
protected object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL")
@@ -61,53 +63,38 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
delegatingCall
private val builtCoroutines = mutableMapOf<IrFunction, BuiltCoroutine>()
private val suspendLambdas = mutableMapOf<IrFunction, IrFunctionReference>()
override fun lower(irFile: IrFile) {
markSuspendLambdas(irFile)
buildCoroutines(irFile)
transformCallableReferencesToSuspendLambdas(irFile)
override fun lower(irBody: IrBody, container: IrDeclaration) {
transformCallableReferencesToSuspendLambdas(irBody)
if (container.isLambda) return
tryTransformSuspendFunction(container, null)
}
private fun buildCoroutines(irFile: IrFile) {
irFile.transformDeclarationsFlat(::tryTransformSuspendFunction)
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.transformDeclarationsFlat(::tryTransformSuspendFunction)
}
})
}
// Suppress since it is used in native
@Suppress("MemberVisibilityCanBePrivate")
protected fun IrCall.isReturnIfSuspendedCall() =
symbol.owner.run { fqNameWhenAvailable == context.internalPackageFqn.child(Name.identifier("returnIfSuspended")) }
private fun tryTransformSuspendFunction(element: IrElement) =
if (element is IrSimpleFunction && element.isSuspend && element.modality != Modality.ABSTRACT)
transformSuspendFunction(element, suspendLambdas[element])
else null
private fun tryTransformSuspendFunction(element: IrElement, functionReference: IrFunctionReference?) {
if (element is IrSimpleFunction && element.isSuspend && element.modality != Modality.ABSTRACT) {
private fun markSuspendLambdas(irElement: IrElement) {
irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
transformCallableReferencesToSuspendLambdas(element)
override fun visitFunctionReference(expression: IrFunctionReference) {
expression.acceptChildrenVoid(this)
transformSuspendFunction(element, functionReference)?.let { result ->
result.forEach { declaration ->
if (declaration !== element) {
// TODO Use proper means to emerge declarations
element.file.declarations += declaration
declaration.parent = element.file
if (expression.isSuspend) {
suspendLambdas[expression.symbol.owner] = expression
// TODO investigate IrJsCodegenBoxTestGenerated$Coroutines$ControlFlow.testBreakFinally_1_3
declaration.patchDeclarationParents(declaration.parent)
}
}
}
})
}
}
private fun transformCallableReferencesToSuspendLambdas(irElement: IrElement) {
@@ -118,16 +105,33 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
if (!expression.isSuspend)
return expression
val coroutine = builtCoroutines[expression.symbol.owner]
?: throw Error("Non-local callable reference to suspend lambda: $expression")
val constructorParameters = coroutine.coroutineConstructor.valueParameters
val coroutineConstructor = if (expression.symbol.owner.isLambda) {
if (expression.symbol.owner in builtCoroutines) {
error("Lambda revisiting?")
}
tryTransformSuspendFunction(expression.symbol.owner, expression)
builtCoroutines[expression.symbol.owner]?.coroutineConstructor
?: throw Error("Non-local callable reference to suspend lambda: $expression")
} else {
expression.symbol.owner.coroutineConstructor ?: run {
tryTransformSuspendFunction(expression.symbol.owner, expression)
builtCoroutines[expression.symbol.owner]!!.coroutineConstructor
}
}
val constructorParameters = coroutineConstructor.valueParameters
val expressionArguments = expression.getArguments().map { it.second }
assert(constructorParameters.size == expressionArguments.size) {
"Inconsistency between callable reference to suspend lambda and the corresponding coroutine"
}
val irBuilder = context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset)
irBuilder.run {
return irCall(coroutine.coroutineConstructor.symbol).apply {
return irCall(coroutineConstructor.symbol).apply {
expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
@@ -144,7 +148,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
}
private fun transformSuspendFunction(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?) =
when (val suspendFunctionKind = getSuspendFunctionKind(irFunction)) {
when (val suspendFunctionKind = getSuspendFunctionKind(irFunction, functionReference)) {
is SuspendFunctionKind.NO_SUSPEND_CALLS -> {
null // No suspend function calls - just an ordinary function.
}
@@ -156,15 +160,15 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
is SuspendFunctionKind.NEEDS_STATE_MACHINE -> {
val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation.
if (irFunction in suspendLambdas) // Suspend lambdas are called through factory method <create>,
if (irFunction.isLambda) // Suspend lambdas are called through factory method <create>,
listOf(coroutine) // thus we can eliminate original body.
else
listOf<IrDeclaration>(coroutine, irFunction)
}
}
private fun getSuspendFunctionKind(irFunction: IrSimpleFunction): SuspendFunctionKind {
if (irFunction in suspendLambdas)
private fun getSuspendFunctionKind(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?): SuspendFunctionKind {
if (irFunction.isLambda || functionReference != null)
return SuspendFunctionKind.NEEDS_STATE_MACHINE // Suspend lambdas always need coroutine implementation.
val body = irFunction.body ?: return SuspendFunctionKind.NO_SUSPEND_CALLS
@@ -237,29 +241,33 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
private fun buildCoroutine(irFunction: IrSimpleFunction, functionReference: IrFunctionReference?): IrClass {
val coroutine = CoroutineBuilder(irFunction, functionReference).build()
builtCoroutines[irFunction] = coroutine
irFunction.coroutineConstructor = coroutine.coroutineConstructor
if (functionReference == null) {
if (!irFunction.isLambda && functionReference == null) {
// It is not a lambda - replace original function with a call to constructor of the built coroutine.
val irBuilder = context.createIrBuilder(irFunction.symbol, irFunction.startOffset, irFunction.endOffset)
irFunction.body = irBuilder.irBlockBody(irFunction) {
val constructor = coroutine.coroutineConstructor
generateCoroutineStart(coroutine.stateMachineFunction,
irCallConstructor(constructor.symbol, irFunction.typeParameters.map {
IrSimpleTypeImpl(it.symbol, true, emptyList(), emptyList())
}).apply {
val functionParameters = irFunction.explicitParameters
functionParameters.forEachIndexed { index, argument ->
putValueArgument(index, irGet(argument))
}
putValueArgument(
functionParameters.size,
irCall(
getContinuationSymbol,
getContinuationSymbol.owner.returnType,
listOf(irFunction.returnType)
(irFunction.body as IrBlockBody).statements.let {
it.clear()
it += irBuilder.irBlockBody(irFunction) {
val constructor = coroutine.coroutineConstructor
generateCoroutineStart(coroutine.stateMachineFunction,
irCallConstructor(constructor.symbol, irFunction.typeParameters.map {
IrSimpleTypeImpl(it.symbol, true, emptyList(), emptyList())
}).apply {
val functionParameters = irFunction.explicitParameters
functionParameters.forEachIndexed { index, argument ->
putValueArgument(index, irGet(argument))
}
putValueArgument(
functionParameters.size,
irCall(
getContinuationSymbol,
getContinuationSymbol.owner.returnType,
listOf(irFunction.returnType)
)
)
)
})
})
}.statements
}
}
@@ -300,7 +308,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
d.bind(this)
parent = irFunction.parent
createParameterDeclarations()
irFunction.typeParameters.mapTo(typeParameters) { typeParam ->
typeParameters = irFunction.typeParameters.map { typeParam ->
typeParam.copyToWithoutSuperTypes(this).apply { superTypes += typeParam.superTypes }
}
}
@@ -403,13 +411,15 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
coroutineClass.declarations += this
coroutineConstructors += this
functionParameters.mapIndexedTo(valueParameters) { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
valueParameters = functionParameters.mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index, defaultValue = null)
}
val continuationParameter = coroutineBaseClassConstructor.valueParameters[0]
valueParameters += continuationParameter.copyTo(
this, DECLARATION_ORIGIN_COROUTINE_IMPL,
index = valueParameters.size, type = continuationType
index = valueParameters.size,
type = continuationType,
defaultValue = null
)
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
@@ -449,7 +459,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
coroutineClass.declarations += this
coroutineConstructors += this
boundParams.mapIndexedTo(valueParameters) { index, parameter ->
valueParameters = boundParams.mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
@@ -498,15 +508,14 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
parent = coroutineClass
coroutineClass.declarations += this
irFunction.typeParameters.mapTo(typeParameters) { parameter ->
typeParameters = irFunction.typeParameters.map { parameter ->
parameter.copyToWithoutSuperTypes(this, origin = DECLARATION_ORIGIN_COROUTINE_IMPL)
.apply { superTypes += parameter.superTypes }
}
(unboundArgs + create1CompletionParameter)
.mapIndexedTo(valueParameters) { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
valueParameters = (unboundArgs + create1CompletionParameter).mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
this.createDispatchReceiverParameter()
@@ -566,15 +575,15 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
parent = coroutineClass
coroutineClass.declarations += this
irFunction.typeParameters.mapTo(typeParameters) { parameter ->
typeParameters = irFunction.typeParameters.map { parameter ->
parameter.copyToWithoutSuperTypes(this, origin = DECLARATION_ORIGIN_COROUTINE_IMPL)
.apply { superTypes += parameter.superTypes }
}
createFunction.valueParameters
valueParameters = createFunction.valueParameters
// Skip completion - invoke() already has it implicitly as a suspend function.
.take(createFunction.valueParameters.size - 1)
.mapIndexedTo(valueParameters) { index, parameter ->
.mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
@@ -626,12 +635,12 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
parent = coroutineClass
coroutineClass.declarations += this
stateMachineFunction.typeParameters.mapTo(typeParameters) { parameter ->
typeParameters = stateMachineFunction.typeParameters.map { parameter ->
parameter.copyToWithoutSuperTypes(this, origin = DECLARATION_ORIGIN_COROUTINE_IMPL)
.apply { superTypes += parameter.superTypes }
}
stateMachineFunction.valueParameters.mapIndexedTo(valueParameters) { index, parameter ->
valueParameters = stateMachineFunction.valueParameters.mapIndexed { index, parameter ->
parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index)
}
@@ -696,3 +705,13 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
}
}
}
private val IrDeclaration.isLambda
get() = origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
class RemoveSuspendLambdas() : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
return if (declaration.isLambda && declaration is IrFunction && declaration.isSuspend) emptyList() else null
}
}

View File

@@ -5,31 +5,38 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.asSimpleLambda
import org.jetbrains.kotlin.backend.common.ir.inline
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTransformerVoidWithContext(), BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(ArrayConstructorTransformer(context, container as IrSymbolOwner))
}
}
private class ArrayConstructorTransformer(
val context: CommonBackendContext,
val container: IrSymbolOwner
) : IrElementTransformerVoidWithContext() {
// Array(size, init) -> Array(size)
private fun arrayInlineToSizeConstructor(irConstructor: IrConstructor): IrFunctionSymbol? {
@@ -64,7 +71,7 @@ class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTra
// (and similar for primitive arrays)
val size = expression.getValueArgument(0)!!.transform(this, null)
val invokable = expression.getValueArgument(1)!!.transform(this, null)
val scope = currentScope!!.scope
val scope = (currentScope ?: createScope(container)).scope
return context.createIrBuilder(scope.scopeOwnerSymbol).irBlock(expression.startOffset, expression.endOffset) {
val index = irTemporaryVar(irInt(0))
val sizeVar = irTemporary(size)
@@ -83,7 +90,7 @@ class ArrayConstructorLowering(val context: CommonBackendContext) : IrElementTra
}
body = irBlock {
val tempIndex = irTemporary(irGet(index))
val value = lambda?.inline(parent, listOf(tempIndex)) ?: irCallOp(
val value = lambda?.inline(parent, listOf(tempIndex))?.patchDeclarationParents(scope.getLocalDeclarationParent()) ?: irCallOp(
invoke.symbol,
invoke.returnType,
irGet(invokableVar!!),

View File

@@ -17,9 +17,6 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
@@ -29,19 +26,21 @@ import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import kotlin.collections.set
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
data class Closure(val capturedValues: List<IrValueSymbol>, val capturedTypeParameters: List<IrTypeParameter>)
class ClosureAnnotator(irFile: IrFile) {
class ClosureAnnotator(body: IrBody, declaration: IrDeclaration) {
private val closureBuilders = mutableMapOf<IrDeclaration, ClosureBuilder>()
init {
// Collect all closures for classes and functions. Collect call graph
irFile.acceptChildrenVoid(ClosureCollectorVisitor())
if (declaration is IrSimpleFunction && declaration.name.asString() == "arrayIterator") {
1
}
body.accept(ClosureCollectorVisitor(), declaration.closureBuilderOrNull ?: declaration.parentClosureBuilder)
}
fun getFunctionClosure(declaration: IrFunction) = getClosure(declaration)
@@ -126,129 +125,150 @@ class ClosureAnnotator(irFile: IrFile) {
}
}
private inner class ClosureCollectorVisitor : IrElementVisitorVoid {
val closuresStack = mutableListOf<ClosureBuilder>()
fun includeInParent(builder: ClosureBuilder) {
// We don't include functions or classes in a parent function when they are declared.
// Instead we will include them when are is used (use = call for a function or constructor call for a class).
val parentBuilder = closuresStack.peek()
if (parentBuilder != null && parentBuilder.owner !is IrFunction) {
parentBuilder.include(builder)
}
private fun includeInParent(builder: ClosureBuilder) {
// We don't include functions or classes in a parent function when they are declared.
// Instead we will include them when are is used (use = call for a function or constructor call for a class).
val parentBuilder = builder.owner.parentClosureBuilder
if (parentBuilder != null && parentBuilder.owner !is IrFunction) {
parentBuilder.include(builder)
}
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
val closureBuilder = ClosureBuilder(declaration)
closureBuilders[declaration] = closureBuilder
private val IrClass.closureBuilder: ClosureBuilder
get() = closureBuilders.getOrPut(this) {
val closureBuilder = ClosureBuilder(this)
collectPotentiallyCapturedTypeParameters(closureBuilder)
closureBuilder.declareVariable(declaration.thisReceiver)
if (declaration.isInner) {
closureBuilder.declareVariable((declaration.parent as IrClass).thisReceiver)
closureBuilder.declareVariable(this.thisReceiver)
if (this.isInner) {
closureBuilder.declareVariable((this.parent as IrClass).thisReceiver)
includeInParent(closureBuilder)
}
declaration.declarations.firstOrNull { it is IrConstructor && it.isPrimary }?.let {
this.declarations.firstOrNull { it is IrConstructor && it.isPrimary }?.let {
val constructor = it as IrConstructor
constructor.valueParameters.forEach { v -> closureBuilder.declareVariable(v) }
}
closuresStack.push(closureBuilder)
declaration.acceptChildrenVoid(this)
closuresStack.pop()
closureBuilder
}
override fun visitFunction(declaration: IrFunction) {
val closureBuilder = ClosureBuilder(declaration)
closureBuilders[declaration] = closureBuilder
private val IrFunction.closureBuilder: ClosureBuilder
get() = closureBuilders.getOrPut(this) {
val closureBuilder = ClosureBuilder(this)
collectPotentiallyCapturedTypeParameters(closureBuilder)
declaration.valueParameters.forEach { closureBuilder.declareVariable(it) }
closureBuilder.declareVariable(declaration.dispatchReceiverParameter)
closureBuilder.declareVariable(declaration.extensionReceiverParameter)
this.valueParameters.forEach { closureBuilder.declareVariable(it) }
closureBuilder.declareVariable(this.dispatchReceiverParameter)
closureBuilder.declareVariable(this.extensionReceiverParameter)
if (declaration is IrConstructor) {
val constructedClass = (declaration.parent as IrClass)
if (this is IrConstructor) {
val constructedClass = (this.parent as IrClass)
closureBuilder.declareVariable(constructedClass.thisReceiver)
// Include closure of the class in the constructor closure.
val classBuilder = closuresStack.peek()
classBuilder?.let {
assert(classBuilder.owner == constructedClass)
closureBuilder.include(classBuilder)
}
val classBuilder = constructedClass.closureBuilder
closureBuilder.include(classBuilder)
}
closuresStack.push(closureBuilder)
declaration.acceptChildrenVoid(this)
closuresStack.pop()
closureBuilder
}
private fun collectPotentiallyCapturedTypeParameters(closureBuilder: ClosureBuilder) {
fun ClosureBuilder.doCollect() {
if (owner !is IrClass) {
(owner as? IrTypeParametersContainer)?.let { container ->
for (tp in container.typeParameters) {
closureBuilder.addPotentiallyCapturedTypeParameter(tp)
}
}
owner.parentClosureBuilder?.doCollect()
}
}
closureBuilder.owner.parentClosureBuilder?.doCollect()
}
private val IrDeclaration.parentClosureBuilder: ClosureBuilder?
get() = when (val p = parent) {
is IrClass -> p.closureBuilder
is IrFunction -> p.closureBuilder
is IrDeclaration -> p.parentClosureBuilder
else -> null
}
private val IrDeclaration.closureBuilderOrNull: ClosureBuilder?
get() = when (this) {
is IrClass -> closureBuilder
is IrFunction -> closureBuilder
else -> null
}
private inner class ClosureCollectorVisitor() : IrElementVisitor<Unit, ClosureBuilder?> {
override fun visitElement(element: IrElement, data: ClosureBuilder?) {
element.acceptChildren(this, data)
}
override fun visitClass(declaration: IrClass, data: ClosureBuilder?) {
declaration.acceptChildren(this, declaration.closureBuilder)
}
override fun visitFunction(declaration: IrFunction, data: ClosureBuilder?) {
val closureBuilder = declaration.closureBuilder
declaration.acceptChildren(this, closureBuilder)
includeInParent(closureBuilder)
}
override fun visitVariableAccess(expression: IrValueAccessExpression) {
closuresStack.peek()?.seeVariable(expression.symbol)
super.visitVariableAccess(expression)
override fun visitValueAccess(expression: IrValueAccessExpression, data: ClosureBuilder?) {
data?.seeVariable(expression.symbol)
super.visitValueAccess(expression, data)
}
override fun visitVariable(declaration: IrVariable) {
closuresStack.peek()?.declareVariable(declaration)
super.visitVariable(declaration)
override fun visitVariable(declaration: IrVariable, data: ClosureBuilder?) {
data?.declareVariable(declaration)
super.visitVariable(declaration, data)
}
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) {
super.visitFunctionAccess(expression)
processMemberAccess(expression.symbol.owner)
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: ClosureBuilder?) {
super.visitFunctionAccess(expression, data)
processMemberAccess(expression.symbol.owner, data)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
super.visitFunctionReference(expression)
processMemberAccess(expression.symbol.owner)
override fun visitFunctionReference(expression: IrFunctionReference, data: ClosureBuilder?) {
super.visitFunctionReference(expression, data)
processMemberAccess(expression.symbol.owner, data)
}
override fun visitPropertyReference(expression: IrPropertyReference) {
super.visitPropertyReference(expression)
expression.getter?.let { processMemberAccess(it.owner) }
expression.setter?.let { processMemberAccess(it.owner) }
override fun visitPropertyReference(expression: IrPropertyReference, data: ClosureBuilder?) {
super.visitPropertyReference(expression, data)
expression.getter?.let { processMemberAccess(it.owner, data) }
expression.setter?.let { processMemberAccess(it.owner, data) }
}
override fun visitExpression(expression: IrExpression) {
super.visitExpression(expression)
val typeParameterContainerScopeBuilder = closuresStack.peek()?.let {
if (it.owner is IrConstructor) {
closuresStack[closuresStack.size - 2]
} else it
override fun visitExpression(expression: IrExpression, data: ClosureBuilder?) {
super.visitExpression(expression, data)
val typeParameterContainerScopeBuilder = data?.let {
(it.owner as? IrConstructor)?.closureBuilder ?: it
}
typeParameterContainerScopeBuilder?.seeType(expression.type)
}
private fun processMemberAccess(declaration: IrDeclaration) {
private fun processMemberAccess(declaration: IrDeclaration, parentClosure: ClosureBuilder?) {
if (declaration.isLocal) {
if (declaration is IrSimpleFunction && declaration.visibility != Visibilities.LOCAL) {
return
}
val builder = closureBuilders[declaration]
val builder = declaration.closureBuilderOrNull
builder?.let {
closuresStack.peek()?.include(builder)
}
}
}
private fun collectPotentiallyCapturedTypeParameters(closureBuilder: ClosureBuilder) {
closuresStack.takeLastWhile { it.owner !is IrClass }.forEach {
(it.owner as? IrTypeParametersContainer)?.let { container ->
for (tp in container.typeParameters) {
closureBuilder.addPotentiallyCapturedTypeParameter(tp)
}
parentClosure?.include(builder)
}
}
}

View File

@@ -36,22 +36,26 @@ import org.jetbrains.kotlin.name.Name
open class DefaultArgumentStubGenerator(
open val context: CommonBackendContext,
private val skipInlineMethods: Boolean = true,
private val skipExternalMethods: Boolean = false
) : DeclarationContainerLoweringPass {
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
) : DeclarationTransformer {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { memberDeclaration ->
if (memberDeclaration is IrFunction)
lower(memberDeclaration)
else
null
}
override fun lower(irFile: IrFile) {
runPostfix(true).toFileLoweringPass().lower(irFile)
}
private fun lower(irFunction: IrFunction): List<IrFunction> {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrFunction) {
return lower(declaration)
}
return null
}
private fun lower(irFunction: IrFunction): List<IrFunction>? {
val visibility = defaultArgumentStubVisibility(irFunction)
val newIrFunction = irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods, visibility)
?: return listOf(irFunction)
val newIrFunction = irFunction.generateDefaultsFunction(context, skipInlineMethods, skipExternalMethods, forceSetOverrideSymbols, visibility)
?: return null
if (newIrFunction.origin == IrDeclarationOrigin.FAKE_OVERRIDE) {
return listOf(irFunction, newIrFunction)
}
@@ -59,80 +63,77 @@ open class DefaultArgumentStubGenerator(
log { "$irFunction -> $newIrFunction" }
val builder = context.createIrBuilder(newIrFunction.symbol)
newIrFunction.body = builder.irBlockBody(newIrFunction) {
val params = mutableListOf<IrValueDeclaration>()
val variables = mutableMapOf<IrValueDeclaration, IrValueDeclaration>()
newIrFunction.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += builder.irBlockBody(newIrFunction) {
val params = mutableListOf<IrValueDeclaration>()
val variables = mutableMapOf<IrValueDeclaration, IrValueDeclaration>()
irFunction.dispatchReceiverParameter?.let {
variables[it] = newIrFunction.dispatchReceiverParameter!!
}
irFunction.extensionReceiverParameter?.let {
variables[it] = newIrFunction.extensionReceiverParameter!!
}
// In order to deal with forward references in default value lambdas,
// accesses to the parameter before it has been determined if there is
// a default value or not is redirected to the actual parameter of the
// $default function. This is to ensure that examples such as:
//
// fun f(f1: () -> String = { f2() },
// f2: () -> String = { "OK" }) = f1()
//
// works correctly so that `f() { "OK" }` returns "OK" and
// `f()` throws a NullPointerException.
irFunction.valueParameters.associateWithTo(variables) {
newIrFunction.valueParameters[it.index]
}
var sourceParameterIndex = -1
for (valueParameter in irFunction.valueParameters) {
if (!valueParameter.isMovedReceiver()) {
++sourceParameterIndex
irFunction.dispatchReceiverParameter?.let {
variables[it] = newIrFunction.dispatchReceiverParameter!!
}
val parameter = newIrFunction.valueParameters[valueParameter.index]
val remapped = if (valueParameter.defaultValue != null) {
val mask = irGet(newIrFunction.valueParameters[irFunction.valueParameters.size + valueParameter.index / 32])
val bit = irInt(1 shl (sourceParameterIndex % 32))
val defaultFlag = irCallOp(this@DefaultArgumentStubGenerator.context.ir.symbols.intAnd, context.irBuiltIns.intType, mask, bit)
val expressionBody = valueParameter.defaultValue!!
expressionBody.patchDeclarationParents(newIrFunction)
expressionBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
log { "GetValue: ${expression.symbol.owner}" }
val valueSymbol = variables[expression.symbol.owner] ?: return expression
return irGet(valueSymbol)
}
})
selectArgumentOrDefault(defaultFlag, parameter, expressionBody.expression)
} else {
parameter
irFunction.extensionReceiverParameter?.let {
variables[it] = newIrFunction.extensionReceiverParameter!!
}
params.add(remapped)
variables[valueParameter] = remapped
}
when (irFunction) {
is IrConstructor -> +irDelegatingConstructorCall(irFunction).apply {
passTypeArgumentsFrom(newIrFunction.parentAsClass)
// This is for Kotlin/Native, which differs from the other backends in that constructors
// apparently do have dispatch receivers (though *probably* not type arguments, but copy
// those as well just in case):
passTypeArgumentsFrom(newIrFunction, offset = newIrFunction.parentAsClass.typeParameters.size)
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
// In order to deal with forward references in default value lambdas,
// accesses to the parameter before it has been determined if there is
// a default value or not is redirected to the actual parameter of the
// $default function. This is to ensure that examples such as:
//
// fun f(f1: () -> String = { f2() },
// f2: () -> String = { "OK" }) = f1()
//
// works correctly so that `f() { "OK" }` returns "OK" and
// `f()` throws a NullPointerException.
irFunction.valueParameters.associateWithTo(variables) {
newIrFunction.valueParameters[it.index]
}
is IrSimpleFunction -> +irReturn(dispatchToImplementation(irFunction, newIrFunction, params))
else -> error("Unknown function declaration")
}
}
// Remove default argument initializers.
irFunction.valueParameters.forEach {
if (it.defaultValue != null) {
it.defaultValue = IrExpressionBodyImpl(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub"))
}
var sourceParameterIndex = -1
for (valueParameter in irFunction.valueParameters) {
if (!valueParameter.isMovedReceiver()) {
++sourceParameterIndex
}
val parameter = newIrFunction.valueParameters[valueParameter.index]
val remapped = if (valueParameter.defaultValue != null) {
val mask = irGet(newIrFunction.valueParameters[irFunction.valueParameters.size + valueParameter.index / 32])
val bit = irInt(1 shl (sourceParameterIndex % 32))
val defaultFlag =
irCallOp(this@DefaultArgumentStubGenerator.context.ir.symbols.intAnd, context.irBuiltIns.intType, mask, bit)
val expressionBody = valueParameter.defaultValue!!
expressionBody.patchDeclarationParents(newIrFunction)
expressionBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
log { "GetValue: ${expression.symbol.owner}" }
val valueSymbol = variables[expression.symbol.owner] ?: return expression
return irGet(valueSymbol)
}
})
selectArgumentOrDefault(defaultFlag, parameter, expressionBody.expression)
} else {
parameter
}
params.add(remapped)
variables[valueParameter] = remapped
}
when (irFunction) {
is IrConstructor -> +irDelegatingConstructorCall(irFunction).apply {
passTypeArgumentsFrom(newIrFunction.parentAsClass)
// This is for Kotlin/Native, which differs from the other backends in that constructors
// apparently do have dispatch receivers (though *probably* not type arguments, but copy
// those as well just in case):
passTypeArgumentsFrom(newIrFunction, offset = newIrFunction.parentAsClass.typeParameters.size)
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
is IrSimpleFunction -> +irReturn(dispatchToImplementation(irFunction, newIrFunction, params))
else -> error("Unknown function declaration")
}
}.statements
}
return listOf(irFunction, newIrFunction)
}
@@ -195,19 +196,41 @@ open class DefaultArgumentStubGenerator(
private fun log(msg: () -> String) = context.log { "DEFAULT-REPLACER: ${msg()}" }
}
private fun IrFunction.findBaseFunctionWithDefaultArguments(skipInlineMethods: Boolean, skipExternalMethods: Boolean): IrFunction? {
val visited = mutableSetOf<IrFunction>()
fun IrFunction.dfsImpl(): IrFunction? {
visited += this
if (isInline && skipInlineMethods) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (this is IrSimpleFunction) {
overriddenSymbols.forEach {
val base = it.owner
if (base !in visited) base.dfsImpl()?.let { return it }
}
}
if (valueParameters.any { it.defaultValue != null }) return this
return null
}
return dfsImpl()
}
val DEFAULT_DISPATCH_CALL = object : IrStatementOriginImpl("DEFAULT_DISPATCH_CALL") {}
open class DefaultParameterInjector(
open val context: CommonBackendContext,
private val skipInline: Boolean = true,
private val skipExternalMethods: Boolean = false
) : IrElementTransformerVoid(), BodyLoweringPass, FileLoweringPass {
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
) : IrElementTransformerVoid(), BodyLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun lower(irBody: IrBody) {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(this)
}
@@ -279,12 +302,15 @@ open class DefaultParameterInjector(
val endOffset = expression.endOffset
val declaration = expression.symbol.owner
val visibility = defaultArgumentStubVisibility(declaration)
val stubOverride = declaration.generateDefaultsFunction(context, skipInline, skipExternalMethods, visibility) ?: return null
// We *have* to resolve the fake override here since on the JVM, a default stub for a function implemented
// We *have* to find the actual function here since on the JVM, a default stub for a function implemented
// in an interface does not leave an abstract method after being moved to DefaultImpls (see InterfaceLowering).
// Calling the fake override on an implementation of that interface would then result in a call to a method
// that does not actually exist as DefaultImpls is not part of the inheritance hierarchy.
val stubFunction = if (stubOverride is IrSimpleFunction) stubOverride.resolveFakeOverride()!! else stubOverride
val stubFunction = declaration.findBaseFunctionWithDefaultArguments(skipInline, skipExternalMethods)
?.generateDefaultsFunction(context, skipInline, skipExternalMethods, forceSetOverrideSymbols, visibility)
?: return null
log { "$declaration -> $stubFunction" }
val realArgumentsNumber = declaration.valueParameters.size
@@ -335,11 +361,44 @@ open class DefaultParameterInjector(
private fun log(msg: () -> String) = context.log { "DEFAULT-INJECTOR: ${msg()}" }
}
class DefaultParameterCleaner constructor(val context: CommonBackendContext) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
if (!context.scriptMode) {
irFunction.valueParameters.forEach { it.defaultValue = null }
// Remove default argument initializers.
class DefaultParameterCleaner(
val context: CommonBackendContext,
val replaceDefaultValuesWithStubs: Boolean = false
) : DeclarationTransformer {
override fun lower(irFile: IrFile) {
runPostfix(true).toFileLoweringPass().lower(irFile)
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrValueParameter && !context.scriptMode && declaration.defaultValue != null) {
if (replaceDefaultValuesWithStubs) {
if (context.mapping.defaultArgumentsOriginalFunction[declaration.parent as IrFunction] == null) {
declaration.defaultValue =
IrExpressionBodyImpl(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, declaration.type, "Default Stub"))
}
} else {
declaration.defaultValue = null
}
}
return null
}
}
// Sets overriden symbols. Should be used in case `forceSetOverrideSymbols = false`
class DefaultParameterPatchOverridenSymbolsLowering(
val context: CommonBackendContext
) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrSimpleFunction) {
(context.mapping.defaultArgumentsOriginalFunction[declaration] as? IrSimpleFunction)?.run {
declaration.overriddenSymbols += overriddenSymbols.mapNotNull {
(context.mapping.defaultArgumentsDispatchFunction[it.owner] as? IrSimpleFunction)?.symbol
}
}
}
return null
}
}
@@ -347,27 +406,32 @@ private fun IrFunction.generateDefaultsFunction(
context: CommonBackendContext,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean,
forceSetOverrideSymbols: Boolean,
visibility: Visibility
): IrFunction? {
if (skipInlineMethods && isInline) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
context.ir.defaultParameterDeclarationsCache[this]?.let { return it }
if (context.mapping.defaultArgumentsOriginalFunction[this] != null) return null
context.mapping.defaultArgumentsDispatchFunction[this]?.let { return it }
if (this is IrSimpleFunction) {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
val overriddenStubs = overriddenSymbols.mapNotNull {
it.owner.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
visibility
)?.symbol as IrSimpleFunctionSymbol?
}
if (overriddenStubs.isNotEmpty()) {
if (overriddenSymbols.any { it.owner.findBaseFunctionWithDefaultArguments(skipInlineMethods, skipExternalMethods) != null })
return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FAKE_OVERRIDE, visibility).also {
(it as IrSimpleFunction).overriddenSymbols.addAll(overriddenStubs)
context.ir.defaultParameterDeclarationsCache[this] = it
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
if (forceSetOverrideSymbols) {
(it as IrSimpleFunction).overriddenSymbols += overriddenSymbols.mapNotNull {
it.owner.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
visibility
)?.symbol as IrSimpleFunctionSymbol?
}
}
}
}
}
// Note: this is intentionally done *after* checking for overrides. While normally `override fun`s
// have no default parameters, there is an exception in case of interface delegation:
@@ -381,7 +445,8 @@ private fun IrFunction.generateDefaultsFunction(
// binaries, it's way too late to fix it. Hence the workaround.
if (valueParameters.any { it.defaultValue != null }) {
return generateDefaultsFunctionImpl(context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, visibility).also {
context.ir.defaultParameterDeclarationsCache[this] = it
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
}
}
return null
@@ -420,7 +485,7 @@ private fun IrFunction.generateDefaultsFunctionImpl(
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(newFunction)
newFunction.extensionReceiverParameter = extensionReceiverParameter?.copyTo(newFunction)
valueParameters.mapTo(newFunction.valueParameters) {
newFunction.valueParameters = valueParameters.map {
val newType = it.type.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
val makeNullable = it.defaultValue != null &&
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
@@ -445,7 +510,7 @@ private fun IrFunction.generateDefaultsFunctionImpl(
}
// TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant.
annotations.mapTo(newFunction.annotations) { it.deepCopyWithSymbols() }
newFunction.annotations = annotations.map { it.deepCopyWithSymbols() }
return newFunction
}

View File

@@ -6,20 +6,22 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.ExpectDeclarationRemover
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
/**
* This pass removes all declarations with `isExpect == true`.
*/
class ExpectDeclarationsRemoveLowering(val context: BackendContext, keepOptionalAnnotations: Boolean = false) : FileLoweringPass {
class ExpectDeclarationsRemoveLowering(context: BackendContext, keepOptionalAnnotations: Boolean = false) : DeclarationTransformer {
val visitor = ExpectDeclarationRemover(context.ir.symbols.externalSymbolTable, doRemove = true, keepOptionalAnnotations)
private val remover = ExpectDeclarationRemover(
symbolTable = context.ir.symbols.externalSymbolTable,
doRemove = true,
keepOptionalAnnotations = keepOptionalAnnotations
)
override fun lower(irFile: IrFile) {
irFile.acceptVoid(visitor)
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
return remover.transformFlat(declaration)
}
}

View File

@@ -5,67 +5,81 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.visitors.*
object SYNTHESIZED_INIT_BLOCK : IrStatementOriginImpl("SYNTHESIZED_INIT_BLOCK")
open class InitializersLowering(context: CommonBackendContext) : InitializersLoweringBase(context) {
override fun lower(irClass: IrClass) {
open class InitializersLowering(context: CommonBackendContext) : InitializersLoweringBase(context), BodyLoweringPass {
override fun lower(irFile: IrFile) {
runOnFilePostfix(irFile, true)
}
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container !is IrConstructor) return
val irClass = container.constructedClass
val instanceInitializerStatements = extractInitializers(irClass) {
(it is IrField && !it.isStatic) || (it is IrAnonymousInitializer && !it.isStatic)
}
irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
// Only transform constructors of current class.
override fun visitClassNew(declaration: IrClass) = declaration
override fun visitSimpleFunction(declaration: IrSimpleFunction) = declaration
container.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall): IrExpression {
return IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements)
.deepCopyWithSymbols(container).also {
// Handle declarations, copied from initializers
// Otherwise local classes inside them won't get processed.
// Yes, there are such cases - see testData/codegen/box/properties/complexPropertyInitializer.kt
it.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall): IrExpression =
IrBlockImpl(irClass.startOffset, irClass.endOffset, context.irBuiltIns.unitType, null, instanceInitializerStatements)
.deepCopyWithSymbols(currentScope!!.scope.getLocalDeclarationParent())
override fun visitConstructor(declaration: IrConstructor) {
super.visitConstructor(declaration)
declaration.body?.let { lower(it, declaration) }
}
})
}
}
})
}
}
abstract class InitializersLoweringBase(open val context: CommonBackendContext) : ClassLoweringPass {
abstract class InitializersLoweringBase(open val context: CommonBackendContext) {
protected fun extractInitializers(irClass: IrClass, filter: (IrDeclaration) -> Boolean) =
irClass.declarations.filter(filter).mapNotNull {
// TODO What about fields that were added by lowerings? e.g. captured outer class or locals?
ArrayList(irClass.declarations).mapNotNull { if (it is IrProperty) it.backingField else it }.filter(filter).mapNotNull {
when (it) {
is IrField -> handleField(irClass, it)
is IrAnonymousInitializer -> handleAnonymousInitializer(it)
else -> null
}
}.also {
irClass.declarations.removeAll { it is IrAnonymousInitializer && filter(it) }
}
protected open fun shouldEraseFieldInitializer(irField: IrField): Boolean = irField.correspondingPropertySymbol?.owner?.isConst != true
private fun handleField(irClass: IrClass, declaration: IrField): IrStatement? =
declaration.initializer?.run {
val receiver = if (!declaration.isStatic) // TODO isStaticField
IrGetValueImpl(startOffset, endOffset, irClass.thisReceiver!!.type, irClass.thisReceiver!!.symbol)
else
null
val value = if (shouldEraseFieldInitializer(declaration)) {
declaration.initializer = null
expression
} else {
expression.deepCopyWithSymbols()
}
IrSetFieldImpl(startOffset, endOffset, declaration.symbol, receiver, value, context.irBuiltIns.unitType)
IrSetFieldImpl(startOffset, endOffset, declaration.symbol, receiver, expression, context.irBuiltIns.unitType)
}
private fun handleAnonymousInitializer(declaration: IrAnonymousInitializer): IrStatement =
@@ -73,3 +87,31 @@ abstract class InitializersLoweringBase(open val context: CommonBackendContext)
IrBlockImpl(startOffset, endOffset, context.irBuiltIns.unitType, SYNTHESIZED_INIT_BLOCK, body.statements)
}
}
// Remove anonymous initializers and set field initializers to `null`
class InitializersCleanupLowering(
val context: CommonBackendContext,
private val shouldEraseFieldInitializer: (IrField) -> Boolean = { it.correspondingPropertySymbol?.owner?.isConst != true }
) : DeclarationTransformer {
override fun lower(irFile: IrFile) {
runPostfix(withLocalDeclarations = true).toFileLoweringPass().lower(irFile)
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrAnonymousInitializer) return emptyList()
if (declaration is IrField && declaration.parent is IrClass) {
if (shouldEraseFieldInitializer(declaration)) {
declaration.initializer = null
} else {
declaration.initializer?.let {
declaration.initializer =
IrExpressionBodyImpl(it.startOffset, it.endOffset) { expression = it.expression.deepCopyWithSymbols() }
}
}
}
return null
}
}

View File

@@ -5,14 +5,17 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
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.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -21,164 +24,190 @@ import org.jetbrains.kotlin.name.Name
private const val INLINE_CLASS_IMPL_SUFFIX = "-impl"
// TODO: Support incremental compilation
class InlineClassLowering(val context: CommonBackendContext) {
private val transformedFunction = if (context.scriptMode) context.transformedFunction else mutableMapOf()
private val transformedFunction = context.mapping.inlineClassMemberToStatic
val inlineClassDeclarationLowering = object : ClassLoweringPass {
override fun lower(irClass: IrClass) {
if (!irClass.isInline) return
val inlineClassDeclarationLowering = object : DeclarationTransformer {
irClass.transformDeclarationsFlat { declaration ->
when (declaration) {
is IrConstructor -> listOf(transformConstructor(declaration))
is IrSimpleFunction -> transformMethodFlat(declaration)
is IrProperty -> listOf(declaration) // Getters and setters should be flattened
is IrField -> listOf(declaration)
is IrClass -> listOf(declaration)
else -> error("Unexpected declaration: $declaration")
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
val irClass = declaration.parent as? IrClass ?: return null
if (!irClass.isInline) return null
return when (declaration) {
is IrConstructor -> transformConstructor(declaration)
is IrSimpleFunction -> transformMethodFlat(declaration)
else -> null
}
}
private fun transformConstructor(irConstructor: IrConstructor): IrDeclaration {
if (irConstructor.isPrimary) return irConstructor
private fun transformConstructor(irConstructor: IrConstructor): List<IrDeclaration>? {
if (irConstructor.isPrimary) return null
// Secondary constructors are lowered into static function
val result = transformedFunction.getOrPut(irConstructor.symbol) { createStaticBodilessMethod(irConstructor).symbol }.owner
val irClass = irConstructor.parentAsClass
val result = getOrCreateStaticMethod(irConstructor)
// Copied and adapted from Kotlin/Native InlineClassTransformer
result.body = context.createIrBuilder(result.symbol).irBlockBody(result) {
transformConstructorBody(irConstructor, result)
// Secondary ctors of inline class must delegate to some other constructors.
// Use these delegating call later to initialize this variable.
lateinit var thisVar: IrVariable
val parameterMapping = result.valueParameters.associateBy {
irConstructor.valueParameters[it.index].symbol
}
(irConstructor.body as IrBlockBody).statements.forEach { statement ->
+statement.transform(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid()
return irBlock(expression) {
thisVar = createTmpVariable(
expression,
irType = irClass.defaultType
)
thisVar.parent = result
}
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
if (expression.symbol == irClass.thisReceiver?.symbol) {
return irGet(thisVar)
}
parameterMapping[expression.symbol]?.let { return irGet(it) }
return expression
}
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
declaration.transformChildrenVoid(this)
if (declaration.parent == irConstructor)
declaration.parent = result
return declaration
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid()
if (expression.returnTargetSymbol == irConstructor.symbol) {
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
+expression.value
+irGet(thisVar)
})
}
return expression
}
}, null)
}
+irReturn(irGet(thisVar))
}
return result
return listOf(result)
}
private fun transformMethodFlat(function: IrSimpleFunction): List<IrDeclaration> {
private fun transformMethodFlat(function: IrSimpleFunction): List<IrDeclaration>? {
// TODO: Support fake-overridden methods without boxing
if (function.isStaticMethodOfClass || !function.isReal)
return listOf(function)
return null
val staticMethod = createStaticBodilessMethod(function)
transformedFunction[function.symbol] = staticMethod.symbol
val staticMethod = getOrCreateStaticMethod(function)
// Move function body to static method, transforming value parameters and nested declarations
function.body!!.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
declaration.transformChildrenVoid(this)
if (declaration.parent == function)
declaration.parent = staticMethod
return declaration
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
val valueDeclaration = expression.symbol.owner as? IrValueParameter ?: return super.visitGetValue(expression)
return context.createIrBuilder(staticMethod.symbol).irGet(
when (valueDeclaration) {
function.dispatchReceiverParameter, function.parentAsClass.thisReceiver ->
staticMethod.valueParameters[0]
function.extensionReceiverParameter ->
staticMethod.valueParameters[1]
in function.valueParameters -> {
val offset = if (function.extensionReceiverParameter != null) 2 else 1
staticMethod.valueParameters[valueDeclaration.index + offset]
}
else -> return expression
}
)
}
})
staticMethod.body = function.body
transformMethodBodyFlat(function, staticMethod)
function.body = delegateToStaticMethod(function, staticMethod)
if (function.overriddenSymbols.isEmpty()) // Function is used only in unboxed context
return listOf(staticMethod)
// Delegate original function to static implementation
function.body = context.createIrBuilder(function.symbol).irBlockBody {
+irReturn(
irCall(staticMethod).apply {
val parameters =
listOfNotNull(
function.dispatchReceiverParameter!!,
function.extensionReceiverParameter
) + function.valueParameters
for ((index, valueParameter) in parameters.withIndex()) {
putValueArgument(index, irGet(valueParameter))
}
}
)
}
return listOf(function, staticMethod)
}
private fun transformConstructorBody(irConstructor: IrConstructor, staticMethod: IrSimpleFunction) {
if (irConstructor.isPrimary) return // TODO error() maybe?
val irClass = irConstructor.parentAsClass
// Copied and adapted from Kotlin/Native InlineClassTransformer
staticMethod.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(staticMethod.symbol).irBlockBody(staticMethod) {
// Secondary ctors of inline class must delegate to some other constructors.
// Use these delegating call later to initialize this variable.
lateinit var thisVar: IrVariable
val parameterMapping = staticMethod.valueParameters.associateBy {
irConstructor.valueParameters[it.index].symbol
}
(irConstructor.body as IrBlockBody).statements.forEach { statement ->
+statement.transform(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid()
return irBlock(expression) {
thisVar = createTmpVariable(
expression,
irType = irClass.defaultType
)
thisVar.parent = staticMethod
}
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid()
if (expression.symbol == irClass.thisReceiver?.symbol) {
return irGet(thisVar)
}
parameterMapping[expression.symbol]?.let { return irGet(it) }
return expression
}
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
declaration.transformChildrenVoid(this)
if (declaration.parent == irConstructor)
declaration.parent = staticMethod
return declaration
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid()
if (expression.returnTargetSymbol == irConstructor.symbol) {
return irReturn(irBlock(expression.startOffset, expression.endOffset) {
+expression.value
+irGet(thisVar)
})
}
return expression
}
}, null)
}
+irReturn(irGet(thisVar))
}.statements
}
}
private fun transformMethodBodyFlat(function: IrSimpleFunction, staticMethod: IrSimpleFunction) {
// TODO: Support fake-overridden methods without boxing
if (function.isStaticMethodOfClass || !function.isReal) return // TODO error()
val functionBody = function.body
// Move function body to static method, transforming value parameters and nested declarations
staticMethod.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements.addAll((functionBody as IrBlockBody).statements)
transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
declaration.transformChildrenVoid(this)
if (declaration.parent == function)
declaration.parent = staticMethod
return declaration
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
val valueDeclaration = expression.symbol.owner as? IrValueParameter ?: return super.visitGetValue(expression)
return context.createIrBuilder(staticMethod.symbol).irGet(
when (valueDeclaration) {
function.dispatchReceiverParameter, function.parentAsClass.thisReceiver ->
staticMethod.valueParameters[0]
function.extensionReceiverParameter ->
staticMethod.valueParameters[1]
in function.valueParameters -> {
val offset = if (function.extensionReceiverParameter != null) 2 else 1
staticMethod.valueParameters[valueDeclaration.index + offset]
}
else -> return expression
}
)
}
})
}
}
private fun delegateToStaticMethod(function: IrSimpleFunction, staticMethod: IrSimpleFunction): IrBlockBody {
// Delegate original function to static implementation
return IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(function.symbol).irBlockBody {
+irReturn(
irCall(staticMethod).apply {
val parameters =
listOfNotNull(
function.dispatchReceiverParameter!!,
function.extensionReceiverParameter
) + function.valueParameters
for ((index, valueParameter) in parameters.withIndex()) {
putValueArgument(index, irGet(valueParameter))
}
}
)
}.statements
}
}
}
val inlineClassUsageLowering = object : FileLoweringPass {
private fun getOrCreateStaticMethod(function: IrFunction): IrSimpleFunction =
transformedFunction.getOrPut(function) {
createStaticBodilessMethod(function)
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
val inlineClassUsageLowering = object : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
expression.transformChildrenVoid(this)
@@ -219,13 +248,6 @@ class InlineClassLowering(val context: CommonBackendContext) {
else -> irCall(expression, getOrCreateStaticMethod(function))
}
}
private fun getOrCreateStaticMethod(function: IrFunction): IrSimpleFunctionSymbol =
transformedFunction.getOrPut(function.symbol) {
createStaticBodilessMethod(function).also {
function.parentAsClass.declarations.add(it)
}.symbol
}
})
}
}

View File

@@ -17,15 +17,97 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.utils.addToStdlib.cast
class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
class InnerClassesLowering(val context: BackendContext) : DeclarationTransformer {
override fun lower(irFile: IrFile) {
runPostfix(true).toFileLoweringPass().lower(irFile)
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrClass && declaration.isInner) {
stageController.unrestrictDeclarationListsAccess {
declaration.declarations += context.declarationFactory.getOuterThisField(declaration)
}
} else if (declaration is IrConstructor) {
val irClass = declaration.parentAsClass
if (!irClass.isInner) return null
val newConstructor = lowerConstructor(declaration)
val oldConstructorParameterToNew = context.primaryConstructorParameterMap(declaration)
for ((oldParam, newParam) in oldConstructorParameterToNew.entries) {
newParam.defaultValue = oldParam.defaultValue?.let { oldDefault ->
IrExpressionBodyImpl(oldDefault.startOffset, oldDefault.endOffset) {
expression = oldDefault.expression.patchDeclarationParents(newConstructor)
}
}
}
return listOf(newConstructor)
}
return null
}
private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor {
val loweredConstructor = context.declarationFactory.getInnerClassConstructorWithOuterThisParameter(irConstructor)
val outerThisParameter = loweredConstructor.valueParameters[0]
val irClass = irConstructor.parentAsClass
val parentThisField = context.declarationFactory.getOuterThisField(irClass)
val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}")
loweredConstructor.body = IrBlockBodyImpl(blockBody.startOffset, blockBody.endOffset) {
context.createIrBuilder(irConstructor.symbol, irConstructor.startOffset, irConstructor.endOffset).apply {
statements.add(0, irSetField(irGet(irClass.thisReceiver!!), parentThisField, irGet(outerThisParameter)))
}
statements.addAll(blockBody.statements)
if (statements.find { it is IrInstanceInitializerCall } == null) {
val delegatingConstructorCall =
statements.find { it is IrDelegatingConstructorCall } as IrDelegatingConstructorCall?
?: throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}")
delegatingConstructorCall.apply { dispatchReceiver = IrGetValueImpl(startOffset, endOffset, outerThisParameter.symbol) }
}
patchDeclarationParents(loweredConstructor)
val oldConstructorParameterToNew = context.primaryConstructorParameterMap(irConstructor)
transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew))
}
return loweredConstructor
}
}
private fun BackendContext.primaryConstructorParameterMap(originalConstructor: IrConstructor): Map<IrValueParameter, IrValueParameter> {
val oldConstructorParameterToNew = HashMap<IrValueParameter, IrValueParameter>()
val loweredConstructor = declarationFactory.getInnerClassConstructorWithOuterThisParameter(originalConstructor)
originalConstructor.valueParameters.forEach { old ->
oldConstructorParameterToNew[old] = loweredConstructor.valueParameters[old.index + 1]
}
return oldConstructorParameterToNew
}
class InnerClassesMemberBodyLowering(val context: BackendContext) : BodyLoweringPass {
override fun lower(irFile: IrFile) {
runOnFilePostfix(irFile, true)
}
private val IrValueSymbol.classForImplicitThis: IrClass?
// TODO: is this the correct way to get the class?
get() =
@@ -37,40 +119,31 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
} else
null
override fun lower(irClass: IrClass) {
override fun lower(irBody: IrBody, container: IrDeclaration) {
val irClass = container.parent as? IrClass ?: return
if (!irClass.isInner) return
val parentThisField = context.declarationFactory.getOuterThisField(irClass)
val oldConstructorParameterToNew = HashMap<IrValueParameter, IrValueParameter>()
fun lowerConstructor(irConstructor: IrConstructor): IrConstructor {
val loweredConstructor = context.declarationFactory.getInnerClassConstructorWithOuterThisParameter(irConstructor)
val outerThisParameter = loweredConstructor.valueParameters[0]
irConstructor.valueParameters.forEach { old ->
oldConstructorParameterToNew[old] = loweredConstructor.valueParameters[old.index + 1]
if (container is IrField || container is IrAnonymousInitializer || container is IrValueParameter) {
val primaryConstructor = context.declarationFactory.getInnerClassOriginalPrimaryConstructorOrNull(irClass)
if (primaryConstructor != null) {
val oldConstructorParameterToNew = context.primaryConstructorParameterMap(primaryConstructor)
irBody.transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew))
}
val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}")
context.createIrBuilder(irConstructor.symbol, irConstructor.startOffset, irConstructor.endOffset).apply {
blockBody.statements.add(0, irSetField(irGet(irClass.thisReceiver!!), parentThisField, irGet(outerThisParameter)))
}
if (blockBody.statements.find { it is IrInstanceInitializerCall } == null) {
val delegatingConstructorCall =
blockBody.statements.find { it is IrDelegatingConstructorCall } as IrDelegatingConstructorCall?
?: throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}")
delegatingConstructorCall.apply { dispatchReceiver = IrGetValueImpl(startOffset, endOffset, outerThisParameter.symbol) }
}
blockBody.patchDeclarationParents(loweredConstructor)
loweredConstructor.body = blockBody
return loweredConstructor
}
irClass.declarations += parentThisField
irClass.transformDeclarationsFlat { irMember -> (irMember as? IrConstructor)?.let { listOf(lowerConstructor(it)) } }
irClass.transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew))
irBody.fixThisReference(irClass, container)
}
irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
fun IrBody.fixThisReference(irClass: IrClass, container: IrDeclaration) {
val enclosingFunction: IrDeclaration? = run {
var current: IrDeclaration? = container
while (current != null && current !is IrFunction) {
current = current.parent as? IrDeclaration
}
current
}
transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitClassNew(declaration: IrClass): IrStatement =
declaration
@@ -83,7 +156,7 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val origin = expression.origin
val function = currentFunction?.irElement as? IrFunction
val function = (currentFunction?.irElement ?: enclosingFunction) as? IrFunction
val enclosingThisReceiver = function?.dispatchReceiverParameter ?: irClass.thisReceiver!!
var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, enclosingThisReceiver.symbol, origin)
@@ -117,9 +190,9 @@ val innerClassConstructorCallsPhase = makeIrFilePhase(
description = "Handle constructor calls for inner classes"
)
class InnerClassConstructorCallsLowering(val context: BackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
class InnerClassConstructorCallsLowering(val context: BackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
expression.transformChildrenVoid(this)

View File

@@ -16,8 +16,7 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
@@ -35,43 +34,77 @@ import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringPass {
class NullableFieldsForLateinitCreationLowering(val backendContext: CommonBackendContext) : DeclarationTransformer {
private val nullableFields = backendContext.lateinitNullableFields
private fun buildOrGetNullableField(originalField: IrField): IrField {
if (originalField.type.isMarkedNullable()) return originalField
return nullableFields.getOrPut(originalField) {
buildField {
updateFrom(originalField)
type = originalField.type.makeNullable()
name = originalField.name
}.apply {
parent = originalField.parent
correspondingPropertySymbol = originalField.correspondingPropertySymbol
annotations += originalField.annotations
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrField) {
declaration.correspondingPropertySymbol?.owner?.let { property ->
if (property.isRealLateinit) {
val newField = backendContext.buildOrGetNullableField(declaration)
if (declaration != newField && declaration.parent != property.parent) return listOf(newField)
}
}
}
return null
}
}
// Transform declarations
class NullableFieldsDeclarationLowering(val backendContext: CommonBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
when (declaration) {
is IrProperty -> {
if (declaration.isRealLateinit) {
declaration.backingField = backendContext.buildOrGetNullableField(declaration.backingField!!)
}
}
is IrSimpleFunction -> {
declaration.correspondingPropertySymbol?.owner?.let { property ->
if (declaration == property.getter && property.isRealLateinit) {
// f = buildOrGetNullableField is idempotent, i.e. f(f(x)) == f(x)
transformGetter(backendContext.buildOrGetNullableField(property.backingField!!), declaration)
}
}
}
}
return null
}
private fun transformGetter(backingField: IrField, getter: IrFunction) {
val type = backingField.type
assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
val startOffset = getter.startOffset
val endOffset = getter.endOffset
getter.body = IrBlockBodyImpl(startOffset, endOffset) {
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run {
val resultVar = scope.createTemporaryVariable(
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField)
)
resultVar.parent = getter
statements.add(resultVar)
val throwIfNull = irIfThenElse(
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
)
statements.add(throwIfNull)
}
}
}
}
override fun lower(irFile: IrFile) {
// Transform usages
class LateinitUsageLowering(val backendContext: CommonBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
val nullableVariables = mutableMapOf<IrVariable, IrVariable>()
// Transform declarations
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitProperty(declaration: IrProperty): IrStatement {
declaration.transformChildrenVoid(this)
if (declaration.isLateinit && declaration.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
val oldField = declaration.backingField!!
val newField = buildOrGetNullableField(oldField)
declaration.backingField = newField
transformGetter(newField, declaration.getter!!)
}
return declaration
}
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid(this)
@@ -92,41 +125,17 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
).also {
descriptor.bind(it)
it.parent = declaration.parent
it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
it.initializer =
IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType)
}
nullableVariables[declaration] = newVar
return newVar
}
private fun transformGetter(backingField: IrField, getter: IrFunction) {
val type = backingField.type
assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" }
val startOffset = getter.startOffset
val endOffset = getter.endOffset
val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset)
irBuilder.run {
val body = IrBlockBodyImpl(startOffset, endOffset)
val resultVar = scope.createTemporaryVariable(
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField)
)
resultVar.parent = getter
body.statements.add(resultVar)
val throwIfNull = irIfThenElse(
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString())
)
body.statements.add(throwIfNull)
getter.body = body
}
}
})
// Transform usages
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
@@ -152,7 +161,7 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
override fun visitGetField(expression: IrGetField): IrExpression {
expression.transformChildrenVoid(this)
val newField = nullableFields[expression.symbol.owner] ?: return expression
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
return with(expression) {
IrGetFieldImpl(startOffset, endOffset, newField.symbol, newField.type, receiver, origin, superQualifierSymbol)
}
@@ -160,7 +169,7 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
override fun visitSetField(expression: IrSetField): IrExpression {
expression.transformChildrenVoid(this)
val newField = nullableFields[expression.symbol.owner] ?: return expression
val newField = backendContext.mapping.lateInitFieldToNullableField[expression.symbol.owner] ?: return expression
return with(expression) {
IrSetFieldImpl(startOffset, endOffset, newField.symbol, receiver, value, type, origin, superQualifierSymbol)
}
@@ -177,7 +186,9 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
receiver.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol!!.owner.also { assert(it.isLateinit) }
val nullableField =
buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field"))
backendContext.buildOrGetNullableField(
property.backingField ?: error("Lateinit property is supposed to have backing field")
)
return expression.run { backendContext.createIrBuilder(symbol, startOffset, endOffset) }.run {
irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull())
@@ -186,3 +197,20 @@ class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringP
})
}
}
private fun CommonBackendContext.buildOrGetNullableField(originalField: IrField): IrField {
if (originalField.type.isMarkedNullable()) return originalField
return mapping.lateInitFieldToNullableField.getOrPut(originalField) {
buildField {
updateFrom(originalField)
type = originalField.type.makeNullable()
name = originalField.name
}.apply {
parent = originalField.parent
correspondingPropertySymbol = originalField.correspondingPropertySymbol
annotations += originalField.annotations
}
}
}
private val IrProperty.isRealLateinit get() = isLateinit && origin != IrDeclarationOrigin.FAKE_OVERRIDE

View File

@@ -5,44 +5,46 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrScript
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
//This lower takes part of old LocalDeclarationLowering job to pop up local classes from functions
open class LocalClassPopupLowering(val context: BackendContext) : FileLoweringPass {
open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPass {
override fun lower(irFile: IrFile) {
runOnFilePostfix(irFile, withLocalDeclarations = true, allowDeclarationModification = true)
}
override fun lower(irBody: IrBody, container: IrDeclaration) {
val extractedLocalClasses = arrayListOf<Pair<IrClass, IrDeclarationContainer>>()
irFile.transform(object : IrElementTransformerVoidWithContext() {
irBody.transform(object : IrElementTransformerVoidWithContext() {
override fun visitClassNew(declaration: IrClass): IrStatement {
val newDeclaration = super.visitClassNew(declaration)
if (newDeclaration !is IrClass) return newDeclaration
val currentScope =
if (allScopes.size > 1) allScopes[allScopes.lastIndex - 1] else createScope(container as IrSymbolOwner)
if (!shouldPopUp(declaration, currentScope)) return declaration
val currentScope = allScopes[allScopes.lastIndex - 1]
if (!shouldPopUp(declaration, currentScope)) return newDeclaration
val newContainer = run {
var currentParent = declaration.parent
while (currentParent is IrDeclaration && currentParent !is IrClass && currentParent !is IrScript) {
currentParent = currentParent.parent
}
val newContainer = allScopes.asReversed().drop(1/*skip self*/).firstOrNull {
//find first class local or not;
// to reproduce original LocalDeclarationLowering behaviour add: '&& !it.irElement.isLocal' condition
it.irElement is IrClass || it.irElement is IrScript
}?.irElement?.let { it as? IrClass ?: it as? IrScript } ?: currentFile
extractedLocalClasses.add(newDeclaration to newContainer)
currentParent as IrDeclarationContainer // IrClass or IrScript or IrPackageFragment
}
extractedLocalClasses.add(declaration to newContainer)
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.irBuiltIns.unitType)
}
}, null)
for ((local, newContainer) in extractedLocalClasses) {
newContainer.addChild(local)
context.extractedLocalClasses += local
}
}

View File

@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementVisitorVoidWithContext
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.Modality
@@ -43,6 +40,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -95,7 +93,11 @@ class LocalDeclarationsLowering(
val localNameProvider: LocalNameProvider = LocalNameProvider.DEFAULT,
val visibilityPolicy: VisibilityPolicy = VisibilityPolicy.DEFAULT
) :
FileLoweringPass {
BodyLoweringPass {
override fun lower(irFile: IrFile) {
runOnFilePostfix(irFile, allowDeclarationModification = true)
}
object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE", isSynthetic = true)
@@ -106,10 +108,23 @@ class LocalDeclarationsLowering(
private object STATEMENT_ORIGIN_INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE :
IrStatementOriginImpl("INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE")
override fun lower(irFile: IrFile) {
LocalDeclarationsTransformer(irFile).lowerLocalDeclarations()
override fun lower(irBody: IrBody, container: IrDeclaration) {
LocalDeclarationsTransformer(irBody, container).lowerLocalDeclarations()
}
private class ScopeWithCounter(scope: Scope, irElement: IrElement) : ScopeWithIr(scope, irElement) {
// Continuous numbering across all declarations in the container.
var counter: Int = 0
}
private val scopeMap: MutableMap<IrSymbolOwner, ScopeWithCounter> = mutableMapOf()
// Need to keep LocalFunctionContext.index
private val IrSymbolOwner.scopeWithCounter: ScopeWithCounter
get() = scopeMap.getOrPut(this) {
ScopeWithCounter(Scope(symbol), this)
}
private abstract class LocalContext {
val capturedTypeParameterToTypeParameter: MutableMap<IrTypeParameter, IrTypeParameter> = mutableMapOf()
@@ -203,7 +218,7 @@ class LocalDeclarationsLowering(
abbreviation.annotations
)
private inner class LocalDeclarationsTransformer(val irFile: IrFile) {
private inner class LocalDeclarationsTransformer(val irBody: IrBody, val container: IrDeclaration) {
val localFunctions: MutableMap<IrFunction, LocalFunctionContext> = LinkedHashMap()
val localClasses: MutableMap<IrClass, LocalClassContext> = LinkedHashMap()
val localClassConstructors: MutableMap<IrConstructor, LocalClassConstructorContext> = LinkedHashMap()
@@ -481,7 +496,7 @@ class LocalDeclarationsLowering(
rewriteClassMembers(it.declaration, it)
}
rewriteFunctionBody(irFile, null)
rewriteFunctionBody(container, null)
}
private fun createNewCall(oldCall: IrCall, newCallee: IrFunction) =
@@ -613,7 +628,7 @@ class LocalDeclarationsLowering(
)
newDeclaration.recordTransformedValueParameters(localFunctionContext)
newDeclaration.annotations.addAll(oldDeclaration.annotations)
newDeclaration.annotations = oldDeclaration.annotations
transformedDeclarations[oldDeclaration] = newDeclaration
}
@@ -794,7 +809,7 @@ class LocalDeclarationsLowering(
private fun collectClosureForLocalDeclarations() {
//TODO: maybe use for granular declarations
val annotator = ClosureAnnotator(irFile)
val annotator = ClosureAnnotator(irBody, container)
localFunctions.forEach { (declaration, context) ->
context.closure = annotator.getFunctionClosure(declaration)
@@ -806,19 +821,25 @@ class LocalDeclarationsLowering(
}
private fun collectLocalDeclarations() {
class ScopeWithCounter(scope: Scope, irElement: IrElement) : ScopeWithIr(scope, irElement) {
// Continuous numbering across all declarations in the container.
var counter: Int = 0
}
val enclosingFileScope = container.file.scopeWithCounter
irFile.acceptVoid(object : IrElementVisitorVoidWithContext() {
val enclosingClassScope = run {
var currentParent = container as? IrClass ?: container.parent
while (currentParent is IrDeclaration && currentParent !is IrClass) {
currentParent = currentParent.parent
}
currentParent as? IrClass
}?.scopeWithCounter
irBody.acceptVoid(object : IrElementVisitorVoidWithContext() {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun createScope(declaration: IrSymbolOwner): ScopeWithIr {
return ScopeWithCounter(Scope(declaration.symbol), declaration)
return ScopeWithCounter(Scope(declaration.symbol), declaration) // Don't cache local declarations
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
@@ -826,7 +847,7 @@ class LocalDeclarationsLowering(
if (declaration.visibility == Visibilities.LOCAL) {
val scopeWithIr =
(currentClass ?: currentFile /*file is required for K/N cause file declarations are not split by classes*/
(currentClass ?: enclosingClassScope ?: enclosingFileScope /*file is required for K/N cause file declarations are not split by classes*/
?: error("No scope for ${declaration.dump()}"))
localFunctions[declaration] =
LocalFunctionContext(
@@ -858,7 +879,8 @@ class LocalDeclarationsLowering(
}
private val inInlineFunctionScope: Boolean
get() = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false }
get() = allScopes.any { scope -> (scope.irElement as? IrFunction)?.isInline ?: false } ||
generateSequence(container) { it.parent as? IrDeclaration }.any { it is IrFunction && it.isInline }
})
}
}

View File

@@ -5,43 +5,46 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.*
class PropertiesLowering : IrElementTransformerVoid(), FileLoweringPass {
class PropertiesLowering : DeclarationTransformer {
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
runPostfix(true).toFileLoweringPass().lower(irFile)
}
override fun visitFile(declaration: IrFile): IrFile {
declaration.transformChildrenVoid(this)
declaration.transformDeclarationsFlat { lowerProperty(it) }
return declaration
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
when (declaration) {
is IrSimpleFunction -> {
declaration.correspondingPropertySymbol?.owner?.let { property ->
if (!property.isEffectivelyExternal()) {
return listOf(declaration)
}
}
}
is IrField -> {
declaration.correspondingPropertySymbol?.owner?.let { property ->
if (!property.isEffectivelyExternal()) {
return listOf(declaration)
}
}
}
is IrProperty -> {
if (!declaration.isEffectivelyExternal()) {
return listOf()
}
}
}
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid(this)
declaration.transformDeclarationsFlat { lowerProperty(it) }
return declaration
return null
}
override fun visitScript(declaration: IrScript): IrStatement {
declaration.transformChildrenVoid(this)
declaration.transformDeclarationsFlat { lowerProperty(it) }
return declaration
}
private fun lowerProperty(declaration: IrDeclaration): List<IrDeclaration>? =
if (declaration is IrProperty && !declaration.isEffectivelyExternal())
listOfNotNull(declaration.backingField, declaration.getter, declaration.setter)
else null
companion object {
fun checkNoProperties(irFile: IrFile) {
irFile.acceptVoid(object : IrElementVisitorVoid {
@@ -57,9 +60,10 @@ class PropertiesLowering : IrElementTransformerVoid(), FileLoweringPass {
}
}
class LocalDelegatedPropertiesLowering : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
class LocalDelegatedPropertiesLowering : IrElementTransformerVoid(), BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.accept(this, null)
}
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement {

View File

@@ -5,8 +5,11 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
@@ -16,10 +19,10 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class ProvisionalFunctionExpressionLowering :
IrElementTransformerVoid(),
FileLoweringPass {
BodyLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(this)
}
override fun visitFunctionExpression(expression: IrFunctionExpression): IrExpression {

View File

@@ -5,22 +5,18 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.ir.builders.irBoolean
import org.jetbrains.kotlin.ir.builders.irBreak
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irSetVar
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDoWhileLoopImpl
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
/**
* Replaces returnable blocks and `return`'s with loops and `break`'s correspondingly.
@@ -69,13 +65,13 @@ import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
* }
*
*/
class ReturnableBlockLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transform(ReturnableBlockTransformer(context), null)
class ReturnableBlockLowering(val context: CommonBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
container.transform(ReturnableBlockTransformer(context, (container as IrSymbolOwner).symbol), null)
}
}
class ReturnableBlockTransformer(val context: CommonBackendContext) : IrElementTransformerVoidWithContext() {
class ReturnableBlockTransformer(val context: CommonBackendContext, val containerSymbol: IrSymbol? = null) : IrElementTransformerVoidWithContext() {
private var labelCnt = 0
private val returnMap = mutableMapOf<IrReturnableBlockSymbol, (IrReturn) -> IrExpression>()
@@ -87,7 +83,8 @@ class ReturnableBlockTransformer(val context: CommonBackendContext) : IrElementT
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlock) return super.visitContainerExpression(expression)
val builder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol)
val scopeSymbol = currentScope?.scope?.scopeOwnerSymbol ?: containerSymbol
val builder = context.createIrBuilder(scopeSymbol!!)
val variable by lazy {
builder.scope.createTemporaryVariableDeclaration(expression.type, "tmp\$ret\$${labelCnt++}", true)
}

View File

@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
val sharedVariablesPhase = makeIrFilePhase(
::SharedVariablesLowering,
@@ -36,35 +38,16 @@ val sharedVariablesPhase = makeIrFilePhase(
object CoroutineIntrinsicLambdaOrigin : IrStatementOriginImpl("Coroutine intrinsic lambda")
class SharedVariablesLowering(val context: BackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
class SharedVariablesLowering(val context: BackendContext) : BodyLoweringPass {
override fun visitFunction(declaration: IrFunction) {
declaration.acceptChildrenVoid(this)
SharedVariablesTransformer(declaration).lowerSharedVariables()
}
override fun visitField(declaration: IrField) {
declaration.acceptChildrenVoid(this)
SharedVariablesTransformer(declaration).lowerSharedVariables()
}
override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) {
declaration.acceptChildrenVoid(this)
SharedVariablesTransformer(declaration).lowerSharedVariables()
}
})
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO remove this condition
if (container is IrFunction || container is IrField || container is IrAnonymousInitializer) {
SharedVariablesTransformer(irBody, container).lowerSharedVariables()
}
}
private inner class SharedVariablesTransformer(val irDeclaration: IrDeclaration) {
private inner class SharedVariablesTransformer(val irBody: IrBody, val irDeclaration: IrDeclaration) {
private val sharedVariables = HashSet<IrVariable>()
fun lowerSharedVariables() {
@@ -75,7 +58,7 @@ class SharedVariablesLowering(val context: BackendContext) : FileLoweringPass {
}
private fun collectSharedVariables() {
irDeclaration.accept(object : IrElementVisitor<Unit, IrDeclarationParent?> {
irBody.accept(object : IrElementVisitor<Unit, IrDeclarationParent?> {
val relevantVars = HashSet<IrVariable>()
val relevantVals = HashSet<IrVariable>()
@@ -134,7 +117,7 @@ class SharedVariablesLowering(val context: BackendContext) : FileLoweringPass {
private fun rewriteSharedVariables() {
val transformedSymbols = HashMap<IrValueSymbol, IrVariableSymbol>()
irDeclaration.transformChildrenVoid(object : IrElementTransformerVoid() {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid(this)
@@ -148,7 +131,7 @@ class SharedVariablesLowering(val context: BackendContext) : FileLoweringPass {
}
})
irDeclaration.transformChildrenVoid(object : IrElementTransformerVoid() {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this)

View File

@@ -196,7 +196,7 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) :
}.apply {
overriddenSymbols += superMethod.symbol
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
valueParameters = superMethod.valueParameters.map { it.copyTo(this) }
body = context.createIrBuilder(symbol).irBlockBody {
+irReturn(irCall(wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }).apply {
dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field)

View File

@@ -5,40 +5,13 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrTypeAlias
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
class StripTypeAliasDeclarationsLowering :
IrElementVisitorVoid,
FileLoweringPass {
class StripTypeAliasDeclarationsLowering : DeclarationTransformer {
override fun lower(irFile: IrFile) {
irFile.acceptVoid(this)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFile(declaration: IrFile) {
declaration.acceptChildrenVoid(this)
declaration.declarations.removeAll { it is IrTypeAlias }
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.declarations.removeAll { it is IrTypeAlias }
}
override fun visitContainerExpression(expression: IrContainerExpression) {
expression.acceptChildrenVoid(this)
expression.statements.removeAll { it is IrTypeAlias }
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
return if (declaration is IrTypeAlias) listOf() else null
}
}

View File

@@ -17,20 +17,20 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.collectTailRecursionCalls
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.util.explicitParameters
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
/**
@@ -39,9 +39,24 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
* Note: it currently can't handle local functions and classes declared in default arguments.
* See [deepCopyWithVariables].
*/
open class TailrecLowering(val context: BackendContext) : FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
lowerTailRecursionCalls(context, irFunction, useProperComputationOrderOfTailrecDefaultParameters())
open class TailrecLowering(val context: BackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrFunction) {
// TODO Shouldn't this be done after local declarations lowering?
// Lower local declarations
irBody.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunction(declaration: IrFunction) {
declaration.acceptChildrenVoid(this)
lowerTailRecursionCalls(context, declaration, useProperComputationOrderOfTailrecDefaultParameters())
}
})
lowerTailRecursionCalls(context, container, useProperComputationOrderOfTailrecDefaultParameters())
}
}
open fun useProperComputationOrderOfTailrecDefaultParameters() = true
@@ -54,11 +69,13 @@ private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunct
}
val oldBody = irFunction.body as IrBlockBody
val oldBodyStatements = ArrayList(oldBody.statements)
val builder = context.createIrBuilder(irFunction.symbol).at(oldBody)
val parameters = irFunction.explicitParameters
irFunction.body = builder.irBlockBody {
oldBody.statements.clear()
oldBody.statements += builder.irBlockBody {
// Define variables containing current values of parameters:
val parameterToVariable = parameters.associate {
it to createTmpVariable(irGet(it), nameHint = it.symbol.suggestVariableName(), isMutable = true)
@@ -82,14 +99,14 @@ private fun lowerTailRecursionCalls(context: BackendContext, irFunction: IrFunct
properComputationOrderOfTailrecDefaultParameters
)
oldBody.statements.forEach {
oldBodyStatements.forEach {
+it.transform(transformer, null)
}
+irBreak(loop)
}
}
}
}.statements
}
private class BodyTransformer(

View File

@@ -31,7 +31,18 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions
class FunctionInlining(val context: CommonBackendContext) : IrElementTransformerVoidWithContext() {
class FunctionInlining(val context: CommonBackendContext) : IrElementTransformerVoidWithContext(), BodyLoweringPass {
private var containerScope: ScopeWithIr? = null
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO container: IrSymbolDeclaration
containerScope = createScope(container as IrSymbolOwner)
irBody.accept(this, null)
containerScope = null
irBody.patchDeclarationParents(container as? IrDeclarationParent ?: container.parent)
}
fun inline(irModule: IrModuleFragment) = irModule.accept(this, data = null)
@@ -52,8 +63,11 @@ class FunctionInlining(val context: CommonBackendContext) : IrElementTransformer
val actualCallee = getFunctionDeclaration(callee.symbol)
val parent = allScopes.map { it.irElement }.filterIsInstance<IrDeclarationParent>().lastOrNull()
?: allScopes.map { it.irElement }.filterIsInstance<IrDeclaration>().lastOrNull()?.parent
?: containerScope?.irElement as? IrDeclarationParent
?: (containerScope?.irElement as? IrDeclaration)?.parent
val inliner = Inliner(expression, actualCallee, currentScope!!, parent, context)
val inliner = Inliner(expression, actualCallee, currentScope ?: containerScope!!, parent, context)
return inliner.inline()
}

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.common.lower.loops
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
@@ -12,9 +13,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.util.dump
@@ -96,15 +95,15 @@ val forLoopsPhase = makeIrFilePhase(
* }
* ```
*/
class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass {
class ForLoopsLowering(val context: CommonBackendContext) : BodyLoweringPass {
override fun lower(irFile: IrFile) {
override fun lower(irBody: IrBody, container: IrDeclaration) {
val oldLoopToNewLoop = mutableMapOf<IrLoop, IrLoop>()
val transformer = RangeLoopTransformer(context, oldLoopToNewLoop)
irFile.transformChildrenVoid(transformer)
val transformer = RangeLoopTransformer(context, container as IrSymbolOwner, oldLoopToNewLoop)
irBody.transformChildrenVoid(transformer)
// Update references in break/continue.
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitBreakContinue(jump: IrBreakContinue): IrExpression {
oldLoopToNewLoop[jump.loop]?.let { jump.loop = it }
return jump
@@ -115,6 +114,7 @@ class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass {
private class RangeLoopTransformer(
val context: CommonBackendContext,
val container: IrSymbolOwner,
val oldLoopToNewLoop: MutableMap<IrLoop, IrLoop>
) : IrElementTransformerVoidWithContext() {
@@ -122,7 +122,7 @@ private class RangeLoopTransformer(
private val headerInfoBuilder = DefaultHeaderInfoBuilder(context, this::getScopeOwnerSymbol)
private val headerProcessor = HeaderProcessor(context, headerInfoBuilder, this::getScopeOwnerSymbol)
fun getScopeOwnerSymbol() = currentScope!!.scope.scopeOwnerSymbol
fun getScopeOwnerSymbol() = currentScope?.scope?.scopeOwnerSymbol ?: container.symbol
override fun visitBlock(expression: IrBlock): IrExpression {
// LoopExpressionGenerator in psi2ir lowers `for (loopVar in <someIterable>) { // Loop body }` into an IrBlock with origin FOR_LOOP.

View File

@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.backend.common.lower.optimizations
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
@@ -39,7 +39,7 @@ val foldConstantLoweringPhase = makeIrFilePhase(
class FoldConstantLowering(
private val context: CommonBackendContext,
// In K/JS Float and Double are the same so Float constant should be fold similar to Double
private val floatSpecial: Boolean = false) : IrElementTransformerVoid(), FileLoweringPass {
private val floatSpecial: Boolean = false) : IrElementTransformerVoid(), BodyLoweringPass {
/**
* ID of an binary operator / method.
*
@@ -243,8 +243,8 @@ class FoldConstantLowering(
}
@ExperimentalUnsignedTypes
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)

View File

@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.backend.common.lower.optimizations
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
@@ -15,13 +15,11 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
class PropertyAccessorInlineLowering(private val context: CommonBackendContext) : FileLoweringPass {
class PropertyAccessorInlineLowering(private val context: CommonBackendContext) : BodyLoweringPass {
private val IrProperty.isSafeToInline: Boolean get() = isTopLevel || (modality === Modality.FINAL || visibility == Visibilities.PRIVATE) || (parent as IrClass).modality === Modality.FINAL
@@ -128,7 +126,7 @@ class PropertyAccessorInlineLowering(private val context: CommonBackendContext)
}
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(AccessorInliner())
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(AccessorInliner())
}
}

View File

@@ -234,7 +234,7 @@ inline fun IrFunction.addValueParameter(builder: IrValueParameterBuilder.() -> U
index = valueParameters.size
}
build().also { valueParameter ->
valueParameters.add(valueParameter)
valueParameters += valueParameter
valueParameter.parent = this@addValueParameter
}
}
@@ -293,7 +293,7 @@ inline fun IrTypeParametersContainer.addTypeParameter(builder: IrTypeParameterBu
index = typeParameters.size
}
build().also { typeParameter ->
typeParameters.add(typeParameter)
typeParameters += typeParameter
typeParameter.parent = this@addTypeParameter
}
}

View File

@@ -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
@@ -78,16 +78,11 @@ private fun removeUselessDeclarations(module: IrModuleFragment, usefulDeclaratio
process(declaration)
}
override fun visitConstructor(declaration: IrConstructor) {
if (declaration !in usefulDeclarations) {
// Keep the constructor declaration without body in order to declare the JS constructor function
declaration.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, emptyList())
}
}
// TODO bring back the primary constructor fix
private fun process(container: IrDeclarationContainer) {
container.declarations.transformFlat { member ->
if (member !in usefulDeclarations && member !is IrConstructor) {
if (member !in usefulDeclarations) {
emptyList()
} else {
member.acceptVoid(this)
@@ -128,14 +123,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" }
@@ -148,6 +145,9 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
while (queue.isNotEmpty()) {
val declaration = queue.pollFirst()
// TODO remove?
stageController.lazyLower(declaration)
if (declaration is IrClass) {
declaration.superTypes.forEach {
(it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue()
@@ -191,6 +191,9 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
else -> null
}
// TODO remove?
(body as? IrBody)?.let { stageController.lazyLower(it) }
body?.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
@@ -265,7 +268,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()) {
@@ -284,6 +288,34 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
declaration.enqueue()
}
}
// TODO is this needed?
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() }
}
}
// TODO deduplicate
// 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()
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.CommonBackendContext
interface JsCommonBackendContext : CommonBackendContext {
override val mapping: JsMapping
}

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.js
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.DeclarationFactory
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
@@ -12,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsMapping
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.*
@@ -25,12 +27,12 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.name.Name
import java.util.*
class JsDeclarationFactory : DeclarationFactory {
private val singletonFieldDescriptors = HashMap<IrClass, IrField>()
private val outerThisFieldSymbols = HashMap<IrClass, IrField>()
private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>()
class JsDeclarationFactory(mapping: JsMapping) : DeclarationFactory {
private val singletonFieldDescriptors = mapping.singletonFieldDescriptors
private val outerThisFieldSymbols = mapping.outerThisFieldSymbols
private val innerClassConstructors = mapping.innerClassConstructors
private val originalInnerClassPrimaryConstructorByClass = mapping.originalInnerClassPrimaryConstructorByClass
override fun getFieldForEnumEntry(enumEntry: IrEnumEntry): IrField = TODO()
@@ -79,9 +81,19 @@ class JsDeclarationFactory : DeclarationFactory {
return innerClassConstructors.getOrPut(innerClassConstructor) {
createInnerClassConstructorWithOuterThisParameter(innerClassConstructor)
}.also {
if (innerClassConstructor.isPrimary) {
originalInnerClassPrimaryConstructorByClass[innerClass] = innerClassConstructor
}
}
}
override fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor? {
assert(innerClass.isInner) { "Class is not inner: $innerClass" }
return originalInnerClassPrimaryConstructorByClass[innerClass]
}
private fun createInnerClassConstructorWithOuterThisParameter(oldConstructor: IrConstructor): IrConstructor {
val irClass = oldConstructor.parent as IrClass
val outerThisType = (irClass.parent as IrClass).defaultType

View File

@@ -194,12 +194,12 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val anyConstructorSymbol = anyClassSymbol.constructors.single()
val jsObjectClassSymbol = getInternalClassWithoutPackage("kotlin.js.JsObject")
val jsObjectConstructorSymbol by lazy { jsObjectClassSymbol.constructors.single() }
val jsObjectConstructorSymbol by lazy2 { jsObjectClassSymbol.constructors.single() }
val uByteClassSymbol by lazy { getInternalClassWithoutPackage("kotlin.UByte") }
val uShortClassSymbol by lazy { getInternalClassWithoutPackage("kotlin.UShort") }
val uIntClassSymbol by lazy { getInternalClassWithoutPackage("kotlin.UInt") }
val uLongClassSymbol by lazy { getInternalClassWithoutPackage("kotlin.ULong") }
val uByteClassSymbol by lazy2 { getInternalClassWithoutPackage("kotlin.UByte") }
val uShortClassSymbol by lazy2 { getInternalClassWithoutPackage("kotlin.UShort") }
val uIntClassSymbol by lazy2 { getInternalClassWithoutPackage("kotlin.UInt") }
val uLongClassSymbol by lazy2 { getInternalClassWithoutPackage("kotlin.ULong") }
val unreachable = defineUnreachableIntrinsic()
@@ -274,16 +274,16 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
// TODO move CharSequence-related stiff to IntrinsifyCallsLowering
val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence")))
val charSequenceLengthPropertyGetterSymbol by lazy {
val charSequenceLengthPropertyGetterSymbol by lazy2 {
with(charSequenceClassSymbol.owner.declarations) {
filterIsInstance<IrProperty>().firstOrNull { it.name.asString() == "length" }?.getter ?:
filterIsInstance<IrFunction>().first { it.name.asString() == "<get-length>" }
}.symbol
}
val charSequenceGetFunctionSymbol by lazy {
val charSequenceGetFunctionSymbol by lazy2 {
charSequenceClassSymbol.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "get" }.symbol
}
val charSequenceSubSequenceFunctionSymbol by lazy {
val charSequenceSubSequenceFunctionSymbol by lazy2 {
charSequenceClassSymbol.owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "subSequence" }.symbol
}

View File

@@ -5,17 +5,13 @@
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.js.JsDeclarationFactory
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
@@ -24,9 +20,9 @@ import org.jetbrains.kotlin.ir.SourceRangeInfo
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceKey
import org.jetbrains.kotlin.ir.backend.js.lower.ConstructorPair
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
@@ -47,11 +43,14 @@ class JsIrBackendContext(
val additionalExportedDeclarations: Set<FqName>,
override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context
override val scriptMode: Boolean = false
) : CommonBackendContext {
override val transformedFunction = mutableMapOf<IrFunctionSymbol, IrSimpleFunctionSymbol>()
override val lateinitNullableFields = mutableMapOf<IrField, IrField>()
) : JsCommonBackendContext {
override val transformedFunction
get() = error("Use Mapping.inlineClassMemberToStatic instead")
val memberMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunction>()
override val lateinitNullableFields
get() = error("Use Mapping.lateInitFieldToNullableField instead")
override val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
override val builtIns = module.builtIns
@@ -59,15 +58,39 @@ class JsIrBackendContext(
val devMode = configuration[JSConfigurationKeys.DEVELOPER_MODE] ?: false
var externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>()
lateinit var bodilessBuiltInsPackageFragment: IrPackageFragment
val externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>()
val externalDeclarations = hashSetOf<IrDeclaration>()
val bodilessBuiltInsPackageFragment: IrPackageFragment = run {
val externalNestedClasses = mutableListOf<IrClass>()
val packageLevelJsModules = mutableListOf<IrFile>()
class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol {
override val descriptor: PackageFragmentDescriptor
get() = error("Operation is unsupported")
private var _owner: IrExternalPackageFragment? = null
override val owner get() = _owner!!
override var uniqId: UniqId
get() = error("Operation is unsupported")
set(value) { error("Operation is unsupported") }
override val isBound get() = _owner != null
override fun bind(owner: IrExternalPackageFragment) {
_owner = owner
}
}
IrExternalPackageFragmentImpl(
DescriptorlessExternalPackageFragmentSymbol(),
FqName("kotlin")
)
}
val packageLevelJsModules = mutableSetOf<IrFile>()
val declarationLevelJsModules = mutableListOf<IrDeclarationWithName>()
val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal"))
val implicitDeclarationFile by lazy {
val implicitDeclarationFile by lazy2 {
IrFileImpl(object : SourceManager.FileEntry {
override val name = "<implicitDeclarations>"
override val maxOffset = UNDEFINED_OFFSET
@@ -102,7 +125,9 @@ class JsIrBackendContext(
}
override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile)
override val declarationFactory = JsDeclarationFactory()
override val mapping = JsMapping()
override val declarationFactory = JsDeclarationFactory(mapping)
companion object {
val KOTLIN_PACKAGE_FQN = FqName.fromSegments(listOf("kotlin"))
@@ -136,11 +161,7 @@ class JsIrBackendContext(
private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunction>()
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
val enumEntryExternalToInstanceField = mutableMapOf<IrEnumEntrySymbol, IrField>()
val callableReferencesCache = mutableMapOf<CallableReferenceKey, IrSimpleFunction>()
val secondaryConstructorToFactoryCache = mutableMapOf<IrConstructor, ConstructorPair>()
val intrinsics = JsIntrinsics(irBuiltIns, this)
@@ -263,27 +284,27 @@ class JsIrBackendContext(
val suiteFun = getFunctions(FqName("kotlin.test.suite")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
val testFun = getFunctions(FqName("kotlin.test.test")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
val coroutineImplLabelPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
val coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
val coroutineImplResultSymbolGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
val coroutineImplResultSymbolSetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("result")!!.owner }
val coroutineImplExceptionPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("exception")!!.owner }
val coroutineImplExceptionPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("exception")!!.owner }
val coroutineImplExceptionStatePropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
val coroutineImplExceptionStatePropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("exceptionState")!!.owner }
val coroutineImplLabelPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
val coroutineImplLabelPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
val coroutineImplResultSymbolGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
val coroutineImplResultSymbolSetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("result")!!.owner }
val coroutineImplExceptionPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exception")!!.owner }
val coroutineImplExceptionPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exception")!!.owner }
val coroutineImplExceptionStatePropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
val coroutineImplExceptionStatePropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exceptionState")!!.owner }
val primitiveClassProperties by lazy {
val primitiveClassProperties by lazy2 {
primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
}
val primitiveClassFunctionClass by lazy {
val primitiveClassFunctionClass by lazy2 {
primitiveClassesObject.owner.declarations
.filterIsInstance<IrSimpleFunction>()
.find { it.name == Name.identifier("functionClass") }!!
}
val throwableConstructors by lazy { throwableClass.owner.declarations.filterIsInstance<IrConstructor>().map { it.symbol } }
val defaultThrowableCtor by lazy { throwableConstructors.single { !it.owner.isPrimary && it.owner.valueParameters.size == 0 } }
val throwableConstructors by lazy2 { throwableClass.owner.declarations.filterIsInstance<IrConstructor>().map { it.symbol } }
val defaultThrowableCtor by lazy2 { throwableConstructors.single { !it.owner.isPrimary && it.owner.valueParameters.size == 0 } }
private fun referenceOperators(): Map<Name, MutableMap<IrClassifierSymbol, IrSimpleFunctionSymbol>> {
val primitiveIrSymbols = irBuiltIns.primitiveIrTypes.map { it.classifierOrFail as IrClassSymbol }
@@ -334,3 +355,6 @@ class JsIrBackendContext(
print(message)
}
}
// TODO: investigate if it could be removed
fun <T> lazy2(fn: () -> T) = lazy { stageController.withInitialIr(fn) }

View File

@@ -15,10 +15,10 @@ import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.CopyInlineFunctionBodyLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
private fun DeclarationContainerLoweringPass.runOnFilesPostfix(files: Iterable<IrFile>) = files.forEach { runOnFilePostfix(it) }
@@ -55,175 +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 lateinitLoweringPhase = makeJsModulePhase(
::LateinitLowering,
name = "LateinitLowering",
private val lateinitNullableFieldsPhase = makeDeclarationTransformerPhase(
::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields",
description = "Create nullable fields for lateinit properties"
)
private val lateinitDeclarationLoweringPhase = makeDeclarationTransformerPhase(
::NullableFieldsDeclarationLowering,
name = "LateinitDeclarations",
description = "Reference nullable fields from properties and getters + insert checks"
)
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 = makeCustomJsModulePhase(
{ context, module ->
FunctionInlining(context).inline(module)
module.patchDeclarationParents()
},
private val functionInliningPhase = makeBodyLoweringPhase(
::FunctionInlining,
name = "FunctionInliningPhase",
description = "Perform function inlining",
prerequisite = setOf(expectDeclarationsRemovingPhase)
)
private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase(
private val copyInlineFunctionBodyLoweringPhase = makeDeclarationTransformerPhase(
::CopyInlineFunctionBodyLowering,
name = "CopyInlineFunctionBody",
description = "Copy inline function body",
prerequisite = setOf(functionInliningPhase)
)
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 enumClassLoweringPhase = makeJsModulePhase(
::EnumClassLowering,
name = "EnumClassLowering",
description = "Transform Enum Class into regular Class",
private val enumClassConstructorBodyLoweringPhase = makeBodyLoweringPhase(
::EnumClassConstructorBodyTransformer,
name = "EnumClassConstructorBodyLowering",
description = "Transform Enum Class into regular Class"
)
private val enumEntryInstancesLoweringPhase = makeDeclarationTransformerPhase(
::EnumEntryInstancesLowering,
name = "EnumEntryInstancesLowering",
description = "Create instance variable for each enum entry initialized with `null`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumUsageLoweringPhase = makeJsModulePhase(
private val enumEntryInstancesBodyLoweringPhase = makeBodyLoweringPhase(
::EnumEntryInstancesBodyLowering,
name = "EnumEntryInstancesBodyLowering",
description = "Insert enum entry field initialization into correxposnding class constructors",
prerequisite = setOf(enumEntryInstancesLoweringPhase)
)
private val enumClassCreateInitializerLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassCreateInitializerLowering,
name = "EnumClassCreateInitializerLowering",
description = "Create initializer for enum entries",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumEntryCreateGetInstancesFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumEntryCreateGetInstancesFunsLowering,
name = "EnumEntryCreateGetInstancesFunsLowering",
description = "Create enumEntry_getInstance functions",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumSyntheticFunsLoweringPhase = makeDeclarationTransformerPhase(
::EnumSyntheticFunctionsLowering,
name = "EnumSyntheticFunctionsLowering",
description = "Implement `valueOf` and `values`",
prerequisite = setOf(enumClassConstructorLoweringPhase)
)
private val enumUsageLoweringPhase = makeBodyLoweringPhase(
::EnumUsageLowering,
name = "EnumUsageLowering",
description = "Replace enum access with invocation of corresponding function",
prerequisite = setOf(enumClassLoweringPhase)
prerequisite = setOf(enumEntryCreateGetInstancesFunsLoweringPhase)
)
private val sharedVariablesLoweringPhase = makeJsModulePhase(
private val enumEntryRemovalLoweringPhase = makeDeclarationTransformerPhase(
::EnumClassRemoveEntriesLowering,
name = "EnumEntryRemovalLowering",
description = "Replace enum entry with corresponding class",
prerequisite = setOf(enumUsageLoweringPhase)
)
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 innerClassConstructorCallsLoweringPhase = makeJsModulePhase(
private val innerClassesMemberBodyLoweringPhase = makeBodyLoweringPhase(
::InnerClassesMemberBodyLowering,
name = "InnerClassesMemberBody",
description = "Replace `this` with 'outer this' field references",
prerequisite = setOf(innerClassesLoweringPhase)
)
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 privateMembersLoweringPhase = makeJsModulePhase(
private val suspendLambdasRemovalLoweringPhase = makeDeclarationTransformerPhase(
{ RemoveSuspendLambdas() },
name = "RemoveSuspendLambdas",
description = "Remove suspend lambdas"
)
private val privateMembersLoweringPhase = makeDeclarationTransformerPhase(
::PrivateMembersLowering,
name = "PrivateMembersLowering",
description = "Extract private members from classes"
)
private val callableReferenceLoweringPhase = makeJsModulePhase(
private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase(
::PrivateMemberBodiesLowering,
name = "PrivateMemberUsagesLowering",
description = "Rewrite the private member usages"
)
private val callableReferenceLoweringPhase = makeBodyLoweringPhase(
::CallableReferenceLowering,
name = "CallableReferenceLowering",
description = "Handle callable references",
@@ -235,144 +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 defaultParameterInjectorPhase = makeJsModulePhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) },
private val defaultArgumentPatchOverridesPhase = makeDeclarationTransformerPhase(
::DefaultParameterPatchOverridenSymbolsLowering,
name = "DefaultArgumentsPatchOverrides",
description = "Patch overrides for fake override dispatch functions",
prerequisite = setOf(defaultArgumentStubGeneratorPhase)
)
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 annotationConstructorLowering = makeJsModulePhase(
private val delegateToPrimaryConstructorLoweringPhase = makeBodyLoweringPhase(
::DelegateToSyntheticPrimaryConstructor,
name = "DelegateToSyntheticPrimaryConstructor",
description = "Delegates to synthetic primary constructor",
prerequisite = setOf(primaryConstructorLoweringPhase)
)
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 multipleCatchesLoweringPhase = makeJsModulePhase(
private val initializersCleanupLoweringPhase = makeDeclarationTransformerPhase(
::InitializersCleanupLowering,
name = "InitializersCleanupLowering",
description = "Remove non-static anonymous initializers and field init expressions",
prerequisite = setOf(initializersLoweringPhase)
)
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 inlineClassLoweringPhase = makeCustomJsModulePhase(
{ context, module ->
InlineClassLowering(context).run {
inlineClassDeclarationLowering.runOnFilesPostfix(module)
inlineClassUsageLowering.lower(module)
}
},
name = "InlineClassLowering",
description = "Handle inline classes"
private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase(
{ InlineClassLowering(it).inlineClassDeclarationLowering },
name = "InlineClassDeclarationLowering",
description = "Handle inline class declarations"
)
private val autoboxingTransformerPhase = makeJsModulePhase(
private val inlineClassUsageLoweringPhase = makeBodyLoweringPhase(
{ InlineClassLowering(it).inlineClassUsageLowering },
name = "InlineClassUsageLowering",
description = "Handle inline class usages"
)
private val autoboxingTransformerPhase = makeBodyLoweringPhase(
::AutoboxingTransformer,
name = "AutoboxingTransformer",
description = "Insert box/unbox intrinsics"
)
private val blockDecomposerLoweringPhase = makeCustomJsModulePhase(
{ context, module ->
JsBlockDecomposerLowering(context).lower(module)
module.patchDeclarationParents()
},
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"
@@ -382,81 +548,107 @@ 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 = makeCustomJsModulePhase(
{ context, module -> ObjectDeclarationLowering(context, context.objectToGetInstanceFunction).lower(module) },
private val objectDeclarationLoweringPhase = makeDeclarationTransformerPhase(
::ObjectDeclarationLowering,
name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions"
)
private val objectUsageLoweringPhase = makeCustomJsModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) },
private val objectUsageLoweringPhase = makeBodyLoweringPhase(
::ObjectUsageLowering,
name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call"
)
val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering,
validateIrBeforeLowering,
testGenerationPhase,
expectDeclarationsRemovingPhase,
stripTypeAliasDeclarationsPhase,
arrayConstructorPhase,
functionInliningPhase,
copyInlineFunctionBodyLoweringPhase,
createScriptFunctionsPhase,
provisionalFunctionExpressionPhase,
lateinitNullableFieldsPhase,
lateinitDeclarationLoweringPhase,
lateinitUsageLoweringPhase,
tailrecLoweringPhase,
enumClassConstructorLoweringPhase,
enumClassConstructorBodyLoweringPhase,
sharedVariablesLoweringPhase,
localDelegatedPropertiesLoweringPhase,
localDeclarationsLoweringPhase,
localClassExtractionPhase,
innerClassesLoweringPhase,
innerClassesMemberBodyLoweringPhase,
innerClassConstructorCallsLoweringPhase,
propertiesLoweringPhase,
primaryConstructorLoweringPhase,
delegateToPrimaryConstructorLoweringPhase,
annotationConstructorLowering,
initializersLoweringPhase,
initializersCleanupLoweringPhase,
// Common prefix ends
enumEntryInstancesLoweringPhase,
enumEntryInstancesBodyLoweringPhase,
enumClassCreateInitializerLoweringPhase,
enumEntryCreateGetInstancesFunsLoweringPhase,
enumSyntheticFunsLoweringPhase,
enumUsageLoweringPhase,
enumEntryRemovalLoweringPhase,
suspendFunctionsLoweringPhase,
suspendLambdasRemovalLoweringPhase,
returnableBlockLoweringPhase,
forLoopsLoweringPhase,
primitiveCompanionLoweringPhase,
propertyAccessorInlinerLoweringPhase,
foldConstantLoweringPhase,
privateMembersLoweringPhase,
privateMemberUsagesLoweringPhase,
callableReferenceLoweringPhase,
defaultArgumentStubGeneratorPhase,
defaultArgumentPatchOverridesPhase,
defaultParameterInjectorPhase,
defaultParameterCleanerPhase,
jsDefaultCallbackGeneratorPhase,
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase,
throwableSuccessorsLoweringPhase,
varargLoweringPhase,
multipleCatchesLoweringPhase,
bridgesConstructionPhase,
typeOperatorLoweringPhase,
secondaryConstructorLoweringPhase,
secondaryFactoryInjectorLoweringPhase,
classReferenceLoweringPhase,
inlineClassDeclarationLoweringPhase,
inlineClassUsageLoweringPhase,
autoboxingTransformerPhase,
blockDecomposerLoweringPhase,
constLoweringPhase,
objectDeclarationLoweringPhase,
objectUsageLoweringPhase,
callsLoweringPhase,
validateIrAfterLowering
)
// TODO comment? Eliminate MouduleLowering's? Don't filter them here?
val pirLowerings = loweringList.filter { it is DeclarationLowering || it is BodyLowering } + staticMembersLoweringPhase
// TODO `fold` -> `reduce`
val jsPhases = namedIrModulePhase(
name = "IrModuleLowering",
description = "IR module lowering",
lower = scriptRemoveReceiverLowering then
validateIrBeforeLowering then
testGenerationPhase then
expectDeclarationsRemovingPhase then
stripTypeAliasDeclarationsPhase then
arrayConstructorPhase then
functionInliningPhase then
createScriptFunctionsPhase then
provisionalFunctionExpressionPhase then
lateinitLoweringPhase then
tailrecLoweringPhase then
enumClassConstructorLoweringPhase then
sharedVariablesLoweringPhase then
localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then
localClassExtractionPhase then
innerClassesLoweringPhase then
innerClassConstructorCallsLoweringPhase then
propertiesLoweringPhase then
primaryConstructorLoweringPhase then
annotationConstructorLowering then
initializersLoweringPhase then
// Common prefix ends
enumClassLoweringPhase then
enumUsageLoweringPhase then
suspendFunctionsLoweringPhase then
returnableBlockLoweringPhase then
forLoopsLoweringPhase then
primitiveCompanionLoweringPhase then
propertyAccessorInlinerLoweringPhase then
foldConstantLoweringPhase then
privateMembersLoweringPhase then
callableReferenceLoweringPhase then
defaultArgumentStubGeneratorPhase then
defaultParameterInjectorPhase then
defaultParameterCleanerPhase then
jsDefaultCallbackGeneratorPhase then
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
throwableSuccessorsLoweringPhase then
varargLoweringPhase then
multipleCatchesLoweringPhase then
bridgesConstructionPhase then
typeOperatorLoweringPhase then
secondaryConstructorLoweringPhase then
secondaryFactoryInjectorLoweringPhase then
classReferenceLoweringPhase then
inlineClassLoweringPhase then
autoboxingTransformerPhase then
blockDecomposerLoweringPhase then
constLoweringPhase then
objectDeclarationLoweringPhase then
objectUsageLoweringPhase then
callsLoweringPhase then
validateIrAfterLowering
)
lower = loweringList.drop(1).fold(loweringList[0].modulePhase) { acc: CompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>, lowering ->
acc.then(lowering.modulePhase)
}
)

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.ir.declarations.*
class JsMapping : DefaultMapping() {
val singletonFieldDescriptors = newMapping<IrClass, IrField>()
val outerThisFieldSymbols = newMapping<IrClass, IrField>()
val innerClassConstructors = newMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = newMapping<IrClass, IrConstructor>()
val secondaryConstructorToDelegate = newMapping<IrConstructor, IrSimpleFunction>()
val secondaryConstructorToFactory = newMapping<IrConstructor, IrSimpleFunction>()
val objectToGetInstanceFunction = newMapping<IrClass, IrSimpleFunction>()
val objectToInstanceField = newMapping<IrClass, IrField>()
val classToSyntheticPrimaryConstructor = newMapping<IrClass, IrConstructor>()
val privateMemberToCorrespondingStatic = newMapping<IrFunction, IrSimpleFunction>()
val enumEntryToGetInstanceFun = newMapping<IrEnumEntry, IrSimpleFunction>()
val enumEntryToInstanceField = newMapping<IrEnumEntry, IrField>()
val enumConstructorToNewConstructor = newMapping<IrConstructor, IrConstructor>()
val enumClassToCorrespondingEnumEntry = newMapping<IrClass, IrEnumEntry>()
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
}
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js
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.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(val context: JsIrBackendContext, val lowerings: List<Lowering>) : StageController {
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
currentStage = stage
try {
return fn()
} finally {
currentStage = prevStage
}
}
override fun <T> withInitialIr(block: () -> T): T = { withStage(0, block) }.withRestrictions(newRestrictedToDeclaration = null)
override fun <T> restrictTo(declaration: IrDeclaration, fn: () -> T): T = fn.withRestrictions(newRestrictedToDeclaration = declaration)
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
// TODO fix and enable
// 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
private inline fun <T> (() -> T).withRestrictions(
newRestrictedToDeclaration: IrDeclaration? = null,
newBodiesEnabled: Boolean? = null,
newRestricted: Boolean? = null,
newDeclarationListsRestricted: Boolean? = null
): T {
val prev = restrictedToDeclaration
restrictedToDeclaration = newRestrictedToDeclaration
val wereBodiesEnabled = bodiesEnabled
bodiesEnabled = newBodiesEnabled ?: bodiesEnabled
val wasRestricted = restricted
restricted = newRestricted ?: restricted
val wereDeclarationListsRestricted = declarationListsRestricted
declarationListsRestricted = newDeclarationListsRestricted ?: declarationListsRestricted
try {
return this.invoke()
} finally {
restrictedToDeclaration = prev
bodiesEnabled = wereBodiesEnabled
restricted = wasRestricted
declarationListsRestricted = wereDeclarationListsRestricted
}
}
}

View File

@@ -8,18 +8,16 @@ package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AbstractAnalyzerWithCompilerReport
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
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList
@@ -29,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?,
@@ -57,8 +45,11 @@ 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 {}
val (moduleFragment: IrModuleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
loadIr(project, mainModule, analyzer, configuration, allDependencies, friendDependencies)
@@ -93,10 +84,31 @@ fun compile(
moveBodilessDeclarationsToSeparatePlace(context, moduleFragment)
jsPhases.invokeToplevel(phaseConfig, context, moduleFragment)
if (dceDriven) {
val transformer = IrModuleToJsTransformer(context, mainFunction, mainArguments)
return transformer.generateModule(moduleFragment, generateFullJs, generateDceJs)
// 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)
// TODO investigate whether this is needed anymore
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)
}
}
fun generateJsCode(

View File

@@ -5,26 +5,32 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.util.parentAsClass
class AnnotationConstructorLowering(context: CommonBackendContext) : ClassLoweringPass {
class AnnotationConstructorLowering(context: CommonBackendContext) : DeclarationTransformer {
private val unitType = context.irBuiltIns.unitType
override fun lower(irClass: IrClass) {
if (irClass.kind != ClassKind.ANNOTATION_CLASS) return
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrConstructor || !declaration.isPrimary) return null
val irClass = declaration.parentAsClass
if (irClass.kind != ClassKind.ANNOTATION_CLASS) return null
val constructor = irClass.declarations.filterIsInstance<IrConstructor>().single()
assert(constructor.isPrimary)
// put empty body to make sure proper initializer is generated
constructor.body = IrBlockBodyImpl(constructor.startOffset, constructor.endOffset).apply {
// TODO what about its previous body?
declaration.body = IrBlockBodyImpl(declaration.startOffset, declaration.endOffset) {
statements += IrInstanceInitializerCallImpl(startOffset, endOffset, irClass.symbol, unitType)
}
return null
}
}

View File

@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.lower.AbstractValueUsageTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
@@ -18,12 +18,19 @@ import org.jetbrains.kotlin.ir.util.*
// Copied and adapted from Kotlin/Native
class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid()
class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsageTransformer(context.irBuiltIns), BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO workaround for callable references
// Prevents from revisiting local
if (container.parent is IrFunction) return
val replacement = container.transform(this, null) as IrDeclaration
if (container !== replacement) error("Declaration has changed: ${container}")
// TODO: Track & insert parents for temporary variables
irFile.patchDeclarationParents()
irBody.patchDeclarationParents(container as? IrDeclarationParent ?: container.parent)
}
private tailrec fun IrExpression.isGetUnit(): Boolean =

View File

@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.ir.isElseBranch
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement
@@ -30,31 +32,56 @@ class JsBlockDecomposerLowering(val context: JsIrBackendContext) : AbstractBlock
JsIrBuilder.buildCall(context.intrinsics.unreachable.symbol, context.irBuiltIns.nothingType)
}
abstract class AbstractBlockDecomposerLowering(context: CommonBackendContext) : DeclarationContainerLoweringPass {
abstract class AbstractBlockDecomposerLowering(
context: CommonBackendContext
) : BodyLoweringPass {
private val nothingType = context.irBuiltIns.nothingType
// Expression with Nothing type to be inserted in places of unreachable expression
abstract fun unreachableExpression(): IrExpression
private val decomposerTransformer = BlockDecomposerTransformer(context, ::unreachableExpression)
private val nothingType = context.irBuiltIns.nothingType
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
when (declaration) {
is IrScript -> {
lower(declaration)
listOf(declaration)
override fun lower(irBody: IrBody, container: IrDeclaration) {
when (container) {
is IrFunction -> {
container.accept(decomposerTransformer, null)
irBody.patchDeclarationParents(container)
}
is IrField -> {
container.initializer?.apply {
val initFunction = JsIrBuilder.buildFunction(
container.name.asString() + "\$init\$",
container.type,
container.parent,
Visibilities.PRIVATE
)
val newBody = toBlockBody(initFunction)
newBody.patchDeclarationParents(initFunction)
initFunction.body = newBody
initFunction.accept(decomposerTransformer, null)
val lastStatement = newBody.statements.last()
val actualParent = if (newBody.statements.size > 1 || lastStatement !is IrReturn || lastStatement.value != expression) {
expression = JsIrBuilder.buildCall(initFunction.symbol, expression.type)
stageController.unrestrictDeclarationListsAccess {
(container.parent as IrDeclarationContainer).declarations += initFunction
}
initFunction
} else {
container
}
patchDeclarationParents(actualParent)
}
is IrFunction -> {
lower(declaration)
listOf(declaration)
}
is IrField -> lower(declaration, irDeclarationContainer)
else -> listOf(declaration)
}
}
irDeclarationContainer.transformChildrenVoid(CodeCleaner())
// irDeclarationContainer.transformChildrenVoid(CodeCleaner()) TODO
}
private inner class CodeCleaner : IrElementTransformerVoid() {
@@ -98,49 +125,13 @@ abstract class AbstractBlockDecomposerLowering(context: CommonBackendContext) :
}
}
fun lower(irScript: IrScript) {
irScript.transform(decomposerTransformer, null)
}
fun lower(irFunction: IrFunction) {
(irFunction.body as? IrExpressionBody)?.apply {
irFunction.body = toBlockBody(irFunction)
}
irFunction.accept(decomposerTransformer, null)
}
private fun IrExpressionBody.toBlockBody(containingFunction: IrFunction): IrBlockBody {
expression.patchDeclarationParents(containingFunction)
val returnStatement = JsIrBuilder.buildReturn(containingFunction.symbol, expression, nothingType)
return IrBlockBodyImpl(expression.startOffset, expression.endOffset).apply {
return IrBlockBodyImpl(startOffset, endOffset) {
expression.patchDeclarationParents(containingFunction)
val returnStatement = JsIrBuilder.buildReturn(containingFunction.symbol, expression, nothingType)
statements += returnStatement
}
}
fun lower(irField: IrField, container: IrDeclarationContainer): List<IrDeclaration> {
irField.initializer?.apply {
val initFunction = JsIrBuilder.buildFunction(
irField.name.asString() + "\$init\$",
expression.type,
container,
Visibilities.PRIVATE
)
val newBody = toBlockBody(initFunction)
newBody.patchDeclarationParents(initFunction)
initFunction.body = newBody
lower(initFunction)
val lastStatement = newBody.statements.last()
if (newBody.statements.size > 1 || lastStatement !is IrReturn || lastStatement.value != expression) {
expression = JsIrBuilder.buildCall(initFunction.symbol, expression.type)
return listOf(initFunction, irField)
}
}
return listOf(irField)
}
}
class BlockDecomposerTransformer(

View File

@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.bridges.FunctionHandle
import org.jetbrains.kotlin.backend.common.bridges.generateBridges
import org.jetbrains.kotlin.backend.common.ir.copyTo
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.functionSignature
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.*
@@ -46,23 +48,20 @@ import org.jetbrains.kotlin.ir.util.*
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
// }
//
class BridgesConstruction(val context: CommonBackendContext) : ClassLoweringPass {
class BridgesConstruction(val context: CommonBackendContext) : DeclarationTransformer {
private val specialBridgeMethods = SpecialBridgeMethods(context)
override fun lower(irClass: IrClass) {
irClass.declarations
.asSequence()
.filterIsInstance<IrSimpleFunction>()
.filter { !it.isStaticMethodOfClass }
.toList()
.forEach { generateBridges(it, irClass) }
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrSimpleFunction || declaration.isStaticMethodOfClass || declaration.parent !is IrClass) return null
return generateBridges(declaration)?.let { listOf(declaration) + it }
}
private fun generateBridges(function: IrSimpleFunction, irClass: IrClass) {
private fun generateBridges(function: IrSimpleFunction): List<IrDeclaration>? {
// equals(Any?), hashCode(), toString() never need bridges
if (function.isMethodOfAny())
return
return null
val (specialOverride: IrSimpleFunction?, specialOverrideInfo) =
specialBridgeMethods.findSpecialWithOverride(function) ?: Pair(null, null)
@@ -74,6 +73,10 @@ class BridgesConstruction(val context: CommonBackendContext) : ClassLoweringPass
signature = { FunctionAndSignature(it.function) }
)
if (bridgesToGenerate.isEmpty()) return null
val result = mutableListOf<IrDeclaration>()
for ((from, to) in bridgesToGenerate) {
if (!from.function.parentAsClass.isInterface &&
from.function.isReal &&
@@ -97,8 +100,10 @@ class BridgesConstruction(val context: CommonBackendContext) : ClassLoweringPass
}
irClass.declarations.add(bridge)
result += bridge
}
return result
}
// Ported from from jvm.lower.BridgeLowering
@@ -137,36 +142,36 @@ class BridgesConstruction(val context: CommonBackendContext) : ClassLoweringPass
extensionReceiverParameter = bridge.extensionReceiverParameter?.copyTo(this)
valueParameters += bridge.valueParameters.map { p -> p.copyTo(this) }
annotations += bridge.annotations
overriddenSymbols.addAll(delegateTo.overriddenSymbols)
overriddenSymbols.add(bridge.symbol)
overriddenSymbols += delegateTo.overriddenSymbols
overriddenSymbols += bridge.symbol
}
context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) {
if (specialMethodInfo != null) {
irFunction.valueParameters.take(specialMethodInfo.argumentsToCheck).forEach {
+irIfThen(
context.irBuiltIns.unitType,
irNot(irIs(irGet(it), delegateTo.valueParameters[it.index].type)),
irReturn(specialMethodInfo.defaultValueGenerator(irFunction))
)
irFunction.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) {
if (specialMethodInfo != null) {
irFunction.valueParameters.take(specialMethodInfo.argumentsToCheck).forEach {
+irIfThen(
context.irBuiltIns.unitType,
irNot(irIs(irGet(it), delegateTo.valueParameters[it.index].type)),
irReturn(specialMethodInfo.defaultValueGenerator(irFunction))
)
}
}
}
val call = irCall(delegateTo.symbol)
call.dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!)
irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
}
val call = irCall(delegateTo.symbol)
call.dispatchReceiver = irGet(irFunction.dispatchReceiverParameter!!)
irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
}
val toTake = irFunction.valueParameters.size - if (call.isSuspend xor irFunction.isSuspend) 1 else 0
val toTake = irFunction.valueParameters.size - if (call.isSuspend xor irFunction.isSuspend) 1 else 0
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
}
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
}
+irReturn(call)
}.apply {
irFunction.body = this
+irReturn(call)
}.statements
}
return irFunction

View File

@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.isInlined
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
@@ -42,14 +43,15 @@ data class CallableReferenceKey(
)
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
class CallableReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val callableToFactoryFunction = context.callableReferencesCache
private val newDeclarations = mutableListOf<IrDeclaration>()
private val implicitDeclarationFile = context.implicitDeclarationFile
private lateinit var implicitDeclarationFile: IrFile //= context.implicitDeclarationFile
override fun lower(irFile: IrFile) {
override fun lower(irBody: IrBody, container: IrDeclaration) {
newDeclarations.clear()
irFile.transformChildrenVoid(CallableReferenceLowerTransformer())
implicitDeclarationFile = container.file // TODO
irBody.transformChildrenVoid(CallableReferenceLowerTransformer())
implicitDeclarationFile.declarations += newDeclarations
}
@@ -496,7 +498,7 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
closureFunction.valueParameters += unboundParamDeclarations
val callTarget = context.ir.defaultParameterDeclarationsCache[declaration] ?: declaration
val callTarget = declaration
val target = callTarget.symbol

View File

@@ -5,19 +5,14 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetClass
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -28,7 +23,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.*
class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
class ClassReferenceLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val intrinsics = context.intrinsics
private val primitiveClassesObject = context.primitiveClassesObject
@@ -223,8 +218,8 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
)
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitGetClass(expression: IrGetClass) =
callGetKClassFromExpression(
returnType = expression.type,

View File

@@ -5,10 +5,11 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -70,8 +71,8 @@ class ConstTransformer(private val context: JsIrBackendContext) : IrElementTrans
}
}
class ConstLowering(private val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(ConstTransformer(context))
class ConstLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(ConstTransformer(context))
}
}
}

View File

@@ -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 {

View File

@@ -5,16 +5,19 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.copyParameterDeclarationsFrom
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
@@ -26,11 +29,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedClassConstructorDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
@@ -40,11 +39,12 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import java.util.*
class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
class EnumUsageLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumEntry = expression.symbol.owner
val klass = enumEntry.parent as IrClass
@@ -54,7 +54,7 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
}
private fun lowerExternalEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.enumEntryExternalToInstanceField.getOrPut(enumEntry.symbol) { createFieldForEntry(enumEntry, klass) }.let {
context.mapping.enumEntryToInstanceField.getOrPut(enumEntry) { createFieldForEntry(enumEntry, klass) }.let {
JsIrBuilder.buildGetField(it.symbol, classAsReceiver(klass), null, klass.defaultType)
}
@@ -74,31 +74,17 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
).also {
descriptor.bind(it)
it.parent = irClass
irClass.declarations += it
// TODO need a way to emerge local declarations from BodyLoweringPass
stageController.unrestrictDeclarationListsAccess {
irClass.declarations += it
}
}
}
}
private fun lowerEnumEntry(enumEntry: IrEnumEntry, klass: IrClass) =
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
JsIrBuilder.buildFunction(
createEntryAccessorName(klass.name.identifier, enumEntry),
returnType = enumEntry.getType(klass),
parent = klass
)
}.run { JsIrBuilder.buildCall(symbol) }
}
class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration is IrClass && declaration.isEnumClass &&
!declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
) {
EnumClassTransformer(context, declaration).transform()
} else null
}
}
enumEntry.getInstanceFun!!.run { JsIrBuilder.buildCall(symbol) }
}
@@ -107,52 +93,30 @@ private fun createEntryAccessorName(enumName: String, enumEntry: IrEnumEntry) =
private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType
class EnumClassConstructorLowering(val context: CommonBackendContext) : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration is IrClass && declaration.isEnumClass &&
!declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
) {
EnumClassConstructorTransformer(context, declaration).transform()
} else null
}
}
}
// Should be applied recursively
class EnumClassConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
class EnumClassConstructorTransformer(val context: CommonBackendContext, private val irClass: IrClass) {
private val builder = context.createIrBuilder(irClass.symbol)
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructor>()
private var IrConstructor.newConstructor by context.mapping.enumConstructorToNewConstructor
private var IrClass.correspondingEntry by context.mapping.enumClassToCorrespondingEnumEntry
private var IrValueDeclaration.valueParameter by context.mapping.enumConstructorOldToNewValueParameters
fun transform(): List<IrDeclaration> {
// Add `name` and `ordinal` parameters to enum class constructors
lowerEnumConstructorsSignature()
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
(declaration.parent as? IrClass)?.let { irClass ->
if (!irClass.isEnumClass || irClass.descriptor.isExpect || irClass.isEffectivelyExternal()) return null
// Pass these parameters to delegating constructor calls
lowerEnumConstructorsBody()
// The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones.
fixReferencesToConstructorParameters()
// Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s.
// Add `name` and `ordinal` parameters.
lowerEnumEntryClassConstructors()
// Lower `IrEnumConstructorCall`s to corresponding `IrCall`s.
// Add `name` and `ordinal` constant parameters only for calls to the "enum class" constructors ("enum entry class" constructors
// already delegate these parameters)
lowerEnumEntryInitializerExpression()
return listOf(irClass)
}
private fun lowerEnumConstructorsSignature() {
irClass.declarations.transform { declaration ->
if (declaration is IrConstructor) {
transformEnumConstructor(declaration, irClass)
} else
declaration
// Add `name` and `ordinal` parameters to enum class constructors
return listOf(transformEnumConstructor(declaration, irClass))
}
if (declaration is IrEnumEntry) {
declaration.correspondingClass?.let { klass ->
klass.correspondingEntry = declaration
}
}
}
return null
}
private fun transformEnumConstructor(enumConstructor: IrConstructor, enumClass: IrClass): IrConstructor {
@@ -177,17 +141,94 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
valueParameters += JsIrBuilder.buildValueParameter("name", 0, context.irBuiltIns.stringType).also { it.parent = this }
valueParameters += JsIrBuilder.buildValueParameter("ordinal", 1, context.irBuiltIns.intType).also { it.parent = this }
copyParameterDeclarationsFrom(enumConstructor)
body = enumConstructor.body
loweredEnumConstructors[enumConstructor.symbol] = this
this.acceptVoid(PatchDeclarationParentsVisitor(enumClass))
val newConstructor = this
enumConstructor.newConstructor = this
enumConstructor.body?.let { oldBody ->
body = IrBlockBodyImpl(oldBody.startOffset, oldBody.endOffset) {
statements += (oldBody as IrBlockBody).statements
context.fixReferencesToConstructorParameters(enumClass, this)
acceptVoid(PatchDeclarationParentsVisitor(enumClass))
body = this
}
}
// TODO except for `fixReferencesToConstructorParameters` this code seems to be obsolete
val oldParameters = enumConstructor.valueParameters
val newParameters = valueParameters
oldParameters.forEach { old ->
// TODO Match by index?
val new = newParameters.single { it.name == old.name }
old.valueParameter = new
old.defaultValue?.let { default ->
new.defaultValue = IrExpressionBodyImpl(default.startOffset, default.endOffset) {
expression = default.expression
expression.patchDeclarationParents(newConstructor)
context.fixReferencesToConstructorParameters(enumClass, this)
}
}
}
}
}
}
// The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones.
private fun JsCommonBackendContext.fixReferencesToConstructorParameters(irClass: IrClass, body: IrBody) {
body.transformChildrenVoid(object : IrElementTransformerVoid() {
private val builder = createIrBuilder(irClass.symbol)
override fun visitGetValue(expression: IrGetValue): IrExpression {
mapping.enumConstructorOldToNewValueParameters[expression.symbol.owner]?.let {
return builder.irGet(it)
}
return super.visitGetValue(expression)
}
})
}
class EnumClassConstructorBodyTransformer(val context: JsCommonBackendContext) : BodyLoweringPass {
private var IrConstructor.newConstructor by context.mapping.enumConstructorToNewConstructor
private var IrClass.correspondingEntry by context.mapping.enumClassToCorrespondingEnumEntry
override fun lower(irBody: IrBody, container: IrDeclaration) {
(container.parent as? IrClass)?.let { irClass ->
// TODO Don't apply to everything
context.fixReferencesToConstructorParameters(irClass, irBody)
if (container is IrConstructor) {
if (irClass.goodEnum) {
// Pass new parameters to delegating constructor calls
lowerEnumConstructorsBody(container)
}
irClass.correspondingEntry?.let { enumEntry ->
// Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s.
// Add `name` and `ordinal` parameters.
lowerEnumEntryClassConstructors(irClass, enumEntry, container)
}
}
if (container is IrEnumEntry) {
// Lower `IrEnumConstructorCall`s to corresponding `IrCall`s.
// Add `name` and `ordinal` constant parameters only for calls to the "enum class" constructors ("enum entry class" constructors
// already delegate these parameters)
lowerEnumEntryInitializerExpression(irClass, container)
}
}
}
private fun lowerEnumConstructorsBody() {
irClass.declarations.filterIsInstance<IrConstructor>().forEach {
IrEnumClassConstructorTransformer(it).transformBody()
}
private fun lowerEnumConstructorsBody(constructor: IrConstructor) {
IrEnumClassConstructorTransformer(constructor).transformBody()
}
private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() {
@@ -205,11 +246,7 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
var delegatingConstructor = expression.symbol.owner
val constructorWasTransformed = delegatingConstructor.symbol in loweredEnumConstructors
if (constructorWasTransformed)
delegatingConstructor = loweredEnumConstructors[delegatingConstructor.symbol]!!
val delegatingConstructor = expression.symbol.owner. let { it.newConstructor ?: it }
return builder.irDelegatingConstructorCall(delegatingConstructor).apply {
var valueArgIdx = 0
@@ -223,41 +260,25 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
}
}
private fun fixReferencesToConstructorParameters() {
val fromOldToNewParameter = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
loweredEnumConstructors.forEach { (oldCtorSymbol, newCtor) ->
val oldParameters = oldCtorSymbol.owner.valueParameters
val newParameters = newCtor.valueParameters
oldParameters.forEach { old ->
fromOldToNewParameter[old.symbol] = newParameters.single { it.name == old.name }
}
}
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
fromOldToNewParameter[expression.symbol]?.let {
return builder.irGet(it)
}
return super.visitGetValue(expression)
}
})
private fun lowerEnumEntryClassConstructors(irClass: IrClass, entry: IrEnumEntry, constructor: IrConstructor) {
constructor.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(irClass, entry, true))
}
private fun lowerEnumEntryClassConstructors() {
for (entry in enumEntries) {
entry.correspondingClass?.constructors?.forEach {
it.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(entry, true))
}
}
}
private inner class IrEnumEntryClassConstructorTransformer(val entry: IrEnumEntry, val isInsideConstructor: Boolean) :
private inner class IrEnumEntryClassConstructorTransformer(
val irClass: IrClass,
val entry: IrEnumEntry,
val isInsideConstructor: Boolean
) :
IrElementTransformerVoid() {
private val enumEntries = irClass.enumEntries
private val builder = context.createIrBuilder(irClass.symbol)
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
private fun buildConstructorCall(constructor: IrConstructor) =
if (isInsideConstructor)
builder.irDelegatingConstructorCall(constructor)
@@ -266,11 +287,11 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
var constructor = expression.symbol.owner
val constructorWasTransformed = constructor.symbol in loweredEnumConstructors
val constructorWasTransformed = constructor.newConstructor != null
// Enum entry class constructors are not transformed
if (constructorWasTransformed)
constructor = loweredEnumConstructors[constructor.symbol]!!
constructor = constructor.newConstructor!!
return buildConstructorCall(constructor).apply {
var valueArgIdx = 0
@@ -287,64 +308,210 @@ class EnumClassConstructorTransformer(val context: CommonBackendContext, private
}
}
private fun lowerEnumEntryInitializerExpression() {
for (entry in enumEntries) {
entry.initializerExpression =
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(entry, false), null)
}
private fun lowerEnumEntryInitializerExpression(irClass: IrClass, entry: IrEnumEntry) {
entry.initializerExpression =
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(irClass, entry, false), null)
}
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
}
//-------------------------------------------------------
private val IrClass.goodEnum: Boolean
get() = isEnumClass && !descriptor.isExpect && !isEffectivelyExternal()
class EnumClassTransformer(val context: JsIrBackendContext, private val irClass: IrClass) {
private val builder = context.createIrBuilder(irClass.symbol)
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
private val enumName = irClass.name.identifier
private val throwISESymbol = context.throwISEsymbol
class EnumEntryInstancesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
fun transform(): List<IrDeclaration> {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
// Create instance variable for each enum entry initialized with `null`
val entryInstances = createEnumEntryInstanceVariables()
// Create boolean flag that indicates if entry instances were initialized.
val entryInstancesInitializedVar = createEntryInstancesInitializedVar()
// Create function that initializes all enum entry instances using `IrEnumEntry.initializationExpression`.
// It should be called on the first `IrGetEnumValue`, consecutive calls to this function will do nothing.
val initEntryInstancesFun = createInitEntryInstancesFun(entryInstancesInitializedVar, entryInstances)
// Create entry instance getters. These are used to lower `IrGetEnumValue`.
val entryGetInstanceFuns = createGetEntryInstanceFuns(initEntryInstancesFun, entryInstances)
// Create body for `values` and `valueOf` functions
lowerSyntheticFunctions()
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
replaceIrEntriesWithCorrespondingClasses()
return listOf(irClass) + entryInstances + listOf(entryInstancesInitializedVar, initEntryInstancesFun) + entryGetInstanceFuns
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrEnumEntry) {
val irClass = declaration.parentAsClass
if (irClass.goodEnum) {
// Create instance variable for each enum entry initialized with `null`
return listOf(declaration, createEnumEntryInstanceVariable(irClass, declaration))
}
}
return null
}
private fun createEnumEntryInstanceVariable(irClass: IrClass, enumEntry: IrEnumEntry): IrField {
val enumName = irClass.name.identifier
private fun createEnumValueOfBody(): IrBody {
val valueOfFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF)
val result = buildField {
name = Name.identifier("${enumName}_${enumEntry.name.identifier}_instance")
type = enumEntry.getType(irClass).makeNullable()
isStatic = true
}.apply {
parent = irClass
val builder = context.createIrBuilder(irClass.symbol)
initializer = builder.run { irExprBody(irImplicitCast(irNull(), type)) }
}
enumEntry.correspondingField = result
return result
}
}
class EnumEntryInstancesBodyLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrConstructor && container.constructedClass.kind == ClassKind.ENUM_ENTRY) {
val entryClass = container.constructedClass
val enum = entryClass.parentAsClass
if (enum.goodEnum) {
val entry = enum.declarations.filterIsInstance<IrEnumEntry>().find { it.correspondingClass === entryClass }!!
(irBody as IrBlockBody).statements.add(0, context.createIrBuilder(container.symbol).run {
irSetField(null, entry.correspondingField!!, irGet(entryClass.thisReceiver!!))
})
}
}
}
}
class EnumClassCreateInitializerLowering(val context: JsIrBackendContext) : DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrClass && declaration.goodEnum) {
// Create boolean flag that indicates if entry instances were initialized.
val entryInstancesInitializedVar = createEntryInstancesInitializedVar(declaration)
// Create function that initializes all enum entry instances using `IrEnumEntry.initializationExpression`.
// It should be called on the first `IrGetEnumValue`, consecutive calls to this function will do nothing.
val initEntryInstancesFun = createInitEntryInstancesFun(declaration, entryInstancesInitializedVar)
declaration.initEntryInstancesFun = initEntryInstancesFun
// TODO Why not move to upper level?
// TODO Also doesn't fit the transformFlat-ish API
stageController.unrestrictDeclarationListsAccess {
declaration.declarations += entryInstancesInitializedVar
declaration.declarations += initEntryInstancesFun
}
return null
}
return null
}
private fun createEntryInstancesInitializedVar(irClass: IrClass): IrField = buildField {
val enumName = irClass.name.identifier
name = Name.identifier("${enumName}_entriesInitialized")
type = context.irBuiltIns.booleanType
isStatic = true
}.apply {
parent = irClass
val builder = context.createIrBuilder(irClass.symbol)
initializer = builder.run { irExprBody(irBoolean(false)) }
}
private fun createInitEntryInstancesFun(irClass: IrClass, entryInstancesInitializedField: IrField): IrSimpleFunction =
buildFunction(irClass, "${irClass.name.identifier}_initEntries", context.irBuiltIns.unitType).also {
it.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(it.symbol).irBlockBody(it) {
+irIfThen(irGetField(null, entryInstancesInitializedField), irReturnUnit())
+irSetField(null, entryInstancesInitializedField, irBoolean(true))
irClass.enumEntries.forEach { entry ->
entry.correspondingField?.let { instanceField ->
+irSetField(null, instanceField, entry.initializerExpression!!.expression)
}
}
}.also {
// entry.initializerExpression can have local declarations
it.acceptVoid(PatchDeclarationParentsVisitor(irClass))
}.statements
}
}
}
private fun buildFunction(
irClass: IrClass,
name: String,
returnType: IrType
) = JsIrBuilder.buildFunction(name, returnType, irClass)
class EnumEntryCreateGetInstancesFunsLowering(val context: JsIrBackendContext): DeclarationTransformer {
private var IrEnumEntry.correspondingField by context.mapping.enumEntryToCorrespondingField
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrEnumEntry) {
val irClass = declaration.parentAsClass
if (irClass.goodEnum) {
// Create entry instance getters. These are used to lower `IrGetEnumValue`.
val entryGetInstanceFun = createGetEntryInstanceFun(irClass, declaration, irClass.initEntryInstancesFun!!)
// TODO prettify
entryGetInstanceFun.parent = irClass.parent
stageController.unrestrictDeclarationListsAccess {
(irClass.parent as IrDeclarationContainer).declarations += entryGetInstanceFun
}
return listOf(declaration) // TODO not null?
}
}
return null
}
private fun createGetEntryInstanceFun(irClass: IrClass, enumEntry: IrEnumEntry, initEntryInstancesFun: IrSimpleFunction): IrSimpleFunction {
return context.mapping.enumEntryToGetInstanceFun.getOrPut(enumEntry) { buildFunction(irClass, createEntryAccessorName(irClass.name.identifier, enumEntry), enumEntry.getType(irClass)) }
.also {
it.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(it.symbol).irBlockBody(it) {
+irCall(initEntryInstancesFun)
+irReturn(irGetField(null, enumEntry.correspondingField!!))
}.statements
}
}
}
}
class EnumSyntheticFunctionsLowering(val context: JsIrBackendContext): DeclarationTransformer {
private var IrEnumEntry.getInstanceFun by context.mapping.enumEntryToGetInstanceFun
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrSimpleFunction) {
(declaration.body as? IrSyntheticBody)?.let { body ->
val kind = body.kind
declaration.parents.filterIsInstance<IrClass>().firstOrNull { it.goodEnum }?.let { irClass ->
declaration.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += when (kind) {
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody(declaration, irClass)
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody(declaration, irClass)
}.statements
}
}
}
}
return null
}
private val throwISESymbol = context.throwISEsymbol
private fun createEnumValueOfBody(valueOfFun: IrFunction, irClass: IrClass): IrBlockBody {
val nameParameter = valueOfFun.valueParameters[0]
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
return context.createIrBuilder(valueOfFun.symbol).run {
irBlockBody {
+irReturn(
irWhen(
irClass.defaultType,
enumEntries.map {
irClass.enumEntries.map {
irBranch(
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(entryInstanceToFunction[it.symbol]!!)
irEquals(irString(it.name.identifier), irGet(nameParameter)), irCall(it.getInstanceFun!!)
)
} + irElseBranch(irCall(throwISESymbol))
)
@@ -353,131 +520,38 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
}
}
private fun createEnumValuesBody(): IrBody {
val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES)
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, context.intrinsics.arrayLiteral).apply {
putValueArgument(0, irVararg)
}
}
private fun createEnumValuesBody(valuesFun: IrFunction, irClass: IrClass): IrBlockBody {
val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody {
+irReturn(
enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) }
irClass.enumEntries.map { irCall(it.getInstanceFun!!) }
.toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
)
}
}
}
private fun lowerEnumEntryClassConstructors(entryInstances: List<IrVariable>) {
for ((entry, instance) in enumEntries.zip(entryInstances)) {
entry.correspondingClass?.constructors?.forEach {
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body
(it.body as? IrBlockBody)?.apply {
statements.add(0, context.createIrBuilder(it.symbol).run {
irSetVar(instance.symbol, irGet(entry.correspondingClass!!.thisReceiver!!))
})
}
}
}
}
private fun createEnumEntryInstanceVariables() = enumEntries.map { enumEntry ->
val result = buildField {
name = Name.identifier("${enumName}_${enumEntry.name.identifier}_instance")
type = enumEntry.getType(irClass).makeNullable()
isStatic = true
}.apply {
parent = irClass
initializer = builder.run { irExprBody(irImplicitCast(irNull(), type)) }
}
enumEntry.correspondingClass?.constructors?.forEach {
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body
(it.body as? IrBlockBody)?.apply {
statements.add(0, context.createIrBuilder(it.symbol).run {
irSetField(null, result, irGet(enumEntry.correspondingClass!!.thisReceiver!!))
})
}
}
result
}
private fun replaceIrEntriesWithCorrespondingClasses() {
irClass.transformDeclarationsFlat {
listOfNotNull(if (it is IrEnumEntry) it.correspondingClass else it)
}
}
private fun lowerSyntheticFunctions() {
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitSyntheticBody(body: IrSyntheticBody): IrBody {
return when (body.kind) {
IrSyntheticBodyKind.ENUM_VALUES -> createEnumValuesBody()
IrSyntheticBodyKind.ENUM_VALUEOF -> createEnumValueOfBody()
}
}
})
}
private fun createGetEntryInstanceFuns(
initEntryInstancesFun: IrSimpleFunction,
entryInstances: List<IrField>
) = enumEntries.mapIndexed { index, enumEntry ->
context.enumEntryToGetInstanceFunction.getOrPut(enumEntry.symbol) {
buildFunction(createEntryAccessorName(enumName, enumEntry), enumEntry.getType(irClass))
}.apply {
body = context.createIrBuilder(symbol).irBlockBody(this) {
+irCall(initEntryInstancesFun)
+irReturn(irGetField(null, entryInstances[index]))
}
}
}
private fun createInitEntryInstancesFun(
entryInstancesInitializedField: IrField,
entryInstances: List<IrField>
) = buildFunction("${enumName}_initEntries") {
+irIfThen(irGetField(null, entryInstancesInitializedField), irReturnUnit())
+irSetField(null, entryInstancesInitializedField, irBoolean(true))
for ((entry, instanceField) in enumEntries.zip(entryInstances)) {
+irSetField(null, instanceField, entry.initializerExpression!!)
}
}.also {
// entry.initializerExpression can have local declarations
it.acceptVoid(PatchDeclarationParentsVisitor(irClass))
}
private fun createEntryInstancesInitializedVar(): IrField = buildField {
name = Name.identifier("${enumName}_entriesInitialized")
type = context.irBuiltIns.booleanType
isStatic = true
}.apply {
parent = irClass
initializer = builder.run { irExprBody(irBoolean(false)) }
}
private fun findFunctionDescriptorForMemberWithSyntheticBodyKind(kind: IrSyntheticBodyKind): IrFunction =
irClass.declarations.asSequence().filterIsInstance<IrFunction>()
.first {
it.body.let { body ->
body is IrSyntheticBody && body.kind == kind
}
}
private fun buildFunction(
name: String,
returnType: IrType = context.irBuiltIns.unitType
) = JsIrBuilder.buildFunction(name, returnType, irClass)
private fun buildFunction(
name: String,
returnType: IrType = context.irBuiltIns.unitType,
bodyBuilder: IrBlockBodyBuilder.() -> Unit
) = JsIrBuilder.buildFunction(name, returnType, irClass).also {
it.body = context.createIrBuilder(it.symbol).irBlockBody(it, bodyBuilder)
}
}
private val IrClass.enumEntries: List<IrEnumEntry>
get() = declarations.filterIsInstance<IrEnumEntry>()
// Should be applied recursively
class EnumClassRemoveEntriesLowering(val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
// Remove IrEnumEntry nodes from class declarations. Replace them with corresponding class declarations (if they have them).
if (declaration is IrEnumEntry && !declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()) {
return listOfNotNull(declaration.correspondingClass)
}
return null
}
}

View File

@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) : DefaultArgumentStubGenerator(context, true, true) {
class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) : DefaultArgumentStubGenerator(context, true, true, false) {
override fun needSpecialDispatch(irFunction: IrSimpleFunction) = irFunction.isOverridableOrOverrides
@@ -54,7 +54,7 @@ class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) :
val BIND_CALL = object : IrStatementOriginImpl("BIND_CALL") {}
class JsDefaultCallbackGenerator(val context: JsIrBackendContext): BodyLoweringPass {
override fun lower(irBody: IrBody) {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
@@ -71,7 +71,7 @@ class JsDefaultCallbackGenerator(val context: JsIrBackendContext): BodyLoweringP
private fun buildBoundSuperCall(irCall: IrCall): IrExpression {
val originalFunction = context.ir.defaultParameterDeclarationsCache.entries.first { it.value == irCall.symbol.owner }.key
val originalFunction = context.mapping.defaultArgumentsOriginalFunction[irCall.symbol.owner]!!
val reference = irCall.run {
IrFunctionReferenceImpl(

View File

@@ -5,20 +5,21 @@
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
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.symbols.IrExternalPackageFragmentSymbol
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.util.transformFlat
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(
@@ -43,24 +44,6 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
"kotlin.Function"
).map { FqName(it) }.toSet()
private class DescriptorlessExternalPackageFragmentSymbol : IrExternalPackageFragmentSymbol {
override val descriptor: PackageFragmentDescriptor
get() = error("Operation is unsupported")
private var _owner: IrExternalPackageFragment? = null
override val owner get() = _owner!!
override var uniqId: UniqId
get() = error("Operation is unsupported")
set(value) { error("Operation is unsupported") }
override val isBound get() = _owner != null
override fun bind(owner: IrExternalPackageFragment) {
_owner = owner
}
}
private class DescriptorlessIrFileSymbol : IrFileSymbol {
override fun bind(owner: IrFile) {
_owner = owner
@@ -79,33 +62,22 @@ private class DescriptorlessIrFileSymbol : IrFileSymbol {
override val isBound get() = _owner != null
}
private fun isBuiltInClass(declaration: IrDeclaration): Boolean =
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module: IrModuleFragment) {
val bodilessBuiltInsPackageFragment = IrExternalPackageFragmentImpl(
DescriptorlessExternalPackageFragmentSymbol(),
FqName("kotlin")
)
context.bodilessBuiltInsPackageFragment = bodilessBuiltInsPackageFragment
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
fun collectExternalClasses(container: IrDeclarationContainer, includeCurrentLevel: Boolean): List<IrClass> {
val externalClasses =
container.declarations.filterIsInstance<IrClass>().filter { it.isEffectivelyExternal() }
val nestedExternalClasses =
externalClasses.flatMap { collectExternalClasses(it, true) }
return if (includeCurrentLevel)
externalClasses + nestedExternalClasses
else
nestedExternalClasses
fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, moduleFragment: IrModuleFragment) {
MoveBodilessDeclarationsToSeparatePlaceLowering(context).let { moveBodiless ->
moduleFragment.files.forEach {
moveBodiless.lower(it)
}
}
}
class MoveBodilessDeclarationsToSeparatePlaceLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
val irFile = declaration.parent as? IrFile ?: return null
fun lowerFile(irFile: IrFile): IrFile? {
val externalPackageFragment by lazy {
context.externalPackageFragment.getOrPut(irFile.symbol) {
IrFileImpl(fileEntry = irFile.fileEntry, fqName = irFile.fqName, symbol = DescriptorlessIrFileSymbol()).also {
@@ -114,33 +86,49 @@ fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, module:
}
}
context.externalNestedClasses += collectExternalClasses(irFile, includeCurrentLevel = false)
if (irFile.getJsModule() != null || irFile.getJsQualifier() != null) {
context.packageLevelJsModules.add(irFile)
return null
}
externalPackageFragment.declarations += declaration
declaration.parent = externalPackageFragment
val it = irFile.declarations.iterator()
context.packageLevelJsModules += externalPackageFragment
while (it.hasNext()) {
val d = it.next() as? IrDeclarationWithName ?: continue
declaration.collectAllExternalDeclarations()
return emptyList()
} else {
val d = declaration as? IrDeclarationWithName ?: return null
if (isBuiltInClass(d)) {
it.remove()
bodilessBuiltInsPackageFragment.addChild(d)
context.bodilessBuiltInsPackageFragment.addChild(d)
d.collectAllExternalDeclarations()
return emptyList()
} else if (d.isEffectivelyExternal()) {
if (d.getJsModule() != null)
context.declarationLevelJsModules.add(d)
it.remove()
externalPackageFragment.addChild(d)
externalPackageFragment.declarations += d
d.parent = externalPackageFragment
d.collectAllExternalDeclarations()
return emptyList()
}
return null
}
return irFile
}
module.files.transformFlat { irFile ->
listOfNotNull(lowerFile(irFile))
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)
}
}
}

View File

@@ -5,13 +5,12 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCatchImpl
@@ -45,12 +44,12 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
* finally {}
*/
class MultipleCatchesLowering(private val context: JsIrBackendContext) : FileLoweringPass {
class MultipleCatchesLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
private val litTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val nothingType = context.irBuiltIns.nothingType
override fun lower(irFile: IrFile) {
irFile.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transform(object : IrElementTransformer<IrDeclarationParent> {
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclarationParent): IrStatement {
val parent = (declaration as? IrDeclarationParent) ?: data
@@ -117,6 +116,6 @@ class MultipleCatchesLowering(private val context: JsIrBackendContext) : FileLow
private fun buildImplicitCast(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType)
}, irFile)
}, container as? IrDeclarationParent ?: container.parent)
}
}

View File

@@ -5,94 +5,104 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
class ObjectDeclarationLowering(
val context: CommonBackendContext,
val objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction>
) : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
return@transformDeclarationsFlat null
val context: JsCommonBackendContext
) : DeclarationTransformer {
val getInstanceFun = getOrCreateGetInstanceFunction(objectToGetInstanceFunction, declaration)
private var IrClass.instanceField by context.mapping.objectToInstanceField
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
val instanceField = buildField {
name = Name.identifier(declaration.name.asString() + "_instance")
type = declaration.defaultType.makeNullable()
isStatic = true
}.apply {
parent = declaration.parent
initializer = null // Initialized with 'undefined'
}
val primaryConstructor = declaration.primaryConstructor!!
val body = primaryConstructor.body as IrBlockBody
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
return null
// Initialize instance field in the beginning of the constructor because it can be used inside the constructor later
val initInstanceField = context.createIrBuilder(primaryConstructor.symbol).buildStatement(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
irSetField(null, instanceField, irGet(declaration.thisReceiver!!))
}
body.statements.add(0, initInstanceField)
val getInstanceFun = context.getOrCreateGetInstanceFunction(declaration)
getInstanceFun.body = context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) {
val instanceField = buildField {
name = Name.identifier(declaration.name.asString() + "_instance")
type = declaration.defaultType.makeNullable()
isStatic = true
}.apply {
parent = declaration.parent
initializer = null // Initialized with 'undefined'
}
declaration.instanceField = instanceField
val primaryConstructor = declaration.primaryConstructor ?: declaration.syntheticPrimaryConstructor!!
getInstanceFun.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += context.createIrBuilder(getInstanceFun.symbol).irBlockBody(getInstanceFun) {
+irIfThen(
irEqualsNull(irGetField(null, instanceField)),
// Instance field initialized inside constructor
irCallConstructor(primaryConstructor.symbol, emptyList())
)
+irReturn(irGetField(null, instanceField))
}
listOf(declaration, instanceField, getInstanceFun)
}.statements
}
return listOf(declaration, instanceField, getInstanceFun)
}
}
class ObjectUsageLowering(
val context: CommonBackendContext,
val objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction>
) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
val context: JsCommonBackendContext
) : BodyLoweringPass {
private var IrClass.instanceField by context.mapping.objectToInstanceField
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrConstructor && container.isPrimary) {
val irClass = container.parentAsClass
irClass.instanceField?.let { instanceField ->
// Initialize instance field in the beginning of the constructor because it can be used inside the constructor later
val initInstanceField = context.createIrBuilder(container.symbol).buildStatement(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
irSetField(null, instanceField, irGet(irClass.thisReceiver!!))
}
(irBody as IrBlockBody).statements.add(0, initInstanceField)
}
}
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
val obj: IrClass = expression.symbol.owner
if (obj.isEffectivelyExternal()) return expression
return JsIrBuilder.buildCall(getOrCreateGetInstanceFunction(objectToGetInstanceFunction, obj).symbol)
return JsIrBuilder.buildCall(context.getOrCreateGetInstanceFunction(obj).symbol)
}
})
}
}
fun getOrCreateGetInstanceFunction(objectToGetInstanceFunction: MutableMap<IrClassSymbol, IrSimpleFunction>, obj: IrClass) =
objectToGetInstanceFunction.getOrPut(obj.symbol) {
fun JsCommonBackendContext.getOrCreateGetInstanceFunction(obj: IrClass) =
mapping.objectToGetInstanceFunction.getOrPut(obj) {
JsIrBuilder.buildFunction(
obj.name.asString() + "_getInstance",
returnType = obj.defaultType,

View File

@@ -5,52 +5,51 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
// Create primary constructor if it doesn't exist
class PrimaryConstructorLowering(context: CommonBackendContext) : ClassLoweringPass {
private val unitType = context.irBuiltIns.unitType
class PrimaryConstructorLowering(context: JsCommonBackendContext) : DeclarationTransformer {
override fun lower(irClass: IrClass) {
val constructors = irClass.declarations.filterIsInstance<IrConstructor>()
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
if (constructors.any { it.isPrimary }) return
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrClass) {
val constructors = declaration.constructors
val primary = createPrimaryConstructor(irClass)
if (constructors.any { it.isPrimary }) return null
val initializeTransformer = object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclaration) = declaration // optimize visiting
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall) = expression.run {
IrDelegatingConstructorCallImpl(startOffset, endOffset, type, primary.symbol)
}
declaration.syntheticPrimaryConstructor = createPrimaryConstructor(declaration)
}
constructors.forEach { it.transformChildrenVoid(initializeTransformer) }
return null
}
private object SYNTHETIC_PRIMARY_CONSTRUCTOR : IrDeclarationOriginImpl("SYNTHETIC_PRIMARY_CONSTRUCTOR")
private val unitType = context.irBuiltIns.unitType
private fun createPrimaryConstructor(irClass: IrClass): IrConstructor {
val declaration = irClass.addConstructor {
origin = SYNTHETIC_PRIMARY_CONSTRUCTOR
isPrimary = true
visibility = Visibilities.PRIVATE
// TODO better API for declaration creation. This case doesn't fit the usual transformFlat-like API.
val declaration = stageController.unrestrictDeclarationListsAccess {
irClass.addConstructor {
origin = SYNTHETIC_PRIMARY_CONSTRUCTOR
isPrimary = true
visibility = Visibilities.PRIVATE
}
}
declaration.body = irClass.run {
@@ -59,4 +58,25 @@ class PrimaryConstructorLowering(context: CommonBackendContext) : ClassLoweringP
return declaration
}
}
class DelegateToSyntheticPrimaryConstructor(context: JsCommonBackendContext) : BodyLoweringPass {
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrConstructor && !container.isPrimary) {
container.parentAsClass.syntheticPrimaryConstructor?.let { primary ->
val initializeTransformer = object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclaration) = declaration // optimize visiting
override fun visitInstanceInitializerCall(expression: IrInstanceInitializerCall) = expression.run {
IrDelegatingConstructorCallImpl(startOffset, endOffset, type, primary.symbol)
}
}
irBody.transformChildrenVoid(initializeTransformer)
}
}
}
}

View File

@@ -5,12 +5,13 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
@@ -19,10 +20,11 @@ import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.types.isString
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLoweringPass {
class PrimitiveCompanionLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private fun getActualPrimitiveCompanion(irClass: IrClass): IrClass? {
if (!irClass.isCompanion)
@@ -50,13 +52,18 @@ class PrimitiveCompanionLowering(val context: JsIrBackendContext) : FileLowering
val actualCompanion = getActualPrimitiveCompanion(companion)
?: return null
for (p in actualCompanion.properties) {
p.getter?.let { if (it.name == function.name) return it }
p.setter?.let { if (it.name == function.name) return it }
}
return actualCompanion.declarations
.filterIsInstance<IrSimpleFunction>()
.single { it.name == function.name }
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
val irClass = expression.symbol.owner
val actualCompanion = getActualPrimitiveCompanion(irClass) ?: return expression

View File

@@ -5,10 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.*
@@ -17,56 +17,143 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.ir.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
private val STATIC_THIS_PARAMETER = object : IrDeclarationOriginImpl("STATIC_THIS_PARAMETER") {}
class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val memberMap = context.memberMap
class PrivateMembersLowering(val context: JsIrBackendContext) : DeclarationTransformer {
override fun lower(irFile: IrFile) {
transformPrivateDeclarations(irFile)
transformPrivateUseSites(irFile)
private var IrFunction.correspondingStatic by context.mapping.privateMemberToCorrespondingStatic
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
return when (declaration) {
is IrSimpleFunction -> transformMemberToStaticFunction(declaration)?.let { staticFunction ->
declaration.correspondingStatic = staticFunction
listOf(staticFunction)
}
is IrProperty -> listOf(declaration.apply {
// Detach old function from corresponding property
this.getter = this.getter?.let { g -> transformAccessor(g) }
this.setter = this.setter?.let { s -> transformAccessor(s) }
})
else -> null
}
}
private fun transformPrivateDeclarations(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid(this)
declaration.declarations.transformFlat {
when (it) {
is IrSimpleFunction -> transformMemberToStaticFunction(it)?.let { staticFunction ->
listOf(staticFunction)
}
is IrProperty -> listOf(it.apply {
this.getter = this.getter?.let { g -> transformAccessor(g) }
this.setter = this.setter?.let { s -> transformAccessor(s) }
})
else -> null
private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: accessor
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null
val descriptor = WrappedSimpleFunctionDescriptor()
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
val staticFunction = function.run {
IrFunctionImpl(
startOffset, endOffset, origin,
symbol, name, visibility, modality,
returnType,
isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect,
isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE,
isOperator = isOperator
).also {
descriptor.bind(it)
it.parent = parent
it.correspondingPropertySymbol = correspondingPropertySymbol
}
}
staticFunction.typeParameters += function.typeParameters.map { it.deepCopyWithSymbols(staticFunction) }
staticFunction.extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(staticFunction)
val thisDesc = WrappedValueParameterDescriptor()
val thisSymbol = IrValueParameterSymbolImpl(thisDesc)
staticFunction.valueParameters += IrValueParameterImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
STATIC_THIS_PARAMETER,
thisSymbol,
Name.identifier("\$this"),
0,
function.dispatchReceiverParameter!!.type,
null,
isCrossinline = false,
isNoinline = false
).also {
thisDesc.bind(it)
it.parent = staticFunction
}
function.correspondingStatic = staticFunction
staticFunction.valueParameters += function.valueParameters.map {
// TODO better way to avoid copying default value
it.copyTo(staticFunction, index = it.index + 1, defaultValue = null)
}
val oldParameters =
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
assert(oldParameters.size == newParameters.size)
val parameterMapping = oldParameters.zip(newParameters).toMap()
val parameterTransformer = object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue) = parameterMapping[expression.symbol.owner]?.let {
expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) }
} ?: expression
}
fun IrBody.copyWithParameters(): IrBody {
return deepCopyWithSymbols(staticFunction).also {
it.transform(parameterTransformer, null)
}
}
function.valueParameters.forEach {
// TODO better way to avoid copying default value
parameterMapping[it]?.apply {
it.defaultValue?.let { originalDefault ->
defaultValue = IrExpressionBodyImpl(it.startOffset, it.endOffset) {
expression = (originalDefault.copyWithParameters() as IrExpressionBody).expression
}
}
return declaration
}
})
}
}
private fun transformPrivateUseSites(irFile: IrFile) {
irFile.transform(object : IrElementTransformerVoid() {
function.body?.let {
staticFunction.body = when (it) {
is IrBlockBody -> IrBlockBodyImpl(it.startOffset, it.endOffset) {
statements += (it.copyWithParameters() as IrBlockBody).statements
}
is IrExpressionBody -> IrExpressionBodyImpl(it.startOffset, it.endOffset) {
expression = (it.copyWithParameters() as IrExpressionBody).expression
}
is IrSyntheticBody -> it
else -> error("Unexpected body kind: ${it.javaClass}")
}
}
return staticFunction
}
}
class PrivateMemberBodiesLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private var IrFunction.correspondingStatic by context.mapping.privateMemberToCorrespondingStatic
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transform(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
return getOrPutStaticFunction(expression.symbol)?.let {
return expression.symbol.owner.correspondingStatic?.let {
transformPrivateToStaticCall(expression, it)
} ?: expression
}
@@ -74,7 +161,7 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
super.visitFunctionReference(expression)
return getOrPutStaticFunction(expression.symbol)?.let {
return expression.symbol.owner.correspondingStatic?.let {
transformPrivateToStaticReference(expression) {
IrFunctionReferenceImpl(
expression.startOffset, expression.endOffset,
@@ -89,8 +176,8 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
super.visitPropertyReference(expression)
val staticGetter = expression.getter?.let { getOrPutStaticFunction(it) }
val staticSetter = expression.setter?.let { getOrPutStaticFunction(it) }
val staticGetter = expression.getter?.owner?.correspondingStatic
val staticSetter = expression.setter?.owner?.correspondingStatic
return if (staticGetter != null || staticSetter != null) {
transformPrivateToStaticReference(expression) {
@@ -147,88 +234,4 @@ class PrivateMembersLowering(val context: JsIrBackendContext) : FileLoweringPass
}
}, null)
}
private fun transformAccessor(accessor: IrSimpleFunction) = transformMemberToStaticFunction(accessor) ?: accessor
private fun transformMemberToStaticFunction(function: IrSimpleFunction): IrSimpleFunction? {
val staticFunction = getOrPutStaticFunction(function.symbol) ?: return null
// Detach old function from corresponding property
val correspondingProperty = function.correspondingPropertySymbol?.owner
if (correspondingProperty != null) {
when (function) {
correspondingProperty.getter -> correspondingProperty.getter = staticFunction
correspondingProperty.setter -> correspondingProperty.setter = staticFunction
}
}
val oldParameters =
listOfNotNull(function.extensionReceiverParameter, function.dispatchReceiverParameter) + function.valueParameters
val newParameters = listOfNotNull(staticFunction.extensionReceiverParameter) + staticFunction.valueParameters
assert(oldParameters.size == newParameters.size)
val parameterMapping = oldParameters.zip(newParameters).toMap()
staticFunction.body = function.body?.deepCopyWithSymbols(staticFunction)
staticFunction.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue) = parameterMapping[expression.symbol.owner]?.let {
expression.run { IrGetValueImpl(startOffset, endOffset, type, it.symbol, origin) }
} ?: expression
}, null)
return staticFunction
}
private fun getOrPutStaticFunction(functionSymbol: IrFunctionSymbol): IrSimpleFunction? {
val function = functionSymbol.owner
if (function !is IrSimpleFunction) return null
if (function.visibility != Visibilities.PRIVATE || function.dispatchReceiverParameter == null) return null
return memberMap.getOrPut(function.symbol) {
val descriptor = WrappedSimpleFunctionDescriptor()
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
val staticFunction = function.run {
IrFunctionImpl(
startOffset, endOffset, origin,
symbol, name, visibility, modality,
returnType,
isInline = isInline, isExternal = isExternal, isTailrec = isTailrec, isSuspend = isSuspend, isExpect = isExpect,
isFakeOverride = origin == IrDeclarationOrigin.FAKE_OVERRIDE,
isOperator = isOperator
).also {
descriptor.bind(it)
it.parent = parent
it.correspondingPropertySymbol = correspondingPropertySymbol
}
}
staticFunction.typeParameters += function.typeParameters.map { it.deepCopyWithSymbols(staticFunction) }
staticFunction.extensionReceiverParameter = function.extensionReceiverParameter?.copyTo(staticFunction)
val thisDesc = WrappedValueParameterDescriptor()
val thisSymbol = IrValueParameterSymbolImpl(thisDesc)
staticFunction.valueParameters += IrValueParameterImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
STATIC_THIS_PARAMETER,
thisSymbol,
Name.identifier("\$this"),
0,
function.dispatchReceiverParameter!!.type,
null,
isCrossinline = false,
isNoinline = false
).also {
thisDesc.bind(it)
it.parent = staticFunction
}
staticFunction.valueParameters += function.valueParameters.map { it.copyTo(staticFunction, index = it.index + 1) }
staticFunction
}
}
}

View File

@@ -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 {

View File

@@ -5,18 +5,21 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
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.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
@@ -26,38 +29,36 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
data class ConstructorPair(val delegate: IrSimpleFunction, val stub: IrSimpleFunction)
class SecondaryConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
class SecondaryConstructorLowering(val context: JsIrBackendContext) : ClassLoweringPass {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
if (declaration is IrConstructor && !declaration.isPrimary) {
val irClass = declaration.parentAsClass
override fun lower(irClass: IrClass) {
if (irClass.isInline) return
if (irClass.isInline) return null
irClass.declarations.transformFlat {
if (it is IrConstructor) {
if (it.isPrimary) null else transformConstructor(it, irClass)
} else null
return transformConstructor(declaration, irClass)
}
return null
}
private fun transformConstructor(constructor: IrConstructor, irClass: IrClass): List<IrSimpleFunction> {
val stubs = oldCtorToNewMap.getOrPut(constructor) {
buildConstructorStubDeclarations(constructor, irClass)
}
val delegate = context.buildConstructorDelegate(constructor, irClass)
generateStubsBody(constructor, irClass, stubs)
val factory = context.buildConstructorFactory(constructor, irClass)
return listOf(stubs.delegate, stubs.stub)
generateStubsBody(constructor, irClass, delegate, factory)
return listOf(delegate, factory)
}
private fun generateStubsBody(constructor: IrConstructor, irClass: IrClass, stubs: ConstructorPair) {
private fun generateStubsBody(constructor: IrConstructor, irClass: IrClass, delegate: IrSimpleFunction, factory: IrSimpleFunction) {
// We should split secondary constructor into two functions,
// * Initializer which contains constructor's body and takes just created object as implicit param `$this`
// ** This function is also delegation constructor
@@ -76,41 +77,42 @@ class SecondaryConstructorLowering(val context: JsIrBackendContext) : ClassLower
// val t = Object.create(Foo.prototype);
// return Foo_init_$Init$(..., t)
// }
generateInitBody(constructor, irClass, stubs.delegate)
generateFactoryBody(constructor, irClass, stubs.stub, stubs.delegate)
generateInitBody(constructor, irClass, delegate)
generateFactoryBody(constructor, irClass, factory, delegate)
}
private fun generateFactoryBody(constructor: IrConstructor, irClass: IrClass, stub: IrSimpleFunction, delegate: IrSimpleFunction) {
val type = irClass.defaultType
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic.symbol, type, listOf(type))
val irDelegateCall = JsIrBuilder.buildCall(delegate.symbol, type).also { call ->
for (i in 0 until stub.typeParameters.size) {
call.putTypeArgument(i, stub.typeParameters[i].toIrType())
}
stub.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
val type = irClass.defaultType
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic.symbol, type, listOf(type))
val irDelegateCall = JsIrBuilder.buildCall(delegate.symbol, type).also { call ->
for (i in 0 until stub.typeParameters.size) {
call.putTypeArgument(i, stub.typeParameters[i].toIrType())
}
for (i in 0 until stub.valueParameters.size) {
call.putValueArgument(i, JsIrBuilder.buildGetValue(stub.valueParameters[i].symbol))
}
for (i in 0 until stub.valueParameters.size) {
call.putValueArgument(i, JsIrBuilder.buildGetValue(stub.valueParameters[i].symbol))
}
call.putValueArgument(constructor.valueParameters.size, irCreateCall)
call.putValueArgument(constructor.valueParameters.size, irCreateCall)
}
val irReturn = JsIrBuilder.buildReturn(stub.symbol, irDelegateCall, context.irBuiltIns.nothingType)
statements += irReturn
}
val irReturn = JsIrBuilder.buildReturn(stub.symbol, irDelegateCall, context.irBuiltIns.nothingType)
stub.body = JsIrBuilder.buildBlockBody(listOf(irReturn))
}
private fun generateInitBody(constructor: IrConstructor, irClass: IrClass, delegate: IrSimpleFunction) {
val thisParam = delegate.valueParameters.last()
val oldThisReceiver = irClass.thisReceiver!!
val retStmt = JsIrBuilder.buildReturn(delegate.symbol, JsIrBuilder.buildGetValue(thisParam.symbol), context.irBuiltIns.nothingType)
val statements = (constructor.body!!.deepCopyWithSymbols(delegate) as IrStatementContainer).statements
val constructorBody = constructor.body!!
val oldValueParameters = constructor.valueParameters + oldThisReceiver
// TODO: replace parameters as well
delegate.body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
delegate.body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += (constructorBody.deepCopyWithSymbols(delegate) as IrStatementContainer).statements
statements += JsIrBuilder.buildReturn(delegate.symbol, JsIrBuilder.buildGetValue(thisParam.symbol), context.irBuiltIns.nothingType)
transformChildrenVoid(ThisUsageReplaceTransformer(delegate.symbol, oldValueParameters.zip(delegate.valueParameters).toMap()))
}
}
@@ -155,7 +157,7 @@ private fun buildInitDeclaration(constructor: IrConstructor, irClass: IrClass):
).also {
it.copyTypeParametersFrom(constructor.parentAsClass)
constructor.valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
it.valueParameters = constructor.valueParameters.map { p -> p.copyTo(it) }
it.valueParameters += JsIrBuilder.buildValueParameter("\$this", constructor.valueParameters.size, type).apply { parent = it }
}
}
@@ -179,18 +181,40 @@ private fun buildFactoryDeclaration(constructor: IrConstructor, irClass: IrClass
}
}
private fun buildConstructorStubDeclarations(constructor: IrConstructor, klass: IrClass) =
ConstructorPair(buildInitDeclaration(constructor, klass), buildFactoryDeclaration(constructor, klass))
class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(CallsiteRedirectionTransformer(context), null)
private fun JsIrBackendContext.buildConstructorDelegate(constructor: IrConstructor, klass: IrClass): IrSimpleFunction {
return mapping.secondaryConstructorToDelegate.getOrPut(constructor) {
buildInitDeclaration(constructor, klass)
}
}
private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
private fun JsIrBackendContext.buildConstructorFactory(constructor: IrConstructor, klass: IrClass): IrSimpleFunction {
return mapping.secondaryConstructorToFactory.getOrPut(constructor) {
buildFactoryDeclaration(constructor, klass)
}
}
class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
// TODO Simplify? Is this needed at all?
var parentFunction: IrFunction? = container as? IrFunction
var declaration = container
while (parentFunction == null) {
val parent = declaration.parent
if (parent is IrFunction) {
parentFunction = parent
}
declaration = parent as? IrDeclaration ?: break
}
irBody.accept(CallsiteRedirectionTransformer(context), parentFunction)
}
}
private class CallsiteRedirectionTransformer(private val context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
private val oldCtorToNewMap = context.secondaryConstructorToFactoryCache
private val defaultThrowableConstructor = context.defaultThrowableCtor
private val IrConstructor.isSecondaryConstructorCall
@@ -204,10 +228,8 @@ private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrEl
val target = expression.symbol.owner
return if (target.isSecondaryConstructorCall) {
val ctor = oldCtorToNewMap.getOrPut(target) {
buildConstructorStubDeclarations(target, target.parentAsClass)
}
replaceSecondaryConstructorWithFactoryFunction(expression, ctor.stub.symbol)
val factory = context.buildConstructorFactory(target, target.parentAsClass)
replaceSecondaryConstructorWithFactoryFunction(expression, factory.symbol)
} else expression
}
@@ -218,8 +240,8 @@ private class CallsiteRedirectionTransformer(context: JsIrBackendContext) : IrEl
return if (target.isSecondaryConstructorCall) {
val klass = target.parentAsClass
val ctor = oldCtorToNewMap.getOrPut(target) { buildConstructorStubDeclarations(target, klass) }
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, ctor.delegate.symbol)
val delegate = context.buildConstructorDelegate(target, klass)
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, delegate.symbol)
val readThis = expression.run {
if (data!! is IrConstructor) {

View File

@@ -6,51 +6,29 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
// Move static member declarations from classes to top level
class StaticMembersLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
val staticDeclarationsInClasses = mutableListOf<IrDeclaration>()
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
class StaticMembersLowering(val context: CommonBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
(declaration.parent as? IrClass)?.let { irClass ->
val isStatic = when (declaration) {
is IrClass -> !declaration.isEffectivelyExternal()
is IrSimpleFunction -> declaration.isStaticMethodOfClass && !declaration.isEffectivelyExternal()
is IrField -> declaration.isStatic
else -> false
}
override fun visitClass(declaration: IrClass) {
if (declaration.parent is IrClass && !declaration.isEffectivelyExternal())
staticDeclarationsInClasses.add(declaration)
super.visitClass(declaration)
if (isStatic) {
declaration.parent = irClass.file
irClass.file.declarations += declaration
return listOf()
}
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
if (declaration.parent is IrClass && declaration.isStaticMethodOfClass && !declaration.isEffectivelyExternal())
staticDeclarationsInClasses.add(declaration)
super.visitSimpleFunction(declaration)
}
override fun visitField(declaration: IrField) {
if (declaration.parent is IrClass && declaration.isStatic)
staticDeclarationsInClasses.add(declaration)
super.visitField(declaration)
}
})
for (declaration in staticDeclarationsInClasses) {
val klass = declaration.parentAsClass
klass.declarations.remove(declaration)
irFile.addChild(declaration)
declaration.parent = irFile
}
return null
}
}

View File

@@ -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) {

View File

@@ -5,21 +5,21 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.types.isNullableString
import org.jetbrains.kotlin.ir.types.makeNotNull
import org.jetbrains.kotlin.ir.util.isThrowable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
class ThrowableLowering(val context: JsIrBackendContext) : FileLoweringPass {
class ThrowableLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val nothingNType get() = context.irBuiltIns.nothingNType
private val throwableConstructors = context.throwableConstructors
@@ -33,8 +33,10 @@ class ThrowableLowering(val context: JsIrBackendContext) : FileLoweringPass {
val cause: IrExpression
)
override fun lower(irFile: IrFile) {
irFile.transformChildren(Transformer(), irFile)
override fun lower(irBody: IrBody, container: IrDeclaration) {
container.parentClassOrNull.let { enclosingClass ->
irBody.transformChildren(Transformer(), enclosingClass ?: container.file)
}
}
private fun IrFunctionAccessExpression.extractThrowableArguments(): ThrowableArguments =

View File

@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
@@ -14,11 +14,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isPure
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionWithCopy
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
@@ -27,7 +23,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val unit = context.irBuiltIns.unitType
private val unitValue get() = JsIrBuilder.buildGetObjectValue(unit, unit.classifierOrFail as IrClassSymbol)
@@ -66,8 +62,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val litFalse: IrExpression get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val litNull: IrExpression get() = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType)
override fun lower(irFile: IrFile) {
irFile.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclarationParent) =
super.visitDeclaration(declaration, declaration as? IrDeclarationParent ?: data)
@@ -375,6 +371,6 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
return expression.run { IrCompositeImpl(startOffset, endOffset, toType, null, newStatements) }
}
}, irFile)
}, container as? IrDeclarationParent ?: container.parent)
}
}

View File

@@ -5,11 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
@@ -21,12 +20,12 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class VarargLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(VarargTransformer(context))
class VarargLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(VarargTransformer(context))
}
}
private class VarargTransformer(
val context: JsIrBackendContext
) : IrElementTransformerVoid() {

View File

@@ -5,18 +5,19 @@
package org.jetbrains.kotlin.ir.backend.js.lower.calls
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass {
class CallsLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val transformers = listOf(
NumberOperatorCallsTransformer(context),
NumberConversionCallsTransformer(context),
@@ -30,8 +31,10 @@ class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass {
JsonIntrinsics(context)
)
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrFunction && container.hasAnnotation(context.intrinsics.doNotIntrinsifyAnnotationSymbol)) return
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitFunction(declaration: IrFunction): IrStatement {
if (declaration.hasAnnotation(context.intrinsics.doNotIntrinsifyAnnotationSymbol))
return declaration

View File

@@ -5,16 +5,47 @@
package org.jetbrains.kotlin.ir.backend.js.lower.inline
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
class RemoveInlineFunctionsWithReifiedTypeParametersLowering: DeclarationTransformer {
class RemoveInlineFunctionsWithReifiedTypeParametersLowering: DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat {
if (it is IrFunction && it.isInline && it.typeParameters.any { it.isReified }) listOf() else null
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrFunction && declaration.isInline && declaration.typeParameters.any { it.isReified }) {
return emptyList()
}
return null
}
}
class CopyInlineFunctionBodyLowering(val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrFunction && declaration.isInline) {
declaration.body?.let { originalBody ->
declaration.body = IrBlockBodyImpl(originalBody.startOffset, originalBody.endOffset) {
statements += (originalBody.deepCopyWithSymbols(declaration) as IrBlockBody).statements
}
}
}
if (declaration is IrValueParameter && declaration.parent.let { it is IrFunction && it.isInline }) {
declaration.defaultValue?.let { originalDefault ->
declaration.defaultValue = IrExpressionBodyImpl(originalDefault.startOffset, originalDefault.endOffset) {
expression = originalDefault.expression.deepCopyWithSymbols(declaration.parent)
}
}
}
return null
}
}

View File

@@ -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(

View File

@@ -33,6 +33,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
fun generate(): JsStatement {
assert(!irClass.descriptor.isExpect)
maybeGeneratePrimaryConstructor()
val transformer = IrDeclarationToJsTransformer()
// Properties might be lowered out of classes
@@ -142,6 +143,15 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return null
}
private fun maybeGeneratePrimaryConstructor() {
if (!irClass.declarations.any { it is IrConstructor }) {
val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}")
func.name = className
classBlock.statements += func.makeStmt()
classModel.preDeclarationBlock.statements += generateInheritanceCode()
}
}
private fun generateInheritanceCode(): List<JsStatement> {
if (baseClass == null || baseClass.isAny()) {
return emptyList()

View File

@@ -332,6 +332,11 @@ class NameTables(
val signature = fieldSignature(field)
val name = memberNames.names[signature] ?: mappedNames[mapToKey(signature)]
// TODO investigate
if (name == null) {
return sanitizeName(field.name.asString()) + "__error"
}
require(name != null) {
"Can't find name for member field ${field.render()}"
}
@@ -346,6 +351,14 @@ class NameTables(
// of `invoke` functions in FunctionN interfaces
if (name == null && signature is ParameterTypeBasedSignature && signature.suggestedName.startsWith("invoke"))
return signature.suggestedName
// TODO Add a compiler flag, which enables this behaviour
// TODO remove in DCE
if (name == null) {
return sanitizeName(function.name.asString()) + "__error" // TODO one case is a virtual method of an abstract class with no implementation
}
// TODO report backend error
require(name != null) {
"Can't find name for member function ${function.render()}"
}

View File

@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.backend.common.Mapping
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.lower.irThrow
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
@@ -32,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.*
@@ -58,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>()
@@ -68,6 +72,8 @@ class JvmBackendContext(
override val declarationFactory: JvmDeclarationFactory = JvmDeclarationFactory(methodSignatureMapper)
override val sharedVariablesManager = JvmSharedVariablesManager(state.module, builtIns, irBuiltIns)
override val mapping: Mapping = DefaultMapping()
val psiErrorBuilder = PsiErrorBuilder(psiSourceManager, state.diagnostics)
override val ir = JvmIr(irModuleFragment, this.symbolTable)

View File

@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub
import org.jetbrains.kotlin.backend.jvm.lower.*
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
@@ -79,9 +80,21 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase<JvmBackendContex
description = "Remove expect declaration from module fragment"
)
private val lateinitPhase = makeIrFilePhase(
::LateinitLowering,
name = "Lateinit",
private val lateinitNullableFieldsPhase = makeIrFilePhase(
::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields",
description = "Create nullable fields for lateinit properties"
)
private val lateinitDeclarationLoweringPhase = makeIrFilePhase(
::NullableFieldsDeclarationLowering,
name = "LateinitDeclarations",
description = "Reference nullable fields from properties and getters + insert checks"
)
private val lateinitUsageLoweringPhase = makeIrFilePhase(
::LateinitUsageLowering,
name = "LateinitUsage",
description = "Insert checks for lateinit field references"
)
@@ -150,11 +163,18 @@ private val defaultArgumentStubPhase = makeIrFilePhase(
prerequisite = setOf(localDeclarationsPhase)
)
private val defaultArgumentCleanerPhase = makeIrFilePhase(
{ context: JvmBackendContext -> DefaultParameterCleaner(context, replaceDefaultValuesWithStubs = true) },
name = "DefaultParameterCleaner",
description = "Replace default values arguments with stubs",
prerequisite = setOf(defaultArgumentStubPhase)
)
private val defaultArgumentInjectorPhase = makeIrFilePhase(
::JvmDefaultParameterInjector,
name = "DefaultParameterInjector",
description = "Transform calls with default arguments into calls to stubs",
prerequisite = setOf(defaultArgumentStubPhase, callableReferencePhase, inlineCallableReferenceToLambdaPhase)
prerequisite = setOf(callableReferencePhase, inlineCallableReferenceToLambdaPhase)
)
private val interfacePhase = makeIrFilePhase(
@@ -171,6 +191,13 @@ private val innerClassesPhase = makeIrFilePhase(
prerequisite = setOf(localDeclarationsPhase)
)
private val innerClassesMemberBodyPhase = makeIrFilePhase(
::InnerClassesMemberBodyLowering,
name = "InnerClassesMemberBody",
description = "Replace `this` with 'outer this' field references",
prerequisite = setOf(innerClassesPhase)
)
private val staticInitializersPhase = makeIrFilePhase(
::StaticInitializersLowering,
name = "StaticInitializers",
@@ -178,14 +205,21 @@ private val staticInitializersPhase = makeIrFilePhase(
)
private val initializersPhase = makeIrFilePhase<JvmBackendContext>(
{ context ->
object : InitializersLowering(context) {
override fun shouldEraseFieldInitializer(irField: IrField): Boolean =
irField.constantValue(context) == null
}
},
::InitializersLowering,
name = "Initializers",
description = "Merge init blocks and field initializers into constructors",
// Depends on local class extraction, because otherwise local classes in initializers will be copied into each constructor.
prerequisite = setOf(jvmLocalClassExtractionPhase)
)
private val initializersCleanupPhase = makeIrFilePhase<JvmBackendContext>(
{ context ->
InitializersCleanupLowering(context) {
it.constantValue(context) == null && (!it.isStatic || it.correspondingPropertySymbol?.owner?.isConst != true)
}
},
name = "InitializersCleanup",
description = "Remove non-static anonymous initializers and non-constant non-static field init expressions",
stickyPostconditions = setOf(fun(irFile: IrFile) {
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
@@ -197,8 +231,7 @@ private val initializersPhase = makeIrFilePhase<JvmBackendContext>(
}
})
}),
// Depends on local class extraction, because otherwise local classes in initializers will be copied into each constructor.
prerequisite = setOf(jvmLocalClassExtractionPhase)
prerequisite = setOf(initializersPhase)
)
private val returnableBlocksPhase = makeIrFilePhase(
@@ -235,7 +268,9 @@ private val jvmFilePhases =
arrayConstructorPhase then
checkNotNullPhase then
lateinitPhase then
lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then
moveOrCopyCompanionObjectFieldsPhase then
inlineCallableReferenceToLambdaPhase then
@@ -274,6 +309,7 @@ private val jvmFilePhases =
defaultArgumentStubPhase then
defaultArgumentInjectorPhase then
defaultArgumentCleanerPhase then
interfacePhase then
inheritedDefaultMethodsOnClassesPhase then
@@ -285,6 +321,7 @@ private val jvmFilePhases =
addContinuationPhase then
innerClassesPhase then
innerClassesMemberBodyPhase then
innerClassConstructorCallsPhase then
makePatchParentsPhase(2) then
@@ -293,6 +330,7 @@ private val jvmFilePhases =
objectClassPhase then
staticInitializersPhase then
initializersPhase then
initializersCleanupPhase then
collectionStubMethodLowering then
functionNVarargBridgePhase then
bridgePhase then

View File

@@ -39,6 +39,7 @@ class JvmDeclarationFactory(
private val interfaceCompanionFieldDeclarations = HashMap<IrSymbolOwner, IrField>()
private val outerThisDeclarations = HashMap<IrClass, IrField>()
private val innerClassConstructors = HashMap<IrConstructor, IrConstructor>()
private val originalInnerClassPrimaryConstructorByClass = HashMap<IrClass, IrConstructor>()
private val staticBackingFields = HashMap<IrProperty, IrField>()
private val defaultImplsMethods = HashMap<IrSimpleFunction, IrSimpleFunction>()
@@ -74,13 +75,24 @@ class JvmDeclarationFactory(
}
override fun getInnerClassConstructorWithOuterThisParameter(innerClassConstructor: IrConstructor): IrConstructor {
assert((innerClassConstructor.parent as IrClass).isInner) { "Class is not inner: ${(innerClassConstructor.parent as IrClass).dump()}" }
val innerClass = innerClassConstructor.parent as IrClass
assert(innerClass.isInner) { "Class is not inner: ${(innerClassConstructor.parent as IrClass).dump()}" }
return innerClassConstructors.getOrPut(innerClassConstructor) {
createInnerClassConstructorWithOuterThisParameter(innerClassConstructor)
}.also {
if (innerClassConstructor.isPrimary) {
originalInnerClassPrimaryConstructorByClass[innerClass] = innerClassConstructor
}
}
}
override fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor? {
assert(innerClass.isInner) { "Class is not inner: $innerClass" }
return originalInnerClassPrimaryConstructorByClass[innerClass]
}
private fun createInnerClassConstructorWithOuterThisParameter(oldConstructor: IrConstructor): IrConstructor {
val newDescriptor = WrappedClassConstructorDescriptor(oldConstructor.descriptor.annotations)
return IrConstructorImpl(
@@ -97,7 +109,7 @@ class JvmDeclarationFactory(
isExpect = oldConstructor.isExpect
).apply {
newDescriptor.bind(this)
annotations.addAll(oldConstructor.annotations.map { it.deepCopyWithSymbols(this) })
annotations = oldConstructor.annotations.map { it.deepCopyWithSymbols(this) }
parent = oldConstructor.parent
returnType = oldConstructor.returnType
copyTypeParametersFrom(oldConstructor)
@@ -117,9 +129,7 @@ class JvmDeclarationFactory(
outerThisDescriptor.bind(it)
it.parent = this
}
valueParameters.add(outerThisValueParameter)
oldConstructor.valueParameters.mapTo(valueParameters) { it.copyTo(this, index = it.index + 1) }
valueParameters = listOf(outerThisValueParameter) + oldConstructor.valueParameters.map { it.copyTo(this, index = it.index + 1) }
metadata = oldConstructor.metadata
}
}
@@ -267,9 +277,9 @@ class JvmDeclarationFactory(
).apply {
descriptor.bind(this)
parent = irClass
overriddenSymbols.addAll(fakeOverride.overriddenSymbols)
overriddenSymbols = fakeOverride.overriddenSymbols
copyParameterDeclarationsFrom(fakeOverride)
annotations.addAll(fakeOverride.annotations)
annotations = fakeOverride.annotations
fakeOverride.correspondingPropertySymbol?.owner?.let { fakeOverrideProperty ->
// NB: property is only generated for the sake of the type mapper.
// If both setter and getter are present, original property will be duplicated.

View File

@@ -58,7 +58,7 @@ class JvmSharedVariablesManager(
}.apply {
parent = jvmInternalPackage
jvmInternalPackage.addChild(this)
superTypes.add(irBuiltIns.anyType)
superTypes += irBuiltIns.anyType
}
private abstract class RefProvider {
@@ -96,7 +96,7 @@ class JvmSharedVariablesManager(
}.apply {
parent = refNamespaceClass
refNamespaceClass.addMember(this)
superTypes.add(irBuiltIns.anyType)
superTypes += irBuiltIns.anyType
thisReceiver = buildValueParameter {
type = IrSimpleTypeImpl(symbol, hasQuestionMark = false, arguments = emptyList(), annotations = emptyList())
name = Name.identifier("$this")
@@ -118,7 +118,7 @@ class JvmSharedVariablesManager(
name = Name.identifier("ObjectRef")
}.apply {
val irClass = this
typeParameters.add(
typeParameters +=
IrTypeParameterImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
SHARED_VARIABLE_ORIGIN,
@@ -132,10 +132,9 @@ class JvmSharedVariablesManager(
parent = irClass
superTypes.add(irBuiltIns.anyNType)
}
)
parent = refNamespaceClass
refNamespaceClass.addMember(this)
superTypes.add(irBuiltIns.anyType)
superTypes += irBuiltIns.anyType
thisReceiver = buildValueParameter {
type = IrSimpleTypeImpl(
symbol,

View File

@@ -138,7 +138,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
copyAttributes(info.reference)
copyTypeParametersFrom(info.function)
val functionNClass = context.ir.symbols.getJvmFunctionClass(info.arity + 1)
superTypes.add(
superTypes +=
IrSimpleTypeImpl(
functionNClass,
hasQuestionMark = false,
@@ -147,7 +147,6 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
.map { makeTypeProjection(it, Variance.INVARIANT) },
annotations = emptyList()
)
)
addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType)
@@ -217,7 +216,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
Modality.FINAL,
origin = JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE
).apply {
invokeSuspend.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
valueParameters += invokeSuspend.valueParameters.map { it.copyTo(this) }
}.also { it.copySuspendLambdaBodyFrom(irFunction, receiverField, fields) }
}
@@ -268,8 +267,8 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
modality: Modality = Modality.FINAL
): IrSimpleFunction =
addFunction(function.name.asString(), function.returnType, modality).apply {
overriddenSymbols.add(function.symbol)
function.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
overriddenSymbols += function.symbol
valueParameters += function.valueParameters.map { it.copyTo(this) }
}
// Invoke function in lambdas is responsible for
@@ -394,7 +393,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
visibility = newVisibility
}.also { irClass ->
irClass.createImplicitParameterDeclarationWithWrappedDescriptor()
irClass.superTypes.add(defaultType)
irClass.superTypes += defaultType
irClass.parent = parent
}
@@ -622,7 +621,7 @@ private class AddContinuationLowering(private val context: JvmBackendContext) :
copyTypeParameters(view.typeParameters)
dispatchReceiverParameter = view.dispatchReceiverParameter?.copyTo(this)
extensionReceiverParameter = view.extensionReceiverParameter?.copyTo(this)
view.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
valueParameters += view.valueParameters.map { it.copyTo(this) }
body = view.copyBodyTo(this)
copyAttributes(view)
}
@@ -758,15 +757,15 @@ private fun IrFunction.suspendFunctionStub(context: JvmBackendContext): IrFuncti
}.also { function ->
function.parent = parent
function.annotations.addAll(annotations)
function.annotations += annotations
function.metadata = metadata
function.copyAttributes(this)
function.copyTypeParametersFrom(this)
if (origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE) {
function.overriddenSymbols
.addAll(overriddenSymbols.map { it.owner.suspendFunctionViewOrStub(context).symbol as IrSimpleFunctionSymbol })
function.overriddenSymbols +=
overriddenSymbols.map { it.owner.suspendFunctionViewOrStub(context).symbol as IrSimpleFunctionSymbol }
}
// Copy the value parameters and insert the continuation parameter. The continuation parameter

View File

@@ -137,11 +137,10 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC
irClass.hasAnnotation(FqName("java.lang.annotation.Documented"))
) return
irClass.annotations.add(
irClass.annotations +=
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, documentedConstructor.returnType, documentedConstructor.symbol, 0
)
)
}
private val annotationRetentionMap = mapOf(
@@ -158,7 +157,7 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC
val kotlinRetentionPolicy = kotlinRetentionPolicyName?.let { KotlinRetention.valueOf(it) }
val javaRetentionPolicy = kotlinRetentionPolicy?.let { annotationRetentionMap[it] } ?: rpRuntime
irClass.annotations.add(
irClass.annotations +=
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, retentionConstructor.returnType, retentionConstructor.symbol, 0
).apply {
@@ -169,7 +168,6 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC
)
)
}
)
}
private val jvm6TargetMap = mutableMapOf(
@@ -221,13 +219,12 @@ private class AdditionalClassAnnotationLowering(private val context: JvmBackendC
)
}
irClass.annotations.add(
irClass.annotations +=
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0
).apply {
putValueArgument(0, vararg)
}
)
// TODO
}
}

View File

@@ -205,8 +205,8 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
// For lambda classes, we move override from the `invoke` function to its bridge. This will allow us to avoid boxing
// the return type of `invoke` in codegen, in case lambda's return type is primitive.
if (method.name == OperatorNameConventions.INVOKE && irClass.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL) {
target.overriddenSymbols.remove(method.symbol)
bridge.overriddenSymbols.add(method.symbol)
target.overriddenSymbols = target.overriddenSymbols.filter { it != method.symbol }
bridge.overriddenSymbols += method.symbol
}
signaturesToSkip.add(signature)
@@ -227,7 +227,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
parent = this@copyRenamingTo.parent
dispatchReceiverParameter = this@copyRenamingTo.dispatchReceiverParameter?.copyTo(this)
extensionReceiverParameter = this@copyRenamingTo.extensionReceiverParameter?.copyTo(this)
valueParameters.addAll(this@copyRenamingTo.valueParameters.map { it.copyTo(this) })
valueParameters = this@copyRenamingTo.valueParameters.map { it.copyTo(this) }
}
}
@@ -267,8 +267,8 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
dispatchReceiverParameter = irClass.thisReceiver?.copyTo(this, type = irClass.defaultType)
extensionReceiverParameter = signatureFunction.extensionReceiverParameter
?.copyWithTypeErasure(this)
signatureFunction.valueParameters.mapIndexed { i, param ->
valueParameters.add(i, param.copyWithTypeErasure(this))
valueParameters = signatureFunction.valueParameters.map { param ->
param.copyWithTypeErasure(this)
}
}
}
@@ -304,12 +304,13 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
body = irBlockBody {
// Change the parameter types to be Any? so that null checks are not generated. The checks
// we insert here make them superfluous.
val newValueParameters = ArrayList(valueParameters)
argumentsToCheck.forEach {
val parameterType = it.type
if (!parameterType.isNullable()) {
val newParameter = it.copyTo(this@rewriteSpecialMethodBody, type = context.irBuiltIns.anyNType)
variableMap.put(valueParameters[it.index], newParameter)
valueParameters[it.index] = newParameter
newValueParameters[it.index] = newParameter
addParameterTypeCheck(
newParameter,
parameterType,
@@ -318,6 +319,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
)
}
}
valueParameters = newValueParameters
// After the checks, insert the orignal method body.
if (body is IrExpressionBody) {
+irReturn((body as IrExpressionBody).expression)
@@ -330,11 +332,13 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
} else {
// If the signature of this method will be changed in the output to take a boxed argument instead of a primitive,
// rewrite the argument so that code will be generated for a boxed argument and not a primitive.
for ((i, p) in valueParameters.withIndex()) {
valueParameters = valueParameters.mapIndexed { i, p ->
if (AsmUtil.isPrimitive(context.typeMapper.mapType(p.type)) && ourSignature.argumentTypes[i].sort == Type.OBJECT) {
val newParameter = p.copyTo(this, type = p.type.makeNullable())
variableMap[p] = newParameter
valueParameters[i] = newParameter
newParameter
} else {
p
}
}
}
@@ -408,9 +412,7 @@ private class BridgeLowering(val context: JvmBackendContext) : ClassLoweringPass
copyTypeParametersFrom(this@orphanedCopy)
this@orphanedCopy.dispatchReceiverParameter?.let { dispatchReceiverParameter = it.copyTo(this) }
this@orphanedCopy.extensionReceiverParameter?.let { extensionReceiverParameter = it.copyTo(this) }
this@orphanedCopy.valueParameters.forEachIndexed { index, param ->
valueParameters.add(index, param.copyTo(this))
}
valueParameters = this@orphanedCopy.valueParameters.map { it.copyTo(this) }
/* Do NOT copy overriddenSymbols */
}
}

View File

@@ -57,7 +57,7 @@ private class CollectionStubMethodLowering(val context: JvmBackendContext) : Cla
if (existingMethod != null) {
// In the case that we find a defined method that matches the stub signature, we add the overridden symbols to that
// defined method, so that bridge lowering can still generate correct bridge for that method
existingMethod.overriddenSymbols.addAll(member.overriddenSymbols)
existingMethod.overriddenSymbols += member.overriddenSymbols
} else {
irClass.declarations.add(member)
}
@@ -78,13 +78,11 @@ private class CollectionStubMethodLowering(val context: JvmBackendContext) : Cla
}.apply {
// Replace Function metadata with the data from class
// Add the abstract function symbol to stub function for bridge lowering
overriddenSymbols.add(function.symbol)
overriddenSymbols = listOf(function.symbol)
parent = irClass
dispatchReceiverParameter = function.dispatchReceiverParameter?.copyWithSubstitution(this, substitutionMap)
extensionReceiverParameter = function.extensionReceiverParameter?.copyWithSubstitution(this, substitutionMap)
for (parameter in function.valueParameters) {
valueParameters.add(parameter.copyWithSubstitution(this, substitutionMap))
}
valueParameters = function.valueParameters.map { it.copyWithSubstitution(this, substitutionMap) }
// Function body consist only of throwing UnsupportedOperationException statement
body = context.createIrBuilder(function.symbol).irBlockBody {
+irThrow(

View File

@@ -81,7 +81,7 @@ private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringP
private fun buildEnumEntryField(enumEntry: IrEnumEntry): IrField =
context.declarationFactory.getFieldForEnumEntry(enumEntry).apply {
initializer = IrExpressionBodyImpl(enumEntry.initializerExpression!!.patchDeclarationParents(this))
initializer = IrExpressionBodyImpl(enumEntry.initializerExpression!!.expression.patchDeclarationParents(this))
annotations += enumEntry.annotations
}
@@ -119,7 +119,7 @@ private class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringP
addValueParameter(
"\$enum\$ordinal", context.irBuiltIns.intType, JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
)
declaration.valueParameters.mapTo(valueParameters) { param ->
valueParameters += declaration.valueParameters.map { param ->
param.copyTo(this, index = param.index + 2).also { newParam ->
loweredEnumConstructorParameters[param.symbol] = newParam
}

View File

@@ -111,7 +111,7 @@ private class FileClassLowering(val context: JvmBackendContext) : FileLoweringPa
isFun = false
).apply {
descriptor.bind(this)
superTypes.add(context.irBuiltIns.anyType)
superTypes += context.irBuiltIns.anyType
parent = irFile
declarations.addAll(fileClassMembers)
createImplicitParameterDeclarationWithWrappedDescriptor()

View File

@@ -81,7 +81,7 @@ private class FunctionNVarargBridgeLowering(val context: JvmBackendContext) :
// Fix super class
val superType = bigArityFunctionSuperTypes.single()
declaration.superTypes.remove(superType)
declaration.superTypes -= superType
declaration.superTypes += context.ir.symbols.functionN.typeWith(
(superType.arguments.last() as IrTypeProjection).type
)
@@ -90,7 +90,7 @@ private class FunctionNVarargBridgeLowering(val context: JvmBackendContext) :
val invokeFunction = declaration.functions.single {
it.name.asString() == "invoke" && it.valueParameters.size == superType.arguments.size - if (it.isSuspend) 0 else 1
}
invokeFunction.overriddenSymbols.clear()
invokeFunction.overriddenSymbols = emptyList()
declaration.addBridge(invokeFunction, functionNInvokeFun.owner)
return declaration

View File

@@ -123,7 +123,7 @@ private fun generateMultifileFacades(
}
if (shouldGeneratePartHierarchy) {
val superClass = modifyMultifilePartsForHierarchy(context, partClasses)
superTypes.add(superClass.typeWith())
superTypes += superClass.typeWith()
addConstructor {
visibility = Visibilities.PRIVATE
@@ -165,8 +165,7 @@ private fun modifyMultifilePartsForHierarchy(context: JvmBackendContext, unsorte
klass.modality = Modality.OPEN
klass.visibility = JavaVisibilities.PACKAGE_VISIBILITY
klass.superTypes.clear()
klass.superTypes.add(superClass.typeWith())
klass.superTypes = listOf(superClass.typeWith())
klass.addConstructor {
isPrimary = true
@@ -211,8 +210,7 @@ private fun IrSimpleFunction.createMultifileDelegateIfNeeded(
if (shouldGeneratePartHierarchy) {
function.body = null
function.origin = IrDeclarationOrigin.FAKE_OVERRIDE
function.overriddenSymbols.clear()
function.overriddenSymbols.add(symbol)
function.overriddenSymbols = listOf(symbol)
} else {
function.body = context.createIrBuilder(function.symbol).irBlockBody {
+irReturn(irCall(this@createMultifileDelegateIfNeeded).also { call ->

View File

@@ -70,7 +70,7 @@ private class InheritedDefaultMethodsOnClassesLowering(val context: JvmBackendCo
// Here we use the same logic as the delegation itself (`getTargetForRedirection`) to determine
// if the overriden symbol has been, or will be, replaced and patch it accordingly.
override fun visitSimpleFunction(declaration: IrSimpleFunction) {
declaration.overriddenSymbols.replaceAll { symbol ->
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
if (symbol.owner.findInterfaceImplementation() != null)
context.declarationFactory.getDefaultImplsRedirection(symbol.owner).symbol
else symbol

View File

@@ -53,7 +53,7 @@ private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : Cl
visibility = primaryConstructor.visibility
}.apply {
val irBuilder = context.createIrBuilder(this.symbol, startOffset, endOffset)
primaryConstructor.annotations.mapTo(annotations) { it.deepCopyWithSymbols(this) }
annotations += primaryConstructor.annotations.map { it.deepCopyWithSymbols(this) }
body = irBuilder.irBlockBody {
+irDelegatingConstructorCall(primaryConstructor).apply {
passTypeArgumentsFrom(irClass)

View File

@@ -165,11 +165,11 @@ private class JvmOverloadsAnnotationLowering(val context: JvmBackendContext) : C
}
res.parent = oldFunction.parent
res.annotations.addAll(oldFunction.annotations.map { it.deepCopyWithSymbols(res) })
res.annotations += oldFunction.annotations.map { it.deepCopyWithSymbols(res) }
res.copyTypeParametersFrom(oldFunction)
res.dispatchReceiverParameter = oldFunction.dispatchReceiverParameter?.copyTo(res)
res.extensionReceiverParameter = oldFunction.extensionReceiverParameter?.copyTo(res)
res.valueParameters.addAll(res.generateNewValueParameters(oldFunction, numDefaultParametersToExpect))
res.valueParameters += res.generateNewValueParameters(oldFunction, numDefaultParametersToExpect)
return res
}

View File

@@ -83,7 +83,7 @@ class JvmPropertiesLowering(private val context: JvmBackendContext) : IrElementT
body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
parent = declaration.parent
annotations.addAll(declaration.annotations)
annotations = declaration.annotations
metadata = declaration.metadata
}
}

View File

@@ -120,9 +120,9 @@ private class CompanionObjectJvmStaticLowering(val context: JvmBackendContext) :
parent = irClass
copyTypeParametersFrom(target)
target.extensionReceiverParameter?.let { extensionReceiverParameter = it.copyTo(this) }
target.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
valueParameters = target.valueParameters.map { it.copyTo(this) }
target.annotations.mapTo(annotations) { it.deepCopyWithSymbols() }
annotations = target.annotations.map { it.deepCopyWithSymbols() }
body = createProxyBody(target, this, companion)
}
@@ -241,7 +241,7 @@ private class MakeCallsStatic(
newDescriptor.bind(it)
it.parent = parent
it.correspondingPropertySymbol = correspondingPropertySymbol
it.annotations.addAll(annotations)
it.annotations += annotations
it.copyParameterDeclarationsFrom(this)
it.dispatchReceiverParameter = null
}

View File

@@ -123,10 +123,9 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
modality = Modality.OPEN
origin = JvmLoweredDeclarationOrigin.GENERATED_MEMBER_IN_CALLABLE_REFERENCE
}.apply {
overriddenSymbols.add(method.symbol)
overriddenSymbols += method.symbol
dispatchReceiverParameter = thisReceiver!!.copyTo(this)
for (parameter in method.valueParameters)
valueParameters.add(parameter.copyTo(this))
valueParameters = method.valueParameters.map { it.copyTo(this) }
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
irExprBody(buildBody(listOf(dispatchReceiverParameter!!) + valueParameters))
}
@@ -139,10 +138,9 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
visibility = method.visibility
origin = IrDeclarationOrigin.FAKE_OVERRIDE
}.apply {
overriddenSymbols.add(method.symbol)
overriddenSymbols += method.symbol
dispatchReceiverParameter = thisReceiver!!.copyTo(this)
for (parameter in method.valueParameters)
valueParameters.add(parameter.copyTo(this))
valueParameters = method.valueParameters.map { it.copyTo(this) }
}
private class PropertyReferenceKind(

View File

@@ -39,7 +39,7 @@ private class RenameAnonymousParametersLowering(val context: JvmBackendContext)
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
override fun visitValueParameter(declaration: IrValueParameter) =
override fun visitValueParameterNew(declaration: IrValueParameter) =
declaration.computeNewParameterName()?.let { name ->
val descriptor = if (declaration.descriptor is ReceiverParameterDescriptor) {
WrappedReceiverParameterDescriptor(declaration.descriptor.annotations)
@@ -63,7 +63,7 @@ private class RenameAnonymousParametersLowering(val context: JvmBackendContext)
annotations += declaration.annotations
oldParameterToNew[declaration] = this
}
} ?: super.visitValueParameter(declaration)
} ?: super.visitValueParameterNew(declaration)
override fun visitGetValue(expression: IrGetValue): IrExpression {
val oldParameter = expression.symbol.owner

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.lower.InitializersLoweringBase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
@@ -19,7 +20,7 @@ import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.Name
class StaticInitializersLowering(override val context: JvmBackendContext) : InitializersLoweringBase(context) {
class StaticInitializersLowering(override val context: JvmBackendContext) : InitializersLoweringBase(context), ClassLoweringPass {
override fun lower(irClass: IrClass) {
val staticInitializerStatements = extractInitializers(irClass) {
// JVM implementations are required to generate initializers for all static fields with ConstantValue,

View File

@@ -156,8 +156,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
accessor.metadata = declaration.metadata
declaration.safeAs<IrConstructorImpl>()?.metadata = null
accessor.annotations += declaration.annotations
declaration.annotations.clear()
declaration.valueParameters.forEach { it.annotations.clear() }
declaration.annotations = emptyList()
declaration.valueParameters.forEach { it.annotations = emptyList() }
}
}
}

View File

@@ -95,7 +95,7 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
irFunction.parent = irClass
typeParameter.parent = irFunction
irFunction.typeParameters.add(typeParameter)
irFunction.typeParameters += typeParameter
val dispatchReceiverParameterDescriptor = WrappedValueParameterDescriptor()
irFunction.dispatchReceiverParameter = IrValueParameterImpl(
@@ -112,7 +112,7 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
parent = irFunction
}
val valueParameterDescriptor = WrappedValueParameterDescriptor()
irFunction.valueParameters.add(
irFunction.valueParameters +=
IrValueParameterImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
JvmLoweredDeclarationOrigin.TO_ARRAY,
@@ -127,7 +127,6 @@ private class ToArrayLowering(private val context: JvmBackendContext) : ClassLow
valueParameterDescriptor.bind(this)
parent = irFunction
}
)
irFunction.body = context.createIrBuilder(irFunction.symbol).irBlockBody {
+irReturn(

View File

@@ -50,7 +50,7 @@ class TypeAliasAnnotationMethodsLowering(val context: CommonBackendContext) :
origin = JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_TYPEALIAS_ANNOTATIONS
}.apply {
body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
annotations.addAll(alias.annotations)
annotations += alias.annotations
metadata = alias.metadata
}
}

View File

@@ -146,8 +146,9 @@ class MemoizedInlineClassReplacements {
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
val replacement = buildReplacement(function) {
metadata = function.metadata
overriddenSymbols.addAll(overrides)
overriddenSymbols += overrides
val newValueParameters = ArrayList<IrValueParameter>()
for ((index, parameter) in function.explicitParameters.withIndex()) {
val name = if (parameter == function.extensionReceiverParameter) Name.identifier("\$receiver") else parameter.name
val newParameter: IrValueParameter
@@ -156,13 +157,14 @@ class MemoizedInlineClassReplacements {
dispatchReceiverParameter = newParameter
} else {
newParameter = parameter.copyTo(this, index = index - 1, name = name, defaultValue = null)
valueParameters.add(newParameter)
newValueParameters += newParameter
}
// Assuming that constructors and non-override functions are always replaced with the unboxed
// equivalent, deep-copying the value here is unnecessary. See `JvmInlineClassLowering`.
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
parameterMap[parameter.symbol] = newParameter
}
valueParameters = newValueParameters
}
return IrReplacementFunction(replacement, parameterMap)
}
@@ -171,12 +173,13 @@ class MemoizedInlineClassReplacements {
val parameterMap = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
val replacement = buildReplacement(function) {
// Generate metadata for the replacement function instead of the original.
if (function is IrFunctionBase) {
if (function is IrFunctionBase<*>) {
metadata = function.metadata
function.metadata = null
}
for ((index, parameter) in function.explicitParameters.withIndex()) {
valueParameters += function.explicitParameters.mapIndexed { index, parameter ->
val name = when (parameter) {
function.dispatchReceiverParameter -> Name.identifier("arg$index")
function.extensionReceiverParameter -> Name.identifier("\$this\$${function.name}")
@@ -188,10 +191,12 @@ class MemoizedInlineClassReplacements {
else -> parameter.origin
}
val newParameter = parameter.copyTo(this, index = index, name = name, defaultValue = null, origin = parameterOrigin)
valueParameters.add(newParameter)
valueParameters += newParameter
// See comment next to a similar line above.
newParameter.defaultValue = parameter.defaultValue?.patchDeclarationParents(this)
parameterMap[parameter.symbol] = newParameter
newParameter
}
}
return IrReplacementFunction(replacement, parameterMap)

View File

@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.wasm
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.js.JsDeclarationFactory
@@ -14,14 +13,16 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsMapping
import org.jetbrains.kotlin.ir.backend.js.JsSharedVariablesManager
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
@@ -34,12 +35,13 @@ class WasmBackendContext(
irModuleFragment: IrModuleFragment,
val additionalExportedDeclarations: Set<FqName>,
override val configuration: CompilerConfiguration
) : CommonBackendContext {
) : JsCommonBackendContext {
override val builtIns = module.builtIns
override var inVerbosePhase: Boolean = false
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 {
@@ -49,7 +51,9 @@ class WasmBackendContext(
)
}
override val declarationFactory = JsDeclarationFactory()
override val mapping = JsMapping()
override val declarationFactory = JsDeclarationFactory(mapping)
val objectToGetInstanceFunction = mutableMapOf<IrClassSymbol, IrSimpleFunction>()
override val internalPackageFqn = FqName("kotlin.wasm")

View File

@@ -71,12 +71,25 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
description = "Remove expect declaration from module fragment"
)
private val lateinitLoweringPhase = makeWasmModulePhase(
::LateinitLowering,
name = "LateinitLowering",
private val lateinitNullableFieldsPhase = makeWasmModulePhase(
::NullableFieldsForLateinitCreationLowering,
name = "LateinitNullableFields",
description = "Create nullable fields for lateinit properties"
)
private val lateinitDeclarationLoweringPhase = makeWasmModulePhase(
::NullableFieldsDeclarationLowering,
name = "LateinitDeclarations",
description = "Reference nullable fields from properties and getters + insert checks"
)
private val lateinitUsageLoweringPhase = makeWasmModulePhase(
::LateinitUsageLowering,
name = "LateinitUsage",
description = "Insert checks for lateinit field references"
)
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
{ ProvisionalFunctionExpressionLowering() },
@@ -152,6 +165,13 @@ private val innerClassesLoweringPhase = makeWasmModulePhase(
description = "Capture outer this reference to inner class"
)
private val innerClassesMemberBodyLoweringPhase = makeWasmModulePhase(
::InnerClassesMemberBodyLowering,
name = "InnerClassesMemberBody",
description = "Replace `this` with 'outer this' field references",
prerequisite = setOf(innerClassesLoweringPhase)
)
private val innerClassConstructorCallsLoweringPhase = makeWasmModulePhase(
::InnerClassConstructorCallsLowering,
name = "InnerClassConstructorCallsLowering",
@@ -164,6 +184,13 @@ private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
description = "Generate synthetic stubs for functions with default parameter values"
)
private val defaultArgumentPatchOverridesPhase = makeWasmModulePhase(
::DefaultParameterPatchOverridenSymbolsLowering,
name = "DefaultArgumentsPatchOverrides",
description = "Patch overrides for fake override dispatch functions",
prerequisite = setOf(defaultArgumentStubGeneratorPhase)
)
private val defaultParameterInjectorPhase = makeWasmModulePhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true) },
name = "DefaultParameterInjector",
@@ -201,6 +228,13 @@ private val primaryConstructorLoweringPhase = makeWasmModulePhase(
description = "Creates primary constructor if it doesn't exist"
)
private val delegateToPrimaryConstructorLoweringPhase = makeWasmModulePhase(
::DelegateToSyntheticPrimaryConstructor,
name = "DelegateToSyntheticPrimaryConstructor",
description = "Delegates to synthetic primary constructor",
prerequisite = setOf(primaryConstructorLoweringPhase)
)
private val initializersLoweringPhase = makeWasmModulePhase(
::InitializersLowering,
name = "InitializersLowering",
@@ -208,6 +242,13 @@ private val initializersLoweringPhase = makeWasmModulePhase(
prerequisite = setOf(primaryConstructorLoweringPhase)
)
private val initializersCleanupLoweringPhase = makeWasmModulePhase(
::InitializersCleanupLowering,
name = "InitializersCleanupLowering",
description = "Remove non-static anonymous initializers and field init expressions",
prerequisite = setOf(initializersLoweringPhase)
)
private val excludeDeclarationsFromCodegenPhase = makeCustomWasmModulePhase(
{ context, module ->
excludeDeclarationsFromCodegen(context, module)
@@ -229,15 +270,16 @@ private val bridgesConstructionPhase = makeWasmModulePhase(
description = "Generate bridges"
)
private val inlineClassLoweringPhase = makeCustomWasmModulePhase(
{ context, module ->
InlineClassLowering(context).run {
inlineClassDeclarationLowering.runOnFilesPostfix(module)
inlineClassUsageLowering.lower(module)
}
},
name = "InlineClassLowering",
description = "Handle inline classes"
private val inlineClassDeclarationLoweringPhase = makeWasmModulePhase(
{ InlineClassLowering(it).inlineClassDeclarationLowering },
name = "InlineClassDeclarationLowering",
description = "Handle inline class declarations"
)
private val inlineClassUsageLoweringPhase = makeWasmModulePhase(
{ InlineClassLowering(it).inlineClassUsageLowering },
name = "InlineClassUsageLowering",
description = "Handle inline class usages"
)
//private val autoboxingTransformerPhase = makeJsModulePhase(
@@ -297,14 +339,14 @@ private val builtInsLoweringPhase = makeWasmModulePhase(
description = "Lower IR buildins"
)
private val objectDeclarationLoweringPhase = makeCustomWasmModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) },
private val objectDeclarationLoweringPhase = makeWasmModulePhase(
::ObjectUsageLowering,
name = "ObjectDeclarationLowering",
description = "Create lazy object instance generator functions"
)
private val objectUsageLoweringPhase = makeCustomWasmModulePhase(
{ context, module -> ObjectUsageLowering(context, context.objectToGetInstanceFunction).lower(module) },
private val objectUsageLoweringPhase = makeWasmModulePhase(
::ObjectUsageLowering,
name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call"
)
@@ -321,7 +363,9 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
// arrayConstructorPhase then
functionInliningPhase then
lateinitLoweringPhase then
lateinitNullableFieldsPhase then
lateinitDeclarationLoweringPhase then
lateinitUsageLoweringPhase then
tailrecLoweringPhase then
enumClassConstructorLoweringPhase then
@@ -331,10 +375,13 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
localDeclarationsLoweringPhase then
localClassExtractionPhase then
innerClassesLoweringPhase then
innerClassesMemberBodyLoweringPhase then
innerClassConstructorCallsLoweringPhase then
propertiesLoweringPhase then
primaryConstructorLoweringPhase then
delegateToPrimaryConstructorLoweringPhase then
initializersLoweringPhase then
initializersCleanupLoweringPhase then
// Common prefix ends
builtInsLoweringPhase then
@@ -357,6 +404,7 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
// callableReferenceLoweringPhase then
defaultArgumentStubGeneratorPhase then
defaultArgumentPatchOverridesPhase then
defaultParameterInjectorPhase then
defaultParameterCleanerPhase then
@@ -384,7 +432,8 @@ val wasmPhases = namedIrModulePhase<WasmBackendContext>(
// TODO: Reimplement
// classReferenceLoweringPhase then
inlineClassLoweringPhase then
inlineClassDeclarationLoweringPhase then
inlineClassUsageLoweringPhase then
// TODO: Commonize box/unbox intrinsics
// autoboxingTransformerPhase then

View File

@@ -40,8 +40,10 @@ class AnnotationGenerator(context: GeneratorContext) : IrElementVisitorVoid {
declaration.descriptor.backingField
else declaration.descriptor
annotatedDescriptor?.annotations?.mapNotNullTo(declaration.annotations) {
constantValueGenerator.generateAnnotationConstructorCall(it)
if (annotatedDescriptor != null) {
declaration.annotations += annotatedDescriptor.annotations.mapNotNull {
constantValueGenerator.generateAnnotationConstructorCall(it)
}
}
}
}

View File

@@ -87,7 +87,7 @@ class ClassGenerator(
).buildWithScope { irClass ->
declarationGenerator.generateGlobalTypeParametersDeclarations(irClass, classDescriptor.declaredTypeParameters)
classDescriptor.typeConstructor.supertypes.mapTo(irClass.superTypes) {
irClass.superTypes = classDescriptor.typeConstructor.supertypes.map {
it.toIrType()
}
@@ -462,8 +462,8 @@ class ClassGenerator(
if (!enumEntryDescriptor.isExpect) {
irEnumEntry.initializerExpression =
createBodyGenerator(irEnumEntry.symbol)
.generateEnumEntryInitializer(ktEnumEntry, enumEntryDescriptor)
IrExpressionBodyImpl(createBodyGenerator(irEnumEntry.symbol)
.generateEnumEntryInitializer(ktEnumEntry, enumEntryDescriptor))
}
if (ktEnumEntry.hasMemberDeclarations()) {

View File

@@ -126,7 +126,7 @@ class DeclarationGenerator(override val context: GeneratorContext) : Generator {
from: List<TypeParameterDescriptor>,
declareTypeParameter: (Int, Int, TypeParameterDescriptor) -> IrTypeParameter
) {
from.mapTo(irTypeParametersOwner.typeParameters) { typeParameterDescriptor ->
irTypeParametersOwner.typeParameters += from.map { typeParameterDescriptor ->
val ktTypeParameterDeclaration = DescriptorToSourceUtils.getSourceFromDescriptor(typeParameterDescriptor)
val startOffset = ktTypeParameterDeclaration.startOffsetOrUndefined
val endOffset = ktTypeParameterDeclaration.endOffsetOrUndefined

Some files were not shown because too many files have changed in this diff Show More