Support new coroutine convention in JVM backend

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