1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * Copyright (c) 2011 Google 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the copyright holders nor the names of its 45 * contributors may be used to endorse or promote products derived from 46 * this software without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 49 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 52 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 58 * THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 package jdk.internal.org.objectweb.asm.commons; 61 62 import java.util.ArrayList; 63 import java.util.HashMap; 64 import java.util.List; 65 import java.util.Map; 66 67 import jdk.internal.org.objectweb.asm.Handle; 68 import jdk.internal.org.objectweb.asm.Label; 69 import jdk.internal.org.objectweb.asm.MethodVisitor; 70 import jdk.internal.org.objectweb.asm.Opcodes; 71 import jdk.internal.org.objectweb.asm.Type; 72 73 /** 74 * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} to insert before, after and around 75 * advices in methods and constructors. <p> The behavior for constructors is 76 * like this: <ol> 77 * 78 * <li>as long as the INVOKESPECIAL for the object initialization has not been 79 * reached, every bytecode instruction is dispatched in the ctor code visitor</li> 80 * 81 * <li>when this one is reached, it is only added in the ctor code visitor and 82 * a JP invoke is added</li> 83 * 84 * <li>after that, only the other code visitor receives the instructions</li> 85 * 86 * </ol> 87 * 88 * @author Eugene Kuleshov 89 * @author Eric Bruneton 90 */ 91 public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes 92 { 93 94 private static final Object THIS = new Object(); 95 96 private static final Object OTHER = new Object(); 97 98 protected int methodAccess; 99 100 protected String methodDesc; 101 102 private boolean constructor; 103 104 private boolean superInitialized; 105 106 private List<Object> stackFrame; 107 108 private Map<Label, List<Object>> branches; 109 110 /** 111 * Creates a new {@link AdviceAdapter}. 112 * 113 * @param api the ASM API version implemented by this visitor. Must be one 114 * of {@link Opcodes#ASM4}. 115 * @param mv the method visitor to which this adapter delegates calls. 116 * @param access the method's access flags (see {@link Opcodes}). 117 * @param name the method's name. 118 * @param desc the method's descriptor (see {@link Type Type}). 119 */ 120 protected AdviceAdapter( 121 final int api, 122 final MethodVisitor mv, 123 final int access, 124 final String name, 125 final String desc) 126 { 127 super(api, mv, access, name, desc); 128 methodAccess = access; 129 methodDesc = desc; 130 constructor = "<init>".equals(name); 131 } 132 133 @Override 134 public void visitCode() { 135 mv.visitCode(); 136 if (constructor) { 137 stackFrame = new ArrayList<Object>(); 138 branches = new HashMap<Label, List<Object>>(); 139 } else { 140 superInitialized = true; 141 onMethodEnter(); 142 } 143 } 144 145 @Override 146 public void visitLabel(final Label label) { 147 mv.visitLabel(label); 148 if (constructor && branches != null) { 149 List<Object> frame = branches.get(label); 150 if (frame != null) { 151 stackFrame = frame; 152 branches.remove(label); 153 } 154 } 155 } 156 157 @Override 158 public void visitInsn(final int opcode) { 159 if (constructor) { 160 int s; 161 switch (opcode) { 162 case RETURN: // empty stack 163 onMethodExit(opcode); 164 break; 165 166 case IRETURN: // 1 before n/a after 167 case FRETURN: // 1 before n/a after 168 case ARETURN: // 1 before n/a after 169 case ATHROW: // 1 before n/a after 170 popValue(); 171 onMethodExit(opcode); 172 break; 173 174 case LRETURN: // 2 before n/a after 175 case DRETURN: // 2 before n/a after 176 popValue(); 177 popValue(); 178 onMethodExit(opcode); 179 break; 180 181 case NOP: 182 case LALOAD: // remove 2 add 2 183 case DALOAD: // remove 2 add 2 184 case LNEG: 185 case DNEG: 186 case FNEG: 187 case INEG: 188 case L2D: 189 case D2L: 190 case F2I: 191 case I2B: 192 case I2C: 193 case I2S: 194 case I2F: 195 case ARRAYLENGTH: 196 break; 197 198 case ACONST_NULL: 199 case ICONST_M1: 200 case ICONST_0: 201 case ICONST_1: 202 case ICONST_2: 203 case ICONST_3: 204 case ICONST_4: 205 case ICONST_5: 206 case FCONST_0: 207 case FCONST_1: 208 case FCONST_2: 209 case F2L: // 1 before 2 after 210 case F2D: 211 case I2L: 212 case I2D: 213 pushValue(OTHER); 214 break; 215 216 case LCONST_0: 217 case LCONST_1: 218 case DCONST_0: 219 case DCONST_1: 220 pushValue(OTHER); 221 pushValue(OTHER); 222 break; 223 224 case IALOAD: // remove 2 add 1 225 case FALOAD: // remove 2 add 1 226 case AALOAD: // remove 2 add 1 227 case BALOAD: // remove 2 add 1 228 case CALOAD: // remove 2 add 1 229 case SALOAD: // remove 2 add 1 230 case POP: 231 case IADD: 232 case FADD: 233 case ISUB: 234 case LSHL: // 3 before 2 after 235 case LSHR: // 3 before 2 after 236 case LUSHR: // 3 before 2 after 237 case L2I: // 2 before 1 after 238 case L2F: // 2 before 1 after 239 case D2I: // 2 before 1 after 240 case D2F: // 2 before 1 after 241 case FSUB: 242 case FMUL: 243 case FDIV: 244 case FREM: 245 case FCMPL: // 2 before 1 after 246 case FCMPG: // 2 before 1 after 247 case IMUL: 248 case IDIV: 249 case IREM: 250 case ISHL: 251 case ISHR: 252 case IUSHR: 253 case IAND: 254 case IOR: 255 case IXOR: 256 case MONITORENTER: 257 case MONITOREXIT: 258 popValue(); 259 break; 260 261 case POP2: 262 case LSUB: 263 case LMUL: 264 case LDIV: 265 case LREM: 266 case LADD: 267 case LAND: 268 case LOR: 269 case LXOR: 270 case DADD: 271 case DMUL: 272 case DSUB: 273 case DDIV: 274 case DREM: 275 popValue(); 276 popValue(); 277 break; 278 279 case IASTORE: 280 case FASTORE: 281 case AASTORE: 282 case BASTORE: 283 case CASTORE: 284 case SASTORE: 285 case LCMP: // 4 before 1 after 286 case DCMPL: 287 case DCMPG: 288 popValue(); 289 popValue(); 290 popValue(); 291 break; 292 293 case LASTORE: 294 case DASTORE: 295 popValue(); 296 popValue(); 297 popValue(); 298 popValue(); 299 break; 300 301 case DUP: 302 pushValue(peekValue()); 303 break; 304 305 case DUP_X1: 306 s = stackFrame.size(); 307 stackFrame.add(s - 2, stackFrame.get(s - 1)); 308 break; 309 310 case DUP_X2: 311 s = stackFrame.size(); 312 stackFrame.add(s - 3, stackFrame.get(s - 1)); 313 break; 314 315 case DUP2: 316 s = stackFrame.size(); 317 stackFrame.add(s - 2, stackFrame.get(s - 1)); 318 stackFrame.add(s - 2, stackFrame.get(s - 1)); 319 break; 320 321 case DUP2_X1: 322 s = stackFrame.size(); 323 stackFrame.add(s - 3, stackFrame.get(s - 1)); 324 stackFrame.add(s - 3, stackFrame.get(s - 1)); 325 break; 326 327 case DUP2_X2: 328 s = stackFrame.size(); 329 stackFrame.add(s - 4, stackFrame.get(s - 1)); 330 stackFrame.add(s - 4, stackFrame.get(s - 1)); 331 break; 332 333 case SWAP: 334 s = stackFrame.size(); 335 stackFrame.add(s - 2, stackFrame.get(s - 1)); 336 stackFrame.remove(s); 337 break; 338 } 339 } else { 340 switch (opcode) { 341 case RETURN: 342 case IRETURN: 343 case FRETURN: 344 case ARETURN: 345 case LRETURN: 346 case DRETURN: 347 case ATHROW: 348 onMethodExit(opcode); 349 break; 350 } 351 } 352 mv.visitInsn(opcode); 353 } 354 355 @Override 356 public void visitVarInsn(final int opcode, final int var) { 357 super.visitVarInsn(opcode, var); 358 if (constructor) { 359 switch (opcode) { 360 case ILOAD: 361 case FLOAD: 362 pushValue(OTHER); 363 break; 364 case LLOAD: 365 case DLOAD: 366 pushValue(OTHER); 367 pushValue(OTHER); 368 break; 369 case ALOAD: 370 pushValue(var == 0 ? THIS : OTHER); 371 break; 372 case ASTORE: 373 case ISTORE: 374 case FSTORE: 375 popValue(); 376 break; 377 case LSTORE: 378 case DSTORE: 379 popValue(); 380 popValue(); 381 break; 382 } 383 } 384 } 385 386 @Override 387 public void visitFieldInsn( 388 final int opcode, 389 final String owner, 390 final String name, 391 final String desc) 392 { 393 mv.visitFieldInsn(opcode, owner, name, desc); 394 if (constructor) { 395 char c = desc.charAt(0); 396 boolean longOrDouble = c == 'J' || c == 'D'; 397 switch (opcode) { 398 case GETSTATIC: 399 pushValue(OTHER); 400 if (longOrDouble) { 401 pushValue(OTHER); 402 } 403 break; 404 case PUTSTATIC: 405 popValue(); 406 if (longOrDouble) { 407 popValue(); 408 } 409 break; 410 case PUTFIELD: 411 popValue(); 412 if (longOrDouble) { 413 popValue(); 414 popValue(); 415 } 416 break; 417 // case GETFIELD: 418 default: 419 if (longOrDouble) { 420 pushValue(OTHER); 421 } 422 } 423 } 424 } 425 426 @Override 427 public void visitIntInsn(final int opcode, final int operand) { 428 mv.visitIntInsn(opcode, operand); 429 if (constructor && opcode != NEWARRAY) { 430 pushValue(OTHER); 431 } 432 } 433 434 @Override 435 public void visitLdcInsn(final Object cst) { 436 mv.visitLdcInsn(cst); 437 if (constructor) { 438 pushValue(OTHER); 439 if (cst instanceof Double || cst instanceof Long) { 440 pushValue(OTHER); 441 } 442 } 443 } 444 445 @Override 446 public void visitMultiANewArrayInsn(final String desc, final int dims) { 447 mv.visitMultiANewArrayInsn(desc, dims); 448 if (constructor) { 449 for (int i = 0; i < dims; i++) { 450 popValue(); 451 } 452 pushValue(OTHER); 453 } 454 } 455 456 @Override 457 public void visitTypeInsn(final int opcode, final String type) { 458 mv.visitTypeInsn(opcode, type); 459 // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack 460 if (constructor && opcode == NEW) { 461 pushValue(OTHER); 462 } 463 } 464 465 @Override 466 public void visitMethodInsn( 467 final int opcode, 468 final String owner, 469 final String name, 470 final String desc) 471 { 472 mv.visitMethodInsn(opcode, owner, name, desc); 473 if (constructor) { 474 Type[] types = Type.getArgumentTypes(desc); 475 for (int i = 0; i < types.length; i++) { 476 popValue(); 477 if (types[i].getSize() == 2) { 478 popValue(); 479 } 480 } 481 switch (opcode) { 482 // case INVOKESTATIC: 483 // break; 484 485 case INVOKEINTERFACE: 486 case INVOKEVIRTUAL: 487 popValue(); // objectref 488 break; 489 490 case INVOKESPECIAL: 491 Object type = popValue(); // objectref 492 if (type == THIS && !superInitialized) { 493 onMethodEnter(); 494 superInitialized = true; 495 // once super has been initialized it is no longer 496 // necessary to keep track of stack state 497 constructor = false; 498 } 499 break; 500 } 501 502 Type returnType = Type.getReturnType(desc); 503 if (returnType != Type.VOID_TYPE) { 504 pushValue(OTHER); 505 if (returnType.getSize() == 2) { 506 pushValue(OTHER); 507 } 508 } 509 } 510 } 511 512 @Override 513 public void visitInvokeDynamicInsn( 514 String name, 515 String desc, 516 Handle bsm, 517 Object... bsmArgs) 518 { 519 mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 520 if (constructor) { 521 Type[] types = Type.getArgumentTypes(desc); 522 for (int i = 0; i < types.length; i++) { 523 popValue(); 524 if (types[i].getSize() == 2) { 525 popValue(); 526 } 527 } 528 529 Type returnType = Type.getReturnType(desc); 530 if (returnType != Type.VOID_TYPE) { 531 pushValue(OTHER); 532 if (returnType.getSize() == 2) { 533 pushValue(OTHER); 534 } 535 } 536 } 537 } 538 539 @Override 540 public void visitJumpInsn(final int opcode, final Label label) { 541 mv.visitJumpInsn(opcode, label); 542 if (constructor) { 543 switch (opcode) { 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; 554 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; 566 567 case JSR: 568 pushValue(OTHER); 569 break; 570 } 571 addBranch(label); 572 } 573 } 574 575 @Override 576 public void visitLookupSwitchInsn( 577 final Label dflt, 578 final int[] keys, 579 final Label[] labels) 580 { 581 mv.visitLookupSwitchInsn(dflt, keys, labels); 582 if (constructor) { 583 popValue(); 584 addBranches(dflt, labels); 585 } 586 } 587 588 @Override 589 public void visitTableSwitchInsn( 590 final int min, 591 final int max, 592 final Label dflt, 593 final Label... labels) 594 { 595 mv.visitTableSwitchInsn(min, max, dflt, labels); 596 if (constructor) { 597 popValue(); 598 addBranches(dflt, labels); 599 } 600 } 601 602 @Override 603 public void visitTryCatchBlock( 604 Label start, 605 Label end, 606 Label handler, 607 String type) 608 { 609 super.visitTryCatchBlock(start, end, handler, type); 610 if (constructor && !branches.containsKey(handler)) { 611 List<Object> stackFrame = new ArrayList<Object>(); 612 stackFrame.add(OTHER); 613 branches.put(handler, stackFrame); 614 } 615 } 616 617 private void addBranches(final Label dflt, final Label[] labels) { 618 addBranch(dflt); 619 for (int i = 0; i < labels.length; i++) { 620 addBranch(labels[i]); 621 } 622 } 623 624 private void addBranch(final Label label) { 625 if (branches.containsKey(label)) { 626 return; 627 } 628 branches.put(label, new ArrayList<Object>(stackFrame)); 629 } 630 631 private Object popValue() { 632 return stackFrame.remove(stackFrame.size() - 1); 633 } 634 635 private Object peekValue() { 636 return stackFrame.get(stackFrame.size() - 1); 637 } 638 639 private void pushValue(final Object o) { 640 stackFrame.add(o); 641 } 642 643 /** 644 * Called at the beginning of the method or after super class class call in 645 * the constructor. <br><br> 646 * 647 * <i>Custom code can use or change all the local variables, but should not 648 * change state of the stack.</i> 649 */ 650 protected void onMethodEnter() { 651 } 652 653 /** 654 * Called before explicit exit from the method using either return or throw. 655 * Top element on the stack contains the return value or exception instance. 656 * For example: 657 * 658 * <pre> 659 * public void onMethodExit(int opcode) { 660 * if(opcode==RETURN) { 661 * visitInsn(ACONST_NULL); 662 * } else if(opcode==ARETURN || opcode==ATHROW) { 663 * dup(); 664 * } else { 665 * if(opcode==LRETURN || opcode==DRETURN) { 666 * dup2(); 667 * } else { 668 * dup(); 669 * } 670 * box(Type.getReturnType(this.methodDesc)); 671 * } 672 * visitIntInsn(SIPUSH, opcode); 673 * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V"); 674 * } 675 * 676 * // an actual call back method 677 * public static void onExit(Object param, int opcode) { 678 * ... 679 * </pre> 680 * 681 * <br><br> 682 * 683 * <i>Custom code can use or change all the local variables, but should not 684 * change state of the stack.</i> 685 * 686 * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, 687 * DRETURN or ATHROW 688 * 689 */ 690 protected void onMethodExit(int opcode) { 691 } 692 693 // TODO onException, onMethodCall 694 }