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