added PseudocodeData
to store information about variables for each instruction: initialization and use statuses
This commit is contained in:
@@ -19,12 +19,12 @@ package org.jetbrains.jet.lang.cfg;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.intellij.openapi.util.Pair;
|
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.tree.IElementType;
|
import com.intellij.psi.tree.IElementType;
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.jet.lang.cfg.data.*;
|
||||||
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
||||||
import org.jetbrains.jet.lang.descriptors.*;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||||
@@ -50,12 +50,14 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
|||||||
public class JetFlowInformationProvider {
|
public class JetFlowInformationProvider {
|
||||||
|
|
||||||
private final Map<JetElement, Pseudocode> pseudocodeMap;
|
private final Map<JetElement, Pseudocode> pseudocodeMap;
|
||||||
|
private final Map<JetElement, PseudocodeData> pseudocodeDataMap;
|
||||||
private BindingTrace trace;
|
private BindingTrace trace;
|
||||||
|
|
||||||
public JetFlowInformationProvider(@NotNull JetDeclaration declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) {
|
public JetFlowInformationProvider(@NotNull JetDeclaration declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) {
|
||||||
this.trace = trace;
|
this.trace = trace;
|
||||||
final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration);
|
final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration);
|
||||||
pseudocodeMap = new HashMap<JetElement, Pseudocode>();
|
pseudocodeMap = new LinkedHashMap<JetElement, Pseudocode>();
|
||||||
|
pseudocodeDataMap = new HashMap<JetElement, PseudocodeData>();
|
||||||
final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
|
final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
|
||||||
final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
|
final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
|
||||||
JetPseudocodeTrace wrappedTrace = new JetPseudocodeTrace() {
|
JetPseudocodeTrace wrappedTrace = new JetPseudocodeTrace() {
|
||||||
@@ -81,7 +83,9 @@ public class JetFlowInformationProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
pseudocodeTrace.close();
|
pseudocodeTrace.close();
|
||||||
for (Pseudocode pseudocode : pseudocodeMap.values()) {
|
List<Pseudocode> values = Lists.newArrayList(pseudocodeMap.values());
|
||||||
|
Collections.reverse(values);
|
||||||
|
for (Pseudocode pseudocode : values) {
|
||||||
pseudocode.postProcess();
|
pseudocode.postProcess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +95,17 @@ public class JetFlowInformationProvider {
|
|||||||
wrappedTrace.close();
|
wrappedTrace.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PseudocodeData getPseudocodeData(@NotNull JetElement element) {
|
||||||
|
PseudocodeData pseudocodeData = pseudocodeDataMap.get(element);
|
||||||
|
if (pseudocodeData == null) {
|
||||||
|
Pseudocode pseudocode = pseudocodeMap.get(element);
|
||||||
|
assert pseudocode != null;
|
||||||
|
pseudocodeData = new PseudocodeData(pseudocode, trace);
|
||||||
|
pseudocodeDataMap.put(element, pseudocodeData);
|
||||||
|
}
|
||||||
|
return pseudocodeData;
|
||||||
|
}
|
||||||
|
|
||||||
private void collectReturnExpressions(@NotNull JetElement subroutine, @NotNull final Collection<JetElement> returnedExpressions) {
|
private void collectReturnExpressions(@NotNull JetElement subroutine, @NotNull final Collection<JetElement> returnedExpressions) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
@@ -214,63 +229,49 @@ public class JetFlowInformationProvider {
|
|||||||
// Uninitialized variables analysis
|
// Uninitialized variables analysis
|
||||||
|
|
||||||
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean processLocalDeclaration) {
|
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean processLocalDeclaration) {
|
||||||
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
|
||||||
assert pseudocode != null;
|
|
||||||
|
|
||||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false, true);
|
|
||||||
|
|
||||||
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
|
||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
|
||||||
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
|
|
||||||
|
|
||||||
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction,
|
|
||||||
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
|
||||||
@Override
|
|
||||||
public Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> execute(
|
|
||||||
@NotNull Instruction instruction,
|
|
||||||
@NotNull Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
|
||||||
|
|
||||||
Map<VariableDescriptor, VariableInitializers> enterInstructionData = mergeIncomingEdgesData(incomingEdgesData);
|
|
||||||
Map<VariableDescriptor, VariableInitializers> exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData);
|
|
||||||
return Pair.create(enterInstructionData, exitInstructionData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
||||||
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
||||||
final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
|
final boolean processClassOrObject = subroutine instanceof JetClassOrObject;
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
|
||||||
|
final PseudocodeData pseudocodeData = getPseudocodeData(subroutine);
|
||||||
|
pseudocodeData.traverseInstructionsGraph(true, true, new PseudocodeData.TraverseInstructionGraphStrategy() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull Instruction instruction, @Nullable Map<VariableDescriptor, VariableInitializers> enterData, @Nullable Map<VariableDescriptor, VariableInitializers> exitData) {
|
public void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData) {
|
||||||
assert enterData != null && exitData != null;
|
//todo move to util
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
VariableDescriptor variableDescriptor = pseudocodeData.extractVariableDescriptorIfAny(instruction, true);
|
||||||
if (variableDescriptor == null) return;
|
if (variableDescriptor == null) return;
|
||||||
|
if (!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction)) return;
|
||||||
|
InstructionData.EdgesData<VariableInitializers> variableInitializers = instructionData.getInitializersMap().get(
|
||||||
|
variableDescriptor);
|
||||||
|
if (variableInitializers == null) return;
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
||||||
boolean error = checkBackingField(variableDescriptor, element);
|
boolean error = checkBackingField(variableDescriptor, element);
|
||||||
if (!error && declaredVariables.contains(variableDescriptor)) {
|
if (!error && declarationData.declaredVariables.contains(variableDescriptor)) {
|
||||||
checkIsInitialized(variableDescriptor, element, exitData.get(variableDescriptor), varWithUninitializedErrorGenerated);
|
checkIsInitialized(variableDescriptor, element, variableInitializers.getOut(), varWithUninitializedErrorGenerated);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
boolean error = checkBackingField(variableDescriptor, element);
|
||||||
boolean error = checkBackingField(variableDescriptor, element);
|
if (!(element instanceof JetExpression)) return;
|
||||||
if (!(element instanceof JetExpression)) return;
|
if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration
|
||||||
if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration
|
error = checkValReassignment(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), varWithValReassignErrorGenerated);
|
||||||
error = checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated);
|
}
|
||||||
}
|
if (!error && processClassOrObject) {
|
||||||
if (!error && processClassOrObject) {
|
error = checkAssignmentBeforeDeclaration(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), variableInitializers.getOut());
|
||||||
error = checkAssignmentBeforeDeclaration(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
}
|
||||||
}
|
if (!error && processClassOrObject) {
|
||||||
if (!error && processClassOrObject) {
|
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), variableInitializers.getOut());
|
||||||
checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
recordInitializedVariables(declaredVariables, traverser.getResultInfo());
|
Pseudocode pseudocode = pseudocodeData.getPseudocode();
|
||||||
analyzeLocalDeclarations(pseudocode, processLocalDeclaration);
|
recordInitializedVariables(pseudocodeData.getDeclarationData(pseudocode), pseudocodeData.getResultInfo(pseudocode));
|
||||||
|
for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) {
|
||||||
|
recordInitializedVariables(pseudocodeData.getDeclarationData(localPseudocode), pseudocodeData.getResultInfo(localPseudocode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor,
|
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor,
|
||||||
@@ -436,12 +437,12 @@ public class JetFlowInformationProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordInitializedVariables(Collection<VariableDescriptor> declaredVariables, Map<VariableDescriptor, VariableInitializers> resultInfo) {
|
private void recordInitializedVariables(DeclarationData declarationData, InstructionData instructionData) {
|
||||||
for (Map.Entry<VariableDescriptor, VariableInitializers> entry : resultInfo.entrySet()) {
|
for (VariableDescriptor variable : declarationData.usedVariables) {
|
||||||
VariableDescriptor variable = entry.getKey();
|
if (variable instanceof PropertyDescriptor && declarationData.declaredVariables.contains(variable)) {
|
||||||
if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) {
|
InstructionData.EdgesData<VariableInitializers> variableInitializers = instructionData.getInitializersMap().get(variable);
|
||||||
VariableInitializers initializers = entry.getValue();
|
if (variableInitializers == null) return;
|
||||||
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializers.isInitialized());
|
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitializers.getIn().isInitialized());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,161 +456,33 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<VariableDescriptor, VariableInitializers> addVariableInitializerFromCurrentInstructionIfAny(Instruction instruction, Map<VariableDescriptor, VariableInitializers> enterInstructionData) {
|
|
||||||
Map<VariableDescriptor, VariableInitializers> exitInstructionData = Maps.newHashMap(enterInstructionData);
|
|
||||||
if (instruction instanceof WriteValueInstruction) {
|
|
||||||
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
VariableInitializers enterInitializers = enterInstructionData.get(variable);
|
|
||||||
VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement(), enterInitializers);
|
|
||||||
exitInstructionData.put(variable, initializationAtThisElement);
|
|
||||||
}
|
|
||||||
else if (instruction instanceof VariableDeclarationInstruction) {
|
|
||||||
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
VariableInitializers enterInitializers = enterInstructionData.get(variable);
|
|
||||||
if (enterInitializers == null || !enterInitializers.isInitialized() || !enterInitializers.isDeclared()) {
|
|
||||||
JetElement element = ((VariableDeclarationInstruction) instruction).getElement();
|
|
||||||
if (element instanceof JetProperty) {
|
|
||||||
JetProperty property = (JetProperty) element;
|
|
||||||
if (property.getInitializer() == null) {
|
|
||||||
boolean isInitialized = enterInitializers != null && enterInitializers.isInitialized();
|
|
||||||
VariableInitializers variableDeclarationInfo = new VariableInitializers(isInitialized, true);
|
|
||||||
exitInstructionData.put(variable, variableDeclarationInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exitInstructionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<VariableDescriptor, VariableInitializers> mergeIncomingEdgesData(Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
|
||||||
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
|
||||||
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
|
||||||
variablesInScope.addAll(edgeData.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<VariableDescriptor, VariableInitializers> enterInstructionData = Maps.newHashMap();
|
|
||||||
for (VariableDescriptor variable : variablesInScope) {
|
|
||||||
Set<VariableInitializers> edgesDataForVariable = Sets.newHashSet();
|
|
||||||
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
|
||||||
VariableInitializers initializers = edgeData.get(variable);
|
|
||||||
if (initializers != null) {
|
|
||||||
edgesDataForVariable.add(initializers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enterInstructionData.put(variable, new VariableInitializers(edgesDataForVariable));
|
|
||||||
}
|
|
||||||
return enterInstructionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<VariableDescriptor, VariableInitializers> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) {
|
|
||||||
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = Maps.newHashMap();
|
|
||||||
VariableInitializers isInitializedForExternalVariable = new VariableInitializers(true);
|
|
||||||
VariableInitializers isNotInitializedForDeclaredVariable = new VariableInitializers(false);
|
|
||||||
|
|
||||||
for (VariableDescriptor variable : usedVariables) {
|
|
||||||
if (declaredVariables.contains(variable)) {
|
|
||||||
initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
initialMapForStartInstruction.put(variable, isInitializedForExternalVariable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return initialMapForStartInstruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List<? extends VariableDescriptor> variables) {
|
|
||||||
final List<VariableDescriptor> functionVariables = Lists.newArrayList();
|
|
||||||
for (VariableDescriptor variable : variables) {
|
|
||||||
if (JetStandardClasses.isFunctionType(variable.getReturnType())) {
|
|
||||||
functionVariables.add(variable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
|
||||||
assert pseudocode != null;
|
|
||||||
|
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
|
||||||
@Override
|
|
||||||
public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) {
|
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
if (variableDescriptor != null && functionVariables.contains(variableDescriptor)) {
|
|
||||||
//check that we only invoke this variable
|
|
||||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
|
||||||
if (element instanceof JetSimpleNameExpression && !(element.getParent() instanceof JetCallExpression)) {
|
|
||||||
trace.report(Errors.FUNCTION_PARAMETERS_OF_INLINE_FUNCTION.on((JetSimpleNameExpression) element, variableDescriptor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// "Unused variable" & "unused value" analyses
|
// "Unused variable" & "unused value" analyses
|
||||||
|
|
||||||
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
final PseudocodeData pseudocodeData = getPseudocodeData(subroutine);
|
||||||
assert pseudocode != null;
|
pseudocodeData.traverseInstructionsGraph(true, false, new PseudocodeData.TraverseInstructionGraphStrategy() {
|
||||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
|
||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
|
||||||
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
|
|
||||||
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData, new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(@NotNull Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
|
public void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData) {
|
||||||
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
|
VariableDescriptor variableDescriptor = pseudocodeData.extractVariableDescriptorIfAny(instruction, false);
|
||||||
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
|
if (variableDescriptor == null || !declarationData.declaredVariables.contains(variableDescriptor) ||
|
||||||
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
|
|
||||||
VariableDescriptor variableDescriptor = entry.getKey();
|
|
||||||
VariableStatus variableStatus = entry.getValue();
|
|
||||||
enterResult.put(variableDescriptor, variableStatus.merge(enterResult.get(variableDescriptor)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Map<VariableDescriptor, VariableStatus> exitResult = Maps.newHashMap(enterResult);
|
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
|
||||||
if (variableDescriptor != null) {
|
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
|
||||||
exitResult.put(variableDescriptor, VariableStatus.READ);
|
|
||||||
}
|
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
|
||||||
VariableStatus variableStatus = enterResult.get(variableDescriptor);
|
|
||||||
if (variableStatus == null) variableStatus = VariableStatus.UNUSED;
|
|
||||||
switch(variableStatus) {
|
|
||||||
case UNUSED:
|
|
||||||
case ONLY_WRITTEN:
|
|
||||||
exitResult.put(variableDescriptor, VariableStatus.ONLY_WRITTEN);
|
|
||||||
break;
|
|
||||||
case WRITTEN:
|
|
||||||
case READ:
|
|
||||||
exitResult.put(variableDescriptor, VariableStatus.WRITTEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>>(enterResult, exitResult);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
|
||||||
@Override
|
|
||||||
public void execute(@NotNull Instruction instruction, @Nullable Map<VariableDescriptor, VariableStatus> enterData, @Nullable Map<VariableDescriptor, VariableStatus> exitData) {
|
|
||||||
assert enterData != null && exitData != null;
|
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) ||
|
|
||||||
!DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
|
!DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return;
|
||||||
VariableStatus variableStatus = enterData.get(variableDescriptor);
|
InstructionData.EdgesData<VariableUseStatus> statusEdgesData = instructionData.getUseStatusMap().get(variableDescriptor);
|
||||||
|
VariableUseStatus variableUseStatus = statusEdgesData != null ? statusEdgesData.getIn() : null;
|
||||||
if (instruction instanceof WriteValueInstruction) {
|
if (instruction instanceof WriteValueInstruction) {
|
||||||
if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor)) return;
|
if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor)) return;
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||||
if (variableStatus != VariableStatus.READ) {
|
if (variableUseStatus != VariableUseStatus.LAST_READ) {
|
||||||
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
if (element instanceof JetBinaryExpression &&
|
||||||
|
((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
||||||
JetExpression right = ((JetBinaryExpression) element).getRight();
|
JetExpression right = ((JetBinaryExpression) element).getRight();
|
||||||
if (right != null) {
|
if (right != null) {
|
||||||
trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor));
|
trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (element instanceof JetPostfixExpression) {
|
else if (element instanceof JetPostfixExpression) {
|
||||||
IElementType operationToken = ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
|
IElementType operationToken =
|
||||||
|
((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType();
|
||||||
if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
|
if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) {
|
||||||
trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element));
|
trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element));
|
||||||
}
|
}
|
||||||
@@ -621,55 +494,48 @@ public class JetFlowInformationProvider {
|
|||||||
if (element instanceof JetNamedDeclaration) {
|
if (element instanceof JetNamedDeclaration) {
|
||||||
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
||||||
if (nameIdentifier == null) return;
|
if (nameIdentifier == null) return;
|
||||||
if (variableStatus == null || variableStatus == VariableStatus.UNUSED) {
|
if (variableUseStatus == null || variableUseStatus == VariableUseStatus.UNUSED) {
|
||||||
if (element instanceof JetProperty) {
|
if (element instanceof JetProperty) {
|
||||||
trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, variableDescriptor));
|
trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, variableDescriptor));
|
||||||
}
|
}
|
||||||
else if (element instanceof JetParameter) {
|
else if (element instanceof JetParameter) {
|
||||||
PsiElement psiElement = element.getParent().getParent();
|
PsiElement psiElement = element.getParent().getParent();
|
||||||
if (psiElement instanceof JetFunction) {
|
if (psiElement instanceof JetFunction) {
|
||||||
boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement);
|
boolean isMain = (psiElement instanceof JetNamedFunction) &&
|
||||||
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
|
JetMainDetector.isMain((JetNamedFunction) psiElement);
|
||||||
assert descriptor instanceof FunctionDescriptor;
|
//todo
|
||||||
|
if (psiElement instanceof JetFunctionLiteral) {
|
||||||
|
//psiElement = psiElement.getParent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DeclarationDescriptor descriptor =
|
||||||
|
trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement);
|
||||||
|
assert descriptor instanceof FunctionDescriptor : psiElement.getText();
|
||||||
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
|
FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor;
|
||||||
if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) {
|
if (!isMain &&
|
||||||
|
!functionDescriptor.getModality().isOverridable() &&
|
||||||
|
functionDescriptor.getOverriddenDescriptors().isEmpty()) {
|
||||||
trace.report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor));
|
trace.report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (variableStatus == VariableStatus.ONLY_WRITTEN && element instanceof JetProperty) {
|
else if (variableUseStatus == VariableUseStatus.ONLY_WRITTEN_NEVER_READ && element instanceof JetProperty) {
|
||||||
trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor));
|
trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
|
||||||
|
.on((JetNamedDeclaration) element, variableDescriptor));
|
||||||
}
|
}
|
||||||
else if (variableStatus == VariableStatus.WRITTEN && element instanceof JetProperty) {
|
else if (variableUseStatus == VariableUseStatus.LAST_WRITTEN && element instanceof JetProperty) {
|
||||||
JetExpression initializer = ((JetProperty) element).getInitializer();
|
JetExpression initializer = ((JetProperty) element).getInitializer();
|
||||||
if (initializer != null) {
|
if (initializer != null) {
|
||||||
trace.report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor));
|
trace.report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private static enum VariableStatus {
|
|
||||||
READ(3),
|
|
||||||
WRITTEN(2),
|
|
||||||
ONLY_WRITTEN(1),
|
|
||||||
UNUSED(0);
|
|
||||||
|
|
||||||
private int importance;
|
|
||||||
|
|
||||||
private VariableStatus(int importance) {
|
|
||||||
this.importance = importance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableStatus merge(@Nullable VariableStatus variableStatus) {
|
|
||||||
if (variableStatus == null || importance > variableStatus.importance) return this;
|
|
||||||
return variableStatus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -704,133 +570,4 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Util methods 7
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
|
||||||
JetElement element = null;
|
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
|
||||||
element = ((ReadValueInstruction) instruction).getElement();
|
|
||||||
}
|
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
|
||||||
element = ((WriteValueInstruction) instruction).getlValue();
|
|
||||||
}
|
|
||||||
else if (instruction instanceof VariableDeclarationInstruction) {
|
|
||||||
element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
|
||||||
}
|
|
||||||
return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
|
|
||||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
|
||||||
@Override
|
|
||||||
public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) {
|
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
if (variableDescriptor != null) {
|
|
||||||
usedVariables.add(variableDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return usedVariables;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<VariableDescriptor> collectDeclaredVariables(JetElement element) {
|
|
||||||
final Pseudocode pseudocode = pseudocodeMap.get(element);
|
|
||||||
assert pseudocode != null;
|
|
||||||
|
|
||||||
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
|
||||||
@Override
|
|
||||||
public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
|
||||||
if (instruction instanceof VariableDeclarationInstruction) {
|
|
||||||
JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
|
||||||
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
|
|
||||||
if (descriptor != null) {
|
|
||||||
assert descriptor instanceof VariableDescriptor;
|
|
||||||
declaredVariables.add((VariableDescriptor) descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return declaredVariables;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Local class for uninitialized variables analysis
|
|
||||||
|
|
||||||
private static class VariableInitializers {
|
|
||||||
private Set<JetElement> possibleLocalInitializers = Sets.newHashSet();
|
|
||||||
private boolean isInitialized;
|
|
||||||
private boolean isDeclared;
|
|
||||||
|
|
||||||
public VariableInitializers(boolean isInitialized) {
|
|
||||||
this(isInitialized, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableInitializers(boolean isInitialized, boolean isDeclared) {
|
|
||||||
this.isInitialized = isInitialized;
|
|
||||||
this.isDeclared = isDeclared;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableInitializers(JetElement element, @Nullable VariableInitializers previous) {
|
|
||||||
isInitialized = true;
|
|
||||||
isDeclared = element instanceof JetProperty || (previous != null && previous.isDeclared());
|
|
||||||
possibleLocalInitializers.add(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableInitializers(Set<VariableInitializers> edgesData) {
|
|
||||||
isInitialized = true;
|
|
||||||
isDeclared = true;
|
|
||||||
for (VariableInitializers edgeData : edgesData) {
|
|
||||||
if (!edgeData.isInitialized) {
|
|
||||||
isInitialized = false;
|
|
||||||
}
|
|
||||||
if (!edgeData.isDeclared) {
|
|
||||||
isDeclared = false;
|
|
||||||
}
|
|
||||||
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<JetElement> getPossibleLocalInitializers() {
|
|
||||||
return possibleLocalInitializers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInitialized() {
|
|
||||||
return isInitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeclared() {
|
|
||||||
return isDeclared;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof VariableInitializers)) return false;
|
|
||||||
|
|
||||||
VariableInitializers that = (VariableInitializers) o;
|
|
||||||
|
|
||||||
if (isDeclared != that.isDeclared) return false;
|
|
||||||
if (isInitialized != that.isInitialized) return false;
|
|
||||||
if (possibleLocalInitializers != null
|
|
||||||
? !possibleLocalInitializers.equals(that.possibleLocalInitializers)
|
|
||||||
: that.possibleLocalInitializers != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0;
|
|
||||||
result = 31 * result + (isInitialized ? 1 : 0);
|
|
||||||
result = 31 * result + (isDeclared ? 1 : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 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.jet.lang.cfg.data;
|
||||||
|
|
||||||
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetElement;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author svtk
|
||||||
|
*/
|
||||||
|
public class DeclarationData {
|
||||||
|
public final JetElement element;
|
||||||
|
public final PseudocodeData pseudocodeData;
|
||||||
|
public final Set<VariableDescriptor> declaredVariables;
|
||||||
|
public final Set<VariableDescriptor> usedVariables;
|
||||||
|
|
||||||
|
public DeclarationData(JetElement element,
|
||||||
|
PseudocodeData data,
|
||||||
|
Set<VariableDescriptor> declaredVariables,
|
||||||
|
Set<VariableDescriptor> usedVariables) {
|
||||||
|
this.element = element;
|
||||||
|
pseudocodeData = data;
|
||||||
|
this.declaredVariables = declaredVariables;
|
||||||
|
this.usedVariables = usedVariables;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 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.jet.lang.cfg.data;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.intellij.openapi.util.Pair;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.jet.lang.cfg.pseudocode.Instruction;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author svtk
|
||||||
|
*/
|
||||||
|
public class InstructionData {
|
||||||
|
public final Instruction instruction;
|
||||||
|
public final PseudocodeData pseudocodeData;
|
||||||
|
|
||||||
|
private final Map<VariableDescriptor, EdgesData<VariableInitializers>> initializersMap = Maps.newHashMap();
|
||||||
|
private final Map<VariableDescriptor, EdgesData<VariableUseStatus>> useStatusMap = Maps.newHashMap();
|
||||||
|
|
||||||
|
public InstructionData(@NotNull PseudocodeData data, @NotNull Instruction instruction,
|
||||||
|
@NotNull Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> pairOfVariableInitializersMap,
|
||||||
|
@NotNull Pair<Map<VariableDescriptor, VariableUseStatus>, Map<VariableDescriptor, VariableUseStatus>> pairOfVariableStatusMap) {
|
||||||
|
pseudocodeData = data;
|
||||||
|
this.instruction = instruction;
|
||||||
|
|
||||||
|
for (Map.Entry<VariableDescriptor, VariableInitializers> entry : pairOfVariableInitializersMap.second.entrySet()) {
|
||||||
|
VariableDescriptor variableDescriptor = entry.getKey();
|
||||||
|
VariableInitializers in = pairOfVariableInitializersMap.first.get(variableDescriptor);
|
||||||
|
VariableInitializers out = entry.getValue();
|
||||||
|
initializersMap.put(variableDescriptor, EdgesData.create(in, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<VariableDescriptor, VariableUseStatus> entry : pairOfVariableStatusMap.second.entrySet()) {
|
||||||
|
VariableDescriptor variableDescriptor = entry.getKey();
|
||||||
|
VariableUseStatus in = pairOfVariableStatusMap.first.get(variableDescriptor);
|
||||||
|
VariableUseStatus out = entry.getValue();
|
||||||
|
if (in == null || out == null) continue;
|
||||||
|
useStatusMap.put(variableDescriptor, EdgesData.create(in, out));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Map<VariableDescriptor, EdgesData<VariableInitializers>> getInitializersMap() {
|
||||||
|
return initializersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Map<VariableDescriptor, EdgesData<VariableUseStatus>> getUseStatusMap() {
|
||||||
|
return useStatusMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EdgesData<T> {
|
||||||
|
private final T in;
|
||||||
|
private final T out;
|
||||||
|
|
||||||
|
private EdgesData(@NotNull T in, @NotNull T out) {
|
||||||
|
this.in = in;
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> EdgesData<T> create(@NotNull T in, @NotNull T out) {
|
||||||
|
return new EdgesData<T>(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public T getIn() {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public T getOut() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 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.jet.lang.cfg.data;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.intellij.openapi.util.Pair;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.jet.lang.cfg.JetControlFlowGraphTraverser;
|
||||||
|
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetDeclaration;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetElement;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetProperty;
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author svtk
|
||||||
|
*/
|
||||||
|
public class PseudocodeData {
|
||||||
|
private final Pseudocode pseudocode;
|
||||||
|
private final Map<Instruction, InstructionData> instructionDataMap = Maps.newLinkedHashMap();
|
||||||
|
private final Map<Pseudocode, DeclarationData> declarationDataMap = Maps.newLinkedHashMap();
|
||||||
|
private final BindingTrace trace;
|
||||||
|
|
||||||
|
public PseudocodeData(@NotNull Pseudocode pseudocode, @NotNull BindingTrace trace) {
|
||||||
|
this.pseudocode = pseudocode;
|
||||||
|
this.trace = trace;
|
||||||
|
collectDeclarationData(pseudocode);
|
||||||
|
|
||||||
|
DeclarationData declarationData = declarationDataMap.get(pseudocode);
|
||||||
|
|
||||||
|
final Map<Instruction, Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>>> variableInitializersMap =
|
||||||
|
collectVariableInitializers(pseudocode, declarationData);
|
||||||
|
final Map<Instruction, Pair<Map<VariableDescriptor, VariableUseStatus>, Map<VariableDescriptor, VariableUseStatus>>> variableStatusMap =
|
||||||
|
collectVariableStatusData();
|
||||||
|
|
||||||
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(
|
||||||
|
new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) {
|
||||||
|
instructionDataMap.put(instruction,
|
||||||
|
new InstructionData(PseudocodeData.this, instruction, variableInitializersMap.get(instruction),
|
||||||
|
variableStatusMap.get(instruction)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Pseudocode getPseudocode() {
|
||||||
|
return pseudocode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Map<Instruction, InstructionData> getInstructionDataMap() {
|
||||||
|
return instructionDataMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public DeclarationData getDeclarationData(Pseudocode pseudocode) {
|
||||||
|
return declarationDataMap.get(pseudocode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public InstructionData getResultInfo(Pseudocode pseudocode) {
|
||||||
|
return instructionDataMap.get(pseudocode.getExitInstruction());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void traverseInstructionsGraph(boolean lookInside, boolean straightDirection,
|
||||||
|
@NotNull TraverseInstructionGraphStrategy traverseInstructionGraphStrategy) {
|
||||||
|
traverseInstructionsGraph(pseudocode, lookInside, straightDirection, traverseInstructionGraphStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void traverseInstructionsGraph(@NotNull Pseudocode pseudocode,
|
||||||
|
boolean lookInside,
|
||||||
|
boolean straightDirection,
|
||||||
|
@NotNull TraverseInstructionGraphStrategy traverseInstructionGraphStrategy) {
|
||||||
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
|
if (!straightDirection) {
|
||||||
|
instructions = Lists.newArrayList(instructions);
|
||||||
|
Collections.reverse(instructions);
|
||||||
|
}
|
||||||
|
for (Instruction instruction : instructions) {
|
||||||
|
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||||
|
traverseInstructionsGraph(((LocalDeclarationInstruction) instruction).getBody(), lookInside, straightDirection,
|
||||||
|
traverseInstructionGraphStrategy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
traverseInstructionGraphStrategy.execute(instruction, declarationDataMap.get(pseudocode), instructionDataMap.get(instruction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface TraverseInstructionGraphStrategy {
|
||||||
|
void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectDeclarationData(Pseudocode pseudocode) {
|
||||||
|
DeclarationData declarationData = new DeclarationData(pseudocode.getCorrespondingElement(), this, collectDeclaredVariables(pseudocode), collectUsedVariables(pseudocode));
|
||||||
|
declarationDataMap.put(pseudocode, declarationData);
|
||||||
|
|
||||||
|
for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) {
|
||||||
|
collectDeclarationData(localPseudocode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<VariableDescriptor> collectUsedVariables(@NotNull Pseudocode pseudocode) {
|
||||||
|
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
||||||
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||||
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
|
if (variableDescriptor != null) {
|
||||||
|
usedVariables.add(variableDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return usedVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<VariableDescriptor> collectDeclaredVariables(@NotNull Pseudocode pseudocode) {
|
||||||
|
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||||
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||||
|
if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||||
|
DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
|
||||||
|
if (descriptor != null) {
|
||||||
|
assert descriptor instanceof VariableDescriptor;
|
||||||
|
declaredVariables.add((VariableDescriptor) descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return declaredVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable initializers
|
||||||
|
|
||||||
|
private Map<Instruction, Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>>> collectVariableInitializers(
|
||||||
|
Pseudocode pseudocode, DeclarationData data) {
|
||||||
|
|
||||||
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false, true);
|
||||||
|
|
||||||
|
final Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = prepareInitialMapForStartInstruction(data.usedVariables, data.declaredVariables);
|
||||||
|
|
||||||
|
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction,
|
||||||
|
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitializers>>() {
|
||||||
|
@Override
|
||||||
|
public Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>> execute(
|
||||||
|
@NotNull Instruction instruction,
|
||||||
|
@NotNull Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
||||||
|
|
||||||
|
Map<VariableDescriptor, VariableInitializers> enterInstructionData = mergeIncomingEdgesData(incomingEdgesData);
|
||||||
|
Map<VariableDescriptor, VariableInitializers> exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData);
|
||||||
|
return Pair.create(enterInstructionData, exitInstructionData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<Instruction, Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>>> result =
|
||||||
|
traverser.getDataMap();
|
||||||
|
for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) {
|
||||||
|
Map<Instruction, Pair<Map<VariableDescriptor, VariableInitializers>, Map<VariableDescriptor, VariableInitializers>>> initializersForLocalDeclaration =
|
||||||
|
collectVariableInitializers(localPseudocode, declarationDataMap.get(localPseudocode));
|
||||||
|
|
||||||
|
for (Instruction instruction : initializersForLocalDeclaration.keySet()) {
|
||||||
|
//todo
|
||||||
|
if (!result.containsKey(instruction)) {
|
||||||
|
result.put(instruction, initializersForLocalDeclaration.get(instruction));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.putAll(initializersForLocalDeclaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) {
|
||||||
|
Map<VariableDescriptor, VariableInitializers> initialMapForStartInstruction = Maps.newHashMap();
|
||||||
|
VariableInitializers isInitializedForExternalVariable = new VariableInitializers(true);
|
||||||
|
VariableInitializers isNotInitializedForDeclaredVariable = new VariableInitializers(false);
|
||||||
|
|
||||||
|
for (VariableDescriptor variable : usedVariables) {
|
||||||
|
if (declaredVariables.contains(variable)) {
|
||||||
|
initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
initialMapForStartInstruction.put(variable, isInitializedForExternalVariable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return initialMapForStartInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> mergeIncomingEdgesData(Collection<Map<VariableDescriptor, VariableInitializers>> incomingEdgesData) {
|
||||||
|
Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
|
||||||
|
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
||||||
|
variablesInScope.addAll(edgeData.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<VariableDescriptor, VariableInitializers> enterInstructionData = Maps.newHashMap();
|
||||||
|
for (VariableDescriptor variable : variablesInScope) {
|
||||||
|
Set<VariableInitializers> edgesDataForVariable = Sets.newHashSet();
|
||||||
|
for (Map<VariableDescriptor, VariableInitializers> edgeData : incomingEdgesData) {
|
||||||
|
VariableInitializers initializers = edgeData.get(variable);
|
||||||
|
if (initializers != null) {
|
||||||
|
edgesDataForVariable.add(initializers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enterInstructionData.put(variable, new VariableInitializers(edgesDataForVariable));
|
||||||
|
}
|
||||||
|
return enterInstructionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<VariableDescriptor, VariableInitializers> addVariableInitializerFromCurrentInstructionIfAny(Instruction instruction, Map<VariableDescriptor, VariableInitializers> enterInstructionData) {
|
||||||
|
if (!(instruction instanceof WriteValueInstruction) && !(instruction instanceof VariableDeclarationInstruction)) {
|
||||||
|
return enterInstructionData;
|
||||||
|
}
|
||||||
|
VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false);
|
||||||
|
if (variable == null) {
|
||||||
|
return enterInstructionData;
|
||||||
|
}
|
||||||
|
Map<VariableDescriptor, VariableInitializers> exitInstructionData = Maps.newHashMap(enterInstructionData);
|
||||||
|
if (instruction instanceof WriteValueInstruction) {
|
||||||
|
VariableInitializers enterInitializers = enterInstructionData.get(variable);
|
||||||
|
VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement(), enterInitializers);
|
||||||
|
exitInstructionData.put(variable, initializationAtThisElement);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VariableInitializers enterInitializers = enterInstructionData.get(variable);
|
||||||
|
if (enterInitializers == null || !enterInitializers.isInitialized() || !enterInitializers.isDeclared()) {
|
||||||
|
JetElement element = ((VariableDeclarationInstruction) instruction).getElement();
|
||||||
|
if (element instanceof JetProperty) {
|
||||||
|
JetProperty property = (JetProperty) element;
|
||||||
|
if (property.getInitializer() == null) {
|
||||||
|
boolean isInitialized = enterInitializers != null && enterInitializers.isInitialized();
|
||||||
|
VariableInitializers variableDeclarationInfo = new VariableInitializers(isInitialized, true);
|
||||||
|
exitInstructionData.put(variable, variableDeclarationInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exitInstructionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable use
|
||||||
|
|
||||||
|
private Map<Instruction, Pair<Map<VariableDescriptor, VariableUseStatus>, Map<VariableDescriptor, VariableUseStatus>>> collectVariableStatusData() {
|
||||||
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableUseStatus>> traverser =
|
||||||
|
JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
||||||
|
Map<VariableDescriptor, VariableUseStatus> sinkInstructionData = Maps.newHashMap();
|
||||||
|
for (VariableDescriptor usedVariable : declarationDataMap.get(pseudocode).usedVariables) {
|
||||||
|
sinkInstructionData.put(usedVariable, VariableUseStatus.UNUSED);
|
||||||
|
}
|
||||||
|
traverser.collectInformationFromInstructionGraph(Collections.<VariableDescriptor, VariableUseStatus>emptyMap(), sinkInstructionData,
|
||||||
|
new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableUseStatus>>() {
|
||||||
|
@Override
|
||||||
|
public Pair<Map<VariableDescriptor, VariableUseStatus>, Map<VariableDescriptor, VariableUseStatus>> execute(@NotNull Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableUseStatus>> incomingEdgesData) {
|
||||||
|
Map<VariableDescriptor, VariableUseStatus> enterResult = Maps.newHashMap();
|
||||||
|
for (Map<VariableDescriptor, VariableUseStatus> edgeData : incomingEdgesData) {
|
||||||
|
for (Map.Entry<VariableDescriptor, VariableUseStatus> entry : edgeData.entrySet()) {
|
||||||
|
VariableDescriptor variableDescriptor = entry.getKey();
|
||||||
|
VariableUseStatus variableUseStatus = entry.getValue();
|
||||||
|
enterResult.put(variableDescriptor, variableUseStatus.merge(enterResult.get(variableDescriptor)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||||
|
if (variableDescriptor == null || (!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction))) {
|
||||||
|
return Pair.create(enterResult, enterResult);
|
||||||
|
}
|
||||||
|
Map<VariableDescriptor, VariableUseStatus> exitResult = Maps.newHashMap(enterResult);
|
||||||
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
|
exitResult.put(variableDescriptor, VariableUseStatus.LAST_READ);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VariableUseStatus variableUseStatus = enterResult.get(variableDescriptor);
|
||||||
|
if (variableUseStatus == null) variableUseStatus = VariableUseStatus.UNUSED;
|
||||||
|
switch (variableUseStatus) {
|
||||||
|
case UNUSED:
|
||||||
|
case ONLY_WRITTEN_NEVER_READ:
|
||||||
|
exitResult.put(variableDescriptor, VariableUseStatus.ONLY_WRITTEN_NEVER_READ);
|
||||||
|
break;
|
||||||
|
case LAST_WRITTEN:
|
||||||
|
case LAST_READ:
|
||||||
|
exitResult.put(variableDescriptor, VariableUseStatus.LAST_WRITTEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Pair.create(enterResult, exitResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return traverser.getDataMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Util methods
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public VariableDescriptor extractVariableDescriptorIfAny(@NotNull Instruction instruction, boolean onlyReference) {
|
||||||
|
JetElement element = null;
|
||||||
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
|
element = ((ReadValueInstruction) instruction).getElement();
|
||||||
|
}
|
||||||
|
else if (instruction instanceof WriteValueInstruction) {
|
||||||
|
element = ((WriteValueInstruction) instruction).getlValue();
|
||||||
|
}
|
||||||
|
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||||
|
}
|
||||||
|
return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 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.jet.lang.cfg.data;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetElement;
|
||||||
|
import org.jetbrains.jet.lang.psi.JetProperty;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author svtk
|
||||||
|
*/
|
||||||
|
public class VariableInitializers {
|
||||||
|
private final Set<JetElement> possibleLocalInitializers = Sets.newHashSet();
|
||||||
|
private boolean isInitialized;
|
||||||
|
private boolean isDeclared;
|
||||||
|
|
||||||
|
public VariableInitializers(boolean isInitialized) {
|
||||||
|
this(isInitialized, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableInitializers(boolean isInitialized, boolean isDeclared) {
|
||||||
|
this.isInitialized = isInitialized;
|
||||||
|
this.isDeclared = isDeclared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableInitializers(JetElement element, @Nullable VariableInitializers previous) {
|
||||||
|
isInitialized = true;
|
||||||
|
isDeclared = element instanceof JetProperty || (previous != null && previous.isDeclared());
|
||||||
|
possibleLocalInitializers.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableInitializers(Set<VariableInitializers> edgesData) {
|
||||||
|
isInitialized = true;
|
||||||
|
isDeclared = true;
|
||||||
|
for (VariableInitializers edgeData : edgesData) {
|
||||||
|
if (!edgeData.isInitialized) {
|
||||||
|
isInitialized = false;
|
||||||
|
}
|
||||||
|
if (!edgeData.isDeclared) {
|
||||||
|
isDeclared = false;
|
||||||
|
}
|
||||||
|
possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<JetElement> getPossibleLocalInitializers() {
|
||||||
|
return possibleLocalInitializers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeclared() {
|
||||||
|
return isDeclared;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof VariableInitializers)) return false;
|
||||||
|
|
||||||
|
VariableInitializers that = (VariableInitializers) o;
|
||||||
|
|
||||||
|
if (isDeclared != that.isDeclared) return false;
|
||||||
|
if (isInitialized != that.isInitialized) return false;
|
||||||
|
if (possibleLocalInitializers != null
|
||||||
|
? !possibleLocalInitializers.equals(that.possibleLocalInitializers)
|
||||||
|
: that.possibleLocalInitializers != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0;
|
||||||
|
result = 31 * result + (isInitialized ? 1 : 0);
|
||||||
|
result = 31 * result + (isDeclared ? 1 : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 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.jet.lang.cfg.data;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author svtk
|
||||||
|
*/
|
||||||
|
public enum VariableUseStatus {
|
||||||
|
LAST_READ(3),
|
||||||
|
LAST_WRITTEN(2),
|
||||||
|
ONLY_WRITTEN_NEVER_READ(1),
|
||||||
|
UNUSED(0);
|
||||||
|
|
||||||
|
private final int importance;
|
||||||
|
|
||||||
|
VariableUseStatus(int importance) {
|
||||||
|
this.importance = importance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableUseStatus merge(@Nullable VariableUseStatus variableUseStatus) {
|
||||||
|
if (variableUseStatus == null || importance > variableUseStatus.importance) return this;
|
||||||
|
return variableUseStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user