1 /*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2005 INRIA, France Telecom 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 * THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package org.objectweb.asm; 31 32 /** 33 * A {@link MethodVisitor} that generates methods in bytecode form. Each visit 34 * method of this class appends the bytecode corresponding to the visited 35 * instruction to a byte vector, in the order these methods are called. 36 * 37 * @author Eric Bruneton 38 * @author Eugene Kuleshov 39 */ 40 class MethodWriter implements MethodVisitor { 41 42 /** 43 * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). 44 */ 45 MethodWriter next; 46 47 /** 48 * The class writer to which this method must be added. 49 */ 50 ClassWriter cw; 51 52 /** 53 * Access flags of this method. 54 */ 55 private int access; 56 57 /** 58 * The index of the constant pool item that contains the name of this 59 * method. 60 */ 61 private int name; 62 63 /** 64 * The index of the constant pool item that contains the descriptor of this 65 * method. 66 */ 67 private int desc; 68 69 /** 70 * The descriptor of this method. 71 */ 72 private String descriptor; 73 74 /** 75 * If not zero, indicates that the code of this method must be copied from 76 * the ClassReader associated to this writer in <code>cw.cr</code>. More 77 * precisely, this field gives the index of the first byte to copied from 78 * <code>cw.cr.b</code>. 79 */ 80 int classReaderOffset; 81 82 /** 83 * If not zero, indicates that the code of this method must be copied from 84 * the ClassReader associated to this writer in <code>cw.cr</code>. More 85 * precisely, this field gives the number of bytes to copied from 86 * <code>cw.cr.b</code>. 87 */ 88 int classReaderLength; 89 90 /** 91 * The signature of this method. 92 */ 93 String signature; 94 95 /** 96 * Number of exceptions that can be thrown by this method. 97 */ 98 int exceptionCount; 99 100 /** 101 * The exceptions that can be thrown by this method. More precisely, this 102 * array contains the indexes of the constant pool items that contain the 103 * internal names of these exception classes. 104 */ 105 int[] exceptions; 106 107 /** 108 * The annotation default attribute of this method. May be <tt>null</tt>. 109 */ 110 private ByteVector annd; 111 112 /** 113 * The runtime visible annotations of this method. May be <tt>null</tt>. 114 */ 115 private AnnotationWriter anns; 116 117 /** 118 * The runtime invisible annotations of this method. May be <tt>null</tt>. 119 */ 120 private AnnotationWriter ianns; 121 122 /** 123 * The runtime visible parameter annotations of this method. May be 124 * <tt>null</tt>. 125 */ 126 private AnnotationWriter[] panns; 127 128 /** 129 * The runtime invisible parameter annotations of this method. May be 130 * <tt>null</tt>. 131 */ 132 private AnnotationWriter[] ipanns; 133 134 /** 135 * The non standard attributes of the method. 136 */ 137 private Attribute attrs; 138 139 /** 140 * The bytecode of this method. 141 */ 142 private ByteVector code = new ByteVector(); 143 144 /** 145 * Maximum stack size of this method. 146 */ 147 private int maxStack; 148 149 /** 150 * Maximum number of local variables for this method. 151 */ 152 private int maxLocals; 153 154 /** 155 * Number of entries in the catch table of this method. 156 */ 157 private int catchCount; 158 159 /** 160 * The catch table of this method. 161 */ 162 private Handler catchTable; 163 164 /** 165 * The last element in the catchTable handler list. 166 */ 167 private Handler lastHandler; 168 169 /** 170 * Number of entries in the LocalVariableTable attribute. 171 */ 172 private int localVarCount; 173 174 /** 175 * The LocalVariableTable attribute. 176 */ 177 private ByteVector localVar; 178 179 /** 180 * Number of entries in the LocalVariableTypeTable attribute. 181 */ 182 private int localVarTypeCount; 183 184 /** 185 * The LocalVariableTypeTable attribute. 186 */ 187 private ByteVector localVarType; 188 189 /** 190 * Number of entries in the LineNumberTable attribute. 191 */ 192 private int lineNumberCount; 193 194 /** 195 * The LineNumberTable attribute. 196 */ 197 private ByteVector lineNumber; 198 199 /** 200 * The non standard attributes of the method's code. 201 */ 202 private Attribute cattrs; 203 204 /** 205 * Indicates if some jump instructions are too small and need to be resized. 206 */ 207 private boolean resize; 208 209 /* 210 * Fields for the control flow graph analysis algorithm (used to compute the 211 * maximum stack size). A control flow graph contains one node per "basic 212 * block", and one edge per "jump" from one basic block to another. Each 213 * node (i.e., each basic block) is represented by the Label object that 214 * corresponds to the first instruction of this basic block. Each node also 215 * stores the list of its successors in the graph, as a linked list of Edge 216 * objects. 217 */ 218 219 /** 220 * <tt>true</tt> if the maximum stack size and number of local variables 221 * must be automatically computed. 222 */ 223 private final boolean computeMaxs; 224 225 /** 226 * The (relative) stack size after the last visited instruction. This size 227 * is relative to the beginning of the current basic block, i.e., the true 228 * stack size after the last visited instruction is equal to the {@link 229 * Label#beginStackSize beginStackSize} of the current basic block plus 230 * <tt>stackSize</tt>. 231 */ 232 private int stackSize; 233 234 /** 235 * The (relative) maximum stack size after the last visited instruction. 236 * This size is relative to the beginning of the current basic block, i.e., 237 * the true maximum stack size after the last visited instruction is equal 238 * to the {@link Label#beginStackSize beginStackSize} of the current basic 239 * block plus <tt>stackSize</tt>. 240 */ 241 private int maxStackSize; 242 243 /** 244 * The current basic block. This block is the basic block to which the next 245 * instruction to be visited must be added. 246 */ 247 private Label currentBlock; 248 249 /** 250 * The basic block stack used by the control flow analysis algorithm. This 251 * stack is represented by a linked list of {@link Label Label} objects, 252 * linked to each other by their {@link Label#next} field. This stack must 253 * not be confused with the JVM stack used to execute the JVM instructions! 254 */ 255 private Label blockStack; 256 257 /** 258 * The stack size variation corresponding to each JVM instruction. This 259 * stack variation is equal to the size of the values produced by an 260 * instruction, minus the size of the values consumed by this instruction. 261 */ 262 private final static int[] SIZE; 263 264 // ------------------------------------------------------------------------ 265 // Static initializer 266 // ------------------------------------------------------------------------ 267 268 /** 269 * Computes the stack size variation corresponding to each JVM instruction. 270 */ 271 static { 272 int i; 273 int[] b = new int[202]; 274 String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" 275 + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" 276 + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" 277 + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; 278 for (i = 0; i < b.length; ++i) { 279 b[i] = s.charAt(i) - 'E'; 280 } 281 SIZE = b; 282 283 // code to generate the above string 284 // 285 // int NA = 0; // not applicable (unused opcode or variable size opcode) 286 // 287 // b = new int[] { 288 // 0, //NOP, // visitInsn 289 // 1, //ACONST_NULL, // - 290 // 1, //ICONST_M1, // - 291 // 1, //ICONST_0, // - 292 // 1, //ICONST_1, // - 293 // 1, //ICONST_2, // - 294 // 1, //ICONST_3, // - 295 // 1, //ICONST_4, // - 296 // 1, //ICONST_5, // - 297 // 2, //LCONST_0, // - 298 // 2, //LCONST_1, // - 299 // 1, //FCONST_0, // - 300 // 1, //FCONST_1, // - 301 // 1, //FCONST_2, // - 302 // 2, //DCONST_0, // - 303 // 2, //DCONST_1, // - 304 // 1, //BIPUSH, // visitIntInsn 305 // 1, //SIPUSH, // - 306 // 1, //LDC, // visitLdcInsn 307 // NA, //LDC_W, // - 308 // NA, //LDC2_W, // - 309 // 1, //ILOAD, // visitVarInsn 310 // 2, //LLOAD, // - 311 // 1, //FLOAD, // - 312 // 2, //DLOAD, // - 313 // 1, //ALOAD, // - 314 // NA, //ILOAD_0, // - 315 // NA, //ILOAD_1, // - 316 // NA, //ILOAD_2, // - 317 // NA, //ILOAD_3, // - 318 // NA, //LLOAD_0, // - 319 // NA, //LLOAD_1, // - 320 // NA, //LLOAD_2, // - 321 // NA, //LLOAD_3, // - 322 // NA, //FLOAD_0, // - 323 // NA, //FLOAD_1, // - 324 // NA, //FLOAD_2, // - 325 // NA, //FLOAD_3, // - 326 // NA, //DLOAD_0, // - 327 // NA, //DLOAD_1, // - 328 // NA, //DLOAD_2, // - 329 // NA, //DLOAD_3, // - 330 // NA, //ALOAD_0, // - 331 // NA, //ALOAD_1, // - 332 // NA, //ALOAD_2, // - 333 // NA, //ALOAD_3, // - 334 // -1, //IALOAD, // visitInsn 335 // 0, //LALOAD, // - 336 // -1, //FALOAD, // - 337 // 0, //DALOAD, // - 338 // -1, //AALOAD, // - 339 // -1, //BALOAD, // - 340 // -1, //CALOAD, // - 341 // -1, //SALOAD, // - 342 // -1, //ISTORE, // visitVarInsn 343 // -2, //LSTORE, // - 344 // -1, //FSTORE, // - 345 // -2, //DSTORE, // - 346 // -1, //ASTORE, // - 347 // NA, //ISTORE_0, // - 348 // NA, //ISTORE_1, // - 349 // NA, //ISTORE_2, // - 350 // NA, //ISTORE_3, // - 351 // NA, //LSTORE_0, // - 352 // NA, //LSTORE_1, // - 353 // NA, //LSTORE_2, // - 354 // NA, //LSTORE_3, // - 355 // NA, //FSTORE_0, // - 356 // NA, //FSTORE_1, // - 357 // NA, //FSTORE_2, // - 358 // NA, //FSTORE_3, // - 359 // NA, //DSTORE_0, // - 360 // NA, //DSTORE_1, // - 361 // NA, //DSTORE_2, // - 362 // NA, //DSTORE_3, // - 363 // NA, //ASTORE_0, // - 364 // NA, //ASTORE_1, // - 365 // NA, //ASTORE_2, // - 366 // NA, //ASTORE_3, // - 367 // -3, //IASTORE, // visitInsn 368 // -4, //LASTORE, // - 369 // -3, //FASTORE, // - 370 // -4, //DASTORE, // - 371 // -3, //AASTORE, // - 372 // -3, //BASTORE, // - 373 // -3, //CASTORE, // - 374 // -3, //SASTORE, // - 375 // -1, //POP, // - 376 // -2, //POP2, // - 377 // 1, //DUP, // - 378 // 1, //DUP_X1, // - 379 // 1, //DUP_X2, // - 380 // 2, //DUP2, // - 381 // 2, //DUP2_X1, // - 382 // 2, //DUP2_X2, // - 383 // 0, //SWAP, // - 384 // -1, //IADD, // - 385 // -2, //LADD, // - 386 // -1, //FADD, // - 387 // -2, //DADD, // - 388 // -1, //ISUB, // - 389 // -2, //LSUB, // - 390 // -1, //FSUB, // - 391 // -2, //DSUB, // - 392 // -1, //IMUL, // - 393 // -2, //LMUL, // - 394 // -1, //FMUL, // - 395 // -2, //DMUL, // - 396 // -1, //IDIV, // - 397 // -2, //LDIV, // - 398 // -1, //FDIV, // - 399 // -2, //DDIV, // - 400 // -1, //IREM, // - 401 // -2, //LREM, // - 402 // -1, //FREM, // - 403 // -2, //DREM, // - 404 // 0, //INEG, // - 405 // 0, //LNEG, // - 406 // 0, //FNEG, // - 407 // 0, //DNEG, // - 408 // -1, //ISHL, // - 409 // -1, //LSHL, // - 410 // -1, //ISHR, // - 411 // -1, //LSHR, // - 412 // -1, //IUSHR, // - 413 // -1, //LUSHR, // - 414 // -1, //IAND, // - 415 // -2, //LAND, // - 416 // -1, //IOR, // - 417 // -2, //LOR, // - 418 // -1, //IXOR, // - 419 // -2, //LXOR, // - 420 // 0, //IINC, // visitIincInsn 421 // 1, //I2L, // visitInsn 422 // 0, //I2F, // - 423 // 1, //I2D, // - 424 // -1, //L2I, // - 425 // -1, //L2F, // - 426 // 0, //L2D, // - 427 // 0, //F2I, // - 428 // 1, //F2L, // - 429 // 1, //F2D, // - 430 // -1, //D2I, // - 431 // 0, //D2L, // - 432 // -1, //D2F, // - 433 // 0, //I2B, // - 434 // 0, //I2C, // - 435 // 0, //I2S, // - 436 // -3, //LCMP, // - 437 // -1, //FCMPL, // - 438 // -1, //FCMPG, // - 439 // -3, //DCMPL, // - 440 // -3, //DCMPG, // - 441 // -1, //IFEQ, // visitJumpInsn 442 // -1, //IFNE, // - 443 // -1, //IFLT, // - 444 // -1, //IFGE, // - 445 // -1, //IFGT, // - 446 // -1, //IFLE, // - 447 // -2, //IF_ICMPEQ, // - 448 // -2, //IF_ICMPNE, // - 449 // -2, //IF_ICMPLT, // - 450 // -2, //IF_ICMPGE, // - 451 // -2, //IF_ICMPGT, // - 452 // -2, //IF_ICMPLE, // - 453 // -2, //IF_ACMPEQ, // - 454 // -2, //IF_ACMPNE, // - 455 // 0, //GOTO, // - 456 // 1, //JSR, // - 457 // 0, //RET, // visitVarInsn 458 // -1, //TABLESWITCH, // visiTableSwitchInsn 459 // -1, //LOOKUPSWITCH, // visitLookupSwitch 460 // -1, //IRETURN, // visitInsn 461 // -2, //LRETURN, // - 462 // -1, //FRETURN, // - 463 // -2, //DRETURN, // - 464 // -1, //ARETURN, // - 465 // 0, //RETURN, // - 466 // NA, //GETSTATIC, // visitFieldInsn 467 // NA, //PUTSTATIC, // - 468 // NA, //GETFIELD, // - 469 // NA, //PUTFIELD, // - 470 // NA, //INVOKEVIRTUAL, // visitMethodInsn 471 // NA, //INVOKESPECIAL, // - 472 // NA, //INVOKESTATIC, // - 473 // NA, //INVOKEINTERFACE, // - 474 // NA, //UNUSED, // NOT VISITED 475 // 1, //NEW, // visitTypeInsn 476 // 0, //NEWARRAY, // visitIntInsn 477 // 0, //ANEWARRAY, // visitTypeInsn 478 // 0, //ARRAYLENGTH, // visitInsn 479 // NA, //ATHROW, // - 480 // 0, //CHECKCAST, // visitTypeInsn 481 // 0, //INSTANCEOF, // - 482 // -1, //MONITORENTER, // visitInsn 483 // -1, //MONITOREXIT, // - 484 // NA, //WIDE, // NOT VISITED 485 // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn 486 // -1, //IFNULL, // visitJumpInsn 487 // -1, //IFNONNULL, // - 488 // NA, //GOTO_W, // - 489 // NA, //JSR_W, // - 490 // }; 491 // for (i = 0; i < b.length; ++i) { 492 // System.err.print((char)('E' + b[i])); 493 // } 494 // System.err.println(); 495 } 496 497 // ------------------------------------------------------------------------ 498 // Constructor 499 // ------------------------------------------------------------------------ 500 501 /** 502 * Constructs a new {@link MethodWriter}. 503 * 504 * @param cw the class writer in which the method must be added. 505 * @param access the method's access flags (see {@link Opcodes}). 506 * @param name the method's name. 507 * @param desc the method's descriptor (see {@link Type}). 508 * @param signature the method's signature. May be <tt>null</tt>. 509 * @param exceptions the internal names of the method's exceptions. May be 510 * <tt>null</tt>. 511 * @param computeMaxs <tt>true</tt> if the maximum stack size and number 512 * of local variables must be automatically computed. 513 */ MethodWriter( final ClassWriter cw, final int access, final String name, final String desc, final String signature, final String[] exceptions, final boolean computeMaxs)514 MethodWriter( 515 final ClassWriter cw, 516 final int access, 517 final String name, 518 final String desc, 519 final String signature, 520 final String[] exceptions, 521 final boolean computeMaxs) 522 { 523 if (cw.firstMethod == null) { 524 cw.firstMethod = this; 525 } else { 526 cw.lastMethod.next = this; 527 } 528 cw.lastMethod = this; 529 this.cw = cw; 530 this.access = access; 531 this.name = cw.newUTF8(name); 532 this.desc = cw.newUTF8(desc); 533 this.descriptor = desc; 534 this.signature = signature; 535 if (exceptions != null && exceptions.length > 0) { 536 exceptionCount = exceptions.length; 537 this.exceptions = new int[exceptionCount]; 538 for (int i = 0; i < exceptionCount; ++i) { 539 this.exceptions[i] = cw.newClass(exceptions[i]); 540 } 541 } 542 this.computeMaxs = computeMaxs; 543 if (computeMaxs) { 544 // updates maxLocals 545 int size = getArgumentsAndReturnSizes(desc) >> 2; 546 if ((access & Opcodes.ACC_STATIC) != 0) { 547 --size; 548 } 549 maxLocals = size; 550 // pushes the first block onto the stack of blocks to be visited 551 currentBlock = new Label(); 552 currentBlock.pushed = true; 553 blockStack = currentBlock; 554 } 555 } 556 557 // ------------------------------------------------------------------------ 558 // Implementation of the MethodVisitor interface 559 // ------------------------------------------------------------------------ 560 visitAnnotationDefault()561 public AnnotationVisitor visitAnnotationDefault() { 562 annd = new ByteVector(); 563 return new AnnotationWriter(cw, false, annd, null, 0); 564 } 565 visitAnnotation( final String desc, final boolean visible)566 public AnnotationVisitor visitAnnotation( 567 final String desc, 568 final boolean visible) 569 { 570 ByteVector bv = new ByteVector(); 571 // write type, and reserve space for values count 572 bv.putShort(cw.newUTF8(desc)).putShort(0); 573 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); 574 if (visible) { 575 aw.next = anns; 576 anns = aw; 577 } else { 578 aw.next = ianns; 579 ianns = aw; 580 } 581 return aw; 582 } 583 visitParameterAnnotation( final int parameter, final String desc, final boolean visible)584 public AnnotationVisitor visitParameterAnnotation( 585 final int parameter, 586 final String desc, 587 final boolean visible) 588 { 589 ByteVector bv = new ByteVector(); 590 // write type, and reserve space for values count 591 bv.putShort(cw.newUTF8(desc)).putShort(0); 592 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); 593 if (visible) { 594 if (panns == null) { 595 panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; 596 } 597 aw.next = panns[parameter]; 598 panns[parameter] = aw; 599 } else { 600 if (ipanns == null) { 601 ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; 602 } 603 aw.next = ipanns[parameter]; 604 ipanns[parameter] = aw; 605 } 606 return aw; 607 } 608 visitAttribute(final Attribute attr)609 public void visitAttribute(final Attribute attr) { 610 if (attr.isCodeAttribute()) { 611 attr.next = cattrs; 612 cattrs = attr; 613 } else { 614 attr.next = attrs; 615 attrs = attr; 616 } 617 } 618 visitCode()619 public void visitCode() { 620 } 621 visitInsn(final int opcode)622 public void visitInsn(final int opcode) { 623 if (computeMaxs) { 624 // updates current and max stack sizes 625 int size = stackSize + SIZE[opcode]; 626 if (size > maxStackSize) { 627 maxStackSize = size; 628 } 629 stackSize = size; 630 // if opcode == ATHROW or xRETURN, ends current block (no successor) 631 if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) 632 || opcode == Opcodes.ATHROW) 633 { 634 if (currentBlock != null) { 635 currentBlock.maxStackSize = maxStackSize; 636 currentBlock = null; 637 } 638 } 639 } 640 // adds the instruction to the bytecode of the method 641 code.putByte(opcode); 642 } 643 visitIntInsn(final int opcode, final int operand)644 public void visitIntInsn(final int opcode, final int operand) { 645 if (computeMaxs && opcode != Opcodes.NEWARRAY) { 646 // updates current and max stack sizes only if opcode == NEWARRAY 647 // (stack size variation = 0 for BIPUSH or SIPUSH) 648 int size = stackSize + 1; 649 if (size > maxStackSize) { 650 maxStackSize = size; 651 } 652 stackSize = size; 653 } 654 // adds the instruction to the bytecode of the method 655 if (opcode == Opcodes.SIPUSH) { 656 code.put12(opcode, operand); 657 } else { // BIPUSH or NEWARRAY 658 code.put11(opcode, operand); 659 } 660 } 661 visitVarInsn(final int opcode, final int var)662 public void visitVarInsn(final int opcode, final int var) { 663 if (computeMaxs) { 664 // updates current and max stack sizes 665 if (opcode == Opcodes.RET) { 666 // no stack change, but end of current block (no successor) 667 if (currentBlock != null) { 668 currentBlock.maxStackSize = maxStackSize; 669 currentBlock = null; 670 } 671 } else { // xLOAD or xSTORE 672 int size = stackSize + SIZE[opcode]; 673 if (size > maxStackSize) { 674 maxStackSize = size; 675 } 676 stackSize = size; 677 } 678 // updates max locals 679 int n; 680 if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD 681 || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) 682 { 683 n = var + 2; 684 } else { 685 n = var + 1; 686 } 687 if (n > maxLocals) { 688 maxLocals = n; 689 } 690 } 691 // adds the instruction to the bytecode of the method 692 if (var < 4 && opcode != Opcodes.RET) { 693 int opt; 694 if (opcode < Opcodes.ISTORE) { 695 /* ILOAD_0 */ 696 opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; 697 } else { 698 /* ISTORE_0 */ 699 opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; 700 } 701 code.putByte(opt); 702 } else if (var >= 256) { 703 code.putByte(196 /* WIDE */).put12(opcode, var); 704 } else { 705 code.put11(opcode, var); 706 } 707 } 708 visitTypeInsn(final int opcode, final String desc)709 public void visitTypeInsn(final int opcode, final String desc) { 710 if (computeMaxs && opcode == Opcodes.NEW) { 711 // updates current and max stack sizes only if opcode == NEW 712 // (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF) 713 int size = stackSize + 1; 714 if (size > maxStackSize) { 715 maxStackSize = size; 716 } 717 stackSize = size; 718 } 719 // adds the instruction to the bytecode of the method 720 code.put12(opcode, cw.newClass(desc)); 721 } 722 visitFieldInsn( final int opcode, final String owner, final String name, final String desc)723 public void visitFieldInsn( 724 final int opcode, 725 final String owner, 726 final String name, 727 final String desc) 728 { 729 if (computeMaxs) { 730 int size; 731 // computes the stack size variation 732 char c = desc.charAt(0); 733 switch (opcode) { 734 case Opcodes.GETSTATIC: 735 size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); 736 break; 737 case Opcodes.PUTSTATIC: 738 size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); 739 break; 740 case Opcodes.GETFIELD: 741 size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); 742 break; 743 // case Constants.PUTFIELD: 744 default: 745 size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); 746 break; 747 } 748 // updates current and max stack sizes 749 if (size > maxStackSize) { 750 maxStackSize = size; 751 } 752 stackSize = size; 753 } 754 // adds the instruction to the bytecode of the method 755 code.put12(opcode, cw.newField(owner, name, desc)); 756 } 757 visitMethodInsn( final int opcode, final String owner, final String name, final String desc)758 public void visitMethodInsn( 759 final int opcode, 760 final String owner, 761 final String name, 762 final String desc) 763 { 764 boolean itf = opcode == Opcodes.INVOKEINTERFACE; 765 Item i = cw.newMethodItem(owner, name, desc, itf); 766 int argSize = i.intVal; 767 if (computeMaxs) { 768 /* 769 * computes the stack size variation. In order not to recompute 770 * several times this variation for the same Item, we use the intVal 771 * field of this item to store this variation, once it has been 772 * computed. More precisely this intVal field stores the sizes of 773 * the arguments and of the return value corresponding to desc. 774 */ 775 if (argSize == 0) { 776 // the above sizes have not been computed yet, so we compute 777 // them... 778 argSize = getArgumentsAndReturnSizes(desc); 779 // ... and we save them in order not to recompute them in the 780 // future 781 i.intVal = argSize; 782 } 783 int size; 784 if (opcode == Opcodes.INVOKESTATIC) { 785 size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; 786 } else { 787 size = stackSize - (argSize >> 2) + (argSize & 0x03); 788 } 789 // updates current and max stack sizes 790 if (size > maxStackSize) { 791 maxStackSize = size; 792 } 793 stackSize = size; 794 } 795 // adds the instruction to the bytecode of the method 796 if (itf) { 797 if (!computeMaxs) { 798 if (argSize == 0) { 799 argSize = getArgumentsAndReturnSizes(desc); 800 i.intVal = argSize; 801 } 802 } 803 code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); 804 } else { 805 code.put12(opcode, i.index); 806 } 807 } 808 visitJumpInsn(final int opcode, final Label label)809 public void visitJumpInsn(final int opcode, final Label label) { 810 if (computeMaxs) { 811 if (opcode == Opcodes.GOTO) { 812 // no stack change, but end of current block (with one new 813 // successor) 814 if (currentBlock != null) { 815 currentBlock.maxStackSize = maxStackSize; 816 addSuccessor(stackSize, label); 817 currentBlock = null; 818 } 819 } else if (opcode == Opcodes.JSR) { 820 if (currentBlock != null) { 821 addSuccessor(stackSize + 1, label); 822 } 823 } else { 824 // updates current stack size (max stack size unchanged because 825 // stack size variation always negative in this case) 826 stackSize += SIZE[opcode]; 827 if (currentBlock != null) { 828 addSuccessor(stackSize, label); 829 } 830 } 831 } 832 // adds the instruction to the bytecode of the method 833 if (label.resolved && label.position - code.length < Short.MIN_VALUE) { 834 /* 835 * case of a backward jump with an offset < -32768. In this case we 836 * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx 837 * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the 838 * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'> 839 * designates the instruction just after the GOTO_W. 840 */ 841 if (opcode == Opcodes.GOTO) { 842 code.putByte(200); // GOTO_W 843 } else if (opcode == Opcodes.JSR) { 844 code.putByte(201); // JSR_W 845 } else { 846 code.putByte(opcode <= 166 847 ? ((opcode + 1) ^ 1) - 1 848 : opcode ^ 1); 849 code.putShort(8); // jump offset 850 code.putByte(200); // GOTO_W 851 } 852 label.put(this, code, code.length - 1, true); 853 } else { 854 /* 855 * case of a backward jump with an offset >= -32768, or of a forward 856 * jump with, of course, an unknown offset. In these cases we store 857 * the offset in 2 bytes (which will be increased in 858 * resizeInstructions, if needed). 859 */ 860 code.putByte(opcode); 861 label.put(this, code, code.length - 1, false); 862 } 863 } 864 visitLabel(final Label label)865 public void visitLabel(final Label label) { 866 if (computeMaxs) { 867 if (currentBlock != null) { 868 // ends current block (with one new successor) 869 currentBlock.maxStackSize = maxStackSize; 870 addSuccessor(stackSize, label); 871 } 872 // begins a new current block, 873 // resets the relative current and max stack sizes 874 currentBlock = label; 875 stackSize = 0; 876 maxStackSize = 0; 877 } 878 // resolves previous forward references to label, if any 879 resize |= label.resolve(this, code.length, code.data); 880 } 881 visitLdcInsn(final Object cst)882 public void visitLdcInsn(final Object cst) { 883 Item i = cw.newConstItem(cst); 884 if (computeMaxs) { 885 int size; 886 // computes the stack size variation 887 if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { 888 size = stackSize + 2; 889 } else { 890 size = stackSize + 1; 891 } 892 // updates current and max stack sizes 893 if (size > maxStackSize) { 894 maxStackSize = size; 895 } 896 stackSize = size; 897 } 898 // adds the instruction to the bytecode of the method 899 int index = i.index; 900 if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { 901 code.put12(20 /* LDC2_W */, index); 902 } else if (index >= 256) { 903 code.put12(19 /* LDC_W */, index); 904 } else { 905 code.put11(Opcodes.LDC, index); 906 } 907 } 908 visitIincInsn(final int var, final int increment)909 public void visitIincInsn(final int var, final int increment) { 910 if (computeMaxs) { 911 // updates max locals only (no stack change) 912 int n = var + 1; 913 if (n > maxLocals) { 914 maxLocals = n; 915 } 916 } 917 // adds the instruction to the bytecode of the method 918 if ((var > 255) || (increment > 127) || (increment < -128)) { 919 code.putByte(196 /* WIDE */) 920 .put12(Opcodes.IINC, var) 921 .putShort(increment); 922 } else { 923 code.putByte(Opcodes.IINC).put11(var, increment); 924 } 925 } 926 visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label labels[])927 public void visitTableSwitchInsn( 928 final int min, 929 final int max, 930 final Label dflt, 931 final Label labels[]) 932 { 933 if (computeMaxs) { 934 // updates current stack size (max stack size unchanged) 935 --stackSize; 936 // ends current block (with many new successors) 937 if (currentBlock != null) { 938 currentBlock.maxStackSize = maxStackSize; 939 addSuccessor(stackSize, dflt); 940 for (int i = 0; i < labels.length; ++i) { 941 addSuccessor(stackSize, labels[i]); 942 } 943 currentBlock = null; 944 } 945 } 946 // adds the instruction to the bytecode of the method 947 int source = code.length; 948 code.putByte(Opcodes.TABLESWITCH); 949 while (code.length % 4 != 0) { 950 code.putByte(0); 951 } 952 dflt.put(this, code, source, true); 953 code.putInt(min).putInt(max); 954 for (int i = 0; i < labels.length; ++i) { 955 labels[i].put(this, code, source, true); 956 } 957 } 958 visitLookupSwitchInsn( final Label dflt, final int keys[], final Label labels[])959 public void visitLookupSwitchInsn( 960 final Label dflt, 961 final int keys[], 962 final Label labels[]) 963 { 964 if (computeMaxs) { 965 // updates current stack size (max stack size unchanged) 966 --stackSize; 967 // ends current block (with many new successors) 968 if (currentBlock != null) { 969 currentBlock.maxStackSize = maxStackSize; 970 addSuccessor(stackSize, dflt); 971 for (int i = 0; i < labels.length; ++i) { 972 addSuccessor(stackSize, labels[i]); 973 } 974 currentBlock = null; 975 } 976 } 977 // adds the instruction to the bytecode of the method 978 int source = code.length; 979 code.putByte(Opcodes.LOOKUPSWITCH); 980 while (code.length % 4 != 0) { 981 code.putByte(0); 982 } 983 dflt.put(this, code, source, true); 984 code.putInt(labels.length); 985 for (int i = 0; i < labels.length; ++i) { 986 code.putInt(keys[i]); 987 labels[i].put(this, code, source, true); 988 } 989 } 990 visitMultiANewArrayInsn(final String desc, final int dims)991 public void visitMultiANewArrayInsn(final String desc, final int dims) { 992 if (computeMaxs) { 993 // updates current stack size (max stack size unchanged because 994 // stack size variation always negative or null) 995 stackSize += 1 - dims; 996 } 997 // adds the instruction to the bytecode of the method 998 code.put12(Opcodes.MULTIANEWARRAY, cw.newClass(desc)).putByte(dims); 999 } 1000 visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type)1001 public void visitTryCatchBlock( 1002 final Label start, 1003 final Label end, 1004 final Label handler, 1005 final String type) 1006 { 1007 if (computeMaxs) { 1008 // pushes handler block onto the stack of blocks to be visited 1009 if (!handler.pushed) { 1010 handler.beginStackSize = 1; 1011 handler.pushed = true; 1012 handler.next = blockStack; 1013 blockStack = handler; 1014 } 1015 } 1016 ++catchCount; 1017 Handler h = new Handler(); 1018 h.start = start; 1019 h.end = end; 1020 h.handler = handler; 1021 h.desc = type; 1022 h.type = type != null ? cw.newClass(type) : 0; 1023 if (lastHandler == null) { 1024 catchTable = h; 1025 } else { 1026 lastHandler.next = h; 1027 } 1028 lastHandler = h; 1029 } 1030 visitLocalVariable( final String name, final String desc, final String signature, final Label start, final Label end, final int index)1031 public void visitLocalVariable( 1032 final String name, 1033 final String desc, 1034 final String signature, 1035 final Label start, 1036 final Label end, 1037 final int index) 1038 { 1039 if (signature != null) { 1040 if (localVarType == null) { 1041 localVarType = new ByteVector(); 1042 } 1043 ++localVarTypeCount; 1044 localVarType.putShort(start.position) 1045 .putShort(end.position - start.position) 1046 .putShort(cw.newUTF8(name)) 1047 .putShort(cw.newUTF8(signature)) 1048 .putShort(index); 1049 } 1050 if (localVar == null) { 1051 localVar = new ByteVector(); 1052 } 1053 ++localVarCount; 1054 localVar.putShort(start.position) 1055 .putShort(end.position - start.position) 1056 .putShort(cw.newUTF8(name)) 1057 .putShort(cw.newUTF8(desc)) 1058 .putShort(index); 1059 1060 if(computeMaxs) { 1061 // updates max locals 1062 char c = desc.charAt(0); 1063 int n = index + ( c=='L' || c=='D' ? 2 : 1); 1064 if (n > maxLocals) { 1065 maxLocals = n; 1066 } 1067 } 1068 } 1069 visitLineNumber(final int line, final Label start)1070 public void visitLineNumber(final int line, final Label start) { 1071 if (lineNumber == null) { 1072 lineNumber = new ByteVector(); 1073 } 1074 ++lineNumberCount; 1075 lineNumber.putShort(start.position); 1076 lineNumber.putShort(line); 1077 } 1078 visitMaxs(final int maxStack, final int maxLocals)1079 public void visitMaxs(final int maxStack, final int maxLocals) { 1080 if (computeMaxs) { 1081 // true (non relative) max stack size 1082 int max = 0; 1083 /* 1084 * control flow analysis algorithm: while the block stack is not 1085 * empty, pop a block from this stack, update the max stack size, 1086 * compute the true (non relative) begin stack size of the 1087 * successors of this block, and push these successors onto the 1088 * stack (unless they have already been pushed onto the stack). 1089 * Note: by hypothesis, the {@link Label#beginStackSize} of the 1090 * blocks in the block stack are the true (non relative) beginning 1091 * stack sizes of these blocks. 1092 */ 1093 Label stack = blockStack; 1094 while (stack != null) { 1095 // pops a block from the stack 1096 Label l = stack; 1097 stack = stack.next; 1098 // computes the true (non relative) max stack size of this block 1099 int start = l.beginStackSize; 1100 int blockMax = start + l.maxStackSize; 1101 // updates the global max stack size 1102 if (blockMax > max) { 1103 max = blockMax; 1104 } 1105 // analyses the successors of the block 1106 Edge b = l.successors; 1107 while (b != null) { 1108 l = b.successor; 1109 // if this successor has not already been pushed onto the 1110 // stack... 1111 if (!l.pushed) { 1112 // computes the true beginning stack size of this 1113 // successor block 1114 l.beginStackSize = start + b.stackSize; 1115 // pushes this successor onto the stack 1116 l.pushed = true; 1117 l.next = stack; 1118 stack = l; 1119 } 1120 b = b.next; 1121 } 1122 } 1123 this.maxStack = max; 1124 } else { 1125 this.maxStack = maxStack; 1126 this.maxLocals = maxLocals; 1127 } 1128 } 1129 visitEnd()1130 public void visitEnd() { 1131 } 1132 1133 // ------------------------------------------------------------------------ 1134 // Utility methods: control flow analysis algorithm 1135 // ------------------------------------------------------------------------ 1136 1137 /** 1138 * Computes the size of the arguments and of the return value of a method. 1139 * 1140 * @param desc the descriptor of a method. 1141 * @return the size of the arguments of the method (plus one for the 1142 * implicit this argument), argSize, and the size of its return 1143 * value, retSize, packed into a single int i = 1144 * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal 1145 * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). 1146 */ getArgumentsAndReturnSizes(final String desc)1147 private static int getArgumentsAndReturnSizes(final String desc) { 1148 int n = 1; 1149 int c = 1; 1150 while (true) { 1151 char car = desc.charAt(c++); 1152 if (car == ')') { 1153 car = desc.charAt(c); 1154 return n << 2 1155 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); 1156 } else if (car == 'L') { 1157 while (desc.charAt(c++) != ';') { 1158 } 1159 n += 1; 1160 } else if (car == '[') { 1161 while ((car = desc.charAt(c)) == '[') { 1162 ++c; 1163 } 1164 if (car == 'D' || car == 'J') { 1165 n -= 1; 1166 } 1167 } else if (car == 'D' || car == 'J') { 1168 n += 2; 1169 } else { 1170 n += 1; 1171 } 1172 } 1173 } 1174 1175 /** 1176 * Adds a successor to the {@link #currentBlock currentBlock} block. 1177 * 1178 * @param stackSize the current (relative) stack size in the current block. 1179 * @param successor the successor block to be added to the current block. 1180 */ addSuccessor(final int stackSize, final Label successor)1181 private void addSuccessor(final int stackSize, final Label successor) { 1182 Edge b = new Edge(); 1183 // initializes the previous Edge object... 1184 b.stackSize = stackSize; 1185 b.successor = successor; 1186 // ...and adds it to the successor list of the currentBlock block 1187 b.next = currentBlock.successors; 1188 currentBlock.successors = b; 1189 } 1190 1191 // ------------------------------------------------------------------------ 1192 // Utility methods: dump bytecode array 1193 // ------------------------------------------------------------------------ 1194 1195 /** 1196 * Returns the size of the bytecode of this method. 1197 * 1198 * @return the size of the bytecode of this method. 1199 */ getSize()1200 final int getSize() { 1201 if (classReaderOffset != 0) { 1202 return 6 + classReaderLength; 1203 } 1204 if (resize) { 1205 // replaces the temporary jump opcodes introduced by Label.resolve. 1206 resizeInstructions(new int[0], new int[0], 0); 1207 } 1208 int size = 8; 1209 if (code.length > 0) { 1210 cw.newUTF8("Code"); 1211 size += 18 + code.length + 8 * catchCount; 1212 if (localVar != null) { 1213 cw.newUTF8("LocalVariableTable"); 1214 size += 8 + localVar.length; 1215 } 1216 if (localVarType != null) { 1217 cw.newUTF8("LocalVariableTypeTable"); 1218 size += 8 + localVarType.length; 1219 } 1220 if (lineNumber != null) { 1221 cw.newUTF8("LineNumberTable"); 1222 size += 8 + lineNumber.length; 1223 } 1224 if (cattrs != null) { 1225 size += cattrs.getSize(cw, 1226 code.data, 1227 code.length, 1228 maxStack, 1229 maxLocals); 1230 } 1231 } 1232 if (exceptionCount > 0) { 1233 cw.newUTF8("Exceptions"); 1234 size += 8 + 2 * exceptionCount; 1235 } 1236 if ((access & Opcodes.ACC_SYNTHETIC) != 0 1237 && (cw.version & 0xffff) < Opcodes.V1_5) 1238 { 1239 cw.newUTF8("Synthetic"); 1240 size += 6; 1241 } 1242 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 1243 cw.newUTF8("Deprecated"); 1244 size += 6; 1245 } 1246 if (cw.version == Opcodes.V1_4) { 1247 if ((access & Opcodes.ACC_VARARGS) != 0) { 1248 cw.newUTF8("Varargs"); 1249 size += 6; 1250 } 1251 if ((access & Opcodes.ACC_BRIDGE) != 0) { 1252 cw.newUTF8("Bridge"); 1253 size += 6; 1254 } 1255 } 1256 if (signature != null) { 1257 cw.newUTF8("Signature"); 1258 cw.newUTF8(signature); 1259 size += 8; 1260 } 1261 if (annd != null) { 1262 cw.newUTF8("AnnotationDefault"); 1263 size += 6 + annd.length; 1264 } 1265 if (anns != null) { 1266 cw.newUTF8("RuntimeVisibleAnnotations"); 1267 size += 8 + anns.getSize(); 1268 } 1269 if (ianns != null) { 1270 cw.newUTF8("RuntimeInvisibleAnnotations"); 1271 size += 8 + ianns.getSize(); 1272 } 1273 if (panns != null) { 1274 cw.newUTF8("RuntimeVisibleParameterAnnotations"); 1275 size += 7 + 2 * panns.length; 1276 for (int i = panns.length - 1; i >= 0; --i) { 1277 size += panns[i] == null ? 0 : panns[i].getSize(); 1278 } 1279 } 1280 if (ipanns != null) { 1281 cw.newUTF8("RuntimeInvisibleParameterAnnotations"); 1282 size += 7 + 2 * ipanns.length; 1283 for (int i = ipanns.length - 1; i >= 0; --i) { 1284 size += ipanns[i] == null ? 0 : ipanns[i].getSize(); 1285 } 1286 } 1287 if (attrs != null) { 1288 size += attrs.getSize(cw, null, 0, -1, -1); 1289 } 1290 return size; 1291 } 1292 1293 /** 1294 * Puts the bytecode of this method in the given byte vector. 1295 * 1296 * @param out the byte vector into which the bytecode of this method must be 1297 * copied. 1298 */ put(final ByteVector out)1299 final void put(final ByteVector out) { 1300 out.putShort(access).putShort(name).putShort(desc); 1301 if (classReaderOffset != 0) { 1302 out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); 1303 return; 1304 } 1305 int attributeCount = 0; 1306 if (code.length > 0) { 1307 ++attributeCount; 1308 } 1309 if (exceptionCount > 0) { 1310 ++attributeCount; 1311 } 1312 if ((access & Opcodes.ACC_SYNTHETIC) != 0 1313 && (cw.version & 0xffff) < Opcodes.V1_5) 1314 { 1315 ++attributeCount; 1316 } 1317 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 1318 ++attributeCount; 1319 } 1320 if (cw.version == Opcodes.V1_4) { 1321 if ((access & Opcodes.ACC_VARARGS) != 0) { 1322 ++attributeCount; 1323 } 1324 if ((access & Opcodes.ACC_BRIDGE) != 0) { 1325 ++attributeCount; 1326 } 1327 } 1328 if (signature != null) { 1329 ++attributeCount; 1330 } 1331 if (annd != null) { 1332 ++attributeCount; 1333 } 1334 if (anns != null) { 1335 ++attributeCount; 1336 } 1337 if (ianns != null) { 1338 ++attributeCount; 1339 } 1340 if (panns != null) { 1341 ++attributeCount; 1342 } 1343 if (ipanns != null) { 1344 ++attributeCount; 1345 } 1346 if (attrs != null) { 1347 attributeCount += attrs.getCount(); 1348 } 1349 out.putShort(attributeCount); 1350 if (code.length > 0) { 1351 int size = 12 + code.length + 8 * catchCount; 1352 if (localVar != null) { 1353 size += 8 + localVar.length; 1354 } 1355 if (localVarType != null) { 1356 size += 8 + localVarType.length; 1357 } 1358 if (lineNumber != null) { 1359 size += 8 + lineNumber.length; 1360 } 1361 if (cattrs != null) { 1362 size += cattrs.getSize(cw, 1363 code.data, 1364 code.length, 1365 maxStack, 1366 maxLocals); 1367 } 1368 out.putShort(cw.newUTF8("Code")).putInt(size); 1369 out.putShort(maxStack).putShort(maxLocals); 1370 out.putInt(code.length).putByteArray(code.data, 0, code.length); 1371 out.putShort(catchCount); 1372 if (catchCount > 0) { 1373 Handler h = catchTable; 1374 while (h != null) { 1375 out.putShort(h.start.position) 1376 .putShort(h.end.position) 1377 .putShort(h.handler.position) 1378 .putShort(h.type); 1379 h = h.next; 1380 } 1381 } 1382 attributeCount = 0; 1383 if (localVar != null) { 1384 ++attributeCount; 1385 } 1386 if (localVarType != null) { 1387 ++attributeCount; 1388 } 1389 if (lineNumber != null) { 1390 ++attributeCount; 1391 } 1392 if (cattrs != null) { 1393 attributeCount += cattrs.getCount(); 1394 } 1395 out.putShort(attributeCount); 1396 if (localVar != null) { 1397 out.putShort(cw.newUTF8("LocalVariableTable")); 1398 out.putInt(localVar.length + 2).putShort(localVarCount); 1399 out.putByteArray(localVar.data, 0, localVar.length); 1400 } 1401 if (localVarType != null) { 1402 out.putShort(cw.newUTF8("LocalVariableTypeTable")); 1403 out.putInt(localVarType.length + 2).putShort(localVarTypeCount); 1404 out.putByteArray(localVarType.data, 0, localVarType.length); 1405 } 1406 if (lineNumber != null) { 1407 out.putShort(cw.newUTF8("LineNumberTable")); 1408 out.putInt(lineNumber.length + 2).putShort(lineNumberCount); 1409 out.putByteArray(lineNumber.data, 0, lineNumber.length); 1410 } 1411 if (cattrs != null) { 1412 cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); 1413 } 1414 } 1415 if (exceptionCount > 0) { 1416 out.putShort(cw.newUTF8("Exceptions")) 1417 .putInt(2 * exceptionCount + 2); 1418 out.putShort(exceptionCount); 1419 for (int i = 0; i < exceptionCount; ++i) { 1420 out.putShort(exceptions[i]); 1421 } 1422 } 1423 if ((access & Opcodes.ACC_SYNTHETIC) != 0 1424 && (cw.version & 0xffff) < Opcodes.V1_5) 1425 { 1426 out.putShort(cw.newUTF8("Synthetic")).putInt(0); 1427 } 1428 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 1429 out.putShort(cw.newUTF8("Deprecated")).putInt(0); 1430 } 1431 if (cw.version == Opcodes.V1_4) { 1432 if ((access & Opcodes.ACC_VARARGS) != 0) { 1433 out.putShort(cw.newUTF8("Varargs")).putInt(0); 1434 } 1435 if ((access & Opcodes.ACC_BRIDGE) != 0) { 1436 out.putShort(cw.newUTF8("Bridge")).putInt(0); 1437 } 1438 } 1439 if (signature != null) { 1440 out.putShort(cw.newUTF8("Signature")) 1441 .putInt(2) 1442 .putShort(cw.newUTF8(signature)); 1443 } 1444 if (annd != null) { 1445 out.putShort(cw.newUTF8("AnnotationDefault")); 1446 out.putInt(annd.length); 1447 out.putByteArray(annd.data, 0, annd.length); 1448 } 1449 if (anns != null) { 1450 out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); 1451 anns.put(out); 1452 } 1453 if (ianns != null) { 1454 out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); 1455 ianns.put(out); 1456 } 1457 if (panns != null) { 1458 out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); 1459 AnnotationWriter.put(panns, out); 1460 } 1461 if (ipanns != null) { 1462 out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); 1463 AnnotationWriter.put(ipanns, out); 1464 } 1465 if (attrs != null) { 1466 attrs.put(cw, null, 0, -1, -1, out); 1467 } 1468 } 1469 1470 // ------------------------------------------------------------------------ 1471 // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) 1472 // ------------------------------------------------------------------------ 1473 1474 /** 1475 * Resizes the designated instructions, while keeping jump offsets and 1476 * instruction addresses consistent. This may require to resize other 1477 * existing instructions, or even to introduce new instructions: for 1478 * example, increasing the size of an instruction by 2 at the middle of a 1479 * method can increases the offset of an IFEQ instruction from 32766 to 1480 * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 1481 * 32765. This, in turn, may require to increase the size of another jump 1482 * instruction, and so on... All these operations are handled automatically 1483 * by this method. <p> <i>This method must be called after all the method 1484 * that is being built has been visited</i>. In particular, the 1485 * {@link Label Label} objects used to construct the method are no longer 1486 * valid after this method has been called. 1487 * 1488 * @param indexes current positions of the instructions to be resized. Each 1489 * instruction must be designated by the index of its <i>last</i> 1490 * byte, plus one (or, in other words, by the index of the <i>first</i> 1491 * byte of the <i>next</i> instruction). 1492 * @param sizes the number of bytes to be <i>added</i> to the above 1493 * instructions. More precisely, for each i < <tt>len</tt>, 1494 * <tt>sizes</tt>[i] bytes will be added at the end of the 1495 * instruction designated by <tt>indexes</tt>[i] or, if 1496 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| 1497 * bytes of the instruction will be removed (the instruction size 1498 * <i>must not</i> become negative or null). The gaps introduced by 1499 * this method must be filled in "manually" in {@link #code code} 1500 * method. 1501 * @param len the number of instruction to be resized. Must be smaller than 1502 * or equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length. 1503 * @return the <tt>indexes</tt> array, which now contains the new 1504 * positions of the resized instructions (designated as above). 1505 */ resizeInstructions( final int[] indexes, final int[] sizes, final int len)1506 private int[] resizeInstructions( 1507 final int[] indexes, 1508 final int[] sizes, 1509 final int len) 1510 { 1511 byte[] b = code.data; // bytecode of the method 1512 int u, v, label; // indexes in b 1513 int i, j; // loop indexes 1514 1515 /* 1516 * 1st step: As explained above, resizing an instruction may require to 1517 * resize another one, which may require to resize yet another one, and 1518 * so on. The first step of the algorithm consists in finding all the 1519 * instructions that need to be resized, without modifying the code. 1520 * This is done by the following "fix point" algorithm: 1521 * 1522 * Parse the code to find the jump instructions whose offset will need 1523 * more than 2 bytes to be stored (the future offset is computed from 1524 * the current offset and from the number of bytes that will be inserted 1525 * or removed between the source and target instructions). For each such 1526 * instruction, adds an entry in (a copy of) the indexes and sizes 1527 * arrays (if this has not already been done in a previous iteration!). 1528 * 1529 * If at least one entry has been added during the previous step, go 1530 * back to the beginning, otherwise stop. 1531 * 1532 * In fact the real algorithm is complicated by the fact that the size 1533 * of TABLESWITCH and LOOKUPSWITCH instructions depends on their 1534 * position in the bytecode (because of padding). In order to ensure the 1535 * convergence of the algorithm, the number of bytes to be added or 1536 * removed from these instructions is over estimated during the previous 1537 * loop, and computed exactly only after the loop is finished (this 1538 * requires another pass to parse the bytecode of the method). 1539 */ 1540 int[] allIndexes = new int[len]; // copy of indexes 1541 int[] allSizes = new int[len]; // copy of sizes 1542 boolean[] resize; // instructions to be resized 1543 int newOffset; // future offset of a jump instruction 1544 1545 System.arraycopy(indexes, 0, allIndexes, 0, len); 1546 System.arraycopy(sizes, 0, allSizes, 0, len); 1547 resize = new boolean[code.length]; 1548 1549 // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done 1550 int state = 3; 1551 do { 1552 if (state == 3) { 1553 state = 2; 1554 } 1555 u = 0; 1556 while (u < b.length) { 1557 int opcode = b[u] & 0xFF; // opcode of current instruction 1558 int insert = 0; // bytes to be added after this instruction 1559 1560 switch (ClassWriter.TYPE[opcode]) { 1561 case ClassWriter.NOARG_INSN: 1562 case ClassWriter.IMPLVAR_INSN: 1563 u += 1; 1564 break; 1565 case ClassWriter.LABEL_INSN: 1566 if (opcode > 201) { 1567 // converts temporary opcodes 202 to 217, 218 and 1568 // 219 to IFEQ ... JSR (inclusive), IFNULL and 1569 // IFNONNULL 1570 opcode = opcode < 218 ? opcode - 49 : opcode - 20; 1571 label = u + readUnsignedShort(b, u + 1); 1572 } else { 1573 label = u + readShort(b, u + 1); 1574 } 1575 newOffset = getNewOffset(allIndexes, allSizes, u, label); 1576 if (newOffset < Short.MIN_VALUE 1577 || newOffset > Short.MAX_VALUE) 1578 { 1579 if (!resize[u]) { 1580 if (opcode == Opcodes.GOTO 1581 || opcode == Opcodes.JSR) 1582 { 1583 // two additional bytes will be required to 1584 // replace this GOTO or JSR instruction with 1585 // a GOTO_W or a JSR_W 1586 insert = 2; 1587 } else { 1588 // five additional bytes will be required to 1589 // replace this IFxxx <l> instruction with 1590 // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx 1591 // is the "opposite" opcode of IFxxx (i.e., 1592 // IFNE for IFEQ) and where <l'> designates 1593 // the instruction just after the GOTO_W. 1594 insert = 5; 1595 } 1596 resize[u] = true; 1597 } 1598 } 1599 u += 3; 1600 break; 1601 case ClassWriter.LABELW_INSN: 1602 u += 5; 1603 break; 1604 case ClassWriter.TABL_INSN: 1605 if (state == 1) { 1606 // true number of bytes to be added (or removed) 1607 // from this instruction = (future number of padding 1608 // bytes - current number of padding byte) - 1609 // previously over estimated variation = 1610 // = ((3 - newOffset%4) - (3 - u%4)) - u%4 1611 // = (-newOffset%4 + u%4) - u%4 1612 // = -(newOffset & 3) 1613 newOffset = getNewOffset(allIndexes, allSizes, 0, u); 1614 insert = -(newOffset & 3); 1615 } else if (!resize[u]) { 1616 // over estimation of the number of bytes to be 1617 // added to this instruction = 3 - current number 1618 // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 1619 insert = u & 3; 1620 resize[u] = true; 1621 } 1622 // skips instruction 1623 u = u + 4 - (u & 3); 1624 u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; 1625 break; 1626 case ClassWriter.LOOK_INSN: 1627 if (state == 1) { 1628 // like TABL_INSN 1629 newOffset = getNewOffset(allIndexes, allSizes, 0, u); 1630 insert = -(newOffset & 3); 1631 } else if (!resize[u]) { 1632 // like TABL_INSN 1633 insert = u & 3; 1634 resize[u] = true; 1635 } 1636 // skips instruction 1637 u = u + 4 - (u & 3); 1638 u += 8 * readInt(b, u + 4) + 8; 1639 break; 1640 case ClassWriter.WIDE_INSN: 1641 opcode = b[u + 1] & 0xFF; 1642 if (opcode == Opcodes.IINC) { 1643 u += 6; 1644 } else { 1645 u += 4; 1646 } 1647 break; 1648 case ClassWriter.VAR_INSN: 1649 case ClassWriter.SBYTE_INSN: 1650 case ClassWriter.LDC_INSN: 1651 u += 2; 1652 break; 1653 case ClassWriter.SHORT_INSN: 1654 case ClassWriter.LDCW_INSN: 1655 case ClassWriter.FIELDORMETH_INSN: 1656 case ClassWriter.TYPE_INSN: 1657 case ClassWriter.IINC_INSN: 1658 u += 3; 1659 break; 1660 case ClassWriter.ITFMETH_INSN: 1661 u += 5; 1662 break; 1663 // case ClassWriter.MANA_INSN: 1664 default: 1665 u += 4; 1666 break; 1667 } 1668 if (insert != 0) { 1669 // adds a new (u, insert) entry in the allIndexes and 1670 // allSizes arrays 1671 int[] newIndexes = new int[allIndexes.length + 1]; 1672 int[] newSizes = new int[allSizes.length + 1]; 1673 System.arraycopy(allIndexes, 1674 0, 1675 newIndexes, 1676 0, 1677 allIndexes.length); 1678 System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); 1679 newIndexes[allIndexes.length] = u; 1680 newSizes[allSizes.length] = insert; 1681 allIndexes = newIndexes; 1682 allSizes = newSizes; 1683 if (insert > 0) { 1684 state = 3; 1685 } 1686 } 1687 } 1688 if (state < 3) { 1689 --state; 1690 } 1691 } while (state != 0); 1692 1693 // 2nd step: 1694 // copies the bytecode of the method into a new bytevector, updates the 1695 // offsets, and inserts (or removes) bytes as requested. 1696 1697 ByteVector newCode = new ByteVector(code.length); 1698 1699 u = 0; 1700 while (u < code.length) { 1701 for (i = allIndexes.length - 1; i >= 0; --i) { 1702 if (allIndexes[i] == u) { 1703 if (i < len) { 1704 if (sizes[i] > 0) { 1705 newCode.putByteArray(null, 0, sizes[i]); 1706 } else { 1707 newCode.length += sizes[i]; 1708 } 1709 indexes[i] = newCode.length; 1710 } 1711 } 1712 } 1713 int opcode = b[u] & 0xFF; 1714 switch (ClassWriter.TYPE[opcode]) { 1715 case ClassWriter.NOARG_INSN: 1716 case ClassWriter.IMPLVAR_INSN: 1717 newCode.putByte(opcode); 1718 u += 1; 1719 break; 1720 case ClassWriter.LABEL_INSN: 1721 if (opcode > 201) { 1722 // changes temporary opcodes 202 to 217 (inclusive), 218 1723 // and 219 to IFEQ ... JSR (inclusive), IFNULL and 1724 // IFNONNULL 1725 opcode = opcode < 218 ? opcode - 49 : opcode - 20; 1726 label = u + readUnsignedShort(b, u + 1); 1727 } else { 1728 label = u + readShort(b, u + 1); 1729 } 1730 newOffset = getNewOffset(allIndexes, allSizes, u, label); 1731 if (resize[u]) { 1732 // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx 1733 // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is 1734 // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) 1735 // and where <l'> designates the instruction just after 1736 // the GOTO_W. 1737 if (opcode == Opcodes.GOTO) { 1738 newCode.putByte(200); // GOTO_W 1739 } else if (opcode == Opcodes.JSR) { 1740 newCode.putByte(201); // JSR_W 1741 } else { 1742 newCode.putByte(opcode <= 166 1743 ? ((opcode + 1) ^ 1) - 1 1744 : opcode ^ 1); 1745 newCode.putShort(8); // jump offset 1746 newCode.putByte(200); // GOTO_W 1747 // newOffset now computed from start of GOTO_W 1748 newOffset -= 3; 1749 } 1750 newCode.putInt(newOffset); 1751 } else { 1752 newCode.putByte(opcode); 1753 newCode.putShort(newOffset); 1754 } 1755 u += 3; 1756 break; 1757 case ClassWriter.LABELW_INSN: 1758 label = u + readInt(b, u + 1); 1759 newOffset = getNewOffset(allIndexes, allSizes, u, label); 1760 newCode.putByte(opcode); 1761 newCode.putInt(newOffset); 1762 u += 5; 1763 break; 1764 case ClassWriter.TABL_INSN: 1765 // skips 0 to 3 padding bytes 1766 v = u; 1767 u = u + 4 - (v & 3); 1768 // reads and copies instruction 1769 newCode.putByte(Opcodes.TABLESWITCH); 1770 while (newCode.length % 4 != 0) { 1771 newCode.putByte(0); 1772 } 1773 label = v + readInt(b, u); 1774 u += 4; 1775 newOffset = getNewOffset(allIndexes, allSizes, v, label); 1776 newCode.putInt(newOffset); 1777 j = readInt(b, u); 1778 u += 4; 1779 newCode.putInt(j); 1780 j = readInt(b, u) - j + 1; 1781 u += 4; 1782 newCode.putInt(readInt(b, u - 4)); 1783 for (; j > 0; --j) { 1784 label = v + readInt(b, u); 1785 u += 4; 1786 newOffset = getNewOffset(allIndexes, allSizes, v, label); 1787 newCode.putInt(newOffset); 1788 } 1789 break; 1790 case ClassWriter.LOOK_INSN: 1791 // skips 0 to 3 padding bytes 1792 v = u; 1793 u = u + 4 - (v & 3); 1794 // reads and copies instruction 1795 newCode.putByte(Opcodes.LOOKUPSWITCH); 1796 while (newCode.length % 4 != 0) { 1797 newCode.putByte(0); 1798 } 1799 label = v + readInt(b, u); 1800 u += 4; 1801 newOffset = getNewOffset(allIndexes, allSizes, v, label); 1802 newCode.putInt(newOffset); 1803 j = readInt(b, u); 1804 u += 4; 1805 newCode.putInt(j); 1806 for (; j > 0; --j) { 1807 newCode.putInt(readInt(b, u)); 1808 u += 4; 1809 label = v + readInt(b, u); 1810 u += 4; 1811 newOffset = getNewOffset(allIndexes, allSizes, v, label); 1812 newCode.putInt(newOffset); 1813 } 1814 break; 1815 case ClassWriter.WIDE_INSN: 1816 opcode = b[u + 1] & 0xFF; 1817 if (opcode == Opcodes.IINC) { 1818 newCode.putByteArray(b, u, 6); 1819 u += 6; 1820 } else { 1821 newCode.putByteArray(b, u, 4); 1822 u += 4; 1823 } 1824 break; 1825 case ClassWriter.VAR_INSN: 1826 case ClassWriter.SBYTE_INSN: 1827 case ClassWriter.LDC_INSN: 1828 newCode.putByteArray(b, u, 2); 1829 u += 2; 1830 break; 1831 case ClassWriter.SHORT_INSN: 1832 case ClassWriter.LDCW_INSN: 1833 case ClassWriter.FIELDORMETH_INSN: 1834 case ClassWriter.TYPE_INSN: 1835 case ClassWriter.IINC_INSN: 1836 newCode.putByteArray(b, u, 3); 1837 u += 3; 1838 break; 1839 case ClassWriter.ITFMETH_INSN: 1840 newCode.putByteArray(b, u, 5); 1841 u += 5; 1842 break; 1843 // case MANA_INSN: 1844 default: 1845 newCode.putByteArray(b, u, 4); 1846 u += 4; 1847 break; 1848 } 1849 } 1850 1851 // updates the exception handler block labels 1852 Handler h = catchTable; 1853 while (h != null) { 1854 getNewOffset(allIndexes, allSizes, h.start); 1855 getNewOffset(allIndexes, allSizes, h.end); 1856 getNewOffset(allIndexes, allSizes, h.handler); 1857 h = h.next; 1858 } 1859 for (i = 0; i < 2; ++i) { 1860 ByteVector bv = i == 0 ? localVar : localVarType; 1861 if (bv != null) { 1862 b = bv.data; 1863 u = 0; 1864 while (u < bv.length) { 1865 label = readUnsignedShort(b, u); 1866 newOffset = getNewOffset(allIndexes, allSizes, 0, label); 1867 writeShort(b, u, newOffset); 1868 label += readUnsignedShort(b, u + 2); 1869 newOffset = getNewOffset(allIndexes, allSizes, 0, label) 1870 - newOffset; 1871 writeShort(b, u + 2, newOffset); 1872 u += 10; 1873 } 1874 } 1875 } 1876 if (lineNumber != null) { 1877 b = lineNumber.data; 1878 u = 0; 1879 while (u < lineNumber.length) { 1880 writeShort(b, u, getNewOffset(allIndexes, 1881 allSizes, 1882 0, 1883 readUnsignedShort(b, u))); 1884 u += 4; 1885 } 1886 } 1887 // updates the labels of the other attributes 1888 while (cattrs != null) { 1889 Label[] labels = cattrs.getLabels(); 1890 if (labels != null) { 1891 for (i = labels.length - 1; i >= 0; --i) { 1892 if (!labels[i].resized) { 1893 labels[i].position = getNewOffset(allIndexes, 1894 allSizes, 1895 0, 1896 labels[i].position); 1897 labels[i].resized = true; 1898 } 1899 } 1900 } 1901 } 1902 1903 // replaces old bytecodes with new ones 1904 code = newCode; 1905 1906 // returns the positions of the resized instructions 1907 return indexes; 1908 } 1909 1910 /** 1911 * Reads an unsigned short value in the given byte array. 1912 * 1913 * @param b a byte array. 1914 * @param index the start index of the value to be read. 1915 * @return the read value. 1916 */ readUnsignedShort(final byte[] b, final int index)1917 static int readUnsignedShort(final byte[] b, final int index) { 1918 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); 1919 } 1920 1921 /** 1922 * Reads a signed short value in the given byte array. 1923 * 1924 * @param b a byte array. 1925 * @param index the start index of the value to be read. 1926 * @return the read value. 1927 */ readShort(final byte[] b, final int index)1928 static short readShort(final byte[] b, final int index) { 1929 return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); 1930 } 1931 1932 /** 1933 * Reads a signed int value in the given byte array. 1934 * 1935 * @param b a byte array. 1936 * @param index the start index of the value to be read. 1937 * @return the read value. 1938 */ readInt(final byte[] b, final int index)1939 static int readInt(final byte[] b, final int index) { 1940 return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) 1941 | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); 1942 } 1943 1944 /** 1945 * Writes a short value in the given byte array. 1946 * 1947 * @param b a byte array. 1948 * @param index where the first byte of the short value must be written. 1949 * @param s the value to be written in the given byte array. 1950 */ writeShort(final byte[] b, final int index, final int s)1951 static void writeShort(final byte[] b, final int index, final int s) { 1952 b[index] = (byte) (s >>> 8); 1953 b[index + 1] = (byte) s; 1954 } 1955 1956 /** 1957 * Computes the future value of a bytecode offset. <p> Note: it is possible 1958 * to have several entries for the same instruction in the <tt>indexes</tt> 1959 * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b') 1960 * are equivalent to a single entry (index=a,size=b+b'). 1961 * 1962 * @param indexes current positions of the instructions to be resized. Each 1963 * instruction must be designated by the index of its <i>last</i> 1964 * byte, plus one (or, in other words, by the index of the <i>first</i> 1965 * byte of the <i>next</i> instruction). 1966 * @param sizes the number of bytes to be <i>added</i> to the above 1967 * instructions. More precisely, for each i < <tt>len</tt>, 1968 * <tt>sizes</tt>[i] bytes will be added at the end of the 1969 * instruction designated by <tt>indexes</tt>[i] or, if 1970 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| 1971 * bytes of the instruction will be removed (the instruction size 1972 * <i>must not</i> become negative or null). 1973 * @param begin index of the first byte of the source instruction. 1974 * @param end index of the first byte of the target instruction. 1975 * @return the future value of the given bytecode offset. 1976 */ getNewOffset( final int[] indexes, final int[] sizes, final int begin, final int end)1977 static int getNewOffset( 1978 final int[] indexes, 1979 final int[] sizes, 1980 final int begin, 1981 final int end) 1982 { 1983 int offset = end - begin; 1984 for (int i = 0; i < indexes.length; ++i) { 1985 if (begin < indexes[i] && indexes[i] <= end) { 1986 // forward jump 1987 offset += sizes[i]; 1988 } else if (end < indexes[i] && indexes[i] <= begin) { 1989 // backward jump 1990 offset -= sizes[i]; 1991 } 1992 } 1993 return offset; 1994 } 1995 1996 /** 1997 * Updates the offset of the given label. 1998 * 1999 * @param indexes current positions of the instructions to be resized. Each 2000 * instruction must be designated by the index of its <i>last</i> 2001 * byte, plus one (or, in other words, by the index of the <i>first</i> 2002 * byte of the <i>next</i> instruction). 2003 * @param sizes the number of bytes to be <i>added</i> to the above 2004 * instructions. More precisely, for each i < <tt>len</tt>, 2005 * <tt>sizes</tt>[i] bytes will be added at the end of the 2006 * instruction designated by <tt>indexes</tt>[i] or, if 2007 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| 2008 * bytes of the instruction will be removed (the instruction size 2009 * <i>must not</i> become negative or null). 2010 * @param label the label whose offset must be updated. 2011 */ getNewOffset( final int[] indexes, final int[] sizes, final Label label)2012 static void getNewOffset( 2013 final int[] indexes, 2014 final int[] sizes, 2015 final Label label) 2016 { 2017 if (!label.resized) { 2018 label.position = getNewOffset(indexes, sizes, 0, label.position); 2019 label.resized = true; 2020 } 2021 } 2022 } 2023