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