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