[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:
@@ -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>
|
||||
|
||||
|
||||
+318
@@ -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
|
||||
)
|
||||
}
|
||||
+25
@@ -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)
|
||||
|
||||
|
||||
+3
@@ -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")
|
||||
|
||||
|
||||
+73
-16
@@ -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
|
||||
}
|
||||
+35
-21
@@ -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() {
|
||||
|
||||
+17
-7
@@ -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
|
||||
|
||||
+40
-10
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -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)
|
||||
|
||||
+44
-31
@@ -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)
|
||||
|
||||
+735
@@ -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)
|
||||
}
|
||||
+943
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+174
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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)
|
||||
}
|
||||
|
||||
+35
-1
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
+11
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -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
|
||||
|
||||
+17
@@ -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")
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
compiler/testData/codegen/box/callableReference/function/innerConstructorFromTopLevelOneStringArg.kt
Vendored
-1
@@ -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
@@ -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
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -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 {
|
||||
|
||||
-1
@@ -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
-1
@@ -1,4 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR, JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_COROUTINES
|
||||
// COMMON_COROUTINES_TEST
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
-1
@@ -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
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
-1
@@ -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
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
-1
@@ -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
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
-1
@@ -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
Reference in New Issue
Block a user