0001: /***
0002: * ASM: a very small and fast Java bytecode manipulation framework
0003: * Copyright (c) 2000-2005 INRIA, France Telecom
0004: * All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: * 1. Redistributions of source code must retain the above copyright
0010: * notice, this list of conditions and the following disclaimer.
0011: * 2. Redistributions in binary form must reproduce the above copyright
0012: * notice, this list of conditions and the following disclaimer in the
0013: * documentation and/or other materials provided with the distribution.
0014: * 3. Neither the name of the copyright holders nor the names of its
0015: * contributors may be used to endorse or promote products derived from
0016: * this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
0028: * THE POSSIBILITY OF SUCH DAMAGE.
0029: */package org.objectweb.asm.jip;
0030:
0031: /**
0032: * A {@link MethodVisitor} that generates methods in bytecode form. Each visit
0033: * method of this class appends the bytecode corresponding to the visited
0034: * instruction to a byte vector, in the order these methods are called.
0035: *
0036: * @author Eric Bruneton
0037: */
0038: class MethodWriter implements MethodVisitor {
0039:
0040: /**
0041: * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
0042: */
0043: MethodWriter next;
0044:
0045: /**
0046: * The class writer to which this method must be added.
0047: */
0048: ClassWriter cw;
0049:
0050: /**
0051: * Access flags of this method.
0052: */
0053: private int access;
0054:
0055: /**
0056: * The index of the constant pool item that contains the name of this
0057: * method.
0058: */
0059: private int name;
0060:
0061: /**
0062: * The index of the constant pool item that contains the descriptor of this
0063: * method.
0064: */
0065: private int desc;
0066:
0067: /**
0068: * The descriptor of this method.
0069: */
0070: private String descriptor;
0071:
0072: /**
0073: * If not zero, indicates that the code of this method must be copied from
0074: * the ClassReader associated to this writer in <code>cw.cr</code>. More
0075: * precisely, this field gives the index of the first byte to copied from
0076: * <code>cw.cr.b</code>.
0077: */
0078: int classReaderOffset;
0079:
0080: /**
0081: * If not zero, indicates that the code of this method must be copied from
0082: * the ClassReader associated to this writer in <code>cw.cr</code>. More
0083: * precisely, this field gives the number of bytes to copied from
0084: * <code>cw.cr.b</code>.
0085: */
0086: int classReaderLength;
0087:
0088: /**
0089: * The signature of this method.
0090: */
0091: String signature;
0092:
0093: /**
0094: * Number of exceptions that can be thrown by this method.
0095: */
0096: int exceptionCount;
0097:
0098: /**
0099: * The exceptions that can be thrown by this method. More precisely, this
0100: * array contains the indexes of the constant pool items that contain the
0101: * internal names of these exception classes.
0102: */
0103: int[] exceptions;
0104:
0105: /**
0106: * The annotation default attribute of this method. May be <tt>null</tt>.
0107: */
0108: private ByteVector annd;
0109:
0110: /**
0111: * The runtime visible annotations of this method. May be <tt>null</tt>.
0112: */
0113: private AnnotationWriter anns;
0114:
0115: /**
0116: * The runtime invisible annotations of this method. May be <tt>null</tt>.
0117: */
0118: private AnnotationWriter ianns;
0119:
0120: /**
0121: * The runtime visible parameter annotations of this method. May be
0122: * <tt>null</tt>.
0123: */
0124: private AnnotationWriter[] panns;
0125:
0126: /**
0127: * The runtime invisible parameter annotations of this method. May be
0128: * <tt>null</tt>.
0129: */
0130: private AnnotationWriter[] ipanns;
0131:
0132: /**
0133: * The non standard attributes of the method.
0134: */
0135: private Attribute attrs;
0136:
0137: /**
0138: * The bytecode of this method.
0139: */
0140: private ByteVector code = new ByteVector();
0141:
0142: /**
0143: * Maximum stack size of this method.
0144: */
0145: private int maxStack;
0146:
0147: /**
0148: * Maximum number of local variables for this method.
0149: */
0150: private int maxLocals;
0151:
0152: /**
0153: * Number of entries in the catch table of this method.
0154: */
0155: private int catchCount;
0156:
0157: /**
0158: * The catch table of this method.
0159: */
0160: private Handler catchTable;
0161:
0162: /**
0163: * The last element in the catchTable handler list.
0164: */
0165: private Handler lastHandler;
0166:
0167: /**
0168: * Number of entries in the LocalVariableTable attribute.
0169: */
0170: private int localVarCount;
0171:
0172: /**
0173: * The LocalVariableTable attribute.
0174: */
0175: private ByteVector localVar;
0176:
0177: /**
0178: * Number of entries in the LocalVariableTypeTable attribute.
0179: */
0180: private int localVarTypeCount;
0181:
0182: /**
0183: * The LocalVariableTypeTable attribute.
0184: */
0185: private ByteVector localVarType;
0186:
0187: /**
0188: * Number of entries in the LineNumberTable attribute.
0189: */
0190: private int lineNumberCount;
0191:
0192: /**
0193: * The LineNumberTable attribute.
0194: */
0195: private ByteVector lineNumber;
0196:
0197: /**
0198: * The non standard attributes of the method's code.
0199: */
0200: private Attribute cattrs;
0201:
0202: /**
0203: * Indicates if some jump instructions are too small and need to be resized.
0204: */
0205: private boolean resize;
0206:
0207: /*
0208: * Fields for the control flow graph analysis algorithm (used to compute the
0209: * maximum stack size). A control flow graph contains one node per "basic
0210: * block", and one edge per "jump" from one basic block to another. Each
0211: * node (i.e., each basic block) is represented by the Label object that
0212: * corresponds to the first instruction of this basic block. Each node also
0213: * stores the list of its successors in the graph, as a linked list of Edge
0214: * objects.
0215: */
0216:
0217: /**
0218: * <tt>true</tt> if the maximum stack size and number of local variables
0219: * must be automatically computed.
0220: */
0221: private final boolean computeMaxs;
0222:
0223: /**
0224: * The (relative) stack size after the last visited instruction. This size
0225: * is relative to the beginning of the current basic block, i.e., the true
0226: * stack size after the last visited instruction is equal to the {@link
0227: * Label#beginStackSize beginStackSize} of the current basic block plus
0228: * <tt>stackSize</tt>.
0229: */
0230: private int stackSize;
0231:
0232: /**
0233: * The (relative) maximum stack size after the last visited instruction.
0234: * This size is relative to the beginning of the current basic block, i.e.,
0235: * the true maximum stack size after the last visited instruction is equal
0236: * to the {@link Label#beginStackSize beginStackSize} of the current basic
0237: * block plus <tt>stackSize</tt>.
0238: */
0239: private int maxStackSize;
0240:
0241: /**
0242: * The current basic block. This block is the basic block to which the next
0243: * instruction to be visited must be added.
0244: */
0245: private Label currentBlock;
0246:
0247: /**
0248: * The basic block stack used by the control flow analysis algorithm. This
0249: * stack is represented by a linked list of {@link Label Label} objects,
0250: * linked to each other by their {@link Label#next} field. This stack must
0251: * not be confused with the JVM stack used to execute the JVM instructions!
0252: */
0253: private Label blockStack;
0254:
0255: /**
0256: * The stack size variation corresponding to each JVM instruction. This
0257: * stack variation is equal to the size of the values produced by an
0258: * instruction, minus the size of the values consumed by this instruction.
0259: */
0260: private final static int[] SIZE;
0261:
0262: // ------------------------------------------------------------------------
0263: // Static initializer
0264: // ------------------------------------------------------------------------
0265:
0266: /**
0267: * Computes the stack size variation corresponding to each JVM instruction.
0268: */
0269: static {
0270: int i;
0271: int[] b = new int[202];
0272: String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
0273: + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
0274: + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
0275: + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
0276: for (i = 0; i < b.length; ++i) {
0277: b[i] = s.charAt(i) - 'E';
0278: }
0279: SIZE = b;
0280:
0281: // code to generate the above string
0282: //
0283: // int NA = 0; // not applicable (unused opcode or variable size opcode)
0284: //
0285: // b = new int[] {
0286: // 0, //NOP, // visitInsn
0287: // 1, //ACONST_NULL, // -
0288: // 1, //ICONST_M1, // -
0289: // 1, //ICONST_0, // -
0290: // 1, //ICONST_1, // -
0291: // 1, //ICONST_2, // -
0292: // 1, //ICONST_3, // -
0293: // 1, //ICONST_4, // -
0294: // 1, //ICONST_5, // -
0295: // 2, //LCONST_0, // -
0296: // 2, //LCONST_1, // -
0297: // 1, //FCONST_0, // -
0298: // 1, //FCONST_1, // -
0299: // 1, //FCONST_2, // -
0300: // 2, //DCONST_0, // -
0301: // 2, //DCONST_1, // -
0302: // 1, //BIPUSH, // visitIntInsn
0303: // 1, //SIPUSH, // -
0304: // 1, //LDC, // visitLdcInsn
0305: // NA, //LDC_W, // -
0306: // NA, //LDC2_W, // -
0307: // 1, //ILOAD, // visitVarInsn
0308: // 2, //LLOAD, // -
0309: // 1, //FLOAD, // -
0310: // 2, //DLOAD, // -
0311: // 1, //ALOAD, // -
0312: // NA, //ILOAD_0, // -
0313: // NA, //ILOAD_1, // -
0314: // NA, //ILOAD_2, // -
0315: // NA, //ILOAD_3, // -
0316: // NA, //LLOAD_0, // -
0317: // NA, //LLOAD_1, // -
0318: // NA, //LLOAD_2, // -
0319: // NA, //LLOAD_3, // -
0320: // NA, //FLOAD_0, // -
0321: // NA, //FLOAD_1, // -
0322: // NA, //FLOAD_2, // -
0323: // NA, //FLOAD_3, // -
0324: // NA, //DLOAD_0, // -
0325: // NA, //DLOAD_1, // -
0326: // NA, //DLOAD_2, // -
0327: // NA, //DLOAD_3, // -
0328: // NA, //ALOAD_0, // -
0329: // NA, //ALOAD_1, // -
0330: // NA, //ALOAD_2, // -
0331: // NA, //ALOAD_3, // -
0332: // -1, //IALOAD, // visitInsn
0333: // 0, //LALOAD, // -
0334: // -1, //FALOAD, // -
0335: // 0, //DALOAD, // -
0336: // -1, //AALOAD, // -
0337: // -1, //BALOAD, // -
0338: // -1, //CALOAD, // -
0339: // -1, //SALOAD, // -
0340: // -1, //ISTORE, // visitVarInsn
0341: // -2, //LSTORE, // -
0342: // -1, //FSTORE, // -
0343: // -2, //DSTORE, // -
0344: // -1, //ASTORE, // -
0345: // NA, //ISTORE_0, // -
0346: // NA, //ISTORE_1, // -
0347: // NA, //ISTORE_2, // -
0348: // NA, //ISTORE_3, // -
0349: // NA, //LSTORE_0, // -
0350: // NA, //LSTORE_1, // -
0351: // NA, //LSTORE_2, // -
0352: // NA, //LSTORE_3, // -
0353: // NA, //FSTORE_0, // -
0354: // NA, //FSTORE_1, // -
0355: // NA, //FSTORE_2, // -
0356: // NA, //FSTORE_3, // -
0357: // NA, //DSTORE_0, // -
0358: // NA, //DSTORE_1, // -
0359: // NA, //DSTORE_2, // -
0360: // NA, //DSTORE_3, // -
0361: // NA, //ASTORE_0, // -
0362: // NA, //ASTORE_1, // -
0363: // NA, //ASTORE_2, // -
0364: // NA, //ASTORE_3, // -
0365: // -3, //IASTORE, // visitInsn
0366: // -4, //LASTORE, // -
0367: // -3, //FASTORE, // -
0368: // -4, //DASTORE, // -
0369: // -3, //AASTORE, // -
0370: // -3, //BASTORE, // -
0371: // -3, //CASTORE, // -
0372: // -3, //SASTORE, // -
0373: // -1, //POP, // -
0374: // -2, //POP2, // -
0375: // 1, //DUP, // -
0376: // 1, //DUP_X1, // -
0377: // 1, //DUP_X2, // -
0378: // 2, //DUP2, // -
0379: // 2, //DUP2_X1, // -
0380: // 2, //DUP2_X2, // -
0381: // 0, //SWAP, // -
0382: // -1, //IADD, // -
0383: // -2, //LADD, // -
0384: // -1, //FADD, // -
0385: // -2, //DADD, // -
0386: // -1, //ISUB, // -
0387: // -2, //LSUB, // -
0388: // -1, //FSUB, // -
0389: // -2, //DSUB, // -
0390: // -1, //IMUL, // -
0391: // -2, //LMUL, // -
0392: // -1, //FMUL, // -
0393: // -2, //DMUL, // -
0394: // -1, //IDIV, // -
0395: // -2, //LDIV, // -
0396: // -1, //FDIV, // -
0397: // -2, //DDIV, // -
0398: // -1, //IREM, // -
0399: // -2, //LREM, // -
0400: // -1, //FREM, // -
0401: // -2, //DREM, // -
0402: // 0, //INEG, // -
0403: // 0, //LNEG, // -
0404: // 0, //FNEG, // -
0405: // 0, //DNEG, // -
0406: // -1, //ISHL, // -
0407: // -1, //LSHL, // -
0408: // -1, //ISHR, // -
0409: // -1, //LSHR, // -
0410: // -1, //IUSHR, // -
0411: // -1, //LUSHR, // -
0412: // -1, //IAND, // -
0413: // -2, //LAND, // -
0414: // -1, //IOR, // -
0415: // -2, //LOR, // -
0416: // -1, //IXOR, // -
0417: // -2, //LXOR, // -
0418: // 0, //IINC, // visitIincInsn
0419: // 1, //I2L, // visitInsn
0420: // 0, //I2F, // -
0421: // 1, //I2D, // -
0422: // -1, //L2I, // -
0423: // -1, //L2F, // -
0424: // 0, //L2D, // -
0425: // 0, //F2I, // -
0426: // 1, //F2L, // -
0427: // 1, //F2D, // -
0428: // -1, //D2I, // -
0429: // 0, //D2L, // -
0430: // -1, //D2F, // -
0431: // 0, //I2B, // -
0432: // 0, //I2C, // -
0433: // 0, //I2S, // -
0434: // -3, //LCMP, // -
0435: // -1, //FCMPL, // -
0436: // -1, //FCMPG, // -
0437: // -3, //DCMPL, // -
0438: // -3, //DCMPG, // -
0439: // -1, //IFEQ, // visitJumpInsn
0440: // -1, //IFNE, // -
0441: // -1, //IFLT, // -
0442: // -1, //IFGE, // -
0443: // -1, //IFGT, // -
0444: // -1, //IFLE, // -
0445: // -2, //IF_ICMPEQ, // -
0446: // -2, //IF_ICMPNE, // -
0447: // -2, //IF_ICMPLT, // -
0448: // -2, //IF_ICMPGE, // -
0449: // -2, //IF_ICMPGT, // -
0450: // -2, //IF_ICMPLE, // -
0451: // -2, //IF_ACMPEQ, // -
0452: // -2, //IF_ACMPNE, // -
0453: // 0, //GOTO, // -
0454: // 1, //JSR, // -
0455: // 0, //RET, // visitVarInsn
0456: // -1, //TABLESWITCH, // visiTableSwitchInsn
0457: // -1, //LOOKUPSWITCH, // visitLookupSwitch
0458: // -1, //IRETURN, // visitInsn
0459: // -2, //LRETURN, // -
0460: // -1, //FRETURN, // -
0461: // -2, //DRETURN, // -
0462: // -1, //ARETURN, // -
0463: // 0, //RETURN, // -
0464: // NA, //GETSTATIC, // visitFieldInsn
0465: // NA, //PUTSTATIC, // -
0466: // NA, //GETFIELD, // -
0467: // NA, //PUTFIELD, // -
0468: // NA, //INVOKEVIRTUAL, // visitMethodInsn
0469: // NA, //INVOKESPECIAL, // -
0470: // NA, //INVOKESTATIC, // -
0471: // NA, //INVOKEINTERFACE, // -
0472: // NA, //UNUSED, // NOT VISITED
0473: // 1, //NEW, // visitTypeInsn
0474: // 0, //NEWARRAY, // visitIntInsn
0475: // 0, //ANEWARRAY, // visitTypeInsn
0476: // 0, //ARRAYLENGTH, // visitInsn
0477: // NA, //ATHROW, // -
0478: // 0, //CHECKCAST, // visitTypeInsn
0479: // 0, //INSTANCEOF, // -
0480: // -1, //MONITORENTER, // visitInsn
0481: // -1, //MONITOREXIT, // -
0482: // NA, //WIDE, // NOT VISITED
0483: // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn
0484: // -1, //IFNULL, // visitJumpInsn
0485: // -1, //IFNONNULL, // -
0486: // NA, //GOTO_W, // -
0487: // NA, //JSR_W, // -
0488: // };
0489: // for (i = 0; i < b.length; ++i) {
0490: // System.err.print((char)('E' + b[i]));
0491: // }
0492: // System.err.println();
0493: }
0494:
0495: // ------------------------------------------------------------------------
0496: // Constructor
0497: // ------------------------------------------------------------------------
0498:
0499: /**
0500: * Constructs a new {@link MethodWriter}.
0501: *
0502: * @param cw the class writer in which the method must be added.
0503: * @param access the method's access flags (see {@link Opcodes}).
0504: * @param name the method's name.
0505: * @param desc the method's descriptor (see {@link Type}).
0506: * @param signature the method's signature. May be <tt>null</tt>.
0507: * @param exceptions the internal names of the method's exceptions. May be
0508: * <tt>null</tt>.
0509: * @param computeMaxs <tt>true</tt> if the maximum stack size and number
0510: * of local variables must be automatically computed.
0511: */
0512: MethodWriter(final ClassWriter cw, final int access,
0513: final String name, final String desc,
0514: final String signature, final String[] exceptions,
0515: final boolean computeMaxs) {
0516: if (cw.firstMethod == null) {
0517: cw.firstMethod = this ;
0518: } else {
0519: cw.lastMethod.next = this ;
0520: }
0521: cw.lastMethod = this ;
0522: this .cw = cw;
0523: this .access = access;
0524: this .name = cw.newUTF8(name);
0525: this .desc = cw.newUTF8(desc);
0526: this .descriptor = desc;
0527: this .signature = signature;
0528: if (exceptions != null && exceptions.length > 0) {
0529: exceptionCount = exceptions.length;
0530: this .exceptions = new int[exceptionCount];
0531: for (int i = 0; i < exceptionCount; ++i) {
0532: this .exceptions[i] = cw.newClass(exceptions[i]);
0533: }
0534: }
0535: this .computeMaxs = computeMaxs;
0536: if (computeMaxs) {
0537: // updates maxLocals
0538: int size = getArgumentsAndReturnSizes(desc) >> 2;
0539: if ((access & Opcodes.ACC_STATIC) != 0) {
0540: --size;
0541: }
0542: maxLocals = size;
0543: // pushes the first block onto the stack of blocks to be visited
0544: currentBlock = new Label();
0545: currentBlock.pushed = true;
0546: blockStack = currentBlock;
0547: }
0548: }
0549:
0550: // ------------------------------------------------------------------------
0551: // Implementation of the MethodVisitor interface
0552: // ------------------------------------------------------------------------
0553:
0554: public AnnotationVisitor visitAnnotationDefault() {
0555: annd = new ByteVector();
0556: return new AnnotationWriter(cw, false, annd, null, 0);
0557: }
0558:
0559: public AnnotationVisitor visitAnnotation(final String desc,
0560: final boolean visible) {
0561: ByteVector bv = new ByteVector();
0562: // write type, and reserve space for values count
0563: bv.putShort(cw.newUTF8(desc)).putShort(0);
0564: AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
0565: if (visible) {
0566: aw.next = anns;
0567: anns = aw;
0568: } else {
0569: aw.next = ianns;
0570: ianns = aw;
0571: }
0572: return aw;
0573: }
0574:
0575: public AnnotationVisitor visitParameterAnnotation(
0576: final int parameter, final String desc,
0577: final boolean visible) {
0578: ByteVector bv = new ByteVector();
0579: // write type, and reserve space for values count
0580: bv.putShort(cw.newUTF8(desc)).putShort(0);
0581: AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
0582: if (visible) {
0583: if (panns == null) {
0584: panns = new AnnotationWriter[Type
0585: .getArgumentTypes(descriptor).length];
0586: }
0587: aw.next = panns[parameter];
0588: panns[parameter] = aw;
0589: } else {
0590: if (ipanns == null) {
0591: ipanns = new AnnotationWriter[Type
0592: .getArgumentTypes(descriptor).length];
0593: }
0594: aw.next = ipanns[parameter];
0595: ipanns[parameter] = aw;
0596: }
0597: return aw;
0598: }
0599:
0600: public void visitAttribute(final Attribute attr) {
0601: if (attr.isCodeAttribute()) {
0602: attr.next = cattrs;
0603: cattrs = attr;
0604: } else {
0605: attr.next = attrs;
0606: attrs = attr;
0607: }
0608: }
0609:
0610: public void visitCode() {
0611: }
0612:
0613: public void visitInsn(final int opcode) {
0614: if (computeMaxs) {
0615: // updates current and max stack sizes
0616: int size = stackSize + SIZE[opcode];
0617: if (size > maxStackSize) {
0618: maxStackSize = size;
0619: }
0620: stackSize = size;
0621: // if opcode == ATHROW or xRETURN, ends current block (no successor)
0622: if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
0623: || opcode == Opcodes.ATHROW) {
0624: if (currentBlock != null) {
0625: currentBlock.maxStackSize = maxStackSize;
0626: currentBlock = null;
0627: }
0628: }
0629: }
0630: // adds the instruction to the bytecode of the method
0631: code.putByte(opcode);
0632: }
0633:
0634: public void visitIntInsn(final int opcode, final int operand) {
0635: if (computeMaxs && opcode != Opcodes.NEWARRAY) {
0636: // updates current and max stack sizes only if opcode == NEWARRAY
0637: // (stack size variation = 0 for BIPUSH or SIPUSH)
0638: int size = stackSize + 1;
0639: if (size > maxStackSize) {
0640: maxStackSize = size;
0641: }
0642: stackSize = size;
0643: }
0644: // adds the instruction to the bytecode of the method
0645: if (opcode == Opcodes.SIPUSH) {
0646: code.put12(opcode, operand);
0647: } else { // BIPUSH or NEWARRAY
0648: code.put11(opcode, operand);
0649: }
0650: }
0651:
0652: public void visitVarInsn(final int opcode, final int var) {
0653: if (computeMaxs) {
0654: // updates current and max stack sizes
0655: if (opcode == Opcodes.RET) {
0656: // no stack change, but end of current block (no successor)
0657: if (currentBlock != null) {
0658: currentBlock.maxStackSize = maxStackSize;
0659: currentBlock = null;
0660: }
0661: } else { // xLOAD or xSTORE
0662: int size = stackSize + SIZE[opcode];
0663: if (size > maxStackSize) {
0664: maxStackSize = size;
0665: }
0666: stackSize = size;
0667: }
0668: // updates max locals
0669: int n;
0670: if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
0671: || opcode == Opcodes.LSTORE
0672: || opcode == Opcodes.DSTORE) {
0673: n = var + 2;
0674: } else {
0675: n = var + 1;
0676: }
0677: if (n > maxLocals) {
0678: maxLocals = n;
0679: }
0680: }
0681: // adds the instruction to the bytecode of the method
0682: if (var < 4 && opcode != Opcodes.RET) {
0683: int opt;
0684: if (opcode < Opcodes.ISTORE) {
0685: /* ILOAD_0 */
0686: opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
0687: } else {
0688: /* ISTORE_0 */
0689: opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
0690: }
0691: code.putByte(opt);
0692: } else if (var >= 256) {
0693: code.putByte(196 /* WIDE */).put12(opcode, var);
0694: } else {
0695: code.put11(opcode, var);
0696: }
0697: }
0698:
0699: public void visitTypeInsn(final int opcode, final String desc) {
0700: if (computeMaxs && opcode == Opcodes.NEW) {
0701: // updates current and max stack sizes only if opcode == NEW
0702: // (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF)
0703: int size = stackSize + 1;
0704: if (size > maxStackSize) {
0705: maxStackSize = size;
0706: }
0707: stackSize = size;
0708: }
0709: // adds the instruction to the bytecode of the method
0710: code.put12(opcode, cw.newClass(desc));
0711: }
0712:
0713: public void visitFieldInsn(final int opcode, final String owner,
0714: final String name, final String desc) {
0715: if (computeMaxs) {
0716: int size;
0717: // computes the stack size variation
0718: char c = desc.charAt(0);
0719: switch (opcode) {
0720: case Opcodes.GETSTATIC:
0721: size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
0722: break;
0723: case Opcodes.PUTSTATIC:
0724: size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
0725: break;
0726: case Opcodes.GETFIELD:
0727: size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
0728: break;
0729: // case Constants.PUTFIELD:
0730: default:
0731: size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
0732: break;
0733: }
0734: // updates current and max stack sizes
0735: if (size > maxStackSize) {
0736: maxStackSize = size;
0737: }
0738: stackSize = size;
0739: }
0740: // adds the instruction to the bytecode of the method
0741: code.put12(opcode, cw.newField(owner, name, desc));
0742: }
0743:
0744: public void visitMethodInsn(final int opcode, final String owner,
0745: final String name, final String desc) {
0746: boolean itf = opcode == Opcodes.INVOKEINTERFACE;
0747: Item i = cw.newMethodItem(owner, name, desc, itf);
0748: int argSize = i.intVal;
0749: if (computeMaxs) {
0750: /*
0751: * computes the stack size variation. In order not to recompute
0752: * several times this variation for the same Item, we use the intVal
0753: * field of this item to store this variation, once it has been
0754: * computed. More precisely this intVal field stores the sizes of
0755: * the arguments and of the return value corresponding to desc.
0756: */
0757: if (argSize == 0) {
0758: // the above sizes have not been computed yet, so we compute
0759: // them...
0760: argSize = getArgumentsAndReturnSizes(desc);
0761: // ... and we save them in order not to recompute them in the
0762: // future
0763: i.intVal = argSize;
0764: }
0765: int size;
0766: if (opcode == Opcodes.INVOKESTATIC) {
0767: size = stackSize - (argSize >> 2) + (argSize & 0x03)
0768: + 1;
0769: } else {
0770: size = stackSize - (argSize >> 2) + (argSize & 0x03);
0771: }
0772: // updates current and max stack sizes
0773: if (size > maxStackSize) {
0774: maxStackSize = size;
0775: }
0776: stackSize = size;
0777: }
0778: // adds the instruction to the bytecode of the method
0779: if (itf) {
0780: if (!computeMaxs) {
0781: if (argSize == 0) {
0782: argSize = getArgumentsAndReturnSizes(desc);
0783: i.intVal = argSize;
0784: }
0785: }
0786: code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(
0787: argSize >> 2, 0);
0788: } else {
0789: code.put12(opcode, i.index);
0790: }
0791: }
0792:
0793: public void visitJumpInsn(final int opcode, final Label label) {
0794: if (computeMaxs) {
0795: if (opcode == Opcodes.GOTO) {
0796: // no stack change, but end of current block (with one new
0797: // successor)
0798: if (currentBlock != null) {
0799: currentBlock.maxStackSize = maxStackSize;
0800: addSuccessor(stackSize, label);
0801: currentBlock = null;
0802: }
0803: } else if (opcode == Opcodes.JSR) {
0804: if (currentBlock != null) {
0805: addSuccessor(stackSize + 1, label);
0806: }
0807: } else {
0808: // updates current stack size (max stack size unchanged because
0809: // stack size variation always negative in this case)
0810: stackSize += SIZE[opcode];
0811: if (currentBlock != null) {
0812: addSuccessor(stackSize, label);
0813: }
0814: }
0815: }
0816: // adds the instruction to the bytecode of the method
0817: if (label.resolved
0818: && label.position - code.length < Short.MIN_VALUE) {
0819: /*
0820: * case of a backward jump with an offset < -32768. In this case we
0821: * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
0822: * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
0823: * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
0824: * designates the instruction just after the GOTO_W.
0825: */
0826: if (opcode == Opcodes.GOTO) {
0827: code.putByte(200); // GOTO_W
0828: } else if (opcode == Opcodes.JSR) {
0829: code.putByte(201); // JSR_W
0830: } else {
0831: code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
0832: : opcode ^ 1);
0833: code.putShort(8); // jump offset
0834: code.putByte(200); // GOTO_W
0835: }
0836: label.put(this , code, code.length - 1, true);
0837: } else {
0838: /*
0839: * case of a backward jump with an offset >= -32768, or of a forward
0840: * jump with, of course, an unknown offset. In these cases we store
0841: * the offset in 2 bytes (which will be increased in
0842: * resizeInstructions, if needed).
0843: */
0844: code.putByte(opcode);
0845: label.put(this , code, code.length - 1, false);
0846: }
0847: }
0848:
0849: public void visitLabel(final Label label) {
0850: if (computeMaxs) {
0851: if (currentBlock != null) {
0852: // ends current block (with one new successor)
0853: currentBlock.maxStackSize = maxStackSize;
0854: addSuccessor(stackSize, label);
0855: }
0856: // begins a new current block,
0857: // resets the relative current and max stack sizes
0858: currentBlock = label;
0859: stackSize = 0;
0860: maxStackSize = 0;
0861: }
0862: // resolves previous forward references to label, if any
0863: resize |= label.resolve(this , code.length, code.data);
0864: }
0865:
0866: public void visitLdcInsn(final Object cst) {
0867: Item i = cw.newConstItem(cst);
0868: if (computeMaxs) {
0869: int size;
0870: // computes the stack size variation
0871: if (i.type == ClassWriter.LONG
0872: || i.type == ClassWriter.DOUBLE) {
0873: size = stackSize + 2;
0874: } else {
0875: size = stackSize + 1;
0876: }
0877: // updates current and max stack sizes
0878: if (size > maxStackSize) {
0879: maxStackSize = size;
0880: }
0881: stackSize = size;
0882: }
0883: // adds the instruction to the bytecode of the method
0884: int index = i.index;
0885: if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
0886: code.put12(20 /* LDC2_W */, index);
0887: } else if (index >= 256) {
0888: code.put12(19 /* LDC_W */, index);
0889: } else {
0890: code.put11(Opcodes.LDC, index);
0891: }
0892: }
0893:
0894: public void visitIincInsn(final int var, final int increment) {
0895: if (computeMaxs) {
0896: // updates max locals only (no stack change)
0897: int n = var + 1;
0898: if (n > maxLocals) {
0899: maxLocals = n;
0900: }
0901: }
0902: // adds the instruction to the bytecode of the method
0903: if ((var > 255) || (increment > 127) || (increment < -128)) {
0904: code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var)
0905: .putShort(increment);
0906: } else {
0907: code.putByte(Opcodes.IINC).put11(var, increment);
0908: }
0909: }
0910:
0911: public void visitTableSwitchInsn(final int min, final int max,
0912: final Label dflt, final Label labels[]) {
0913: if (computeMaxs) {
0914: // updates current stack size (max stack size unchanged)
0915: --stackSize;
0916: // ends current block (with many new successors)
0917: if (currentBlock != null) {
0918: currentBlock.maxStackSize = maxStackSize;
0919: addSuccessor(stackSize, dflt);
0920: for (int i = 0; i < labels.length; ++i) {
0921: addSuccessor(stackSize, labels[i]);
0922: }
0923: currentBlock = null;
0924: }
0925: }
0926: // adds the instruction to the bytecode of the method
0927: int source = code.length;
0928: code.putByte(Opcodes.TABLESWITCH);
0929: while (code.length % 4 != 0) {
0930: code.putByte(0);
0931: }
0932: dflt.put(this , code, source, true);
0933: code.putInt(min).putInt(max);
0934: for (int i = 0; i < labels.length; ++i) {
0935: labels[i].put(this , code, source, true);
0936: }
0937: }
0938:
0939: public void visitLookupSwitchInsn(final Label dflt,
0940: final int keys[], final Label labels[]) {
0941: if (computeMaxs) {
0942: // updates current stack size (max stack size unchanged)
0943: --stackSize;
0944: // ends current block (with many new successors)
0945: if (currentBlock != null) {
0946: currentBlock.maxStackSize = maxStackSize;
0947: addSuccessor(stackSize, dflt);
0948: for (int i = 0; i < labels.length; ++i) {
0949: addSuccessor(stackSize, labels[i]);
0950: }
0951: currentBlock = null;
0952: }
0953: }
0954: // adds the instruction to the bytecode of the method
0955: int source = code.length;
0956: code.putByte(Opcodes.LOOKUPSWITCH);
0957: while (code.length % 4 != 0) {
0958: code.putByte(0);
0959: }
0960: dflt.put(this , code, source, true);
0961: code.putInt(labels.length);
0962: for (int i = 0; i < labels.length; ++i) {
0963: code.putInt(keys[i]);
0964: labels[i].put(this , code, source, true);
0965: }
0966: }
0967:
0968: public void visitMultiANewArrayInsn(final String desc,
0969: final int dims) {
0970: if (computeMaxs) {
0971: // updates current stack size (max stack size unchanged because
0972: // stack size variation always negative or null)
0973: stackSize += 1 - dims;
0974: }
0975: // adds the instruction to the bytecode of the method
0976: code.put12(Opcodes.MULTIANEWARRAY, cw.newClass(desc)).putByte(
0977: dims);
0978: }
0979:
0980: public void visitTryCatchBlock(final Label start, final Label end,
0981: final Label handler, final String type) {
0982: if (computeMaxs) {
0983: // pushes handler block onto the stack of blocks to be visited
0984: if (!handler.pushed) {
0985: handler.beginStackSize = 1;
0986: handler.pushed = true;
0987: handler.next = blockStack;
0988: blockStack = handler;
0989: }
0990: }
0991: ++catchCount;
0992: Handler h = new Handler();
0993: h.start = start;
0994: h.end = end;
0995: h.handler = handler;
0996: h.desc = type;
0997: h.type = type != null ? cw.newClass(type) : 0;
0998: if (lastHandler == null) {
0999: catchTable = h;
1000: } else {
1001: lastHandler.next = h;
1002: }
1003: lastHandler = h;
1004: }
1005:
1006: public void visitLocalVariable(final String name,
1007: final String desc, final String signature,
1008: final Label start, final Label end, final int index) {
1009: if (signature != null) {
1010: if (localVarType == null) {
1011: localVarType = new ByteVector();
1012: }
1013: ++localVarTypeCount;
1014: localVarType.putShort(start.position).putShort(
1015: end.position - start.position).putShort(
1016: cw.newUTF8(name)).putShort(cw.newUTF8(signature))
1017: .putShort(index);
1018: }
1019: if (localVar == null) {
1020: localVar = new ByteVector();
1021: }
1022: ++localVarCount;
1023: localVar.putShort(start.position).putShort(
1024: end.position - start.position).putShort(
1025: cw.newUTF8(name)).putShort(cw.newUTF8(desc)).putShort(
1026: index);
1027: }
1028:
1029: public void visitLineNumber(final int line, final Label start) {
1030: if (lineNumber == null) {
1031: lineNumber = new ByteVector();
1032: }
1033: ++lineNumberCount;
1034: lineNumber.putShort(start.position);
1035: lineNumber.putShort(line);
1036: }
1037:
1038: public void visitMaxs(final int maxStack, final int maxLocals) {
1039: if (computeMaxs) {
1040: // true (non relative) max stack size
1041: int max = 0;
1042: /*
1043: * control flow analysis algorithm: while the block stack is not
1044: * empty, pop a block from this stack, update the max stack size,
1045: * compute the true (non relative) begin stack size of the
1046: * successors of this block, and push these successors onto the
1047: * stack (unless they have already been pushed onto the stack).
1048: * Note: by hypothesis, the {@link Label#beginStackSize} of the
1049: * blocks in the block stack are the true (non relative) beginning
1050: * stack sizes of these blocks.
1051: */
1052: Label stack = blockStack;
1053: while (stack != null) {
1054: // pops a block from the stack
1055: Label l = stack;
1056: stack = stack.next;
1057: // computes the true (non relative) max stack size of this block
1058: int start = l.beginStackSize;
1059: int blockMax = start + l.maxStackSize;
1060: // updates the global max stack size
1061: if (blockMax > max) {
1062: max = blockMax;
1063: }
1064: // analyses the successors of the block
1065: Edge b = l.successors;
1066: while (b != null) {
1067: l = b.successor;
1068: // if this successor has not already been pushed onto the
1069: // stack...
1070: if (!l.pushed) {
1071: // computes the true beginning stack size of this
1072: // successor block
1073: l.beginStackSize = start + b.stackSize;
1074: // pushes this successor onto the stack
1075: l.pushed = true;
1076: l.next = stack;
1077: stack = l;
1078: }
1079: b = b.next;
1080: }
1081: }
1082: this .maxStack = max;
1083: } else {
1084: this .maxStack = maxStack;
1085: this .maxLocals = maxLocals;
1086: }
1087: }
1088:
1089: public void visitEnd() {
1090: }
1091:
1092: // ------------------------------------------------------------------------
1093: // Utility methods: control flow analysis algorithm
1094: // ------------------------------------------------------------------------
1095:
1096: /**
1097: * Computes the size of the arguments and of the return value of a method.
1098: *
1099: * @param desc the descriptor of a method.
1100: * @return the size of the arguments of the method (plus one for the
1101: * implicit this argument), argSize, and the size of its return
1102: * value, retSize, packed into a single int i =
1103: * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
1104: * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
1105: */
1106: private static int getArgumentsAndReturnSizes(final String desc) {
1107: int n = 1;
1108: int c = 1;
1109: while (true) {
1110: char car = desc.charAt(c++);
1111: if (car == ')') {
1112: car = desc.charAt(c);
1113: return n << 2
1114: | (car == 'V' ? 0
1115: : (car == 'D' || car == 'J' ? 2 : 1));
1116: } else if (car == 'L') {
1117: while (desc.charAt(c++) != ';') {
1118: }
1119: n += 1;
1120: } else if (car == '[') {
1121: while ((car = desc.charAt(c)) == '[') {
1122: ++c;
1123: }
1124: if (car == 'D' || car == 'J') {
1125: n -= 1;
1126: }
1127: } else if (car == 'D' || car == 'J') {
1128: n += 2;
1129: } else {
1130: n += 1;
1131: }
1132: }
1133: }
1134:
1135: /**
1136: * Adds a successor to the {@link #currentBlock currentBlock} block.
1137: *
1138: * @param stackSize the current (relative) stack size in the current block.
1139: * @param successor the successor block to be added to the current block.
1140: */
1141: private void addSuccessor(final int stackSize, final Label successor) {
1142: Edge b = new Edge();
1143: // initializes the previous Edge object...
1144: b.stackSize = stackSize;
1145: b.successor = successor;
1146: // ...and adds it to the successor list of the currentBlock block
1147: b.next = currentBlock.successors;
1148: currentBlock.successors = b;
1149: }
1150:
1151: // ------------------------------------------------------------------------
1152: // Utility methods: dump bytecode array
1153: // ------------------------------------------------------------------------
1154:
1155: /**
1156: * Returns the size of the bytecode of this method.
1157: *
1158: * @return the size of the bytecode of this method.
1159: */
1160: final int getSize() {
1161: if (classReaderOffset != 0) {
1162: return 6 + classReaderLength;
1163: }
1164: if (resize) {
1165: // replaces the temporary jump opcodes introduced by Label.resolve.
1166: resizeInstructions(new int[0], new int[0], 0);
1167: }
1168: int size = 8;
1169: if (code.length > 0) {
1170: cw.newUTF8("Code");
1171: size += 18 + code.length + 8 * catchCount;
1172: if (localVar != null) {
1173: cw.newUTF8("LocalVariableTable");
1174: size += 8 + localVar.length;
1175: }
1176: if (localVarType != null) {
1177: cw.newUTF8("LocalVariableTypeTable");
1178: size += 8 + localVarType.length;
1179: }
1180: if (lineNumber != null) {
1181: cw.newUTF8("LineNumberTable");
1182: size += 8 + lineNumber.length;
1183: }
1184: if (cattrs != null) {
1185: size += cattrs.getSize(cw, code.data, code.length,
1186: maxStack, maxLocals);
1187: }
1188: }
1189: if (exceptionCount > 0) {
1190: cw.newUTF8("Exceptions");
1191: size += 8 + 2 * exceptionCount;
1192: }
1193: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1194: && (cw.version & 0xffff) < Opcodes.V1_5) {
1195: cw.newUTF8("Synthetic");
1196: size += 6;
1197: }
1198: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1199: cw.newUTF8("Deprecated");
1200: size += 6;
1201: }
1202: if (cw.version == Opcodes.V1_4) {
1203: if ((access & Opcodes.ACC_VARARGS) != 0) {
1204: cw.newUTF8("Varargs");
1205: size += 6;
1206: }
1207: if ((access & Opcodes.ACC_BRIDGE) != 0) {
1208: cw.newUTF8("Bridge");
1209: size += 6;
1210: }
1211: }
1212: if (signature != null) {
1213: cw.newUTF8("Signature");
1214: cw.newUTF8(signature);
1215: size += 8;
1216: }
1217: if (annd != null) {
1218: cw.newUTF8("AnnotationDefault");
1219: size += 6 + annd.length;
1220: }
1221: if (anns != null) {
1222: cw.newUTF8("RuntimeVisibleAnnotations");
1223: size += 8 + anns.getSize();
1224: }
1225: if (ianns != null) {
1226: cw.newUTF8("RuntimeInvisibleAnnotations");
1227: size += 8 + ianns.getSize();
1228: }
1229: if (panns != null) {
1230: cw.newUTF8("RuntimeVisibleParameterAnnotations");
1231: size += 7 + 2 * panns.length;
1232: for (int i = panns.length - 1; i >= 0; --i) {
1233: size += panns[i] == null ? 0 : panns[i].getSize();
1234: }
1235: }
1236: if (ipanns != null) {
1237: cw.newUTF8("RuntimeInvisibleParameterAnnotations");
1238: size += 7 + 2 * ipanns.length;
1239: for (int i = ipanns.length - 1; i >= 0; --i) {
1240: size += ipanns[i] == null ? 0 : ipanns[i].getSize();
1241: }
1242: }
1243: if (attrs != null) {
1244: size += attrs.getSize(cw, null, 0, -1, -1);
1245: }
1246: return size;
1247: }
1248:
1249: /**
1250: * Puts the bytecode of this method in the given byte vector.
1251: *
1252: * @param out the byte vector into which the bytecode of this method must be
1253: * copied.
1254: */
1255: final void put(final ByteVector out) {
1256: out.putShort(access).putShort(name).putShort(desc);
1257: if (classReaderOffset != 0) {
1258: out.putByteArray(cw.cr.b, classReaderOffset,
1259: classReaderLength);
1260: return;
1261: }
1262: int attributeCount = 0;
1263: if (code.length > 0) {
1264: ++attributeCount;
1265: }
1266: if (exceptionCount > 0) {
1267: ++attributeCount;
1268: }
1269: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1270: && (cw.version & 0xffff) < Opcodes.V1_5) {
1271: ++attributeCount;
1272: }
1273: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1274: ++attributeCount;
1275: }
1276: if (cw.version == Opcodes.V1_4) {
1277: if ((access & Opcodes.ACC_VARARGS) != 0) {
1278: ++attributeCount;
1279: }
1280: if ((access & Opcodes.ACC_BRIDGE) != 0) {
1281: ++attributeCount;
1282: }
1283: }
1284: if (signature != null) {
1285: ++attributeCount;
1286: }
1287: if (annd != null) {
1288: ++attributeCount;
1289: }
1290: if (anns != null) {
1291: ++attributeCount;
1292: }
1293: if (ianns != null) {
1294: ++attributeCount;
1295: }
1296: if (panns != null) {
1297: ++attributeCount;
1298: }
1299: if (ipanns != null) {
1300: ++attributeCount;
1301: }
1302: if (attrs != null) {
1303: attributeCount += attrs.getCount();
1304: }
1305: out.putShort(attributeCount);
1306: if (code.length > 0) {
1307: int size = 12 + code.length + 8 * catchCount;
1308: if (localVar != null) {
1309: size += 8 + localVar.length;
1310: }
1311: if (localVarType != null) {
1312: size += 8 + localVarType.length;
1313: }
1314: if (lineNumber != null) {
1315: size += 8 + lineNumber.length;
1316: }
1317: if (cattrs != null) {
1318: size += cattrs.getSize(cw, code.data, code.length,
1319: maxStack, maxLocals);
1320: }
1321: out.putShort(cw.newUTF8("Code")).putInt(size);
1322: out.putShort(maxStack).putShort(maxLocals);
1323: out.putInt(code.length).putByteArray(code.data, 0,
1324: code.length);
1325: out.putShort(catchCount);
1326: if (catchCount > 0) {
1327: Handler h = catchTable;
1328: while (h != null) {
1329: out.putShort(h.start.position).putShort(
1330: h.end.position)
1331: .putShort(h.handler.position).putShort(
1332: h.type);
1333: h = h.next;
1334: }
1335: }
1336: attributeCount = 0;
1337: if (localVar != null) {
1338: ++attributeCount;
1339: }
1340: if (localVarType != null) {
1341: ++attributeCount;
1342: }
1343: if (lineNumber != null) {
1344: ++attributeCount;
1345: }
1346: if (cattrs != null) {
1347: attributeCount += cattrs.getCount();
1348: }
1349: out.putShort(attributeCount);
1350: if (localVar != null) {
1351: out.putShort(cw.newUTF8("LocalVariableTable"));
1352: out.putInt(localVar.length + 2).putShort(localVarCount);
1353: out.putByteArray(localVar.data, 0, localVar.length);
1354: }
1355: if (localVarType != null) {
1356: out.putShort(cw.newUTF8("LocalVariableTypeTable"));
1357: out.putInt(localVarType.length + 2).putShort(
1358: localVarTypeCount);
1359: out.putByteArray(localVarType.data, 0,
1360: localVarType.length);
1361: }
1362: if (lineNumber != null) {
1363: out.putShort(cw.newUTF8("LineNumberTable"));
1364: out.putInt(lineNumber.length + 2).putShort(
1365: lineNumberCount);
1366: out.putByteArray(lineNumber.data, 0, lineNumber.length);
1367: }
1368: if (cattrs != null) {
1369: cattrs.put(cw, code.data, code.length, maxLocals,
1370: maxStack, out);
1371: }
1372: }
1373: if (exceptionCount > 0) {
1374: out.putShort(cw.newUTF8("Exceptions")).putInt(
1375: 2 * exceptionCount + 2);
1376: out.putShort(exceptionCount);
1377: for (int i = 0; i < exceptionCount; ++i) {
1378: out.putShort(exceptions[i]);
1379: }
1380: }
1381: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1382: && (cw.version & 0xffff) < Opcodes.V1_5) {
1383: out.putShort(cw.newUTF8("Synthetic")).putInt(0);
1384: }
1385: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1386: out.putShort(cw.newUTF8("Deprecated")).putInt(0);
1387: }
1388: if (cw.version == Opcodes.V1_4) {
1389: if ((access & Opcodes.ACC_VARARGS) != 0) {
1390: out.putShort(cw.newUTF8("Varargs")).putInt(0);
1391: }
1392: if ((access & Opcodes.ACC_BRIDGE) != 0) {
1393: out.putShort(cw.newUTF8("Bridge")).putInt(0);
1394: }
1395: }
1396: if (signature != null) {
1397: out.putShort(cw.newUTF8("Signature")).putInt(2).putShort(
1398: cw.newUTF8(signature));
1399: }
1400: if (annd != null) {
1401: out.putShort(cw.newUTF8("AnnotationDefault"));
1402: out.putInt(annd.length);
1403: out.putByteArray(annd.data, 0, annd.length);
1404: }
1405: if (anns != null) {
1406: out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
1407: anns.put(out);
1408: }
1409: if (ianns != null) {
1410: out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
1411: ianns.put(out);
1412: }
1413: if (panns != null) {
1414: out.putShort(cw
1415: .newUTF8("RuntimeVisibleParameterAnnotations"));
1416: AnnotationWriter.put(panns, out);
1417: }
1418: if (ipanns != null) {
1419: out.putShort(cw
1420: .newUTF8("RuntimeInvisibleParameterAnnotations"));
1421: AnnotationWriter.put(ipanns, out);
1422: }
1423: if (attrs != null) {
1424: attrs.put(cw, null, 0, -1, -1, out);
1425: }
1426: }
1427:
1428: // ------------------------------------------------------------------------
1429: // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
1430: // ------------------------------------------------------------------------
1431:
1432: /**
1433: * Resizes the designated instructions, while keeping jump offsets and
1434: * instruction addresses consistent. This may require to resize other
1435: * existing instructions, or even to introduce new instructions: for
1436: * example, increasing the size of an instruction by 2 at the middle of a
1437: * method can increases the offset of an IFEQ instruction from 32766 to
1438: * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
1439: * 32765. This, in turn, may require to increase the size of another jump
1440: * instruction, and so on... All these operations are handled automatically
1441: * by this method. <p> <i>This method must be called after all the method
1442: * that is being built has been visited</i>. In particular, the
1443: * {@link Label Label} objects used to construct the method are no longer
1444: * valid after this method has been called.
1445: *
1446: * @param indexes current positions of the instructions to be resized. Each
1447: * instruction must be designated by the index of its <i>last</i>
1448: * byte, plus one (or, in other words, by the index of the <i>first</i>
1449: * byte of the <i>next</i> instruction).
1450: * @param sizes the number of bytes to be <i>added</i> to the above
1451: * instructions. More precisely, for each i < <tt>len</tt>,
1452: * <tt>sizes</tt>[i] bytes will be added at the end of the
1453: * instruction designated by <tt>indexes</tt>[i] or, if
1454: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1455: * bytes of the instruction will be removed (the instruction size
1456: * <i>must not</i> become negative or null). The gaps introduced by
1457: * this method must be filled in "manually" in {@link #code code}
1458: * method.
1459: * @param len the number of instruction to be resized. Must be smaller than
1460: * or equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length.
1461: * @return the <tt>indexes</tt> array, which now contains the new
1462: * positions of the resized instructions (designated as above).
1463: */
1464: private int[] resizeInstructions(final int[] indexes,
1465: final int[] sizes, final int len) {
1466: byte[] b = code.data; // bytecode of the method
1467: int u, v, label; // indexes in b
1468: int i, j; // loop indexes
1469:
1470: /*
1471: * 1st step: As explained above, resizing an instruction may require to
1472: * resize another one, which may require to resize yet another one, and
1473: * so on. The first step of the algorithm consists in finding all the
1474: * instructions that need to be resized, without modifying the code.
1475: * This is done by the following "fix point" algorithm:
1476: *
1477: * Parse the code to find the jump instructions whose offset will need
1478: * more than 2 bytes to be stored (the future offset is computed from
1479: * the current offset and from the number of bytes that will be inserted
1480: * or removed between the source and target instructions). For each such
1481: * instruction, adds an entry in (a copy of) the indexes and sizes
1482: * arrays (if this has not already been done in a previous iteration!).
1483: *
1484: * If at least one entry has been added during the previous step, go
1485: * back to the beginning, otherwise stop.
1486: *
1487: * In fact the real algorithm is complicated by the fact that the size
1488: * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
1489: * position in the bytecode (because of padding). In order to ensure the
1490: * convergence of the algorithm, the number of bytes to be added or
1491: * removed from these instructions is over estimated during the previous
1492: * loop, and computed exactly only after the loop is finished (this
1493: * requires another pass to parse the bytecode of the method).
1494: */
1495: int[] allIndexes = new int[len]; // copy of indexes
1496: int[] allSizes = new int[len]; // copy of sizes
1497: boolean[] resize; // instructions to be resized
1498: int newOffset; // future offset of a jump instruction
1499:
1500: System.arraycopy(indexes, 0, allIndexes, 0, len);
1501: System.arraycopy(sizes, 0, allSizes, 0, len);
1502: resize = new boolean[code.length];
1503:
1504: // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
1505: int state = 3;
1506: do {
1507: if (state == 3) {
1508: state = 2;
1509: }
1510: u = 0;
1511: while (u < b.length) {
1512: int opcode = b[u] & 0xFF; // opcode of current instruction
1513: int insert = 0; // bytes to be added after this instruction
1514:
1515: switch (ClassWriter.TYPE[opcode]) {
1516: case ClassWriter.NOARG_INSN:
1517: case ClassWriter.IMPLVAR_INSN:
1518: u += 1;
1519: break;
1520: case ClassWriter.LABEL_INSN:
1521: if (opcode > 201) {
1522: // converts temporary opcodes 202 to 217, 218 and
1523: // 219 to IFEQ ... JSR (inclusive), IFNULL and
1524: // IFNONNULL
1525: opcode = opcode < 218 ? opcode - 49
1526: : opcode - 20;
1527: label = u + readUnsignedShort(b, u + 1);
1528: } else {
1529: label = u + readShort(b, u + 1);
1530: }
1531: newOffset = getNewOffset(allIndexes, allSizes, u,
1532: label);
1533: if (newOffset < Short.MIN_VALUE
1534: || newOffset > Short.MAX_VALUE) {
1535: if (!resize[u]) {
1536: if (opcode == Opcodes.GOTO
1537: || opcode == Opcodes.JSR) {
1538: // two additional bytes will be required to
1539: // replace this GOTO or JSR instruction with
1540: // a GOTO_W or a JSR_W
1541: insert = 2;
1542: } else {
1543: // five additional bytes will be required to
1544: // replace this IFxxx <l> instruction with
1545: // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
1546: // is the "opposite" opcode of IFxxx (i.e.,
1547: // IFNE for IFEQ) and where <l'> designates
1548: // the instruction just after the GOTO_W.
1549: insert = 5;
1550: }
1551: resize[u] = true;
1552: }
1553: }
1554: u += 3;
1555: break;
1556: case ClassWriter.LABELW_INSN:
1557: u += 5;
1558: break;
1559: case ClassWriter.TABL_INSN:
1560: if (state == 1) {
1561: // true number of bytes to be added (or removed)
1562: // from this instruction = (future number of padding
1563: // bytes - current number of padding byte) -
1564: // previously over estimated variation =
1565: // = ((3 - newOffset%4) - (3 - u%4)) - u%4
1566: // = (-newOffset%4 + u%4) - u%4
1567: // = -(newOffset & 3)
1568: newOffset = getNewOffset(allIndexes, allSizes,
1569: 0, u);
1570: insert = -(newOffset & 3);
1571: } else if (!resize[u]) {
1572: // over estimation of the number of bytes to be
1573: // added to this instruction = 3 - current number
1574: // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
1575: insert = u & 3;
1576: resize[u] = true;
1577: }
1578: // skips instruction
1579: u = u + 4 - (u & 3);
1580: u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
1581: break;
1582: case ClassWriter.LOOK_INSN:
1583: if (state == 1) {
1584: // like TABL_INSN
1585: newOffset = getNewOffset(allIndexes, allSizes,
1586: 0, u);
1587: insert = -(newOffset & 3);
1588: } else if (!resize[u]) {
1589: // like TABL_INSN
1590: insert = u & 3;
1591: resize[u] = true;
1592: }
1593: // skips instruction
1594: u = u + 4 - (u & 3);
1595: u += 8 * readInt(b, u + 4) + 8;
1596: break;
1597: case ClassWriter.WIDE_INSN:
1598: opcode = b[u + 1] & 0xFF;
1599: if (opcode == Opcodes.IINC) {
1600: u += 6;
1601: } else {
1602: u += 4;
1603: }
1604: break;
1605: case ClassWriter.VAR_INSN:
1606: case ClassWriter.SBYTE_INSN:
1607: case ClassWriter.LDC_INSN:
1608: u += 2;
1609: break;
1610: case ClassWriter.SHORT_INSN:
1611: case ClassWriter.LDCW_INSN:
1612: case ClassWriter.FIELDORMETH_INSN:
1613: case ClassWriter.TYPE_INSN:
1614: case ClassWriter.IINC_INSN:
1615: u += 3;
1616: break;
1617: case ClassWriter.ITFMETH_INSN:
1618: u += 5;
1619: break;
1620: // case ClassWriter.MANA_INSN:
1621: default:
1622: u += 4;
1623: break;
1624: }
1625: if (insert != 0) {
1626: // adds a new (u, insert) entry in the allIndexes and
1627: // allSizes arrays
1628: int[] newIndexes = new int[allIndexes.length + 1];
1629: int[] newSizes = new int[allSizes.length + 1];
1630: System.arraycopy(allIndexes, 0, newIndexes, 0,
1631: allIndexes.length);
1632: System.arraycopy(allSizes, 0, newSizes, 0,
1633: allSizes.length);
1634: newIndexes[allIndexes.length] = u;
1635: newSizes[allSizes.length] = insert;
1636: allIndexes = newIndexes;
1637: allSizes = newSizes;
1638: if (insert > 0) {
1639: state = 3;
1640: }
1641: }
1642: }
1643: if (state < 3) {
1644: --state;
1645: }
1646: } while (state != 0);
1647:
1648: // 2nd step:
1649: // copies the bytecode of the method into a new bytevector, updates the
1650: // offsets, and inserts (or removes) bytes as requested.
1651:
1652: ByteVector newCode = new ByteVector(code.length);
1653:
1654: u = 0;
1655: while (u < code.length) {
1656: for (i = allIndexes.length - 1; i >= 0; --i) {
1657: if (allIndexes[i] == u) {
1658: if (i < len) {
1659: if (sizes[i] > 0) {
1660: newCode.putByteArray(null, 0, sizes[i]);
1661: } else {
1662: newCode.length += sizes[i];
1663: }
1664: indexes[i] = newCode.length;
1665: }
1666: }
1667: }
1668: int opcode = b[u] & 0xFF;
1669: switch (ClassWriter.TYPE[opcode]) {
1670: case ClassWriter.NOARG_INSN:
1671: case ClassWriter.IMPLVAR_INSN:
1672: newCode.putByte(opcode);
1673: u += 1;
1674: break;
1675: case ClassWriter.LABEL_INSN:
1676: if (opcode > 201) {
1677: // changes temporary opcodes 202 to 217 (inclusive), 218
1678: // and 219 to IFEQ ... JSR (inclusive), IFNULL and
1679: // IFNONNULL
1680: opcode = opcode < 218 ? opcode - 49 : opcode - 20;
1681: label = u + readUnsignedShort(b, u + 1);
1682: } else {
1683: label = u + readShort(b, u + 1);
1684: }
1685: newOffset = getNewOffset(allIndexes, allSizes, u, label);
1686: if (resize[u]) {
1687: // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
1688: // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
1689: // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
1690: // and where <l'> designates the instruction just after
1691: // the GOTO_W.
1692: if (opcode == Opcodes.GOTO) {
1693: newCode.putByte(200); // GOTO_W
1694: } else if (opcode == Opcodes.JSR) {
1695: newCode.putByte(201); // JSR_W
1696: } else {
1697: newCode
1698: .putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
1699: : opcode ^ 1);
1700: newCode.putShort(8); // jump offset
1701: newCode.putByte(200); // GOTO_W
1702: // newOffset now computed from start of GOTO_W
1703: newOffset -= 3;
1704: }
1705: newCode.putInt(newOffset);
1706: } else {
1707: newCode.putByte(opcode);
1708: newCode.putShort(newOffset);
1709: }
1710: u += 3;
1711: break;
1712: case ClassWriter.LABELW_INSN:
1713: label = u + readInt(b, u + 1);
1714: newOffset = getNewOffset(allIndexes, allSizes, u, label);
1715: newCode.putByte(opcode);
1716: newCode.putInt(newOffset);
1717: u += 5;
1718: break;
1719: case ClassWriter.TABL_INSN:
1720: // skips 0 to 3 padding bytes
1721: v = u;
1722: u = u + 4 - (v & 3);
1723: // reads and copies instruction
1724: newCode.putByte(Opcodes.TABLESWITCH);
1725: while (newCode.length % 4 != 0) {
1726: newCode.putByte(0);
1727: }
1728: label = v + readInt(b, u);
1729: u += 4;
1730: newOffset = getNewOffset(allIndexes, allSizes, v, label);
1731: newCode.putInt(newOffset);
1732: j = readInt(b, u);
1733: u += 4;
1734: newCode.putInt(j);
1735: j = readInt(b, u) - j + 1;
1736: u += 4;
1737: newCode.putInt(readInt(b, u - 4));
1738: for (; j > 0; --j) {
1739: label = v + readInt(b, u);
1740: u += 4;
1741: newOffset = getNewOffset(allIndexes, allSizes, v,
1742: label);
1743: newCode.putInt(newOffset);
1744: }
1745: break;
1746: case ClassWriter.LOOK_INSN:
1747: // skips 0 to 3 padding bytes
1748: v = u;
1749: u = u + 4 - (v & 3);
1750: // reads and copies instruction
1751: newCode.putByte(Opcodes.LOOKUPSWITCH);
1752: while (newCode.length % 4 != 0) {
1753: newCode.putByte(0);
1754: }
1755: label = v + readInt(b, u);
1756: u += 4;
1757: newOffset = getNewOffset(allIndexes, allSizes, v, label);
1758: newCode.putInt(newOffset);
1759: j = readInt(b, u);
1760: u += 4;
1761: newCode.putInt(j);
1762: for (; j > 0; --j) {
1763: newCode.putInt(readInt(b, u));
1764: u += 4;
1765: label = v + readInt(b, u);
1766: u += 4;
1767: newOffset = getNewOffset(allIndexes, allSizes, v,
1768: label);
1769: newCode.putInt(newOffset);
1770: }
1771: break;
1772: case ClassWriter.WIDE_INSN:
1773: opcode = b[u + 1] & 0xFF;
1774: if (opcode == Opcodes.IINC) {
1775: newCode.putByteArray(b, u, 6);
1776: u += 6;
1777: } else {
1778: newCode.putByteArray(b, u, 4);
1779: u += 4;
1780: }
1781: break;
1782: case ClassWriter.VAR_INSN:
1783: case ClassWriter.SBYTE_INSN:
1784: case ClassWriter.LDC_INSN:
1785: newCode.putByteArray(b, u, 2);
1786: u += 2;
1787: break;
1788: case ClassWriter.SHORT_INSN:
1789: case ClassWriter.LDCW_INSN:
1790: case ClassWriter.FIELDORMETH_INSN:
1791: case ClassWriter.TYPE_INSN:
1792: case ClassWriter.IINC_INSN:
1793: newCode.putByteArray(b, u, 3);
1794: u += 3;
1795: break;
1796: case ClassWriter.ITFMETH_INSN:
1797: newCode.putByteArray(b, u, 5);
1798: u += 5;
1799: break;
1800: // case MANA_INSN:
1801: default:
1802: newCode.putByteArray(b, u, 4);
1803: u += 4;
1804: break;
1805: }
1806: }
1807:
1808: // updates the exception handler block labels
1809: Handler h = catchTable;
1810: while (h != null) {
1811: getNewOffset(allIndexes, allSizes, h.start);
1812: getNewOffset(allIndexes, allSizes, h.end);
1813: getNewOffset(allIndexes, allSizes, h.handler);
1814: h = h.next;
1815: }
1816: for (i = 0; i < 2; ++i) {
1817: ByteVector bv = i == 0 ? localVar : localVarType;
1818: if (bv != null) {
1819: b = bv.data;
1820: u = 0;
1821: while (u < bv.length) {
1822: label = readUnsignedShort(b, u);
1823: newOffset = getNewOffset(allIndexes, allSizes, 0,
1824: label);
1825: writeShort(b, u, newOffset);
1826: label += readUnsignedShort(b, u + 2);
1827: newOffset = getNewOffset(allIndexes, allSizes, 0,
1828: label)
1829: - newOffset;
1830: writeShort(b, u + 2, newOffset);
1831: u += 10;
1832: }
1833: }
1834: }
1835: if (lineNumber != null) {
1836: b = lineNumber.data;
1837: u = 0;
1838: while (u < lineNumber.length) {
1839: writeShort(b, u, getNewOffset(allIndexes, allSizes, 0,
1840: readUnsignedShort(b, u)));
1841: u += 4;
1842: }
1843: }
1844: // updates the labels of the other attributes
1845: while (cattrs != null) {
1846: Label[] labels = cattrs.getLabels();
1847: if (labels != null) {
1848: for (i = labels.length - 1; i >= 0; --i) {
1849: if (!labels[i].resized) {
1850: labels[i].position = getNewOffset(allIndexes,
1851: allSizes, 0, labels[i].position);
1852: labels[i].resized = true;
1853: }
1854: }
1855: }
1856: }
1857:
1858: // replaces old bytecodes with new ones
1859: code = newCode;
1860:
1861: // returns the positions of the resized instructions
1862: return indexes;
1863: }
1864:
1865: /**
1866: * Reads an unsigned short value in the given byte array.
1867: *
1868: * @param b a byte array.
1869: * @param index the start index of the value to be read.
1870: * @return the read value.
1871: */
1872: static int readUnsignedShort(final byte[] b, final int index) {
1873: return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
1874: }
1875:
1876: /**
1877: * Reads a signed short value in the given byte array.
1878: *
1879: * @param b a byte array.
1880: * @param index the start index of the value to be read.
1881: * @return the read value.
1882: */
1883: static short readShort(final byte[] b, final int index) {
1884: return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
1885: }
1886:
1887: /**
1888: * Reads a signed int value in the given byte array.
1889: *
1890: * @param b a byte array.
1891: * @param index the start index of the value to be read.
1892: * @return the read value.
1893: */
1894: static int readInt(final byte[] b, final int index) {
1895: return ((b[index] & 0xFF) << 24)
1896: | ((b[index + 1] & 0xFF) << 16)
1897: | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
1898: }
1899:
1900: /**
1901: * Writes a short value in the given byte array.
1902: *
1903: * @param b a byte array.
1904: * @param index where the first byte of the short value must be written.
1905: * @param s the value to be written in the given byte array.
1906: */
1907: static void writeShort(final byte[] b, final int index, final int s) {
1908: b[index] = (byte) (s >>> 8);
1909: b[index + 1] = (byte) s;
1910: }
1911:
1912: /**
1913: * Computes the future value of a bytecode offset. <p> Note: it is possible
1914: * to have several entries for the same instruction in the <tt>indexes</tt>
1915: * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')
1916: * are equivalent to a single entry (index=a,size=b+b').
1917: *
1918: * @param indexes current positions of the instructions to be resized. Each
1919: * instruction must be designated by the index of its <i>last</i>
1920: * byte, plus one (or, in other words, by the index of the <i>first</i>
1921: * byte of the <i>next</i> instruction).
1922: * @param sizes the number of bytes to be <i>added</i> to the above
1923: * instructions. More precisely, for each i < <tt>len</tt>,
1924: * <tt>sizes</tt>[i] bytes will be added at the end of the
1925: * instruction designated by <tt>indexes</tt>[i] or, if
1926: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1927: * bytes of the instruction will be removed (the instruction size
1928: * <i>must not</i> become negative or null).
1929: * @param begin index of the first byte of the source instruction.
1930: * @param end index of the first byte of the target instruction.
1931: * @return the future value of the given bytecode offset.
1932: */
1933: static int getNewOffset(final int[] indexes, final int[] sizes,
1934: final int begin, final int end) {
1935: int offset = end - begin;
1936: for (int i = 0; i < indexes.length; ++i) {
1937: if (begin < indexes[i] && indexes[i] <= end) {
1938: // forward jump
1939: offset += sizes[i];
1940: } else if (end < indexes[i] && indexes[i] <= begin) {
1941: // backward jump
1942: offset -= sizes[i];
1943: }
1944: }
1945: return offset;
1946: }
1947:
1948: /**
1949: * Updates the offset of the given label.
1950: *
1951: * @param indexes current positions of the instructions to be resized. Each
1952: * instruction must be designated by the index of its <i>last</i>
1953: * byte, plus one (or, in other words, by the index of the <i>first</i>
1954: * byte of the <i>next</i> instruction).
1955: * @param sizes the number of bytes to be <i>added</i> to the above
1956: * instructions. More precisely, for each i < <tt>len</tt>,
1957: * <tt>sizes</tt>[i] bytes will be added at the end of the
1958: * instruction designated by <tt>indexes</tt>[i] or, if
1959: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1960: * bytes of the instruction will be removed (the instruction size
1961: * <i>must not</i> become negative or null).
1962: * @param label the label whose offset must be updated.
1963: */
1964: static void getNewOffset(final int[] indexes, final int[] sizes,
1965: final Label label) {
1966: if (!label.resized) {
1967: label.position = getNewOffset(indexes, sizes, 0,
1968: label.position);
1969: label.resized = true;
1970: }
1971: }
1972: }
|