Package org.drools.asm.commons

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

/***
* 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.HashMap;

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

/**
* A <code>MethodAdapter</code> to dispatch method body instruction
* <p>
* The behavior is like this:
* <ol>
*
* <li>as long as the INVOKESPECIAL for the object initialization has not been
*     reached, every bytecode instruction is dispatched in the ctor code visitor</li>
*
* <li>when this one is reached, it is only added in the ctor code visitor and
*     a JP invoke is added</li>
* <li>after that, only the other code visitor receives the instructions</li>
*
* </ol>
*
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public abstract class AdviceAdapter extends GeneratorAdapter
    implements
    Opcodes {
    private static final Object THIS  = new Object();
    private static final Object OTHER = new Object();

    protected int               methodAccess;
    protected String            methodDesc;

    private boolean             constructor;
    private boolean             superInitialized;
    private ArrayList           stackFrame;
    private HashMap             branches;

    /**
     * Creates a new {@link AdviceAdapter}.
     *
     * @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 AdviceAdapter(final MethodVisitor mv,
                         final int access,
                         final String name,
                         final String desc) {
        super( mv,
               access,
               name,
               desc );
        this.methodAccess = access;
        this.methodDesc = desc;

        this.constructor = "<init>".equals( name );
        if ( !this.constructor ) {
            this.superInitialized = true;
            onMethodEnter();
        } else {
            this.stackFrame = new ArrayList();
            this.branches = new HashMap();
        }
    }

    public void visitLabel(final Label label) {
        this.mv.visitLabel( label );

        if ( this.constructor && this.branches != null ) {
            final ArrayList frame = (ArrayList) this.branches.get( label );
            if ( frame != null ) {
                this.stackFrame = frame;
                this.branches.remove( label );
            }
        }
    }

    public void visitInsn(final int opcode) {
        if ( this.constructor ) {
            switch ( opcode ) {
                case RETURN : // empty stack
                    onMethodExit( opcode );
                    break;

                case IRETURN : // 1 before n/a after
                case FRETURN : // 1 before n/a after
                case ARETURN : // 1 before n/a after
                case ATHROW : // 1 before n/a after
                    popValue();
                    popValue();
                    onMethodExit( opcode );
                    break;

                case LRETURN : // 2 before n/a after
                case DRETURN : // 2 before n/a after
                    popValue();
                    popValue();
                    onMethodExit( opcode );
                    break;

                case NOP :
                case LALOAD : // remove 2 add 2
                case DALOAD : // remove 2 add 2
                case LNEG :
                case DNEG :
                case FNEG :
                case INEG :
                case L2D :
                case D2L :
                case F2I :
                case I2B :
                case I2C :
                case I2S :
                case I2F :
                case Opcodes.ARRAYLENGTH :
                    break;

                case ACONST_NULL :
                case ICONST_M1 :
                case ICONST_0 :
                case ICONST_1 :
                case ICONST_2 :
                case ICONST_3 :
                case ICONST_4 :
                case ICONST_5 :
                case FCONST_0 :
                case FCONST_1 :
                case FCONST_2 :
                case F2L : // 1 before 2 after
                case F2D :
                case I2L :
                case I2D :
                    pushValue( AdviceAdapter.OTHER );
                    break;

                case LCONST_0 :
                case LCONST_1 :
                case DCONST_0 :
                case DCONST_1 :
                    pushValue( AdviceAdapter.OTHER );
                    pushValue( AdviceAdapter.OTHER );
                    break;

                case IALOAD : // remove 2 add 1
                case FALOAD : // remove 2 add 1
                case AALOAD : // remove 2 add 1
                case BALOAD : // remove 2 add 1
                case CALOAD : // remove 2 add 1
                case SALOAD : // remove 2 add 1
                case POP :
                case IADD :
                case FADD :
                case ISUB :
                case LSHL : // 3 before 2 after
                case LSHR : // 3 before 2 after
                case LUSHR : // 3 before 2 after
                case L2I : // 2 before 1 after
                case L2F : // 2 before 1 after
                case D2I : // 2 before 1 after
                case D2F : // 2 before 1 after
                case FSUB :
                case FMUL :
                case FDIV :
                case FREM :
                case FCMPL : // 2 before 1 after
                case FCMPG : // 2 before 1 after
                case IMUL :
                case IDIV :
                case IREM :
                case ISHL :
                case ISHR :
                case IUSHR :
                case IAND :
                case IOR :
                case IXOR :
                case MONITORENTER :
                case MONITOREXIT :
                    popValue();
                    break;

                case POP2 :
                case LSUB :
                case LMUL :
                case LDIV :
                case LREM :
                case LADD :
                case LAND :
                case LOR :
                case LXOR :
                case DADD :
                case DMUL :
                case DSUB :
                case DDIV :
                case DREM :
                    popValue();
                    popValue();
                    break;

                case IASTORE :
                case FASTORE :
                case AASTORE :
                case BASTORE :
                case CASTORE :
                case SASTORE :
                case LCMP : // 4 before 1 after
                case DCMPL :
                case DCMPG :
                    popValue();
                    popValue();
                    popValue();
                    break;

                case LASTORE :
                case DASTORE :
                    popValue();
                    popValue();
                    popValue();
                    popValue();
                    break;

                case DUP :
                    pushValue( peekValue() );
                    break;

                case DUP_X1 :
                    // TODO optimize this
                {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    pushValue( o1 );
                    pushValue( o2 );
                    pushValue( o1 );
                }
                    break;

                case DUP_X2 :
                    // TODO optimize this
                {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    final Object o3 = popValue();
                    pushValue( o1 );
                    pushValue( o3 );
                    pushValue( o2 );
                    pushValue( o1 );
                }
                    break;

                case DUP2 :
                    // TODO optimize this
                {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    pushValue( o2 );
                    pushValue( o1 );
                    pushValue( o2 );
                    pushValue( o1 );
                }
                    break;

                case DUP2_X1 :
                    // TODO optimize this
                {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    final Object o3 = popValue();
                    pushValue( o2 );
                    pushValue( o1 );
                    pushValue( o3 );
                    pushValue( o2 );
                    pushValue( o1 );
                }
                    break;

                case DUP2_X2 :
                    // TODO optimize this
                {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    final Object o3 = popValue();
                    final Object o4 = popValue();
                    pushValue( o2 );
                    pushValue( o1 );
                    pushValue( o4 );
                    pushValue( o3 );
                    pushValue( o2 );
                    pushValue( o1 );
                }
                    break;

                case SWAP : {
                    final Object o1 = popValue();
                    final Object o2 = popValue();
                    pushValue( o1 );
                    pushValue( o2 );
                }
                    break;
            }
        } else {
            switch ( opcode ) {
                case RETURN :
                case IRETURN :
                case FRETURN :
                case ARETURN :
                case LRETURN :
                case DRETURN :
                case ATHROW :
                    onMethodExit( opcode );
                    break;
            }
        }
        this.mv.visitInsn( opcode );
    }

    public void visitVarInsn(final int opcode,
                             final int var) {
        super.visitVarInsn( opcode,
                            var );

        if ( this.constructor ) {
            switch ( opcode ) {
                case ILOAD :
                case FLOAD :
                    pushValue( AdviceAdapter.OTHER );
                    break;
                case LLOAD :
                case DLOAD :
                    pushValue( AdviceAdapter.OTHER );
                    pushValue( AdviceAdapter.OTHER );
                    break;
                case ALOAD :
                    pushValue( var == 0 ? AdviceAdapter.THIS : AdviceAdapter.OTHER );
                    break;
                case ASTORE :
                case ISTORE :
                case FSTORE :
                    popValue();
                    break;
                case LSTORE :
                case DSTORE :
                    popValue();
                    popValue();
                    break;
            }
        }
    }

    public void visitFieldInsn(final int opcode,
                               final String owner,
                               final String name,
                               final String desc) {
        this.mv.visitFieldInsn( opcode,
                                owner,
                                name,
                                desc );

        if ( this.constructor ) {
            final char c = desc.charAt( 0 );
            final boolean longOrDouble = c == 'J' || c == 'D';
            switch ( opcode ) {
                case GETSTATIC :
                    pushValue( AdviceAdapter.OTHER );
                    if ( longOrDouble ) {
                        pushValue( AdviceAdapter.OTHER );
                    }
                    break;
                case PUTSTATIC :
                    popValue();
                    if ( longOrDouble ) {
                        popValue();
                    }
                    break;
                case PUTFIELD :
                    popValue();
                    if ( longOrDouble ) {
                        popValue();
                        popValue();
                    }
                    break;
                // case GETFIELD:
                default :
                    if ( longOrDouble ) {
                        pushValue( AdviceAdapter.OTHER );
                    }
            }
        }
    }

    public void visitIntInsn(final int opcode,
                             final int operand) {
        this.mv.visitIntInsn( opcode,
                              operand );

        if ( this.constructor ) {
            switch ( opcode ) {
                case BIPUSH :
                case SIPUSH :
                    pushValue( AdviceAdapter.OTHER );
            }
        }
    }

    public void visitLdcInsn(final Object cst) {
        this.mv.visitLdcInsn( cst );

        if ( this.constructor ) {
            pushValue( AdviceAdapter.OTHER );
            if ( cst instanceof Double || cst instanceof Long ) {
                pushValue( AdviceAdapter.OTHER );
            }
        }
    }

    public void visitMultiANewArrayInsn(final String desc,
                                        final int dims) {
        this.mv.visitMultiANewArrayInsn( desc,
                                         dims );

        if ( this.constructor ) {
            for ( int i = 0; i < dims; i++ ) {
                popValue();
            }
            pushValue( AdviceAdapter.OTHER );
        }
    }

    public void visitTypeInsn(final int opcode,
                              final String name) {
        this.mv.visitTypeInsn( opcode,
                               name );

        // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
        if ( this.constructor && opcode == Opcodes.NEW ) {
            pushValue( AdviceAdapter.OTHER );
        }
    }

    public void visitMethodInsn(final int opcode,
                                final String owner,
                                final String name,
                                final String desc) {
        this.mv.visitMethodInsn( opcode,
                                 owner,
                                 name,
                                 desc );

        if ( this.constructor ) {
            final Type[] types = Type.getArgumentTypes( desc );
            for ( int i = 0; i < types.length; i++ ) {
                popValue();
                if ( types[i].getSize() == 2 ) {
                    popValue();
                }
            }
            switch ( opcode ) {
                // case INVOKESTATIC:
                // break;

                case INVOKEINTERFACE :
                case INVOKEVIRTUAL :
                    popValue(); // objectref
                    break;

                case INVOKESPECIAL :
                    final Object type = popValue(); // objectref
                    if ( type == AdviceAdapter.THIS && !this.superInitialized ) {
                        onMethodEnter();
                        this.superInitialized = true;
                        // once super has been initialized it is no longer
                        // necessary to keep track of stack state                       
                        this.constructor = false;
                    }
                    break;
            }

            final Type returnType = Type.getReturnType( desc );
            if ( returnType != Type.VOID_TYPE ) {
                pushValue( AdviceAdapter.OTHER );
                if ( returnType.getSize() == 2 ) {
                    pushValue( AdviceAdapter.OTHER );
                }
            }
        }
    }

    public void visitJumpInsn(final int opcode,
                              final Label label) {
        this.mv.visitJumpInsn( opcode,
                               label );

        if ( this.constructor ) {
            switch ( opcode ) {
                case IFEQ :
                case IFNE :
                case IFLT :
                case IFGE :
                case IFGT :
                case IFLE :
                case IFNULL :
                case IFNONNULL :
                    popValue();
                    break;

                case IF_ICMPEQ :
                case IF_ICMPNE :
                case IF_ICMPLT :
                case IF_ICMPGE :
                case IF_ICMPGT :
                case IF_ICMPLE :
                case IF_ACMPEQ :
                case IF_ACMPNE :
                    popValue();
                    popValue();
                    break;

                case JSR :
                    pushValue( AdviceAdapter.OTHER );
                    break;
            }
            addBranch( label );
        }
    }

    public void visitLookupSwitchInsn(final Label dflt,
                                      final int[] keys,
                                      final Label[] labels) {
        this.mv.visitLookupSwitchInsn( dflt,
                                       keys,
                                       labels );

        if ( this.constructor ) {
            popValue();
            addBranches( dflt,
                         labels );
        }
    }

    public void visitTableSwitchInsn(final int min,
                                     final int max,
                                     final Label dflt,
                                     final Label[] labels) {
        this.mv.visitTableSwitchInsn( min,
                                      max,
                                      dflt,
                                      labels );

        if ( this.constructor ) {
            popValue();
            addBranches( dflt,
                         labels );
        }
    }

    private void addBranches(final Label dflt,
                             final Label[] labels) {
        addBranch( dflt );
        for ( int i = 0; i < labels.length; i++ ) {
            addBranch( labels[i] );
        }
    }

    private void addBranch(final Label label) {
        if ( this.branches.containsKey( label ) ) {
            return;
        }
        final ArrayList frame = new ArrayList();
        frame.addAll( this.stackFrame );
        this.branches.put( label,
                           frame );
    }

    private Object popValue() {
        return this.stackFrame.remove( this.stackFrame.size() - 1 );
    }

    private Object peekValue() {
        return this.stackFrame.get( this.stackFrame.size() - 1 );
    }

    private void pushValue(final Object o) {
        this.stackFrame.add( o );
    }

    /**
     * Called at the beginning of the method or after super
     * class class call in the constructor.
     * <br><br>
     *
     * <i>Custom code can use or change all the local variables,
     * but should not change state of the stack.</i>
     */
    protected abstract void onMethodEnter();

    /**
     * Called before explicit exit from the method using either
     * return or throw. Top element on the stack contains the
     * return value or exception instance. For example:
     *
     * <pre>
     *   public void onMethodExit(int opcode) {
     *     if(opcode==RETURN) {
     *         visitInsn(ACONST_NULL);
     *     } else if(opcode==ARETURN || opcode==ATHROW) {
     *         dup();
     *     } else {
     *         if(opcode==LRETURN || opcode==DRETURN) {
     *             dup2();
     *         } else {
     *             dup();
     *         }
     *         box(Type.getReturnType(this.methodDesc));
     *     }
     *     visitIntInsn(SIPUSH, opcode);
     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
     *   }
     *
     *   // an actual call back method
     *   public static void onExit(int opcode, Object param) {
     *     ...
     * </pre>
     *
     * <br><br>
     *
     * <i>Custom code can use or change all the local variables,
     * but should not change state of the stack.</i>
     *
     * @param opcode one of the RETURN, IRETURN, FRETURN,
     *   ARETURN, LRETURN, DRETURN or ATHROW
     *
     */
    protected abstract void onMethodExit(int opcode);

    // TODO onException, onMethodCall

}
TOP

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

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.