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