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