Support new coroutine convention in JVM backend
This commit is contained in:
committed by
Stanislav Erokhin
parent
5a6b4a3224
commit
6649f64e9f
-8
@@ -18,12 +18,9 @@ package org.jetbrains.kotlin.backend.common
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
|
|
||||||
val SUSPEND_WITH_CURRENT_CONTINUATION_NAME = Name.identifier("suspendWithCurrentContinuation")
|
val SUSPEND_WITH_CURRENT_CONTINUATION_NAME = Name.identifier("suspendWithCurrentContinuation")
|
||||||
|
|
||||||
@@ -32,8 +29,3 @@ fun FunctionDescriptor.getBuiltInSuspendWithCurrentContinuation() =
|
|||||||
?.getMemberScope()
|
?.getMemberScope()
|
||||||
?.getContributedFunctions(SUSPEND_WITH_CURRENT_CONTINUATION_NAME, NoLookupLocation.FROM_BACKEND)
|
?.getContributedFunctions(SUSPEND_WITH_CURRENT_CONTINUATION_NAME, NoLookupLocation.FROM_BACKEND)
|
||||||
?.singleOrNull()
|
?.singleOrNull()
|
||||||
|
|
||||||
fun KotlinType.findInterceptResume() = findOperatorInController(this, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME)
|
|
||||||
|
|
||||||
fun findOperatorInController(controllerType: KotlinType, name: Name): SimpleFunctionDescriptor? =
|
|
||||||
controllerType.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).singleOrNull { it.isOperator }
|
|
||||||
@@ -457,9 +457,18 @@ public class AsmUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
|
public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
|
||||||
|
return genAssignInstanceFieldFromParam(info, index, iv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int genAssignInstanceFieldFromParam(
|
||||||
|
FieldInfo info,
|
||||||
|
int index,
|
||||||
|
InstructionAdapter iv,
|
||||||
|
int ownerIndex
|
||||||
|
) {
|
||||||
assert !info.isStatic();
|
assert !info.isStatic();
|
||||||
Type fieldType = info.getFieldType();
|
Type fieldType = info.getFieldType();
|
||||||
iv.load(0, info.getOwnerType());//this
|
iv.load(ownerIndex, info.getOwnerType());//this
|
||||||
iv.load(index, fieldType); //param
|
iv.load(index, fieldType); //param
|
||||||
iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
|
iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
|
||||||
index += fieldType.getSize();
|
index += fieldType.getSize();
|
||||||
|
|||||||
@@ -77,12 +77,12 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
private final FunctionDescriptor functionReferenceTarget;
|
private final FunctionDescriptor functionReferenceTarget;
|
||||||
private final FunctionGenerationStrategy strategy;
|
private final FunctionGenerationStrategy strategy;
|
||||||
protected final CalculatedClosure closure;
|
protected final CalculatedClosure closure;
|
||||||
private final Type asmType;
|
protected final Type asmType;
|
||||||
private final int visibilityFlag;
|
protected final int visibilityFlag;
|
||||||
private final boolean shouldHaveBoundReferenceReceiver;
|
private final boolean shouldHaveBoundReferenceReceiver;
|
||||||
|
|
||||||
private Method constructor;
|
private Method constructor;
|
||||||
private Type superClassAsmType;
|
protected Type superClassAsmType;
|
||||||
|
|
||||||
public ClosureCodegen(
|
public ClosureCodegen(
|
||||||
@NotNull GenerationState state,
|
@NotNull GenerationState state,
|
||||||
@@ -406,7 +406,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Method generateConstructor() {
|
protected Method generateConstructor() {
|
||||||
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
|
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
|
||||||
|
|
||||||
Type[] argTypes = fieldListToTypeArray(args);
|
Type[] argTypes = fieldListToTypeArray(args);
|
||||||
@@ -430,9 +430,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
|
|
||||||
String superClassConstructorDescriptor;
|
String superClassConstructorDescriptor;
|
||||||
if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) || superClassAsmType.equals(COROUTINE_IMPL)) {
|
if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) || superClassAsmType.equals(COROUTINE_IMPL)) {
|
||||||
int arity = funDescriptor.getValueParameters().size();
|
int arity = calculateArity();
|
||||||
if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
|
|
||||||
if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
|
|
||||||
iv.iconst(arity);
|
iv.iconst(arity);
|
||||||
if (shouldHaveBoundReferenceReceiver) {
|
if (shouldHaveBoundReferenceReceiver) {
|
||||||
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType);
|
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType);
|
||||||
@@ -455,6 +453,13 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected int calculateArity() {
|
||||||
|
int arity = funDescriptor.getValueParameters().size();
|
||||||
|
if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
|
||||||
|
if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
|
||||||
|
return arity;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static List<FieldInfo> calculateConstructorParameters(
|
public static List<FieldInfo> calculateConstructorParameters(
|
||||||
@NotNull KotlinTypeMapper typeMapper,
|
@NotNull KotlinTypeMapper typeMapper,
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
||||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
|
||||||
@@ -78,7 +78,6 @@ import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
|||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
|
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
|
||||||
import org.jetbrains.kotlin.resolve.coroutine.CoroutineReceiverValue;
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||||
@@ -128,6 +127,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
public final FrameMap myFrameMap;
|
public final FrameMap myFrameMap;
|
||||||
public final MethodContext context;
|
public final MethodContext context;
|
||||||
private final Type returnType;
|
private final Type returnType;
|
||||||
|
private final Type boxedReturnTypeForCoroutine;
|
||||||
|
|
||||||
private final CodegenStatementVisitor statementVisitor = new CodegenStatementVisitor(this);
|
private final CodegenStatementVisitor statementVisitor = new CodegenStatementVisitor(this);
|
||||||
private final MemberCodegen<?> parentCodegen;
|
private final MemberCodegen<?> parentCodegen;
|
||||||
@@ -160,11 +160,37 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
this.v = new InstructionAdapter(mv);
|
this.v = new InstructionAdapter(mv);
|
||||||
this.myFrameMap = frameMap;
|
this.myFrameMap = frameMap;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.returnType = returnType;
|
|
||||||
|
FunctionDescriptor descriptorForCoroutine = getOriginalLambdaDescriptorForCoroutine(context);
|
||||||
|
if (descriptorForCoroutine != null && descriptorForCoroutine.getReturnType() != null) {
|
||||||
|
this.returnType = typeMapper.mapReturnType(descriptorForCoroutine);
|
||||||
|
this.boxedReturnTypeForCoroutine = getBoxedReturnTypeForCoroutine(descriptorForCoroutine);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.boxedReturnTypeForCoroutine = null;
|
||||||
|
}
|
||||||
|
|
||||||
this.parentCodegen = parentCodegen;
|
this.parentCodegen = parentCodegen;
|
||||||
this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state);
|
this.tailRecursionCodegen = new TailRecursionCodegen(context, this, this.v, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Type getBoxedReturnTypeForCoroutine(FunctionDescriptor descriptorForCoroutine) {
|
||||||
|
assert descriptorForCoroutine.getReturnType() != null : "Uninitialized coroutine return type";
|
||||||
|
return AsmUtil.boxType(typeMapper.mapType(descriptorForCoroutine.getReturnType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static FunctionDescriptor getOriginalLambdaDescriptorForCoroutine(MethodContext context) {
|
||||||
|
if ((context.getParentContext() instanceof ClosureContext) &&
|
||||||
|
(context.getParentContext().closure != null) &&
|
||||||
|
context.getParentContext().closure.isCoroutine()) {
|
||||||
|
return ((ClosureContext) context.getParentContext()).getCoroutineDescriptor();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static class BlockStackElement {
|
static class BlockStackElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +595,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
statements.add(condition);
|
statements.add(condition);
|
||||||
|
|
||||||
//Need to split leave task and condition cause otherwise BranchedValue optimizations wouldn't work
|
//Need to split leave task and condition cause otherwise BranchedValue optimizations wouldn't work
|
||||||
leaveTask = generateBlock((KtBlockExpression) body, statements, false, continueLabel, null);
|
leaveTask = generateBlock(statements, false, continueLabel, null);
|
||||||
conditionValue = leaveTask.getStackValue();
|
conditionValue = leaveTask.getStackValue();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1718,10 +1744,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
KotlinType captureReceiver = closure.getCaptureReceiverType();
|
KotlinType captureReceiver = closure.getCaptureReceiverType();
|
||||||
if (captureReceiver != null) {
|
if (captureReceiver != null) {
|
||||||
Type asmType = typeMapper.mapType(captureReceiver);
|
|
||||||
StackValue capturedReceiver =
|
StackValue capturedReceiver =
|
||||||
functionReferenceReceiver != null ? functionReferenceReceiver :
|
functionReferenceReceiver != null ? functionReferenceReceiver :
|
||||||
StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
|
generateExtensionReceiver(unwrapOriginalLambdaDescriptorForCoroutine(context));
|
||||||
callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
|
callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1741,14 +1766,26 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
superClass, putThis && closure.getCaptureThis() == null, callGenerator, /* functionReferenceReceiver = */ null
|
superClass, putThis && closure.getCaptureThis() == null, callGenerator, /* functionReferenceReceiver = */ null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (closure.isCoroutine()) {
|
||||||
|
// resultContinuation
|
||||||
|
v.aconst(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static CallableDescriptor unwrapOriginalLambdaDescriptorForCoroutine(@NotNull MethodContext context) {
|
||||||
|
FunctionDescriptor coroutine = getOriginalLambdaDescriptorForCoroutine(context);
|
||||||
|
if (coroutine != null) return coroutine;
|
||||||
|
return context.getFunctionDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ StackValue generateBlock(@NotNull KtBlockExpression expression, boolean isStatement) {
|
/* package */ StackValue generateBlock(@NotNull KtBlockExpression expression, boolean isStatement) {
|
||||||
if (expression.getParent() instanceof KtNamedFunction) {
|
if (expression.getParent() instanceof KtNamedFunction) {
|
||||||
// For functions end of block should be end of function label
|
// For functions end of block should be end of function label
|
||||||
return generateBlock(expression, expression.getStatements(), isStatement, null, context.getMethodEndLabel());
|
return generateBlock(expression.getStatements(), isStatement, null, context.getMethodEndLabel());
|
||||||
}
|
}
|
||||||
return generateBlock(expression, expression.getStatements(), isStatement, null, null);
|
return generateBlock(expression.getStatements(), isStatement, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -1763,7 +1800,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
private StackValueWithLeaveTask generateBlock(
|
private StackValueWithLeaveTask generateBlock(
|
||||||
@NotNull KtBlockExpression block,
|
|
||||||
@NotNull List<KtExpression> statements,
|
@NotNull List<KtExpression> statements,
|
||||||
boolean isStatement,
|
boolean isStatement,
|
||||||
@Nullable Label labelBeforeLastExpression,
|
@Nullable Label labelBeforeLastExpression,
|
||||||
@@ -1800,13 +1836,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
StackValue statementResult = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
|
StackValue statementResult = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
|
||||||
|
|
||||||
if (!iterator.hasNext()) {
|
if (!iterator.hasNext()) {
|
||||||
StackValue handleResultValue = genControllerHandleResultForLastStatementInCoroutine(block, possiblyLabeledStatement);
|
blockResult = statementResult;
|
||||||
if (handleResultValue != null) {
|
|
||||||
blockResult = handleResultValue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
blockResult = statementResult;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
statementResult.put(Type.VOID_TYPE, v);
|
statementResult.put(Type.VOID_TYPE, v);
|
||||||
@@ -1816,13 +1846,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statements.isEmpty()) {
|
if (statements.isEmpty()) {
|
||||||
StackValue handleResultValue = genControllerHandleResultForLastStatementInCoroutine(block, null);
|
blockResult = StackValue.none();
|
||||||
if (handleResultValue != null) {
|
|
||||||
blockResult = handleResultValue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
blockResult = StackValue.none();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert blockResult != null : "Block result should be initialized in the loop or the condition above";
|
assert blockResult != null : "Block result should be initialized in the loop or the condition above";
|
||||||
@@ -1841,107 +1865,22 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private StackValue genControllerHandleResultForLastStatementInCoroutine(
|
|
||||||
@NotNull KtBlockExpression block,
|
|
||||||
@Nullable KtExpression lastStatement
|
|
||||||
) {
|
|
||||||
if (!(block.getParent() instanceof KtFunctionLiteral)) return null;
|
|
||||||
KtFunctionLiteral functionLiteral = (KtFunctionLiteral) block.getParent();
|
|
||||||
|
|
||||||
return genControllerHandleResultCallIfNeeded(functionLiteral, lastStatement);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private StackValue genControllerHandleResultCallIfNeeded(@NotNull KtExpression callOwner, @Nullable KtExpression returnValue) {
|
|
||||||
ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(RETURN_HANDLE_RESULT_RESOLVED_CALL, callOwner);
|
|
||||||
|
|
||||||
if (resolvedCall != null) {
|
|
||||||
assert resolvedCall.getValueArgumentsByIndex() != null : "Arguments were not resolved for call element: " + callOwner.getText();
|
|
||||||
KtExpression argumentExpression =
|
|
||||||
resolvedCall.getValueArgumentsByIndex().get(0).getArguments().get(0).getArgumentExpression();
|
|
||||||
|
|
||||||
final StackValue putValueBeforeCall;
|
|
||||||
// This condition may be true in cases like return-statement without value or for last statement in a lambda block that
|
|
||||||
// has a type different from Unit, while 'handleResult' method accepts exactly the latter
|
|
||||||
// (see org.jetbrains.kotlin.coroutines.resolveCoroutineHandleResultCallIfNeeded for clarifications)
|
|
||||||
if (argumentExpression != returnValue) {
|
|
||||||
assert KotlinBuiltIns.isUnit(resolvedCall.getResultingDescriptor().getValueParameters().get(0).getType())
|
|
||||||
: "If handleResult argument is different from returnValue, handleResult's first parameter must accept Unit, but " +
|
|
||||||
resolvedCall.getResultingDescriptor() + " was found";
|
|
||||||
|
|
||||||
// generate last statement in the coroutine lambda
|
|
||||||
putValueBeforeCall = returnValue != null ? genStatement(returnValue) : null;
|
|
||||||
|
|
||||||
// Here 'argumentExpression' is a special fake one that used as an expression of Unit type
|
|
||||||
// when 'handleResult' call was resolved
|
|
||||||
tempVariables.put(argumentExpression, StackValue.unit());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
putValueBeforeCall = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tempVariables.put(
|
|
||||||
resolvedCall.getValueArgumentsByIndex().get(1).getArguments().get(0).getArgumentExpression(),
|
|
||||||
genCoroutineInstanceValueFromResolvedCall(resolvedCall));
|
|
||||||
|
|
||||||
|
|
||||||
final StackValue handleResultCallValue = invokeFunction(resolvedCall, StackValue.none());
|
|
||||||
if (putValueBeforeCall == null) return handleResultCallValue;
|
|
||||||
|
|
||||||
return new StackValue(handleResultCallValue.type) {
|
|
||||||
@Override
|
|
||||||
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
|
|
||||||
putValueBeforeCall.put(type, v);
|
|
||||||
handleResultCallValue.putSelector(type, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putReceiver(@NotNull InstructionAdapter v, boolean isRead) {
|
|
||||||
handleResultCallValue.putReceiver(v, isRead);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public StackValue genCoroutineInstanceValueFromResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
|
|
||||||
return getCoroutineInstanceValueByReceiver(getControllerReceiverFromResolvedCall(resolvedCall));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private StackValue getCoroutineInstanceValueByReceiver(
|
|
||||||
@NotNull ExtensionReceiver descriptor
|
|
||||||
) {
|
|
||||||
ClassDescriptor coroutineClassDescriptor =
|
|
||||||
bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, descriptor.getDeclarationDescriptor());
|
|
||||||
assert coroutineClassDescriptor != null : "Coroutine class descriptor should not be null";
|
|
||||||
return StackValue.thisOrOuter(this, coroutineClassDescriptor, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private StackValue getCoroutineInstanceValueForSuspensionPoint(@NotNull ResolvedCall<?> resolvedCall) {
|
private StackValue getCoroutineInstanceValueForSuspensionPoint(@NotNull ResolvedCall<?> resolvedCall) {
|
||||||
CoroutineReceiverValue coroutineReceiverValue =
|
CallableDescriptor enclosingSuspendLambdaForSuspensionPoint =
|
||||||
bindingContext.get(COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, resolvedCall.getCall());
|
bindingContext.get(ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, resolvedCall.getCall());
|
||||||
|
|
||||||
if (coroutineReceiverValue == null) return null;
|
if (enclosingSuspendLambdaForSuspensionPoint == null) return null;
|
||||||
|
return genCoroutineInstanceByLambda(enclosingSuspendLambdaForSuspensionPoint);
|
||||||
return getCoroutineInstanceValueByReceiver(coroutineReceiverValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ExtensionReceiver getControllerReceiverFromResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
|
@NotNull
|
||||||
ReceiverValue controllerReceiver =
|
private StackValue genCoroutineInstanceByLambda(@NotNull CallableDescriptor suspendLambda) {
|
||||||
resolvedCall.getDispatchReceiver() != null
|
ClassDescriptor suspendLambdaClassDescriptor =
|
||||||
? resolvedCall.getDispatchReceiver()
|
bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendLambda);
|
||||||
: resolvedCall.getExtensionReceiver();
|
assert suspendLambdaClassDescriptor != null : "Coroutine class descriptor should not be null";
|
||||||
|
|
||||||
assert controllerReceiver != null : "Both dispatch and extension receivers are null for handleResult/suspend to " + resolvedCall.getResultingDescriptor();
|
return StackValue.thisOrOuter(this, suspendLambdaClassDescriptor, false, false);
|
||||||
assert controllerReceiver instanceof ExtensionReceiver
|
|
||||||
: "Argument for handleResult call to " + resolvedCall.getResultingDescriptor() +
|
|
||||||
" should be a coroutine receiver parameter, but " + controllerReceiver + " found";
|
|
||||||
|
|
||||||
return (ExtensionReceiver) controllerReceiver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -2204,7 +2143,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StackValue visitReturnExpression(@NotNull final KtReturnExpression expression, StackValue receiver) {
|
public StackValue visitReturnExpression(@NotNull final KtReturnExpression expression, final StackValue receiver) {
|
||||||
return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
|
return StackValue.operation(Type.VOID_TYPE, new Function1<InstructionAdapter, Unit>() {
|
||||||
@Override
|
@Override
|
||||||
public Unit invoke(InstructionAdapter adapter) {
|
public Unit invoke(InstructionAdapter adapter) {
|
||||||
@@ -2221,13 +2160,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : ExpressionCodegen.this.returnType;
|
Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : ExpressionCodegen.this.returnType;
|
||||||
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : null;
|
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : null;
|
||||||
StackValue handleResultValue = genControllerHandleResultCallIfNeeded(expression, returnedExpression);
|
|
||||||
|
|
||||||
if (handleResultValue != null) {
|
if (returnedExpression != null && valueToReturn != null) {
|
||||||
handleResultValue.put(Type.VOID_TYPE, v);
|
|
||||||
returnType = Type.VOID_TYPE;
|
|
||||||
}
|
|
||||||
else if (returnedExpression != null && valueToReturn != null) {
|
|
||||||
putStackValue(returnedExpression, returnType, valueToReturn);
|
putStackValue(returnedExpression, returnType, valueToReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2235,9 +2169,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
|
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
|
||||||
|
|
||||||
if (isNonLocalReturn) {
|
if (isNonLocalReturn) {
|
||||||
|
if (nonLocalReturn.boxedCoroutineReturnType != null && !nonLocalReturn.boxedCoroutineReturnType.equals(returnType)) {
|
||||||
|
StackValue.coerce(nonLocalReturn.returnType, nonLocalReturn.boxedCoroutineReturnType, v);
|
||||||
|
returnType = nonLocalReturn.boxedCoroutineReturnType;
|
||||||
|
}
|
||||||
|
|
||||||
InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName);
|
InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName);
|
||||||
|
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
||||||
}
|
}
|
||||||
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
else {
|
||||||
|
emitLocalReturnInsn();
|
||||||
|
}
|
||||||
|
|
||||||
v.mark(afterReturnLabel);
|
v.mark(afterReturnLabel);
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
}
|
}
|
||||||
@@ -2270,7 +2213,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
FunctionDescriptor containingFunction =
|
FunctionDescriptor containingFunction =
|
||||||
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
|
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
|
||||||
//FIRST_FUN_LABEL to prevent clashing with existing labels
|
//FIRST_FUN_LABEL to prevent clashing with existing labels
|
||||||
return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), InlineCodegenUtil.FIRST_FUN_LABEL);
|
return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), InlineCodegenUtil.FIRST_FUN_LABEL, null);
|
||||||
} else {
|
} else {
|
||||||
//local
|
//local
|
||||||
return null;
|
return null;
|
||||||
@@ -2282,7 +2225,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
DeclarationDescriptor elementDescriptor = typeMapper.getBindingContext().get(DECLARATION_TO_DESCRIPTOR, element);
|
DeclarationDescriptor elementDescriptor = typeMapper.getBindingContext().get(DECLARATION_TO_DESCRIPTOR, element);
|
||||||
assert element != null : "Expression should be not null " + expression.getText();
|
assert element != null : "Expression should be not null " + expression.getText();
|
||||||
assert elementDescriptor != null : "Descriptor should be not null: " + element.getText();
|
assert elementDescriptor != null : "Descriptor should be not null: " + element.getText();
|
||||||
return new NonLocalReturnInfo(typeMapper.mapReturnType((CallableDescriptor) elementDescriptor), expression.getLabelName());
|
Type boxedCoroutineReturnType =
|
||||||
|
elementDescriptor instanceof AnonymousFunctionDescriptor
|
||||||
|
&& ((AnonymousFunctionDescriptor) elementDescriptor).isCoroutine()
|
||||||
|
? getBoxedReturnTypeForCoroutine((FunctionDescriptor) elementDescriptor)
|
||||||
|
: null;
|
||||||
|
return new NonLocalReturnInfo(
|
||||||
|
typeMapper.mapReturnType((CallableDescriptor) elementDescriptor), expression.getLabelName(),
|
||||||
|
boxedCoroutineReturnType
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -2307,6 +2258,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
StackValue.none().put(returnType, v);
|
StackValue.none().put(returnType, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitLocalReturnInsn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emitLocalReturnInsn() {
|
||||||
|
if (boxedReturnTypeForCoroutine != null && !boxedReturnTypeForCoroutine.equals(returnType)) {
|
||||||
|
StackValue.coerce(returnType, boxedReturnTypeForCoroutine, v);
|
||||||
|
v.areturn(boxedReturnTypeForCoroutine);
|
||||||
|
}
|
||||||
|
else {
|
||||||
v.areturn(returnType);
|
v.areturn(returnType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3098,46 +3059,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private StackValue generateExtensionReceiver(@NotNull CallableDescriptor descriptor) {
|
private StackValue generateExtensionReceiver(@NotNull CallableDescriptor descriptor) {
|
||||||
KotlinType coroutineControllerType = CoroutineUtilKt.getControllerTypeIfCoroutine(descriptor);
|
if (myFrameMap.getIndex(descriptor.getExtensionReceiverParameter()) != -1) {
|
||||||
if (coroutineControllerType != null) {
|
return StackValue.local(
|
||||||
ClassDescriptor classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, descriptor);
|
myFrameMap.getIndex(descriptor.getExtensionReceiverParameter()),
|
||||||
assert classDescriptor != null : "class descriptor for coroutine " + descriptor + " should not be null";
|
typeMapper.mapType(descriptor.getExtensionReceiverParameter())
|
||||||
|
);
|
||||||
final StackValue coroutineReceiver = StackValue.thisOrOuter(this, classDescriptor, /* isSuper =*/ false, /* castReceiver */ false);
|
|
||||||
|
|
||||||
StackValue controllerValue;
|
|
||||||
if (InlineUtil.checkNonLocalReturnUsage(
|
|
||||||
context.getFunctionDescriptor(),
|
|
||||||
descriptor, DescriptorToSourceUtils.descriptorToDeclaration(descriptor), bindingContext
|
|
||||||
)) {
|
|
||||||
// This branch should work for major part of cases and it's needed mostly for optimizations purpose
|
|
||||||
// else branch must have just the same semantics
|
|
||||||
controllerValue = StackValue.field(
|
|
||||||
FieldInfo.createForHiddenField(
|
|
||||||
AsmTypes.COROUTINE_IMPL,
|
|
||||||
AsmTypes.OBJECT_TYPE,
|
|
||||||
CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_FIELD_NAME
|
|
||||||
),
|
|
||||||
coroutineReceiver
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
controllerValue = StackValue.functionCall(AsmTypes.OBJECT_TYPE, new Function1<InstructionAdapter, Unit>() {
|
|
||||||
@Override
|
|
||||||
public Unit invoke(InstructionAdapter adapter) {
|
|
||||||
coroutineReceiver.put(AsmTypes.COROUTINE_IMPL, adapter);
|
|
||||||
adapter.invokevirtual(
|
|
||||||
AsmTypes.COROUTINE_IMPL.getInternalName(),
|
|
||||||
CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_GETTER_NAME,
|
|
||||||
"()" + AsmTypes.OBJECT_TYPE,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return StackValue.coercion(controllerValue, typeMapper.mapType(coroutineControllerType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.generateReceiver(descriptor, state, false);
|
return context.generateReceiver(descriptor, state, false);
|
||||||
@@ -4784,13 +4710,15 @@ The "returned" value of try expression with no finally is either the last expres
|
|||||||
|
|
||||||
private static class NonLocalReturnInfo {
|
private static class NonLocalReturnInfo {
|
||||||
|
|
||||||
final Type returnType;
|
private final Type returnType;
|
||||||
|
|
||||||
final String labelName;
|
private final String labelName;
|
||||||
|
private final Type boxedCoroutineReturnType;
|
||||||
|
|
||||||
private NonLocalReturnInfo(Type type, String name) {
|
private NonLocalReturnInfo(@NotNull Type type, @NotNull String name, @Nullable Type boxedCoroutineReturnType) {
|
||||||
returnType = type;
|
returnType = type;
|
||||||
labelName = name;
|
labelName = name;
|
||||||
|
this.boxedCoroutineReturnType = boxedCoroutineReturnType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,8 @@
|
|||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
import org.jetbrains.kotlin.builtins.createFunctionType
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.continuationClassDescriptor
|
import org.jetbrains.kotlin.codegen.coroutines.createJvmSuspendFunctionView
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.hasNoinlineInterceptResume
|
import org.jetbrains.kotlin.coroutines.isSuspendLambda
|
||||||
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
@@ -28,11 +27,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
|
||||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
|
||||||
import org.jetbrains.kotlin.types.Variance
|
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
|
||||||
|
|
||||||
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||||
@@ -53,19 +48,6 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
|||||||
(0..2).map { i -> createClass(kotlinJvmInternalPackage, "MutablePropertyReference$i") }
|
(0..2).map { i -> createClass(kotlinJvmInternalPackage, "MutablePropertyReference$i") }
|
||||||
}
|
}
|
||||||
|
|
||||||
val continuationOfAny: KotlinType by lazy { createNullableAnyContinuation(module) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return `Continuation<Any?>` type
|
|
||||||
*/
|
|
||||||
private fun createNullableAnyContinuation(module: ModuleDescriptor): KotlinType {
|
|
||||||
val classDescriptor = module.builtIns.continuationClassDescriptor
|
|
||||||
|
|
||||||
return TypeConstructorSubstitution.createByParametersMap(
|
|
||||||
mapOf(classDescriptor.declaredTypeParameters.single() to TypeProjectionImpl(module.builtIns.nullableAnyType))
|
|
||||||
).buildSubstitutor().substitute(classDescriptor.defaultType, Variance.INVARIANT)!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createClass(packageFragment: PackageFragmentDescriptor, name: String): ClassDescriptor =
|
private fun createClass(packageFragment: PackageFragmentDescriptor, name: String): ClassDescriptor =
|
||||||
MutableClassDescriptor(packageFragment, ClassKind.CLASS, /* isInner = */ false, /* isExternal = */ false,
|
MutableClassDescriptor(packageFragment, ClassKind.CLASS, /* isInner = */ false, /* isExternal = */ false,
|
||||||
Name.identifier(name), SourceElement.NO_SOURCE).apply {
|
Name.identifier(name), SourceElement.NO_SOURCE).apply {
|
||||||
@@ -76,33 +58,24 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
|
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
|
||||||
|
|
||||||
|
val actualFunctionDescriptor =
|
||||||
|
if (descriptor.isSuspendLambda)
|
||||||
|
createJvmSuspendFunctionView(descriptor)
|
||||||
|
else
|
||||||
|
descriptor
|
||||||
|
|
||||||
val functionType = createFunctionType(
|
val functionType = createFunctionType(
|
||||||
descriptor.builtIns,
|
descriptor.builtIns,
|
||||||
Annotations.EMPTY,
|
Annotations.EMPTY,
|
||||||
descriptor.extensionReceiverParameter?.type,
|
actualFunctionDescriptor.extensionReceiverParameter?.type,
|
||||||
ExpressionTypingUtils.getValueParametersTypes(descriptor.valueParameters),
|
ExpressionTypingUtils.getValueParametersTypes(actualFunctionDescriptor.valueParameters),
|
||||||
null,
|
null,
|
||||||
descriptor.returnType!!
|
actualFunctionDescriptor.returnType!!
|
||||||
)
|
)
|
||||||
|
|
||||||
val coroutineControllerType = descriptor.controllerTypeIfCoroutine
|
if (descriptor.isSuspendLambda) {
|
||||||
if (coroutineControllerType != null) {
|
return listOf(coroutineImplClass.defaultType, functionType)
|
||||||
val additionalType: KotlinType?
|
|
||||||
if (coroutineControllerType.hasNoinlineInterceptResume()) {
|
|
||||||
// for non-inline interceptResume we use coroutine instance as an argument for interceptRun call, i.e. it must be a Function0<Unit>
|
|
||||||
// See org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen.processInterceptResume() for details
|
|
||||||
additionalType =
|
|
||||||
createFunctionType(
|
|
||||||
descriptor.builtIns, Annotations.EMPTY,
|
|
||||||
/* recieverParameter = */ null, /* parameterTypes = */ emptyList(), /* parameterNames = */ emptyList(),
|
|
||||||
/* returnType = */ descriptor.builtIns.unitType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
additionalType = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return listOf(coroutineImplClass.defaultType, functionType) + additionalType.singletonOrEmptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return listOf(lambda.defaultType, functionType)
|
return listOf(lambda.defaultType, functionType)
|
||||||
|
|||||||
+1
-26
@@ -33,11 +33,9 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
|
||||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||||
@@ -290,31 +288,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
|||||||
classStack.push(classDescriptor);
|
classStack.push(classDescriptor);
|
||||||
nameStack.push(name);
|
nameStack.push(name);
|
||||||
|
|
||||||
KotlinType controllerTypeIfCoroutine = CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor);
|
if (CoroutineUtilKt.isSuspendLambda(functionDescriptor)) {
|
||||||
if (controllerTypeIfCoroutine != null) {
|
|
||||||
closure.setCoroutine(true);
|
closure.setCoroutine(true);
|
||||||
|
|
||||||
if (CoroutineCodegenUtilKt.hasInlineInterceptResume(controllerTypeIfCoroutine)) {
|
|
||||||
// for inline interceptResume we create a descriptor for fake lambda that must be inlined when generating interceptRun call
|
|
||||||
// See org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen.processInterceptResume() for details
|
|
||||||
AnonymousFunctionDescriptor fakeDescriptorForInlineLambda =
|
|
||||||
new AnonymousFunctionDescriptor(functionDescriptor, Annotations.Companion.getEMPTY(),
|
|
||||||
CallableMemberDescriptor.Kind.DECLARATION, SourceElement.NO_SOURCE, false
|
|
||||||
);
|
|
||||||
|
|
||||||
fakeDescriptorForInlineLambda.initialize(
|
|
||||||
null, null,
|
|
||||||
Collections.<TypeParameterDescriptor>emptyList(),
|
|
||||||
Collections.<ValueParameterDescriptor>emptyList(),
|
|
||||||
DescriptorUtilsKt.getBuiltIns(functionDescriptor).getUnitType(),
|
|
||||||
Modality.FINAL, Visibilities.PUBLIC
|
|
||||||
);
|
|
||||||
|
|
||||||
bindingTrace.record(CUSTOM_DESCRIPTOR_FOR_INLINE_LAMBDA, functionLiteral, fakeDescriptorForInlineLambda);
|
|
||||||
|
|
||||||
recordClosure(recordClassForCallable(functionLiteral, fakeDescriptorForInlineLambda, supertypes, name),
|
|
||||||
inventAnonymousClassName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visitLambdaExpression(lambdaExpression);
|
super.visitLambdaExpression(lambdaExpression);
|
||||||
|
|||||||
+113
-298
@@ -16,17 +16,15 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.coroutines
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.findOperatorInController
|
import com.intellij.util.ArrayUtil
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
|
||||||
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
import org.jetbrains.kotlin.coroutines.isSuspendLambda
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
@@ -39,8 +37,8 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
@@ -54,98 +52,143 @@ class CoroutineCodegen(
|
|||||||
strategy: FunctionGenerationStrategy,
|
strategy: FunctionGenerationStrategy,
|
||||||
parentCodegen: MemberCodegen<*>,
|
parentCodegen: MemberCodegen<*>,
|
||||||
classBuilder: ClassBuilder,
|
classBuilder: ClassBuilder,
|
||||||
private val coroutineLambdaDescriptor: FunctionDescriptor,
|
private val coroutineLambdaDescriptor: FunctionDescriptor
|
||||||
private val controllerType: KotlinType
|
|
||||||
) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) {
|
) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) {
|
||||||
|
|
||||||
|
private val classDescriptor = closureContext.contextDescriptor
|
||||||
|
|
||||||
|
private lateinit var constructorToUseFromInvoke: Method
|
||||||
|
|
||||||
|
// protected fun doResume(result, throwable)
|
||||||
|
private val doResumeDescriptor =
|
||||||
|
SimpleFunctionDescriptorImpl.create(
|
||||||
|
classDescriptor, Annotations.EMPTY, Name.identifier("doResume"), CallableMemberDescriptor.Kind.DECLARATION,
|
||||||
|
funDescriptor.source
|
||||||
|
).apply doResume@{
|
||||||
|
initialize(
|
||||||
|
/* receiverParameterType = */ null,
|
||||||
|
classDescriptor.thisAsReceiverParameter,
|
||||||
|
/* typeParameters = */ emptyList(),
|
||||||
|
listOf(
|
||||||
|
ValueParameterDescriptorImpl(
|
||||||
|
this@doResume, null, 0, Annotations.EMPTY, Name.identifier("data"),
|
||||||
|
module.builtIns.nullableAnyType,
|
||||||
|
/* isDefault = */ false, /* isCrossinline = */ false,
|
||||||
|
/* isNoinline = */ false, /* isCoroutine = */ false,
|
||||||
|
/* varargElementType = */ null, SourceElement.NO_SOURCE
|
||||||
|
),
|
||||||
|
ValueParameterDescriptorImpl(
|
||||||
|
this@doResume, null, 1, Annotations.EMPTY, Name.identifier("throwable"),
|
||||||
|
module.builtIns.throwable.defaultType.makeNullable(),
|
||||||
|
/* isDefault = */ false, /* isCrossinline = */ false,
|
||||||
|
/* isNoinline = */ false, /* isCoroutine = */ false,
|
||||||
|
/* varargElementType = */ null, SourceElement.NO_SOURCE
|
||||||
|
)
|
||||||
|
),
|
||||||
|
funDescriptor.builtIns.nullableAnyType,
|
||||||
|
Modality.FINAL,
|
||||||
|
Visibilities.PROTECTED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateClosureBody() {
|
override fun generateClosureBody() {
|
||||||
for (parameter in funDescriptor.valueParameters) {
|
for (parameter in allLambdaParameters()) {
|
||||||
|
val fieldInfo = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||||
v.newField(
|
v.newField(
|
||||||
OtherOrigin(parameter),
|
OtherOrigin(parameter),
|
||||||
Opcodes.ACC_PRIVATE,
|
Opcodes.ACC_PRIVATE,
|
||||||
COROUTINE_LAMBDA_PARAMETER_PREFIX + parameter.index,
|
fieldInfo.fieldName,
|
||||||
typeMapper.mapType(parameter.type).descriptor, null, null)
|
fieldInfo.fieldType.descriptor, null, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
generateDoResume()
|
generateDoResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generateBody() {
|
||||||
|
super.generateBody()
|
||||||
|
|
||||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
generateInvokeMethod(codegen, signature)
|
generateInvokeMethod(codegen)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke for lambda being passes to builder
|
override fun generateConstructor(): Method {
|
||||||
// fun builder(coroutine c: Controller.() -> Continuation<Unit>)
|
val args = calculateConstructorParameters(typeMapper, closure, asmType)
|
||||||
//
|
val argTypes = args.map { it.fieldType }.plus(AsmTypes.CONTINUATION).toTypedArray()
|
||||||
// This lambda must have a receiver parameter, may have value parameters and returns Continuation<Unit> (`this` instance or a copy of it)
|
|
||||||
private fun generateInvokeMethod(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
val constructor = Method("<init>", Type.VOID_TYPE, argTypes)
|
||||||
|
val mv = v.newMethod(
|
||||||
|
OtherOrigin(element, funDescriptor), visibilityFlag, "<init>", constructor.descriptor, null,
|
||||||
|
ArrayUtil.EMPTY_STRING_ARRAY
|
||||||
|
)
|
||||||
|
|
||||||
|
constructorToUseFromInvoke = constructor
|
||||||
|
|
||||||
|
if (state.classBuilderMode.generateBodies) {
|
||||||
|
mv.visitCode()
|
||||||
|
val iv = InstructionAdapter(mv)
|
||||||
|
|
||||||
|
iv.generateClosureFieldsInitializationFromParameters(closure, args)
|
||||||
|
|
||||||
|
iv.load(0, AsmTypes.OBJECT_TYPE)
|
||||||
|
iv.iconst(calculateArity())
|
||||||
|
iv.load(argTypes.map { it.size }.sum(), AsmTypes.OBJECT_TYPE)
|
||||||
|
|
||||||
|
val superClassConstructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, AsmTypes.CONTINUATION)
|
||||||
|
iv.invokespecial(superClassAsmType.internalName, "<init>", superClassConstructorDescriptor, false)
|
||||||
|
|
||||||
|
iv.visitInsn(Opcodes.RETURN)
|
||||||
|
|
||||||
|
FunctionCodegen.endVisit(iv, "constructor", element)
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateInvokeMethod(codegen: ExpressionCodegen) {
|
||||||
val classDescriptor = closureContext.contextDescriptor
|
val classDescriptor = closureContext.contextDescriptor
|
||||||
val owner = typeMapper.mapClass(classDescriptor)
|
val owner = typeMapper.mapClass(classDescriptor)
|
||||||
val controllerFieldInfo =
|
|
||||||
FieldInfo.createForHiddenField(
|
|
||||||
AsmTypes.COROUTINE_IMPL,
|
|
||||||
AsmTypes.OBJECT_TYPE, COROUTINE_CONTROLLER_FIELD_NAME
|
|
||||||
)
|
|
||||||
|
|
||||||
val thisInstance = StackValue.thisOrOuter(codegen, classDescriptor, false, false)
|
val thisInstance = StackValue.thisOrOuter(codegen, classDescriptor, false, false)
|
||||||
|
|
||||||
with(codegen.v) {
|
with(codegen.v) {
|
||||||
// if (controller != null)
|
|
||||||
StackValue.field(controllerFieldInfo, thisInstance).put(AsmTypes.OBJECT_TYPE, this)
|
|
||||||
val repeated = Label()
|
|
||||||
ifnonnull(repeated)
|
|
||||||
|
|
||||||
// first call
|
|
||||||
AsmUtil.genAssignInstanceFieldFromParam(controllerFieldInfo, 1, this)
|
|
||||||
|
|
||||||
setLabelValue(LABEL_VALUE_BEFORE_FIRST_SUSPENSION)
|
|
||||||
|
|
||||||
// Save lambda parameters to fields
|
|
||||||
// 0 - this
|
|
||||||
// 1 - controller
|
|
||||||
var index = 2
|
|
||||||
for (parameter in funDescriptor.valueParameters) {
|
|
||||||
val fieldInfoForCoroutineLambdaParameter = parameter.getFieldInfoForCoroutineLambdaParameter()
|
|
||||||
AsmUtil.genAssignInstanceFieldFromParam(
|
|
||||||
fieldInfoForCoroutineLambdaParameter, index, this)
|
|
||||||
index += fieldInfoForCoroutineLambdaParameter.fieldType.size
|
|
||||||
}
|
|
||||||
|
|
||||||
load(0, AsmTypes.OBJECT_TYPE)
|
|
||||||
areturn(AsmTypes.OBJECT_TYPE)
|
|
||||||
|
|
||||||
// repeated call
|
|
||||||
visitLabel(repeated)
|
|
||||||
anew(owner)
|
anew(owner)
|
||||||
dup()
|
dup()
|
||||||
|
|
||||||
// pass closure parameters to constructor
|
// pass captured closure to constructor
|
||||||
val constructorParameters = calculateConstructorParameters(typeMapper, closure, owner)
|
val constructorParameters = calculateConstructorParameters(typeMapper, closure, owner)
|
||||||
for (parameter in constructorParameters) {
|
for (parameter in constructorParameters) {
|
||||||
StackValue.field(parameter, thisInstance).put(parameter.fieldType, this)
|
StackValue.field(parameter, thisInstance).put(parameter.fieldType, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val constructor = Method("<init>", Type.VOID_TYPE, constructorParameters.map { it.fieldType }.toTypedArray())
|
// load resultContinuation
|
||||||
invokespecial(owner.internalName, constructor.name, constructor.descriptor, false)
|
load(allLambdaParameters().map { typeMapper.mapType(it.type).size }.sum() + 1, AsmTypes.OBJECT_TYPE)
|
||||||
|
|
||||||
|
invokespecial(owner.internalName, constructorToUseFromInvoke.name, constructorToUseFromInvoke.descriptor, false)
|
||||||
|
|
||||||
|
val cloneIndex = codegen.frameMap.enterTemp(AsmTypes.OBJECT_TYPE)
|
||||||
|
store(cloneIndex, AsmTypes.OBJECT_TYPE)
|
||||||
|
|
||||||
// Pass lambda parameters to 'invoke' call on newly constructed object
|
// Pass lambda parameters to 'invoke' call on newly constructed object
|
||||||
index = 1
|
var index = 1
|
||||||
for (parameter in signature.valueParameters) {
|
for (parameter in allLambdaParameters()) {
|
||||||
load(index, parameter.asmType)
|
val fieldInfoForCoroutineLambdaParameter = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||||
index += parameter.asmType.size
|
load(index, fieldInfoForCoroutineLambdaParameter.fieldType)
|
||||||
|
AsmUtil.genAssignInstanceFieldFromParam(fieldInfoForCoroutineLambdaParameter, index, this, cloneIndex)
|
||||||
|
index += fieldInfoForCoroutineLambdaParameter.fieldType.size
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'invoke' call on freshly constructed coroutine returns receiver itself
|
load(cloneIndex, AsmTypes.OBJECT_TYPE)
|
||||||
invokevirtual(owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false)
|
|
||||||
areturn(AsmTypes.OBJECT_TYPE)
|
areturn(AsmTypes.OBJECT_TYPE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExpressionCodegen.initializeCoroutineParameters() {
|
private fun ExpressionCodegen.initializeCoroutineParameters() {
|
||||||
for (parameter in coroutineLambdaDescriptor.valueParameters) {
|
for (parameter in allLambdaParameters()) {
|
||||||
val mappedType = typeMapper.mapType(parameter.type)
|
val mappedType = typeMapper.mapType(parameter.type)
|
||||||
val newIndex = myFrameMap.enter(parameter, mappedType)
|
val newIndex = myFrameMap.enter(parameter, mappedType)
|
||||||
|
|
||||||
@@ -154,12 +197,15 @@ class CoroutineCodegen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun allLambdaParameters() =
|
||||||
|
coroutineLambdaDescriptor.extensionReceiverParameter.singletonOrEmptyList()
|
||||||
|
|
||||||
private fun ExpressionCodegen.generateLoadField(fieldInfo: FieldInfo) {
|
private fun ExpressionCodegen.generateLoadField(fieldInfo: FieldInfo) {
|
||||||
StackValue.field(fieldInfo, generateThisOrOuter(context.thisDescriptor, false)).put(fieldInfo.fieldType, v)
|
StackValue.field(fieldInfo, generateThisOrOuter(context.thisDescriptor, false)).put(fieldInfo.fieldType, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ValueParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
private fun ParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
||||||
createHiddenFieldInfo(type, COROUTINE_LAMBDA_PARAMETER_PREFIX + index)
|
createHiddenFieldInfo(type, COROUTINE_LAMBDA_PARAMETER_PREFIX + (this.safeAs<ValueParameterDescriptor>()?.index ?: ""))
|
||||||
|
|
||||||
private fun createHiddenFieldInfo(type: KotlinType, name: String) =
|
private fun createHiddenFieldInfo(type: KotlinType, name: String) =
|
||||||
FieldInfo.createForHiddenField(
|
FieldInfo.createForHiddenField(
|
||||||
@@ -168,243 +214,21 @@ class CoroutineCodegen(
|
|||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun generateExceptionHandlingBlock(codegen: ExpressionCodegen) {
|
|
||||||
val handleExceptionFunction = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION) ?: return
|
|
||||||
|
|
||||||
val (resolvedCall, fakeExceptionExpression, fakeThisContinuationException) =
|
|
||||||
createResolvedCallForHandleExceptionCall(element, handleExceptionFunction, coroutineLambdaDescriptor)
|
|
||||||
|
|
||||||
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.tempVariables.put(fakeThisContinuationException, codegen.genCoroutineInstanceValueFromResolvedCall(resolvedCall))
|
|
||||||
|
|
||||||
codegen.v.invokestatic(COROUTINE_MARKER_OWNER, HANDLE_EXCEPTION_MARKER_NAME, "()V", false)
|
|
||||||
codegen.invokeFunction(resolvedCall, StackValue.none()).put(Type.VOID_TYPE, codegen.v)
|
|
||||||
codegen.v.areturn(Type.VOID_TYPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateDoResume() {
|
private fun generateDoResume() {
|
||||||
val classDescriptor = closureContext.contextDescriptor
|
|
||||||
|
|
||||||
// protected fun doResume(result, throwable)
|
|
||||||
val doResumeDescriptor =
|
|
||||||
SimpleFunctionDescriptorImpl.create(
|
|
||||||
classDescriptor, Annotations.EMPTY, Name.identifier("doResume"), CallableMemberDescriptor.Kind.DECLARATION,
|
|
||||||
funDescriptor.source
|
|
||||||
).apply doResume@{
|
|
||||||
initialize(
|
|
||||||
/* receiverParameterType = */ null,
|
|
||||||
classDescriptor.thisAsReceiverParameter,
|
|
||||||
/* typeParameters = */ emptyList(),
|
|
||||||
listOf(
|
|
||||||
ValueParameterDescriptorImpl(
|
|
||||||
this@doResume, null, 0, Annotations.EMPTY, Name.identifier("data"),
|
|
||||||
module.builtIns.nullableAnyType,
|
|
||||||
/* isDefault = */ false, /* isCrossinline = */ false,
|
|
||||||
/* isNoinline = */ false, /* isCoroutine = */ false,
|
|
||||||
/* varargElementType = */ null, SourceElement.NO_SOURCE
|
|
||||||
),
|
|
||||||
ValueParameterDescriptorImpl(
|
|
||||||
this@doResume, null, 1, Annotations.EMPTY, Name.identifier("throwable"),
|
|
||||||
module.builtIns.throwable.defaultType.makeNullable(),
|
|
||||||
/* isDefault = */ false, /* isCrossinline = */ false,
|
|
||||||
/* isNoinline = */ false, /* isCoroutine = */ false,
|
|
||||||
/* varargElementType = */ null, SourceElement.NO_SOURCE
|
|
||||||
)
|
|
||||||
),
|
|
||||||
module.builtIns.unitType,
|
|
||||||
Modality.FINAL,
|
|
||||||
Visibilities.PROTECTED
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val interceptResume = findOperatorInController(controllerType, OperatorNameConventions.COROUTINE_INTERCEPT_RESUME)
|
|
||||||
if (interceptResume != null) {
|
|
||||||
processInterceptResume(doResumeDescriptor, interceptResume)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
functionCodegen.generateMethod(
|
|
||||||
OtherOrigin(element),
|
|
||||||
doResumeDescriptor,
|
|
||||||
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
|
||||||
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
|
||||||
codegen.initializeCoroutineParameters()
|
|
||||||
super.doGenerateBody(codegen, signature)
|
|
||||||
generateExceptionHandlingBlock(codegen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is defined an interceptResume operator in the controller class (it must have 'fun interceptResume(x: () -> Unit): Unit' signature),
|
|
||||||
* then continuation resume* operations must behave as they defined like `fun resume(x: T) = controller.interceptResume { doResume(x) }`.
|
|
||||||
*
|
|
||||||
* We do it the following way, our doResume implementation is defined as (at least for noinline case):
|
|
||||||
* fun doResume(v: Any?, t: Throwable?) {
|
|
||||||
* this.$v = v
|
|
||||||
* this.$t = t
|
|
||||||
* controller.interceptResume(this)
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* override fun invoke() {
|
|
||||||
* val v = this.$v
|
|
||||||
* val t = this.$t
|
|
||||||
* .. // common continuation state machine encoding
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* And yes, it means that `this`-object (i.e. a Continuation instance) also implements Function0<Unit>, along with
|
|
||||||
* FunctionK<Controller, .., Unit>.
|
|
||||||
* There are two kinds of interceptResume:
|
|
||||||
* 1. When the defined as not inline. In that case a continuation object is also implements `Function0<Unit>` (literally in class-file) and defines
|
|
||||||
* an `invoke()Lj/l/Object` bridge.
|
|
||||||
* 2. Otherwise, it's defined as inline and we generate interceptResume call in the following way:
|
|
||||||
* controller.interceptResume { this.invoke() }
|
|
||||||
*
|
|
||||||
* The subtle difference with noinline case is that `this.invoke()` call is devirtualized after inlining
|
|
||||||
* (so there's no need to literally implement `Function0<Unit>` nor to generate the bridges)
|
|
||||||
*
|
|
||||||
* Note that in the second case there must be created a fake lambda descriptor (and it's done via CodegenAnnotatingVisitor)
|
|
||||||
*/
|
|
||||||
private fun processInterceptResume(doResumeDescriptor: SimpleFunctionDescriptor, interceptResume: SimpleFunctionDescriptor) {
|
|
||||||
val (interceptResumeResolvedCall, lambdaExpressionForInterceptResume) =
|
|
||||||
createResolvedCallForInterceptResume(element as KtFunctionLiteral, interceptResume, coroutineLambdaDescriptor)
|
|
||||||
|
|
||||||
val fieldInfoForValue =
|
|
||||||
createHiddenFieldInfo(funDescriptor.builtIns.anyType, COROUTINE_VALUE_FIELD_NAME_FOR_INTERCEPT_RESUME)
|
|
||||||
|
|
||||||
val fieldInfoForThrowable =
|
|
||||||
createHiddenFieldInfo(funDescriptor.builtIns.throwable.defaultType, COROUTINE_THROWABLE_FIELD_NAME_FOR_INTERCEPT_RESUME)
|
|
||||||
|
|
||||||
v.newField(
|
|
||||||
JvmDeclarationOrigin.NO_ORIGIN,
|
|
||||||
Opcodes.ACC_PRIVATE,
|
|
||||||
fieldInfoForValue.fieldName,
|
|
||||||
fieldInfoForValue.fieldType.descriptor, null, null)
|
|
||||||
|
|
||||||
v.newField(
|
|
||||||
JvmDeclarationOrigin.NO_ORIGIN,
|
|
||||||
Opcodes.ACC_PRIVATE,
|
|
||||||
fieldInfoForThrowable.fieldName,
|
|
||||||
fieldInfoForThrowable.fieldType.descriptor, null, null)
|
|
||||||
|
|
||||||
val classDescriptor = closureContext.contextDescriptor
|
|
||||||
val invokeDescriptor =
|
|
||||||
SimpleFunctionDescriptorImpl.create(
|
|
||||||
classDescriptor, Annotations.EMPTY, Name.identifier("invoke"), CallableMemberDescriptor.Kind.DECLARATION,
|
|
||||||
funDescriptor.source
|
|
||||||
).apply {
|
|
||||||
initialize(
|
|
||||||
/* receiverParameterType = */ null, classDescriptor.thisAsReceiverParameter,
|
|
||||||
/* typeParameters = */ emptyList(), emptyList(),
|
|
||||||
module.builtIns.unitType,
|
|
||||||
Modality.FINAL,
|
|
||||||
Visibilities.PUBLIC
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!interceptResume.isInline) {
|
|
||||||
// for generating necessary bridges
|
|
||||||
overriddenDescriptors =
|
|
||||||
funDescriptor.builtIns.getFunction(0)
|
|
||||||
.defaultType.memberScope.getContributedFunctions(Name.identifier("invoke"),
|
|
||||||
NoLookupLocation.FROM_BACKEND
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interceptResume.isInline) {
|
|
||||||
state.bindingTrace.record(
|
|
||||||
CodegenBinding.CUSTOM_STRATEGY_FOR_INLINE_LAMBDA, element,
|
|
||||||
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
|
||||||
// ?
|
|
||||||
val label = Label()
|
|
||||||
codegen.v.visitLineNumber(1, label)
|
|
||||||
codegen.v.visitLabel(label)
|
|
||||||
|
|
||||||
codegen.generateThisOrOuter(context.thisDescriptor, false)
|
|
||||||
.put(Type.getObjectType(this@CoroutineCodegen.v.thisName), codegen.v)
|
|
||||||
codegen.v.invokevirtual(this@CoroutineCodegen.v.thisName, "invoke", "()V", false)
|
|
||||||
codegen.v.areturn(Type.VOID_TYPE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
functionCodegen.generateMethod(
|
|
||||||
OtherOrigin(element),
|
|
||||||
invokeDescriptor,
|
|
||||||
object : FunctionGenerationStrategy.FunctionDefault(state, element) {
|
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
|
||||||
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
|
||||||
|
|
||||||
assert(codegen.frameMap.enterTemp(AsmTypes.OBJECT_TYPE) == COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME) {
|
|
||||||
"Next free slot must be $COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME"
|
|
||||||
}
|
|
||||||
assert(codegen.frameMap.enterTemp(AsmTypes.JAVA_THROWABLE_TYPE) == COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME) {
|
|
||||||
"Next free slot must be $COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME"
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen.generateLoadField(fieldInfoForValue)
|
|
||||||
codegen.v.store(COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME, AsmTypes.OBJECT_TYPE)
|
|
||||||
|
|
||||||
codegen.generateLoadField(fieldInfoForThrowable)
|
|
||||||
codegen.v.store(COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME, AsmTypes.JAVA_THROWABLE_TYPE)
|
|
||||||
|
|
||||||
codegen.v.invokestatic(
|
|
||||||
COROUTINE_MARKER_OWNER, ACTUAL_COROUTINE_START_MARKER_NAME, "()V", false
|
|
||||||
)
|
|
||||||
|
|
||||||
codegen.initializeCoroutineParameters()
|
|
||||||
super.doGenerateBody(codegen, signature)
|
|
||||||
generateExceptionHandlingBlock(codegen)
|
|
||||||
codegen.v.areturn(Type.VOID_TYPE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
functionCodegen.generateMethod(
|
functionCodegen.generateMethod(
|
||||||
OtherOrigin(element),
|
OtherOrigin(element),
|
||||||
doResumeDescriptor,
|
doResumeDescriptor,
|
||||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
AsmUtil.genAssignInstanceFieldFromParam(
|
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
||||||
fieldInfoForValue, COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME, codegen.v
|
codegen.initializeCoroutineParameters()
|
||||||
)
|
super.doGenerateBody(codegen, signature)
|
||||||
|
|
||||||
AsmUtil.genAssignInstanceFieldFromParam(
|
|
||||||
fieldInfoForThrowable, COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME, codegen.v
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!interceptResume.isInline) {
|
|
||||||
codegen.tempVariables.put(
|
|
||||||
lambdaExpressionForInterceptResume,
|
|
||||||
codegen.generateThisOrOuter(context.thisDescriptor, false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen.invokeFunction(
|
|
||||||
interceptResumeResolvedCall, StackValue.none()
|
|
||||||
).put(Type.VOID_TYPE, codegen.v)
|
|
||||||
|
|
||||||
codegen.v.areturn(Type.VOID_TYPE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun InstructionAdapter.setLabelValue(value: Int) {
|
|
||||||
load(0, AsmTypes.OBJECT_TYPE)
|
|
||||||
iconst(value)
|
|
||||||
putfield(AsmTypes.COROUTINE_IMPL.internalName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val LABEL_VALUE_BEFORE_FIRST_SUSPENSION = 0
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(
|
fun create(
|
||||||
@@ -414,13 +238,9 @@ class CoroutineCodegen(
|
|||||||
classBuilder: ClassBuilder
|
classBuilder: ClassBuilder
|
||||||
): ClosureCodegen? {
|
): ClosureCodegen? {
|
||||||
if (declaration !is KtFunctionLiteral) return null
|
if (declaration !is KtFunctionLiteral) return null
|
||||||
val controllerType = originalCoroutineLambdaDescriptor.controllerTypeIfCoroutine ?: return null
|
if (!originalCoroutineLambdaDescriptor.isSuspendLambda) return null
|
||||||
|
|
||||||
val descriptorWithContinuationReturnType =
|
val descriptorWithContinuationReturnType = createJvmSuspendFunctionView(originalCoroutineLambdaDescriptor)
|
||||||
originalCoroutineLambdaDescriptor.newCopyBuilder()
|
|
||||||
.setPreserveSourceElement()
|
|
||||||
.setReturnType(expressionCodegen.state.jvmRuntimeTypes.continuationOfAny)
|
|
||||||
.build()!!
|
|
||||||
|
|
||||||
val state = expressionCodegen.state
|
val state = expressionCodegen.state
|
||||||
return CoroutineCodegen(
|
return CoroutineCodegen(
|
||||||
@@ -431,15 +251,10 @@ class CoroutineCodegen(
|
|||||||
),
|
),
|
||||||
FunctionGenerationStrategy.FunctionDefault(state, declaration),
|
FunctionGenerationStrategy.FunctionDefault(state, declaration),
|
||||||
expressionCodegen.parentCodegen, classBuilder,
|
expressionCodegen.parentCodegen, classBuilder,
|
||||||
originalCoroutineLambdaDescriptor,
|
originalCoroutineLambdaDescriptor
|
||||||
controllerType
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$"
|
private const val COROUTINE_LAMBDA_PARAMETER_PREFIX = "p$"
|
||||||
private const val COROUTINE_VALUE_FIELD_NAME_FOR_INTERCEPT_RESUME = "v$"
|
|
||||||
private const val COROUTINE_THROWABLE_FIELD_NAME_FOR_INTERCEPT_RESUME = "throwable$"
|
|
||||||
private const val COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME = 1
|
|
||||||
private const val COROUTINE_THROWABLE_PARAMETER_SLOT_IN_DO_RESUME = 2
|
|
||||||
|
|||||||
+6
-11
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.analyzeLiveness
|
import org.jetbrains.kotlin.codegen.optimization.common.analyzeLiveness
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
|
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
@@ -119,17 +118,12 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
// tableswitch(this.label)
|
// tableswitch(this.label)
|
||||||
insertBefore(firstToInsertBefore,
|
insertBefore(firstToInsertBefore,
|
||||||
insnListOf(
|
insnListOf(
|
||||||
FieldInsnNode(
|
*withInstructionAdapter { loadSuspendMarker() }.toArray(),
|
||||||
Opcodes.GETSTATIC,
|
|
||||||
AsmTypes.COROUTINES_SUSPEND.internalName,
|
|
||||||
JvmAbi.INSTANCE_FIELD,
|
|
||||||
AsmTypes.COROUTINES_SUSPEND.descriptor
|
|
||||||
),
|
|
||||||
VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex),
|
VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex),
|
||||||
VarInsnNode(Opcodes.ALOAD, 0),
|
VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
FieldInsnNode(
|
FieldInsnNode(
|
||||||
Opcodes.GETFIELD,
|
Opcodes.GETFIELD,
|
||||||
AsmTypes.COROUTINE_IMPL.internalName,
|
AsmTypes.RESTRICTED_COROUTINE_IMPL.internalName,
|
||||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||||
),
|
),
|
||||||
TableSwitchInsnNode(0,
|
TableSwitchInsnNode(0,
|
||||||
@@ -338,7 +332,7 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
VarInsnNode(Opcodes.ALOAD, 0),
|
VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
*withInstructionAdapter { iconst(id) }.toArray(),
|
*withInstructionAdapter { iconst(id) }.toArray(),
|
||||||
FieldInsnNode(
|
FieldInsnNode(
|
||||||
Opcodes.PUTFIELD, AsmTypes.COROUTINE_IMPL.internalName, COROUTINE_LABEL_FIELD_NAME,
|
Opcodes.PUTFIELD, AsmTypes.RESTRICTED_COROUTINE_IMPL.internalName, COROUTINE_LABEL_FIELD_NAME,
|
||||||
Type.INT_TYPE.descriptor
|
Type.INT_TYPE.descriptor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -349,11 +343,12 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
|
|
||||||
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
|
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
|
||||||
dup()
|
dup()
|
||||||
load(suspendMarkerVarIndex, AsmTypes.COROUTINES_SUSPEND)
|
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
|
||||||
ifacmpne(continuationLabelAfterLoadedResult.label)
|
ifacmpne(continuationLabelAfterLoadedResult.label)
|
||||||
|
|
||||||
// Exit
|
// Exit
|
||||||
areturn(Type.VOID_TYPE)
|
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
|
||||||
|
areturn(AsmTypes.OBJECT_TYPE)
|
||||||
// Mark place for continuation
|
// Mark place for continuation
|
||||||
visitLabel(continuationLabel.label)
|
visitLabel(continuationLabel.label)
|
||||||
})
|
})
|
||||||
|
|||||||
+15
-17
@@ -18,20 +18,18 @@ package org.jetbrains.kotlin.codegen.coroutines
|
|||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.backend.common.SUSPEND_WITH_CURRENT_CONTINUATION_NAME
|
import org.jetbrains.kotlin.backend.common.SUSPEND_WITH_CURRENT_CONTINUATION_NAME
|
||||||
import org.jetbrains.kotlin.backend.common.findInterceptResume
|
|
||||||
import org.jetbrains.kotlin.backend.common.getBuiltInSuspendWithCurrentContinuation
|
import org.jetbrains.kotlin.backend.common.getBuiltInSuspendWithCurrentContinuation
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||||
|
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments
|
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
@@ -42,13 +40,14 @@ import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
|||||||
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||||
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
// These classes do not actually exist at runtime
|
// These classes do not actually exist at runtime
|
||||||
@@ -61,8 +60,6 @@ const val HANDLE_EXCEPTION_MARKER_NAME = "handleException"
|
|||||||
const val HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME = "handleExceptionArgument"
|
const val HANDLE_EXCEPTION_ARGUMENT_MARKER_NAME = "handleExceptionArgument"
|
||||||
const val ACTUAL_COROUTINE_START_MARKER_NAME = "actualCoroutineStart"
|
const val ACTUAL_COROUTINE_START_MARKER_NAME = "actualCoroutineStart"
|
||||||
|
|
||||||
const val COROUTINE_CONTROLLER_FIELD_NAME = "_controller"
|
|
||||||
const val COROUTINE_CONTROLLER_GETTER_NAME = "getController"
|
|
||||||
const val COROUTINE_LABEL_FIELD_NAME = "label"
|
const val COROUTINE_LABEL_FIELD_NAME = "label"
|
||||||
|
|
||||||
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
|
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
|
||||||
@@ -188,7 +185,7 @@ fun createResolvedCallForInterceptResume(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ResolvedCall<*>.isSuspensionPoint(bindingContext: BindingContext) =
|
fun ResolvedCall<*>.isSuspensionPoint(bindingContext: BindingContext) =
|
||||||
bindingContext[BindingContext.COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, call] != null
|
bindingContext[BindingContext.ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, call] != null
|
||||||
|
|
||||||
// Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation<return-type>`,
|
// Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation<return-type>`,
|
||||||
// and return type Any?
|
// and return type Any?
|
||||||
@@ -237,12 +234,6 @@ private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction() =
|
|||||||
arguments = listOf(returnType!!.asTypeProjection())
|
arguments = listOf(returnType!!.asTypeProjection())
|
||||||
)
|
)
|
||||||
|
|
||||||
val KotlinBuiltIns.continuationClassDescriptor get() = getBuiltInClassByFqName(DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME)
|
|
||||||
|
|
||||||
fun KotlinType.hasInlineInterceptResume() = findInterceptResume()?.isInline == true
|
|
||||||
|
|
||||||
fun KotlinType.hasNoinlineInterceptResume() = findInterceptResume()?.isInline == false
|
|
||||||
|
|
||||||
fun FunctionDescriptor.isBuiltInSuspendWithCurrentContinuation(): Boolean {
|
fun FunctionDescriptor.isBuiltInSuspendWithCurrentContinuation(): Boolean {
|
||||||
if (name != SUSPEND_WITH_CURRENT_CONTINUATION_NAME) return false
|
if (name != SUSPEND_WITH_CURRENT_CONTINUATION_NAME) return false
|
||||||
|
|
||||||
@@ -286,3 +277,10 @@ fun createMethodNodeForSuspendWithCurrentContinuation(
|
|||||||
|
|
||||||
fun CallableDescriptor?.unwrapInitialDescriptorForSuspendFunction() =
|
fun CallableDescriptor?.unwrapInitialDescriptorForSuspendFunction() =
|
||||||
(this as? SimpleFunctionDescriptor)?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) ?: this
|
(this as? SimpleFunctionDescriptor)?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) ?: this
|
||||||
|
|
||||||
|
fun InstructionAdapter.loadSuspendMarker() = invokestatic(
|
||||||
|
AsmTypes.COROUTINES_SUSPEND_MARKER_OWNER.internalName,
|
||||||
|
"getSUSPENDED",
|
||||||
|
Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|||||||
@@ -331,11 +331,20 @@ public class KotlinTypeMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final ClassId FAKE_CLASS_ID_FOR_BUILTINS = ClassId.topLevel(new FqName("kotlin.KotlinPackage"));
|
private static final ClassId FAKE_CLASS_ID_FOR_BUILTINS = ClassId.topLevel(new FqName("kotlin.KotlinPackage"));
|
||||||
|
private static final FqName COROUTINE_SUSPENDED_PROPERTY = new FqName("kotlin.coroutines.SUSPENDED");
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static ContainingClassesInfo getPackageMemberContainingClassesInfo(@NotNull DeserializedCallableMemberDescriptor descriptor) {
|
private static ContainingClassesInfo getPackageMemberContainingClassesInfo(@NotNull DeserializedCallableMemberDescriptor descriptor) {
|
||||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||||
if (containingDeclaration instanceof BuiltInsPackageFragment) {
|
if (containingDeclaration instanceof BuiltInsPackageFragment) {
|
||||||
|
// TODO: Top level callables are not supported in built-ins
|
||||||
|
// (while they are loaded from .kotlin-builtins files information about the actual file part is lost)
|
||||||
|
// This must be fixed when built-ins get loaded through their class files
|
||||||
|
if (DescriptorUtils.getFqName(descriptor).equals(COROUTINE_SUSPENDED_PROPERTY.toUnsafe())) {
|
||||||
|
ClassId id = ClassId.topLevel(new FqName("kotlin.coroutines.CoroutinesKt"));
|
||||||
|
return new ContainingClassesInfo(id, id);
|
||||||
|
}
|
||||||
|
|
||||||
return new ContainingClassesInfo(FAKE_CLASS_ID_FOR_BUILTINS, FAKE_CLASS_ID_FOR_BUILTINS);
|
return new ContainingClassesInfo(FAKE_CLASS_ID_FOR_BUILTINS, FAKE_CLASS_ID_FOR_BUILTINS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ public class AsmTypes {
|
|||||||
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
|
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
|
||||||
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
|
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
|
||||||
public static final Type COROUTINE_IMPL = Type.getObjectType("kotlin/jvm/internal/CoroutineImpl");
|
public static final Type COROUTINE_IMPL = Type.getObjectType("kotlin/jvm/internal/CoroutineImpl");
|
||||||
public static final Type COROUTINES_SUSPEND = Type.getObjectType("kotlin/coroutines/Suspend");
|
public static final Type RESTRICTED_COROUTINE_IMPL = Type.getObjectType("kotlin/jvm/internal/RestrictedCoroutineImpl");
|
||||||
|
public static final Type COROUTINES_SUSPEND_MARKER_OWNER = Type.getObjectType("kotlin/coroutines/CoroutinesKt");
|
||||||
|
public static final Type CONTINUATION = Type.getObjectType("kotlin/coroutines/Continuation");
|
||||||
|
|
||||||
|
|
||||||
public static final Type[] PROPERTY_REFERENCE_IMPL = {
|
public static final Type[] PROPERTY_REFERENCE_IMPL = {
|
||||||
|
|||||||
+4
-10
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// NO_INTERCEPT_RESUME_TESTS
|
||||||
// FILE: promise.kt
|
// FILE: promise.kt
|
||||||
class Promise<T>(private val executor: ((T) -> Unit) -> Unit) {
|
class Promise<T>(private val executor: ((T) -> Unit) -> Unit) {
|
||||||
@@ -48,12 +49,6 @@ fun processQueue() {
|
|||||||
// FILE: await.kt
|
// FILE: await.kt
|
||||||
private var log = ""
|
private var log = ""
|
||||||
|
|
||||||
class Controller<T>(private val resolve: (T) -> Unit) {
|
|
||||||
operator fun handleResult(result: T, c: Continuation<Nothing>) {
|
|
||||||
resolve(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var inAwait = false
|
private var inAwait = false
|
||||||
|
|
||||||
suspend fun <S> await(value: Promise<S>): S = suspendWithCurrentContinuation { continuation: Continuation<S> ->
|
suspend fun <S> await(value: Promise<S>): S = suspendWithCurrentContinuation { continuation: Continuation<S> ->
|
||||||
@@ -67,7 +62,7 @@ suspend fun <S> await(value: Promise<S>): S = suspendWithCurrentContinuation { c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
inAwait = false
|
inAwait = false
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun <S> awaitAndLog(value: Promise<S>): S {
|
suspend fun <S> awaitAndLog(value: Promise<S>): S {
|
||||||
@@ -78,10 +73,9 @@ suspend fun <S> awaitAndLog(value: Promise<S>): S {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> async(coroutine c: Controller<T>.() -> Continuation<Unit>): Promise<T> {
|
fun <T> async(c: @Suspend() (() -> T)): Promise<T> {
|
||||||
return Promise { resolve ->
|
return Promise { resolve ->
|
||||||
val controller = Controller<T> { resolve(it) }
|
c.startCoroutine(handleResultContinuation(resolve))
|
||||||
c(controller).resume(Unit)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -1,22 +1,22 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
class Controller {
|
// WITH_COROUTINES
|
||||||
|
|
||||||
|
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x -> }
|
||||||
|
|
||||||
|
fun builder(c: @Suspend() (() -> Unit)) {
|
||||||
var exception: Throwable? = null
|
var exception: Throwable? = null
|
||||||
|
|
||||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
c.createCoroutine(object : Continuation<Unit> {
|
||||||
exception = t
|
override fun resume(data: Unit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x -> }
|
override fun resumeWithException(e: Throwable) {
|
||||||
|
exception = e
|
||||||
|
}
|
||||||
|
}).resumeWithException(RuntimeException("OK"))
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
if (exception?.message != "OK") {
|
||||||
}
|
throw RuntimeException("Unexpected result: ${exception?.message}")
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
|
||||||
val controller = Controller()
|
|
||||||
c(controller).resumeWithException(RuntimeException("OK"))
|
|
||||||
|
|
||||||
if (controller.exception?.message != "OK") {
|
|
||||||
throw RuntimeException("Unexpected result: ${controller.exception?.message}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -1,14 +1,10 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
class Controller {
|
// WITH_COROUTINES
|
||||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x ->}
|
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x ->}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
fun builder(c: @Suspend() (() -> Unit)) {
|
||||||
}
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
|
||||||
try {
|
try {
|
||||||
val controller = Controller()
|
c.createCoroutine(EmptyContinuation).resumeWithException(RuntimeException("OK"))
|
||||||
c(controller).resumeWithException(RuntimeException("OK"))
|
|
||||||
}
|
}
|
||||||
catch(e: Exception) {
|
catch(e: Exception) {
|
||||||
if (e?.message != "OK") {
|
if (e?.message != "OK") {
|
||||||
|
|||||||
+11
-15
@@ -1,21 +1,17 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var result = "fail"
|
// WITH_COROUTINES
|
||||||
operator fun handleResult(u: Unit, c: Continuation<Nothing>) {
|
|
||||||
result = "OK"
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <T> await(t: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> await(t: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(t)
|
c.resume(t)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (() -> Unit)): String {
|
||||||
val controller = Controller()
|
var result = "fail"
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
return controller.result
|
result = "OK"
|
||||||
|
})
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var TRUE = true
|
var TRUE = true
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
// Does not work in JVM backend, probably due to bug. It's not clear which behaviour is right.
|
// Does not work in JVM backend, probably due to bug. It's not clear which behaviour is right.
|
||||||
// TODO: fix the bug and enable for JVM backend
|
// TODO: fix the bug and enable for JVM backend
|
||||||
@@ -11,17 +11,15 @@ class Controller {
|
|||||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
result += "suspend($value);"
|
result += "suspend($value);"
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(value: String, c: Continuation<Nothing>) {
|
|
||||||
result += "return($value);"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(coroutine c: () -> String): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResult {
|
||||||
|
controller.result += "return($value);"
|
||||||
|
})
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
@@ -7,13 +7,13 @@ class Controller {
|
|||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
result += "["
|
result += "["
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
@@ -7,24 +7,29 @@ class Controller {
|
|||||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
result += "suspend($value);"
|
result += "suspend($value);"
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tail calls are not allowed to be Nothing typed. See KT-15051
|
// Tail calls are not allowed to be Nothing typed. See KT-15051
|
||||||
suspend fun suspendLogAndThrow(exception: Throwable): Any? = suspendWithCurrentContinuation { c ->
|
suspend fun suspendLogAndThrow(exception: Throwable): Any? = suspendWithCurrentContinuation { c ->
|
||||||
result += "throw(${exception.message});"
|
result += "throw(${exception.message});"
|
||||||
c.resumeWithException(exception)
|
c.resumeWithException(exception)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
|
|
||||||
result += "caught(${exception.message});"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, object : Continuation<Unit> {
|
||||||
|
override fun resume(data: Unit) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
controller.result += "caught(${exception.message});"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
@@ -7,17 +7,19 @@ class Controller {
|
|||||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
result += "suspend($value);"
|
result += "suspend($value);"
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
|
|
||||||
result += "ignoreCaught(${exception.message});"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, object : Continuation<Unit> {
|
||||||
|
override fun resume(data: Unit) {}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
controller.result += "ignoreCaught(${exception.message});"
|
||||||
|
}
|
||||||
|
})
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||||
c.resume(value)
|
c.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -1,8 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = false
|
var result = false
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
@@ -12,9 +14,9 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
if (!controller.result) throw RuntimeException("fail")
|
if (!controller.result) throw RuntimeException("fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(a: String = "abc", i: Int = 2): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(a: String = "abc", i: Int = 2): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(a + "#" + (i + 1))
|
x.resume(a + "#" + (i + 1))
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var result = 0
|
var result = 0
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
result++
|
result++
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun <T> suspendHere(v: T): T = suspendWithCurrentContinuation { x ->
|
suspend fun <T> suspendHere(v: T): T = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v)
|
x.resume(v)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: Any = ""
|
var result: Any = ""
|
||||||
|
|||||||
+22
-22
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// FULL_JDK
|
// FULL_JDK
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
@@ -24,35 +25,34 @@ fun gen() = generate<Int> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LIBRARY CODE
|
// LIBRARY CODE
|
||||||
fun <T> generate(coroutine c: GeneratorController<T>.() -> Continuation<Unit>): Sequence<T> = object : Sequence<T> {
|
interface Generator<in T> {
|
||||||
override fun iterator(): Iterator<T> {
|
suspend fun yield(value: T)
|
||||||
val iterator = GeneratorController<T>()
|
|
||||||
iterator.setNextStep(c(iterator))
|
|
||||||
return iterator
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeneratorController<T>() : AbstractIterator<T>() {
|
fun <T> generate(block: @Suspend() (Generator<T>.() -> Unit)): Sequence<T> = GeneratedSequence(block)
|
||||||
private lateinit var nextStep: Continuation<Unit>
|
|
||||||
|
class GeneratedSequence<out T>(private val block: @Suspend() (Generator<T>.() -> Unit)) : Sequence<T> {
|
||||||
|
override fun iterator(): Iterator<T> = GeneratedIterator(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneratedIterator<T>(block: @Suspend() (Generator<T>.() -> Unit)) : AbstractIterator<T>(), Generator<T> {
|
||||||
|
private var nextStep: Continuation<Unit> = block.createCoroutine(this, object : Continuation<Unit> {
|
||||||
|
override fun resume(data: Unit) {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
override fun computeNext() {
|
override fun computeNext() {
|
||||||
nextStep.resume(Unit)
|
nextStep.resume(Unit)
|
||||||
}
|
}
|
||||||
|
suspend override fun yield(value: T) = suspendWithCurrentContinuation<Unit> { c ->
|
||||||
fun setNextStep(step: Continuation<Unit>) {
|
|
||||||
this.nextStep = step
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun yield(value: T): Unit = suspendWithCurrentContinuation { c ->
|
|
||||||
setNext(value)
|
setNext(value)
|
||||||
setNextStep(c)
|
nextStep = c
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(result: Unit, c: Continuation<Nothing>) {
|
|
||||||
done()
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-11
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var exception: Throwable? = null
|
var exception: Throwable? = null
|
||||||
val postponedActions = ArrayList<() -> Unit>()
|
val postponedActions = ArrayList<() -> Unit>()
|
||||||
@@ -8,7 +9,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -16,25 +17,21 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
exception = t
|
c.startCoroutine(this, handleExceptionContinuation {
|
||||||
}
|
exception = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
controller.run(c)
|
controller.run(c)
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
|
||||||
|
fun builder(c: @Suspend() (() -> Unit)): String {
|
||||||
var ok = false
|
var ok = false
|
||||||
|
c.startCoroutine(handleResultContinuation {
|
||||||
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
|
|
||||||
ok = true
|
ok = true
|
||||||
}
|
})
|
||||||
|
if (!ok) throw RuntimeException("Was not called")
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
|
||||||
val controller = Controller()
|
|
||||||
c(controller).resume(Unit)
|
|
||||||
if (!controller.ok) throw RuntimeException("Was not called")
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-15
@@ -1,21 +1,17 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var isCompleted = false
|
// WITH_COROUTINES
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
|
||||||
x.resume("OK")
|
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
isCompleted = true
|
x.resume("OK")
|
||||||
}
|
SUSPENDED
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (() -> Unit)) {
|
||||||
val controller = Controller()
|
var isCompleted = false
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
if (!controller.isCompleted) throw RuntimeException("fail")
|
isCompleted = true
|
||||||
|
})
|
||||||
|
if (!isCompleted) throw RuntimeException("fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var log = ""
|
var log = ""
|
||||||
|
|
||||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { x ->
|
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { x ->
|
||||||
log += "suspend($value);"
|
log += "suspend($value);"
|
||||||
x.resume(value)
|
x.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(value: String, y: Continuation<Nothing>) {
|
|
||||||
log += "return($value);"
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> String)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
|
controller.log += "return($it);"
|
||||||
|
})
|
||||||
return controller.log
|
return controller.log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-13
@@ -1,20 +1,19 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// NO_INTERCEPT_RESUME_TESTS
|
||||||
class Controller {
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
x.resume(Unit)
|
||||||
x.resume(Unit)
|
SUSPENDED
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder1(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder1(c: @Suspend() (() -> Unit)) {
|
||||||
(c as Continuation<Unit>).resume(Unit)
|
(c as Continuation<Unit>).resume(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder2(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder2(c: @Suspend() (() -> Unit)) {
|
||||||
val continuation = c(Controller())
|
val continuation = c.createCoroutine(EmptyContinuation)
|
||||||
val declaredField = continuation.javaClass.superclass.getDeclaredField("label")
|
val declaredField = continuation.javaClass.superclass.superclass.getDeclaredField("label")
|
||||||
declaredField.setAccessible(true)
|
declaredField.setAccessible(true)
|
||||||
declaredField.set(continuation, -3)
|
declaredField.set(continuation, -3)
|
||||||
continuation.resume(Unit)
|
continuation.resume(Unit)
|
||||||
@@ -27,8 +26,7 @@ fun box(): String {
|
|||||||
suspendHere()
|
suspendHere()
|
||||||
}
|
}
|
||||||
return "fail 1"
|
return "fail 1"
|
||||||
} catch (e: java.lang.IllegalStateException) {
|
} catch (e: kotlin.KotlinNullPointerException) {
|
||||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 2: ${e.message!!}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -47,8 +45,7 @@ fun box(): String {
|
|||||||
result = "fail 5"
|
result = "fail 5"
|
||||||
}
|
}
|
||||||
return "fail 6"
|
return "fail 6"
|
||||||
} catch (e: java.lang.IllegalStateException) {
|
} catch (e: kotlin.KotlinNullPointerException) {
|
||||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 7: ${e.message!!}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
||||||
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
||||||
@@ -10,7 +11,7 @@ class Controller {
|
|||||||
|
|
||||||
suspend inline fun suspendInline(v: String): String = suspendWithCurrentContinuation { x ->
|
suspend inline fun suspendInline(v: String): String = suspendWithCurrentContinuation { x ->
|
||||||
withValue(v, x)
|
withValue(v, x)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend inline fun suspendInline(crossinline b: () -> String): String = suspendInline(b())
|
suspend inline fun suspendInline(crossinline b: () -> String): String = suspendInline(b())
|
||||||
@@ -20,8 +21,8 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
class OK
|
class OK
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
var wasCalled = false
|
var wasCalled = false
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -9,7 +10,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -17,25 +18,21 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||||
globalResult = x
|
c.startCoroutine(this, handleResultContinuation {
|
||||||
}
|
globalResult = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(expectException: Boolean = false, coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(expectException: Boolean = false, c: @Suspend() (Controller.() -> String)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
|
|
||||||
globalResult = "#"
|
globalResult = "#"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var i = 0
|
var i = 0
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume((i++).toString())
|
x.resume((i++).toString())
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun runInstanceOf(): Boolean = suspendWithCurrentContinuation { x ->
|
suspend fun runInstanceOf(): Boolean = suspendWithCurrentContinuation { x ->
|
||||||
val y: Any = x
|
val y: Any = x
|
||||||
x.resume(x is Continuation<*>)
|
x.resume(x is Continuation<*>)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun runCast(): Boolean = suspendWithCurrentContinuation { x ->
|
suspend fun runCast(): Boolean = suspendWithCurrentContinuation { x ->
|
||||||
val y: Any = x
|
val y: Any = x
|
||||||
x.resume(Continuation::class.isInstance(y as Continuation<*>))
|
x.resume(Continuation::class.isInstance(y as Continuation<*>))
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo() = true
|
fun foo() = true
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var byteResult: Byte = 0
|
private var byteResult: Byte = 0
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var booleanResult = false
|
private var booleanResult = false
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var byteResult: Byte = 0
|
private var byteResult: Byte = 0
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var booleanResult = false
|
private var booleanResult = false
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var result: String = ""
|
private var result: String = ""
|
||||||
|
|||||||
+4
-3
@@ -1,16 +1,17 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var booleanResult = false
|
private var booleanResult = false
|
||||||
|
|||||||
+4
-3
@@ -1,16 +1,17 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+27
-11
@@ -1,3 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// NO_INTERCEPT_RESUME_TESTS
|
// NO_INTERCEPT_RESUME_TESTS
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -7,26 +9,40 @@ class Controller {
|
|||||||
suspend fun <T> suspendWithValue(value: T): T = suspendWithCurrentContinuation { continuation ->
|
suspend fun <T> suspendWithValue(value: T): T = suspendWithCurrentContinuation { continuation ->
|
||||||
log += "suspend($value);"
|
log += "suspend($value);"
|
||||||
continuation.resume(value)
|
continuation.resume(value)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(value: String): Unit = suspendWithCurrentContinuation { continuation ->
|
suspend fun suspendWithException(value: String): Unit = suspendWithCurrentContinuation { continuation ->
|
||||||
log += "error($value);"
|
log += "error($value);"
|
||||||
continuation.resumeWithException(RuntimeException(value))
|
continuation.resumeWithException(RuntimeException(value))
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun interceptResume(block: () -> Unit) {
|
|
||||||
var id = resumeIndex++
|
|
||||||
log += "before $id;"
|
|
||||||
block()
|
|
||||||
log += "after $id;"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun test(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, EmptyContinuation, object: ResumeInterceptor {
|
||||||
|
private fun interceptResume(block: () -> Unit) {
|
||||||
|
val id = controller.resumeIndex++
|
||||||
|
controller.log += "before $id;"
|
||||||
|
block()
|
||||||
|
controller.log += "after $id;"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <P> interceptResume(data: P, continuation: Continuation<P>): Boolean {
|
||||||
|
interceptResume {
|
||||||
|
continuation.resume(data)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun interceptResumeWithException(exception: Throwable, continuation: Continuation<*>): Boolean {
|
||||||
|
interceptResume {
|
||||||
|
continuation.resumeWithException(exception)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
return controller.log
|
return controller.log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+12
-16
@@ -1,22 +1,18 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var result = "fail"
|
// WITH_COROUTINES
|
||||||
suspend fun <V> suspendHere(v: V): V = suspendWithCurrentContinuation { x ->
|
// WITH_CONTINUATION
|
||||||
x.resume(v)
|
suspend fun <V> suspendHere(v: V): V = suspendWithCurrentContinuation { x ->
|
||||||
Suspend
|
x.resume(v)
|
||||||
}
|
SUSPENDED
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
|
||||||
result = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (() -> String)): String {
|
||||||
val controller = Controller()
|
var result = "fail"
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
|
result = it
|
||||||
|
})
|
||||||
|
|
||||||
return controller.result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun foo(): String = builder {
|
fun foo(): String = builder {
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
class Controller {
|
|
||||||
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
|
|
||||||
x.resume(v)
|
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
|
||||||
c(Controller(), 56L, "OK").resume(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun noinline(l: () -> String) = l()
|
|
||||||
inline fun inline(l: () -> String) = l()
|
|
||||||
|
|
||||||
fun box(): String {
|
|
||||||
var result = ""
|
|
||||||
|
|
||||||
builder { l, s ->
|
|
||||||
result = suspendHere(s + "#" + l)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != "OK#56") return "fail 1: $result"
|
|
||||||
|
|
||||||
builder { l, s ->
|
|
||||||
result = suspendHere(noinline {
|
|
||||||
s + "#" + l
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != "OK#56") return "fail 2: $result"
|
|
||||||
|
|
||||||
builder { l, s ->
|
|
||||||
result = suspendHere(inline {
|
|
||||||
s + "#" + l
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != "OK#56") return "fail 3: $result"
|
|
||||||
|
|
||||||
return "OK"
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var result = ""
|
var result = ""
|
||||||
var ok = false
|
var ok = false
|
||||||
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
||||||
result += v
|
result += v
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
|
controller.ok = true
|
||||||
|
})
|
||||||
if (!controller.ok) throw RuntimeException("Fail ok")
|
if (!controller.ok) throw RuntimeException("Fail ok")
|
||||||
return controller.result
|
return controller.result
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-16
@@ -1,22 +1,17 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var wasHandleResultCalled = false
|
// WITH_COROUTINES
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
|
||||||
wasHandleResultCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (() -> Unit)) {
|
||||||
val controller = Controller()
|
var wasHandleResultCalled = false
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
|
wasHandleResultCalled = true
|
||||||
|
})
|
||||||
|
|
||||||
if (!controller.wasHandleResultCalled) throw RuntimeException("fail 1")
|
if (!wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var wasHandleResultCalled = false
|
// WITH_COROUTINES
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
|
||||||
wasHandleResultCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (() -> Unit)) {
|
||||||
val controller = Controller()
|
var wasHandleResultCalled = false
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
|
wasHandleResultCalled = true
|
||||||
|
})
|
||||||
|
|
||||||
if (!controller.wasHandleResultCalled) throw RuntimeException("fail 1")
|
if (!wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
var varWithCustomSetter: String = ""
|
var varWithCustomSetter: String = ""
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var ok = false
|
var ok = false
|
||||||
var v = "fail"
|
var v = "fail"
|
||||||
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
||||||
this.v = v
|
this.v = v
|
||||||
x.resume(Unit)
|
x.resume(Unit)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
|
controller.ok = true
|
||||||
|
})
|
||||||
if (!controller.ok) throw RuntimeException("Fail 1")
|
if (!controller.ok) throw RuntimeException("Fail 1")
|
||||||
return controller.v
|
return controller.v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// IGNORE_BACKEND: JS
|
|
||||||
// WITH_RUNTIME
|
|
||||||
class Controller {
|
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
|
||||||
x.resume("OK")
|
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(Controller()).resume(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun box(): String {
|
|
||||||
var result = "fail"
|
|
||||||
|
|
||||||
val lambda: Controller.() -> Continuation<Unit> = l1@{
|
|
||||||
object : Continuation<Any?> {
|
|
||||||
override fun resume(data: Any?) {
|
|
||||||
if (data == Unit) {
|
|
||||||
this@l1.javaClass.getMethod("suspendHere", Continuation::class.java).invoke(this@l1, this)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data != "OK") {
|
|
||||||
throw RuntimeException("fail: $data")
|
|
||||||
}
|
|
||||||
|
|
||||||
result = "OK"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumeWithException(exception: Throwable) = throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder(lambda)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// MODULE: controller
|
// MODULE: controller
|
||||||
// FILE: controller.kt
|
// FILE: controller.kt
|
||||||
package lib
|
package lib
|
||||||
@@ -5,7 +7,7 @@ package lib
|
|||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
@@ -15,8 +17,8 @@ class Controller {
|
|||||||
// FILE: main.kt
|
// FILE: main.kt
|
||||||
import lib.*
|
import lib.*
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// MODULE: controller
|
// MODULE: controller
|
||||||
// FILE: controller.kt
|
// FILE: controller.kt
|
||||||
package lib
|
package lib
|
||||||
@@ -6,7 +8,7 @@ package lib
|
|||||||
class Controller {
|
class Controller {
|
||||||
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(this)
|
x.resume(this)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
|
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
|
||||||
@@ -26,8 +28,8 @@ suspend fun Controller.localSuspendExtension(v: String) = v.suspendHere()
|
|||||||
|
|
||||||
inline suspend fun Controller.localInlineSuspendExtension(v: String) = v.inlineSuspendHere()
|
inline suspend fun Controller.localInlineSuspendExtension(v: String) = v.inlineSuspendHere()
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+22
-23
@@ -1,9 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var lastSuspension: Continuation<String>? = null
|
var lastSuspension: Continuation<String>? = null
|
||||||
var result = "fail"
|
var result = "fail"
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
lastSuspension = x
|
lastSuspension = x
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasNext() = lastSuspension != null
|
fun hasNext() = lastSuspension != null
|
||||||
@@ -16,26 +18,26 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller1 = Controller()
|
val controller1 = Controller()
|
||||||
val controller2 = Controller()
|
val controller2 = Controller()
|
||||||
|
|
||||||
c(controller1).resume(Unit)
|
c.startCoroutine(controller1, EmptyContinuation)
|
||||||
c(controller2).resume(Unit)
|
c.startCoroutine(controller2, EmptyContinuation)
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
|
||||||
val controller1 = Controller()
|
|
||||||
val controller2 = Controller()
|
|
||||||
|
|
||||||
c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
runControllers(controller1, controller2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: additional parameters are not supported yet
|
||||||
|
//fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
||||||
|
// val controller1 = Controller()
|
||||||
|
// val controller2 = Controller()
|
||||||
|
//
|
||||||
|
// c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
// c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
//
|
||||||
|
// runControllers(controller1, controller2)
|
||||||
|
//}
|
||||||
|
|
||||||
private fun runControllers(controller1: Controller, controller2: Controller) {
|
private fun runControllers(controller1: Controller, controller2: Controller) {
|
||||||
while (controller1.hasNext()) {
|
while (controller1.hasNext()) {
|
||||||
@@ -72,28 +74,25 @@ fun box(): String {
|
|||||||
result = "OK"
|
result = "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
// with capture and params
|
// with capture
|
||||||
|
|
||||||
var x = "O"
|
var x = "O"
|
||||||
var y = "K"
|
var y = "K"
|
||||||
|
|
||||||
// no suspension
|
// no suspension
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 suspension
|
// 1 suspension
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
if (suspendHere() != "56") return@builder
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 suspensions
|
// 2 suspensions
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
if (suspendHere() != "56") return@builder
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
suspendHere()
|
suspendHere()
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-22
@@ -1,9 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var lastSuspension: Continuation<String>? = null
|
var lastSuspension: Continuation<String>? = null
|
||||||
var result = "fail"
|
var result = "fail"
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
lastSuspension = x
|
lastSuspension = x
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasNext() = lastSuspension != null
|
fun hasNext() = lastSuspension != null
|
||||||
@@ -16,26 +18,26 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller1 = Controller()
|
val controller1 = Controller()
|
||||||
val controller2 = Controller()
|
val controller2 = Controller()
|
||||||
|
|
||||||
c(controller1).resume(Unit)
|
c.startCoroutine(controller1, EmptyContinuation)
|
||||||
c(controller2).resume(Unit)
|
c.startCoroutine(controller2, EmptyContinuation)
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
|
||||||
val controller1 = Controller()
|
|
||||||
val controller2 = Controller()
|
|
||||||
|
|
||||||
c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
runControllers(controller1, controller2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: additional parameters are not supported yet
|
||||||
|
//fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
||||||
|
// val controller1 = Controller()
|
||||||
|
// val controller2 = Controller()
|
||||||
|
//
|
||||||
|
// c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
// c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
//
|
||||||
|
// runControllers(controller1, controller2)
|
||||||
|
//}
|
||||||
|
|
||||||
private fun runControllers(controller1: Controller, controller2: Controller) {
|
private fun runControllers(controller1: Controller, controller2: Controller) {
|
||||||
while (controller1.hasNext()) {
|
while (controller1.hasNext()) {
|
||||||
@@ -66,22 +68,19 @@ fun box(): String {
|
|||||||
// inlined
|
// inlined
|
||||||
run {
|
run {
|
||||||
// no suspension
|
// no suspension
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 suspension
|
// 1 suspension
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
if (suspendHere() != "56") return@builder
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 suspensions
|
// 2 suspensions
|
||||||
builder2 { a, b ->
|
builder {
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
if (suspendHere() != "56") return@builder
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
suspendHere()
|
suspendHere()
|
||||||
result = x + y
|
result = x + y
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-36
@@ -1,9 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var lastSuspension: Continuation<String>? = null
|
var lastSuspension: Continuation<String>? = null
|
||||||
var result = "fail"
|
var result = "fail"
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
lastSuspension = x
|
lastSuspension = x
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasNext() = lastSuspension != null
|
fun hasNext() = lastSuspension != null
|
||||||
@@ -16,26 +18,26 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller1 = Controller()
|
val controller1 = Controller()
|
||||||
val controller2 = Controller()
|
val controller2 = Controller()
|
||||||
|
|
||||||
c(controller1).resume(Unit)
|
c.startCoroutine(controller1, EmptyContinuation)
|
||||||
c(controller2).resume(Unit)
|
c.startCoroutine(controller2, EmptyContinuation)
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
|
||||||
val controller1 = Controller()
|
|
||||||
val controller2 = Controller()
|
|
||||||
|
|
||||||
c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
runControllers(controller1, controller2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: additional parameters are not supported yet
|
||||||
|
//fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
||||||
|
// val controller1 = Controller()
|
||||||
|
// val controller2 = Controller()
|
||||||
|
//
|
||||||
|
// c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
// c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
//
|
||||||
|
// runControllers(controller1, controller2)
|
||||||
|
//}
|
||||||
|
|
||||||
private fun runControllers(controller1: Controller, controller2: Controller) {
|
private fun runControllers(controller1: Controller, controller2: Controller) {
|
||||||
while (controller1.hasNext()) {
|
while (controller1.hasNext()) {
|
||||||
@@ -83,28 +85,6 @@ fun box(): String {
|
|||||||
suspendHere()
|
suspendHere()
|
||||||
result = "OK"
|
result = "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
// no suspension
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 suspension
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2 suspensions
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
suspendHere()
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-36
@@ -1,9 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var lastSuspension: Continuation<String>? = null
|
var lastSuspension: Continuation<String>? = null
|
||||||
var result = "fail"
|
var result = "fail"
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
lastSuspension = x
|
lastSuspension = x
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasNext() = lastSuspension != null
|
fun hasNext() = lastSuspension != null
|
||||||
@@ -16,26 +18,26 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
val controller1 = Controller()
|
val controller1 = Controller()
|
||||||
val controller2 = Controller()
|
val controller2 = Controller()
|
||||||
|
|
||||||
c(controller1).resume(Unit)
|
c.startCoroutine(controller1, EmptyContinuation)
|
||||||
c(controller2).resume(Unit)
|
c.startCoroutine(controller2, EmptyContinuation)
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
|
||||||
val controller1 = Controller()
|
|
||||||
val controller2 = Controller()
|
|
||||||
|
|
||||||
c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
|
||||||
|
|
||||||
runControllers(controller1, controller2)
|
runControllers(controller1, controller2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: additional parameters are not supported yet
|
||||||
|
//fun builder2(coroutine c: Controller.(Long, String) -> Continuation<Unit>) {
|
||||||
|
// val controller1 = Controller()
|
||||||
|
// val controller2 = Controller()
|
||||||
|
//
|
||||||
|
// c(controller1, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
// c(controller2, 1234567890123456789L, "Q").resume(Unit)
|
||||||
|
//
|
||||||
|
// runControllers(controller1, controller2)
|
||||||
|
//}
|
||||||
|
|
||||||
private fun runControllers(controller1: Controller, controller2: Controller) {
|
private fun runControllers(controller1: Controller, controller2: Controller) {
|
||||||
while (controller1.hasNext()) {
|
while (controller1.hasNext()) {
|
||||||
@@ -81,28 +83,6 @@ fun box(): String {
|
|||||||
suspendHere()
|
suspendHere()
|
||||||
result = "OK"
|
result = "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// no suspension
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1 suspension
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2 suspensions
|
|
||||||
builder2 { a, b ->
|
|
||||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
|
||||||
if (suspendHere() != "56") return@builder2
|
|
||||||
suspendHere()
|
|
||||||
result = x + y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} ()
|
} ()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
var wasCalled = false
|
var wasCalled = false
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -9,7 +10,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -17,15 +18,13 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||||
globalResult = x
|
c.startCoroutine(this, handleResultContinuation {
|
||||||
}
|
globalResult = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
@@ -35,7 +34,7 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(expectException: Boolean = false, coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(expectException: Boolean = false, c: @Suspend() (Controller.() -> String)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
|
|
||||||
globalResult = "#"
|
globalResult = "#"
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
|
||||||
|
fun builder(c: @Suspend() (() -> Int)): Int {
|
||||||
var res = 0
|
var res = 0
|
||||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
c.startCoroutine(handleResultContinuation {
|
||||||
res = x
|
res = it
|
||||||
}
|
})
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
return res
|
||||||
}
|
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
|
||||||
val controller = Controller()
|
|
||||||
c(controller).resume(Unit)
|
|
||||||
|
|
||||||
return controller.res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var cResult = 0
|
var cResult = 0
|
||||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v * 2)
|
x.resume(v * 2)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
|
||||||
cResult = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Controller {
|
fun builder(c: @Suspend() (Controller.() -> Int)): Controller {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
|
controller.cResult = it
|
||||||
|
})
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-9
@@ -1,22 +1,19 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
var cResult = 0
|
var cResult = 0
|
||||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v * 2)
|
x.resume(v * 2)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
|
||||||
cResult = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Controller {
|
fun builder(c: @Suspend() (Controller.() -> Int)): Controller {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(controller, handleResultContinuation {
|
||||||
|
controller.cResult = it
|
||||||
|
})
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-16
@@ -1,22 +1,17 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
var res = 0
|
// WITH_COROUTINES
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
|
||||||
res = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
fun builder(c: @Suspend() (() -> Int)): Int {
|
||||||
val controller = Controller()
|
var res = 0
|
||||||
c(controller).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
|
res = it
|
||||||
|
})
|
||||||
|
|
||||||
return controller.res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+8
-8
@@ -1,14 +1,14 @@
|
|||||||
class Controller {
|
// IGNORE_BACKEND: JS
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
// WITH_RUNTIME
|
||||||
x.resume("OK")
|
// WITH_COROUTINES
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
|
x.resume("OK")
|
||||||
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() () -> Unit) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resumeWithException(RuntimeException("OK"))
|
x.resumeWithException(RuntimeException("OK"))
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
class Controller {
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
|
x.resume("OK")
|
||||||
|
SUSPENDED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(c: @Suspend() () -> Int): Int {
|
||||||
var res = 0
|
var res = 0
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
|
||||||
x.resume("OK")
|
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
c.createCoroutine(object : Continuation<Int> {
|
||||||
res = x
|
override fun resume(data: Int) {
|
||||||
}
|
res = data
|
||||||
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}).resume(Unit)
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
|
||||||
val controller = Controller()
|
|
||||||
c(controller).resume(Unit)
|
|
||||||
|
|
||||||
return controller.res
|
|
||||||
}
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = throw RuntimeException("OK")
|
suspend fun suspendHere(): String = throw RuntimeException("OK")
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+3
-2
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
// WITH_REFLECT
|
// WITH_REFLECT
|
||||||
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
||||||
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
||||||
@@ -13,8 +14,8 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
class OK
|
class OK
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere() = "OK"
|
suspend fun suspendHere() = "OK"
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
||||||
1
|
1
|
||||||
@@ -9,8 +11,8 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
class Controller {
|
suspend fun suspendWithValue(v: String): String = suspendWithCurrentContinuation { x ->
|
||||||
suspend fun suspendWithValue(v: String): String = suspendWithCurrentContinuation { x ->
|
x.resume(v)
|
||||||
x.resume(v)
|
SUSPENDED
|
||||||
Suspend
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
|
||||||
globalResult = x
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (() -> String)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(handleResultContinuation {
|
||||||
|
globalResult = it
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendThere()
|
suspend fun suspendHere(): String = suspendThere()
|
||||||
|
|
||||||
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
@AllowSuspendExtensions
|
@AllowSuspendExtensions
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(this)
|
x.resume(this)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
|
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
|
||||||
@@ -14,8 +16,8 @@ suspend fun Controller.suspendExtension(v: String): String = v.suspendHere()
|
|||||||
|
|
||||||
inline suspend fun Controller.inlineSuspendExtension(v: String): String = v.inlineSuspendHere()
|
inline suspend fun Controller.inlineSuspendExtension(v: String): String = v.inlineSuspendHere()
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v * 2)
|
x.resume(v * 2)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun foo(x: (Int) -> Unit) {
|
inline fun foo(x: (Int) -> Unit) {
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
var i = 0
|
var i = 0
|
||||||
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(i++)
|
x.resume(i++)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("?")
|
x.resume("?")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+7
-5
@@ -1,24 +1,26 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("K")
|
x.resume("K")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithArgument(v: String): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithArgument(v: String): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v)
|
x.resume(v)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithDouble(v: Double): Double = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithDouble(v: Double): Double = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v)
|
x.resume(v)
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
class A(val first: String, val second: String) {
|
class A(val first: String, val second: String) {
|
||||||
|
|||||||
+8
-9
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
var wasCalled = false
|
var wasCalled = false
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -9,7 +10,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -17,15 +18,13 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||||
globalResult = x
|
c.startCoroutine(this, handleResultContinuation {
|
||||||
}
|
globalResult = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
@@ -35,7 +34,7 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(expectException: Boolean = false, coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(expectException: Boolean = false, c: @Suspend() (Controller.() -> String)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
|
|
||||||
globalResult = "#"
|
globalResult = "#"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
var wasCalled = false
|
var wasCalled = false
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -9,7 +10,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -17,25 +18,21 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||||
globalResult = x
|
c.startCoroutine(this, handleResultContinuation {
|
||||||
}
|
globalResult = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(expectException: Boolean = false, coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(expectException: Boolean = false, c: @Suspend() (Controller.() -> String)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
|
|
||||||
globalResult = "#"
|
globalResult = "#"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume(v)
|
x.resume(v)
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun run(block: () -> Unit) {
|
inline fun run(block: () -> Unit) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
var globalResult = ""
|
var globalResult = ""
|
||||||
var wasCalled = false
|
var wasCalled = false
|
||||||
class Controller {
|
class Controller {
|
||||||
@@ -9,7 +10,7 @@ class Controller {
|
|||||||
x.resume(v)
|
x.resume(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||||
@@ -17,15 +18,13 @@ class Controller {
|
|||||||
x.resumeWithException(e)
|
x.resumeWithException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||||
globalResult = x
|
c.startCoroutine(this, handleResultContinuation {
|
||||||
}
|
globalResult = it
|
||||||
|
})
|
||||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
|
||||||
c(this).resume(Unit)
|
|
||||||
while (postponedActions.isNotEmpty()) {
|
while (postponedActions.isNotEmpty()) {
|
||||||
postponedActions[0]()
|
postponedActions[0]()
|
||||||
postponedActions.removeAt(0)
|
postponedActions.removeAt(0)
|
||||||
@@ -35,7 +34,7 @@ class Controller {
|
|||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(expectException: Boolean = false, coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(expectException: Boolean = false, c: @Suspend() (Controller.() -> String)) {
|
||||||
val controller = Controller()
|
val controller = Controller()
|
||||||
|
|
||||||
globalResult = "#"
|
globalResult = "#"
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+5
-3
@@ -1,14 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
class Controller {
|
class Controller {
|
||||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||||
x.resume("OK")
|
x.resume("OK")
|
||||||
Suspend
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERCEPT_RESUME_PLACEHOLDER
|
// INTERCEPT_RESUME_PLACEHOLDER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||||
c(Controller()).resume(Unit)
|
c.startCoroutine(Controller(), EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+19
-25
@@ -43,32 +43,26 @@ fun box(): String {
|
|||||||
|
|
||||||
// LIBRARY CODE
|
// LIBRARY CODE
|
||||||
|
|
||||||
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
|
fun <T> async(c: @Suspend() (() -> T)): CompletableFuture<T> {
|
||||||
val controller = FutureController<T>()
|
|
||||||
c(controller).resume(Unit)
|
|
||||||
return controller.future
|
|
||||||
}
|
|
||||||
|
|
||||||
class FutureController<T> {
|
|
||||||
val future = CompletableFuture<T>()
|
val future = CompletableFuture<T>()
|
||||||
|
c.startCoroutine(object : Continuation<T> {
|
||||||
|
override fun resume(data: T) {
|
||||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
future.complete(data)
|
||||||
f.whenComplete { value, throwable ->
|
|
||||||
if (throwable == null)
|
|
||||||
machine.resume(value)
|
|
||||||
else
|
|
||||||
machine.resumeWithException(throwable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
override fun resumeWithException(exception: Throwable) {
|
||||||
}
|
future.completeExceptionally(exception)
|
||||||
|
}
|
||||||
operator fun handleResult(value: T, c: Continuation<Nothing>) {
|
})
|
||||||
future.complete(value)
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||||
future.completeExceptionally(t)
|
f.whenComplete { value, throwable ->
|
||||||
}
|
if (throwable == null)
|
||||||
|
machine.resume(value)
|
||||||
|
else
|
||||||
|
machine.resumeWithException(throwable)
|
||||||
|
}
|
||||||
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-24
@@ -40,31 +40,26 @@ fun box(): String {
|
|||||||
return "No exception"
|
return "No exception"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
|
fun <T> async(c: @Suspend() (() -> T)): CompletableFuture<T> {
|
||||||
val controller = FutureController<T>()
|
|
||||||
c(controller).resume(Unit)
|
|
||||||
return controller.future
|
|
||||||
}
|
|
||||||
|
|
||||||
class FutureController<T> {
|
|
||||||
val future = CompletableFuture<T>()
|
val future = CompletableFuture<T>()
|
||||||
|
c.startContinuation(object : Continuation<T> {
|
||||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
override fun resume(data: T) {
|
||||||
f.whenComplete { value, throwable ->
|
future.complete(data)
|
||||||
if (throwable == null)
|
|
||||||
machine.resume(value)
|
|
||||||
else
|
|
||||||
machine.resumeWithException(throwable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Suspend
|
override fun resumeWithException(exception: Throwable) {
|
||||||
}
|
future.completeExceptionally(exception)
|
||||||
|
}
|
||||||
operator fun handleResult(value: T, c: Continuation<Nothing>) {
|
})
|
||||||
future.complete(value)
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||||
future.completeExceptionally(t)
|
f.whenComplete { value, throwable ->
|
||||||
}
|
if (throwable == null)
|
||||||
|
machine.resume(value)
|
||||||
|
else
|
||||||
|
machine.resumeWithException(throwable)
|
||||||
|
}
|
||||||
|
SUSPENDED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isCompatibleTarget;
|
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isCompatibleTarget;
|
||||||
|
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isDirectiveDefined;
|
||||||
|
|
||||||
public class KotlinTestUtils {
|
public class KotlinTestUtils {
|
||||||
public static String TEST_MODULE_NAME = "test-module";
|
public static String TEST_MODULE_NAME = "test-module";
|
||||||
@@ -639,6 +640,37 @@ public class KotlinTestUtils {
|
|||||||
" to " +
|
" to " +
|
||||||
(expectedText.length() - 1);
|
(expectedText.length() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) {
|
||||||
|
testFiles.add(factory.createFile(null,
|
||||||
|
"CoroutineUtil.kt",
|
||||||
|
"fun <T> handleResultContinuation(x: (T) -> Unit): Continuation<T> = object: Continuation<T> {\n" +
|
||||||
|
" override fun resumeWithException(exception: Throwable) {\n" +
|
||||||
|
" throw exception\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" override fun resume(data: T) = x(data)\n" +
|
||||||
|
"}\n" +
|
||||||
|
"\n" +
|
||||||
|
"fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = object: Continuation<Any?> {\n" +
|
||||||
|
" override fun resumeWithException(exception: Throwable) {\n" +
|
||||||
|
" x(exception)\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" override fun resume(data: Any?) { }\n" +
|
||||||
|
"}\n" +
|
||||||
|
"\n" +
|
||||||
|
"object EmptyContinuation : Continuation<Any?> {\n" +
|
||||||
|
" override fun resume(data: Any?) {}\n" +
|
||||||
|
"\n" +
|
||||||
|
" override fun resumeWithException(exception: Throwable) {\n" +
|
||||||
|
" throw exception\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}",
|
||||||
|
directives
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return testFiles;
|
return testFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-12
@@ -4619,12 +4619,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
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("lastExpressionIsLoop.kt")
|
@TestMetadata("lastExpressionIsLoop.kt")
|
||||||
public void testLastExpressionIsLoop() throws Exception {
|
public void testLastExpressionIsLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||||
@@ -4649,12 +4643,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("manualContinuationImpl.kt")
|
|
||||||
public void testManualContinuationImpl() throws Exception {
|
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("multipleInvokeCalls.kt")
|
@TestMetadata("multipleInvokeCalls.kt")
|
||||||
public void testMultipleInvokeCalls() throws Exception {
|
public void testMultipleInvokeCalls() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||||
|
|||||||
+65
-12
@@ -144,12 +144,6 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
|
|||||||
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("lastExpressionIsLoop.kt")
|
@TestMetadata("lastExpressionIsLoop.kt")
|
||||||
public void testLastExpressionIsLoop() throws Exception {
|
public void testLastExpressionIsLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||||
@@ -174,12 +168,6 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("manualContinuationImpl.kt")
|
|
||||||
public void testManualContinuationImpl() throws Exception {
|
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("multipleInvokeCalls.kt")
|
@TestMetadata("multipleInvokeCalls.kt")
|
||||||
public void testMultipleInvokeCalls() throws Exception {
|
public void testMultipleInvokeCalls() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||||
@@ -332,6 +320,71 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/controlFlow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/controlFlow"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("breakFinally.kt")
|
||||||
|
public void testBreakFinally() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/breakFinally.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("breakStatement.kt")
|
||||||
|
public void testBreakStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/breakStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("doWhileStatement.kt")
|
||||||
|
public void testDoWhileStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/doWhileStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forContinue.kt")
|
||||||
|
public void testForContinue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/forContinue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("forStatement.kt")
|
||||||
|
public void testForStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/forStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ifStatement.kt")
|
||||||
|
public void testIfStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/ifStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnFromFinally.kt")
|
||||||
|
public void testReturnFromFinally() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("switchLikeWhen.kt")
|
||||||
|
public void testSwitchLikeWhen() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/switchLikeWhen.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("throwFromCatch.kt")
|
||||||
|
public void testThrowFromCatch() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/throwFromCatch.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("throwInTryWithHandleResult.kt")
|
||||||
|
public void testThrowInTryWithHandleResult() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/throwInTryWithHandleResult.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whileStatement.kt")
|
||||||
|
public void testWhileStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controlFlow/whileStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling")
|
@TestMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling")
|
||||||
|
|||||||
@@ -4619,12 +4619,6 @@ 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("lastExpressionIsLoop.kt")
|
@TestMetadata("lastExpressionIsLoop.kt")
|
||||||
public void testLastExpressionIsLoop() throws Exception {
|
public void testLastExpressionIsLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||||
@@ -4649,12 +4643,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("manualContinuationImpl.kt")
|
|
||||||
public void testManualContinuationImpl() throws Exception {
|
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("multipleInvokeCalls.kt")
|
@TestMetadata("multipleInvokeCalls.kt")
|
||||||
public void testMultipleInvokeCalls() throws Exception {
|
public void testMultipleInvokeCalls() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||||
|
|||||||
-12
@@ -4619,12 +4619,6 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
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("lastExpressionIsLoop.kt")
|
@TestMetadata("lastExpressionIsLoop.kt")
|
||||||
public void testLastExpressionIsLoop() throws Exception {
|
public void testLastExpressionIsLoop() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||||
@@ -4649,12 +4643,6 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("manualContinuationImpl.kt")
|
|
||||||
public void testManualContinuationImpl() throws Exception {
|
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("multipleInvokeCalls.kt")
|
@TestMetadata("multipleInvokeCalls.kt")
|
||||||
public void testMultipleInvokeCalls() throws Exception {
|
public void testMultipleInvokeCalls() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||||
|
|||||||
+26
-1
@@ -16,16 +16,21 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.load.kotlin
|
package org.jetbrains.kotlin.load.kotlin
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.*
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
|
||||||
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||||
import org.jetbrains.kotlin.utils.DO_NOTHING_3
|
import org.jetbrains.kotlin.utils.DO_NOTHING_3
|
||||||
|
|
||||||
@@ -65,6 +70,26 @@ fun <T : Any> mapType(
|
|||||||
descriptorTypeWriter: JvmDescriptorTypeWriter<T>?,
|
descriptorTypeWriter: JvmDescriptorTypeWriter<T>?,
|
||||||
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3
|
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3
|
||||||
): T {
|
): T {
|
||||||
|
if (kotlinType.isSuspendFunctionType) {
|
||||||
|
return mapType(
|
||||||
|
createFunctionType(
|
||||||
|
kotlinType.builtIns,
|
||||||
|
FilteredAnnotations(kotlinType.annotations) { it != DescriptorUtils.SUSPEND_ANNOTATION_FQ_NAME },
|
||||||
|
kotlinType.getReceiverTypeFromFunctionType(),
|
||||||
|
kotlinType.getValueParameterTypesFromFunctionType().map(TypeProjection::getType) +
|
||||||
|
KotlinTypeFactory.simpleType(
|
||||||
|
Annotations.EMPTY, kotlinType.builtIns.continuationClassDescriptor.typeConstructor,
|
||||||
|
listOf(kotlinType.getReturnTypeFromFunctionType().asTypeProjection()), nullable = false
|
||||||
|
),
|
||||||
|
// TODO: names
|
||||||
|
null,
|
||||||
|
kotlinType.builtIns.nullableAnyType
|
||||||
|
),
|
||||||
|
factory, mode, typeMappingConfiguration, descriptorTypeWriter,
|
||||||
|
writeGenericType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
mapBuiltInType(kotlinType, factory, typeMappingConfiguration)?.let { builtInType ->
|
mapBuiltInType(kotlinType, factory, typeMappingConfiguration)?.let { builtInType ->
|
||||||
val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing)
|
val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing)
|
||||||
writeGenericType(kotlinType, jvmType, mode)
|
writeGenericType(kotlinType, jvmType, mode)
|
||||||
|
|||||||
Reference in New Issue
Block a user