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