rlm@10: /*** rlm@10: * ASM: a very small and fast Java bytecode manipulation framework rlm@10: * Copyright (c) 2000-2005 INRIA, France Telecom rlm@10: * All rights reserved. rlm@10: * rlm@10: * Redistribution and use in source and binary forms, with or without rlm@10: * modification, are permitted provided that the following conditions rlm@10: * are met: rlm@10: * 1. Redistributions of source code must retain the above copyright rlm@10: * notice, this list of conditions and the following disclaimer. rlm@10: * 2. Redistributions in binary form must reproduce the above copyright rlm@10: * notice, this list of conditions and the following disclaimer in the rlm@10: * documentation and/or other materials provided with the distribution. rlm@10: * 3. Neither the name of the copyright holders nor the names of its rlm@10: * contributors may be used to endorse or promote products derived from rlm@10: * this software without specific prior written permission. rlm@10: * rlm@10: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" rlm@10: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE rlm@10: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE rlm@10: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE rlm@10: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR rlm@10: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF rlm@10: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS rlm@10: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN rlm@10: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) rlm@10: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF rlm@10: * THE POSSIBILITY OF SUCH DAMAGE. rlm@10: */ rlm@10: package clojure.asm.commons; rlm@10: rlm@10: import java.util.ArrayList; rlm@10: import java.util.Arrays; rlm@10: import java.util.List; rlm@10: rlm@10: import clojure.asm.ClassVisitor; rlm@10: import clojure.asm.Label; rlm@10: import clojure.asm.MethodVisitor; rlm@10: import clojure.asm.Opcodes; rlm@10: import clojure.asm.Type; rlm@10: rlm@10: /** rlm@10: * A {@link clojure.asm.MethodAdapter} with convenient methods to generate rlm@10: * code. For example, using this adapter, the class below rlm@10: *
rlm@10: *rlm@10: * public class Example { rlm@10: * public static void main(String[] args) { rlm@10: * System.out.println("Hello world!"); rlm@10: * } rlm@10: * } rlm@10: *rlm@10: * rlm@10: * can be generated as follows: rlm@10: * rlm@10: *
rlm@10: * ClassWriter cw = new ClassWriter(true); rlm@10: * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); rlm@10: * rlm@10: * Method m = Method.getMethod("void <init> ()"); rlm@10: * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); rlm@10: * mg.loadThis(); rlm@10: * mg.invokeConstructor(Type.getType(Object.class), m); rlm@10: * mg.returnValue(); rlm@10: * mg.endMethod(); rlm@10: * rlm@10: * m = Method.getMethod("void main (String[])"); rlm@10: * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); rlm@10: * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); rlm@10: * mg.push("Hello world!"); rlm@10: * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); rlm@10: * mg.returnValue(); rlm@10: * mg.endMethod(); rlm@10: * rlm@10: * cw.visitEnd(); rlm@10: *rlm@10: * rlm@10: * @author Juozas Baliuka rlm@10: * @author Chris Nokleberg rlm@10: * @author Eric Bruneton rlm@10: */ rlm@10: public class GeneratorAdapter extends LocalVariablesSorter{ rlm@10: rlm@10: private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); rlm@10: rlm@10: private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); rlm@10: rlm@10: private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); rlm@10: rlm@10: private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); rlm@10: rlm@10: private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); rlm@10: rlm@10: private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); rlm@10: rlm@10: private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long"); rlm@10: rlm@10: private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double"); rlm@10: rlm@10: private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); rlm@10: rlm@10: private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); rlm@10: rlm@10: private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); rlm@10: rlm@10: private final static Method CHAR_VALUE = Method.getMethod("char charValue()"); rlm@10: rlm@10: private final static Method INT_VALUE = Method.getMethod("int intValue()"); rlm@10: rlm@10: private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()"); rlm@10: rlm@10: private final static Method LONG_VALUE = Method.getMethod("long longValue()"); rlm@10: rlm@10: private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int ADD = Opcodes.IADD; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int SUB = Opcodes.ISUB; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int MUL = Opcodes.IMUL; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int DIV = Opcodes.IDIV; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int REM = Opcodes.IREM; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int NEG = Opcodes.INEG; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int SHL = Opcodes.ISHL; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int SHR = Opcodes.ISHR; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int USHR = Opcodes.IUSHR; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int AND = Opcodes.IAND; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int OR = Opcodes.IOR; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #math math} method. rlm@10: */ rlm@10: public final static int XOR = Opcodes.IXOR; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int EQ = Opcodes.IFEQ; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int NE = Opcodes.IFNE; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int LT = Opcodes.IFLT; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int GE = Opcodes.IFGE; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int GT = Opcodes.IFGT; rlm@10: rlm@10: /** rlm@10: * Constant for the {@link #ifCmp ifCmp} method. rlm@10: */ rlm@10: public final static int LE = Opcodes.IFLE; rlm@10: rlm@10: /** rlm@10: * Access flags of the method visited by this adapter. rlm@10: */ rlm@10: private final int access; rlm@10: rlm@10: /** rlm@10: * Return type of the method visited by this adapter. rlm@10: */ rlm@10: private final Type returnType; rlm@10: rlm@10: /** rlm@10: * Argument types of the method visited by this adapter. rlm@10: */ rlm@10: private final Type[] argumentTypes; rlm@10: rlm@10: /** rlm@10: * Types of the local variables of the method visited by this adapter. rlm@10: */ rlm@10: private final List localTypes = new ArrayList(); rlm@10: rlm@10: /** rlm@10: * Creates a new {@link GeneratorAdapter}. rlm@10: * rlm@10: * @param mv the method visitor to which this adapter delegates calls. rlm@10: * @param access the method's access flags (see {@link Opcodes}). rlm@10: * @param name the method's name. rlm@10: * @param desc the method's descriptor (see {@link Type Type}). rlm@10: */ rlm@10: public GeneratorAdapter( rlm@10: final MethodVisitor mv, rlm@10: final int access, rlm@10: final String name, rlm@10: final String desc){ rlm@10: super(access, desc, mv); rlm@10: this.access = access; rlm@10: this.returnType = Type.getReturnType(desc); rlm@10: this.argumentTypes = Type.getArgumentTypes(desc); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Creates a new {@link GeneratorAdapter}. rlm@10: * rlm@10: * @param access access flags of the adapted method. rlm@10: * @param method the adapted method. rlm@10: * @param mv the method visitor to which this adapter delegates calls. rlm@10: */ rlm@10: public GeneratorAdapter( rlm@10: final int access, rlm@10: final Method method, rlm@10: final MethodVisitor mv){ rlm@10: super(access, method.getDescriptor(), mv); rlm@10: this.access = access; rlm@10: this.returnType = method.getReturnType(); rlm@10: this.argumentTypes = method.getArgumentTypes(); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Creates a new {@link GeneratorAdapter}. rlm@10: * rlm@10: * @param access access flags of the adapted method. rlm@10: * @param method the adapted method. rlm@10: * @param signature the signature of the adapted method (may be rlm@10: * null). rlm@10: * @param exceptions the exceptions thrown by the adapted method (may be rlm@10: * null). rlm@10: * @param cv the class visitor to which this adapter delegates calls. rlm@10: */ rlm@10: public GeneratorAdapter( rlm@10: final int access, rlm@10: final Method method, rlm@10: final String signature, rlm@10: final Type[] exceptions, rlm@10: final ClassVisitor cv){ rlm@10: this(access, method, cv.visitMethod(access, rlm@10: method.getName(), rlm@10: method.getDescriptor(), rlm@10: signature, rlm@10: getInternalNames(exceptions))); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Returns the internal names of the given types. rlm@10: * rlm@10: * @param types a set of types. rlm@10: * @return the internal names of the given types. rlm@10: */ rlm@10: private static String[] getInternalNames(final Type[] types){ rlm@10: if(types == null) rlm@10: { rlm@10: return null; rlm@10: } rlm@10: String[] names = new String[types.length]; rlm@10: for(int i = 0; i < names.length; ++i) rlm@10: { rlm@10: names[i] = types[i].getInternalName(); rlm@10: } rlm@10: return names; rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to push constants on the stack rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final boolean value){ rlm@10: push(value ? 1 : 0); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final int value){ rlm@10: if(value >= -1 && value <= 5) rlm@10: { rlm@10: mv.visitInsn(Opcodes.ICONST_0 + value); rlm@10: } rlm@10: else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) rlm@10: { rlm@10: mv.visitIntInsn(Opcodes.BIPUSH, value); rlm@10: } rlm@10: else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) rlm@10: { rlm@10: mv.visitIntInsn(Opcodes.SIPUSH, value); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(new Integer(value)); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final long value){ rlm@10: if(value == 0L || value == 1L) rlm@10: { rlm@10: mv.visitInsn(Opcodes.LCONST_0 + (int) value); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(new Long(value)); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final float value){ rlm@10: int bits = Float.floatToIntBits(value); rlm@10: if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000) rlm@10: { // 0..2 rlm@10: mv.visitInsn(Opcodes.FCONST_0 + (int) value); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(new Float(value)); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final double value){ rlm@10: long bits = Double.doubleToLongBits(value); rlm@10: if(bits == 0L || bits == 0x3ff0000000000000L) rlm@10: { // +0.0d and 1.0d rlm@10: mv.visitInsn(Opcodes.DCONST_0 + (int) value); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(new Double(value)); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. May be null. rlm@10: */ rlm@10: public void push(final String value){ rlm@10: if(value == null) rlm@10: { rlm@10: mv.visitInsn(Opcodes.ACONST_NULL); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(value); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the given value on the stack. rlm@10: * rlm@10: * @param value the value to be pushed on the stack. rlm@10: */ rlm@10: public void push(final Type value){ rlm@10: if(value == null) rlm@10: { rlm@10: mv.visitInsn(Opcodes.ACONST_NULL); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitLdcInsn(value); rlm@10: } rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to load and store method arguments rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Returns the index of the given method argument in the frame's local rlm@10: * variables array. rlm@10: * rlm@10: * @param arg the index of a method argument. rlm@10: * @return the index of the given method argument in the frame's local rlm@10: * variables array. rlm@10: */ rlm@10: private int getArgIndex(final int arg){ rlm@10: int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; rlm@10: for(int i = 0; i < arg; i++) rlm@10: { rlm@10: index += argumentTypes[i].getSize(); rlm@10: } rlm@10: return index; rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push a local variable on the stack. rlm@10: * rlm@10: * @param type the type of the local variable to be loaded. rlm@10: * @param index an index in the frame's local variables array. rlm@10: */ rlm@10: private void loadInsn(final Type type, final int index){ rlm@10: mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in a local rlm@10: * variable. rlm@10: * rlm@10: * @param type the type of the local variable to be stored. rlm@10: * @param index an index in the frame's local variables array. rlm@10: */ rlm@10: private void storeInsn(final Type type, final int index){ rlm@10: mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to load 'this' on the stack. rlm@10: */ rlm@10: public void loadThis(){ rlm@10: if((access & Opcodes.ACC_STATIC) != 0) rlm@10: { rlm@10: throw new IllegalStateException("no 'this' pointer within static method"); rlm@10: } rlm@10: mv.visitVarInsn(Opcodes.ALOAD, 0); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to load the given method argument on the stack. rlm@10: * rlm@10: * @param arg the index of a method argument. rlm@10: */ rlm@10: public void loadArg(final int arg){ rlm@10: loadInsn(argumentTypes[arg], getArgIndex(arg)); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to load the given method arguments on the rlm@10: * stack. rlm@10: * rlm@10: * @param arg the index of the first method argument to be loaded. rlm@10: * @param count the number of method arguments to be loaded. rlm@10: */ rlm@10: public void loadArgs(final int arg, final int count){ rlm@10: int index = getArgIndex(arg); rlm@10: for(int i = 0; i < count; ++i) rlm@10: { rlm@10: Type t = argumentTypes[arg + i]; rlm@10: loadInsn(t, index); rlm@10: index += t.getSize(); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to load all the method arguments on the stack. rlm@10: */ rlm@10: public void loadArgs(){ rlm@10: loadArgs(0, argumentTypes.length); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to load all the method arguments on the stack, rlm@10: * as a single object array. rlm@10: */ rlm@10: public void loadArgArray(){ rlm@10: push(argumentTypes.length); rlm@10: newArray(OBJECT_TYPE); rlm@10: for(int i = 0; i < argumentTypes.length; i++) rlm@10: { rlm@10: dup(); rlm@10: push(i); rlm@10: loadArg(i); rlm@10: box(argumentTypes[i]); rlm@10: arrayStore(OBJECT_TYPE); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in the given rlm@10: * method argument. rlm@10: * rlm@10: * @param arg the index of a method argument. rlm@10: */ rlm@10: public void storeArg(final int arg){ rlm@10: storeInsn(argumentTypes[arg], getArgIndex(arg)); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to load and store local variables rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Returns the type of the given local variable. rlm@10: * rlm@10: * @param local a local variable identifier, as returned by rlm@10: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. rlm@10: * @return the type of the given local variable. rlm@10: */ rlm@10: public Type getLocalType(final int local){ rlm@10: return (Type) localTypes.get(local - firstLocal); rlm@10: } rlm@10: rlm@10: protected void setLocalType(final int local, final Type type){ rlm@10: int index = local - firstLocal; rlm@10: while(localTypes.size() < index + 1) rlm@10: { rlm@10: localTypes.add(null); rlm@10: } rlm@10: localTypes.set(index, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to load the given local variable on the stack. rlm@10: * rlm@10: * @param local a local variable identifier, as returned by rlm@10: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. rlm@10: */ rlm@10: public void loadLocal(final int local){ rlm@10: loadInsn(getLocalType(local), local); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to load the given local variable on the stack. rlm@10: * rlm@10: * @param local a local variable identifier, as returned by rlm@10: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. rlm@10: * @param type the type of this local variable. rlm@10: */ rlm@10: public void loadLocal(final int local, final Type type){ rlm@10: setLocalType(local, type); rlm@10: loadInsn(type, local); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in the given local rlm@10: * variable. rlm@10: * rlm@10: * @param local a local variable identifier, as returned by rlm@10: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. rlm@10: */ rlm@10: public void storeLocal(final int local){ rlm@10: storeInsn(getLocalType(local), local); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in the given local rlm@10: * variable. rlm@10: * rlm@10: * @param local a local variable identifier, as returned by rlm@10: * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. rlm@10: * @param type the type of this local variable. rlm@10: */ rlm@10: public void storeLocal(final int local, final Type type){ rlm@10: setLocalType(local, type); rlm@10: storeInsn(type, local); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to load an element from an array. rlm@10: * rlm@10: * @param type the type of the array element to be loaded. rlm@10: */ rlm@10: public void arrayLoad(final Type type){ rlm@10: mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store an element in an array. rlm@10: * rlm@10: * @param type the type of the array element to be stored. rlm@10: */ rlm@10: public void arrayStore(final Type type){ rlm@10: mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to manage the stack rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates a POP instruction. rlm@10: */ rlm@10: public void pop(){ rlm@10: mv.visitInsn(Opcodes.POP); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a POP2 instruction. rlm@10: */ rlm@10: public void pop2(){ rlm@10: mv.visitInsn(Opcodes.POP2); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP instruction. rlm@10: */ rlm@10: public void dup(){ rlm@10: mv.visitInsn(Opcodes.DUP); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP2 instruction. rlm@10: */ rlm@10: public void dup2(){ rlm@10: mv.visitInsn(Opcodes.DUP2); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP_X1 instruction. rlm@10: */ rlm@10: public void dupX1(){ rlm@10: mv.visitInsn(Opcodes.DUP_X1); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP_X2 instruction. rlm@10: */ rlm@10: public void dupX2(){ rlm@10: mv.visitInsn(Opcodes.DUP_X2); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP2_X1 instruction. rlm@10: */ rlm@10: public void dup2X1(){ rlm@10: mv.visitInsn(Opcodes.DUP2_X1); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a DUP2_X2 instruction. rlm@10: */ rlm@10: public void dup2X2(){ rlm@10: mv.visitInsn(Opcodes.DUP2_X2); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a SWAP instruction. rlm@10: */ rlm@10: public void swap(){ rlm@10: mv.visitInsn(Opcodes.SWAP); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to swap the top two stack values. rlm@10: * rlm@10: * @param prev type of the top - 1 stack value. rlm@10: * @param type type of the top stack value. rlm@10: */ rlm@10: public void swap(final Type prev, final Type type){ rlm@10: if(type.getSize() == 1) rlm@10: { rlm@10: if(prev.getSize() == 1) rlm@10: { rlm@10: swap(); // same as dupX1(), pop(); rlm@10: } rlm@10: else rlm@10: { rlm@10: dupX2(); rlm@10: pop(); rlm@10: } rlm@10: } rlm@10: else rlm@10: { rlm@10: if(prev.getSize() == 1) rlm@10: { rlm@10: dup2X1(); rlm@10: pop2(); rlm@10: } rlm@10: else rlm@10: { rlm@10: dup2X2(); rlm@10: pop2(); rlm@10: } rlm@10: } rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to do mathematical and logical operations rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates the instruction to do the specified mathematical or logical rlm@10: * operation. rlm@10: * rlm@10: * @param op a mathematical or logical operation. Must be one of ADD, SUB, rlm@10: * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. rlm@10: * @param type the type of the operand(s) for this operation. rlm@10: */ rlm@10: public void math(final int op, final Type type){ rlm@10: mv.visitInsn(type.getOpcode(op)); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to compute the bitwise negation of the top rlm@10: * stack value. rlm@10: */ rlm@10: public void not(){ rlm@10: mv.visitInsn(Opcodes.ICONST_1); rlm@10: mv.visitInsn(Opcodes.IXOR); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to increment the given local variable. rlm@10: * rlm@10: * @param local the local variable to be incremented. rlm@10: * @param amount the amount by which the local variable must be incremented. rlm@10: */ rlm@10: public void iinc(final int local, final int amount){ rlm@10: mv.visitIincInsn(local, amount); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to cast a numerical value from one type to rlm@10: * another. rlm@10: * rlm@10: * @param from the type of the top stack value rlm@10: * @param to the type into which this value must be cast. rlm@10: */ rlm@10: public void cast(final Type from, final Type to){ rlm@10: if(from != to) rlm@10: { rlm@10: if(from == Type.DOUBLE_TYPE) rlm@10: { rlm@10: if(to == Type.FLOAT_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.D2F); rlm@10: } rlm@10: else if(to == Type.LONG_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.D2L); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitInsn(Opcodes.D2I); rlm@10: cast(Type.INT_TYPE, to); rlm@10: } rlm@10: } rlm@10: else if(from == Type.FLOAT_TYPE) rlm@10: { rlm@10: if(to == Type.DOUBLE_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.F2D); rlm@10: } rlm@10: else if(to == Type.LONG_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.F2L); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitInsn(Opcodes.F2I); rlm@10: cast(Type.INT_TYPE, to); rlm@10: } rlm@10: } rlm@10: else if(from == Type.LONG_TYPE) rlm@10: { rlm@10: if(to == Type.DOUBLE_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.L2D); rlm@10: } rlm@10: else if(to == Type.FLOAT_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.L2F); rlm@10: } rlm@10: else rlm@10: { rlm@10: mv.visitInsn(Opcodes.L2I); rlm@10: cast(Type.INT_TYPE, to); rlm@10: } rlm@10: } rlm@10: else rlm@10: { rlm@10: if(to == Type.BYTE_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2B); rlm@10: } rlm@10: else if(to == Type.CHAR_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2C); rlm@10: } rlm@10: else if(to == Type.DOUBLE_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2D); rlm@10: } rlm@10: else if(to == Type.FLOAT_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2F); rlm@10: } rlm@10: else if(to == Type.LONG_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2L); rlm@10: } rlm@10: else if(to == Type.SHORT_TYPE) rlm@10: { rlm@10: mv.visitInsn(Opcodes.I2S); rlm@10: } rlm@10: } rlm@10: } rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to do boxing and unboxing operations rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates the instructions to box the top stack value. This value is rlm@10: * replaced by its boxed equivalent on top of the stack. rlm@10: * rlm@10: * @param type the type of the top stack value. rlm@10: */ rlm@10: public void box(final Type type){ rlm@10: if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) rlm@10: { rlm@10: return; rlm@10: } rlm@10: if(type == Type.VOID_TYPE) rlm@10: { rlm@10: push((String) null); rlm@10: } rlm@10: else rlm@10: { rlm@10: Type boxed = type; rlm@10: switch(type.getSort()) rlm@10: { rlm@10: case Type.BYTE: rlm@10: boxed = BYTE_TYPE; rlm@10: break; rlm@10: case Type.BOOLEAN: rlm@10: boxed = BOOLEAN_TYPE; rlm@10: break; rlm@10: case Type.SHORT: rlm@10: boxed = SHORT_TYPE; rlm@10: break; rlm@10: case Type.CHAR: rlm@10: boxed = CHARACTER_TYPE; rlm@10: break; rlm@10: case Type.INT: rlm@10: boxed = INTEGER_TYPE; rlm@10: break; rlm@10: case Type.FLOAT: rlm@10: boxed = FLOAT_TYPE; rlm@10: break; rlm@10: case Type.LONG: rlm@10: boxed = LONG_TYPE; rlm@10: break; rlm@10: case Type.DOUBLE: rlm@10: boxed = DOUBLE_TYPE; rlm@10: break; rlm@10: } rlm@10: newInstance(boxed); rlm@10: if(type.getSize() == 2) rlm@10: { rlm@10: // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o rlm@10: dupX2(); rlm@10: dupX2(); rlm@10: pop(); rlm@10: } rlm@10: else rlm@10: { rlm@10: // p -> po -> opo -> oop -> o rlm@10: dupX1(); rlm@10: swap(); rlm@10: } rlm@10: invokeConstructor(boxed, new Method("