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.Arrays; 034import java.util.List; 035 036/** 037 * A {@link MethodVisitor} with convenient methods to generate code. For example, using this 038 * adapter, the class below 039 * 040 * <pre> 041 * public class Example { 042 * public static void main(String[] args) { 043 * System.out.println("Hello world!"); 044 * } 045 * } 046 * </pre> 047 * 048 * <p>can be generated as follows: 049 * 050 * <pre> 051 * ClassWriter cw = new ClassWriter(0); 052 * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); 053 * 054 * Method m = Method.getMethod("void <init> ()"); 055 * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); 056 * mg.loadThis(); 057 * mg.invokeConstructor(Type.getType(Object.class), m); 058 * mg.returnValue(); 059 * mg.endMethod(); 060 * 061 * m = Method.getMethod("void main (String[])"); 062 * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); 063 * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); 064 * mg.push("Hello world!"); 065 * mg.invokeVirtual(Type.getType(PrintStream.class), 066 * Method.getMethod("void println (String)")); 067 * mg.returnValue(); 068 * mg.endMethod(); 069 * 070 * cw.visitEnd(); 071 * </pre> 072 * 073 * @author Juozas Baliuka 074 * @author Chris Nokleberg 075 * @author Eric Bruneton 076 * @author Prashant Deva 077 */ 078public class GeneratorAdapter extends LocalVariablesSorter { 079 080 private static final String CLASS_DESCRIPTOR = "Ljava/lang/Class;"; 081 082 private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); 083 084 private static final Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); 085 086 private static final Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); 087 088 private static final Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); 089 090 private static final Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); 091 092 private static final Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); 093 094 private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long"); 095 096 private static final Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double"); 097 098 private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); 099 100 private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); 101 102 private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); 103 104 private static final Method CHAR_VALUE = Method.getMethod("char charValue()"); 105 106 private static final Method INT_VALUE = Method.getMethod("int intValue()"); 107 108 private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()"); 109 110 private static final Method LONG_VALUE = Method.getMethod("long longValue()"); 111 112 private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); 113 114 /** Constant for the {@link #math} method. */ 115 public static final int ADD = Opcodes.IADD; 116 117 /** Constant for the {@link #math} method. */ 118 public static final int SUB = Opcodes.ISUB; 119 120 /** Constant for the {@link #math} method. */ 121 public static final int MUL = Opcodes.IMUL; 122 123 /** Constant for the {@link #math} method. */ 124 public static final int DIV = Opcodes.IDIV; 125 126 /** Constant for the {@link #math} method. */ 127 public static final int REM = Opcodes.IREM; 128 129 /** Constant for the {@link #math} method. */ 130 public static final int NEG = Opcodes.INEG; 131 132 /** Constant for the {@link #math} method. */ 133 public static final int SHL = Opcodes.ISHL; 134 135 /** Constant for the {@link #math} method. */ 136 public static final int SHR = Opcodes.ISHR; 137 138 /** Constant for the {@link #math} method. */ 139 public static final int USHR = Opcodes.IUSHR; 140 141 /** Constant for the {@link #math} method. */ 142 public static final int AND = Opcodes.IAND; 143 144 /** Constant for the {@link #math} method. */ 145 public static final int OR = Opcodes.IOR; 146 147 /** Constant for the {@link #math} method. */ 148 public static final int XOR = Opcodes.IXOR; 149 150 /** Constant for the {@link #ifCmp} method. */ 151 public static final int EQ = Opcodes.IFEQ; 152 153 /** Constant for the {@link #ifCmp} method. */ 154 public static final int NE = Opcodes.IFNE; 155 156 /** Constant for the {@link #ifCmp} method. */ 157 public static final int LT = Opcodes.IFLT; 158 159 /** Constant for the {@link #ifCmp} method. */ 160 public static final int GE = Opcodes.IFGE; 161 162 /** Constant for the {@link #ifCmp} method. */ 163 public static final int GT = Opcodes.IFGT; 164 165 /** Constant for the {@link #ifCmp} method. */ 166 public static final int LE = Opcodes.IFLE; 167 168 /** The access flags of the visited method. */ 169 private final int access; 170 171 /** The name of the visited method. */ 172 private final String name; 173 174 /** The return type of the visited method. */ 175 private final Type returnType; 176 177 /** The argument types of the visited method. */ 178 private final Type[] argumentTypes; 179 180 /** The types of the local variables of the visited method. */ 181 private final List<Type> localTypes = new ArrayList<>(); 182 183 /** 184 * Constructs a new {@link GeneratorAdapter}. <i>Subclasses must not use this constructor</i>. 185 * Instead, they must use the {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} 186 * version. 187 * 188 * @param methodVisitor the method visitor to which this adapter delegates calls. 189 * @param access the method's access flags (see {@link Opcodes}). 190 * @param name the method's name. 191 * @param descriptor the method's descriptor (see {@link Type}). 192 * @throws IllegalStateException if a subclass calls this constructor. 193 */ 194 public GeneratorAdapter( 195 final MethodVisitor methodVisitor, 196 final int access, 197 final String name, 198 final String descriptor) { 199 this(/* latest api = */ Opcodes.ASM9, methodVisitor, access, name, descriptor); 200 if (getClass() != GeneratorAdapter.class) { 201 throw new IllegalStateException(); 202 } 203 } 204 205 /** 206 * Constructs a new {@link GeneratorAdapter}. 207 * 208 * @param api the ASM API version implemented by this visitor. Must be one of the {@code 209 * ASM}<i>x</i> values in {@link Opcodes}. 210 * @param methodVisitor the method visitor to which this adapter delegates calls. 211 * @param access the method's access flags (see {@link Opcodes}). 212 * @param name the method's name. 213 * @param descriptor the method's descriptor (see {@link Type}). 214 */ 215 protected GeneratorAdapter( 216 final int api, 217 final MethodVisitor methodVisitor, 218 final int access, 219 final String name, 220 final String descriptor) { 221 super(api, access, descriptor, methodVisitor); 222 this.access = access; 223 this.name = name; 224 this.returnType = Type.getReturnType(descriptor); 225 this.argumentTypes = Type.getArgumentTypes(descriptor); 226 } 227 228 /** 229 * Constructs a new {@link GeneratorAdapter}. <i>Subclasses must not use this constructor</i>. 230 * Instead, they must use the {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} 231 * version. 232 * 233 * @param access access flags of the adapted method. 234 * @param method the adapted method. 235 * @param methodVisitor the method visitor to which this adapter delegates calls. 236 */ 237 public GeneratorAdapter( 238 final int access, final Method method, final MethodVisitor methodVisitor) { 239 this(methodVisitor, access, method.getName(), method.getDescriptor()); 240 } 241 242 /** 243 * Constructs a new {@link GeneratorAdapter}. <i>Subclasses must not use this constructor</i>. 244 * Instead, they must use the {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} 245 * version. 246 * 247 * @param access access flags of the adapted method. 248 * @param method the adapted method. 249 * @param signature the signature of the adapted method (may be {@literal null}). 250 * @param exceptions the exceptions thrown by the adapted method (may be {@literal null}). 251 * @param classVisitor the class visitor to which this adapter delegates calls. 252 */ 253 public GeneratorAdapter( 254 final int access, 255 final Method method, 256 final String signature, 257 final Type[] exceptions, 258 final ClassVisitor classVisitor) { 259 this( 260 access, 261 method, 262 classVisitor.visitMethod( 263 access, 264 method.getName(), 265 method.getDescriptor(), 266 signature, 267 exceptions == null ? null : getInternalNames(exceptions))); 268 } 269 270 /** 271 * Returns the internal names of the given types. 272 * 273 * @param types a set of types. 274 * @return the internal names of the given types (see {@link Type#getInternalName()}). 275 */ 276 private static String[] getInternalNames(final Type[] types) { 277 String[] names = new String[types.length]; 278 for (int i = 0; i < names.length; ++i) { 279 names[i] = types[i].getInternalName(); 280 } 281 return names; 282 } 283 284 public int getAccess() { 285 return access; 286 } 287 288 public String getName() { 289 return name; 290 } 291 292 public Type getReturnType() { 293 return returnType; 294 } 295 296 public Type[] getArgumentTypes() { 297 return argumentTypes.clone(); 298 } 299 300 // ----------------------------------------------------------------------------------------------- 301 // Instructions to push constants on the stack 302 // ----------------------------------------------------------------------------------------------- 303 304 /** 305 * Generates the instruction to push the given value on the stack. 306 * 307 * @param value the value to be pushed on the stack. 308 */ 309 public void push(final boolean value) { 310 push(value ? 1 : 0); 311 } 312 313 /** 314 * Generates the instruction to push the given value on the stack. 315 * 316 * @param value the value to be pushed on the stack. 317 */ 318 public void push(final int value) { 319 if (value >= -1 && value <= 5) { 320 mv.visitInsn(Opcodes.ICONST_0 + value); 321 } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { 322 mv.visitIntInsn(Opcodes.BIPUSH, value); 323 } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { 324 mv.visitIntInsn(Opcodes.SIPUSH, value); 325 } else { 326 mv.visitLdcInsn(value); 327 } 328 } 329 330 /** 331 * Generates the instruction to push the given value on the stack. 332 * 333 * @param value the value to be pushed on the stack. 334 */ 335 public void push(final long value) { 336 if (value == 0L || value == 1L) { 337 mv.visitInsn(Opcodes.LCONST_0 + (int) value); 338 } else { 339 mv.visitLdcInsn(value); 340 } 341 } 342 343 /** 344 * Generates the instruction to push the given value on the stack. 345 * 346 * @param value the value to be pushed on the stack. 347 */ 348 public void push(final float value) { 349 int bits = Float.floatToIntBits(value); 350 if (bits == 0L || bits == 0x3F800000 || bits == 0x40000000) { // 0..2 351 mv.visitInsn(Opcodes.FCONST_0 + (int) value); 352 } else { 353 mv.visitLdcInsn(value); 354 } 355 } 356 357 /** 358 * Generates the instruction to push the given value on the stack. 359 * 360 * @param value the value to be pushed on the stack. 361 */ 362 public void push(final double value) { 363 long bits = Double.doubleToLongBits(value); 364 if (bits == 0L || bits == 0x3FF0000000000000L) { // +0.0d and 1.0d 365 mv.visitInsn(Opcodes.DCONST_0 + (int) value); 366 } else { 367 mv.visitLdcInsn(value); 368 } 369 } 370 371 /** 372 * Generates the instruction to push the given value on the stack. 373 * 374 * @param value the value to be pushed on the stack. May be {@literal null}. 375 */ 376 public void push(final String value) { 377 if (value == null) { 378 mv.visitInsn(Opcodes.ACONST_NULL); 379 } else { 380 mv.visitLdcInsn(value); 381 } 382 } 383 384 /** 385 * Generates the instruction to push the given value on the stack. 386 * 387 * @param value the value to be pushed on the stack. 388 */ 389 public void push(final Type value) { 390 if (value == null) { 391 mv.visitInsn(Opcodes.ACONST_NULL); 392 } else { 393 switch (value.getSort()) { 394 case Type.VOID: 395 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", CLASS_DESCRIPTOR); 396 break; 397 case Type.BOOLEAN: 398 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", CLASS_DESCRIPTOR); 399 break; 400 case Type.CHAR: 401 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", CLASS_DESCRIPTOR); 402 break; 403 case Type.BYTE: 404 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", CLASS_DESCRIPTOR); 405 break; 406 case Type.SHORT: 407 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", CLASS_DESCRIPTOR); 408 break; 409 case Type.INT: 410 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", CLASS_DESCRIPTOR); 411 break; 412 case Type.FLOAT: 413 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", CLASS_DESCRIPTOR); 414 break; 415 case Type.LONG: 416 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", CLASS_DESCRIPTOR); 417 break; 418 case Type.DOUBLE: 419 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", CLASS_DESCRIPTOR); 420 break; 421 default: 422 mv.visitLdcInsn(value); 423 break; 424 } 425 } 426 } 427 428 /** 429 * Generates the instruction to push a handle on the stack. 430 * 431 * @param handle the handle to be pushed on the stack. 432 */ 433 public void push(final Handle handle) { 434 if (handle == null) { 435 mv.visitInsn(Opcodes.ACONST_NULL); 436 } else { 437 mv.visitLdcInsn(handle); 438 } 439 } 440 441 /** 442 * Generates the instruction to push a constant dynamic on the stack. 443 * 444 * @param constantDynamic the constant dynamic to be pushed on the stack. 445 */ 446 public void push(final ConstantDynamic constantDynamic) { 447 if (constantDynamic == null) { 448 mv.visitInsn(Opcodes.ACONST_NULL); 449 } else { 450 mv.visitLdcInsn(constantDynamic); 451 } 452 } 453 454 // ----------------------------------------------------------------------------------------------- 455 // Instructions to load and store method arguments 456 // ----------------------------------------------------------------------------------------------- 457 458 /** 459 * Returns the index of the given method argument in the frame's local variables array. 460 * 461 * @param arg the index of a method argument. 462 * @return the index of the given method argument in the frame's local variables array. 463 */ 464 private int getArgIndex(final int arg) { 465 int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; 466 for (int i = 0; i < arg; i++) { 467 index += argumentTypes[i].getSize(); 468 } 469 return index; 470 } 471 472 /** 473 * Generates the instruction to push a local variable on the stack. 474 * 475 * @param type the type of the local variable to be loaded. 476 * @param index an index in the frame's local variables array. 477 */ 478 private void loadInsn(final Type type, final int index) { 479 mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); 480 } 481 482 /** 483 * Generates the instruction to store the top stack value in a local variable. 484 * 485 * @param type the type of the local variable to be stored. 486 * @param index an index in the frame's local variables array. 487 */ 488 private void storeInsn(final Type type, final int index) { 489 mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); 490 } 491 492 /** Generates the instruction to load 'this' on the stack. */ 493 public void loadThis() { 494 if ((access & Opcodes.ACC_STATIC) != 0) { 495 throw new IllegalStateException("no 'this' pointer within static method"); 496 } 497 mv.visitVarInsn(Opcodes.ALOAD, 0); 498 } 499 500 /** 501 * Generates the instruction to load the given method argument on the stack. 502 * 503 * @param arg the index of a method argument. 504 */ 505 public void loadArg(final int arg) { 506 loadInsn(argumentTypes[arg], getArgIndex(arg)); 507 } 508 509 /** 510 * Generates the instructions to load the given method arguments on the stack. 511 * 512 * @param arg the index of the first method argument to be loaded. 513 * @param count the number of method arguments to be loaded. 514 */ 515 public void loadArgs(final int arg, final int count) { 516 int index = getArgIndex(arg); 517 for (int i = 0; i < count; ++i) { 518 Type argumentType = argumentTypes[arg + i]; 519 loadInsn(argumentType, index); 520 index += argumentType.getSize(); 521 } 522 } 523 524 /** Generates the instructions to load all the method arguments on the stack. */ 525 public void loadArgs() { 526 loadArgs(0, argumentTypes.length); 527 } 528 529 /** 530 * Generates the instructions to load all the method arguments on the stack, as a single object 531 * array. 532 */ 533 public void loadArgArray() { 534 push(argumentTypes.length); 535 newArray(OBJECT_TYPE); 536 for (int i = 0; i < argumentTypes.length; i++) { 537 dup(); 538 push(i); 539 loadArg(i); 540 box(argumentTypes[i]); 541 arrayStore(OBJECT_TYPE); 542 } 543 } 544 545 /** 546 * Generates the instruction to store the top stack value in the given method argument. 547 * 548 * @param arg the index of a method argument. 549 */ 550 public void storeArg(final int arg) { 551 storeInsn(argumentTypes[arg], getArgIndex(arg)); 552 } 553 554 // ----------------------------------------------------------------------------------------------- 555 // Instructions to load and store local variables 556 // ----------------------------------------------------------------------------------------------- 557 558 /** 559 * Returns the type of the given local variable. 560 * 561 * @param local a local variable identifier, as returned by {@link 562 * LocalVariablesSorter#newLocal(Type)}. 563 * @return the type of the given local variable. 564 */ 565 public Type getLocalType(final int local) { 566 return localTypes.get(local - firstLocal); 567 } 568 569 @Override 570 protected void setLocalType(final int local, final Type type) { 571 int index = local - firstLocal; 572 while (localTypes.size() < index + 1) { 573 localTypes.add(null); 574 } 575 localTypes.set(index, type); 576 } 577 578 /** 579 * Generates the instruction to load the given local variable on the stack. 580 * 581 * @param local a local variable identifier, as returned by {@link 582 * LocalVariablesSorter#newLocal(Type)}. 583 */ 584 public void loadLocal(final int local) { 585 loadInsn(getLocalType(local), local); 586 } 587 588 /** 589 * Generates the instruction to load the given local variable on the stack. 590 * 591 * @param local a local variable identifier, as returned by {@link 592 * LocalVariablesSorter#newLocal(Type)}. 593 * @param type the type of this local variable. 594 */ 595 public void loadLocal(final int local, final Type type) { 596 setLocalType(local, type); 597 loadInsn(type, local); 598 } 599 600 /** 601 * Generates the instruction to store the top stack value in the given local variable. 602 * 603 * @param local a local variable identifier, as returned by {@link 604 * LocalVariablesSorter#newLocal(Type)}. 605 */ 606 public void storeLocal(final int local) { 607 storeInsn(getLocalType(local), local); 608 } 609 610 /** 611 * Generates the instruction to store the top stack value in the given local variable. 612 * 613 * @param local a local variable identifier, as returned by {@link 614 * LocalVariablesSorter#newLocal(Type)}. 615 * @param type the type of this local variable. 616 */ 617 public void storeLocal(final int local, final Type type) { 618 setLocalType(local, type); 619 storeInsn(type, local); 620 } 621 622 /** 623 * Generates the instruction to load an element from an array. 624 * 625 * @param type the type of the array element to be loaded. 626 */ 627 public void arrayLoad(final Type type) { 628 mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); 629 } 630 631 /** 632 * Generates the instruction to store an element in an array. 633 * 634 * @param type the type of the array element to be stored. 635 */ 636 public void arrayStore(final Type type) { 637 mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); 638 } 639 640 // ----------------------------------------------------------------------------------------------- 641 // Instructions to manage the stack 642 // ----------------------------------------------------------------------------------------------- 643 644 /** Generates a POP instruction. */ 645 public void pop() { 646 mv.visitInsn(Opcodes.POP); 647 } 648 649 /** Generates a POP2 instruction. */ 650 public void pop2() { 651 mv.visitInsn(Opcodes.POP2); 652 } 653 654 /** Generates a DUP instruction. */ 655 public void dup() { 656 mv.visitInsn(Opcodes.DUP); 657 } 658 659 /** Generates a DUP2 instruction. */ 660 public void dup2() { 661 mv.visitInsn(Opcodes.DUP2); 662 } 663 664 /** Generates a DUP_X1 instruction. */ 665 public void dupX1() { 666 mv.visitInsn(Opcodes.DUP_X1); 667 } 668 669 /** Generates a DUP_X2 instruction. */ 670 public void dupX2() { 671 mv.visitInsn(Opcodes.DUP_X2); 672 } 673 674 /** Generates a DUP2_X1 instruction. */ 675 public void dup2X1() { 676 mv.visitInsn(Opcodes.DUP2_X1); 677 } 678 679 /** Generates a DUP2_X2 instruction. */ 680 public void dup2X2() { 681 mv.visitInsn(Opcodes.DUP2_X2); 682 } 683 684 /** Generates a SWAP instruction. */ 685 public void swap() { 686 mv.visitInsn(Opcodes.SWAP); 687 } 688 689 /** 690 * Generates the instructions to swap the top two stack values. 691 * 692 * @param prev type of the top - 1 stack value. 693 * @param type type of the top stack value. 694 */ 695 public void swap(final Type prev, final Type type) { 696 if (type.getSize() == 1) { 697 if (prev.getSize() == 1) { 698 swap(); // Same as dupX1 pop. 699 } else { 700 dupX2(); 701 pop(); 702 } 703 } else { 704 if (prev.getSize() == 1) { 705 dup2X1(); 706 pop2(); 707 } else { 708 dup2X2(); 709 pop2(); 710 } 711 } 712 } 713 714 // ----------------------------------------------------------------------------------------------- 715 // Instructions to do mathematical and logical operations 716 // ----------------------------------------------------------------------------------------------- 717 718 /** 719 * Generates the instruction to do the specified mathematical or logical operation. 720 * 721 * @param op a mathematical or logical operation. Must be one of ADD, SUB, MUL, DIV, REM, NEG, 722 * SHL, SHR, USHR, AND, OR, XOR. 723 * @param type the type of the operand(s) for this operation. 724 */ 725 public void math(final int op, final Type type) { 726 mv.visitInsn(type.getOpcode(op)); 727 } 728 729 /** Generates the instructions to compute the bitwise negation of the top stack value. */ 730 public void not() { 731 mv.visitInsn(Opcodes.ICONST_1); 732 mv.visitInsn(Opcodes.IXOR); 733 } 734 735 /** 736 * Generates the instruction to increment the given local variable. 737 * 738 * @param local the local variable to be incremented. 739 * @param amount the amount by which the local variable must be incremented. 740 */ 741 public void iinc(final int local, final int amount) { 742 mv.visitIincInsn(local, amount); 743 } 744 745 /** 746 * Generates the instructions to cast a numerical value from one type to another. 747 * 748 * @param from the type of the top stack value 749 * @param to the type into which this value must be cast. 750 */ 751 public void cast(final Type from, final Type to) { 752 if (from != to) { 753 if (from.getSort() < Type.BOOLEAN 754 || from.getSort() > Type.DOUBLE 755 || to.getSort() < Type.BOOLEAN 756 || to.getSort() > Type.DOUBLE) { 757 throw new IllegalArgumentException("Cannot cast from " + from + " to " + to); 758 } 759 InstructionAdapter.cast(mv, from, to); 760 } 761 } 762 763 // ----------------------------------------------------------------------------------------------- 764 // Instructions to do boxing and unboxing operations 765 // ----------------------------------------------------------------------------------------------- 766 767 private static Type getBoxedType(final Type type) { 768 switch (type.getSort()) { 769 case Type.BYTE: 770 return BYTE_TYPE; 771 case Type.BOOLEAN: 772 return BOOLEAN_TYPE; 773 case Type.SHORT: 774 return SHORT_TYPE; 775 case Type.CHAR: 776 return CHARACTER_TYPE; 777 case Type.INT: 778 return INTEGER_TYPE; 779 case Type.FLOAT: 780 return FLOAT_TYPE; 781 case Type.LONG: 782 return LONG_TYPE; 783 case Type.DOUBLE: 784 return DOUBLE_TYPE; 785 default: 786 return type; 787 } 788 } 789 790 /** 791 * Generates the instructions to box the top stack value. This value is replaced by its boxed 792 * equivalent on top of the stack. 793 * 794 * @param type the type of the top stack value. 795 */ 796 public void box(final Type type) { 797 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 798 return; 799 } 800 if (type == Type.VOID_TYPE) { 801 push((String) null); 802 } else { 803 Type boxedType = getBoxedType(type); 804 newInstance(boxedType); 805 if (type.getSize() == 2) { 806 // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o 807 dupX2(); 808 dupX2(); 809 pop(); 810 } else { 811 // p -> po -> opo -> oop -> o 812 dupX1(); 813 swap(); 814 } 815 invokeConstructor(boxedType, new Method("<init>", Type.VOID_TYPE, new Type[] {type})); 816 } 817 } 818 819 /** 820 * Generates the instructions to box the top stack value using Java 5's valueOf() method. This 821 * value is replaced by its boxed equivalent on top of the stack. 822 * 823 * @param type the type of the top stack value. 824 */ 825 public void valueOf(final Type type) { 826 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 827 return; 828 } 829 if (type == Type.VOID_TYPE) { 830 push((String) null); 831 } else { 832 Type boxedType = getBoxedType(type); 833 invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[] {type})); 834 } 835 } 836 837 /** 838 * Generates the instructions to unbox the top stack value. This value is replaced by its unboxed 839 * equivalent on top of the stack. 840 * 841 * @param type the type of the top stack value. 842 */ 843 public void unbox(final Type type) { 844 Type boxedType = NUMBER_TYPE; 845 Method unboxMethod; 846 switch (type.getSort()) { 847 case Type.VOID: 848 return; 849 case Type.CHAR: 850 boxedType = CHARACTER_TYPE; 851 unboxMethod = CHAR_VALUE; 852 break; 853 case Type.BOOLEAN: 854 boxedType = BOOLEAN_TYPE; 855 unboxMethod = BOOLEAN_VALUE; 856 break; 857 case Type.DOUBLE: 858 unboxMethod = DOUBLE_VALUE; 859 break; 860 case Type.FLOAT: 861 unboxMethod = FLOAT_VALUE; 862 break; 863 case Type.LONG: 864 unboxMethod = LONG_VALUE; 865 break; 866 case Type.INT: 867 case Type.SHORT: 868 case Type.BYTE: 869 unboxMethod = INT_VALUE; 870 break; 871 default: 872 unboxMethod = null; 873 break; 874 } 875 if (unboxMethod == null) { 876 checkCast(type); 877 } else { 878 checkCast(boxedType); 879 invokeVirtual(boxedType, unboxMethod); 880 } 881 } 882 883 // ----------------------------------------------------------------------------------------------- 884 // Instructions to jump to other instructions 885 // ----------------------------------------------------------------------------------------------- 886 887 /** 888 * Constructs a new {@link Label}. 889 * 890 * @return a new {@link Label}. 891 */ 892 public Label newLabel() { 893 return new Label(); 894 } 895 896 /** 897 * Marks the current code position with the given label. 898 * 899 * @param label a label. 900 */ 901 public void mark(final Label label) { 902 mv.visitLabel(label); 903 } 904 905 /** 906 * Marks the current code position with a new label. 907 * 908 * @return the label that was created to mark the current code position. 909 */ 910 public Label mark() { 911 Label label = new Label(); 912 mv.visitLabel(label); 913 return label; 914 } 915 916 /** 917 * Generates the instructions to jump to a label based on the comparison of the top two stack 918 * values. 919 * 920 * @param type the type of the top two stack values. 921 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 922 * @param label where to jump if the comparison result is {@literal true}. 923 */ 924 public void ifCmp(final Type type, final int mode, final Label label) { 925 switch (type.getSort()) { 926 case Type.LONG: 927 mv.visitInsn(Opcodes.LCMP); 928 break; 929 case Type.DOUBLE: 930 mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL : Opcodes.DCMPG); 931 break; 932 case Type.FLOAT: 933 mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL : Opcodes.FCMPG); 934 break; 935 case Type.ARRAY: 936 case Type.OBJECT: 937 if (mode == EQ) { 938 mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); 939 return; 940 } else if (mode == NE) { 941 mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); 942 return; 943 } else { 944 throw new IllegalArgumentException("Bad comparison for type " + type); 945 } 946 default: 947 int intOp = -1; 948 switch (mode) { 949 case EQ: 950 intOp = Opcodes.IF_ICMPEQ; 951 break; 952 case NE: 953 intOp = Opcodes.IF_ICMPNE; 954 break; 955 case GE: 956 intOp = Opcodes.IF_ICMPGE; 957 break; 958 case LT: 959 intOp = Opcodes.IF_ICMPLT; 960 break; 961 case LE: 962 intOp = Opcodes.IF_ICMPLE; 963 break; 964 case GT: 965 intOp = Opcodes.IF_ICMPGT; 966 break; 967 default: 968 throw new IllegalArgumentException("Bad comparison mode " + mode); 969 } 970 mv.visitJumpInsn(intOp, label); 971 return; 972 } 973 mv.visitJumpInsn(mode, label); 974 } 975 976 /** 977 * Generates the instructions to jump to a label based on the comparison of the top two integer 978 * stack values. 979 * 980 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 981 * @param label where to jump if the comparison result is {@literal true}. 982 */ 983 public void ifICmp(final int mode, final Label label) { 984 ifCmp(Type.INT_TYPE, mode, label); 985 } 986 987 /** 988 * Generates the instructions to jump to a label based on the comparison of the top integer stack 989 * value with zero. 990 * 991 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 992 * @param label where to jump if the comparison result is {@literal true}. 993 */ 994 public void ifZCmp(final int mode, final Label label) { 995 mv.visitJumpInsn(mode, label); 996 } 997 998 /** 999 * Generates the instruction to jump to the given label if the top stack value is null. 1000 * 1001 * @param label where to jump if the condition is {@literal true}. 1002 */ 1003 public void ifNull(final Label label) { 1004 mv.visitJumpInsn(Opcodes.IFNULL, label); 1005 } 1006 1007 /** 1008 * Generates the instruction to jump to the given label if the top stack value is not null. 1009 * 1010 * @param label where to jump if the condition is {@literal true}. 1011 */ 1012 public void ifNonNull(final Label label) { 1013 mv.visitJumpInsn(Opcodes.IFNONNULL, label); 1014 } 1015 1016 /** 1017 * Generates the instruction to jump to the given label. 1018 * 1019 * @param label where to jump if the condition is {@literal true}. 1020 */ 1021 public void goTo(final Label label) { 1022 mv.visitJumpInsn(Opcodes.GOTO, label); 1023 } 1024 1025 /** 1026 * Generates a RET instruction. 1027 * 1028 * @param local a local variable identifier, as returned by {@link 1029 * LocalVariablesSorter#newLocal(Type)}. 1030 */ 1031 public void ret(final int local) { 1032 mv.visitVarInsn(Opcodes.RET, local); 1033 } 1034 1035 /** 1036 * Generates the instructions for a switch statement. 1037 * 1038 * @param keys the switch case keys. 1039 * @param generator a generator to generate the code for the switch cases. 1040 */ 1041 public void tableSwitch(final int[] keys, final TableSwitchGenerator generator) { 1042 float density; 1043 if (keys.length == 0) { 1044 density = 0; 1045 } else { 1046 density = (float) keys.length / (keys[keys.length - 1] - keys[0] + 1); 1047 } 1048 tableSwitch(keys, generator, density >= 0.5f); 1049 } 1050 1051 /** 1052 * Generates the instructions for a switch statement. 1053 * 1054 * @param keys the switch case keys. 1055 * @param generator a generator to generate the code for the switch cases. 1056 * @param useTable {@literal true} to use a TABLESWITCH instruction, or {@literal false} to use a 1057 * LOOKUPSWITCH instruction. 1058 */ 1059 public void tableSwitch( 1060 final int[] keys, final TableSwitchGenerator generator, final boolean useTable) { 1061 for (int i = 1; i < keys.length; ++i) { 1062 if (keys[i] < keys[i - 1]) { 1063 throw new IllegalArgumentException("keys must be sorted in ascending order"); 1064 } 1065 } 1066 Label defaultLabel = newLabel(); 1067 Label endLabel = newLabel(); 1068 if (keys.length > 0) { 1069 int numKeys = keys.length; 1070 if (useTable) { 1071 int min = keys[0]; 1072 int max = keys[numKeys - 1]; 1073 int range = max - min + 1; 1074 Label[] labels = new Label[range]; 1075 Arrays.fill(labels, defaultLabel); 1076 for (int i = 0; i < numKeys; ++i) { 1077 labels[keys[i] - min] = newLabel(); 1078 } 1079 mv.visitTableSwitchInsn(min, max, defaultLabel, labels); 1080 for (int i = 0; i < range; ++i) { 1081 Label label = labels[i]; 1082 if (label != defaultLabel) { 1083 mark(label); 1084 generator.generateCase(i + min, endLabel); 1085 } 1086 } 1087 } else { 1088 Label[] labels = new Label[numKeys]; 1089 for (int i = 0; i < numKeys; ++i) { 1090 labels[i] = newLabel(); 1091 } 1092 mv.visitLookupSwitchInsn(defaultLabel, keys, labels); 1093 for (int i = 0; i < numKeys; ++i) { 1094 mark(labels[i]); 1095 generator.generateCase(keys[i], endLabel); 1096 } 1097 } 1098 } 1099 mark(defaultLabel); 1100 generator.generateDefault(); 1101 mark(endLabel); 1102 } 1103 1104 /** Generates the instruction to return the top stack value to the caller. */ 1105 public void returnValue() { 1106 mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); 1107 } 1108 1109 // ----------------------------------------------------------------------------------------------- 1110 // Instructions to load and store fields 1111 // ----------------------------------------------------------------------------------------------- 1112 1113 /** 1114 * Generates a get field or set field instruction. 1115 * 1116 * @param opcode the instruction's opcode. 1117 * @param ownerType the class in which the field is defined. 1118 * @param name the name of the field. 1119 * @param fieldType the type of the field. 1120 */ 1121 private void fieldInsn( 1122 final int opcode, final Type ownerType, final String name, final Type fieldType) { 1123 mv.visitFieldInsn(opcode, ownerType.getInternalName(), name, fieldType.getDescriptor()); 1124 } 1125 1126 /** 1127 * Generates the instruction to push the value of a static field on the stack. 1128 * 1129 * @param owner the class in which the field is defined. 1130 * @param name the name of the field. 1131 * @param type the type of the field. 1132 */ 1133 public void getStatic(final Type owner, final String name, final Type type) { 1134 fieldInsn(Opcodes.GETSTATIC, owner, name, type); 1135 } 1136 1137 /** 1138 * Generates the instruction to store the top stack value in a static field. 1139 * 1140 * @param owner the class in which the field is defined. 1141 * @param name the name of the field. 1142 * @param type the type of the field. 1143 */ 1144 public void putStatic(final Type owner, final String name, final Type type) { 1145 fieldInsn(Opcodes.PUTSTATIC, owner, name, type); 1146 } 1147 1148 /** 1149 * Generates the instruction to push the value of a non static field on the stack. 1150 * 1151 * @param owner the class in which the field is defined. 1152 * @param name the name of the field. 1153 * @param type the type of the field. 1154 */ 1155 public void getField(final Type owner, final String name, final Type type) { 1156 fieldInsn(Opcodes.GETFIELD, owner, name, type); 1157 } 1158 1159 /** 1160 * Generates the instruction to store the top stack value in a non static field. 1161 * 1162 * @param owner the class in which the field is defined. 1163 * @param name the name of the field. 1164 * @param type the type of the field. 1165 */ 1166 public void putField(final Type owner, final String name, final Type type) { 1167 fieldInsn(Opcodes.PUTFIELD, owner, name, type); 1168 } 1169 1170 // ----------------------------------------------------------------------------------------------- 1171 // Instructions to invoke methods 1172 // ----------------------------------------------------------------------------------------------- 1173 1174 /** 1175 * Generates an invoke method instruction. 1176 * 1177 * @param opcode the instruction's opcode. 1178 * @param type the class in which the method is defined. 1179 * @param method the method to be invoked. 1180 * @param isInterface whether the 'type' class is an interface or not. 1181 */ 1182 private void invokeInsn( 1183 final int opcode, final Type type, final Method method, final boolean isInterface) { 1184 String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); 1185 mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor(), isInterface); 1186 } 1187 1188 /** 1189 * Generates the instruction to invoke a normal method. 1190 * 1191 * @param owner the class in which the method is defined. 1192 * @param method the method to be invoked. 1193 */ 1194 public void invokeVirtual(final Type owner, final Method method) { 1195 invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false); 1196 } 1197 1198 /** 1199 * Generates the instruction to invoke a constructor. 1200 * 1201 * @param type the class in which the constructor is defined. 1202 * @param method the constructor to be invoked. 1203 */ 1204 public void invokeConstructor(final Type type, final Method method) { 1205 invokeInsn(Opcodes.INVOKESPECIAL, type, method, false); 1206 } 1207 1208 /** 1209 * Generates the instruction to invoke a static method. 1210 * 1211 * @param owner the class in which the method is defined. 1212 * @param method the method to be invoked. 1213 */ 1214 public void invokeStatic(final Type owner, final Method method) { 1215 invokeInsn(Opcodes.INVOKESTATIC, owner, method, false); 1216 } 1217 1218 /** 1219 * Generates the instruction to invoke an interface method. 1220 * 1221 * @param owner the class in which the method is defined. 1222 * @param method the method to be invoked. 1223 */ 1224 public void invokeInterface(final Type owner, final Method method) { 1225 invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true); 1226 } 1227 1228 /** 1229 * Generates an invokedynamic instruction. 1230 * 1231 * @param name the method's name. 1232 * @param descriptor the method's descriptor (see {@link Type}). 1233 * @param bootstrapMethodHandle the bootstrap method. 1234 * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be 1235 * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link 1236 * Type} or {@link Handle} value. This method is allowed to modify the content of the array so 1237 * a caller should expect that this array may change. 1238 */ 1239 public void invokeDynamic( 1240 final String name, 1241 final String descriptor, 1242 final Handle bootstrapMethodHandle, 1243 final Object... bootstrapMethodArguments) { 1244 mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 1245 } 1246 1247 // ----------------------------------------------------------------------------------------------- 1248 // Instructions to create objects and arrays 1249 // ----------------------------------------------------------------------------------------------- 1250 1251 /** 1252 * Generates a type dependent instruction. 1253 * 1254 * @param opcode the instruction's opcode. 1255 * @param type the instruction's operand. 1256 */ 1257 private void typeInsn(final int opcode, final Type type) { 1258 mv.visitTypeInsn(opcode, type.getInternalName()); 1259 } 1260 1261 /** 1262 * Generates the instruction to create a new object. 1263 * 1264 * @param type the class of the object to be created. 1265 */ 1266 public void newInstance(final Type type) { 1267 typeInsn(Opcodes.NEW, type); 1268 } 1269 1270 /** 1271 * Generates the instruction to create a new array. 1272 * 1273 * @param type the type of the array elements. 1274 */ 1275 public void newArray(final Type type) { 1276 InstructionAdapter.newarray(mv, type); 1277 } 1278 1279 // ----------------------------------------------------------------------------------------------- 1280 // Miscellaneous instructions 1281 // ----------------------------------------------------------------------------------------------- 1282 1283 /** Generates the instruction to compute the length of an array. */ 1284 public void arrayLength() { 1285 mv.visitInsn(Opcodes.ARRAYLENGTH); 1286 } 1287 1288 /** Generates the instruction to throw an exception. */ 1289 public void throwException() { 1290 mv.visitInsn(Opcodes.ATHROW); 1291 } 1292 1293 /** 1294 * Generates the instructions to create and throw an exception. The exception class must have a 1295 * constructor with a single String argument. 1296 * 1297 * @param type the class of the exception to be thrown. 1298 * @param message the detailed message of the exception. 1299 */ 1300 public void throwException(final Type type, final String message) { 1301 newInstance(type); 1302 dup(); 1303 push(message); 1304 invokeConstructor(type, Method.getMethod("void <init> (String)")); 1305 throwException(); 1306 } 1307 1308 /** 1309 * Generates the instruction to check that the top stack value is of the given type. 1310 * 1311 * @param type a class or interface type. 1312 */ 1313 public void checkCast(final Type type) { 1314 if (!type.equals(OBJECT_TYPE)) { 1315 typeInsn(Opcodes.CHECKCAST, type); 1316 } 1317 } 1318 1319 /** 1320 * Generates the instruction to test if the top stack value is of the given type. 1321 * 1322 * @param type a class or interface type. 1323 */ 1324 public void instanceOf(final Type type) { 1325 typeInsn(Opcodes.INSTANCEOF, type); 1326 } 1327 1328 /** Generates the instruction to get the monitor of the top stack value. */ 1329 public void monitorEnter() { 1330 mv.visitInsn(Opcodes.MONITORENTER); 1331 } 1332 1333 /** Generates the instruction to release the monitor of the top stack value. */ 1334 public void monitorExit() { 1335 mv.visitInsn(Opcodes.MONITOREXIT); 1336 } 1337 1338 // ----------------------------------------------------------------------------------------------- 1339 // Non instructions 1340 // ----------------------------------------------------------------------------------------------- 1341 1342 /** Marks the end of the visited method. */ 1343 public void endMethod() { 1344 if ((access & Opcodes.ACC_ABSTRACT) == 0) { 1345 mv.visitMaxs(0, 0); 1346 } 1347 mv.visitEnd(); 1348 } 1349 1350 /** 1351 * Marks the start of an exception handler. 1352 * 1353 * @param start beginning of the exception handler's scope (inclusive). 1354 * @param end end of the exception handler's scope (exclusive). 1355 * @param exception internal name of the type of exceptions handled by the handler (see {@link 1356 * Type#getInternalName()}). 1357 */ 1358 public void catchException(final Label start, final Label end, final Type exception) { 1359 Label catchLabel = new Label(); 1360 if (exception == null) { 1361 mv.visitTryCatchBlock(start, end, catchLabel, null); 1362 } else { 1363 mv.visitTryCatchBlock(start, end, catchLabel, exception.getInternalName()); 1364 } 1365 mark(catchLabel); 1366 } 1367}