Support coroutine lambda parameters in JVM backend
This commit is contained in:
@@ -50,8 +50,9 @@ class CoroutineCodegen(
|
|||||||
parentCodegen: MemberCodegen<*>,
|
parentCodegen: MemberCodegen<*>,
|
||||||
classBuilder: ClassBuilder,
|
classBuilder: ClassBuilder,
|
||||||
private val continuationSuperType: KotlinType,
|
private val continuationSuperType: KotlinType,
|
||||||
private val controllerType: KotlinType
|
private val coroutineLambdaDescriptor: FunctionDescriptor
|
||||||
) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) {
|
) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) {
|
||||||
|
private val controllerType = coroutineLambdaDescriptor.controllerTypeIfCoroutine!!
|
||||||
|
|
||||||
override fun generateClosureBody() {
|
override fun generateClosureBody() {
|
||||||
v.newField(
|
v.newField(
|
||||||
@@ -64,6 +65,14 @@ class CoroutineCodegen(
|
|||||||
COROUTINE_LABEL_FIELD_NAME,
|
COROUTINE_LABEL_FIELD_NAME,
|
||||||
Type.INT_TYPE.descriptor, null, null)
|
Type.INT_TYPE.descriptor, null, null)
|
||||||
|
|
||||||
|
for (parameter in funDescriptor.valueParameters) {
|
||||||
|
v.newField(
|
||||||
|
OtherOrigin(parameter),
|
||||||
|
Opcodes.ACC_PRIVATE or Opcodes.ACC_FINAL,
|
||||||
|
COROUTINE_LAMBDA_PARAMETER_PREFIX + parameter.index,
|
||||||
|
typeMapper.mapType(parameter.type).descriptor, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
val classDescriptor = closureContext.contextDescriptor
|
val classDescriptor = closureContext.contextDescriptor
|
||||||
|
|
||||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||||
@@ -77,6 +86,15 @@ class CoroutineCodegen(
|
|||||||
|
|
||||||
with(codegen.v) {
|
with(codegen.v) {
|
||||||
setLabelValue(LABEL_VALUE_BEFORE_FIRST_SUSPENSION)
|
setLabelValue(LABEL_VALUE_BEFORE_FIRST_SUSPENSION)
|
||||||
|
|
||||||
|
for (parameter in funDescriptor.valueParameters) {
|
||||||
|
// 0 - this
|
||||||
|
// 1 - controller
|
||||||
|
val parametersIndexShift = 2
|
||||||
|
AsmUtil.genAssignInstanceFieldFromParam(
|
||||||
|
parameter.getFieldInfoForCoroutineLambdaParameter(), parametersIndexShift + parameter.index, this)
|
||||||
|
}
|
||||||
|
|
||||||
load(0, AsmTypes.OBJECT_TYPE)
|
load(0, AsmTypes.OBJECT_TYPE)
|
||||||
areturn(AsmTypes.OBJECT_TYPE)
|
areturn(AsmTypes.OBJECT_TYPE)
|
||||||
}
|
}
|
||||||
@@ -119,12 +137,30 @@ class CoroutineCodegen(
|
|||||||
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
||||||
|
codegen.initializeCoroutineParameters()
|
||||||
super.doGenerateBody(codegen, signature)
|
super.doGenerateBody(codegen, signature)
|
||||||
generateExceptionHandlingBlock(codegen)
|
generateExceptionHandlingBlock(codegen)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ExpressionCodegen.initializeCoroutineParameters() {
|
||||||
|
for (parameter in coroutineLambdaDescriptor.valueParameters) {
|
||||||
|
val mappedType = typeMapper.mapType(parameter.type)
|
||||||
|
val newIndex = myFrameMap.enter(parameter, mappedType)
|
||||||
|
|
||||||
|
StackValue.field(parameter.getFieldInfoForCoroutineLambdaParameter(), generateThisOrOuter(context.thisDescriptor, false))
|
||||||
|
.put(mappedType, v)
|
||||||
|
v.store(newIndex, mappedType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ValueParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
||||||
|
FieldInfo.createForHiddenField(
|
||||||
|
typeMapper.mapClass(closureContext.thisDescriptor),
|
||||||
|
typeMapper.mapType(returnType!!),
|
||||||
|
COROUTINE_LAMBDA_PARAMETER_PREFIX + index)
|
||||||
|
|
||||||
private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) {
|
private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) {
|
||||||
val handleExceptionFunction =
|
val handleExceptionFunction =
|
||||||
controllerType.memberScope.getContributedFunctions(
|
controllerType.memberScope.getContributedFunctions(
|
||||||
@@ -132,7 +168,7 @@ class CoroutineCodegen(
|
|||||||
?: return
|
?: return
|
||||||
|
|
||||||
val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) =
|
val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) =
|
||||||
createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, (context as ClosureContext).coroutineDescriptor!!)
|
createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, coroutineLambdaDescriptor)
|
||||||
|
|
||||||
codegen.tempVariables.put(fakeExceptionExpression, StackValue.operation(AsmTypes.OBJECT_TYPE) {
|
codegen.tempVariables.put(fakeExceptionExpression, StackValue.operation(AsmTypes.OBJECT_TYPE) {
|
||||||
codegen.v.invokestatic(COROUTINE_MARKER_OWNER, HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME, "()Ljava/lang/Object;", false)
|
codegen.v.invokestatic(COROUTINE_MARKER_OWNER, HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME, "()Ljava/lang/Object;", false)
|
||||||
@@ -228,7 +264,9 @@ class CoroutineCodegen(
|
|||||||
descriptorWithContinuationReturnType, originalCoroutineLambdaDescriptor, expressionCodegen, state.typeMapper),
|
descriptorWithContinuationReturnType, originalCoroutineLambdaDescriptor, expressionCodegen, state.typeMapper),
|
||||||
FunctionGenerationStrategy.FunctionDefault(state, declaration), expressionCodegen.parentCodegen, classBuilder,
|
FunctionGenerationStrategy.FunctionDefault(state, declaration), expressionCodegen.parentCodegen, classBuilder,
|
||||||
continuationSupertype,
|
continuationSupertype,
|
||||||
originalCoroutineLambdaDescriptor.controllerTypeIfCoroutine!!)
|
originalCoroutineLambdaDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$"
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
class Controller {
|
||||||
|
suspend fun suspendHere(v: String, x: Continuation<String>) {
|
||||||
|
x.resume(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.(String, Int) -> Continuation<Unit>) {
|
||||||
|
c(Controller(), "OK", 56).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun noinline(l: () -> String) = l()
|
||||||
|
inline fun inline(l: () -> String) = l()
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder { s, i ->
|
||||||
|
result = suspendHere(s + "#" + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK#56") return "fail 1: $result"
|
||||||
|
|
||||||
|
builder { s, i ->
|
||||||
|
result = suspendHere(noinline {
|
||||||
|
s + "#" + i
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK#56") return "fail 2: $result"
|
||||||
|
|
||||||
|
builder { s, i ->
|
||||||
|
result = suspendHere(inline {
|
||||||
|
s + "#" + i
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "OK#56") return "fail 3: $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -4171,6 +4171,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaParameters.kt")
|
||||||
|
public void testLambdaParameters() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lambdaParameters.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("manualContinuationImpl.kt")
|
@TestMetadata("manualContinuationImpl.kt")
|
||||||
public void testManualContinuationImpl() throws Exception {
|
public void testManualContinuationImpl() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user