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("", rlm@10: Type.VOID_TYPE, rlm@10: new Type[]{type})); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to unbox the top stack value. This value is rlm@10: * replaced by its unboxed 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 unbox(final Type type){ rlm@10: Type t = NUMBER_TYPE; rlm@10: Method sig = null; rlm@10: switch(type.getSort()) rlm@10: { rlm@10: case Type.VOID: rlm@10: return; rlm@10: case Type.CHAR: rlm@10: t = CHARACTER_TYPE; rlm@10: sig = CHAR_VALUE; rlm@10: break; rlm@10: case Type.BOOLEAN: rlm@10: t = BOOLEAN_TYPE; rlm@10: sig = BOOLEAN_VALUE; rlm@10: break; rlm@10: case Type.DOUBLE: rlm@10: sig = DOUBLE_VALUE; rlm@10: break; rlm@10: case Type.FLOAT: rlm@10: sig = FLOAT_VALUE; rlm@10: break; rlm@10: case Type.LONG: rlm@10: sig = LONG_VALUE; rlm@10: break; rlm@10: case Type.INT: rlm@10: case Type.SHORT: rlm@10: case Type.BYTE: rlm@10: sig = INT_VALUE; rlm@10: } rlm@10: if(sig == null) rlm@10: { rlm@10: checkCast(type); rlm@10: } rlm@10: else rlm@10: { rlm@10: checkCast(t); rlm@10: invokeVirtual(t, sig); rlm@10: } rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to jump to other instructions rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Creates a new {@link Label}. rlm@10: * rlm@10: * @return a new {@link Label}. rlm@10: */ rlm@10: public Label newLabel(){ rlm@10: return new Label(); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Marks the current code position with the given label. rlm@10: * rlm@10: * @param label a label. rlm@10: */ rlm@10: public void mark(final Label label){ rlm@10: mv.visitLabel(label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Marks the current code position with a new label. rlm@10: * rlm@10: * @return the label that was created to mark the current code position. rlm@10: */ rlm@10: public Label mark(){ rlm@10: Label label = new Label(); rlm@10: mv.visitLabel(label); rlm@10: return label; rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to jump to a label based on the comparison of rlm@10: * the top two stack values. rlm@10: * rlm@10: * @param type the type of the top two stack values. rlm@10: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, rlm@10: * LE. rlm@10: * @param label where to jump if the comparison result is true. rlm@10: */ rlm@10: public void ifCmp(final Type type, final int mode, final Label label){ rlm@10: int intOp = -1; rlm@10: switch(type.getSort()) rlm@10: { rlm@10: case Type.LONG: rlm@10: mv.visitInsn(Opcodes.LCMP); rlm@10: break; rlm@10: case Type.DOUBLE: rlm@10: mv.visitInsn(Opcodes.DCMPG); rlm@10: break; rlm@10: case Type.FLOAT: rlm@10: mv.visitInsn(Opcodes.FCMPG); rlm@10: break; rlm@10: case Type.ARRAY: rlm@10: case Type.OBJECT: rlm@10: switch(mode) rlm@10: { rlm@10: case EQ: rlm@10: mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); rlm@10: return; rlm@10: case NE: rlm@10: mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); rlm@10: return; rlm@10: } rlm@10: throw new IllegalArgumentException("Bad comparison for type " rlm@10: + type); rlm@10: default: rlm@10: switch(mode) rlm@10: { rlm@10: case EQ: rlm@10: intOp = Opcodes.IF_ICMPEQ; rlm@10: break; rlm@10: case NE: rlm@10: intOp = Opcodes.IF_ICMPNE; rlm@10: break; rlm@10: case GE: rlm@10: intOp = Opcodes.IF_ICMPGE; rlm@10: break; rlm@10: case LT: rlm@10: intOp = Opcodes.IF_ICMPLT; rlm@10: break; rlm@10: case LE: rlm@10: intOp = Opcodes.IF_ICMPLE; rlm@10: break; rlm@10: case GT: rlm@10: intOp = Opcodes.IF_ICMPGT; rlm@10: break; rlm@10: } rlm@10: mv.visitJumpInsn(intOp, label); rlm@10: return; rlm@10: } rlm@10: int jumpMode = mode; rlm@10: switch(mode) rlm@10: { rlm@10: case GE: rlm@10: jumpMode = LT; rlm@10: break; rlm@10: case LE: rlm@10: jumpMode = GT; rlm@10: break; rlm@10: } rlm@10: mv.visitJumpInsn(jumpMode, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to jump to a label based on the comparison of rlm@10: * the top two integer stack values. rlm@10: * rlm@10: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, rlm@10: * LE. rlm@10: * @param label where to jump if the comparison result is true. rlm@10: */ rlm@10: public void ifICmp(final int mode, final Label label){ rlm@10: ifCmp(Type.INT_TYPE, mode, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to jump to a label based on the comparison of rlm@10: * the top integer stack value with zero. rlm@10: * rlm@10: * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, rlm@10: * LE. rlm@10: * @param label where to jump if the comparison result is true. rlm@10: */ rlm@10: public void ifZCmp(final int mode, final Label label){ rlm@10: mv.visitJumpInsn(mode, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to jump to the given label if the top stack rlm@10: * value is null. rlm@10: * rlm@10: * @param label where to jump if the condition is true. rlm@10: */ rlm@10: public void ifNull(final Label label){ rlm@10: mv.visitJumpInsn(Opcodes.IFNULL, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to jump to the given label if the top stack rlm@10: * value is not null. rlm@10: * rlm@10: * @param label where to jump if the condition is true. rlm@10: */ rlm@10: public void ifNonNull(final Label label){ rlm@10: mv.visitJumpInsn(Opcodes.IFNONNULL, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to jump to the given label. rlm@10: * rlm@10: * @param label where to jump if the condition is true. rlm@10: */ rlm@10: public void goTo(final Label label){ rlm@10: mv.visitJumpInsn(Opcodes.GOTO, label); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates a RET instruction. 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 ret(final int local){ rlm@10: mv.visitVarInsn(Opcodes.RET, local); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions for a switch statement. rlm@10: * rlm@10: * @param keys the switch case keys. rlm@10: * @param generator a generator to generate the code for the switch cases. rlm@10: */ rlm@10: public void tableSwitch( rlm@10: final int[] keys, rlm@10: final TableSwitchGenerator generator){ rlm@10: float density; rlm@10: if(keys.length == 0) rlm@10: { rlm@10: density = 0; rlm@10: } rlm@10: else rlm@10: { rlm@10: density = (float) keys.length rlm@10: / (keys[keys.length - 1] - keys[0] + 1); rlm@10: } rlm@10: tableSwitch(keys, generator, density >= 0.5f); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions for a switch statement. rlm@10: * rlm@10: * @param keys the switch case keys. rlm@10: * @param generator a generator to generate the code for the switch cases. rlm@10: * @param useTable true to use a TABLESWITCH instruction, or rlm@10: * false to use a LOOKUPSWITCH instruction. rlm@10: */ rlm@10: public void tableSwitch( rlm@10: final int[] keys, rlm@10: final TableSwitchGenerator generator, rlm@10: final boolean useTable){ rlm@10: for(int i = 1; i < keys.length; ++i) rlm@10: { rlm@10: if(keys[i] < keys[i - 1]) rlm@10: { rlm@10: throw new IllegalArgumentException("keys must be sorted ascending"); rlm@10: } rlm@10: } rlm@10: Label def = newLabel(); rlm@10: Label end = newLabel(); rlm@10: if(keys.length > 0) rlm@10: { rlm@10: int len = keys.length; rlm@10: int min = keys[0]; rlm@10: int max = keys[len - 1]; rlm@10: int range = max - min + 1; rlm@10: if(useTable) rlm@10: { rlm@10: Label[] labels = new Label[range]; rlm@10: Arrays.fill(labels, def); rlm@10: for(int i = 0; i < len; ++i) rlm@10: { rlm@10: labels[keys[i] - min] = newLabel(); rlm@10: } rlm@10: mv.visitTableSwitchInsn(min, max, def, labels); rlm@10: for(int i = 0; i < range; ++i) rlm@10: { rlm@10: Label label = labels[i]; rlm@10: if(label != def) rlm@10: { rlm@10: mark(label); rlm@10: generator.generateCase(i + min, end); rlm@10: } rlm@10: } rlm@10: } rlm@10: else rlm@10: { rlm@10: Label[] labels = new Label[len]; rlm@10: for(int i = 0; i < len; ++i) rlm@10: { rlm@10: labels[i] = newLabel(); rlm@10: } rlm@10: mv.visitLookupSwitchInsn(def, keys, labels); rlm@10: for(int i = 0; i < len; ++i) rlm@10: { rlm@10: mark(labels[i]); rlm@10: generator.generateCase(keys[i], end); rlm@10: } rlm@10: } rlm@10: } rlm@10: mark(def); rlm@10: generator.generateDefault(); rlm@10: mark(end); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to return the top stack value to the caller. rlm@10: */ rlm@10: public void returnValue(){ rlm@10: mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to load and store fields rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates a get field or set field instruction. rlm@10: * rlm@10: * @param opcode the instruction's opcode. rlm@10: * @param ownerType the class in which the field is defined. rlm@10: * @param name the name of the field. rlm@10: * @param fieldType the type of the field. rlm@10: */ rlm@10: private void fieldInsn( rlm@10: final int opcode, rlm@10: final Type ownerType, rlm@10: final String name, rlm@10: final Type fieldType){ rlm@10: mv.visitFieldInsn(opcode, rlm@10: ownerType.getInternalName(), rlm@10: name, rlm@10: fieldType.getDescriptor()); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the value of a static field on the rlm@10: * stack. rlm@10: * rlm@10: * @param owner the class in which the field is defined. rlm@10: * @param name the name of the field. rlm@10: * @param type the type of the field. rlm@10: */ rlm@10: public void getStatic(final Type owner, final String name, final Type type){ rlm@10: fieldInsn(Opcodes.GETSTATIC, owner, name, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in a static field. rlm@10: * rlm@10: * @param owner the class in which the field is defined. rlm@10: * @param name the name of the field. rlm@10: * @param type the type of the field. rlm@10: */ rlm@10: public void putStatic(final Type owner, final String name, final Type type){ rlm@10: fieldInsn(Opcodes.PUTSTATIC, owner, name, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to push the value of a non static field on the rlm@10: * stack. rlm@10: * rlm@10: * @param owner the class in which the field is defined. rlm@10: * @param name the name of the field. rlm@10: * @param type the type of the field. rlm@10: */ rlm@10: public void getField(final Type owner, final String name, final Type type){ rlm@10: fieldInsn(Opcodes.GETFIELD, owner, name, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to store the top stack value in a non static rlm@10: * field. rlm@10: * rlm@10: * @param owner the class in which the field is defined. rlm@10: * @param name the name of the field. rlm@10: * @param type the type of the field. rlm@10: */ rlm@10: public void putField(final Type owner, final String name, final Type type){ rlm@10: fieldInsn(Opcodes.PUTFIELD, owner, name, type); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to invoke methods rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates an invoke method instruction. rlm@10: * rlm@10: * @param opcode the instruction's opcode. rlm@10: * @param type the class in which the method is defined. rlm@10: * @param method the method to be invoked. rlm@10: */ rlm@10: private void invokeInsn( rlm@10: final int opcode, rlm@10: final Type type, rlm@10: final Method method){ rlm@10: String owner = type.getSort() == Type.ARRAY rlm@10: ? type.getDescriptor() rlm@10: : type.getInternalName(); rlm@10: mv.visitMethodInsn(opcode, rlm@10: owner, rlm@10: method.getName(), rlm@10: method.getDescriptor()); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to invoke a normal method. rlm@10: * rlm@10: * @param owner the class in which the method is defined. rlm@10: * @param method the method to be invoked. rlm@10: */ rlm@10: public void invokeVirtual(final Type owner, final Method method){ rlm@10: invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to invoke a constructor. rlm@10: * rlm@10: * @param type the class in which the constructor is defined. rlm@10: * @param method the constructor to be invoked. rlm@10: */ rlm@10: public void invokeConstructor(final Type type, final Method method){ rlm@10: invokeInsn(Opcodes.INVOKESPECIAL, type, method); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to invoke a static method. rlm@10: * rlm@10: * @param owner the class in which the method is defined. rlm@10: * @param method the method to be invoked. rlm@10: */ rlm@10: public void invokeStatic(final Type owner, final Method method){ rlm@10: invokeInsn(Opcodes.INVOKESTATIC, owner, method); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to invoke an interface method. rlm@10: * rlm@10: * @param owner the class in which the method is defined. rlm@10: * @param method the method to be invoked. rlm@10: */ rlm@10: public void invokeInterface(final Type owner, final Method method){ rlm@10: invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Instructions to create objects and arrays rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates a type dependent instruction. rlm@10: * rlm@10: * @param opcode the instruction's opcode. rlm@10: * @param type the instruction's operand. rlm@10: */ rlm@10: private void typeInsn(final int opcode, final Type type){ rlm@10: String desc; rlm@10: if(type.getSort() == Type.ARRAY) rlm@10: { rlm@10: desc = type.getDescriptor(); rlm@10: } rlm@10: else rlm@10: { rlm@10: desc = type.getInternalName(); rlm@10: } rlm@10: mv.visitTypeInsn(opcode, desc); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to create a new object. rlm@10: * rlm@10: * @param type the class of the object to be created. rlm@10: */ rlm@10: public void newInstance(final Type type){ rlm@10: typeInsn(Opcodes.NEW, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to create a new array. rlm@10: * rlm@10: * @param type the type of the array elements. rlm@10: */ rlm@10: public void newArray(final Type type){ rlm@10: int typ; rlm@10: switch(type.getSort()) rlm@10: { rlm@10: case Type.BOOLEAN: rlm@10: typ = Opcodes.T_BOOLEAN; rlm@10: break; rlm@10: case Type.CHAR: rlm@10: typ = Opcodes.T_CHAR; rlm@10: break; rlm@10: case Type.BYTE: rlm@10: typ = Opcodes.T_BYTE; rlm@10: break; rlm@10: case Type.SHORT: rlm@10: typ = Opcodes.T_SHORT; rlm@10: break; rlm@10: case Type.INT: rlm@10: typ = Opcodes.T_INT; rlm@10: break; rlm@10: case Type.FLOAT: rlm@10: typ = Opcodes.T_FLOAT; rlm@10: break; rlm@10: case Type.LONG: rlm@10: typ = Opcodes.T_LONG; rlm@10: break; rlm@10: case Type.DOUBLE: rlm@10: typ = Opcodes.T_DOUBLE; rlm@10: break; rlm@10: default: rlm@10: typeInsn(Opcodes.ANEWARRAY, type); rlm@10: return; rlm@10: } rlm@10: mv.visitIntInsn(Opcodes.NEWARRAY, typ); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Miscelaneous instructions rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Generates the instruction to compute the length of an array. rlm@10: */ rlm@10: public void arrayLength(){ rlm@10: mv.visitInsn(Opcodes.ARRAYLENGTH); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to throw an exception. rlm@10: */ rlm@10: public void throwException(){ rlm@10: mv.visitInsn(Opcodes.ATHROW); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instructions to create and throw an exception. The rlm@10: * exception class must have a constructor with a single String argument. rlm@10: * rlm@10: * @param type the class of the exception to be thrown. rlm@10: * @param msg the detailed message of the exception. rlm@10: */ rlm@10: public void throwException(final Type type, final String msg){ rlm@10: newInstance(type); rlm@10: dup(); rlm@10: push(msg); rlm@10: invokeConstructor(type, Method.getMethod("void (String)")); rlm@10: throwException(); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to check that the top stack value is of the rlm@10: * given type. rlm@10: * rlm@10: * @param type a class or interface type. rlm@10: */ rlm@10: public void checkCast(final Type type){ rlm@10: if(!type.equals(OBJECT_TYPE)) rlm@10: { rlm@10: typeInsn(Opcodes.CHECKCAST, type); rlm@10: } rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to test if the top stack value is of the given rlm@10: * type. rlm@10: * rlm@10: * @param type a class or interface type. rlm@10: */ rlm@10: public void instanceOf(final Type type){ rlm@10: typeInsn(Opcodes.INSTANCEOF, type); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to get the monitor of the top stack value. rlm@10: */ rlm@10: public void monitorEnter(){ rlm@10: mv.visitInsn(Opcodes.MONITORENTER); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Generates the instruction to release the monitor of the top stack value. rlm@10: */ rlm@10: public void monitorExit(){ rlm@10: mv.visitInsn(Opcodes.MONITOREXIT); rlm@10: } rlm@10: rlm@10: // ------------------------------------------------------------------------ rlm@10: // Non instructions rlm@10: // ------------------------------------------------------------------------ rlm@10: rlm@10: /** rlm@10: * Marks the end of the visited method. rlm@10: */ rlm@10: public void endMethod(){ rlm@10: if((access & Opcodes.ACC_ABSTRACT) == 0) rlm@10: { rlm@10: mv.visitMaxs(0, 0); rlm@10: } rlm@10: mv.visitEnd(); rlm@10: } rlm@10: rlm@10: /** rlm@10: * Marks the start of an exception handler. rlm@10: * rlm@10: * @param start beginning of the exception handler's scope (inclusive). rlm@10: * @param end end of the exception handler's scope (exclusive). rlm@10: * @param exception internal name of the type of exceptions handled by the rlm@10: * handler. rlm@10: */ rlm@10: public void catchException( rlm@10: final Label start, rlm@10: final Label end, rlm@10: final Type exception){ rlm@10: mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName()); rlm@10: } rlm@10: }