Mercurial > lasercutter
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 framework3 * Copyright (c) 2000-2005 INRIA, France Telecom4 * All rights reserved.5 *6 * Redistribution and use in source and binary forms, with or without7 * modification, are permitted provided that the following conditions8 * are met:9 * 1. Redistributions of source code must retain the above copyright10 * notice, this list of conditions and the following disclaimer.11 * 2. Redistributions in binary form must reproduce the above copyright12 * notice, this list of conditions and the following disclaimer in the13 * documentation and/or other materials provided with the distribution.14 * 3. Neither the name of the copyright holders nor the names of its15 * contributors may be used to endorse or promote products derived from16 * 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, THE20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF28 * 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 around42 * advices in methods and constructors. <p> The behavior for constructors is43 * like this: <ol>44 * <p/>45 * <li>as long as the INVOKESPECIAL for the object initialization has not been46 * 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 and49 * 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 Kuleshov56 * @author Eric Bruneton57 */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 else98 {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 stack124 onMethodExit(opcode);125 break;127 case IRETURN: // 1 before n/a after128 case FRETURN: // 1 before n/a after129 case ARETURN: // 1 before n/a after130 case ATHROW: // 1 before n/a after131 popValue();132 popValue();133 onMethodExit(opcode);134 break;136 case LRETURN: // 2 before n/a after137 case DRETURN: // 2 before n/a after138 popValue();139 popValue();140 onMethodExit(opcode);141 break;143 case NOP:144 case LALOAD: // remove 2 add 2145 case DALOAD: // remove 2 add 2146 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 after172 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 1187 case FALOAD: // remove 2 add 1188 case AALOAD: // remove 2 add 1189 case BALOAD: // remove 2 add 1190 case CALOAD: // remove 2 add 1191 case SALOAD: // remove 2 add 1192 case POP:193 case IADD:194 case FADD:195 case ISUB:196 case LSHL: // 3 before 2 after197 case LSHR: // 3 before 2 after198 case LUSHR: // 3 before 2 after199 case L2I: // 2 before 1 after200 case L2F: // 2 before 1 after201 case D2I: // 2 before 1 after202 case D2F: // 2 before 1 after203 case FSUB:204 case FMUL:205 case FDIV:206 case FREM:207 case FCMPL: // 2 before 1 after208 case FCMPG: // 2 before 1 after209 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 after248 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 this269 {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 this280 {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 this293 {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 this305 {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 this319 {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 else344 {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 stack478 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(); // objectref510 break;512 case INVOKESPECIAL:513 Object type = popValue(); // objectref514 if(type == THIS && !superInitialized)515 {516 onMethodEnter();517 superInitialized = true;518 // once super has been initialized it is no longer519 // necessary to keep track of stack state520 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 in634 * the constructor. <br><br>635 * <p/>636 * <i>Custom code can use or change all the local variables, but should not637 * 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 method665 * 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 not672 * change state of the stack.</i>673 *674 * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN,675 * DRETURN or ATHROW676 */677 protected abstract void onMethodExit(int opcode);679 // TODO onException, onMethodCall681 }