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