1 cananian 1.9.2.11 // Code.java, created Sun Sep 13 22:49:20 1998 by cananian 2 cananian 1.6 // Copyright (C) 1998 C. Scott Ananian <cananian@alumni.princeton.edu> 3 cananian 1.6 // Licensed under the terms of the GNU GPL; see COPYING for details. 4 cananian 1.1 package harpoon.IR.Bytecode; 5 cananian 1.1 6 cananian 1.9.2.6 import harpoon.ClassFile.HClass; 7 cananian 1.9.2.6 import harpoon.ClassFile.HCode; 8 cananian 1.9.2.14 import harpoon.ClassFile.HCodeAndMaps; 9 cananian 1.9.2.6 import harpoon.ClassFile.HCodeElement; 10 cananian 1.9.2.7 import harpoon.ClassFile.HCodeFactory; 11 cananian 1.9.2.6 import harpoon.ClassFile.HMethod; 12 cananian 1.9.2.12 import harpoon.ClassFile.Linker; 13 cananian 1.9.2.5 import harpoon.IR.RawClass.MethodInfo; 14 cananian 1.9.2.5 import harpoon.IR.RawClass.AttributeCode; 15 cananian 1.9.2.5 import harpoon.IR.RawClass.AttributeLineNumberTable; 16 cananian 1.9.2.5 import harpoon.IR.RawClass.LineNumberTable; 17 cananian 1.9.2.5 import harpoon.IR.RawClass.Constant; 18 cananian 1.9.2.14 import harpoon.Temp.TempMap; 19 cananian 1.14 import harpoon.Util.ArrayFactory; 20 cananian 1.14 import harpoon.Util.Collections.Graph; 21 cananian 1.1 import harpoon.Util.Util; 22 cananian 1.1 23 cananian 1.14 import java.util.AbstractSet; 24 cananian 1.9.2.9 import java.util.ArrayList; 25 cananian 1.9.2.9 import java.util.Collections; 26 cananian 1.5 import java.util.Enumeration; 27 cananian 1.9.2.9 import java.util.HashSet; 28 cananian 1.9.2.9 import java.util.List; 29 cananian 1.9.2.9 import java.util.Iterator; 30 cananian 1.9.2.14 import java.util.Map; 31 cananian 1.9.2.9 import java.util.Set; 32 cananian 1.1 /** 33 cananian 1.1 * <code>Bytecode.Code</code> is a code view that exposes the 34 cananian 1.1 * raw java classfile bytecodes. 35 cananian 1.1 * 36 cananian 1.1 * @author C. Scott Ananian <cananian@alumni.princeton.edu> 37 cananian 1.14 * @version $Id: Code.java,v 1.14 2003/05/09 21:08:01 cananian Exp $ 38 cananian 1.1 * @see harpoon.ClassFile.HCode 39 cananian 1.1 */ 40 cananian 1.14 public class Code extends HCode implements Graph<Instr,InstrEdge> { 41 cananian 1.9.2.2 /** The name of this code view. */ 42 cananian 1.9.2.2 public static final String codename = "bytecode"; 43 cananian 1.1 44 cananian 1.9.2.12 final Linker linker; 45 cananian 1.9.2.12 final HMethod parent; 46 cananian 1.9.2.12 final MethodInfo methodinfo; 47 cananian 1.1 48 cananian 1.1 /** Constructor. */ 49 cananian 1.1 public Code(HMethod parent, MethodInfo methodinfo) { 50 cananian 1.9.2.12 this.linker = parent.getDeclaringClass().getLinker(); 51 cananian 1.1 this.parent = parent; 52 cananian 1.1 this.methodinfo = methodinfo; 53 cananian 1.8 } 54 cananian 1.8 /** Clone this code representation. The clone has its own copy of the 55 cananian 1.8 * bytecode graph. */ 56 cananian 1.9.2.14 public HCodeAndMaps clone(HMethod newMethod) { 57 cananian 1.9.2.14 final HCode cloned = new Code(newMethod, methodinfo); 58 cananian 1.9.2.15 return new HCodeAndMaps(cloned, null, null, this, null, null); 59 cananian 1.1 } 60 cananian 1.1 61 cananian 1.1 /** 62 cananian 1.1 * Return the <code>HMethod</code> this codeview 63 cananian 1.1 * belongs to. 64 cananian 1.1 */ 65 cananian 1.1 public HMethod getMethod() { return parent; } 66 cananian 1.1 67 cananian 1.1 /** 68 cananian 1.1 * Return the name of this code view, <code>"bytecode"</code>. 69 cananian 1.1 * @return the string <code>"bytecode"</code>. 70 cananian 1.1 */ 71 cananian 1.9.2.2 public String getName() { return codename; } 72 cananian 1.1 73 cananian 1.1 /** 74 cananian 1.1 * Return an ordered list of the <code>Bytecode.Instr</code>s 75 cananian 1.1 * making up this code view. The first instruction to be 76 cananian 1.1 * executed is in element 0 of the array. 77 cananian 1.1 */ 78 cananian 1.14 public List<Instr> getElementsL() { 79 cananian 1.1 if (elements==null) { 80 cananian 1.9.2.9 if (getCode()==null) return Collections.EMPTY_LIST; // no elements. 81 cananian 1.1 String sf = parent.getDeclaringClass().getSourceFile(); // source file. 82 cananian 1.1 byte[] code = getCode().code; // bytecode array. 83 cananian 1.1 // First locate merge nodes. 84 cananian 1.9.2.8 int[] merge = new int[code.length+1]; // init to 0. 85 cananian 1.7 merge[0]++; // the first instruction is reachable from outside. 86 cananian 1.1 for (int pc=0; pc<code.length; pc+=Op.instrSize(code, pc)) { 87 cananian 1.1 // if its a branch, mark the possible targets. 88 cananian 1.1 if (Op.isBranch(code[pc])) { 89 cananian 1.1 int[] targets = Op.branchTargets(code, pc); 90 cananian 1.1 for (int i=0; i<targets.length; i++) 91 cananian 1.1 merge[targets[i]]++; // mark a jump to this pc. 92 cananian 1.1 } 93 cananian 1.1 // unless its an unconditional branch, we can fall through to the 94 cananian 1.1 // next instr, too. Note that we shouldn't be able to fall off 95 cananian 1.1 // the end of the method. 96 cananian 1.1 if ((!Op.isUnconditionalBranch(code[pc])) || 97 cananian 1.1 Op.isJSR(code[pc])) // jsrs eventually return to next instr, too. 98 cananian 1.1 merge[pc+Op.instrSize(code, pc)]++; 99 cananian 1.1 } 100 cananian 1.1 // try handlers count as targets, too 101 cananian 1.1 for (int i=0; i<getCode().exception_table.length; i++) 102 cananian 1.1 merge[getCode().exception_table[i].handler_pc]++; 103 cananian 1.1 104 cananian 1.9.2.8 // if merge[code.length]!=0, then execution may run off end of method. 105 cananian 1.9.2.8 if (merge[code.length]>0) 106 cananian 1.9.2.8 System.err.println("WARNING: execution may run off end of "+parent); 107 cananian 1.9.2.8 108 cananian 1.1 // now all pc's for which merge>1 are merge nodes. 109 cananian 1.1 Instr[] sparse = new Instr[code.length]; // index by pc still. 110 cananian 1.1 // crank through and add instrs without making links. 111 cananian 1.14 List<Instr> v = new ArrayList<Instr>(code.length); 112 cananian 1.1 for (int pc=0; pc<code.length; pc+=Op.instrSize(code, pc)) { 113 cananian 1.1 int line = getLine(pc); 114 cananian 1.1 // make merge node if appropriate. 115 cananian 1.1 InMerge m = null; 116 cananian 1.1 if (merge[pc] > 1) 117 cananian 1.9.2.9 v.add(m = new InMerge(sf, line, merge[pc] /*arity*/)); 118 cananian 1.1 // make Instr object for this pc. 119 cananian 1.1 if (Op.isBranch(code[pc])) { 120 cananian 1.1 if (code[pc]==Op.TABLESWITCH || code[pc]==Op.LOOKUPSWITCH) 121 cananian 1.1 sparse[pc] = new InSwitch(sf, line, code, pc); 122 cananian 1.9.2.3 else if (code[pc]==Op.RET) 123 cananian 1.9.2.3 sparse[pc] = new InRet(sf, line, code, pc); 124 cananian 1.1 else 125 cananian 1.1 sparse[pc] = new InCti(sf, line, code, pc); 126 cananian 1.1 } else 127 cananian 1.1 sparse[pc] = new InGen(sf, line, code, pc, this); 128 cananian 1.9.2.9 v.add(sparse[pc]); 129 cananian 1.1 // Daisy-chain merge node if appropriate. 130 cananian 1.1 if (m != null) { 131 cananian 1.1 m.addNext(sparse[pc]); 132 cananian 1.1 sparse[pc].addPrev(m); 133 cananian 1.1 sparse[pc] = m; 134 cananian 1.1 } 135 cananian 1.1 } 136 cananian 1.1 // okay. The instructions are made (in pc order, no less...) 137 cananian 1.1 // link 'em. 138 cananian 1.1 for (int pc=0; pc<code.length; pc+=Op.instrSize(code, pc)) { 139 cananian 1.1 Instr curr = sparse[pc]; 140 cananian 1.1 if (curr instanceof InMerge) 141 cananian 1.9.2.4 curr = ((InMerge)curr).next(0); 142 cananian 1.1 if ((!Op.isUnconditionalBranch(code[pc])) || 143 cananian 1.1 Op.isJSR(code[pc])) { // jsrs return to next instruction eventually 144 cananian 1.9.2.8 // JVM spec is not clear whether 'code can run off end of array' 145 cananian 1.9.2.8 // or not. 146 cananian 1.9.2.8 if ((pc+Op.instrSize(code, pc)) < code.length) { 147 cananian 1.9.2.8 // link to next pc. 148 cananian 1.9.2.8 Instr next = sparse[pc+Op.instrSize(code, pc)]; 149 cananian 1.9.2.8 curr.addNext(next); 150 cananian 1.9.2.8 next.addPrev(curr); 151 cananian 1.9.2.8 } 152 cananian 1.1 } 153 cananian 1.1 if (Op.isBranch(code[pc])) { 154 cananian 1.1 // link to branch targets. 155 cananian 1.1 int[] targets = Op.branchTargets(code, pc); 156 cananian 1.1 for (int i=0; i<targets.length; i++) { 157 cananian 1.1 Instr next = sparse[targets[i]]; 158 cananian 1.1 curr.addNext(next); 159 cananian 1.1 next.addPrev(curr); 160 cananian 1.1 } 161 cananian 1.1 } 162 cananian 1.1 } 163 cananian 1.1 // Make tryBlocks table. 164 cananian 1.9.2.5 harpoon.IR.RawClass.ExceptionTable et[] = 165 cananian 1.1 getCode().exception_table; 166 cananian 1.1 tryBlocks = new ExceptionEntry[et.length]; 167 cananian 1.1 for (int i=0; i<tryBlocks.length; i++) { // for each table entry... 168 cananian 1.1 // Add all the PC's in the try block to a list. 169 cananian 1.14 Set<Instr> uv = new HashSet<Instr>(et[i].end_pc-et[i].start_pc); 170 cananian 1.1 for (int pc=et[i].start_pc; 171 cananian 1.1 pc < et[i].end_pc; 172 cananian 1.9.2.4 pc+=Op.instrSize(code,pc)) { 173 cananian 1.9.2.9 uv.add(sparse[pc]); 174 cananian 1.9.2.4 if (sparse[pc] instanceof InMerge) // merges come in pairs. 175 cananian 1.9.2.9 uv.add(sparse[pc].next(0)); 176 cananian 1.9.2.4 } 177 cananian 1.1 // Make an HClass for the exception caught... 178 cananian 1.1 HClass ex = null; 179 cananian 1.1 if (et[i].catch_type != 0) 180 cananian 1.9.2.12 ex = linker.forDescriptor("L"+et[i].catch_type().name()+";"); 181 cananian 1.1 else 182 cananian 1.2 ex = null; // to indicate 'catch any'. 183 cananian 1.1 // and make the official exception entry. 184 cananian 1.9.2.10 tryBlocks[i] = new ExceptionEntry(i, uv, ex, sparse[et[i].handler_pc]); 185 cananian 1.1 } 186 cananian 1.9.2.9 // Okay. Just trim our list and we're ready to rumble. 187 cananian 1.9.2.9 ((ArrayList)v).trimToSize(); 188 cananian 1.9.2.9 elements = Collections.unmodifiableList(v); 189 cananian 1.1 } 190 cananian 1.9.2.9 return elements; 191 cananian 1.1 } 192 cananian 1.1 /** Cached value of <code>getElements</code>. */ 193 cananian 1.14 private List<Instr> elements = null; 194 cananian 1.1 /** Cached value of <code>getTryBlocks</code> blocks. */ 195 cananian 1.1 private ExceptionEntry[] tryBlocks = null; 196 cananian 1.4 197 cananian 1.14 public List<Instr> getLeafElementsL() { 198 cananian 1.4 if (leaves == null) { 199 cananian 1.14 leaves = new ArrayList<Instr>(); 200 cananian 1.14 for (Iterator<Instr> i = getElementsI(); i.hasNext(); ) { 201 cananian 1.14 Instr in = i.next(); 202 cananian 1.9.2.9 if (in.next.size()==0) 203 cananian 1.9.2.9 leaves.add(in); 204 cananian 1.9.2.9 } 205 cananian 1.9.2.9 ((ArrayList)leaves).trimToSize(); 206 cananian 1.9.2.9 leaves = Collections.unmodifiableList(leaves); 207 cananian 1.4 } 208 cananian 1.9.2.9 return leaves; 209 cananian 1.9.2.9 } 210 cananian 1.14 private List<Instr> leaves = null; 211 cananian 1.9.2.9 212 cananian 1.14 public Instr[] getLeafElements() { 213 cananian 1.14 List<Instr> l = getLeafElementsL(); 214 cananian 1.14 return l.toArray(new Instr[l.size()]); 215 cananian 1.4 } 216 cananian 1.9.2.1 217 cananian 1.9.2.1 // implement elementArrayFactory which returns Instr[]s. 218 cananian 1.14 public ArrayFactory<Instr> elementArrayFactory() { 219 cananian 1.14 return Instr.arrayFactory; 220 cananian 1.14 } 221 cananian 1.14 222 cananian 1.14 // Graph interface 223 cananian 1.14 public Set<Instr> nodes() { 224 cananian 1.14 final List<Instr> l = getElementsL(); 225 cananian 1.14 return new AbstractSet<Instr>() { 226 cananian 1.14 public Iterator<Instr> iterator() { return l.iterator(); } 227 cananian 1.14 public int size() { return l.size(); } 228 cananian 1.14 }; 229 cananian 1.14 } 230 cananian 1.1 231 cananian 1.1 // special non-HCode-mandated access functions. 232 cananian 1.1 /** Get the number of local variables used in this method, including 233 cananian 1.1 * the parameters passed to the method on invocation. */ 234 cananian 1.1 public int getMaxLocals() { return getCode().max_locals; } 235 cananian 1.1 /** Get the maximum number of words on the operand stack at any point 236 cananian 1.1 * during execution of this method. */ 237 cananian 1.1 public int getMaxStack() { return getCode().max_stack; } 238 cananian 1.1 /** Get an array with the try-catch blocks/handlers for this bytecode. */ 239 cananian 1.12 public ExceptionEntry[] getTryBlocks() { getElementsL(); return tryBlocks; } 240 cananian 1.1 241 cananian 1.1 /** Represents exception handlers in this code view. */ 242 cananian 1.9.2.10 public static class ExceptionEntry implements Comparable { 243 cananian 1.9.2.10 int order; // smaller numbers have precedence over higher numbers. 244 cananian 1.14 Set<Instr> tryBlock; 245 cananian 1.1 HClass caughtException; 246 cananian 1.1 Instr handler; 247 cananian 1.9.2.10 ExceptionEntry(int order, 248 cananian 1.14 Set<Instr> tryBlock, HClass caughtException, Instr handler){ 249 cananian 1.9.2.10 this.order = order; 250 cananian 1.1 this.tryBlock = tryBlock; 251 cananian 1.1 this.caughtException = caughtException; 252 cananian 1.1 this.handler = handler; 253 cananian 1.1 } 254 cananian 1.1 public boolean inTry(Instr i) { return tryBlock.contains(i); } 255 cananian 1.1 public HClass caughtException() { return caughtException; } 256 cananian 1.1 public Instr handler() { return handler; } 257 cananian 1.9.2.10 258 cananian 1.9.2.10 public int compareTo(Object o) { 259 cananian 1.9.2.10 int cmp = this.order - ((ExceptionEntry)o).order; 260 cananian 1.9.2.10 if (cmp==0 && !this.equals(o)) // check consistency. 261 cananian 1.9.2.10 throw new ClassCastException("Comparing uncomparable objects"); 262 cananian 1.9.2.10 return cmp; 263 cananian 1.9.2.10 } 264 cananian 1.9.2.10 public String toString() { 265 cananian 1.9.2.10 return "Exception Entry #"+order+" for "+caughtException; 266 cananian 1.9.2.10 } 267 cananian 1.1 } 268 cananian 1.1 269 cananian 1.1 // Utility functions. 270 cananian 1.1 /** Return the Code attribute of this method. */ 271 cananian 1.1 private AttributeCode getCode() { 272 cananian 1.1 if (attrcode==null) { // check cache. 273 cananian 1.1 for (int i=0; i<methodinfo.attributes.length; i++) 274 cananian 1.1 if (methodinfo.attributes[i] instanceof AttributeCode) { 275 cananian 1.1 attrcode = (AttributeCode) methodinfo.attributes[i]; 276 cananian 1.1 break; 277 cananian 1.1 } 278 cananian 1.1 } 279 cananian 1.1 return attrcode; // null if no code attribute was found. 280 cananian 1.1 } 281 cananian 1.1 private AttributeCode attrcode = null; 282 cananian 1.1 /** Look up a constant in the appropriate constant_pool. */ 283 cananian 1.1 public Constant getConstant(int index) { return getCode().constant(index); } 284 cananian 1.1 285 cananian 1.1 286 cananian 1.1 /** Get the line number corresponding to a given pc in the code 287 cananian 1.1 * array. Zero is returned if there is no line number information 288 cananian 1.1 * in the code attribute. */ 289 cananian 1.1 private int getLine(int pc) { 290 cananian 1.1 int lineno = 0; 291 cananian 1.1 AttributeCode code = getCode(); 292 cananian 1.1 for (int i=0; i<code.attributes.length; i++) { 293 cananian 1.1 if (code.attributes[i] instanceof AttributeLineNumberTable) { 294 cananian 1.1 LineNumberTable[] lnt = 295 cananian 1.1 ((AttributeLineNumberTable) code.attributes[i]).line_number_table; 296 cananian 1.1 for (int j=0; j<lnt.length; j++) 297 cananian 1.1 if (lnt[j].start_pc <= pc) 298 cananian 1.1 lineno = lnt[j].line_number; 299 cananian 1.1 else 300 cananian 1.1 break; // start_pc must be uniformly increasing within each table. 301 cananian 1.1 } 302 cananian 1.1 } 303 cananian 1.1 // hopefully this has done it. 304 cananian 1.1 return lineno; 305 cananian 1.9.2.7 } 306 cananian 1.9.2.7 307 cananian 1.9.2.7 /** Return an HCodeFactory for Bytecode form. */ 308 cananian 1.9.2.7 public static HCodeFactory codeFactory() { 309 cananian 1.9.2.12 return harpoon.ClassFile.Loader.systemCodeFactory; 310 cananian 1.1 } 311 cananian 1.1 } 312 cananian 1.1 313 cananian 1.1 // set emacs indentation style. 314 cananian 1.1 // Local Variables: 315 cananian 1.1 // c-basic-offset:2 316 cananian 1.1 // End: