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  * All rights reserved.
  34  *
  35  * Redistribution and use in source and binary forms, with or without
  36  * modification, are permitted provided that the following conditions
  37  * are met:
  38  * 1. Redistributions of source code must retain the above copyright
  39  *    notice, this list of conditions and the following disclaimer.
  40  * 2. Redistributions in binary form must reproduce the above copyright
  41  *    notice, this list of conditions and the following disclaimer in the
  42  *    documentation and/or other materials provided with the distribution.
  43  * 3. Neither the name of the copyright holders nor the names of its
  44  *    contributors may be used to endorse or promote products derived from
  45  *    this software without specific prior written permission.
  46  *
  47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  48  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  50  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  51  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  52  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  53  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  54  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  55  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  56  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  57  * THE POSSIBILITY OF SUCH DAMAGE.
  58  */
  59 package jdk.internal.org.objectweb.asm.commons;
  60 
  61 import java.util.ArrayList;
  62 import java.util.HashMap;
  63 import java.util.List;
  64 import java.util.Map;
  65 
  66 import jdk.internal.org.objectweb.asm.Handle;
  67 import jdk.internal.org.objectweb.asm.Label;
  68 import jdk.internal.org.objectweb.asm.MethodVisitor;
  69 import jdk.internal.org.objectweb.asm.Opcodes;
  70 import jdk.internal.org.objectweb.asm.Type;
  71 
  72 /**
  73  * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} to insert before, after and around
  74  * advices in methods and constructors.
  75  * <p>
  76  * The behavior for constructors is like this:
  77  * <ol>
  78  *
  79  * <li>as long as the INVOKESPECIAL for the object initialization has not been
  80  * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
  81  *
  82  * <li>when this one is reached, it is only added in the ctor code visitor and a
  83  * JP invoke is added</li>
  84  *
  85  * <li>after that, only the other code visitor receives the instructions</li>
  86  *
  87  * </ol>
  88  *
  89  * @author Eugene Kuleshov
  90  * @author Eric Bruneton
  91  */
  92 public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
  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
 114      *            the ASM API version implemented by this visitor. Must be one
 115      *            of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
 116      * @param mv
 117      *            the method visitor to which this adapter delegates calls.
 118      * @param access
 119      *            the method's access flags (see {@link Opcodes}).
 120      * @param name
 121      *            the method's name.
 122      * @param desc
 123      *            the method's descriptor (see {@link Type Type}).
 124      */
 125     protected AdviceAdapter(final int api, final MethodVisitor mv,
 126             final int access, final String name, final String desc) {
 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             case IRETURN: // 1 before n/a after
 166             case FRETURN: // 1 before n/a after
 167             case ARETURN: // 1 before n/a after
 168             case ATHROW: // 1 before n/a after
 169                 popValue();
 170                 onMethodExit(opcode);
 171                 break;
 172             case LRETURN: // 2 before n/a after
 173             case DRETURN: // 2 before n/a after
 174                 popValue();
 175                 popValue();
 176                 onMethodExit(opcode);
 177                 break;
 178             case NOP:
 179             case LALOAD: // remove 2 add 2
 180             case DALOAD: // remove 2 add 2
 181             case LNEG:
 182             case DNEG:
 183             case FNEG:
 184             case INEG:
 185             case L2D:
 186             case D2L:
 187             case F2I:
 188             case I2B:
 189             case I2C:
 190             case I2S:
 191             case I2F:
 192             case ARRAYLENGTH:
 193                 break;
 194             case ACONST_NULL:
 195             case ICONST_M1:
 196             case ICONST_0:
 197             case ICONST_1:
 198             case ICONST_2:
 199             case ICONST_3:
 200             case ICONST_4:
 201             case ICONST_5:
 202             case FCONST_0:
 203             case FCONST_1:
 204             case FCONST_2:
 205             case F2L: // 1 before 2 after
 206             case F2D:
 207             case I2L:
 208             case I2D:
 209                 pushValue(OTHER);
 210                 break;
 211             case LCONST_0:
 212             case LCONST_1:
 213             case DCONST_0:
 214             case DCONST_1:
 215                 pushValue(OTHER);
 216                 pushValue(OTHER);
 217                 break;
 218             case IALOAD: // remove 2 add 1
 219             case FALOAD: // remove 2 add 1
 220             case AALOAD: // remove 2 add 1
 221             case BALOAD: // remove 2 add 1
 222             case CALOAD: // remove 2 add 1
 223             case SALOAD: // remove 2 add 1
 224             case POP:
 225             case IADD:
 226             case FADD:
 227             case ISUB:
 228             case LSHL: // 3 before 2 after
 229             case LSHR: // 3 before 2 after
 230             case LUSHR: // 3 before 2 after
 231             case L2I: // 2 before 1 after
 232             case L2F: // 2 before 1 after
 233             case D2I: // 2 before 1 after
 234             case D2F: // 2 before 1 after
 235             case FSUB:
 236             case FMUL:
 237             case FDIV:
 238             case FREM:
 239             case FCMPL: // 2 before 1 after
 240             case FCMPG: // 2 before 1 after
 241             case IMUL:
 242             case IDIV:
 243             case IREM:
 244             case ISHL:
 245             case ISHR:
 246             case IUSHR:
 247             case IAND:
 248             case IOR:
 249             case IXOR:
 250             case MONITORENTER:
 251             case MONITOREXIT:
 252                 popValue();
 253                 break;
 254             case POP2:
 255             case LSUB:
 256             case LMUL:
 257             case LDIV:
 258             case LREM:
 259             case LADD:
 260             case LAND:
 261             case LOR:
 262             case LXOR:
 263             case DADD:
 264             case DMUL:
 265             case DSUB:
 266             case DDIV:
 267             case DREM:
 268                 popValue();
 269                 popValue();
 270                 break;
 271             case IASTORE:
 272             case FASTORE:
 273             case AASTORE:
 274             case BASTORE:
 275             case CASTORE:
 276             case SASTORE:
 277             case LCMP: // 4 before 1 after
 278             case DCMPL:
 279             case DCMPG:
 280                 popValue();
 281                 popValue();
 282                 popValue();
 283                 break;
 284             case LASTORE:
 285             case DASTORE:
 286                 popValue();
 287                 popValue();
 288                 popValue();
 289                 popValue();
 290                 break;
 291             case DUP:
 292                 pushValue(peekValue());
 293                 break;
 294             case DUP_X1:
 295                 s = stackFrame.size();
 296                 stackFrame.add(s - 2, stackFrame.get(s - 1));
 297                 break;
 298             case DUP_X2:
 299                 s = stackFrame.size();
 300                 stackFrame.add(s - 3, stackFrame.get(s - 1));
 301                 break;
 302             case DUP2:
 303                 s = stackFrame.size();
 304                 stackFrame.add(s - 2, stackFrame.get(s - 1));
 305                 stackFrame.add(s - 2, stackFrame.get(s - 1));
 306                 break;
 307             case DUP2_X1:
 308                 s = stackFrame.size();
 309                 stackFrame.add(s - 3, stackFrame.get(s - 1));
 310                 stackFrame.add(s - 3, stackFrame.get(s - 1));
 311                 break;
 312             case DUP2_X2:
 313                 s = stackFrame.size();
 314                 stackFrame.add(s - 4, stackFrame.get(s - 1));
 315                 stackFrame.add(s - 4, stackFrame.get(s - 1));
 316                 break;
 317             case SWAP:
 318                 s = stackFrame.size();
 319                 stackFrame.add(s - 2, stackFrame.get(s - 1));
 320                 stackFrame.remove(s);
 321                 break;
 322             }
 323         } else {
 324             switch (opcode) {
 325             case RETURN:
 326             case IRETURN:
 327             case FRETURN:
 328             case ARETURN:
 329             case LRETURN:
 330             case DRETURN:
 331             case ATHROW:
 332                 onMethodExit(opcode);
 333                 break;
 334             }
 335         }
 336         mv.visitInsn(opcode);
 337     }
 338 
 339     @Override
 340     public void visitVarInsn(final int opcode, final int var) {
 341         super.visitVarInsn(opcode, var);
 342         if (constructor) {
 343             switch (opcode) {
 344             case ILOAD:
 345             case FLOAD:
 346                 pushValue(OTHER);
 347                 break;
 348             case LLOAD:
 349             case DLOAD:
 350                 pushValue(OTHER);
 351                 pushValue(OTHER);
 352                 break;
 353             case ALOAD:
 354                 pushValue(var == 0 ? THIS : OTHER);
 355                 break;
 356             case ASTORE:
 357             case ISTORE:
 358             case FSTORE:
 359                 popValue();
 360                 break;
 361             case LSTORE:
 362             case DSTORE:
 363                 popValue();
 364                 popValue();
 365                 break;
 366             }
 367         }
 368     }
 369 
 370     @Override
 371     public void visitFieldInsn(final int opcode, final String owner,
 372             final String name, final String desc) {
 373         mv.visitFieldInsn(opcode, owner, name, desc);
 374         if (constructor) {
 375             char c = desc.charAt(0);
 376             boolean longOrDouble = c == 'J' || c == 'D';
 377             switch (opcode) {
 378             case GETSTATIC:
 379                 pushValue(OTHER);
 380                 if (longOrDouble) {
 381                     pushValue(OTHER);
 382                 }
 383                 break;
 384             case PUTSTATIC:
 385                 popValue();
 386                 if (longOrDouble) {
 387                     popValue();
 388                 }
 389                 break;
 390             case PUTFIELD:
 391                 popValue();
 392                 popValue();
 393                 if (longOrDouble) {
 394                     popValue();
 395                 }
 396                 break;
 397             // case GETFIELD:
 398             default:
 399                 if (longOrDouble) {
 400                     pushValue(OTHER);
 401                 }
 402             }
 403         }
 404     }
 405 
 406     @Override
 407     public void visitIntInsn(final int opcode, final int operand) {
 408         mv.visitIntInsn(opcode, operand);
 409         if (constructor && opcode != NEWARRAY) {
 410             pushValue(OTHER);
 411         }
 412     }
 413 
 414     @Override
 415     public void visitLdcInsn(final Object cst) {
 416         mv.visitLdcInsn(cst);
 417         if (constructor) {
 418             pushValue(OTHER);
 419             if (cst instanceof Double || cst instanceof Long) {
 420                 pushValue(OTHER);
 421             }
 422         }
 423     }
 424 
 425     @Override
 426     public void visitMultiANewArrayInsn(final String desc, final int dims) {
 427         mv.visitMultiANewArrayInsn(desc, dims);
 428         if (constructor) {
 429             for (int i = 0; i < dims; i++) {
 430                 popValue();
 431             }
 432             pushValue(OTHER);
 433         }
 434     }
 435 
 436     @Override
 437     public void visitTypeInsn(final int opcode, final String type) {
 438         mv.visitTypeInsn(opcode, type);
 439         // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
 440         if (constructor && opcode == NEW) {
 441             pushValue(OTHER);
 442         }
 443     }
 444 
 445     @Deprecated
 446     @Override
 447     public void visitMethodInsn(final int opcode, final String owner,
 448             final String name, final String desc) {
 449         if (api >= Opcodes.ASM5) {
 450             super.visitMethodInsn(opcode, owner, name, desc);
 451             return;
 452         }
 453         doVisitMethodInsn(opcode, owner, name, desc,
 454                 opcode == Opcodes.INVOKEINTERFACE);
 455     }
 456 
 457     @Override
 458     public void visitMethodInsn(final int opcode, final String owner,
 459             final String name, final String desc, final boolean itf) {
 460         if (api < Opcodes.ASM5) {
 461             super.visitMethodInsn(opcode, owner, name, desc, itf);
 462             return;
 463         }
 464         doVisitMethodInsn(opcode, owner, name, desc, itf);
 465     }
 466 
 467     private void doVisitMethodInsn(int opcode, final String owner,
 468             final String name, final String desc, final boolean itf) {
 469         mv.visitMethodInsn(opcode, owner, name, desc, itf);
 470         if (constructor) {
 471             Type[] types = Type.getArgumentTypes(desc);
 472             for (int i = 0; i < types.length; i++) {
 473                 popValue();
 474                 if (types[i].getSize() == 2) {
 475                     popValue();
 476                 }
 477             }
 478             switch (opcode) {
 479             // case INVOKESTATIC:
 480             // break;
 481             case INVOKEINTERFACE:
 482             case INVOKEVIRTUAL:
 483                 popValue(); // objectref
 484                 break;
 485             case INVOKESPECIAL:
 486                 Object type = popValue(); // objectref
 487                 if (type == THIS && !superInitialized) {
 488                     onMethodEnter();
 489                     superInitialized = true;
 490                     // once super has been initialized it is no longer
 491                     // necessary to keep track of stack state
 492                     constructor = false;
 493                 }
 494                 break;
 495             }
 496 
 497             Type returnType = Type.getReturnType(desc);
 498             if (returnType != Type.VOID_TYPE) {
 499                 pushValue(OTHER);
 500                 if (returnType.getSize() == 2) {
 501                     pushValue(OTHER);
 502                 }
 503             }
 504         }
 505     }
 506 
 507     @Override
 508     public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
 509             Object... bsmArgs) {
 510         mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
 511         if (constructor) {
 512             Type[] types = Type.getArgumentTypes(desc);
 513             for (int i = 0; i < types.length; i++) {
 514                 popValue();
 515                 if (types[i].getSize() == 2) {
 516                     popValue();
 517                 }
 518             }
 519 
 520             Type returnType = Type.getReturnType(desc);
 521             if (returnType != Type.VOID_TYPE) {
 522                 pushValue(OTHER);
 523                 if (returnType.getSize() == 2) {
 524                     pushValue(OTHER);
 525                 }
 526             }
 527         }
 528     }
 529 
 530     @Override
 531     public void visitJumpInsn(final int opcode, final Label label) {
 532         mv.visitJumpInsn(opcode, label);
 533         if (constructor) {
 534             switch (opcode) {
 535             case IFEQ:
 536             case IFNE:
 537             case IFLT:
 538             case IFGE:
 539             case IFGT:
 540             case IFLE:
 541             case IFNULL:
 542             case IFNONNULL:
 543                 popValue();
 544                 break;
 545             case IF_ICMPEQ:
 546             case IF_ICMPNE:
 547             case IF_ICMPLT:
 548             case IF_ICMPGE:
 549             case IF_ICMPGT:
 550             case IF_ICMPLE:
 551             case IF_ACMPEQ:
 552             case IF_ACMPNE:
 553                 popValue();
 554                 popValue();
 555                 break;
 556             case JSR:
 557                 pushValue(OTHER);
 558                 break;
 559             }
 560             addBranch(label);
 561         }
 562     }
 563 
 564     @Override
 565     public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
 566             final Label[] labels) {
 567         mv.visitLookupSwitchInsn(dflt, keys, labels);
 568         if (constructor) {
 569             popValue();
 570             addBranches(dflt, labels);
 571         }
 572     }
 573 
 574     @Override
 575     public void visitTableSwitchInsn(final int min, final int max,
 576             final Label dflt, final Label... labels) {
 577         mv.visitTableSwitchInsn(min, max, dflt, labels);
 578         if (constructor) {
 579             popValue();
 580             addBranches(dflt, labels);
 581         }
 582     }
 583 
 584     @Override
 585     public void visitTryCatchBlock(Label start, Label end, Label handler,
 586             String type) {
 587         super.visitTryCatchBlock(start, end, handler, type);
 588         if (constructor && !branches.containsKey(handler)) {
 589             List<Object> stackFrame = new ArrayList<Object>();
 590             stackFrame.add(OTHER);
 591             branches.put(handler, stackFrame);
 592         }
 593     }
 594 
 595     private void addBranches(final Label dflt, final Label[] labels) {
 596         addBranch(dflt);
 597         for (int i = 0; i < labels.length; i++) {
 598             addBranch(labels[i]);
 599         }
 600     }
 601 
 602     private void addBranch(final Label label) {
 603         if (branches.containsKey(label)) {
 604             return;
 605         }
 606         branches.put(label, new ArrayList<Object>(stackFrame));
 607     }
 608 
 609     private Object popValue() {
 610         return stackFrame.remove(stackFrame.size() - 1);
 611     }
 612 
 613     private Object peekValue() {
 614         return stackFrame.get(stackFrame.size() - 1);
 615     }
 616 
 617     private void pushValue(final Object o) {
 618         stackFrame.add(o);
 619     }
 620 
 621     /**
 622      * Called at the beginning of the method or after super class call in
 623      * the constructor. <br>
 624      * <br>
 625      *
 626      * <i>Custom code can use or change all the local variables, but should not
 627      * change state of the stack.</i>
 628      */
 629     protected void onMethodEnter() {
 630     }
 631 
 632     /**
 633      * Called before explicit exit from the method using either return or throw.
 634      * Top element on the stack contains the return value or exception instance.
 635      * For example:
 636      *
 637      * <pre>
 638      *   public void onMethodExit(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(Object param, int opcode) {
 657      *     ...
 658      * </pre>
 659      *
 660      * <br>
 661      * <br>
 662      *
 663      * <i>Custom code can use or change all the local variables, but should not
 664      * change state of the stack.</i>
 665      *
 666      * @param opcode
 667      *            one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN
 668      *            or ATHROW
 669      *
 670      */
 671     protected void onMethodExit(int opcode) {
 672     }
 673 
 674     // TODO onException, onMethodCall
 675 }