1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.commons; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import org.objectweb.asm.ClassVisitor; 34 import org.objectweb.asm.ConstantDynamic; 35 import org.objectweb.asm.Handle; 36 import org.objectweb.asm.Label; 37 import org.objectweb.asm.MethodVisitor; 38 import org.objectweb.asm.Opcodes; 39 import org.objectweb.asm.Type; 40 41 /** 42 * A {@link MethodVisitor} with convenient methods to generate code. For example, using this 43 * adapter, the class below 44 * 45 * <pre> 46 * public class Example { 47 * public static void main(String[] args) { 48 * System.out.println("Hello world!"); 49 * } 50 * } 51 * </pre> 52 * 53 * <p>can be generated as follows: 54 * 55 * <pre> 56 * ClassWriter cw = new ClassWriter(0); 57 * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); 58 * 59 * Method m = Method.getMethod("void <init> ()"); 60 * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); 61 * mg.loadThis(); 62 * mg.invokeConstructor(Type.getType(Object.class), m); 63 * mg.returnValue(); 64 * mg.endMethod(); 65 * 66 * m = Method.getMethod("void main (String[])"); 67 * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); 68 * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); 69 * mg.push("Hello world!"); 70 * mg.invokeVirtual(Type.getType(PrintStream.class), 71 * Method.getMethod("void println (String)")); 72 * mg.returnValue(); 73 * mg.endMethod(); 74 * 75 * cw.visitEnd(); 76 * </pre> 77 * 78 * @author Juozas Baliuka 79 * @author Chris Nokleberg 80 * @author Eric Bruneton 81 * @author Prashant Deva 82 */ 83 public class GeneratorAdapter extends LocalVariablesSorter { 84 85 private static final String CLASS_DESCRIPTOR = "Ljava/lang/Class;"; 86 87 private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); 88 89 private static final Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); 90 91 private static final Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); 92 93 private static final Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); 94 95 private static final Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); 96 97 private static final Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); 98 99 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 */ GeneratorAdapter( final MethodVisitor methodVisitor, final int access, final String name, final String descriptor)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 */ GeneratorAdapter( final int api, final MethodVisitor methodVisitor, final int access, final String name, final String descriptor)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 */ GeneratorAdapter( final int access, final Method method, final MethodVisitor methodVisitor)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 */ GeneratorAdapter( final int access, final Method method, final String signature, final Type[] exceptions, final ClassVisitor classVisitor)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 */ getInternalNames(final Type[] types)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 getAccess()289 public int getAccess() { 290 return access; 291 } 292 getName()293 public String getName() { 294 return name; 295 } 296 getReturnType()297 public Type getReturnType() { 298 return returnType; 299 } 300 getArgumentTypes()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 */ push(final boolean value)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 */ push(final int value)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 */ push(final long value)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 */ push(final float value)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 */ push(final double value)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 */ push(final String value)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 */ push(final Type value)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.BOOLEAN: 400 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", CLASS_DESCRIPTOR); 401 break; 402 case Type.CHAR: 403 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", CLASS_DESCRIPTOR); 404 break; 405 case Type.BYTE: 406 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", CLASS_DESCRIPTOR); 407 break; 408 case Type.SHORT: 409 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", CLASS_DESCRIPTOR); 410 break; 411 case Type.INT: 412 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", CLASS_DESCRIPTOR); 413 break; 414 case Type.FLOAT: 415 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", CLASS_DESCRIPTOR); 416 break; 417 case Type.LONG: 418 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", CLASS_DESCRIPTOR); 419 break; 420 case Type.DOUBLE: 421 mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", CLASS_DESCRIPTOR); 422 break; 423 default: 424 mv.visitLdcInsn(value); 425 break; 426 } 427 } 428 } 429 430 /** 431 * Generates the instruction to push a handle on the stack. 432 * 433 * @param handle the handle to be pushed on the stack. 434 */ push(final Handle handle)435 public void push(final Handle handle) { 436 if (handle == null) { 437 mv.visitInsn(Opcodes.ACONST_NULL); 438 } else { 439 mv.visitLdcInsn(handle); 440 } 441 } 442 443 /** 444 * Generates the instruction to push a constant dynamic on the stack. 445 * 446 * @param constantDynamic the constant dynamic to be pushed on the stack. 447 */ push(final ConstantDynamic constantDynamic)448 public void push(final ConstantDynamic constantDynamic) { 449 if (constantDynamic == null) { 450 mv.visitInsn(Opcodes.ACONST_NULL); 451 } else { 452 mv.visitLdcInsn(constantDynamic); 453 } 454 } 455 456 // ----------------------------------------------------------------------------------------------- 457 // Instructions to load and store method arguments 458 // ----------------------------------------------------------------------------------------------- 459 460 /** 461 * Returns the index of the given method argument in the frame's local variables array. 462 * 463 * @param arg the index of a method argument. 464 * @return the index of the given method argument in the frame's local variables array. 465 */ getArgIndex(final int arg)466 private int getArgIndex(final int arg) { 467 int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; 468 for (int i = 0; i < arg; i++) { 469 index += argumentTypes[i].getSize(); 470 } 471 return index; 472 } 473 474 /** 475 * Generates the instruction to push a local variable on the stack. 476 * 477 * @param type the type of the local variable to be loaded. 478 * @param index an index in the frame's local variables array. 479 */ loadInsn(final Type type, final int index)480 private void loadInsn(final Type type, final int index) { 481 mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); 482 } 483 484 /** 485 * Generates the instruction to store the top stack value in a local variable. 486 * 487 * @param type the type of the local variable to be stored. 488 * @param index an index in the frame's local variables array. 489 */ storeInsn(final Type type, final int index)490 private void storeInsn(final Type type, final int index) { 491 mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); 492 } 493 494 /** Generates the instruction to load 'this' on the stack. */ loadThis()495 public void loadThis() { 496 if ((access & Opcodes.ACC_STATIC) != 0) { 497 throw new IllegalStateException("no 'this' pointer within static method"); 498 } 499 mv.visitVarInsn(Opcodes.ALOAD, 0); 500 } 501 502 /** 503 * Generates the instruction to load the given method argument on the stack. 504 * 505 * @param arg the index of a method argument. 506 */ loadArg(final int arg)507 public void loadArg(final int arg) { 508 loadInsn(argumentTypes[arg], getArgIndex(arg)); 509 } 510 511 /** 512 * Generates the instructions to load the given method arguments on the stack. 513 * 514 * @param arg the index of the first method argument to be loaded. 515 * @param count the number of method arguments to be loaded. 516 */ loadArgs(final int arg, final int count)517 public void loadArgs(final int arg, final int count) { 518 int index = getArgIndex(arg); 519 for (int i = 0; i < count; ++i) { 520 Type argumentType = argumentTypes[arg + i]; 521 loadInsn(argumentType, index); 522 index += argumentType.getSize(); 523 } 524 } 525 526 /** Generates the instructions to load all the method arguments on the stack. */ loadArgs()527 public void loadArgs() { 528 loadArgs(0, argumentTypes.length); 529 } 530 531 /** 532 * Generates the instructions to load all the method arguments on the stack, as a single object 533 * array. 534 */ loadArgArray()535 public void loadArgArray() { 536 push(argumentTypes.length); 537 newArray(OBJECT_TYPE); 538 for (int i = 0; i < argumentTypes.length; i++) { 539 dup(); 540 push(i); 541 loadArg(i); 542 box(argumentTypes[i]); 543 arrayStore(OBJECT_TYPE); 544 } 545 } 546 547 /** 548 * Generates the instruction to store the top stack value in the given method argument. 549 * 550 * @param arg the index of a method argument. 551 */ storeArg(final int arg)552 public void storeArg(final int arg) { 553 storeInsn(argumentTypes[arg], getArgIndex(arg)); 554 } 555 556 // ----------------------------------------------------------------------------------------------- 557 // Instructions to load and store local variables 558 // ----------------------------------------------------------------------------------------------- 559 560 /** 561 * Returns the type of the given local variable. 562 * 563 * @param local a local variable identifier, as returned by {@link 564 * LocalVariablesSorter#newLocal(Type)}. 565 * @return the type of the given local variable. 566 */ getLocalType(final int local)567 public Type getLocalType(final int local) { 568 return localTypes.get(local - firstLocal); 569 } 570 571 @Override setLocalType(final int local, final Type type)572 protected void setLocalType(final int local, final Type type) { 573 int index = local - firstLocal; 574 while (localTypes.size() < index + 1) { 575 localTypes.add(null); 576 } 577 localTypes.set(index, type); 578 } 579 580 /** 581 * Generates the instruction to load the given local variable on the stack. 582 * 583 * @param local a local variable identifier, as returned by {@link 584 * LocalVariablesSorter#newLocal(Type)}. 585 */ loadLocal(final int local)586 public void loadLocal(final int local) { 587 loadInsn(getLocalType(local), local); 588 } 589 590 /** 591 * Generates the instruction to load the given local variable on the stack. 592 * 593 * @param local a local variable identifier, as returned by {@link 594 * LocalVariablesSorter#newLocal(Type)}. 595 * @param type the type of this local variable. 596 */ loadLocal(final int local, final Type type)597 public void loadLocal(final int local, final Type type) { 598 setLocalType(local, type); 599 loadInsn(type, local); 600 } 601 602 /** 603 * Generates the instruction to store the top stack value in the given local variable. 604 * 605 * @param local a local variable identifier, as returned by {@link 606 * LocalVariablesSorter#newLocal(Type)}. 607 */ storeLocal(final int local)608 public void storeLocal(final int local) { 609 storeInsn(getLocalType(local), local); 610 } 611 612 /** 613 * Generates the instruction to store the top stack value in the given local variable. 614 * 615 * @param local a local variable identifier, as returned by {@link 616 * LocalVariablesSorter#newLocal(Type)}. 617 * @param type the type of this local variable. 618 */ storeLocal(final int local, final Type type)619 public void storeLocal(final int local, final Type type) { 620 setLocalType(local, type); 621 storeInsn(type, local); 622 } 623 624 /** 625 * Generates the instruction to load an element from an array. 626 * 627 * @param type the type of the array element to be loaded. 628 */ arrayLoad(final Type type)629 public void arrayLoad(final Type type) { 630 mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); 631 } 632 633 /** 634 * Generates the instruction to store an element in an array. 635 * 636 * @param type the type of the array element to be stored. 637 */ arrayStore(final Type type)638 public void arrayStore(final Type type) { 639 mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); 640 } 641 642 // ----------------------------------------------------------------------------------------------- 643 // Instructions to manage the stack 644 // ----------------------------------------------------------------------------------------------- 645 646 /** Generates a POP instruction. */ pop()647 public void pop() { 648 mv.visitInsn(Opcodes.POP); 649 } 650 651 /** Generates a POP2 instruction. */ pop2()652 public void pop2() { 653 mv.visitInsn(Opcodes.POP2); 654 } 655 656 /** Generates a DUP instruction. */ dup()657 public void dup() { 658 mv.visitInsn(Opcodes.DUP); 659 } 660 661 /** Generates a DUP2 instruction. */ dup2()662 public void dup2() { 663 mv.visitInsn(Opcodes.DUP2); 664 } 665 666 /** Generates a DUP_X1 instruction. */ dupX1()667 public void dupX1() { 668 mv.visitInsn(Opcodes.DUP_X1); 669 } 670 671 /** Generates a DUP_X2 instruction. */ dupX2()672 public void dupX2() { 673 mv.visitInsn(Opcodes.DUP_X2); 674 } 675 676 /** Generates a DUP2_X1 instruction. */ dup2X1()677 public void dup2X1() { 678 mv.visitInsn(Opcodes.DUP2_X1); 679 } 680 681 /** Generates a DUP2_X2 instruction. */ dup2X2()682 public void dup2X2() { 683 mv.visitInsn(Opcodes.DUP2_X2); 684 } 685 686 /** Generates a SWAP instruction. */ swap()687 public void swap() { 688 mv.visitInsn(Opcodes.SWAP); 689 } 690 691 /** 692 * Generates the instructions to swap the top two stack values. 693 * 694 * @param prev type of the top - 1 stack value. 695 * @param type type of the top stack value. 696 */ swap(final Type prev, final Type type)697 public void swap(final Type prev, final Type type) { 698 if (type.getSize() == 1) { 699 if (prev.getSize() == 1) { 700 swap(); // Same as dupX1 pop. 701 } else { 702 dupX2(); 703 pop(); 704 } 705 } else { 706 if (prev.getSize() == 1) { 707 dup2X1(); 708 pop2(); 709 } else { 710 dup2X2(); 711 pop2(); 712 } 713 } 714 } 715 716 // ----------------------------------------------------------------------------------------------- 717 // Instructions to do mathematical and logical operations 718 // ----------------------------------------------------------------------------------------------- 719 720 /** 721 * Generates the instruction to do the specified mathematical or logical operation. 722 * 723 * @param op a mathematical or logical operation. Must be one of ADD, SUB, MUL, DIV, REM, NEG, 724 * SHL, SHR, USHR, AND, OR, XOR. 725 * @param type the type of the operand(s) for this operation. 726 */ math(final int op, final Type type)727 public void math(final int op, final Type type) { 728 mv.visitInsn(type.getOpcode(op)); 729 } 730 731 /** Generates the instructions to compute the bitwise negation of the top stack value. */ not()732 public void not() { 733 mv.visitInsn(Opcodes.ICONST_1); 734 mv.visitInsn(Opcodes.IXOR); 735 } 736 737 /** 738 * Generates the instruction to increment the given local variable. 739 * 740 * @param local the local variable to be incremented. 741 * @param amount the amount by which the local variable must be incremented. 742 */ iinc(final int local, final int amount)743 public void iinc(final int local, final int amount) { 744 mv.visitIincInsn(local, amount); 745 } 746 747 /** 748 * Generates the instructions to cast a numerical value from one type to another. 749 * 750 * @param from the type of the top stack value 751 * @param to the type into which this value must be cast. 752 */ cast(final Type from, final Type to)753 public void cast(final Type from, final Type to) { 754 if (from != to) { 755 if (from.getSort() < Type.BOOLEAN 756 || from.getSort() > Type.DOUBLE 757 || to.getSort() < Type.BOOLEAN 758 || to.getSort() > Type.DOUBLE) { 759 throw new IllegalArgumentException("Cannot cast from " + from + " to " + to); 760 } 761 InstructionAdapter.cast(mv, from, to); 762 } 763 } 764 765 // ----------------------------------------------------------------------------------------------- 766 // Instructions to do boxing and unboxing operations 767 // ----------------------------------------------------------------------------------------------- 768 getBoxedType(final Type type)769 private static Type getBoxedType(final Type type) { 770 switch (type.getSort()) { 771 case Type.BYTE: 772 return BYTE_TYPE; 773 case Type.BOOLEAN: 774 return BOOLEAN_TYPE; 775 case Type.SHORT: 776 return SHORT_TYPE; 777 case Type.CHAR: 778 return CHARACTER_TYPE; 779 case Type.INT: 780 return INTEGER_TYPE; 781 case Type.FLOAT: 782 return FLOAT_TYPE; 783 case Type.LONG: 784 return LONG_TYPE; 785 case Type.DOUBLE: 786 return DOUBLE_TYPE; 787 default: 788 return type; 789 } 790 } 791 792 /** 793 * Generates the instructions to box the top stack value. This value is replaced by its boxed 794 * equivalent on top of the stack. 795 * 796 * @param type the type of the top stack value. 797 */ box(final Type type)798 public void box(final Type type) { 799 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 800 return; 801 } 802 if (type == Type.VOID_TYPE) { 803 push((String) null); 804 } else { 805 Type boxedType = getBoxedType(type); 806 newInstance(boxedType); 807 if (type.getSize() == 2) { 808 // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o 809 dupX2(); 810 dupX2(); 811 pop(); 812 } else { 813 // p -> po -> opo -> oop -> o 814 dupX1(); 815 swap(); 816 } 817 invokeConstructor(boxedType, new Method("<init>", Type.VOID_TYPE, new Type[] {type})); 818 } 819 } 820 821 /** 822 * Generates the instructions to box the top stack value using Java 5's valueOf() method. This 823 * value is replaced by its boxed equivalent on top of the stack. 824 * 825 * @param type the type of the top stack value. 826 */ valueOf(final Type type)827 public void valueOf(final Type type) { 828 if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 829 return; 830 } 831 if (type == Type.VOID_TYPE) { 832 push((String) null); 833 } else { 834 Type boxedType = getBoxedType(type); 835 invokeStatic(boxedType, new Method("valueOf", boxedType, new Type[] {type})); 836 } 837 } 838 839 /** 840 * Generates the instructions to unbox the top stack value. This value is replaced by its unboxed 841 * equivalent on top of the stack. 842 * 843 * @param type the type of the top stack value. 844 */ unbox(final Type type)845 public void unbox(final Type type) { 846 Type boxedType = NUMBER_TYPE; 847 Method unboxMethod; 848 switch (type.getSort()) { 849 case Type.VOID: 850 return; 851 case Type.CHAR: 852 boxedType = CHARACTER_TYPE; 853 unboxMethod = CHAR_VALUE; 854 break; 855 case Type.BOOLEAN: 856 boxedType = BOOLEAN_TYPE; 857 unboxMethod = BOOLEAN_VALUE; 858 break; 859 case Type.DOUBLE: 860 unboxMethod = DOUBLE_VALUE; 861 break; 862 case Type.FLOAT: 863 unboxMethod = FLOAT_VALUE; 864 break; 865 case Type.LONG: 866 unboxMethod = LONG_VALUE; 867 break; 868 case Type.INT: 869 case Type.SHORT: 870 case Type.BYTE: 871 unboxMethod = INT_VALUE; 872 break; 873 default: 874 unboxMethod = null; 875 break; 876 } 877 if (unboxMethod == null) { 878 checkCast(type); 879 } else { 880 checkCast(boxedType); 881 invokeVirtual(boxedType, unboxMethod); 882 } 883 } 884 885 // ----------------------------------------------------------------------------------------------- 886 // Instructions to jump to other instructions 887 // ----------------------------------------------------------------------------------------------- 888 889 /** 890 * Constructs a new {@link Label}. 891 * 892 * @return a new {@link Label}. 893 */ newLabel()894 public Label newLabel() { 895 return new Label(); 896 } 897 898 /** 899 * Marks the current code position with the given label. 900 * 901 * @param label a label. 902 */ mark(final Label label)903 public void mark(final Label label) { 904 mv.visitLabel(label); 905 } 906 907 /** 908 * Marks the current code position with a new label. 909 * 910 * @return the label that was created to mark the current code position. 911 */ mark()912 public Label mark() { 913 Label label = new Label(); 914 mv.visitLabel(label); 915 return label; 916 } 917 918 /** 919 * Generates the instructions to jump to a label based on the comparison of the top two stack 920 * values. 921 * 922 * @param type the type of the top two stack values. 923 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 924 * @param label where to jump if the comparison result is {@literal true}. 925 */ ifCmp(final Type type, final int mode, final Label label)926 public void ifCmp(final Type type, final int mode, final Label label) { 927 switch (type.getSort()) { 928 case Type.LONG: 929 mv.visitInsn(Opcodes.LCMP); 930 break; 931 case Type.DOUBLE: 932 mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL : Opcodes.DCMPG); 933 break; 934 case Type.FLOAT: 935 mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL : Opcodes.FCMPG); 936 break; 937 case Type.ARRAY: 938 case Type.OBJECT: 939 if (mode == EQ) { 940 mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); 941 return; 942 } else if (mode == NE) { 943 mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); 944 return; 945 } else { 946 throw new IllegalArgumentException("Bad comparison for type " + type); 947 } 948 default: 949 int intOp = -1; 950 switch (mode) { 951 case EQ: 952 intOp = Opcodes.IF_ICMPEQ; 953 break; 954 case NE: 955 intOp = Opcodes.IF_ICMPNE; 956 break; 957 case GE: 958 intOp = Opcodes.IF_ICMPGE; 959 break; 960 case LT: 961 intOp = Opcodes.IF_ICMPLT; 962 break; 963 case LE: 964 intOp = Opcodes.IF_ICMPLE; 965 break; 966 case GT: 967 intOp = Opcodes.IF_ICMPGT; 968 break; 969 default: 970 throw new IllegalArgumentException("Bad comparison mode " + mode); 971 } 972 mv.visitJumpInsn(intOp, label); 973 return; 974 } 975 mv.visitJumpInsn(mode, label); 976 } 977 978 /** 979 * Generates the instructions to jump to a label based on the comparison of the top two integer 980 * stack values. 981 * 982 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 983 * @param label where to jump if the comparison result is {@literal true}. 984 */ ifICmp(final int mode, final Label label)985 public void ifICmp(final int mode, final Label label) { 986 ifCmp(Type.INT_TYPE, mode, label); 987 } 988 989 /** 990 * Generates the instructions to jump to a label based on the comparison of the top integer stack 991 * value with zero. 992 * 993 * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, LE. 994 * @param label where to jump if the comparison result is {@literal true}. 995 */ ifZCmp(final int mode, final Label label)996 public void ifZCmp(final int mode, final Label label) { 997 mv.visitJumpInsn(mode, label); 998 } 999 1000 /** 1001 * Generates the instruction to jump to the given label if the top stack value is null. 1002 * 1003 * @param label where to jump if the condition is {@literal true}. 1004 */ ifNull(final Label label)1005 public void ifNull(final Label label) { 1006 mv.visitJumpInsn(Opcodes.IFNULL, label); 1007 } 1008 1009 /** 1010 * Generates the instruction to jump to the given label if the top stack value is not null. 1011 * 1012 * @param label where to jump if the condition is {@literal true}. 1013 */ ifNonNull(final Label label)1014 public void ifNonNull(final Label label) { 1015 mv.visitJumpInsn(Opcodes.IFNONNULL, label); 1016 } 1017 1018 /** 1019 * Generates the instruction to jump to the given label. 1020 * 1021 * @param label where to jump if the condition is {@literal true}. 1022 */ goTo(final Label label)1023 public void goTo(final Label label) { 1024 mv.visitJumpInsn(Opcodes.GOTO, label); 1025 } 1026 1027 /** 1028 * Generates a RET instruction. 1029 * 1030 * @param local a local variable identifier, as returned by {@link 1031 * LocalVariablesSorter#newLocal(Type)}. 1032 */ ret(final int local)1033 public void ret(final int local) { 1034 mv.visitVarInsn(Opcodes.RET, local); 1035 } 1036 1037 /** 1038 * Generates the instructions for a switch statement. 1039 * 1040 * @param keys the switch case keys. 1041 * @param generator a generator to generate the code for the switch cases. 1042 */ tableSwitch(final int[] keys, final TableSwitchGenerator generator)1043 public void tableSwitch(final int[] keys, final TableSwitchGenerator generator) { 1044 float density; 1045 if (keys.length == 0) { 1046 density = 0; 1047 } else { 1048 density = (float) keys.length / (keys[keys.length - 1] - keys[0] + 1); 1049 } 1050 tableSwitch(keys, generator, density >= 0.5f); 1051 } 1052 1053 /** 1054 * Generates the instructions for a switch statement. 1055 * 1056 * @param keys the switch case keys. 1057 * @param generator a generator to generate the code for the switch cases. 1058 * @param useTable {@literal true} to use a TABLESWITCH instruction, or {@literal false} to use a 1059 * LOOKUPSWITCH instruction. 1060 */ tableSwitch( final int[] keys, final TableSwitchGenerator generator, final boolean useTable)1061 public void tableSwitch( 1062 final int[] keys, final TableSwitchGenerator generator, final boolean useTable) { 1063 for (int i = 1; i < keys.length; ++i) { 1064 if (keys[i] < keys[i - 1]) { 1065 throw new IllegalArgumentException("keys must be sorted in ascending order"); 1066 } 1067 } 1068 Label defaultLabel = newLabel(); 1069 Label endLabel = newLabel(); 1070 if (keys.length > 0) { 1071 int numKeys = keys.length; 1072 if (useTable) { 1073 int min = keys[0]; 1074 int max = keys[numKeys - 1]; 1075 int range = max - min + 1; 1076 Label[] labels = new Label[range]; 1077 Arrays.fill(labels, defaultLabel); 1078 for (int i = 0; i < numKeys; ++i) { 1079 labels[keys[i] - min] = newLabel(); 1080 } 1081 mv.visitTableSwitchInsn(min, max, defaultLabel, labels); 1082 for (int i = 0; i < range; ++i) { 1083 Label label = labels[i]; 1084 if (label != defaultLabel) { 1085 mark(label); 1086 generator.generateCase(i + min, endLabel); 1087 } 1088 } 1089 } else { 1090 Label[] labels = new Label[numKeys]; 1091 for (int i = 0; i < numKeys; ++i) { 1092 labels[i] = newLabel(); 1093 } 1094 mv.visitLookupSwitchInsn(defaultLabel, keys, labels); 1095 for (int i = 0; i < numKeys; ++i) { 1096 mark(labels[i]); 1097 generator.generateCase(keys[i], endLabel); 1098 } 1099 } 1100 } 1101 mark(defaultLabel); 1102 generator.generateDefault(); 1103 mark(endLabel); 1104 } 1105 1106 /** Generates the instruction to return the top stack value to the caller. */ returnValue()1107 public void returnValue() { 1108 mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); 1109 } 1110 1111 // ----------------------------------------------------------------------------------------------- 1112 // Instructions to load and store fields 1113 // ----------------------------------------------------------------------------------------------- 1114 1115 /** 1116 * Generates a get field or set field instruction. 1117 * 1118 * @param opcode the instruction's opcode. 1119 * @param ownerType the class in which the field is defined. 1120 * @param name the name of the field. 1121 * @param fieldType the type of the field. 1122 */ fieldInsn( final int opcode, final Type ownerType, final String name, final Type fieldType)1123 private void fieldInsn( 1124 final int opcode, final Type ownerType, final String name, final Type fieldType) { 1125 mv.visitFieldInsn(opcode, ownerType.getInternalName(), name, fieldType.getDescriptor()); 1126 } 1127 1128 /** 1129 * Generates the instruction to push the value of a static field on the stack. 1130 * 1131 * @param owner the class in which the field is defined. 1132 * @param name the name of the field. 1133 * @param type the type of the field. 1134 */ getStatic(final Type owner, final String name, final Type type)1135 public void getStatic(final Type owner, final String name, final Type type) { 1136 fieldInsn(Opcodes.GETSTATIC, owner, name, type); 1137 } 1138 1139 /** 1140 * Generates the instruction to store the top stack value in a static field. 1141 * 1142 * @param owner the class in which the field is defined. 1143 * @param name the name of the field. 1144 * @param type the type of the field. 1145 */ putStatic(final Type owner, final String name, final Type type)1146 public void putStatic(final Type owner, final String name, final Type type) { 1147 fieldInsn(Opcodes.PUTSTATIC, owner, name, type); 1148 } 1149 1150 /** 1151 * Generates the instruction to push the value of a non static field on the stack. 1152 * 1153 * @param owner the class in which the field is defined. 1154 * @param name the name of the field. 1155 * @param type the type of the field. 1156 */ getField(final Type owner, final String name, final Type type)1157 public void getField(final Type owner, final String name, final Type type) { 1158 fieldInsn(Opcodes.GETFIELD, owner, name, type); 1159 } 1160 1161 /** 1162 * Generates the instruction to store the top stack value in a non static field. 1163 * 1164 * @param owner the class in which the field is defined. 1165 * @param name the name of the field. 1166 * @param type the type of the field. 1167 */ putField(final Type owner, final String name, final Type type)1168 public void putField(final Type owner, final String name, final Type type) { 1169 fieldInsn(Opcodes.PUTFIELD, owner, name, type); 1170 } 1171 1172 // ----------------------------------------------------------------------------------------------- 1173 // Instructions to invoke methods 1174 // ----------------------------------------------------------------------------------------------- 1175 1176 /** 1177 * Generates an invoke method instruction. 1178 * 1179 * @param opcode the instruction's opcode. 1180 * @param type the class in which the method is defined. 1181 * @param method the method to be invoked. 1182 * @param isInterface whether the 'type' class is an interface or not. 1183 */ invokeInsn( final int opcode, final Type type, final Method method, final boolean isInterface)1184 private void invokeInsn( 1185 final int opcode, final Type type, final Method method, final boolean isInterface) { 1186 String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName(); 1187 mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor(), isInterface); 1188 } 1189 1190 /** 1191 * Generates the instruction to invoke a normal method. 1192 * 1193 * @param owner the class in which the method is defined. 1194 * @param method the method to be invoked. 1195 */ invokeVirtual(final Type owner, final Method method)1196 public void invokeVirtual(final Type owner, final Method method) { 1197 invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method, false); 1198 } 1199 1200 /** 1201 * Generates the instruction to invoke a constructor. 1202 * 1203 * @param type the class in which the constructor is defined. 1204 * @param method the constructor to be invoked. 1205 */ invokeConstructor(final Type type, final Method method)1206 public void invokeConstructor(final Type type, final Method method) { 1207 invokeInsn(Opcodes.INVOKESPECIAL, type, method, false); 1208 } 1209 1210 /** 1211 * Generates the instruction to invoke a static method. 1212 * 1213 * @param owner the class in which the method is defined. 1214 * @param method the method to be invoked. 1215 */ invokeStatic(final Type owner, final Method method)1216 public void invokeStatic(final Type owner, final Method method) { 1217 invokeInsn(Opcodes.INVOKESTATIC, owner, method, false); 1218 } 1219 1220 /** 1221 * Generates the instruction to invoke an interface method. 1222 * 1223 * @param owner the class in which the method is defined. 1224 * @param method the method to be invoked. 1225 */ invokeInterface(final Type owner, final Method method)1226 public void invokeInterface(final Type owner, final Method method) { 1227 invokeInsn(Opcodes.INVOKEINTERFACE, owner, method, true); 1228 } 1229 1230 /** 1231 * Generates an invokedynamic instruction. 1232 * 1233 * @param name the method's name. 1234 * @param descriptor the method's descriptor (see {@link Type}). 1235 * @param bootstrapMethodHandle the bootstrap method. 1236 * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be 1237 * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link 1238 * Type} or {@link Handle} value. This method is allowed to modify the content of the array so 1239 * a caller should expect that this array may change. 1240 */ invokeDynamic( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments)1241 public void invokeDynamic( 1242 final String name, 1243 final String descriptor, 1244 final Handle bootstrapMethodHandle, 1245 final Object... bootstrapMethodArguments) { 1246 mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 1247 } 1248 1249 // ----------------------------------------------------------------------------------------------- 1250 // Instructions to create objects and arrays 1251 // ----------------------------------------------------------------------------------------------- 1252 1253 /** 1254 * Generates a type dependent instruction. 1255 * 1256 * @param opcode the instruction's opcode. 1257 * @param type the instruction's operand. 1258 */ typeInsn(final int opcode, final Type type)1259 private void typeInsn(final int opcode, final Type type) { 1260 mv.visitTypeInsn(opcode, type.getInternalName()); 1261 } 1262 1263 /** 1264 * Generates the instruction to create a new object. 1265 * 1266 * @param type the class of the object to be created. 1267 */ newInstance(final Type type)1268 public void newInstance(final Type type) { 1269 typeInsn(Opcodes.NEW, type); 1270 } 1271 1272 /** 1273 * Generates the instruction to create a new array. 1274 * 1275 * @param type the type of the array elements. 1276 */ newArray(final Type type)1277 public void newArray(final Type type) { 1278 InstructionAdapter.newarray(mv, type); 1279 } 1280 1281 // ----------------------------------------------------------------------------------------------- 1282 // Miscellaneous instructions 1283 // ----------------------------------------------------------------------------------------------- 1284 1285 /** Generates the instruction to compute the length of an array. */ arrayLength()1286 public void arrayLength() { 1287 mv.visitInsn(Opcodes.ARRAYLENGTH); 1288 } 1289 1290 /** Generates the instruction to throw an exception. */ throwException()1291 public void throwException() { 1292 mv.visitInsn(Opcodes.ATHROW); 1293 } 1294 1295 /** 1296 * Generates the instructions to create and throw an exception. The exception class must have a 1297 * constructor with a single String argument. 1298 * 1299 * @param type the class of the exception to be thrown. 1300 * @param message the detailed message of the exception. 1301 */ throwException(final Type type, final String message)1302 public void throwException(final Type type, final String message) { 1303 newInstance(type); 1304 dup(); 1305 push(message); 1306 invokeConstructor(type, Method.getMethod("void <init> (String)")); 1307 throwException(); 1308 } 1309 1310 /** 1311 * Generates the instruction to check that the top stack value is of the given type. 1312 * 1313 * @param type a class or interface type. 1314 */ checkCast(final Type type)1315 public void checkCast(final Type type) { 1316 if (!type.equals(OBJECT_TYPE)) { 1317 typeInsn(Opcodes.CHECKCAST, type); 1318 } 1319 } 1320 1321 /** 1322 * Generates the instruction to test if the top stack value is of the given type. 1323 * 1324 * @param type a class or interface type. 1325 */ instanceOf(final Type type)1326 public void instanceOf(final Type type) { 1327 typeInsn(Opcodes.INSTANCEOF, type); 1328 } 1329 1330 /** Generates the instruction to get the monitor of the top stack value. */ monitorEnter()1331 public void monitorEnter() { 1332 mv.visitInsn(Opcodes.MONITORENTER); 1333 } 1334 1335 /** Generates the instruction to release the monitor of the top stack value. */ monitorExit()1336 public void monitorExit() { 1337 mv.visitInsn(Opcodes.MONITOREXIT); 1338 } 1339 1340 // ----------------------------------------------------------------------------------------------- 1341 // Non instructions 1342 // ----------------------------------------------------------------------------------------------- 1343 1344 /** Marks the end of the visited method. */ endMethod()1345 public void endMethod() { 1346 if ((access & Opcodes.ACC_ABSTRACT) == 0) { 1347 mv.visitMaxs(0, 0); 1348 } 1349 mv.visitEnd(); 1350 } 1351 1352 /** 1353 * Marks the start of an exception handler. 1354 * 1355 * @param start beginning of the exception handler's scope (inclusive). 1356 * @param end end of the exception handler's scope (exclusive). 1357 * @param exception internal name of the type of exceptions handled by the handler (see {@link 1358 * Type#getInternalName()}). 1359 */ catchException(final Label start, final Label end, final Type exception)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 } 1370