view src/clojure/asm/commons/AdviceAdapter.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
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 */
30 package clojure.asm.commons;
32 import java.util.ArrayList;
33 import java.util.HashMap;
35 import clojure.asm.Label;
36 import clojure.asm.MethodVisitor;
37 import clojure.asm.Opcodes;
38 import clojure.asm.Type;
40 /**
41 * A {@link clojure.asm.MethodAdapter} to insert before, after and around
42 * advices in methods and constructors. <p> The behavior for constructors is
43 * like this: <ol>
44 * <p/>
45 * <li>as long as the INVOKESPECIAL for the object initialization has not been
46 * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
47 * <p/>
48 * <li>when this one is reached, it is only added in the ctor code visitor and
49 * a JP invoke is added</li>
50 * <p/>
51 * <li>after that, only the other code visitor receives the instructions</li>
52 * <p/>
53 * </ol>
54 *
55 * @author Eugene Kuleshov
56 * @author Eric Bruneton
57 */
58 public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes{
59 private static final Object THIS = new Object();
60 private static final Object OTHER = new Object();
62 protected int methodAccess;
63 protected String methodDesc;
65 private boolean constructor;
66 private boolean superInitialized;
67 private ArrayList stackFrame;
68 private HashMap branches;
70 /**
71 * Creates a new {@link AdviceAdapter}.
72 *
73 * @param mv the method visitor to which this adapter delegates calls.
74 * @param access the method's access flags (see {@link Opcodes}).
75 * @param name the method's name.
76 * @param desc the method's descriptor (see {@link Type Type}).
77 */
78 public AdviceAdapter(
79 final MethodVisitor mv,
80 final int access,
81 final String name,
82 final String desc){
83 super(mv, access, name, desc);
84 methodAccess = access;
85 methodDesc = desc;
87 constructor = "<init>".equals(name);
88 }
90 public void visitCode(){
91 mv.visitCode();
92 if(!constructor)
93 {
94 superInitialized = true;
95 onMethodEnter();
96 }
97 else
98 {
99 stackFrame = new ArrayList();
100 branches = new HashMap();
101 }
102 }
104 public void visitLabel(final Label label){
105 mv.visitLabel(label);
107 if(constructor && branches != null)
108 {
109 ArrayList frame = (ArrayList) branches.get(label);
110 if(frame != null)
111 {
112 stackFrame = frame;
113 branches.remove(label);
114 }
115 }
116 }
118 public void visitInsn(final int opcode){
119 if(constructor)
120 {
121 switch(opcode)
122 {
123 case RETURN: // empty stack
124 onMethodExit(opcode);
125 break;
127 case IRETURN: // 1 before n/a after
128 case FRETURN: // 1 before n/a after
129 case ARETURN: // 1 before n/a after
130 case ATHROW: // 1 before n/a after
131 popValue();
132 popValue();
133 onMethodExit(opcode);
134 break;
136 case LRETURN: // 2 before n/a after
137 case DRETURN: // 2 before n/a after
138 popValue();
139 popValue();
140 onMethodExit(opcode);
141 break;
143 case NOP:
144 case LALOAD: // remove 2 add 2
145 case DALOAD: // remove 2 add 2
146 case LNEG:
147 case DNEG:
148 case FNEG:
149 case INEG:
150 case L2D:
151 case D2L:
152 case F2I:
153 case I2B:
154 case I2C:
155 case I2S:
156 case I2F:
157 case Opcodes.ARRAYLENGTH:
158 break;
160 case ACONST_NULL:
161 case ICONST_M1:
162 case ICONST_0:
163 case ICONST_1:
164 case ICONST_2:
165 case ICONST_3:
166 case ICONST_4:
167 case ICONST_5:
168 case FCONST_0:
169 case FCONST_1:
170 case FCONST_2:
171 case F2L: // 1 before 2 after
172 case F2D:
173 case I2L:
174 case I2D:
175 pushValue(OTHER);
176 break;
178 case LCONST_0:
179 case LCONST_1:
180 case DCONST_0:
181 case DCONST_1:
182 pushValue(OTHER);
183 pushValue(OTHER);
184 break;
186 case IALOAD: // remove 2 add 1
187 case FALOAD: // remove 2 add 1
188 case AALOAD: // remove 2 add 1
189 case BALOAD: // remove 2 add 1
190 case CALOAD: // remove 2 add 1
191 case SALOAD: // remove 2 add 1
192 case POP:
193 case IADD:
194 case FADD:
195 case ISUB:
196 case LSHL: // 3 before 2 after
197 case LSHR: // 3 before 2 after
198 case LUSHR: // 3 before 2 after
199 case L2I: // 2 before 1 after
200 case L2F: // 2 before 1 after
201 case D2I: // 2 before 1 after
202 case D2F: // 2 before 1 after
203 case FSUB:
204 case FMUL:
205 case FDIV:
206 case FREM:
207 case FCMPL: // 2 before 1 after
208 case FCMPG: // 2 before 1 after
209 case IMUL:
210 case IDIV:
211 case IREM:
212 case ISHL:
213 case ISHR:
214 case IUSHR:
215 case IAND:
216 case IOR:
217 case IXOR:
218 case MONITORENTER:
219 case MONITOREXIT:
220 popValue();
221 break;
223 case POP2:
224 case LSUB:
225 case LMUL:
226 case LDIV:
227 case LREM:
228 case LADD:
229 case LAND:
230 case LOR:
231 case LXOR:
232 case DADD:
233 case DMUL:
234 case DSUB:
235 case DDIV:
236 case DREM:
237 popValue();
238 popValue();
239 break;
241 case IASTORE:
242 case FASTORE:
243 case AASTORE:
244 case BASTORE:
245 case CASTORE:
246 case SASTORE:
247 case LCMP: // 4 before 1 after
248 case DCMPL:
249 case DCMPG:
250 popValue();
251 popValue();
252 popValue();
253 break;
255 case LASTORE:
256 case DASTORE:
257 popValue();
258 popValue();
259 popValue();
260 popValue();
261 break;
263 case DUP:
264 pushValue(peekValue());
265 break;
267 case DUP_X1:
268 // TODO optimize this
269 {
270 Object o1 = popValue();
271 Object o2 = popValue();
272 pushValue(o1);
273 pushValue(o2);
274 pushValue(o1);
275 }
276 break;
278 case DUP_X2:
279 // TODO optimize this
280 {
281 Object o1 = popValue();
282 Object o2 = popValue();
283 Object o3 = popValue();
284 pushValue(o1);
285 pushValue(o3);
286 pushValue(o2);
287 pushValue(o1);
288 }
289 break;
291 case DUP2:
292 // TODO optimize this
293 {
294 Object o1 = popValue();
295 Object o2 = popValue();
296 pushValue(o2);
297 pushValue(o1);
298 pushValue(o2);
299 pushValue(o1);
300 }
301 break;
303 case DUP2_X1:
304 // TODO optimize this
305 {
306 Object o1 = popValue();
307 Object o2 = popValue();
308 Object o3 = popValue();
309 pushValue(o2);
310 pushValue(o1);
311 pushValue(o3);
312 pushValue(o2);
313 pushValue(o1);
314 }
315 break;
317 case DUP2_X2:
318 // TODO optimize this
319 {
320 Object o1 = popValue();
321 Object o2 = popValue();
322 Object o3 = popValue();
323 Object o4 = popValue();
324 pushValue(o2);
325 pushValue(o1);
326 pushValue(o4);
327 pushValue(o3);
328 pushValue(o2);
329 pushValue(o1);
330 }
331 break;
333 case SWAP:
334 {
335 Object o1 = popValue();
336 Object o2 = popValue();
337 pushValue(o1);
338 pushValue(o2);
339 }
340 break;
341 }
342 }
343 else
344 {
345 switch(opcode)
346 {
347 case RETURN:
348 case IRETURN:
349 case FRETURN:
350 case ARETURN:
351 case LRETURN:
352 case DRETURN:
353 case ATHROW:
354 onMethodExit(opcode);
355 break;
356 }
357 }
358 mv.visitInsn(opcode);
359 }
361 public void visitVarInsn(final int opcode, final int var){
362 super.visitVarInsn(opcode, var);
364 if(constructor)
365 {
366 switch(opcode)
367 {
368 case ILOAD:
369 case FLOAD:
370 pushValue(OTHER);
371 break;
372 case LLOAD:
373 case DLOAD:
374 pushValue(OTHER);
375 pushValue(OTHER);
376 break;
377 case ALOAD:
378 pushValue(var == 0 ? THIS : OTHER);
379 break;
380 case ASTORE:
381 case ISTORE:
382 case FSTORE:
383 popValue();
384 break;
385 case LSTORE:
386 case DSTORE:
387 popValue();
388 popValue();
389 break;
390 }
391 }
392 }
394 public void visitFieldInsn(
395 final int opcode,
396 final String owner,
397 final String name,
398 final String desc){
399 mv.visitFieldInsn(opcode, owner, name, desc);
401 if(constructor)
402 {
403 char c = desc.charAt(0);
404 boolean longOrDouble = c == 'J' || c == 'D';
405 switch(opcode)
406 {
407 case GETSTATIC:
408 pushValue(OTHER);
409 if(longOrDouble)
410 {
411 pushValue(OTHER);
412 }
413 break;
414 case PUTSTATIC:
415 popValue();
416 if(longOrDouble)
417 {
418 popValue();
419 }
420 break;
421 case PUTFIELD:
422 popValue();
423 if(longOrDouble)
424 {
425 popValue();
426 popValue();
427 }
428 break;
429 // case GETFIELD:
430 default:
431 if(longOrDouble)
432 {
433 pushValue(OTHER);
434 }
435 }
436 }
437 }
439 public void visitIntInsn(final int opcode, final int operand){
440 mv.visitIntInsn(opcode, operand);
442 if(constructor && opcode != NEWARRAY)
443 {
444 pushValue(OTHER);
445 }
446 }
448 public void visitLdcInsn(final Object cst){
449 mv.visitLdcInsn(cst);
451 if(constructor)
452 {
453 pushValue(OTHER);
454 if(cst instanceof Double || cst instanceof Long)
455 {
456 pushValue(OTHER);
457 }
458 }
459 }
461 public void visitMultiANewArrayInsn(final String desc, final int dims){
462 mv.visitMultiANewArrayInsn(desc, dims);
464 if(constructor)
465 {
466 for(int i = 0; i < dims; i++)
467 {
468 popValue();
469 }
470 pushValue(OTHER);
471 }
472 }
474 public void visitTypeInsn(final int opcode, final String name){
475 mv.visitTypeInsn(opcode, name);
477 // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
478 if(constructor && opcode == NEW)
479 {
480 pushValue(OTHER);
481 }
482 }
484 public void visitMethodInsn(
485 final int opcode,
486 final String owner,
487 final String name,
488 final String desc){
489 mv.visitMethodInsn(opcode, owner, name, desc);
491 if(constructor)
492 {
493 Type[] types = Type.getArgumentTypes(desc);
494 for(int i = 0; i < types.length; i++)
495 {
496 popValue();
497 if(types[i].getSize() == 2)
498 {
499 popValue();
500 }
501 }
502 switch(opcode)
503 {
504 // case INVOKESTATIC:
505 // break;
507 case INVOKEINTERFACE:
508 case INVOKEVIRTUAL:
509 popValue(); // objectref
510 break;
512 case INVOKESPECIAL:
513 Object type = popValue(); // objectref
514 if(type == THIS && !superInitialized)
515 {
516 onMethodEnter();
517 superInitialized = true;
518 // once super has been initialized it is no longer
519 // necessary to keep track of stack state
520 constructor = false;
521 }
522 break;
523 }
525 Type returnType = Type.getReturnType(desc);
526 if(returnType != Type.VOID_TYPE)
527 {
528 pushValue(OTHER);
529 if(returnType.getSize() == 2)
530 {
531 pushValue(OTHER);
532 }
533 }
534 }
535 }
537 public void visitJumpInsn(final int opcode, final Label label){
538 mv.visitJumpInsn(opcode, label);
540 if(constructor)
541 {
542 switch(opcode)
543 {
544 case IFEQ:
545 case IFNE:
546 case IFLT:
547 case IFGE:
548 case IFGT:
549 case IFLE:
550 case IFNULL:
551 case IFNONNULL:
552 popValue();
553 break;
555 case IF_ICMPEQ:
556 case IF_ICMPNE:
557 case IF_ICMPLT:
558 case IF_ICMPGE:
559 case IF_ICMPGT:
560 case IF_ICMPLE:
561 case IF_ACMPEQ:
562 case IF_ACMPNE:
563 popValue();
564 popValue();
565 break;
567 case JSR:
568 pushValue(OTHER);
569 break;
570 }
571 addBranch(label);
572 }
573 }
575 public void visitLookupSwitchInsn(
576 final Label dflt,
577 final int[] keys,
578 final Label[] labels){
579 mv.visitLookupSwitchInsn(dflt, keys, labels);
581 if(constructor)
582 {
583 popValue();
584 addBranches(dflt, labels);
585 }
586 }
588 public void visitTableSwitchInsn(
589 final int min,
590 final int max,
591 final Label dflt,
592 final Label[] labels){
593 mv.visitTableSwitchInsn(min, max, dflt, labels);
595 if(constructor)
596 {
597 popValue();
598 addBranches(dflt, labels);
599 }
600 }
602 private void addBranches(final Label dflt, final Label[] labels){
603 addBranch(dflt);
604 for(int i = 0; i < labels.length; i++)
605 {
606 addBranch(labels[i]);
607 }
608 }
610 private void addBranch(final Label label){
611 if(branches.containsKey(label))
612 {
613 return;
614 }
615 ArrayList frame = new ArrayList();
616 frame.addAll(stackFrame);
617 branches.put(label, frame);
618 }
620 private Object popValue(){
621 return stackFrame.remove(stackFrame.size() - 1);
622 }
624 private Object peekValue(){
625 return stackFrame.get(stackFrame.size() - 1);
626 }
628 private void pushValue(final Object o){
629 stackFrame.add(o);
630 }
632 /**
633 * Called at the beginning of the method or after super class class call in
634 * the constructor. <br><br>
635 * <p/>
636 * <i>Custom code can use or change all the local variables, but should not
637 * change state of the stack.</i>
638 */
639 protected abstract void onMethodEnter();
641 /**
642 * Called before explicit exit from the method using either return or throw.
643 * Top element on the stack contains the return value or exception instance.
644 * For example:
645 * <p/>
646 * <pre>
647 * public void onMethodExit(int opcode) {
648 * if(opcode==RETURN) {
649 * visitInsn(ACONST_NULL);
650 * } else if(opcode==ARETURN || opcode==ATHROW) {
651 * dup();
652 * } else {
653 * if(opcode==LRETURN || opcode==DRETURN) {
654 * dup2();
655 * } else {
656 * dup();
657 * }
658 * box(Type.getReturnType(this.methodDesc));
659 * }
660 * visitIntInsn(SIPUSH, opcode);
661 * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
662 * }
663 * <p/>
664 * // an actual call back method
665 * public static void onExit(int opcode, Object param) {
666 * ...
667 * </pre>
668 * <p/>
669 * <br><br>
670 * <p/>
671 * <i>Custom code can use or change all the local variables, but should not
672 * change state of the stack.</i>
673 *
674 * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,
675 * DRETURN or ATHROW
676 */
677 protected abstract void onMethodExit(int opcode);
679 // TODO onException, onMethodCall
681 }