cfg analyzing enhancement

This commit is contained in:
svtk
2011-11-03 18:31:18 +04:00
parent 2eb7835ae1
commit 6a16cecce9
7 changed files with 175 additions and 81 deletions
@@ -18,14 +18,16 @@ import java.util.Map;
*/
public class JetControlFlowGraphTraverser<D> {
private final Pseudocode pseudocode;
private final boolean lookInside;
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode) {
return new JetControlFlowGraphTraverser<D>(pseudocode);
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside) {
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside);
}
private JetControlFlowGraphTraverser(Pseudocode pseudocode) {
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside) {
this.pseudocode = pseudocode;
this.lookInside = lookInside;
}
public void collectInformationFromInstructionGraph(
@@ -51,7 +53,7 @@ public class JetControlFlowGraphTraverser<D> {
Pair<D, D> initialPair = Pair.create(initialDataValue, initialDataValue);
for (Instruction instruction : instructions) {
dataMap.put(instruction, initialPair);
if (instruction instanceof LocalDeclarationInstruction) {
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
initializeDataMap(((LocalDeclarationInstruction) instruction).getBody(), initialDataValue);
}
}
@@ -82,7 +84,7 @@ public class JetControlFlowGraphTraverser<D> {
allPreviousInstructions = previousInstructions;
}
if (instruction instanceof LocalDeclarationInstruction) {
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
traverseSubGraph(subroutinePseudocode, instructionsMergeStrategy, previousInstructions, straightDirection, changed, true);
}
@@ -111,11 +113,11 @@ public class JetControlFlowGraphTraverser<D> {
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
List<Instruction> instructions = pseudocode.getInstructions();
for (Instruction instruction : instructions) {
if (((InstructionImpl)instruction).isDead()) continue;
if (instruction instanceof LocalDeclarationInstruction) {
if (instruction.isDead()) continue;
Pair<D, D> pair = dataMap.get(instruction);
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
}
Pair<D, D> pair = dataMap.get(instruction);
instructionDataAnalyzeStrategy.execute(instruction,
pair != null ? pair.getFirst() : null,
pair != null ? pair.getSecond() : null);
@@ -123,7 +125,7 @@ public class JetControlFlowGraphTraverser<D> {
}
public D getResultInfo() {
return dataMap.get(pseudocode.getExitInstruction()).getFirst();
return dataMap.get(pseudocode.getSinkInstruction()).getFirst();
}
interface InstructionsMergeStrategy<D> {
@@ -733,36 +733,47 @@ public class JetControlFlowProcessor {
@Override
public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) {
value(expression.getObjectDeclaration(), inCondition);
JetObjectDeclaration declaration = expression.getObjectDeclaration();
value(declaration, inCondition);
List<JetDeclaration> declarations = declaration.getDeclarations();
List<JetDeclaration> functions = Lists.newArrayList();
for (JetDeclaration localDeclaration : declarations) {
if (!(localDeclaration instanceof JetProperty) && !(localDeclaration instanceof JetClassInitializer)) {
functions.add(localDeclaration);
}
}
Queue<Label> declarationLabels = new LinkedList<Label>();
for (JetDeclaration function : functions) {
declarationLabels.add(builder.createUnboundLabel());
}
builder.nondeterministicJump(Lists.newArrayList(declarationLabels));
for (JetDeclaration function : functions) {
if (function instanceof JetNamedDeclaration) {
if (function instanceof JetDeclarationWithBody) {
JetExpression bodyExpression = ((JetDeclarationWithBody) function).getBodyExpression();
generate(function, bodyExpression != null ? bodyExpression : function);
}
else {
generate(function, function);
}
}
else {
generate(declaration, function);
}
builder.bindLabel(declarationLabels.remove());
}
builder.read(expression);
}
@Override
public void visitObjectDeclaration(JetObjectDeclaration declaration) {
// for (JetDelegationSpecifier delegationSpecifier : declaration.getDelegationSpecifiers()) {
public void visitObjectDeclaration(JetObjectDeclaration objectDeclaration) {
// for (JetDelegationSpecifier delegationSpecifier : objectDeclaration.getDelegationSpecifiers()) {
// value(delegationSpecifier, inCondition);
// }
Queue<Label> declarationLabels = new LinkedList<Label>();
List<JetDeclaration> declarations = declaration.getDeclarations();
for (JetDeclaration localDeclaration : declarations) {
declarationLabels.add(builder.createUnboundLabel());
}
builder.nondeterministicJump(Lists.newArrayList(declarationLabels));
for (JetDeclaration localDeclaration : declarations) {
if (localDeclaration instanceof JetNamedDeclaration) {
if (localDeclaration instanceof JetDeclarationWithBody) {
JetExpression bodyExpression = ((JetDeclarationWithBody) localDeclaration).getBodyExpression();
generate(localDeclaration, bodyExpression != null ? bodyExpression : localDeclaration);
}
else {
generate(localDeclaration, localDeclaration);
}
}
else {
generate(declaration, localDeclaration);
}
builder.bindLabel(declarationLabels.remove());
}
visitClassOrObject(objectDeclaration);
}
@Override
@@ -783,17 +794,22 @@ public class JetControlFlowProcessor {
@Override
public void visitAnonymousInitializer(JetClassInitializer classInitializer) {
classInitializer.getBody().accept(this);
value(classInitializer.getBody(), inCondition);
}
private void visitClassOrObject(JetClassOrObject classOrObject) {
List<JetDeclaration> declarations = classOrObject.getDeclarations();
for (JetDeclaration declaration : declarations) {
if (declaration instanceof JetProperty || declaration instanceof JetClassInitializer) {
//declaration.accept(this);
value(declaration, inCondition);
}
}
}
@Override
public void visitClass(JetClass klass) {
List<JetDeclaration> declarations = klass.getDeclarations();
for (JetDeclaration declaration : declarations) {
if (declaration instanceof JetProperty || declaration instanceof JetClassInitializer) {
declaration.accept(this);
}
}
visitClassOrObject(klass);
}
@Override
@@ -4,6 +4,7 @@ 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 com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.pseudocode.*;
@@ -122,11 +123,11 @@ public class JetFlowInformationProvider {
}
}
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean inAnonymousInitializers) {
public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean inAnonymousInitializers, final boolean declaredLocally) {
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
JetControlFlowGraphTraverser<Map<VariableDescriptor, InitializationPoints>> traverser = JetControlFlowGraphTraverser.create(pseudocode);
JetControlFlowGraphTraverser<Map<VariableDescriptor, InitializationPoints>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true);
JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>> instructionsMergeStrategy =
new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Map<VariableDescriptor, InitializationPoints>>() {
@@ -163,7 +164,9 @@ public class JetFlowInformationProvider {
}
};
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = prepareInitialMapForStartInstruction(subroutine, pseudocode);
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables);
traverser.collectInformationFromInstructionGraph(instructionsMergeStrategy,
Collections.<VariableDescriptor, InitializationPoints>emptyMap(),
@@ -189,7 +192,7 @@ public class JetFlowInformationProvider {
isInitialized = true;
}
}
if (!isInitialized) {
if (!declaredLocally && !isInitialized) {
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, variableDescriptor));
}
}
@@ -211,10 +214,12 @@ public class JetFlowInformationProvider {
}
}
JetSimpleNameExpression expression = (JetSimpleNameExpression) element;
if (hasInitializer && !variableDescriptor.isVar()) {
trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor));
if (!declaredLocally && hasInitializer && !variableDescriptor.isVar()) {
PsiElement psiElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
JetProperty property = psiElement instanceof JetProperty ? (JetProperty) psiElement : null;
trace.report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor, property == null ? new JetProperty[0] : new JetProperty[] { property }));
}
if (inAnonymousInitializers && variableDescriptor instanceof PropertyDescriptor &&
if (!declaredLocally && inAnonymousInitializers && variableDescriptor instanceof PropertyDescriptor &&
!enterInitializationPoints.isInitialized() && exitInitializationPoints.isInitialized()) {
if (expression.getReferencedNameElementType() != JetTokens.FIELD_IDENTIFIER) {
trace.report(Errors.INITIALIZATION_USING_BACKING_FIELD.on(expression, variableDescriptor));
@@ -225,22 +230,17 @@ public class JetFlowInformationProvider {
}
});
if (inAnonymousInitializers) {
Map<VariableDescriptor, InitializationPoints> lastInfo = traverser.getResultInfo();
for (Map.Entry<VariableDescriptor, InitializationPoints> entry : lastInfo.entrySet()) {
VariableDescriptor variable = entry.getKey();
if (variable instanceof PropertyDescriptor) {
InitializationPoints initializationPoints = entry.getValue();
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializationPoints.isInitialized());
}
Map<VariableDescriptor, InitializationPoints> lastInfo = traverser.getResultInfo();
for (Map.Entry<VariableDescriptor, InitializationPoints> entry : lastInfo.entrySet()) {
VariableDescriptor variable = entry.getKey();
if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) {
InitializationPoints initializationPoints = entry.getValue();
trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializationPoints.isInitialized());
}
}
}
private Map<VariableDescriptor, InitializationPoints> prepareInitialMapForStartInstruction(JetElement subroutine, Pseudocode pseudocode) {
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
private Map<VariableDescriptor, InitializationPoints> prepareInitialMapForStartInstruction(Collection<VariableDescriptor> usedVariables, Collection<VariableDescriptor> declaredVariables) {
Map<VariableDescriptor, InitializationPoints> initialMapForStartInstruction = Maps.newHashMap();
InitializationPoints initialPointsForDeclaredVariable = new InitializationPoints(false);
InitializationPoints initialPointsForExternalVariable = new InitializationPoints(true);
@@ -267,7 +267,7 @@ public class JetFlowInformationProvider {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
JetControlFlowGraphTraverser.<Void>create(pseudocode).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
@Override
public void execute(Instruction instruction, Void enterData, Void exitData) {
if (instruction instanceof ReadValueInstruction) {
@@ -314,7 +314,7 @@ public class JetFlowInformationProvider {
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
JetControlFlowGraphTraverser.<Void>create(pseudocode).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
@Override
public void execute(Instruction instruction, Void enterData, Void exitData) {
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction);
@@ -336,7 +336,7 @@ public class JetFlowInformationProvider {
assert descriptor instanceof VariableDescriptor;
declaredVariables.add((VariableDescriptor) descriptor);
}
return null;
return super.visitProperty(property, data);
}
@Override
@@ -349,7 +349,7 @@ public class JetFlowInformationProvider {
declaredVariables.add((VariableDescriptor) descriptor);
}
}
return null;
return super.visitForExpression(expression, data);
}
}, null);
return declaredVariables;
@@ -30,9 +30,12 @@ public class LocalDeclarationInstruction extends InstructionWithNext {
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
ArrayList<Instruction> instructions = Lists.newArrayList(sink);
instructions.addAll(super.getNextInstructions());
return instructions;
if (sink != null) {
ArrayList<Instruction> instructions = Lists.newArrayList(sink);
instructions.addAll(super.getNextInstructions());
return instructions;
}
return super.getNextInstructions();
}
public void setSink(SubroutineSinkInstruction sink) {
@@ -12,7 +12,6 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,13 +26,13 @@ public class ControlFlowAnalyzer {
private TopDownAnalysisContext context;
private ExpressionTypingServices typeInferrerServices;
private final JetControlFlowDataTraceFactory flowDataTraceFactory;
private final boolean declaredLocally;
private final boolean isDeclaredLocally;
public ControlFlowAnalyzer(TopDownAnalysisContext context, JetControlFlowDataTraceFactory flowDataTraceFactory, boolean declaredLocally) {
this.context = context;
this.flowDataTraceFactory = flowDataTraceFactory;
isDeclaredLocally = declaredLocally;
this.typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace());
this.declaredLocally = declaredLocally;
}
public void process() {
@@ -43,8 +42,18 @@ public class ControlFlowAnalyzer {
if (!context.completeAnalysisNeeded(aClass)) continue;
checkClass(aClass, classDescriptor);
checkClassOrObject(aClass, classDescriptor);
}
for (Map.Entry<JetObjectDeclaration, MutableClassDescriptor> entry : context.getObjects().entrySet()) {
JetObjectDeclaration objectDeclaration = entry.getKey();
MutableClassDescriptor objectDescriptor = entry.getValue();
if (!context.completeAnalysisNeeded(objectDeclaration)) continue;
checkClassOrObject(objectDeclaration, objectDescriptor);
}
for (Map.Entry<JetNamedFunction, FunctionDescriptorImpl> entry : context.getFunctions().entrySet()) {
JetNamedFunction function = entry.getKey();
FunctionDescriptorImpl functionDescriptor = entry.getValue();
@@ -73,9 +82,9 @@ public class ControlFlowAnalyzer {
}
}
private void checkClass(JetClass klass, MutableClassDescriptor classDescriptor) {
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider(klass, klass, flowDataTraceFactory, context.getTrace());
flowInformationProvider.markUninitializedVariables(klass, true);
private void checkClassOrObject(JetClassOrObject klass, MutableClassDescriptor classDescriptor) {
JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) klass, (JetExpression) klass, flowDataTraceFactory, context.getTrace());
flowInformationProvider.markUninitializedVariables((JetElement) klass, true, isDeclaredLocally);
}
private void checkFunction(JetDeclarationWithBody function, FunctionDescriptor functionDescriptor, final @NotNull JetType expectedReturnType) {
@@ -129,12 +138,10 @@ public class ControlFlowAnalyzer {
}
});
}
if (!declaredLocally) {
flowInformationProvider.markUninitializedVariables(function.asElement(), false);
if (((JetDeclaration) function).hasModifier(JetTokens.INLINE_KEYWORD)) {
//inline functions after M1
flowInformationProvider.markUninitializedVariables(function.asElement(), false, isDeclaredLocally);
if (((JetDeclaration) function).hasModifier(JetTokens.INLINE_KEYWORD)) {
//inline functions after M1
// flowInformationProvider.markNotOnlyInvokedFunctionVariables(function.asElement(), functionDescriptor.getValueParameters());
}
}
}
@@ -247,4 +247,62 @@ class ForwardAccessToBackingField() { //kt-147
val a = <!UNRESOLVED_REFERENCE, UNINITIALIZED_VARIABLE!>$a<!> // error
val b = <!UNINITIALIZED_VARIABLE!>$c<!> // error
val c = 1
}
class ClassObject() {
class object {
val x : Int
{
$x = 1
}
fun foo() {
val a : Int
doSmth(<!UNINITIALIZED_VARIABLE!>a<!>)
}
}
}
fun foo() {
val a = object {
val x : Int
val <!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>y<!> : Int
{
$x = 1
}
//todo
/* fun foo() {
<VAL_REASSIGNMENT>y<> = 10
}*/
}
}
object O {
val x : Int
val <!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>y<!> : Int
{
$x = 1
}
fun foo() {
<!VAL_REASSIGNMENT!>y<!> = 10
val i: Int
if (1 < 3) {
i = 10
}
doSmth(<!UNINITIALIZED_VARIABLE!>i<!>)
}
}
fun foo() {
val b = 1
val a = object {
val x = b
{
<!VAL_REASSIGNMENT!>b<!> = 4
<!UNRESOLVED_REFERENCE!>$b<!> = 3
}
}
}
@@ -285,8 +285,9 @@ public class JetControlFlowTest extends JetLiteFixture {
@Override
public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
//todo print edges
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getNext()), null);
for (Instruction nextInstruction : instruction.getNextInstructions()) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(nextInstruction), null);
}
}
@Override
@@ -313,6 +314,13 @@ public class JetControlFlowTest extends JetLiteFixture {
@Override
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
if (!instruction.getNextInstructions().isEmpty()) {
printEdge(out, nodeToName.get(instruction), nodeToName.get(instruction.getNextInstructions().iterator().next()), null);
}
}
@Override
public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
// Nothing
}
@@ -367,7 +375,7 @@ public class JetControlFlowTest extends JetLiteFixture {
}
private void dumpDot(String name, Collection<Pseudocode> pseudocodes) throws FileNotFoundException {
String graphFileName = getTestDataPath() + "/" + getTestFilePath() + ".dot";
String graphFileName = getTestFilePath() + ".dot";
File target = new File(graphFileName);
PrintStream out = new PrintStream(target);