Pseudocode: Add receiver values to call instructions. Refactor InstructionWithReceiver

This commit is contained in:
Alexey Sedunov
2014-06-03 18:55:11 +04:00
parent 650fac0081
commit 9ddfc32cdc
10 changed files with 179 additions and 77 deletions
@@ -21,12 +21,15 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import java.util.List;
import java.util.Map;
public interface JetControlFlowBuilder {
// Subroutines
@@ -123,7 +126,12 @@ public interface JetControlFlowBuilder {
);
@Nullable
PseudoValue call(@NotNull JetExpression expression, @NotNull ResolvedCall<?> resolvedCall, @NotNull List<PseudoValue> inputValues);
PseudoValue call(
@NotNull JetExpression expression,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull Map<PseudoValue, ReceiverValue> receiverValues,
@NotNull Map<PseudoValue, ValueParameterDescriptor> arguments
);
enum PredefinedOperation {
AND,
@@ -21,12 +21,15 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue;
import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import java.util.List;
import java.util.Map;
public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuilder {
@@ -90,8 +93,13 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil
@Nullable
@Override
public PseudoValue call(@NotNull JetExpression expression, @NotNull ResolvedCall<?> resolvedCall, @NotNull List<PseudoValue> inputValues) {
return getDelegateBuilder().call(expression, resolvedCall, inputValues);
public PseudoValue call(
@NotNull JetExpression expression,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull Map<PseudoValue, ReceiverValue> receiverValues,
@NotNull Map<PseudoValue, ValueParameterDescriptor> arguments
) {
return getDelegateBuilder().call(expression, resolvedCall, receiverValues, arguments);
}
@NotNull
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
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 kotlin.Function0;
import kotlin.Function1;
import kotlin.KotlinPackage;
@@ -36,9 +38,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.CompileTimeConstantUtils;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
@@ -438,25 +438,64 @@ public class JetControlFlowProcessor {
mark(lhs);
}
generateArrayAccessArguments(lhs);
generateInstructions(lhs.getArrayExpression(), NOT_IN_CONDITION);
List<JetExpression> inputExpressions = new ArrayList<JetExpression>();
inputExpressions.add(lhs.getArrayExpression());
inputExpressions.addAll(lhs.getIndexExpressions());
Map<PseudoValue, ReceiverValue> receiverValues = getReceiverValues(setResolvedCall, false);
SmartFMap<PseudoValue, ValueParameterDescriptor> argumentValues =
getArraySetterArguments(rhsDeferredValue, setResolvedCall);
List<PseudoValue> inputValues = elementsToValues(inputExpressions);
PseudoValue rhsValue = rhsDeferredValue.invoke();
if (rhsValue != null) {
inputValues.add(rhsValue);
builder.call(parentExpression, setResolvedCall, receiverValues, argumentValues);
}
/* We assume that assignment right-hand side corresponds to the last argument of the call
* So receiver instructions/pseudo-values are generated for all arguments except the last one which is replaced
* by pre-generated pseudo-value
* For example, assignment a[1, 2] += 3 means a.set(1, 2, a.get(1) + 3), so in order to generate "set" call
* we first generate instructions for 1 and 2 whereas 3 is replaced by pseudo-value corresponding to "a.get(1) + 3"
*/
private SmartFMap<PseudoValue, ValueParameterDescriptor> getArraySetterArguments(
Function0<PseudoValue> rhsDeferredValue,
final ResolvedCall<FunctionDescriptor> setResolvedCall
) {
List<ValueArgument> valueArguments = KotlinPackage.flatMapTo(
setResolvedCall.getResultingDescriptor().getValueParameters(),
new ArrayList<ValueArgument>(),
new Function1<ValueParameterDescriptor, Iterable<? extends ValueArgument>>() {
@Override
public Iterable<? extends ValueArgument> invoke(ValueParameterDescriptor descriptor) {
ResolvedValueArgument resolvedValueArgument = setResolvedCall.getValueArguments().get(descriptor);
return resolvedValueArgument != null
? resolvedValueArgument.getArguments()
: Collections.<ValueArgument>emptyList();
}
}
);
ValueArgument rhsArgument = KotlinPackage.lastOrNull(valueArguments);
SmartFMap<PseudoValue, ValueParameterDescriptor> argumentValues = SmartFMap.emptyMap();
for (ValueArgument valueArgument : valueArguments) {
ArgumentMapping argumentMapping = setResolvedCall.getArgumentMapping(valueArgument);
if (argumentMapping.isError() || (!(argumentMapping instanceof ArgumentMatch))) continue;
ValueParameterDescriptor parameterDescriptor = ((ArgumentMatch) argumentMapping).getValueParameter();
if (valueArgument != rhsArgument) {
argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues);
}
else {
PseudoValue rhsValue = rhsDeferredValue.invoke();
if (rhsValue != null) {
argumentValues = argumentValues.plus(rhsValue, parameterDescriptor);
}
}
}
builder.call(parentExpression, setResolvedCall, inputValues);
return argumentValues;
}
private void recordWrite(JetExpression left, PseudoValue rightValue, PseudoValue receiverValue, JetExpression parentExpression) {
VariableDescriptor descriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), left, false);
if (descriptor != null) {
builder.write(parentExpression, left, rightValue != null ? rightValue : createSyntheticValue(parentExpression), receiverValue);
builder.write(parentExpression, left, rightValue != null ? rightValue : createSyntheticValue(parentExpression),
receiverValue);
}
}
@@ -1027,13 +1066,18 @@ public class JetControlFlowProcessor {
private void visitMultiDeclaration(@NotNull JetMultiDeclaration declaration, boolean generateWriteForEntries) {
JetExpression initializer = declaration.getInitializer();
generateInstructions(initializer, NOT_IN_CONDITION);
List<PseudoValue> inputValues = elementsToValues(Collections.singletonList(initializer));
for (JetMultiDeclarationEntry entry : declaration.getEntries()) {
builder.declareVariable(entry);
ResolvedCall<FunctionDescriptor> resolvedCall = trace.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
PseudoValue writtenValue = resolvedCall != null
? builder.call(entry, resolvedCall, inputValues)
: builder.magic(entry, null, inputValues, true);
? builder.call(
entry,
resolvedCall,
getReceiverValues(resolvedCall, false),
Collections.<PseudoValue, ValueParameterDescriptor>emptyMap()
)
: builder.magic(entry, null,
ContainerUtil.createMaybeSingletonList(builder.getBoundValue(initializer)), true);
if (generateWriteForEntries) {
builder.write(entry, entry, writtenValue != null ? writtenValue : createSyntheticValue(entry), null);
}
@@ -1286,41 +1330,54 @@ public class JetControlFlowProcessor {
return;
}
List<PseudoValue> inputValues = new ArrayList<PseudoValue>();
CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
generateReceiver(resolvedCall.getThisObject(), inputValues);
generateReceiver(resolvedCall.getReceiverArgument(), inputValues);
Map<PseudoValue, ReceiverValue> receivers = getReceiverValues(resolvedCall, true);
SmartFMap<PseudoValue, ValueParameterDescriptor> parameterValues = SmartFMap.emptyMap();
for (ValueParameterDescriptor parameterDescriptor : resultingDescriptor.getValueParameters()) {
ResolvedValueArgument argument = resolvedCall.getValueArguments().get(parameterDescriptor);
if (argument == null) continue;
generateValueArgument(argument, inputValues);
parameterValues = generateValueArgument(argument, parameterDescriptor, parameterValues);
}
if (resultingDescriptor instanceof VariableDescriptor) {
assert inputValues.size() <= 1 : "Wrong input values for variable-based call (must be at most 1): " + resolvedCall.getCall().getCallElement();
builder.readVariable(calleeExpression, (VariableDescriptor) resultingDescriptor, KotlinPackage.firstOrNull(inputValues));
assert parameterValues.isEmpty() && receivers.size() <= 1
: "Wrong input values for variable-based call (must be at most 1 receiver): " + resolvedCall.getCall().getCallElement().getText();
builder.readVariable(calleeExpression, (VariableDescriptor) resultingDescriptor, KotlinPackage.firstOrNull(receivers.keySet()));
}
else {
builder.call(calleeExpression, resolvedCall, inputValues);
builder.call(calleeExpression, resolvedCall, receivers, parameterValues);
}
}
private void generateReceiver(ReceiverValue receiver, List<PseudoValue> values) {
if (!receiver.exists()) return;
@NotNull
private Map<PseudoValue, ReceiverValue> getReceiverValues(ResolvedCall<?> resolvedCall, boolean generateInstructions) {
SmartFMap<PseudoValue, ReceiverValue> receiverValues = SmartFMap.emptyMap();
receiverValues = getReceiverValues(resolvedCall.getThisObject(), generateInstructions, receiverValues);
receiverValues = getReceiverValues(resolvedCall.getReceiverArgument(), generateInstructions, receiverValues);
return receiverValues;
}
@NotNull
private SmartFMap<PseudoValue, ReceiverValue> getReceiverValues(
ReceiverValue receiver,
boolean generateInstructions,
SmartFMap<PseudoValue, ReceiverValue> receiverValues
) {
if (!receiver.exists()) return receiverValues;
if (receiver instanceof ThisReceiver) {
// TODO: Receiver is passed implicitly: no expression to tie the read to
}
else if (receiver instanceof ExpressionReceiver) {
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
generateInstructions(expression, NOT_IN_CONDITION);
if (generateInstructions) {
generateInstructions(expression, NOT_IN_CONDITION);
}
PseudoValue receiverValue = builder.getBoundValue(expression);
if (receiverValue != null) {
values.add(receiverValue);
PseudoValue receiverPseudoValue = builder.getBoundValue(expression);
if (receiverPseudoValue != null) {
receiverValues = receiverValues.plus(receiverPseudoValue, receiver);
}
}
else if (receiver instanceof TransientReceiver) {
@@ -1329,20 +1386,38 @@ public class JetControlFlowProcessor {
else {
throw new IllegalArgumentException("Unknown receiver kind: " + receiver);
}
return receiverValues;
}
private void generateValueArgument(ResolvedValueArgument argument, List<PseudoValue> values) {
@NotNull
private SmartFMap<PseudoValue, ValueParameterDescriptor> generateValueArgument(
ResolvedValueArgument argument,
ValueParameterDescriptor parameterDescriptor,
SmartFMap<PseudoValue, ValueParameterDescriptor> parameterValues
) {
for (ValueArgument valueArgument : argument.getArguments()) {
JetExpression expression = valueArgument.getArgumentExpression();
if (expression != null) {
generateInstructions(expression, NOT_IN_CONDITION);
parameterValues = generateValueArgument(valueArgument, parameterDescriptor, parameterValues);
}
PseudoValue argValue = builder.getBoundValue(expression);
if (argValue != null) {
values.add(argValue);
}
return parameterValues;
}
@NotNull
private SmartFMap<PseudoValue, ValueParameterDescriptor> generateValueArgument(
ValueArgument valueArgument,
ValueParameterDescriptor parameterDescriptor,
SmartFMap<PseudoValue, ValueParameterDescriptor> parameterValues) {
JetExpression expression = valueArgument.getArgumentExpression();
if (expression != null) {
generateInstructions(expression, NOT_IN_CONDITION);
PseudoValue argValue = builder.getBoundValue(expression);
if (argValue != null) {
parameterValues = parameterValues.plus(argValue, parameterDescriptor);
}
}
return parameterValues;
}
}
}
@@ -26,10 +26,12 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.*;
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
@@ -456,14 +458,16 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
public PseudoValue call(
@NotNull JetExpression expression,
@NotNull ResolvedCall<?> resolvedCall,
@NotNull List<PseudoValue> inputValues
@NotNull Map<PseudoValue, ReceiverValue> receiverValues,
@NotNull Map<PseudoValue, ValueParameterDescriptor> arguments
) {
JetType returnType = resolvedCall.getResultingDescriptor().getReturnType();
OperationInstruction instruction = CallInstruction.object$.create(
expression,
getCurrentScope(),
resolvedCall,
inputValues,
receiverValues,
arguments,
returnType != null && KotlinBuiltIns.getInstance().isNothing(returnType) ? null : valueFactory
);
add(instruction);
@@ -22,12 +22,8 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.*
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*
public open class InstructionVisitor() {
public open fun visitInstructionWithReceiver(instruction: InstructionWithReceiver) {
visitInstructionWithNext(instruction)
}
public open fun visitReadValue(instruction: ReadValueInstruction) {
visitInstructionWithReceiver(instruction)
visitInstructionWithNext(instruction)
}
public open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction) {
@@ -90,7 +86,7 @@ public open class InstructionVisitor() {
}
public open fun visitWriteValue(instruction: WriteValueInstruction) {
visitInstructionWithReceiver(instruction)
visitInstructionWithNext(instruction)
}
public open fun visitLoadUnitValue(instruction: LoadUnitValueInstruction) {
@@ -24,12 +24,8 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.*
public abstract class InstructionVisitorWithResult<R>() {
public abstract fun visitInstruction(instruction: Instruction): R
public open fun visitInstructionWithReceiver(instruction: InstructionWithReceiver): R {
return visitInstructionWithNext(instruction)
}
public open fun visitReadValue(instruction: ReadValueInstruction): R {
return visitInstructionWithReceiver(instruction)
return visitInstructionWithNext(instruction)
}
public open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R {
@@ -89,7 +85,7 @@ public abstract class InstructionVisitorWithResult<R>() {
}
public open fun visitWriteValue(instruction: WriteValueInstruction): R {
return visitInstructionWithReceiver(instruction)
return visitInstructionWithNext(instruction)
}
public open fun visitLoadUnitValue(instruction: LoadUnitValueInstruction): R {
@@ -16,16 +16,9 @@
package org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionWithNext
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope
import org.jetbrains.jet.lang.cfg.pseudocode.PseudoValue
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.Instruction
public abstract class InstructionWithReceiver(
element: JetElement,
lexicalScope: LexicalScope,
protected val receiverValue: PseudoValue?
) : InstructionWithNext(element, lexicalScope) {
override val inputValues: List<PseudoValue> = ContainerUtil.createMaybeSingletonList(receiverValue)
public trait InstructionWithReceivers: Instruction {
public val receiverValues: List<PseudoValue>
}
@@ -23,17 +23,26 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionWithNext
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
public class ReadValueInstruction private (
element: JetElement,
lexicalScope: LexicalScope,
receiverValue: PseudoValue?,
public val receiverValue: PseudoValue?,
private var _outputValue: PseudoValue?
) : InstructionWithReceiver(element, lexicalScope, receiverValue), InstructionWithValue {
) : InstructionWithNext(element, lexicalScope), InstructionWithReceivers, InstructionWithValue {
private fun newResultValue(factory: PseudoValueFactory) {
_outputValue = factory.newValue(element, this)
}
override val receiverValues: List<PseudoValue>
get() = ContainerUtil.createMaybeSingletonList(receiverValue)
override val inputValues: List<PseudoValue>
get() = receiverValues
override val outputValue: PseudoValue
get() = _outputValue!!
@@ -25,14 +25,19 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor
import java.util.Arrays
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionWithNext
import com.intellij.util.containers.ContainerUtil
public class WriteValueInstruction(
assignment: JetElement,
public val lValue: JetElement,
public val rValue: PseudoValue,
receiverValue: PseudoValue?,
public val receiverValue: PseudoValue?,
lexicalScope: LexicalScope
) : InstructionWithReceiver(assignment, lexicalScope, receiverValue) {
) : InstructionWithNext(assignment, lexicalScope), InstructionWithReceivers {
override val receiverValues: List<PseudoValue>
get() = ContainerUtil.createMaybeSingletonList(receiverValue)
override val inputValues: List<PseudoValue>
get() = receiverValue?.let{ receiverValue -> Arrays.asList(receiverValue, rValue) } ?: Collections.singletonList(rValue)
@@ -24,6 +24,8 @@ import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionWithNext
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.InstructionVisitorWithResult
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor
public abstract class OperationInstruction protected(
element: JetElement,
@@ -54,8 +56,12 @@ public class CallInstruction private(
element: JetElement,
lexicalScope: LexicalScope,
val resolvedCall: ResolvedCall<*>,
inputValues: List<PseudoValue>
) : OperationInstruction(element, lexicalScope, inputValues) {
public val receiverValueMap: Map<PseudoValue, ReceiverValue>,
public val arguments: Map<PseudoValue, ValueParameterDescriptor>
) : OperationInstruction(element, lexicalScope, receiverValueMap.keySet() + arguments.keySet()), InstructionWithReceivers {
override val receiverValues: List<PseudoValue>
get() = receiverValueMap.keySet().toList()
override fun accept(visitor: InstructionVisitor) {
visitor.visitCallInstruction(this)
}
@@ -65,7 +71,7 @@ public class CallInstruction private(
}
override fun createCopy() =
CallInstruction(element, lexicalScope, resolvedCall, inputValues).setResult(resultValue)
CallInstruction(element, lexicalScope, resolvedCall, receiverValueMap, arguments).setResult(resultValue)
override fun toString() =
renderInstruction("call", "${render(element)}, ${resolvedCall.getResultingDescriptor()!!.getName()}")
@@ -75,9 +81,11 @@ public class CallInstruction private(
element: JetElement,
lexicalScope: LexicalScope,
resolvedCall: ResolvedCall<*>,
inputValues: List<PseudoValue>,
receiverValues: Map<PseudoValue, ReceiverValue>,
arguments: Map<PseudoValue, ValueParameterDescriptor>,
factory: PseudoValueFactory?
): CallInstruction = CallInstruction(element, lexicalScope, resolvedCall, inputValues).setResult(factory) as CallInstruction
): CallInstruction =
CallInstruction(element, lexicalScope, resolvedCall, receiverValues, arguments).setResult(factory) as CallInstruction
}
}