001 package edu.rice.cs.cunit.classFile.attributes; 002 003 import edu.rice.cs.cunit.classFile.attributes.visitors.IAttributeVisitor; 004 import edu.rice.cs.cunit.classFile.code.instructions.LineNumberTable; 005 import edu.rice.cs.cunit.classFile.constantPool.AUTFPoolInfo; 006 import edu.rice.cs.cunit.classFile.constantPool.ConstantPool; 007 import edu.rice.cs.cunit.util.Types; 008 009 import java.io.*; 010 011 /** 012 * Represents the Code attribute in a class file. 013 * 014 * @author Mathias Ricken 015 */ 016 public class CodeAttributeInfo extends AAttributeInfo { 017 /** 018 * Storage class for code properties. 019 */ 020 public static class CodeProperties { 021 public int maxStack; 022 public int maxLocals; 023 public int codeLength; 024 public int exceptionTableLength; 025 public int attributesCount; 026 027 public CodeProperties(int maxStack, 028 int maxLocals, 029 int codeLength, 030 int exceptionTableLength, 031 int attributesCount) { 032 this.maxStack = maxStack; 033 this.maxLocals = maxLocals; 034 this.codeLength = codeLength; 035 this.exceptionTableLength = exceptionTableLength; 036 this.attributesCount = attributesCount; 037 } 038 } 039 040 public static class ExceptionTableEntry { 041 public int startPC; 042 public int endPC; 043 public int handlerPC; 044 public int catchType; 045 046 public ExceptionTableEntry(int startPC, int endPC, int handlerPC, int catchType) { 047 this.startPC = startPC; 048 this.endPC = endPC; 049 this.handlerPC = handlerPC; 050 this.catchType = catchType; 051 } 052 } 053 054 /** 055 * Properties read out from _data field. 056 */ 057 protected CodeProperties _props = null; 058 059 /** 060 * Constructor. 061 * 062 * @param name attribute name 063 * @param data attribute data 064 * @param cp constant pool 065 * 066 * @throws ClassFormatError 067 */ 068 public CodeAttributeInfo(AUTFPoolInfo name, byte[] data, ConstantPool cp) throws ClassFormatError { 069 super(name, data, cp); 070 } 071 072 /** 073 * Constructor. 074 * 075 * @param name attribute name, must be a UTF item "Code" 076 * @param maxStack maximum state size 077 * @param maxLocals maximum number of locals 078 * @param code bytecode 079 * @param exceptionTableEntries exception table entries 080 * @param attributes attributes 081 * @param cp constant pool 082 * 083 * @throws ClassFormatError 084 * @throws IOException 085 */ 086 public CodeAttributeInfo(AUTFPoolInfo name, int maxStack, int maxLocals, byte[] code, 087 ExceptionTableEntry[] exceptionTableEntries, AAttributeInfo[] attributes, 088 ConstantPool cp) throws ClassFormatError, IOException { 089 super(name, null, cp); 090 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 091 DataOutputStream dos = new DataOutputStream(bos); 092 dos.writeShort(maxStack); 093 dos.writeShort(maxLocals); 094 dos.writeInt(0); // no code 095 dos.writeShort(0); // no exceptions 096 dos.writeShort(0); // no attributes 097 setData(bos.toByteArray()); 098 099 setCode(code); 100 setExceptionTableEntries(exceptionTableEntries); 101 setAttributes(attributes); 102 } 103 104 /** 105 * Mutator for data. 106 * 107 * @param data data 108 */ 109 public void setData(byte[] data) { 110 _props = null; 111 super.setData(data); 112 } 113 114 /** 115 * Return a copy of the code properties. 116 * 117 * @return code properties 118 * 119 * @throws ClassFormatError 120 */ 121 public CodeProperties getProperties() throws ClassFormatError { 122 if (_props == null) { 123 int maxStack = Types.ushortFromBytes(_data, 0); 124 int maxLocals = Types.ushortFromBytes(_data, 2); 125 int codeLength = Types.uintFromBytes(_data, 4); 126 int exceptionTableLength = Types.ushortFromBytes(_data, 8 + codeLength); 127 int attributesCount = Types.ushortFromBytes(_data, 10 + codeLength + 8 * exceptionTableLength); 128 _props = new CodeProperties(maxStack, maxLocals, codeLength, exceptionTableLength, attributesCount); 129 } 130 131 return new CodeProperties(_props.maxStack, _props.maxLocals, _props.codeLength, _props.exceptionTableLength, 132 _props.attributesCount); 133 } 134 135 /** 136 * Set the code properties. 137 * 138 * @param maxStack new maximum state 139 * @param maxLocals new maximum locals 140 * 141 * @throws ClassFormatError 142 */ 143 public void setProperties(int maxStack, int maxLocals) { 144 assert(maxStack <= 0xffff); 145 assert(maxLocals <= 0xffff); 146 147 getProperties(); 148 149 _props.maxStack = maxStack; 150 _props.maxLocals = maxLocals; 151 Types.bytesFromShort((short)(maxStack & 0xffff), _data, 0); 152 Types.bytesFromShort((short)(maxLocals & 0xffff), _data, 2); 153 } 154 155 /** 156 * Return a copy of the code bytes. 157 * 158 * @return code bytes 159 */ 160 public byte[] getCode() { 161 getProperties(); 162 163 byte[] b = new byte[_props.codeLength]; 164 System.arraycopy(_data, 8, b, 0, _props.codeLength); 165 166 return b; 167 } 168 169 /** 170 * Set the code bytes. 171 * 172 * @param code new code bytes 173 */ 174 public void setCode(byte[] code) throws IllegalArgumentException { 175 if (code.length == 0) { 176 throw new IllegalArgumentException("Code block cannot be empty"); 177 } 178 179 getProperties(); 180 181 byte[] newData = new byte[_data.length - _props.codeLength + code.length]; 182 System.arraycopy(_data, 0, newData, 0, 4); 183 Types.bytesFromInt(code.length, newData, 4); 184 System.arraycopy(code, 0, newData, 8, code.length); 185 System.arraycopy(_data, 8 + _props.codeLength, newData, 8 + code.length, _data.length - 8 - _props.codeLength); 186 setData(newData); 187 } 188 189 /** 190 * Return a copy of the exception table entries. 191 * 192 * @return entries 193 */ 194 public ExceptionTableEntry[] getExceptionTableEntries() { 195 getProperties(); 196 197 ExceptionTableEntry[] e = new ExceptionTableEntry[_props.exceptionTableLength]; 198 for(int i = 0, index = 10 + _props.codeLength; i < _props.exceptionTableLength; ++i, index += 8) { 199 int startPC = Types.ushortFromBytes(_data, index); 200 int endPC = Types.ushortFromBytes(_data, index + 2); 201 int handlerPC = Types.ushortFromBytes(_data, index + 4); 202 int catchType = Types.ushortFromBytes(_data, index + 6); 203 e[i] = new ExceptionTableEntry(startPC, endPC, handlerPC, catchType); 204 } 205 206 return e; 207 } 208 209 /** 210 * Set the exception table entries. 211 * 212 * @param e entries 213 * 214 * @throws IllegalArgumentException 215 */ 216 public void setExceptionTableEntries(ExceptionTableEntry[] e) throws IllegalArgumentException { 217 if (e.length > 0xffff) { 218 throw new IllegalArgumentException("Too many exception table entries, max=0xffff"); 219 } 220 221 getProperties(); 222 223 byte[] newData = new byte[_data.length - 8 * _props.exceptionTableLength + 8 * e.length]; 224 System.arraycopy(_data, 0, newData, 0, 8 + _props.codeLength); 225 Types.bytesFromShort((short)e.length, newData, 8 + _props.codeLength); 226 227 for(int i = 0; i < e.length; ++i) { 228 Types.bytesFromShort((short)(e[i].startPC & 0xffff), newData, 10 + _props.codeLength + i * 8); 229 Types.bytesFromShort((short)(e[i].endPC & 0xffff), newData, 12 + _props.codeLength + i * 8); 230 Types.bytesFromShort((short)(e[i].handlerPC & 0xffff), newData, 14 + _props.codeLength + i * 8); 231 Types.bytesFromShort((short)(e[i].catchType & 0xffff), newData, 16 + _props.codeLength + i * 8); 232 } 233 234 System.arraycopy(_data, 235 10 + _props.codeLength + 8 * _props.exceptionTableLength, 236 newData, 237 10 + _props.codeLength + 8 * e.length, 238 _data.length - 10 - _props.codeLength - 8 * _props.exceptionTableLength); 239 setData(newData); 240 } 241 242 /** 243 * Return a copy of the attributes. 244 * 245 * @return attributes 246 * 247 * @throws ClassFormatError 248 */ 249 public AAttributeInfo[] getAttributes() throws ClassFormatError { 250 getProperties(); 251 252 AAttributeInfo[] a = new AAttributeInfo[_props.attributesCount]; 253 byte[] b = new byte[_data.length - 12 - _props.codeLength - 8 * _props.exceptionTableLength]; 254 System.arraycopy(_data, 255 12 + _props.codeLength + 8 * _props.exceptionTableLength, 256 b, 257 0, 258 b.length); 259 try { 260 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(b)); 261 for(int i = 0; i < _props.attributesCount; ++i) { 262 a[i] = AAttributeInfo.read(dis, _constantPool); 263 } 264 } 265 catch(IOException e) { 266 throw new ClassFormatError("Could not get code attributes"); 267 } 268 269 return a; 270 } 271 272 /** 273 * Sets the attribute list. 274 * 275 * @param a attributes 276 * 277 * @throws ClassFormatError 278 * @throws IllegalArgumentException 279 */ 280 public void setAttributes(AAttributeInfo[] a) throws ClassFormatError, IllegalArgumentException { 281 if (a.length > 0xffff) { 282 throw new IllegalArgumentException("Too many atttributes, max=0xffff"); 283 } 284 285 getProperties(); 286 287 byte[] newData = new byte[0]; 288 try { 289 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 290 DataOutputStream dos = new DataOutputStream(baos); 291 for(AAttributeInfo attr : a) { 292 attr.write(dos); 293 } 294 295 byte[] attr = baos.toByteArray(); 296 newData = new byte[12 + _props.codeLength + 8 * _props.exceptionTableLength + attr.length]; 297 System.arraycopy(_data, 0, newData, 0, 10 + _props.codeLength + 8 * _props.exceptionTableLength); 298 Types.bytesFromShort((short)a.length, newData, 10 + _props.codeLength + 8 * _props.exceptionTableLength); 299 System.arraycopy(attr, 0, newData, newData.length - attr.length, attr.length); 300 } 301 catch(IOException e) { 302 throw new ClassFormatError("Cannot set code attributes"); 303 } 304 305 setData(newData); 306 } 307 308 /** 309 * Execute a visitor on this attribute. 310 * 311 * @param visitor visitor 312 * @param param visitor-specific parameter 313 * 314 * @return visitor-specific return value 315 */ 316 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) { 317 return visitor.codeCase(this, param); 318 } 319 320 /** 321 * Return a human-readable version of this attribute. 322 * 323 * @return string 324 */ 325 public String toString() { 326 getProperties(); 327 StringBuilder x = new StringBuilder(); 328 x.append("Code <" + _data.length + " bytes, " + _props.maxStack + " max stack, " + 329 _props.maxLocals + " max locals, " + _props.codeLength + " code bytes, " + 330 _props.exceptionTableLength + " exception table entries { "); 331 332 for (ExceptionTableEntry e: getExceptionTableEntries()) { 333 x.append("(pc="+e.startPC+".."+e.endPC+" handler="+e.handlerPC+" type="+e.catchType+") "); 334 } 335 336 x.append("}, " + _props.attributesCount + " attributes = { "); 337 338 boolean first = true; 339 for(AAttributeInfo a : getAttributes()) { 340 if (first) { 341 first = false; 342 } 343 else { 344 x.append(", "); 345 } 346 x.append(a.toString()); 347 } 348 349 x.append("} >"); 350 351 return x.toString(); 352 } 353 354 /** 355 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them. 356 * 357 * NOTE: This does not transform branch targets in the code. 358 * 359 * @param startPC program counter to start at 360 * @param deltaPC change in program counter values 361 */ 362 public void adjustPC(int startPC, int deltaPC) { 363 // adjust PCs in exception table 364 ExceptionTableEntry[] exceptions = getExceptionTableEntries(); 365 for(ExceptionTableEntry exc : exceptions) { 366 if (exc.startPC >= startPC) { 367 // Debug.out.println("Adjusted CodeAttribute.exceptions[].startPC: "+exc.startPC+" --> "+(exc.startPC+deltaPC)); 368 exc.startPC += deltaPC; 369 } 370 if (exc.endPC >= startPC) { 371 // Debug.out.println("Adjusted CodeAttribute.exceptions[].endPC: "+exc.endPC+" --> "+(exc.endPC+deltaPC)); 372 exc.endPC += deltaPC; 373 } 374 if (exc.handlerPC >= startPC) { 375 // Debug.out.println("Adjusted CodeAttribute.exceptions[].handlerPC: "+exc.handlerPC+" --> "+(exc.handlerPC+deltaPC)); 376 exc.handlerPC += deltaPC; 377 } 378 } 379 setExceptionTableEntries(exceptions); 380 381 // recursively adjust PC in all contained attributes 382 AAttributeInfo[] attributes = getAttributes(); 383 for(AAttributeInfo attr : attributes) { 384 attr.adjustPC(startPC, deltaPC); 385 } 386 setAttributes(attributes); 387 } 388 389 /** 390 * Translate the program counter values contained in this attribute from an old line number table to a new one. 391 * 392 * @param index critical point (insertion or deletion point) 393 * @param deltaIndex delta value to add to all old line numbers greater than the critical point 394 * @param oldLnt old line number table 395 * @param newLnt new line number table 396 */ 397 public void translatePC(int index, int deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) { 398 // adjust PCs in exception table 399 ExceptionTableEntry[] exceptions = getExceptionTableEntries(); 400 for(ExceptionTableEntry exc : exceptions) { 401 int oldLineNo = oldLnt.getLineNumber(exc.startPC); 402 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 403 exc.startPC = newLnt.getPC(oldLineNo); 404 405 oldLineNo = oldLnt.getLineNumber(exc.endPC); 406 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 407 exc.endPC = newLnt.getPC(oldLineNo); 408 409 oldLineNo = oldLnt.getLineNumber(exc.handlerPC); 410 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 411 exc.handlerPC = newLnt.getPC(oldLineNo); 412 } 413 setExceptionTableEntries(exceptions); 414 415 // recursively adjust PC in all contained attributes 416 AAttributeInfo[] attributes = getAttributes(); 417 for(AAttributeInfo attr : attributes) { 418 attr.translatePC(index, deltaIndex, oldLnt, newLnt); 419 } 420 setAttributes(attributes); 421 } 422 423 /** 424 * Creates and returns a copy of this object. 425 */ 426 public Object clone() throws CloneNotSupportedException { 427 return super.clone(); 428 } 429 430 /** 431 * Returns the name of the attribute as it appears in the class file. 432 * @return name of the attribute. 433 */ 434 public static String getAttributeName() { 435 return "Code"; 436 } 437 }