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