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