[JS IR BE] Reorder phases in order to extract a common prefix

This commit is contained in:
Anton Bannykh
2019-02-20 20:19:05 +03:00
parent 017cd3a0f6
commit 9a56b0e6d6
13 changed files with 345 additions and 278 deletions
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -42,25 +43,26 @@ class PropertiesLowering(
override fun visitClass(declaration: IrClass): IrStatement { override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformChildrenVoid(this) declaration.transformChildrenVoid(this)
declaration.transformDeclarationsFlat { lowerProperty(it, declaration.kind) } declaration.transformDeclarationsFlat { lowerProperty(it, declaration.kind) }
declaration.declarations.removeAll { it is IrProperty }
return declaration return declaration
} }
private fun lowerProperty(declaration: IrDeclaration, kind: ClassKind): List<IrDeclaration>? = private fun lowerProperty(declaration: IrDeclaration, kind: ClassKind): List<IrDeclaration>? =
if (declaration is IrProperty) if (declaration is IrProperty)
ArrayList<IrDeclaration>(4).apply { if (declaration.isEffectivelyExternal()) listOf(declaration) else {
// JvmFields in a companion object refer to companion's owners and should not be generated within companion. ArrayList<IrDeclaration>(4).apply {
if (kind != ClassKind.ANNOTATION_CLASS && declaration.backingField?.parent == declaration.parent) { // JvmFields in a companion object refer to companion's owners and should not be generated within companion.
addIfNotNull(declaration.backingField) if (kind != ClassKind.ANNOTATION_CLASS && declaration.backingField?.parent == declaration.parent) {
} addIfNotNull(declaration.backingField)
addIfNotNull(declaration.getter) }
addIfNotNull(declaration.setter) addIfNotNull(declaration.getter)
addIfNotNull(declaration.setter)
if (declaration.annotations.isNotEmpty() && originOfSyntheticMethodForAnnotations != null if (declaration.annotations.isNotEmpty() && originOfSyntheticMethodForAnnotations != null
&& computeSyntheticMethodName != null && computeSyntheticMethodName != null
) { ) {
val methodName = computeSyntheticMethodName.invoke(declaration.name) // Workaround KT-4113 val methodName = computeSyntheticMethodName.invoke(declaration.name) // Workaround KT-4113
add(createSyntheticMethodForAnnotations(declaration, originOfSyntheticMethodForAnnotations, methodName)) add(createSyntheticMethodForAnnotations(declaration, originOfSyntheticMethodForAnnotations, methodName))
}
} }
} }
else else
@@ -18,20 +18,24 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
fun IrClassSymbol.getPropertyDeclaration(name: String) = fun IrClassSymbol.getPropertyDeclaration(name: String) =
this.owner.declarations.filterIsInstance<IrProperty>() this.owner.declarations.filterIsInstance<IrProperty>()
.atMostOne { it.descriptor.name == Name.identifier(name) } .atMostOne { it.descriptor.name == Name.identifier(name) }
fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
this.getPropertyDeclaration(name)?.getter?.symbol owner.findDeclaration<IrSimpleFunction> { it.name.asString() == name }?.symbol
fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getPropertyGetter(name: String): IrSimpleFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol this.getPropertyDeclaration(name)?.getter?.symbol ?: this.getSimpleFunction("<get-$name>")
fun IrClassSymbol.getPropertySetter(name: String): IrSimpleFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol ?: this.getSimpleFunction("<set-$name>")
fun IrClassSymbol.getPropertyField(name: String): IrFieldSymbol? = fun IrClassSymbol.getPropertyField(name: String): IrFieldSymbol? =
this.getPropertyDeclaration(name)?.backingField?.symbol this.getPropertyDeclaration(name)?.backingField?.symbol
@@ -31,9 +31,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.getPropertyDeclaration
import org.jetbrains.kotlin.ir.util.kotlinPackageFqn
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -257,10 +255,14 @@ class JsIrBackendContext(
val throwNWBESymbol = ir.symbols.ThrowNoWhenBranchMatchedException val throwNWBESymbol = ir.symbols.ThrowNoWhenBranchMatchedException
val throwUPAESymbol = ir.symbols.ThrowUninitializedPropertyAccessException val throwUPAESymbol = ir.symbols.ThrowUninitializedPropertyAccessException
val coroutineImplLabelProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("state")!! } val coroutineImplLabelPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
val coroutineImplResultSymbol by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("result")!! } val coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
val coroutineImplExceptionProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("exception")!! } val coroutineImplResultSymbolGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
val coroutineImplExceptionStateProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("exceptionState")!! } 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 primitiveClassProperties by lazy { val primitiveClassProperties by lazy {
primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>() primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
@@ -81,18 +81,6 @@ private val expectDeclarationsRemovingPhase = makeJsModulePhase(
description = "Remove expect declaration from module fragment" description = "Remove expect declaration from module fragment"
) )
private val coroutineIntrinsicLoweringPhase = makeJsModulePhase(
::CoroutineIntrinsicLowering,
name = "CoroutineIntrinsicLowering",
description = "Replace common coroutine intrinsics with platform specific ones"
)
private val arrayInlineConstructorLoweringPhase = makeJsModulePhase(
::ArrayInlineConstructorLowering,
name = "ArrayInlineConstructorLowering",
description = "Replace array constructor with platform specific factory functions"
)
private val lateinitLoweringPhase = makeJsModulePhase( private val lateinitLoweringPhase = makeJsModulePhase(
::LateinitLowering, ::LateinitLowering,
name = "LateinitLowering", name = "LateinitLowering",
@@ -107,7 +95,7 @@ private val functionInliningPhase = makeCustomJsModulePhase(
}, },
name = "FunctionInliningPhase", name = "FunctionInliningPhase",
description = "Perform function inlining", description = "Perform function inlining",
prerequisite = setOf(lateinitLoweringPhase, arrayInlineConstructorLoweringPhase, coroutineIntrinsicLoweringPhase) prerequisite = setOf(expectDeclarationsRemovingPhase)
) )
private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase( private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJsModulePhase(
@@ -136,16 +124,24 @@ private val unitMaterializationLoweringPhase = makeJsModulePhase(
prerequisite = setOf(tailrecLoweringPhase) prerequisite = setOf(tailrecLoweringPhase)
) )
private val enumClassConstructorLoweringPhase = makeJsModulePhase(
::EnumClassConstructorLowering,
name = "EnumClassConstructorLowering",
description = "Transform Enum Class into regular Class"
)
private val enumClassLoweringPhase = makeJsModulePhase( private val enumClassLoweringPhase = makeJsModulePhase(
::EnumClassLowering, ::EnumClassLowering,
name = "EnumClassLowering", name = "EnumClassLowering",
description = "Transform Enum Class into regular Class" description = "Transform Enum Class into regular Class",
prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val enumUsageLoweringPhase = makeJsModulePhase( private val enumUsageLoweringPhase = makeJsModulePhase(
::EnumUsageLowering, ::EnumUsageLowering,
name = "EnumUsageLowering", name = "EnumUsageLowering",
description = "Replace enum access with invocation of corresponding function" description = "Replace enum access with invocation of corresponding function",
prerequisite = setOf(enumClassLoweringPhase)
) )
private val sharedVariablesLoweringPhase = makeJsModulePhase( private val sharedVariablesLoweringPhase = makeJsModulePhase(
@@ -171,7 +167,7 @@ private val localDeclarationsLoweringPhase = makeJsModulePhase(
::LocalDeclarationsLowering, ::LocalDeclarationsLowering,
name = "LocalDeclarationsLowering", name = "LocalDeclarationsLowering",
description = "Move local declarations into nearest declaration container", description = "Move local declarations into nearest declaration container",
prerequisite = setOf(sharedVariablesLoweringPhase) prerequisite = setOf(sharedVariablesLoweringPhase, localDelegatedPropertiesLoweringPhase)
) )
private val innerClassesLoweringPhase = makeJsModulePhase( private val innerClassesLoweringPhase = makeJsModulePhase(
@@ -190,7 +186,7 @@ private val suspendFunctionsLoweringPhase = makeJsModulePhase(
::SuspendFunctionsLowering, ::SuspendFunctionsLowering,
name = "SuspendFunctionsLowering", name = "SuspendFunctionsLowering",
description = "Transform suspend functions into CoroutineImpl instance and build state machine", description = "Transform suspend functions into CoroutineImpl instance and build state machine",
prerequisite = setOf(unitMaterializationLoweringPhase, coroutineIntrinsicLoweringPhase) prerequisite = setOf(unitMaterializationLoweringPhase)
) )
private val privateMembersLoweringPhase = makeJsModulePhase( private val privateMembersLoweringPhase = makeJsModulePhase(
@@ -253,7 +249,7 @@ private val initializersLoweringPhase = makeCustomJsModulePhase(
{ context, module -> InitializersLowering(context, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).lower(module) }, { context, module -> InitializersLowering(context, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).lower(module) },
name = "InitializersLowering", name = "InitializersLowering",
description = "Merge init block and field initializers into [primary] constructor", description = "Merge init block and field initializers into [primary] constructor",
prerequisite = setOf(enumClassLoweringPhase) prerequisite = setOf(enumClassConstructorLoweringPhase)
) )
private val multipleCatchesLoweringPhase = makeJsModulePhase( private val multipleCatchesLoweringPhase = makeJsModulePhase(
@@ -344,24 +340,24 @@ private val callsLoweringPhase = makeJsModulePhase(
val jsPhases = namedIrModulePhase( val jsPhases = namedIrModulePhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = moveBodilessDeclarationsToSeparatePlacePhase then lower = expectDeclarationsRemovingPhase then
expectDeclarationsRemovingPhase then
coroutineIntrinsicLoweringPhase then
arrayInlineConstructorLoweringPhase then
lateinitLoweringPhase then
functionInliningPhase then functionInliningPhase then
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then lateinitLoweringPhase then
throwableSuccessorsLoweringPhase then
tailrecLoweringPhase then tailrecLoweringPhase then
unitMaterializationLoweringPhase then enumClassConstructorLoweringPhase then
enumClassLoweringPhase then
enumUsageLoweringPhase then
sharedVariablesLoweringPhase then sharedVariablesLoweringPhase then
returnableBlockLoweringPhase then
localDelegatedPropertiesLoweringPhase then localDelegatedPropertiesLoweringPhase then
localDeclarationsLoweringPhase then localDeclarationsLoweringPhase then
innerClassesLoweringPhase then innerClassesLoweringPhase then
innerClassConstructorCallsLoweringPhase then innerClassConstructorCallsLoweringPhase then
propertiesLoweringPhase then
initializersLoweringPhase then
// Common prefix ends
moveBodilessDeclarationsToSeparatePlacePhase then
enumClassLoweringPhase then
enumUsageLoweringPhase then
returnableBlockLoweringPhase then
unitMaterializationLoweringPhase then
suspendFunctionsLoweringPhase then suspendFunctionsLoweringPhase then
privateMembersLoweringPhase then privateMembersLoweringPhase then
callableReferenceLoweringPhase then callableReferenceLoweringPhase then
@@ -369,9 +365,9 @@ val jsPhases = namedIrModulePhase(
defaultParameterInjectorPhase then defaultParameterInjectorPhase then
defaultParameterCleanerPhase then defaultParameterCleanerPhase then
jsDefaultCallbackGeneratorPhase then jsDefaultCallbackGeneratorPhase then
removeInlineFunctionsWithReifiedTypeParametersLoweringPhase then
throwableSuccessorsLoweringPhase then
varargLoweringPhase then varargLoweringPhase then
propertiesLoweringPhase then
initializersLoweringPhase then
multipleCatchesLoweringPhase then multipleCatchesLoweringPhase then
bridgesConstructionPhase then bridgesConstructionPhase then
typeOperatorLoweringPhase then typeOperatorLoweringPhase then
@@ -5,40 +5,25 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
// Replace array inline constructors with stdlib function invocations // Replace array inline constructors with stdlib function invocations
// Should be performed before inliner class ArrayConstructorTransformer(
class ArrayInlineConstructorLowering(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(ArrayConstructorTransformer(context))
}
}
private class ArrayConstructorTransformer(
val context: JsIrBackendContext val context: JsIrBackendContext
) : IrElementTransformerVoid() { ) {
// Inline constructor for CharArray is implemented in runtime // Inline constructor for CharArray is implemented in runtime
private val primitiveArrayInlineToSizeConstructorMap = private val primitiveArrayInlineToSizeConstructorMap =
context.intrinsics.primitiveArrays.filter { it.value != PrimitiveType.CHAR }.keys.associate { context.intrinsics.primitiveArrays.filter { it.value != PrimitiveType.CHAR }.keys.associate {
it.inlineConstructor to it.sizeConstructor it.inlineConstructor to it.sizeConstructor
} }
override fun visitCall(expression: IrCall): IrExpression { fun transformCall(expression: IrCall): IrCall {
expression.transformChildrenVoid(this)
if (expression.symbol == context.intrinsics.array.inlineConstructor) { if (expression.symbol == context.intrinsics.array.inlineConstructor) {
return irCall(expression, context.intrinsics.jsArray) return irCall(expression, context.intrinsics.jsArray)
} else { } else {
@@ -85,15 +85,11 @@ class EnumUsageLowering(val context: JsIrBackendContext) : FileLoweringPass {
class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass { class EnumClassLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) { override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.transformDeclarationsFlat { declaration -> irDeclarationContainer.transformDeclarationsFlat { declaration ->
if (declaration is IrClass && declaration.isEnumClass) { if (declaration is IrClass && declaration.isEnumClass &&
if (declaration.descriptor.isExpect) { !declaration.descriptor.isExpect && !declaration.isEffectivelyExternal()
emptyList() ) {
} else { EnumClassTransformer(context, declaration).transform()
EnumClassTransformer(context, declaration).transform() } else null
}
} else {
listOf(declaration)
}
} }
} }
} }
@@ -104,12 +100,22 @@ private fun createEntryAccessorName(enumName: String, enumEntry: IrEnumEntry) =
private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType private fun IrEnumEntry.getType(irClass: IrClass) = (correspondingClass ?: irClass).defaultType
class EnumClassTransformer(val context: JsIrBackendContext, private val irClass: IrClass) { class EnumClassConstructorLowering(val context: JsIrBackendContext) : 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
}
}
}
class EnumClassConstructorTransformer(val context: JsIrBackendContext, private val irClass: IrClass) {
private val builder = context.createIrBuilder(irClass.symbol) private val builder = context.createIrBuilder(irClass.symbol)
private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>() private val enumEntries = irClass.declarations.filterIsInstance<IrEnumEntry>()
private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructor>() private val loweredEnumConstructors = HashMap<IrConstructorSymbol, IrConstructor>()
private val enumName = irClass.name.identifier
private val throwISESymbol = context.throwISEymbol
fun transform(): List<IrDeclaration> { fun transform(): List<IrDeclaration> {
// Make sure InstanceInitializer exists // Make sure InstanceInitializer exists
@@ -124,35 +130,16 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
// The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones. // The first step creates a new `IrConstructor` with new `IrValueParameter`s so references to old `IrValueParameter`s must be replaced with new ones.
fixReferencesToConstructorParameters() fixReferencesToConstructorParameters()
// Create instance variable for each enum entry initialized with `null`
val entryInstances = createEnumEntryInstanceVariables()
// Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s. // Lower `IrEnumConstructorCall`s inside of enum entry class constructors to corresponding `IrDelegatingConstructorCall`s.
// Add `name` and `ordinal` parameters. // Add `name` and `ordinal` parameters.
lowerEnumEntryClassConstructors(entryInstances) lowerEnumEntryClassConstructors()
// Lower `IrEnumConstructorCall`s to corresponding `IrCall`s. // 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 // Add `name` and `ordinal` constant parameters only for calls to the "enum class" constructors ("enum entry class" constructors
// already delegate these parameters) // already delegate these parameters)
lowerEnumEntryInitializerExpression() lowerEnumEntryInitializerExpression()
// Create boolean flag that indicates if entry instances were initialized. return listOf(irClass)
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
} }
private fun insertInstanceInitializer() { private fun insertInstanceInitializer() {
@@ -182,6 +169,82 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
}) })
} }
private fun lowerEnumConstructorsSignature() {
irClass.declarations.transform { declaration ->
if (declaration is IrConstructor) {
transformEnumConstructor(declaration, irClass)
} else
declaration
}
}
private fun transformEnumConstructor(enumConstructor: IrConstructor, enumClass: IrClass): IrConstructor {
val loweredConstructorDescriptor = WrappedClassConstructorDescriptor()
val loweredConstructorSymbol = IrConstructorSymbolImpl(loweredConstructorDescriptor)
return IrConstructorImpl(
enumConstructor.startOffset,
enumConstructor.endOffset,
enumConstructor.origin,
loweredConstructorSymbol,
enumConstructor.name,
enumConstructor.visibility,
enumConstructor.returnType,
enumConstructor.isInline,
enumConstructor.isExternal,
enumConstructor.isPrimary
).apply {
loweredConstructorDescriptor.bind(this)
parent = enumClass
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))
}
}
private fun lowerEnumConstructorsBody() {
irClass.declarations.filterIsInstance<IrConstructor>().forEach {
IrEnumClassConstructorTransformer(it).transformBody()
}
}
private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() {
val builder = context.createIrBuilder(constructor.symbol)
fun transformBody() {
constructor.body?.transformChildrenVoid(this)
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall) =
builder.irDelegatingConstructorCall(expression.symbol.owner).apply {
for (i in 0..1) {
putValueArgument(i, builder.irGet(constructor.valueParameters[i]))
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
var delegatingConstructor = expression.symbol.owner
val constructorWasTransformed = delegatingConstructor.symbol in loweredEnumConstructors
if (constructorWasTransformed)
delegatingConstructor = loweredEnumConstructors[delegatingConstructor.symbol]!!
return builder.irDelegatingConstructorCall(delegatingConstructor).apply {
var valueArgIdx = 0
for (i in 0..1) {
putValueArgument(valueArgIdx++, builder.irGet(constructor.valueParameters[i]))
}
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(valueArgIdx++, expression.getValueArgument(i))
}
}
}
}
private fun fixReferencesToConstructorParameters() { private fun fixReferencesToConstructorParameters() {
val fromOldToNewParameter = mutableMapOf<IrValueParameterSymbol, IrValueParameter>() val fromOldToNewParameter = mutableMapOf<IrValueParameterSymbol, IrValueParameter>()
@@ -205,6 +268,92 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
}) })
} }
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) :
IrElementTransformerVoid() {
private fun buildConstructorCall(constructor: IrConstructor) =
if (isInsideConstructor)
builder.irDelegatingConstructorCall(constructor)
else
builder.irCall(constructor)
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
var constructor = expression.symbol.owner
val constructorWasTransformed = constructor.symbol in loweredEnumConstructors
// Enum entry class constructors are not transformed
if (constructorWasTransformed)
constructor = loweredEnumConstructors[constructor.symbol]!!
return buildConstructorCall(constructor).apply {
var valueArgIdx = 0
// Enum entry class constructors already delegate name and ordinal parameters in their body
if (constructorWasTransformed) {
putValueArgument(valueArgIdx++, entry.getNameExpression())
putValueArgument(valueArgIdx++, entry.getOrdinalExpression())
}
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(valueArgIdx++, expression.getValueArgument(i))
}
}
}
}
private fun lowerEnumEntryInitializerExpression() {
for (entry in enumEntries) {
entry.initializerExpression =
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(entry, false), null)
}
}
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
}
//-------------------------------------------------------
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.throwISEymbol
fun transform(): List<IrDeclaration> {
// 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
}
private fun createEnumValueOfBody(): IrBody { private fun createEnumValueOfBody(): IrBody {
val valueOfFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF) val valueOfFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUEOF)
val nameParameter = valueOfFun.valueParameters[0] val nameParameter = valueOfFun.valueParameters[0]
@@ -249,25 +398,9 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
} }
private fun lowerEnumConstructorsSignature() {
irClass.declarations.transform { declaration ->
if (declaration is IrConstructor) {
transformEnumConstructor(declaration, irClass)
} else
declaration
}
}
private fun lowerEnumConstructorsBody() {
irClass.declarations.filterIsInstance<IrConstructor>().forEach {
IrEnumClassConstructorTransformer(it).transformBody()
}
}
private fun lowerEnumEntryClassConstructors(entryInstances: List<IrVariable>) { private fun lowerEnumEntryClassConstructors(entryInstances: List<IrVariable>) {
for ((entry, instance) in enumEntries.zip(entryInstances)) { for ((entry, instance) in enumEntries.zip(entryInstances)) {
entry.correspondingClass?.constructors?.forEach { entry.correspondingClass?.constructors?.forEach {
it.transformChildrenVoid(IrEnumEntryClassConstructorTransformer(entry, true))
// Initialize entry instance at the beginning of constructor so it can be used inside constructor body // Initialize entry instance at the beginning of constructor so it can be used inside constructor body
(it.body as? IrBlockBody)?.apply { (it.body as? IrBlockBody)?.apply {
@@ -279,19 +412,23 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
} }
} }
private fun lowerEnumEntryInitializerExpression() {
for (entry in enumEntries) {
entry.initializerExpression =
entry.initializerExpression?.transform(IrEnumEntryClassConstructorTransformer(entry, false), null)
}
}
private fun createEnumEntryInstanceVariables() = enumEntries.map { enumEntry -> private fun createEnumEntryInstanceVariables() = enumEntries.map { enumEntry ->
val type = enumEntry.getType(irClass).makeNullable(false) val type = enumEntry.getType(irClass).makeNullable(false)
val name = "${enumName}_${enumEntry.name.identifier}_instance" val name = "${enumName}_${enumEntry.name.identifier}_instance"
builder.run { val result = builder.run {
scope.createTmpVariable(irImplicitCast(irNull(), type), name) scope.createTmpVariable(irImplicitCast(irNull(), type), name)
} }
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 {
irSetVar(result.symbol, irGet(enumEntry.correspondingClass!!.thisReceiver!!))
})
}
}
result
} }
private fun replaceIrEntriesWithCorrespondingClasses() { private fun replaceIrEntriesWithCorrespondingClasses() {
@@ -346,98 +483,6 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
) )
} }
private inner class IrEnumClassConstructorTransformer(val constructor: IrConstructor) : IrElementTransformerVoid() {
val builder = context.createIrBuilder(constructor.symbol)
fun transformBody() {
constructor.body?.transformChildrenVoid(this)
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall) =
builder.irDelegatingConstructorCall(expression.symbol.owner).apply {
for (i in 0..1) {
putValueArgument(i, builder.irGet(constructor.valueParameters[i]))
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
var delegatingConstructor = expression.symbol.owner
val constructorWasTransformed = delegatingConstructor.symbol in loweredEnumConstructors
if (constructorWasTransformed)
delegatingConstructor = loweredEnumConstructors[delegatingConstructor.symbol]!!
return builder.irDelegatingConstructorCall(delegatingConstructor).apply {
var valueArgIdx = 0
for (i in 0..1) {
putValueArgument(valueArgIdx++, builder.irGet(constructor.valueParameters[i]))
}
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(valueArgIdx++, expression.getValueArgument(i))
}
}
}
}
private inner class IrEnumEntryClassConstructorTransformer(val entry: IrEnumEntry, val isInsideConstructor: Boolean) :
IrElementTransformerVoid() {
private fun buildConstructorCall(constructor: IrConstructor) =
if (isInsideConstructor)
builder.irDelegatingConstructorCall(constructor)
else
builder.irCall(constructor)
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
var constructor = expression.symbol.owner
val constructorWasTransformed = constructor.symbol in loweredEnumConstructors
// Enum entry class constructors are not transformed
if (constructorWasTransformed)
constructor = loweredEnumConstructors[constructor.symbol]!!
return buildConstructorCall(constructor).apply {
var valueArgIdx = 0
// Enum entry class constructors already delegate name and ordinal parameters in their body
if (constructorWasTransformed) {
putValueArgument(valueArgIdx++, entry.getNameExpression())
putValueArgument(valueArgIdx++, entry.getOrdinalExpression())
}
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(valueArgIdx++, expression.getValueArgument(i))
}
}
}
}
private fun transformEnumConstructor(enumConstructor: IrConstructor, enumClass: IrClass): IrConstructor {
val loweredConstructorDescriptor = WrappedClassConstructorDescriptor()
val loweredConstructorSymbol = IrConstructorSymbolImpl(loweredConstructorDescriptor)
return IrConstructorImpl(
enumConstructor.startOffset,
enumConstructor.endOffset,
enumConstructor.origin,
loweredConstructorSymbol,
enumConstructor.name,
enumConstructor.visibility,
enumConstructor.returnType,
enumConstructor.isInline,
enumConstructor.isExternal,
enumConstructor.isPrimary
).apply {
loweredConstructorDescriptor.bind(this)
parent = enumClass
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))
}
}
private fun findFunctionDescriptorForMemberWithSyntheticBodyKind(kind: IrSyntheticBodyKind): IrFunction = private fun findFunctionDescriptorForMemberWithSyntheticBodyKind(kind: IrSyntheticBodyKind): IrFunction =
irClass.declarations.asSequence().filterIsInstance<IrFunction>() irClass.declarations.asSequence().filterIsInstance<IrFunction>()
@@ -459,7 +504,4 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
) = JsIrBuilder.buildFunction(name, returnType, irClass).also { ) = JsIrBuilder.buildFunction(name, returnType, irClass).also {
it.body = context.createIrBuilder(it.symbol).irBlockBody(it, bodyBuilder) it.body = context.createIrBuilder(it.symbol).irBlockBody(it, bodyBuilder)
} }
private fun IrEnumEntry.getNameExpression() = builder.irString(this.name.identifier)
private fun IrEnumEntry.getOrdinalExpression() = builder.irInt(enumEntries.indexOf(this))
} }
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isThrowable import org.jetbrains.kotlin.ir.util.isThrowable
import org.jetbrains.kotlin.ir.util.isThrowableTypeOrSubtype import org.jetbrains.kotlin.ir.util.isThrowableTypeOrSubtype
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -147,12 +148,25 @@ class ThrowableSuccessorsLowering(val context: JsIrBackendContext) : FileLowerin
if (isDirectChildOfThrowable(declaration)) { if (isDirectChildOfThrowable(declaration)) {
val messageField = createBackingField(declaration, messagePropertyName, stringType) val messageField = createBackingField(declaration, messagePropertyName, stringType)
val causeField = createBackingField(declaration, causePropertyName, throwableType) val causeField = createBackingField(declaration, causePropertyName, throwableType)
val existedMessageAccessor = ownPropertyAccessor(declaration, messageGetter) val existedMessageAccessor = ownPropertyAccessor(declaration, messageGetter)
if (existedMessageAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) val newMessageAccessor = if (existedMessageAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) {
createPropertyAccessor(existedMessageAccessor, messageField) createPropertyAccessor(existedMessageAccessor, messageField)
} else existedMessageAccessor
val existedCauseAccessor = ownPropertyAccessor(declaration, causeGetter) val existedCauseAccessor = ownPropertyAccessor(declaration, causeGetter)
if (existedCauseAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) val newCauseAccessor = if (existedCauseAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) {
createPropertyAccessor(existedCauseAccessor, causeField) createPropertyAccessor(existedCauseAccessor, causeField)
} else existedCauseAccessor
declaration.declarations.transformFlat {
when (it) {
existedMessageAccessor -> listOf(newMessageAccessor)
existedCauseAccessor -> listOf(newCauseAccessor)
else -> null
}
}
pendingSuperUsages += DirectThrowableSuccessors(declaration, messageField, causeField) pendingSuperUsages += DirectThrowableSuccessors(declaration, messageField, causeField)
} }
@@ -181,7 +195,7 @@ class ThrowableSuccessorsLowering(val context: JsIrBackendContext) : FileLowerin
return fieldDeclaration return fieldDeclaration
} }
private fun createPropertyAccessor(fakeAccessor: IrSimpleFunction, field: IrField) { private fun createPropertyAccessor(fakeAccessor: IrSimpleFunction, field: IrField): IrSimpleFunction {
val name = fakeAccessor.name val name = fakeAccessor.name
val function = JsIrBuilder.buildFunction(name, fakeAccessor.returnType, fakeAccessor.parent).apply { val function = JsIrBuilder.buildFunction(name, fakeAccessor.returnType, fakeAccessor.parent).apply {
overriddenSymbols += fakeAccessor.overriddenSymbols overriddenSymbols += fakeAccessor.overriddenSymbols
@@ -194,7 +208,7 @@ class ThrowableSuccessorsLowering(val context: JsIrBackendContext) : FileLowerin
val returnStatement = JsIrBuilder.buildReturn(function.symbol, returnValue, nothingType) val returnStatement = JsIrBuilder.buildReturn(function.symbol, returnValue, nothingType)
function.body = JsIrBuilder.buildBlockBody(listOf(returnStatement)) function.body = JsIrBuilder.buildBlockBody(listOf(returnStatement))
fakeAccessor.correspondingProperty?.getter = function return function
} }
} }
@@ -315,7 +329,8 @@ class ThrowableSuccessorsLowering(val context: JsIrBackendContext) : FileLowerin
private fun isDirectChildOfThrowable(irClass: IrClass) = irClass.superTypes.any { it.isThrowable() } private fun isDirectChildOfThrowable(irClass: IrClass) = irClass.superTypes.any { it.isThrowable() }
private fun ownPropertyAccessor(irClass: IrClass, irBase: IrFunctionSymbol) = private fun ownPropertyAccessor(irClass: IrClass, irBase: IrFunctionSymbol) =
irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter } irClass.declarations.filterIsInstance<IrProperty>().mapNotNull { it.getter }
.single { it.overriddenSymbols.any { s -> s == irBase } } .singleOrNull { it.overriddenSymbols.any { s -> s == irBase } }
?: irClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.overriddenSymbols.any { s -> s == irBase } }
inner class ThrowablePropertiesUsageTransformer : IrElementTransformerVoid() { inner class ThrowablePropertiesUsageTransformer : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
@@ -244,7 +244,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
} }
val typeParameter = typeParameterSymbol.owner val typeParameter = typeParameterSymbol.owner
assert(!typeParameter.isReified) { "reified parameters have to be lowered before" } // TODO either remove functions with reified type parameters or support this case
// assert(!typeParameter.isReified) { "reified parameters have to be lowered before" }
return typeParameter.superTypes.fold(litTrue) { r, t -> return typeParameter.superTypes.fold(litTrue) { r, t ->
val check = generateTypeCheckNonNull(argument.copy(), t.makeNotNull(false)) val check = generateTypeCheckNonNull(argument.copy(), t.makeNotNull(false))
calculator.and(r, check) calculator.and(r, check)
@@ -7,12 +7,12 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.getSimpleFunction
import org.jetbrains.kotlin.ir.util.getPropertyGetter
class PrimitiveContainerMemberCallTransformer(private val context: JsIrBackendContext) : CallsTransformer { class PrimitiveContainerMemberCallTransformer(private val context: JsIrBackendContext) : CallsTransformer {
private val intrinsics = context.intrinsics private val intrinsics = context.intrinsics
@@ -65,22 +65,22 @@ class PrimitiveContainerMemberCallTransformer(private val context: JsIrBackendCo
} }
private val IrClassSymbol.sizeProperty private val IrClassSymbol.sizeProperty
get() = owner.declarations.filterIsInstance<IrProperty>().first { it.name.asString() == "size" }.getter!!.symbol get() = getPropertyGetter("size")!!
private val IrClassSymbol.getFunction private val IrClassSymbol.getFunction
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "get" }.symbol get() = getSimpleFunction("get")!!
private val IrClassSymbol.setFunction private val IrClassSymbol.setFunction
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "set" }.symbol get() = getSimpleFunction("set")!!
private val IrClassSymbol.iterator private val IrClassSymbol.iterator
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "iterator" }.symbol get() = getSimpleFunction("iterator")!!
private val IrClassSymbol.sizeConstructor private val IrClassSymbol.sizeConstructor
get() = owner.declarations.filterIsInstance<IrConstructor>().first { it.valueParameters.size == 1 }.symbol get() = owner.declarations.filterIsInstance<IrConstructor>().first { it.valueParameters.size == 1 }.symbol
private val IrClassSymbol.lengthProperty private val IrClassSymbol.lengthProperty
get() = owner.declarations.filterIsInstance<IrProperty>().first { it.name.asString() == "length" }.getter!!.symbol get() = getPropertyGetter("length")!!
private val IrClassSymbol.subSequence private val IrClassSymbol.subSequence
get() = owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "subSequence" }.symbol get() = getSimpleFunction("subSequence")!!
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
@@ -64,9 +65,11 @@ class StateMachineBuilder(
val context: JsIrBackendContext, val context: JsIrBackendContext,
val function: IrFunctionSymbol, val function: IrFunctionSymbol,
private val rootLoop: IrLoop, private val rootLoop: IrLoop,
private val exceptionSymbol: IrProperty, private val exceptionSymbolGetter: IrSimpleFunction,
private val exStateSymbol: IrProperty, private val exceptionSymbolSetter: IrSimpleFunction,
private val stateSymbol: IrProperty, private val exStateSymbolGetter: IrSimpleFunction,
private val exStateSymbolSetter: IrSimpleFunction,
private val stateSymbolSetter: IrSimpleFunction,
private val thisSymbol: IrValueParameterSymbol, private val thisSymbol: IrValueParameterSymbol,
private val suspendResult: IrVariableSymbol private val suspendResult: IrVariableSymbol
) : IrElementVisitorVoid { ) : IrElementVisitorVoid {
@@ -84,7 +87,7 @@ class StateMachineBuilder(
val entryState = SuspendState(unit) val entryState = SuspendState(unit)
val rootExceptionTrap = buildExceptionTrapState() val rootExceptionTrap = buildExceptionTrapState()
private val globalExceptionVar = JsIrBuilder.buildVar(exceptionSymbol.getter!!.returnType, function.owner, "e") private val globalExceptionVar = JsIrBuilder.buildVar(exceptionSymbolGetter.returnType, function.owner, "e")
lateinit var globalCatch: IrCatch lateinit var globalCatch: IrCatch
fun finalizeStateMachine() { fun finalizeStateMachine() {
@@ -118,11 +121,11 @@ class StateMachineBuilder(
) )
// TODO: exception table // TODO: exception table
elseBlock.statements += JsIrBuilder.buildCall(stateSymbol.setter!!.symbol, unit).apply { elseBlock.statements += JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).apply {
dispatchReceiver = thisReceiver dispatchReceiver = thisReceiver
putValueArgument(0, exceptionState()) putValueArgument(0, exceptionState())
} }
elseBlock.statements += JsIrBuilder.buildCall(exceptionSymbol.setter!!.symbol, unit).apply { elseBlock.statements += JsIrBuilder.buildCall(exceptionSymbolSetter.symbol, unit).apply {
dispatchReceiver = thisReceiver dispatchReceiver = thisReceiver
putValueArgument(0, JsIrBuilder.buildGetValue(globalExceptionSymbol)) putValueArgument(0, JsIrBuilder.buildGetValue(globalExceptionSymbol))
} }
@@ -180,7 +183,7 @@ class StateMachineBuilder(
private fun doDispatchImpl(target: SuspendState, block: IrContainerExpression, andContinue: Boolean) { private fun doDispatchImpl(target: SuspendState, block: IrContainerExpression, andContinue: Boolean) {
val irDispatch = IrDispatchPoint(target) val irDispatch = IrDispatchPoint(target)
currentState.successors.add(target) currentState.successors.add(target)
block.addStatement(JsIrBuilder.buildCall(stateSymbol.setter!!.symbol, unit).apply { block.addStatement(JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).apply {
dispatchReceiver = thisReceiver dispatchReceiver = thisReceiver
putValueArgument(0, irDispatch) putValueArgument(0, irDispatch)
}) })
@@ -297,7 +300,7 @@ class StateMachineBuilder(
currentState.successors += continueState currentState.successors += continueState
transformLastExpression { transformLastExpression {
JsIrBuilder.buildCall(stateSymbol.setter!!.symbol, unit).apply { JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).apply {
dispatchReceiver = thisReceiver dispatchReceiver = thisReceiver
putValueArgument(0, dispatch) putValueArgument(0, dispatch)
} }
@@ -686,7 +689,7 @@ class StateMachineBuilder(
aTry.finallyExpression?.acceptVoid(this) aTry.finallyExpression?.acceptVoid(this)
currentState.successors += listOf(throwExitState, exitState) currentState.successors += listOf(throwExitState, exitState)
addStatement( addStatement(
JsIrBuilder.buildCall(stateSymbol.setter!!.symbol, unit).also { JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).also {
it.dispatchReceiver = thisReceiver it.dispatchReceiver = thisReceiver
it.putValueArgument(0, JsIrBuilder.buildGetValue(finallyStateVar.symbol)) it.putValueArgument(0, JsIrBuilder.buildGetValue(finallyStateVar.symbol))
} }
@@ -706,15 +709,15 @@ class StateMachineBuilder(
private fun setupExceptionState(target: SuspendState) { private fun setupExceptionState(target: SuspendState) {
addStatement( addStatement(
JsIrBuilder.buildCall(exStateSymbol.setter!!.symbol, unit).apply { JsIrBuilder.buildCall(exStateSymbolSetter.symbol, unit).apply {
dispatchReceiver = thisReceiver dispatchReceiver = thisReceiver
putValueArgument(0, IrDispatchPoint(target)) putValueArgument(0, IrDispatchPoint(target))
} }
) )
} }
private fun exceptionState() = JsIrBuilder.buildCall(exStateSymbol.getter!!.symbol).also { it.dispatchReceiver = thisReceiver } private fun exceptionState() = JsIrBuilder.buildCall(exStateSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
private fun pendingException() = JsIrBuilder.buildCall(exceptionSymbol.getter!!.symbol).also { it.dispatchReceiver = thisReceiver } private fun pendingException() = JsIrBuilder.buildCall(exceptionSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
private fun buildTryState(aTry: IrTry) = private fun buildTryState(aTry: IrTry) =
TryState( TryState(
@@ -228,8 +228,8 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
builtCoroutines[irFunction] = coroutine builtCoroutines[irFunction] = coroutine
if (functionReference == null) { if (functionReference == null) {
val resultSetter = context.coroutineImplResultSymbol.setter!! val resultSetter = context.coroutineImplResultSymbolSetter
val exceptionSetter = context.coroutineImplExceptionProperty.setter!! val exceptionSetter = context.coroutineImplExceptionPropertySetter
// It is not a lambda - replace original function with a call to constructor of the built coroutine. // 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) val irBuilder = context.createIrBuilder(irFunction.symbol, irFunction.startOffset, irFunction.endOffset)
irFunction.body = irBuilder.irBlockBody(irFunction) { irFunction.body = irBuilder.irBlockBody(irFunction) {
@@ -290,10 +290,13 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
private val create1CompletionParameter = create1Function.valueParameters[0] private val create1CompletionParameter = create1Function.valueParameters[0]
private val coroutineImplLabelProperty = context.coroutineImplLabelProperty private val coroutineImplLabelPropertyGetter = context.coroutineImplLabelPropertyGetter
private val coroutineImplResultSymbol = context.coroutineImplResultSymbol private val coroutineImplLabelPropertySetter = context.coroutineImplLabelPropertySetter
private val coroutineImplExceptionProperty = context.coroutineImplExceptionProperty private val coroutineImplResultSymbolGetter = context.coroutineImplResultSymbolGetter
private val coroutineImplExceptionStateProperty = context.coroutineImplExceptionStateProperty private val coroutineImplExceptionPropertyGetter = context.coroutineImplExceptionPropertyGetter
private val coroutineImplExceptionPropertySetter = context.coroutineImplExceptionPropertySetter
private val coroutineImplExceptionStatePropertyGetter = context.coroutineImplExceptionStatePropertyGetter
private val coroutineImplExceptionStatePropertySetter = context.coroutineImplExceptionStatePropertySetter
private val coroutineConstructors = mutableListOf<IrConstructor>() private val coroutineConstructors = mutableListOf<IrConstructor>()
private var exceptionTrapId = -1 private var exceptionTrapId = -1
@@ -727,8 +730,8 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
JsIrBuilder.buildValueParameter(p.name, p.index, p.type, p.origin).also { it.parent = declaration } JsIrBuilder.buildValueParameter(p.name, p.index, p.type, p.origin).also { it.parent = declaration }
} }
val resultSetter = context.coroutineImplResultSymbol.setter!! val resultSetter = context.coroutineImplResultSymbolSetter
val exceptionSetter = context.coroutineImplExceptionProperty.setter!! val exceptionSetter = context.coroutineImplExceptionPropertySetter
val thisReceiver = declaration.dispatchReceiverParameter!! val thisReceiver = declaration.dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, irFunction.startOffset, irFunction.endOffset) val irBuilder = context.createIrBuilder(symbol, irFunction.startOffset, irFunction.endOffset)
@@ -821,10 +824,10 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
function, function,
"suspendResult", "suspendResult",
true, true,
initializer = JsIrBuilder.buildCall(coroutineImplResultSymbol.getter!!.symbol).apply { dispatchReceiver = thisReceiver } initializer = JsIrBuilder.buildCall(coroutineImplResultSymbolGetter.symbol).apply { dispatchReceiver = thisReceiver }
) )
suspendState = JsIrBuilder.buildVar(coroutineImplLabelProperty.getter!!.returnType, function, "suspendState", true) suspendState = JsIrBuilder.buildVar(coroutineImplLabelPropertyGetter.returnType, function, "suspendState", true)
val body = val body =
(originalBody as IrBlockBody).run { (originalBody as IrBlockBody).run {
@@ -848,7 +851,7 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
(it.body as? IrBlockBody)?.run { (it.body as? IrBlockBody)?.run {
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol) val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId) val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
statements += JsIrBuilder.buildCall(coroutineImplExceptionStateProperty.setter!!.symbol).also { call -> statements += JsIrBuilder.buildCall(coroutineImplExceptionStatePropertySetter.symbol).also { call ->
call.dispatchReceiver = receiver call.dispatchReceiver = receiver
call.putValueArgument(0, id) call.putValueArgument(0, id)
} }
@@ -880,9 +883,11 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
context, context,
function.symbol, function.symbol,
rootLoop, rootLoop,
coroutineImplExceptionProperty, coroutineImplExceptionPropertyGetter,
coroutineImplExceptionStateProperty, coroutineImplExceptionPropertySetter,
coroutineImplLabelProperty, coroutineImplExceptionStatePropertyGetter,
coroutineImplExceptionStatePropertySetter,
coroutineImplLabelPropertySetter,
thisReceiver, thisReceiver,
suspendResult.symbol suspendResult.symbol
) )
@@ -907,7 +912,7 @@ internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLo
for (state in sortedStates) { for (state in sortedStates) {
val condition = JsIrBuilder.buildCall(eqeqeqInt).apply { val condition = JsIrBuilder.buildCall(eqeqeqInt).apply {
putValueArgument(0, JsIrBuilder.buildCall(coroutineImplLabelProperty.getter!!.symbol).also { putValueArgument(0, JsIrBuilder.buildCall(coroutineImplLabelPropertyGetter.symbol).also {
it.dispatchReceiver = JsIrBuilder.buildGetValue(thisReceiver) it.dispatchReceiver = JsIrBuilder.buildGetValue(thisReceiver)
}) })
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id)) putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id))
@@ -7,8 +7,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower.inline package org.jetbrains.kotlin.ir.backend.js.lower.inline
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.ScopeWithIr
import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.lower.ArrayConstructorTransformer
import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.builders.irReturn
@@ -31,11 +31,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.types.irTypeKotlinBuiltIns import org.jetbrains.kotlin.ir.types.irTypeKotlinBuiltIns
import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -53,11 +49,27 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
return irModule.accept(this, data = null) return irModule.accept(this, data = null)
} }
private val arrayConstructorTransformer = ArrayConstructorTransformer(context)
override fun visitCall(expression: IrCall): IrExpression { override fun visitCall(expression: IrCall): IrExpression {
val callSite = super.visitCall(expression) as IrCall val callSite = arrayConstructorTransformer.transformCall(super.visitCall(expression) as IrCall)
val functionDescriptor = callSite.descriptor val functionDescriptor = callSite.descriptor
if (!functionDescriptor.needsInlining)
return callSite // This call does not need inlining. if (!functionDescriptor.needsInlining) return callSite // This call does not need inlining.
val languageVersionSettings = context.configuration.languageVersionSettings
when {
callSite.symbol == context.ir.symbols.lateinitIsInitializedPropertyGetter ->
return callSite
// Handle coroutine intrinsics
// TODO These should actually be inlined.
callSite.descriptor.isBuiltInIntercepted(languageVersionSettings) ->
error("Continuation.intercepted is not available with release coroutines")
callSite.symbol.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
return irCall(callSite, context.coroutineSuspendOrReturn)
callSite.symbol == context.intrinsics.jsCoroutineContext ->
return irCall(callSite, context.coroutineGetContextJs)
}
val callee = getFunctionDeclaration(callSite.symbol) // Get declaration of the function to be inlined. val callee = getFunctionDeclaration(callSite.symbol) // Get declaration of the function to be inlined.
callee.transformChildrenVoid(this) // Process recursive inline. callee.transformChildrenVoid(this) // Process recursive inline.
@@ -21,7 +21,7 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitFunction(declaration: IrFunction, data: JsGenerationContext) = JsEmpty.also { override fun visitFunction(declaration: IrFunction, data: JsGenerationContext) = JsEmpty.also {
assert(declaration.origin == JsIrBackendContext.callableClosureOrigin) { assert(declaration.origin == JsIrBackendContext.callableClosureOrigin) {
"The only possible Function Declarartion is one composed in Callable Reference Lowering" "The only possible Function Declaration is one composed in Callable Reference Lowering"
} }
} }