Implement basic support for coroutines in JVM backend
This commit is contained in:
+1
-1
@@ -43,5 +43,5 @@ open class AbstractAccessorForFunctionDescriptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun copyValueParameters(descriptor: FunctionDescriptor): List<ValueParameterDescriptor> =
|
protected fun copyValueParameters(descriptor: FunctionDescriptor): List<ValueParameterDescriptor> =
|
||||||
descriptor.valueParameters.map { it.copy(this, it.name) }
|
descriptor.valueParameters.map { it.copy(this, it.name, it.index) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.
|
|||||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
public class ClosureCodegen extends MemberCodegen<KtElement> {
|
public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||||
private final FunctionDescriptor funDescriptor;
|
protected final FunctionDescriptor funDescriptor;
|
||||||
private final ClassDescriptor classDescriptor;
|
private final ClassDescriptor classDescriptor;
|
||||||
private final SamType samType;
|
private final SamType samType;
|
||||||
private final KotlinType superClassType;
|
private final KotlinType superClassType;
|
||||||
private final List<KotlinType> superInterfaceTypes;
|
private final List<KotlinType> superInterfaceTypes;
|
||||||
private final FunctionDescriptor functionReferenceTarget;
|
private final FunctionDescriptor functionReferenceTarget;
|
||||||
private final FunctionGenerationStrategy strategy;
|
protected final FunctionGenerationStrategy strategy;
|
||||||
private final CalculatedClosure closure;
|
private final CalculatedClosure closure;
|
||||||
private final Type asmType;
|
private final Type asmType;
|
||||||
private final int visibilityFlag;
|
private final int visibilityFlag;
|
||||||
@@ -170,6 +170,32 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateBody() {
|
protected void generateBody() {
|
||||||
|
generateBridges();
|
||||||
|
generateClosureBody();
|
||||||
|
|
||||||
|
this.constructor = generateConstructor();
|
||||||
|
|
||||||
|
if (isConst(closure)) {
|
||||||
|
generateConstInstance(asmType, asmType);
|
||||||
|
}
|
||||||
|
|
||||||
|
genClosureFields(closure, v, typeMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateClosureBody() {
|
||||||
|
functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy);
|
||||||
|
|
||||||
|
|
||||||
|
if (functionReferenceTarget != null) {
|
||||||
|
generateFunctionReferenceMethods(functionReferenceTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
functionCodegen.generateDefaultIfNeeded(
|
||||||
|
context.intoFunction(funDescriptor), funDescriptor, context.getContextKind(), DefaultParameterValueLoader.DEFAULT, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateBridges() {
|
||||||
FunctionDescriptor erasedInterfaceFunction;
|
FunctionDescriptor erasedInterfaceFunction;
|
||||||
if (samType == null) {
|
if (samType == null) {
|
||||||
erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
|
erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
|
||||||
@@ -183,8 +209,6 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
typeMapper.mapAsmMethod(funDescriptor)
|
typeMapper.mapAsmMethod(funDescriptor)
|
||||||
);
|
);
|
||||||
|
|
||||||
functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy);
|
|
||||||
|
|
||||||
//TODO: rewrite cause ugly hack
|
//TODO: rewrite cause ugly hack
|
||||||
if (samType != null) {
|
if (samType != null) {
|
||||||
SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl
|
SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl
|
||||||
@@ -200,22 +224,6 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
DescriptorUtilsKt.setSingleOverridden(descriptorForBridges, erasedInterfaceFunction);
|
DescriptorUtilsKt.setSingleOverridden(descriptorForBridges, erasedInterfaceFunction);
|
||||||
functionCodegen.generateBridges(descriptorForBridges);
|
functionCodegen.generateBridges(descriptorForBridges);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionReferenceTarget != null) {
|
|
||||||
generateFunctionReferenceMethods(functionReferenceTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
functionCodegen.generateDefaultIfNeeded(
|
|
||||||
context.intoFunction(funDescriptor), funDescriptor, context.getContextKind(), DefaultParameterValueLoader.DEFAULT, null
|
|
||||||
);
|
|
||||||
|
|
||||||
this.constructor = generateConstructor();
|
|
||||||
|
|
||||||
if (isConst(closure)) {
|
|
||||||
generateConstInstance(asmType, asmType);
|
|
||||||
}
|
|
||||||
|
|
||||||
genClosureFields(closure, v, typeMapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
|||||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||||
import org.jetbrains.kotlin.codegen.context.*;
|
import org.jetbrains.kotlin.codegen.context.*;
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegen;
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
|
||||||
import org.jetbrains.kotlin.codegen.inline.*;
|
import org.jetbrains.kotlin.codegen.inline.*;
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.*;
|
import org.jetbrains.kotlin.codegen.intrinsics.*;
|
||||||
@@ -47,6 +49,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
||||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
|
||||||
|
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
||||||
@@ -102,6 +105,7 @@ import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
|
|||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
||||||
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
||||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.*;
|
import static org.jetbrains.kotlin.resolve.BindingContextUtils.*;
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
|
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry;
|
||||||
@@ -314,6 +318,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
public void gen(KtElement expr, Type type) {
|
public void gen(KtElement expr, Type type) {
|
||||||
StackValue value = Type.VOID_TYPE.equals(type) ? genStatement(expr) : gen(expr);
|
StackValue value = Type.VOID_TYPE.equals(type) ? genStatement(expr) : gen(expr);
|
||||||
|
putStackValue(expr, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putStackValue(KtElement expr, Type type, StackValue value) {
|
||||||
// for repl store the result of the last line into special field
|
// for repl store the result of the last line into special field
|
||||||
if (value.type != Type.VOID_TYPE && state.getReplSpecific().getShouldGenerateScriptResultValue()) {
|
if (value.type != Type.VOID_TYPE && state.getReplSpecific().getShouldGenerateScriptResultValue()) {
|
||||||
ScriptContext context = getScriptContext();
|
ScriptContext context = getScriptContext();
|
||||||
@@ -1438,7 +1446,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
declaration.getContainingFile()
|
declaration.getContainingFile()
|
||||||
);
|
);
|
||||||
|
|
||||||
ClosureCodegen closureCodegen = new ClosureCodegen(
|
ClosureCodegen coroutineCodegen = CoroutineCodegen.create(this, descriptor, declaration, cv);
|
||||||
|
ClosureCodegen closureCodegen = coroutineCodegen != null ? coroutineCodegen : new ClosureCodegen(
|
||||||
state, declaration, samType, context.intoClosure(descriptor, this, typeMapper),
|
state, declaration, samType, context.intoClosure(descriptor, this, typeMapper),
|
||||||
functionReferenceTarget, strategy, parentCodegen, cv
|
functionReferenceTarget, strategy, parentCodegen, cv
|
||||||
);
|
);
|
||||||
@@ -1613,6 +1622,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
if (!iterator.hasNext()) {
|
if (!iterator.hasNext()) {
|
||||||
answer = result;
|
answer = result;
|
||||||
|
StackValue handleResultValue = !(possiblyLabeledStatement instanceof KtReturnExpression)
|
||||||
|
? genControllerHandleResultCallIfNeeded(possiblyLabeledStatement, result)
|
||||||
|
: null;
|
||||||
|
if (handleResultValue != null) {
|
||||||
|
answer = handleResultValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.put(Type.VOID_TYPE, v);
|
result.put(Type.VOID_TYPE, v);
|
||||||
@@ -1635,6 +1650,31 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private StackValue genControllerHandleResultCallIfNeeded(@NotNull KtExpression callOwner, @Nullable StackValue valueToReturn) {
|
||||||
|
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();
|
||||||
|
if (KotlinBuiltIns.isUnit(resolvedCall.getResultingDescriptor().getValueParameters().get(0).getType())) {
|
||||||
|
tempVariables.put(argumentExpression, StackValue.unit());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tempVariables.put(argumentExpression, valueToReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second argument for handleResult is always Continuation<T> ('this'-object in current implementation)
|
||||||
|
tempVariables.put(
|
||||||
|
resolvedCall.getValueArgumentsByIndex().get(1).getArguments().get(0).getArgumentExpression(),
|
||||||
|
StackValue.thisOrOuter(this, context.getThisDescriptor(), false, false));
|
||||||
|
|
||||||
|
return invokeFunction(resolvedCall, StackValue.none());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Type getVariableType(@NotNull VariableDescriptor variableDescriptor) {
|
private Type getVariableType(@NotNull VariableDescriptor variableDescriptor) {
|
||||||
Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
|
Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
|
||||||
@@ -1910,8 +1950,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : ExpressionCodegen.this.returnType;
|
Type returnType = isNonLocalReturn ? nonLocalReturn.returnType : ExpressionCodegen.this.returnType;
|
||||||
if (returnedExpression != null) {
|
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : null;
|
||||||
gen(returnedExpression, returnType);
|
StackValue handleResultValue = genControllerHandleResultCallIfNeeded(expression, valueToReturn);
|
||||||
|
|
||||||
|
if (handleResultValue != null) {
|
||||||
|
handleResultValue.put(Type.VOID_TYPE, v);
|
||||||
|
}
|
||||||
|
else if (returnedExpression != null && valueToReturn != null) {
|
||||||
|
putStackValue(returnedExpression, returnType, valueToReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
Label afterReturnLabel = new Label();
|
Label afterReturnLabel = new Label();
|
||||||
@@ -2491,6 +2537,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
@NotNull CallGenerator callGenerator,
|
@NotNull CallGenerator callGenerator,
|
||||||
@NotNull ArgumentGenerator argumentGenerator
|
@NotNull ArgumentGenerator argumentGenerator
|
||||||
) {
|
) {
|
||||||
|
boolean isCallToSuspendFunction = isCallToSuspendFunction(resolvedCall);
|
||||||
|
if (isCallToSuspendFunction) {
|
||||||
|
// Inline markers are used to spill the stack before coroutine suspension
|
||||||
|
addInlineMarker(v, true);
|
||||||
|
}
|
||||||
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
|
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
|
||||||
if (!isConstructor) { // otherwise already
|
if (!isConstructor) { // otherwise already
|
||||||
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
||||||
@@ -2523,8 +2574,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCallToSuspendFunction) {
|
||||||
|
v.invokestatic(
|
||||||
|
CoroutineCodegenUtilKt.SUSPENSION_POINT_MARKER_OWNER,
|
||||||
|
CoroutineCodegenUtilKt.SUSPENSION_POINT_MARKER_NAME, "()V", false);
|
||||||
|
}
|
||||||
|
|
||||||
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
|
||||||
|
|
||||||
|
if (isCallToSuspendFunction) {
|
||||||
|
addInlineMarker(v, false);
|
||||||
|
}
|
||||||
|
|
||||||
KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
|
KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
|
||||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||||
v.aconst(null);
|
v.aconst(null);
|
||||||
@@ -2532,6 +2593,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isCallToSuspendFunction(@NotNull ResolvedCall<?> resolvedCall) {
|
||||||
|
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
|
||||||
|
return descriptor instanceof FunctionDescriptor && ((FunctionDescriptor) descriptor).isSuspend();
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private CallGenerator getOrCreateCallGenerator(
|
private CallGenerator getOrCreateCallGenerator(
|
||||||
@NotNull CallableDescriptor descriptor,
|
@NotNull CallableDescriptor descriptor,
|
||||||
@@ -2634,7 +2700,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (receiverValue instanceof ExtensionReceiver) {
|
else if (receiverValue instanceof ExtensionReceiver) {
|
||||||
return generateReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
|
return generateExtensionReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
|
||||||
}
|
}
|
||||||
else if (receiverValue instanceof ExpressionReceiver) {
|
else if (receiverValue instanceof ExpressionReceiver) {
|
||||||
return gen(((ExpressionReceiver) receiverValue).getExpression());
|
return gen(((ExpressionReceiver) receiverValue).getExpression());
|
||||||
@@ -2645,7 +2711,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private StackValue generateReceiver(@NotNull CallableDescriptor descriptor) {
|
private StackValue generateExtensionReceiver(@NotNull CallableDescriptor descriptor) {
|
||||||
|
KotlinType coroutineControllerType = CoroutineUtilKt.getControllerTypeIfCoroutine(descriptor);
|
||||||
|
if (coroutineControllerType != null) {
|
||||||
|
ClassDescriptor thisDescriptor = context.getThisDescriptor();
|
||||||
|
|
||||||
|
return StackValue.field(
|
||||||
|
FieldInfo.createForHiddenField(
|
||||||
|
typeMapper.mapClass(thisDescriptor),
|
||||||
|
typeMapper.mapType(coroutineControllerType),
|
||||||
|
CoroutineCodegenUtilKt.COROUTINE_CONTROLLER_FIELD_NAME),
|
||||||
|
StackValue.thisOrOuter(this, thisDescriptor, /* isSuper =*/ false, /* castReceiver */ false));
|
||||||
|
}
|
||||||
|
|
||||||
return context.generateReceiver(descriptor, state, false);
|
return context.generateReceiver(descriptor, state, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3736,7 +3814,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
return StackValue.thisOrOuter(this, (ClassDescriptor) descriptor, false, true);
|
return StackValue.thisOrOuter(this, (ClassDescriptor) descriptor, false, true);
|
||||||
}
|
}
|
||||||
if (descriptor instanceof CallableDescriptor) {
|
if (descriptor instanceof CallableDescriptor) {
|
||||||
return generateReceiver((CallableDescriptor) descriptor);
|
return generateExtensionReceiver((CallableDescriptor) descriptor);
|
||||||
}
|
}
|
||||||
throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor);
|
throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,20 +17,21 @@
|
|||||||
package org.jetbrains.kotlin.codegen;
|
package org.jetbrains.kotlin.codegen;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns;
|
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
|
|
||||||
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor;
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||||
import org.jetbrains.kotlin.resolve.FunctionTypeResolveUtilsKt;
|
import org.jetbrains.kotlin.resolve.FunctionTypeResolveUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.TargetPlatformKt;
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform;
|
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType;
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
import org.jetbrains.kotlin.types.TypeConstructorSubstitution;
|
||||||
|
import org.jetbrains.kotlin.types.TypeProjectionImpl;
|
||||||
|
import org.jetbrains.kotlin.types.Variance;
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -42,13 +43,9 @@ public class JvmRuntimeTypes {
|
|||||||
private final List<ClassDescriptor> mutablePropertyReferences;
|
private final List<ClassDescriptor> mutablePropertyReferences;
|
||||||
private final ClassDescriptor localVariableReference;
|
private final ClassDescriptor localVariableReference;
|
||||||
private final ClassDescriptor mutableLocalVariableReference;
|
private final ClassDescriptor mutableLocalVariableReference;
|
||||||
|
private final KotlinType defaultContinuationSupertype;
|
||||||
|
|
||||||
public JvmRuntimeTypes() {
|
public JvmRuntimeTypes(@NotNull ModuleDescriptor module) {
|
||||||
ModuleDescriptorImpl module = TargetPlatformKt.createModule(
|
|
||||||
JvmPlatform.INSTANCE,
|
|
||||||
Name.special("<jvm functions impl>"),
|
|
||||||
LockBasedStorageManager.NO_LOCKS,
|
|
||||||
DefaultBuiltIns.getInstance());
|
|
||||||
PackageFragmentDescriptor kotlinJvmInternal = new MutablePackageFragmentDescriptor(module, new FqName("kotlin.jvm.internal"));
|
PackageFragmentDescriptor kotlinJvmInternal = new MutablePackageFragmentDescriptor(module, new FqName("kotlin.jvm.internal"));
|
||||||
|
|
||||||
this.lambda = createClass(kotlinJvmInternal, "Lambda");
|
this.lambda = createClass(kotlinJvmInternal, "Lambda");
|
||||||
@@ -62,6 +59,27 @@ public class JvmRuntimeTypes {
|
|||||||
propertyReferences.add(createClass(kotlinJvmInternal, "PropertyReference" + i));
|
propertyReferences.add(createClass(kotlinJvmInternal, "PropertyReference" + i));
|
||||||
mutablePropertyReferences.add(createClass(kotlinJvmInternal, "MutablePropertyReference" + i));
|
mutablePropertyReferences.add(createClass(kotlinJvmInternal, "MutablePropertyReference" + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultContinuationSupertype = createNullableAnyContinuation(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param module
|
||||||
|
* @return Continuation<Any?> type
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static KotlinType createNullableAnyContinuation(@NotNull ModuleDescriptor module) {
|
||||||
|
ClassDescriptor classDescriptor =
|
||||||
|
DescriptorUtilsKt.resolveTopLevelClass(
|
||||||
|
module, DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME, NoLookupLocation.FROM_BACKEND);
|
||||||
|
|
||||||
|
assert classDescriptor != null : DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME + " was not found in built-ins";
|
||||||
|
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
return TypeConstructorSubstitution
|
||||||
|
.createByParametersMap(Collections.singletonMap(classDescriptor.getDeclaredTypeParameters().get(0),
|
||||||
|
new TypeProjectionImpl(module.getBuiltIns().getNullableAnyType())))
|
||||||
|
.buildSubstitutor().substitute(classDescriptor.getDefaultType(), Variance.INVARIANT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -91,6 +109,13 @@ public class JvmRuntimeTypes {
|
|||||||
descriptor.getReturnType()
|
descriptor.getReturnType()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
KotlinType coroutineControllerType = CoroutineUtilKt.getControllerTypeIfCoroutine(descriptor);
|
||||||
|
|
||||||
|
if (coroutineControllerType != null) {
|
||||||
|
return Arrays.asList(
|
||||||
|
lambda.getDefaultType(), functionType, /*coroutineType,*/ defaultContinuationSupertype);
|
||||||
|
}
|
||||||
|
|
||||||
return Arrays.asList(lambda.getDefaultType(), functionType);
|
return Arrays.asList(lambda.getDefaultType(), functionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
|
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.descriptors.*
|
||||||
|
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
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
|
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.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
|
|
||||||
|
class CoroutineCodegen(
|
||||||
|
state: GenerationState,
|
||||||
|
element: KtElement,
|
||||||
|
private val closureContext: ClosureContext,
|
||||||
|
strategy: FunctionGenerationStrategy,
|
||||||
|
parentCodegen: MemberCodegen<*>,
|
||||||
|
classBuilder: ClassBuilder,
|
||||||
|
private val continuationSuperType: KotlinType,
|
||||||
|
private val controllerType: KotlinType
|
||||||
|
) : ClosureCodegen(state, element, null, closureContext, null, strategy, parentCodegen, classBuilder) {
|
||||||
|
|
||||||
|
override fun generateClosureBody() {
|
||||||
|
v.newField(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_PRIVATE,
|
||||||
|
COROUTINE_CONTROLLER_FIELD_NAME,
|
||||||
|
typeMapper.mapType(controllerType).descriptor, null, null)
|
||||||
|
|
||||||
|
v.newField(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_PRIVATE,
|
||||||
|
COROUTINE_LABEL_FIELD_NAME,
|
||||||
|
Type.INT_TYPE.descriptor, null, null)
|
||||||
|
|
||||||
|
val classDescriptor = closureContext.contextDescriptor
|
||||||
|
|
||||||
|
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||||
|
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
|
AsmUtil.genAssignInstanceFieldFromParam(
|
||||||
|
FieldInfo.createForHiddenField(
|
||||||
|
typeMapper.mapClass(classDescriptor),
|
||||||
|
typeMapper.mapType(controllerType), COROUTINE_CONTROLLER_FIELD_NAME),
|
||||||
|
1, codegen.v)
|
||||||
|
|
||||||
|
with(codegen.v) {
|
||||||
|
load(0, AsmTypes.OBJECT_TYPE)
|
||||||
|
iconst(0)
|
||||||
|
putfield(v.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor)
|
||||||
|
load(0, AsmTypes.OBJECT_TYPE)
|
||||||
|
areturn(AsmTypes.OBJECT_TYPE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val resumeFunctionDescriptor =
|
||||||
|
createSynthesizedImplementationByName(
|
||||||
|
"resume", interfaceSupertype = continuationSuperType, implementationClass = classDescriptor)
|
||||||
|
|
||||||
|
val resumeWithExceptionFunctionDescriptor =
|
||||||
|
createSynthesizedImplementationByName(
|
||||||
|
"resumeWithException", interfaceSupertype = continuationSuperType, implementationClass = classDescriptor)
|
||||||
|
|
||||||
|
// private fun resume(result, throwable)
|
||||||
|
val combinedResumeFunctionDescriptor =
|
||||||
|
resumeFunctionDescriptor.newCopyBuilder()
|
||||||
|
.setVisibility(Visibilities.PRIVATE)
|
||||||
|
.setName(Name.identifier("doResume"))
|
||||||
|
.setCopyOverrides(false)
|
||||||
|
.setValueParameters(
|
||||||
|
listOf(
|
||||||
|
resumeFunctionDescriptor.valueParameters[0].copy(
|
||||||
|
resumeFunctionDescriptor, Name.identifier("value"), 0),
|
||||||
|
resumeWithExceptionFunctionDescriptor.valueParameters[0].copy(
|
||||||
|
resumeFunctionDescriptor, Name.identifier("exception"), 1))
|
||||||
|
).build()!!
|
||||||
|
|
||||||
|
generatedDelegationToCombinedResume(resumeFunctionDescriptor, combinedResumeFunctionDescriptor, isSuccess = true)
|
||||||
|
generatedDelegationToCombinedResume(resumeWithExceptionFunctionDescriptor, combinedResumeFunctionDescriptor, isSuccess = false)
|
||||||
|
|
||||||
|
functionCodegen.generateMethod(OtherOrigin(element), combinedResumeFunctionDescriptor,
|
||||||
|
object : FunctionGenerationStrategy.FunctionDefault(state, element as KtDeclarationWithBody) {
|
||||||
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
|
codegen.v.visitAnnotation(CONTINUATION_METHOD_ANNOTATION_DESC, true).visitEnd()
|
||||||
|
super.doGenerateBody(codegen, signature)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createSynthesizedImplementationByName(
|
||||||
|
name: String,
|
||||||
|
interfaceSupertype: KotlinType,
|
||||||
|
implementationClass: ClassDescriptor
|
||||||
|
): SimpleFunctionDescriptor {
|
||||||
|
val inSuperType = interfaceSupertype.memberScope.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).single()
|
||||||
|
val result = inSuperType.newCopyBuilder().setOwner(implementationClass).setModality(Modality.FINAL).build()!!
|
||||||
|
result.setSingleOverridden(inSuperType)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generatedDelegationToCombinedResume(
|
||||||
|
from: SimpleFunctionDescriptor,
|
||||||
|
combinedResume: SimpleFunctionDescriptor,
|
||||||
|
isSuccess: Boolean
|
||||||
|
) {
|
||||||
|
functionCodegen.generateMethod(OtherOrigin(element), from,
|
||||||
|
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
|
with(codegen.v) {
|
||||||
|
load(0, AsmTypes.OBJECT_TYPE)
|
||||||
|
if (isSuccess) {
|
||||||
|
load(1, AsmTypes.OBJECT_TYPE)
|
||||||
|
aconst(null)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aconst(null)
|
||||||
|
load(1, AsmTypes.OBJECT_TYPE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val delegateTo = typeMapper.mapAsmMethod(combinedResume)
|
||||||
|
invokevirtual(className, delegateTo.name, delegateTo.descriptor, false)
|
||||||
|
areturn(Type.VOID_TYPE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic fun create(
|
||||||
|
expressionCodegen: ExpressionCodegen,
|
||||||
|
originalCoroutineLambdaDescriptor: FunctionDescriptor,
|
||||||
|
declaration: KtElement,
|
||||||
|
classBuilder: ClassBuilder
|
||||||
|
): ClosureCodegen? {
|
||||||
|
val classDescriptor = expressionCodegen.bindingContext[CodegenBinding.CLASS_FOR_CALLABLE, originalCoroutineLambdaDescriptor] ?: return null
|
||||||
|
declaration as? KtFunction ?: return null
|
||||||
|
|
||||||
|
val continuationSupertype =
|
||||||
|
classDescriptor.typeConstructor.supertypes.firstOrNull {
|
||||||
|
it.constructor.declarationDescriptor?.fqNameUnsafe ==
|
||||||
|
DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.toUnsafe()
|
||||||
|
} ?: return null
|
||||||
|
|
||||||
|
val descriptorWithContinuationReturnType =
|
||||||
|
originalCoroutineLambdaDescriptor.newCopyBuilder().setReturnType(continuationSupertype).build()!!
|
||||||
|
|
||||||
|
val state = expressionCodegen.state
|
||||||
|
return CoroutineCodegen(
|
||||||
|
state,
|
||||||
|
declaration,
|
||||||
|
expressionCodegen.context.intoClosure(descriptorWithContinuationReturnType, expressionCodegen, state.typeMapper),
|
||||||
|
FunctionGenerationStrategy.FunctionDefault(state, declaration), expressionCodegen.parentCodegen, classBuilder,
|
||||||
|
continuationSupertype,
|
||||||
|
originalCoroutineLambdaDescriptor.controllerTypeIfCoroutine!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+268
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.*
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.MandatoryMethodTransformer
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.SKIP_MANDATORY_TRANSFORMATIONS_ANNOTATION_DESC
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
|
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.commons.Method
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
|
|
||||||
|
class CoroutineTransformationClassBuilder(private val delegate: ClassBuilder) : DelegatingClassBuilder() {
|
||||||
|
override fun getDelegate() = delegate
|
||||||
|
|
||||||
|
override fun newMethod(
|
||||||
|
origin: JvmDeclarationOrigin,
|
||||||
|
access: Int, name: String,
|
||||||
|
desc: String, signature:
|
||||||
|
String?,
|
||||||
|
exceptions: Array<out String>?
|
||||||
|
) = CoroutineTransformerMethodVisitor(
|
||||||
|
delegate.newMethod(origin, access, name, desc, signature, exceptions),
|
||||||
|
access, name, desc, signature, exceptions, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoroutineTransformerClassBuilderFactory(delegate: ClassBuilderFactory) : DelegatingClassBuilderFactory(delegate) {
|
||||||
|
override fun newClassBuilder(origin: JvmDeclarationOrigin) = CoroutineTransformationClassBuilder(delegate.newClassBuilder(origin))
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoroutineTransformerMethodVisitor(
|
||||||
|
delegate: MethodVisitor,
|
||||||
|
access: Int,
|
||||||
|
name: String,
|
||||||
|
desc: String,
|
||||||
|
signature: String?,
|
||||||
|
exceptions: Array<out String>?,
|
||||||
|
private val classBuilder: ClassBuilder
|
||||||
|
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
||||||
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
|
if (methodNode.visibleAnnotations?.none { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC } != false) return
|
||||||
|
methodNode.visibleAnnotations.removeAll { it.desc == CONTINUATION_METHOD_ANNOTATION_DESC }
|
||||||
|
|
||||||
|
MandatoryMethodTransformer().transform("fake", methodNode)
|
||||||
|
methodNode.visibleAnnotations.add(AnnotationNode(SKIP_MANDATORY_TRANSFORMATIONS_ANNOTATION_DESC))
|
||||||
|
|
||||||
|
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||||
|
if (suspensionPoints.isEmpty()) return
|
||||||
|
|
||||||
|
spillVariables(suspensionPoints, methodNode)
|
||||||
|
|
||||||
|
val suspensionPointLabels = suspensionPoints.map {
|
||||||
|
transformCallAndReturnContinuationLabel(it, methodNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
methodNode.instructions.apply {
|
||||||
|
val startLabel = LabelNode()
|
||||||
|
val defaultLabel = LabelNode()
|
||||||
|
// tableswitch(this.label)
|
||||||
|
insertBefore(first,
|
||||||
|
insnListOf(
|
||||||
|
VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
FieldInsnNode(
|
||||||
|
Opcodes.GETFIELD, classBuilder.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor),
|
||||||
|
TableSwitchInsnNode(0,
|
||||||
|
suspensionPoints.map { it.id }.max()!!,
|
||||||
|
defaultLabel,
|
||||||
|
*(arrayOf(startLabel) + suspensionPointLabels)),
|
||||||
|
startLabel))
|
||||||
|
|
||||||
|
|
||||||
|
insert(last, withInstructionAdapter {
|
||||||
|
visitLabel(defaultLabel.label)
|
||||||
|
AsmUtil.genThrow(this, "java/lang/IllegalStateException", "call to 'resume' before 'invoke' with coroutine")
|
||||||
|
areturn(Type.VOID_TYPE)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectSuspensionPoints(methodNode: MethodNode): List<SuspensionPoint> {
|
||||||
|
val suspensionPoints = mutableListOf<SuspensionPoint>()
|
||||||
|
|
||||||
|
for (methodInsn in methodNode.instructions.asSequence().filterIsInstance<MethodInsnNode>()) {
|
||||||
|
if (methodInsn.owner != SUSPENSION_POINT_MARKER_OWNER) continue
|
||||||
|
|
||||||
|
when (methodInsn.name) {
|
||||||
|
SUSPENSION_POINT_MARKER_NAME -> {
|
||||||
|
assert(methodInsn.next is MethodInsnNode) {
|
||||||
|
"Expected method call instruction after suspension point, but ${methodInsn.next} found"
|
||||||
|
}
|
||||||
|
|
||||||
|
suspensionPoints.add(SuspensionPoint(suspensionPoints.size + 1, methodInsn.next as MethodInsnNode))
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> error("Unexpected suspension point marker kind '${methodInsn.name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop markers
|
||||||
|
suspensionPoints.forEach { methodNode.instructions.remove(it.suspensionCall.previous) }
|
||||||
|
|
||||||
|
return suspensionPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spillVariables(suspensionPoints: List<SuspensionPoint>, methodNode: MethodNode) {
|
||||||
|
val instructions = methodNode.instructions
|
||||||
|
val frames = MethodTransformer.analyze("fake", methodNode, OptimizationBasicInterpreter())
|
||||||
|
fun AbstractInsnNode.index() = instructions.indexOf(this)
|
||||||
|
|
||||||
|
// We postpone these actions because they change instruction indices that we use when obtaining frames
|
||||||
|
val postponedActions = mutableListOf<() -> Unit>()
|
||||||
|
val maxVarsCountByType = mutableMapOf<Type, Int>()
|
||||||
|
|
||||||
|
for (suspension in suspensionPoints) {
|
||||||
|
val call = suspension.suspensionCall
|
||||||
|
assert(frames[call.next.index()].stackSize == (if (Type.getReturnType(call.desc).sort == Type.VOID) 0 else 1)) {
|
||||||
|
"Stack should be spilled before suspension call"
|
||||||
|
}
|
||||||
|
|
||||||
|
val frame = frames[call.index()]
|
||||||
|
val localsCount = frame.locals
|
||||||
|
val varsCountByType = mutableMapOf<Type, Int>()
|
||||||
|
// 0 - this
|
||||||
|
// 1 - continuation argument
|
||||||
|
// 2 - continuation exception
|
||||||
|
val variablesToSpill =
|
||||||
|
(3 until localsCount).map { Pair(it, frame.getLocal(it)) }.filter { it.second != BasicValue.UNINITIALIZED_VALUE }
|
||||||
|
|
||||||
|
for ((index, basicValue) in variablesToSpill) {
|
||||||
|
val type = basicValue.type
|
||||||
|
val normalizedType = type.normalize()
|
||||||
|
|
||||||
|
val indexBySort = varsCountByType[normalizedType]?.plus(1) ?: 0
|
||||||
|
varsCountByType[normalizedType] = indexBySort
|
||||||
|
|
||||||
|
val fieldName = normalizedType.fieldNameForVar(indexBySort)
|
||||||
|
|
||||||
|
postponedActions.add {
|
||||||
|
with(instructions) {
|
||||||
|
// store variable before suspension call
|
||||||
|
insertBefore(call, VarInsnNode(Opcodes.ALOAD, 0))
|
||||||
|
insertBefore(call, VarInsnNode(type.getOpcode(Opcodes.ILOAD), index))
|
||||||
|
insertBefore(call, coercionInsns(type, normalizedType))
|
||||||
|
insertBefore(call, FieldInsnNode(Opcodes.PUTFIELD, classBuilder.thisName, fieldName, normalizedType.descriptor))
|
||||||
|
|
||||||
|
// restore variable after suspension call
|
||||||
|
val nextInsnAfterCall = call.next
|
||||||
|
insertBefore(nextInsnAfterCall, VarInsnNode(Opcodes.ALOAD, 0))
|
||||||
|
insertBefore(nextInsnAfterCall,
|
||||||
|
FieldInsnNode(Opcodes.GETFIELD, classBuilder.thisName, fieldName, normalizedType.descriptor))
|
||||||
|
insertBefore(nextInsnAfterCall, coercionInsns(normalizedType, type))
|
||||||
|
insertBefore(nextInsnAfterCall, VarInsnNode(type.getOpcode(Opcodes.ISTORE), index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
varsCountByType.forEach {
|
||||||
|
maxVarsCountByType[it.key] = Math.max(maxVarsCountByType[it.key] ?: 0, it.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postponedActions.forEach(Function0<Unit>::invoke)
|
||||||
|
|
||||||
|
maxVarsCountByType.forEach { entry ->
|
||||||
|
val (type, maxIndex) = entry
|
||||||
|
for (index in 0..maxIndex) {
|
||||||
|
classBuilder.newField(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_PRIVATE or Opcodes.ACC_VOLATILE,
|
||||||
|
type.fieldNameForVar(index), type.descriptor, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformCallAndReturnContinuationLabel(suspension: SuspensionPoint, methodNode: MethodNode): LabelNode {
|
||||||
|
val call = suspension.suspensionCall
|
||||||
|
val method = Method(call.name, call.desc)
|
||||||
|
val newParameters = method.argumentTypes + CONTINUATION_INTERFACE_ASM_TYPE
|
||||||
|
|
||||||
|
call.desc = Method(method.name, Type.VOID_TYPE, newParameters).descriptor
|
||||||
|
|
||||||
|
val continuationLabel = LabelNode()
|
||||||
|
|
||||||
|
with(methodNode.instructions) {
|
||||||
|
// Save state
|
||||||
|
insertBefore(call,
|
||||||
|
insnListOf(
|
||||||
|
VarInsnNode(Opcodes.ALOAD, 0),
|
||||||
|
*withInstructionAdapter { iconst(suspension.id) }.toArray(),
|
||||||
|
FieldInsnNode(
|
||||||
|
Opcodes.PUTFIELD, classBuilder.thisName, COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor)))
|
||||||
|
|
||||||
|
// Pass continuation
|
||||||
|
insertBefore(call, VarInsnNode(Opcodes.ALOAD, 0))
|
||||||
|
|
||||||
|
val nextInsnAfterCall = call.next
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
insertBefore(nextInsnAfterCall, InsnNode(Opcodes.RETURN))
|
||||||
|
|
||||||
|
// Mark place for continuation
|
||||||
|
insertBefore(nextInsnAfterCall, continuationLabel)
|
||||||
|
|
||||||
|
// Check if resumeWithException has been called
|
||||||
|
insertBefore(nextInsnAfterCall, withInstructionAdapter {
|
||||||
|
load(2, AsmTypes.OBJECT_TYPE)
|
||||||
|
dup()
|
||||||
|
val noExceptionLabel = Label()
|
||||||
|
ifnull(noExceptionLabel)
|
||||||
|
athrow()
|
||||||
|
|
||||||
|
mark(noExceptionLabel)
|
||||||
|
pop()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Load continuation argument just like suspending function returns it
|
||||||
|
insertBefore(nextInsnAfterCall, VarInsnNode(Opcodes.ALOAD, 1))
|
||||||
|
insertBefore(nextInsnAfterCall, coercionInsns(AsmTypes.OBJECT_TYPE, method.returnType))
|
||||||
|
}
|
||||||
|
|
||||||
|
return continuationLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Type.fieldNameForVar(index: Int) = descriptor.first() + "$" + index
|
||||||
|
|
||||||
|
private fun coercionInsns(from: Type, to: Type) = withInstructionAdapter { StackValue.coerce(from, to, this) }
|
||||||
|
|
||||||
|
private fun withInstructionAdapter(block: InstructionAdapter.() -> Unit): InsnList {
|
||||||
|
val tmpMethodNode = MethodNode()
|
||||||
|
|
||||||
|
InstructionAdapter(tmpMethodNode).apply(block)
|
||||||
|
|
||||||
|
return tmpMethodNode.instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Type.normalize() =
|
||||||
|
when (sort) {
|
||||||
|
Type.ARRAY, Type.OBJECT -> AsmTypes.OBJECT_TYPE
|
||||||
|
else -> this
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SuspensionPoint(val id: Int, val suspensionCall: MethodInsnNode)
|
||||||
|
|
||||||
|
private fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.coroutines.CONTINUATION_INTERFACE_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
|
val CONTINUATION_INTERFACE_ASM_TYPE = Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(CONTINUATION_INTERFACE_FQ_NAME).internalName)
|
||||||
|
|
||||||
|
// These classes do not actually exist at runtime
|
||||||
|
val CONTINUATION_METHOD_ANNOTATION_DESC = "Lkotlin/ContinuationMethod;"
|
||||||
|
|
||||||
|
const val SUSPENSION_POINT_MARKER_OWNER = "kotlin/Markers"
|
||||||
|
const val SUSPENSION_POINT_MARKER_NAME = "suspensionPoint"
|
||||||
|
|
||||||
|
const val COROUTINE_CONTROLLER_FIELD_NAME = "controller"
|
||||||
|
const val COROUTINE_LABEL_FIELD_NAME = "label"
|
||||||
+5
@@ -20,11 +20,16 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransfor
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
|
val SKIP_MANDATORY_TRANSFORMATIONS_ANNOTATION_DESC = "Lkotlin/SkipMandatoryTransformations;"
|
||||||
|
|
||||||
class MandatoryMethodTransformer : MethodTransformer() {
|
class MandatoryMethodTransformer : MethodTransformer() {
|
||||||
private val labelNormalization = LabelNormalizationMethodTransformer()
|
private val labelNormalization = LabelNormalizationMethodTransformer()
|
||||||
private val fixStack = FixStackMethodTransformer()
|
private val fixStack = FixStackMethodTransformer()
|
||||||
|
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
|
// Mandatory transformations have already been applied
|
||||||
|
if (methodNode.visibleAnnotations?.removeAll { it.desc == SKIP_MANDATORY_TRANSFORMATIONS_ANNOTATION_DESC } == true) return
|
||||||
|
|
||||||
labelNormalization.transform(internalClassName, methodNode)
|
labelNormalization.transform(internalClassName, methodNode)
|
||||||
fixStack.transform(internalClassName, methodNode)
|
fixStack.transform(internalClassName, methodNode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun InsnList.asSequence() = InsnSequence(this)
|
||||||
|
|
||||||
fun MethodNode.prepareForEmitting() {
|
fun MethodNode.prepareForEmitting() {
|
||||||
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
|
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
|
||||||
InsnSequence(tcb.start, tcb.end).any { insn ->
|
InsnSequence(tcb.start, tcb.end).any { insn ->
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ public abstract class MethodTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected static <V extends Value> Frame<V>[] analyze(
|
public static <V extends Value> Frame<V>[] analyze(
|
||||||
@NotNull String internalClassName,
|
@NotNull String internalClassName,
|
||||||
@NotNull MethodNode node,
|
@NotNull MethodNode node,
|
||||||
@NotNull Interpreter<V> interpreter
|
@NotNull Interpreter<V> interpreter
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.codegen.`when`.MappingsClassesForWhenByEnum
|
|||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
||||||
import org.jetbrains.kotlin.codegen.context.RootContext
|
import org.jetbrains.kotlin.codegen.context.RootContext
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerClassBuilderFactory
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.codegen.inline.InlineCache
|
import org.jetbrains.kotlin.codegen.inline.InlineCache
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
@@ -134,7 +135,7 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
|
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
|
||||||
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
|
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
|
||||||
val reflectionTypes: ReflectionTypes = ReflectionTypes(module)
|
val reflectionTypes: ReflectionTypes = ReflectionTypes(module)
|
||||||
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes()
|
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes(module)
|
||||||
val factory: ClassFileFactory
|
val factory: ClassFileFactory
|
||||||
private lateinit var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics
|
private lateinit var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics
|
||||||
|
|
||||||
@@ -160,6 +161,7 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
this.interceptedBuilderFactory = builderFactory
|
this.interceptedBuilderFactory = builderFactory
|
||||||
.wrapWith(
|
.wrapWith(
|
||||||
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
|
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
|
||||||
|
::CoroutineTransformerClassBuilderFactory,
|
||||||
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
{ BuilderFactoryForDuplicateSignatureDiagnostics(
|
||||||
it, this.bindingContext, diagnostics, fileClassesProvider, incrementalCacheForThisTarget, this.moduleName
|
it, this.bindingContext, diagnostics, fileClassesProvider, incrementalCacheForThisTarget, this.moduleName
|
||||||
).apply { duplicateSignatureFactory = this } },
|
).apply { duplicateSignatureFactory = this } },
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// FULL_JDK
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val x = gen().joinToString()
|
||||||
|
if (x != "1, 2") return "fail1: $x"
|
||||||
|
|
||||||
|
val y = gen().joinToString()
|
||||||
|
if (y != "-1") return "fail2: $y"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
var was = false
|
||||||
|
|
||||||
|
fun gen() = generate<Int> {
|
||||||
|
if (was) {
|
||||||
|
yield(-1)
|
||||||
|
return@generate
|
||||||
|
}
|
||||||
|
for (i in 1..2) {
|
||||||
|
yield(i)
|
||||||
|
}
|
||||||
|
was = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeneratorController<T>() : AbstractIterator<T>() {
|
||||||
|
private lateinit var nextStep: Continuation<Unit>
|
||||||
|
|
||||||
|
override fun computeNext() {
|
||||||
|
nextStep.resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNextStep(step: Continuation<Unit>) {
|
||||||
|
this.nextStep = step
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun yield(value: T, c: Continuation<Unit>) {
|
||||||
|
setNext(value)
|
||||||
|
setNextStep(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleResult(result: Unit, c: Continuation<Nothing>) {
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
class Controller {
|
||||||
|
var i = 0
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resume((i++).toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||||
|
c(Controller()).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder {
|
||||||
|
result += "-"
|
||||||
|
result += suspendHere()
|
||||||
|
|
||||||
|
if (result == "-0") {
|
||||||
|
builder {
|
||||||
|
result += "+"
|
||||||
|
result += suspendHere()
|
||||||
|
result += suspendHere()
|
||||||
|
result += "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
result += suspendHere()
|
||||||
|
result += "&"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "-0+01#1&") return "fail: $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
class Controller {
|
||||||
|
var res = 0
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resume("OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||||
|
res = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
||||||
|
val controller = Controller()
|
||||||
|
c(controller).resume(Unit)
|
||||||
|
|
||||||
|
return controller.res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
val handledResult = builder {
|
||||||
|
result = suspendHere()
|
||||||
|
return@builder 56
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handledResult != 56) return "fail 1: $handledResult"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class Controller {
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resume("OK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||||
|
c(Controller()).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder {
|
||||||
|
result = suspendHere()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
class Controller {
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resumeWithException(RuntimeException("OK"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||||
|
c(Controller()).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder {
|
||||||
|
try {
|
||||||
|
suspendHere()
|
||||||
|
result = "fail"
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
result = "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
class Controller {
|
||||||
|
var res = 0
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resume("OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||||
|
res = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>): Int {
|
||||||
|
val controller = Controller()
|
||||||
|
c(controller).resume(Unit)
|
||||||
|
|
||||||
|
return controller.res
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
val handledResult = builder {
|
||||||
|
result = suspendHere()
|
||||||
|
56
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handledResult != 56) return "fail 1: $handledResult"
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
class Controller {
|
||||||
|
var i = 0
|
||||||
|
suspend fun suspendHere(x: Continuation<Int>) {
|
||||||
|
x.resume(i++)
|
||||||
|
}
|
||||||
|
suspend fun suspendThere(x: Continuation<String>) {
|
||||||
|
x.resume("?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||||
|
c(Controller()).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder {
|
||||||
|
result += "-"
|
||||||
|
for (i in 0..5) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
result += suspendHere().toString()
|
||||||
|
}
|
||||||
|
else if (i == 3) {
|
||||||
|
result += suspendThere()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "+"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != "-01?2+") return "fail: $result"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// FULL_JDK
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
fun foo(): CompletableFuture<String> = CompletableFuture.supplyAsync { "foo" }
|
||||||
|
fun bar(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { "bar with $v" }
|
||||||
|
fun exception(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { throw RuntimeException(v) }
|
||||||
|
|
||||||
|
fun foobar(x: String, y: String) = x + y
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
fun log(x: String) {
|
||||||
|
synchronized(result) {
|
||||||
|
if (result.isNotEmpty()) result += "\n"
|
||||||
|
result += x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val future = async<String> {
|
||||||
|
log("start")
|
||||||
|
val x = await(foo())
|
||||||
|
log("got '$x'")
|
||||||
|
val y = foobar("123 ", await(bar(x)))
|
||||||
|
log("got '$y' after '$x'")
|
||||||
|
y
|
||||||
|
}
|
||||||
|
|
||||||
|
future.whenComplete { value, t ->
|
||||||
|
log("completed with '$value'")
|
||||||
|
}
|
||||||
|
|
||||||
|
future.join()
|
||||||
|
java.lang.Thread.sleep(1000)
|
||||||
|
|
||||||
|
val readResult = synchronized(result) {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
val expectedResult =
|
||||||
|
"""
|
||||||
|
|start
|
||||||
|
|got 'foo'
|
||||||
|
|got '123 bar with foo' after 'foo'
|
||||||
|
|completed with '123 bar with foo'""".trimMargin().trim('\n', ' ')
|
||||||
|
|
||||||
|
if (expectedResult != readResult) return readResult
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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> {
|
||||||
|
val future = CompletableFuture<T>()
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun <V> await(f: CompletableFuture<V>, machine: Continuation<V>) {
|
||||||
|
f.whenComplete { value, throwable ->
|
||||||
|
if (throwable == null)
|
||||||
|
machine.resume(value)
|
||||||
|
else
|
||||||
|
machine.resumeWithException(throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleResult(value: T, c: Continuation<Nothing>) {
|
||||||
|
future.complete(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||||
|
future.completeExceptionally(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// FULL_JDK
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
|
||||||
|
fun exception(v: String): CompletableFuture<String> = CompletableFuture.supplyAsync { throw RuntimeException(v) }
|
||||||
|
|
||||||
|
fun foobar(x: String, y: String) = x + y
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
val future = async<String>() {
|
||||||
|
try {
|
||||||
|
await(exception("OK"))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result = e.cause?.message!!
|
||||||
|
}
|
||||||
|
"56"
|
||||||
|
}
|
||||||
|
|
||||||
|
future.join()
|
||||||
|
|
||||||
|
if (future.get() != "56") return "fail: ${future.get()}"
|
||||||
|
|
||||||
|
java.lang.Thread.sleep(1000)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
val future = CompletableFuture<T>()
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun <V> await(f: CompletableFuture<V>, machine: Continuation<V>) {
|
||||||
|
f.whenComplete { value, throwable ->
|
||||||
|
try {
|
||||||
|
if (throwable == null)
|
||||||
|
machine.resume(value)
|
||||||
|
else
|
||||||
|
machine.resumeWithException(throwable)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
future.completeExceptionally(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleResult(value: T, c: Continuation<Nothing>) {
|
||||||
|
future.complete(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleException(t: Throwable, c: Continuation<Nothing>) {
|
||||||
|
future.completeExceptionally(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// FILE: A.kt
|
||||||
|
package a
|
||||||
|
class Controller {
|
||||||
|
suspend fun suspendHere(x: Continuation<String>) {
|
||||||
|
x.resume("OK")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||||
|
c(Controller()).resume(Unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: B.kt
|
||||||
|
import a.builder
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
builder {
|
||||||
|
result = suspendHere()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_VARIABLE
|
||||||
|
class GenericController<T> {
|
||||||
|
operator fun handleResult(x: T, c: Continuation<Nothing>) { }
|
||||||
|
suspend fun <V> await(f: V, machine: Continuation<V>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> genericBuilder(coroutine c: GenericController<T>.() -> Continuation<Unit>): T = null!!
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
var result = ""
|
||||||
|
genericBuilder<String> {
|
||||||
|
try {
|
||||||
|
await("")
|
||||||
|
} catch(e: Exception) {
|
||||||
|
<!EXPECTED_TYPE_MISMATCH!>result = "fail"<!>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun foo(): kotlin.Unit
|
||||||
|
public fun </*0*/ T> genericBuilder(/*0*/ coroutine c: GenericController<T>.() -> kotlin.coroutines.Continuation<kotlin.Unit>): T
|
||||||
|
|
||||||
|
public final class GenericController</*0*/ T> {
|
||||||
|
public constructor GenericController</*0*/ T>()
|
||||||
|
public final suspend fun </*0*/ V> await(/*0*/ f: V, /*1*/ machine: kotlin.coroutines.Continuation<V>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public final operator fun handleResult(/*0*/ x: T, /*1*/ c: kotlin.coroutines.Continuation<kotlin.Nothing>): kotlin.Unit
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
+12
@@ -35,6 +35,18 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box"), Pattern.compile("^(.+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/java8/box"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("async.kt")
|
||||||
|
public void testAsync() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/async.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("asyncException.kt")
|
||||||
|
public void testAsyncException() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/asyncException.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultMethodCallFromInterface.kt")
|
@TestMetadata("defaultMethodCallFromInterface.kt")
|
||||||
public void testDefaultMethodCallFromInterface() throws Exception {
|
public void testDefaultMethodCallFromInterface() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/defaultMethodCallFromInterface.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/defaultMethodCallFromInterface.kt");
|
||||||
|
|||||||
@@ -3879,6 +3879,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("tryCatchLambda.kt")
|
||||||
|
public void testTryCatchLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/tryCatchLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("wrongHandleResult.kt")
|
@TestMetadata("wrongHandleResult.kt")
|
||||||
public void testWrongHandleResult() throws Exception {
|
public void testWrongHandleResult() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleResult.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/wrongHandleResult.kt");
|
||||||
|
|||||||
@@ -4103,6 +4103,57 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/box/coroutines")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class Coroutines extends AbstractBlackBoxCodegenTest {
|
||||||
|
public void testAllFilesPresentInCoroutines() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("generate.kt")
|
||||||
|
public void testGenerate() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/generate.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("innerSuspensionCalls.kt")
|
||||||
|
public void testInnerSuspensionCalls() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/innerSuspensionCalls.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnByLabel.kt")
|
||||||
|
public void testReturnByLabel() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simple.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleException.kt")
|
||||||
|
public void testSimpleException() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simpleException.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simpleWithHandleResult.kt")
|
||||||
|
public void testSimpleWithHandleResult() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simpleWithHandleResult.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInCycle.kt")
|
||||||
|
public void testSuspendInCycle() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendInCycle.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/box/dataClasses")
|
@TestMetadata("compiler/testData/codegen/box/dataClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+6
@@ -77,6 +77,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("coroutinesBinary.kt")
|
||||||
|
public void testCoroutinesBinary() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/coroutinesBinary.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("defaultConstructor.kt")
|
@TestMetadata("defaultConstructor.kt")
|
||||||
public void testDefaultConstructor() throws Exception {
|
public void testDefaultConstructor() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/defaultConstructor.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/defaultConstructor.kt");
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ interface ValueParameterDescriptor : VariableDescriptor, ParameterDescriptor {
|
|||||||
|
|
||||||
override fun substitute(substitutor: TypeSubstitutor): ValueParameterDescriptor
|
override fun substitute(substitutor: TypeSubstitutor): ValueParameterDescriptor
|
||||||
|
|
||||||
fun copy(newOwner: CallableDescriptor, newName: Name): ValueParameterDescriptor
|
fun copy(newOwner: CallableDescriptor, newName: Name, newIndex: Int): ValueParameterDescriptor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameter p1 overrides p2 iff
|
* Parameter p1 overrides p2 iff
|
||||||
|
|||||||
+2
-3
@@ -58,10 +58,9 @@ class ValueParameterDescriptorImpl(
|
|||||||
override fun isVar() = false
|
override fun isVar() = false
|
||||||
|
|
||||||
override fun getCompileTimeInitializer() = null
|
override fun getCompileTimeInitializer() = null
|
||||||
|
override fun copy(newOwner: CallableDescriptor, newName: Name, newIndex: Int): ValueParameterDescriptor {
|
||||||
override fun copy(newOwner: CallableDescriptor, newName: Name): ValueParameterDescriptor {
|
|
||||||
return ValueParameterDescriptorImpl(
|
return ValueParameterDescriptorImpl(
|
||||||
newOwner, null, index, annotations, newName, type, declaresDefaultValue(),
|
newOwner, null, newIndex, annotations, newName, type, declaresDefaultValue(),
|
||||||
isCrossinline, isNoinline, isCoroutine, varargElementType, SourceElement.NO_SOURCE
|
isCrossinline, isNoinline, isCoroutine, varargElementType, SourceElement.NO_SOURCE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ class ChangeMemberFunctionSignatureFix private constructor(
|
|||||||
override fun choose(parameter: ValueParameterDescriptor, superParameter: ValueParameterDescriptor): ValueParameterDescriptor? {
|
override fun choose(parameter: ValueParameterDescriptor, superParameter: ValueParameterDescriptor): ValueParameterDescriptor? {
|
||||||
// TODO: support for generic functions
|
// TODO: support for generic functions
|
||||||
if (KotlinTypeChecker.DEFAULT.equalTypes(parameter.type, superParameter.type)) {
|
if (KotlinTypeChecker.DEFAULT.equalTypes(parameter.type, superParameter.type)) {
|
||||||
return superParameter.copy(parameter.containingDeclaration, parameter.name)
|
return superParameter.copy(parameter.containingDeclaration, parameter.name, parameter.index)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null
|
return null
|
||||||
|
|||||||
Reference in New Issue
Block a user