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