package org.kantega.revoc.analysis; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.Analyzer; import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.BasicInterpreter; import java.util.*; /** * */ public class OneLineAnalyze { private final Map<Integer, BitSet> oneLiners; private final Map<Integer, BitSet> mustHaveRun; private final Map<Integer, BitSet> cantHaveRun; private OneLineAnalyze(Map<Integer, BitSet> oneLiners, Map<Integer, BitSet> mustHaveRun, Map<Integer, BitSet> cantHaveRun) { this.oneLiners = oneLiners; this.mustHaveRun = mustHaveRun; this.cantHaveRun = cantHaveRun; } public boolean mustHaveRunOnce(int exitInstruction, int lineIndex) { return oneLiners.get(exitInstruction).get(lineIndex); } public boolean mustHaveRun(int exitInstruction, int lineIndex) { return mustHaveRun.get(exitInstruction).get(lineIndex); } public boolean cantHaveRun(int exitInstruction, int lineIndex) { return cantHaveRun.get(exitInstruction).get(lineIndex); } public static OneLineAnalyze analyze(MethodNode node) { Map<Integer, Set<Integer>> jumpSources = new TreeMap<>(); Analyzer analyzer = new Analyzer(new BasicInterpreter()) { @Override protected void newControlFlowEdge(int insn, int successor) { addJumpSource(jumpSources, insn, successor); super.newControlFlowEdge(insn, successor); } @Override protected boolean newControlFlowExceptionEdge(int insn, int successor) { addJumpSource(jumpSources, insn, successor); return super.newControlFlowExceptionEdge(insn, successor); } }; try { analyzer.analyze(node.name, node); } catch (AnalyzerException e) { throw new RuntimeException(e); } InsnList instructions = node.instructions; Map<Integer, Set<Integer>> dominators = new HashMap<>(); Set<Integer> all = new HashSet<>(); for (int i = 0; i < instructions.size(); i++) { all.add(i); } for (int i = 0; i < instructions.size(); i++) { dominators.put(i, new HashSet<>()); // Dominator of first is itself if(i == 0) { dominators.get(i).add(i); } else { // All other nodes dominate dominators.get(i).addAll(new HashSet<>(all)); } } int hash = dominators.hashCode(); Map<Integer, Set<Integer>> predecessors = new TreeMap<>(); for (int i = 0; i < instructions.size(); i++) { predecessors.put(i, new HashSet<>()); } int prehas = predecessors.hashCode(); while(true) { for (int i = 0; i < instructions.size() - 1; i++) { AbstractInsnNode ins = instructions.get(i); if (ins instanceof JumpInsnNode) { int index = instructions.indexOf(((JumpInsnNode) ins).label); if (ins.getOpcode() == Opcodes.GOTO) { predecessors.get(index).add(i); predecessors.get(index).addAll(predecessors.get(i)); } else { predecessors.get(i + 1).add(i); predecessors.get(i + 1).addAll(predecessors.get(i)); predecessors.get(index).add(i); predecessors.get(index).addAll(predecessors.get(i)); } } else { predecessors.get(i + 1).add(i); predecessors.get(i + 1).addAll(predecessors.get(i)); } } int newPrehash = predecessors.hashCode(); if(newPrehash == prehas) { break; } prehas = newPrehash; } BitSet loops = new BitSet(); int lineNumber = -1; for (int i = 0; i < instructions.size(); i++) { AbstractInsnNode ins = instructions.get(i); if(ins instanceof LineNumberNode) { lineNumber = ((LineNumberNode) ins).line; } if(lineNumber != -1 && predecessors.get(i).contains(i)) { loops.set(lineNumber); } } while (true) { for (int i = 1; i < instructions.size(); i++) { Set<Integer> doms = new TreeSet<>(); doms.add(i); Set<Integer> preds = new HashSet<>(); if(jumpSources.containsKey(i)) { preds.addAll(jumpSources.get(i)); } doms.addAll(intersectDominators(dominators, preds)); dominators.put(i, doms); } if(hash == dominators.hashCode()) { break; } else { hash = dominators.hashCode(); } } Map<Integer, BitSet> oneLiners = new TreeMap<>(); Map<Integer, BitSet> mustHaveRun = new TreeMap<>(); Map<Integer, BitSet> cantHaveRun = new TreeMap<>(); Map<Integer, Integer> lineIndex = new TreeMap<>(); for(int i = 0; i < instructions.size(); i++) { int index = lineIndex.size(); if(instructions.get(i) instanceof LineNumberNode) { int line = ((LineNumberNode) instructions.get(i)).line; if(!lineIndex.containsKey(line)) { lineIndex.put(line, index); } } oneLiners.put(i, new BitSet()); mustHaveRun.put(i, new BitSet()); cantHaveRun.put(i, new BitSet()); } for(int i = 0; i < instructions.size(); i++) { for (Integer d : dominators.get(instructions.indexOf(instructions.get(i)))) { if (instructions.get(d) instanceof LineNumberNode) { int line = ((LineNumberNode) instructions.get(d)).line; int index = lineIndex.get(line); if(!loops.get(line)) { oneLiners.get(i).set(index, true); } mustHaveRun.get(i).set(index); } } for(int x = 0; x < instructions.size(); x++) { if (instructions.get(x) instanceof LineNumberNode) { if (!predecessors.get(i).contains(x)) { int line = ((LineNumberNode) instructions.get(x)).line; int index = lineIndex.get(line); cantHaveRun.get(i).set(index); } } } } return new OneLineAnalyze(oneLiners, mustHaveRun, cantHaveRun); } private static void addJumpSource(Map<Integer, Set<Integer>> jumpSources, int insn, int successor) { if(!jumpSources.containsKey(successor)) { jumpSources.put(successor, new HashSet<>()); } jumpSources.get(successor).add(insn); } private static Set<Integer> intersectDominators(Map<Integer, Set<Integer>> dominators, Set<Integer> preds) { if(preds.size() == 0) { return Collections.emptySet(); } Set<Integer> intercetions = null; for (Integer pred : preds) { Set<Integer> dom = dominators.get(pred); if(intercetions == null) { intercetions = new HashSet<>(dom); } else { intercetions.retainAll(dom); } } return intercetions; } public int instructionSize() { return oneLiners.size(); } }