/** * Returns LiveVariables object with information concerning local variables, parameters and fields */ public static LiveVariables analyzeWithFields(CFG cfg) { return analyze(cfg, true); }
private boolean includeSymbol(Symbol symbol) { return isLocalVariable(symbol) || (includeFields && isField(symbol)); }
private static boolean isLiveInMethodEntry(Symbol privateFieldSymbol, MethodTree methodTree) { CFG cfg = CFG.build(methodTree); LiveVariables liveVariables = LiveVariables.analyzeWithFields(cfg); return liveVariables.getIn(cfg.entryBlock()).contains(privateFieldSymbol); }
@Override public void visitForEachStatement(ForEachStatement tree) { CFG cfg = CFG.buildCFG(Collections.singletonList(tree), true); Symbol var = tree.variable().symbol(); boolean liveVar = true; if(var.owner().isMethodSymbol()) { cfg.setMethodSymbol((Symbol.MethodSymbol) var.owner()); LiveVariables analyze = LiveVariables.analyze(cfg); Set<Symbol> live = analyze.getOut(cfg.reversedBlocks().get(1)); liveVar = live.contains(var); } if(!liveVar) { variables.add(var); } super.visitForEachStatement(tree); if(!liveVar) { variables.remove(var); } }
private static LiveVariables analyze(CFG cfg, boolean includeFields) { LiveVariables liveVariables = new LiveVariables(cfg, includeFields); // Generate kill/gen for each block in isolation Map<CFG.Block, Set<Symbol>> kill = new HashMap<>(); Map<CFG.Block, Set<Symbol>> gen = new HashMap<>(); for (CFG.Block block : liveVariables.cfg.reversedBlocks()) { Set<Symbol> blockKill = new HashSet<>(); Set<Symbol> blockGen = new HashSet<>(); liveVariables.processBlockElements(block, blockKill, blockGen); kill.put(block, blockKill); gen.put(block, blockGen); } liveVariables.analyzeCFG(liveVariables.in, kill, gen); // out of exit block are empty by definition. if (!liveVariables.out.get(liveVariables.cfg.reversedBlocks().get(0)).isEmpty()) { throw new IllegalStateException("Out of exit block should be empty"); } // Make things immutable. for (Map.Entry<CFG.Block, Set<Symbol>> blockSetEntry : liveVariables.out.entrySet()) { blockSetEntry.setValue(ImmutableSet.copyOf(blockSetEntry.getValue())); } return liveVariables; }
@Override public void visitMethod(MethodTree tree) { BlockTree block = tree.block(); if(block == null) { return; } CFG cfg = CFG.build(tree); LiveVariables analyze = LiveVariables.analyze(cfg); Set<Symbol> live = analyze.getIn(cfg.entryBlock()); for (VariableTree parameterTree : tree.parameters()) { if(!live.contains(parameterTree.symbol())) { variables.add(parameterTree.symbol()); } } super.visitMethod(tree); for (VariableTree parameterTree : tree.parameters()) { if(!live.contains(parameterTree.symbol())) { variables.remove(parameterTree.symbol()); } } }
switch (element.kind()) { case ASSIGNMENT: processAssignment((AssignmentExpressionTree) element, blockKill, blockGen, assignmentLHS); break; case IDENTIFIER: processIdentifier((IdentifierTree) element, blockGen, assignmentLHS); break; case MEMBER_SELECT: processMemberSelect((MemberSelectExpressionTree) element, assignmentLHS, blockGen); break; case VARIABLE: break; case LAMBDA_EXPRESSION: blockGen.addAll(getUsedVariables(((LambdaExpressionTree) element).body(), cfg.methodSymbol())); break; case METHOD_REFERENCE: blockGen.addAll(getUsedVariables(((MethodReferenceTree) element).expression(), cfg.methodSymbol())); break; case NEW_CLASS: blockGen.addAll(getUsedVariables(((NewClassTree) element).classBody(), cfg.methodSymbol())); break; default:
if (lhs.is(Tree.Kind.IDENTIFIER)) { symbol = ((IdentifierTree) lhs).symbol(); if (isLocalVariable(symbol)) { assignmentLHS.add(lhs); blockGen.remove(symbol); case IDENTIFIER: symbol = ((IdentifierTree) element).symbol(); if (!assignmentLHS.contains(element) && isLocalVariable(symbol)) { blockGen.add(symbol); break; case LAMBDA_EXPRESSION: blockGen.addAll(getUsedVariables(((LambdaExpressionTree) element).body(), cfg.methodSymbol())); break; case NEW_CLASS: blockGen.addAll(getUsedVariables(((NewClassTree) element).classBody(), cfg.methodSymbol())); break; default:
private void cleanUpProgramState(CFG.Block block) { if (cleanup) { programState = programState.cleanupDeadSymbols(liveVariables.getOut(block)); programState = programState.cleanupConstraints(); } }
private void processAssignment(AssignmentExpressionTree element, Set<Symbol> blockKill, Set<Symbol> blockGen, Set<Tree> assignmentLHS) { Symbol symbol = null; ExpressionTree lhs = element.variable(); if (lhs.is(Kind.IDENTIFIER)) { symbol = ((IdentifierTree) lhs).symbol(); } else if (includeFields && lhs.is(Kind.MEMBER_SELECT)) { symbol = getField((MemberSelectExpressionTree) lhs); } if (symbol != null && includeSymbol(symbol)) { assignmentLHS.add(lhs); blockGen.remove(symbol); blockKill.add(symbol); } }
private void processMemberSelect(MemberSelectExpressionTree element, Set<Tree> assignmentLHS, Set<Symbol> blockGen) { Symbol symbol; if (!assignmentLHS.contains(element) && includeFields) { symbol = getField(element); if (symbol != null) { blockGen.add(symbol); } } }
@Override public void visitForEachStatement(ForEachStatement tree) { CFG cfg = CFG.buildCFG(Collections.singletonList(tree), true); Symbol var = tree.variable().symbol(); boolean liveVar = true; if(var.owner().isMethodSymbol()) { cfg.setMethodSymbol((Symbol.MethodSymbol) var.owner()); LiveVariables analyze = LiveVariables.analyze(cfg); Set<Symbol> live = analyze.getOut(cfg.reversedBlocks().get(1)); liveVar = live.contains(var); } if(!liveVar) { variables.add(var); } super.visitForEachStatement(tree); if(!liveVar) { variables.remove(var); } }
private static LiveVariables analyze(CFG cfg, boolean includeFields) { LiveVariables liveVariables = new LiveVariables(cfg, includeFields); // Generate kill/gen for each block in isolation Map<CFG.Block, Set<Symbol>> kill = new HashMap<>(); Map<CFG.Block, Set<Symbol>> gen = new HashMap<>(); for (CFG.Block block : liveVariables.cfg.reversedBlocks()) { Set<Symbol> blockKill = new HashSet<>(); Set<Symbol> blockGen = new HashSet<>(); liveVariables.processBlockElements(block, blockKill, blockGen); kill.put(block, blockKill); gen.put(block, blockGen); } liveVariables.analyzeCFG(liveVariables.in, kill, gen); // out of exit block are empty by definition. if (!liveVariables.out.get(liveVariables.cfg.reversedBlocks().get(0)).isEmpty()) { throw new IllegalStateException("Out of exit block should be empty"); } // Make things immutable. for (Map.Entry<CFG.Block, Set<Symbol>> blockSetEntry : liveVariables.out.entrySet()) { blockSetEntry.setValue(ImmutableSet.copyOf(blockSetEntry.getValue())); } return liveVariables; }
@Override public void visitMethod(MethodTree tree) { BlockTree block = tree.block(); if(block == null) { return; } CFG cfg = CFG.build(tree); LiveVariables analyze = LiveVariables.analyze(cfg); Set<Symbol> live = analyze.getIn(cfg.entryBlock()); for (VariableTree parameterTree : tree.parameters()) { if(!live.contains(parameterTree.symbol())) { variables.add(parameterTree.symbol()); } } super.visitMethod(tree); for (VariableTree parameterTree : tree.parameters()) { if(!live.contains(parameterTree.symbol())) { variables.remove(parameterTree.symbol()); } } }
switch (element.kind()) { case ASSIGNMENT: processAssignment((AssignmentExpressionTree) element, blockKill, blockGen, assignmentLHS); break; case IDENTIFIER: processIdentifier((IdentifierTree) element, blockGen, assignmentLHS); break; case MEMBER_SELECT: processMemberSelect((MemberSelectExpressionTree) element, assignmentLHS, blockGen); break; case VARIABLE: break; case LAMBDA_EXPRESSION: blockGen.addAll(getUsedVariables(((LambdaExpressionTree) element).body(), cfg.methodSymbol())); break; case METHOD_REFERENCE: blockGen.addAll(getUsedVariables(((MethodReferenceTree) element).expression(), cfg.methodSymbol())); break; case NEW_CLASS: blockGen.addAll(getUsedVariables(((NewClassTree) element).classBody(), cfg.methodSymbol())); break; default:
private void cleanUpProgramState(CFG.Block block) { if (cleanup) { Collection<SymbolicValue> protectedSVs = methodBehavior == null ? Collections.emptyList() : methodBehavior.parameters(); programState = programState.cleanupDeadSymbols(liveVariables.getOut(block), protectedSVs); programState = programState.cleanupConstraints(protectedSVs); } }
private void processAssignment(AssignmentExpressionTree element, Set<Symbol> blockKill, Set<Symbol> blockGen, Set<Tree> assignmentLHS) { Symbol symbol = null; ExpressionTree lhs = element.variable(); if (lhs.is(Kind.IDENTIFIER)) { symbol = ((IdentifierTree) lhs).symbol(); } else if (includeFields && lhs.is(Kind.MEMBER_SELECT)) { symbol = getField((MemberSelectExpressionTree) lhs); } if (symbol != null && includeSymbol(symbol)) { assignmentLHS.add(lhs); blockGen.remove(symbol); blockKill.add(symbol); } }
private void processMemberSelect(MemberSelectExpressionTree element, Set<Tree> assignmentLHS, Set<Symbol> blockGen) { Symbol symbol; if (!assignmentLHS.contains(element) && includeFields) { symbol = getField(element); if (symbol != null) { blockGen.add(symbol); } } }
@Override public void visitNode(Tree tree) { if (!hasSemantic()) { return; } MethodTree methodTree = (MethodTree) tree; if (methodTree.block() == null) { return; } // TODO(npe) Exclude try statements with finally as CFG is incorrect for those and lead to false positive if (hasTryFinallyWithLocalVar(methodTree.block(), methodTree.symbol())) { return; } Symbol.MethodSymbol methodSymbol = methodTree.symbol(); CFG cfg = CFG.build(methodTree); LiveVariables liveVariables = LiveVariables.analyze(cfg); // Liveness analysis provides information only for block boundaries, so we should do analysis between elements within blocks for (CFG.Block block : cfg.blocks()) { checkElements(block, liveVariables.getOut(block), methodSymbol); } }
public static LiveVariables analyze(CFG cfg) { LiveVariables liveVariables = new LiveVariables(cfg); final Map<CFG.Block, Set<Symbol>> in = new HashMap<>(); // Generate kill/gen for each block in isolation Map<CFG.Block, Set<Symbol>> kill = new HashMap<>(); Map<CFG.Block, Set<Symbol>> gen = new HashMap<>(); for (CFG.Block block : liveVariables.cfg.reversedBlocks()) { Set<Symbol> blockKill = new HashSet<>(); Set<Symbol> blockGen = new HashSet<>(); liveVariables.processBlockElements(block, blockKill, blockGen); kill.put(block, blockKill); gen.put(block, blockGen); } liveVariables.analyzeCFG(in, kill, gen); // out of exit block are empty by definition. if (!liveVariables.out.get(liveVariables.cfg.reversedBlocks().get(0)).isEmpty()) { throw new IllegalStateException("Out of exit block should be empty"); } // Make things immutable. for (Map.Entry<CFG.Block, Set<Symbol>> blockSetEntry : liveVariables.out.entrySet()) { blockSetEntry.setValue(ImmutableSet.copyOf(blockSetEntry.getValue())); } return liveVariables; }