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