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