Support calling experimental coroutines API in JVM

The support is very limited, though

 #KT-25683 Fixed
This commit is contained in:
Denis Zharkov
2018-08-20 14:46:33 +03:00
parent 20c7a97bcd
commit ca39cc47c9
9 changed files with 250 additions and 57 deletions
@@ -298,6 +298,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
StackValue stackValue = selector.accept(visitor, receiver);
stackValue = suspendFunctionTypeWrapperIfNeeded(selector, stackValue);
RuntimeAssertionInfo runtimeAssertionInfo = null;
if (selector instanceof KtExpression) {
KtExpression expression = (KtExpression) selector;
@@ -321,6 +323,32 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
private StackValue suspendFunctionTypeWrapperIfNeeded(KtElement selector, StackValue stackValue) {
Type functionTypeForWrapper =
selector instanceof KtExpression
? bindingContext.get(CodegenBinding.FUNCTION_TYPE_FOR_SUSPEND_WRAPPER, (KtExpression) selector)
: null;
if (functionTypeForWrapper == null) return stackValue;
StackValue stackValueToWrap = stackValue;
stackValue = new StackValue(stackValue.type, stackValue.kotlinType) {
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
stackValueToWrap.put(functionTypeForWrapper, null, v);
invokeCoroutineMigrationMethod(
v,
"toExperimentalSuspendFunction",
Type.getMethodDescriptor(functionTypeForWrapper, functionTypeForWrapper)
);
coerce(functionTypeForWrapper, type, v);
}
};
return stackValue;
}
public StackValue gen(KtElement expr) {
StackValue tempVar = tempVariables.get(expr);
return tempVar != null ? tempVar : genQualified(StackValue.none(), expr);
@@ -2299,9 +2327,43 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
coroutineInstanceValueForSuspensionPoint != null
? coroutineInstanceValueForSuspensionPoint
: getContinuationParameterFromEnclosingSuspendFunction(resolvedCall);
if (coroutineInstanceValue != null && needsExperimentalCoroutinesWrapper(resolvedCall.getCandidateDescriptor())) {
StackValue releaseContinuation = coroutineInstanceValue;
coroutineInstanceValue = new StackValue(CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE) {
@Override
public void putSelector(
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
) {
releaseContinuation.put(CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE, v);
invokeCoroutineMigrationMethod(
v,
"toExperimentalContinuation",
Type.getMethodDescriptor(
CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE,
CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE
)
);
}
};
}
tempVariables.put(continuationExpression, coroutineInstanceValue);
}
private static void invokeCoroutineMigrationMethod(
@NotNull InstructionAdapter v,
String methodName,
String descriptor
) {
v.invokestatic(
"kotlin/coroutines/experimental/migration/CoroutinesMigrationKt",
methodName,
descriptor,
false
);
}
private StackValue getContinuationParameterFromEnclosingSuspendFunction(@NotNull ResolvedCall<?> resolvedCall) {
FunctionDescriptor enclosingSuspendFunction =
bindingContext.get(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.getCall());
@@ -15,6 +15,7 @@ import kotlin.Pair;
import kotlin.collections.CollectionsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.cfg.WhenChecker;
import org.jetbrains.kotlin.codegen.*;
@@ -52,6 +53,7 @@ import org.jetbrains.kotlin.resolve.constants.EnumValue;
import org.jetbrains.kotlin.resolve.constants.NullValue;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionsOnDeclarationBodyChecker;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.kotlin.types.KotlinType;
@@ -61,6 +63,7 @@ import org.jetbrains.org.objectweb.asm.Type;
import java.util.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.NUMBERED_FUNCTION_PREFIX;
import static org.jetbrains.kotlin.lexer.KtTokens.*;
import static org.jetbrains.kotlin.name.SpecialNames.safeIdentifier;
import static org.jetbrains.kotlin.resolve.BindingContext.*;
@@ -645,6 +648,54 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
super.visitCallExpression(expression);
checkSamCall(expression);
checkCrossinlineSuspendCall(expression);
recordSuspendFunctionTypeWrapperForArguments(expression);
}
private void recordSuspendFunctionTypeWrapperForArguments(@NotNull KtCallExpression expression) {
ResolvedCall<?> call = CallUtilKt.getResolvedCall(expression, bindingContext);
if (call == null) return;
CallableDescriptor descriptor = call.getResultingDescriptor();
if (!CodegenUtilKt.needsExperimentalCoroutinesWrapper(descriptor)) return;
List<ResolvedValueArgument> argumentsByIndex = call.getValueArgumentsByIndex();
if (argumentsByIndex == null) return;
for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
ResolvedValueArgument resolvedValueArgument = argumentsByIndex.get(parameter.getIndex());
if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue;
ValueArgument valueArgument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
if (valueArgument == null) continue;
KtExpression argumentExpression = valueArgument.getArgumentExpression();
if (argumentExpression == null) continue;
recordSuspendFunctionTypeWrapperForArgument(parameter, argumentExpression);
}
ReceiverValue receiver = call.getExtensionReceiver();
if (descriptor.getExtensionReceiverParameter() != null && receiver instanceof ExpressionReceiver) {
recordSuspendFunctionTypeWrapperForArgument(
descriptor.getExtensionReceiverParameter(),
((ExpressionReceiver) receiver).getExpression()
);
}
}
private void recordSuspendFunctionTypeWrapperForArgument(ParameterDescriptor parameter, KtExpression argumentExpression) {
if (FunctionTypesKt.isSuspendFunctionTypeOrSubtype(parameter.getType())) {
// SuspendFunctionN type is mapped to is mapped to FunctionTypeN+1, but we also need to remove an argument for return type
// So, it could be parameter.getType().getArguments().size() + 1 - 1
int functionTypeArity = parameter.getType().getArguments().size();
Type functionType = Type.getObjectType(NUMBERED_FUNCTION_PREFIX + functionTypeArity);
bindingTrace.record(
FUNCTION_TYPE_FOR_SUSPEND_WRAPPER,
argumentExpression,
functionType
);
}
}
private void checkCrossinlineSuspendCall(@NotNull KtCallExpression expression) {
@@ -43,6 +43,7 @@ public class CodegenBinding {
private static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
public static final WritableSlice<KtExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
public static final WritableSlice<KtExpression, Type> FUNCTION_TYPE_FOR_SUSPEND_WRAPPER = Slices.createSimpleSlice();
public static final WritableSlice<KtCallElement, KtExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice();
@@ -41,6 +41,8 @@ import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
@@ -431,4 +433,7 @@ fun KotlinType.isInlineClassTypeWithPrimitiveEquality(): Boolean {
// TODO support other inline classes that can be compared as underlying primitives
return false
}
}
fun CallableDescriptor.needsExperimentalCoroutinesWrapper() =
(this as? DeserializedMemberDescriptor)?.coroutinesExperimentalCompatibilityMode == CoroutinesCompatibilityMode.NEEDS_WRAPPER
@@ -295,7 +295,7 @@ fun <D : FunctionDescriptor> D.createCustomCopy(
}
private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction(isReleaseCoroutines: Boolean) =
module.getContinuationOfTypeOrAny(returnType!!, isReleaseCoroutines)
module.getContinuationOfTypeOrAny(returnType!!, if (this.needsExperimentalCoroutinesWrapper()) false else isReleaseCoroutines)
fun ModuleDescriptor.getSuccessOrFailure(kotlinType: KotlinType) =
module.resolveTopLevelClass(
@@ -498,3 +498,9 @@ fun FunctionDescriptor.isSuspendLambdaOrLocalFunction() = this.isSuspend && when
}
fun FunctionDescriptor.isLocalSuspendFunctionNotSuspendLambda() = isSuspendLambdaOrLocalFunction() && this !is AnonymousFunctionDescriptor
@JvmField
val EXPERIMENTAL_CONTINUATION_ASM_TYPE = DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_EXPERIMENTAL.topLevelClassAsmType()
@JvmField
val RELEASE_CONTINUATION_ASM_TYPE = DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME_RELEASE.topLevelClassAsmType()