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.drools.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: */
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: final int[] b = new int[202];
0272: final 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: this .exceptionCount = exceptions.length;
0530: this .exceptions = new int[this .exceptionCount];
0531: for (int i = 0; i < this .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: this .maxLocals = size;
0543: // pushes the first block onto the stack of blocks to be visited
0544: this .currentBlock = new Label();
0545: this .currentBlock.pushed = true;
0546: this .blockStack = this .currentBlock;
0547: }
0548: }
0549:
0550: // ------------------------------------------------------------------------
0551: // Implementation of the MethodVisitor interface
0552: // ------------------------------------------------------------------------
0553:
0554: public AnnotationVisitor visitAnnotationDefault() {
0555: this .annd = new ByteVector();
0556: return new AnnotationWriter(this .cw, false, this .annd, null, 0);
0557: }
0558:
0559: public AnnotationVisitor visitAnnotation(final String desc,
0560: final boolean visible) {
0561: final ByteVector bv = new ByteVector();
0562: // write type, and reserve space for values count
0563: bv.putShort(this .cw.newUTF8(desc)).putShort(0);
0564: final AnnotationWriter aw = new AnnotationWriter(this .cw, true,
0565: bv, bv, 2);
0566: if (visible) {
0567: aw.next = this .anns;
0568: this .anns = aw;
0569: } else {
0570: aw.next = this .ianns;
0571: this .ianns = aw;
0572: }
0573: return aw;
0574: }
0575:
0576: public AnnotationVisitor visitParameterAnnotation(
0577: final int parameter, final String desc,
0578: final boolean visible) {
0579: final ByteVector bv = new ByteVector();
0580: // write type, and reserve space for values count
0581: bv.putShort(this .cw.newUTF8(desc)).putShort(0);
0582: final AnnotationWriter aw = new AnnotationWriter(this .cw, true,
0583: bv, bv, 2);
0584: if (visible) {
0585: if (this .panns == null) {
0586: this .panns = new AnnotationWriter[Type
0587: .getArgumentTypes(this .descriptor).length];
0588: }
0589: aw.next = this .panns[parameter];
0590: this .panns[parameter] = aw;
0591: } else {
0592: if (this .ipanns == null) {
0593: this .ipanns = new AnnotationWriter[Type
0594: .getArgumentTypes(this .descriptor).length];
0595: }
0596: aw.next = this .ipanns[parameter];
0597: this .ipanns[parameter] = aw;
0598: }
0599: return aw;
0600: }
0601:
0602: public void visitAttribute(final Attribute attr) {
0603: if (attr.isCodeAttribute()) {
0604: attr.next = this .cattrs;
0605: this .cattrs = attr;
0606: } else {
0607: attr.next = this .attrs;
0608: this .attrs = attr;
0609: }
0610: }
0611:
0612: public void visitCode() {
0613: }
0614:
0615: public void visitInsn(final int opcode) {
0616: if (this .computeMaxs) {
0617: // updates current and min stack sizes
0618: final int size = this .stackSize + MethodWriter.SIZE[opcode];
0619: if (size > this .maxStackSize) {
0620: this .maxStackSize = size;
0621: }
0622: this .stackSize = size;
0623: // if opcode == ATHROW or xRETURN, ends current block (no successor)
0624: if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
0625: || opcode == Opcodes.ATHROW) {
0626: if (this .currentBlock != null) {
0627: this .currentBlock.maxStackSize = this .maxStackSize;
0628: this .currentBlock = null;
0629: }
0630: }
0631: }
0632: // adds the instruction to the bytecode of the method
0633: this .code.putByte(opcode);
0634: }
0635:
0636: public void visitIntInsn(final int opcode, final int operand) {
0637: if (this .computeMaxs && opcode != Opcodes.NEWARRAY) {
0638: // updates current and min stack sizes only if opcode == NEWARRAY
0639: // (stack size variation = 0 for BIPUSH or SIPUSH)
0640: final int size = this .stackSize + 1;
0641: if (size > this .maxStackSize) {
0642: this .maxStackSize = size;
0643: }
0644: this .stackSize = size;
0645: }
0646: // adds the instruction to the bytecode of the method
0647: if (opcode == Opcodes.SIPUSH) {
0648: this .code.put12(opcode, operand);
0649: } else { // BIPUSH or NEWARRAY
0650: this .code.put11(opcode, operand);
0651: }
0652: }
0653:
0654: public void visitVarInsn(final int opcode, final int var) {
0655: if (this .computeMaxs) {
0656: // updates current and min stack sizes
0657: if (opcode == Opcodes.RET) {
0658: // no stack change, but end of current block (no successor)
0659: if (this .currentBlock != null) {
0660: this .currentBlock.maxStackSize = this .maxStackSize;
0661: this .currentBlock = null;
0662: }
0663: } else { // xLOAD or xSTORE
0664: final int size = this .stackSize
0665: + MethodWriter.SIZE[opcode];
0666: if (size > this .maxStackSize) {
0667: this .maxStackSize = size;
0668: }
0669: this .stackSize = size;
0670: }
0671: // updates min locals
0672: int n;
0673: if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
0674: || opcode == Opcodes.LSTORE
0675: || opcode == Opcodes.DSTORE) {
0676: n = var + 2;
0677: } else {
0678: n = var + 1;
0679: }
0680: if (n > this .maxLocals) {
0681: this .maxLocals = n;
0682: }
0683: }
0684: // adds the instruction to the bytecode of the method
0685: if (var < 4 && opcode != Opcodes.RET) {
0686: int opt;
0687: if (opcode < Opcodes.ISTORE) {
0688: /* ILOAD_0 */
0689: opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
0690: } else {
0691: /* ISTORE_0 */
0692: opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
0693: }
0694: this .code.putByte(opt);
0695: } else if (var >= 256) {
0696: this .code.putByte(196 /* WIDE */).put12(opcode, var);
0697: } else {
0698: this .code.put11(opcode, var);
0699: }
0700: }
0701:
0702: public void visitTypeInsn(final int opcode, final String desc) {
0703: if (this .computeMaxs && opcode == Opcodes.NEW) {
0704: // updates current and min stack sizes only if opcode == NEW
0705: // (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF)
0706: final int size = this .stackSize + 1;
0707: if (size > this .maxStackSize) {
0708: this .maxStackSize = size;
0709: }
0710: this .stackSize = size;
0711: }
0712: // adds the instruction to the bytecode of the method
0713: this .code.put12(opcode, this .cw.newClass(desc));
0714: }
0715:
0716: public void visitFieldInsn(final int opcode, final String owner,
0717: final String name, final String desc) {
0718: if (this .computeMaxs) {
0719: int size;
0720: // computes the stack size variation
0721: final char c = desc.charAt(0);
0722: switch (opcode) {
0723: case Opcodes.GETSTATIC:
0724: size = this .stackSize + (c == 'D' || c == 'J' ? 2 : 1);
0725: break;
0726: case Opcodes.PUTSTATIC:
0727: size = this .stackSize
0728: + (c == 'D' || c == 'J' ? -2 : -1);
0729: break;
0730: case Opcodes.GETFIELD:
0731: size = this .stackSize + (c == 'D' || c == 'J' ? 1 : 0);
0732: break;
0733: // case Constants.PUTFIELD:
0734: default:
0735: size = this .stackSize
0736: + (c == 'D' || c == 'J' ? -3 : -2);
0737: break;
0738: }
0739: // updates current and min stack sizes
0740: if (size > this .maxStackSize) {
0741: this .maxStackSize = size;
0742: }
0743: this .stackSize = size;
0744: }
0745: // adds the instruction to the bytecode of the method
0746: this .code.put12(opcode, this .cw.newField(owner, name, desc));
0747: }
0748:
0749: public void visitMethodInsn(final int opcode, final String owner,
0750: final String name, final String desc) {
0751: final boolean itf = opcode == Opcodes.INVOKEINTERFACE;
0752: final Item i = this .cw.newMethodItem(owner, name, desc, itf);
0753: int argSize = i.intVal;
0754: if (this .computeMaxs) {
0755: /*
0756: * computes the stack size variation. In order not to recompute
0757: * several times this variation for the same Item, we use the intVal
0758: * field of this item to store this variation, once it has been
0759: * computed. More precisely this intVal field stores the sizes of
0760: * the arguments and of the return value corresponding to desc.
0761: */
0762: if (argSize == 0) {
0763: // the above sizes have not been computed yet, so we compute
0764: // them...
0765: argSize = getArgumentsAndReturnSizes(desc);
0766: // ... and we save them in order not to recompute them in the
0767: // future
0768: i.intVal = argSize;
0769: }
0770: int size;
0771: if (opcode == Opcodes.INVOKESTATIC) {
0772: size = this .stackSize - (argSize >> 2)
0773: + (argSize & 0x03) + 1;
0774: } else {
0775: size = this .stackSize - (argSize >> 2)
0776: + (argSize & 0x03);
0777: }
0778: // updates current and min stack sizes
0779: if (size > this .maxStackSize) {
0780: this .maxStackSize = size;
0781: }
0782: this .stackSize = size;
0783: }
0784: // adds the instruction to the bytecode of the method
0785: if (itf) {
0786: if (!this .computeMaxs) {
0787: if (argSize == 0) {
0788: argSize = getArgumentsAndReturnSizes(desc);
0789: i.intVal = argSize;
0790: }
0791: }
0792: this .code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(
0793: argSize >> 2, 0);
0794: } else {
0795: this .code.put12(opcode, i.index);
0796: }
0797: }
0798:
0799: public void visitJumpInsn(final int opcode, final Label label) {
0800: if (this .computeMaxs) {
0801: if (opcode == Opcodes.GOTO) {
0802: // no stack change, but end of current block (with one new
0803: // successor)
0804: if (this .currentBlock != null) {
0805: this .currentBlock.maxStackSize = this .maxStackSize;
0806: addSuccessor(this .stackSize, label);
0807: this .currentBlock = null;
0808: }
0809: } else if (opcode == Opcodes.JSR) {
0810: if (this .currentBlock != null) {
0811: addSuccessor(this .stackSize + 1, label);
0812: }
0813: } else {
0814: // updates current stack size (min stack size unchanged because
0815: // stack size variation always negative in this case)
0816: this .stackSize += MethodWriter.SIZE[opcode];
0817: if (this .currentBlock != null) {
0818: addSuccessor(this .stackSize, label);
0819: }
0820: }
0821: }
0822: // adds the instruction to the bytecode of the method
0823: if (label.resolved
0824: && label.position - this .code.length < Short.MIN_VALUE) {
0825: /*
0826: * case of a backward jump with an offset < -32768. In this case we
0827: * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
0828: * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
0829: * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
0830: * designates the instruction just after the GOTO_W.
0831: */
0832: if (opcode == Opcodes.GOTO) {
0833: this .code.putByte(200); // GOTO_W
0834: } else if (opcode == Opcodes.JSR) {
0835: this .code.putByte(201); // JSR_W
0836: } else {
0837: this .code
0838: .putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
0839: : opcode ^ 1);
0840: this .code.putShort(8); // jump offset
0841: this .code.putByte(200); // GOTO_W
0842: }
0843: label.put(this , this .code, this .code.length - 1, true);
0844: } else {
0845: /*
0846: * case of a backward jump with an offset >= -32768, or of a forward
0847: * jump with, of course, an unknown offset. In these cases we store
0848: * the offset in 2 bytes (which will be increased in
0849: * resizeInstructions, if needed).
0850: */
0851: this .code.putByte(opcode);
0852: label.put(this , this .code, this .code.length - 1, false);
0853: }
0854: }
0855:
0856: public void visitLabel(final Label label) {
0857: if (this .computeMaxs) {
0858: if (this .currentBlock != null) {
0859: // ends current block (with one new successor)
0860: this .currentBlock.maxStackSize = this .maxStackSize;
0861: addSuccessor(this .stackSize, label);
0862: }
0863: // begins a new current block,
0864: // resets the relative current and min stack sizes
0865: this .currentBlock = label;
0866: this .stackSize = 0;
0867: this .maxStackSize = 0;
0868: }
0869: // resolves previous forward references to label, if any
0870: this .resize |= label.resolve(this , this .code.length,
0871: this .code.data);
0872: }
0873:
0874: public void visitLdcInsn(final Object cst) {
0875: final Item i = this .cw.newConstItem(cst);
0876: if (this .computeMaxs) {
0877: int size;
0878: // computes the stack size variation
0879: if (i.type == ClassWriter.LONG
0880: || i.type == ClassWriter.DOUBLE) {
0881: size = this .stackSize + 2;
0882: } else {
0883: size = this .stackSize + 1;
0884: }
0885: // updates current and min stack sizes
0886: if (size > this .maxStackSize) {
0887: this .maxStackSize = size;
0888: }
0889: this .stackSize = size;
0890: }
0891: // adds the instruction to the bytecode of the method
0892: final int index = i.index;
0893: if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
0894: this .code.put12(20 /* LDC2_W */, index);
0895: } else if (index >= 256) {
0896: this .code.put12(19 /* LDC_W */, index);
0897: } else {
0898: this .code.put11(Opcodes.LDC, index);
0899: }
0900: }
0901:
0902: public void visitIincInsn(final int var, final int increment) {
0903: if (this .computeMaxs) {
0904: // updates min locals only (no stack change)
0905: final int n = var + 1;
0906: if (n > this .maxLocals) {
0907: this .maxLocals = n;
0908: }
0909: }
0910: // adds the instruction to the bytecode of the method
0911: if ((var > 255) || (increment > 127) || (increment < -128)) {
0912: this .code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var)
0913: .putShort(increment);
0914: } else {
0915: this .code.putByte(Opcodes.IINC).put11(var, increment);
0916: }
0917: }
0918:
0919: public void visitTableSwitchInsn(final int min, final int max,
0920: final Label dflt, final Label labels[]) {
0921: if (this .computeMaxs) {
0922: // updates current stack size (min stack size unchanged)
0923: --this .stackSize;
0924: // ends current block (with many new successors)
0925: if (this .currentBlock != null) {
0926: this .currentBlock.maxStackSize = this .maxStackSize;
0927: addSuccessor(this .stackSize, dflt);
0928: for (int i = 0; i < labels.length; ++i) {
0929: addSuccessor(this .stackSize, labels[i]);
0930: }
0931: this .currentBlock = null;
0932: }
0933: }
0934: // adds the instruction to the bytecode of the method
0935: final int source = this .code.length;
0936: this .code.putByte(Opcodes.TABLESWITCH);
0937: while (this .code.length % 4 != 0) {
0938: this .code.putByte(0);
0939: }
0940: dflt.put(this , this .code, source, true);
0941: this .code.putInt(min).putInt(max);
0942: for (int i = 0; i < labels.length; ++i) {
0943: labels[i].put(this , this .code, source, true);
0944: }
0945: }
0946:
0947: public void visitLookupSwitchInsn(final Label dflt,
0948: final int keys[], final Label labels[]) {
0949: if (this .computeMaxs) {
0950: // updates current stack size (min stack size unchanged)
0951: --this .stackSize;
0952: // ends current block (with many new successors)
0953: if (this .currentBlock != null) {
0954: this .currentBlock.maxStackSize = this .maxStackSize;
0955: addSuccessor(this .stackSize, dflt);
0956: for (int i = 0; i < labels.length; ++i) {
0957: addSuccessor(this .stackSize, labels[i]);
0958: }
0959: this .currentBlock = null;
0960: }
0961: }
0962: // adds the instruction to the bytecode of the method
0963: final int source = this .code.length;
0964: this .code.putByte(Opcodes.LOOKUPSWITCH);
0965: while (this .code.length % 4 != 0) {
0966: this .code.putByte(0);
0967: }
0968: dflt.put(this , this .code, source, true);
0969: this .code.putInt(labels.length);
0970: for (int i = 0; i < labels.length; ++i) {
0971: this .code.putInt(keys[i]);
0972: labels[i].put(this , this .code, source, true);
0973: }
0974: }
0975:
0976: public void visitMultiANewArrayInsn(final String desc,
0977: final int dims) {
0978: if (this .computeMaxs) {
0979: // updates current stack size (min stack size unchanged because
0980: // stack size variation always negative or null)
0981: this .stackSize += 1 - dims;
0982: }
0983: // adds the instruction to the bytecode of the method
0984: this .code.put12(Opcodes.MULTIANEWARRAY, this .cw.newClass(desc))
0985: .putByte(dims);
0986: }
0987:
0988: public void visitTryCatchBlock(final Label start, final Label end,
0989: final Label handler, final String type) {
0990: if (this .computeMaxs) {
0991: // pushes handler block onto the stack of blocks to be visited
0992: if (!handler.pushed) {
0993: handler.beginStackSize = 1;
0994: handler.pushed = true;
0995: handler.next = this .blockStack;
0996: this .blockStack = handler;
0997: }
0998: }
0999: ++this .catchCount;
1000: final Handler h = new Handler();
1001: h.start = start;
1002: h.end = end;
1003: h.handler = handler;
1004: h.desc = type;
1005: h.type = type != null ? this .cw.newClass(type) : 0;
1006: if (this .lastHandler == null) {
1007: this .catchTable = h;
1008: } else {
1009: this .lastHandler.next = h;
1010: }
1011: this .lastHandler = h;
1012: }
1013:
1014: public void visitLocalVariable(final String name,
1015: final String desc, final String signature,
1016: final Label start, final Label end, final int index) {
1017: if (signature != null) {
1018: if (this .localVarType == null) {
1019: this .localVarType = new ByteVector();
1020: }
1021: ++this .localVarTypeCount;
1022: this .localVarType.putShort(start.position).putShort(
1023: end.position - start.position).putShort(
1024: this .cw.newUTF8(name)).putShort(
1025: this .cw.newUTF8(signature)).putShort(index);
1026: }
1027: if (this .localVar == null) {
1028: this .localVar = new ByteVector();
1029: }
1030: ++this .localVarCount;
1031: this .localVar.putShort(start.position).putShort(
1032: end.position - start.position).putShort(
1033: this .cw.newUTF8(name)).putShort(this .cw.newUTF8(desc))
1034: .putShort(index);
1035: }
1036:
1037: public void visitLineNumber(final int line, final Label start) {
1038: if (this .lineNumber == null) {
1039: this .lineNumber = new ByteVector();
1040: }
1041: ++this .lineNumberCount;
1042: this .lineNumber.putShort(start.position);
1043: this .lineNumber.putShort(line);
1044: }
1045:
1046: public void visitMaxs(final int maxStack, final int maxLocals) {
1047: if (this .computeMaxs) {
1048: // true (non relative) min stack size
1049: int max = 0;
1050: /*
1051: * control flow analysis algorithm: while the block stack is not
1052: * empty, pop a block from this stack, update the min stack size,
1053: * compute the true (non relative) begin stack size of the
1054: * successors of this block, and push these successors onto the
1055: * stack (unless they have already been pushed onto the stack).
1056: * Note: by hypothesis, the {@link Label#beginStackSize} of the
1057: * blocks in the block stack are the true (non relative) beginning
1058: * stack sizes of these blocks.
1059: */
1060: Label stack = this .blockStack;
1061: while (stack != null) {
1062: // pops a block from the stack
1063: Label l = stack;
1064: stack = stack.next;
1065: // computes the true (non relative) min stack size of this block
1066: final int start = l.beginStackSize;
1067: final int blockMax = start + l.maxStackSize;
1068: // updates the global min stack size
1069: if (blockMax > max) {
1070: max = blockMax;
1071: }
1072: // analyses the successors of the block
1073: Edge b = l.successors;
1074: while (b != null) {
1075: l = b.successor;
1076: // if this successor has not already been pushed onto the
1077: // stack...
1078: if (!l.pushed) {
1079: // computes the true beginning stack size of this
1080: // successor block
1081: l.beginStackSize = start + b.stackSize;
1082: // pushes this successor onto the stack
1083: l.pushed = true;
1084: l.next = stack;
1085: stack = l;
1086: }
1087: b = b.next;
1088: }
1089: }
1090: this .maxStack = max;
1091: } else {
1092: this .maxStack = maxStack;
1093: this .maxLocals = maxLocals;
1094: }
1095: }
1096:
1097: public void visitEnd() {
1098: }
1099:
1100: // ------------------------------------------------------------------------
1101: // Utility methods: control flow analysis algorithm
1102: // ------------------------------------------------------------------------
1103:
1104: /**
1105: * Computes the size of the arguments and of the return value of a method.
1106: *
1107: * @param desc the descriptor of a method.
1108: * @return the size of the arguments of the method (plus one for the
1109: * implicit this argument), argSize, and the size of its return
1110: * value, retSize, packed into a single int i =
1111: * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
1112: * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
1113: */
1114: private static int getArgumentsAndReturnSizes(final String desc) {
1115: int n = 1;
1116: int c = 1;
1117: while (true) {
1118: char car = desc.charAt(c++);
1119: if (car == ')') {
1120: car = desc.charAt(c);
1121: return n << 2
1122: | (car == 'V' ? 0
1123: : (car == 'D' || car == 'J' ? 2 : 1));
1124: } else if (car == 'L') {
1125: while (desc.charAt(c++) != ';') {
1126: }
1127: n += 1;
1128: } else if (car == '[') {
1129: while ((car = desc.charAt(c)) == '[') {
1130: ++c;
1131: }
1132: if (car == 'D' || car == 'J') {
1133: n -= 1;
1134: }
1135: } else if (car == 'D' || car == 'J') {
1136: n += 2;
1137: } else {
1138: n += 1;
1139: }
1140: }
1141: }
1142:
1143: /**
1144: * Adds a successor to the {@link #currentBlock currentBlock} block.
1145: *
1146: * @param stackSize the current (relative) stack size in the current block.
1147: * @param successor the successor block to be added to the current block.
1148: */
1149: private void addSuccessor(final int stackSize, final Label successor) {
1150: final Edge b = new Edge();
1151: // initializes the previous Edge object...
1152: b.stackSize = stackSize;
1153: b.successor = successor;
1154: // ...and adds it to the successor list of the currentBlock block
1155: b.next = this .currentBlock.successors;
1156: this .currentBlock.successors = b;
1157: }
1158:
1159: // ------------------------------------------------------------------------
1160: // Utility methods: dump bytecode array
1161: // ------------------------------------------------------------------------
1162:
1163: /**
1164: * Returns the size of the bytecode of this method.
1165: *
1166: * @return the size of the bytecode of this method.
1167: */
1168: final int getSize() {
1169: if (this .classReaderOffset != 0) {
1170: return 6 + this .classReaderLength;
1171: }
1172: if (this .resize) {
1173: // replaces the temporary jump opcodes introduced by Label.resolve.
1174: resizeInstructions(new int[0], new int[0], 0);
1175: }
1176: int size = 8;
1177: if (this .code.length > 0) {
1178: this .cw.newUTF8("Code");
1179: size += 18 + this .code.length + 8 * this .catchCount;
1180: if (this .localVar != null) {
1181: this .cw.newUTF8("LocalVariableTable");
1182: size += 8 + this .localVar.length;
1183: }
1184: if (this .localVarType != null) {
1185: this .cw.newUTF8("LocalVariableTypeTable");
1186: size += 8 + this .localVarType.length;
1187: }
1188: if (this .lineNumber != null) {
1189: this .cw.newUTF8("LineNumberTable");
1190: size += 8 + this .lineNumber.length;
1191: }
1192: if (this .cattrs != null) {
1193: size += this .cattrs
1194: .getSize(this .cw, this .code.data,
1195: this .code.length, this .maxStack,
1196: this .maxLocals);
1197: }
1198: }
1199: if (this .exceptionCount > 0) {
1200: this .cw.newUTF8("Exceptions");
1201: size += 8 + 2 * this .exceptionCount;
1202: }
1203: if ((this .access & Opcodes.ACC_SYNTHETIC) != 0
1204: && (this .cw.version & 0xffff) < Opcodes.V1_5) {
1205: this .cw.newUTF8("Synthetic");
1206: size += 6;
1207: }
1208: if ((this .access & Opcodes.ACC_DEPRECATED) != 0) {
1209: this .cw.newUTF8("Deprecated");
1210: size += 6;
1211: }
1212: if (this .cw.version == Opcodes.V1_4) {
1213: if ((this .access & Opcodes.ACC_VARARGS) != 0) {
1214: this .cw.newUTF8("Varargs");
1215: size += 6;
1216: }
1217: if ((this .access & Opcodes.ACC_BRIDGE) != 0) {
1218: this .cw.newUTF8("Bridge");
1219: size += 6;
1220: }
1221: }
1222: if (this .signature != null) {
1223: this .cw.newUTF8("Signature");
1224: this .cw.newUTF8(this .signature);
1225: size += 8;
1226: }
1227: if (this .annd != null) {
1228: this .cw.newUTF8("AnnotationDefault");
1229: size += 6 + this .annd.length;
1230: }
1231: if (this .anns != null) {
1232: this .cw.newUTF8("RuntimeVisibleAnnotations");
1233: size += 8 + this .anns.getSize();
1234: }
1235: if (this .ianns != null) {
1236: this .cw.newUTF8("RuntimeInvisibleAnnotations");
1237: size += 8 + this .ianns.getSize();
1238: }
1239: if (this .panns != null) {
1240: this .cw.newUTF8("RuntimeVisibleParameterAnnotations");
1241: size += 7 + 2 * this .panns.length;
1242: for (int i = this .panns.length - 1; i >= 0; --i) {
1243: size += this .panns[i] == null ? 0 : this .panns[i]
1244: .getSize();
1245: }
1246: }
1247: if (this .ipanns != null) {
1248: this .cw.newUTF8("RuntimeInvisibleParameterAnnotations");
1249: size += 7 + 2 * this .ipanns.length;
1250: for (int i = this .ipanns.length - 1; i >= 0; --i) {
1251: size += this .ipanns[i] == null ? 0 : this .ipanns[i]
1252: .getSize();
1253: }
1254: }
1255: if (this .attrs != null) {
1256: size += this .attrs.getSize(this .cw, null, 0, -1, -1);
1257: }
1258: return size;
1259: }
1260:
1261: /**
1262: * Puts the bytecode of this method in the given byte vector.
1263: *
1264: * @param out the byte vector into which the bytecode of this method must be
1265: * copied.
1266: */
1267: final void put(final ByteVector out) {
1268: out.putShort(this .access).putShort(this .name).putShort(
1269: this .desc);
1270: if (this .classReaderOffset != 0) {
1271: out.putByteArray(this .cw.cr.b, this .classReaderOffset,
1272: this .classReaderLength);
1273: return;
1274: }
1275: int attributeCount = 0;
1276: if (this .code.length > 0) {
1277: ++attributeCount;
1278: }
1279: if (this .exceptionCount > 0) {
1280: ++attributeCount;
1281: }
1282: if ((this .access & Opcodes.ACC_SYNTHETIC) != 0
1283: && (this .cw.version & 0xffff) < Opcodes.V1_5) {
1284: ++attributeCount;
1285: }
1286: if ((this .access & Opcodes.ACC_DEPRECATED) != 0) {
1287: ++attributeCount;
1288: }
1289: if (this .cw.version == Opcodes.V1_4) {
1290: if ((this .access & Opcodes.ACC_VARARGS) != 0) {
1291: ++attributeCount;
1292: }
1293: if ((this .access & Opcodes.ACC_BRIDGE) != 0) {
1294: ++attributeCount;
1295: }
1296: }
1297: if (this .signature != null) {
1298: ++attributeCount;
1299: }
1300: if (this .annd != null) {
1301: ++attributeCount;
1302: }
1303: if (this .anns != null) {
1304: ++attributeCount;
1305: }
1306: if (this .ianns != null) {
1307: ++attributeCount;
1308: }
1309: if (this .panns != null) {
1310: ++attributeCount;
1311: }
1312: if (this .ipanns != null) {
1313: ++attributeCount;
1314: }
1315: if (this .attrs != null) {
1316: attributeCount += this .attrs.getCount();
1317: }
1318: out.putShort(attributeCount);
1319: if (this .code.length > 0) {
1320: int size = 12 + this .code.length + 8 * this .catchCount;
1321: if (this .localVar != null) {
1322: size += 8 + this .localVar.length;
1323: }
1324: if (this .localVarType != null) {
1325: size += 8 + this .localVarType.length;
1326: }
1327: if (this .lineNumber != null) {
1328: size += 8 + this .lineNumber.length;
1329: }
1330: if (this .cattrs != null) {
1331: size += this .cattrs
1332: .getSize(this .cw, this .code.data,
1333: this .code.length, this .maxStack,
1334: this .maxLocals);
1335: }
1336: out.putShort(this .cw.newUTF8("Code")).putInt(size);
1337: out.putShort(this .maxStack).putShort(this .maxLocals);
1338: out.putInt(this .code.length).putByteArray(this .code.data,
1339: 0, this .code.length);
1340: out.putShort(this .catchCount);
1341: if (this .catchCount > 0) {
1342: Handler h = this .catchTable;
1343: while (h != null) {
1344: out.putShort(h.start.position).putShort(
1345: h.end.position)
1346: .putShort(h.handler.position).putShort(
1347: h.type);
1348: h = h.next;
1349: }
1350: }
1351: attributeCount = 0;
1352: if (this .localVar != null) {
1353: ++attributeCount;
1354: }
1355: if (this .localVarType != null) {
1356: ++attributeCount;
1357: }
1358: if (this .lineNumber != null) {
1359: ++attributeCount;
1360: }
1361: if (this .cattrs != null) {
1362: attributeCount += this .cattrs.getCount();
1363: }
1364: out.putShort(attributeCount);
1365: if (this .localVar != null) {
1366: out.putShort(this .cw.newUTF8("LocalVariableTable"));
1367: out.putInt(this .localVar.length + 2).putShort(
1368: this .localVarCount);
1369: out.putByteArray(this .localVar.data, 0,
1370: this .localVar.length);
1371: }
1372: if (this .localVarType != null) {
1373: out.putShort(this .cw.newUTF8("LocalVariableTypeTable"));
1374: out.putInt(this .localVarType.length + 2).putShort(
1375: this .localVarTypeCount);
1376: out.putByteArray(this .localVarType.data, 0,
1377: this .localVarType.length);
1378: }
1379: if (this .lineNumber != null) {
1380: out.putShort(this .cw.newUTF8("LineNumberTable"));
1381: out.putInt(this .lineNumber.length + 2).putShort(
1382: this .lineNumberCount);
1383: out.putByteArray(this .lineNumber.data, 0,
1384: this .lineNumber.length);
1385: }
1386: if (this .cattrs != null) {
1387: this .cattrs.put(this .cw, this .code.data,
1388: this .code.length, this .maxLocals,
1389: this .maxStack, out);
1390: }
1391: }
1392: if (this .exceptionCount > 0) {
1393: out.putShort(this .cw.newUTF8("Exceptions")).putInt(
1394: 2 * this .exceptionCount + 2);
1395: out.putShort(this .exceptionCount);
1396: for (int i = 0; i < this .exceptionCount; ++i) {
1397: out.putShort(this .exceptions[i]);
1398: }
1399: }
1400: if ((this .access & Opcodes.ACC_SYNTHETIC) != 0
1401: && (this .cw.version & 0xffff) < Opcodes.V1_5) {
1402: out.putShort(this .cw.newUTF8("Synthetic")).putInt(0);
1403: }
1404: if ((this .access & Opcodes.ACC_DEPRECATED) != 0) {
1405: out.putShort(this .cw.newUTF8("Deprecated")).putInt(0);
1406: }
1407: if (this .cw.version == Opcodes.V1_4) {
1408: if ((this .access & Opcodes.ACC_VARARGS) != 0) {
1409: out.putShort(this .cw.newUTF8("Varargs")).putInt(0);
1410: }
1411: if ((this .access & Opcodes.ACC_BRIDGE) != 0) {
1412: out.putShort(this .cw.newUTF8("Bridge")).putInt(0);
1413: }
1414: }
1415: if (this .signature != null) {
1416: out.putShort(this .cw.newUTF8("Signature")).putInt(2)
1417: .putShort(this .cw.newUTF8(this .signature));
1418: }
1419: if (this .annd != null) {
1420: out.putShort(this .cw.newUTF8("AnnotationDefault"));
1421: out.putInt(this .annd.length);
1422: out.putByteArray(this .annd.data, 0, this .annd.length);
1423: }
1424: if (this .anns != null) {
1425: out.putShort(this .cw.newUTF8("RuntimeVisibleAnnotations"));
1426: this .anns.put(out);
1427: }
1428: if (this .ianns != null) {
1429: out
1430: .putShort(this .cw
1431: .newUTF8("RuntimeInvisibleAnnotations"));
1432: this .ianns.put(out);
1433: }
1434: if (this .panns != null) {
1435: out.putShort(this .cw
1436: .newUTF8("RuntimeVisibleParameterAnnotations"));
1437: AnnotationWriter.put(this .panns, out);
1438: }
1439: if (this .ipanns != null) {
1440: out.putShort(this .cw
1441: .newUTF8("RuntimeInvisibleParameterAnnotations"));
1442: AnnotationWriter.put(this .ipanns, out);
1443: }
1444: if (this .attrs != null) {
1445: this .attrs.put(this .cw, null, 0, -1, -1, out);
1446: }
1447: }
1448:
1449: // ------------------------------------------------------------------------
1450: // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
1451: // ------------------------------------------------------------------------
1452:
1453: /**
1454: * Resizes the designated instructions, while keeping jump offsets and
1455: * instruction addresses consistent. This may require to resize other
1456: * existing instructions, or even to introduce new instructions: for
1457: * example, increasing the size of an instruction by 2 at the middle of a
1458: * method can increases the offset of an IFEQ instruction from 32766 to
1459: * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
1460: * 32765. This, in turn, may require to increase the size of another jump
1461: * instruction, and so on... All these operations are handled automatically
1462: * by this method. <p> <i>This method must be called after all the method
1463: * that is being built has been visited</i>. In particular, the
1464: * {@link Label Label} objects used to construct the method are no longer
1465: * valid after this method has been called.
1466: *
1467: * @param indexes current positions of the instructions to be resized. Each
1468: * instruction must be designated by the index of its <i>last</i>
1469: * byte, plus one (or, in other words, by the index of the <i>first</i>
1470: * byte of the <i>next</i> instruction).
1471: * @param sizes the number of bytes to be <i>added</i> to the above
1472: * instructions. More precisely, for each i < <tt>len</tt>,
1473: * <tt>sizes</tt>[i] bytes will be added at the end of the
1474: * instruction designated by <tt>indexes</tt>[i] or, if
1475: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1476: * bytes of the instruction will be removed (the instruction size
1477: * <i>must not</i> become negative or null). The gaps introduced by
1478: * this method must be filled in "manually" in {@link #code code}
1479: * method.
1480: * @param len the number of instruction to be resized. Must be smaller than
1481: * or equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length.
1482: * @return the <tt>indexes</tt> array, which now contains the new
1483: * positions of the resized instructions (designated as above).
1484: */
1485: private int[] resizeInstructions(final int[] indexes,
1486: final int[] sizes, final int len) {
1487: byte[] b = this .code.data; // bytecode of the method
1488: int u, v, label; // indexes in b
1489: int i, j; // loop indexes
1490:
1491: /*
1492: * 1st step: As explained above, resizing an instruction may require to
1493: * resize another one, which may require to resize yet another one, and
1494: * so on. The first step of the algorithm consists in finding all the
1495: * instructions that need to be resized, without modifying the code.
1496: * This is done by the following "fix point" algorithm:
1497: *
1498: * Parse the code to find the jump instructions whose offset will need
1499: * more than 2 bytes to be stored (the future offset is computed from
1500: * the current offset and from the number of bytes that will be inserted
1501: * or removed between the source and target instructions). For each such
1502: * instruction, adds an entry in (a copy of) the indexes and sizes
1503: * arrays (if this has not already been done in a previous iteration!).
1504: *
1505: * If at least one entry has been added during the previous step, go
1506: * back to the beginning, otherwise stop.
1507: *
1508: * In fact the real algorithm is complicated by the fact that the size
1509: * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
1510: * position in the bytecode (because of padding). In order to ensure the
1511: * convergence of the algorithm, the number of bytes to be added or
1512: * removed from these instructions is over estimated during the previous
1513: * loop, and computed exactly only after the loop is finished (this
1514: * requires another pass to parse the bytecode of the method).
1515: */
1516: int[] allIndexes = new int[len]; // copy of indexes
1517: int[] allSizes = new int[len]; // copy of sizes
1518: boolean[] resize; // instructions to be resized
1519: int newOffset; // future offset of a jump instruction
1520:
1521: System.arraycopy(indexes, 0, allIndexes, 0, len);
1522: System.arraycopy(sizes, 0, allSizes, 0, len);
1523: resize = new boolean[this .code.length];
1524:
1525: // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
1526: int state = 3;
1527: do {
1528: if (state == 3) {
1529: state = 2;
1530: }
1531: u = 0;
1532: while (u < b.length) {
1533: int opcode = b[u] & 0xFF; // opcode of current instruction
1534: int insert = 0; // bytes to be added after this instruction
1535:
1536: switch (ClassWriter.TYPE[opcode]) {
1537: case ClassWriter.NOARG_INSN:
1538: case ClassWriter.IMPLVAR_INSN:
1539: u += 1;
1540: break;
1541: case ClassWriter.LABEL_INSN:
1542: if (opcode > 201) {
1543: // converts temporary opcodes 202 to 217, 218 and
1544: // 219 to IFEQ ... JSR (inclusive), IFNULL and
1545: // IFNONNULL
1546: opcode = opcode < 218 ? opcode - 49
1547: : opcode - 20;
1548: label = u + readUnsignedShort(b, u + 1);
1549: } else {
1550: label = u + readShort(b, u + 1);
1551: }
1552: newOffset = getNewOffset(allIndexes, allSizes, u,
1553: label);
1554: if (newOffset < Short.MIN_VALUE
1555: || newOffset > Short.MAX_VALUE) {
1556: if (!resize[u]) {
1557: if (opcode == Opcodes.GOTO
1558: || opcode == Opcodes.JSR) {
1559: // two additional bytes will be required to
1560: // replace this GOTO or JSR instruction with
1561: // a GOTO_W or a JSR_W
1562: insert = 2;
1563: } else {
1564: // five additional bytes will be required to
1565: // replace this IFxxx <l> instruction with
1566: // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
1567: // is the "opposite" opcode of IFxxx (i.e.,
1568: // IFNE for IFEQ) and where <l'> designates
1569: // the instruction just after the GOTO_W.
1570: insert = 5;
1571: }
1572: resize[u] = true;
1573: }
1574: }
1575: u += 3;
1576: break;
1577: case ClassWriter.LABELW_INSN:
1578: u += 5;
1579: break;
1580: case ClassWriter.TABL_INSN:
1581: if (state == 1) {
1582: // true number of bytes to be added (or removed)
1583: // from this instruction = (future number of padding
1584: // bytes - current number of padding byte) -
1585: // previously over estimated variation =
1586: // = ((3 - newOffset%4) - (3 - u%4)) - u%4
1587: // = (-newOffset%4 + u%4) - u%4
1588: // = -(newOffset & 3)
1589: newOffset = getNewOffset(allIndexes, allSizes,
1590: 0, u);
1591: insert = -(newOffset & 3);
1592: } else if (!resize[u]) {
1593: // over estimation of the number of bytes to be
1594: // added to this instruction = 3 - current number
1595: // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
1596: insert = u & 3;
1597: resize[u] = true;
1598: }
1599: // skips instruction
1600: u = u + 4 - (u & 3);
1601: u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
1602: break;
1603: case ClassWriter.LOOK_INSN:
1604: if (state == 1) {
1605: // like TABL_INSN
1606: newOffset = getNewOffset(allIndexes, allSizes,
1607: 0, u);
1608: insert = -(newOffset & 3);
1609: } else if (!resize[u]) {
1610: // like TABL_INSN
1611: insert = u & 3;
1612: resize[u] = true;
1613: }
1614: // skips instruction
1615: u = u + 4 - (u & 3);
1616: u += 8 * readInt(b, u + 4) + 8;
1617: break;
1618: case ClassWriter.WIDE_INSN:
1619: opcode = b[u + 1] & 0xFF;
1620: if (opcode == Opcodes.IINC) {
1621: u += 6;
1622: } else {
1623: u += 4;
1624: }
1625: break;
1626: case ClassWriter.VAR_INSN:
1627: case ClassWriter.SBYTE_INSN:
1628: case ClassWriter.LDC_INSN:
1629: u += 2;
1630: break;
1631: case ClassWriter.SHORT_INSN:
1632: case ClassWriter.LDCW_INSN:
1633: case ClassWriter.FIELDORMETH_INSN:
1634: case ClassWriter.TYPE_INSN:
1635: case ClassWriter.IINC_INSN:
1636: u += 3;
1637: break;
1638: case ClassWriter.ITFMETH_INSN:
1639: u += 5;
1640: break;
1641: // case ClassWriter.MANA_INSN:
1642: default:
1643: u += 4;
1644: break;
1645: }
1646: if (insert != 0) {
1647: // adds a new (u, insert) entry in the allIndexes and
1648: // allSizes arrays
1649: final int[] newIndexes = new int[allIndexes.length + 1];
1650: final int[] newSizes = new int[allSizes.length + 1];
1651: System.arraycopy(allIndexes, 0, newIndexes, 0,
1652: allIndexes.length);
1653: System.arraycopy(allSizes, 0, newSizes, 0,
1654: allSizes.length);
1655: newIndexes[allIndexes.length] = u;
1656: newSizes[allSizes.length] = insert;
1657: allIndexes = newIndexes;
1658: allSizes = newSizes;
1659: if (insert > 0) {
1660: state = 3;
1661: }
1662: }
1663: }
1664: if (state < 3) {
1665: --state;
1666: }
1667: } while (state != 0);
1668:
1669: // 2nd step:
1670: // copies the bytecode of the method into a new bytevector, updates the
1671: // offsets, and inserts (or removes) bytes as requested.
1672:
1673: final ByteVector newCode = new ByteVector(this .code.length);
1674:
1675: u = 0;
1676: while (u < this .code.length) {
1677: for (i = allIndexes.length - 1; i >= 0; --i) {
1678: if (allIndexes[i] == u) {
1679: if (i < len) {
1680: if (sizes[i] > 0) {
1681: newCode.putByteArray(null, 0, sizes[i]);
1682: } else {
1683: newCode.length += sizes[i];
1684: }
1685: indexes[i] = newCode.length;
1686: }
1687: }
1688: }
1689: int opcode = b[u] & 0xFF;
1690: switch (ClassWriter.TYPE[opcode]) {
1691: case ClassWriter.NOARG_INSN:
1692: case ClassWriter.IMPLVAR_INSN:
1693: newCode.putByte(opcode);
1694: u += 1;
1695: break;
1696: case ClassWriter.LABEL_INSN:
1697: if (opcode > 201) {
1698: // changes temporary opcodes 202 to 217 (inclusive), 218
1699: // and 219 to IFEQ ... JSR (inclusive), IFNULL and
1700: // IFNONNULL
1701: opcode = opcode < 218 ? opcode - 49 : opcode - 20;
1702: label = u + readUnsignedShort(b, u + 1);
1703: } else {
1704: label = u + readShort(b, u + 1);
1705: }
1706: newOffset = getNewOffset(allIndexes, allSizes, u, label);
1707: if (resize[u]) {
1708: // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
1709: // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
1710: // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
1711: // and where <l'> designates the instruction just after
1712: // the GOTO_W.
1713: if (opcode == Opcodes.GOTO) {
1714: newCode.putByte(200); // GOTO_W
1715: } else if (opcode == Opcodes.JSR) {
1716: newCode.putByte(201); // JSR_W
1717: } else {
1718: newCode
1719: .putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
1720: : opcode ^ 1);
1721: newCode.putShort(8); // jump offset
1722: newCode.putByte(200); // GOTO_W
1723: // newOffset now computed from start of GOTO_W
1724: newOffset -= 3;
1725: }
1726: newCode.putInt(newOffset);
1727: } else {
1728: newCode.putByte(opcode);
1729: newCode.putShort(newOffset);
1730: }
1731: u += 3;
1732: break;
1733: case ClassWriter.LABELW_INSN:
1734: label = u + readInt(b, u + 1);
1735: newOffset = getNewOffset(allIndexes, allSizes, u, label);
1736: newCode.putByte(opcode);
1737: newCode.putInt(newOffset);
1738: u += 5;
1739: break;
1740: case ClassWriter.TABL_INSN:
1741: // skips 0 to 3 padding bytes
1742: v = u;
1743: u = u + 4 - (v & 3);
1744: // reads and copies instruction
1745: newCode.putByte(Opcodes.TABLESWITCH);
1746: while (newCode.length % 4 != 0) {
1747: newCode.putByte(0);
1748: }
1749: label = v + readInt(b, u);
1750: u += 4;
1751: newOffset = getNewOffset(allIndexes, allSizes, v, label);
1752: newCode.putInt(newOffset);
1753: j = readInt(b, u);
1754: u += 4;
1755: newCode.putInt(j);
1756: j = readInt(b, u) - j + 1;
1757: u += 4;
1758: newCode.putInt(readInt(b, u - 4));
1759: for (; j > 0; --j) {
1760: label = v + readInt(b, u);
1761: u += 4;
1762: newOffset = getNewOffset(allIndexes, allSizes, v,
1763: label);
1764: newCode.putInt(newOffset);
1765: }
1766: break;
1767: case ClassWriter.LOOK_INSN:
1768: // skips 0 to 3 padding bytes
1769: v = u;
1770: u = u + 4 - (v & 3);
1771: // reads and copies instruction
1772: newCode.putByte(Opcodes.LOOKUPSWITCH);
1773: while (newCode.length % 4 != 0) {
1774: newCode.putByte(0);
1775: }
1776: label = v + readInt(b, u);
1777: u += 4;
1778: newOffset = getNewOffset(allIndexes, allSizes, v, label);
1779: newCode.putInt(newOffset);
1780: j = readInt(b, u);
1781: u += 4;
1782: newCode.putInt(j);
1783: for (; j > 0; --j) {
1784: newCode.putInt(readInt(b, u));
1785: u += 4;
1786: label = v + readInt(b, u);
1787: u += 4;
1788: newOffset = getNewOffset(allIndexes, allSizes, v,
1789: label);
1790: newCode.putInt(newOffset);
1791: }
1792: break;
1793: case ClassWriter.WIDE_INSN:
1794: opcode = b[u + 1] & 0xFF;
1795: if (opcode == Opcodes.IINC) {
1796: newCode.putByteArray(b, u, 6);
1797: u += 6;
1798: } else {
1799: newCode.putByteArray(b, u, 4);
1800: u += 4;
1801: }
1802: break;
1803: case ClassWriter.VAR_INSN:
1804: case ClassWriter.SBYTE_INSN:
1805: case ClassWriter.LDC_INSN:
1806: newCode.putByteArray(b, u, 2);
1807: u += 2;
1808: break;
1809: case ClassWriter.SHORT_INSN:
1810: case ClassWriter.LDCW_INSN:
1811: case ClassWriter.FIELDORMETH_INSN:
1812: case ClassWriter.TYPE_INSN:
1813: case ClassWriter.IINC_INSN:
1814: newCode.putByteArray(b, u, 3);
1815: u += 3;
1816: break;
1817: case ClassWriter.ITFMETH_INSN:
1818: newCode.putByteArray(b, u, 5);
1819: u += 5;
1820: break;
1821: // case MANA_INSN:
1822: default:
1823: newCode.putByteArray(b, u, 4);
1824: u += 4;
1825: break;
1826: }
1827: }
1828:
1829: // updates the exception handler block labels
1830: Handler h = this .catchTable;
1831: while (h != null) {
1832: getNewOffset(allIndexes, allSizes, h.start);
1833: getNewOffset(allIndexes, allSizes, h.end);
1834: getNewOffset(allIndexes, allSizes, h.handler);
1835: h = h.next;
1836: }
1837: for (i = 0; i < 2; ++i) {
1838: final ByteVector bv = i == 0 ? this .localVar
1839: : this .localVarType;
1840: if (bv != null) {
1841: b = bv.data;
1842: u = 0;
1843: while (u < bv.length) {
1844: label = readUnsignedShort(b, u);
1845: newOffset = getNewOffset(allIndexes, allSizes, 0,
1846: label);
1847: writeShort(b, u, newOffset);
1848: label += readUnsignedShort(b, u + 2);
1849: newOffset = getNewOffset(allIndexes, allSizes, 0,
1850: label)
1851: - newOffset;
1852: writeShort(b, u + 2, newOffset);
1853: u += 10;
1854: }
1855: }
1856: }
1857: if (this .lineNumber != null) {
1858: b = this .lineNumber.data;
1859: u = 0;
1860: while (u < this .lineNumber.length) {
1861: writeShort(b, u, getNewOffset(allIndexes, allSizes, 0,
1862: readUnsignedShort(b, u)));
1863: u += 4;
1864: }
1865: }
1866: // updates the labels of the other attributes
1867: while (this .cattrs != null) {
1868: final Label[] labels = this .cattrs.getLabels();
1869: if (labels != null) {
1870: for (i = labels.length - 1; i >= 0; --i) {
1871: if (!labels[i].resized) {
1872: labels[i].position = getNewOffset(allIndexes,
1873: allSizes, 0, labels[i].position);
1874: labels[i].resized = true;
1875: }
1876: }
1877: }
1878: }
1879:
1880: // replaces old bytecodes with new ones
1881: this .code = newCode;
1882:
1883: // returns the positions of the resized instructions
1884: return indexes;
1885: }
1886:
1887: /**
1888: * Reads an unsigned short 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 readUnsignedShort(final byte[] b, final int index) {
1895: return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
1896: }
1897:
1898: /**
1899: * Reads a signed short value in the given byte array.
1900: *
1901: * @param b a byte array.
1902: * @param index the start index of the value to be read.
1903: * @return the read value.
1904: */
1905: static short readShort(final byte[] b, final int index) {
1906: return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
1907: }
1908:
1909: /**
1910: * Reads a signed int value in the given byte array.
1911: *
1912: * @param b a byte array.
1913: * @param index the start index of the value to be read.
1914: * @return the read value.
1915: */
1916: static int readInt(final byte[] b, final int index) {
1917: return ((b[index] & 0xFF) << 24)
1918: | ((b[index + 1] & 0xFF) << 16)
1919: | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
1920: }
1921:
1922: /**
1923: * Writes a short value in the given byte array.
1924: *
1925: * @param b a byte array.
1926: * @param index where the first byte of the short value must be written.
1927: * @param s the value to be written in the given byte array.
1928: */
1929: static void writeShort(final byte[] b, final int index, final int s) {
1930: b[index] = (byte) (s >>> 8);
1931: b[index + 1] = (byte) s;
1932: }
1933:
1934: /**
1935: * Computes the future value of a bytecode offset. <p> Note: it is possible
1936: * to have several entries for the same instruction in the <tt>indexes</tt>
1937: * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')
1938: * are equivalent to a single entry (index=a,size=b+b').
1939: *
1940: * @param indexes current positions of the instructions to be resized. Each
1941: * instruction must be designated by the index of its <i>last</i>
1942: * byte, plus one (or, in other words, by the index of the <i>first</i>
1943: * byte of the <i>next</i> instruction).
1944: * @param sizes the number of bytes to be <i>added</i> to the above
1945: * instructions. More precisely, for each i < <tt>len</tt>,
1946: * <tt>sizes</tt>[i] bytes will be added at the end of the
1947: * instruction designated by <tt>indexes</tt>[i] or, if
1948: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1949: * bytes of the instruction will be removed (the instruction size
1950: * <i>must not</i> become negative or null).
1951: * @param begin index of the first byte of the source instruction.
1952: * @param end index of the first byte of the target instruction.
1953: * @return the future value of the given bytecode offset.
1954: */
1955: static int getNewOffset(final int[] indexes, final int[] sizes,
1956: final int begin, final int end) {
1957: int offset = end - begin;
1958: for (int i = 0; i < indexes.length; ++i) {
1959: if (begin < indexes[i] && indexes[i] <= end) {
1960: // forward jump
1961: offset += sizes[i];
1962: } else if (end < indexes[i] && indexes[i] <= begin) {
1963: // backward jump
1964: offset -= sizes[i];
1965: }
1966: }
1967: return offset;
1968: }
1969:
1970: /**
1971: * Updates the offset of the given label.
1972: *
1973: * @param indexes current positions of the instructions to be resized. Each
1974: * instruction must be designated by the index of its <i>last</i>
1975: * byte, plus one (or, in other words, by the index of the <i>first</i>
1976: * byte of the <i>next</i> instruction).
1977: * @param sizes the number of bytes to be <i>added</i> to the above
1978: * instructions. More precisely, for each i < <tt>len</tt>,
1979: * <tt>sizes</tt>[i] bytes will be added at the end of the
1980: * instruction designated by <tt>indexes</tt>[i] or, if
1981: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
1982: * bytes of the instruction will be removed (the instruction size
1983: * <i>must not</i> become negative or null).
1984: * @param label the label whose offset must be updated.
1985: */
1986: static void getNewOffset(final int[] indexes, final int[] sizes,
1987: final Label label) {
1988: if (!label.resized) {
1989: label.position = getNewOffset(indexes, sizes, 0,
1990: label.position);
1991: label.resized = true;
1992: }
1993: }
1994: }
|