| /*** |
| * ASM examples: examples showing how ASM can be used |
| * Copyright (c) 2000-2011 INRIA, France Telecom |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.objectweb.asm.ClassReader; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.tree.AbstractInsnNode; |
| import org.objectweb.asm.tree.ClassNode; |
| import org.objectweb.asm.tree.IincInsnNode; |
| import org.objectweb.asm.tree.MethodNode; |
| import org.objectweb.asm.tree.VarInsnNode; |
| import org.objectweb.asm.tree.analysis.Analyzer; |
| import org.objectweb.asm.tree.analysis.BasicValue; |
| import org.objectweb.asm.tree.analysis.BasicVerifier; |
| import org.objectweb.asm.tree.analysis.Frame; |
| import org.objectweb.asm.tree.analysis.SourceInterpreter; |
| import org.objectweb.asm.tree.analysis.SourceValue; |
| import org.objectweb.asm.util.TraceMethodVisitor; |
| import org.objectweb.asm.util.Textifier; |
| |
| /** |
| * @author Eric Bruneton |
| */ |
| public class Analysis implements Opcodes { |
| |
| public static void main(final String[] args) throws Exception { |
| ClassReader cr = new ClassReader("Analysis"); |
| ClassNode cn = new ClassNode(); |
| cr.accept(cn, ClassReader.SKIP_DEBUG); |
| |
| List<MethodNode> methods = cn.methods; |
| for (int i = 0; i < methods.size(); ++i) { |
| MethodNode method = methods.get(i); |
| if (method.instructions.size() > 0) { |
| if (!analyze(cn, method)) { |
| Analyzer<?> a = new Analyzer<BasicValue>( |
| new BasicVerifier()); |
| try { |
| a.analyze(cn.name, method); |
| } catch (Exception ignored) { |
| } |
| final Frame<?>[] frames = a.getFrames(); |
| |
| Textifier t = new Textifier() { |
| @Override |
| public void visitMaxs(final int maxStack, |
| final int maxLocals) { |
| for (int i = 0; i < text.size(); ++i) { |
| StringBuffer s = new StringBuffer( |
| frames[i] == null ? "null" |
| : frames[i].toString()); |
| while (s.length() < Math.max(20, maxStack |
| + maxLocals + 1)) { |
| s.append(' '); |
| } |
| System.err.print(Integer.toString(i + 1000) |
| .substring(1) |
| + " " |
| + s |
| + " : " |
| + text.get(i)); |
| } |
| System.err.println(); |
| } |
| }; |
| MethodVisitor mv = new TraceMethodVisitor(t); |
| for (int j = 0; j < method.instructions.size(); ++j) { |
| Object insn = method.instructions.get(j); |
| ((AbstractInsnNode) insn).accept(mv); |
| } |
| mv.visitMaxs(0, 0); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Detects unused xSTORE instructions, i.e. xSTORE instructions without at |
| * least one xLOAD corresponding instruction in their successor instructions |
| * (in the control flow graph). |
| */ |
| public static boolean analyze(final ClassNode c, final MethodNode m) |
| throws Exception { |
| Analyzer<SourceValue> a = new Analyzer<SourceValue>( |
| new SourceInterpreter()); |
| Frame<SourceValue>[] frames = a.analyze(c.name, m); |
| |
| // for each xLOAD instruction, we find the xSTORE instructions that can |
| // produce the value loaded by this instruction, and we put them in |
| // 'stores' |
| Set<AbstractInsnNode> stores = new HashSet<AbstractInsnNode>(); |
| for (int i = 0; i < m.instructions.size(); ++i) { |
| AbstractInsnNode insn = m.instructions.get(i); |
| int opcode = insn.getOpcode(); |
| if ((opcode >= ILOAD && opcode <= ALOAD) || opcode == IINC) { |
| int var = opcode == IINC ? ((IincInsnNode) insn).var |
| : ((VarInsnNode) insn).var; |
| Frame<SourceValue> f = frames[i]; |
| if (f != null) { |
| Set<AbstractInsnNode> s = f.getLocal(var).insns; |
| Iterator<AbstractInsnNode> j = s.iterator(); |
| while (j.hasNext()) { |
| insn = j.next(); |
| if (insn instanceof VarInsnNode) { |
| stores.add(insn); |
| } |
| } |
| } |
| } |
| } |
| |
| // we then find all the xSTORE instructions that are not in 'stores' |
| boolean ok = true; |
| for (int i = 0; i < m.instructions.size(); ++i) { |
| AbstractInsnNode insn = m.instructions.get(i); |
| int opcode = insn.getOpcode(); |
| if (opcode >= ISTORE && opcode <= ASTORE) { |
| if (!stores.contains(insn)) { |
| ok = false; |
| System.err.println("method " + m.name + ", instruction " |
| + i + ": useless store instruction"); |
| } |
| } |
| } |
| return ok; |
| } |
| |
| /* |
| * Test for the above method, with three useless xSTORE instructions. |
| */ |
| public int test(int i, int j) { |
| i = i + 1; // ok, because i can be read after this point |
| |
| if (j == 0) { |
| j = 1; // useless |
| } else { |
| try { |
| j = j - 1; // ok, because j can be accessed in the catch |
| int k = 0; |
| if (i > 0) { |
| k = i - 1; |
| } |
| return k; |
| } catch (Exception e) { // useless ASTORE (e is never used) |
| j = j + 1; // useless |
| } |
| } |
| |
| return 0; |
| } |
| } |