Control-Flow: Move type predicate computation out of control-flow analysis

This commit is contained in:
Alexey Sedunov
2015-05-28 16:26:46 +03:00
parent 34cd2e0ac3
commit 969183fbf3
7 changed files with 158 additions and 233 deletions
@@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue;
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
import org.jetbrains.kotlin.cfg.pseudocode.TypePredicate;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.*;
@@ -120,7 +119,6 @@ public interface JetControlFlowBuilder {
@NotNull JetElement instructionElement,
@Nullable JetElement valueElement,
@NotNull List<PseudoValue> inputValues,
@NotNull Map<PseudoValue, TypePredicate> expectedTypes,
@NotNull MagicKind kind
);
@@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue;
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
import org.jetbrains.kotlin.cfg.pseudocode.TypePredicate;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.*;
@@ -71,10 +70,9 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil
@NotNull JetElement instructionElement,
@Nullable JetElement valueElement,
@NotNull List<PseudoValue> inputValues,
@NotNull Map<PseudoValue, TypePredicate> expectedTypes,
@NotNull MagicKind kind
) {
return getDelegateBuilder().magic(instructionElement, valueElement, inputValues, expectedTypes, kind);
return getDelegateBuilder().magic(instructionElement, valueElement, inputValues, kind);
}
@NotNull
@@ -17,20 +17,21 @@
package org.jetbrains.kotlin.cfg;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartFMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import kotlin.KotlinPackage;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.cfg.pseudocode.*;
import org.jetbrains.kotlin.cfg.pseudocode.JetControlFlowInstructionsGenerator;
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue;
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode;
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind;
@@ -41,15 +42,12 @@ import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils;
import org.jetbrains.kotlin.resolve.calls.model.*;
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate;
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
@@ -142,7 +140,7 @@ public class JetControlFlowProcessor {
if (!generateCall(condition.getOperationReference())) {
JetExpression rangeExpression = condition.getRangeExpression();
generateInstructions(rangeExpression);
createUnresolvedCall(condition, rangeExpression);
createNonSyntheticValue(condition, MagicKind.UNRESOLVED_CALL, rangeExpression);
}
}
@@ -209,7 +207,7 @@ public class JetControlFlowProcessor {
@NotNull
private PseudoValue createSyntheticValue(@NotNull JetElement instructionElement, @NotNull MagicKind kind, JetElement... from) {
List<PseudoValue> values = elementsToValues(from.length > 0 ? Arrays.asList(from) : Collections.<JetElement>emptyList());
return builder.magic(instructionElement, null, values, defaultTypeMap(values), kind).getOutputValue();
return builder.magic(instructionElement, null, values, kind).getOutputValue();
}
@NotNull
@@ -217,7 +215,7 @@ public class JetControlFlowProcessor {
@NotNull JetElement to, @NotNull List<? extends JetElement> from, @NotNull MagicKind kind
) {
List<PseudoValue> values = elementsToValues(from);
return builder.magic(to, to, values, defaultTypeMap(values), kind).getOutputValue();
return builder.magic(to, to, values, kind).getOutputValue();
}
@NotNull
@@ -225,114 +223,6 @@ public class JetControlFlowProcessor {
return createNonSyntheticValue(to, Arrays.asList(from), kind);
}
@Nullable
private Map<PseudoValue, TypePredicate> getTypeMapForUnresolvedCall(@NotNull JetElement to, List<PseudoValue> arguments) {
Call call = CallUtilPackage.getCall(to, trace.getBindingContext());
if (call == null) return null;
JetExpression callee = call.getCalleeExpression();
if (callee == null) return null;
Collection<FunctionDescriptor> candidates = KotlinPackage.sortBy(
KotlinPackage.filterIsInstance(
BindingContextUtilPackage.getReferenceTargets(callee, trace.getBindingContext()),
FunctionDescriptor.class
),
new Function1<FunctionDescriptor, Comparable>() {
@Override
public Comparable invoke(FunctionDescriptor descriptor) {
return DescriptorRenderer.DEBUG_TEXT.render(descriptor);
}
}
);
if (candidates.isEmpty()) return null;
ReceiverValue explicitReceiver = call.getExplicitReceiver();
int argValueOffset = explicitReceiver.exists() ? 1 : 0;
MultiMap<PseudoValue, TypePredicate> valuesToPredicates = new MultiMap<PseudoValue, TypePredicate>(arguments.size(), 1);
candidateLoop:
for (FunctionDescriptor candidate : candidates) {
ResolvedCallImpl<FunctionDescriptor> candidateCall = ResolvedCallImpl.create(
ResolutionCandidate.create(call,
candidate,
call.getDispatchReceiver(),
explicitReceiver,
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
null),
new DelegatingBindingTrace(trace.getBindingContext(), "Compute type predicates for unresolved call arguments"),
TracingStrategy.EMPTY,
new DataFlowInfoForArgumentsImpl(call)
);
ValueArgumentsToParametersMapper.Status status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(
call,
TracingStrategy.EMPTY,
candidateCall,
Sets.<ValueArgument>newLinkedHashSet()
);
if (!status.isSuccess()) continue;
if ((candidate.getExtensionReceiverParameter() == null) == candidateCall.getExtensionReceiver().exists()) continue;
Map<ValueParameterDescriptor, ResolvedValueArgument> candidateArgumentMap = candidateCall.getValueArguments();
List<? extends ValueArgument> callArguments = call.getValueArguments();
for (int i = 0; i < callArguments.size(); i++) {
int valueIndex = i + argValueOffset;
if (valueIndex >= arguments.size()) continue candidateLoop;
PseudoValue argumentValue = arguments.get(valueIndex);
ArgumentMapping mapping = candidateCall.getArgumentMapping(callArguments.get(i));
if (!(mapping instanceof ArgumentMatch)) continue candidateLoop;
ValueParameterDescriptor candidateParameter = ((ArgumentMatch) mapping).getValueParameter();
ResolvedValueArgument resolvedArgument = candidateArgumentMap.get(candidateParameter);
JetType expectedType = resolvedArgument instanceof VarargValueArgument
? candidateParameter.getVarargElementType()
: candidateParameter.getType();
valuesToPredicates.putValue(argumentValue, expectedType != null ? new AllSubtypes(expectedType) : AllTypes.INSTANCE$);
}
}
SmartFMap<PseudoValue, TypePredicate> result = SmartFMap.emptyMap();
for (Map.Entry<PseudoValue, Collection<TypePredicate>> entry : valuesToPredicates.entrySet()) {
result = result.plus(entry.getKey(), PseudocodePackage.or(entry.getValue()));
}
return result;
}
@NotNull
private PseudoValue createUnresolvedCallByValues(@NotNull JetElement to, @Nullable JetElement valueElement, List<PseudoValue> arguments) {
Map<PseudoValue, TypePredicate> typeMap = getTypeMapForUnresolvedCall(to, arguments);
if (typeMap == null) {
typeMap = defaultTypeMap(arguments);
}
return builder.magic(to, valueElement, arguments, typeMap, MagicKind.UNRESOLVED_CALL).getOutputValue();
}
@NotNull
private PseudoValue createUnresolvedCallByValues(@NotNull JetElement to, List<PseudoValue> arguments) {
return createUnresolvedCallByValues(to, to, arguments);
}
@NotNull
private PseudoValue createUnresolvedCall(@NotNull JetElement to, List<? extends JetElement> from) {
return createUnresolvedCallByValues(to, elementsToValues(from));
}
@NotNull
private PseudoValue createUnresolvedCall(@NotNull JetElement to, JetElement... from) {
return createUnresolvedCall(to, Arrays.asList(from));
}
@NotNull
private Map<PseudoValue, TypePredicate> defaultTypeMap(List<PseudoValue> values) {
return PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, values);
}
private void mergeValues(@NotNull List<JetExpression> from, @NotNull JetExpression to) {
builder.merge(to, elementsToValues(from));
}
@@ -414,7 +304,7 @@ public class JetControlFlowProcessor {
public void visitThisExpression(@NotNull JetThisExpression expression) {
ResolvedCall<?> resolvedCall = getResolvedCall(expression, trace.getBindingContext());
if (resolvedCall == null) {
createUnresolvedCall(expression);
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL);
return;
}
@@ -440,7 +330,7 @@ public class JetControlFlowProcessor {
generateCall(variableAsFunctionResolvedCall.getVariableCall());
}
else if (!generateCall(expression) && !(expression.getParent() instanceof JetCallExpression)) {
createUnresolvedCall(expression, generateAndGetReceiverIfAny(expression));
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, generateAndGetReceiverIfAny(expression));
}
}
@@ -553,7 +443,7 @@ public class JetControlFlowProcessor {
generateInstructions(right);
}
mark(expression);
createUnresolvedCall(expression, left, right);
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, left, right);
}
private void visitAssignment(
@@ -564,7 +454,7 @@ public class JetControlFlowProcessor {
JetExpression left = JetPsiUtil.deparenthesize(lhs);
if (left == null) {
List<PseudoValue> arguments = Collections.singletonList(rhsDeferredValue.invoke());
builder.magic(parentExpression, parentExpression, arguments, defaultTypeMap(arguments), MagicKind.UNSUPPORTED_ELEMENT);
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNSUPPORTED_ELEMENT);
return;
}
@@ -609,7 +499,7 @@ public class JetControlFlowProcessor {
List<PseudoValue> arguments = KotlinPackage.filterNotNull(
Arrays.asList(getBoundOrUnreachableValue(lhs), rhsDeferredValue.invoke())
);
createUnresolvedCallByValues(parentExpression, arguments);
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNRESOLVED_CALL);
return;
}
@@ -680,7 +570,7 @@ public class JetControlFlowProcessor {
}
private void generateArrayAccessWithoutCall(JetArrayAccessExpression arrayAccessExpression) {
createUnresolvedCall(arrayAccessExpression, generateArrayAccessArguments(arrayAccessExpression));
createNonSyntheticValue(arrayAccessExpression, generateArrayAccessArguments(arrayAccessExpression), MagicKind.UNRESOLVED_CALL);
}
private List<JetExpression> generateArrayAccessArguments(JetArrayAccessExpression arrayAccessExpression) {
@@ -719,7 +609,7 @@ public class JetControlFlowProcessor {
}
else {
generateInstructions(baseExpression);
rhsValue = createUnresolvedCall(expression, baseExpression);
rhsValue = createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression);
}
if (incrementOrDecrement) {
@@ -917,10 +807,7 @@ public class JetControlFlowProcessor {
}
else {
assert condition != null : "Invalid while condition: " + expression.getText();
List<PseudoValue> values = ContainerUtil.createMaybeSingletonList(builder.getBoundValue(condition));
Map<PseudoValue, TypePredicate> typePredicates =
PseudocodePackage.expectedTypeFor(new SingleType(KotlinBuiltIns.getInstance().getBooleanType()), values);
builder.magic(condition, null, values, typePredicates, MagicKind.VALUE_CONSUMER);
createSyntheticValue(condition, MagicKind.VALUE_CONSUMER, condition);
}
builder.enterLoopBody(expression);
@@ -1006,17 +893,10 @@ public class JetControlFlowProcessor {
JetMultiDeclaration multiDeclaration = expression.getMultiParameter();
JetExpression loopRange = expression.getLoopRange();
TypePredicate loopRangeTypePredicate =
getTypePredicateByReceiverValue(trace.get(BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRange));
PseudoValue loopRangeValue = builder.getBoundValue(loopRange);
PseudoValue value = builder.magic(
loopRange != null ? loopRange : expression,
null,
ContainerUtil.createMaybeSingletonList(loopRangeValue),
loopRangeValue != null
? Collections.singletonMap(loopRangeValue, loopRangeTypePredicate)
: Collections.<PseudoValue, TypePredicate>emptyMap(),
ContainerUtil.createMaybeSingletonList(builder.getBoundValue(loopRange)),
MagicKind.LOOP_RANGE_ITERATION
).getOutputValue();
@@ -1030,17 +910,6 @@ public class JetControlFlowProcessor {
}
}
private ReceiverValue getExplicitReceiverValue(ResolvedCall<?> resolvedCall) {
switch(resolvedCall.getExplicitReceiverKind()) {
case DISPATCH_RECEIVER:
return resolvedCall.getDispatchReceiver();
case EXTENSION_RECEIVER:
return resolvedCall.getExtensionReceiver();
default:
return ReceiverValue.NO_RECEIVER;
}
}
@Override
public void visitBreakExpression(@NotNull JetBreakExpression expression) {
JetElement loop = getCorrespondingLoop(expression);
@@ -1250,7 +1119,7 @@ public class JetControlFlowProcessor {
inputExpressions.add(generateAndGetReceiverIfAny(expression));
mark(expression);
createUnresolvedCall(expression, inputExpressions);
createNonSyntheticValue(expression, inputExpressions, MagicKind.UNRESOLVED_CALL);
}
}
@@ -1292,33 +1161,9 @@ public class JetControlFlowProcessor {
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property);
if (!(descriptor instanceof PropertyDescriptor)) return;
PseudoValue delegateValue = builder.getBoundValue(delegate);
if (delegateValue == null) return;
if (builder.getBoundValue(delegate) == null) return;
List<TypePredicate> typePredicates = KotlinPackage.map(
((PropertyDescriptor) descriptor).getAccessors(),
new Function1<PropertyAccessorDescriptor, TypePredicate>() {
@Override
public TypePredicate invoke(PropertyAccessorDescriptor descriptor) {
return getTypePredicateByReceiverValue(trace.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, descriptor));
}
}
);
Map<PseudoValue, TypePredicate> valuesToTypePredicates = SmartFMap
.<PseudoValue, TypePredicate>emptyMap()
.plus(delegateValue, PseudocodePackage.and(KotlinPackage.filterNotNull(typePredicates)));
builder.magic(property, null, Collections.singletonList(delegateValue), valuesToTypePredicates, MagicKind.VALUE_CONSUMER);
}
private TypePredicate getTypePredicateByReceiverValue(@Nullable ResolvedCall<?> resolvedCall) {
if (resolvedCall == null) return AllTypes.INSTANCE$;
ReceiverValue receiverValue = getExplicitReceiverValue(resolvedCall);
if (receiverValue.exists()) {
return PseudocodePackage.getReceiverTypePredicate(resolvedCall, receiverValue);
}
return AllTypes.INSTANCE$;
createSyntheticValue(property, MagicKind.VALUE_CONSUMER, delegate);
}
@Override
@@ -1344,8 +1189,7 @@ public class JetControlFlowProcessor {
).getOutputValue();
}
else {
List<PseudoValue> arguments = ContainerUtil.createMaybeSingletonList(getBoundOrUnreachableValue(initializer));
writtenValue = createUnresolvedCallByValues(entry, null, arguments);
writtenValue = createSyntheticValue(entry, MagicKind.UNRESOLVED_CALL, initializer);
}
if (generateWriteForEntries) {
@@ -1591,19 +1435,15 @@ public class JetControlFlowProcessor {
for (JetExpression argument : arguments) {
generateInstructions(argument);
}
createUnresolvedCall(call, arguments);
createNonSyntheticValue(call, arguments, MagicKind.UNRESOLVED_CALL);
}
}
@Override
public void visitDelegationByExpressionSpecifier(@NotNull JetDelegatorByExpressionSpecifier specifier) {
generateInstructions(specifier.getDelegateExpression());
List<PseudoValue> arguments = ContainerUtil.createMaybeSingletonList(builder.getBoundValue(specifier.getDelegateExpression()));
JetType jetType = trace.get(BindingContext.TYPE, specifier.getTypeReference());
TypePredicate expectedTypePredicate = jetType != null ? PseudocodePackage.getSubtypesPredicate(jetType) : AllTypes.INSTANCE$;
builder.magic(specifier, null, arguments, PseudocodePackage.expectedTypeFor(expectedTypePredicate, arguments),
MagicKind.VALUE_CONSUMER);
JetExpression delegateExpression = specifier.getDelegateExpression();
generateInstructions(delegateExpression);
createSyntheticValue(specifier, MagicKind.VALUE_CONSUMER, delegateExpression);
}
@Override
@@ -432,9 +432,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
@NotNull
@Override
public InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List<PseudoValue> inputValues) {
if (inputValues.isEmpty()) return read(expression);
Map<PseudoValue, TypePredicate> predicate = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues);
return magic(expression, expression, inputValues, predicate, MagicKind.STRING_TEMPLATE);
return inputValues.isEmpty() ? read(expression) : magic(expression, expression, inputValues, MagicKind.STRING_TEMPLATE);
}
@NotNull
@@ -443,11 +441,10 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
@NotNull JetElement instructionElement,
@Nullable JetElement valueElement,
@NotNull List<PseudoValue> inputValues,
@NotNull Map<PseudoValue, TypePredicate> expectedTypes,
@NotNull MagicKind kind
) {
MagicInstruction instruction = new MagicInstruction(
instructionElement, valueElement, getCurrentScope(), inputValues, expectedTypes, kind, valueFactory
instructionElement, valueElement, getCurrentScope(), inputValues, kind, valueFactory
);
add(instruction);
return instruction;
@@ -499,21 +496,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
@NotNull PredefinedOperation operation,
@NotNull List<PseudoValue> inputValues
) {
Map<PseudoValue, TypePredicate> expectedTypes;
switch(operation) {
case AND:
case OR:
SingleType onlyBoolean = new SingleType(KotlinBuiltIns.getInstance().getBooleanType());
expectedTypes = PseudocodePackage.expectedTypeFor(onlyBoolean, inputValues);
break;
case NOT_NULL_ASSERTION:
expectedTypes = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues);
break;
default:
throw new IllegalArgumentException("Invalid operation: " + operation);
}
return magic(expression, expression, inputValues, expectedTypes, getMagicKind(operation));
return magic(expression, expression, inputValues, getMagicKind(operation));
}
@NotNull
@@ -95,7 +95,6 @@ public class MagicInstruction(
element: JetElement,
lexicalScope: LexicalScope,
inputValues: List<PseudoValue>,
val expectedTypes: Map<PseudoValue, TypePredicate>,
val kind: MagicKind
) : OperationInstruction(element, lexicalScope, inputValues) {
public constructor (
@@ -103,10 +102,9 @@ public class MagicInstruction(
valueElement: JetElement?,
lexicalScope: LexicalScope,
inputValues: List<PseudoValue>,
expectedTypes: Map<PseudoValue, TypePredicate>,
kind: MagicKind,
factory: PseudoValueFactory
): this(element, lexicalScope, inputValues, expectedTypes, kind) {
): this(element, lexicalScope, inputValues, kind) {
setResult(factory, valueElement)
}
@@ -120,7 +118,7 @@ public class MagicInstruction(
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitMagic(this)
override fun createCopy() =
MagicInstruction(element, lexicalScope, inputValues, expectedTypes, kind).setResult(resultValue)
MagicInstruction(element, lexicalScope, inputValues, kind).setResult(resultValue)
override fun toString() = renderInstruction("magic[$kind]", render(element))
}
@@ -16,8 +16,12 @@
package org.jetbrains.kotlin.cfg.pseudocode
import com.google.common.collect.Sets
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartFMap
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction
@@ -26,23 +30,28 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInst
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument
import org.jetbrains.kotlin.resolve.calls.ValueArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.ArrayList
import java.util.LinkedHashSet
import java.util.*
fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: ReceiverValue): TypePredicate? {
val callableDescriptor = resolvedCall.getResultingDescriptor()
if (callableDescriptor == null) return null
val callableDescriptor = resolvedCall.getResultingDescriptor() ?: return null
when (receiverValue) {
resolvedCall.getExtensionReceiver() -> {
@@ -61,13 +70,75 @@ fun getReceiverTypePredicate(resolvedCall: ResolvedCall<*>, receiverValue: Recei
}
public fun getExpectedTypePredicate(value: PseudoValue, bindingContext: BindingContext): TypePredicate {
val pseudocode = value.createdAt?.owner
if (pseudocode == null) return AllTypes
val pseudocode = value.createdAt?.owner ?: return AllTypes
val builtIns = KotlinBuiltIns.getInstance()
val typePredicates = LinkedHashSet<TypePredicate?>()
fun addSubtypesOf(jetType: JetType?) = typePredicates.add(jetType?.getSubtypesPredicate())
fun addByExplicitReceiver(resolvedCall: ResolvedCall<*>?) {
val receiverValue = (resolvedCall ?: return).getExplicitReceiverValue()
if (receiverValue.exists()) typePredicates.add(getReceiverTypePredicate(resolvedCall, receiverValue))
}
fun getTypePredicateForUnresolvedCallArgument(to: JetElement, inputValueIndex: Int): TypePredicate? {
if (inputValueIndex < 0) return null
val call = to.getCall(bindingContext) ?: return null
val callee = call.getCalleeExpression() ?: return null
val candidates = callee.getReferenceTargets(bindingContext)
.filterIsInstance<FunctionDescriptor>()
.sortBy { DescriptorRenderer.DEBUG_TEXT.render(it) }
if (candidates.isEmpty()) return null
val explicitReceiver = call.getExplicitReceiver()
val argValueOffset = if (explicitReceiver.exists()) 1 else 0
val predicates = ArrayList<TypePredicate>()
for (candidate in candidates) {
val resolutionCandidate = ResolutionCandidate.create(
call,
candidate,
call.getDispatchReceiver(),
explicitReceiver,
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER,
null
)
val candidateCall = ResolvedCallImpl.create(
resolutionCandidate,
DelegatingBindingTrace(bindingContext, "Compute type predicates for unresolved call arguments"),
TracingStrategy.EMPTY,
DataFlowInfoForArgumentsImpl(call)
)
val status = ValueArgumentsToParametersMapper.mapValueArgumentsToParameters(call,
TracingStrategy.EMPTY,
candidateCall,
LinkedHashSet())
if (!status.isSuccess()) continue
val candidateArgumentMap = candidateCall.getValueArguments()
val callArguments = call.getValueArguments()
val i = inputValueIndex - argValueOffset
if (i < 0 || i >= callArguments.size()) continue
val mapping = candidateCall.getArgumentMapping(callArguments.get(i))
if (mapping !is ArgumentMatch) continue
val candidateParameter = mapping.valueParameter
val resolvedArgument = candidateArgumentMap.get(candidateParameter)
val expectedType = if (resolvedArgument is VarargValueArgument)
candidateParameter.getVarargElementType()
else
candidateParameter.getType()
predicates.add(if (expectedType != null) AllSubtypes(expectedType) else AllTypes)
}
return or(predicates)
}
fun addTypePredicates(value: PseudoValue) {
pseudocode.getUsages(value).forEach {
when (it) {
@@ -75,16 +146,16 @@ public fun getExpectedTypePredicate(value: PseudoValue, bindingContext: BindingC
val returnElement = it.element
val functionDescriptor = when(returnElement) {
is JetReturnExpression -> returnElement.getTargetFunctionDescriptor(bindingContext)
else -> bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, pseudocode.getCorrespondingElement()]
else -> bindingContext[DECLARATION_TO_DESCRIPTOR, pseudocode.getCorrespondingElement()]
}
addSubtypesOf((functionDescriptor as? CallableDescriptor)?.getReturnType())
}
is ConditionalJumpInstruction ->
addSubtypesOf(KotlinBuiltIns.getInstance().getBooleanType())
addSubtypesOf(builtIns.getBooleanType())
is ThrowExceptionInstruction ->
addSubtypesOf(KotlinBuiltIns.getInstance().getThrowable().getDefaultType())
addSubtypesOf(builtIns.getThrowable().getDefaultType())
is MergeInstruction ->
addTypePredicates(it.outputValue)
@@ -126,8 +197,36 @@ public fun getExpectedTypePredicate(value: PseudoValue, bindingContext: BindingC
}
}
is MagicInstruction ->
typePredicates.add(it.expectedTypes[value])
is MagicInstruction -> @suppress("NON_EXHAUSTIVE_WHEN") when (it.kind) {
AND, OR ->
addSubtypesOf(builtIns.getBooleanType())
LOOP_RANGE_ITERATION ->
addByExplicitReceiver(bindingContext[LOOP_RANGE_ITERATOR_RESOLVED_CALL, value.element as? JetExpression])
VALUE_CONSUMER -> {
val element = it.element
when {
element.getStrictParentOfType<JetWhileExpression>()?.getCondition() == element ->
addSubtypesOf(builtIns.getBooleanType())
element is JetProperty -> {
val propertyDescriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, element] as? PropertyDescriptor
propertyDescriptor?.getAccessors()?.map {
addByExplicitReceiver(bindingContext[DELEGATED_PROPERTY_RESOLVED_CALL, it])
}
}
element is JetDelegatorByExpressionSpecifier ->
addSubtypesOf(bindingContext[TYPE, element.getTypeReference()])
}
}
UNRESOLVED_CALL -> {
val typePredicate = getTypePredicateForUnresolvedCallArgument(it.element, it.inputValues.indexOf(value))
typePredicates.add(typePredicate)
}
}
}
}
}
@@ -26,8 +26,10 @@ import org.jetbrains.kotlin.psi.JetThisExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.descriptorUtil.getOwnerForEffectiveDispatchReceiverParameter
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
// it returns true if call has no dispatch receiver (e.g. resulting descriptor is top-level function or local variable)
@@ -66,3 +68,10 @@ private fun ResolvedCall<*>.hasThisOrNoDispatchReceiver(
return dispatchReceiverDescriptor == getResultingDescriptor().getOwnerForEffectiveDispatchReceiverParameter()
}
public fun ResolvedCall<*>.getExplicitReceiverValue(): ReceiverValue {
return when (getExplicitReceiverKind()) {
ExplicitReceiverKind.DISPATCH_RECEIVER -> getDispatchReceiver()
ExplicitReceiverKind.EXTENSION_RECEIVER, ExplicitReceiverKind.BOTH_RECEIVERS -> getExtensionReceiver()
else -> ReceiverValue.NO_RECEIVER
}
}