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