1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. 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 16package javassist.bytecode; 17 18import java.io.DataInputStream; 19import java.io.DataOutputStream; 20import java.io.IOException; 21import java.util.List; 22import java.util.ArrayList; 23import java.util.Iterator; 24import java.util.Map; 25 26/** 27 * <code>Code_attribute</code>. 28 * 29 * <p>To browse the <code>code</code> field of 30 * a <code>Code_attribute</code> structure, 31 * use <code>CodeIterator</code>. 32 * 33 * @see CodeIterator 34 */ 35public class CodeAttribute extends AttributeInfo implements Opcode { 36 /** 37 * The name of this attribute <code>"Code"</code>. 38 */ 39 public static final String tag = "Code"; 40 41 // code[] is stored in AttributeInfo.info. 42 43 private int maxStack; 44 private int maxLocals; 45 private ExceptionTable exceptions; 46 private ArrayList attributes; 47 48 /** 49 * Constructs a <code>Code_attribute</code>. 50 * 51 * @param cp constant pool table 52 * @param stack <code>max_stack</code> 53 * @param locals <code>max_locals</code> 54 * @param code <code>code[]</code> 55 * @param etable <code>exception_table[]</code> 56 */ 57 public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code, 58 ExceptionTable etable) 59 { 60 super(cp, tag); 61 maxStack = stack; 62 maxLocals = locals; 63 info = code; 64 exceptions = etable; 65 attributes = new ArrayList(); 66 } 67 68 /** 69 * Constructs a copy of <code>Code_attribute</code>. 70 * Specified class names are replaced during the copy. 71 * 72 * @param cp constant pool table. 73 * @param src source Code attribute. 74 * @param classnames pairs of replaced and substituted 75 * class names. 76 */ 77 private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames) 78 throws BadBytecode 79 { 80 super(cp, tag); 81 82 maxStack = src.getMaxStack(); 83 maxLocals = src.getMaxLocals(); 84 exceptions = src.getExceptionTable().copy(cp, classnames); 85 attributes = new ArrayList(); 86 List src_attr = src.getAttributes(); 87 int num = src_attr.size(); 88 for (int i = 0; i < num; ++i) { 89 AttributeInfo ai = (AttributeInfo)src_attr.get(i); 90 attributes.add(ai.copy(cp, classnames)); 91 } 92 93 info = src.copyCode(cp, classnames, exceptions, this); 94 } 95 96 CodeAttribute(ConstPool cp, int name_id, DataInputStream in) 97 throws IOException 98 { 99 super(cp, name_id, (byte[])null); 100 int attr_len = in.readInt(); 101 102 maxStack = in.readUnsignedShort(); 103 maxLocals = in.readUnsignedShort(); 104 105 int code_len = in.readInt(); 106 info = new byte[code_len]; 107 in.readFully(info); 108 109 exceptions = new ExceptionTable(cp, in); 110 111 attributes = new ArrayList(); 112 int num = in.readUnsignedShort(); 113 for (int i = 0; i < num; ++i) 114 attributes.add(AttributeInfo.read(cp, in)); 115 } 116 117 /** 118 * Makes a copy. Class names are replaced according to the 119 * given <code>Map</code> object. 120 * 121 * @param newCp the constant pool table used by the new copy. 122 * @param classnames pairs of replaced and substituted 123 * class names. 124 * @exception RuntimeCopyException if a <code>BadBytecode</code> 125 * exception is thrown, it is 126 * converted into 127 * <code>RuntimeCopyException</code>. 128 * 129 * @return <code>CodeAttribute</code> object. 130 */ 131 public AttributeInfo copy(ConstPool newCp, Map classnames) 132 throws RuntimeCopyException 133 { 134 try { 135 return new CodeAttribute(newCp, this, classnames); 136 } 137 catch (BadBytecode e) { 138 throw new RuntimeCopyException("bad bytecode. fatal?"); 139 } 140 } 141 142 /** 143 * An exception that may be thrown by <code>copy()</code> 144 * in <code>CodeAttribute</code>. 145 */ 146 public static class RuntimeCopyException extends RuntimeException { 147 /** 148 * Constructs an exception. 149 */ 150 public RuntimeCopyException(String s) { 151 super(s); 152 } 153 } 154 155 /** 156 * Returns the length of this <code>attribute_info</code> 157 * structure. 158 * The returned value is <code>attribute_length + 6</code>. 159 */ 160 public int length() { 161 return 18 + info.length + exceptions.size() * 8 162 + AttributeInfo.getLength(attributes); 163 } 164 165 void write(DataOutputStream out) throws IOException { 166 out.writeShort(name); // attribute_name_index 167 out.writeInt(length() - 6); // attribute_length 168 out.writeShort(maxStack); // max_stack 169 out.writeShort(maxLocals); // max_locals 170 out.writeInt(info.length); // code_length 171 out.write(info); // code 172 exceptions.write(out); 173 out.writeShort(attributes.size()); // attributes_count 174 AttributeInfo.writeAll(attributes, out); // attributes 175 } 176 177 /** 178 * This method is not available. 179 * 180 * @throws java.lang.UnsupportedOperationException always thrown. 181 */ 182 public byte[] get() { 183 throw new UnsupportedOperationException("CodeAttribute.get()"); 184 } 185 186 /** 187 * This method is not available. 188 * 189 * @throws java.lang.UnsupportedOperationException always thrown. 190 */ 191 public void set(byte[] newinfo) { 192 throw new UnsupportedOperationException("CodeAttribute.set()"); 193 } 194 195 void renameClass(String oldname, String newname) { 196 AttributeInfo.renameClass(attributes, oldname, newname); 197 } 198 199 void renameClass(Map classnames) { 200 AttributeInfo.renameClass(attributes, classnames); 201 } 202 203 void getRefClasses(Map classnames) { 204 AttributeInfo.getRefClasses(attributes, classnames); 205 } 206 207 /** 208 * Returns the name of the class declaring the method including 209 * this code attribute. 210 */ 211 public String getDeclaringClass() { 212 ConstPool cp = getConstPool(); 213 return cp.getClassName(); 214 } 215 216 /** 217 * Returns <code>max_stack</code>. 218 */ 219 public int getMaxStack() { 220 return maxStack; 221 } 222 223 /** 224 * Sets <code>max_stack</code>. 225 */ 226 public void setMaxStack(int value) { 227 maxStack = value; 228 } 229 230 /** 231 * Computes the maximum stack size and sets <code>max_stack</code> 232 * to the computed size. 233 * 234 * @throws BadBytecode if this method fails in computing. 235 * @return the newly computed value of <code>max_stack</code> 236 */ 237 public int computeMaxStack() throws BadBytecode { 238 maxStack = new CodeAnalyzer(this).computeMaxStack(); 239 return maxStack; 240 } 241 242 /** 243 * Returns <code>max_locals</code>. 244 */ 245 public int getMaxLocals() { 246 return maxLocals; 247 } 248 249 /** 250 * Sets <code>max_locals</code>. 251 */ 252 public void setMaxLocals(int value) { 253 maxLocals = value; 254 } 255 256 /** 257 * Returns <code>code_length</code>. 258 */ 259 public int getCodeLength() { 260 return info.length; 261 } 262 263 /** 264 * Returns <code>code[]</code>. 265 */ 266 public byte[] getCode() { 267 return info; 268 } 269 270 /** 271 * Sets <code>code[]</code>. 272 */ 273 void setCode(byte[] newinfo) { super.set(newinfo); } 274 275 /** 276 * Makes a new iterator for reading this code attribute. 277 */ 278 public CodeIterator iterator() { 279 return new CodeIterator(this); 280 } 281 282 /** 283 * Returns <code>exception_table[]</code>. 284 */ 285 public ExceptionTable getExceptionTable() { return exceptions; } 286 287 /** 288 * Returns <code>attributes[]</code>. 289 * It returns a list of <code>AttributeInfo</code>. 290 * A new element can be added to the returned list 291 * and an existing element can be removed from the list. 292 * 293 * @see AttributeInfo 294 */ 295 public List getAttributes() { return attributes; } 296 297 /** 298 * Returns the attribute with the specified name. 299 * If it is not found, this method returns null. 300 * 301 * @param name attribute name 302 * @return an <code>AttributeInfo</code> object or null. 303 */ 304 public AttributeInfo getAttribute(String name) { 305 return AttributeInfo.lookup(attributes, name); 306 } 307 308 /** 309 * Adds a stack map table. If another copy of stack map table 310 * is already contained, the old one is removed. 311 * 312 * @param smt the stack map table added to this code attribute. 313 * If it is null, a new stack map is not added. 314 * Only the old stack map is removed. 315 */ 316 public void setAttribute(StackMapTable smt) { 317 AttributeInfo.remove(attributes, StackMapTable.tag); 318 if (smt != null) 319 attributes.add(smt); 320 } 321 322 /** 323 * Adds a stack map table for J2ME (CLDC). If another copy of stack map table 324 * is already contained, the old one is removed. 325 * 326 * @param sm the stack map table added to this code attribute. 327 * If it is null, a new stack map is not added. 328 * Only the old stack map is removed. 329 * @since 3.12 330 */ 331 public void setAttribute(StackMap sm) { 332 AttributeInfo.remove(attributes, StackMap.tag); 333 if (sm != null) 334 attributes.add(sm); 335 } 336 337 /** 338 * Copies code. 339 */ 340 private byte[] copyCode(ConstPool destCp, Map classnames, 341 ExceptionTable etable, CodeAttribute destCa) 342 throws BadBytecode 343 { 344 int len = getCodeLength(); 345 byte[] newCode = new byte[len]; 346 destCa.info = newCode; 347 LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), 348 newCode, destCp, classnames); 349 return LdcEntry.doit(newCode, ldc, etable, destCa); 350 } 351 352 private static LdcEntry copyCode(byte[] code, int beginPos, int endPos, 353 ConstPool srcCp, byte[] newcode, 354 ConstPool destCp, Map classnameMap) 355 throws BadBytecode 356 { 357 int i2, index; 358 LdcEntry ldcEntry = null; 359 360 for (int i = beginPos; i < endPos; i = i2) { 361 i2 = CodeIterator.nextOpcode(code, i); 362 byte c = code[i]; 363 newcode[i] = c; 364 switch (c & 0xff) { 365 case LDC_W : 366 case LDC2_W : 367 case GETSTATIC : 368 case PUTSTATIC : 369 case GETFIELD : 370 case PUTFIELD : 371 case INVOKEVIRTUAL : 372 case INVOKESPECIAL : 373 case INVOKESTATIC : 374 case NEW : 375 case ANEWARRAY : 376 case CHECKCAST : 377 case INSTANCEOF : 378 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, 379 classnameMap); 380 break; 381 case LDC : 382 index = code[i + 1] & 0xff; 383 index = srcCp.copy(index, destCp, classnameMap); 384 if (index < 0x100) 385 newcode[i + 1] = (byte)index; 386 else { 387 newcode[i] = NOP; 388 newcode[i + 1] = NOP; 389 LdcEntry ldc = new LdcEntry(); 390 ldc.where = i; 391 ldc.index = index; 392 ldc.next = ldcEntry; 393 ldcEntry = ldc; 394 } 395 break; 396 case INVOKEINTERFACE : 397 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, 398 classnameMap); 399 newcode[i + 3] = code[i + 3]; 400 newcode[i + 4] = code[i + 4]; 401 break; 402 case MULTIANEWARRAY : 403 copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp, 404 classnameMap); 405 newcode[i + 3] = code[i + 3]; 406 break; 407 default : 408 while (++i < i2) 409 newcode[i] = code[i]; 410 411 break; 412 } 413 } 414 415 return ldcEntry; 416 } 417 418 private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, 419 byte[] newcode, ConstPool destCp, 420 Map classnameMap) { 421 int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff); 422 index = srcCp.copy(index, destCp, classnameMap); 423 newcode[i] = (byte)(index >> 8); 424 newcode[i + 1] = (byte)index; 425 } 426 427 static class LdcEntry { 428 LdcEntry next; 429 int where; 430 int index; 431 432 static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, 433 CodeAttribute ca) 434 throws BadBytecode 435 { 436 if (ldc != null) 437 code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); 438 439 /* The original code was the following: 440 441 while (ldc != null) { 442 int where = ldc.where; 443 code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); 444 code[where] = (byte)Opcode.LDC_W; 445 ByteArray.write16bit(ldc.index, code, where + 1); 446 ldc = ldc.next; 447 } 448 449 But this code does not support a large method > 32KB. 450 */ 451 452 return code; 453 } 454 } 455 456 /** 457 * Changes the index numbers of the local variables 458 * to append a new parameter. 459 * This method does not update <code>LocalVariableAttribute</code>, 460 * <code>StackMapTable</code>, or <code>StackMap</code>. 461 * These attributes must be explicitly updated. 462 * 463 * @param where the index of the new parameter. 464 * @param size the type size of the new parameter (1 or 2). 465 * 466 * @see LocalVariableAttribute#shiftIndex(int, int) 467 * @see StackMapTable#insertLocal(int, int, int) 468 * @see StackMap#insertLocal(int, int, int) 469 */ 470 public void insertLocalVar(int where, int size) throws BadBytecode { 471 CodeIterator ci = iterator(); 472 while (ci.hasNext()) 473 shiftIndex(ci, where, size); 474 475 setMaxLocals(getMaxLocals() + size); 476 } 477 478 /** 479 * @param lessThan If the index of the local variable is 480 * less than this value, it does not change. 481 * Otherwise, the index is increased. 482 * @param delta the indexes of the local variables are 483 * increased by this value. 484 */ 485 private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { 486 int index = ci.next(); 487 int opcode = ci.byteAt(index); 488 if (opcode < ILOAD) 489 return; 490 else if (opcode < IASTORE) { 491 if (opcode < ILOAD_0) { 492 // iload, lload, fload, dload, aload 493 shiftIndex8(ci, index, opcode, lessThan, delta); 494 } 495 else if (opcode < IALOAD) { 496 // iload_0, ..., aload_3 497 shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); 498 } 499 else if (opcode < ISTORE) 500 return; 501 else if (opcode < ISTORE_0) { 502 // istore, lstore, ... 503 shiftIndex8(ci, index, opcode, lessThan, delta); 504 } 505 else { 506 // istore_0, ..., astore_3 507 shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); 508 } 509 } 510 else if (opcode == IINC) { 511 int var = ci.byteAt(index + 1); 512 if (var < lessThan) 513 return; 514 515 var += delta; 516 if (var < 0x100) 517 ci.writeByte(var, index + 1); 518 else { 519 int plus = (byte)ci.byteAt(index + 2); 520 int pos = ci.insertExGap(3); 521 ci.writeByte(WIDE, pos - 3); 522 ci.writeByte(IINC, pos - 2); 523 ci.write16bit(var, pos - 1); 524 ci.write16bit(plus, pos + 1); 525 } 526 } 527 else if (opcode == RET) 528 shiftIndex8(ci, index, opcode, lessThan, delta); 529 else if (opcode == WIDE) { 530 int var = ci.u16bitAt(index + 2); 531 if (var < lessThan) 532 return; 533 534 var += delta; 535 ci.write16bit(var, index + 2); 536 } 537 } 538 539 private static void shiftIndex8(CodeIterator ci, int index, int opcode, 540 int lessThan, int delta) 541 throws BadBytecode 542 { 543 int var = ci.byteAt(index + 1); 544 if (var < lessThan) 545 return; 546 547 var += delta; 548 if (var < 0x100) 549 ci.writeByte(var, index + 1); 550 else { 551 int pos = ci.insertExGap(2); 552 ci.writeByte(WIDE, pos - 2); 553 ci.writeByte(opcode, pos - 1); 554 ci.write16bit(var, pos); 555 } 556 } 557 558 private static void shiftIndex0(CodeIterator ci, int index, int opcode, 559 int lessThan, int delta, 560 int opcode_i_0, int opcode_i) 561 throws BadBytecode 562 { 563 int var = (opcode - opcode_i_0) % 4; 564 if (var < lessThan) 565 return; 566 567 var += delta; 568 if (var < 4) 569 ci.writeByte(opcode + delta, index); 570 else { 571 opcode = (opcode - opcode_i_0) / 4 + opcode_i; 572 if (var < 0x100) { 573 int pos = ci.insertExGap(1); 574 ci.writeByte(opcode, pos - 1); 575 ci.writeByte(var, pos); 576 } 577 else { 578 int pos = ci.insertExGap(3); 579 ci.writeByte(WIDE, pos - 1); 580 ci.writeByte(opcode, pos); 581 ci.write16bit(var, pos + 1); 582 } 583 } 584 } 585} 586