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 }