[JS IR BE] Support coroutines

* Move FinallyBlockLowering to common part
* Fix catching of dynamic exception
* Fix bridges for suspend functions
* Disable explicit cast to Unit
* Run lowering per module
* Update some test data
This commit is contained in:
Roman Artemev
2018-06-18 20:01:39 +03:00
committed by romanart
parent 37fadb8ee5
commit c62e4b4fcf
127 changed files with 3539 additions and 256 deletions
@@ -44,7 +44,7 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, private val
return initializer()
}
val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) }
// val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) }
//abstract val areEqualByValue: List<IrFunctionSymbol>
@@ -0,0 +1,318 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
class FinallyBlocksLowering(val context: CommonBackendContext, private val throwableType: IrType): FileLoweringPass, IrElementTransformerVoidWithContext() {
private interface HighLevelJump {
fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression): IrExpression
}
private data class Return(val target: IrReturnTargetSymbol): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
=
IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, target, value)
}
private data class Break(val loop: IrLoop): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
= IrCompositeImpl(
startOffset, endOffset, context.irBuiltIns.unitType, null,
statements = listOf(
value,
IrBreakImpl(startOffset, endOffset, context.irBuiltIns.nothingType, loop)
)
)
}
private data class Continue(val loop: IrLoop): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
= IrCompositeImpl(
startOffset, endOffset, context.irBuiltIns.unitType, null,
statements = listOf(
value,
IrContinueImpl(startOffset, endOffset, context.irBuiltIns.nothingType, loop)
)
)
}
private abstract class Scope
private class ReturnableScope(val descriptor: CallableDescriptor): Scope()
private class LoopScope(val loop: IrLoop): Scope()
private class TryScope(var expression: IrExpression,
val finallyExpression: IrExpression,
val irBuilder: IrBuilderWithScope
): Scope() {
val jumps = mutableMapOf<HighLevelJump, IrReturnTargetSymbol>()
}
private val scopeStack = mutableListOf<Scope>()
private inline fun <S: Scope, R> using(scope: S, block: (S) -> R): R {
scopeStack.push(scope)
try {
return block(scope)
} finally {
scopeStack.pop()
}
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitFunctionNew(declaration: IrFunction): IrStatement {
using(ReturnableScope(declaration.descriptor)) {
return super.visitFunctionNew(declaration)
}
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlockImpl)
return super.visitContainerExpression(expression)
using(ReturnableScope(expression.descriptor)) {
return super.visitContainerExpression(expression)
}
}
override fun visitLoop(loop: IrLoop): IrExpression {
using(LoopScope(loop)) {
return super.visitLoop(loop)
}
}
override fun visitBreak(jump: IrBreak): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Break(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitContinue(jump: IrContinue): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Continue(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
return performHighLevelJump(
targetScopePredicate = { it is ReturnableScope && it.descriptor == expression.returnTarget },
jump = Return(expression.returnTargetSymbol),
startOffset = expression.startOffset,
endOffset = expression.endOffset,
value = expression.value
) ?: expression
}
private fun performHighLevelJump(targetScopePredicate: (Scope) -> Boolean,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression
): IrExpression? {
val tryScopes = scopeStack.reversed()
.takeWhile { !targetScopePredicate(it) }
.filterIsInstance<TryScope>()
.toList()
if (tryScopes.isEmpty())
return null
return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value)
}
private val IrReturnTarget.returnType: IrType
get() = when (this) {
is IrConstructor -> context.irBuiltIns.unitType
is IrFunction -> returnType
is IrReturnableBlock -> type
else -> error("Unknown ReturnTarget: $this")
}
private fun performHighLevelJump(tryScopes: List<TryScope>,
index: Int,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression
): IrExpression {
if (index == tryScopes.size)
return jump.toIr(context, startOffset, endOffset, value)
val currentTryScope = tryScopes[index]
currentTryScope.jumps.getOrPut(jump) {
val type = (jump as? Return)?.target?.owner?.returnType ?: value.type
val symbol = getIrReturnableBlockSymbol(jump.toString(), type)
with(currentTryScope) {
irBuilder.run {
val inlinedFinally = irInlineFinally(symbol, type, expression, finallyExpression)
expression = performHighLevelJump(
tryScopes = tryScopes,
index = index + 1,
jump = jump,
startOffset = startOffset,
endOffset = endOffset,
value = inlinedFinally)
}
}
symbol
}.let {
return IrReturnImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.nothingType,
returnTargetSymbol = it,
value = value
)
}
}
override fun visitTry(aTry: IrTry): IrExpression {
val finallyExpression = aTry.finallyExpression
?: return super.visitTry(aTry)
val startOffset = aTry.startOffset
val endOffset = aTry.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
val transformer = this
irBuilder.run {
val transformedTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.unitType
)
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
val parameter = IrTemporaryVariableDescriptorImpl(
containingDeclaration = currentScope!!.scope.scopeOwner,
name = Name.identifier("t"),
outType = throwableType.toKotlinType()
)
val catchParameter = IrVariableImpl(
startOffset, endOffset, IrDeclarationOrigin.CATCH_PARAMETER, parameter,
throwableType
)
val syntheticTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.unitType,
tryResult = transformedTry,
catches = listOf(
irCatch(catchParameter).apply {
result = irComposite {
+finallyExpression.copy()
+irThrow(irGet(catchParameter))
}
}),
finallyExpression = null
)
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
val fallThroughType = aTry.type
val fallThroughSymbol = getIrReturnableBlockSymbol("fallThrough", fallThroughType)
val transformedResult = aTry.tryResult.transform(transformer, null)
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
for (aCatch in aTry.catches) {
val transformedCatch = aCatch.transform(transformer, null)
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
transformedTry.catches.add(transformedCatch)
}
return irInlineFinally(fallThroughSymbol, fallThroughType, it.expression, it.finallyExpression)
}
}
}
private fun IrBuilderWithScope.irInlineFinally(symbol: IrReturnableBlockSymbol, type: IrType,
value: IrExpression,
finallyExpression: IrExpression
): IrExpression {
val returnType = symbol.descriptor.returnType!!
return when {
returnType.isUnit() || returnType.isNothing() -> irBlock(value, null, type) {
+irReturnableBlock(symbol, type) {
+value
}
+finallyExpression.copy()
}
else -> irComposite(value, null, type) {
val tmp = irTemporary(irReturnableBlock(symbol, type) {
+irReturn(symbol, value)
})
+finallyExpression.copy()
+irGet(tmp)
}
}
}
private fun getFakeFunctionDescriptor(name: String, returnType: KotlinType) =
SimpleFunctionDescriptorImpl.create(
currentScope!!.scope.scopeOwner,
Annotations.EMPTY,
name.synthesizedName,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE
).apply {
initialize(null, null, emptyList(), emptyList(), returnType,
Modality.ABSTRACT,
Visibilities.PRIVATE
)
}
private fun getIrReturnableBlockSymbol(name: String, returnType: IrType): IrReturnableBlockSymbol =
IrReturnableBlockSymbolImpl(getFakeFunctionDescriptor(name, returnType.toKotlinType()))
private inline fun <reified T : IrElement> T.copy() = this.deepCopyWithVariables()
fun IrBuilderWithScope.irReturn(target: IrReturnTargetSymbol, value: IrExpression) =
IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, target, value)
inline fun IrBuilderWithScope.irReturnableBlock(symbol: IrReturnableBlockSymbol, type: IrType, body: IrBlockBuilder.() -> Unit) =
IrReturnableBlockImpl(
startOffset, endOffset, type, symbol, null,
IrBlockBuilder(context, scope, startOffset, endOffset, null, type, true)
.block(body).statements
)
}
@@ -247,6 +247,31 @@ class InnerClassConstructorCallsLowering(val context: BackendContext) : BodyLowe
return newCall
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
val callee = expression.symbol as? IrConstructorSymbol ?: return expression
val parent = callee.owner.parent as? IrClass ?: return expression
if (!parent.isInner) return expression
val newCallee = context.descriptorsFactory.getInnerClassConstructorWithOuterThisParameter(callee.owner)
val newReference = expression.run { IrFunctionReferenceImpl(startOffset, endOffset, type, newCallee, newCallee.descriptor, typeArgumentsCount, origin) }
newReference.let {
it.dispatchReceiver = expression.dispatchReceiver
it.extensionReceiver = expression.extensionReceiver
for (t in 0 until expression.typeArgumentsCount) {
it.putTypeArgument(t, expression.getTypeArgument(t))
}
for (v in 0 until expression.valueArgumentsCount) {
it.putValueArgument(v, expression.getValueArgument(v))
}
}
return newReference
}
// TODO callable references?
})
}
@@ -88,6 +88,13 @@ inline fun IrGeneratorWithScope.irBlock(
) =
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
inline fun IrGeneratorWithScope.irComposite(
expression: IrExpression, origin: IrStatementOrigin? = null,
resultType: IrType? = expression.type,
body: IrBlockBuilder.() -> Unit
) =
this.irComposite(expression.startOffset, expression.endOffset, origin, resultType, body)
inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.common.utils
import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrType
@@ -36,3 +37,5 @@ fun List<IrType>.commonSupertype() = CommonSupertypes.commonSupertype(map(IrType
fun IrType.isSubtypeOf(superType: IrType) = toKotlinType().isSubtypeOf(superType.toKotlinType())
fun IrType.isSubtypeOfClass(superClass: IrClassSymbol) = DescriptorUtils.isSubtypeOfClass(toKotlinType(), superClass.descriptor)
fun IrType.isBuiltinFunctionalTypeOrSubtype() = toKotlinType().isBuiltinFunctionalTypeOrSubtype
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.name.Name
@@ -31,3 +32,6 @@ fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? =
fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol
fun IrClassSymbol.getPropertyField(name: String): IrFieldSymbol? =
this.getPropertyDeclaration(name)?.backingField?.symbol
@@ -99,7 +99,6 @@ class JsIntrinsics(
val jsInstanceOf = binOpBool("jsInstanceOf")
val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string)
// Number conversions:
val jsNumberToByte = getInternalFunction("numberToByte")
@@ -132,6 +131,14 @@ class JsIntrinsics(
val jsCompareTo = getInternalFunction("compareTo")
val jsEquals = getInternalFunction("equals")
// Coroutines
val jsCoroutineContext = context.symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
val jsGetContinuation = context.run {
val f = getInternalFunctions("getContinuation")
symbolTable.referenceSimpleFunction(f.single())
}
val jsNumberRangeToNumber = getInternalFunction("numberRangeToNumber")
val jsNumberRangeToLong = getInternalFunction("numberRangeToLong")
@@ -13,20 +13,28 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.js.JsDescriptorsFactory
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ModuleIndex
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.createDynamicType
class JsIrBackendContext(
val module: ModuleDescriptor,
@@ -48,28 +56,75 @@ class JsIrBackendContext(
private val internalPackageName = FqName("kotlin.js")
private val internalPackage = module.getPackage(internalPackageName)
// TODO: replace it with appropriate package name once we migrate to 1.3 coroutines
private val coroutinePackageNameSrting = "kotlin.coroutines.experimental"
private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
private val CONTINUATION_NAME = Name.identifier("Continuation")
// TODO: what is more clear way reference this getter?
private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("<get-context>")
private val coroutinePackageName = FqName(coroutinePackageNameSrting)
private val coroutineIntrinsicsPackageName = coroutinePackageName.child(INTRINSICS_PACKAGE_NAME)
private val coroutinePackage = module.getPackage(coroutinePackageName)
private val coroutineIntrinsicsPackage = module.getPackage(coroutineIntrinsicsPackageName)
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunctionSymbol>()
val coroutineGetContext: IrFunctionSymbol
get() {
val continuation = symbolTable.referenceClass(
coroutinePackage.memberScope.getContributedClassifier(
CONTINUATION_NAME,
NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
val contextGetter = continuation.owner.declarations.single { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME } as IrFunction
return contextGetter.symbol
}
val coroutineContextProperty: PropertyDescriptor
get() {
val vars = internalPackage.memberScope.getContributedVariables(
COROUTINE_CONTEXT_NAME,
NoLookupLocation.FROM_BACKEND
)
return vars.single()
}
val intrinsics = JsIntrinsics(module, irBuiltIns, this)
private val operatorMap = referenceOperators()
data class SecondaryCtorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol)
val functions = (0..22).map { symbolTable.referenceClass(builtIns.getFunction(it)) }
val secondaryConstructorsMap = mutableMapOf<IrConstructorSymbol, SecondaryCtorPair>()
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunctionSymbol>()
val kFunctions by lazy {
(0..22).map { symbolTable.referenceClass(reflectionTypes.getKFunction(it)) }
}
fun getOperatorByName(name: Name, type: IrType) = operatorMap[name]?.get(type.toKotlinType())
val suspendFunctions = (0..22).map { symbolTable.referenceClass(builtIns.getSuspendFunction(it)) }
val dynamicType = IrDynamicTypeImpl(createDynamicType(builtIns), emptyList(), Variance.INVARIANT)
val originalModuleIndex = ModuleIndex(irModuleFragment)
fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type)
override val ir = object : Ir<CommonBackendContext>(this, irModuleFragment) {
override val symbols = object : Symbols<CommonBackendContext>(this@JsIrBackendContext, symbolTable.lazyWrapper) {
override fun calc(initializer: () -> IrClassSymbol): IrClassSymbol {
val v = lazy { initializer() }
return object : IrClassSymbol {
override val owner: IrClass get() = TODO("not implemented")
override val isBound: Boolean get() = TODO("not implemented")
override fun bind(owner: IrClass) = TODO("not implemented")
override val descriptor: ClassDescriptor get() = TODO("not implemented")
override val owner: IrClass get() = v.value.owner
override val isBound: Boolean get() = v.value.isBound
override fun bind(owner: IrClass) = v.value.bind(owner)
override val descriptor: ClassDescriptor get() = v.value.descriptor
}
}
@@ -97,10 +152,10 @@ class JsIrBackendContext(
get() = TODO("not implemented")
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
get() = TODO("not implemented")
override val coroutineImpl: IrClassSymbol
get() = TODO("not implemented")
override val coroutineSuspendedGetter: IrSimpleFunctionSymbol
get() = TODO("not implemented")
override val coroutineImpl = symbolTable.referenceClass(getInternalClass(COROUTINE_IMPL_NAME.identifier))
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
coroutineIntrinsicsPackage.memberScope.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND).single().getter!!
)
}
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
@@ -132,6 +187,8 @@ class JsIrBackendContext(
override fun getInternalFunctions(name: String) = findFunctions(internalPackage.memberScope, name)
fun getFunctions(fqName: FqName) = findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName())
override fun log(message: () -> String) {
/*TODO*/
print(message())
@@ -6,13 +6,18 @@
package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.FunctionInlining
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.replaceUnboundSymbols
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -59,9 +64,7 @@ fun compile(
context.performInlining(moduleFragment)
context.lower(moduleFragment.files)
val transformer = SecondaryCtorLowering.CallsiteRedirectionTransformer(context)
moduleFragment.files.forEach { it.accept(transformer, null) }
context.lower(moduleFragment)
val program = moduleFragment.accept(IrModuleToJsTransformer(context), null)
@@ -80,30 +83,32 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
}
}
private fun JsIrBackendContext.lower(files: List<IrFile>) {
LateinitLowering(this, true).lower(files)
DefaultArgumentStubGenerator(this).runOnFilePostfix(files)
DefaultParameterInjector(this).runOnFilePostfix(files)
SharedVariablesLowering(this).runOnFilePostfix(files)
EnumClassLowering(this).runOnFilePostfix(files)
EnumUsageLowering(this).lower(files)
ReturnableBlockLowering(this).lower(files)
LocalDeclarationsLowering(this).runOnFilePostfix(files)
InnerClassesLowering(this).runOnFilePostfix(files)
InnerClassConstructorCallsLowering(this).runOnFilePostfix(files)
PropertiesLowering().lower(files)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(files)
MultipleCatchesLowering(this).lower(files)
BridgesConstruction(this).runOnFilePostfix(files)
TypeOperatorLowering(this).lower(files)
BlockDecomposerLowering(this).runOnFilePostfix(files)
SecondaryCtorLowering(this).runOnFilePostfix(files)
CallableReferenceLowering(this).lower(files)
IntrinsicifyCallsLowering(this).lower(files)
private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment) {
moduleFragment.files.forEach(LateinitLowering(this, true)::lower)
moduleFragment.files.forEach(DefaultArgumentStubGenerator(this)::runOnFilePostfix)
moduleFragment.files.forEach(DefaultParameterInjector(this)::runOnFilePostfix)
moduleFragment.files.forEach(SharedVariablesLowering(this)::runOnFilePostfix)
moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix)
moduleFragment.files.forEach(EnumUsageLowering(this)::lower)
moduleFragment.files.forEach(ReturnableBlockLowering(this)::lower)
moduleFragment.files.forEach(LocalDeclarationsLowering(this)::runOnFilePostfix)
moduleFragment.files.forEach(InnerClassesLowering(this)::runOnFilePostfix)
moduleFragment.files.forEach(InnerClassConstructorCallsLowering(this)::runOnFilePostfix)
moduleFragment.files.forEach(SuspendFunctionsLowering(this)::lower)
moduleFragment.files.forEach(PropertiesLowering()::lower)
moduleFragment.files.forEach(InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false)::runOnFilePostfix)
moduleFragment.files.forEach(MultipleCatchesLowering(this)::lower)
moduleFragment.files.forEach(BridgesConstruction(this)::runOnFilePostfix)
moduleFragment.files.forEach(TypeOperatorLowering(this)::lower)
moduleFragment.files.forEach(BlockDecomposerLowering(this)::runOnFilePostfix)
val sctor = SecondaryCtorLowering(this)
moduleFragment.files.forEach(sctor.getConstructorProcessorLowering())
moduleFragment.files.forEach(sctor.getConstructorRedirectorLowering())
val clble = CallableReferenceLowering(this)
moduleFragment.files.forEach(clble.getReferenceCollector())
moduleFragment.files.forEach(clble.getClosureBuilder())
moduleFragment.files.forEach(clble.getReferenceReplacer())
moduleFragment.files.forEach(IntrinsicifyCallsLowering(this)::lower)
}
private fun FileLoweringPass.lower(files: List<IrFile>) = files.forEach { lower(it) }
private fun DeclarationContainerLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun BodyLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun FunctionLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun ClassLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.ir
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.name.Name
class JsIrArithBuilder(val context: JsIrBackendContext) {
@@ -15,15 +16,15 @@ class JsIrArithBuilder(val context: JsIrBackendContext) {
val symbols = context.ir.symbols
private fun buildBinaryOperator(name: Name, l: IrExpression, r: IrExpression): IrExpression {
val symbol = context.getOperatorByName(name, l.type)!!
return JsIrBuilder.buildCall(symbol).apply {
val symbol = context.getOperatorByName(name, l.type.toKotlinType())
return JsIrBuilder.buildCall(symbol!!).apply {
dispatchReceiver = l
putValueArgument(0, r)
}
}
private fun buildUnaryOperator(name: Name, v: IrExpression): IrExpression {
val symbol = context.getOperatorByName(name, v.type)!!
val symbol = context.getOperatorByName(name, v.type.toKotlinType())!!
return JsIrBuilder.buildCall(symbol).apply { dispatchReceiver = v }
}
@@ -5,16 +5,31 @@
package org.jetbrains.kotlin.ir.backend.js.ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.createDispatchReceiverParameter
import org.jetbrains.kotlin.ir.util.endOffset
import org.jetbrains.kotlin.ir.util.startOffset
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.resolve.OverridingStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.lang.reflect.Proxy
object JsIrBuilder {
@@ -77,8 +92,8 @@ object JsIrBuilder {
fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol) =
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor, 0, null)
fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType? = null) =
IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type ?: symbol.owner.type)
fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType) =
IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type)
.apply { this.initializer = initializer }
fun buildBreak(type: IrType, loop: IrLoop) = IrBreakImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop)
@@ -107,8 +122,8 @@ object JsIrBuilder {
return element
}
fun buildWhen(type: IrType, branches: List<IrBranch>) =
IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, SYNTHESIZED_STATEMENT, branches)
fun buildWhen(type: IrType, branches: List<IrBranch>, origin: IrStatementOrigin = SYNTHESIZED_STATEMENT) =
IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, origin, branches)
fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) =
IrTypeOperatorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, operator, toType, symbol, argument)
@@ -117,4 +132,276 @@ object JsIrBuilder {
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildString(type: IrType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s)
fun buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block)
}
object SetDeclarationsParentVisitor : IrElementVisitor<Unit, IrDeclarationParent> {
override fun visitElement(element: IrElement, data: IrDeclarationParent) {
if (element !is IrDeclarationParent) {
element.acceptChildren(this, data)
}
}
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclarationParent) {
declaration.parent = data
super.visitDeclaration(declaration, data)
}
}
fun SymbolTable.translateErased(type: KotlinType): IrSimpleType {
val descriptor = TypeUtils.getClassDescriptor(type)
if (descriptor == null) return translateErased(type.immediateSupertypes().first())
val classSymbol = this.referenceClass(descriptor)
val nullable = type.isMarkedNullable
val arguments = type.arguments.map { IrStarProjectionImpl }
return classSymbol.createType(nullable, arguments)
}
fun IrDeclarationContainer.addChildren(declarations: List<IrDeclaration>) {
declarations.forEach { this.addChild(it) }
}
fun IrDeclarationContainer.addChild(declaration: IrDeclaration) {
this.declarations += declaration
declaration.accept(SetDeclarationsParentVisitor, this)
}
fun IrClass.simpleFunctions(): List<IrSimpleFunction> = this.declarations.flatMap {
when (it) {
is IrSimpleFunction -> listOf(it)
is IrProperty -> listOfNotNull(it.getter as IrSimpleFunction?, it.setter as IrSimpleFunction?)
else -> emptyList()
}
}
fun IrClass.setSuperSymbols(superTypes: List<IrType>) {
val supers = superTypes.map { it.getClass()!! }
assert(this.superDescriptors().toSet() == supers.map { it.descriptor }.toSet())
assert(this.superTypes.isEmpty())
this.superTypes += superTypes
val superMembers = supers.flatMap {
it.simpleFunctions()
}.associateBy { it.descriptor }
this.simpleFunctions().forEach {
assert(it.overriddenSymbols.isEmpty())
it.descriptor.overriddenDescriptors.mapTo(it.overriddenSymbols) {
val superMember = superMembers[it.original] ?: error(it.original)
superMember.symbol
}
}
}
fun IrSimpleFunction.setOverrides(symbolTable: SymbolTable) {
assert(this.overriddenSymbols.isEmpty())
this.descriptor.overriddenDescriptors.mapTo(this.overriddenSymbols) {
symbolTable.referenceSimpleFunction(it.original)
}
}
private fun IrClass.superDescriptors() =
this.descriptor.typeConstructor.supertypes.map { it.constructor.declarationDescriptor as ClassDescriptor }
fun IrClass.setSuperSymbols(symbolTable: SymbolTable) {
assert(this.superTypes.isEmpty())
this.descriptor.typeConstructor.supertypes.mapTo(this.superTypes) { symbolTable.translateErased(it) }
this.simpleFunctions().forEach {
it.setOverrides(symbolTable)
}
}
private fun IrElement.innerStartOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.startOffset ?: this.startOffset
private fun IrElement.innerEndOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.endOffset ?: this.endOffset
fun IrFunction.createParameterDeclarations(symbolTable: SymbolTable) {
fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl(
innerStartOffset(this), innerEndOffset(this),
IrDeclarationOrigin.DEFINED,
this, symbolTable.translateErased(this.type),
(this as? ValueParameterDescriptor)?.varargElementType?.let { symbolTable.translateErased(it) }
).also {
it.parent = this@createParameterDeclarations
}
dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.irValueParameter()
extensionReceiverParameter = descriptor.extensionReceiverParameter?.irValueParameter()
assert(valueParameters.isEmpty())
descriptor.valueParameters.mapTo(valueParameters) { it.irValueParameter() }
assert(typeParameters.isEmpty())
descriptor.typeParameters.mapTo(typeParameters) {
IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
).also { typeParameter ->
typeParameter.parent = this
typeParameter.descriptor.upperBounds.mapTo(typeParameter.superTypes, symbolTable::translateErased)
}
}
}
private fun createFakeOverride(
descriptor: CallableMemberDescriptor,
startOffset: Int,
endOffset: Int,
symbolTable: SymbolTable
): IrDeclaration {
fun FunctionDescriptor.createFunction(): IrSimpleFunction = IrFunctionImpl(
startOffset, endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE, this
).apply {
returnType = symbolTable.translateErased(this@createFunction.returnType!!)
createParameterDeclarations(symbolTable)
}
return when (descriptor) {
is FunctionDescriptor -> descriptor.createFunction()
is PropertyDescriptor ->
IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, descriptor).apply {
// TODO: add field if getter is missing?
getter = descriptor.getter?.createFunction() as IrSimpleFunction?
setter = descriptor.setter?.createFunction() as IrSimpleFunction?
}
else -> TODO(descriptor.toString())
}
}
private fun createFakeOverride(
descriptor: CallableMemberDescriptor,
overriddenDeclarations: List<IrDeclaration>,
irClass: IrClass
): IrDeclaration {
// TODO: this function doesn't substitute types.
fun IrSimpleFunction.copyFake(descriptor: FunctionDescriptor): IrSimpleFunction = IrFunctionImpl(
irClass.startOffset, irClass.endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, descriptor
).also {
it.returnType = returnType
it.parent = irClass
it.createDispatchReceiverParameter()
it.extensionReceiverParameter = this.extensionReceiverParameter?.let {
IrValueParameterImpl(
it.startOffset,
it.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.extensionReceiverParameter!!,
it.type,
null
)
}
this.valueParameters.mapTo(it.valueParameters) { oldParameter ->
IrValueParameterImpl(
oldParameter.startOffset,
oldParameter.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.valueParameters[oldParameter.index],
oldParameter.type,
(oldParameter as? IrValueParameter)?.varargElementType
)
}
this.typeParameters.mapTo(it.typeParameters) { oldParameter ->
IrTypeParameterImpl(
irClass.startOffset,
irClass.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.typeParameters[oldParameter.index]
).apply {
superTypes += oldParameter.superTypes
}
}
}
val copiedDeclaration = overriddenDeclarations.first()
return when (copiedDeclaration) {
is IrSimpleFunction -> copiedDeclaration.copyFake(descriptor as FunctionDescriptor)
is IrProperty -> IrPropertyImpl(
irClass.startOffset,
irClass.endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE,
descriptor as PropertyDescriptor
).apply {
parent = irClass
getter = copiedDeclaration.getter?.copyFake(descriptor.getter!!)
setter = copiedDeclaration.setter?.copyFake(descriptor.setter!!)
}
else -> error(copiedDeclaration)
}
}
fun IrClass.setSuperSymbolsAndAddFakeOverrides(superTypes: List<IrType>) {
val overriddenSuperMembers = this.declarations.map { it.descriptor }
.filterIsInstance<CallableMemberDescriptor>().flatMap { it.overriddenDescriptors.map { it.original } }.toSet()
val unoverriddenSuperMembers = superTypes.map { it.getClass()!! }.flatMap {
it.declarations.filter { it.descriptor !in overriddenSuperMembers }.mapNotNull {
when (it) {
is IrSimpleFunction -> it.descriptor to it
is IrProperty -> it.descriptor to it
else -> null
}
}
}.toMap()
val irClass = this
val overridingStrategy = object : OverridingStrategy() {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
val overriddenDeclarations =
fakeOverride.overriddenDescriptors.map { unoverriddenSuperMembers[it]!! }
assert(overriddenDeclarations.isNotEmpty())
irClass.declarations.add(createFakeOverride(fakeOverride, overriddenDeclarations, irClass))
}
override fun inheritanceConflict(first: CallableMemberDescriptor, second: CallableMemberDescriptor) {
error("inheritance conflict in synthesized class ${irClass.descriptor}:\n $first\n $second")
}
override fun overrideConflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("override conflict in synthesized class ${irClass.descriptor}:\n $fromSuper\n $fromCurrent")
}
}
unoverriddenSuperMembers.keys.groupBy { it.name }.forEach { (name, members) ->
OverridingUtil.generateOverridesInFunctionGroup(
name,
members,
emptyList(),
this.descriptor,
overridingStrategy
)
}
this.setSuperSymbols(superTypes)
}
inline fun <reified T> stub(name: String): T {
return Proxy.newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) {
_ /* proxy */, method, _ /* methodArgs */ ->
if (method.name == "toString" && method.parameterCount == 0) {
"${T::class.simpleName} stub for $name"
} else {
error("${T::class.simpleName}.${method.name} is not supported for $name")
}
} as T
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
@@ -22,22 +23,10 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass {
private lateinit var function: IrFunction
private var tmpVarCounter: Int = 0
class BlockDecomposerLowering(context: JsIrBackendContext) : DeclarationContainerLoweringPass {
private val statementTransformer = StatementTransformer()
private val expressionTransformer = ExpressionTransformer()
private val constTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit))
private val unreachableFunction =
JsSymbolBuilder.buildSimpleFunction(context.module, Namer.UNREACHABLE_NAME).initialize(returnType = nothingType)
private val decomposerTransformer = BlockDecomposerTransformer(context)
private val nothingType = context.irBuiltIns.nothingType
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat { declaration ->
@@ -53,9 +42,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
}
fun lower(irFunction: IrFunction) {
function = irFunction
tmpVarCounter = 0
irFunction.transformChildrenVoid(statementTransformer)
irFunction.accept(decomposerTransformer, null)
}
fun lower(irField: IrField, container: IrDeclarationContainer): List<IrDeclaration> {
@@ -86,6 +73,33 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
return listOf(irField)
}
}
class BlockDecomposerTransformer(context: JsIrBackendContext) : IrElementTransformerVoid() {
private lateinit var function: IrFunction
private var tmpVarCounter: Int = 0
private val statementTransformer = StatementTransformer()
private val expressionTransformer = ExpressionTransformer()
private val constTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit))
private val unreachableFunction =
JsSymbolBuilder.buildSimpleFunction(context.module, Namer.UNREACHABLE_NAME).initialize(returnType = nothingType)
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
override fun visitFunction(declaration: IrFunction): IrStatement {
function = declaration
tmpVarCounter = 0
return declaration.transform(statementTransformer, null)
}
override fun visitElement(element: IrElement) = element.transform(statementTransformer, null)
private fun processStatements(statements: MutableList<IrStatement>) {
statements.transformFlat {
@@ -171,7 +185,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
val result = IrCompositeImpl(receiverResult.startOffset, expression.endOffset, unitType)
val receiverValue = receiverResult.statements.last() as IrExpression
val tmp = makeTempVar(receiverResult.type)
val irVar = JsIrBuilder.buildVar(tmp, receiverValue)
val irVar = JsIrBuilder.buildVar(tmp, receiverValue, receiverResult.type)
val setValue = valueResult.statements.last() as IrExpression
result.statements += receiverResult.statements.run { subList(0, lastIndex) }
result.statements += irVar
@@ -249,7 +263,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
val newLoopCondition = newCondition.statements.last() as IrExpression
val breakCond = JsIrBuilder.buildCall(context.irBuiltIns.booleanNotSymbol).apply {
val breakCond = JsIrBuilder.buildCall(booleanNotSymbol).apply {
putValueArgument(0, newLoopCondition)
}
@@ -373,7 +387,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
return block
}
override fun visitTry(aTry: IrTry) = aTry.also { it.transformChildrenVoid(this); }
override fun visitTry(aTry: IrTry) = aTry.also { it.transformChildrenVoid(this) }
}
private inner class ExpressionTransformer : IrElementTransformerVoid() {
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.createParameterDeclarations
import org.jetbrains.kotlin.ir.util.isInterface
@@ -48,18 +49,21 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
// Example: for given class hierarchy
//
// class C<T> {
// fun foo(t: T) = ...
// fun foo(t: T) = ... // bridge
// }
//
// class D : C<Int> {
// override fun foo(t: Int) = impl
// override fun foo(t: Int) = impl // delegate to
// }
//
// class E : D {
// <fake override> fun foo(t: Int) // function
// }
//
// it adds method D that delegates generic calls to implementation:
//
// class D : C<Int> {
// override fun foo(t: Int) = impl
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
// class E : D {
// fun foo(t: Any?) = foo(t as Int) // bridgeDescriptorForIrFunction
// }
//
class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
@@ -125,6 +129,8 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
bridge.descriptor.returnType, bridge.descriptor.modality, function.visibility
)
bridgeDescriptorForIrFunction.isSuspend = bridge.descriptor.isSuspend
// TODO: Support offsets for debug info
val irFunction = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, bridgeDescriptorForIrFunction)
irFunction.createParameterDeclarations()
@@ -136,7 +142,10 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
}
irFunction.valueParameters.mapIndexed { i, valueParameter ->
val toTake = irFunction.valueParameters.size - if (call.descriptor.isSuspend xor irFunction.descriptor.isSuspend) 1 else 0
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
}
+irReturn(call)
@@ -147,8 +156,9 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
return irFunction
}
// TODO: get rid of Unit check
private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression =
if (argument.type.classifierOrNull == type.classifierOrNull) argument else irAs(argument, type)
if (argument.type.classifierOrNull == type.classifierOrNull || type.isUnit()) argument else irAs(argument, type)
}
// Handle for common.bridges
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
@@ -30,7 +31,7 @@ import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
class CallableReferenceLowering(val context: JsIrBackendContext) {
private data class CallableReferenceKey(
val declaration: IrFunction,
@@ -47,12 +48,26 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
private val newDeclarations = mutableListOf<IrDeclaration>()
override fun lower(irFile: IrFile) {
irFile.acceptVoid(CallableReferenceCollector())
buildClosures()
irFile.transformChildrenVoid(CallableReferenceTransformer())
irFile.declarations += newDeclarations
}
fun getReferenceCollector() = object : FileLoweringPass {
private val collector = CallableReferenceCollector()
override fun lower(irFile: IrFile) = irFile.acceptVoid(collector)
}::lower
fun getClosureBuilder() = object : FileLoweringPass {
override fun lower(irFile: IrFile) {
newDeclarations.clear()
buildClosures(irFile)
irFile.declarations += newDeclarations
}
}::lower
fun getReferenceReplacer() = object : FileLoweringPass {
private val replacer = CallableReferenceTransformer()
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(replacer)
}
}::lower
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
CallableReferenceKey(declaration, reference.dispatchReceiver != null, reference.extensionReceiver != null)
@@ -72,15 +87,30 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
}
}
private fun buildClosures() {
private fun buildClosures(irFile: IrFile) {
val declarationsSet = mutableSetOf<IrFunctionSymbol>()
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
declarationsSet += declaration.symbol
}
})
for (v in collectedReferenceMap.values) {
newDeclarations += v.accept(object : IrElementVisitor<List<IrDeclaration>, Nothing?> {
override fun visitElement(element: IrElement, data: Nothing?) = error("Unreachable execution")
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?) =
lowerKFunctionReference(expression.symbol.owner, expression)
if (expression.symbol in declarationsSet) lowerKFunctionReference(expression.symbol.owner, expression) else emptyList()
override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?) =
lowerKPropertyReference(expression.getter!!.owner, expression)
if (expression.getter in declarationsSet) lowerKPropertyReference(
expression.getter!!.owner,
expression
) else emptyList()
}, null)
}
}
@@ -67,21 +67,25 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type })
val pendingExceptionSymbol = JsSymbolBuilder.buildVar(data!!.descriptor, commonType, "\$pending\$", false)
val pendingExceptionSymbol = JsSymbolBuilder.buildVar(data!!.descriptor, commonType, "\$p", false)
val pendingExceptionDeclaration = JsIrBuilder.buildVar(pendingExceptionSymbol, type = commonType)
val pendingException = JsIrBuilder.buildGetValue(pendingExceptionSymbol)
val branches = mutableListOf<IrBranch>()
for (catch in aTry.catches) {
assert(!catch.catchParameter.isVar) { "caught exception parameter has to immutable" }
assert(!catch.catchParameter.isVar) { "caught exception parameter has to be immutable" }
val type = catch.catchParameter.type
val typeSymbol = type.classifierOrNull
val castedPendingException = if (type !is IrDynamicType)
buildImplicitCast(pendingException, type, typeSymbol!!)
else pendingException
val catchBody = catch.result.transform(object : IrElementTransformer<VariableDescriptor> {
override fun visitGetValue(expression: IrGetValue, data: VariableDescriptor) =
if (typeSymbol != null && expression.descriptor == data)
// TODO how is it good to generate implicit cast for each access?
buildImplicitCast(pendingException, type, typeSymbol)
if (expression.descriptor == data)
castedPendingException
else
expression
}, catch.parameter)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement
@@ -14,7 +15,6 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.symbols.JsSymbolBuilder
import org.jetbrains.kotlin.ir.backend.js.symbols.initialize
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.*
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
@@ -33,19 +33,29 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransformerVoid(), DeclarationContainerLoweringPass {
class SecondaryCtorLowering(val context: JsIrBackendContext) {
private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, JsIrBackendContext.SecondaryCtorPair>()
data class ConstructorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol)
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat {
if (it is IrClass) {
listOf(it) + lowerClass(it)
} else null
private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, ConstructorPair>()
fun getConstructorProcessorLowering() = object : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat {
if (it is IrClass) {
listOf(it) + lowerClass(it)
} else null
}
}
}::runOnFilePostfix
context.secondaryConstructorsMap.putAll(oldCtorToNewMap)
}
fun getConstructorRedirectorLowering() = object : DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
for (it in irDeclarationContainer.declarations) {
it.accept(CallsiteRedirectionTransformer(), null)
}
}
}::runOnFilePostfix
private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> {
val className = irClass.name.asString()
@@ -74,12 +84,10 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
// val t = Object.create(Foo.prototype);
// return Foo_init_$Init$(..., t)
// }
val newInitConstructor = createInitConstructor(declaration, constructorName, irClass.defaultType)
val newInitConstructor = createInitConstructor(declaration, irClass, constructorName, irClass.defaultType)
val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName, irClass.defaultType)
oldCtorToNewMap[declaration.symbol] =
JsIrBackendContext.SecondaryCtorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
oldCtorToNewMap[declaration.symbol] = ConstructorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
oldConstructors += declaration
newConstructors += newInitConstructor
@@ -92,7 +100,11 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return newConstructors
}
private class ThisUsageReplaceTransformer(val function: IrFunctionSymbol, val thisSymbol: IrValueParameterSymbol) :
private class ThisUsageReplaceTransformer(
val function: IrFunctionSymbol,
val newThisSymbol: IrValueSymbol,
val oldThisSymbol: IrValueSymbol?
) :
IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression = IrReturnImpl(
@@ -100,15 +112,15 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
expression.endOffset,
expression.type,
function,
IrGetValueImpl(expression.startOffset, expression.endOffset, thisSymbol.owner.type, thisSymbol)
IrGetValueImpl(expression.startOffset, expression.endOffset, newThisSymbol.owner.type, newThisSymbol)
)
override fun visitGetValue(expression: IrGetValue): IrExpression =
if (expression.descriptor.name.isSpecial && expression.descriptor.name.asString() == Namer.THIS_SPECIAL_NAME) IrGetValueImpl(
if (expression.symbol == oldThisSymbol) IrGetValueImpl(
expression.startOffset,
expression.endOffset,
expression.type,
thisSymbol,
newThisSymbol,
expression.origin
) else {
expression
@@ -117,6 +129,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
private fun createInitConstructor(
declaration: IrConstructor,
klass: IrClass,
name: String,
type: IrType
): IrSimpleFunction =
@@ -135,6 +148,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
)
val thisParam = JsIrBuilder.buildValueParameter(thisSymbol, type)
val oldThisReceiver = klass.thisReceiver?.symbol
return IrFunctionImpl(
declaration.startOffset, declaration.endOffset,
@@ -147,7 +161,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
typeParameters += declaration.typeParameters
// parent = declaration.parent
body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
transformChildrenVoid(ThisUsageReplaceTransformer(it, thisSymbol))
transformChildrenVoid(ThisUsageReplaceTransformer(it, thisSymbol, oldThisReceiver))
}
}
@@ -198,23 +212,22 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
}
class CallsiteRedirectionTransformer(val context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
inner class CallsiteRedirectionTransformer : IrElementTransformer<IrFunction?> {
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration)
override fun visitCall(expression: IrCall, ownerFunc: IrFunction?): IrElement {
super.visitCall(expression, ownerFunc)
override fun visitCall(expression: IrCall, data: IrFunction?): IrElement {
super.visitCall(expression, data)
// TODO: figure out the reason why symbol is not bound
if (expression.symbol.isBound) {
val target = expression.symbol.owner as IrFunction
val target = expression.symbol.owner
if (target is IrConstructor) {
if (!target.descriptor.isPrimary) {
val ctor = context.secondaryConstructorsMap[target.symbol]
val ctor = oldCtorToNewMap[target.symbol]
if (ctor != null) {
return redirectCall(expression, ctor.stub)
}
}
@@ -224,8 +237,8 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return expression
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, ownerFunc: IrFunction?): IrElement {
super.visitDelegatingConstructorCall(expression, ownerFunc)
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
super.visitDelegatingConstructorCall(expression, data)
val target = expression.symbol
if (target.owner.isPrimary) {
@@ -233,9 +246,9 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return expression
}
val fromPrimary = ownerFunc!! is IrConstructor
val fromPrimary = data!! is IrConstructor
// TODO: what is `deserialized` constructor?
val ctor = context.secondaryConstructorsMap[target] ?: return expression
val ctor = oldCtorToNewMap[target] ?: return expression
val newCall = redirectCall(expression, ctor.delegate)
val readThis = if (fromPrimary) {
@@ -246,7 +259,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
IrValueParameterSymbolImpl(LazyClassReceiverParameterDescriptor(target.descriptor.containingDeclaration))
)
} else {
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, ownerFunc.valueParameters.last().symbol)
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, data.valueParameters.last().symbol)
}
newCall.putValueArgument(expression.valueArgumentsCount, readThis)
@@ -0,0 +1,735 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.lower.coroutines
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.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.backend.js.symbols.JsSymbolBuilder
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.visitors.*
class SuspendState(type: IrType) {
val entryBlock: IrContainerExpression = JsIrBuilder.buildComposite(type)
val successors = mutableSetOf<SuspendState>()
var id = -1
}
data class LoopBounds(val headState: SuspendState, val exitState: SuspendState)
data class FinallyTargets(val normal: SuspendState, val fromThrow: SuspendState)
data class TryState(val tryState: SuspendState, val catchState: SuspendState, val finallyState: FinallyTargets?)
class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.entryBlock.type) {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D) = visitor.visitExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {}
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {}
}
class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrElementTransformerVoid() {
override fun visitExpression(expression: IrExpression): IrExpression {
val dispatchPoint = expression as? IrDispatchPoint
?: return super.visitExpression(expression)
return action(dispatchPoint.target)
}
}
class StateMachineBuilder(
private val suspendableNodes: MutableSet<IrElement>,
val context: JsIrBackendContext,
val function: IrFunctionSymbol,
private val rootLoop: IrLoop,
private val exceptionSymbol: IrFieldSymbol,
private val exStateSymbol: IrFieldSymbol,
private val stateSymbol: IrFieldSymbol,
thisSymbol: IrValueParameterSymbol,
private val suspendResult: IrVariableSymbol
) : IrElementVisitorVoid {
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
private val unit = context.irBuiltIns.unitType
private val nothing = context.irBuiltIns.nothingType
private val int = context.irBuiltIns.intType
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
private val thisReceiver = JsIrBuilder.buildGetValue(thisSymbol)
private var hasExceptions = false
val entryState = SuspendState(unit)
val rootExceptionTrap = buildExceptionTrapState()
private val globalExceptionSymbol =
JsSymbolBuilder.buildTempVar(function, exceptionSymbol.owner.type, "e")
lateinit var globalCatch: IrCatch
fun finalizeStateMachine() {
val unitValue = JsIrBuilder.buildGetObjectValue(
unit,
context.symbolTable.referenceClass(context.builtIns.unit)
)
globalCatch = buildGlobalCatch()
if (currentBlock.statements.lastOrNull() !is IrReturn) {
addStatement(JsIrBuilder.buildReturn(function, unitValue, nothing))
}
if (!hasExceptions) entryState.successors += rootExceptionTrap
}
private fun buildGlobalCatch(): IrCatch {
val catchVariable =
JsIrBuilder.buildVar(globalExceptionSymbol, type = exceptionSymbol.owner.type)
val block = JsIrBuilder.buildBlock(unit)
if (hasExceptions) {
val thenBlock = JsIrBuilder.buildBlock(unit)
val elseBlock = JsIrBuilder.buildBlock(unit)
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
putValueArgument(0, exceptionState())
putValueArgument(1, IrDispatchPoint(rootExceptionTrap))
}
block.statements += JsIrBuilder.buildIfElse(unit, check, thenBlock, elseBlock)
thenBlock.statements += JsIrBuilder.buildThrow(
nothing,
JsIrBuilder.buildGetValue(globalExceptionSymbol)
)
// TODO: exception table
elseBlock.statements += JsIrBuilder.buildSetField(
stateSymbol,
thisReceiver,
exceptionState(),
unit
)
elseBlock.statements += JsIrBuilder.buildSetField(
exceptionSymbol,
thisReceiver,
JsIrBuilder.buildGetValue(globalExceptionSymbol),
unit
)
} else {
block.statements += JsIrBuilder.buildThrow(
nothing,
JsIrBuilder.buildGetValue(globalExceptionSymbol)
)
}
return JsIrBuilder.buildCatch(catchVariable, block)
}
private var currentState = entryState
private var currentBlock = entryState.entryBlock
private val returnableBlockMap = mutableMapOf<IrReturnableBlockSymbol, Pair<SuspendState, IrVariableSymbol?>>()
private val catchBlockStack = mutableListOf(rootExceptionTrap)
private fun buildExceptionTrapState(): SuspendState {
val state = SuspendState(unit)
state.entryBlock.statements += JsIrBuilder.buildThrow(nothing, pendingException())
return state
}
private fun newState() {
val newState = SuspendState(unit)
doDispatch(newState)
updateState(newState)
}
private fun updateState(newState: SuspendState) {
currentState = newState
currentBlock = newState.entryBlock
}
private fun lastExpression() = currentBlock.statements.lastOrNull() as? IrExpression ?: unitValue
private fun IrContainerExpression.addStatement(statement: IrStatement) {
statements.add(statement)
}
private fun addStatement(statement: IrStatement) = currentBlock.addStatement(statement)
private fun maybeDoDispatch(target: SuspendState) {
val lastStatement = currentBlock.statements.lastOrNull()
if (lastStatement !is IrReturn && lastStatement !is IrContinue && lastStatement !is IrThrow) {
doDispatch(target)
}
}
private fun doDispatch(target: SuspendState, andContinue: Boolean = true) = doDispatchImpl(target, currentBlock, andContinue)
private fun doDispatchImpl(target: SuspendState, block: IrContainerExpression, andContinue: Boolean) {
val irDispatch = IrDispatchPoint(target)
currentState.successors.add(target)
block.addStatement(JsIrBuilder.buildSetField(stateSymbol, thisReceiver, irDispatch, unit))
if (andContinue) doContinue(block)
}
private fun doContinue(block: IrContainerExpression = currentBlock) {
block.addStatement(JsIrBuilder.buildContinue(nothing, rootLoop))
}
private fun transformLastExpression(transformer: (IrExpression) -> IrStatement) {
val expression = lastExpression()
val newStatement = transformer(expression)
currentBlock.statements.let { if (it.isNotEmpty()) it[it.lastIndex] = newStatement else it += newStatement }
}
private fun buildDispatchBlock(target: SuspendState) = JsIrBuilder.buildComposite(unit)
.also { doDispatchImpl(target, it, true) }
override fun visitElement(element: IrElement) {
if (element in suspendableNodes) {
element.acceptChildrenVoid(this)
} else {
addStatement(element as IrStatement)
}
}
private fun transformLoop(loop: IrLoop, transformer: (IrLoop, SuspendState /*head*/, SuspendState /*exit*/) -> Unit) {
if (loop !in suspendableNodes) return addStatement(loop)
newState()
val loopHeadState = currentState
val loopExitState = SuspendState(unit)
loopMap[loop] = LoopBounds(loopHeadState, loopExitState)
transformer(loop, loopHeadState, loopExitState)
loopMap.remove(loop)
updateState(loopExitState)
}
override fun visitWhileLoop(loop: IrWhileLoop) = transformLoop(loop) { l, head, exit ->
l.condition.acceptVoid(this)
transformLastExpression {
val exitCond = JsIrBuilder.buildCall(booleanNotSymbol).apply { putValueArgument(0, it) }
val irBreak = buildDispatchBlock(exit)
JsIrBuilder.buildIfElse(unit, exitCond, irBreak)
}
l.body?.acceptVoid(this)
doDispatch(head)
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop) = transformLoop(loop) { l, head, exit ->
l.body?.acceptVoid(this)
l.condition.acceptVoid(this)
transformLastExpression {
val irContinue = buildDispatchBlock(head)
JsIrBuilder.buildIfElse(unit, it, irContinue)
}
doDispatch(exit)
}
private fun processReturnableBlock(expression: IrReturnableBlock) {
if (expression !in suspendableNodes) return super.visitBlock(expression)
val exitState = SuspendState(unit)
val resultVariable = if (hasResultingValue(expression)) {
val symbol = tempVar(expression.type, "RETURNABLE_BLOCK")
addStatement(JsIrBuilder.buildVar(symbol, null, expression.type))
symbol
} else null
returnableBlockMap[expression.symbol] = Pair(exitState, resultVariable)
super.visitBlock(expression)
returnableBlockMap.remove(expression.symbol)
maybeDoDispatch(exitState)
updateState(exitState)
if (resultVariable != null) {
addStatement(JsIrBuilder.buildGetValue(resultVariable))
}
}
override fun visitBlock(expression: IrBlock) =
if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression)
private fun implicitCast(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType, toType.classifierOrFail)
override fun visitCall(expression: IrCall) {
super.visitCall(expression)
if (expression.descriptor.isSuspend) {
val result = lastExpression()
val continueState = SuspendState(unit)
val dispatch = IrDispatchPoint(continueState)
currentState.successors += continueState
transformLastExpression { JsIrBuilder.buildSetField(stateSymbol, thisReceiver, dispatch, unit) }
addStatement(JsIrBuilder.buildSetVariable(suspendResult, result, unit))
val irReturn = JsIrBuilder.buildReturn(function, JsIrBuilder.buildGetValue(suspendResult), nothing)
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(suspendResult))
putValueArgument(1, JsIrBuilder.buildCall(context.ir.symbols.coroutineSuspendedGetter))
}
val suspensionBlock = JsIrBuilder.buildBlock(unit, listOf(irReturn))
addStatement(JsIrBuilder.buildIfElse(unit, check, suspensionBlock))
doContinue()
updateState(continueState)
addStatement(implicitCast(JsIrBuilder.buildGetValue(suspendResult), expression.type))
}
}
override fun visitBreak(jump: IrBreak) {
val exitState = loopMap[jump.loop]!!.exitState
doDispatch(exitState)
}
override fun visitContinue(jump: IrContinue) {
val headState = loopMap[jump.loop]!!.headState
doDispatch(headState)
}
private fun wrap(expression: IrExpression, variable: IrVariableSymbol) =
JsIrBuilder.buildSetVariable(variable, expression, unit)
override fun visitWhen(expression: IrWhen) {
if (expression !in suspendableNodes) return addStatement(expression)
val exitState = SuspendState(expression.type)
val varSymbol: IrVariableSymbol?
val branches: List<IrBranch>
if (hasResultingValue(expression)) {
varSymbol = tempVar(expression.type, "WHEN_RESULT")
addStatement(JsIrBuilder.buildVar(varSymbol, type = expression.type))
branches = expression.branches.map {
val wrapped = wrap(it.result, varSymbol)
if (it.result in suspendableNodes) {
suspendableNodes += wrapped
}
when (it) {
is IrElseBranch -> IrElseBranchImpl(
it.startOffset,
it.endOffset,
it.condition,
wrapped
)
else /* IrBranch */ -> IrBranchImpl(
it.startOffset,
it.endOffset,
it.condition,
wrapped
)
}
}
} else {
varSymbol = null
branches = expression.branches
}
val rootState = currentState
val rootBlock = currentBlock
for (branch in branches) {
if (branch !is IrElseBranch) {
branch.condition.acceptVoid(this)
val branchBlock = JsIrBuilder.buildComposite(branch.result.type)
val elseBlock = JsIrBuilder.buildComposite(expression.type)
val dispatchState = currentState
transformLastExpression {
// TODO: make sure elseBlock is added iff it really needs
JsIrBuilder.buildIfElse(unit, it, branchBlock, elseBlock)
}
currentBlock = branchBlock
branch.result.acceptVoid(this)
if (currentBlock.statements.last() !is IrContinue) {
if (currentState !== rootState) {
doDispatch(exitState)
}
}
currentState = dispatchState
currentBlock = elseBlock
} else {
branch.result.acceptVoid(this)
if (currentBlock.statements.last() !is IrContinue) {
if (currentState !== rootState) {
doDispatch(exitState)
}
}
break
}
}
currentState = rootState
currentBlock = rootBlock
maybeDoDispatch(exitState)
updateState(exitState)
if (varSymbol != null) {
addStatement(JsIrBuilder.buildGetValue(varSymbol))
}
}
override fun visitSetVariable(expression: IrSetVariable) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { value = it } }
}
override fun visitVariable(declaration: IrVariable) {
if (declaration !in suspendableNodes) return addStatement(declaration)
declaration.acceptChildrenVoid(this)
transformLastExpression { declaration.apply { initializer = it } }
}
override fun visitGetField(expression: IrGetField) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { receiver = it } }
}
override fun visitGetClass(expression: IrGetClass) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { argument = it } }
}
private fun transformArguments(arguments: Array<IrExpression?>): Array<IrExpression?> {
var suspendableCount = arguments.fold(0) { r, n -> if (n in suspendableNodes) r + 1 else r }
val newArguments = arrayOfNulls<IrExpression>(arguments.size)
for ((i, arg) in arguments.withIndex()) {
newArguments[i] = if (arg != null && suspendableCount > 0) {
if (arg in suspendableNodes) suspendableCount--
arg.acceptVoid(this)
val tmp = tempVar(arg.type, "ARGUMENT")
transformLastExpression { JsIrBuilder.buildVar(tmp, it, it.type) }
JsIrBuilder.buildGetValue(tmp)
} else arg
}
return newArguments
}
override fun visitMemberAccess(expression: IrMemberAccessExpression) {
if (expression !in suspendableNodes) {
addExceptionEdge()
return addStatement(expression)
}
val arguments = arrayOfNulls<IrExpression>(expression.valueArgumentsCount + 2)
arguments[0] = expression.dispatchReceiver
arguments[1] = expression.extensionReceiver
for (i in 0 until expression.valueArgumentsCount) {
arguments[i + 2] = expression.getValueArgument(i)
}
val newArguments = transformArguments(arguments)
expression.dispatchReceiver = newArguments[0]
expression.extensionReceiver = newArguments[1]
for (i in 0 until expression.valueArgumentsCount) {
expression.putValueArgument(i, newArguments[i + 2])
}
addExceptionEdge()
addStatement(expression)
}
override fun visitSetField(expression: IrSetField) {
if (expression !in suspendableNodes) return addStatement(expression)
val newArguments = transformArguments(arrayOf(expression.receiver, expression.value))
val receiver = newArguments[0]
val value = newArguments[1] as IrExpression
addStatement(expression.run {
IrSetFieldImpl(
startOffset,
endOffset,
symbol,
receiver,
value,
unit,
origin,
superQualifierSymbol
)
})
}
// TODO: should it be lowered before?
override fun visitStringConcatenation(expression: IrStringConcatenation) {
assert(expression in suspendableNodes)
val arguments = arrayOfNulls<IrExpression>(expression.arguments.size)
expression.arguments.forEachIndexed { i, a -> arguments[i] = a }
val newArguments = transformArguments(arguments)
addStatement(expression.run {
IrStringConcatenationImpl(
startOffset,
endOffset,
type,
newArguments.map { it!! })
})
}
private val unitValue = JsIrBuilder.buildGetObjectValue(
unit,
context.symbolTable.referenceClass(context.builtIns.unit)
)
override fun visitReturn(expression: IrReturn) {
expression.acceptChildrenVoid(this)
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
val (exitState, varSymbol) = returnableBlockMap[expression.returnTargetSymbol]!!
if (varSymbol != null) {
transformLastExpression { JsIrBuilder.buildSetVariable(varSymbol, it, it.type) }
}
doDispatch(exitState)
} else {
transformLastExpression { expression.apply { value = it } }
}
}
private fun addExceptionEdge() {
hasExceptions = true
currentState.successors += catchBlockStack.peek()!!
}
private fun hasResultingValue(expression: IrExpression) = expression.type.run { !isUnit() && !isNothing() }
override fun visitThrow(expression: IrThrow) {
expression.acceptChildrenVoid(this)
addExceptionEdge()
transformLastExpression { expression.apply { value = it } }
}
override fun visitTry(aTry: IrTry) {
val tryState = buildTryState(aTry)
val enclosingCatch = catchBlockStack.peek()!!
catchBlockStack.push(tryState.catchState)
val finallyStateVarSymbol = tempVar(int, "FINALLY_STATE")
val exitState = SuspendState(unit)
val varSymbol = if (hasResultingValue(aTry)) tempVar(aTry.type, "TRY_RESULT") else null
if (aTry.finallyExpression != null) {
addStatement(
JsIrBuilder.buildVar(
finallyStateVarSymbol,
IrDispatchPoint(exitState), int
)
)
}
if (varSymbol != null) {
addStatement(JsIrBuilder.buildVar(varSymbol, type = aTry.type))
}
// TODO: refact it with exception table, see coroutinesInternal.kt
setupExceptionState(tryState.catchState)
val tryResult = if (varSymbol != null) {
JsIrBuilder.buildSetVariable(varSymbol, aTry.tryResult, unit).also {
if (it.value in suspendableNodes) suspendableNodes += it
}
} else aTry.tryResult
tryResult.acceptVoid(this)
if (tryState.finallyState != null) {
doDispatch(tryState.finallyState.normal)
} else {
setupExceptionState(enclosingCatch)
doDispatch(exitState)
}
addExceptionEdge()
catchBlockStack.pop()
updateState(tryState.catchState)
if (tryState.finallyState != null) {
setupExceptionState(tryState.finallyState.fromThrow)
} else {
setupExceptionState(enclosingCatch)
}
val ex = pendingException()
var rethrowNeeded = true
for (catch in aTry.catches) {
val type = catch.catchParameter.type
val initializer = if (type !is IrDynamicType) implicitCast(ex, type) else ex
val irVar = catch.catchParameter.also {
it.initializer = initializer
}
val catchResult = if (varSymbol != null) {
JsIrBuilder.buildSetVariable(varSymbol, catch.result, unit).also {
if (it.value in suspendableNodes) suspendableNodes += it
}
} else catch.result
if (type is IrDynamicType) {
rethrowNeeded = false
addStatement(irVar)
catchResult.acceptVoid(this)
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
maybeDoDispatch(exitDispatch)
} else {
val check = buildIsCheck(ex, type)
val branchBlock = JsIrBuilder.buildComposite(catchResult.type)
val elseBlock = JsIrBuilder.buildComposite(catchResult.type)
val irIf = JsIrBuilder.buildIfElse(catchResult.type, check, branchBlock, elseBlock)
val ifBlock = currentBlock
currentBlock = branchBlock
addStatement(irVar)
catchResult.acceptVoid(this)
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
maybeDoDispatch(exitDispatch)
currentBlock = ifBlock
addStatement(irIf)
currentBlock = elseBlock
}
}
if (rethrowNeeded) {
addExceptionEdge()
addStatement(JsIrBuilder.buildThrow(nothing, ex))
}
if (tryState.finallyState == null) {
currentState.successors += enclosingCatch
}
val finallyState = tryState.finallyState
if (finallyState != null) {
val throwExitState = SuspendState(unit)
updateState(finallyState.fromThrow)
tryState.tryState.successors += finallyState.fromThrow
addStatement(
JsIrBuilder.buildSetVariable(
finallyStateVarSymbol,
IrDispatchPoint(throwExitState), int
)
)
doDispatch(finallyState.normal)
updateState(finallyState.normal)
tryState.tryState.successors += finallyState.normal
setupExceptionState(enclosingCatch)
aTry.finallyExpression?.acceptVoid(this)
currentState.successors += listOf(throwExitState, exitState)
addStatement(
JsIrBuilder.buildSetField(
stateSymbol,
thisReceiver,
JsIrBuilder.buildGetValue(
finallyStateVarSymbol
),
unit
)
)
doContinue()
updateState(throwExitState)
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
addExceptionEdge()
}
updateState(exitState)
if (varSymbol != null) {
addStatement(JsIrBuilder.buildGetValue(varSymbol))
}
}
private fun setupExceptionState(target: SuspendState) {
addStatement(
JsIrBuilder.buildSetField(
exStateSymbol, thisReceiver,
IrDispatchPoint(target), unit
)
)
}
private fun exceptionState() = JsIrBuilder.buildGetField(exStateSymbol, thisReceiver)
private fun pendingException() = JsIrBuilder.buildGetField(exceptionSymbol, thisReceiver)
private fun buildTryState(aTry: IrTry) =
TryState(
currentState,
SuspendState(unit),
aTry.finallyExpression?.run {
FinallyTargets(
SuspendState(
unit
), SuspendState(unit)
)
}
)
private fun buildIsCheck(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(
context.irBuiltIns.booleanType,
IrTypeOperator.INSTANCEOF,
value,
toType,
toType.classifierOrNull!!
)
private fun tempVar(type: IrType, name: String? = null) =
JsSymbolBuilder.buildTempVar(function, type, name)
}
@@ -0,0 +1,943 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.lower.coroutines
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.getFunction
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.createOverriddenDescriptor
import org.jetbrains.kotlin.backend.common.lower.SymbolWithIrBuilder
import org.jetbrains.kotlin.backend.common.lower.copyAsValueParameter
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.DFS
internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLoweringPass {
private object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL")
private object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL")
private val builtCoroutines = mutableMapOf<FunctionDescriptor, BuiltCoroutine>()
private val suspendLambdas = mutableMapOf<FunctionDescriptor, IrFunctionReference>()
override fun lower(irFile: IrFile) {
markSuspendLambdas(irFile)
buildCoroutines(irFile)
transformCallableReferencesToSuspendLambdas(irFile)
}
private fun buildCoroutines(irFile: IrFile) {
irFile.declarations.transformFlat(::tryTransformSuspendFunction)
irFile.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.declarations.transformFlat(::tryTransformSuspendFunction)
}
})
}
private fun tryTransformSuspendFunction(element: IrElement) =
if (element is IrFunction && element.descriptor.isSuspend && element.descriptor.modality != Modality.ABSTRACT)
transformSuspendFunction(element, suspendLambdas[element.descriptor])
else null
private fun markSuspendLambdas(irElement: IrElement) {
irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
expression.acceptChildrenVoid(this)
val descriptor = expression.descriptor
if (descriptor.isSuspend)
suspendLambdas.put(descriptor, expression)
}
})
}
private fun transformCallableReferencesToSuspendLambdas(irElement: IrElement) {
irElement.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
val descriptor = expression.descriptor
if (!descriptor.isSuspend)
return expression
val coroutine = builtCoroutines[descriptor]
?: throw Error("Non-local callable reference to suspend lambda: $descriptor")
val constructorParameters = coroutine.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 {
expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
}
}
}
})
}
private sealed class SuspendFunctionKind {
object NO_SUSPEND_CALLS : SuspendFunctionKind()
class DELEGATING(val delegatingCall: IrCall) : SuspendFunctionKind()
object NEEDS_STATE_MACHINE : SuspendFunctionKind()
}
private fun transformSuspendFunction(irFunction: IrFunction, functionReference: IrFunctionReference?): List<IrDeclaration>? {
val suspendFunctionKind = getSuspendFunctionKind(irFunction)
return when (suspendFunctionKind) {
is SuspendFunctionKind.NO_SUSPEND_CALLS -> {
null // No suspend function calls - just an ordinary function.
}
is SuspendFunctionKind.DELEGATING -> { // Calls another suspend function at the end.
removeReturnIfSuspendedCallAndSimplifyDelegatingCall(
irFunction, suspendFunctionKind.delegatingCall)
null // No need in state machine.
}
is SuspendFunctionKind.NEEDS_STATE_MACHINE -> {
val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation.
if (suspendLambdas.contains(irFunction.descriptor)) // 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: IrFunction): SuspendFunctionKind {
if (suspendLambdas.contains(irFunction.descriptor))
return SuspendFunctionKind.NEEDS_STATE_MACHINE // Suspend lambdas always need coroutine implementation.
val body = irFunction.body
?: return SuspendFunctionKind.NO_SUSPEND_CALLS
var numberOfSuspendCalls = 0
body.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitCall(expression: IrCall) {
expression.acceptChildrenVoid(this)
if (expression.descriptor.isSuspend)
++numberOfSuspendCalls
}
})
// It is important to optimize the case where there is only one suspend call and it is the last statement
// because we don't need to build a fat coroutine class in that case.
// This happens a lot in practise because of suspend functions with default arguments.
// TODO: use TailRecursionCallsCollector.
val lastStatement = (body as IrBlockBody).statements.lastOrNull()
val lastCall = when (lastStatement) {
is IrCall -> lastStatement
is IrReturn -> {
var value: IrElement = lastStatement
/*
* Check if matches this pattern:
* block/return {
* block/return {
* .. suspendCall()
* }
* }
*/
loop@while (true) {
when {
value is IrBlock && value.statements.size == 1 -> value = value.statements.first()
value is IrReturn -> value = value.value
else -> break@loop
}
}
value as? IrCall
}
else -> null
}
val suspendCallAtEnd = lastCall != null && lastCall.descriptor.isSuspend // Suspend call.
return when {
numberOfSuspendCalls == 0 -> SuspendFunctionKind.NO_SUSPEND_CALLS
numberOfSuspendCalls == 1
&& suspendCallAtEnd -> SuspendFunctionKind.DELEGATING(
lastCall!!
)
else -> SuspendFunctionKind.NEEDS_STATE_MACHINE
}
}
private val symbols = context.ir.symbols
private val unit = context.run { symbolTable.referenceClass(builtIns.unit) }
private val getContinuationSymbol =
context.run {
val f = getInternalFunctions("getContinuation")
symbolTable.referenceSimpleFunction(f.single())
}
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
private val returnIfSuspendedDescriptor = context.getInternalFunctions("returnIfSuspended").single()
private fun removeReturnIfSuspendedCallAndSimplifyDelegatingCall(irFunction: IrFunction, delegatingCall: IrCall) {
val returnValue =
if (delegatingCall.descriptor.original == returnIfSuspendedDescriptor)
delegatingCall.getValueArgument(0)!!
else delegatingCall
context.createIrBuilder(irFunction.symbol).run {
val statements = (irFunction.body as IrBlockBody).statements
val lastStatement = statements.last()
assert (lastStatement == delegatingCall || lastStatement is IrReturn) { "Unexpected statement $lastStatement" }
statements[statements.size - 1] = irReturn(returnValue)
}
}
private fun buildCoroutine(irFunction: IrFunction, functionReference: IrFunctionReference?): IrClass {
val descriptor = irFunction.descriptor
val coroutine = CoroutineBuilder(irFunction, functionReference).build()
builtCoroutines.put(descriptor, coroutine)
if (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) {
+irReturn(
irCall(coroutine.doResumeFunction.symbol).apply {
dispatchReceiver = irCall(coroutine.coroutineConstructor.symbol).apply {
val functionParameters = irFunction.explicitParameters
functionParameters.forEachIndexed { index, argument ->
putValueArgument(index, irGet(argument))
}
putValueArgument(functionParameters.size,
irCall(getContinuationSymbol, getContinuationSymbol.owner.returnType, listOf(irFunction.returnType)))
}
putValueArgument(0, irGetObject(unit)) // value
putValueArgument(1, irNull()) // exception
})
}
}
return coroutine.coroutineClass
}
private class BuiltCoroutine(val coroutineClass: IrClass,
val coroutineConstructor: IrConstructor,
val doResumeFunction: IrFunction)
private var coroutineId = 0
private inner class CoroutineBuilder(val irFunction: IrFunction, val functionReference: IrFunctionReference?) {
private val functionParameters = irFunction.explicitParameters
private val boundFunctionParameters = functionReference?.getArgumentsWithIr()?.map { it.first }
private val unboundFunctionParameters = boundFunctionParameters?.let { functionParameters - it }
private lateinit var suspendResult: IrVariable
private lateinit var suspendState: IrVariable
private lateinit var dataArgument: IrValueParameter
private lateinit var exceptionArgument: IrValueParameter
private lateinit var coroutineClassDescriptor: ClassDescriptorImpl
private lateinit var coroutineClass: IrClassImpl
private lateinit var coroutineClassThis: IrValueParameter
private lateinit var argumentToPropertiesMap: Map<ParameterDescriptor, IrField>
private val coroutineImplSymbol = symbols.coroutineImpl
private val coroutineImplConstructorSymbol = coroutineImplSymbol.constructors.single()
private val coroutineImplClassDescriptor = coroutineImplSymbol.descriptor
private val create1Function = coroutineImplSymbol.owner.simpleFunctions()
.single { it.name.asString() == "create" && it.valueParameters.size == 1 }
private val create1CompletionParameter = create1Function.valueParameters[0]
private val coroutineImplLabelFieldSymbol = coroutineImplSymbol.getPropertyField("label")!!
// private val coroutineImplResultFieldSymbol = coroutineImplSymbol.getPropertyField("pendingResult")!!
private val coroutineImplExceptionFieldSymbol = coroutineImplSymbol.getPropertyField("pendingException")!!
private val coroutineImplExceptionStateFieldSymbol = coroutineImplSymbol.getPropertyField("exceptionState")!!
private val coroutineConstructors = mutableListOf<IrConstructor>()
private var exceptionTrapId = -1
fun build(): BuiltCoroutine {
val superTypes = mutableListOf<IrType>(coroutineImplSymbol.owner.defaultType)
var suspendFunctionClass: IrClass? = null
var functionClass: IrClass? = null
var suspendFunctionClassTypeArguments: List<IrType>? = null
var functionClassTypeArguments: List<IrType>? = null
if (unboundFunctionParameters != null) {
// Suspend lambda inherits SuspendFunction.
val numberOfParameters = unboundFunctionParameters.size
suspendFunctionClass = context.suspendFunctions[numberOfParameters].owner
val unboundParameterTypes = unboundFunctionParameters.map { it.type }
suspendFunctionClassTypeArguments = unboundParameterTypes + irFunction.returnType
superTypes += suspendFunctionClass.typeWith(suspendFunctionClassTypeArguments)
functionClass = context.functions[numberOfParameters + 1].owner
val continuationType = continuationClassSymbol.typeWith(irFunction.returnType)
functionClassTypeArguments = unboundParameterTypes + continuationType + context.irBuiltIns.anyNType
superTypes += functionClass.typeWith(functionClassTypeArguments)
}
coroutineClassDescriptor = ClassDescriptorImpl(
/* containingDeclaration = */ irFunction.descriptor.containingDeclaration,
/* name = */ "${irFunction.descriptor.name}\$${coroutineId++}".synthesizedName,
/* modality = */ Modality.FINAL,
/* kind = */ ClassKind.CLASS,
/* superTypes = */ superTypes.map { it.toKotlinType() },
/* source = */ SourceElement.NO_SOURCE,
/* isExternal = */ false,
/* storageManager = */ LockBasedStorageManager.NO_LOCKS
).also {
it.initialize(stub("coroutine class"), stub("coroutine class constructors"), null)
}
coroutineClass = IrClassImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
descriptor = coroutineClassDescriptor
)
coroutineClass.parent = irFunction.parent
coroutineClass.createParameterDeclarations()
coroutineClassThis = coroutineClass.thisReceiver!!
val overriddenMap = mutableMapOf<CallableMemberDescriptor, CallableMemberDescriptor>()
val constructors = mutableSetOf<ClassConstructorDescriptor>()
val coroutineConstructorBuilder = createConstructorBuilder()
constructors.add(coroutineConstructorBuilder.symbol.descriptor)
coroutineConstructorBuilder.initialize()
val doResumeFunction = coroutineImplSymbol.owner.simpleFunctions()
.single { it.name.asString() == "doResume" }
val doResumeMethodBuilder = createDoResumeMethodBuilder(doResumeFunction, coroutineClass)
doResumeMethodBuilder.initialize()
overriddenMap += doResumeFunction.descriptor to doResumeMethodBuilder.symbol.descriptor
var coroutineFactoryConstructorBuilder: SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>? = null
var createMethodBuilder: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>? = null
var invokeMethodBuilder: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>? = null
if (functionReference != null) {
// Suspend lambda - create factory methods.
coroutineFactoryConstructorBuilder = createFactoryConstructorBuilder(boundFunctionParameters!!)
constructors.add(coroutineFactoryConstructorBuilder.symbol.descriptor)
val createFunctionDescriptor = coroutineImplClassDescriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("create"), NoLookupLocation.FROM_BACKEND)
.atMostOne { it.valueParameters.size == unboundFunctionParameters!!.size + 1 }
createMethodBuilder = createCreateMethodBuilder(
unboundArgs = unboundFunctionParameters!!,
superFunctionDescriptor = createFunctionDescriptor,
coroutineConstructor = coroutineConstructorBuilder.ir,
coroutineClass = coroutineClass)
createMethodBuilder.initialize()
if (createFunctionDescriptor != null)
overriddenMap += createFunctionDescriptor to createMethodBuilder.symbol.descriptor
val invokeFunctionDescriptor = functionClass!!.descriptor
.getFunction("invoke", functionClassTypeArguments!!.map { it.toKotlinType() })
val suspendInvokeFunctionDescriptor = suspendFunctionClass!!.descriptor
.getFunction("invoke", suspendFunctionClassTypeArguments!!.map { it.toKotlinType() })
invokeMethodBuilder = createInvokeMethodBuilder(
suspendFunctionInvokeFunctionDescriptor = suspendInvokeFunctionDescriptor,
functionInvokeFunctionDescriptor = invokeFunctionDescriptor,
createFunction = createMethodBuilder.ir,
doResumeFunction = doResumeMethodBuilder.ir,
coroutineClass = coroutineClass)
}
coroutineClass.addChild(coroutineConstructorBuilder.ir)
coroutineConstructors += coroutineConstructorBuilder.ir
coroutineFactoryConstructorBuilder?.let {
it.initialize()
coroutineClass.addChild(it.ir)
coroutineConstructors += it.ir
}
createMethodBuilder?.let {
coroutineClass.addChild(it.ir)
}
invokeMethodBuilder?.let {
it.initialize()
coroutineClass.addChild(it.ir)
}
coroutineClass.addChild(doResumeMethodBuilder.ir)
coroutineClass.setSuperSymbolsAndAddFakeOverrides(superTypes)
setupExceptionState()
return BuiltCoroutine(
coroutineClass = coroutineClass,
coroutineConstructor = coroutineFactoryConstructorBuilder?.ir
?: coroutineConstructorBuilder.ir,
doResumeFunction = doResumeMethodBuilder.ir
)
}
private fun createConstructorBuilder()
= object : SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
override fun buildSymbol() = IrConstructorSymbolImpl(
ClassConstructorDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* isPrimary = */ false,
/* source = */ SourceElement.NO_SOURCE
)
)
private lateinit var constructorParameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
constructorParameters = (
functionParameters
+ coroutineImplConstructorSymbol.owner.valueParameters[0] // completion.
).mapIndexed { index, parameter ->
val parameterDescriptor = parameter.descriptor.copyAsValueParameter(descriptor, index)
parameter.copy(parameterDescriptor)
}
descriptor.initialize(
constructorParameters.map { it.descriptor as ValueParameterDescriptor },
Visibilities.PUBLIC
)
descriptor.returnType = coroutineClassDescriptor.defaultType
}
override fun buildIr(): IrConstructor {
// Save all arguments to fields.
argumentToPropertiesMap = functionParameters.associate {
it.descriptor to addField(it.name, it.type, false)
}
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrConstructorImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
this.valueParameters += constructorParameters
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody {
val completionParameter = valueParameters.last()
+IrDelegatingConstructorCallImpl(startOffset, endOffset,
context.irBuiltIns.unitType,
coroutineImplConstructorSymbol, coroutineImplConstructorSymbol.descriptor).apply {
putValueArgument(0, irGet(completionParameter))
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClass.symbol, context.irBuiltIns.unitType)
functionParameters.forEachIndexed { index, parameter ->
+irSetField(
irGet(coroutineClassThis),
argumentToPropertiesMap[parameter.descriptor]!!,
irGet(valueParameters[index])
)
}
}
}
}
}
private fun createFactoryConstructorBuilder(boundParams: List<IrValueParameter>)
= object : SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
override fun buildSymbol() = IrConstructorSymbolImpl(
ClassConstructorDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* isPrimary = */ false,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var constructorParameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
constructorParameters = boundParams.mapIndexed { index, parameter ->
val parameterDescriptor = parameter.descriptor.copyAsValueParameter(descriptor, index)
parameter.copy(parameterDescriptor)
}
descriptor.initialize(
constructorParameters.map { it.descriptor as ValueParameterDescriptor },
Visibilities.PUBLIC
)
descriptor.returnType = coroutineClassDescriptor.defaultType
}
override fun buildIr(): IrConstructor {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrConstructorImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
this.valueParameters += constructorParameters
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody {
+IrDelegatingConstructorCallImpl(startOffset, endOffset, context.irBuiltIns.unitType,
coroutineImplConstructorSymbol, coroutineImplConstructorSymbol.descriptor).apply {
putValueArgument(0, irNull()) // Completion.
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClass.symbol,
context.irBuiltIns.unitType)
// Save all arguments to fields.
boundParams.forEachIndexed { index, parameter ->
+irSetField(irGet(coroutineClassThis), argumentToPropertiesMap[parameter.descriptor]!!,
irGet(valueParameters[index]))
}
}
}
}
}
private fun createCreateMethodBuilder(unboundArgs: List<IrValueParameter>,
superFunctionDescriptor: FunctionDescriptor?,
coroutineConstructor: IrConstructor,
coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* name = */ Name.identifier("create"),
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var parameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
parameters = (
unboundArgs + create1CompletionParameter
).mapIndexed { index, parameter ->
parameter.copy(parameter.descriptor.copyAsValueParameter(descriptor, index))
}
descriptor.initialize(
/* receiverParameterType = */ null,
/* dispatchReceiverParameter = */ coroutineClassDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
/* unsubstitutedValueParameters = */ parameters.map { it.descriptor as ValueParameterDescriptor },
/* unsubstitutedReturnType = */ coroutineClassDescriptor.defaultType,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE).apply {
if (superFunctionDescriptor != null) {
overriddenDescriptors += superFunctionDescriptor.overriddenDescriptors
overriddenDescriptors += superFunctionDescriptor
}
}
}
override fun buildIr(): IrSimpleFunction {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
parent = coroutineClass
this.valueParameters += parameters
this.createDispatchReceiverParameter()
val thisReceiver = this.dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
irCall(coroutineConstructor).apply {
var unboundIndex = 0
val unboundArgsSet = unboundArgs.toSet()
functionParameters.map {
if (unboundArgsSet.contains(it))
irGet(valueParameters[unboundIndex++])
else
irGetField(irGet(thisReceiver), argumentToPropertiesMap[it.descriptor]!!)
}.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
putValueArgument(functionParameters.size, irGet(valueParameters[unboundIndex]))
assert(unboundIndex == valueParameters.size - 1,
{ "Not all arguments of <create> are used" })
})
}
}
}
}
private fun createInvokeMethodBuilder(suspendFunctionInvokeFunctionDescriptor: FunctionDescriptor,
functionInvokeFunctionDescriptor: FunctionDescriptor,
createFunction: IrFunction,
doResumeFunction: IrFunction,
coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* name = */ Name.identifier("invoke"),
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var parameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
parameters = createFunction.valueParameters
// Skip completion - invoke() already has it implicitly as a suspend function.
.take(createFunction.valueParameters.size - 1)
.map { it.copy(it.descriptor.copyAsValueParameter(descriptor, it.index)) }
descriptor.initialize(
/* receiverParameterType = */ null,
/* dispatchReceiverParameter = */ coroutineClassDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
/* unsubstitutedValueParameters = */ parameters.map { it.descriptor as ValueParameterDescriptor },
/* unsubstitutedReturnType = */ irFunction.descriptor.returnType,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE).apply {
overriddenDescriptors += suspendFunctionInvokeFunctionDescriptor
overriddenDescriptors += functionInvokeFunctionDescriptor
isSuspend = true
}
}
override fun buildIr(): IrSimpleFunction {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = irFunction.returnType
parent = coroutineClass
valueParameters += parameters
this.createDispatchReceiverParameter()
val thisReceiver = this.dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
irCall(doResumeFunction).apply {
dispatchReceiver = irCall(createFunction).apply {
dispatchReceiver = irGet(thisReceiver)
valueParameters.forEachIndexed { index, parameter ->
putValueArgument(index, irGet(parameter))
}
putValueArgument(valueParameters.size,
irCall(getContinuationSymbol, getContinuationSymbol.owner.returnType, listOf(returnType)))
}
putValueArgument(0, irGetObject(symbols.unit)) // value
putValueArgument(1, irNull()) // exception
}
)
}
}
}
}
private fun addField(name: Name, type: IrType, isMutable: Boolean): IrField = createField(
irFunction.startOffset,
irFunction.endOffset,
type,
name,
isMutable,
DECLARATION_ORIGIN_COROUTINE_IMPL,
coroutineClassDescriptor
).also {
coroutineClass.addChild(it)
}
private fun createDoResumeMethodBuilder(doResumeFunction: IrFunction, coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
doResumeFunction.descriptor.createOverriddenDescriptor(coroutineClassDescriptor)
)
override fun doInitialize() { }
override fun buildIr(): IrSimpleFunction {
val originalBody = irFunction.body!!
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
val function = IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = context.irBuiltIns.anyNType
parent = coroutineClass
this.createDispatchReceiverParameter()
doResumeFunction.valueParameters.mapIndexedTo(this.valueParameters) { index, it ->
it.copy(descriptor.valueParameters[index])
}
}
dataArgument = function.valueParameters[0]
exceptionArgument = function.valueParameters[1]
suspendResult = JsIrBuilder.buildVar(IrVariableSymbolImpl(
IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor,
name = "suspendResult".synthesizedName,
outType = context.builtIns.nullableAnyType,
isMutable = true
)
), JsIrBuilder.buildGetValue(dataArgument.symbol), context.irBuiltIns.anyNType)
suspendState = JsIrBuilder.buildVar(IrVariableSymbolImpl(
IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor,
name = "suspendState".synthesizedName,
outType = coroutineImplLabelFieldSymbol.owner.type.toKotlinType(),
isMutable = true
)
), type = coroutineImplLabelFieldSymbol.owner.type)
val body =
(originalBody as IrBlockBody).run {
IrBlockImpl(
startOffset,
endOffset,
context.irBuiltIns.unitType,
STATEMENT_ORIGIN_COROUTINE_IMPL,
statements
)
}
buildStateMachine(body, function)
return function
}
}
private fun setupExceptionState() {
for (it in coroutineConstructors) {
(it.body as? IrBlockBody)?.run {
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
statements += JsIrBuilder.buildSetField(coroutineImplExceptionStateFieldSymbol, receiver, id, context.irBuiltIns.unitType)
}
}
}
private fun buildStateMachine(body: IrBlock, function: IrFunction) {
val unit = context.irBuiltIns.unitType
val switch = IrWhenImpl(body.startOffset, body.endOffset, unit,
COROUTINE_SWITCH
)
val rootTry = IrTryImpl(body.startOffset, body.endOffset, unit).apply {
tryResult = switch
}
val rootLoop = IrDoWhileLoopImpl(
body.startOffset,
body.endOffset,
unit,
COROUTINE_ROOT_LOOP,
rootTry,
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
)
val suspendableNodes = mutableSetOf<IrElement>()
val loweredBody =
collectSuspendableNodes(body, suspendableNodes, context, function)
val thisReceiver = (function.dispatchReceiverParameter as IrValueParameter).symbol
val stateMachineBuilder = StateMachineBuilder(
suspendableNodes,
context,
function.symbol,
rootLoop,
coroutineImplExceptionFieldSymbol,
coroutineImplExceptionStateFieldSymbol,
coroutineImplLabelFieldSymbol,
thisReceiver,
suspendResult.symbol
)
loweredBody.acceptVoid(stateMachineBuilder)
stateMachineBuilder.finalizeStateMachine()
rootTry.catches += stateMachineBuilder.globalCatch
val visited = mutableSetOf<SuspendState>()
val sortedStates = DFS.topologicalOrder(listOf(stateMachineBuilder.entryState), { it.successors }, { visited.add(it) })
sortedStates.withIndex().forEach { it.value.id = it.index }
fun buildDispatch(target: SuspendState) = target.run {
assert(id >= 0)
JsIrBuilder.buildInt(context.irBuiltIns.intType, id)
}
val eqeqeqInt = context.irBuiltIns.eqeqeqSymbol
for (state in sortedStates) {
val condition = JsIrBuilder.buildCall(eqeqeqInt).apply {
putValueArgument(0, JsIrBuilder.buildGetField(coroutineImplLabelFieldSymbol, JsIrBuilder.buildGetValue(thisReceiver)))
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id))
}
switch.branches += IrBranchImpl(state.entryBlock.startOffset, state.entryBlock.endOffset, condition, state.entryBlock)
}
val irResultDeclaration = suspendResult
rootLoop.transform(DispatchPointTransformer(::buildDispatch), null)
exceptionTrapId = stateMachineBuilder.rootExceptionTrap.id
val functionBody = IrBlockBodyImpl(function.startOffset, function.endOffset, listOf(irResultDeclaration, rootLoop))
function.body = functionBody
val liveLocals = computeLivenessAtSuspensionPoints(functionBody).values.flatten().toSet()
val localToPropertyMap = mutableMapOf<IrValueSymbol, IrFieldSymbol>()
var localCounter = 0
// TODO: optimize by using the same property for different locals.
liveLocals.forEach {
if (it != suspendState && it != suspendResult) {
localToPropertyMap.getOrPut(it.symbol) {
addField(Name.identifier("${it.name}${localCounter++}"), it.type, (it as? IrVariable)?.isVar ?: false).symbol
}
}
}
irFunction.explicitParameters.forEach {
localToPropertyMap.getOrPut(it.symbol) {
argumentToPropertiesMap.getValue(it.descriptor).symbol
}
}
function.transform(
LiveLocalsTransformer(
localToPropertyMap,
JsIrBuilder.buildGetValue(
thisReceiver
),
unit
), null)
}
private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> {
// TODO: data flow analysis.
// Just save all visible for now.
val result = mutableMapOf<IrCall, List<IrValueDeclaration>>()
body.acceptChildrenVoid(object : VariablesScopeTracker() {
override fun visitCall(expression: IrCall) {
if (!expression.descriptor.isSuspend) return super.visitCall(expression)
expression.acceptChildrenVoid(this)
val visibleVariables = mutableListOf<IrValueDeclaration>()
scopeStack.forEach { visibleVariables += it }
result.put(expression, visibleVariables)
}
})
return result
}
}
private open class VariablesScopeTracker: IrElementVisitorVoid {
protected val scopeStack = mutableListOf<MutableSet<IrVariable>>(mutableSetOf())
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitContainerExpression(expression: IrContainerExpression) {
if (!expression.isTransparentScope)
scopeStack.push(mutableSetOf())
super.visitContainerExpression(expression)
if (!expression.isTransparentScope)
scopeStack.pop()
}
override fun visitCatch(aCatch: IrCatch) {
scopeStack.push(mutableSetOf())
super.visitCatch(aCatch)
scopeStack.pop()
}
override fun visitVariable(declaration: IrVariable) {
super.visitVariable(declaration)
scopeStack.peek()!!.add(declaration)
}
}
}
@@ -0,0 +1,174 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.lower.coroutines
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.ir.IrElement
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.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.visitors.*
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
object COROUTINE_SWITCH : IrStatementOriginImpl("COROUTINE_SWITCH")
open class SuspendableNodesCollector(protected val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
protected var hasSuspendableChildren = false
override fun visitElement(element: IrElement) {
val current = hasSuspendableChildren
hasSuspendableChildren = false
element.acceptChildrenVoid(this)
if (hasSuspendableChildren) {
suspendableNodes += element
}
hasSuspendableChildren = hasSuspendableChildren || current
}
override fun visitCall(expression: IrCall) {
super.visitCall(expression)
if (expression.descriptor.isSuspend) {
suspendableNodes += expression
hasSuspendableChildren = true
}
}
}
fun collectSuspendableNodes(
body: IrBlock,
suspendableNodes: MutableSet<IrElement>,
context: JsIrBackendContext,
function: IrFunction
): IrBlock {
// 1st: mark suspendable loops and tries
body.acceptVoid(SuspendableNodesCollector(suspendableNodes))
// 2nd: mark inner terminators
val terminatorsCollector = SuspendedTerminatorsCollector(suspendableNodes)
body.acceptVoid(terminatorsCollector)
if (terminatorsCollector.shouldFinalliesBeLowered) {
val finallyLower = FinallyBlocksLowering(context, context.dynamicType)
function.body = IrBlockBodyImpl(body.startOffset, body.endOffset, body.statements)
function.transform(finallyLower, null)
val newBody = function.body as IrBlockBody
function.body = null
suspendableNodes.clear()
val newBlock = JsIrBuilder.buildBlock(body.type, newBody.statements)
return collectSuspendableNodes(newBlock, suspendableNodes, context, function)
}
return body
}
class SuspendedTerminatorsCollector(suspendableNodes: MutableSet<IrElement>) : SuspendableNodesCollector(suspendableNodes) {
var shouldFinalliesBeLowered = false
override fun visitBreakContinue(jump: IrBreakContinue) {
if (jump.loop in suspendableNodes) {
suspendableNodes.add(jump)
hasSuspendableChildren = true
}
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
}
private val tryStack = mutableListOf<IrTry>()
private val tryLoopStack = mutableListOf<IrStatement>()
private fun pushTry(aTry: IrTry) {
tryStack.push(aTry)
tryLoopStack.push(aTry)
}
private fun popTry() {
tryLoopStack.pop()
tryStack.pop()
}
private fun pushLoop(loop: IrLoop) {
tryLoopStack.push(loop)
}
private fun popLoop() {
tryLoopStack.pop()
}
override fun visitLoop(loop: IrLoop) {
pushLoop(loop)
super.visitLoop(loop)
popLoop()
}
override fun visitTry(aTry: IrTry) {
pushTry(aTry)
super.visitTry(aTry)
popTry()
}
override fun visitReturn(expression: IrReturn) {
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
super.visitReturn(expression)
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
suspendableNodes.add(expression)
hasSuspendableChildren = true
}
}
}
class LiveLocalsTransformer(
private val localMap: Map<IrValueSymbol, IrFieldSymbol>,
private val receiver: IrExpression,
private val unitType: IrType
) :
IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val field = localMap[expression.symbol] ?: return expression
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver, origin) }
}
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
expression.transformChildrenVoid(this)
val field = localMap[expression.symbol] ?: return expression
return expression.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, value, unitType, origin) }
}
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid(this)
val field = localMap[declaration.symbol] ?: return declaration
val initializer = declaration.initializer
return if (initializer != null) {
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, initializer, unitType) }
} else {
JsIrBuilder.buildComposite(declaration.type)
}
}
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.OperatorNameConventions
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression {
@@ -158,7 +159,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val arguments = translateCallArguments(expression, context)
if (dispatchReceiver != null &&
dispatchReceiver.type.isFunctionTypeOrSubtype() && symbol.owner.name == OperatorNameConventions.INVOKE
dispatchReceiver.type.isFunctionTypeOrSubtype() && symbol.descriptor.name == OperatorNameConventions.INVOKE && !symbol.descriptor.isSuspend
) {
return JsInvocation(jsDispatchReceiver!!, arguments)
}
@@ -6,12 +6,14 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.COROUTINE_SWITCH
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.constructedClass
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.*
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement {
@@ -81,9 +83,40 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
}
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsStatement {
if (expression.origin == COROUTINE_SWITCH) return toSwitch(expression, context)
return expression.toJsNode(this, context, ::JsIf) ?: JsEmpty
}
private fun toSwitch(expression: IrWhen, context: JsGenerationContext): JsStatement {
var expr: IrExpression? = null
val cases = expression.branches.map {
val body = it.result
val id = if (it is IrElseBranch) null else {
val call = it.condition as IrCall
expr = call.getValueArgument(0) as IrExpression
call.getValueArgument(1)
}
Pair(id, body)
}
val exprTransformer = IrElementToJsExpressionTransformer()
val jsExpr = expr!!.accept(exprTransformer, context)
return JsSwitch(jsExpr, cases.map { (id, body) ->
val jsId = id?.accept(exprTransformer, context)
val jsBody = body.accept(this, context).asBlock()
val case: JsSwitchMember
if (jsId == null) {
case = JsDefault()
} else {
case = JsCase().also { it.caseExpression = jsId }
}
case.also { it.statements += jsBody.statements }
})
}
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
val label = context.getNameForLoop(loop)
@@ -94,7 +127,8 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null?
val label = context.getNameForLoop(loop)
val loopStatement = JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
val loopStatement =
JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.onlyIf
import org.jetbrains.kotlin.backend.common.utils.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
@@ -166,7 +168,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
irClass.superTypes.mapNotNull {
val symbol = it.classifierOrFail
// TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal
if (symbol.isInterface && !symbol.isEffectivelyExternal()) JsNameRef(context.getNameForSymbol(symbol)) else null
if (symbol.isInterface && !irClass.defaultType.isBuiltinFunctionalTypeOrSubtype() && !symbol.isEffectivelyExternal()) JsNameRef(context.getNameForSymbol(symbol)) else null
}
)
)
@@ -132,6 +132,17 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val value = args[2]
JsInvocation(JsNameRef(Namer.KPROPERTY_SET, reference), listOf(receiver, value))
}
add(intrinsics.jsGetContinuation) { _, context: JsGenerationContext ->
context.continuation
}
add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext ->
val contextGetter = backendContext.coroutineGetContext
val getterName = context.getNameForSymbol(contextGetter)
val continuation = context.continuation
JsInvocation(JsNameRef(getterName, continuation))
}
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -49,6 +50,9 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForSymbol(it.symbol)) }
functionParams.forEach { function.addParameter(it) }
if (declaration.descriptor.isSuspend) {
function.addParameter(context.currentScope.declareName(Namer.CONTINUATION))
}
return function
}
@@ -57,11 +61,15 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
val transformer = IrElementToJsExpressionTransformer()
val size = expression.valueArgumentsCount
return (0 until size).mapTo(ArrayList(size)) {
val arguments = (0 until size).mapTo(ArrayList(size)) {
val argument = expression.getValueArgument(it)
val result = argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
result
}
return if (expression.descriptor.isSuspend) {
arguments + context.continuation
} else arguments
}
val IrFunction.isStatic: Boolean get() = this.dispatchReceiverParameter == null
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
@@ -44,4 +45,20 @@ class JsGenerationContext {
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, this)
fun getNameForLoop(loop: IrLoop): JsName? = staticContext.getNameForLoop(loop, this)
val continuation
get() = if (isCoroutineDoResume()) {
JsThisRef()
} else {
if (currentFunction!!.descriptor.isSuspend) {
JsNameRef(currentScope.declareName(Namer.CONTINUATION))
} else {
getNameForSymbol(currentFunction.valueParameters.last().symbol).makeRef()
}
}
private fun isCoroutineDoResume(): Boolean {
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
return staticContext.doResumeFunctionSymbol in overriddenSymbols
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.JsClassModel
@@ -24,6 +25,9 @@ class JsStaticContext(
val intrinsics = JsIntrinsicTransformers(backendContext)
// TODO: use IrSymbol instead of JsName
val classModels = mutableMapOf<JsName, JsClassModel>()
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
val doResumeFunctionSymbol = coroutineImplDeclaration.declarations
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "doResume" }.symbol
val initializerBlock = JsGlobalBlock()
@@ -60,6 +60,8 @@ object Namer {
val DEFINE_INLINE_FUNCTION = "defineInlineFunction"
val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default"
val CONTINUATION = "\$cont"
val JS_ERROR = JsNameRef("Error")
val JS_OBJECT = JsNameRef("Object")
@@ -60,7 +60,7 @@ class SimpleNameGenerator : NameGenerator {
nameDeclarator = context.currentScope::declareFreshName
}
is PropertyDescriptor -> {
nameBuilder.append(descriptor.name.identifier)
nameBuilder.append(descriptor.name.asString())
if (descriptor.visibility == Visibilities.PRIVATE || descriptor.modality != Modality.FINAL) {
nameBuilder.append('$')
nameBuilder.append(getNameForDescriptor(descriptor.containingDeclaration, context))
@@ -183,8 +183,12 @@ fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression, getterSymbol:
origin = IrStatementOrigin.GET_PROPERTY
)
fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType): IrCall =
IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor)
fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType, typeArguments: List<IrType> = emptyList()): IrCall =
IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor).apply {
typeArguments.forEachIndexed { index, irType ->
this.putTypeArgument(index, irType)
}
}
fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrCall =
IrCallImpl(startOffset, endOffset, callee.owner.returnType, callee, callee.descriptor)
@@ -237,3 +241,14 @@ fun IrBuilderWithScope.irString(value: String) =
fun IrBuilderWithScope.irConcat() =
IrStringConcatenationImpl(startOffset, endOffset, context.irBuiltIns.stringType)
fun IrBuilderWithScope.irSetField(receiver: IrExpression, irField: IrField, value: IrExpression): IrExpression =
IrSetFieldImpl(
startOffset,
endOffset,
irField.symbol,
receiver = receiver,
value = value,
type = context.irBuiltIns.unitType
)
@@ -19,12 +19,13 @@ package org.jetbrains.kotlin.ir.builders
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.expressions.IrBlock
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
@@ -84,12 +85,13 @@ class IrBlockBuilder(
startOffset: Int,
endOffset: Int,
val origin: IrStatementOrigin? = null,
var resultType: IrType? = null
) : IrStatementsBuilder<IrBlock>(context, scope, startOffset, endOffset) {
var resultType: IrType? = null,
val isTransparent:Boolean = false
) : IrStatementsBuilder<IrContainerExpression>(context, scope, startOffset, endOffset) {
private val statements = ArrayList<IrStatement>()
inline fun block(body: IrBlockBuilder.() -> Unit): IrBlock {
inline fun block(body: IrBlockBuilder.() -> Unit): IrContainerExpression {
body()
return doBuild()
}
@@ -98,11 +100,11 @@ class IrBlockBuilder(
statements.add(irStatement)
}
override fun doBuild(): IrBlock {
override fun doBuild(): IrContainerExpression {
val resultType = this.resultType
?: statements.lastOrNull().safeAs<IrExpression>()?.type
?: context.irBuiltIns.unitType
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, origin)
val irBlock = if (isTransparent) IrCompositeImpl(startOffset, endOffset, resultType, origin) else IrBlockImpl(startOffset, endOffset, resultType, origin)
irBlock.statements.addAll(statements)
return irBlock
}
@@ -154,6 +156,20 @@ inline fun IrGeneratorWithScope.irBlock(
origin, resultType
).block(body)
inline fun IrGeneratorWithScope.irComposite(
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET,
origin: IrStatementOrigin? = null,
resultType: IrType? = null,
body: IrBlockBuilder.() -> Unit
): IrExpression =
IrBlockBuilder(
context, scope,
startOffset,
endOffset,
origin, resultType, true
).block(body)
inline fun IrGeneratorWithScope.irBlockBody(
startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET,
body: IrBlockBodyBuilder.() -> Unit
@@ -8,6 +8,9 @@ package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
@@ -75,6 +78,17 @@ fun IrType.toKotlinType(): KotlinType {
}
}
fun IrType.getClass(): IrClass? =
(this.classifierOrNull as? IrClassSymbol)?.owner
fun IrClassSymbol.createType(hasQuestionMark: Boolean, arguments: List<IrTypeArgument>): IrSimpleType =
IrSimpleTypeImpl(
this,
hasQuestionMark,
arguments,
emptyList()
)
private fun makeKotlinType(
classifier: IrClassifierSymbol,
arguments: List<IrTypeArgument>,
@@ -95,6 +109,20 @@ fun ClassifierDescriptor.toIrType(hasQuestionMark: Boolean = false): IrType {
return IrSimpleTypeImpl(defaultType, symbol, hasQuestionMark, listOf(), listOf())
}
val IrTypeParameter.defaultType: IrType get() = symbol.owner.defaultType
fun IrClassifierSymbol.typeWith(vararg arguments: IrType): IrSimpleType = typeWith(arguments.toList())
fun IrClassifierSymbol.typeWith(arguments: List<IrType>): IrSimpleType =
IrSimpleTypeImpl(
this,
false,
arguments.map { makeTypeProjection(it, Variance.INVARIANT) },
emptyList()
)
fun IrClass.typeWith(arguments: List<IrType>) = this.symbol.typeWith(arguments)
fun KotlinType.toIrType(): IrType? {
if (isDynamic()) return IrDynamicTypeImpl(this, listOf(), Variance.INVARIANT)
@@ -113,6 +141,7 @@ fun KotlinType.toIrType(): IrType? {
return IrSimpleTypeImpl(this, symbol, isMarkedNullable, arguments, annotations)
}
// TODO: this function creates unbound symbol which is the great source of problems
private fun ClassifierDescriptor.getSymbol(): IrClassifierSymbol = when (this) {
is ClassDescriptor -> IrClassSymbolImpl(this)
is TypeParameterDescriptor -> IrTypeParameterSymbolImpl(this)
@@ -6,20 +6,30 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toIrType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.types.KotlinType
/**
* Binds the arguments explicitly represented in the IR to the parameters of the accessed function.
@@ -168,6 +178,21 @@ fun IrFunction.createParameterDeclarations() {
}
}
fun IrClass.createParameterDeclarations() {
thisReceiver = IrValueParameterImpl(
startOffset, endOffset,
IrDeclarationOrigin.INSTANCE_RECEIVER,
descriptor.thisAsReceiverParameter,
this.symbol.typeWith(this.typeParameters.map { it.defaultType }),
null
).also { valueParameter ->
valueParameter.parent = this
}
assert(typeParameters.isEmpty())
assert(descriptor.declaredTypeParameters.isEmpty())
}
//fun IrClass.createParameterDeclarations() {
// descriptor.thisAsReceiverParameter.let {
// thisReceiver = IrValueParameterImpl(
@@ -311,6 +336,40 @@ fun IrValueParameter.copy(newDescriptor: ParameterDescriptor): IrValueParameter
)
}
fun createField(
startOffset: Int,
endOffset: Int,
type: IrType,
name: Name,
isMutable: Boolean,
origin: IrDeclarationOrigin,
owner: ClassDescriptor
): IrField {
val descriptor = PropertyDescriptorImpl.create(
/* containingDeclaration = */ owner,
/* annotations = */ Annotations.EMPTY,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE,
/* isVar = */ isMutable,
/* name = */ name,
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE,
/* lateInit = */ false,
/* isConst = */ false,
/* isExpect = */ false,
/* isActual = */ false,
/* isExternal = */ false,
/* isDelegated = */ false
).apply {
initialize(null, null)
val receiverType: KotlinType? = null
setType(type.toKotlinType(), emptyList(), owner.thisAsReceiverParameter, receiverType)
}
return IrFieldImpl(startOffset, endOffset, origin, descriptor, type)
}
fun IrFunction.createDispatchReceiverParameter() {
assert(this.dispatchReceiverParameter == null)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
class A {
inner class Inner(val result: Int)
}
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
private fun <T> upcast(value: T): T = value
fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -6,7 +6,6 @@
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
import kotlin.test.assertEquals
class A(val w: String) {
suspend fun String.ext(): String = suspendCoroutineUninterceptedOrReturn {
@@ -6,7 +6,6 @@
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
import kotlin.test.assertEquals
class A(val w: String) {
suspend fun Long.ext(): String = suspendCoroutineUninterceptedOrReturn {
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR, JVM_IR
// IGNORE_BACKEND: JVM_IR
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -3,6 +3,7 @@
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST
// DONT_RUN_GENERATED_CODE: JS_IR
import helpers.*
import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
// FILE: common.kt
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
// FILE: common.kt

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