Add diagnostics tests. Forbid callable reference to coroutineContext

#KT-16908: Fixed
This commit is contained in:
Ilmir Usmanov
2018-06-06 18:54:08 +03:00
parent f94b579d19
commit eea95441c5
107 changed files with 780 additions and 4849 deletions
@@ -982,11 +982,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
ClosureCodegen coroutineCodegen = CoroutineCodegenForLambda.create(this, descriptor, declaration, cv);
ClosureContext closureContext = descriptor.isSuspend() ? this.context.intoCoroutineClosure(
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
descriptor,
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines),
state.getBindingContext()
),
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, state),
descriptor, this, state.getTypeMapper()
) : this.context.intoClosure(descriptor, this, typeMapper);
ClosureCodegen closureCodegen = coroutineCodegen != null ? coroutineCodegen : new ClosureCodegen(
@@ -178,8 +178,7 @@ public class FunctionCodegen {
@NotNull FunctionGenerationStrategy strategy
) {
if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(descriptor)) {
generateMethod(origin, CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, state.getLanguageVersionSettings()
.supportsFeature(LanguageFeature.ReleaseCoroutines), bindingContext), strategy);
generateMethod(origin, CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, state), strategy);
return;
}
@@ -675,8 +674,7 @@ public class FunctionCodegen {
if (functionDescriptor instanceof AnonymousFunctionDescriptor && functionDescriptor.isSuspend()) {
functionDescriptor = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
functionDescriptor,
parentCodegen.state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines),
typeMapper.getBindingContext()
parentCodegen.state
);
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 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.
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
@@ -42,6 +31,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.addFakeContinuationMarker;
/*
* Notice the difference between two function descriptors in this class.
* - [referencedFunction] is the function declaration which is referenced by the "::" expression. This is a real function present in code.
@@ -78,19 +69,14 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
) {
super(state);
this.resolvedCall = resolvedCall;
if (resolvedCall.getResultingDescriptor() instanceof FunctionDescriptor &&
((FunctionDescriptor) resolvedCall.getResultingDescriptor()).isSuspend()) {
this.referencedFunction = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(FunctionDescriptor) resolvedCall.getResultingDescriptor(),
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines),
state.getBindingContext());
this.functionDescriptor = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
functionDescriptor,
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines),
state.getBindingContext());
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
if (resultingDescriptor instanceof FunctionDescriptor && ((FunctionDescriptor) resultingDescriptor).isSuspend()) {
this.referencedFunction =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView((FunctionDescriptor) resultingDescriptor, state);
this.functionDescriptor = CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(functionDescriptor, state);
}
else {
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
this.referencedFunction = (FunctionDescriptor) resultingDescriptor;
this.functionDescriptor = functionDescriptor;
}
this.receiverType = receiverType;
@@ -407,20 +407,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (callableDescriptor instanceof SimpleFunctionDescriptor) {
SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) callableDescriptor;
if (functionDescriptor.isSuspend()){
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
functionDescriptor,
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
/*bindingContext*/ null
);
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
closure.setSuspend(true);
createAndRecordSuspendFunctionView(closure, functionDescriptor, /* isSuspendLambda */ false, /* isDropSuspend */ false);
}
}
@@ -431,6 +418,33 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
super.visitCallableReferenceExpression(expression);
}
private SimpleFunctionDescriptor createAndRecordSuspendFunctionView(
MutableClosure closure,
SimpleFunctionDescriptor functionDescriptor,
boolean isSuspendLambda,
boolean isDropSuspend
) {
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
functionDescriptor,
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
this.bindingContext,
isDropSuspend
);
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
closure.setSuspend(true);
if (isSuspendLambda) {
closure.setSuspendLambda();
}
return jvmSuspendFunctionView;
}
@NotNull
private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
return CodegenBinding.recordClosure(bindingTrace, classDescriptor, getProperEnclosingClass(), Type.getObjectType(name));
@@ -557,11 +571,18 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend() &&
!functionDescriptor.getVisibility().equals(Visibilities.LOCAL)) {
if (nameForClassOrPackageMember != null) {
nameStack.push(nameForClassOrPackageMember);
}
String name = inventAnonymousClassName();
ClassDescriptor classDescriptor = recordClassForFunction(function, functionDescriptor, name, functionDescriptor);
MutableClosure closure = recordClosure(classDescriptor, name);
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(SimpleFunctionDescriptor) functionDescriptor,
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
);
createAndRecordSuspendFunctionView(closure, (SimpleFunctionDescriptor) functionDescriptor,
/* isSuspendLambda*/ false, /* isDropSuspend */ false);
// This is a very subtle place (hack).
// When generating bytecode of some suspend function, we replace the original descriptor
@@ -577,21 +598,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
);
}
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
if (nameForClassOrPackageMember != null) {
nameStack.push(nameForClassOrPackageMember);
}
String name = inventAnonymousClassName();
ClassDescriptor classDescriptor =
recordClassForFunction(function, functionDescriptor, name, functionDescriptor);
MutableClosure closure = recordClosure(classDescriptor, name);
closure.setSuspend(true);
functionsStack.push(functionDescriptor);
super.visitNamedFunction(function);
@@ -620,21 +626,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
nameStack.push(name);
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) {
SimpleFunctionDescriptor jvmSuspendFunctionView =
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
(SimpleFunctionDescriptor) functionDescriptor,
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
/*bindingContext*/ null,
/*dropSuspend*/ true
);
bindingTrace.record(
CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW,
functionDescriptor,
jvmSuspendFunctionView
);
closure.setSuspend(true);
closure.setSuspendLambda();
createAndRecordSuspendFunctionView(closure, (SimpleFunctionDescriptor) functionDescriptor,
/* isSuspendLambda */ true, /* isDropSuspend */ true);
}
functionsStack.push(functionDescriptor);
@@ -338,11 +338,7 @@ class CoroutineCodegenForLambda private constructor(
expressionCodegen,
declaration,
expressionCodegen.context.intoCoroutineClosure(
getOrCreateJvmSuspendFunctionView(
originalSuspendLambdaDescriptor,
expressionCodegen.state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
expressionCodegen.state.bindingContext
),
getOrCreateJvmSuspendFunctionView(originalSuspendLambdaDescriptor, expressionCodegen.state),
originalSuspendLambdaDescriptor, expressionCodegen, expressionCodegen.state.typeMapper
),
classBuilder,
@@ -201,6 +201,12 @@ fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean {
return this.isSuspend && this.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) == null
}
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, state: GenerationState): D = getOrCreateJvmSuspendFunctionView(
function,
state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
state.bindingContext
)
// Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation<return-type>`,
// and return type Any?
// This function returns a function descriptor reflecting how the suspend function looks from point of view of JVM
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClosureCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
@@ -21,6 +22,7 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
@@ -214,12 +216,34 @@ class MethodInliner(
return
}
// in case of inlining suspend lambda reference as ordinary parameter of inline function:
// suspend fun foo (...) ...
// inline fun inlineMe(c: (...) -> ...) ...
// builder {
// inlineMe(::foo)
// }
// we should create additional parameter for continuation.
var coroutineDesc = desc
if (info.invokeMethodDescriptor.isSuspend) {
val coroutineInvokeMethodDescriptor = getOrCreateJvmSuspendFunctionView(
info.invokeMethodDescriptor,
inliningContext.state
)
val coroutineInvokeDesc = typeMapper.mapAsmMethod(coroutineInvokeMethodDescriptor).descriptor
// And here we expect invoke(...Ljava/lang/Object;) be replaced with invoke(...Lkotlin/coroutines/Continuation;)
// if this does not happen, insert fake continuation, since we could not have one yet.
val argumentTypes = Type.getArgumentTypes(desc)
if (Type.getArgumentTypes(coroutineInvokeDesc).size != argumentTypes.size) {
addFakeContinuationMarker(this)
coroutineDesc = Type.getMethodDescriptor(Type.getReturnType(desc), *argumentTypes, AsmTypes.OBJECT_TYPE)
}
}
val valueParameters =
listOfNotNull(info.invokeMethodDescriptor.extensionReceiverParameter) + info.invokeMethodDescriptor.valueParameters
val valueParamShift = Math.max(nextLocalIndex, markerShift)//NB: don't inline cause it changes
putStackValuesIntoLocalsForLambdaOnInvoke(
listOf(*info.invokeMethod.argumentTypes), valueParameters, valueParamShift, this, desc
listOf(*info.invokeMethod.argumentTypes), valueParameters, valueParamShift, this, coroutineDesc
)
if (invokeCall.lambdaInfo.invokeMethodDescriptor.valueParameters.isEmpty()) {
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor;
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
@@ -1024,8 +1025,8 @@ public class KotlinTypeMapper {
}
private static boolean isSuspendFunctionReference(FunctionDescriptor descriptor) {
return descriptor.getSource() instanceof KotlinSourceElement &&
((KotlinSourceElement) descriptor.getSource()).getPsi() instanceof KtCallableReferenceExpression &&
return descriptor instanceof SimpleFunctionDescriptor &&
descriptor.getName().isSpecial() &&
CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(descriptor) != null &&
CoroutineCodegenUtilKt.unwrapInitialDescriptorForSuspendFunction(descriptor).isSuspend();
}