0001: /***
0002: * ASM: a very small and fast Java bytecode manipulation framework
0003: * Copyright (c) 2000-2005 INRIA, France Telecom
0004: * All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: * 1. Redistributions of source code must retain the above copyright
0010: * notice, this list of conditions and the following disclaimer.
0011: * 2. Redistributions in binary form must reproduce the above copyright
0012: * notice, this list of conditions and the following disclaimer in the
0013: * documentation and/or other materials provided with the distribution.
0014: * 3. Neither the name of the copyright holders nor the names of its
0015: * contributors may be used to endorse or promote products derived from
0016: * this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
0028: * THE POSSIBILITY OF SUCH DAMAGE.
0029: */package org.objectweb.asm;
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: * Pseudo access flag used to denote constructors.
0043: */
0044: final static int ACC_CONSTRUCTOR = 262144;
0045:
0046: /**
0047: * Frame has exactly the same locals as the previous stack map frame and
0048: * number of stack items is zero.
0049: */
0050: final static int SAME_FRAME = 0; // to 63 (0-3f)
0051:
0052: /**
0053: * Frame has exactly the same locals as the previous stack map frame and
0054: * number of stack items is 1
0055: */
0056: final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
0057:
0058: /**
0059: * Reserved for future use
0060: */
0061: final static int RESERVED = 128;
0062:
0063: /**
0064: * Frame has exactly the same locals as the previous stack map frame and
0065: * number of stack items is 1. Offset is bigger then 63;
0066: */
0067: final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
0068:
0069: /**
0070: * Frame where current locals are the same as the locals in the previous
0071: * frame, except that the k last locals are absent. The value of k is given
0072: * by the formula 251-frame_type.
0073: */
0074: final static int CHOP_FRAME = 248; // to 250 (f8-fA)
0075:
0076: /**
0077: * Frame has exactly the same locals as the previous stack map frame and
0078: * number of stack items is zero. Offset is bigger then 63;
0079: */
0080: final static int SAME_FRAME_EXTENDED = 251; // fb
0081:
0082: /**
0083: * Frame where current locals are the same as the locals in the previous
0084: * frame, except that k additional locals are defined. The value of k is
0085: * given by the formula frame_type-251.
0086: */
0087: final static int APPEND_FRAME = 252; // to 254 // fc-fe
0088:
0089: /**
0090: * Full frame
0091: */
0092: final static int FULL_FRAME = 255; // ff
0093:
0094: /**
0095: * Indicates that the stack map frames must be recomputed from scratch. In
0096: * this case the maximum stack size and number of local variables is also
0097: * recomputed from scratch.
0098: *
0099: * @see #compute
0100: */
0101: private final static int FRAMES = 0;
0102:
0103: /**
0104: * Indicates that the maximum stack size and number of local variables must
0105: * be automatically computed.
0106: *
0107: * @see #compute
0108: */
0109: private final static int MAXS = 1;
0110:
0111: /**
0112: * Indicates that nothing must be automatically computed.
0113: *
0114: * @see #compute
0115: */
0116: private final static int NOTHING = 2;
0117:
0118: /**
0119: * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
0120: */
0121: MethodWriter next;
0122:
0123: /**
0124: * The class writer to which this method must be added.
0125: */
0126: ClassWriter cw;
0127:
0128: /**
0129: * Access flags of this method.
0130: */
0131: private int access;
0132:
0133: /**
0134: * The index of the constant pool item that contains the name of this
0135: * method.
0136: */
0137: private int name;
0138:
0139: /**
0140: * The index of the constant pool item that contains the descriptor of this
0141: * method.
0142: */
0143: private int desc;
0144:
0145: /**
0146: * The descriptor of this method.
0147: */
0148: private String descriptor;
0149:
0150: /**
0151: * The signature of this method.
0152: */
0153: String signature;
0154:
0155: /**
0156: * If not zero, indicates that the code of this method must be copied from
0157: * the ClassReader associated to this writer in <code>cw.cr</code>. More
0158: * precisely, this field gives the index of the first byte to copied from
0159: * <code>cw.cr.b</code>.
0160: */
0161: int classReaderOffset;
0162:
0163: /**
0164: * If not zero, indicates that the code of this method must be copied from
0165: * the ClassReader associated to this writer in <code>cw.cr</code>. More
0166: * precisely, this field gives the number of bytes to copied from
0167: * <code>cw.cr.b</code>.
0168: */
0169: int classReaderLength;
0170:
0171: /**
0172: * Number of exceptions that can be thrown by this method.
0173: */
0174: int exceptionCount;
0175:
0176: /**
0177: * The exceptions that can be thrown by this method. More precisely, this
0178: * array contains the indexes of the constant pool items that contain the
0179: * internal names of these exception classes.
0180: */
0181: int[] exceptions;
0182:
0183: /**
0184: * The annotation default attribute of this method. May be <tt>null</tt>.
0185: */
0186: private ByteVector annd;
0187:
0188: /**
0189: * The runtime visible annotations of this method. May be <tt>null</tt>.
0190: */
0191: private AnnotationWriter anns;
0192:
0193: /**
0194: * The runtime invisible annotations of this method. May be <tt>null</tt>.
0195: */
0196: private AnnotationWriter ianns;
0197:
0198: /**
0199: * The runtime visible parameter annotations of this method. May be
0200: * <tt>null</tt>.
0201: */
0202: private AnnotationWriter[] panns;
0203:
0204: /**
0205: * The runtime invisible parameter annotations of this method. May be
0206: * <tt>null</tt>.
0207: */
0208: private AnnotationWriter[] ipanns;
0209:
0210: /**
0211: * The non standard attributes of the method.
0212: */
0213: private Attribute attrs;
0214:
0215: /**
0216: * The bytecode of this method.
0217: */
0218: private ByteVector code = new ByteVector();
0219:
0220: /**
0221: * Maximum stack size of this method.
0222: */
0223: private int maxStack;
0224:
0225: /**
0226: * Maximum number of local variables for this method.
0227: */
0228: private int maxLocals;
0229:
0230: /**
0231: * Number of stack map frames in the StackMapTable attribute.
0232: */
0233: private int frameCount;
0234:
0235: /**
0236: * The StackMapTable attribute.
0237: */
0238: private ByteVector stackMap;
0239:
0240: /**
0241: * The offset of the last frame that was written in the StackMapTable
0242: * attribute.
0243: */
0244: private int previousFrameOffset;
0245:
0246: /**
0247: * The last frame that was written in the StackMapTable attribute.
0248: *
0249: * @see #frame
0250: */
0251: private int[] previousFrame;
0252:
0253: /**
0254: * Index of the next element to be added in {@link #frame}.
0255: */
0256: private int frameIndex;
0257:
0258: /**
0259: * The current stack map frame. The first element contains the offset of the
0260: * instruction to which the frame corresponds, the second element is the
0261: * number of locals and the third one is the number of stack elements. The
0262: * local variables start at index 3 and are followed by the operand stack
0263: * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
0264: * nStack, frame[3] = nLocal. All types are encoded as integers, with the
0265: * same format as the one used in {@link Label}, but limited to BASE types.
0266: */
0267: private int[] frame;
0268:
0269: /**
0270: * Number of elements in the exception handler list.
0271: */
0272: private int handlerCount;
0273:
0274: /**
0275: * The first element in the exception handler list.
0276: */
0277: private Handler firstHandler;
0278:
0279: /**
0280: * The last element in the exception handler list.
0281: */
0282: private Handler lastHandler;
0283:
0284: /**
0285: * Number of entries in the LocalVariableTable attribute.
0286: */
0287: private int localVarCount;
0288:
0289: /**
0290: * The LocalVariableTable attribute.
0291: */
0292: private ByteVector localVar;
0293:
0294: /**
0295: * Number of entries in the LocalVariableTypeTable attribute.
0296: */
0297: private int localVarTypeCount;
0298:
0299: /**
0300: * The LocalVariableTypeTable attribute.
0301: */
0302: private ByteVector localVarType;
0303:
0304: /**
0305: * Number of entries in the LineNumberTable attribute.
0306: */
0307: private int lineNumberCount;
0308:
0309: /**
0310: * The LineNumberTable attribute.
0311: */
0312: private ByteVector lineNumber;
0313:
0314: /**
0315: * The non standard attributes of the method's code.
0316: */
0317: private Attribute cattrs;
0318:
0319: /**
0320: * Indicates if some jump instructions are too small and need to be resized.
0321: */
0322: private boolean resize;
0323:
0324: // ------------------------------------------------------------------------
0325:
0326: /*
0327: * Fields for the control flow graph analysis algorithm (used to compute the
0328: * maximum stack size). A control flow graph contains one node per "basic
0329: * block", and one edge per "jump" from one basic block to another. Each
0330: * node (i.e., each basic block) is represented by the Label object that
0331: * corresponds to the first instruction of this basic block. Each node also
0332: * stores the list of its successors in the graph, as a linked list of Edge
0333: * objects.
0334: */
0335:
0336: /**
0337: * Indicates what must be automatically computed.
0338: *
0339: * @see FRAMES
0340: * @see MAXS
0341: * @see NOTHING
0342: */
0343: private int compute;
0344:
0345: /**
0346: * A list of labels. If {@link #FRAMES} option is used, this list is the
0347: * list of basic blocks in the method, i.e. a list of Label objects linked
0348: * to each other by their {@link Label#successor} field, and starting with
0349: * the first basic block. If {@link #MAXS} option is used, this list is the
0350: * basic block stack used by the control flow analysis algorithm, i.e. a
0351: * list of Label objects linked to each other by their {@link Label#next}
0352: * field.
0353: */
0354: private Label labels;
0355:
0356: /**
0357: * The previous basic block.
0358: */
0359: private Label previousBlock;
0360:
0361: /**
0362: * The current basic block.
0363: */
0364: private Label currentBlock;
0365:
0366: /**
0367: * The (relative) stack size after the last visited instruction. This size
0368: * is relative to the beginning of the current basic block, i.e., the true
0369: * stack size after the last visited instruction is equal to the
0370: * {@link Label#inputStackTop beginStackSize} of the current basic block
0371: * plus <tt>stackSize</tt>.
0372: */
0373: private int stackSize;
0374:
0375: /**
0376: * The (relative) maximum stack size after the last visited instruction.
0377: * This size is relative to the beginning of the current basic block, i.e.,
0378: * the true maximum stack size after the last visited instruction is equal
0379: * to the {@link Label#inputStackTop beginStackSize} of the current basic
0380: * block plus <tt>stackSize</tt>.
0381: */
0382: private int maxStackSize;
0383:
0384: // ------------------------------------------------------------------------
0385: // Constructor
0386: // ------------------------------------------------------------------------
0387:
0388: /**
0389: * Constructs a new {@link MethodWriter}.
0390: *
0391: * @param cw the class writer in which the method must be added.
0392: * @param access the method's access flags (see {@link Opcodes}).
0393: * @param name the method's name.
0394: * @param desc the method's descriptor (see {@link Type}).
0395: * @param signature the method's signature. May be <tt>null</tt>.
0396: * @param exceptions the internal names of the method's exceptions. May be
0397: * <tt>null</tt>.
0398: * @param computeMaxs <tt>true</tt> if the maximum stack size and number
0399: * of local variables must be automatically computed.
0400: * @param computeFrames <tt>true</tt> if the stack map tables must be
0401: * recomputed from scratch.
0402: */
0403: MethodWriter(final ClassWriter cw, final int access,
0404: final String name, final String desc,
0405: final String signature, final String[] exceptions,
0406: final boolean computeMaxs, final boolean computeFrames) {
0407: if (cw.firstMethod == null) {
0408: cw.firstMethod = this ;
0409: } else {
0410: cw.lastMethod.next = this ;
0411: }
0412: cw.lastMethod = this ;
0413: this .cw = cw;
0414: this .access = access;
0415: this .name = cw.newUTF8(name);
0416: this .desc = cw.newUTF8(desc);
0417: this .descriptor = desc;
0418: this .signature = signature;
0419: if (exceptions != null && exceptions.length > 0) {
0420: exceptionCount = exceptions.length;
0421: this .exceptions = new int[exceptionCount];
0422: for (int i = 0; i < exceptionCount; ++i) {
0423: this .exceptions[i] = cw.newClass(exceptions[i]);
0424: }
0425: }
0426: this .compute = computeFrames ? FRAMES : (computeMaxs ? MAXS
0427: : NOTHING);
0428: if (computeMaxs || computeFrames) {
0429: if (computeFrames && name.equals("<init>")) {
0430: this .access |= ACC_CONSTRUCTOR;
0431: }
0432: // updates maxLocals
0433: int size = getArgumentsAndReturnSizes(descriptor) >> 2;
0434: if ((access & Opcodes.ACC_STATIC) != 0) {
0435: --size;
0436: }
0437: maxLocals = size;
0438: // creates and visits the label for the first basic block
0439: labels = new Label();
0440: labels.status |= Label.PUSHED;
0441: visitLabel(labels);
0442: }
0443: }
0444:
0445: // ------------------------------------------------------------------------
0446: // Implementation of the MethodVisitor interface
0447: // ------------------------------------------------------------------------
0448:
0449: public AnnotationVisitor visitAnnotationDefault() {
0450: annd = new ByteVector();
0451: return new AnnotationWriter(cw, false, annd, null, 0);
0452: }
0453:
0454: public AnnotationVisitor visitAnnotation(final String desc,
0455: final boolean visible) {
0456: ByteVector bv = new ByteVector();
0457: // write type, and reserve space for values count
0458: bv.putShort(cw.newUTF8(desc)).putShort(0);
0459: AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
0460: if (visible) {
0461: aw.next = anns;
0462: anns = aw;
0463: } else {
0464: aw.next = ianns;
0465: ianns = aw;
0466: }
0467: return aw;
0468: }
0469:
0470: public AnnotationVisitor visitParameterAnnotation(
0471: final int parameter, final String desc,
0472: final boolean visible) {
0473: ByteVector bv = new ByteVector();
0474: // write type, and reserve space for values count
0475: bv.putShort(cw.newUTF8(desc)).putShort(0);
0476: AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
0477: if (visible) {
0478: if (panns == null) {
0479: panns = new AnnotationWriter[Type
0480: .getArgumentTypes(descriptor).length];
0481: }
0482: aw.next = panns[parameter];
0483: panns[parameter] = aw;
0484: } else {
0485: if (ipanns == null) {
0486: ipanns = new AnnotationWriter[Type
0487: .getArgumentTypes(descriptor).length];
0488: }
0489: aw.next = ipanns[parameter];
0490: ipanns[parameter] = aw;
0491: }
0492: return aw;
0493: }
0494:
0495: public void visitAttribute(final Attribute attr) {
0496: if (attr.isCodeAttribute()) {
0497: attr.next = cattrs;
0498: cattrs = attr;
0499: } else {
0500: attr.next = attrs;
0501: attrs = attr;
0502: }
0503: }
0504:
0505: public void visitCode() {
0506: }
0507:
0508: public void visitFrame(final int type, final int nLocal,
0509: final Object[] local, final int nStack, final Object[] stack) {
0510: if (compute == FRAMES) {
0511: return;
0512: }
0513:
0514: if (type == Opcodes.F_NEW) {
0515: startFrame(code.length, nLocal, nStack);
0516: for (int i = 0; i < nLocal; ++i) {
0517: if (local[i] instanceof String) {
0518: frame[frameIndex++] = Frame.OBJECT
0519: | cw.addType((String) local[i]);
0520: } else if (local[i] instanceof Integer) {
0521: frame[frameIndex++] = ((Integer) local[i])
0522: .intValue();
0523: } else {
0524: frame[frameIndex++] = Frame.UNINITIALIZED
0525: | cw.addUninitializedType("",
0526: ((Label) local[i]).position);
0527: }
0528: }
0529: for (int i = 0; i < nStack; ++i) {
0530: if (stack[i] instanceof String) {
0531: frame[frameIndex++] = Frame.OBJECT
0532: | cw.addType((String) stack[i]);
0533: } else if (stack[i] instanceof Integer) {
0534: frame[frameIndex++] = ((Integer) stack[i])
0535: .intValue();
0536: } else {
0537: frame[frameIndex++] = Frame.UNINITIALIZED
0538: | cw.addUninitializedType("",
0539: ((Label) stack[i]).position);
0540: }
0541: }
0542: endFrame();
0543: } else {
0544: int delta;
0545: if (stackMap == null) {
0546: stackMap = new ByteVector();
0547: delta = code.length;
0548: } else {
0549: delta = code.length - previousFrameOffset - 1;
0550: }
0551:
0552: switch (type) {
0553: case Opcodes.F_FULL:
0554: stackMap.putByte(FULL_FRAME).putShort(delta).putShort(
0555: nLocal);
0556: for (int i = 0; i < nLocal; ++i) {
0557: writeFrameType(local[i]);
0558: }
0559: stackMap.putShort(nStack);
0560: for (int i = 0; i < nStack; ++i) {
0561: writeFrameType(stack[i]);
0562: }
0563: break;
0564: case Opcodes.F_APPEND:
0565: stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)
0566: .putShort(delta);
0567: for (int i = 0; i < nLocal; ++i) {
0568: writeFrameType(local[i]);
0569: }
0570: break;
0571: case Opcodes.F_CHOP:
0572: stackMap.putByte(SAME_FRAME_EXTENDED - nLocal)
0573: .putShort(delta);
0574: break;
0575: case Opcodes.F_SAME:
0576: if (delta < 64) {
0577: stackMap.putByte(delta);
0578: } else {
0579: stackMap.putByte(SAME_FRAME_EXTENDED).putShort(
0580: delta);
0581: }
0582: break;
0583: case Opcodes.F_SAME1:
0584: if (delta < 64) {
0585: stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME
0586: + delta);
0587: } else {
0588: stackMap.putByte(
0589: SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
0590: .putShort(delta);
0591: }
0592: writeFrameType(stack[0]);
0593: break;
0594: }
0595:
0596: previousFrameOffset = code.length;
0597: ++frameCount;
0598: }
0599: }
0600:
0601: public void visitInsn(final int opcode) {
0602: // adds the instruction to the bytecode of the method
0603: code.putByte(opcode);
0604: // update currentBlock
0605: // Label currentBlock = this.currentBlock;
0606: if (currentBlock != null) {
0607: if (compute == FRAMES) {
0608: currentBlock.frame.execute(opcode, 0, null, null);
0609: } else {
0610: // updates current and max stack sizes
0611: int size = stackSize + Frame.SIZE[opcode];
0612: if (size > maxStackSize) {
0613: maxStackSize = size;
0614: }
0615: stackSize = size;
0616: }
0617: // if opcode == ATHROW or xRETURN, ends current block (no successor)
0618: if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
0619: || opcode == Opcodes.ATHROW) {
0620: noSuccessor();
0621: }
0622: }
0623: }
0624:
0625: public void visitIntInsn(final int opcode, final int operand) {
0626: // Label currentBlock = this.currentBlock;
0627: if (currentBlock != null) {
0628: if (compute == FRAMES) {
0629: currentBlock.frame.execute(opcode, operand, null, null);
0630: } else if (opcode != Opcodes.NEWARRAY) {
0631: // updates current and max stack sizes only for NEWARRAY
0632: // (stack size variation = 0 for BIPUSH or SIPUSH)
0633: int size = stackSize + 1;
0634: if (size > maxStackSize) {
0635: maxStackSize = size;
0636: }
0637: stackSize = size;
0638: }
0639: }
0640: // adds the instruction to the bytecode of the method
0641: if (opcode == Opcodes.SIPUSH) {
0642: code.put12(opcode, operand);
0643: } else { // BIPUSH or NEWARRAY
0644: code.put11(opcode, operand);
0645: }
0646: }
0647:
0648: public void visitVarInsn(final int opcode, final int var) {
0649: // Label currentBlock = this.currentBlock;
0650: if (currentBlock != null) {
0651: if (compute == FRAMES) {
0652: currentBlock.frame.execute(opcode, var, null, null);
0653: } else {
0654: // updates current and max stack sizes
0655: if (opcode == Opcodes.RET) {
0656: // no stack change, but end of current block (no successor)
0657: noSuccessor();
0658: } else { // xLOAD or xSTORE
0659: int size = stackSize + Frame.SIZE[opcode];
0660: if (size > maxStackSize) {
0661: maxStackSize = size;
0662: }
0663: stackSize = size;
0664: }
0665: }
0666: // updates max locals
0667: int n;
0668: if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
0669: || opcode == Opcodes.LSTORE
0670: || opcode == Opcodes.DSTORE) {
0671: n = var + 2;
0672: } else {
0673: n = var + 1;
0674: }
0675: if (n > maxLocals) {
0676: maxLocals = n;
0677: }
0678: }
0679: // adds the instruction to the bytecode of the method
0680: if (var < 4 && opcode != Opcodes.RET) {
0681: int opt;
0682: if (opcode < Opcodes.ISTORE) {
0683: /* ILOAD_0 */
0684: opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
0685: } else {
0686: /* ISTORE_0 */
0687: opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
0688: }
0689: code.putByte(opt);
0690: } else if (var >= 256) {
0691: code.putByte(196 /* WIDE */).put12(opcode, var);
0692: } else {
0693: code.put11(opcode, var);
0694: }
0695: if (opcode >= Opcodes.ISTORE && compute == FRAMES
0696: && handlerCount > 0) {
0697: visitLabel(new Label());
0698: }
0699: }
0700:
0701: public void visitTypeInsn(final int opcode, final String desc) {
0702: Item i = cw.newClassItem(desc);
0703: // Label currentBlock = this.currentBlock;
0704: if (currentBlock != null) {
0705: if (compute == FRAMES) {
0706: currentBlock.frame.execute(opcode, code.length, cw, i);
0707: } else if (opcode == Opcodes.NEW) {
0708: // updates current and max stack sizes only if opcode == NEW
0709: // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)
0710: int size = stackSize + 1;
0711: if (size > maxStackSize) {
0712: maxStackSize = size;
0713: }
0714: stackSize = size;
0715: }
0716: }
0717: // adds the instruction to the bytecode of the method
0718: code.put12(opcode, i.index);
0719: }
0720:
0721: public void visitFieldInsn(final int opcode, final String owner,
0722: final String name, final String desc) {
0723: Item i = cw.newFieldItem(owner, name, desc);
0724: // Label currentBlock = this.currentBlock;
0725: if (currentBlock != null) {
0726: if (compute == FRAMES) {
0727: currentBlock.frame.execute(opcode, 0, cw, i);
0728: } else {
0729: int size;
0730: // computes the stack size variation
0731: char c = desc.charAt(0);
0732: switch (opcode) {
0733: case Opcodes.GETSTATIC:
0734: size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
0735: break;
0736: case Opcodes.PUTSTATIC:
0737: size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
0738: break;
0739: case Opcodes.GETFIELD:
0740: size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
0741: break;
0742: // case Constants.PUTFIELD:
0743: default:
0744: size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
0745: break;
0746: }
0747: // updates current and max stack sizes
0748: if (size > maxStackSize) {
0749: maxStackSize = size;
0750: }
0751: stackSize = size;
0752: }
0753: }
0754: // adds the instruction to the bytecode of the method
0755: code.put12(opcode, i.index);
0756: }
0757:
0758: public void visitMethodInsn(final int opcode, final String owner,
0759: final String name, final String desc) {
0760: boolean itf = opcode == Opcodes.INVOKEINTERFACE;
0761: Item i = cw.newMethodItem(owner, name, desc, itf);
0762: int argSize = i.intVal;
0763: // Label currentBlock = this.currentBlock;
0764: if (currentBlock != null) {
0765: if (compute == FRAMES) {
0766: currentBlock.frame.execute(opcode, 0, cw, i);
0767: } else {
0768: /*
0769: * computes the stack size variation. In order not to recompute
0770: * several times this variation for the same Item, we use the
0771: * intVal field of this item to store this variation, once it
0772: * has been computed. More precisely this intVal field stores
0773: * the sizes of the arguments and of the return value
0774: * corresponding to desc.
0775: */
0776: if (argSize == 0) {
0777: // the above sizes have not been computed yet,
0778: // so we compute them...
0779: argSize = getArgumentsAndReturnSizes(desc);
0780: // ... and we save them in order
0781: // not to recompute them in the future
0782: i.intVal = argSize;
0783: }
0784: int size;
0785: if (opcode == Opcodes.INVOKESTATIC) {
0786: size = stackSize - (argSize >> 2)
0787: + (argSize & 0x03) + 1;
0788: } else {
0789: size = stackSize - (argSize >> 2)
0790: + (argSize & 0x03);
0791: }
0792: // updates current and max stack sizes
0793: if (size > maxStackSize) {
0794: maxStackSize = size;
0795: }
0796: stackSize = size;
0797: }
0798: }
0799: // adds the instruction to the bytecode of the method
0800: if (itf) {
0801: if (argSize == 0) {
0802: argSize = getArgumentsAndReturnSizes(desc);
0803: i.intVal = argSize;
0804: }
0805: code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(
0806: argSize >> 2, 0);
0807: } else {
0808: code.put12(opcode, i.index);
0809: }
0810: }
0811:
0812: public void visitJumpInsn(final int opcode, final Label label) {
0813: Label nextInsn = null;
0814: // Label currentBlock = this.currentBlock;
0815: if (currentBlock != null) {
0816: if (compute == FRAMES) {
0817: currentBlock.frame.execute(opcode, 0, null, null);
0818: // 'label' is the target of a jump instruction
0819: label.getFirst().status |= Label.TARGET;
0820: // adds 'label' as a successor of this basic block
0821: addSuccessor(opcode == Opcodes.JSR ? Edge.JSR
0822: : Edge.NORMAL, label);
0823: if (opcode != Opcodes.GOTO) {
0824: // creates a Label for the next basic block
0825: nextInsn = new Label();
0826: }
0827: } else {
0828: if (opcode == Opcodes.JSR) {
0829: addSuccessor(stackSize + 1, label);
0830: } else {
0831: // updates current stack size (max stack size unchanged
0832: // because stack size variation always negative in this
0833: // case)
0834: stackSize += Frame.SIZE[opcode];
0835: addSuccessor(stackSize, label);
0836: }
0837: if (opcode == Opcodes.GOTO) {
0838: noSuccessor();
0839: }
0840: }
0841: }
0842: // adds the instruction to the bytecode of the method
0843: if ((label.status & Label.RESOLVED) != 0
0844: && label.position - code.length < Short.MIN_VALUE) {
0845: /*
0846: * case of a backward jump with an offset < -32768. In this case we
0847: * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
0848: * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
0849: * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
0850: * designates the instruction just after the GOTO_W.
0851: */
0852: if (opcode == Opcodes.GOTO) {
0853: code.putByte(200); // GOTO_W
0854: } else if (opcode == Opcodes.JSR) {
0855: code.putByte(201); // JSR_W
0856: } else {
0857: // if the IF instruction is transformed into IFNOT GOTO_W the
0858: // next instruction becomes the target of the IFNOT instruction
0859: if (nextInsn != null) {
0860: nextInsn.status |= Label.TARGET;
0861: }
0862: code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
0863: : opcode ^ 1);
0864: code.putShort(8); // jump offset
0865: code.putByte(200); // GOTO_W
0866: }
0867: label.put(this , code, code.length - 1, true);
0868: } else {
0869: /*
0870: * case of a backward jump with an offset >= -32768, or of a forward
0871: * jump with, of course, an unknown offset. In these cases we store
0872: * the offset in 2 bytes (which will be increased in
0873: * resizeInstructions, if needed).
0874: */
0875: code.putByte(opcode);
0876: label.put(this , code, code.length - 1, false);
0877: }
0878: if (currentBlock != null && compute == FRAMES) {
0879: // if the jump instruction is not a GOTO, the next instruction is
0880: // also a successor of this instruction. Calling visitLabel adds the
0881: // label of this next instruction as a successor of the current
0882: // block, and starts a new basic block
0883: if (nextInsn != null) {
0884: visitLabel(nextInsn);
0885: } else {
0886: noSuccessor();
0887: }
0888: }
0889: }
0890:
0891: public void visitLabel(final Label label) {
0892: // resolves previous forward references to label, if any
0893: resize |= label.resolve(this , code.length, code.data);
0894: // updates currentBlock
0895: if ((label.status & Label.DEBUG) != 0) {
0896: return;
0897: }
0898: if (compute == FRAMES) {
0899: if (currentBlock != null) {
0900: if (label.position == currentBlock.position) {
0901: // successive labels, do not start a new basic block
0902: currentBlock.status |= (label.status & Label.TARGET);
0903: label.frame = currentBlock.frame;
0904: return;
0905: }
0906: // ends current block (with one new successor)
0907: addSuccessor(Edge.NORMAL, label);
0908: }
0909: // begins a new current block
0910: currentBlock = label;
0911: if (label.frame == null) {
0912: label.frame = new Frame();
0913: label.frame.owner = label;
0914: }
0915: // updates the basic block list
0916: if (previousBlock != null) {
0917: if (label.position == previousBlock.position) {
0918: previousBlock.status |= (label.status & Label.TARGET);
0919: label.frame = previousBlock.frame;
0920: currentBlock = previousBlock;
0921: return;
0922: }
0923: previousBlock.successor = label;
0924: }
0925: previousBlock = label;
0926: } else if (compute == MAXS) {
0927: if (currentBlock != null) {
0928: // ends current block (with one new successor)
0929: currentBlock.outputStackMax = maxStackSize;
0930: addSuccessor(stackSize, label);
0931: }
0932: // begins a new current block
0933: currentBlock = label;
0934: // resets the relative current and max stack sizes
0935: stackSize = 0;
0936: maxStackSize = 0;
0937: }
0938: }
0939:
0940: public void visitLdcInsn(final Object cst) {
0941: Item i = cw.newConstItem(cst);
0942: // Label currentBlock = this.currentBlock;
0943: if (currentBlock != null) {
0944: if (compute == FRAMES) {
0945: currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
0946: } else {
0947: int size;
0948: // computes the stack size variation
0949: if (i.type == ClassWriter.LONG
0950: || i.type == ClassWriter.DOUBLE) {
0951: size = stackSize + 2;
0952: } else {
0953: size = stackSize + 1;
0954: }
0955: // updates current and max stack sizes
0956: if (size > maxStackSize) {
0957: maxStackSize = size;
0958: }
0959: stackSize = size;
0960: }
0961: }
0962: // adds the instruction to the bytecode of the method
0963: int index = i.index;
0964: if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
0965: code.put12(20 /* LDC2_W */, index);
0966: } else if (index >= 256) {
0967: code.put12(19 /* LDC_W */, index);
0968: } else {
0969: code.put11(Opcodes.LDC, index);
0970: }
0971: }
0972:
0973: public void visitIincInsn(final int var, final int increment) {
0974: if (currentBlock != null) {
0975: if (compute == FRAMES) {
0976: currentBlock.frame.execute(Opcodes.IINC, var, null,
0977: null);
0978: }
0979: // updates max locals
0980: int n = var + 1;
0981: if (n > maxLocals) {
0982: maxLocals = n;
0983: }
0984: }
0985: // adds the instruction to the bytecode of the method
0986: if ((var > 255) || (increment > 127) || (increment < -128)) {
0987: code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var)
0988: .putShort(increment);
0989: } else {
0990: code.putByte(Opcodes.IINC).put11(var, increment);
0991: }
0992: }
0993:
0994: public void visitTableSwitchInsn(final int min, final int max,
0995: final Label dflt, final Label labels[]) {
0996: // adds the instruction to the bytecode of the method
0997: int source = code.length;
0998: code.putByte(Opcodes.TABLESWITCH);
0999: code.length += (4 - code.length % 4) % 4;
1000: dflt.put(this , code, source, true);
1001: code.putInt(min).putInt(max);
1002: for (int i = 0; i < labels.length; ++i) {
1003: labels[i].put(this , code, source, true);
1004: }
1005: // updates currentBlock
1006: visitSwitchInsn(dflt, labels);
1007: }
1008:
1009: public void visitLookupSwitchInsn(final Label dflt,
1010: final int keys[], final Label labels[]) {
1011: // adds the instruction to the bytecode of the method
1012: int source = code.length;
1013: code.putByte(Opcodes.LOOKUPSWITCH);
1014: code.length += (4 - code.length % 4) % 4;
1015: dflt.put(this , code, source, true);
1016: code.putInt(labels.length);
1017: for (int i = 0; i < labels.length; ++i) {
1018: code.putInt(keys[i]);
1019: labels[i].put(this , code, source, true);
1020: }
1021: // updates currentBlock
1022: visitSwitchInsn(dflt, labels);
1023: }
1024:
1025: private void visitSwitchInsn(final Label dflt, final Label[] labels) {
1026: // Label currentBlock = this.currentBlock;
1027: if (currentBlock != null) {
1028: if (compute == FRAMES) {
1029: currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0,
1030: null, null);
1031: // adds current block successors
1032: addSuccessor(Edge.NORMAL, dflt);
1033: dflt.getFirst().status |= Label.TARGET;
1034: for (int i = 0; i < labels.length; ++i) {
1035: addSuccessor(Edge.NORMAL, labels[i]);
1036: labels[i].getFirst().status |= Label.TARGET;
1037: }
1038: } else {
1039: // updates current stack size (max stack size unchanged)
1040: --stackSize;
1041: // adds current block successors
1042: addSuccessor(stackSize, dflt);
1043: for (int i = 0; i < labels.length; ++i) {
1044: addSuccessor(stackSize, labels[i]);
1045: }
1046: }
1047: // ends current block
1048: noSuccessor();
1049: }
1050: }
1051:
1052: public void visitMultiANewArrayInsn(final String desc,
1053: final int dims) {
1054: Item i = cw.newClassItem(desc);
1055: // Label currentBlock = this.currentBlock;
1056: if (currentBlock != null) {
1057: if (compute == FRAMES) {
1058: currentBlock.frame.execute(Opcodes.MULTIANEWARRAY,
1059: dims, cw, i);
1060: } else {
1061: // updates current stack size (max stack size unchanged because
1062: // stack size variation always negative or null)
1063: stackSize += 1 - dims;
1064: }
1065: }
1066: // adds the instruction to the bytecode of the method
1067: code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
1068: }
1069:
1070: public void visitTryCatchBlock(final Label start, final Label end,
1071: final Label handler, final String type) {
1072: if (compute == MAXS && (handler.status & Label.PUSHED) == 0) {
1073: // pushes handler block onto the stack of blocks to be visited
1074: handler.status |= Label.PUSHED;
1075: handler.inputStackTop = 1;
1076: handler.next = labels;
1077: labels = handler;
1078: }
1079: ++handlerCount;
1080: Handler h = new Handler();
1081: h.start = start;
1082: h.end = end;
1083: h.handler = handler;
1084: h.desc = type;
1085: h.type = type != null ? cw.newClass(type) : 0;
1086: if (lastHandler == null) {
1087: firstHandler = h;
1088: } else {
1089: lastHandler.next = h;
1090: }
1091: lastHandler = h;
1092: }
1093:
1094: public void visitLocalVariable(final String name,
1095: final String desc, final String signature,
1096: final Label start, final Label end, final int index) {
1097: if (signature != null) {
1098: if (localVarType == null) {
1099: localVarType = new ByteVector();
1100: }
1101: ++localVarTypeCount;
1102: localVarType.putShort(start.position).putShort(
1103: end.position - start.position).putShort(
1104: cw.newUTF8(name)).putShort(cw.newUTF8(signature))
1105: .putShort(index);
1106: }
1107: if (localVar == null) {
1108: localVar = new ByteVector();
1109: }
1110: ++localVarCount;
1111: localVar.putShort(start.position).putShort(
1112: end.position - start.position).putShort(
1113: cw.newUTF8(name)).putShort(cw.newUTF8(desc)).putShort(
1114: index);
1115: if (compute <= MAXS) {
1116: // updates max locals
1117: char c = desc.charAt(0);
1118: int n = index + (c == 'L' || c == 'D' ? 2 : 1);
1119: if (n > maxLocals) {
1120: maxLocals = n;
1121: }
1122: }
1123: }
1124:
1125: public void visitLineNumber(final int line, final Label start) {
1126: if (lineNumber == null) {
1127: lineNumber = new ByteVector();
1128: }
1129: ++lineNumberCount;
1130: lineNumber.putShort(start.position);
1131: lineNumber.putShort(line);
1132: }
1133:
1134: public void visitMaxs(final int maxStack, final int maxLocals) {
1135: if (compute == FRAMES) {
1136: // creates and visits the first (implicit) frame
1137: Frame f = labels.frame;
1138: Type[] args = Type.getArgumentTypes(descriptor);
1139: f.initInputFrame(cw, access, args, this .maxLocals);
1140: visitFrame(f);
1141:
1142: // completes the control flow graph with exception handler blocks
1143: Handler handler = firstHandler;
1144: while (handler != null) {
1145: Label l = handler.start.getFirst();
1146: Label h = handler.handler.getFirst();
1147: Label e = handler.end.getFirst();
1148: // computes the kind of the edges to 'h'
1149: String t = handler.desc == null ? "java/lang/Throwable"
1150: : handler.desc;
1151: int kind = Frame.OBJECT | cw.addType(t);
1152: // h is an exception handler
1153: h.status |= Label.TARGET;
1154: // adds 'h' as a successor of labels between 'start' and 'end'
1155: while (l != e) {
1156: // creates an edge to 'h'
1157: Edge b = new Edge();
1158: b.info = kind;
1159: b.successor = h;
1160: // adds it to the successors of 'l'
1161: b.next = l.successors;
1162: l.successors = b;
1163: // goes to the next label
1164: l = l.successor;
1165: }
1166: handler = handler.next;
1167: }
1168:
1169: /*
1170: * fix point algorithm: mark the first basic block as 'changed'
1171: * (i.e. put it in the 'changed' list) and, while there are changed
1172: * basic blocks, choose one, mark it as unchanged, and update its
1173: * successors (which can be changed in the process).
1174: */
1175: int max = 0;
1176: Label changed = labels;
1177: while (changed != null) {
1178: // removes a basic block from the list of changed basic blocks
1179: Label l = changed;
1180: changed = changed.next;
1181: l.next = null;
1182: f = l.frame;
1183: // a reacheable jump target must be stored in the stack map
1184: if ((l.status & Label.TARGET) != 0) {
1185: l.status |= Label.STORE;
1186: }
1187: // all visited labels are reacheable, by definition
1188: l.status |= Label.REACHABLE;
1189: // updates the (absolute) maximum stack size
1190: int blockMax = f.inputStack.length + l.outputStackMax;
1191: if (blockMax > max) {
1192: max = blockMax;
1193: }
1194: // updates the successors of the current basic block
1195: Edge e = l.successors;
1196: while (e != null) {
1197: Label n = e.successor.getFirst();
1198: boolean change = f.merge(cw, n.frame, e.info);
1199: if (change && n.next == null) {
1200: // if n has changed and is not already in the 'changed'
1201: // list, adds it to this list
1202: n.next = changed;
1203: changed = n;
1204: }
1205: e = e.next;
1206: }
1207: }
1208: this .maxStack = max;
1209:
1210: // visits all the frames that must be stored in the stack map
1211: Label l = labels;
1212: while (l != null) {
1213: f = l.frame;
1214: if ((l.status & Label.STORE) != 0) {
1215: visitFrame(f);
1216: }
1217: if ((l.status & Label.REACHABLE) == 0) {
1218: // finds start and end of dead basic block
1219: Label k = l.successor;
1220: int start = l.position;
1221: int end = (k == null ? code.length : k.position) - 1;
1222: // if non empty basic block
1223: if (end >= start) {
1224: // replaces instructions with NOP ... NOP ATHROW
1225: for (int i = start; i < end; ++i) {
1226: code.data[i] = Opcodes.NOP;
1227: }
1228: code.data[end] = (byte) Opcodes.ATHROW;
1229: // emits a frame for this unreachable block
1230: startFrame(start, 0, 1);
1231: frame[frameIndex++] = Frame.OBJECT
1232: | cw.addType("java/lang/Throwable");
1233: endFrame();
1234: }
1235: }
1236: l = l.successor;
1237: }
1238: } else if (compute == MAXS) {
1239: /*
1240: * control flow analysis algorithm: while the block stack is not
1241: * empty, pop a block from this stack, update the max stack size,
1242: * compute the true (non relative) begin stack size of the
1243: * successors of this block, and push these successors onto the
1244: * stack (unless they have already been pushed onto the stack).
1245: * Note: by hypothesis, the {@link Label#inputStackTop} of the
1246: * blocks in the block stack are the true (non relative) beginning
1247: * stack sizes of these blocks.
1248: */
1249: int max = 0;
1250: Label stack = labels;
1251: while (stack != null) {
1252: // pops a block from the stack
1253: Label l = stack;
1254: stack = stack.next;
1255: // computes the true (non relative) max stack size of this block
1256: int start = l.inputStackTop;
1257: int blockMax = start + l.outputStackMax;
1258: // updates the global max stack size
1259: if (blockMax > max) {
1260: max = blockMax;
1261: }
1262: // analyses the successors of the block
1263: Edge b = l.successors;
1264: while (b != null) {
1265: l = b.successor;
1266: // if this successor has not already been pushed onto the
1267: // stack...
1268: if ((l.status & Label.PUSHED) == 0) {
1269: // computes the true beginning stack size of this
1270: // successor block
1271: l.inputStackTop = start + b.info;
1272: // pushes this successor onto the stack
1273: l.status |= Label.PUSHED;
1274: l.next = stack;
1275: stack = l;
1276: }
1277: b = b.next;
1278: }
1279: }
1280: this .maxStack = max;
1281: } else {
1282: this .maxStack = maxStack;
1283: this .maxLocals = maxLocals;
1284: }
1285: }
1286:
1287: public void visitEnd() {
1288: }
1289:
1290: // ------------------------------------------------------------------------
1291: // Utility methods: control flow analysis algorithm
1292: // ------------------------------------------------------------------------
1293:
1294: /**
1295: * Computes the size of the arguments and of the return value of a method.
1296: *
1297: * @param desc the descriptor of a method.
1298: * @return the size of the arguments of the method (plus one for the
1299: * implicit this argument), argSize, and the size of its return
1300: * value, retSize, packed into a single int i =
1301: * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
1302: * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
1303: */
1304: static int getArgumentsAndReturnSizes(final String desc) {
1305: int n = 1;
1306: int c = 1;
1307: while (true) {
1308: char car = desc.charAt(c++);
1309: if (car == ')') {
1310: car = desc.charAt(c);
1311: return n << 2
1312: | (car == 'V' ? 0
1313: : (car == 'D' || car == 'J' ? 2 : 1));
1314: } else if (car == 'L') {
1315: while (desc.charAt(c++) != ';') {
1316: }
1317: n += 1;
1318: } else if (car == '[') {
1319: while ((car = desc.charAt(c)) == '[') {
1320: ++c;
1321: }
1322: if (car == 'D' || car == 'J') {
1323: n -= 1;
1324: }
1325: } else if (car == 'D' || car == 'J') {
1326: n += 2;
1327: } else {
1328: n += 1;
1329: }
1330: }
1331: }
1332:
1333: /**
1334: * Adds a successor to the {@link #currentBlock currentBlock} block.
1335: *
1336: * @param info information about the control flow edge to be added.
1337: * @param successor the successor block to be added to the current block.
1338: */
1339: private void addSuccessor(final int info, final Label successor) {
1340: Edge b = new Edge();
1341: // initializes the previous Edge object...
1342: b.info = info;
1343: b.successor = successor;
1344: // ...and adds it to the successor list of the currentBlock block
1345: b.next = currentBlock.successors;
1346: currentBlock.successors = b;
1347: }
1348:
1349: /**
1350: * Ends the current basic block. This method must be used in the case where
1351: * the current basic block does not have any successor.
1352: */
1353: private void noSuccessor() {
1354: if (compute == FRAMES) {
1355: Label l = new Label();
1356: l.frame = new Frame();
1357: l.frame.owner = l;
1358: l.resolve(this , code.length, code.data);
1359: previousBlock.successor = l;
1360: previousBlock = l;
1361: } else {
1362: currentBlock.outputStackMax = maxStackSize;
1363: }
1364: currentBlock = null;
1365: }
1366:
1367: // ------------------------------------------------------------------------
1368: // Utility methods: stack map frames
1369: // ------------------------------------------------------------------------
1370:
1371: /**
1372: * Visits a frame that has been computed from scratch.
1373: *
1374: * @param f the frame that must be visited.
1375: */
1376: private void visitFrame(final Frame f) {
1377: int i, t;
1378: int nTop = 0;
1379: int nLocal = 0;
1380: int nStack = 0;
1381: int[] locals = f.inputLocals;
1382: int[] stacks = f.inputStack;
1383: // computes the number of locals (ignores TOP types that are just after
1384: // a LONG or a DOUBLE, and all trailing TOP types)
1385: for (i = 0; i < locals.length; ++i) {
1386: t = locals[i];
1387: if (t == Frame.TOP) {
1388: ++nTop;
1389: } else {
1390: nLocal += nTop + 1;
1391: nTop = 0;
1392: }
1393: if (t == Frame.LONG || t == Frame.DOUBLE) {
1394: ++i;
1395: }
1396: }
1397: // computes the stack size (ignores TOP types that are just after
1398: // a LONG or a DOUBLE)
1399: for (i = 0; i < stacks.length; ++i) {
1400: t = stacks[i];
1401: ++nStack;
1402: if (t == Frame.LONG || t == Frame.DOUBLE) {
1403: ++i;
1404: }
1405: }
1406: // visits the frame and its content
1407: startFrame(f.owner.position, nLocal, nStack);
1408: for (i = 0; nLocal > 0; ++i, --nLocal) {
1409: t = locals[i];
1410: frame[frameIndex++] = t;
1411: if (t == Frame.LONG || t == Frame.DOUBLE) {
1412: ++i;
1413: }
1414: }
1415: for (i = 0; i < stacks.length; ++i) {
1416: t = stacks[i];
1417: frame[frameIndex++] = t;
1418: if (t == Frame.LONG || t == Frame.DOUBLE) {
1419: ++i;
1420: }
1421: }
1422: endFrame();
1423: }
1424:
1425: /**
1426: * Starts the visit of a stack map frame.
1427: *
1428: * @param offset the offset of the instruction to which the frame
1429: * corresponds.
1430: * @param nLocal the number of local variables in the frame.
1431: * @param nStack the number of stack elements in the frame.
1432: */
1433: private void startFrame(final int offset, final int nLocal,
1434: final int nStack) {
1435: int n = 3 + nLocal + nStack;
1436: if (frame == null || frame.length < n) {
1437: frame = new int[n];
1438: }
1439: frame[0] = offset;
1440: frame[1] = nLocal;
1441: frame[2] = nStack;
1442: frameIndex = 3;
1443: }
1444:
1445: /**
1446: * Checks if the visit of the current frame {@link #frame} is finished, and
1447: * if yes, write it in the StackMapTable attribute.
1448: */
1449: private void endFrame() {
1450: if (previousFrame != null) { // do not write the first frame
1451: if (stackMap == null) {
1452: stackMap = new ByteVector();
1453: }
1454: writeFrame();
1455: ++frameCount;
1456: }
1457: previousFrame = frame;
1458: frame = null;
1459: }
1460:
1461: /**
1462: * Compress and writes the current frame {@link #frame} in the StackMapTable
1463: * attribute.
1464: */
1465: private void writeFrame() {
1466: int clocalsSize = frame[1];
1467: int cstackSize = frame[2];
1468: if ((cw.version & 0xFFFF) < Opcodes.V1_6) {
1469: stackMap.putShort(frame[0]).putShort(clocalsSize);
1470: writeFrameTypes(3, 3 + clocalsSize);
1471: stackMap.putShort(cstackSize);
1472: writeFrameTypes(3 + clocalsSize, 3 + clocalsSize
1473: + cstackSize);
1474: return;
1475: }
1476: int localsSize = previousFrame[1];
1477: int type = FULL_FRAME;
1478: int k = 0;
1479: int delta;
1480: if (frameCount == 0) {
1481: delta = frame[0];
1482: } else {
1483: delta = frame[0] - previousFrame[0] - 1;
1484: }
1485: if (cstackSize == 0) {
1486: k = clocalsSize - localsSize;
1487: switch (k) {
1488: case -3:
1489: case -2:
1490: case -1:
1491: type = CHOP_FRAME;
1492: localsSize = clocalsSize;
1493: break;
1494: case 0:
1495: type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
1496: break;
1497: case 1:
1498: case 2:
1499: case 3:
1500: type = APPEND_FRAME;
1501: break;
1502: }
1503: } else if (clocalsSize == localsSize && cstackSize == 1) {
1504: type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME
1505: : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
1506: }
1507: if (type != FULL_FRAME) {
1508: // verify if locals are the same
1509: int l = 3;
1510: for (int j = 0; j < localsSize; j++) {
1511: if (frame[l] != previousFrame[l]) {
1512: type = FULL_FRAME;
1513: break;
1514: }
1515: l++;
1516: }
1517: }
1518: switch (type) {
1519: case SAME_FRAME:
1520: stackMap.putByte(delta);
1521: break;
1522: case SAME_LOCALS_1_STACK_ITEM_FRAME:
1523: stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
1524: writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
1525: break;
1526: case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
1527: stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
1528: .putShort(delta);
1529: writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
1530: break;
1531: case SAME_FRAME_EXTENDED:
1532: stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
1533: break;
1534: case CHOP_FRAME:
1535: stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
1536: break;
1537: case APPEND_FRAME:
1538: stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
1539: writeFrameTypes(3 + localsSize, 3 + clocalsSize);
1540: break;
1541: // case FULL_FRAME:
1542: default:
1543: stackMap.putByte(FULL_FRAME).putShort(delta).putShort(
1544: clocalsSize);
1545: writeFrameTypes(3, 3 + clocalsSize);
1546: stackMap.putShort(cstackSize);
1547: writeFrameTypes(3 + clocalsSize, 3 + clocalsSize
1548: + cstackSize);
1549: }
1550: }
1551:
1552: /**
1553: * Writes some types of the current frame {@link #frame} into the
1554: * StackMapTableAttribute. This method converts types from the format used
1555: * in {@link Label} to the format used in StackMapTable attributes. In
1556: * particular, it converts type table indexes to constant pool indexes.
1557: *
1558: * @param start index of the first type in {@link #frame} to write.
1559: * @param end index of last type in {@link #frame} to write (exclusive).
1560: */
1561: private void writeFrameTypes(final int start, final int end) {
1562: for (int i = start; i < end; ++i) {
1563: int t = frame[i];
1564: int d = t & Frame.DIM;
1565: if (d == 0) {
1566: int v = t & Frame.BASE_VALUE;
1567: switch (t & Frame.BASE_KIND) {
1568: case Frame.OBJECT:
1569: stackMap.putByte(7).putShort(
1570: cw.newClass(cw.typeTable[v].strVal1));
1571: break;
1572: case Frame.UNINITIALIZED:
1573: stackMap.putByte(8)
1574: .putShort(cw.typeTable[v].intVal);
1575: break;
1576: default:
1577: stackMap.putByte(v);
1578: }
1579: } else {
1580: StringBuffer buf = new StringBuffer();
1581: d >>= 28;
1582: while (d-- > 0) {
1583: buf.append('[');
1584: }
1585: if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
1586: buf.append('L');
1587: buf
1588: .append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
1589: buf.append(';');
1590: } else {
1591: switch (t & 0xF) {
1592: case 1:
1593: buf.append('I');
1594: break;
1595: case 2:
1596: buf.append('F');
1597: break;
1598: case 3:
1599: buf.append('D');
1600: break;
1601: case 9:
1602: buf.append('Z');
1603: break;
1604: case 10:
1605: buf.append('B');
1606: break;
1607: case 11:
1608: buf.append('C');
1609: break;
1610: case 12:
1611: buf.append('S');
1612: break;
1613: default:
1614: buf.append('J');
1615: }
1616: }
1617: stackMap.putByte(7).putShort(
1618: cw.newClass(buf.toString()));
1619: }
1620: }
1621: }
1622:
1623: private void writeFrameType(final Object type) {
1624: if (type instanceof String) {
1625: stackMap.putByte(7).putShort(cw.newClass((String) type));
1626: } else if (type instanceof Integer) {
1627: stackMap.putByte(((Integer) type).intValue());
1628: } else {
1629: stackMap.putByte(8).putShort(((Label) type).position);
1630: }
1631: }
1632:
1633: // ------------------------------------------------------------------------
1634: // Utility methods: dump bytecode array
1635: // ------------------------------------------------------------------------
1636:
1637: /**
1638: * Returns the size of the bytecode of this method.
1639: *
1640: * @return the size of the bytecode of this method.
1641: */
1642: final int getSize() {
1643: if (classReaderOffset != 0) {
1644: return 6 + classReaderLength;
1645: }
1646: if (resize) {
1647: // replaces the temporary jump opcodes introduced by Label.resolve.
1648: resizeInstructions();
1649: }
1650: int size = 8;
1651: if (code.length > 0) {
1652: cw.newUTF8("Code");
1653: size += 18 + code.length + 8 * handlerCount;
1654: if (localVar != null) {
1655: cw.newUTF8("LocalVariableTable");
1656: size += 8 + localVar.length;
1657: }
1658: if (localVarType != null) {
1659: cw.newUTF8("LocalVariableTypeTable");
1660: size += 8 + localVarType.length;
1661: }
1662: if (lineNumber != null) {
1663: cw.newUTF8("LineNumberTable");
1664: size += 8 + lineNumber.length;
1665: }
1666: if (stackMap != null) {
1667: boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
1668: cw.newUTF8(zip ? "StackMapTable" : "StackMap");
1669: size += 8 + stackMap.length;
1670: }
1671: if (cattrs != null) {
1672: size += cattrs.getSize(cw, code.data, code.length,
1673: maxStack, maxLocals);
1674: }
1675: }
1676: if (exceptionCount > 0) {
1677: cw.newUTF8("Exceptions");
1678: size += 8 + 2 * exceptionCount;
1679: }
1680: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1681: && (cw.version & 0xffff) < Opcodes.V1_5) {
1682: cw.newUTF8("Synthetic");
1683: size += 6;
1684: }
1685: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1686: cw.newUTF8("Deprecated");
1687: size += 6;
1688: }
1689: if (signature != null) {
1690: cw.newUTF8("Signature");
1691: cw.newUTF8(signature);
1692: size += 8;
1693: }
1694: if (annd != null) {
1695: cw.newUTF8("AnnotationDefault");
1696: size += 6 + annd.length;
1697: }
1698: if (anns != null) {
1699: cw.newUTF8("RuntimeVisibleAnnotations");
1700: size += 8 + anns.getSize();
1701: }
1702: if (ianns != null) {
1703: cw.newUTF8("RuntimeInvisibleAnnotations");
1704: size += 8 + ianns.getSize();
1705: }
1706: if (panns != null) {
1707: cw.newUTF8("RuntimeVisibleParameterAnnotations");
1708: size += 7 + 2 * panns.length;
1709: for (int i = panns.length - 1; i >= 0; --i) {
1710: size += panns[i] == null ? 0 : panns[i].getSize();
1711: }
1712: }
1713: if (ipanns != null) {
1714: cw.newUTF8("RuntimeInvisibleParameterAnnotations");
1715: size += 7 + 2 * ipanns.length;
1716: for (int i = ipanns.length - 1; i >= 0; --i) {
1717: size += ipanns[i] == null ? 0 : ipanns[i].getSize();
1718: }
1719: }
1720: if (attrs != null) {
1721: size += attrs.getSize(cw, null, 0, -1, -1);
1722: }
1723: return size;
1724: }
1725:
1726: /**
1727: * Puts the bytecode of this method in the given byte vector.
1728: *
1729: * @param out the byte vector into which the bytecode of this method must be
1730: * copied.
1731: */
1732: final void put(final ByteVector out) {
1733: out.putShort(access).putShort(name).putShort(desc);
1734: if (classReaderOffset != 0) {
1735: out.putByteArray(cw.cr.b, classReaderOffset,
1736: classReaderLength);
1737: return;
1738: }
1739: int attributeCount = 0;
1740: if (code.length > 0) {
1741: ++attributeCount;
1742: }
1743: if (exceptionCount > 0) {
1744: ++attributeCount;
1745: }
1746: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1747: && (cw.version & 0xffff) < Opcodes.V1_5) {
1748: ++attributeCount;
1749: }
1750: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1751: ++attributeCount;
1752: }
1753: if (signature != null) {
1754: ++attributeCount;
1755: }
1756: if (annd != null) {
1757: ++attributeCount;
1758: }
1759: if (anns != null) {
1760: ++attributeCount;
1761: }
1762: if (ianns != null) {
1763: ++attributeCount;
1764: }
1765: if (panns != null) {
1766: ++attributeCount;
1767: }
1768: if (ipanns != null) {
1769: ++attributeCount;
1770: }
1771: if (attrs != null) {
1772: attributeCount += attrs.getCount();
1773: }
1774: out.putShort(attributeCount);
1775: if (code.length > 0) {
1776: int size = 12 + code.length + 8 * handlerCount;
1777: if (localVar != null) {
1778: size += 8 + localVar.length;
1779: }
1780: if (localVarType != null) {
1781: size += 8 + localVarType.length;
1782: }
1783: if (lineNumber != null) {
1784: size += 8 + lineNumber.length;
1785: }
1786: if (stackMap != null) {
1787: size += 8 + stackMap.length;
1788: }
1789: if (cattrs != null) {
1790: size += cattrs.getSize(cw, code.data, code.length,
1791: maxStack, maxLocals);
1792: }
1793: out.putShort(cw.newUTF8("Code")).putInt(size);
1794: out.putShort(maxStack).putShort(maxLocals);
1795: out.putInt(code.length).putByteArray(code.data, 0,
1796: code.length);
1797: out.putShort(handlerCount);
1798: if (handlerCount > 0) {
1799: Handler h = firstHandler;
1800: while (h != null) {
1801: out.putShort(h.start.position).putShort(
1802: h.end.position)
1803: .putShort(h.handler.position).putShort(
1804: h.type);
1805: h = h.next;
1806: }
1807: }
1808: attributeCount = 0;
1809: if (localVar != null) {
1810: ++attributeCount;
1811: }
1812: if (localVarType != null) {
1813: ++attributeCount;
1814: }
1815: if (lineNumber != null) {
1816: ++attributeCount;
1817: }
1818: if (stackMap != null) {
1819: ++attributeCount;
1820: }
1821: if (cattrs != null) {
1822: attributeCount += cattrs.getCount();
1823: }
1824: out.putShort(attributeCount);
1825: if (localVar != null) {
1826: out.putShort(cw.newUTF8("LocalVariableTable"));
1827: out.putInt(localVar.length + 2).putShort(localVarCount);
1828: out.putByteArray(localVar.data, 0, localVar.length);
1829: }
1830: if (localVarType != null) {
1831: out.putShort(cw.newUTF8("LocalVariableTypeTable"));
1832: out.putInt(localVarType.length + 2).putShort(
1833: localVarTypeCount);
1834: out.putByteArray(localVarType.data, 0,
1835: localVarType.length);
1836: }
1837: if (lineNumber != null) {
1838: out.putShort(cw.newUTF8("LineNumberTable"));
1839: out.putInt(lineNumber.length + 2).putShort(
1840: lineNumberCount);
1841: out.putByteArray(lineNumber.data, 0, lineNumber.length);
1842: }
1843: if (stackMap != null) {
1844: boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
1845: out.putShort(cw.newUTF8(zip ? "StackMapTable"
1846: : "StackMap"));
1847: out.putInt(stackMap.length + 2).putShort(frameCount);
1848: out.putByteArray(stackMap.data, 0, stackMap.length);
1849: }
1850: if (cattrs != null) {
1851: cattrs.put(cw, code.data, code.length, maxLocals,
1852: maxStack, out);
1853: }
1854: }
1855: if (exceptionCount > 0) {
1856: out.putShort(cw.newUTF8("Exceptions")).putInt(
1857: 2 * exceptionCount + 2);
1858: out.putShort(exceptionCount);
1859: for (int i = 0; i < exceptionCount; ++i) {
1860: out.putShort(exceptions[i]);
1861: }
1862: }
1863: if ((access & Opcodes.ACC_SYNTHETIC) != 0
1864: && (cw.version & 0xffff) < Opcodes.V1_5) {
1865: out.putShort(cw.newUTF8("Synthetic")).putInt(0);
1866: }
1867: if ((access & Opcodes.ACC_DEPRECATED) != 0) {
1868: out.putShort(cw.newUTF8("Deprecated")).putInt(0);
1869: }
1870: if (signature != null) {
1871: out.putShort(cw.newUTF8("Signature")).putInt(2).putShort(
1872: cw.newUTF8(signature));
1873: }
1874: if (annd != null) {
1875: out.putShort(cw.newUTF8("AnnotationDefault"));
1876: out.putInt(annd.length);
1877: out.putByteArray(annd.data, 0, annd.length);
1878: }
1879: if (anns != null) {
1880: out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
1881: anns.put(out);
1882: }
1883: if (ianns != null) {
1884: out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
1885: ianns.put(out);
1886: }
1887: if (panns != null) {
1888: out.putShort(cw
1889: .newUTF8("RuntimeVisibleParameterAnnotations"));
1890: AnnotationWriter.put(panns, out);
1891: }
1892: if (ipanns != null) {
1893: out.putShort(cw
1894: .newUTF8("RuntimeInvisibleParameterAnnotations"));
1895: AnnotationWriter.put(ipanns, out);
1896: }
1897: if (attrs != null) {
1898: attrs.put(cw, null, 0, -1, -1, out);
1899: }
1900: }
1901:
1902: // ------------------------------------------------------------------------
1903: // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
1904: // ------------------------------------------------------------------------
1905:
1906: /**
1907: * Resizes and replaces the temporary instructions inserted by
1908: * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
1909: * and instruction addresses consistent. This may require to resize other
1910: * existing instructions, or even to introduce new instructions: for
1911: * example, increasing the size of an instruction by 2 at the middle of a
1912: * method can increases the offset of an IFEQ instruction from 32766 to
1913: * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
1914: * 32765. This, in turn, may require to increase the size of another jump
1915: * instruction, and so on... All these operations are handled automatically
1916: * by this method. <p> <i>This method must be called after all the method
1917: * that is being built has been visited</i>. In particular, the
1918: * {@link Label Label} objects used to construct the method are no longer
1919: * valid after this method has been called.
1920: */
1921: private void resizeInstructions() {
1922: byte[] b = code.data; // bytecode of the method
1923: int u, v, label; // indexes in b
1924: int i, j; // loop indexes
1925: /*
1926: * 1st step: As explained above, resizing an instruction may require to
1927: * resize another one, which may require to resize yet another one, and
1928: * so on. The first step of the algorithm consists in finding all the
1929: * instructions that need to be resized, without modifying the code.
1930: * This is done by the following "fix point" algorithm:
1931: *
1932: * Parse the code to find the jump instructions whose offset will need
1933: * more than 2 bytes to be stored (the future offset is computed from
1934: * the current offset and from the number of bytes that will be inserted
1935: * or removed between the source and target instructions). For each such
1936: * instruction, adds an entry in (a copy of) the indexes and sizes
1937: * arrays (if this has not already been done in a previous iteration!).
1938: *
1939: * If at least one entry has been added during the previous step, go
1940: * back to the beginning, otherwise stop.
1941: *
1942: * In fact the real algorithm is complicated by the fact that the size
1943: * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
1944: * position in the bytecode (because of padding). In order to ensure the
1945: * convergence of the algorithm, the number of bytes to be added or
1946: * removed from these instructions is over estimated during the previous
1947: * loop, and computed exactly only after the loop is finished (this
1948: * requires another pass to parse the bytecode of the method).
1949: */
1950: int[] allIndexes = new int[0]; // copy of indexes
1951: int[] allSizes = new int[0]; // copy of sizes
1952: boolean[] resize; // instructions to be resized
1953: int newOffset; // future offset of a jump instruction
1954:
1955: resize = new boolean[code.length];
1956:
1957: // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
1958: int state = 3;
1959: do {
1960: if (state == 3) {
1961: state = 2;
1962: }
1963: u = 0;
1964: while (u < b.length) {
1965: int opcode = b[u] & 0xFF; // opcode of current instruction
1966: int insert = 0; // bytes to be added after this instruction
1967:
1968: switch (ClassWriter.TYPE[opcode]) {
1969: case ClassWriter.NOARG_INSN:
1970: case ClassWriter.IMPLVAR_INSN:
1971: u += 1;
1972: break;
1973: case ClassWriter.LABEL_INSN:
1974: if (opcode > 201) {
1975: // converts temporary opcodes 202 to 217, 218 and
1976: // 219 to IFEQ ... JSR (inclusive), IFNULL and
1977: // IFNONNULL
1978: opcode = opcode < 218 ? opcode - 49
1979: : opcode - 20;
1980: label = u + readUnsignedShort(b, u + 1);
1981: } else {
1982: label = u + readShort(b, u + 1);
1983: }
1984: newOffset = getNewOffset(allIndexes, allSizes, u,
1985: label);
1986: if (newOffset < Short.MIN_VALUE
1987: || newOffset > Short.MAX_VALUE) {
1988: if (!resize[u]) {
1989: if (opcode == Opcodes.GOTO
1990: || opcode == Opcodes.JSR) {
1991: // two additional bytes will be required to
1992: // replace this GOTO or JSR instruction with
1993: // a GOTO_W or a JSR_W
1994: insert = 2;
1995: } else {
1996: // five additional bytes will be required to
1997: // replace this IFxxx <l> instruction with
1998: // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
1999: // is the "opposite" opcode of IFxxx (i.e.,
2000: // IFNE for IFEQ) and where <l'> designates
2001: // the instruction just after the GOTO_W.
2002: insert = 5;
2003: }
2004: resize[u] = true;
2005: }
2006: }
2007: u += 3;
2008: break;
2009: case ClassWriter.LABELW_INSN:
2010: u += 5;
2011: break;
2012: case ClassWriter.TABL_INSN:
2013: if (state == 1) {
2014: // true number of bytes to be added (or removed)
2015: // from this instruction = (future number of padding
2016: // bytes - current number of padding byte) -
2017: // previously over estimated variation =
2018: // = ((3 - newOffset%4) - (3 - u%4)) - u%4
2019: // = (-newOffset%4 + u%4) - u%4
2020: // = -(newOffset & 3)
2021: newOffset = getNewOffset(allIndexes, allSizes,
2022: 0, u);
2023: insert = -(newOffset & 3);
2024: } else if (!resize[u]) {
2025: // over estimation of the number of bytes to be
2026: // added to this instruction = 3 - current number
2027: // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
2028: insert = u & 3;
2029: resize[u] = true;
2030: }
2031: // skips instruction
2032: u = u + 4 - (u & 3);
2033: u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
2034: break;
2035: case ClassWriter.LOOK_INSN:
2036: if (state == 1) {
2037: // like TABL_INSN
2038: newOffset = getNewOffset(allIndexes, allSizes,
2039: 0, u);
2040: insert = -(newOffset & 3);
2041: } else if (!resize[u]) {
2042: // like TABL_INSN
2043: insert = u & 3;
2044: resize[u] = true;
2045: }
2046: // skips instruction
2047: u = u + 4 - (u & 3);
2048: u += 8 * readInt(b, u + 4) + 8;
2049: break;
2050: case ClassWriter.WIDE_INSN:
2051: opcode = b[u + 1] & 0xFF;
2052: if (opcode == Opcodes.IINC) {
2053: u += 6;
2054: } else {
2055: u += 4;
2056: }
2057: break;
2058: case ClassWriter.VAR_INSN:
2059: case ClassWriter.SBYTE_INSN:
2060: case ClassWriter.LDC_INSN:
2061: u += 2;
2062: break;
2063: case ClassWriter.SHORT_INSN:
2064: case ClassWriter.LDCW_INSN:
2065: case ClassWriter.FIELDORMETH_INSN:
2066: case ClassWriter.TYPE_INSN:
2067: case ClassWriter.IINC_INSN:
2068: u += 3;
2069: break;
2070: case ClassWriter.ITFMETH_INSN:
2071: u += 5;
2072: break;
2073: // case ClassWriter.MANA_INSN:
2074: default:
2075: u += 4;
2076: break;
2077: }
2078: if (insert != 0) {
2079: // adds a new (u, insert) entry in the allIndexes and
2080: // allSizes arrays
2081: int[] newIndexes = new int[allIndexes.length + 1];
2082: int[] newSizes = new int[allSizes.length + 1];
2083: System.arraycopy(allIndexes, 0, newIndexes, 0,
2084: allIndexes.length);
2085: System.arraycopy(allSizes, 0, newSizes, 0,
2086: allSizes.length);
2087: newIndexes[allIndexes.length] = u;
2088: newSizes[allSizes.length] = insert;
2089: allIndexes = newIndexes;
2090: allSizes = newSizes;
2091: if (insert > 0) {
2092: state = 3;
2093: }
2094: }
2095: }
2096: if (state < 3) {
2097: --state;
2098: }
2099: } while (state != 0);
2100:
2101: // 2nd step:
2102: // copies the bytecode of the method into a new bytevector, updates the
2103: // offsets, and inserts (or removes) bytes as requested.
2104:
2105: ByteVector newCode = new ByteVector(code.length);
2106:
2107: u = 0;
2108: while (u < code.length) {
2109: int opcode = b[u] & 0xFF;
2110: switch (ClassWriter.TYPE[opcode]) {
2111: case ClassWriter.NOARG_INSN:
2112: case ClassWriter.IMPLVAR_INSN:
2113: newCode.putByte(opcode);
2114: u += 1;
2115: break;
2116: case ClassWriter.LABEL_INSN:
2117: if (opcode > 201) {
2118: // changes temporary opcodes 202 to 217 (inclusive), 218
2119: // and 219 to IFEQ ... JSR (inclusive), IFNULL and
2120: // IFNONNULL
2121: opcode = opcode < 218 ? opcode - 49 : opcode - 20;
2122: label = u + readUnsignedShort(b, u + 1);
2123: } else {
2124: label = u + readShort(b, u + 1);
2125: }
2126: newOffset = getNewOffset(allIndexes, allSizes, u, label);
2127: if (resize[u]) {
2128: // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
2129: // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
2130: // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
2131: // and where <l'> designates the instruction just after
2132: // the GOTO_W.
2133: if (opcode == Opcodes.GOTO) {
2134: newCode.putByte(200); // GOTO_W
2135: } else if (opcode == Opcodes.JSR) {
2136: newCode.putByte(201); // JSR_W
2137: } else {
2138: newCode
2139: .putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
2140: : opcode ^ 1);
2141: newCode.putShort(8); // jump offset
2142: newCode.putByte(200); // GOTO_W
2143: // newOffset now computed from start of GOTO_W
2144: newOffset -= 3;
2145: }
2146: newCode.putInt(newOffset);
2147: } else {
2148: newCode.putByte(opcode);
2149: newCode.putShort(newOffset);
2150: }
2151: u += 3;
2152: break;
2153: case ClassWriter.LABELW_INSN:
2154: label = u + readInt(b, u + 1);
2155: newOffset = getNewOffset(allIndexes, allSizes, u, label);
2156: newCode.putByte(opcode);
2157: newCode.putInt(newOffset);
2158: u += 5;
2159: break;
2160: case ClassWriter.TABL_INSN:
2161: // skips 0 to 3 padding bytes
2162: v = u;
2163: u = u + 4 - (v & 3);
2164: // reads and copies instruction
2165: newCode.putByte(Opcodes.TABLESWITCH);
2166: newCode.length += (4 - newCode.length % 4) % 4;
2167: label = v + readInt(b, u);
2168: u += 4;
2169: newOffset = getNewOffset(allIndexes, allSizes, v, label);
2170: newCode.putInt(newOffset);
2171: j = readInt(b, u);
2172: u += 4;
2173: newCode.putInt(j);
2174: j = readInt(b, u) - j + 1;
2175: u += 4;
2176: newCode.putInt(readInt(b, u - 4));
2177: for (; j > 0; --j) {
2178: label = v + readInt(b, u);
2179: u += 4;
2180: newOffset = getNewOffset(allIndexes, allSizes, v,
2181: label);
2182: newCode.putInt(newOffset);
2183: }
2184: break;
2185: case ClassWriter.LOOK_INSN:
2186: // skips 0 to 3 padding bytes
2187: v = u;
2188: u = u + 4 - (v & 3);
2189: // reads and copies instruction
2190: newCode.putByte(Opcodes.LOOKUPSWITCH);
2191: newCode.length += (4 - newCode.length % 4) % 4;
2192: label = v + readInt(b, u);
2193: u += 4;
2194: newOffset = getNewOffset(allIndexes, allSizes, v, label);
2195: newCode.putInt(newOffset);
2196: j = readInt(b, u);
2197: u += 4;
2198: newCode.putInt(j);
2199: for (; j > 0; --j) {
2200: newCode.putInt(readInt(b, u));
2201: u += 4;
2202: label = v + readInt(b, u);
2203: u += 4;
2204: newOffset = getNewOffset(allIndexes, allSizes, v,
2205: label);
2206: newCode.putInt(newOffset);
2207: }
2208: break;
2209: case ClassWriter.WIDE_INSN:
2210: opcode = b[u + 1] & 0xFF;
2211: if (opcode == Opcodes.IINC) {
2212: newCode.putByteArray(b, u, 6);
2213: u += 6;
2214: } else {
2215: newCode.putByteArray(b, u, 4);
2216: u += 4;
2217: }
2218: break;
2219: case ClassWriter.VAR_INSN:
2220: case ClassWriter.SBYTE_INSN:
2221: case ClassWriter.LDC_INSN:
2222: newCode.putByteArray(b, u, 2);
2223: u += 2;
2224: break;
2225: case ClassWriter.SHORT_INSN:
2226: case ClassWriter.LDCW_INSN:
2227: case ClassWriter.FIELDORMETH_INSN:
2228: case ClassWriter.TYPE_INSN:
2229: case ClassWriter.IINC_INSN:
2230: newCode.putByteArray(b, u, 3);
2231: u += 3;
2232: break;
2233: case ClassWriter.ITFMETH_INSN:
2234: newCode.putByteArray(b, u, 5);
2235: u += 5;
2236: break;
2237: // case MANA_INSN:
2238: default:
2239: newCode.putByteArray(b, u, 4);
2240: u += 4;
2241: break;
2242: }
2243: }
2244:
2245: // recomputes the stack map frames
2246: if (frameCount > 0) {
2247: if (compute == FRAMES) {
2248: frameCount = 0;
2249: stackMap = null;
2250: previousFrame = null;
2251: frame = null;
2252: Frame f = new Frame();
2253: f.owner = labels;
2254: Type[] args = Type.getArgumentTypes(descriptor);
2255: f.initInputFrame(cw, access, args, maxLocals);
2256: visitFrame(f);
2257: Label l = labels;
2258: while (l != null) {
2259: /*
2260: * here we need the original label position. getNewOffset
2261: * must therefore never have been called for this label.
2262: */
2263: u = l.position - 3;
2264: if ((l.status & Label.STORE) != 0
2265: || (u >= 0 && resize[u])) {
2266: getNewOffset(allIndexes, allSizes, l);
2267: // TODO update offsets in UNINITIALIZED values
2268: visitFrame(l.frame);
2269: }
2270: l = l.successor;
2271: }
2272: } else {
2273: /*
2274: * Resizing an existing stack map frame table is really hard.
2275: * Not only the table must be parsed to update the offets, but
2276: * new frames may be needed for jump instructions that were
2277: * inserted by this method. And updating the offsets or
2278: * inserting frames can change the format of the following
2279: * frames, in case of packed frames. In practice the whole table
2280: * must be recomputed. For this the frames are marked as
2281: * potentially invalid. This will cause the whole class to be
2282: * reread and rewritten with the COMPUTE_FRAMES option (see the
2283: * ClassWriter.toByteArray method). This is not very efficient
2284: * but is much easier and requires much less code than any other
2285: * method I can think of.
2286: */
2287: cw.invalidFrames = true;
2288: }
2289: }
2290: // updates the exception handler block labels
2291: Handler h = firstHandler;
2292: while (h != null) {
2293: getNewOffset(allIndexes, allSizes, h.start);
2294: getNewOffset(allIndexes, allSizes, h.end);
2295: getNewOffset(allIndexes, allSizes, h.handler);
2296: h = h.next;
2297: }
2298: // updates the instructions addresses in the
2299: // local var and line number tables
2300: for (i = 0; i < 2; ++i) {
2301: ByteVector bv = i == 0 ? localVar : localVarType;
2302: if (bv != null) {
2303: b = bv.data;
2304: u = 0;
2305: while (u < bv.length) {
2306: label = readUnsignedShort(b, u);
2307: newOffset = getNewOffset(allIndexes, allSizes, 0,
2308: label);
2309: writeShort(b, u, newOffset);
2310: label += readUnsignedShort(b, u + 2);
2311: newOffset = getNewOffset(allIndexes, allSizes, 0,
2312: label)
2313: - newOffset;
2314: writeShort(b, u + 2, newOffset);
2315: u += 10;
2316: }
2317: }
2318: }
2319: if (lineNumber != null) {
2320: b = lineNumber.data;
2321: u = 0;
2322: while (u < lineNumber.length) {
2323: writeShort(b, u, getNewOffset(allIndexes, allSizes, 0,
2324: readUnsignedShort(b, u)));
2325: u += 4;
2326: }
2327: }
2328: // updates the labels of the other attributes
2329: Attribute attr = cattrs;
2330: while (attr != null) {
2331: Label[] labels = attr.getLabels();
2332: if (labels != null) {
2333: for (i = labels.length - 1; i >= 0; --i) {
2334: getNewOffset(allIndexes, allSizes, labels[i]);
2335: }
2336: }
2337: attr = attr.next;
2338: }
2339:
2340: // replaces old bytecodes with new ones
2341: code = newCode;
2342: }
2343:
2344: /**
2345: * Reads an unsigned short value in the given byte array.
2346: *
2347: * @param b a byte array.
2348: * @param index the start index of the value to be read.
2349: * @return the read value.
2350: */
2351: static int readUnsignedShort(final byte[] b, final int index) {
2352: return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
2353: }
2354:
2355: /**
2356: * Reads a signed short value in the given byte array.
2357: *
2358: * @param b a byte array.
2359: * @param index the start index of the value to be read.
2360: * @return the read value.
2361: */
2362: static short readShort(final byte[] b, final int index) {
2363: return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
2364: }
2365:
2366: /**
2367: * Reads a signed int value in the given byte array.
2368: *
2369: * @param b a byte array.
2370: * @param index the start index of the value to be read.
2371: * @return the read value.
2372: */
2373: static int readInt(final byte[] b, final int index) {
2374: return ((b[index] & 0xFF) << 24)
2375: | ((b[index + 1] & 0xFF) << 16)
2376: | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
2377: }
2378:
2379: /**
2380: * Writes a short value in the given byte array.
2381: *
2382: * @param b a byte array.
2383: * @param index where the first byte of the short value must be written.
2384: * @param s the value to be written in the given byte array.
2385: */
2386: static void writeShort(final byte[] b, final int index, final int s) {
2387: b[index] = (byte) (s >>> 8);
2388: b[index + 1] = (byte) s;
2389: }
2390:
2391: /**
2392: * Computes the future value of a bytecode offset. <p> Note: it is possible
2393: * to have several entries for the same instruction in the <tt>indexes</tt>
2394: * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')
2395: * are equivalent to a single entry (index=a,size=b+b').
2396: *
2397: * @param indexes current positions of the instructions to be resized. Each
2398: * instruction must be designated by the index of its <i>last</i>
2399: * byte, plus one (or, in other words, by the index of the <i>first</i>
2400: * byte of the <i>next</i> instruction).
2401: * @param sizes the number of bytes to be <i>added</i> to the above
2402: * instructions. More precisely, for each i < <tt>len</tt>,
2403: * <tt>sizes</tt>[i] bytes will be added at the end of the
2404: * instruction designated by <tt>indexes</tt>[i] or, if
2405: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
2406: * bytes of the instruction will be removed (the instruction size
2407: * <i>must not</i> become negative or null).
2408: * @param begin index of the first byte of the source instruction.
2409: * @param end index of the first byte of the target instruction.
2410: * @return the future value of the given bytecode offset.
2411: */
2412: static int getNewOffset(final int[] indexes, final int[] sizes,
2413: final int begin, final int end) {
2414: int offset = end - begin;
2415: for (int i = 0; i < indexes.length; ++i) {
2416: if (begin < indexes[i] && indexes[i] <= end) {
2417: // forward jump
2418: offset += sizes[i];
2419: } else if (end < indexes[i] && indexes[i] <= begin) {
2420: // backward jump
2421: offset -= sizes[i];
2422: }
2423: }
2424: return offset;
2425: }
2426:
2427: /**
2428: * Updates the offset of the given label.
2429: *
2430: * @param indexes current positions of the instructions to be resized. Each
2431: * instruction must be designated by the index of its <i>last</i>
2432: * byte, plus one (or, in other words, by the index of the <i>first</i>
2433: * byte of the <i>next</i> instruction).
2434: * @param sizes the number of bytes to be <i>added</i> to the above
2435: * instructions. More precisely, for each i < <tt>len</tt>,
2436: * <tt>sizes</tt>[i] bytes will be added at the end of the
2437: * instruction designated by <tt>indexes</tt>[i] or, if
2438: * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
2439: * bytes of the instruction will be removed (the instruction size
2440: * <i>must not</i> become negative or null).
2441: * @param label the label whose offset must be updated.
2442: */
2443: static void getNewOffset(final int[] indexes, final int[] sizes,
2444: final Label label) {
2445: if ((label.status & Label.RESIZED) == 0) {
2446: label.position = getNewOffset(indexes, sizes, 0,
2447: label.position);
2448: label.status |= Label.RESIZED;
2449: }
2450: }
2451: }
|