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