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 16 package javassist.bytecode; 17 18 import java.io.DataInputStream; 19 import java.io.DataOutputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.PrintWriter; 22 import java.io.IOException; 23 import java.util.Map; 24 import javassist.CannotCompileException; 25 26 /** 27 * <code>stack_map</code> attribute. 28 * 29 * <p>This is an entry in the attributes table of a Code attribute. 30 * It was introduced by J2SE 6 for the process of verification by 31 * typechecking. 32 * 33 * @see StackMap 34 * @since 3.4 35 */ 36 public class StackMapTable extends AttributeInfo { 37 /** 38 * The name of this attribute <code>"StackMapTable"</code>. 39 */ 40 public static final String tag = "StackMapTable"; 41 42 /** 43 * Constructs a <code>stack_map</code> attribute. 44 */ StackMapTable(ConstPool cp, byte[] newInfo)45 StackMapTable(ConstPool cp, byte[] newInfo) { 46 super(cp, tag, newInfo); 47 } 48 StackMapTable(ConstPool cp, int name_id, DataInputStream in)49 StackMapTable(ConstPool cp, int name_id, DataInputStream in) 50 throws IOException 51 { 52 super(cp, name_id, in); 53 } 54 55 /** 56 * Makes a copy. 57 * 58 * @exception RuntimeCopyException if a <code>BadBytecode</code> 59 * exception is thrown while copying, 60 * it is converted into 61 * <code>RuntimeCopyException</code>. 62 * 63 */ copy(ConstPool newCp, Map classnames)64 public AttributeInfo copy(ConstPool newCp, Map classnames) 65 throws RuntimeCopyException 66 { 67 try { 68 return new StackMapTable(newCp, 69 new Copier(this.constPool, info, newCp).doit()); 70 } 71 catch (BadBytecode e) { 72 throw new RuntimeCopyException("bad bytecode. fatal?"); 73 } 74 } 75 76 /** 77 * An exception that may be thrown by <code>copy()</code> 78 * in <code>StackMapTable</code>. 79 */ 80 public static class RuntimeCopyException extends RuntimeException { 81 /** 82 * Constructs an exception. 83 */ RuntimeCopyException(String s)84 public RuntimeCopyException(String s) { 85 super(s); 86 } 87 } 88 write(DataOutputStream out)89 void write(DataOutputStream out) throws IOException { 90 super.write(out); 91 } 92 93 /** 94 * <code>Top_variable_info.tag</code>. 95 */ 96 public static final int TOP = 0; 97 98 /** 99 * <code>Integer_variable_info.tag</code>. 100 */ 101 public static final int INTEGER = 1; 102 103 /** 104 * <code>Float_variable_info.tag</code>. 105 */ 106 public static final int FLOAT = 2; 107 108 /** 109 * <code>Double_variable_info.tag</code>. 110 */ 111 public static final int DOUBLE = 3; 112 113 /** 114 * <code>Long_variable_info.tag</code>. 115 */ 116 public static final int LONG = 4; 117 118 /** 119 * <code>Null_variable_info.tag</code>. 120 */ 121 public static final int NULL = 5; 122 123 /** 124 * <code>UninitializedThis_variable_info.tag</code>. 125 */ 126 public static final int THIS = 6; 127 128 /** 129 * <code>Object_variable_info.tag</code>. 130 */ 131 public static final int OBJECT = 7; 132 133 /** 134 * <code>Uninitialized_variable_info.tag</code>. 135 */ 136 public static final int UNINIT = 8; 137 138 /** 139 * A code walker for a StackMapTable attribute. 140 */ 141 public static class Walker { 142 byte[] info; 143 int numOfEntries; 144 145 /** 146 * Constructs a walker. 147 * 148 * @param smt the StackMapTable that this walker 149 * walks around. 150 */ Walker(StackMapTable smt)151 public Walker(StackMapTable smt) { 152 this(smt.get()); 153 } 154 155 /** 156 * Constructs a walker. 157 * 158 * @param data the <code>info</code> field of the 159 * <code>attribute_info</code> structure. 160 * It can be obtained by <code>get()</code> 161 * in the <code>AttributeInfo</code> class. 162 */ Walker(byte[] data)163 public Walker(byte[] data) { 164 info = data; 165 numOfEntries = ByteArray.readU16bit(data, 0); 166 } 167 168 /** 169 * Returns the number of the entries. 170 */ size()171 public final int size() { return numOfEntries; } 172 173 /** 174 * Visits each entry of the stack map frames. 175 */ parse()176 public void parse() throws BadBytecode { 177 int n = numOfEntries; 178 int pos = 2; 179 for (int i = 0; i < n; i++) 180 pos = stackMapFrames(pos, i); 181 } 182 183 /** 184 * Invoked when the next entry of the stack map frames is visited. 185 * 186 * @param pos the position of the frame in the <code>info</code> 187 * field of <code>attribute_info</code> structure. 188 * @param nth the frame is the N-th 189 * (0, 1st, 2nd, 3rd, 4th, ...) entry. 190 * @return the position of the next frame. 191 */ stackMapFrames(int pos, int nth)192 int stackMapFrames(int pos, int nth) throws BadBytecode { 193 int type = info[pos] & 0xff; 194 if (type < 64) { 195 sameFrame(pos, type); 196 pos++; 197 } 198 else if (type < 128) 199 pos = sameLocals(pos, type); 200 else if (type < 247) 201 throw new BadBytecode("bad frame_type in StackMapTable"); 202 else if (type == 247) // SAME_LOCALS_1_STACK_ITEM_EXTENDED 203 pos = sameLocals(pos, type); 204 else if (type < 251) { 205 int offset = ByteArray.readU16bit(info, pos + 1); 206 chopFrame(pos, offset, 251 - type); 207 pos += 3; 208 } 209 else if (type == 251) { // SAME_FRAME_EXTENDED 210 int offset = ByteArray.readU16bit(info, pos + 1); 211 sameFrame(pos, offset); 212 pos += 3; 213 } 214 else if (type < 255) 215 pos = appendFrame(pos, type); 216 else // FULL_FRAME 217 pos = fullFrame(pos); 218 219 return pos; 220 } 221 222 /** 223 * Invoked if the visited frame is a <code>same_frame</code> or 224 * a <code>same_frame_extended</code>. 225 * 226 * @param pos the position of this frame in the <code>info</code> 227 * field of <code>attribute_info</code> structure. 228 * @param offsetDelta 229 */ sameFrame(int pos, int offsetDelta)230 public void sameFrame(int pos, int offsetDelta) throws BadBytecode {} 231 sameLocals(int pos, int type)232 private int sameLocals(int pos, int type) throws BadBytecode { 233 int top = pos; 234 int offset; 235 if (type < 128) 236 offset = type - 64; 237 else { // type == 247 238 offset = ByteArray.readU16bit(info, pos + 1); 239 pos += 2; 240 } 241 242 int tag = info[pos + 1] & 0xff; 243 int data = 0; 244 if (tag == OBJECT || tag == UNINIT) { 245 data = ByteArray.readU16bit(info, pos + 2); 246 pos += 2; 247 } 248 249 sameLocals(top, offset, tag, data); 250 return pos + 2; 251 } 252 253 /** 254 * Invoked if the visited frame is a <code>same_locals_1_stack_item_frame</code> 255 * or a <code>same_locals_1_stack_item_frame_extended</code>. 256 * 257 * @param pos the position. 258 * @param offsetDelta 259 * @param stackTag <code>stack[0].tag</code>. 260 * @param stackData <code>stack[0].cpool_index</code> 261 * if the tag is <code>OBJECT</code>, 262 * or <code>stack[0].offset</code> 263 * if the tag is <code>UNINIT</code>. 264 */ sameLocals(int pos, int offsetDelta, int stackTag, int stackData)265 public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) 266 throws BadBytecode {} 267 268 /** 269 * Invoked if the visited frame is a <code>chop_frame</code>. 270 * 271 * @param pos the position. 272 * @param offsetDelta 273 * @param k the <cod>k</code> last locals are absent. 274 */ chopFrame(int pos, int offsetDelta, int k)275 public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {} 276 appendFrame(int pos, int type)277 private int appendFrame(int pos, int type) throws BadBytecode { 278 int k = type - 251; 279 int offset = ByteArray.readU16bit(info, pos + 1); 280 int[] tags = new int[k]; 281 int[] data = new int[k]; 282 int p = pos + 3; 283 for (int i = 0; i < k; i++) { 284 int tag = info[p] & 0xff; 285 tags[i] = tag; 286 if (tag == OBJECT || tag == UNINIT) { 287 data[i] = ByteArray.readU16bit(info, p + 1); 288 p += 3; 289 } 290 else { 291 data[i] = 0; 292 p++; 293 } 294 } 295 296 appendFrame(pos, offset, tags, data); 297 return p; 298 } 299 300 /** 301 * Invoked if the visited frame is a <code>append_frame</code>. 302 * 303 * @param pos the position. 304 * @param offsetDelta 305 * @param tags <code>locals[i].tag</code>. 306 * @param data <code>locals[i].cpool_index</code> 307 * or <cod>locals[i].offset</code>. 308 */ appendFrame(int pos, int offsetDelta, int[] tags, int[] data)309 public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) 310 throws BadBytecode {} 311 fullFrame(int pos)312 private int fullFrame(int pos) throws BadBytecode { 313 int offset = ByteArray.readU16bit(info, pos + 1); 314 int numOfLocals = ByteArray.readU16bit(info, pos + 3); 315 int[] localsTags = new int[numOfLocals]; 316 int[] localsData = new int[numOfLocals]; 317 int p = verifyTypeInfo(pos + 5, numOfLocals, localsTags, localsData); 318 int numOfItems = ByteArray.readU16bit(info, p); 319 int[] itemsTags = new int[numOfItems]; 320 int[] itemsData = new int[numOfItems]; 321 p = verifyTypeInfo(p + 2, numOfItems, itemsTags, itemsData); 322 fullFrame(pos, offset, localsTags, localsData, itemsTags, itemsData); 323 return p; 324 } 325 326 /** 327 * Invoked if the visited frame is <code>full_frame</code>. 328 * 329 * @param pos the position. 330 * @param offsetDelta 331 * @param localTags <code>locals[i].tag</code> 332 * @param localData <code>locals[i].cpool_index</code> 333 * or <code>locals[i].offset</code> 334 * @param stackTags <code>stack[i].tag</code> 335 * @param stackData <code>stack[i].cpool_index</code> 336 * or <code>stack[i].offset</code> 337 */ fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)338 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 339 int[] stackTags, int[] stackData) 340 throws BadBytecode {} 341 verifyTypeInfo(int pos, int n, int[] tags, int[] data)342 private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) { 343 for (int i = 0; i < n; i++) { 344 int tag = info[pos++] & 0xff; 345 tags[i] = tag; 346 if (tag == OBJECT || tag == UNINIT) { 347 data[i] = ByteArray.readU16bit(info, pos); 348 pos += 2; 349 } 350 } 351 352 return pos; 353 } 354 } 355 356 static class SimpleCopy extends Walker { 357 private Writer writer; 358 SimpleCopy(byte[] data)359 public SimpleCopy(byte[] data) { 360 super(data); 361 writer = new Writer(data.length); 362 } 363 doit()364 public byte[] doit() throws BadBytecode { 365 parse(); 366 return writer.toByteArray(); 367 } 368 sameFrame(int pos, int offsetDelta)369 public void sameFrame(int pos, int offsetDelta) { 370 writer.sameFrame(offsetDelta); 371 } 372 sameLocals(int pos, int offsetDelta, int stackTag, int stackData)373 public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { 374 writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData)); 375 } 376 chopFrame(int pos, int offsetDelta, int k)377 public void chopFrame(int pos, int offsetDelta, int k) { 378 writer.chopFrame(offsetDelta, k); 379 } 380 appendFrame(int pos, int offsetDelta, int[] tags, int[] data)381 public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { 382 writer.appendFrame(offsetDelta, tags, copyData(tags, data)); 383 } 384 fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)385 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 386 int[] stackTags, int[] stackData) { 387 writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData), 388 stackTags, copyData(stackTags, stackData)); 389 } 390 copyData(int tag, int data)391 protected int copyData(int tag, int data) { 392 return data; 393 } 394 copyData(int[] tags, int[] data)395 protected int[] copyData(int[] tags, int[] data) { 396 return data; 397 } 398 } 399 400 static class Copier extends SimpleCopy { 401 private ConstPool srcPool, destPool; 402 Copier(ConstPool src, byte[] data, ConstPool dest)403 public Copier(ConstPool src, byte[] data, ConstPool dest) { 404 super(data); 405 srcPool = src; 406 destPool = dest; 407 } 408 copyData(int tag, int data)409 protected int copyData(int tag, int data) { 410 if (tag == OBJECT) 411 return srcPool.copy(data, destPool, null); 412 else 413 return data; 414 } 415 copyData(int[] tags, int[] data)416 protected int[] copyData(int[] tags, int[] data) { 417 int[] newData = new int[data.length]; 418 for (int i = 0; i < data.length; i++) 419 if (tags[i] == OBJECT) 420 newData[i] = srcPool.copy(data[i], destPool, null); 421 else 422 newData[i] = data[i]; 423 424 return newData; 425 } 426 } 427 428 /** 429 * Updates this stack map table when a new local variable is inserted 430 * for a new parameter. 431 * 432 * @param index the index of the added local variable. 433 * @param tag the type tag of that local variable. 434 * @param classInfo the index of the <code>CONSTANT_Class_info</code> structure 435 * in a constant pool table. This should be zero unless the tag 436 * is <code>ITEM_Object</code>. 437 * 438 * @see javassist.CtBehavior#addParameter(javassist.CtClass) 439 * @see #typeTagOf(char) 440 * @see ConstPool 441 */ insertLocal(int index, int tag, int classInfo)442 public void insertLocal(int index, int tag, int classInfo) 443 throws BadBytecode 444 { 445 byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit(); 446 this.set(data); 447 } 448 449 /** 450 * Returns the tag of the type specified by the 451 * descriptor. This method returns <code>INTEGER</code> 452 * unless the descriptor is either D (double), F (float), 453 * J (long), L (class type), or [ (array). 454 * 455 * @param descriptor the type descriptor. 456 * @see Descriptor 457 */ typeTagOf(char descriptor)458 public static int typeTagOf(char descriptor) { 459 switch (descriptor) { 460 case 'D' : 461 return DOUBLE; 462 case 'F' : 463 return FLOAT; 464 case 'J' : 465 return LONG; 466 case 'L' : 467 case '[' : 468 return OBJECT; 469 // case 'V' : 470 default : 471 return INTEGER; 472 } 473 } 474 475 /* This implementation assumes that a local variable initially 476 * holding a parameter value is never changed to be a different 477 * type. 478 * 479 */ 480 static class InsertLocal extends SimpleCopy { 481 private int varIndex; 482 private int varTag, varData; 483 InsertLocal(byte[] data, int varIndex, int varTag, int varData)484 public InsertLocal(byte[] data, int varIndex, int varTag, int varData) { 485 super(data); 486 this.varIndex = varIndex; 487 this.varTag = varTag; 488 this.varData = varData; 489 } 490 fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)491 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 492 int[] stackTags, int[] stackData) { 493 int len = localTags.length; 494 if (len < varIndex) { 495 super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); 496 return; 497 } 498 499 int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1; 500 int[] localTags2 = new int[len + typeSize]; 501 int[] localData2 = new int[len + typeSize]; 502 int index = varIndex; 503 int j = 0; 504 for (int i = 0; i < len; i++) { 505 if (j == index) 506 j += typeSize; 507 508 localTags2[j] = localTags[i]; 509 localData2[j++] = localData[i]; 510 } 511 512 localTags2[index] = varTag; 513 localData2[index] = varData; 514 if (typeSize > 1) { 515 localTags2[index + 1] = TOP; 516 localData2[index + 1] = 0; 517 } 518 519 super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData); 520 } 521 } 522 523 /** 524 * A writer of stack map tables. 525 */ 526 public static class Writer { 527 ByteArrayOutputStream output; 528 int numOfEntries; 529 530 /** 531 * Constructs a writer. 532 * @param size the initial buffer size. 533 */ Writer(int size)534 public Writer(int size) { 535 output = new ByteArrayOutputStream(size); 536 numOfEntries = 0; 537 output.write(0); // u2 number_of_entries 538 output.write(0); 539 } 540 541 /** 542 * Returns the stack map table written out. 543 */ toByteArray()544 public byte[] toByteArray() { 545 byte[] b = output.toByteArray(); 546 ByteArray.write16bit(numOfEntries, b, 0); 547 return b; 548 } 549 550 /** 551 * Constructs and a return a stack map table containing 552 * the written stack map entries. 553 * 554 * @param cp the constant pool used to write 555 * the stack map entries. 556 */ toStackMapTable(ConstPool cp)557 public StackMapTable toStackMapTable(ConstPool cp) { 558 return new StackMapTable(cp, toByteArray()); 559 } 560 561 /** 562 * Writes a <code>same_frame</code> or a <code>same_frame_extended</code>. 563 */ sameFrame(int offsetDelta)564 public void sameFrame(int offsetDelta) { 565 numOfEntries++; 566 if (offsetDelta < 64) 567 output.write(offsetDelta); 568 else { 569 output.write(251); // SAME_FRAME_EXTENDED 570 write16(offsetDelta); 571 } 572 } 573 574 /** 575 * Writes a <code>same_locals_1_stack_item</code> 576 * or a <code>same_locals_1_stack_item_extended</code>. 577 * 578 * @param tag <code>stack[0].tag</code>. 579 * @param data <code>stack[0].cpool_index</code> 580 * if the tag is <code>OBJECT</code>, 581 * or <cod>stack[0].offset</code> 582 * if the tag is <code>UNINIT</code>. 583 * Otherwise, this parameter is not used. 584 */ sameLocals(int offsetDelta, int tag, int data)585 public void sameLocals(int offsetDelta, int tag, int data) { 586 numOfEntries++; 587 if (offsetDelta < 64) 588 output.write(offsetDelta + 64); 589 else { 590 output.write(247); // SAME_LOCALS_1_STACK_ITEM_EXTENDED 591 write16(offsetDelta); 592 } 593 594 writeTypeInfo(tag, data); 595 } 596 597 /** 598 * Writes a <code>chop_frame</code>. 599 * 600 * @param k the number of absent locals. 1, 2, or 3. 601 */ chopFrame(int offsetDelta, int k)602 public void chopFrame(int offsetDelta, int k) { 603 numOfEntries++; 604 output.write(251 - k); 605 write16(offsetDelta); 606 } 607 608 /** 609 * Writes a <code>append_frame</code>. The number of the appended 610 * locals is specified by the length of <code>tags</code>. 611 * 612 * @param tags <code>locals[].tag</code>. 613 * The length of this array must be 614 * either 1, 2, or 3. 615 * @param data <code>locals[].cpool_index</code> 616 * if the tag is <code>OBJECT</code>, 617 * or <cod>locals[].offset</code> 618 * if the tag is <code>UNINIT</code>. 619 * Otherwise, this parameter is not used. 620 */ appendFrame(int offsetDelta, int[] tags, int[] data)621 public void appendFrame(int offsetDelta, int[] tags, int[] data) { 622 numOfEntries++; 623 int k = tags.length; // k is 1, 2, or 3 624 output.write(k + 251); 625 write16(offsetDelta); 626 for (int i = 0; i < k; i++) 627 writeTypeInfo(tags[i], data[i]); 628 } 629 630 /** 631 * Writes a <code>full_frame</code>. 632 * <code>number_of_locals</code> and <code>number_of_stack_items</code> 633 * are specified by the the length of <code>localTags</code> and 634 * <code>stackTags</code>. 635 * 636 * @param localTags <code>locals[].tag</code>. 637 * @param localData <code>locals[].cpool_index</code> 638 * if the tag is <code>OBJECT</code>, 639 * or <cod>locals[].offset</code> 640 * if the tag is <code>UNINIT</code>. 641 * Otherwise, this parameter is not used. 642 * @param stackTags <code>stack[].tag</code>. 643 * @param stackData <code>stack[].cpool_index</code> 644 * if the tag is <code>OBJECT</code>, 645 * or <cod>stack[].offset</code> 646 * if the tag is <code>UNINIT</code>. 647 * Otherwise, this parameter is not used. 648 */ fullFrame(int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)649 public void fullFrame(int offsetDelta, int[] localTags, int[] localData, 650 int[] stackTags, int[] stackData) { 651 numOfEntries++; 652 output.write(255); // FULL_FRAME 653 write16(offsetDelta); 654 int n = localTags.length; 655 write16(n); 656 for (int i = 0; i < n; i++) 657 writeTypeInfo(localTags[i], localData[i]); 658 659 n = stackTags.length; 660 write16(n); 661 for (int i = 0; i < n; i++) 662 writeTypeInfo(stackTags[i], stackData[i]); 663 } 664 writeTypeInfo(int tag, int data)665 private void writeTypeInfo(int tag, int data) { 666 output.write(tag); 667 if (tag == OBJECT || tag == UNINIT) 668 write16(data); 669 } 670 write16(int value)671 private void write16(int value) { 672 output.write((value >>> 8) & 0xff); 673 output.write(value & 0xff); 674 } 675 } 676 677 /** 678 * Prints the stack table map. 679 */ println(PrintWriter w)680 public void println(PrintWriter w) { 681 Printer.print(this, w); 682 } 683 684 /** 685 * Prints the stack table map. 686 * 687 * @param ps a print stream such as <code>System.out</code>. 688 */ println(java.io.PrintStream ps)689 public void println(java.io.PrintStream ps) { 690 Printer.print(this, new java.io.PrintWriter(ps, true)); 691 } 692 693 static class Printer extends Walker { 694 private PrintWriter writer; 695 private int offset; 696 697 /** 698 * Prints the stack table map. 699 */ print(StackMapTable smt, PrintWriter writer)700 public static void print(StackMapTable smt, PrintWriter writer) { 701 try { 702 new Printer(smt.get(), writer).parse(); 703 } 704 catch (BadBytecode e) { 705 writer.println(e.getMessage()); 706 } 707 } 708 Printer(byte[] data, PrintWriter pw)709 Printer(byte[] data, PrintWriter pw) { 710 super(data); 711 writer = pw; 712 offset = -1; 713 } 714 sameFrame(int pos, int offsetDelta)715 public void sameFrame(int pos, int offsetDelta) { 716 offset += offsetDelta + 1; 717 writer.println(offset + " same frame: " + offsetDelta); 718 } 719 sameLocals(int pos, int offsetDelta, int stackTag, int stackData)720 public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { 721 offset += offsetDelta + 1; 722 writer.println(offset + " same locals: " + offsetDelta); 723 printTypeInfo(stackTag, stackData); 724 } 725 chopFrame(int pos, int offsetDelta, int k)726 public void chopFrame(int pos, int offsetDelta, int k) { 727 offset += offsetDelta + 1; 728 writer.println(offset + " chop frame: " + offsetDelta + ", " + k + " last locals"); 729 } 730 appendFrame(int pos, int offsetDelta, int[] tags, int[] data)731 public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { 732 offset += offsetDelta + 1; 733 writer.println(offset + " append frame: " + offsetDelta); 734 for (int i = 0; i < tags.length; i++) 735 printTypeInfo(tags[i], data[i]); 736 } 737 fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData)738 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 739 int[] stackTags, int[] stackData) { 740 offset += offsetDelta + 1; 741 writer.println(offset + " full frame: " + offsetDelta); 742 writer.println("[locals]"); 743 for (int i = 0; i < localTags.length; i++) 744 printTypeInfo(localTags[i], localData[i]); 745 746 writer.println("[stack]"); 747 for (int i = 0; i < stackTags.length; i++) 748 printTypeInfo(stackTags[i], stackData[i]); 749 } 750 printTypeInfo(int tag, int data)751 private void printTypeInfo(int tag, int data) { 752 String msg = null; 753 switch (tag) { 754 case TOP : 755 msg = "top"; 756 break; 757 case INTEGER : 758 msg = "integer"; 759 break; 760 case FLOAT : 761 msg = "float"; 762 break; 763 case DOUBLE : 764 msg = "double"; 765 break; 766 case LONG : 767 msg = "long"; 768 break; 769 case NULL : 770 msg = "null"; 771 break; 772 case THIS : 773 msg = "this"; 774 break; 775 case OBJECT : 776 msg = "object (cpool_index " + data + ")"; 777 break; 778 case UNINIT : 779 msg = "uninitialized (offset " + data + ")"; 780 break; 781 } 782 783 writer.print(" "); 784 writer.println(msg); 785 } 786 } 787 shiftPc(int where, int gapSize, boolean exclusive)788 void shiftPc(int where, int gapSize, boolean exclusive) 789 throws BadBytecode 790 { 791 new Shifter(this, where, gapSize, exclusive).doit(); 792 } 793 794 static class Shifter extends Walker { 795 private StackMapTable stackMap; 796 private int where, gap; 797 private int position; 798 private byte[] updatedInfo; 799 private boolean exclusive; 800 Shifter(StackMapTable smt, int where, int gap, boolean exclusive)801 public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) { 802 super(smt); 803 stackMap = smt; 804 this.where = where; 805 this.gap = gap; 806 this.position = 0; 807 this.updatedInfo = null; 808 this.exclusive = exclusive; 809 } 810 doit()811 public void doit() throws BadBytecode { 812 parse(); 813 if (updatedInfo != null) 814 stackMap.set(updatedInfo); 815 } 816 sameFrame(int pos, int offsetDelta)817 public void sameFrame(int pos, int offsetDelta) { 818 update(pos, offsetDelta, 0, 251); 819 } 820 sameLocals(int pos, int offsetDelta, int stackTag, int stackData)821 public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { 822 update(pos, offsetDelta, 64, 247); 823 } 824 update(int pos, int offsetDelta, int base, int entry)825 private void update(int pos, int offsetDelta, int base, int entry) { 826 int oldPos = position; 827 position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); 828 boolean match; 829 if (exclusive) 830 match = oldPos < where && where <= position; 831 else 832 match = oldPos <= where && where < position; 833 834 if (match) { 835 int newDelta = offsetDelta + gap; 836 position += gap; 837 if (newDelta < 64) 838 info[pos] = (byte)(newDelta + base); 839 else if (offsetDelta < 64) { 840 byte[] newinfo = insertGap(info, pos, 2); 841 newinfo[pos] = (byte)entry; 842 ByteArray.write16bit(newDelta, newinfo, pos + 1); 843 updatedInfo = newinfo; 844 } 845 else 846 ByteArray.write16bit(newDelta, info, pos + 1); 847 } 848 } 849 850 private static byte[] insertGap(byte[] info, int where, int gap) { 851 int len = info.length; 852 byte[] newinfo = new byte[len + gap]; 853 for (int i = 0; i < len; i++) 854 newinfo[i + (i < where ? 0 : gap)] = info[i]; 855 856 return newinfo; 857 } 858 859 public void chopFrame(int pos, int offsetDelta, int k) { 860 update(pos, offsetDelta); 861 } 862 863 public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { 864 update(pos, offsetDelta); 865 } 866 867 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 868 int[] stackTags, int[] stackData) { 869 update(pos, offsetDelta); 870 } 871 872 private void update(int pos, int offsetDelta) { 873 int oldPos = position; 874 position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); 875 boolean match; 876 if (exclusive) 877 match = oldPos < where && where <= position; 878 else 879 match = oldPos <= where && where < position; 880 881 if (match) { 882 int newDelta = offsetDelta + gap; 883 ByteArray.write16bit(newDelta, info, pos + 1); 884 position += gap; 885 } 886 } 887 } 888 889 /** 890 * Undocumented method. Do not use; internal-use only. 891 * 892 * <p>This method is for javassist.convert.TransformNew. 893 * It is called to update the stack map table when 894 * the NEW opcode (and the following DUP) is removed. 895 * 896 * @param where the position of the removed NEW opcode. 897 */ 898 public void removeNew(int where) throws CannotCompileException { 899 try { 900 byte[] data = new NewRemover(this.get(), where).doit(); 901 this.set(data); 902 } 903 catch (BadBytecode e) { 904 throw new CannotCompileException("bad stack map table", e); 905 } 906 } 907 908 static class NewRemover extends SimpleCopy { 909 int posOfNew; 910 911 public NewRemover(byte[] data, int pos) { 912 super(data); 913 posOfNew = pos; 914 } 915 916 public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { 917 if (stackTag == UNINIT && stackData == posOfNew) 918 super.sameFrame(pos, offsetDelta); 919 else 920 super.sameLocals(pos, offsetDelta, stackTag, stackData); 921 } 922 923 public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, 924 int[] stackTags, int[] stackData) { 925 int n = stackTags.length - 1; 926 for (int i = 0; i < n; i++) 927 if (stackTags[i] == UNINIT && stackData[i] == posOfNew 928 && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) { 929 n++; 930 int[] stackTags2 = new int[n - 2]; 931 int[] stackData2 = new int[n - 2]; 932 int k = 0; 933 for (int j = 0; j < n; j++) 934 if (j == i) 935 j++; 936 else { 937 stackTags2[k] = stackTags[j]; 938 stackData2[k++] = stackData[j]; 939 } 940 941 stackTags = stackTags2; 942 stackData = stackData2; 943 break; 944 } 945 946 super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); 947 } 948 } 949 } 950