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