Minor. Simplify suspension point processing

Move type coercing logic from transformer to ExpressionCodegen.

For suspension points it's always guaranteed that values
returned from suspension points are always can be successfully casted
to appropriate type, because they cannot return SUSPENDED marker
(it gets processed via CoroutineTransformerMethodVisitor).
This commit is contained in:
Denis Zharkov
2016-12-18 11:48:57 +03:00
parent 5dbc04abbb
commit 1076397530
2 changed files with 9 additions and 61 deletions
@@ -2781,11 +2781,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
StackValue result = callable.invokeMethodWithArguments(resolvedCall, receiver, this);
if (CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, bindingContext)) {
// Suspension points should behave like they leave actual values on stack, while real methods return VOID
return new OperationStackValue(getSuspensionBoxedReturnTypeByResolvedCall(resolvedCall), ((OperationStackValue) result).getLambda());
}
if (bindingContext.get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall()) != null) {
// Suspend function's calls inside another suspend function should behave like they leave values of correct type,
// while real methods return java/lang/Object.
@@ -2907,11 +2902,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
if (isSuspensionPoint) {
v.tconst(getSuspensionBoxedReturnTypeByResolvedCall(resolvedCall));
v.invokestatic(
CoroutineCodegenUtilKt.COROUTINE_MARKER_OWNER,
CoroutineCodegenUtilKt.BEFORE_SUSPENSION_POINT_MARKER_NAME,
"(Ljava/lang/Class;)V", false);
"()V", false
);
}
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
@@ -2930,23 +2925,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
@NotNull
private Type getSuspensionBoxedReturnTypeByResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
assert resolvedCall.getResultingDescriptor() instanceof SimpleFunctionDescriptor
: "Suspension point resolved call should be built on SimpleFunctionDescriptor";
FunctionDescriptor initialSignature =
((SimpleFunctionDescriptor) resolvedCall.getResultingDescriptor())
.getUserData(CoroutineCodegenUtilKt.INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION);
assert initialSignature != null : "Initial signature must be not null for suspension point";
KotlinType returnType = initialSignature.getReturnType();
assert returnType != null : "Return type of suspension point should not be null";
return typeMapper.mapType(TypeUtils.makeNullable(returnType));
}
@NotNull
private CallGenerator getOrCreateCallGenerator(
@NotNull CallableDescriptor descriptor,
@@ -162,36 +162,12 @@ class CoroutineTransformerMethodVisitor(
}
AFTER_SUSPENSION_POINT_MARKER_NAME -> {
assert(beforeSuspensionPointMarkerStack.isNotEmpty()) { "Expected non-empty stack beforeSuspensionPointMarkerStack" }
val beforeSuspensionPointMarker = beforeSuspensionPointMarkerStack.pop()
assert(beforeSuspensionPointMarker.previous is LdcInsnNode) {
"Instruction before BEFORE_SUSPENSION_POINT_MARKER_NAME should be LDC with expected return type, " +
"but ${beforeSuspensionPointMarker.previous} was found"
}
val fakeReturnValueInsns = mutableListOf<AbstractInsnNode>()
val suspensionPoint = SuspensionPoint(
suspensionCallBegin = beforeSuspensionPointMarker,
suspensionCallEnd = methodInsn,
fakeReturnValueInsns = fakeReturnValueInsns,
returnType = (beforeSuspensionPointMarker.previous as LdcInsnNode).cst as Type)
suspensionPoints.add(suspensionPoint)
// Drop type info from marker
methodNode.instructions.remove(beforeSuspensionPointMarker.previous)
beforeSuspensionPointMarker.desc = "()V"
assert(suspensionPoint.returnType != Type.VOID_TYPE) { "Suspension point can't be VOID" }
// Checkcast only is needed to tell analyzer what type exactly should be returned from suspension point
// This type is used when for restoring spilled variables into fields
val checkCast = TypeInsnNode(Opcodes.CHECKCAST, suspensionPoint.returnType.internalName)
methodNode.instructions.insertBefore(
suspensionPoint.suspensionCallEnd, checkCast
suspensionPoints.add(
SuspensionPoint(
suspensionCallBegin = beforeSuspensionPointMarkerStack.pop(),
suspensionCallEnd = methodInsn
)
)
fakeReturnValueInsns.add(checkCast)
}
}
}
@@ -224,7 +200,7 @@ class CoroutineTransformerMethodVisitor(
for (suspension in suspensionPoints) {
val suspensionCallBegin = suspension.suspensionCallBegin
val suspensionCallEnd = suspension.suspensionCallEnd
assert(frames[suspensionCallEnd.next.index()]?.stackSize == (if (suspension.returnType.sort == Type.VOID) 0 else 1)) {
assert(frames[suspensionCallEnd.next.index()]?.stackSize == 1) {
"Stack should be spilled before suspension call"
}
@@ -338,9 +314,6 @@ class CoroutineTransformerMethodVisitor(
)
)
// Drop default value that purpose was to simulate returned value by suspension method
suspension.fakeReturnValueInsns.forEach { methodNode.instructions.remove(it) }
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
dup()
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
@@ -371,7 +344,6 @@ class CoroutineTransformerMethodVisitor(
load(1, AsmTypes.OBJECT_TYPE)
visitLabel(continuationLabelAfterLoadedResult.label)
StackValue.coerce(AsmTypes.OBJECT_TYPE, suspension.returnType, this)
})
}
@@ -500,9 +472,7 @@ private class SuspensionPoint(
// INVOKESTATIC beforeSuspensionMarker
val suspensionCallBegin: AbstractInsnNode,
// INVOKESTATIC afterSuspensionMarker
val suspensionCallEnd: AbstractInsnNode,
val fakeReturnValueInsns: List<AbstractInsnNode>,
val returnType: Type
val suspensionCallEnd: AbstractInsnNode
) {
lateinit var tryCatchBlocksContinuationLabel: LabelNode
}