001 package sysModel.classFile.attributes; 002 003 import sysModel.classFile.attributes.visitors.IAttributeVisitor; 004 import sysModel.classFile.code.instructions.LineNumberTable; 005 import sysModel.classFile.constantPool.AUTFPoolInfo; 006 import sysModel.classFile.constantPool.ConstantPool; 007 import sysModel.classFile.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 short maxStack; 022 public short maxLocals; 023 public int codeLength; 024 public short exceptionTableLength; 025 public short attributesCount; 026 027 public CodeProperties(short maxStack, 028 short maxLocals, 029 int codeLength, 030 short exceptionTableLength, 031 short 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 short startPC; 042 public short endPC; 043 public short handlerPC; 044 public short catchType; 045 046 public ExceptionTableEntry(short startPC, short endPC, short handlerPC, short 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, short maxStack, short 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 (null == _props) { 123 short maxStack = Types.ushortFromBytes(_data, 0); 124 short maxLocals = Types.ushortFromBytes(_data, 2); 125 int codeLength = Types.uintFromBytes(_data, 4); 126 short exceptionTableLength = Types.ushortFromBytes(_data, 8 + codeLength); 127 short 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(short maxStack, short maxLocals) { 144 getProperties(); 145 146 Types.bytesFromShort(_props.maxStack = maxStack, _data, 0); 147 Types.bytesFromShort(_props.maxLocals = maxLocals, _data, 2); 148 } 149 150 /** 151 * Return a copy of the code bytes. 152 * 153 * @return code bytes 154 */ 155 public byte[] getCode() { 156 getProperties(); 157 158 byte[] b = new byte[_props.codeLength]; 159 System.arraycopy(_data, 8, b, 0, _props.codeLength); 160 161 return b; 162 } 163 164 /** 165 * Set the code bytes. 166 * 167 * @param code new code bytes 168 */ 169 public void setCode(byte[] code) throws IllegalArgumentException { 170 if (0 == code.length) { 171 throw new IllegalArgumentException("Code block cannot be empty"); 172 } 173 174 getProperties(); 175 176 byte[] newData = new byte[_data.length - _props.codeLength + code.length]; 177 System.arraycopy(_data, 0, newData, 0, 4); 178 Types.bytesFromInt(code.length, newData, 4); 179 System.arraycopy(code, 0, newData, 8, code.length); 180 System.arraycopy(_data, 8 + _props.codeLength, newData, 8 + code.length, _data.length - 8 - _props.codeLength); 181 setData(newData); 182 } 183 184 /** 185 * Return a copy of the exception table entries. 186 * 187 * @return entries 188 */ 189 public ExceptionTableEntry[] getExceptionTableEntries() { 190 getProperties(); 191 192 ExceptionTableEntry[] e = new ExceptionTableEntry[_props.exceptionTableLength]; 193 for(int i = 0, index = 10 + _props.codeLength; i < _props.exceptionTableLength; ++i, index += 8) { 194 short startPC = Types.ushortFromBytes(_data, index); 195 short endPC = Types.ushortFromBytes(_data, index + 2); 196 short handlerPC = Types.ushortFromBytes(_data, index + 4); 197 short catchType = Types.ushortFromBytes(_data, index + 6); 198 e[i] = new ExceptionTableEntry(startPC, endPC, handlerPC, catchType); 199 } 200 201 return e; 202 } 203 204 /** 205 * Set the exception table entries. 206 * 207 * @param e entries 208 * 209 * @throws IllegalArgumentException 210 */ 211 public void setExceptionTableEntries(ExceptionTableEntry[] e) throws IllegalArgumentException { 212 if (0xffff < e.length) { 213 throw new IllegalArgumentException("Too many exception table entries, max=0xffff"); 214 } 215 216 getProperties(); 217 218 byte[] newData = new byte[_data.length - 8 * _props.exceptionTableLength + 8 * e.length]; 219 System.arraycopy(_data, 0, newData, 0, 8 + _props.codeLength); 220 Types.bytesFromShort((short)e.length, newData, 8 + _props.codeLength); 221 222 for(int i = 0; i < e.length; ++i) { 223 Types.bytesFromShort(e[i].startPC, newData, 10 + _props.codeLength + i * 8); 224 Types.bytesFromShort(e[i].endPC, newData, 12 + _props.codeLength + i * 8); 225 Types.bytesFromShort(e[i].handlerPC, newData, 14 + _props.codeLength + i * 8); 226 Types.bytesFromShort(e[i].catchType, newData, 16 + _props.codeLength + i * 8); 227 } 228 229 System.arraycopy(_data, 230 10 + _props.codeLength + 8 * _props.exceptionTableLength, 231 newData, 232 10 + _props.codeLength + 8 * e.length, 233 _data.length - 10 - _props.codeLength - 8 * _props.exceptionTableLength); 234 setData(newData); 235 } 236 237 /** 238 * Return a copy of the attributes. 239 * 240 * @return attributes 241 * 242 * @throws ClassFormatError 243 */ 244 public AAttributeInfo[] getAttributes() throws ClassFormatError { 245 getProperties(); 246 247 AAttributeInfo[] a = new AAttributeInfo[_props.attributesCount]; 248 byte[] b = new byte[_data.length - 12 - _props.codeLength - 8 * _props.exceptionTableLength]; 249 System.arraycopy(_data, 250 12 + _props.codeLength + 8 * _props.exceptionTableLength, 251 b, 252 0, 253 b.length); 254 try { 255 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(b)); 256 for(int i = 0; i < _props.attributesCount; ++i) { 257 a[i] = AAttributeInfo.read(dis, _constantPool); 258 } 259 } 260 catch(IOException e) { 261 throw new ClassFormatError("Could not get code attributes: "+e); 262 } 263 264 return a; 265 } 266 267 /** 268 * Sets the attribute list. 269 * 270 * @param a attributes 271 * 272 * @throws ClassFormatError 273 * @throws IllegalArgumentException 274 */ 275 public void setAttributes(AAttributeInfo[] a) throws ClassFormatError, IllegalArgumentException { 276 if (0xffff < a.length) { 277 throw new IllegalArgumentException("Too many atttributes, max=0xffff"); 278 } 279 280 getProperties(); 281 282 byte[] newData = new byte[0]; 283 try { 284 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 285 DataOutputStream dos = new DataOutputStream(baos); 286 for(AAttributeInfo attr : a) { 287 attr.write(dos); 288 } 289 290 byte[] attr = baos.toByteArray(); 291 newData = new byte[12 + _props.codeLength + 8 * _props.exceptionTableLength + attr.length]; 292 System.arraycopy(_data, 0, newData, 0, 10 + _props.codeLength + 8 * _props.exceptionTableLength); 293 Types.bytesFromShort((short)a.length, newData, 10 + _props.codeLength + 8 * _props.exceptionTableLength); 294 System.arraycopy(attr, 0, newData, newData.length - attr.length, attr.length); 295 } 296 catch(IOException e) { 297 throw new ClassFormatError("Cannot set code attributes: "+e); 298 } 299 300 setData(newData); 301 } 302 303 /** 304 * Execute a visitor on this attribute. 305 * 306 * @param visitor visitor 307 * @param param visitor-specific parameter 308 * 309 * @return visitor-specific return value 310 */ 311 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) { 312 return visitor.codeCase(this, param); 313 } 314 315 /** 316 * Return a human-readable version of this attribute. 317 * 318 * @return string 319 */ 320 public String toString() { 321 getProperties(); 322 StringBuffer x = new StringBuffer(); 323 x.append("Code <" + _data.length + " bytes, " + _props.maxStack + " max stack, " + 324 _props.maxLocals + " max locals, " + _props.codeLength + " code bytes, " + 325 _props.exceptionTableLength + " exception table entries { "); 326 327 for (ExceptionTableEntry e: getExceptionTableEntries()) { 328 x.append("(pc="+e.startPC+".."+e.endPC+" handler="+e.handlerPC+" type="+e.catchType+") "); 329 } 330 331 x.append("}, " + _props.attributesCount + " attributes = { "); 332 333 boolean first = true; 334 for(AAttributeInfo a : getAttributes()) { 335 if (first) { 336 first = false; 337 } 338 else { 339 x.append(", "); 340 } 341 x.append(a.toString()); 342 } 343 344 x.append("} >"); 345 346 return x.toString(); 347 } 348 349 /** 350 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them. 351 * 352 * NOTE: This does not transform branch targets in the code. 353 * 354 * @param startPC program counter to start at 355 * @param deltaPC change in program counter values 356 */ 357 public void adjustPC(short startPC, short deltaPC) { 358 // adjust PCs in exception table 359 ExceptionTableEntry[] exceptions = getExceptionTableEntries(); 360 for(ExceptionTableEntry exc : exceptions) { 361 if (exc.startPC >= startPC) { 362 // Debug.out.println("Adjusted CodeAttribute.exceptions[].startPC: "+exc.startPC+" --> "+(exc.startPC+deltaPC)); 363 exc.startPC += deltaPC; 364 } 365 if (exc.endPC >= startPC) { 366 // Debug.out.println("Adjusted CodeAttribute.exceptions[].endPC: "+exc.endPC+" --> "+(exc.endPC+deltaPC)); 367 exc.endPC += deltaPC; 368 } 369 if (exc.handlerPC >= startPC) { 370 // Debug.out.println("Adjusted CodeAttribute.exceptions[].handlerPC: "+exc.handlerPC+" --> "+(exc.handlerPC+deltaPC)); 371 exc.handlerPC += deltaPC; 372 } 373 } 374 setExceptionTableEntries(exceptions); 375 376 // recursively adjust PC in all contained attributes 377 AAttributeInfo[] attributes = getAttributes(); 378 for(AAttributeInfo attr : attributes) { 379 attr.adjustPC(startPC, deltaPC); 380 } 381 setAttributes(attributes); 382 } 383 384 /** 385 * Translate the program counter values contained in this attribute from an old line number table to a new one. 386 * 387 * @param index critical point (insertion or deletion point) 388 * @param deltaIndex delta value to add to all old line numbers greater than the critical point 389 * @param oldLnt old line number table 390 * @param newLnt new line number table 391 */ 392 public void translatePC(short index, short deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) { 393 // adjust PCs in exception table 394 ExceptionTableEntry[] exceptions = getExceptionTableEntries(); 395 for(ExceptionTableEntry exc : exceptions) { 396 int oldLineNo = oldLnt.getLineNumber(exc.startPC); 397 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 398 exc.startPC = (short)newLnt.getPC(oldLineNo); 399 400 oldLineNo = oldLnt.getLineNumber(exc.endPC); 401 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 402 exc.endPC = (short)newLnt.getPC(oldLineNo); 403 404 oldLineNo = oldLnt.getLineNumber(exc.handlerPC); 405 oldLineNo += (oldLineNo > index) ? deltaIndex : 0; 406 exc.handlerPC = (short)newLnt.getPC(oldLineNo); 407 } 408 setExceptionTableEntries(exceptions); 409 410 // recursively adjust PC in all contained attributes 411 AAttributeInfo[] attributes = getAttributes(); 412 for(AAttributeInfo attr : attributes) { 413 attr.translatePC(index, deltaIndex, oldLnt, newLnt); 414 } 415 setAttributes(attributes); 416 } 417 418 /** 419 * Creates and returns a copy of this object. 420 */ 421 public Object clone() throws CloneNotSupportedException { 422 return super.clone(); 423 } 424 425 /** 426 * Returns the name of the attribute as it appears in the class file. 427 * @return name of the attribute. 428 */ 429 public static String getAttributeName() { 430 return "Code"; 431 } 432 }