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