Implement basic support for coroutines in JVM backend

This commit is contained in:
Denis Zharkov
2016-05-25 14:46:38 +03:00
parent 077fc528d1
commit 75e112e752
30 changed files with 1151 additions and 45 deletions
@@ -43,5 +43,5 @@ open class AbstractAccessorForFunctionDescriptor(
}
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.*;
public class ClosureCodegen extends MemberCodegen<KtElement> {
private final FunctionDescriptor funDescriptor;
protected final FunctionDescriptor funDescriptor;
private final ClassDescriptor classDescriptor;
private final SamType samType;
private final KotlinType superClassType;
private final List<KotlinType> superInterfaceTypes;
private final FunctionDescriptor functionReferenceTarget;
private final FunctionGenerationStrategy strategy;
protected final FunctionGenerationStrategy strategy;
private final CalculatedClosure closure;
private final Type asmType;
private final int visibilityFlag;
@@ -170,6 +170,32 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
@Override
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;
if (samType == null) {
erasedInterfaceFunction = getErasedInvokeFunction(funDescriptor);
@@ -183,8 +209,6 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
typeMapper.mapAsmMethod(funDescriptor)
);
functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy);
//TODO: rewrite cause ugly hack
if (samType != null) {
SimpleFunctionDescriptorImpl descriptorForBridges = SimpleFunctionDescriptorImpl
@@ -200,22 +224,6 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
DescriptorUtilsKt.setSingleOverridden(descriptorForBridges, erasedInterfaceFunction);
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
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
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.inline.*;
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.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.LocalVariableDescriptor;
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.JvmCodegenUtil.*;
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.BindingContextUtils.*;
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) {
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
if (value.type != Type.VOID_TYPE && state.getReplSpecific().getShouldGenerateScriptResultValue()) {
ScriptContext context = getScriptContext();
@@ -1438,7 +1446,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
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),
functionReferenceTarget, strategy, parentCodegen, cv
);
@@ -1613,6 +1622,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (!iterator.hasNext()) {
answer = result;
StackValue handleResultValue = !(possiblyLabeledStatement instanceof KtReturnExpression)
? genControllerHandleResultCallIfNeeded(possiblyLabeledStatement, result)
: null;
if (handleResultValue != null) {
answer = handleResultValue;
}
}
else {
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
private Type getVariableType(@NotNull VariableDescriptor 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;
if (returnedExpression != null) {
gen(returnedExpression, returnType);
StackValue valueToReturn = returnedExpression != null ? gen(returnedExpression) : null;
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();
@@ -2491,6 +2537,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull CallGenerator callGenerator,
@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;
if (!isConstructor) { // otherwise already
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);
if (isCallToSuspendFunction) {
addInlineMarker(v, false);
}
KotlinType returnType = resolvedCall.getResultingDescriptor().getReturnType();
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
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
private CallGenerator getOrCreateCallGenerator(
@NotNull CallableDescriptor descriptor,
@@ -2634,7 +2700,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
else if (receiverValue instanceof ExtensionReceiver) {
return generateReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
return generateExtensionReceiver(((ExtensionReceiver) receiverValue).getDeclarationDescriptor());
}
else if (receiverValue instanceof ExpressionReceiver) {
return gen(((ExpressionReceiver) receiverValue).getExpression());
@@ -2645,7 +2711,19 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@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);
}
@@ -3736,7 +3814,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.thisOrOuter(this, (ClassDescriptor) descriptor, false, true);
}
if (descriptor instanceof CallableDescriptor) {
return generateReceiver((CallableDescriptor) descriptor);
return generateExtensionReceiver((CallableDescriptor) descriptor);
}
throw new UnsupportedOperationException("Neither this nor receiver: " + descriptor);
}
@@ -17,20 +17,21 @@
package org.jetbrains.kotlin.codegen;
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.annotations.Annotations;
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.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.FunctionTypeResolveUtilsKt;
import org.jetbrains.kotlin.resolve.TargetPlatformKt;
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.TypeConstructorSubstitution;
import org.jetbrains.kotlin.types.TypeProjectionImpl;
import org.jetbrains.kotlin.types.Variance;
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
import java.util.*;
@@ -42,13 +43,9 @@ public class JvmRuntimeTypes {
private final List<ClassDescriptor> mutablePropertyReferences;
private final ClassDescriptor localVariableReference;
private final ClassDescriptor mutableLocalVariableReference;
private final KotlinType defaultContinuationSupertype;
public JvmRuntimeTypes() {
ModuleDescriptorImpl module = TargetPlatformKt.createModule(
JvmPlatform.INSTANCE,
Name.special("<jvm functions impl>"),
LockBasedStorageManager.NO_LOCKS,
DefaultBuiltIns.getInstance());
public JvmRuntimeTypes(@NotNull ModuleDescriptor module) {
PackageFragmentDescriptor kotlinJvmInternal = new MutablePackageFragmentDescriptor(module, new FqName("kotlin.jvm.internal"));
this.lambda = createClass(kotlinJvmInternal, "Lambda");
@@ -62,6 +59,27 @@ public class JvmRuntimeTypes {
propertyReferences.add(createClass(kotlinJvmInternal, "PropertyReference" + 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
@@ -91,6 +109,13 @@ public class JvmRuntimeTypes {
descriptor.getReturnType()
);
KotlinType coroutineControllerType = CoroutineUtilKt.getControllerTypeIfCoroutine(descriptor);
if (coroutineControllerType != null) {
return Arrays.asList(
lambda.getDefaultType(), functionType, /*coroutineType,*/ defaultContinuationSupertype);
}
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!!)
}
}
}
@@ -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"
@@ -20,11 +20,16 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransfor
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.tree.MethodNode
val SKIP_MANDATORY_TRANSFORMATIONS_ANNOTATION_DESC = "Lkotlin/SkipMandatoryTransformations;"
class MandatoryMethodTransformer : MethodTransformer() {
private val labelNormalization = LabelNormalizationMethodTransformer()
private val fixStack = FixStackMethodTransformer()
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)
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() {
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
InsnSequence(tcb.start, tcb.end).any { insn ->
@@ -36,7 +36,7 @@ public abstract class MethodTransformer {
}
@NotNull
protected static <V extends Value> Frame<V>[] analyze(
public static <V extends Value> Frame<V>[] analyze(
@NotNull String internalClassName,
@NotNull MethodNode node,
@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.context.CodegenContext
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.inline.InlineCache
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
@@ -134,7 +135,7 @@ class GenerationState @JvmOverloads constructor(
val inlineCycleReporter: InlineCycleReporter = InlineCycleReporter(diagnostics)
val mappingsClassesForWhenByEnum: MappingsClassesForWhenByEnum = MappingsClassesForWhenByEnum(this)
val reflectionTypes: ReflectionTypes = ReflectionTypes(module)
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes()
val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes(module)
val factory: ClassFileFactory
private lateinit var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics
@@ -160,6 +161,7 @@ class GenerationState @JvmOverloads constructor(
this.interceptedBuilderFactory = builderFactory
.wrapWith(
{ OptimizationClassBuilderFactory(it, configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false)) },
::CoroutineTransformerClassBuilderFactory,
{ BuilderFactoryForDuplicateSignatureDiagnostics(
it, this.bindingContext, diagnostics, fileClassesProvider, incrementalCacheForThisTarget, this.moduleName
).apply { duplicateSignatureFactory = this } },
+54
View File
@@ -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
}
+19
View File
@@ -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
View File
@@ -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)
}
}
+61
View File
@@ -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
}
@@ -35,6 +35,18 @@ public class BlackBoxWithJava8CodegenTestGenerated extends AbstractBlackBoxCodeg
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")
public void testDefaultMethodCallFromInterface() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/java8/box/defaultMethodCallFromInterface.kt");
@@ -3879,6 +3879,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
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")
public void testWrongHandleResult() throws Exception {
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")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -77,6 +77,12 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
doTest(fileName);
}
@TestMetadata("coroutinesBinary.kt")
public void testCoroutinesBinary() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/coroutinesBinary.kt");
doTest(fileName);
}
@TestMetadata("defaultConstructor.kt")
public void testDefaultConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/compileKotlinAgainstKotlin/defaultConstructor.kt");
@@ -42,7 +42,7 @@ interface ValueParameterDescriptor : VariableDescriptor, ParameterDescriptor {
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
@@ -58,10 +58,9 @@ class ValueParameterDescriptorImpl(
override fun isVar() = false
override fun getCompileTimeInitializer() = null
override fun copy(newOwner: CallableDescriptor, newName: Name): ValueParameterDescriptor {
override fun copy(newOwner: CallableDescriptor, newName: Name, newIndex: Int): ValueParameterDescriptor {
return ValueParameterDescriptorImpl(
newOwner, null, index, annotations, newName, type, declaresDefaultValue(),
newOwner, null, newIndex, annotations, newName, type, declaresDefaultValue(),
isCrossinline, isNoinline, isCoroutine, varargElementType, SourceElement.NO_SOURCE
)
}
@@ -259,7 +259,7 @@ class ChangeMemberFunctionSignatureFix private constructor(
override fun choose(parameter: ValueParameterDescriptor, superParameter: ValueParameterDescriptor): ValueParameterDescriptor? {
// TODO: support for generic functions
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 {
return null