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