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