Package org.drools.asm.commons

Source Code of org.drools.asm.commons.GeneratorAdapter

/***
* ASM: a very small and fast Java bytecode manipulation framework
* Copyright (c) 2000-2005 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
*    contributors may be used to endorse or promote products derived from
*    this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.drools.asm.commons;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.drools.asm.ClassVisitor;
import org.drools.asm.Label;
import org.drools.asm.MethodVisitor;
import org.drools.asm.Opcodes;
import org.drools.asm.Type;

/**
* A {@link org.drools.asm.MethodAdapter} with convenient methods to generate
* code. For example, using this adapter, the class below
*
* <pre>
* public class Example {
*     public static void main(String[] args) {
*         System.out.println(&quot;Hello world!&quot;);
*     }
* }
* </pre>
*
* can be generated as follows:
*
* <pre>
* ClassWriter cw = new ClassWriter(true);
* cw.visit(V1_1, ACC_PUBLIC, &quot;Example&quot;, null, &quot;java/lang/Object&quot;, null);
*
* Method m = Method.getMethod(&quot;void &lt;init&gt; ()&quot;);
* GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
* mg.loadThis();
* mg.invokeConstructor(Type.getType(Object.class), m);
* mg.returnValue();
* mg.endMethod();
*
* m = Method.getMethod(&quot;void main (String[])&quot;);
* mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
* mg.getStatic(Type.getType(System.class), &quot;out&quot;, Type.getType(PrintStream.class));
* mg.push(&quot;Hello world!&quot;);
* mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod(&quot;void println (String)&quot;));
* mg.returnValue();
* mg.endMethod();
*
* cw.visitEnd();
* </pre>
*
* @author Juozas Baliuka
* @author Chris Nokleberg
* @author Eric Bruneton
*/
public class GeneratorAdapter extends LocalVariablesSorter {

    private final static Type   BYTE_TYPE      = Type.getType( "Ljava/lang/Byte;" );

    private final static Type   BOOLEAN_TYPE   = Type.getType( "Ljava/lang/Boolean;" );

    private final static Type   SHORT_TYPE     = Type.getType( "Ljava/lang/Short;" );

    private final static Type   CHARACTER_TYPE = Type.getType( "Ljava/lang/Character;" );

    private final static Type   INTEGER_TYPE   = Type.getType( "Ljava/lang/Integer;" );

    private final static Type   FLOAT_TYPE     = Type.getType( "Ljava/lang/Float;" );

    private final static Type   LONG_TYPE      = Type.getType( "Ljava/lang/Long;" );

    private final static Type   DOUBLE_TYPE    = Type.getType( "Ljava/lang/Double;" );

    private final static Type   NUMBER_TYPE    = Type.getType( "Ljava/lang/Number;" );

    private final static Type   OBJECT_TYPE    = Type.getType( "Ljava/lang/Object;" );

    private final static Method BOOLEAN_VALUE  = Method.getMethod( "boolean booleanValue()" );

    private final static Method CHAR_VALUE     = Method.getMethod( "char charValue()" );

    private final static Method INT_VALUE      = Method.getMethod( "int intValue()" );

    private final static Method FLOAT_VALUE    = Method.getMethod( "float floatValue()" );

    private final static Method LONG_VALUE     = Method.getMethod( "long longValue()" );

    private final static Method DOUBLE_VALUE   = Method.getMethod( "double doubleValue()" );

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     ADD            = Opcodes.IADD;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     SUB            = Opcodes.ISUB;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     MUL            = Opcodes.IMUL;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     DIV            = Opcodes.IDIV;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     REM            = Opcodes.IREM;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     NEG            = Opcodes.INEG;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     SHL            = Opcodes.ISHL;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     SHR            = Opcodes.ISHR;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     USHR           = Opcodes.IUSHR;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     AND            = Opcodes.IAND;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     OR             = Opcodes.IOR;

    /**
     * Constant for the {@link #math math} method.
     */
    public final static int     XOR            = Opcodes.IXOR;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     EQ             = Opcodes.IFEQ;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     NE             = Opcodes.IFNE;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     LT             = Opcodes.IFLT;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     GE             = Opcodes.IFGE;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     GT             = Opcodes.IFGT;

    /**
     * Constant for the {@link #ifCmp ifCmp} method.
     */
    public final static int     LE             = Opcodes.IFLE;

    /**
     * Access flags of the method visited by this adapter.
     */
    private final int           access;

    /**
     * Return type of the method visited by this adapter.
     */
    private final Type          returnType;

    /**
     * Argument types of the method visited by this adapter.
     */
    private final Type[]        argumentTypes;

    /**
     * Types of the local variables of the method visited by this adapter.
     */
    private final List          localTypes;

    /**
     * Creates a new {@link GeneratorAdapter}.
     *
     * @param mv the method visitor to which this adapter delegates calls.
     * @param access the method's access flags (see {@link Opcodes}).
     * @param name the method's name.
     * @param desc the method's descriptor (see {@link Type Type}).
     */
    public GeneratorAdapter(final MethodVisitor mv,
                            final int access,
                            final String name,
                            final String desc) {
        super( access,
               desc,
               mv );
        this.access = access;
        this.returnType = Type.getReturnType( desc );
        this.argumentTypes = Type.getArgumentTypes( desc );
        this.localTypes = new ArrayList();
    }

    /**
     * Creates a new {@link GeneratorAdapter}.
     *
     * @param access access flags of the adapted method.
     * @param method the adapted method.
     * @param mv the method visitor to which this adapter delegates calls.
     */
    public GeneratorAdapter(final int access,
                            final Method method,
                            final MethodVisitor mv) {
        super( access,
               method.getDescriptor(),
               mv );
        this.access = access;
        this.returnType = method.getReturnType();
        this.argumentTypes = method.getArgumentTypes();
        this.localTypes = new ArrayList();
    }

    /**
     * Creates a new {@link GeneratorAdapter}.
     *
     * @param access access flags of the adapted method.
     * @param method the adapted method.
     * @param signature the signature of the adapted method (may be
     *        <tt>null</tt>).
     * @param exceptions the exceptions thrown by the adapted method (may be
     *        <tt>null</tt>).
     * @param cv the class visitor to which this adapter delegates calls.
     */
    public GeneratorAdapter(final int access,
                            final Method method,
                            final String signature,
                            final Type[] exceptions,
                            final ClassVisitor cv) {
        this( access,
              method,
              cv.visitMethod( access,
                              method.getName(),
                              method.getDescriptor(),
                              signature,
                              getInternalNames( exceptions ) ) );
    }

    /**
     * Returns the internal names of the given types.
     *
     * @param types a set of types.
     * @return the internal names of the given types.
     */
    private static String[] getInternalNames(final Type[] types) {
        if ( types == null ) {
            return null;
        }
        final String[] names = new String[types.length];
        for ( int i = 0; i < names.length; ++i ) {
            names[i] = types[i].getInternalName();
        }
        return names;
    }

    // ------------------------------------------------------------------------
    // Instructions to push constants on the stack
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final boolean value) {
        push( value ? 1 : 0 );
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final int value) {
        if ( value >= -1 && value <= 5 ) {
            this.mv.visitInsn( Opcodes.ICONST_0 + value );
        } else if ( value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE ) {
            this.mv.visitIntInsn( Opcodes.BIPUSH,
                                  value );
        } else if ( value >= Short.MIN_VALUE && value <= Short.MAX_VALUE ) {
            this.mv.visitIntInsn( Opcodes.SIPUSH,
                                  value );
        } else {
            this.mv.visitLdcInsn( new Integer( value ) );
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final long value) {
        if ( value == 0L || value == 1L ) {
            this.mv.visitInsn( Opcodes.LCONST_0 + (int) value );
        } else {
            this.mv.visitLdcInsn( new Long( value ) );
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final float value) {
        final int bits = Float.floatToIntBits( value );
        if ( bits == 0L || bits == 0x3f800000 || bits == 0x40000000 ) { // 0..2
            this.mv.visitInsn( Opcodes.FCONST_0 + (int) value );
        } else {
            this.mv.visitLdcInsn( new Float( value ) );
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final double value) {
        final long bits = Double.doubleToLongBits( value );
        if ( bits == 0L || bits == 0x3ff0000000000000L ) { // +0.0d and 1.0d
            this.mv.visitInsn( Opcodes.DCONST_0 + (int) value );
        } else {
            this.mv.visitLdcInsn( new Double( value ) );
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack. May be <tt>null</tt>.
     */
    public void push(final String value) {
        if ( value == null ) {
            this.mv.visitInsn( Opcodes.ACONST_NULL );
        } else {
            this.mv.visitLdcInsn( value );
        }
    }

    /**
     * Generates the instruction to push the given value on the stack.
     *
     * @param value the value to be pushed on the stack.
     */
    public void push(final Type value) {
        if ( value == null ) {
            this.mv.visitInsn( Opcodes.ACONST_NULL );
        } else {
            this.mv.visitLdcInsn( value );
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store method arguments
    // ------------------------------------------------------------------------

    /**
     * Returns the index of the given method argument in the frame's local
     * variables array.
     *
     * @param arg the index of a method argument.
     * @return the index of the given method argument in the frame's local
     *         variables array.
     */
    private int getArgIndex(final int arg) {
        int index = ((this.access & Opcodes.ACC_STATIC) == 0 ? 1 : 0);
        for ( int i = 0; i < arg; i++ ) {
            index += this.argumentTypes[i].getSize();
        }
        return index;
    }

    /**
     * Generates the instruction to push a local variable on the stack.
     *
     * @param type the type of the local variable to be loaded.
     * @param index an index in the frame's local variables array.
     */
    private void loadInsn(final Type type,
                          final int index) {
        this.mv.visitVarInsn( type.getOpcode( Opcodes.ILOAD ),
                              index );
    }

    /**
     * Generates the instruction to store the top stack value in a local
     * variable.
     *
     * @param type the type of the local variable to be stored.
     * @param index an index in the frame's local variables array.
     */
    private void storeInsn(final Type type,
                           final int index) {
        this.mv.visitVarInsn( type.getOpcode( Opcodes.ISTORE ),
                              index );
    }

    /**
     * Generates the instruction to load 'this' on the stack.
     */
    public void loadThis() {
        if ( (this.access & Opcodes.ACC_STATIC) != 0 ) {
            throw new IllegalStateException( "no 'this' pointer within static method" );
        }
        this.mv.visitVarInsn( Opcodes.ALOAD,
                              0 );
    }

    /**
     * Generates the instruction to load the given method argument on the stack.
     *
     * @param arg the index of a method argument.
     */
    public void loadArg(final int arg) {
        loadInsn( this.argumentTypes[arg],
                  getArgIndex( arg ) );
    }

    /**
     * Generates the instructions to load the given method arguments on the
     * stack.
     *
     * @param arg the index of the first method argument to be loaded.
     * @param count the number of method arguments to be loaded.
     */
    public void loadArgs(final int arg,
                         final int count) {
        int index = getArgIndex( arg );
        for ( int i = 0; i < count; ++i ) {
            final Type t = this.argumentTypes[arg + i];
            loadInsn( t,
                      index );
            index += t.getSize();
        }
    }

    /**
     * Generates the instructions to load all the method arguments on the stack.
     */
    public void loadArgs() {
        loadArgs( 0,
                  this.argumentTypes.length );
    }

    /**
     * Generates the instructions to load all the method arguments on the stack,
     * as a single object array.
     */
    public void loadArgArray() {
        push( this.argumentTypes.length );
        newArray( GeneratorAdapter.OBJECT_TYPE );
        for ( int i = 0; i < this.argumentTypes.length; i++ ) {
            dup();
            push( i );
            loadArg( i );
            box( this.argumentTypes[i] );
            arrayStore( GeneratorAdapter.OBJECT_TYPE );
        }
    }

    /**
     * Generates the instruction to store the top stack value in the given
     * method argument.
     *
     * @param arg the index of a method argument.
     */
    public void storeArg(final int arg) {
        storeInsn( this.argumentTypes[arg],
                   getArgIndex( arg ) );
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store local variables
    // ------------------------------------------------------------------------

    /**
     * Creates a new local variable of the given type.
     *
     * @param type the type of the local variable to be created.
     * @return the identifier of the newly created local variable.
     */
    public int newLocal(final Type type) {
        final int local = super.newLocal( type.getSize() );
        setLocalType( local,
                      type );
        return local;
    }

    /**
     * Returns the type of the given local variable.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     * @return the type of the given local variable.
     */
    public Type getLocalType(final int local) {
        return (Type) this.localTypes.get( local - this.firstLocal );
    }

    /**
     * Sets the current type of the given local variable.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     * @param type the type of the value being stored in the local variable
     */
    private void setLocalType(final int local,
                              final Type type) {
        final int index = local - this.firstLocal;
        while ( this.localTypes.size() < index + 1 ) {
            this.localTypes.add( null );
        }
        this.localTypes.set( index,
                             type );
    }

    /**
     * Generates the instruction to load the given local variable on the stack.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     */
    public void loadLocal(final int local) {
        loadInsn( getLocalType( local ),
                  local );
    }

    /**
     * Generates the instruction to load the given local variable on the stack.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     * @param type the type of this local variable.
     */
    public void loadLocal(final int local,
                          final Type type) {
        setLocalType( local,
                      type );
        loadInsn( type,
                  local );
    }

    /**
     * Generates the instruction to store the top stack value in the given local
     * variable.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     */
    public void storeLocal(final int local) {
        storeInsn( getLocalType( local ),
                   local );
    }

    /**
     * Generates the instruction to store the top stack value in the given local
     * variable.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     * @param type the type of this local variable.
     */
    public void storeLocal(final int local,
                           final Type type) {
        setLocalType( local,
                      type );
        storeInsn( type,
                   local );
    }

    /**
     * Generates the instruction to load an element from an array.
     *
     * @param type the type of the array element to be loaded.
     */
    public void arrayLoad(final Type type) {
        this.mv.visitInsn( type.getOpcode( Opcodes.IALOAD ) );
    }

    /**
     * Generates the instruction to store an element in an array.
     *
     * @param type the type of the array element to be stored.
     */
    public void arrayStore(final Type type) {
        this.mv.visitInsn( type.getOpcode( Opcodes.IASTORE ) );
    }

    // ------------------------------------------------------------------------
    // Instructions to manage the stack
    // ------------------------------------------------------------------------

    /**
     * Generates a POP instruction.
     */
    public void pop() {
        this.mv.visitInsn( Opcodes.POP );
    }

    /**
     * Generates a POP2 instruction.
     */
    public void pop2() {
        this.mv.visitInsn( Opcodes.POP2 );
    }

    /**
     * Generates a DUP instruction.
     */
    public void dup() {
        this.mv.visitInsn( Opcodes.DUP );
    }

    /**
     * Generates a DUP2 instruction.
     */
    public void dup2() {
        this.mv.visitInsn( Opcodes.DUP2 );
    }

    /**
     * Generates a DUP_X1 instruction.
     */
    public void dupX1() {
        this.mv.visitInsn( Opcodes.DUP_X1 );
    }

    /**
     * Generates a DUP_X2 instruction.
     */
    public void dupX2() {
        this.mv.visitInsn( Opcodes.DUP_X2 );
    }

    /**
     * Generates a DUP2_X1 instruction.
     */
    public void dup2X1() {
        this.mv.visitInsn( Opcodes.DUP2_X1 );
    }

    /**
     * Generates a DUP2_X2 instruction.
     */
    public void dup2X2() {
        this.mv.visitInsn( Opcodes.DUP2_X2 );
    }

    /**
     * Generates a SWAP instruction.
     */
    public void swap() {
        this.mv.visitInsn( Opcodes.SWAP );
    }

    /**
     * Generates the instructions to swap the top two stack values.
     *
     * @param prev type of the top - 1 stack value.
     * @param type type of the top stack value.
     */
    public void swap(final Type prev,
                     final Type type) {
        if ( type.getSize() == 1 ) {
            if ( prev.getSize() == 1 ) {
                swap(); // same as dupX1(), pop();
            } else {
                dupX2();
                pop();
            }
        } else {
            if ( prev.getSize() == 1 ) {
                dup2X1();
                pop2();
            } else {
                dup2X2();
                pop2();
            }
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to do mathematical and logical operations
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to do the specified mathematical or logical
     * operation.
     *
     * @param op a mathematical or logical operation. Must be one of ADD, SUB,
     *        MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
     * @param type the type of the operand(s) for this operation.
     */
    public void math(final int op,
                     final Type type) {
        this.mv.visitInsn( type.getOpcode( op ) );
    }

    /**
     * Generates the instructions to compute the bitwise negation of the top
     * stack value.
     */
    public void not() {
        this.mv.visitInsn( Opcodes.ICONST_1 );
        this.mv.visitInsn( Opcodes.IXOR );
    }

    /**
     * Generates the instruction to increment the given local variable.
     *
     * @param local the local variable to be incremented.
     * @param amount the amount by which the local variable must be incremented.
     */
    public void iinc(final int local,
                     final int amount) {
        this.mv.visitIincInsn( local,
                               amount );
    }

    /**
     * Generates the instructions to cast a numerical value from one type to
     * another.
     *
     * @param from the type of the top stack value
     * @param to the type into which this value must be cast.
     */
    public void cast(final Type from,
                     final Type to) {
        if ( from != to ) {
            if ( from == Type.DOUBLE_TYPE ) {
                if ( to == Type.FLOAT_TYPE ) {
                    this.mv.visitInsn( Opcodes.D2F );
                } else if ( to == Type.LONG_TYPE ) {
                    this.mv.visitInsn( Opcodes.D2L );
                } else {
                    this.mv.visitInsn( Opcodes.D2I );
                    cast( Type.INT_TYPE,
                          to );
                }
            } else if ( from == Type.FLOAT_TYPE ) {
                if ( to == Type.DOUBLE_TYPE ) {
                    this.mv.visitInsn( Opcodes.F2D );
                } else if ( to == Type.LONG_TYPE ) {
                    this.mv.visitInsn( Opcodes.F2L );
                } else {
                    this.mv.visitInsn( Opcodes.F2I );
                    cast( Type.INT_TYPE,
                          to );
                }
            } else if ( from == Type.LONG_TYPE ) {
                if ( to == Type.DOUBLE_TYPE ) {
                    this.mv.visitInsn( Opcodes.L2D );
                } else if ( to == Type.FLOAT_TYPE ) {
                    this.mv.visitInsn( Opcodes.L2F );
                } else {
                    this.mv.visitInsn( Opcodes.L2I );
                    cast( Type.INT_TYPE,
                          to );
                }
            } else {
                if ( to == Type.BYTE_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2B );
                } else if ( to == Type.CHAR_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2C );
                } else if ( to == Type.DOUBLE_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2D );
                } else if ( to == Type.FLOAT_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2F );
                } else if ( to == Type.LONG_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2L );
                } else if ( to == Type.SHORT_TYPE ) {
                    this.mv.visitInsn( Opcodes.I2S );
                }
            }
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to do boxing and unboxing operations
    // ------------------------------------------------------------------------

    /**
     * Generates the instructions to box the top stack value. This value is
     * replaced by its boxed equivalent on top of the stack.
     *
     * @param type the type of the top stack value.
     */
    public void box(final Type type) {
        if ( type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY ) {
            return;
        }
        if ( type == Type.VOID_TYPE ) {
            push( (String) null );
        } else {
            Type boxed = type;
            switch ( type.getSort() ) {
                case Type.BYTE :
                    boxed = GeneratorAdapter.BYTE_TYPE;
                    break;
                case Type.BOOLEAN :
                    boxed = GeneratorAdapter.BOOLEAN_TYPE;
                    break;
                case Type.SHORT :
                    boxed = GeneratorAdapter.SHORT_TYPE;
                    break;
                case Type.CHAR :
                    boxed = GeneratorAdapter.CHARACTER_TYPE;
                    break;
                case Type.INT :
                    boxed = GeneratorAdapter.INTEGER_TYPE;
                    break;
                case Type.FLOAT :
                    boxed = GeneratorAdapter.FLOAT_TYPE;
                    break;
                case Type.LONG :
                    boxed = GeneratorAdapter.LONG_TYPE;
                    break;
                case Type.DOUBLE :
                    boxed = GeneratorAdapter.DOUBLE_TYPE;
                    break;
            }
            newInstance( boxed );
            if ( type.getSize() == 2 ) {
                // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
                dupX2();
                dupX2();
                pop();
            } else {
                // p -> po -> opo -> oop -> o
                dupX1();
                swap();
            }
            invokeConstructor( boxed,
                               new Method( "<init>",
                                           Type.VOID_TYPE,
                                           new Type[]{type} ) );
        }
    }

    /**
     * Generates the instructions to unbox the top stack value. This value is
     * replaced by its unboxed equivalent on top of the stack.
     *
     * @param type the type of the top stack value.
     */
    public void unbox(final Type type) {
        Type t = GeneratorAdapter.NUMBER_TYPE;
        Method sig = null;
        switch ( type.getSort() ) {
            case Type.VOID :
                return;
            case Type.CHAR :
                t = GeneratorAdapter.CHARACTER_TYPE;
                sig = GeneratorAdapter.CHAR_VALUE;
                break;
            case Type.BOOLEAN :
                t = GeneratorAdapter.BOOLEAN_TYPE;
                sig = GeneratorAdapter.BOOLEAN_VALUE;
                break;
            case Type.DOUBLE :
                sig = GeneratorAdapter.DOUBLE_VALUE;
                break;
            case Type.FLOAT :
                sig = GeneratorAdapter.FLOAT_VALUE;
                break;
            case Type.LONG :
                sig = GeneratorAdapter.LONG_VALUE;
                break;
            case Type.INT :
            case Type.SHORT :
            case Type.BYTE :
                sig = GeneratorAdapter.INT_VALUE;
        }
        if ( sig == null ) {
            checkCast( type );
        } else {
            checkCast( t );
            invokeVirtual( t,
                           sig );
        }
    }

    // ------------------------------------------------------------------------
    // Instructions to jump to other instructions
    // ------------------------------------------------------------------------

    /**
     * Creates a new {@link Label}.
     *
     * @return a new {@link Label}.
     */
    public Label newLabel() {
        return new Label();
    }

    /**
     * Marks the current code position with the given label.
     *
     * @param label a label.
     */
    public void mark(final Label label) {
        this.mv.visitLabel( label );
    }

    /**
     * Marks the current code position with a new label.
     *
     * @return the label that was created to mark the current code position.
     */
    public Label mark() {
        final Label label = new Label();
        this.mv.visitLabel( label );
        return label;
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top two stack values.
     *
     * @param type the type of the top two stack values.
     * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
     *        LE.
     * @param label where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifCmp(final Type type,
                      final int mode,
                      final Label label) {
        int intOp = -1;
        int jumpMode = mode;
        switch ( mode ) {
            case GE :
                jumpMode = GeneratorAdapter.LT;
                break;
            case LE :
                jumpMode = GeneratorAdapter.GT;
                break;
        }
        switch ( type.getSort() ) {
            case Type.LONG :
                this.mv.visitInsn( Opcodes.LCMP );
                break;
            case Type.DOUBLE :
                this.mv.visitInsn( Opcodes.DCMPG );
                break;
            case Type.FLOAT :
                this.mv.visitInsn( Opcodes.FCMPG );
                break;
            case Type.ARRAY :
            case Type.OBJECT :
                switch ( mode ) {
                    case EQ :
                        this.mv.visitJumpInsn( Opcodes.IF_ACMPEQ,
                                               label );
                        return;
                    case NE :
                        this.mv.visitJumpInsn( Opcodes.IF_ACMPNE,
                                               label );
                        return;
                }
                throw new IllegalArgumentException( "Bad comparison for type " + type );
            default :
                switch ( mode ) {
                    case EQ :
                        intOp = Opcodes.IF_ICMPEQ;
                        break;
                    case NE :
                        intOp = Opcodes.IF_ICMPNE;
                        break;
                    case GE :
                        intOp = Opcodes.IF_ICMPGE;
                        break;
                    case LT :
                        intOp = Opcodes.IF_ICMPLT;
                        break;
                    case LE :
                        intOp = Opcodes.IF_ICMPLE;
                        break;
                    case GT :
                        intOp = Opcodes.IF_ICMPGT;
                        break;
                }
                this.mv.visitJumpInsn( intOp,
                                       label );
                return;
        }
        this.mv.visitJumpInsn( jumpMode,
                               label );
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top two integer stack values.
     *
     * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
     *        LE.
     * @param label where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifICmp(final int mode,
                       final Label label) {
        ifCmp( Type.INT_TYPE,
               mode,
               label );
    }

    /**
     * Generates the instructions to jump to a label based on the comparison of
     * the top integer stack value with zero.
     *
     * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT,
     *        LE.
     * @param label where to jump if the comparison result is <tt>true</tt>.
     */
    public void ifZCmp(final int mode,
                       final Label label) {
        this.mv.visitJumpInsn( mode,
                               label );
    }

    /**
     * Generates the instruction to jump to the given label if the top stack
     * value is null.
     *
     * @param label where to jump if the condition is <tt>true</tt>.
     */
    public void ifNull(final Label label) {
        this.mv.visitJumpInsn( Opcodes.IFNULL,
                               label );
    }

    /**
     * Generates the instruction to jump to the given label if the top stack
     * value is not null.
     *
     * @param label where to jump if the condition is <tt>true</tt>.
     */
    public void ifNonNull(final Label label) {
        this.mv.visitJumpInsn( Opcodes.IFNONNULL,
                               label );
    }

    /**
     * Generates the instruction to jump to the given label.
     *
     * @param label where to jump if the condition is <tt>true</tt>.
     */
    public void goTo(final Label label) {
        this.mv.visitJumpInsn( Opcodes.GOTO,
                               label );
    }

    /**
     * Generates a RET instruction.
     *
     * @param local a local variable identifier, as returned by {@link #newLocal
     *        newLocal}.
     */
    public void ret(final int local) {
        this.mv.visitVarInsn( Opcodes.RET,
                              local );
    }

    /**
     * Generates the instructions for a switch statement.
     *
     * @param keys the switch case keys.
     * @param generator a generator to generate the code for the switch cases.
     */
    public void tableSwitch(final int[] keys,
                            final TableSwitchGenerator generator) {
        float density;
        if ( keys.length == 0 ) {
            density = 0;
        } else {
            density = (float) keys.length / (keys[keys.length - 1] - keys[0] + 1);
        }
        tableSwitch( keys,
                     generator,
                     density >= 0.5f );
    }

    /**
     * Generates the instructions for a switch statement.
     *
     * @param keys the switch case keys.
     * @param generator a generator to generate the code for the switch cases.
     * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or
     *        <tt>false</tt> to use a LOOKUPSWITCH instruction.
     */
    public void tableSwitch(final int[] keys,
                            final TableSwitchGenerator generator,
                            final boolean useTable) {
        for ( int i = 1; i < keys.length; ++i ) {
            if ( keys[i] < keys[i - 1] ) {
                throw new IllegalArgumentException( "keys must be sorted ascending" );
            }
        }
        final Label def = newLabel();
        final Label end = newLabel();
        if ( keys.length > 0 ) {
            final int len = keys.length;
            final int min = keys[0];
            final int max = keys[len - 1];
            final int range = max - min + 1;
            if ( useTable ) {
                final Label[] labels = new Label[range];
                Arrays.fill( labels,
                             def );
                for ( int i = 0; i < len; ++i ) {
                    labels[keys[i] - min] = newLabel();
                }
                this.mv.visitTableSwitchInsn( min,
                                              max,
                                              def,
                                              labels );
                for ( int i = 0; i < range; ++i ) {
                    final Label label = labels[i];
                    if ( label != def ) {
                        mark( label );
                        generator.generateCase( i + min,
                                                end );
                    }
                }
            } else {
                final Label[] labels = new Label[len];
                for ( int i = 0; i < len; ++i ) {
                    labels[i] = newLabel();
                }
                this.mv.visitLookupSwitchInsn( def,
                                               keys,
                                               labels );
                for ( int i = 0; i < len; ++i ) {
                    mark( labels[i] );
                    generator.generateCase( keys[i],
                                            end );
                }
            }
        }
        mark( def );
        generator.generateDefault();
        mark( end );
    }

    /**
     * Generates the instruction to return the top stack value to the caller.
     */
    public void returnValue() {
        this.mv.visitInsn( this.returnType.getOpcode( Opcodes.IRETURN ) );
    }

    // ------------------------------------------------------------------------
    // Instructions to load and store fields
    // ------------------------------------------------------------------------

    /**
     * Generates a get field or set field instruction.
     *
     * @param opcode the instruction's opcode.
     * @param ownerType the class in which the field is defined.
     * @param name the name of the field.
     * @param fieldType the type of the field.
     */
    private void fieldInsn(final int opcode,
                           final Type ownerType,
                           final String name,
                           final Type fieldType) {
        this.mv.visitFieldInsn( opcode,
                                ownerType.getInternalName(),
                                name,
                                fieldType.getDescriptor() );
    }

    /**
     * Generates the instruction to push the value of a static field on the
     * stack.
     *
     * @param owner the class in which the field is defined.
     * @param name the name of the field.
     * @param type the type of the field.
     */
    public void getStatic(final Type owner,
                          final String name,
                          final Type type) {
        fieldInsn( Opcodes.GETSTATIC,
                   owner,
                   name,
                   type );
    }

    /**
     * Generates the instruction to store the top stack value in a static field.
     *
     * @param owner the class in which the field is defined.
     * @param name the name of the field.
     * @param type the type of the field.
     */
    public void putStatic(final Type owner,
                          final String name,
                          final Type type) {
        fieldInsn( Opcodes.PUTSTATIC,
                   owner,
                   name,
                   type );
    }

    /**
     * Generates the instruction to push the value of a non static field on the
     * stack.
     *
     * @param owner the class in which the field is defined.
     * @param name the name of the field.
     * @param type the type of the field.
     */
    public void getField(final Type owner,
                         final String name,
                         final Type type) {
        fieldInsn( Opcodes.GETFIELD,
                   owner,
                   name,
                   type );
    }

    /**
     * Generates the instruction to store the top stack value in a non static
     * field.
     *
     * @param owner the class in which the field is defined.
     * @param name the name of the field.
     * @param type the type of the field.
     */
    public void putField(final Type owner,
                         final String name,
                         final Type type) {
        fieldInsn( Opcodes.PUTFIELD,
                   owner,
                   name,
                   type );
    }

    // ------------------------------------------------------------------------
    // Instructions to invoke methods
    // ------------------------------------------------------------------------

    /**
     * Generates an invoke method instruction.
     *
     * @param opcode the instruction's opcode.
     * @param type the class in which the method is defined.
     * @param method the method to be invoked.
     */
    private void invokeInsn(final int opcode,
                            final Type type,
                            final Method method) {
        final String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
        this.mv.visitMethodInsn( opcode,
                                 owner,
                                 method.getName(),
                                 method.getDescriptor() );
    }

    /**
     * Generates the instruction to invoke a normal method.
     *
     * @param owner the class in which the method is defined.
     * @param method the method to be invoked.
     */
    public void invokeVirtual(final Type owner,
                              final Method method) {
        invokeInsn( Opcodes.INVOKEVIRTUAL,
                    owner,
                    method );
    }

    /**
     * Generates the instruction to invoke a constructor.
     *
     * @param type the class in which the constructor is defined.
     * @param method the constructor to be invoked.
     */
    public void invokeConstructor(final Type type,
                                  final Method method) {
        invokeInsn( Opcodes.INVOKESPECIAL,
                    type,
                    method );
    }

    /**
     * Generates the instruction to invoke a static method.
     *
     * @param owner the class in which the method is defined.
     * @param method the method to be invoked.
     */
    public void invokeStatic(final Type owner,
                             final Method method) {
        invokeInsn( Opcodes.INVOKESTATIC,
                    owner,
                    method );
    }

    /**
     * Generates the instruction to invoke an interface method.
     *
     * @param owner the class in which the method is defined.
     * @param method the method to be invoked.
     */
    public void invokeInterface(final Type owner,
                                final Method method) {
        invokeInsn( Opcodes.INVOKEINTERFACE,
                    owner,
                    method );
    }

    // ------------------------------------------------------------------------
    // Instructions to create objects and arrays
    // ------------------------------------------------------------------------

    /**
     * Generates a type dependent instruction.
     *
     * @param opcode the instruction's opcode.
     * @param type the instruction's operand.
     */
    private void typeInsn(final int opcode,
                          final Type type) {
        String desc;
        if ( type.getSort() == Type.ARRAY ) {
            desc = type.getDescriptor();
        } else {
            desc = type.getInternalName();
        }
        this.mv.visitTypeInsn( opcode,
                               desc );
    }

    /**
     * Generates the instruction to create a new object.
     *
     * @param type the class of the object to be created.
     */
    public void newInstance(final Type type) {
        typeInsn( Opcodes.NEW,
                  type );
    }

    /**
     * Generates the instruction to create a new array.
     *
     * @param type the type of the array elements.
     */
    public void newArray(final Type type) {
        int typ;
        switch ( type.getSort() ) {
            case Type.BOOLEAN :
                typ = Opcodes.T_BOOLEAN;
                break;
            case Type.CHAR :
                typ = Opcodes.T_CHAR;
                break;
            case Type.BYTE :
                typ = Opcodes.T_BYTE;
                break;
            case Type.SHORT :
                typ = Opcodes.T_SHORT;
                break;
            case Type.INT :
                typ = Opcodes.T_INT;
                break;
            case Type.FLOAT :
                typ = Opcodes.T_FLOAT;
                break;
            case Type.LONG :
                typ = Opcodes.T_LONG;
                break;
            case Type.DOUBLE :
                typ = Opcodes.T_DOUBLE;
                break;
            default :
                typeInsn( Opcodes.ANEWARRAY,
                          type );
                return;
        }
        this.mv.visitIntInsn( Opcodes.NEWARRAY,
                              typ );
    }

    // ------------------------------------------------------------------------
    // Miscelaneous instructions
    // ------------------------------------------------------------------------

    /**
     * Generates the instruction to compute the length of an array.
     */
    public void arrayLength() {
        this.mv.visitInsn( Opcodes.ARRAYLENGTH );
    }

    /**
     * Generates the instruction to throw an exception.
     */
    public void throwException() {
        this.mv.visitInsn( Opcodes.ATHROW );
    }

    /**
     * Generates the instructions to create and throw an exception. The
     * exception class must have a constructor with a single String argument.
     *
     * @param type the class of the exception to be thrown.
     * @param msg the detailed message of the exception.
     */
    public void throwException(final Type type,
                               final String msg) {
        newInstance( type );
        dup();
        push( msg );
        invokeConstructor( type,
                           Method.getMethod( "void <init> (String)" ) );
        throwException();
    }

    /**
     * Generates the instruction to check that the top stack value is of the
     * given type.
     *
     * @param type a class or interface type.
     */
    public void checkCast(final Type type) {
        if ( !type.equals( GeneratorAdapter.OBJECT_TYPE ) ) {
            typeInsn( Opcodes.CHECKCAST,
                      type );
        }
    }

    /**
     * Generates the instruction to test if the top stack value is of the given
     * type.
     *
     * @param type a class or interface type.
     */
    public void instanceOf(final Type type) {
        typeInsn( Opcodes.INSTANCEOF,
                  type );
    }

    /**
     * Generates the instruction to get the monitor of the top stack value.
     */
    public void monitorEnter() {
        this.mv.visitInsn( Opcodes.MONITORENTER );
    }

    /**
     * Generates the instruction to release the monitor of the top stack value.
     */
    public void monitorExit() {
        this.mv.visitInsn( Opcodes.MONITOREXIT );
    }

    // ------------------------------------------------------------------------
    // Non instructions
    // ------------------------------------------------------------------------

    /**
     * Marks the end of the visited method.
     */
    public void endMethod() {
        if ( (this.access & Opcodes.ACC_ABSTRACT) == 0 ) {
            this.mv.visitMaxs( 0,
                               0 );
        }
    }

    /**
     * Marks the start of an exception handler.
     *
     * @param start beginning of the exception handler's scope (inclusive).
     * @param end end of the exception handler's scope (exclusive).
     * @param exception internal name of the type of exceptions handled by the
     *        handler.
     */
    public void catchException(final Label start,
                               final Label end,
                               final Type exception) {
        this.mv.visitTryCatchBlock( start,
                                    end,
                                    mark(),
                                    exception.getInternalName() );
    }
}
TOP

Related Classes of org.drools.asm.commons.GeneratorAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.