1 /* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15 package javassist.bytecode.analysis; 16 17 import java.util.HashMap; 18 import java.util.HashSet; 19 import java.util.Map; 20 import java.util.Set; 21 22 import javassist.bytecode.BadBytecode; 23 import javassist.bytecode.CodeAttribute; 24 import javassist.bytecode.CodeIterator; 25 import javassist.bytecode.ExceptionTable; 26 import javassist.bytecode.MethodInfo; 27 import javassist.bytecode.Opcode; 28 29 /** 30 * Discovers the subroutines in a method, and tracks all callers. 31 * 32 * @author Jason T. Greene 33 */ 34 public class SubroutineScanner implements Opcode { 35 36 private Subroutine[] subroutines; 37 Map subTable = new HashMap(); 38 Set done = new HashSet(); 39 40 41 public Subroutine[] scan(MethodInfo method) throws BadBytecode { 42 CodeAttribute code = method.getCodeAttribute(); 43 CodeIterator iter = code.iterator(); 44 45 subroutines = new Subroutine[code.getCodeLength()]; 46 subTable.clear(); 47 done.clear(); 48 49 scan(0, iter, null); 50 51 ExceptionTable exceptions = code.getExceptionTable(); 52 for (int i = 0; i < exceptions.size(); i++) { 53 int handler = exceptions.handlerPc(i); 54 // If an exception is thrown in subroutine, the handler 55 // is part of the same subroutine. 56 scan(handler, iter, subroutines[exceptions.startPc(i)]); 57 } 58 59 return subroutines; 60 } 61 62 private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { 63 // Skip already processed blocks 64 if (done.contains(new Integer(pos))) 65 return; 66 67 done.add(new Integer(pos)); 68 69 int old = iter.lookAhead(); 70 iter.move(pos); 71 72 boolean next; 73 do { 74 pos = iter.next(); 75 next = scanOp(pos, iter, sub) && iter.hasNext(); 76 } while (next); 77 78 iter.move(old); 79 } 80 81 private boolean scanOp(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { 82 subroutines[pos] = sub; 83 84 int opcode = iter.byteAt(pos); 85 86 if (opcode == TABLESWITCH) { 87 scanTableSwitch(pos, iter, sub); 88 89 return false; 90 } 91 92 if (opcode == LOOKUPSWITCH) { 93 scanLookupSwitch(pos, iter, sub); 94 95 return false; 96 } 97 98 // All forms of return and throw end current code flow 99 if (Util.isReturn(opcode) || opcode == RET || opcode == ATHROW) 100 return false; 101 102 if (Util.isJumpInstruction(opcode)) { 103 int target = Util.getJumpTarget(pos, iter); 104 if (opcode == JSR || opcode == JSR_W) { 105 Subroutine s = (Subroutine) subTable.get(new Integer(target)); 106 if (s == null) { 107 s = new Subroutine(target, pos); 108 subTable.put(new Integer(target), s); 109 scan(target, iter, s); 110 } else { 111 s.addCaller(pos); 112 } 113 } else { 114 scan(target, iter, sub); 115 116 // GOTO ends current code flow 117 if (Util.isGoto(opcode)) 118 return false; 119 } 120 } 121 122 return true; 123 } 124 125 private void scanLookupSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { 126 int index = (pos & ~3) + 4; 127 // default 128 scan(pos + iter.s32bitAt(index), iter, sub); 129 int npairs = iter.s32bitAt(index += 4); 130 int end = npairs * 8 + (index += 4); 131 132 // skip "match" 133 for (index += 4; index < end; index += 8) { 134 int target = iter.s32bitAt(index) + pos; 135 scan(target, iter, sub); 136 } 137 } 138 139 private void scanTableSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { 140 // Skip 4 byte alignment padding 141 int index = (pos & ~3) + 4; 142 // default 143 scan(pos + iter.s32bitAt(index), iter, sub); 144 int low = iter.s32bitAt(index += 4); 145 int high = iter.s32bitAt(index += 4); 146 int end = (high - low + 1) * 4 + (index += 4); 147 148 // Offset table 149 for (; index < end; index += 4) { 150 int target = iter.s32bitAt(index) + pos; 151 scan(target, iter, sub); 152 } 153 } 154 155 156 } 157