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.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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")
|
||||
|
||||
@@ -32,8 +29,3 @@ fun FunctionDescriptor.getBuiltInSuspendWithCurrentContinuation() =
|
||||
?.getMemberScope()
|
||||
?.getContributedFunctions(SUSPEND_WITH_CURRENT_CONTINUATION_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
?.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) {
|
||||
return genAssignInstanceFieldFromParam(info, index, iv, 0);
|
||||
}
|
||||
|
||||
public static int genAssignInstanceFieldFromParam(
|
||||
FieldInfo info,
|
||||
int index,
|
||||
InstructionAdapter iv,
|
||||
int ownerIndex
|
||||
) {
|
||||
assert !info.isStatic();
|
||||
Type fieldType = info.getFieldType();
|
||||
iv.load(0, info.getOwnerType());//this
|
||||
iv.load(ownerIndex, info.getOwnerType());//this
|
||||
iv.load(index, fieldType); //param
|
||||
iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
|
||||
index += fieldType.getSize();
|
||||
|
||||
@@ -77,12 +77,12 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
private final FunctionDescriptor functionReferenceTarget;
|
||||
private final FunctionGenerationStrategy strategy;
|
||||
protected final CalculatedClosure closure;
|
||||
private final Type asmType;
|
||||
private final int visibilityFlag;
|
||||
protected final Type asmType;
|
||||
protected final int visibilityFlag;
|
||||
private final boolean shouldHaveBoundReferenceReceiver;
|
||||
|
||||
private Method constructor;
|
||||
private Type superClassAsmType;
|
||||
protected Type superClassAsmType;
|
||||
|
||||
public ClosureCodegen(
|
||||
@NotNull GenerationState state,
|
||||
@@ -406,7 +406,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Method generateConstructor() {
|
||||
protected Method generateConstructor() {
|
||||
List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, asmType);
|
||||
|
||||
Type[] argTypes = fieldListToTypeArray(args);
|
||||
@@ -430,9 +430,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
|
||||
String superClassConstructorDescriptor;
|
||||
if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) || superClassAsmType.equals(COROUTINE_IMPL)) {
|
||||
int arity = funDescriptor.getValueParameters().size();
|
||||
if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
|
||||
if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
|
||||
int arity = calculateArity();
|
||||
iv.iconst(arity);
|
||||
if (shouldHaveBoundReferenceReceiver) {
|
||||
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType);
|
||||
@@ -455,6 +453,13 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
return constructor;
|
||||
}
|
||||
|
||||
protected int calculateArity() {
|
||||
int arity = funDescriptor.getValueParameters().size();
|
||||
if (funDescriptor.getExtensionReceiverParameter() != null) arity++;
|
||||
if (funDescriptor.getDispatchReceiverParameter() != null) arity++;
|
||||
return arity;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<FieldInfo> calculateConstructorParameters(
|
||||
@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.when.SwitchCodegen;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||
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.SyntheticFieldDescriptor;
|
||||
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.evaluate.ConstantExpressionEvaluator;
|
||||
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.inline.InlineUtil;
|
||||
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 MethodContext context;
|
||||
private final Type returnType;
|
||||
private final Type boxedReturnTypeForCoroutine;
|
||||
|
||||
private final CodegenStatementVisitor statementVisitor = new CodegenStatementVisitor(this);
|
||||
private final MemberCodegen<?> parentCodegen;
|
||||
@@ -160,11 +160,37 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
this.v = new InstructionAdapter(mv);
|
||||
this.myFrameMap = frameMap;
|
||||
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.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 {
|
||||
}
|
||||
|
||||
@@ -569,7 +595,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
statements.add(condition);
|
||||
|
||||
//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();
|
||||
}
|
||||
else {
|
||||
@@ -1718,10 +1744,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
KotlinType captureReceiver = closure.getCaptureReceiverType();
|
||||
if (captureReceiver != null) {
|
||||
Type asmType = typeMapper.mapType(captureReceiver);
|
||||
StackValue capturedReceiver =
|
||||
functionReferenceReceiver != null ? functionReferenceReceiver :
|
||||
StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
|
||||
generateExtensionReceiver(unwrapOriginalLambdaDescriptorForCoroutine(context));
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (expression.getParent() instanceof KtNamedFunction) {
|
||||
// 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
|
||||
@@ -1763,7 +1800,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
private StackValueWithLeaveTask generateBlock(
|
||||
@NotNull KtBlockExpression block,
|
||||
@NotNull List<KtExpression> statements,
|
||||
boolean isStatement,
|
||||
@Nullable Label labelBeforeLastExpression,
|
||||
@@ -1800,13 +1836,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
StackValue statementResult = isExpression ? gen(possiblyLabeledStatement) : genStatement(possiblyLabeledStatement);
|
||||
|
||||
if (!iterator.hasNext()) {
|
||||
StackValue handleResultValue = genControllerHandleResultForLastStatementInCoroutine(block, possiblyLabeledStatement);
|
||||
if (handleResultValue != null) {
|
||||
blockResult = handleResultValue;
|
||||
}
|
||||
else {
|
||||
blockResult = statementResult;
|
||||
}
|
||||
blockResult = statementResult;
|
||||
}
|
||||
else {
|
||||
statementResult.put(Type.VOID_TYPE, v);
|
||||
@@ -1816,13 +1846,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
if (statements.isEmpty()) {
|
||||
StackValue handleResultValue = genControllerHandleResultForLastStatementInCoroutine(block, null);
|
||||
if (handleResultValue != null) {
|
||||
blockResult = handleResultValue;
|
||||
}
|
||||
else {
|
||||
blockResult = StackValue.none();
|
||||
}
|
||||
blockResult = StackValue.none();
|
||||
}
|
||||
|
||||
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
|
||||
private StackValue getCoroutineInstanceValueForSuspensionPoint(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
CoroutineReceiverValue coroutineReceiverValue =
|
||||
bindingContext.get(COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, resolvedCall.getCall());
|
||||
CallableDescriptor enclosingSuspendLambdaForSuspensionPoint =
|
||||
bindingContext.get(ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, resolvedCall.getCall());
|
||||
|
||||
if (coroutineReceiverValue == null) return null;
|
||||
|
||||
return getCoroutineInstanceValueByReceiver(coroutineReceiverValue);
|
||||
if (enclosingSuspendLambdaForSuspensionPoint == null) return null;
|
||||
return genCoroutineInstanceByLambda(enclosingSuspendLambdaForSuspensionPoint);
|
||||
}
|
||||
|
||||
private static ExtensionReceiver getControllerReceiverFromResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
ReceiverValue controllerReceiver =
|
||||
resolvedCall.getDispatchReceiver() != null
|
||||
? resolvedCall.getDispatchReceiver()
|
||||
: resolvedCall.getExtensionReceiver();
|
||||
@NotNull
|
||||
private StackValue genCoroutineInstanceByLambda(@NotNull CallableDescriptor suspendLambda) {
|
||||
ClassDescriptor suspendLambdaClassDescriptor =
|
||||
bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, suspendLambda);
|
||||
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();
|
||||
assert controllerReceiver instanceof ExtensionReceiver
|
||||
: "Argument for handleResult call to " + resolvedCall.getResultingDescriptor() +
|
||||
" should be a coroutine receiver parameter, but " + controllerReceiver + " found";
|
||||
|
||||
return (ExtensionReceiver) controllerReceiver;
|
||||
return StackValue.thisOrOuter(this, suspendLambdaClassDescriptor, false, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -2204,7 +2143,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
@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>() {
|
||||
@Override
|
||||
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;
|
||||
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : null;
|
||||
StackValue handleResultValue = genControllerHandleResultCallIfNeeded(expression, returnedExpression);
|
||||
|
||||
if (handleResultValue != null) {
|
||||
handleResultValue.put(Type.VOID_TYPE, v);
|
||||
returnType = Type.VOID_TYPE;
|
||||
}
|
||||
else if (returnedExpression != null && valueToReturn != null) {
|
||||
if (returnedExpression != null && valueToReturn != null) {
|
||||
putStackValue(returnedExpression, returnType, valueToReturn);
|
||||
}
|
||||
|
||||
@@ -2235,9 +2169,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
|
||||
|
||||
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);
|
||||
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
||||
}
|
||||
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
|
||||
else {
|
||||
emitLocalReturnInsn();
|
||||
}
|
||||
|
||||
v.mark(afterReturnLabel);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
@@ -2270,7 +2213,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
FunctionDescriptor containingFunction =
|
||||
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
|
||||
//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 {
|
||||
//local
|
||||
return null;
|
||||
@@ -2282,7 +2225,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
DeclarationDescriptor elementDescriptor = typeMapper.getBindingContext().get(DECLARATION_TO_DESCRIPTOR, element);
|
||||
assert element != null : "Expression should be not null " + expression.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;
|
||||
@@ -2307,6 +2258,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -3098,46 +3059,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@NotNull
|
||||
private StackValue generateExtensionReceiver(@NotNull CallableDescriptor descriptor) {
|
||||
KotlinType coroutineControllerType = CoroutineUtilKt.getControllerTypeIfCoroutine(descriptor);
|
||||
if (coroutineControllerType != null) {
|
||||
ClassDescriptor classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, descriptor);
|
||||
assert classDescriptor != null : "class descriptor for coroutine " + descriptor + " should not be null";
|
||||
|
||||
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));
|
||||
if (myFrameMap.getIndex(descriptor.getExtensionReceiverParameter()) != -1) {
|
||||
return StackValue.local(
|
||||
myFrameMap.getIndex(descriptor.getExtensionReceiverParameter()),
|
||||
typeMapper.mapType(descriptor.getExtensionReceiverParameter())
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
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;
|
||||
labelName = name;
|
||||
this.boxedCoroutineReturnType = boxedCoroutineReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationClassDescriptor
|
||||
import org.jetbrains.kotlin.codegen.coroutines.hasNoinlineInterceptResume
|
||||
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.coroutines.isSuspendLambda
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
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.resolve.descriptorUtil.builtIns
|
||||
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.utils.singletonOrEmptyList
|
||||
|
||||
class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||
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") }
|
||||
}
|
||||
|
||||
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 =
|
||||
MutableClassDescriptor(packageFragment, ClassKind.CLASS, /* isInner = */ false, /* isExternal = */ false,
|
||||
Name.identifier(name), SourceElement.NO_SOURCE).apply {
|
||||
@@ -76,33 +58,24 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
|
||||
}
|
||||
|
||||
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
|
||||
|
||||
val actualFunctionDescriptor =
|
||||
if (descriptor.isSuspendLambda)
|
||||
createJvmSuspendFunctionView(descriptor)
|
||||
else
|
||||
descriptor
|
||||
|
||||
val functionType = createFunctionType(
|
||||
descriptor.builtIns,
|
||||
Annotations.EMPTY,
|
||||
descriptor.extensionReceiverParameter?.type,
|
||||
ExpressionTypingUtils.getValueParametersTypes(descriptor.valueParameters),
|
||||
actualFunctionDescriptor.extensionReceiverParameter?.type,
|
||||
ExpressionTypingUtils.getValueParametersTypes(actualFunctionDescriptor.valueParameters),
|
||||
null,
|
||||
descriptor.returnType!!
|
||||
actualFunctionDescriptor.returnType!!
|
||||
)
|
||||
|
||||
val coroutineControllerType = descriptor.controllerTypeIfCoroutine
|
||||
if (coroutineControllerType != null) {
|
||||
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()
|
||||
if (descriptor.isSuspendLambda) {
|
||||
return listOf(coroutineImplClass.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.when.SwitchCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
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.fileClasses.FileClasses;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||
@@ -290,31 +288,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
classStack.push(classDescriptor);
|
||||
nameStack.push(name);
|
||||
|
||||
KotlinType controllerTypeIfCoroutine = CoroutineUtilKt.getControllerTypeIfCoroutine(functionDescriptor);
|
||||
if (controllerTypeIfCoroutine != null) {
|
||||
if (CoroutineUtilKt.isSuspendLambda(functionDescriptor)) {
|
||||
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);
|
||||
|
||||
+113
-298
@@ -16,17 +16,15 @@
|
||||
|
||||
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.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
||||
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.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
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.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
@@ -54,98 +52,143 @@ class CoroutineCodegen(
|
||||
strategy: FunctionGenerationStrategy,
|
||||
parentCodegen: MemberCodegen<*>,
|
||||
classBuilder: ClassBuilder,
|
||||
private val coroutineLambdaDescriptor: FunctionDescriptor,
|
||||
private val controllerType: KotlinType
|
||||
private val coroutineLambdaDescriptor: FunctionDescriptor
|
||||
) : 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() {
|
||||
for (parameter in funDescriptor.valueParameters) {
|
||||
for (parameter in allLambdaParameters()) {
|
||||
val fieldInfo = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||
v.newField(
|
||||
OtherOrigin(parameter),
|
||||
Opcodes.ACC_PRIVATE,
|
||||
COROUTINE_LAMBDA_PARAMETER_PREFIX + parameter.index,
|
||||
typeMapper.mapType(parameter.type).descriptor, null, null)
|
||||
fieldInfo.fieldName,
|
||||
fieldInfo.fieldType.descriptor, null, null
|
||||
)
|
||||
}
|
||||
|
||||
generateDoResume()
|
||||
}
|
||||
|
||||
override fun generateBody() {
|
||||
super.generateBody()
|
||||
|
||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
generateInvokeMethod(codegen, signature)
|
||||
generateInvokeMethod(codegen)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// invoke for lambda being passes to builder
|
||||
// fun builder(coroutine c: Controller.() -> Continuation<Unit>)
|
||||
//
|
||||
// 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) {
|
||||
override fun generateConstructor(): Method {
|
||||
val args = calculateConstructorParameters(typeMapper, closure, asmType)
|
||||
val argTypes = args.map { it.fieldType }.plus(AsmTypes.CONTINUATION).toTypedArray()
|
||||
|
||||
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 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)
|
||||
|
||||
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)
|
||||
dup()
|
||||
|
||||
// pass closure parameters to constructor
|
||||
// pass captured closure to constructor
|
||||
val constructorParameters = calculateConstructorParameters(typeMapper, closure, owner)
|
||||
for (parameter in constructorParameters) {
|
||||
StackValue.field(parameter, thisInstance).put(parameter.fieldType, this)
|
||||
}
|
||||
|
||||
val constructor = Method("<init>", Type.VOID_TYPE, constructorParameters.map { it.fieldType }.toTypedArray())
|
||||
invokespecial(owner.internalName, constructor.name, constructor.descriptor, false)
|
||||
// load resultContinuation
|
||||
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
|
||||
index = 1
|
||||
for (parameter in signature.valueParameters) {
|
||||
load(index, parameter.asmType)
|
||||
index += parameter.asmType.size
|
||||
var index = 1
|
||||
for (parameter in allLambdaParameters()) {
|
||||
val fieldInfoForCoroutineLambdaParameter = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||
load(index, fieldInfoForCoroutineLambdaParameter.fieldType)
|
||||
AsmUtil.genAssignInstanceFieldFromParam(fieldInfoForCoroutineLambdaParameter, index, this, cloneIndex)
|
||||
index += fieldInfoForCoroutineLambdaParameter.fieldType.size
|
||||
}
|
||||
|
||||
// 'invoke' call on freshly constructed coroutine returns receiver itself
|
||||
invokevirtual(owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false)
|
||||
load(cloneIndex, AsmTypes.OBJECT_TYPE)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExpressionCodegen.initializeCoroutineParameters() {
|
||||
for (parameter in coroutineLambdaDescriptor.valueParameters) {
|
||||
for (parameter in allLambdaParameters()) {
|
||||
val mappedType = typeMapper.mapType(parameter.type)
|
||||
val newIndex = myFrameMap.enter(parameter, mappedType)
|
||||
|
||||
@@ -154,12 +197,15 @@ class CoroutineCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun allLambdaParameters() =
|
||||
coroutineLambdaDescriptor.extensionReceiverParameter.singletonOrEmptyList()
|
||||
|
||||
private fun ExpressionCodegen.generateLoadField(fieldInfo: FieldInfo) {
|
||||
StackValue.field(fieldInfo, generateThisOrOuter(context.thisDescriptor, false)).put(fieldInfo.fieldType, v)
|
||||
}
|
||||
|
||||
private fun ValueParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
||||
createHiddenFieldInfo(type, COROUTINE_LAMBDA_PARAMETER_PREFIX + index)
|
||||
private fun ParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
||||
createHiddenFieldInfo(type, COROUTINE_LAMBDA_PARAMETER_PREFIX + (this.safeAs<ValueParameterDescriptor>()?.index ?: ""))
|
||||
|
||||
private fun createHiddenFieldInfo(type: KotlinType, name: String) =
|
||||
FieldInfo.createForHiddenField(
|
||||
@@ -168,243 +214,21 @@ class CoroutineCodegen(
|
||||
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() {
|
||||
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(
|
||||
OtherOrigin(element),
|
||||
doResumeDescriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
AsmUtil.genAssignInstanceFieldFromParam(
|
||||
fieldInfoForValue, COROUTINE_VALUE_PARAMETER_SLOT_IN_DO_RESUME, codegen.v
|
||||
)
|
||||
|
||||
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)
|
||||
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
||||
codegen.initializeCoroutineParameters()
|
||||
super.doGenerateBody(codegen, signature)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
private const val LABEL_VALUE_BEFORE_FIRST_SUSPENSION = 0
|
||||
|
||||
@JvmStatic
|
||||
fun create(
|
||||
@@ -414,13 +238,9 @@ class CoroutineCodegen(
|
||||
classBuilder: ClassBuilder
|
||||
): ClosureCodegen? {
|
||||
if (declaration !is KtFunctionLiteral) return null
|
||||
val controllerType = originalCoroutineLambdaDescriptor.controllerTypeIfCoroutine ?: return null
|
||||
if (!originalCoroutineLambdaDescriptor.isSuspendLambda) return null
|
||||
|
||||
val descriptorWithContinuationReturnType =
|
||||
originalCoroutineLambdaDescriptor.newCopyBuilder()
|
||||
.setPreserveSourceElement()
|
||||
.setReturnType(expressionCodegen.state.jvmRuntimeTypes.continuationOfAny)
|
||||
.build()!!
|
||||
val descriptorWithContinuationReturnType = createJvmSuspendFunctionView(originalCoroutineLambdaDescriptor)
|
||||
|
||||
val state = expressionCodegen.state
|
||||
return CoroutineCodegen(
|
||||
@@ -431,15 +251,10 @@ class CoroutineCodegen(
|
||||
),
|
||||
FunctionGenerationStrategy.FunctionDefault(state, declaration),
|
||||
expressionCodegen.parentCodegen, classBuilder,
|
||||
originalCoroutineLambdaDescriptor,
|
||||
controllerType
|
||||
originalCoroutineLambdaDescriptor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.insnListOf
|
||||
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.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
@@ -119,17 +118,12 @@ class CoroutineTransformerMethodVisitor(
|
||||
// tableswitch(this.label)
|
||||
insertBefore(firstToInsertBefore,
|
||||
insnListOf(
|
||||
FieldInsnNode(
|
||||
Opcodes.GETSTATIC,
|
||||
AsmTypes.COROUTINES_SUSPEND.internalName,
|
||||
JvmAbi.INSTANCE_FIELD,
|
||||
AsmTypes.COROUTINES_SUSPEND.descriptor
|
||||
),
|
||||
*withInstructionAdapter { loadSuspendMarker() }.toArray(),
|
||||
VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex),
|
||||
VarInsnNode(Opcodes.ALOAD, 0),
|
||||
FieldInsnNode(
|
||||
Opcodes.GETFIELD,
|
||||
AsmTypes.COROUTINE_IMPL.internalName,
|
||||
AsmTypes.RESTRICTED_COROUTINE_IMPL.internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
),
|
||||
TableSwitchInsnNode(0,
|
||||
@@ -338,7 +332,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
VarInsnNode(Opcodes.ALOAD, 0),
|
||||
*withInstructionAdapter { iconst(id) }.toArray(),
|
||||
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
|
||||
)
|
||||
)
|
||||
@@ -349,11 +343,12 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
|
||||
dup()
|
||||
load(suspendMarkerVarIndex, AsmTypes.COROUTINES_SUSPEND)
|
||||
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
|
||||
ifacmpne(continuationLabelAfterLoadedResult.label)
|
||||
|
||||
// Exit
|
||||
areturn(Type.VOID_TYPE)
|
||||
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
// Mark place for continuation
|
||||
visitLabel(continuationLabel.label)
|
||||
})
|
||||
|
||||
+15
-17
@@ -18,20 +18,18 @@ package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
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.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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.MutableDataFlowInfoForArguments
|
||||
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.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeConstructorSubstitution
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
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
|
||||
|
||||
// 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 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"
|
||||
|
||||
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
|
||||
@@ -188,7 +185,7 @@ fun createResolvedCallForInterceptResume(
|
||||
}
|
||||
|
||||
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>`,
|
||||
// and return type Any?
|
||||
@@ -237,12 +234,6 @@ private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction() =
|
||||
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 {
|
||||
if (name != SUSPEND_WITH_CURRENT_CONTINUATION_NAME) return false
|
||||
|
||||
@@ -286,3 +277,10 @@ fun createMethodNodeForSuspendWithCurrentContinuation(
|
||||
|
||||
fun CallableDescriptor?.unwrapInitialDescriptorForSuspendFunction() =
|
||||
(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 FqName COROUTINE_SUSPENDED_PROPERTY = new FqName("kotlin.coroutines.SUSPENDED");
|
||||
|
||||
@Nullable
|
||||
private static ContainingClassesInfo getPackageMemberContainingClassesInfo(@NotNull DeserializedCallableMemberDescriptor descriptor) {
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
|
||||
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 = {
|
||||
|
||||
+4
-10
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// FILE: promise.kt
|
||||
class Promise<T>(private val executor: ((T) -> Unit) -> Unit) {
|
||||
@@ -48,12 +49,6 @@ fun processQueue() {
|
||||
// FILE: await.kt
|
||||
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
|
||||
|
||||
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
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
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 ->
|
||||
val controller = Controller<T> { resolve(it) }
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(handleResultContinuation(resolve))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-14
@@ -1,22 +1,22 @@
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
// WITH_COROUTINES
|
||||
|
||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x -> }
|
||||
|
||||
fun builder(c: @Suspend() (() -> Unit)) {
|
||||
var exception: Throwable? = null
|
||||
|
||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||
exception = t
|
||||
}
|
||||
c.createCoroutine(object : Continuation<Unit> {
|
||||
override fun resume(data: Unit) {
|
||||
}
|
||||
|
||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x -> }
|
||||
override fun resumeWithException(e: Throwable) {
|
||||
exception = e
|
||||
}
|
||||
}).resumeWithException(RuntimeException("OK"))
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
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}")
|
||||
if (exception?.message != "OK") {
|
||||
throw RuntimeException("Unexpected result: ${exception?.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-8
@@ -1,14 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x ->}
|
||||
// WITH_COROUTINES
|
||||
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x ->}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (() -> Unit)) {
|
||||
try {
|
||||
val controller = Controller()
|
||||
c(controller).resumeWithException(RuntimeException("OK"))
|
||||
c.createCoroutine(EmptyContinuation).resumeWithException(RuntimeException("OK"))
|
||||
}
|
||||
catch(e: Exception) {
|
||||
if (e?.message != "OK") {
|
||||
|
||||
+11
-15
@@ -1,21 +1,17 @@
|
||||
class Controller {
|
||||
var result = "fail"
|
||||
operator fun handleResult(u: Unit, c: Continuation<Nothing>) {
|
||||
result = "OK"
|
||||
}
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
suspend fun <T> await(t: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(t)
|
||||
Suspend
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
suspend fun <T> await(t: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(t)
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
return controller.result
|
||||
fun builder(c: @Suspend() (() -> Unit)): String {
|
||||
var result = "fail"
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
result = "OK"
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
var TRUE = true
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// TODO: fix the bug and enable for JVM backend
|
||||
@@ -11,17 +11,15 @@ class Controller {
|
||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
result += "suspend($value);"
|
||||
c.resume(value)
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(value: String, c: Continuation<Nothing>) {
|
||||
result += "return($value);"
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(coroutine c: () -> String): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(handleResult {
|
||||
controller.result += "return($value);"
|
||||
})
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
@@ -7,13 +7,13 @@ class Controller {
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
result += "["
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
@@ -7,24 +7,29 @@ class Controller {
|
||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
result += "suspend($value);"
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// Tail calls are not allowed to be Nothing typed. See KT-15051
|
||||
suspend fun suspendLogAndThrow(exception: Throwable): Any? = suspendWithCurrentContinuation { c ->
|
||||
result += "throw(${exception.message});"
|
||||
c.resumeWithException(exception)
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
|
||||
result += "caught(${exception.message});"
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+10
-8
@@ -1,5 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
@@ -7,17 +7,19 @@ class Controller {
|
||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
result += "suspend($value);"
|
||||
c.resume(value)
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
|
||||
result += "ignoreCaught(${exception.message});"
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
|
||||
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
|
||||
c.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
return controller.result
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -1,8 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var result = false
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
@@ -12,9 +14,9 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, EmptyContinuation)
|
||||
if (!controller.result) throw RuntimeException("fail")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(a: String = "abc", i: Int = 2): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(a + "#" + (i + 1))
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var result = 0
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
result++
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun <T> suspendHere(v: T): T = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
var result: Any = ""
|
||||
|
||||
+22
-22
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// FULL_JDK
|
||||
|
||||
fun box(): String {
|
||||
@@ -24,35 +25,34 @@ fun gen() = generate<Int> {
|
||||
}
|
||||
|
||||
// LIBRARY CODE
|
||||
fun <T> generate(coroutine c: GeneratorController<T>.() -> Continuation<Unit>): Sequence<T> = object : Sequence<T> {
|
||||
override fun iterator(): Iterator<T> {
|
||||
val iterator = GeneratorController<T>()
|
||||
iterator.setNextStep(c(iterator))
|
||||
return iterator
|
||||
}
|
||||
interface Generator<in T> {
|
||||
suspend fun yield(value: T)
|
||||
}
|
||||
|
||||
class GeneratorController<T>() : AbstractIterator<T>() {
|
||||
private lateinit var nextStep: Continuation<Unit>
|
||||
fun <T> generate(block: @Suspend() (Generator<T>.() -> Unit)): Sequence<T> = GeneratedSequence(block)
|
||||
|
||||
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() {
|
||||
nextStep.resume(Unit)
|
||||
}
|
||||
|
||||
fun setNextStep(step: Continuation<Unit>) {
|
||||
this.nextStep = step
|
||||
}
|
||||
|
||||
suspend fun yield(value: T): Unit = suspendWithCurrentContinuation { c ->
|
||||
suspend override fun yield(value: T) = suspendWithCurrentContinuation<Unit> { c ->
|
||||
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_COROUTINES
|
||||
class Controller {
|
||||
var exception: Throwable? = null
|
||||
val postponedActions = ArrayList<() -> Unit>()
|
||||
@@ -8,7 +9,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -16,25 +17,21 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||
exception = t
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(this, handleExceptionContinuation {
|
||||
exception = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[0]()
|
||||
postponedActions.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller = Controller()
|
||||
controller.run(c)
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
class Controller {
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
fun builder(c: @Suspend() (() -> Unit)): String {
|
||||
var ok = false
|
||||
|
||||
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
ok = true
|
||||
}
|
||||
|
||||
// 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")
|
||||
})
|
||||
if (!ok) throw RuntimeException("Was not called")
|
||||
return "OK"
|
||||
}
|
||||
|
||||
|
||||
+11
-15
@@ -1,21 +1,17 @@
|
||||
class Controller {
|
||||
var isCompleted = false
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
||||
isCompleted = true
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
if (!controller.isCompleted) throw RuntimeException("fail")
|
||||
fun builder(c: @Suspend() (() -> Unit)) {
|
||||
var isCompleted = false
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
isCompleted = true
|
||||
})
|
||||
if (!isCompleted) throw RuntimeException("fail")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var log = ""
|
||||
|
||||
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { x ->
|
||||
log += "suspend($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()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.log += "return($it);"
|
||||
})
|
||||
return controller.log
|
||||
}
|
||||
|
||||
|
||||
+10
-13
@@ -1,20 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// TARGET_BACKEND: JVM
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
}
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder1(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder1(c: @Suspend() (() -> Unit)) {
|
||||
(c as Continuation<Unit>).resume(Unit)
|
||||
}
|
||||
|
||||
fun builder2(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
val continuation = c(Controller())
|
||||
val declaredField = continuation.javaClass.superclass.getDeclaredField("label")
|
||||
fun builder2(c: @Suspend() (() -> Unit)) {
|
||||
val continuation = c.createCoroutine(EmptyContinuation)
|
||||
val declaredField = continuation.javaClass.superclass.superclass.getDeclaredField("label")
|
||||
declaredField.setAccessible(true)
|
||||
declaredField.set(continuation, -3)
|
||||
continuation.resume(Unit)
|
||||
@@ -27,8 +26,7 @@ fun box(): String {
|
||||
suspendHere()
|
||||
}
|
||||
return "fail 1"
|
||||
} catch (e: java.lang.IllegalStateException) {
|
||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 2: ${e.message!!}"
|
||||
} catch (e: kotlin.KotlinNullPointerException) {
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -47,8 +45,7 @@ fun box(): String {
|
||||
result = "fail 5"
|
||||
}
|
||||
return "fail 6"
|
||||
} catch (e: java.lang.IllegalStateException) {
|
||||
if (e.message != "call to 'resume' before 'invoke' with coroutine") return "fail 7: ${e.message!!}"
|
||||
} catch (e: kotlin.KotlinNullPointerException) {
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// WITH_REFLECT
|
||||
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
||||
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
||||
@@ -10,7 +11,7 @@ class Controller {
|
||||
|
||||
suspend inline fun suspendInline(v: String): String = suspendWithCurrentContinuation { x ->
|
||||
withValue(v, x)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend inline fun suspendInline(crossinline b: () -> String): String = suspendInline(b())
|
||||
@@ -20,8 +21,8 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
class OK
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
class Controller {
|
||||
@@ -9,7 +10,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -17,25 +18,21 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||
c.startCoroutine(this, handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[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()
|
||||
|
||||
globalResult = "#"
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var i = 0
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume((i++).toString())
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// WITH_REFLECT
|
||||
|
||||
class Controller {
|
||||
suspend fun runInstanceOf(): Boolean = suspendWithCurrentContinuation { x ->
|
||||
val y: Any = x
|
||||
x.resume(x is Continuation<*>)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun runCast(): Boolean = suspendWithCurrentContinuation { x ->
|
||||
val y: Any = x
|
||||
x.resume(Continuation::class.isInstance(y as Continuation<*>))
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun foo() = true
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var byteResult: Byte = 0
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var byteResult: Byte = 0
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var result: String = ""
|
||||
|
||||
+4
-3
@@ -1,16 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// TARGET_BACKEND: JVM
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
|
||||
+4
-3
@@ -1,16 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// TARGET_BACKEND: JVM
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
|
||||
x.resume(Unit)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+28
-12
@@ -1,3 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// NO_INTERCEPT_RESUME_TESTS
|
||||
|
||||
class Controller {
|
||||
@@ -7,26 +9,40 @@ class Controller {
|
||||
suspend fun <T> suspendWithValue(value: T): T = suspendWithCurrentContinuation { continuation ->
|
||||
log += "suspend($value);"
|
||||
continuation.resume(value)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(value: String): Unit = suspendWithCurrentContinuation { continuation ->
|
||||
log += "error($value);"
|
||||
continuation.resumeWithException(RuntimeException(value))
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun interceptResume(block: () -> Unit) {
|
||||
var id = resumeIndex++
|
||||
log += "before $id;"
|
||||
block()
|
||||
log += "after $id;"
|
||||
SUSPENDED
|
||||
}
|
||||
}
|
||||
|
||||
fun test(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
fun test(c: @Suspend() (Controller.() -> Unit)): String {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -50,4 +66,4 @@ fun box(): String {
|
||||
if (result != "before 0;error(OK);before 1;OK;after 1;after 0;") return "fail2: $result"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+12
-16
@@ -1,22 +1,18 @@
|
||||
class Controller {
|
||||
var result = "fail"
|
||||
suspend fun <V> suspendHere(v: V): V = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
result = x
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// WITH_CONTINUATION
|
||||
suspend fun <V> suspendHere(v: V): V = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): String {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
fun builder(c: @Suspend() (() -> String)): String {
|
||||
var result = "fail"
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
result = it
|
||||
})
|
||||
|
||||
return controller.result
|
||||
return result
|
||||
}
|
||||
|
||||
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 {
|
||||
var result = ""
|
||||
var ok = false
|
||||
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
||||
result += v
|
||||
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()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.ok = true
|
||||
})
|
||||
if (!controller.ok) throw RuntimeException("Fail ok")
|
||||
return controller.result
|
||||
}
|
||||
|
||||
+11
-16
@@ -1,22 +1,17 @@
|
||||
class Controller {
|
||||
var wasHandleResultCalled = false
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
||||
wasHandleResultCalled = true
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
fun builder(c: @Suspend() (() -> Unit)) {
|
||||
var wasHandleResultCalled = false
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
wasHandleResultCalled = true
|
||||
})
|
||||
|
||||
if (!controller.wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||
if (!wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
class Controller {
|
||||
var wasHandleResultCalled = false
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
|
||||
wasHandleResultCalled = true
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
fun builder(c: @Suspend() (() -> Unit)) {
|
||||
var wasHandleResultCalled = false
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
wasHandleResultCalled = true
|
||||
})
|
||||
|
||||
if (!controller.wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||
if (!wasHandleResultCalled) throw RuntimeException("fail 1")
|
||||
}
|
||||
|
||||
var varWithCustomSetter: String = ""
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var ok = false
|
||||
var v = "fail"
|
||||
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
|
||||
this.v = v
|
||||
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()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.ok = true
|
||||
})
|
||||
if (!controller.ok) throw RuntimeException("Fail 1")
|
||||
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
|
||||
// FILE: controller.kt
|
||||
package lib
|
||||
@@ -5,7 +7,7 @@ package lib
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
@@ -15,8 +17,8 @@ class Controller {
|
||||
// FILE: main.kt
|
||||
import lib.*
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// MODULE: controller
|
||||
// FILE: controller.kt
|
||||
package lib
|
||||
@@ -6,7 +8,7 @@ package lib
|
||||
class Controller {
|
||||
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(this)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+22
-23
@@ -1,9 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
lastSuspension = x
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun hasNext() = lastSuspension != null
|
||||
@@ -16,26 +18,26 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller1 = Controller()
|
||||
val controller2 = Controller()
|
||||
|
||||
c(controller1).resume(Unit)
|
||||
c(controller2).resume(Unit)
|
||||
|
||||
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)
|
||||
c.startCoroutine(controller1, EmptyContinuation)
|
||||
c.startCoroutine(controller2, EmptyContinuation)
|
||||
|
||||
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) {
|
||||
while (controller1.hasNext()) {
|
||||
@@ -72,28 +74,25 @@ fun box(): String {
|
||||
result = "OK"
|
||||
}
|
||||
|
||||
// with capture and params
|
||||
// with capture
|
||||
|
||||
var x = "O"
|
||||
var y = "K"
|
||||
|
||||
// no suspension
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
builder {
|
||||
result = x + y
|
||||
}
|
||||
|
||||
// 1 suspension
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
if (suspendHere() != "56") return@builder2
|
||||
builder {
|
||||
if (suspendHere() != "56") return@builder
|
||||
result = x + y
|
||||
}
|
||||
|
||||
// 2 suspensions
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
if (suspendHere() != "56") return@builder2
|
||||
builder {
|
||||
if (suspendHere() != "56") return@builder
|
||||
suspendHere()
|
||||
result = x + y
|
||||
}
|
||||
|
||||
+21
-22
@@ -1,9 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
lastSuspension = x
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun hasNext() = lastSuspension != null
|
||||
@@ -16,26 +18,26 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller1 = Controller()
|
||||
val controller2 = Controller()
|
||||
|
||||
c(controller1).resume(Unit)
|
||||
c(controller2).resume(Unit)
|
||||
|
||||
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)
|
||||
c.startCoroutine(controller1, EmptyContinuation)
|
||||
c.startCoroutine(controller2, EmptyContinuation)
|
||||
|
||||
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) {
|
||||
while (controller1.hasNext()) {
|
||||
@@ -66,22 +68,19 @@ fun box(): String {
|
||||
// inlined
|
||||
run {
|
||||
// no suspension
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
builder {
|
||||
result = x + y
|
||||
}
|
||||
|
||||
// 1 suspension
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
if (suspendHere() != "56") return@builder2
|
||||
builder {
|
||||
if (suspendHere() != "56") return@builder
|
||||
result = x + y
|
||||
}
|
||||
|
||||
// 2 suspensions
|
||||
builder2 { a, b ->
|
||||
if (a != 1234567890123456789L || b != "Q" ) return@builder2
|
||||
if (suspendHere() != "56") return@builder2
|
||||
builder {
|
||||
if (suspendHere() != "56") return@builder
|
||||
suspendHere()
|
||||
result = x + y
|
||||
}
|
||||
|
||||
+16
-36
@@ -1,9 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
lastSuspension = x
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun hasNext() = lastSuspension != null
|
||||
@@ -16,26 +18,26 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller1 = Controller()
|
||||
val controller2 = Controller()
|
||||
|
||||
c(controller1).resume(Unit)
|
||||
c(controller2).resume(Unit)
|
||||
|
||||
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)
|
||||
c.startCoroutine(controller1, EmptyContinuation)
|
||||
c.startCoroutine(controller2, EmptyContinuation)
|
||||
|
||||
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) {
|
||||
while (controller1.hasNext()) {
|
||||
@@ -83,28 +85,6 @@ fun box(): String {
|
||||
suspendHere()
|
||||
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 {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
lastSuspension = x
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun hasNext() = lastSuspension != null
|
||||
@@ -16,26 +18,26 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
val controller1 = Controller()
|
||||
val controller2 = Controller()
|
||||
|
||||
c(controller1).resume(Unit)
|
||||
c(controller2).resume(Unit)
|
||||
|
||||
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)
|
||||
c.startCoroutine(controller1, EmptyContinuation)
|
||||
c.startCoroutine(controller2, EmptyContinuation)
|
||||
|
||||
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) {
|
||||
while (controller1.hasNext()) {
|
||||
@@ -81,28 +83,6 @@ fun box(): String {
|
||||
suspendHere()
|
||||
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_COROUTINES
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
class Controller {
|
||||
@@ -9,7 +10,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -17,15 +18,13 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||
c.startCoroutine(this, handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[0]()
|
||||
postponedActions.removeAt(0)
|
||||
@@ -35,7 +34,7 @@ class Controller {
|
||||
// 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()
|
||||
|
||||
globalResult = "#"
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
class Controller {
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
fun builder(c: @Suspend() (() -> Int)): Int {
|
||||
var res = 0
|
||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||
res = x
|
||||
}
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
res = it
|
||||
})
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
|
||||
return controller.res
|
||||
return res
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var cResult = 0
|
||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||
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()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.cResult = it
|
||||
})
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
+6
-9
@@ -1,22 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
class Controller {
|
||||
var cResult = 0
|
||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||
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()
|
||||
c(controller).resume(Unit)
|
||||
c.startCoroutine(controller, handleResultContinuation {
|
||||
controller.cResult = it
|
||||
})
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
+11
-16
@@ -1,22 +1,17 @@
|
||||
class Controller {
|
||||
var res = 0
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||
res = x
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
||||
val controller = Controller()
|
||||
c(controller).resume(Unit)
|
||||
fun builder(c: @Suspend() (() -> Int)): Int {
|
||||
var res = 0
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
res = it
|
||||
})
|
||||
|
||||
return controller.res
|
||||
return res
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
// IGNORE_BACKEND: JS
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resumeWithException(RuntimeException("OK"))
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
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
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||
res = x
|
||||
}
|
||||
c.createCoroutine(object : Continuation<Int> {
|
||||
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 {
|
||||
var result = ""
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = throw RuntimeException("OK")
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
// WITH_REFLECT
|
||||
// CHECK_NOT_CALLED: suspendInline_61zpoe$
|
||||
// CHECK_NOT_CALLED: suspendInline_6r51u9$
|
||||
@@ -13,8 +14,8 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
class OK
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere() = "OK"
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
||||
1
|
||||
@@ -9,8 +11,8 @@ class Controller {
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var globalResult = ""
|
||||
class Controller {
|
||||
suspend fun suspendWithValue(v: String): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
suspend fun suspendWithValue(v: String): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (() -> String)) {
|
||||
c.startCoroutine(handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendThere()
|
||||
|
||||
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
@AllowSuspendExtensions
|
||||
class Controller {
|
||||
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(this)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v * 2)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
inline fun foo(x: (Int) -> Unit) {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
var i = 0
|
||||
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
|
||||
x.resume(i++)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("?")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+7
-5
@@ -1,24 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("K")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithArgument(v: String): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithDouble(v: Double): Double = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
class A(val first: String, val second: String) {
|
||||
|
||||
+8
-9
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
class Controller {
|
||||
@@ -9,7 +10,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -17,15 +18,13 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||
c.startCoroutine(this, handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[0]()
|
||||
postponedActions.removeAt(0)
|
||||
@@ -35,7 +34,7 @@ class Controller {
|
||||
// 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()
|
||||
|
||||
globalResult = "#"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
class Controller {
|
||||
@@ -9,7 +10,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -17,25 +18,21 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||
c.startCoroutine(this, handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[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()
|
||||
|
||||
globalResult = "#"
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume(v)
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
inline fun run(block: () -> Unit) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
class Controller {
|
||||
@@ -9,7 +10,7 @@ class Controller {
|
||||
x.resume(v)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
|
||||
@@ -17,15 +18,13 @@ class Controller {
|
||||
x.resumeWithException(e)
|
||||
}
|
||||
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
operator fun handleResult(x: String, c: Continuation<Nothing>) {
|
||||
globalResult = x
|
||||
}
|
||||
|
||||
fun run(c: Controller.() -> Continuation<Unit>) {
|
||||
c(this).resume(Unit)
|
||||
fun run(c: @Suspend() (Controller.() -> String)) {
|
||||
c.startCoroutine(this, handleResultContinuation {
|
||||
globalResult = it
|
||||
})
|
||||
while (postponedActions.isNotEmpty()) {
|
||||
postponedActions[0]()
|
||||
postponedActions.removeAt(0)
|
||||
@@ -35,7 +34,7 @@ class Controller {
|
||||
// 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()
|
||||
|
||||
globalResult = "#"
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+5
-3
@@ -1,14 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// WITH_COROUTINES
|
||||
class Controller {
|
||||
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
|
||||
x.resume("OK")
|
||||
Suspend
|
||||
SUSPENDED
|
||||
}
|
||||
|
||||
// INTERCEPT_RESUME_PLACEHOLDER
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
fun builder(c: @Suspend() (Controller.() -> Unit)) {
|
||||
c.startCoroutine(Controller(), EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
+19
-25
@@ -43,32 +43,26 @@ fun box(): String {
|
||||
|
||||
// LIBRARY CODE
|
||||
|
||||
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
|
||||
val controller = FutureController<T>()
|
||||
c(controller).resume(Unit)
|
||||
return controller.future
|
||||
}
|
||||
|
||||
class FutureController<T> {
|
||||
fun <T> async(c: @Suspend() (() -> T)): CompletableFuture<T> {
|
||||
val future = CompletableFuture<T>()
|
||||
|
||||
|
||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||
f.whenComplete { value, throwable ->
|
||||
if (throwable == null)
|
||||
machine.resume(value)
|
||||
else
|
||||
machine.resumeWithException(throwable)
|
||||
c.startCoroutine(object : Continuation<T> {
|
||||
override fun resume(data: T) {
|
||||
future.complete(data)
|
||||
}
|
||||
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(value: T, c: Continuation<Nothing>) {
|
||||
future.complete(value)
|
||||
}
|
||||
|
||||
fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||
future.completeExceptionally(t)
|
||||
}
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
future.completeExceptionally(exception)
|
||||
}
|
||||
})
|
||||
return future
|
||||
}
|
||||
|
||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||
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"
|
||||
}
|
||||
|
||||
fun <T> async(coroutine c: FutureController<T>.() -> Continuation<Unit>): CompletableFuture<T> {
|
||||
val controller = FutureController<T>()
|
||||
c(controller).resume(Unit)
|
||||
return controller.future
|
||||
}
|
||||
|
||||
class FutureController<T> {
|
||||
fun <T> async(c: @Suspend() (() -> T)): CompletableFuture<T> {
|
||||
val future = CompletableFuture<T>()
|
||||
|
||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||
f.whenComplete { value, throwable ->
|
||||
if (throwable == null)
|
||||
machine.resume(value)
|
||||
else
|
||||
machine.resumeWithException(throwable)
|
||||
c.startContinuation(object : Continuation<T> {
|
||||
override fun resume(data: T) {
|
||||
future.complete(data)
|
||||
}
|
||||
|
||||
Suspend
|
||||
}
|
||||
|
||||
operator fun handleResult(value: T, c: Continuation<Nothing>) {
|
||||
future.complete(value)
|
||||
}
|
||||
|
||||
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||
future.completeExceptionally(t)
|
||||
}
|
||||
override fun resumeWithException(exception: Throwable) {
|
||||
future.completeExceptionally(exception)
|
||||
}
|
||||
})
|
||||
return future
|
||||
}
|
||||
|
||||
suspend fun <V> await(f: CompletableFuture<V>) = suspendWithCurrentContinuation<V> { machine ->
|
||||
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 static org.jetbrains.kotlin.test.InTextDirectivesUtils.isCompatibleTarget;
|
||||
import static org.jetbrains.kotlin.test.InTextDirectivesUtils.isDirectiveDefined;
|
||||
|
||||
public class KotlinTestUtils {
|
||||
public static String TEST_MODULE_NAME = "test-module";
|
||||
@@ -639,6 +640,37 @@ public class KotlinTestUtils {
|
||||
" to " +
|
||||
(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;
|
||||
}
|
||||
|
||||
|
||||
-12
@@ -4619,12 +4619,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
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")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
@@ -4649,12 +4643,6 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
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")
|
||||
public void testMultipleInvokeCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||
|
||||
+65
-12
@@ -144,12 +144,6 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
|
||||
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")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
@@ -174,12 +168,6 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
|
||||
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")
|
||||
public void testMultipleInvokeCalls() throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
@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")
|
||||
|
||||
@@ -4619,12 +4619,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
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")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
@@ -4649,12 +4643,6 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
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")
|
||||
public void testMultipleInvokeCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||
|
||||
-12
@@ -4619,12 +4619,6 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
||||
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")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
@@ -4649,12 +4643,6 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
||||
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")
|
||||
public void testMultipleInvokeCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||
|
||||
+26
-1
@@ -16,16 +16,21 @@
|
||||
|
||||
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.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
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.utils.DO_NOTHING_3
|
||||
|
||||
@@ -65,6 +70,26 @@ fun <T : Any> mapType(
|
||||
descriptorTypeWriter: JvmDescriptorTypeWriter<T>?,
|
||||
writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3
|
||||
): 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 ->
|
||||
val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing)
|
||||
writeGenericType(kotlinType, jvmType, mode)
|
||||
|
||||
Reference in New Issue
Block a user