| /*** |
| * ASM: a very small and fast Java bytecode manipulation framework |
| * Copyright (c) 2000-2007 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 com.google.gwt.dev.asm.commons; |
| |
| import com.google.gwt.dev.asm.Label; |
| import com.google.gwt.dev.asm.MethodVisitor; |
| import com.google.gwt.dev.asm.Opcodes; |
| import com.google.gwt.dev.asm.Type; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A {@link com.google.gwt.dev.asm.MethodAdapter} to insert before, after and around |
| * advices in methods and constructors. <p> The behavior for constructors 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 List stackFrame; |
| private Map 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}). |
| */ |
| protected AdviceAdapter( |
| final MethodVisitor mv, |
| final int access, |
| final String name, |
| final String desc) |
| { |
| super(mv, access, name, desc); |
| methodAccess = access; |
| methodDesc = desc; |
| |
| constructor = "<init>".equals(name); |
| } |
| |
| public void visitCode() { |
| mv.visitCode(); |
| if (constructor) { |
| stackFrame = new ArrayList(); |
| branches = new HashMap(); |
| } else { |
| superInitialized = true; |
| onMethodEnter(); |
| } |
| } |
| |
| public void visitLabel(final Label label) { |
| mv.visitLabel(label); |
| |
| if (constructor && branches != null) { |
| List frame = (List) branches.get(label); |
| if (frame != null) { |
| stackFrame = frame; |
| branches.remove(label); |
| } |
| } |
| } |
| |
| 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); |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| 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(); |
| if (longOrDouble) { |
| popValue(); |
| popValue(); |
| } |
| break; |
| // case GETFIELD: |
| default: |
| if (longOrDouble) { |
| pushValue(OTHER); |
| } |
| } |
| } |
| } |
| |
| public void visitIntInsn(final int opcode, final int operand) { |
| mv.visitIntInsn(opcode, operand); |
| |
| if (constructor && opcode!=NEWARRAY) { |
| pushValue(OTHER); |
| } |
| } |
| |
| public void visitLdcInsn(final Object cst) { |
| mv.visitLdcInsn(cst); |
| |
| if (constructor) { |
| pushValue(OTHER); |
| if (cst instanceof Double || cst instanceof Long) { |
| pushValue(OTHER); |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| public void visitMethodInsn( |
| final int opcode, |
| final String owner, |
| final String name, |
| final String desc) |
| { |
| mv.visitMethodInsn(opcode, owner, name, desc); |
| |
| 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); |
| } |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| public void visitLookupSwitchInsn( |
| final Label dflt, |
| final int[] keys, |
| final Label[] labels) |
| { |
| mv.visitLookupSwitchInsn(dflt, keys, labels); |
| |
| if (constructor) { |
| popValue(); |
| addBranches(dflt, labels); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| 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 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 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 void onMethodExit(int opcode) { |
| } |
| |
| // TODO onException, onMethodCall |
| |
| } |