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> =
|
||||
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!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
+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.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 ->
|
||||
|
||||
+1
-1
@@ -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 } },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
+6
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user