cfg traversal enhancement
This commit is contained in:
@@ -183,17 +183,17 @@ public class JetFlowInformationProvider {
|
||||
// }
|
||||
// }
|
||||
|
||||
private boolean collectReachable(Instruction current, Set<Instruction> visited, @Nullable Instruction lookFor) {
|
||||
if (!visited.add(current)) return false;
|
||||
if (current == lookFor) return true;
|
||||
|
||||
for (Instruction nextInstruction : current.getNextInstructions()) {
|
||||
if (collectReachable(nextInstruction, visited, lookFor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// private boolean collectReachable(Instruction current, Set<Instruction> visited, @Nullable Instruction lookFor) {
|
||||
// if (!visited.add(current)) return false;
|
||||
// if (current == lookFor) return true;
|
||||
//
|
||||
// for (Instruction nextInstruction : current.getNextInstructions()) {
|
||||
// if (collectReachable(nextInstruction, visited, lookFor)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// private void processPreviousInstructions(Instruction previousFor, final Set<Instruction> visited, final Collection<JetExpression> returnedExpressions, final Collection<JetElement> elementsReturningUnit) {
|
||||
// if (!visited.add(previousFor)) return;
|
||||
@@ -259,16 +259,18 @@ public class JetFlowInformationProvider {
|
||||
private <D> Map<Instruction, D> traverseInstructionGraphUntilFactsStabilization(
|
||||
Pseudocode pseudocode,
|
||||
InstructionsMergeHandler<D> instructionsMergeHandler,
|
||||
D initialDataValue,
|
||||
D initialDataValue,
|
||||
D initialDataValueForEnterInstruction,
|
||||
boolean straightDirection) {
|
||||
Map<Instruction, D> dataMap = Maps.newHashMap();
|
||||
initializeDataMap(dataMap, pseudocode, initialDataValue);
|
||||
dataMap.put(pseudocode.getEnterInstruction(), initialDataValueForEnterInstruction);
|
||||
|
||||
boolean[] changed = new boolean[1];
|
||||
changed[0] = true;
|
||||
while (changed[0]) {
|
||||
changed[0] = false;
|
||||
traverseSubGraph(pseudocode, instructionsMergeHandler, Collections.<Instruction>emptyList(), straightDirection, dataMap, changed);
|
||||
traverseSubGraph(pseudocode, instructionsMergeHandler, Collections.<Instruction>emptyList(), straightDirection, dataMap, changed, false);
|
||||
}
|
||||
return dataMap;
|
||||
}
|
||||
@@ -287,15 +289,18 @@ public class JetFlowInformationProvider {
|
||||
}
|
||||
|
||||
private <D> void traverseSubGraph(
|
||||
Pseudocode pseudocode,
|
||||
InstructionsMergeHandler<D> instructionsMergeHandler,
|
||||
Collection<Instruction> previousSubGraphInstructions,
|
||||
boolean straightDirection,
|
||||
Map<Instruction, D> dataMap,
|
||||
boolean[] changed) {
|
||||
Pseudocode pseudocode,
|
||||
InstructionsMergeHandler<D> instructionsMergeHandler,
|
||||
Collection<Instruction> previousSubGraphInstructions,
|
||||
boolean straightDirection,
|
||||
Map<Instruction, D> dataMap,
|
||||
boolean[] changed,
|
||||
boolean isLocal) {
|
||||
List<Instruction> instructions = pseudocode.getInstructions();
|
||||
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
|
||||
for (Instruction instruction : instructions) {
|
||||
if (!isLocal && instruction instanceof SubroutineEnterInstruction) continue;
|
||||
|
||||
Collection<Instruction> allPreviousInstructions;
|
||||
Collection<Instruction> previousInstructions = straightDirection
|
||||
? instruction.getPreviousInstructions()
|
||||
@@ -311,7 +316,7 @@ public class JetFlowInformationProvider {
|
||||
|
||||
if (instruction instanceof LocalDeclarationInstruction) {
|
||||
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
||||
traverseSubGraph(subroutinePseudocode, instructionsMergeHandler, previousInstructions, straightDirection, dataMap, changed);
|
||||
traverseSubGraph(subroutinePseudocode, instructionsMergeHandler, previousInstructions, straightDirection, dataMap, changed, true);
|
||||
}
|
||||
D previousDataValue = dataMap.get(instruction);
|
||||
|
||||
@@ -320,7 +325,7 @@ public class JetFlowInformationProvider {
|
||||
for (Instruction previousInstruction : allPreviousInstructions) {
|
||||
incomingEdgesData.add(dataMap.get(previousInstruction));
|
||||
}
|
||||
D mergedData = instructionsMergeHandler.merge(instruction, previousDataValue, incomingEdgesData);
|
||||
D mergedData = instructionsMergeHandler.merge(instruction, incomingEdgesData);
|
||||
if (!mergedData.equals(previousDataValue)) {
|
||||
changed[0] = true;
|
||||
dataMap.put(instruction, mergedData);
|
||||
@@ -333,86 +338,203 @@ public class JetFlowInformationProvider {
|
||||
assert pseudocode != null;
|
||||
|
||||
Collection<Instruction> instructions = pseudocode.getInstructions();
|
||||
InstructionsMergeHandler<Set<LocalVariableDescriptor>> instructionsMergeHandler = new InstructionsMergeHandler<Set<LocalVariableDescriptor>>() {
|
||||
InstructionsMergeHandler<Map<LocalVariableDescriptor, InitializationPoints>> instructionsMergeHandler = new InstructionsMergeHandler<Map<LocalVariableDescriptor, InitializationPoints>>() {
|
||||
@Override
|
||||
public Set<LocalVariableDescriptor> merge(Instruction instruction, Set<LocalVariableDescriptor> previousDataValue, Collection<Set<LocalVariableDescriptor>> incomingEdgesData) {
|
||||
Set<LocalVariableDescriptor> initializedVariables = Sets.newHashSet();
|
||||
initializedVariables.addAll(previousDataValue);
|
||||
Set<LocalVariableDescriptor> edgesDataIntersection = Sets.newHashSet();
|
||||
boolean isFirst = true;
|
||||
for (Set<LocalVariableDescriptor> edgeData : incomingEdgesData) {
|
||||
if (isFirst) {
|
||||
edgesDataIntersection.addAll(edgeData);
|
||||
}
|
||||
else {
|
||||
edgesDataIntersection = Sets.intersection(edgesDataIntersection, edgeData).immutableCopy();
|
||||
}
|
||||
isFirst = false;
|
||||
}
|
||||
initializedVariables.addAll(edgesDataIntersection);
|
||||
public Map<LocalVariableDescriptor, InitializationPoints> merge(
|
||||
Instruction instruction,
|
||||
Collection<Map<LocalVariableDescriptor, InitializationPoints>> incomingEdgesData) {
|
||||
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
DeclarationDescriptor descriptor = null;
|
||||
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
||||
if (lValue instanceof JetProperty || lValue instanceof JetParameter) {
|
||||
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
|
||||
}
|
||||
else if (lValue instanceof JetSimpleNameExpression) {
|
||||
descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
|
||||
}
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
initializedVariables.add((LocalVariableDescriptor) descriptor);
|
||||
}
|
||||
Set<LocalVariableDescriptor> variablesInScope = Sets.newHashSet();
|
||||
for (Map<LocalVariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
||||
variablesInScope.addAll(edgePointsMap.keySet());
|
||||
}
|
||||
return initializedVariables;
|
||||
|
||||
Map<LocalVariableDescriptor, InitializationPoints> pointsMap = Maps.newHashMap();
|
||||
for (LocalVariableDescriptor variable : variablesInScope) {
|
||||
Set<InitializationPoints> edgesDataForVariable = Sets.newHashSet();
|
||||
for (Map<LocalVariableDescriptor, InitializationPoints> edgePointsMap : incomingEdgesData) {
|
||||
InitializationPoints points = edgePointsMap.get(variable);
|
||||
if (points != null) {
|
||||
edgesDataForVariable.add(points);
|
||||
}
|
||||
}
|
||||
pointsMap.put(variable, new InitializationPoints(edgesDataForVariable));
|
||||
}
|
||||
|
||||
if (instruction instanceof WriteValueInstruction) {
|
||||
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
||||
// DeclarationDescriptor descriptor = null;
|
||||
// JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
||||
// if (lValue instanceof JetProperty || lValue instanceof JetParameter) {
|
||||
// descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
|
||||
// }
|
||||
// else if (lValue instanceof JetSimpleNameExpression) {
|
||||
// descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
|
||||
// }
|
||||
// if (descriptor instanceof LocalVariableDescriptor) {
|
||||
// LocalVariableDescriptor variableDescriptor = (LocalVariableDescriptor) descriptor;
|
||||
InitializationPoints initializationAtThisPoint = new InitializationPoints(((WriteValueInstruction) instruction).getElement());
|
||||
pointsMap.put(variableDescriptor, initializationAtThisPoint);
|
||||
// }
|
||||
}
|
||||
|
||||
return pointsMap;
|
||||
}
|
||||
};
|
||||
Map<Instruction, Set<LocalVariableDescriptor>> dataMap = traverseInstructionGraphUntilFactsStabilization(pseudocode, instructionsMergeHandler, Collections.<LocalVariableDescriptor>emptySet(), true);
|
||||
InstructionDataAnalyzer instructionDataAnalyzer = new InstructionDataAnalyzer<Set<LocalVariableDescriptor>>() {
|
||||
|
||||
Set<LocalVariableDescriptor> localVariables = collectAllLocalVariables(pseudocode);
|
||||
Map<LocalVariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
|
||||
InitializationPoints initialPoints = new InitializationPoints();
|
||||
for (LocalVariableDescriptor variable : localVariables) {
|
||||
initialMapForStartInstruction.put(variable, initialPoints);
|
||||
}
|
||||
Map<Instruction, Map<LocalVariableDescriptor, InitializationPoints>> dataMap =
|
||||
traverseInstructionGraphUntilFactsStabilization(pseudocode, instructionsMergeHandler, Collections.<LocalVariableDescriptor, InitializationPoints>emptyMap(), initialMapForStartInstruction, true);
|
||||
|
||||
InstructionDataAnalyzer<Map<LocalVariableDescriptor, InitializationPoints>> instructionDataAnalyzer = new InstructionDataAnalyzer<Map<LocalVariableDescriptor, InitializationPoints>>() {
|
||||
@Override
|
||||
public void analyze(Instruction instruction, Map<Instruction, Set<LocalVariableDescriptor>> dataMap) {
|
||||
Set<LocalVariableDescriptor> initializedVariables = dataMap.get(instruction);
|
||||
public void analyze(Instruction instruction, Map<Instruction, Map<LocalVariableDescriptor, InitializationPoints>> dataMap) {
|
||||
Map<LocalVariableDescriptor, InitializationPoints> variablesData = dataMap.get(instruction);
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
||||
if (element instanceof JetSimpleNameExpression) {
|
||||
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
if (!initializedVariables.contains(descriptor)) {
|
||||
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, descriptor));
|
||||
}
|
||||
if (element instanceof JetSimpleNameExpression && variableDescriptor != null) {
|
||||
// DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
||||
// if (descriptor instanceof LocalVariableDescriptor) {
|
||||
InitializationPoints initializationPoints = variablesData.get(variableDescriptor);
|
||||
assert initializationPoints != null;
|
||||
if (initializationPoints.canBeUninitialized) {
|
||||
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof WriteValueInstruction) {
|
||||
JetElement element = ((WriteValueInstruction) instruction).getlValue();
|
||||
if (element instanceof JetSimpleNameExpression) {
|
||||
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
if (initializedVariables.contains(descriptor) && !((LocalVariableDescriptor) descriptor).isVar()) {
|
||||
trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, descriptor));
|
||||
}
|
||||
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
||||
if (element instanceof JetSimpleNameExpression && variableDescriptor != null) {
|
||||
// DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
||||
// if (descriptor instanceof LocalVariableDescriptor) {
|
||||
InitializationPoints initializationPoints = variablesData.get(variableDescriptor);
|
||||
assert initializationPoints != null;
|
||||
if (initializationPoints.hasPossibleInitializers() && !variableDescriptor.isVar()) {
|
||||
trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, variableDescriptor));
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
traverseInstructionGraphAndReportErrors(instructions, dataMap, instructionDataAnalyzer);
|
||||
traverseAndAnalyzeInstructionGraph(instructions, dataMap, instructionDataAnalyzer);
|
||||
}
|
||||
|
||||
private void traverseInstructionGraphAndReportErrors(Collection<Instruction> instructions, Map<Instruction, Set<LocalVariableDescriptor>> dataMap, InstructionDataAnalyzer<Set<LocalVariableDescriptor>> instructionDataAnalyzer) {
|
||||
@Nullable
|
||||
private LocalVariableDescriptor extractVariableDescriptorIfAny(Instruction instruction) {
|
||||
LocalVariableDescriptor variableDescriptor = null;
|
||||
if (instruction instanceof ReadValueInstruction) {
|
||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
||||
if (element instanceof JetSimpleNameExpression) {
|
||||
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
variableDescriptor = (LocalVariableDescriptor) descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof WriteValueInstruction) {
|
||||
DeclarationDescriptor descriptor = null;
|
||||
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
||||
if (lValue instanceof JetProperty || lValue instanceof JetParameter) {
|
||||
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
|
||||
}
|
||||
else if (lValue instanceof JetSimpleNameExpression) {
|
||||
descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
|
||||
}
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
variableDescriptor = (LocalVariableDescriptor) descriptor;
|
||||
}
|
||||
}
|
||||
return variableDescriptor;
|
||||
}
|
||||
|
||||
private Set<LocalVariableDescriptor> collectAllLocalVariables(Pseudocode pseudocode) {
|
||||
final Set<LocalVariableDescriptor> localVariables = Sets.newHashSet();
|
||||
InstructionDataAnalyzer<Void> analyzer = new InstructionDataAnalyzer<Void>() {
|
||||
@Override
|
||||
public void analyze(Instruction instruction, Map<Instruction, Void> dataMap) {
|
||||
LocalVariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
|
||||
if (variableDescriptor != null) {
|
||||
localVariables.add(variableDescriptor);
|
||||
}
|
||||
}
|
||||
};
|
||||
traverseAndAnalyzeInstructionGraph(pseudocode.getInstructions(), Collections.<Instruction, Void>emptyMap(), analyzer);
|
||||
return localVariables;
|
||||
}
|
||||
|
||||
private <D> void traverseAndAnalyzeInstructionGraph(Collection<Instruction> instructions, Map<Instruction, D> dataMap, InstructionDataAnalyzer<D> instructionDataAnalyzer) {
|
||||
for (Instruction instruction : instructions) {
|
||||
if (instruction instanceof LocalDeclarationInstruction) {
|
||||
traverseInstructionGraphAndReportErrors(((LocalDeclarationInstruction) instruction).getBody().getInstructions(), dataMap, instructionDataAnalyzer);
|
||||
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody().getInstructions(), dataMap, instructionDataAnalyzer);
|
||||
}
|
||||
instructionDataAnalyzer.analyze(instruction, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
interface InstructionsMergeHandler<D> {
|
||||
D merge(Instruction instruction, D previousDataValue, Collection<D> incomingEdgesData);
|
||||
D merge(Instruction instruction, Collection<D> incomingEdgesData);
|
||||
}
|
||||
|
||||
interface InstructionDataAnalyzer<D> {
|
||||
void analyze(Instruction instruction, Map<Instruction, D> dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InitializationPoints {
|
||||
private Set<JetElement> possiblePoints = Sets.newHashSet();
|
||||
private boolean canBeUninitialized;
|
||||
|
||||
public InitializationPoints() {
|
||||
canBeUninitialized = true;
|
||||
}
|
||||
|
||||
public InitializationPoints(JetElement element) {
|
||||
canBeUninitialized = false;
|
||||
possiblePoints.add(element);
|
||||
}
|
||||
|
||||
public InitializationPoints(Set<InitializationPoints> edgesData) {
|
||||
canBeUninitialized = false;
|
||||
for (InitializationPoints edgeData : edgesData) {
|
||||
if (edgeData.canBeUninitialized) {
|
||||
canBeUninitialized = true;
|
||||
}
|
||||
possiblePoints.addAll(edgeData.possiblePoints);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasPossibleInitializers() {
|
||||
return !possiblePoints.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof InitializationPoints)) return false;
|
||||
|
||||
InitializationPoints that = (InitializationPoints) o;
|
||||
|
||||
if (canBeUninitialized != that.canBeUninitialized) return false;
|
||||
if (possiblePoints != null ? !possiblePoints.equals(that.possiblePoints) : that.possiblePoints != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = possiblePoints != null ? possiblePoints.hashCode() : 0;
|
||||
result = 31 * result + (canBeUninitialized ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user