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.commons;
0030:
0031: import java.util.ArrayList;
0032: import java.util.Arrays;
0033: import java.util.List;
0034:
0035: import org.objectweb.asm.ClassVisitor;
0036: import org.objectweb.asm.Label;
0037: import org.objectweb.asm.MethodVisitor;
0038: import org.objectweb.asm.Opcodes;
0039: import org.objectweb.asm.Type;
0040:
0041: /**
0042: * A {@link org.objectweb.asm.MethodAdapter} with convenient methods to generate
0043: * code. For example, using this adapter, the class below
0044: *
0045: * <pre>
0046: * public class Example {
0047: * public static void main(String[] args) {
0048: * System.out.println("Hello world!");
0049: * }
0050: * }
0051: * </pre>
0052: *
0053: * can be generated as follows:
0054: *
0055: * <pre>
0056: * ClassWriter cw = new ClassWriter(true);
0057: * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
0058: *
0059: * Method m = Method.getMethod("void <init> ()");
0060: * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
0061: * mg.loadThis();
0062: * mg.invokeConstructor(Type.getType(Object.class), m);
0063: * mg.returnValue();
0064: * mg.endMethod();
0065: *
0066: * m = Method.getMethod("void main (String[])");
0067: * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
0068: * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
0069: * mg.push("Hello world!");
0070: * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)"));
0071: * mg.returnValue();
0072: * mg.endMethod();
0073: *
0074: * cw.visitEnd();
0075: * </pre>
0076: *
0077: * @author Juozas Baliuka
0078: * @author Chris Nokleberg
0079: * @author Eric Bruneton
0080: */
0081: public class GeneratorAdapter extends LocalVariablesSorter {
0082:
0083: private static final String CLDESC = "Ljava/lang/Class;";
0084:
0085: private static final Type BYTE_TYPE = Type
0086: .getObjectType("java/lang/Byte");
0087:
0088: private static final Type BOOLEAN_TYPE = Type
0089: .getObjectType("java/lang/Boolean");
0090:
0091: private static final Type SHORT_TYPE = Type
0092: .getObjectType("java/lang/Short");
0093:
0094: private static final Type CHARACTER_TYPE = Type
0095: .getObjectType("java/lang/Character");
0096:
0097: private static final Type INTEGER_TYPE = Type
0098: .getObjectType("java/lang/Integer");
0099:
0100: private static final Type FLOAT_TYPE = Type
0101: .getObjectType("java/lang/Float");
0102:
0103: private static final Type LONG_TYPE = Type
0104: .getObjectType("java/lang/Long");
0105:
0106: private static final Type DOUBLE_TYPE = Type
0107: .getObjectType("java/lang/Double");
0108:
0109: private static final Type NUMBER_TYPE = Type
0110: .getObjectType("java/lang/Number");
0111:
0112: private static final Type OBJECT_TYPE = Type
0113: .getObjectType("java/lang/Object");
0114:
0115: private static final Method BOOLEAN_VALUE = Method
0116: .getMethod("boolean booleanValue()");
0117:
0118: private static final Method CHAR_VALUE = Method
0119: .getMethod("char charValue()");
0120:
0121: private static final Method INT_VALUE = Method
0122: .getMethod("int intValue()");
0123:
0124: private static final Method FLOAT_VALUE = Method
0125: .getMethod("float floatValue()");
0126:
0127: private static final Method LONG_VALUE = Method
0128: .getMethod("long longValue()");
0129:
0130: private static final Method DOUBLE_VALUE = Method
0131: .getMethod("double doubleValue()");
0132:
0133: /**
0134: * Constant for the {@link #math math} method.
0135: */
0136: public static final int ADD = Opcodes.IADD;
0137:
0138: /**
0139: * Constant for the {@link #math math} method.
0140: */
0141: public static final int SUB = Opcodes.ISUB;
0142:
0143: /**
0144: * Constant for the {@link #math math} method.
0145: */
0146: public static final int MUL = Opcodes.IMUL;
0147:
0148: /**
0149: * Constant for the {@link #math math} method.
0150: */
0151: public static final int DIV = Opcodes.IDIV;
0152:
0153: /**
0154: * Constant for the {@link #math math} method.
0155: */
0156: public static final int REM = Opcodes.IREM;
0157:
0158: /**
0159: * Constant for the {@link #math math} method.
0160: */
0161: public static final int NEG = Opcodes.INEG;
0162:
0163: /**
0164: * Constant for the {@link #math math} method.
0165: */
0166: public static final int SHL = Opcodes.ISHL;
0167:
0168: /**
0169: * Constant for the {@link #math math} method.
0170: */
0171: public static final int SHR = Opcodes.ISHR;
0172:
0173: /**
0174: * Constant for the {@link #math math} method.
0175: */
0176: public static final int USHR = Opcodes.IUSHR;
0177:
0178: /**
0179: * Constant for the {@link #math math} method.
0180: */
0181: public static final int AND = Opcodes.IAND;
0182:
0183: /**
0184: * Constant for the {@link #math math} method.
0185: */
0186: public static final int OR = Opcodes.IOR;
0187:
0188: /**
0189: * Constant for the {@link #math math} method.
0190: */
0191: public static final int XOR = Opcodes.IXOR;
0192:
0193: /**
0194: * Constant for the {@link #ifCmp ifCmp} method.
0195: */
0196: public static final int EQ = Opcodes.IFEQ;
0197:
0198: /**
0199: * Constant for the {@link #ifCmp ifCmp} method.
0200: */
0201: public static final int NE = Opcodes.IFNE;
0202:
0203: /**
0204: * Constant for the {@link #ifCmp ifCmp} method.
0205: */
0206: public static final int LT = Opcodes.IFLT;
0207:
0208: /**
0209: * Constant for the {@link #ifCmp ifCmp} method.
0210: */
0211: public static final int GE = Opcodes.IFGE;
0212:
0213: /**
0214: * Constant for the {@link #ifCmp ifCmp} method.
0215: */
0216: public static final int GT = Opcodes.IFGT;
0217:
0218: /**
0219: * Constant for the {@link #ifCmp ifCmp} method.
0220: */
0221: public static final int LE = Opcodes.IFLE;
0222:
0223: /**
0224: * Access flags of the method visited by this adapter.
0225: */
0226: private final int access;
0227:
0228: /**
0229: * Return type of the method visited by this adapter.
0230: */
0231: private final Type returnType;
0232:
0233: /**
0234: * Argument types of the method visited by this adapter.
0235: */
0236: private final Type[] argumentTypes;
0237:
0238: /**
0239: * Types of the local variables of the method visited by this adapter.
0240: */
0241: private final List localTypes = new ArrayList();
0242:
0243: /**
0244: * Creates a new {@link GeneratorAdapter}.
0245: *
0246: * @param mv the method visitor to which this adapter delegates calls.
0247: * @param access the method's access flags (see {@link Opcodes}).
0248: * @param name the method's name.
0249: * @param desc the method's descriptor (see {@link Type Type}).
0250: */
0251: public GeneratorAdapter(final MethodVisitor mv, final int access,
0252: final String name, final String desc) {
0253: super (access, desc, mv);
0254: this .access = access;
0255: this .returnType = Type.getReturnType(desc);
0256: this .argumentTypes = Type.getArgumentTypes(desc);
0257: }
0258:
0259: /**
0260: * Creates a new {@link GeneratorAdapter}.
0261: *
0262: * @param access access flags of the adapted method.
0263: * @param method the adapted method.
0264: * @param mv the method visitor to which this adapter delegates calls.
0265: */
0266: public GeneratorAdapter(final int access, final Method method,
0267: final MethodVisitor mv) {
0268: super (access, method.getDescriptor(), mv);
0269: this .access = access;
0270: this .returnType = method.getReturnType();
0271: this .argumentTypes = method.getArgumentTypes();
0272: }
0273:
0274: /**
0275: * Creates a new {@link GeneratorAdapter}.
0276: *
0277: * @param access access flags of the adapted method.
0278: * @param method the adapted method.
0279: * @param signature the signature of the adapted method (may be
0280: * <tt>null</tt>).
0281: * @param exceptions the exceptions thrown by the adapted method (may be
0282: * <tt>null</tt>).
0283: * @param cv the class visitor to which this adapter delegates calls.
0284: */
0285: public GeneratorAdapter(final int access, final Method method,
0286: final String signature, final Type[] exceptions,
0287: final ClassVisitor cv) {
0288: this (access, method, cv.visitMethod(access, method.getName(),
0289: method.getDescriptor(), signature,
0290: getInternalNames(exceptions)));
0291: }
0292:
0293: /**
0294: * Returns the internal names of the given types.
0295: *
0296: * @param types a set of types.
0297: * @return the internal names of the given types.
0298: */
0299: private static String[] getInternalNames(final Type[] types) {
0300: if (types == null) {
0301: return null;
0302: }
0303: String[] names = new String[types.length];
0304: for (int i = 0; i < names.length; ++i) {
0305: names[i] = types[i].getInternalName();
0306: }
0307: return names;
0308: }
0309:
0310: // ------------------------------------------------------------------------
0311: // Instructions to push constants on the stack
0312: // ------------------------------------------------------------------------
0313:
0314: /**
0315: * Generates the instruction to push the given value on the stack.
0316: *
0317: * @param value the value to be pushed on the stack.
0318: */
0319: public void push(final boolean value) {
0320: push(value ? 1 : 0);
0321: }
0322:
0323: /**
0324: * Generates the instruction to push the given value on the stack.
0325: *
0326: * @param value the value to be pushed on the stack.
0327: */
0328: public void push(final int value) {
0329: if (value >= -1 && value <= 5) {
0330: mv.visitInsn(Opcodes.ICONST_0 + value);
0331: } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
0332: mv.visitIntInsn(Opcodes.BIPUSH, value);
0333: } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
0334: mv.visitIntInsn(Opcodes.SIPUSH, value);
0335: } else {
0336: mv.visitLdcInsn(new Integer(value));
0337: }
0338: }
0339:
0340: /**
0341: * Generates the instruction to push the given value on the stack.
0342: *
0343: * @param value the value to be pushed on the stack.
0344: */
0345: public void push(final long value) {
0346: if (value == 0L || value == 1L) {
0347: mv.visitInsn(Opcodes.LCONST_0 + (int) value);
0348: } else {
0349: mv.visitLdcInsn(new Long(value));
0350: }
0351: }
0352:
0353: /**
0354: * Generates the instruction to push the given value on the stack.
0355: *
0356: * @param value the value to be pushed on the stack.
0357: */
0358: public void push(final float value) {
0359: int bits = Float.floatToIntBits(value);
0360: if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
0361: mv.visitInsn(Opcodes.FCONST_0 + (int) value);
0362: } else {
0363: mv.visitLdcInsn(new Float(value));
0364: }
0365: }
0366:
0367: /**
0368: * Generates the instruction to push the given value on the stack.
0369: *
0370: * @param value the value to be pushed on the stack.
0371: */
0372: public void push(final double value) {
0373: long bits = Double.doubleToLongBits(value);
0374: if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
0375: mv.visitInsn(Opcodes.DCONST_0 + (int) value);
0376: } else {
0377: mv.visitLdcInsn(new Double(value));
0378: }
0379: }
0380:
0381: /**
0382: * Generates the instruction to push the given value on the stack.
0383: *
0384: * @param value the value to be pushed on the stack. May be <tt>null</tt>.
0385: */
0386: public void push(final String value) {
0387: if (value == null) {
0388: mv.visitInsn(Opcodes.ACONST_NULL);
0389: } else {
0390: mv.visitLdcInsn(value);
0391: }
0392: }
0393:
0394: /**
0395: * Generates the instruction to push the given value on the stack.
0396: *
0397: * @param value the value to be pushed on the stack.
0398: */
0399: public void push(final Type value) {
0400: if (value == null) {
0401: mv.visitInsn(Opcodes.ACONST_NULL);
0402: } else {
0403: switch (value.getSort()) {
0404: case Type.BOOLEAN:
0405: mv.visitFieldInsn(Opcodes.GETSTATIC,
0406: "java/lang/Boolean", "TYPE", CLDESC);
0407: break;
0408: case Type.CHAR:
0409: mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Char",
0410: "TYPE", CLDESC);
0411: break;
0412: case Type.BYTE:
0413: mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte",
0414: "TYPE", CLDESC);
0415: break;
0416: case Type.SHORT:
0417: mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short",
0418: "TYPE", CLDESC);
0419: break;
0420: case Type.INT:
0421: mv.visitFieldInsn(Opcodes.GETSTATIC,
0422: "java/lang/Integer", "TYPE", CLDESC);
0423: break;
0424: case Type.FLOAT:
0425: mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float",
0426: "TYPE", CLDESC);
0427: break;
0428: case Type.LONG:
0429: mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long",
0430: "TYPE", CLDESC);
0431: break;
0432: case Type.DOUBLE:
0433: mv.visitFieldInsn(Opcodes.GETSTATIC,
0434: "java/lang/Double", "TYPE", CLDESC);
0435: break;
0436: default:
0437: mv.visitLdcInsn(value);
0438: }
0439: }
0440: }
0441:
0442: // ------------------------------------------------------------------------
0443: // Instructions to load and store method arguments
0444: // ------------------------------------------------------------------------
0445:
0446: /**
0447: * Returns the index of the given method argument in the frame's local
0448: * variables array.
0449: *
0450: * @param arg the index of a method argument.
0451: * @return the index of the given method argument in the frame's local
0452: * variables array.
0453: */
0454: private int getArgIndex(final int arg) {
0455: int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
0456: for (int i = 0; i < arg; i++) {
0457: index += argumentTypes[i].getSize();
0458: }
0459: return index;
0460: }
0461:
0462: /**
0463: * Generates the instruction to push a local variable on the stack.
0464: *
0465: * @param type the type of the local variable to be loaded.
0466: * @param index an index in the frame's local variables array.
0467: */
0468: private void loadInsn(final Type type, final int index) {
0469: mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
0470: }
0471:
0472: /**
0473: * Generates the instruction to store the top stack value in a local
0474: * variable.
0475: *
0476: * @param type the type of the local variable to be stored.
0477: * @param index an index in the frame's local variables array.
0478: */
0479: private void storeInsn(final Type type, final int index) {
0480: mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
0481: }
0482:
0483: /**
0484: * Generates the instruction to load 'this' on the stack.
0485: */
0486: public void loadThis() {
0487: if ((access & Opcodes.ACC_STATIC) != 0) {
0488: throw new IllegalStateException(
0489: "no 'this' pointer within static method");
0490: }
0491: mv.visitVarInsn(Opcodes.ALOAD, 0);
0492: }
0493:
0494: /**
0495: * Generates the instruction to load the given method argument on the stack.
0496: *
0497: * @param arg the index of a method argument.
0498: */
0499: public void loadArg(final int arg) {
0500: loadInsn(argumentTypes[arg], getArgIndex(arg));
0501: }
0502:
0503: /**
0504: * Generates the instructions to load the given method arguments on the
0505: * stack.
0506: *
0507: * @param arg the index of the first method argument to be loaded.
0508: * @param count the number of method arguments to be loaded.
0509: */
0510: public void loadArgs(final int arg, final int count) {
0511: int index = getArgIndex(arg);
0512: for (int i = 0; i < count; ++i) {
0513: Type t = argumentTypes[arg + i];
0514: loadInsn(t, index);
0515: index += t.getSize();
0516: }
0517: }
0518:
0519: /**
0520: * Generates the instructions to load all the method arguments on the stack.
0521: */
0522: public void loadArgs() {
0523: loadArgs(0, argumentTypes.length);
0524: }
0525:
0526: /**
0527: * Generates the instructions to load all the method arguments on the stack,
0528: * as a single object array.
0529: */
0530: public void loadArgArray() {
0531: push(argumentTypes.length);
0532: newArray(OBJECT_TYPE);
0533: for (int i = 0; i < argumentTypes.length; i++) {
0534: dup();
0535: push(i);
0536: loadArg(i);
0537: box(argumentTypes[i]);
0538: arrayStore(OBJECT_TYPE);
0539: }
0540: }
0541:
0542: /**
0543: * Generates the instruction to store the top stack value in the given
0544: * method argument.
0545: *
0546: * @param arg the index of a method argument.
0547: */
0548: public void storeArg(final int arg) {
0549: storeInsn(argumentTypes[arg], getArgIndex(arg));
0550: }
0551:
0552: // ------------------------------------------------------------------------
0553: // Instructions to load and store local variables
0554: // ------------------------------------------------------------------------
0555:
0556: /**
0557: * Returns the type of the given local variable.
0558: *
0559: * @param local a local variable identifier, as returned by
0560: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0561: * @return the type of the given local variable.
0562: */
0563: public Type getLocalType(final int local) {
0564: return (Type) localTypes.get(local - firstLocal);
0565: }
0566:
0567: protected void setLocalType(final int local, final Type type) {
0568: int index = local - firstLocal;
0569: while (localTypes.size() < index + 1) {
0570: localTypes.add(null);
0571: }
0572: localTypes.set(index, type);
0573: }
0574:
0575: /**
0576: * Generates the instruction to load the given local variable on the stack.
0577: *
0578: * @param local a local variable identifier, as returned by
0579: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0580: */
0581: public void loadLocal(final int local) {
0582: loadInsn(getLocalType(local), local);
0583: }
0584:
0585: /**
0586: * Generates the instruction to load the given local variable on the stack.
0587: *
0588: * @param local a local variable identifier, as returned by
0589: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0590: * @param type the type of this local variable.
0591: */
0592: public void loadLocal(final int local, final Type type) {
0593: setLocalType(local, type);
0594: loadInsn(type, local);
0595: }
0596:
0597: /**
0598: * Generates the instruction to store the top stack value in the given local
0599: * variable.
0600: *
0601: * @param local a local variable identifier, as returned by
0602: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0603: */
0604: public void storeLocal(final int local) {
0605: storeInsn(getLocalType(local), local);
0606: }
0607:
0608: /**
0609: * Generates the instruction to store the top stack value in the given local
0610: * variable.
0611: *
0612: * @param local a local variable identifier, as returned by
0613: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0614: * @param type the type of this local variable.
0615: */
0616: public void storeLocal(final int local, final Type type) {
0617: setLocalType(local, type);
0618: storeInsn(type, local);
0619: }
0620:
0621: /**
0622: * Generates the instruction to load an element from an array.
0623: *
0624: * @param type the type of the array element to be loaded.
0625: */
0626: public void arrayLoad(final Type type) {
0627: mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
0628: }
0629:
0630: /**
0631: * Generates the instruction to store an element in an array.
0632: *
0633: * @param type the type of the array element to be stored.
0634: */
0635: public void arrayStore(final Type type) {
0636: mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
0637: }
0638:
0639: // ------------------------------------------------------------------------
0640: // Instructions to manage the stack
0641: // ------------------------------------------------------------------------
0642:
0643: /**
0644: * Generates a POP instruction.
0645: */
0646: public void pop() {
0647: mv.visitInsn(Opcodes.POP);
0648: }
0649:
0650: /**
0651: * Generates a POP2 instruction.
0652: */
0653: public void pop2() {
0654: mv.visitInsn(Opcodes.POP2);
0655: }
0656:
0657: /**
0658: * Generates a DUP instruction.
0659: */
0660: public void dup() {
0661: mv.visitInsn(Opcodes.DUP);
0662: }
0663:
0664: /**
0665: * Generates a DUP2 instruction.
0666: */
0667: public void dup2() {
0668: mv.visitInsn(Opcodes.DUP2);
0669: }
0670:
0671: /**
0672: * Generates a DUP_X1 instruction.
0673: */
0674: public void dupX1() {
0675: mv.visitInsn(Opcodes.DUP_X1);
0676: }
0677:
0678: /**
0679: * Generates a DUP_X2 instruction.
0680: */
0681: public void dupX2() {
0682: mv.visitInsn(Opcodes.DUP_X2);
0683: }
0684:
0685: /**
0686: * Generates a DUP2_X1 instruction.
0687: */
0688: public void dup2X1() {
0689: mv.visitInsn(Opcodes.DUP2_X1);
0690: }
0691:
0692: /**
0693: * Generates a DUP2_X2 instruction.
0694: */
0695: public void dup2X2() {
0696: mv.visitInsn(Opcodes.DUP2_X2);
0697: }
0698:
0699: /**
0700: * Generates a SWAP instruction.
0701: */
0702: public void swap() {
0703: mv.visitInsn(Opcodes.SWAP);
0704: }
0705:
0706: /**
0707: * Generates the instructions to swap the top two stack values.
0708: *
0709: * @param prev type of the top - 1 stack value.
0710: * @param type type of the top stack value.
0711: */
0712: public void swap(final Type prev, final Type type) {
0713: if (type.getSize() == 1) {
0714: if (prev.getSize() == 1) {
0715: swap(); // same as dupX1(), pop();
0716: } else {
0717: dupX2();
0718: pop();
0719: }
0720: } else {
0721: if (prev.getSize() == 1) {
0722: dup2X1();
0723: pop2();
0724: } else {
0725: dup2X2();
0726: pop2();
0727: }
0728: }
0729: }
0730:
0731: // ------------------------------------------------------------------------
0732: // Instructions to do mathematical and logical operations
0733: // ------------------------------------------------------------------------
0734:
0735: /**
0736: * Generates the instruction to do the specified mathematical or logical
0737: * operation.
0738: *
0739: * @param op a mathematical or logical operation. Must be one of ADD, SUB,
0740: * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
0741: * @param type the type of the operand(s) for this operation.
0742: */
0743: public void math(final int op, final Type type) {
0744: mv.visitInsn(type.getOpcode(op));
0745: }
0746:
0747: /**
0748: * Generates the instructions to compute the bitwise negation of the top
0749: * stack value.
0750: */
0751: public void not() {
0752: mv.visitInsn(Opcodes.ICONST_1);
0753: mv.visitInsn(Opcodes.IXOR);
0754: }
0755:
0756: /**
0757: * Generates the instruction to increment the given local variable.
0758: *
0759: * @param local the local variable to be incremented.
0760: * @param amount the amount by which the local variable must be incremented.
0761: */
0762: public void iinc(final int local, final int amount) {
0763: mv.visitIincInsn(local, amount);
0764: }
0765:
0766: /**
0767: * Generates the instructions to cast a numerical value from one type to
0768: * another.
0769: *
0770: * @param from the type of the top stack value
0771: * @param to the type into which this value must be cast.
0772: */
0773: public void cast(final Type from, final Type to) {
0774: if (from != to) {
0775: if (from == Type.DOUBLE_TYPE) {
0776: if (to == Type.FLOAT_TYPE) {
0777: mv.visitInsn(Opcodes.D2F);
0778: } else if (to == Type.LONG_TYPE) {
0779: mv.visitInsn(Opcodes.D2L);
0780: } else {
0781: mv.visitInsn(Opcodes.D2I);
0782: cast(Type.INT_TYPE, to);
0783: }
0784: } else if (from == Type.FLOAT_TYPE) {
0785: if (to == Type.DOUBLE_TYPE) {
0786: mv.visitInsn(Opcodes.F2D);
0787: } else if (to == Type.LONG_TYPE) {
0788: mv.visitInsn(Opcodes.F2L);
0789: } else {
0790: mv.visitInsn(Opcodes.F2I);
0791: cast(Type.INT_TYPE, to);
0792: }
0793: } else if (from == Type.LONG_TYPE) {
0794: if (to == Type.DOUBLE_TYPE) {
0795: mv.visitInsn(Opcodes.L2D);
0796: } else if (to == Type.FLOAT_TYPE) {
0797: mv.visitInsn(Opcodes.L2F);
0798: } else {
0799: mv.visitInsn(Opcodes.L2I);
0800: cast(Type.INT_TYPE, to);
0801: }
0802: } else {
0803: if (to == Type.BYTE_TYPE) {
0804: mv.visitInsn(Opcodes.I2B);
0805: } else if (to == Type.CHAR_TYPE) {
0806: mv.visitInsn(Opcodes.I2C);
0807: } else if (to == Type.DOUBLE_TYPE) {
0808: mv.visitInsn(Opcodes.I2D);
0809: } else if (to == Type.FLOAT_TYPE) {
0810: mv.visitInsn(Opcodes.I2F);
0811: } else if (to == Type.LONG_TYPE) {
0812: mv.visitInsn(Opcodes.I2L);
0813: } else if (to == Type.SHORT_TYPE) {
0814: mv.visitInsn(Opcodes.I2S);
0815: }
0816: }
0817: }
0818: }
0819:
0820: // ------------------------------------------------------------------------
0821: // Instructions to do boxing and unboxing operations
0822: // ------------------------------------------------------------------------
0823:
0824: private static Type getBoxedType(final Type type) {
0825: switch (type.getSort()) {
0826: case Type.BYTE:
0827: return BYTE_TYPE;
0828: case Type.BOOLEAN:
0829: return BOOLEAN_TYPE;
0830: case Type.SHORT:
0831: return SHORT_TYPE;
0832: case Type.CHAR:
0833: return CHARACTER_TYPE;
0834: case Type.INT:
0835: return INTEGER_TYPE;
0836: case Type.FLOAT:
0837: return FLOAT_TYPE;
0838: case Type.LONG:
0839: return LONG_TYPE;
0840: case Type.DOUBLE:
0841: return DOUBLE_TYPE;
0842: }
0843: return type;
0844: }
0845:
0846: /**
0847: * Generates the instructions to box the top stack value. This value is
0848: * replaced by its boxed equivalent on top of the stack.
0849: *
0850: * @param type the type of the top stack value.
0851: */
0852: public void box(final Type type) {
0853: if (type.getSort() == Type.OBJECT
0854: || type.getSort() == Type.ARRAY) {
0855: return;
0856: }
0857: if (type == Type.VOID_TYPE) {
0858: push((String) null);
0859: } else {
0860: Type boxed = getBoxedType(type);
0861: newInstance(boxed);
0862: if (type.getSize() == 2) {
0863: // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
0864: dupX2();
0865: dupX2();
0866: pop();
0867: } else {
0868: // p -> po -> opo -> oop -> o
0869: dupX1();
0870: swap();
0871: }
0872: invokeConstructor(boxed, new Method("<init>",
0873: Type.VOID_TYPE, new Type[] { type }));
0874: }
0875: }
0876:
0877: /**
0878: * Generates the instructions to box the top stack value using Java 5's
0879: * valueOf() method. This value is replaced by its boxed equivalent on top
0880: * of the stack.
0881: *
0882: * @param type the type of the top stack value.
0883: * @author Prashant Deva
0884: */
0885: public void valueOf(final Type type) {
0886: if (type.getSort() == Type.OBJECT
0887: || type.getSort() == Type.ARRAY) {
0888: return;
0889: }
0890: if (type == Type.VOID_TYPE) {
0891: push((String) null);
0892: } else {
0893: Type boxed = getBoxedType(type);
0894: invokeStatic(boxed, new Method("valueOf", boxed,
0895: new Type[] { type }));
0896: }
0897: }
0898:
0899: /**
0900: * Generates the instructions to unbox the top stack value. This value is
0901: * replaced by its unboxed equivalent on top of the stack.
0902: *
0903: * @param type the type of the top stack value.
0904: */
0905: public void unbox(final Type type) {
0906: Type t = NUMBER_TYPE;
0907: Method sig = null;
0908: switch (type.getSort()) {
0909: case Type.VOID:
0910: return;
0911: case Type.CHAR:
0912: t = CHARACTER_TYPE;
0913: sig = CHAR_VALUE;
0914: break;
0915: case Type.BOOLEAN:
0916: t = BOOLEAN_TYPE;
0917: sig = BOOLEAN_VALUE;
0918: break;
0919: case Type.DOUBLE:
0920: sig = DOUBLE_VALUE;
0921: break;
0922: case Type.FLOAT:
0923: sig = FLOAT_VALUE;
0924: break;
0925: case Type.LONG:
0926: sig = LONG_VALUE;
0927: break;
0928: case Type.INT:
0929: case Type.SHORT:
0930: case Type.BYTE:
0931: sig = INT_VALUE;
0932: }
0933: if (sig == null) {
0934: checkCast(type);
0935: } else {
0936: checkCast(t);
0937: invokeVirtual(t, sig);
0938: }
0939: }
0940:
0941: // ------------------------------------------------------------------------
0942: // Instructions to jump to other instructions
0943: // ------------------------------------------------------------------------
0944:
0945: /**
0946: * Creates a new {@link Label}.
0947: *
0948: * @return a new {@link Label}.
0949: */
0950: public Label newLabel() {
0951: return new Label();
0952: }
0953:
0954: /**
0955: * Marks the current code position with the given label.
0956: *
0957: * @param label a label.
0958: */
0959: public void mark(final Label label) {
0960: mv.visitLabel(label);
0961: }
0962:
0963: /**
0964: * Marks the current code position with a new label.
0965: *
0966: * @return the label that was created to mark the current code position.
0967: */
0968: public Label mark() {
0969: Label label = new Label();
0970: mv.visitLabel(label);
0971: return label;
0972: }
0973:
0974: /**
0975: * Generates the instructions to jump to a label based on the comparison of
0976: * the top two stack values.
0977: *
0978: * @param type the type of the top two stack values.
0979: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
0980: * LE.
0981: * @param label where to jump if the comparison result is <tt>true</tt>.
0982: */
0983: public void ifCmp(final Type type, final int mode, final Label label) {
0984: int intOp = -1;
0985: switch (type.getSort()) {
0986: case Type.LONG:
0987: mv.visitInsn(Opcodes.LCMP);
0988: break;
0989: case Type.DOUBLE:
0990: mv.visitInsn(Opcodes.DCMPG);
0991: break;
0992: case Type.FLOAT:
0993: mv.visitInsn(Opcodes.FCMPG);
0994: break;
0995: case Type.ARRAY:
0996: case Type.OBJECT:
0997: switch (mode) {
0998: case EQ:
0999: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
1000: return;
1001: case NE:
1002: mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
1003: return;
1004: }
1005: throw new IllegalArgumentException(
1006: "Bad comparison for type " + type);
1007: default:
1008: switch (mode) {
1009: case EQ:
1010: intOp = Opcodes.IF_ICMPEQ;
1011: break;
1012: case NE:
1013: intOp = Opcodes.IF_ICMPNE;
1014: break;
1015: case GE:
1016: intOp = Opcodes.IF_ICMPGE;
1017: break;
1018: case LT:
1019: intOp = Opcodes.IF_ICMPLT;
1020: break;
1021: case LE:
1022: intOp = Opcodes.IF_ICMPLE;
1023: break;
1024: case GT:
1025: intOp = Opcodes.IF_ICMPGT;
1026: break;
1027: }
1028: mv.visitJumpInsn(intOp, label);
1029: return;
1030: }
1031: int jumpMode = mode;
1032: switch (mode) {
1033: case GE:
1034: jumpMode = LT;
1035: break;
1036: case LE:
1037: jumpMode = GT;
1038: break;
1039: }
1040: mv.visitJumpInsn(jumpMode, label);
1041: }
1042:
1043: /**
1044: * Generates the instructions to jump to a label based on the comparison of
1045: * the top two integer stack values.
1046: *
1047: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
1048: * LE.
1049: * @param label where to jump if the comparison result is <tt>true</tt>.
1050: */
1051: public void ifICmp(final int mode, final Label label) {
1052: ifCmp(Type.INT_TYPE, mode, label);
1053: }
1054:
1055: /**
1056: * Generates the instructions to jump to a label based on the comparison of
1057: * the top integer stack value with zero.
1058: *
1059: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
1060: * LE.
1061: * @param label where to jump if the comparison result is <tt>true</tt>.
1062: */
1063: public void ifZCmp(final int mode, final Label label) {
1064: mv.visitJumpInsn(mode, label);
1065: }
1066:
1067: /**
1068: * Generates the instruction to jump to the given label if the top stack
1069: * value is null.
1070: *
1071: * @param label where to jump if the condition is <tt>true</tt>.
1072: */
1073: public void ifNull(final Label label) {
1074: mv.visitJumpInsn(Opcodes.IFNULL, label);
1075: }
1076:
1077: /**
1078: * Generates the instruction to jump to the given label if the top stack
1079: * value is not null.
1080: *
1081: * @param label where to jump if the condition is <tt>true</tt>.
1082: */
1083: public void ifNonNull(final Label label) {
1084: mv.visitJumpInsn(Opcodes.IFNONNULL, label);
1085: }
1086:
1087: /**
1088: * Generates the instruction to jump to the given label.
1089: *
1090: * @param label where to jump if the condition is <tt>true</tt>.
1091: */
1092: public void goTo(final Label label) {
1093: mv.visitJumpInsn(Opcodes.GOTO, label);
1094: }
1095:
1096: /**
1097: * Generates a RET instruction.
1098: *
1099: * @param local a local variable identifier, as returned by
1100: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
1101: */
1102: public void ret(final int local) {
1103: mv.visitVarInsn(Opcodes.RET, local);
1104: }
1105:
1106: /**
1107: * Generates the instructions for a switch statement.
1108: *
1109: * @param keys the switch case keys.
1110: * @param generator a generator to generate the code for the switch cases.
1111: */
1112: public void tableSwitch(final int[] keys,
1113: final TableSwitchGenerator generator) {
1114: float density;
1115: if (keys.length == 0) {
1116: density = 0;
1117: } else {
1118: density = (float) keys.length
1119: / (keys[keys.length - 1] - keys[0] + 1);
1120: }
1121: tableSwitch(keys, generator, density >= 0.5f);
1122: }
1123:
1124: /**
1125: * Generates the instructions for a switch statement.
1126: *
1127: * @param keys the switch case keys.
1128: * @param generator a generator to generate the code for the switch cases.
1129: * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
1130: * <tt>false</tt> to use a LOOKUPSWITCH instruction.
1131: */
1132: public void tableSwitch(final int[] keys,
1133: final TableSwitchGenerator generator, final boolean useTable) {
1134: for (int i = 1; i < keys.length; ++i) {
1135: if (keys[i] < keys[i - 1]) {
1136: throw new IllegalArgumentException(
1137: "keys must be sorted ascending");
1138: }
1139: }
1140: Label def = newLabel();
1141: Label end = newLabel();
1142: if (keys.length > 0) {
1143: int len = keys.length;
1144: int min = keys[0];
1145: int max = keys[len - 1];
1146: int range = max - min + 1;
1147: if (useTable) {
1148: Label[] labels = new Label[range];
1149: Arrays.fill(labels, def);
1150: for (int i = 0; i < len; ++i) {
1151: labels[keys[i] - min] = newLabel();
1152: }
1153: mv.visitTableSwitchInsn(min, max, def, labels);
1154: for (int i = 0; i < range; ++i) {
1155: Label label = labels[i];
1156: if (label != def) {
1157: mark(label);
1158: generator.generateCase(i + min, end);
1159: }
1160: }
1161: } else {
1162: Label[] labels = new Label[len];
1163: for (int i = 0; i < len; ++i) {
1164: labels[i] = newLabel();
1165: }
1166: mv.visitLookupSwitchInsn(def, keys, labels);
1167: for (int i = 0; i < len; ++i) {
1168: mark(labels[i]);
1169: generator.generateCase(keys[i], end);
1170: }
1171: }
1172: }
1173: mark(def);
1174: generator.generateDefault();
1175: mark(end);
1176: }
1177:
1178: /**
1179: * Generates the instruction to return the top stack value to the caller.
1180: */
1181: public void returnValue() {
1182: mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1183: }
1184:
1185: // ------------------------------------------------------------------------
1186: // Instructions to load and store fields
1187: // ------------------------------------------------------------------------
1188:
1189: /**
1190: * Generates a get field or set field instruction.
1191: *
1192: * @param opcode the instruction's opcode.
1193: * @param ownerType the class in which the field is defined.
1194: * @param name the name of the field.
1195: * @param fieldType the type of the field.
1196: */
1197: private void fieldInsn(final int opcode, final Type ownerType,
1198: final String name, final Type fieldType) {
1199: mv.visitFieldInsn(opcode, ownerType.getInternalName(), name,
1200: fieldType.getDescriptor());
1201: }
1202:
1203: /**
1204: * Generates the instruction to push the value of a static field on the
1205: * stack.
1206: *
1207: * @param owner the class in which the field is defined.
1208: * @param name the name of the field.
1209: * @param type the type of the field.
1210: */
1211: public void getStatic(final Type owner, final String name,
1212: final Type type) {
1213: fieldInsn(Opcodes.GETSTATIC, owner, name, type);
1214: }
1215:
1216: /**
1217: * Generates the instruction to store the top stack value in a static field.
1218: *
1219: * @param owner the class in which the field is defined.
1220: * @param name the name of the field.
1221: * @param type the type of the field.
1222: */
1223: public void putStatic(final Type owner, final String name,
1224: final Type type) {
1225: fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
1226: }
1227:
1228: /**
1229: * Generates the instruction to push the value of a non static field on the
1230: * stack.
1231: *
1232: * @param owner the class in which the field is defined.
1233: * @param name the name of the field.
1234: * @param type the type of the field.
1235: */
1236: public void getField(final Type owner, final String name,
1237: final Type type) {
1238: fieldInsn(Opcodes.GETFIELD, owner, name, type);
1239: }
1240:
1241: /**
1242: * Generates the instruction to store the top stack value in a non static
1243: * field.
1244: *
1245: * @param owner the class in which the field is defined.
1246: * @param name the name of the field.
1247: * @param type the type of the field.
1248: */
1249: public void putField(final Type owner, final String name,
1250: final Type type) {
1251: fieldInsn(Opcodes.PUTFIELD, owner, name, type);
1252: }
1253:
1254: // ------------------------------------------------------------------------
1255: // Instructions to invoke methods
1256: // ------------------------------------------------------------------------
1257:
1258: /**
1259: * Generates an invoke method instruction.
1260: *
1261: * @param opcode the instruction's opcode.
1262: * @param type the class in which the method is defined.
1263: * @param method the method to be invoked.
1264: */
1265: private void invokeInsn(final int opcode, final Type type,
1266: final Method method) {
1267: String owner = type.getSort() == Type.ARRAY ? type
1268: .getDescriptor() : type.getInternalName();
1269: mv.visitMethodInsn(opcode, owner, method.getName(), method
1270: .getDescriptor());
1271: }
1272:
1273: /**
1274: * Generates the instruction to invoke a normal method.
1275: *
1276: * @param owner the class in which the method is defined.
1277: * @param method the method to be invoked.
1278: */
1279: public void invokeVirtual(final Type owner, final Method method) {
1280: invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
1281: }
1282:
1283: /**
1284: * Generates the instruction to invoke a constructor.
1285: *
1286: * @param type the class in which the constructor is defined.
1287: * @param method the constructor to be invoked.
1288: */
1289: public void invokeConstructor(final Type type, final Method method) {
1290: invokeInsn(Opcodes.INVOKESPECIAL, type, method);
1291: }
1292:
1293: /**
1294: * Generates the instruction to invoke a static method.
1295: *
1296: * @param owner the class in which the method is defined.
1297: * @param method the method to be invoked.
1298: */
1299: public void invokeStatic(final Type owner, final Method method) {
1300: invokeInsn(Opcodes.INVOKESTATIC, owner, method);
1301: }
1302:
1303: /**
1304: * Generates the instruction to invoke an interface method.
1305: *
1306: * @param owner the class in which the method is defined.
1307: * @param method the method to be invoked.
1308: */
1309: public void invokeInterface(final Type owner, final Method method) {
1310: invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
1311: }
1312:
1313: // ------------------------------------------------------------------------
1314: // Instructions to create objects and arrays
1315: // ------------------------------------------------------------------------
1316:
1317: /**
1318: * Generates a type dependent instruction.
1319: *
1320: * @param opcode the instruction's opcode.
1321: * @param type the instruction's operand.
1322: */
1323: private void typeInsn(final int opcode, final Type type) {
1324: mv.visitTypeInsn(opcode, type.getInternalName());
1325: }
1326:
1327: /**
1328: * Generates the instruction to create a new object.
1329: *
1330: * @param type the class of the object to be created.
1331: */
1332: public void newInstance(final Type type) {
1333: typeInsn(Opcodes.NEW, type);
1334: }
1335:
1336: /**
1337: * Generates the instruction to create a new array.
1338: *
1339: * @param type the type of the array elements.
1340: */
1341: public void newArray(final Type type) {
1342: int typ;
1343: switch (type.getSort()) {
1344: case Type.BOOLEAN:
1345: typ = Opcodes.T_BOOLEAN;
1346: break;
1347: case Type.CHAR:
1348: typ = Opcodes.T_CHAR;
1349: break;
1350: case Type.BYTE:
1351: typ = Opcodes.T_BYTE;
1352: break;
1353: case Type.SHORT:
1354: typ = Opcodes.T_SHORT;
1355: break;
1356: case Type.INT:
1357: typ = Opcodes.T_INT;
1358: break;
1359: case Type.FLOAT:
1360: typ = Opcodes.T_FLOAT;
1361: break;
1362: case Type.LONG:
1363: typ = Opcodes.T_LONG;
1364: break;
1365: case Type.DOUBLE:
1366: typ = Opcodes.T_DOUBLE;
1367: break;
1368: default:
1369: typeInsn(Opcodes.ANEWARRAY, type);
1370: return;
1371: }
1372: mv.visitIntInsn(Opcodes.NEWARRAY, typ);
1373: }
1374:
1375: // ------------------------------------------------------------------------
1376: // Miscelaneous instructions
1377: // ------------------------------------------------------------------------
1378:
1379: /**
1380: * Generates the instruction to compute the length of an array.
1381: */
1382: public void arrayLength() {
1383: mv.visitInsn(Opcodes.ARRAYLENGTH);
1384: }
1385:
1386: /**
1387: * Generates the instruction to throw an exception.
1388: */
1389: public void throwException() {
1390: mv.visitInsn(Opcodes.ATHROW);
1391: }
1392:
1393: /**
1394: * Generates the instructions to create and throw an exception. The
1395: * exception class must have a constructor with a single String argument.
1396: *
1397: * @param type the class of the exception to be thrown.
1398: * @param msg the detailed message of the exception.
1399: */
1400: public void throwException(final Type type, final String msg) {
1401: newInstance(type);
1402: dup();
1403: push(msg);
1404: invokeConstructor(type, Method
1405: .getMethod("void <init> (String)"));
1406: throwException();
1407: }
1408:
1409: /**
1410: * Generates the instruction to check that the top stack value is of the
1411: * given type.
1412: *
1413: * @param type a class or interface type.
1414: */
1415: public void checkCast(final Type type) {
1416: if (!type.equals(OBJECT_TYPE)) {
1417: typeInsn(Opcodes.CHECKCAST, type);
1418: }
1419: }
1420:
1421: /**
1422: * Generates the instruction to test if the top stack value is of the given
1423: * type.
1424: *
1425: * @param type a class or interface type.
1426: */
1427: public void instanceOf(final Type type) {
1428: typeInsn(Opcodes.INSTANCEOF, type);
1429: }
1430:
1431: /**
1432: * Generates the instruction to get the monitor of the top stack value.
1433: */
1434: public void monitorEnter() {
1435: mv.visitInsn(Opcodes.MONITORENTER);
1436: }
1437:
1438: /**
1439: * Generates the instruction to release the monitor of the top stack value.
1440: */
1441: public void monitorExit() {
1442: mv.visitInsn(Opcodes.MONITOREXIT);
1443: }
1444:
1445: // ------------------------------------------------------------------------
1446: // Non instructions
1447: // ------------------------------------------------------------------------
1448:
1449: /**
1450: * Marks the end of the visited method.
1451: */
1452: public void endMethod() {
1453: if ((access & Opcodes.ACC_ABSTRACT) == 0) {
1454: mv.visitMaxs(0, 0);
1455: }
1456: mv.visitEnd();
1457: }
1458:
1459: /**
1460: * Marks the start of an exception handler.
1461: *
1462: * @param start beginning of the exception handler's scope (inclusive).
1463: * @param end end of the exception handler's scope (exclusive).
1464: * @param exception internal name of the type of exceptions handled by the
1465: * handler.
1466: */
1467: public void catchException(final Label start, final Label end,
1468: final Type exception) {
1469: mv.visitTryCatchBlock(start, end, mark(), exception
1470: .getInternalName());
1471: }
1472: }
|