[JS IR] Fix boxing/unboxing of inline classes in coroutine scope
- don't box/unbox when value is known to be an inline class - add unbox state when coroutine resumed - correctly handle suspension in case of inline class - add tests
This commit is contained in:
+1
-8
@@ -44,14 +44,7 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
|
||||
|
||||
val actualType = when (this) {
|
||||
is IrConstructorCall -> symbol.owner.returnType
|
||||
is IrCall -> {
|
||||
val function = this.symbol.owner
|
||||
if (function.let { it is IrSimpleFunction && it.isSuspend }) {
|
||||
irBuiltIns.anyNType
|
||||
} else {
|
||||
function.realOverrideTarget.returnType
|
||||
}
|
||||
}
|
||||
is IrCall -> symbol.owner.realOverrideTarget.returnType
|
||||
is IrGetField -> this.symbol.owner.type
|
||||
|
||||
is IrTypeOperatorCall -> {
|
||||
|
||||
+21
-2
@@ -16,13 +16,16 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
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.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
|
||||
|
||||
class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunctionsLowering<JsIrBackendContext>(ctx) {
|
||||
|
||||
@@ -215,6 +218,20 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression {
|
||||
val fromType = (delegatingCall as? IrCall)?.symbol?.owner?.returnType ?: delegatingCall.type
|
||||
if (!needUnboxingOrUnit(fromType, expectedType)) return delegatingCall
|
||||
|
||||
val ctx = this@JsSuspendFunctionsLowering.context
|
||||
return irComposite(resultType = fromType) {
|
||||
val tmp = createTmpVariable(delegatingCall, irType = fromType)
|
||||
val coroutineSuspended = irCall(ctx.coroutineSuspendGetter)
|
||||
val condition = irEqeqeq(irGet(tmp), coroutineSuspended)
|
||||
+irIfThen(fromType, condition, irReturn(irReinterpretCast(irGet(tmp), expectedType)))
|
||||
+irGet(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
override fun IrBlockBodyBuilder.generateCoroutineStart(invokeSuspendFunction: IrFunction, receiver: IrExpression) {
|
||||
val dispatchReceiverVar = createTmpVariable(receiver, irType = receiver.type)
|
||||
+irCall(coroutineImplResultSymbolSetter).apply {
|
||||
@@ -225,8 +242,10 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
dispatchReceiver = irGet(dispatchReceiverVar)
|
||||
putValueArgument(0, irNull())
|
||||
}
|
||||
+irReturn(irCall(invokeSuspendFunction.symbol).apply {
|
||||
val call = irCall(invokeSuspendFunction.symbol).apply {
|
||||
dispatchReceiver = irGet(dispatchReceiverVar)
|
||||
})
|
||||
}
|
||||
val functionReturnType = scope.scopeOwnerSymbol.assertedCast<IrSimpleFunctionSymbol> { "Expected function symbol" }.owner.returnType
|
||||
+irReturn(generateDelegatedCall(functionReturnType, call))
|
||||
}
|
||||
}
|
||||
+35
-5
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
import org.jetbrains.kotlin.ir.util.getInlinedClass
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
|
||||
class SuspendState(type: IrType) {
|
||||
@@ -69,6 +70,7 @@ class StateMachineBuilder(
|
||||
|
||||
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
||||
private val unit = context.irBuiltIns.unitType
|
||||
private val anyN = context.irBuiltIns.anyNType
|
||||
private val nothing = context.irBuiltIns.nothingType
|
||||
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
|
||||
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
|
||||
@@ -278,16 +280,22 @@ class StateMachineBuilder(
|
||||
override fun visitBlock(expression: IrBlock) =
|
||||
if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression)
|
||||
|
||||
private fun implicitCast(value: IrExpression, toType: IrType) =
|
||||
JsIrBuilder.buildImplicitCast(value, toType)
|
||||
private fun implicitCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildImplicitCast(value, toType)
|
||||
private fun reinterpretCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildReinterpretCast(value, toType)
|
||||
|
||||
override fun visitCall(expression: IrCall) {
|
||||
super.visitCall(expression)
|
||||
|
||||
if (expression.isSuspend) {
|
||||
val result = lastExpression()
|
||||
val expectedType = expression.symbol.owner.returnType
|
||||
val isInlineClassExpected = expectedType.getInlinedClass() != null
|
||||
val continueState = SuspendState(unit)
|
||||
val dispatch = IrDispatchPoint(continueState)
|
||||
val unboxState = if (isInlineClassExpected) SuspendState(unit) else null
|
||||
|
||||
val dispatch = IrDispatchPoint(unboxState ?: continueState)
|
||||
|
||||
if (unboxState != null) currentState.successors += unboxState
|
||||
|
||||
currentState.successors += continueState
|
||||
|
||||
@@ -298,7 +306,7 @@ class StateMachineBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
addStatement(JsIrBuilder.buildSetVariable(suspendResult, result, unit))
|
||||
addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(result, anyN), unit))
|
||||
|
||||
val irReturn = JsIrBuilder.buildReturn(function, JsIrBuilder.buildGetValue(suspendResult), nothing)
|
||||
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
|
||||
@@ -308,13 +316,35 @@ class StateMachineBuilder(
|
||||
|
||||
val suspensionBlock = JsIrBuilder.buildBlock(unit, listOf(irReturn))
|
||||
addStatement(JsIrBuilder.buildIfElse(unit, check, suspensionBlock))
|
||||
|
||||
if (isInlineClassExpected) {
|
||||
addStatement(JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).apply {
|
||||
dispatchReceiver = thisReceiver
|
||||
putValueArgument(0, IrDispatchPoint(continueState))
|
||||
})
|
||||
}
|
||||
|
||||
doContinue()
|
||||
|
||||
unboxState?.let { buildUnboxingState(it, continueState, expectedType) }
|
||||
|
||||
updateState(continueState)
|
||||
addStatement(implicitCast(JsIrBuilder.buildGetValue(suspendResult), expression.type))
|
||||
val functionReturnType = expression.symbol.owner.returnType
|
||||
addStatement(reinterpretCast(JsIrBuilder.buildGetValue(suspendResult), functionReturnType))
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildUnboxingState(unboxState: SuspendState, continueState: SuspendState, expectedType: IrType) {
|
||||
unboxState.successors += continueState
|
||||
updateState(unboxState)
|
||||
val result = JsIrBuilder.buildGetValue(suspendResult)
|
||||
val tmp = JsIrBuilder.buildVar(expectedType, function.owner, name = "unboxed", initializer = result)
|
||||
addStatement(tmp)
|
||||
addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(JsIrBuilder.buildGetValue(tmp.symbol), anyN), anyN))
|
||||
|
||||
doDispatch(continueState)
|
||||
}
|
||||
|
||||
override fun visitBreak(jump: IrBreak) {
|
||||
val exitState = loopMap[jump.loop]!!.exitState
|
||||
doDispatch(exitState)
|
||||
|
||||
+7
-2
@@ -6,8 +6,6 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||
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.ir.JsIrBuilder
|
||||
@@ -19,6 +17,8 @@ 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.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.getInlinedClass
|
||||
import org.jetbrains.kotlin.ir.visitors.*
|
||||
|
||||
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
|
||||
@@ -108,4 +108,9 @@ class LiveLocalsTransformer(
|
||||
JsIrBuilder.buildComposite(declaration.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun needUnboxingOrUnit(fromType: IrType, toType: IrType): Boolean {
|
||||
return (fromType.getInlinedClass() == null && toType.getInlinedClass() != null) ||
|
||||
(fromType.isUnit() && !toType.isUnit())
|
||||
}
|
||||
Reference in New Issue
Block a user