All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.snapscript.asm.commons.AdviceAdapter Maven / Gradle / Ivy

/***
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2011 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.snapscript.asm.commons;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.snapscript.asm.Handle;
import org.snapscript.asm.Label;
import org.snapscript.asm.MethodVisitor;
import org.snapscript.asm.Opcodes;
import org.snapscript.asm.Type;

/**
 * A {@link org.snapscript.asm.MethodVisitor} to insert before, after and around
 * advices in methods and constructors.
 * 

* The behavior for constructors is like this: *

    * *
  1. as long as the INVOKESPECIAL for the object initialization has not been * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. * *
  3. when this one is reached, it is only added in the ctor code visitor and a * JP invoke is added
  4. * *
  5. after that, only the other code visitor receives the instructions
  6. * *
* * @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 List stackFrame; private Map> branches; /** * Creates a new {@link AdviceAdapter}. * * @param api * the ASM API version implemented by this visitor. Must be one * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @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}). */ protected AdviceAdapter(final int api, final MethodVisitor mv, final int access, final String name, final String desc) { super(api, mv, access, name, desc); methodAccess = access; methodDesc = desc; constructor = "".equals(name); } @Override public void visitCode() { mv.visitCode(); if (constructor) { stackFrame = new ArrayList(); branches = new HashMap>(); } else { superInitialized = true; onMethodEnter(); } } @Override public void visitLabel(final Label label) { mv.visitLabel(label); if (constructor && branches != null) { List frame = branches.get(label); if (frame != null) { stackFrame = frame; branches.remove(label); } } } @Override public void visitInsn(final int opcode) { if (constructor) { int s; 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(); 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 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(OTHER); break; case LCONST_0: case LCONST_1: case DCONST_0: case DCONST_1: pushValue(OTHER); pushValue(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: s = stackFrame.size(); stackFrame.add(s - 2, stackFrame.get(s - 1)); break; case DUP_X2: s = stackFrame.size(); stackFrame.add(s - 3, stackFrame.get(s - 1)); break; case DUP2: s = stackFrame.size(); stackFrame.add(s - 2, stackFrame.get(s - 1)); stackFrame.add(s - 2, stackFrame.get(s - 1)); break; case DUP2_X1: s = stackFrame.size(); stackFrame.add(s - 3, stackFrame.get(s - 1)); stackFrame.add(s - 3, stackFrame.get(s - 1)); break; case DUP2_X2: s = stackFrame.size(); stackFrame.add(s - 4, stackFrame.get(s - 1)); stackFrame.add(s - 4, stackFrame.get(s - 1)); break; case SWAP: s = stackFrame.size(); stackFrame.add(s - 2, stackFrame.get(s - 1)); stackFrame.remove(s); break; } } else { switch (opcode) { case RETURN: case IRETURN: case FRETURN: case ARETURN: case LRETURN: case DRETURN: case ATHROW: onMethodExit(opcode); break; } } mv.visitInsn(opcode); } @Override public void visitVarInsn(final int opcode, final int var) { super.visitVarInsn(opcode, var); if (constructor) { switch (opcode) { case ILOAD: case FLOAD: pushValue(OTHER); break; case LLOAD: case DLOAD: pushValue(OTHER); pushValue(OTHER); break; case ALOAD: pushValue(var == 0 ? THIS : OTHER); break; case ASTORE: case ISTORE: case FSTORE: popValue(); break; case LSTORE: case DSTORE: popValue(); popValue(); break; } } } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { mv.visitFieldInsn(opcode, owner, name, desc); if (constructor) { char c = desc.charAt(0); boolean longOrDouble = c == 'J' || c == 'D'; switch (opcode) { case GETSTATIC: pushValue(OTHER); if (longOrDouble) { pushValue(OTHER); } break; case PUTSTATIC: popValue(); if (longOrDouble) { popValue(); } break; case PUTFIELD: popValue(); popValue(); if (longOrDouble) { popValue(); } break; // case GETFIELD: default: if (longOrDouble) { pushValue(OTHER); } } } } @Override public void visitIntInsn(final int opcode, final int operand) { mv.visitIntInsn(opcode, operand); if (constructor && opcode != NEWARRAY) { pushValue(OTHER); } } @Override public void visitLdcInsn(final Object cst) { mv.visitLdcInsn(cst); if (constructor) { pushValue(OTHER); if (cst instanceof Double || cst instanceof Long) { pushValue(OTHER); } } } @Override public void visitMultiANewArrayInsn(final String desc, final int dims) { mv.visitMultiANewArrayInsn(desc, dims); if (constructor) { for (int i = 0; i < dims; i++) { popValue(); } pushValue(OTHER); } } @Override public void visitTypeInsn(final int opcode, final String type) { mv.visitTypeInsn(opcode, type); // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack if (constructor && opcode == NEW) { pushValue(OTHER); } } @Deprecated @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { if (api >= Opcodes.ASM5) { super.visitMethodInsn(opcode, owner, name, desc); return; } doVisitMethodInsn(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE); } @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) { if (api < Opcodes.ASM5) { super.visitMethodInsn(opcode, owner, name, desc, itf); return; } doVisitMethodInsn(opcode, owner, name, desc, itf); } private void doVisitMethodInsn(int opcode, final String owner, final String name, final String desc, final boolean itf) { mv.visitMethodInsn(opcode, owner, name, desc, itf); if (constructor) { 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: Object type = popValue(); // objectref if (type == THIS && !superInitialized) { onMethodEnter(); superInitialized = true; // once super has been initialized it is no longer // necessary to keep track of stack state constructor = false; } break; } Type returnType = Type.getReturnType(desc); if (returnType != Type.VOID_TYPE) { pushValue(OTHER); if (returnType.getSize() == 2) { pushValue(OTHER); } } } } @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); if (constructor) { Type[] types = Type.getArgumentTypes(desc); for (int i = 0; i < types.length; i++) { popValue(); if (types[i].getSize() == 2) { popValue(); } } Type returnType = Type.getReturnType(desc); if (returnType != Type.VOID_TYPE) { pushValue(OTHER); if (returnType.getSize() == 2) { pushValue(OTHER); } } } } @Override public void visitJumpInsn(final int opcode, final Label label) { mv.visitJumpInsn(opcode, label); if (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(OTHER); break; } addBranch(label); } } @Override public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { mv.visitLookupSwitchInsn(dflt, keys, labels); if (constructor) { popValue(); addBranches(dflt, labels); } } @Override public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { mv.visitTableSwitchInsn(min, max, dflt, labels); if (constructor) { popValue(); addBranches(dflt, labels); } } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { super.visitTryCatchBlock(start, end, handler, type); if (constructor && !branches.containsKey(handler)) { List stackFrame = new ArrayList(); stackFrame.add(OTHER); branches.put(handler, stackFrame); } } 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 (branches.containsKey(label)) { return; } branches.put(label, new ArrayList(stackFrame)); } private Object popValue() { return stackFrame.remove(stackFrame.size() - 1); } private Object peekValue() { return stackFrame.get(stackFrame.size() - 1); } private void pushValue(final Object o) { stackFrame.add(o); } /** * Called at the beginning of the method or after super class call in * the constructor.
*
* * Custom code can use or change all the local variables, but should not * change state of the stack. */ protected 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: * *
     *   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(Object param, int opcode) {
     *     ...
     * 
* *
*
* * Custom code can use or change all the local variables, but should not * change state of the stack. * * @param opcode * one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN * or ATHROW * */ protected void onMethodExit(int opcode) { } // TODO onException, onMethodCall }