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.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 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<Type> localTypes = new ArrayList<Type>();
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 localTypes.get(local - firstLocal);
0528: }
0529:
0530: @Override
0531: protected void setLocalType(final int local, final Type type) {
0532: int index = local - firstLocal;
0533: while (localTypes.size() < index + 1) {
0534: localTypes.add(null);
0535: }
0536: localTypes.set(index, type);
0537: }
0538:
0539: /**
0540: * Generates the instruction to load the given local variable on the stack.
0541: *
0542: * @param local a local variable identifier, as returned by
0543: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0544: */
0545: public void loadLocal(final int local) {
0546: loadInsn(getLocalType(local), local);
0547: }
0548:
0549: /**
0550: * Generates the instruction to load the given local variable on the stack.
0551: *
0552: * @param local a local variable identifier, as returned by
0553: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0554: * @param type the type of this local variable.
0555: */
0556: public void loadLocal(final int local, final Type type) {
0557: setLocalType(local, type);
0558: loadInsn(type, local);
0559: }
0560:
0561: /**
0562: * Generates the instruction to store the top stack value in the given local
0563: * variable.
0564: *
0565: * @param local a local variable identifier, as returned by
0566: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0567: */
0568: public void storeLocal(final int local) {
0569: storeInsn(getLocalType(local), local);
0570: }
0571:
0572: /**
0573: * Generates the instruction to store the top stack value in the given local
0574: * variable.
0575: *
0576: * @param local a local variable identifier, as returned by
0577: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
0578: * @param type the type of this local variable.
0579: */
0580: public void storeLocal(final int local, final Type type) {
0581: setLocalType(local, type);
0582: storeInsn(type, local);
0583: }
0584:
0585: /**
0586: * Generates the instruction to load an element from an array.
0587: *
0588: * @param type the type of the array element to be loaded.
0589: */
0590: public void arrayLoad(final Type type) {
0591: mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
0592: }
0593:
0594: /**
0595: * Generates the instruction to store an element in an array.
0596: *
0597: * @param type the type of the array element to be stored.
0598: */
0599: public void arrayStore(final Type type) {
0600: mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
0601: }
0602:
0603: // ------------------------------------------------------------------------
0604: // Instructions to manage the stack
0605: // ------------------------------------------------------------------------
0606:
0607: /**
0608: * Generates a POP instruction.
0609: */
0610: public void pop() {
0611: mv.visitInsn(Opcodes.POP);
0612: }
0613:
0614: /**
0615: * Generates a POP2 instruction.
0616: */
0617: public void pop2() {
0618: mv.visitInsn(Opcodes.POP2);
0619: }
0620:
0621: /**
0622: * Generates a DUP instruction.
0623: */
0624: public void dup() {
0625: mv.visitInsn(Opcodes.DUP);
0626: }
0627:
0628: /**
0629: * Generates a DUP2 instruction.
0630: */
0631: public void dup2() {
0632: mv.visitInsn(Opcodes.DUP2);
0633: }
0634:
0635: /**
0636: * Generates a DUP_X1 instruction.
0637: */
0638: public void dupX1() {
0639: mv.visitInsn(Opcodes.DUP_X1);
0640: }
0641:
0642: /**
0643: * Generates a DUP_X2 instruction.
0644: */
0645: public void dupX2() {
0646: mv.visitInsn(Opcodes.DUP_X2);
0647: }
0648:
0649: /**
0650: * Generates a DUP2_X1 instruction.
0651: */
0652: public void dup2X1() {
0653: mv.visitInsn(Opcodes.DUP2_X1);
0654: }
0655:
0656: /**
0657: * Generates a DUP2_X2 instruction.
0658: */
0659: public void dup2X2() {
0660: mv.visitInsn(Opcodes.DUP2_X2);
0661: }
0662:
0663: /**
0664: * Generates a SWAP instruction.
0665: */
0666: public void swap() {
0667: mv.visitInsn(Opcodes.SWAP);
0668: }
0669:
0670: /**
0671: * Generates the instructions to swap the top two stack values.
0672: *
0673: * @param prev type of the top - 1 stack value.
0674: * @param type type of the top stack value.
0675: */
0676: public void swap(final Type prev, final Type type) {
0677: if (type.getSize() == 1) {
0678: if (prev.getSize() == 1) {
0679: swap(); // same as dupX1(), pop();
0680: } else {
0681: dupX2();
0682: pop();
0683: }
0684: } else {
0685: if (prev.getSize() == 1) {
0686: dup2X1();
0687: pop2();
0688: } else {
0689: dup2X2();
0690: pop2();
0691: }
0692: }
0693: }
0694:
0695: // ------------------------------------------------------------------------
0696: // Instructions to do mathematical and logical operations
0697: // ------------------------------------------------------------------------
0698:
0699: /**
0700: * Generates the instruction to do the specified mathematical or logical
0701: * operation.
0702: *
0703: * @param op a mathematical or logical operation. Must be one of ADD, SUB,
0704: * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
0705: * @param type the type of the operand(s) for this operation.
0706: */
0707: public void math(final int op, final Type type) {
0708: mv.visitInsn(type.getOpcode(op));
0709: }
0710:
0711: /**
0712: * Generates the instructions to compute the bitwise negation of the top
0713: * stack value.
0714: */
0715: public void not() {
0716: mv.visitInsn(Opcodes.ICONST_1);
0717: mv.visitInsn(Opcodes.IXOR);
0718: }
0719:
0720: /**
0721: * Generates the instruction to increment the given local variable.
0722: *
0723: * @param local the local variable to be incremented.
0724: * @param amount the amount by which the local variable must be incremented.
0725: */
0726: public void iinc(final int local, final int amount) {
0727: mv.visitIincInsn(local, amount);
0728: }
0729:
0730: /**
0731: * Generates the instructions to cast a numerical value from one type to
0732: * another.
0733: *
0734: * @param from the type of the top stack value
0735: * @param to the type into which this value must be cast.
0736: */
0737: public void cast(final Type from, final Type to) {
0738: if (from != to) {
0739: if (from == Type.DOUBLE_TYPE) {
0740: if (to == Type.FLOAT_TYPE) {
0741: mv.visitInsn(Opcodes.D2F);
0742: } else if (to == Type.LONG_TYPE) {
0743: mv.visitInsn(Opcodes.D2L);
0744: } else {
0745: mv.visitInsn(Opcodes.D2I);
0746: cast(Type.INT_TYPE, to);
0747: }
0748: } else if (from == Type.FLOAT_TYPE) {
0749: if (to == Type.DOUBLE_TYPE) {
0750: mv.visitInsn(Opcodes.F2D);
0751: } else if (to == Type.LONG_TYPE) {
0752: mv.visitInsn(Opcodes.F2L);
0753: } else {
0754: mv.visitInsn(Opcodes.F2I);
0755: cast(Type.INT_TYPE, to);
0756: }
0757: } else if (from == Type.LONG_TYPE) {
0758: if (to == Type.DOUBLE_TYPE) {
0759: mv.visitInsn(Opcodes.L2D);
0760: } else if (to == Type.FLOAT_TYPE) {
0761: mv.visitInsn(Opcodes.L2F);
0762: } else {
0763: mv.visitInsn(Opcodes.L2I);
0764: cast(Type.INT_TYPE, to);
0765: }
0766: } else {
0767: if (to == Type.BYTE_TYPE) {
0768: mv.visitInsn(Opcodes.I2B);
0769: } else if (to == Type.CHAR_TYPE) {
0770: mv.visitInsn(Opcodes.I2C);
0771: } else if (to == Type.DOUBLE_TYPE) {
0772: mv.visitInsn(Opcodes.I2D);
0773: } else if (to == Type.FLOAT_TYPE) {
0774: mv.visitInsn(Opcodes.I2F);
0775: } else if (to == Type.LONG_TYPE) {
0776: mv.visitInsn(Opcodes.I2L);
0777: } else if (to == Type.SHORT_TYPE) {
0778: mv.visitInsn(Opcodes.I2S);
0779: }
0780: }
0781: }
0782: }
0783:
0784: // ------------------------------------------------------------------------
0785: // Instructions to do boxing and unboxing operations
0786: // ------------------------------------------------------------------------
0787:
0788: /**
0789: * Generates the instructions to box the top stack value. This value is
0790: * replaced by its boxed equivalent on top of the stack.
0791: *
0792: * @param type the type of the top stack value.
0793: */
0794: public void box(final Type type) {
0795: if (type.getSort() == Type.OBJECT
0796: || type.getSort() == Type.ARRAY) {
0797: return;
0798: }
0799: if (type == Type.VOID_TYPE) {
0800: push((String) null);
0801: } else {
0802: Type boxed = type;
0803: switch (type.getSort()) {
0804: case Type.BYTE:
0805: boxed = BYTE_TYPE;
0806: break;
0807: case Type.BOOLEAN:
0808: boxed = BOOLEAN_TYPE;
0809: break;
0810: case Type.SHORT:
0811: boxed = SHORT_TYPE;
0812: break;
0813: case Type.CHAR:
0814: boxed = CHARACTER_TYPE;
0815: break;
0816: case Type.INT:
0817: boxed = INTEGER_TYPE;
0818: break;
0819: case Type.FLOAT:
0820: boxed = FLOAT_TYPE;
0821: break;
0822: case Type.LONG:
0823: boxed = LONG_TYPE;
0824: break;
0825: case Type.DOUBLE:
0826: boxed = DOUBLE_TYPE;
0827: break;
0828: }
0829: newInstance(boxed);
0830: if (type.getSize() == 2) {
0831: // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
0832: dupX2();
0833: dupX2();
0834: pop();
0835: } else {
0836: // p -> po -> opo -> oop -> o
0837: dupX1();
0838: swap();
0839: }
0840: invokeConstructor(boxed, new Method("<init>",
0841: Type.VOID_TYPE, new Type[] { type }));
0842: }
0843: }
0844:
0845: /**
0846: * Generates the instructions to unbox the top stack value. This value is
0847: * replaced by its unboxed equivalent on top of the stack.
0848: *
0849: * @param type the type of the top stack value.
0850: */
0851: public void unbox(final Type type) {
0852: Type t = NUMBER_TYPE;
0853: Method sig = null;
0854: switch (type.getSort()) {
0855: case Type.VOID:
0856: return;
0857: case Type.CHAR:
0858: t = CHARACTER_TYPE;
0859: sig = CHAR_VALUE;
0860: break;
0861: case Type.BOOLEAN:
0862: t = BOOLEAN_TYPE;
0863: sig = BOOLEAN_VALUE;
0864: break;
0865: case Type.DOUBLE:
0866: sig = DOUBLE_VALUE;
0867: break;
0868: case Type.FLOAT:
0869: sig = FLOAT_VALUE;
0870: break;
0871: case Type.LONG:
0872: sig = LONG_VALUE;
0873: break;
0874: case Type.INT:
0875: case Type.SHORT:
0876: case Type.BYTE:
0877: sig = INT_VALUE;
0878: }
0879: if (sig == null) {
0880: checkCast(type);
0881: } else {
0882: checkCast(t);
0883: invokeVirtual(t, sig);
0884: }
0885: }
0886:
0887: // ------------------------------------------------------------------------
0888: // Instructions to jump to other instructions
0889: // ------------------------------------------------------------------------
0890:
0891: /**
0892: * Creates a new {@link Label}.
0893: *
0894: * @return a new {@link Label}.
0895: */
0896: public Label newLabel() {
0897: return new Label();
0898: }
0899:
0900: /**
0901: * Marks the current code position with the given label.
0902: *
0903: * @param label a label.
0904: */
0905: public void mark(final Label label) {
0906: mv.visitLabel(label);
0907: }
0908:
0909: /**
0910: * Marks the current code position with a new label.
0911: *
0912: * @return the label that was created to mark the current code position.
0913: */
0914: public Label mark() {
0915: Label label = new Label();
0916: mv.visitLabel(label);
0917: return label;
0918: }
0919:
0920: /**
0921: * Generates the instructions to jump to a label based on the comparison of
0922: * the top two stack values.
0923: *
0924: * @param type the type of the top two stack values.
0925: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
0926: * LE.
0927: * @param label where to jump if the comparison result is <tt>true</tt>.
0928: */
0929: public void ifCmp(final Type type, final int mode, final Label label) {
0930: int intOp = -1;
0931: int jumpMode = mode;
0932: switch (mode) {
0933: case GE:
0934: jumpMode = LT;
0935: break;
0936: case LE:
0937: jumpMode = GT;
0938: break;
0939: }
0940: switch (type.getSort()) {
0941: case Type.LONG:
0942: mv.visitInsn(Opcodes.LCMP);
0943: break;
0944: case Type.DOUBLE:
0945: mv.visitInsn(Opcodes.DCMPG);
0946: break;
0947: case Type.FLOAT:
0948: mv.visitInsn(Opcodes.FCMPG);
0949: break;
0950: case Type.ARRAY:
0951: case Type.OBJECT:
0952: switch (mode) {
0953: case EQ:
0954: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
0955: return;
0956: case NE:
0957: mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
0958: return;
0959: }
0960: throw new IllegalArgumentException(
0961: "Bad comparison for type " + type);
0962: default:
0963: switch (mode) {
0964: case EQ:
0965: intOp = Opcodes.IF_ICMPEQ;
0966: break;
0967: case NE:
0968: intOp = Opcodes.IF_ICMPNE;
0969: break;
0970: case GE:
0971: intOp = Opcodes.IF_ICMPGE;
0972: break;
0973: case LT:
0974: intOp = Opcodes.IF_ICMPLT;
0975: break;
0976: case LE:
0977: intOp = Opcodes.IF_ICMPLE;
0978: break;
0979: case GT:
0980: intOp = Opcodes.IF_ICMPGT;
0981: break;
0982: }
0983: mv.visitJumpInsn(intOp, label);
0984: return;
0985: }
0986: mv.visitJumpInsn(jumpMode, label);
0987: }
0988:
0989: /**
0990: * Generates the instructions to jump to a label based on the comparison of
0991: * the top two integer stack values.
0992: *
0993: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
0994: * LE.
0995: * @param label where to jump if the comparison result is <tt>true</tt>.
0996: */
0997: public void ifICmp(final int mode, final Label label) {
0998: ifCmp(Type.INT_TYPE, mode, label);
0999: }
1000:
1001: /**
1002: * Generates the instructions to jump to a label based on the comparison of
1003: * the top integer stack value with zero.
1004: *
1005: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
1006: * LE.
1007: * @param label where to jump if the comparison result is <tt>true</tt>.
1008: */
1009: public void ifZCmp(final int mode, final Label label) {
1010: mv.visitJumpInsn(mode, label);
1011: }
1012:
1013: /**
1014: * Generates the instruction to jump to the given label if the top stack
1015: * value is null.
1016: *
1017: * @param label where to jump if the condition is <tt>true</tt>.
1018: */
1019: public void ifNull(final Label label) {
1020: mv.visitJumpInsn(Opcodes.IFNULL, label);
1021: }
1022:
1023: /**
1024: * Generates the instruction to jump to the given label if the top stack
1025: * value is not null.
1026: *
1027: * @param label where to jump if the condition is <tt>true</tt>.
1028: */
1029: public void ifNonNull(final Label label) {
1030: mv.visitJumpInsn(Opcodes.IFNONNULL, label);
1031: }
1032:
1033: /**
1034: * Generates the instruction to jump to the given label.
1035: *
1036: * @param label where to jump if the condition is <tt>true</tt>.
1037: */
1038: public void goTo(final Label label) {
1039: mv.visitJumpInsn(Opcodes.GOTO, label);
1040: }
1041:
1042: /**
1043: * Generates a RET instruction.
1044: *
1045: * @param local a local variable identifier, as returned by
1046: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
1047: */
1048: public void ret(final int local) {
1049: mv.visitVarInsn(Opcodes.RET, local);
1050: }
1051:
1052: /**
1053: * Generates the instructions for a switch statement.
1054: *
1055: * @param keys the switch case keys.
1056: * @param generator a generator to generate the code for the switch cases.
1057: */
1058: public void tableSwitch(final int[] keys,
1059: final TableSwitchGenerator generator) {
1060: float density;
1061: if (keys.length == 0) {
1062: density = 0;
1063: } else {
1064: density = (float) keys.length
1065: / (keys[keys.length - 1] - keys[0] + 1);
1066: }
1067: tableSwitch(keys, generator, density >= 0.5f);
1068: }
1069:
1070: /**
1071: * Generates the instructions for a switch statement.
1072: *
1073: * @param keys the switch case keys.
1074: * @param generator a generator to generate the code for the switch cases.
1075: * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
1076: * <tt>false</tt> to use a LOOKUPSWITCH instruction.
1077: */
1078: public void tableSwitch(final int[] keys,
1079: final TableSwitchGenerator generator, final boolean useTable) {
1080: for (int i = 1; i < keys.length; ++i) {
1081: if (keys[i] < keys[i - 1]) {
1082: throw new IllegalArgumentException(
1083: "keys must be sorted ascending");
1084: }
1085: }
1086: Label def = newLabel();
1087: Label end = newLabel();
1088: if (keys.length > 0) {
1089: int len = keys.length;
1090: int min = keys[0];
1091: int max = keys[len - 1];
1092: int range = max - min + 1;
1093: if (useTable) {
1094: Label[] labels = new Label[range];
1095: Arrays.fill(labels, def);
1096: for (int i = 0; i < len; ++i) {
1097: labels[keys[i] - min] = newLabel();
1098: }
1099: mv.visitTableSwitchInsn(min, max, def, labels);
1100: for (int i = 0; i < range; ++i) {
1101: Label label = labels[i];
1102: if (label != def) {
1103: mark(label);
1104: generator.generateCase(i + min, end);
1105: }
1106: }
1107: } else {
1108: Label[] labels = new Label[len];
1109: for (int i = 0; i < len; ++i) {
1110: labels[i] = newLabel();
1111: }
1112: mv.visitLookupSwitchInsn(def, keys, labels);
1113: for (int i = 0; i < len; ++i) {
1114: mark(labels[i]);
1115: generator.generateCase(keys[i], end);
1116: }
1117: }
1118: }
1119: mark(def);
1120: generator.generateDefault();
1121: mark(end);
1122: }
1123:
1124: /**
1125: * Generates the instruction to return the top stack value to the caller.
1126: */
1127: public void returnValue() {
1128: mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
1129: }
1130:
1131: // ------------------------------------------------------------------------
1132: // Instructions to load and store fields
1133: // ------------------------------------------------------------------------
1134:
1135: /**
1136: * Generates a get field or set field instruction.
1137: *
1138: * @param opcode the instruction's opcode.
1139: * @param ownerType the class in which the field is defined.
1140: * @param name the name of the field.
1141: * @param fieldType the type of the field.
1142: */
1143: private void fieldInsn(final int opcode, final Type ownerType,
1144: final String name, final Type fieldType) {
1145: mv.visitFieldInsn(opcode, ownerType.getInternalName(), name,
1146: fieldType.getDescriptor());
1147: }
1148:
1149: /**
1150: * Generates the instruction to push the value of a static field on the
1151: * stack.
1152: *
1153: * @param owner the class in which the field is defined.
1154: * @param name the name of the field.
1155: * @param type the type of the field.
1156: */
1157: public void getStatic(final Type owner, final String name,
1158: final Type type) {
1159: fieldInsn(Opcodes.GETSTATIC, owner, name, type);
1160: }
1161:
1162: /**
1163: * Generates the instruction to store the top stack value in a static field.
1164: *
1165: * @param owner the class in which the field is defined.
1166: * @param name the name of the field.
1167: * @param type the type of the field.
1168: */
1169: public void putStatic(final Type owner, final String name,
1170: final Type type) {
1171: fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
1172: }
1173:
1174: /**
1175: * Generates the instruction to push the value of a non static field on the
1176: * stack.
1177: *
1178: * @param owner the class in which the field is defined.
1179: * @param name the name of the field.
1180: * @param type the type of the field.
1181: */
1182: public void getField(final Type owner, final String name,
1183: final Type type) {
1184: fieldInsn(Opcodes.GETFIELD, owner, name, type);
1185: }
1186:
1187: /**
1188: * Generates the instruction to store the top stack value in a non static
1189: * field.
1190: *
1191: * @param owner the class in which the field is defined.
1192: * @param name the name of the field.
1193: * @param type the type of the field.
1194: */
1195: public void putField(final Type owner, final String name,
1196: final Type type) {
1197: fieldInsn(Opcodes.PUTFIELD, owner, name, type);
1198: }
1199:
1200: // ------------------------------------------------------------------------
1201: // Instructions to invoke methods
1202: // ------------------------------------------------------------------------
1203:
1204: /**
1205: * Generates an invoke method instruction.
1206: *
1207: * @param opcode the instruction's opcode.
1208: * @param type the class in which the method is defined.
1209: * @param method the method to be invoked.
1210: */
1211: private void invokeInsn(final int opcode, final Type type,
1212: final Method method) {
1213: String owner = type.getSort() == Type.ARRAY ? type
1214: .getDescriptor() : type.getInternalName();
1215: mv.visitMethodInsn(opcode, owner, method.getName(), method
1216: .getDescriptor());
1217: }
1218:
1219: /**
1220: * Generates the instruction to invoke a normal method.
1221: *
1222: * @param owner the class in which the method is defined.
1223: * @param method the method to be invoked.
1224: */
1225: public void invokeVirtual(final Type owner, final Method method) {
1226: invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
1227: }
1228:
1229: /**
1230: * Generates the instruction to invoke a constructor.
1231: *
1232: * @param type the class in which the constructor is defined.
1233: * @param method the constructor to be invoked.
1234: */
1235: public void invokeConstructor(final Type type, final Method method) {
1236: invokeInsn(Opcodes.INVOKESPECIAL, type, method);
1237: }
1238:
1239: /**
1240: * Generates the instruction to invoke a static method.
1241: *
1242: * @param owner the class in which the method is defined.
1243: * @param method the method to be invoked.
1244: */
1245: public void invokeStatic(final Type owner, final Method method) {
1246: invokeInsn(Opcodes.INVOKESTATIC, owner, method);
1247: }
1248:
1249: /**
1250: * Generates the instruction to invoke an interface method.
1251: *
1252: * @param owner the class in which the method is defined.
1253: * @param method the method to be invoked.
1254: */
1255: public void invokeInterface(final Type owner, final Method method) {
1256: invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
1257: }
1258:
1259: // ------------------------------------------------------------------------
1260: // Instructions to create objects and arrays
1261: // ------------------------------------------------------------------------
1262:
1263: /**
1264: * Generates a type dependent instruction.
1265: *
1266: * @param opcode the instruction's opcode.
1267: * @param type the instruction's operand.
1268: */
1269: private void typeInsn(final int opcode, final Type type) {
1270: String desc;
1271: if (type.getSort() == Type.ARRAY) {
1272: desc = type.getDescriptor();
1273: } else {
1274: desc = type.getInternalName();
1275: }
1276: mv.visitTypeInsn(opcode, desc);
1277: }
1278:
1279: /**
1280: * Generates the instruction to create a new object.
1281: *
1282: * @param type the class of the object to be created.
1283: */
1284: public void newInstance(final Type type) {
1285: typeInsn(Opcodes.NEW, type);
1286: }
1287:
1288: /**
1289: * Generates the instruction to create a new array.
1290: *
1291: * @param type the type of the array elements.
1292: */
1293: public void newArray(final Type type) {
1294: int typ;
1295: switch (type.getSort()) {
1296: case Type.BOOLEAN:
1297: typ = Opcodes.T_BOOLEAN;
1298: break;
1299: case Type.CHAR:
1300: typ = Opcodes.T_CHAR;
1301: break;
1302: case Type.BYTE:
1303: typ = Opcodes.T_BYTE;
1304: break;
1305: case Type.SHORT:
1306: typ = Opcodes.T_SHORT;
1307: break;
1308: case Type.INT:
1309: typ = Opcodes.T_INT;
1310: break;
1311: case Type.FLOAT:
1312: typ = Opcodes.T_FLOAT;
1313: break;
1314: case Type.LONG:
1315: typ = Opcodes.T_LONG;
1316: break;
1317: case Type.DOUBLE:
1318: typ = Opcodes.T_DOUBLE;
1319: break;
1320: default:
1321: typeInsn(Opcodes.ANEWARRAY, type);
1322: return;
1323: }
1324: mv.visitIntInsn(Opcodes.NEWARRAY, typ);
1325: }
1326:
1327: // ------------------------------------------------------------------------
1328: // Miscelaneous instructions
1329: // ------------------------------------------------------------------------
1330:
1331: /**
1332: * Generates the instruction to compute the length of an array.
1333: */
1334: public void arrayLength() {
1335: mv.visitInsn(Opcodes.ARRAYLENGTH);
1336: }
1337:
1338: /**
1339: * Generates the instruction to throw an exception.
1340: */
1341: public void throwException() {
1342: mv.visitInsn(Opcodes.ATHROW);
1343: }
1344:
1345: /**
1346: * Generates the instructions to create and throw an exception. The
1347: * exception class must have a constructor with a single String argument.
1348: *
1349: * @param type the class of the exception to be thrown.
1350: * @param msg the detailed message of the exception.
1351: */
1352: public void throwException(final Type type, final String msg) {
1353: newInstance(type);
1354: dup();
1355: push(msg);
1356: invokeConstructor(type, Method
1357: .getMethod("void <init> (String)"));
1358: throwException();
1359: }
1360:
1361: /**
1362: * Generates the instruction to check that the top stack value is of the
1363: * given type.
1364: *
1365: * @param type a class or interface type.
1366: */
1367: public void checkCast(final Type type) {
1368: if (!type.equals(OBJECT_TYPE)) {
1369: typeInsn(Opcodes.CHECKCAST, type);
1370: }
1371: }
1372:
1373: /**
1374: * Generates the instruction to test if the top stack value is of the given
1375: * type.
1376: *
1377: * @param type a class or interface type.
1378: */
1379: public void instanceOf(final Type type) {
1380: typeInsn(Opcodes.INSTANCEOF, type);
1381: }
1382:
1383: /**
1384: * Generates the instruction to get the monitor of the top stack value.
1385: */
1386: public void monitorEnter() {
1387: mv.visitInsn(Opcodes.MONITORENTER);
1388: }
1389:
1390: /**
1391: * Generates the instruction to release the monitor of the top stack value.
1392: */
1393: public void monitorExit() {
1394: mv.visitInsn(Opcodes.MONITOREXIT);
1395: }
1396:
1397: // ------------------------------------------------------------------------
1398: // Non instructions
1399: // ------------------------------------------------------------------------
1400:
1401: /**
1402: * Marks the end of the visited method.
1403: */
1404: public void endMethod() {
1405: if ((access & Opcodes.ACC_ABSTRACT) == 0) {
1406: mv.visitMaxs(0, 0);
1407: }
1408: mv.visitEnd();
1409: }
1410:
1411: /**
1412: * Marks the start of an exception handler.
1413: *
1414: * @param start beginning of the exception handler's scope (inclusive).
1415: * @param end end of the exception handler's scope (exclusive).
1416: * @param exception internal name of the type of exceptions handled by the
1417: * handler.
1418: */
1419: public void catchException(final Label start, final Label end,
1420: final Type exception) {
1421: mv.visitTryCatchBlock(start, end, mark(), exception
1422: .getInternalName());
1423: }
1424: }
|