1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Oracle designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Oracle in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25/*
26 * This file is available under and governed by the GNU General Public
27 * License version 2 only, as published by the Free Software Foundation.
28 * However, the following notice accompanied the original version of this
29 * file:
30 *
31 * ASM: a very small and fast Java bytecode manipulation framework
32 * Copyright (c) 2000-2011 INRIA, France Telecom
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 *    notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 *    notice, this list of conditions and the following disclaimer in the
42 *    documentation and/or other materials provided with the distribution.
43 * 3. Neither the name of the copyright holders nor the names of its
44 *    contributors may be used to endorse or promote products derived from
45 *    this software without specific prior written permission.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
57 * THE POSSIBILITY OF SUCH DAMAGE.
58 */
59package jdk.internal.org.objectweb.asm.commons;
60
61import java.util.ArrayList;
62import java.util.HashMap;
63import java.util.List;
64import java.util.Map;
65
66import jdk.internal.org.objectweb.asm.Handle;
67import jdk.internal.org.objectweb.asm.Label;
68import jdk.internal.org.objectweb.asm.MethodVisitor;
69import jdk.internal.org.objectweb.asm.Opcodes;
70import jdk.internal.org.objectweb.asm.Type;
71
72/**
73 * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} to insert before, after and around
74 * advices in methods and constructors.
75 * <p>
76 * The behavior for constructors is like this:
77 * <ol>
78 *
79 * <li>as long as the INVOKESPECIAL for the object initialization has not been
80 * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
81 *
82 * <li>when this one is reached, it is only added in the ctor code visitor and a
83 * JP invoke is added</li>
84 *
85 * <li>after that, only the other code visitor receives the instructions</li>
86 *
87 * </ol>
88 *
89 * @author Eugene Kuleshov
90 * @author Eric Bruneton
91 */
92public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
93
94    private static final Object THIS = new Object();
95
96    private static final Object OTHER = new Object();
97
98    protected int methodAccess;
99
100    protected String methodDesc;
101
102    private boolean constructor;
103
104    private boolean superInitialized;
105
106    private List<Object> stackFrame;
107
108    private Map<Label, List<Object>> branches;
109
110    /**
111     * Creates a new {@link AdviceAdapter}.
112     *
113     * @param api
114     *            the ASM API version implemented by this visitor. Must be one
115     *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
116     * @param mv
117     *            the method visitor to which this adapter delegates calls.
118     * @param access
119     *            the method's access flags (see {@link Opcodes}).
120     * @param name
121     *            the method's name.
122     * @param desc
123     *            the method's descriptor (see {@link Type Type}).
124     */
125    protected AdviceAdapter(final int api, final MethodVisitor mv,
126            final int access, final String name, final String desc) {
127        super(api, mv, access, name, desc);
128        methodAccess = access;
129        methodDesc = desc;
130        constructor = "<init>".equals(name);
131    }
132
133    @Override
134    public void visitCode() {
135        mv.visitCode();
136        if (constructor) {
137            stackFrame = new ArrayList<Object>();
138            branches = new HashMap<Label, List<Object>>();
139        } else {
140            superInitialized = true;
141            onMethodEnter();
142        }
143    }
144
145    @Override
146    public void visitLabel(final Label label) {
147        mv.visitLabel(label);
148        if (constructor && branches != null) {
149            List<Object> frame = branches.get(label);
150            if (frame != null) {
151                stackFrame = frame;
152                branches.remove(label);
153            }
154        }
155    }
156
157    @Override
158    public void visitInsn(final int opcode) {
159        if (constructor) {
160            int s;
161            switch (opcode) {
162            case RETURN: // empty stack
163                onMethodExit(opcode);
164                break;
165            case IRETURN: // 1 before n/a after
166            case FRETURN: // 1 before n/a after
167            case ARETURN: // 1 before n/a after
168            case ATHROW: // 1 before n/a after
169                popValue();
170                onMethodExit(opcode);
171                break;
172            case LRETURN: // 2 before n/a after
173            case DRETURN: // 2 before n/a after
174                popValue();
175                popValue();
176                onMethodExit(opcode);
177                break;
178            case NOP:
179            case LALOAD: // remove 2 add 2
180            case DALOAD: // remove 2 add 2
181            case LNEG:
182            case DNEG:
183            case FNEG:
184            case INEG:
185            case L2D:
186            case D2L:
187            case F2I:
188            case I2B:
189            case I2C:
190            case I2S:
191            case I2F:
192            case ARRAYLENGTH:
193                break;
194            case ACONST_NULL:
195            case ICONST_M1:
196            case ICONST_0:
197            case ICONST_1:
198            case ICONST_2:
199            case ICONST_3:
200            case ICONST_4:
201            case ICONST_5:
202            case FCONST_0:
203            case FCONST_1:
204            case FCONST_2:
205            case F2L: // 1 before 2 after
206            case F2D:
207            case I2L:
208            case I2D:
209                pushValue(OTHER);
210                break;
211            case LCONST_0:
212            case LCONST_1:
213            case DCONST_0:
214            case DCONST_1:
215                pushValue(OTHER);
216                pushValue(OTHER);
217                break;
218            case IALOAD: // remove 2 add 1
219            case FALOAD: // remove 2 add 1
220            case AALOAD: // remove 2 add 1
221            case BALOAD: // remove 2 add 1
222            case CALOAD: // remove 2 add 1
223            case SALOAD: // remove 2 add 1
224            case POP:
225            case IADD:
226            case FADD:
227            case ISUB:
228            case LSHL: // 3 before 2 after
229            case LSHR: // 3 before 2 after
230            case LUSHR: // 3 before 2 after
231            case L2I: // 2 before 1 after
232            case L2F: // 2 before 1 after
233            case D2I: // 2 before 1 after
234            case D2F: // 2 before 1 after
235            case FSUB:
236            case FMUL:
237            case FDIV:
238            case FREM:
239            case FCMPL: // 2 before 1 after
240            case FCMPG: // 2 before 1 after
241            case IMUL:
242            case IDIV:
243            case IREM:
244            case ISHL:
245            case ISHR:
246            case IUSHR:
247            case IAND:
248            case IOR:
249            case IXOR:
250            case MONITORENTER:
251            case MONITOREXIT:
252                popValue();
253                break;
254            case POP2:
255            case LSUB:
256            case LMUL:
257            case LDIV:
258            case LREM:
259            case LADD:
260            case LAND:
261            case LOR:
262            case LXOR:
263            case DADD:
264            case DMUL:
265            case DSUB:
266            case DDIV:
267            case DREM:
268                popValue();
269                popValue();
270                break;
271            case IASTORE:
272            case FASTORE:
273            case AASTORE:
274            case BASTORE:
275            case CASTORE:
276            case SASTORE:
277            case LCMP: // 4 before 1 after
278            case DCMPL:
279            case DCMPG:
280                popValue();
281                popValue();
282                popValue();
283                break;
284            case LASTORE:
285            case DASTORE:
286                popValue();
287                popValue();
288                popValue();
289                popValue();
290                break;
291            case DUP:
292                pushValue(peekValue());
293                break;
294            case DUP_X1:
295                s = stackFrame.size();
296                stackFrame.add(s - 2, stackFrame.get(s - 1));
297                break;
298            case DUP_X2:
299                s = stackFrame.size();
300                stackFrame.add(s - 3, stackFrame.get(s - 1));
301                break;
302            case DUP2:
303                s = stackFrame.size();
304                stackFrame.add(s - 2, stackFrame.get(s - 1));
305                stackFrame.add(s - 2, stackFrame.get(s - 1));
306                break;
307            case DUP2_X1:
308                s = stackFrame.size();
309                stackFrame.add(s - 3, stackFrame.get(s - 1));
310                stackFrame.add(s - 3, stackFrame.get(s - 1));
311                break;
312            case DUP2_X2:
313                s = stackFrame.size();
314                stackFrame.add(s - 4, stackFrame.get(s - 1));
315                stackFrame.add(s - 4, stackFrame.get(s - 1));
316                break;
317            case SWAP:
318                s = stackFrame.size();
319                stackFrame.add(s - 2, stackFrame.get(s - 1));
320                stackFrame.remove(s);
321                break;
322            }
323        } else {
324            switch (opcode) {
325            case RETURN:
326            case IRETURN:
327            case FRETURN:
328            case ARETURN:
329            case LRETURN:
330            case DRETURN:
331            case ATHROW:
332                onMethodExit(opcode);
333                break;
334            }
335        }
336        mv.visitInsn(opcode);
337    }
338
339    @Override
340    public void visitVarInsn(final int opcode, final int var) {
341        super.visitVarInsn(opcode, var);
342        if (constructor) {
343            switch (opcode) {
344            case ILOAD:
345            case FLOAD:
346                pushValue(OTHER);
347                break;
348            case LLOAD:
349            case DLOAD:
350                pushValue(OTHER);
351                pushValue(OTHER);
352                break;
353            case ALOAD:
354                pushValue(var == 0 ? THIS : OTHER);
355                break;
356            case ASTORE:
357            case ISTORE:
358            case FSTORE:
359                popValue();
360                break;
361            case LSTORE:
362            case DSTORE:
363                popValue();
364                popValue();
365                break;
366            }
367        }
368    }
369
370    @Override
371    public void visitFieldInsn(final int opcode, final String owner,
372            final String name, final String desc) {
373        mv.visitFieldInsn(opcode, owner, name, desc);
374        if (constructor) {
375            char c = desc.charAt(0);
376            boolean longOrDouble = c == 'J' || c == 'D';
377            switch (opcode) {
378            case GETSTATIC:
379                pushValue(OTHER);
380                if (longOrDouble) {
381                    pushValue(OTHER);
382                }
383                break;
384            case PUTSTATIC:
385                popValue();
386                if (longOrDouble) {
387                    popValue();
388                }
389                break;
390            case PUTFIELD:
391                popValue();
392                popValue();
393                if (longOrDouble) {
394                    popValue();
395                }
396                break;
397            // case GETFIELD:
398            default:
399                if (longOrDouble) {
400                    pushValue(OTHER);
401                }
402            }
403        }
404    }
405
406    @Override
407    public void visitIntInsn(final int opcode, final int operand) {
408        mv.visitIntInsn(opcode, operand);
409        if (constructor && opcode != NEWARRAY) {
410            pushValue(OTHER);
411        }
412    }
413
414    @Override
415    public void visitLdcInsn(final Object cst) {
416        mv.visitLdcInsn(cst);
417        if (constructor) {
418            pushValue(OTHER);
419            if (cst instanceof Double || cst instanceof Long) {
420                pushValue(OTHER);
421            }
422        }
423    }
424
425    @Override
426    public void visitMultiANewArrayInsn(final String desc, final int dims) {
427        mv.visitMultiANewArrayInsn(desc, dims);
428        if (constructor) {
429            for (int i = 0; i < dims; i++) {
430                popValue();
431            }
432            pushValue(OTHER);
433        }
434    }
435
436    @Override
437    public void visitTypeInsn(final int opcode, final String type) {
438        mv.visitTypeInsn(opcode, type);
439        // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
440        if (constructor && opcode == NEW) {
441            pushValue(OTHER);
442        }
443    }
444
445    @Deprecated
446    @Override
447    public void visitMethodInsn(final int opcode, final String owner,
448            final String name, final String desc) {
449        if (api >= Opcodes.ASM5) {
450            super.visitMethodInsn(opcode, owner, name, desc);
451            return;
452        }
453        doVisitMethodInsn(opcode, owner, name, desc,
454                opcode == Opcodes.INVOKEINTERFACE);
455    }
456
457    @Override
458    public void visitMethodInsn(final int opcode, final String owner,
459            final String name, final String desc, final boolean itf) {
460        if (api < Opcodes.ASM5) {
461            super.visitMethodInsn(opcode, owner, name, desc, itf);
462            return;
463        }
464        doVisitMethodInsn(opcode, owner, name, desc, itf);
465    }
466
467    private void doVisitMethodInsn(int opcode, final String owner,
468            final String name, final String desc, final boolean itf) {
469        mv.visitMethodInsn(opcode, owner, name, desc, itf);
470        if (constructor) {
471            Type[] types = Type.getArgumentTypes(desc);
472            for (int i = 0; i < types.length; i++) {
473                popValue();
474                if (types[i].getSize() == 2) {
475                    popValue();
476                }
477            }
478            switch (opcode) {
479            // case INVOKESTATIC:
480            // break;
481            case INVOKEINTERFACE:
482            case INVOKEVIRTUAL:
483                popValue(); // objectref
484                break;
485            case INVOKESPECIAL:
486                Object type = popValue(); // objectref
487                if (type == THIS && !superInitialized) {
488                    onMethodEnter();
489                    superInitialized = true;
490                    // once super has been initialized it is no longer
491                    // necessary to keep track of stack state
492                    constructor = false;
493                }
494                break;
495            }
496
497            Type returnType = Type.getReturnType(desc);
498            if (returnType != Type.VOID_TYPE) {
499                pushValue(OTHER);
500                if (returnType.getSize() == 2) {
501                    pushValue(OTHER);
502                }
503            }
504        }
505    }
506
507    @Override
508    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
509            Object... bsmArgs) {
510        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
511        if (constructor) {
512            Type[] types = Type.getArgumentTypes(desc);
513            for (int i = 0; i < types.length; i++) {
514                popValue();
515                if (types[i].getSize() == 2) {
516                    popValue();
517                }
518            }
519
520            Type returnType = Type.getReturnType(desc);
521            if (returnType != Type.VOID_TYPE) {
522                pushValue(OTHER);
523                if (returnType.getSize() == 2) {
524                    pushValue(OTHER);
525                }
526            }
527        }
528    }
529
530    @Override
531    public void visitJumpInsn(final int opcode, final Label label) {
532        mv.visitJumpInsn(opcode, label);
533        if (constructor) {
534            switch (opcode) {
535            case IFEQ:
536            case IFNE:
537            case IFLT:
538            case IFGE:
539            case IFGT:
540            case IFLE:
541            case IFNULL:
542            case IFNONNULL:
543                popValue();
544                break;
545            case IF_ICMPEQ:
546            case IF_ICMPNE:
547            case IF_ICMPLT:
548            case IF_ICMPGE:
549            case IF_ICMPGT:
550            case IF_ICMPLE:
551            case IF_ACMPEQ:
552            case IF_ACMPNE:
553                popValue();
554                popValue();
555                break;
556            case JSR:
557                pushValue(OTHER);
558                break;
559            }
560            addBranch(label);
561        }
562    }
563
564    @Override
565    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
566            final Label[] labels) {
567        mv.visitLookupSwitchInsn(dflt, keys, labels);
568        if (constructor) {
569            popValue();
570            addBranches(dflt, labels);
571        }
572    }
573
574    @Override
575    public void visitTableSwitchInsn(final int min, final int max,
576            final Label dflt, final Label... labels) {
577        mv.visitTableSwitchInsn(min, max, dflt, labels);
578        if (constructor) {
579            popValue();
580            addBranches(dflt, labels);
581        }
582    }
583
584    @Override
585    public void visitTryCatchBlock(Label start, Label end, Label handler,
586            String type) {
587        super.visitTryCatchBlock(start, end, handler, type);
588        if (constructor && !branches.containsKey(handler)) {
589            List<Object> stackFrame = new ArrayList<Object>();
590            stackFrame.add(OTHER);
591            branches.put(handler, stackFrame);
592        }
593    }
594
595    private void addBranches(final Label dflt, final Label[] labels) {
596        addBranch(dflt);
597        for (int i = 0; i < labels.length; i++) {
598            addBranch(labels[i]);
599        }
600    }
601
602    private void addBranch(final Label label) {
603        if (branches.containsKey(label)) {
604            return;
605        }
606        branches.put(label, new ArrayList<Object>(stackFrame));
607    }
608
609    private Object popValue() {
610        return stackFrame.remove(stackFrame.size() - 1);
611    }
612
613    private Object peekValue() {
614        return stackFrame.get(stackFrame.size() - 1);
615    }
616
617    private void pushValue(final Object o) {
618        stackFrame.add(o);
619    }
620
621    /**
622     * Called at the beginning of the method or after super class call in
623     * the constructor. <br>
624     * <br>
625     *
626     * <i>Custom code can use or change all the local variables, but should not
627     * change state of the stack.</i>
628     */
629    protected void onMethodEnter() {
630    }
631
632    /**
633     * Called before explicit exit from the method using either return or throw.
634     * Top element on the stack contains the return value or exception instance.
635     * For example:
636     *
637     * <pre>
638     *   public void onMethodExit(int opcode) {
639     *     if(opcode==RETURN) {
640     *         visitInsn(ACONST_NULL);
641     *     } else if(opcode==ARETURN || opcode==ATHROW) {
642     *         dup();
643     *     } else {
644     *         if(opcode==LRETURN || opcode==DRETURN) {
645     *             dup2();
646     *         } else {
647     *             dup();
648     *         }
649     *         box(Type.getReturnType(this.methodDesc));
650     *     }
651     *     visitIntInsn(SIPUSH, opcode);
652     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
653     *   }
654     *
655     *   // an actual call back method
656     *   public static void onExit(Object param, int opcode) {
657     *     ...
658     * </pre>
659     *
660     * <br>
661     * <br>
662     *
663     * <i>Custom code can use or change all the local variables, but should not
664     * change state of the stack.</i>
665     *
666     * @param opcode
667     *            one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN
668     *            or ATHROW
669     *
670     */
671    protected void onMethodExit(int opcode) {
672    }
673
674    // TODO onException, onMethodCall
675}
676