1 /*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2007 INRIA, France Telecom 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 * THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package org.mockito.asm.util; 31 32 import org.mockito.asm.AnnotationVisitor; 33 import org.mockito.asm.Attribute; 34 import org.mockito.asm.Label; 35 import org.mockito.asm.MethodAdapter; 36 import org.mockito.asm.MethodVisitor; 37 import org.mockito.asm.Opcodes; 38 import org.mockito.asm.Type; 39 40 import java.util.HashMap; 41 import java.util.Map; 42 43 /** 44 * A {@link MethodAdapter} that checks that its methods are properly used. More 45 * precisely this code adapter checks each instruction individually (i.e., each 46 * visit method checks some preconditions based <i>only</i> on its arguments - 47 * such as the fact that the given opcode is correct for a given visit method), 48 * but does <i>not</i> check the <i>sequence</i> of instructions. For example, 49 * in a method whose signature is <tt>void m ()</tt>, the invalid instruction 50 * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by 51 * this code adapter. 52 * 53 * @author Eric Bruneton 54 */ 55 public class CheckMethodAdapter extends MethodAdapter { 56 57 /** 58 * <tt>true</tt> if the visitCode method has been called. 59 */ 60 private boolean startCode; 61 62 /** 63 * <tt>true</tt> if the visitMaxs method has been called. 64 */ 65 private boolean endCode; 66 67 /** 68 * <tt>true</tt> if the visitEnd method has been called. 69 */ 70 private boolean endMethod; 71 72 /** 73 * The already visited labels. This map associate Integer values to Label 74 * keys. 75 */ 76 private final Map labels; 77 78 /** 79 * Code of the visit method to be used for each opcode. 80 */ 81 private static final int[] TYPE; 82 83 static { 84 String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD" 85 + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 86 + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD" 87 + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA"; 88 TYPE = new int[s.length()]; 89 for (int i = 0; i < TYPE.length; ++i) { 90 TYPE[i] = s.charAt(i) - 'A' - 1; 91 } 92 } 93 94 // code to generate the above string 95 // public static void main (String[] args) { 96 // int[] TYPE = new int[] { 97 // 0, //NOP 98 // 0, //ACONST_NULL 99 // 0, //ICONST_M1 100 // 0, //ICONST_0 101 // 0, //ICONST_1 102 // 0, //ICONST_2 103 // 0, //ICONST_3 104 // 0, //ICONST_4 105 // 0, //ICONST_5 106 // 0, //LCONST_0 107 // 0, //LCONST_1 108 // 0, //FCONST_0 109 // 0, //FCONST_1 110 // 0, //FCONST_2 111 // 0, //DCONST_0 112 // 0, //DCONST_1 113 // 1, //BIPUSH 114 // 1, //SIPUSH 115 // 7, //LDC 116 // -1, //LDC_W 117 // -1, //LDC2_W 118 // 2, //ILOAD 119 // 2, //LLOAD 120 // 2, //FLOAD 121 // 2, //DLOAD 122 // 2, //ALOAD 123 // -1, //ILOAD_0 124 // -1, //ILOAD_1 125 // -1, //ILOAD_2 126 // -1, //ILOAD_3 127 // -1, //LLOAD_0 128 // -1, //LLOAD_1 129 // -1, //LLOAD_2 130 // -1, //LLOAD_3 131 // -1, //FLOAD_0 132 // -1, //FLOAD_1 133 // -1, //FLOAD_2 134 // -1, //FLOAD_3 135 // -1, //DLOAD_0 136 // -1, //DLOAD_1 137 // -1, //DLOAD_2 138 // -1, //DLOAD_3 139 // -1, //ALOAD_0 140 // -1, //ALOAD_1 141 // -1, //ALOAD_2 142 // -1, //ALOAD_3 143 // 0, //IALOAD 144 // 0, //LALOAD 145 // 0, //FALOAD 146 // 0, //DALOAD 147 // 0, //AALOAD 148 // 0, //BALOAD 149 // 0, //CALOAD 150 // 0, //SALOAD 151 // 2, //ISTORE 152 // 2, //LSTORE 153 // 2, //FSTORE 154 // 2, //DSTORE 155 // 2, //ASTORE 156 // -1, //ISTORE_0 157 // -1, //ISTORE_1 158 // -1, //ISTORE_2 159 // -1, //ISTORE_3 160 // -1, //LSTORE_0 161 // -1, //LSTORE_1 162 // -1, //LSTORE_2 163 // -1, //LSTORE_3 164 // -1, //FSTORE_0 165 // -1, //FSTORE_1 166 // -1, //FSTORE_2 167 // -1, //FSTORE_3 168 // -1, //DSTORE_0 169 // -1, //DSTORE_1 170 // -1, //DSTORE_2 171 // -1, //DSTORE_3 172 // -1, //ASTORE_0 173 // -1, //ASTORE_1 174 // -1, //ASTORE_2 175 // -1, //ASTORE_3 176 // 0, //IASTORE 177 // 0, //LASTORE 178 // 0, //FASTORE 179 // 0, //DASTORE 180 // 0, //AASTORE 181 // 0, //BASTORE 182 // 0, //CASTORE 183 // 0, //SASTORE 184 // 0, //POP 185 // 0, //POP2 186 // 0, //DUP 187 // 0, //DUP_X1 188 // 0, //DUP_X2 189 // 0, //DUP2 190 // 0, //DUP2_X1 191 // 0, //DUP2_X2 192 // 0, //SWAP 193 // 0, //IADD 194 // 0, //LADD 195 // 0, //FADD 196 // 0, //DADD 197 // 0, //ISUB 198 // 0, //LSUB 199 // 0, //FSUB 200 // 0, //DSUB 201 // 0, //IMUL 202 // 0, //LMUL 203 // 0, //FMUL 204 // 0, //DMUL 205 // 0, //IDIV 206 // 0, //LDIV 207 // 0, //FDIV 208 // 0, //DDIV 209 // 0, //IREM 210 // 0, //LREM 211 // 0, //FREM 212 // 0, //DREM 213 // 0, //INEG 214 // 0, //LNEG 215 // 0, //FNEG 216 // 0, //DNEG 217 // 0, //ISHL 218 // 0, //LSHL 219 // 0, //ISHR 220 // 0, //LSHR 221 // 0, //IUSHR 222 // 0, //LUSHR 223 // 0, //IAND 224 // 0, //LAND 225 // 0, //IOR 226 // 0, //LOR 227 // 0, //IXOR 228 // 0, //LXOR 229 // 8, //IINC 230 // 0, //I2L 231 // 0, //I2F 232 // 0, //I2D 233 // 0, //L2I 234 // 0, //L2F 235 // 0, //L2D 236 // 0, //F2I 237 // 0, //F2L 238 // 0, //F2D 239 // 0, //D2I 240 // 0, //D2L 241 // 0, //D2F 242 // 0, //I2B 243 // 0, //I2C 244 // 0, //I2S 245 // 0, //LCMP 246 // 0, //FCMPL 247 // 0, //FCMPG 248 // 0, //DCMPL 249 // 0, //DCMPG 250 // 6, //IFEQ 251 // 6, //IFNE 252 // 6, //IFLT 253 // 6, //IFGE 254 // 6, //IFGT 255 // 6, //IFLE 256 // 6, //IF_ICMPEQ 257 // 6, //IF_ICMPNE 258 // 6, //IF_ICMPLT 259 // 6, //IF_ICMPGE 260 // 6, //IF_ICMPGT 261 // 6, //IF_ICMPLE 262 // 6, //IF_ACMPEQ 263 // 6, //IF_ACMPNE 264 // 6, //GOTO 265 // 6, //JSR 266 // 2, //RET 267 // 9, //TABLESWITCH 268 // 10, //LOOKUPSWITCH 269 // 0, //IRETURN 270 // 0, //LRETURN 271 // 0, //FRETURN 272 // 0, //DRETURN 273 // 0, //ARETURN 274 // 0, //RETURN 275 // 4, //GETSTATIC 276 // 4, //PUTSTATIC 277 // 4, //GETFIELD 278 // 4, //PUTFIELD 279 // 5, //INVOKEVIRTUAL 280 // 5, //INVOKESPECIAL 281 // 5, //INVOKESTATIC 282 // 5, //INVOKEINTERFACE 283 // -1, //UNUSED 284 // 3, //NEW 285 // 1, //NEWARRAY 286 // 3, //ANEWARRAY 287 // 0, //ARRAYLENGTH 288 // 0, //ATHROW 289 // 3, //CHECKCAST 290 // 3, //INSTANCEOF 291 // 0, //MONITORENTER 292 // 0, //MONITOREXIT 293 // -1, //WIDE 294 // 11, //MULTIANEWARRAY 295 // 6, //IFNULL 296 // 6, //IFNONNULL 297 // -1, //GOTO_W 298 // -1 //JSR_W 299 // }; 300 // for (int i = 0; i < TYPE.length; ++i) { 301 // System.out.print((char)(TYPE[i] + 1 + 'A')); 302 // } 303 // System.out.println(); 304 // } 305 306 /** 307 * Constructs a new {@link CheckMethodAdapter} object. 308 * 309 * @param cv the code visitor to which this adapter must delegate calls. 310 */ 311 public CheckMethodAdapter(final MethodVisitor cv) { 312 super(cv); 313 this.labels = new HashMap(); 314 } 315 316 public AnnotationVisitor visitAnnotation( 317 final String desc, 318 final boolean visible) 319 { 320 checkEndMethod(); 321 checkDesc(desc, false); 322 return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible)); 323 } 324 325 public AnnotationVisitor visitAnnotationDefault() { 326 checkEndMethod(); 327 return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false); 328 } 329 330 public AnnotationVisitor visitParameterAnnotation( 331 final int parameter, 332 final String desc, 333 final boolean visible) 334 { 335 checkEndMethod(); 336 checkDesc(desc, false); 337 return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter, 338 desc, 339 visible)); 340 } 341 342 public void visitAttribute(final Attribute attr) { 343 checkEndMethod(); 344 if (attr == null) { 345 throw new IllegalArgumentException("Invalid attribute (must not be null)"); 346 } 347 mv.visitAttribute(attr); 348 } 349 350 public void visitCode() { 351 startCode = true; 352 mv.visitCode(); 353 } 354 355 public void visitFrame( 356 final int type, 357 final int nLocal, 358 final Object[] local, 359 final int nStack, 360 final Object[] stack) 361 { 362 int mLocal; 363 int mStack; 364 switch (type) { 365 case Opcodes.F_NEW: 366 case Opcodes.F_FULL: 367 mLocal = Integer.MAX_VALUE; 368 mStack = Integer.MAX_VALUE; 369 break; 370 371 case Opcodes.F_SAME: 372 mLocal = 0; 373 mStack = 0; 374 break; 375 376 case Opcodes.F_SAME1: 377 mLocal = 0; 378 mStack = 1; 379 break; 380 381 case Opcodes.F_APPEND: 382 case Opcodes.F_CHOP: 383 mLocal = 3; 384 mStack = 0; 385 break; 386 387 default: 388 throw new IllegalArgumentException("Invalid frame type " + type); 389 } 390 391 if (nLocal > mLocal) { 392 throw new IllegalArgumentException("Invalid nLocal=" + nLocal 393 + " for frame type " + type); 394 } 395 if (nStack > mStack) { 396 throw new IllegalArgumentException("Invalid nStack=" + nStack 397 + " for frame type " + type); 398 } 399 400 if (type != Opcodes.F_CHOP) { 401 if (nLocal > 0 && (local == null || local.length < nLocal)) { 402 throw new IllegalArgumentException("Array local[] is shorter than nLocal"); 403 } 404 for (int i = 0; i < nLocal; ++i) { 405 checkFrameValue(local[i]); 406 } 407 } 408 if (nStack > 0 && (stack == null || stack.length < nStack)) { 409 throw new IllegalArgumentException("Array stack[] is shorter than nStack"); 410 } 411 for (int i = 0; i < nStack; ++i) { 412 checkFrameValue(stack[i]); 413 } 414 415 mv.visitFrame(type, nLocal, local, nStack, stack); 416 } 417 418 public void visitInsn(final int opcode) { 419 checkStartCode(); 420 checkEndCode(); 421 checkOpcode(opcode, 0); 422 mv.visitInsn(opcode); 423 } 424 425 public void visitIntInsn(final int opcode, final int operand) { 426 checkStartCode(); 427 checkEndCode(); 428 checkOpcode(opcode, 1); 429 switch (opcode) { 430 case Opcodes.BIPUSH: 431 checkSignedByte(operand, "Invalid operand"); 432 break; 433 case Opcodes.SIPUSH: 434 checkSignedShort(operand, "Invalid operand"); 435 break; 436 // case Constants.NEWARRAY: 437 default: 438 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { 439 throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): " 440 + operand); 441 } 442 } 443 mv.visitIntInsn(opcode, operand); 444 } 445 446 public void visitVarInsn(final int opcode, final int var) { 447 checkStartCode(); 448 checkEndCode(); 449 checkOpcode(opcode, 2); 450 checkUnsignedShort(var, "Invalid variable index"); 451 mv.visitVarInsn(opcode, var); 452 } 453 454 public void visitTypeInsn(final int opcode, final String type) { 455 checkStartCode(); 456 checkEndCode(); 457 checkOpcode(opcode, 3); 458 checkInternalName(type, "type"); 459 if (opcode == Opcodes.NEW && type.charAt(0) == '[') { 460 throw new IllegalArgumentException("NEW cannot be used to create arrays: " 461 + type); 462 } 463 mv.visitTypeInsn(opcode, type); 464 } 465 466 public void visitFieldInsn( 467 final int opcode, 468 final String owner, 469 final String name, 470 final String desc) 471 { 472 checkStartCode(); 473 checkEndCode(); 474 checkOpcode(opcode, 4); 475 checkInternalName(owner, "owner"); 476 checkIdentifier(name, "name"); 477 checkDesc(desc, false); 478 mv.visitFieldInsn(opcode, owner, name, desc); 479 } 480 481 public void visitMethodInsn( 482 final int opcode, 483 final String owner, 484 final String name, 485 final String desc) 486 { 487 checkStartCode(); 488 checkEndCode(); 489 checkOpcode(opcode, 5); 490 checkMethodIdentifier(name, "name"); 491 checkInternalName(owner, "owner"); 492 checkMethodDesc(desc); 493 mv.visitMethodInsn(opcode, owner, name, desc); 494 } 495 496 public void visitJumpInsn(final int opcode, final Label label) { 497 checkStartCode(); 498 checkEndCode(); 499 checkOpcode(opcode, 6); 500 checkLabel(label, false, "label"); 501 mv.visitJumpInsn(opcode, label); 502 } 503 504 public void visitLabel(final Label label) { 505 checkStartCode(); 506 checkEndCode(); 507 checkLabel(label, false, "label"); 508 if (labels.get(label) != null) { 509 throw new IllegalArgumentException("Already visited label"); 510 } 511 labels.put(label, new Integer(labels.size())); 512 mv.visitLabel(label); 513 } 514 515 public void visitLdcInsn(final Object cst) { 516 checkStartCode(); 517 checkEndCode(); 518 if (!(cst instanceof Type)) { 519 checkConstant(cst); 520 } 521 mv.visitLdcInsn(cst); 522 } 523 524 public void visitIincInsn(final int var, final int increment) { 525 checkStartCode(); 526 checkEndCode(); 527 checkUnsignedShort(var, "Invalid variable index"); 528 checkSignedShort(increment, "Invalid increment"); 529 mv.visitIincInsn(var, increment); 530 } 531 532 public void visitTableSwitchInsn( 533 final int min, 534 final int max, 535 final Label dflt, 536 final Label[] labels) 537 { 538 checkStartCode(); 539 checkEndCode(); 540 if (max < min) { 541 throw new IllegalArgumentException("Max = " + max 542 + " must be greater than or equal to min = " + min); 543 } 544 checkLabel(dflt, false, "default label"); 545 if (labels == null || labels.length != max - min + 1) { 546 throw new IllegalArgumentException("There must be max - min + 1 labels"); 547 } 548 for (int i = 0; i < labels.length; ++i) { 549 checkLabel(labels[i], false, "label at index " + i); 550 } 551 mv.visitTableSwitchInsn(min, max, dflt, labels); 552 } 553 554 public void visitLookupSwitchInsn( 555 final Label dflt, 556 final int[] keys, 557 final Label[] labels) 558 { 559 checkEndCode(); 560 checkStartCode(); 561 checkLabel(dflt, false, "default label"); 562 if (keys == null || labels == null || keys.length != labels.length) { 563 throw new IllegalArgumentException("There must be the same number of keys and labels"); 564 } 565 for (int i = 0; i < labels.length; ++i) { 566 checkLabel(labels[i], false, "label at index " + i); 567 } 568 mv.visitLookupSwitchInsn(dflt, keys, labels); 569 } 570 571 public void visitMultiANewArrayInsn(final String desc, final int dims) { 572 checkStartCode(); 573 checkEndCode(); 574 checkDesc(desc, false); 575 if (desc.charAt(0) != '[') { 576 throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): " 577 + desc); 578 } 579 if (dims < 1) { 580 throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): " 581 + dims); 582 } 583 if (dims > desc.lastIndexOf('[') + 1) { 584 throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): " 585 + dims); 586 } 587 mv.visitMultiANewArrayInsn(desc, dims); 588 } 589 590 public void visitTryCatchBlock( 591 final Label start, 592 final Label end, 593 final Label handler, 594 final String type) 595 { 596 checkStartCode(); 597 checkEndCode(); 598 if (type != null) { 599 checkInternalName(type, "type"); 600 } 601 mv.visitTryCatchBlock(start, end, handler, type); 602 } 603 604 public void visitLocalVariable( 605 final String name, 606 final String desc, 607 final String signature, 608 final Label start, 609 final Label end, 610 final int index) 611 { 612 checkStartCode(); 613 checkEndCode(); 614 checkIdentifier(name, "name"); 615 checkDesc(desc, false); 616 checkLabel(start, true, "start label"); 617 checkLabel(end, true, "end label"); 618 checkUnsignedShort(index, "Invalid variable index"); 619 int s = ((Integer) labels.get(start)).intValue(); 620 int e = ((Integer) labels.get(end)).intValue(); 621 if (e < s) { 622 throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)"); 623 } 624 mv.visitLocalVariable(name, desc, signature, start, end, index); 625 } 626 627 public void visitLineNumber(final int line, final Label start) { 628 checkStartCode(); 629 checkEndCode(); 630 checkUnsignedShort(line, "Invalid line number"); 631 checkLabel(start, true, "start label"); 632 mv.visitLineNumber(line, start); 633 } 634 635 public void visitMaxs(final int maxStack, final int maxLocals) { 636 checkStartCode(); 637 checkEndCode(); 638 endCode = true; 639 checkUnsignedShort(maxStack, "Invalid max stack"); 640 checkUnsignedShort(maxLocals, "Invalid max locals"); 641 mv.visitMaxs(maxStack, maxLocals); 642 } 643 644 public void visitEnd() { 645 checkEndMethod(); 646 endMethod = true; 647 mv.visitEnd(); 648 } 649 650 // ------------------------------------------------------------------------- 651 652 /** 653 * Checks that the visitCode method has been called. 654 */ 655 void checkStartCode() { 656 if (!startCode) { 657 throw new IllegalStateException("Cannot visit instructions before visitCode has been called."); 658 } 659 } 660 661 /** 662 * Checks that the visitMaxs method has not been called. 663 */ 664 void checkEndCode() { 665 if (endCode) { 666 throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); 667 } 668 } 669 670 /** 671 * Checks that the visitEnd method has not been called. 672 */ 673 void checkEndMethod() { 674 if (endMethod) { 675 throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); 676 } 677 } 678 679 /** 680 * Checks a stack frame value. 681 * 682 * @param value the value to be checked. 683 */ 684 static void checkFrameValue(final Object value) { 685 if (value == Opcodes.TOP || value == Opcodes.INTEGER 686 || value == Opcodes.FLOAT || value == Opcodes.LONG 687 || value == Opcodes.DOUBLE || value == Opcodes.NULL 688 || value == Opcodes.UNINITIALIZED_THIS) 689 { 690 return; 691 } 692 if (value instanceof String) { 693 checkInternalName((String) value, "Invalid stack frame value"); 694 return; 695 } 696 if (!(value instanceof Label)) { 697 throw new IllegalArgumentException("Invalid stack frame value: " 698 + value); 699 } 700 } 701 702 /** 703 * Checks that the type of the given opcode is equal to the given type. 704 * 705 * @param opcode the opcode to be checked. 706 * @param type the expected opcode type. 707 */ 708 static void checkOpcode(final int opcode, final int type) { 709 if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { 710 throw new IllegalArgumentException("Invalid opcode: " + opcode); 711 } 712 } 713 714 /** 715 * Checks that the given value is a signed byte. 716 * 717 * @param value the value to be checked. 718 * @param msg an message to be used in case of error. 719 */ 720 static void checkSignedByte(final int value, final String msg) { 721 if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { 722 throw new IllegalArgumentException(msg 723 + " (must be a signed byte): " + value); 724 } 725 } 726 727 /** 728 * Checks that the given value is a signed short. 729 * 730 * @param value the value to be checked. 731 * @param msg an message to be used in case of error. 732 */ 733 static void checkSignedShort(final int value, final String msg) { 734 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 735 throw new IllegalArgumentException(msg 736 + " (must be a signed short): " + value); 737 } 738 } 739 740 /** 741 * Checks that the given value is an unsigned short. 742 * 743 * @param value the value to be checked. 744 * @param msg an message to be used in case of error. 745 */ 746 static void checkUnsignedShort(final int value, final String msg) { 747 if (value < 0 || value > 65535) { 748 throw new IllegalArgumentException(msg 749 + " (must be an unsigned short): " + value); 750 } 751 } 752 753 /** 754 * Checks that the given value is an {@link Integer}, a{@link Float}, a 755 * {@link Long}, a {@link Double} or a {@link String}. 756 * 757 * @param cst the value to be checked. 758 */ 759 static void checkConstant(final Object cst) { 760 if (!(cst instanceof Integer) && !(cst instanceof Float) 761 && !(cst instanceof Long) && !(cst instanceof Double) 762 && !(cst instanceof String)) 763 { 764 throw new IllegalArgumentException("Invalid constant: " + cst); 765 } 766 } 767 768 /** 769 * Checks that the given string is a valid Java identifier. 770 * 771 * @param name the string to be checked. 772 * @param msg a message to be used in case of error. 773 */ 774 static void checkIdentifier(final String name, final String msg) { 775 checkIdentifier(name, 0, -1, msg); 776 } 777 778 /** 779 * Checks that the given substring is a valid Java identifier. 780 * 781 * @param name the string to be checked. 782 * @param start index of the first character of the identifier (inclusive). 783 * @param end index of the last character of the identifier (exclusive). -1 784 * is equivalent to <tt>name.length()</tt> if name is not 785 * <tt>null</tt>. 786 * @param msg a message to be used in case of error. 787 */ 788 static void checkIdentifier( 789 final String name, 790 final int start, 791 final int end, 792 final String msg) 793 { 794 if (name == null || (end == -1 ? name.length() <= start : end <= start)) 795 { 796 throw new IllegalArgumentException("Invalid " + msg 797 + " (must not be null or empty)"); 798 } 799 if (!Character.isJavaIdentifierStart(name.charAt(start))) { 800 throw new IllegalArgumentException("Invalid " + msg 801 + " (must be a valid Java identifier): " + name); 802 } 803 int max = end == -1 ? name.length() : end; 804 for (int i = start + 1; i < max; ++i) { 805 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 806 throw new IllegalArgumentException("Invalid " + msg 807 + " (must be a valid Java identifier): " + name); 808 } 809 } 810 } 811 812 /** 813 * Checks that the given string is a valid Java identifier or is equal to 814 * '<init>' or '<clinit>'. 815 * 816 * @param name the string to be checked. 817 * @param msg a message to be used in case of error. 818 */ 819 static void checkMethodIdentifier(final String name, final String msg) { 820 if (name == null || name.length() == 0) { 821 throw new IllegalArgumentException("Invalid " + msg 822 + " (must not be null or empty)"); 823 } 824 if ("<init>".equals(name) || "<clinit>".equals(name)) { 825 return; 826 } 827 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 828 throw new IllegalArgumentException("Invalid " 829 + msg 830 + " (must be a '<init>', '<clinit>' or a valid Java identifier): " 831 + name); 832 } 833 for (int i = 1; i < name.length(); ++i) { 834 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 835 throw new IllegalArgumentException("Invalid " 836 + msg 837 + " (must be '<init>' or '<clinit>' or a valid Java identifier): " 838 + name); 839 } 840 } 841 } 842 843 /** 844 * Checks that the given string is a valid internal class name. 845 * 846 * @param name the string to be checked. 847 * @param msg a message to be used in case of error. 848 */ 849 static void checkInternalName(final String name, final String msg) { 850 if (name == null || name.length() == 0) { 851 throw new IllegalArgumentException("Invalid " + msg 852 + " (must not be null or empty)"); 853 } 854 if (name.charAt(0) == '[') { 855 checkDesc(name, false); 856 } else { 857 checkInternalName(name, 0, -1, msg); 858 } 859 } 860 861 /** 862 * Checks that the given substring is a valid internal class name. 863 * 864 * @param name the string to be checked. 865 * @param start index of the first character of the identifier (inclusive). 866 * @param end index of the last character of the identifier (exclusive). -1 867 * is equivalent to <tt>name.length()</tt> if name is not 868 * <tt>null</tt>. 869 * @param msg a message to be used in case of error. 870 */ 871 static void checkInternalName( 872 final String name, 873 final int start, 874 final int end, 875 final String msg) 876 { 877 int max = end == -1 ? name.length() : end; 878 try { 879 int begin = start; 880 int slash; 881 do { 882 slash = name.indexOf('/', begin + 1); 883 if (slash == -1 || slash > max) { 884 slash = max; 885 } 886 checkIdentifier(name, begin, slash, null); 887 begin = slash + 1; 888 } while (slash != max); 889 } catch (IllegalArgumentException _) { 890 throw new IllegalArgumentException("Invalid " 891 + msg 892 + " (must be a fully qualified class name in internal form): " 893 + name); 894 } 895 } 896 897 /** 898 * Checks that the given string is a valid type descriptor. 899 * 900 * @param desc the string to be checked. 901 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 902 */ 903 static void checkDesc(final String desc, final boolean canBeVoid) { 904 int end = checkDesc(desc, 0, canBeVoid); 905 if (end != desc.length()) { 906 throw new IllegalArgumentException("Invalid descriptor: " + desc); 907 } 908 } 909 910 /** 911 * Checks that a the given substring is a valid type descriptor. 912 * 913 * @param desc the string to be checked. 914 * @param start index of the first character of the identifier (inclusive). 915 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 916 * @return the index of the last character of the type decriptor, plus one. 917 */ 918 static int checkDesc( 919 final String desc, 920 final int start, 921 final boolean canBeVoid) 922 { 923 if (desc == null || start >= desc.length()) { 924 throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); 925 } 926 int index; 927 switch (desc.charAt(start)) { 928 case 'V': 929 if (canBeVoid) { 930 return start + 1; 931 } else { 932 throw new IllegalArgumentException("Invalid descriptor: " 933 + desc); 934 } 935 case 'Z': 936 case 'C': 937 case 'B': 938 case 'S': 939 case 'I': 940 case 'F': 941 case 'J': 942 case 'D': 943 return start + 1; 944 case '[': 945 index = start + 1; 946 while (index < desc.length() && desc.charAt(index) == '[') { 947 ++index; 948 } 949 if (index < desc.length()) { 950 return checkDesc(desc, index, false); 951 } else { 952 throw new IllegalArgumentException("Invalid descriptor: " 953 + desc); 954 } 955 case 'L': 956 index = desc.indexOf(';', start); 957 if (index == -1 || index - start < 2) { 958 throw new IllegalArgumentException("Invalid descriptor: " 959 + desc); 960 } 961 try { 962 checkInternalName(desc, start + 1, index, null); 963 } catch (IllegalArgumentException _) { 964 throw new IllegalArgumentException("Invalid descriptor: " 965 + desc); 966 } 967 return index + 1; 968 default: 969 throw new IllegalArgumentException("Invalid descriptor: " 970 + desc); 971 } 972 } 973 974 /** 975 * Checks that the given string is a valid method descriptor. 976 * 977 * @param desc the string to be checked. 978 */ 979 static void checkMethodDesc(final String desc) { 980 if (desc == null || desc.length() == 0) { 981 throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); 982 } 983 if (desc.charAt(0) != '(' || desc.length() < 3) { 984 throw new IllegalArgumentException("Invalid descriptor: " + desc); 985 } 986 int start = 1; 987 if (desc.charAt(start) != ')') { 988 do { 989 if (desc.charAt(start) == 'V') { 990 throw new IllegalArgumentException("Invalid descriptor: " 991 + desc); 992 } 993 start = checkDesc(desc, start, false); 994 } while (start < desc.length() && desc.charAt(start) != ')'); 995 } 996 start = checkDesc(desc, start + 1, true); 997 if (start != desc.length()) { 998 throw new IllegalArgumentException("Invalid descriptor: " + desc); 999 } 1000 } 1001 1002 /** 1003 * Checks a class signature. 1004 * 1005 * @param signature a string containing the signature that must be checked. 1006 */ 1007 static void checkClassSignature(final String signature) { 1008 // ClassSignature: 1009 // FormalTypeParameters? ClassTypeSignature ClassTypeSignature* 1010 1011 int pos = 0; 1012 if (getChar(signature, 0) == '<') { 1013 pos = checkFormalTypeParameters(signature, pos); 1014 } 1015 pos = checkClassTypeSignature(signature, pos); 1016 while (getChar(signature, pos) == 'L') { 1017 pos = checkClassTypeSignature(signature, pos); 1018 } 1019 if (pos != signature.length()) { 1020 throw new IllegalArgumentException(signature + ": error at index " 1021 + pos); 1022 } 1023 } 1024 1025 /** 1026 * Checks a method signature. 1027 * 1028 * @param signature a string containing the signature that must be checked. 1029 */ 1030 static void checkMethodSignature(final String signature) { 1031 // MethodTypeSignature: 1032 // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) ( 1033 // ^ClassTypeSignature | ^TypeVariableSignature )* 1034 1035 int pos = 0; 1036 if (getChar(signature, 0) == '<') { 1037 pos = checkFormalTypeParameters(signature, pos); 1038 } 1039 pos = checkChar('(', signature, pos); 1040 while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) { 1041 pos = checkTypeSignature(signature, pos); 1042 } 1043 pos = checkChar(')', signature, pos); 1044 if (getChar(signature, pos) == 'V') { 1045 ++pos; 1046 } else { 1047 pos = checkTypeSignature(signature, pos); 1048 } 1049 while (getChar(signature, pos) == '^') { 1050 ++pos; 1051 if (getChar(signature, pos) == 'L') { 1052 pos = checkClassTypeSignature(signature, pos); 1053 } else { 1054 pos = checkTypeVariableSignature(signature, pos); 1055 } 1056 } 1057 if (pos != signature.length()) { 1058 throw new IllegalArgumentException(signature + ": error at index " 1059 + pos); 1060 } 1061 } 1062 1063 /** 1064 * Checks a field signature. 1065 * 1066 * @param signature a string containing the signature that must be checked. 1067 */ 1068 static void checkFieldSignature(final String signature) { 1069 int pos = checkFieldTypeSignature(signature, 0); 1070 if (pos != signature.length()) { 1071 throw new IllegalArgumentException(signature + ": error at index " 1072 + pos); 1073 } 1074 } 1075 1076 /** 1077 * Checks the formal type parameters of a class or method signature. 1078 * 1079 * @param signature a string containing the signature that must be checked. 1080 * @param pos index of first character to be checked. 1081 * @return the index of the first character after the checked part. 1082 */ 1083 private static int checkFormalTypeParameters(final String signature, int pos) 1084 { 1085 // FormalTypeParameters: 1086 // < FormalTypeParameter+ > 1087 1088 pos = checkChar('<', signature, pos); 1089 pos = checkFormalTypeParameter(signature, pos); 1090 while (getChar(signature, pos) != '>') { 1091 pos = checkFormalTypeParameter(signature, pos); 1092 } 1093 return pos + 1; 1094 } 1095 1096 /** 1097 * Checks a formal type parameter of a class or method signature. 1098 * 1099 * @param signature a string containing the signature that must be checked. 1100 * @param pos index of first character to be checked. 1101 * @return the index of the first character after the checked part. 1102 */ 1103 private static int checkFormalTypeParameter(final String signature, int pos) 1104 { 1105 // FormalTypeParameter: 1106 // Identifier : FieldTypeSignature? (: FieldTypeSignature)* 1107 1108 pos = checkIdentifier(signature, pos); 1109 pos = checkChar(':', signature, pos); 1110 if ("L[T".indexOf(getChar(signature, pos)) != -1) { 1111 pos = checkFieldTypeSignature(signature, pos); 1112 } 1113 while (getChar(signature, pos) == ':') { 1114 pos = checkFieldTypeSignature(signature, pos + 1); 1115 } 1116 return pos; 1117 } 1118 1119 /** 1120 * Checks a field type signature. 1121 * 1122 * @param signature a string containing the signature that must be checked. 1123 * @param pos index of first character to be checked. 1124 * @return the index of the first character after the checked part. 1125 */ 1126 private static int checkFieldTypeSignature(final String signature, int pos) 1127 { 1128 // FieldTypeSignature: 1129 // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature 1130 // 1131 // ArrayTypeSignature: 1132 // [ TypeSignature 1133 1134 switch (getChar(signature, pos)) { 1135 case 'L': 1136 return checkClassTypeSignature(signature, pos); 1137 case '[': 1138 return checkTypeSignature(signature, pos + 1); 1139 default: 1140 return checkTypeVariableSignature(signature, pos); 1141 } 1142 } 1143 1144 /** 1145 * Checks a class type signature. 1146 * 1147 * @param signature a string containing the signature that must be checked. 1148 * @param pos index of first character to be checked. 1149 * @return the index of the first character after the checked part. 1150 */ 1151 private static int checkClassTypeSignature(final String signature, int pos) 1152 { 1153 // ClassTypeSignature: 1154 // L Identifier ( / Identifier )* TypeArguments? ( . Identifier 1155 // TypeArguments? )* ; 1156 1157 pos = checkChar('L', signature, pos); 1158 pos = checkIdentifier(signature, pos); 1159 while (getChar(signature, pos) == '/') { 1160 pos = checkIdentifier(signature, pos + 1); 1161 } 1162 if (getChar(signature, pos) == '<') { 1163 pos = checkTypeArguments(signature, pos); 1164 } 1165 while (getChar(signature, pos) == '.') { 1166 pos = checkIdentifier(signature, pos + 1); 1167 if (getChar(signature, pos) == '<') { 1168 pos = checkTypeArguments(signature, pos); 1169 } 1170 } 1171 return checkChar(';', signature, pos); 1172 } 1173 1174 /** 1175 * Checks the type arguments in a class type signature. 1176 * 1177 * @param signature a string containing the signature that must be checked. 1178 * @param pos index of first character to be checked. 1179 * @return the index of the first character after the checked part. 1180 */ 1181 private static int checkTypeArguments(final String signature, int pos) { 1182 // TypeArguments: 1183 // < TypeArgument+ > 1184 1185 pos = checkChar('<', signature, pos); 1186 pos = checkTypeArgument(signature, pos); 1187 while (getChar(signature, pos) != '>') { 1188 pos = checkTypeArgument(signature, pos); 1189 } 1190 return pos + 1; 1191 } 1192 1193 /** 1194 * Checks a type argument in a class type signature. 1195 * 1196 * @param signature a string containing the signature that must be checked. 1197 * @param pos index of first character to be checked. 1198 * @return the index of the first character after the checked part. 1199 */ 1200 private static int checkTypeArgument(final String signature, int pos) { 1201 // TypeArgument: 1202 // * | ( ( + | - )? FieldTypeSignature ) 1203 1204 char c = getChar(signature, pos); 1205 if (c == '*') { 1206 return pos + 1; 1207 } else if (c == '+' || c == '-') { 1208 pos++; 1209 } 1210 return checkFieldTypeSignature(signature, pos); 1211 } 1212 1213 /** 1214 * Checks a type variable signature. 1215 * 1216 * @param signature a string containing the signature that must be checked. 1217 * @param pos index of first character to be checked. 1218 * @return the index of the first character after the checked part. 1219 */ 1220 private static int checkTypeVariableSignature( 1221 final String signature, 1222 int pos) 1223 { 1224 // TypeVariableSignature: 1225 // T Identifier ; 1226 1227 pos = checkChar('T', signature, pos); 1228 pos = checkIdentifier(signature, pos); 1229 return checkChar(';', signature, pos); 1230 } 1231 1232 /** 1233 * Checks a type signature. 1234 * 1235 * @param signature a string containing the signature that must be checked. 1236 * @param pos index of first character to be checked. 1237 * @return the index of the first character after the checked part. 1238 */ 1239 private static int checkTypeSignature(final String signature, int pos) { 1240 // TypeSignature: 1241 // Z | C | B | S | I | F | J | D | FieldTypeSignature 1242 1243 switch (getChar(signature, pos)) { 1244 case 'Z': 1245 case 'C': 1246 case 'B': 1247 case 'S': 1248 case 'I': 1249 case 'F': 1250 case 'J': 1251 case 'D': 1252 return pos + 1; 1253 default: 1254 return checkFieldTypeSignature(signature, pos); 1255 } 1256 } 1257 1258 /** 1259 * Checks an identifier. 1260 * 1261 * @param signature a string containing the signature that must be checked. 1262 * @param pos index of first character to be checked. 1263 * @return the index of the first character after the checked part. 1264 */ 1265 private static int checkIdentifier(final String signature, int pos) { 1266 if (!Character.isJavaIdentifierStart(getChar(signature, pos))) { 1267 throw new IllegalArgumentException(signature 1268 + ": identifier expected at index " + pos); 1269 } 1270 ++pos; 1271 while (Character.isJavaIdentifierPart(getChar(signature, pos))) { 1272 ++pos; 1273 } 1274 return pos; 1275 } 1276 1277 /** 1278 * Checks a single character. 1279 * 1280 * @param signature a string containing the signature that must be checked. 1281 * @param pos index of first character to be checked. 1282 * @return the index of the first character after the checked part. 1283 */ 1284 private static int checkChar(final char c, final String signature, int pos) 1285 { 1286 if (getChar(signature, pos) == c) { 1287 return pos + 1; 1288 } 1289 throw new IllegalArgumentException(signature + ": '" + c 1290 + "' expected at index " + pos); 1291 } 1292 1293 /** 1294 * Returns the signature car at the given index. 1295 * 1296 * @param signature a signature. 1297 * @param pos an index in signature. 1298 * @return the character at the given index, or 0 if there is no such 1299 * character. 1300 */ 1301 private static char getChar(final String signature, int pos) { 1302 return pos < signature.length() ? signature.charAt(pos) : (char) 0; 1303 } 1304 1305 /** 1306 * Checks that the given label is not null. This method can also check that 1307 * the label has been visited. 1308 * 1309 * @param label the label to be checked. 1310 * @param checkVisited <tt>true</tt> to check that the label has been 1311 * visited. 1312 * @param msg a message to be used in case of error. 1313 */ 1314 void checkLabel( 1315 final Label label, 1316 final boolean checkVisited, 1317 final String msg) 1318 { 1319 if (label == null) { 1320 throw new IllegalArgumentException("Invalid " + msg 1321 + " (must not be null)"); 1322 } 1323 if (checkVisited && labels.get(label) == null) { 1324 throw new IllegalArgumentException("Invalid " + msg 1325 + " (must be visited first)"); 1326 } 1327 } 1328 } 1329