CheckMethodAdapter.java revision 422c005785a1a2bf1dbf1ae1e695d7b9d26a2158
1/*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2005 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 */ 30package org.objectweb.asm.util; 31 32import org.objectweb.asm.AnnotationVisitor; 33import org.objectweb.asm.Label; 34import org.objectweb.asm.MethodAdapter; 35import org.objectweb.asm.MethodVisitor; 36import org.objectweb.asm.Opcodes; 37import org.objectweb.asm.Attribute; 38import org.objectweb.asm.Type; 39 40import java.util.HashMap; 41 42/** 43 * A {@link MethodAdapter} that checks that its methods are properly used. More 44 * precisely this code adapter checks each instruction individually (i.e., each 45 * visit method checks some preconditions based <i>only</i> on its arguments - 46 * such as the fact that the given opcode is correct for a given visit method), 47 * but does <i>not</i> check the <i>sequence</i> of instructions. For example, 48 * in a method whose signature is <tt>void m ()</tt>, the invalid instruction 49 * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by 50 * this code adapter. 51 * 52 * @author Eric Bruneton 53 */ 54public class CheckMethodAdapter extends MethodAdapter { 55 56 /** 57 * <tt>true</tt> if the visitCode method has been called. 58 */ 59 private boolean startCode; 60 61 /** 62 * <tt>true</tt> if the visitMaxs method has been called. 63 */ 64 private boolean endCode; 65 66 /** 67 * <tt>true</tt> if the visitEnd method has been called. 68 */ 69 private boolean endMethod; 70 71 /** 72 * The already visited labels. This map associate Integer values to Label 73 * keys. 74 */ 75 private HashMap labels; 76 77 /** 78 * Code of the visit method to be used for each opcode. 79 */ 80 private final static int[] TYPE; 81 82 static { 83 String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD" 84 + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 85 + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD" 86 + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA"; 87 TYPE = new int[s.length()]; 88 for (int i = 0; i < TYPE.length; ++i) { 89 TYPE[i] = (s.charAt(i) - 'A' - 1); 90 } 91 } 92 93 // code to generate the above string 94 // public static void main (String[] args) { 95 // int[] TYPE = new int[] { 96 // 0, //NOP 97 // 0, //ACONST_NULL 98 // 0, //ICONST_M1 99 // 0, //ICONST_0 100 // 0, //ICONST_1 101 // 0, //ICONST_2 102 // 0, //ICONST_3 103 // 0, //ICONST_4 104 // 0, //ICONST_5 105 // 0, //LCONST_0 106 // 0, //LCONST_1 107 // 0, //FCONST_0 108 // 0, //FCONST_1 109 // 0, //FCONST_2 110 // 0, //DCONST_0 111 // 0, //DCONST_1 112 // 1, //BIPUSH 113 // 1, //SIPUSH 114 // 7, //LDC 115 // -1, //LDC_W 116 // -1, //LDC2_W 117 // 2, //ILOAD 118 // 2, //LLOAD 119 // 2, //FLOAD 120 // 2, //DLOAD 121 // 2, //ALOAD 122 // -1, //ILOAD_0 123 // -1, //ILOAD_1 124 // -1, //ILOAD_2 125 // -1, //ILOAD_3 126 // -1, //LLOAD_0 127 // -1, //LLOAD_1 128 // -1, //LLOAD_2 129 // -1, //LLOAD_3 130 // -1, //FLOAD_0 131 // -1, //FLOAD_1 132 // -1, //FLOAD_2 133 // -1, //FLOAD_3 134 // -1, //DLOAD_0 135 // -1, //DLOAD_1 136 // -1, //DLOAD_2 137 // -1, //DLOAD_3 138 // -1, //ALOAD_0 139 // -1, //ALOAD_1 140 // -1, //ALOAD_2 141 // -1, //ALOAD_3 142 // 0, //IALOAD 143 // 0, //LALOAD 144 // 0, //FALOAD 145 // 0, //DALOAD 146 // 0, //AALOAD 147 // 0, //BALOAD 148 // 0, //CALOAD 149 // 0, //SALOAD 150 // 2, //ISTORE 151 // 2, //LSTORE 152 // 2, //FSTORE 153 // 2, //DSTORE 154 // 2, //ASTORE 155 // -1, //ISTORE_0 156 // -1, //ISTORE_1 157 // -1, //ISTORE_2 158 // -1, //ISTORE_3 159 // -1, //LSTORE_0 160 // -1, //LSTORE_1 161 // -1, //LSTORE_2 162 // -1, //LSTORE_3 163 // -1, //FSTORE_0 164 // -1, //FSTORE_1 165 // -1, //FSTORE_2 166 // -1, //FSTORE_3 167 // -1, //DSTORE_0 168 // -1, //DSTORE_1 169 // -1, //DSTORE_2 170 // -1, //DSTORE_3 171 // -1, //ASTORE_0 172 // -1, //ASTORE_1 173 // -1, //ASTORE_2 174 // -1, //ASTORE_3 175 // 0, //IASTORE 176 // 0, //LASTORE 177 // 0, //FASTORE 178 // 0, //DASTORE 179 // 0, //AASTORE 180 // 0, //BASTORE 181 // 0, //CASTORE 182 // 0, //SASTORE 183 // 0, //POP 184 // 0, //POP2 185 // 0, //DUP 186 // 0, //DUP_X1 187 // 0, //DUP_X2 188 // 0, //DUP2 189 // 0, //DUP2_X1 190 // 0, //DUP2_X2 191 // 0, //SWAP 192 // 0, //IADD 193 // 0, //LADD 194 // 0, //FADD 195 // 0, //DADD 196 // 0, //ISUB 197 // 0, //LSUB 198 // 0, //FSUB 199 // 0, //DSUB 200 // 0, //IMUL 201 // 0, //LMUL 202 // 0, //FMUL 203 // 0, //DMUL 204 // 0, //IDIV 205 // 0, //LDIV 206 // 0, //FDIV 207 // 0, //DDIV 208 // 0, //IREM 209 // 0, //LREM 210 // 0, //FREM 211 // 0, //DREM 212 // 0, //INEG 213 // 0, //LNEG 214 // 0, //FNEG 215 // 0, //DNEG 216 // 0, //ISHL 217 // 0, //LSHL 218 // 0, //ISHR 219 // 0, //LSHR 220 // 0, //IUSHR 221 // 0, //LUSHR 222 // 0, //IAND 223 // 0, //LAND 224 // 0, //IOR 225 // 0, //LOR 226 // 0, //IXOR 227 // 0, //LXOR 228 // 8, //IINC 229 // 0, //I2L 230 // 0, //I2F 231 // 0, //I2D 232 // 0, //L2I 233 // 0, //L2F 234 // 0, //L2D 235 // 0, //F2I 236 // 0, //F2L 237 // 0, //F2D 238 // 0, //D2I 239 // 0, //D2L 240 // 0, //D2F 241 // 0, //I2B 242 // 0, //I2C 243 // 0, //I2S 244 // 0, //LCMP 245 // 0, //FCMPL 246 // 0, //FCMPG 247 // 0, //DCMPL 248 // 0, //DCMPG 249 // 6, //IFEQ 250 // 6, //IFNE 251 // 6, //IFLT 252 // 6, //IFGE 253 // 6, //IFGT 254 // 6, //IFLE 255 // 6, //IF_ICMPEQ 256 // 6, //IF_ICMPNE 257 // 6, //IF_ICMPLT 258 // 6, //IF_ICMPGE 259 // 6, //IF_ICMPGT 260 // 6, //IF_ICMPLE 261 // 6, //IF_ACMPEQ 262 // 6, //IF_ACMPNE 263 // 6, //GOTO 264 // 6, //JSR 265 // 2, //RET 266 // 9, //TABLESWITCH 267 // 10, //LOOKUPSWITCH 268 // 0, //IRETURN 269 // 0, //LRETURN 270 // 0, //FRETURN 271 // 0, //DRETURN 272 // 0, //ARETURN 273 // 0, //RETURN 274 // 4, //GETSTATIC 275 // 4, //PUTSTATIC 276 // 4, //GETFIELD 277 // 4, //PUTFIELD 278 // 5, //INVOKEVIRTUAL 279 // 5, //INVOKESPECIAL 280 // 5, //INVOKESTATIC 281 // 5, //INVOKEINTERFACE 282 // -1, //UNUSED 283 // 3, //NEW 284 // 1, //NEWARRAY 285 // 3, //ANEWARRAY 286 // 0, //ARRAYLENGTH 287 // 0, //ATHROW 288 // 3, //CHECKCAST 289 // 3, //INSTANCEOF 290 // 0, //MONITORENTER 291 // 0, //MONITOREXIT 292 // -1, //WIDE 293 // 11, //MULTIANEWARRAY 294 // 6, //IFNULL 295 // 6, //IFNONNULL 296 // -1, //GOTO_W 297 // -1 //JSR_W 298 // }; 299 // for (int i = 0; i < TYPE.length; ++i) { 300 // System.out.print((char)(TYPE[i] + 1 + 'A')); 301 // } 302 // System.out.println(); 303 // } 304 305 /** 306 * Constructs a new {@link CheckMethodAdapter} object. 307 * 308 * @param cv the code visitor to which this adapter must delegate calls. 309 */ 310 public CheckMethodAdapter(final MethodVisitor cv) { 311 super(cv); 312 this.labels = new HashMap(); 313 } 314 315 public AnnotationVisitor visitAnnotation( 316 final String desc, 317 final boolean visible) 318 { 319 checkEndMethod(); 320 checkDesc(desc, false); 321 return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible)); 322 } 323 324 public AnnotationVisitor visitAnnotationDefault() { 325 checkEndMethod(); 326 return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false); 327 } 328 329 public AnnotationVisitor visitParameterAnnotation( 330 final int parameter, 331 final String desc, 332 final boolean visible) 333 { 334 checkEndMethod(); 335 checkDesc(desc, false); 336 return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter, 337 desc, 338 visible)); 339 } 340 341 public void visitAttribute(final Attribute attr) { 342 checkEndMethod(); 343 if (attr == null) { 344 throw new IllegalArgumentException("Invalid attribute (must not be null)"); 345 } 346 mv.visitAttribute(attr); 347 } 348 349 public void visitCode() { 350 startCode = true; 351 mv.visitCode(); 352 } 353 354 public void visitInsn(final int opcode) { 355 checkStartCode(); 356 checkEndCode(); 357 checkOpcode(opcode, 0); 358 mv.visitInsn(opcode); 359 } 360 361 public void visitIntInsn(final int opcode, final int operand) { 362 checkStartCode(); 363 checkEndCode(); 364 checkOpcode(opcode, 1); 365 switch (opcode) { 366 case Opcodes.BIPUSH: 367 checkSignedByte(operand, "Invalid operand"); 368 break; 369 case Opcodes.SIPUSH: 370 checkSignedShort(operand, "Invalid operand"); 371 break; 372 // case Constants.NEWARRAY: 373 default: 374 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { 375 throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): " 376 + operand); 377 } 378 } 379 mv.visitIntInsn(opcode, operand); 380 } 381 382 public void visitVarInsn(final int opcode, final int var) { 383 checkStartCode(); 384 checkEndCode(); 385 checkOpcode(opcode, 2); 386 checkUnsignedShort(var, "Invalid variable index"); 387 mv.visitVarInsn(opcode, var); 388 } 389 390 public void visitTypeInsn(final int opcode, final String desc) { 391 checkStartCode(); 392 checkEndCode(); 393 checkOpcode(opcode, 3); 394 if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') { 395 checkDesc(desc, false); 396 } else { 397 checkInternalName(desc, "type"); 398 } 399 if (opcode == Opcodes.NEW && desc.charAt(0) == '[') { 400 throw new IllegalArgumentException("NEW cannot be used to create arrays: " 401 + desc); 402 } 403 mv.visitTypeInsn(opcode, desc); 404 } 405 406 public void visitFieldInsn( 407 final int opcode, 408 final String owner, 409 final String name, 410 final String desc) 411 { 412 checkStartCode(); 413 checkEndCode(); 414 checkOpcode(opcode, 4); 415 checkInternalName(owner, "owner"); 416 checkIdentifier(name, "name"); 417 checkDesc(desc, false); 418 mv.visitFieldInsn(opcode, owner, name, desc); 419 } 420 421 public void visitMethodInsn( 422 final int opcode, 423 final String owner, 424 final String name, 425 final String desc) 426 { 427 checkStartCode(); 428 checkEndCode(); 429 checkOpcode(opcode, 5); 430 checkMethodIdentifier(name, "name"); 431 if (!name.equals("clone")) { 432 // In JDK1.5, clone method can be called on array class descriptors 433 checkInternalName(owner, "owner"); 434 } 435 checkMethodDesc(desc); 436 mv.visitMethodInsn(opcode, owner, name, desc); 437 } 438 439 public void visitJumpInsn(final int opcode, final Label label) { 440 checkStartCode(); 441 checkEndCode(); 442 checkOpcode(opcode, 6); 443 checkLabel(label, false, "label"); 444 mv.visitJumpInsn(opcode, label); 445 } 446 447 public void visitLabel(final Label label) { 448 checkStartCode(); 449 checkEndCode(); 450 checkLabel(label, false, "label"); 451 if (labels.get(label) != null) { 452 throw new IllegalArgumentException("Already visited label"); 453 } else { 454 labels.put(label, new Integer(labels.size())); 455 } 456 mv.visitLabel(label); 457 } 458 459 public void visitLdcInsn(final Object cst) { 460 checkStartCode(); 461 checkEndCode(); 462 if (!(cst instanceof Type)) { 463 checkConstant(cst); 464 } 465 mv.visitLdcInsn(cst); 466 } 467 468 public void visitIincInsn(final int var, final int increment) { 469 checkStartCode(); 470 checkEndCode(); 471 checkUnsignedShort(var, "Invalid variable index"); 472 checkSignedShort(increment, "Invalid increment"); 473 mv.visitIincInsn(var, increment); 474 } 475 476 public void visitTableSwitchInsn( 477 final int min, 478 final int max, 479 final Label dflt, 480 final Label labels[]) 481 { 482 checkStartCode(); 483 checkEndCode(); 484 if (max < min) { 485 throw new IllegalArgumentException("Max = " + max 486 + " must be greater than or equal to min = " + min); 487 } 488 checkLabel(dflt, false, "default label"); 489 if (labels == null || labels.length != max - min + 1) { 490 throw new IllegalArgumentException("There must be max - min + 1 labels"); 491 } 492 for (int i = 0; i < labels.length; ++i) { 493 checkLabel(labels[i], false, "label at index " + i); 494 } 495 mv.visitTableSwitchInsn(min, max, dflt, labels); 496 } 497 498 public void visitLookupSwitchInsn( 499 final Label dflt, 500 final int keys[], 501 final Label labels[]) 502 { 503 checkEndCode(); 504 checkStartCode(); 505 checkLabel(dflt, false, "default label"); 506 if (keys == null || labels == null || keys.length != labels.length) { 507 throw new IllegalArgumentException("There must be the same number of keys and labels"); 508 } 509 for (int i = 0; i < labels.length; ++i) { 510 checkLabel(labels[i], false, "label at index " + i); 511 } 512 mv.visitLookupSwitchInsn(dflt, keys, labels); 513 } 514 515 public void visitMultiANewArrayInsn(final String desc, final int dims) { 516 checkStartCode(); 517 checkEndCode(); 518 checkDesc(desc, false); 519 if (desc.charAt(0) != '[') { 520 throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): " 521 + desc); 522 } 523 if (dims < 1) { 524 throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): " 525 + dims); 526 } 527 if (dims > desc.lastIndexOf('[') + 1) { 528 throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): " 529 + dims); 530 } 531 mv.visitMultiANewArrayInsn(desc, dims); 532 } 533 534 public void visitTryCatchBlock( 535 final Label start, 536 final Label end, 537 final Label handler, 538 final String type) 539 { 540 checkStartCode(); 541 checkEndCode(); 542 if (type != null) { 543 checkInternalName(type, "type"); 544 } 545 mv.visitTryCatchBlock(start, end, handler, type); 546 } 547 548 public void visitLocalVariable( 549 final String name, 550 final String desc, 551 final String signature, 552 final Label start, 553 final Label end, 554 final int index) 555 { 556 checkStartCode(); 557 checkEndCode(); 558 checkIdentifier(name, "name"); 559 checkDesc(desc, false); 560 checkLabel(start, true, "start label"); 561 checkLabel(end, true, "end label"); 562 checkUnsignedShort(index, "Invalid variable index"); 563 int s = ((Integer) labels.get(start)).intValue(); 564 int e = ((Integer) labels.get(end)).intValue(); 565 if (e < s) { 566 throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)"); 567 } 568 mv.visitLocalVariable(name, desc, signature, start, end, index); 569 } 570 571 public void visitLineNumber(final int line, final Label start) { 572 checkStartCode(); 573 checkEndCode(); 574 checkUnsignedShort(line, "Invalid line number"); 575 checkLabel(start, true, "start label"); 576 mv.visitLineNumber(line, start); 577 } 578 579 public void visitMaxs(final int maxStack, final int maxLocals) { 580 checkStartCode(); 581 checkEndCode(); 582 endCode = true; 583 checkUnsignedShort(maxStack, "Invalid max stack"); 584 checkUnsignedShort(maxLocals, "Invalid max locals"); 585 mv.visitMaxs(maxStack, maxLocals); 586 } 587 588 public void visitEnd() { 589 checkEndMethod(); 590 endMethod = true; 591 mv.visitEnd(); 592 } 593 594 // ------------------------------------------------------------------------- 595 596 /** 597 * Checks that the visitCode method has been called. 598 */ 599 void checkStartCode() { 600 if (!startCode) { 601 throw new IllegalStateException("Cannot visit instructions before visitCode has been called."); 602 } 603 } 604 605 /** 606 * Checks that the visitMaxs method has not been called. 607 */ 608 void checkEndCode() { 609 if (endCode) { 610 throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); 611 } 612 } 613 614 /** 615 * Checks that the visitEnd method has not been called. 616 */ 617 void checkEndMethod() { 618 if (endMethod) { 619 throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); 620 } 621 } 622 623 /** 624 * Checks that the type of the given opcode is equal to the given type. 625 * 626 * @param opcode the opcode to be checked. 627 * @param type the expected opcode type. 628 */ 629 static void checkOpcode(final int opcode, final int type) { 630 if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { 631 throw new IllegalArgumentException("Invalid opcode: " + opcode); 632 } 633 } 634 635 /** 636 * Checks that the given value is a signed byte. 637 * 638 * @param value the value to be checked. 639 * @param msg an message to be used in case of error. 640 */ 641 static void checkSignedByte(final int value, final String msg) { 642 if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { 643 throw new IllegalArgumentException(msg 644 + " (must be a signed byte): " + value); 645 } 646 } 647 648 /** 649 * Checks that the given value is a signed short. 650 * 651 * @param value the value to be checked. 652 * @param msg an message to be used in case of error. 653 */ 654 static void checkSignedShort(final int value, final String msg) { 655 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 656 throw new IllegalArgumentException(msg 657 + " (must be a signed short): " + value); 658 } 659 } 660 661 /** 662 * Checks that the given value is an unsigned short. 663 * 664 * @param value the value to be checked. 665 * @param msg an message to be used in case of error. 666 */ 667 static void checkUnsignedShort(final int value, final String msg) { 668 if (value < 0 || value > 65535) { 669 throw new IllegalArgumentException(msg 670 + " (must be an unsigned short): " + value); 671 } 672 } 673 674 /** 675 * Checks that the given value is an {@link Integer}, a{@link Float}, a 676 * {@link Long}, a {@link Double} or a {@link String}. 677 * 678 * @param cst the value to be checked. 679 */ 680 static void checkConstant(final Object cst) { 681 if (!(cst instanceof Integer) && !(cst instanceof Float) 682 && !(cst instanceof Long) && !(cst instanceof Double) 683 && !(cst instanceof String)) 684 { 685 throw new IllegalArgumentException("Invalid constant: " + cst); 686 } 687 } 688 689 /** 690 * Checks that the given string is a valid Java identifier. 691 * 692 * @param name the string to be checked. 693 * @param msg a message to be used in case of error. 694 */ 695 static void checkIdentifier(final String name, final String msg) { 696 checkIdentifier(name, 0, -1, msg); 697 } 698 699 /** 700 * Checks that the given substring is a valid Java identifier. 701 * 702 * @param name the string to be checked. 703 * @param start index of the first character of the identifier (inclusive). 704 * @param end index of the last character of the identifier (exclusive). -1 705 * is equivalent to <tt>name.length()</tt> if name is not 706 * <tt>null</tt>. 707 * @param msg a message to be used in case of error. 708 */ 709 static void checkIdentifier( 710 final String name, 711 final int start, 712 final int end, 713 final String msg) 714 { 715 if (name == null || (end == -1 ? name.length() <= start : end <= start)) 716 { 717 throw new IllegalArgumentException("Invalid " + msg 718 + " (must not be null or empty)"); 719 } 720 if (!Character.isJavaIdentifierStart(name.charAt(start))) { 721 throw new IllegalArgumentException("Invalid " + msg 722 + " (must be a valid Java identifier): " + name); 723 } 724 int max = (end == -1 ? name.length() : end); 725 for (int i = start + 1; i < max; ++i) { 726 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 727 throw new IllegalArgumentException("Invalid " + msg 728 + " (must be a valid Java identifier): " + name); 729 } 730 } 731 } 732 733 /** 734 * Checks that the given string is a valid Java identifier or is equal to 735 * '<init>' or '<clinit>'. 736 * 737 * @param name the string to be checked. 738 * @param msg a message to be used in case of error. 739 */ 740 static void checkMethodIdentifier(final String name, final String msg) { 741 if (name == null || name.length() == 0) { 742 throw new IllegalArgumentException("Invalid " + msg 743 + " (must not be null or empty)"); 744 } 745 if (name.equals("<init>") || name.equals("<clinit>")) { 746 return; 747 } 748 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 749 throw new IllegalArgumentException("Invalid " 750 + msg 751 + " (must be a '<init>', '<clinit>' or a valid Java identifier): " 752 + name); 753 } 754 for (int i = 1; i < name.length(); ++i) { 755 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 756 throw new IllegalArgumentException("Invalid " 757 + msg 758 + " (must be '<init>' or '<clinit>' or a valid Java identifier): " 759 + name); 760 } 761 } 762 } 763 764 /** 765 * Checks that the given string is a valid internal class name. 766 * 767 * @param name the string to be checked. 768 * @param msg a message to be used in case of error. 769 */ 770 static void checkInternalName(final String name, final String msg) { 771 checkInternalName(name, 0, -1, msg); 772 } 773 774 /** 775 * Checks that the given substring is a valid internal class name. 776 * 777 * @param name the string to be checked. 778 * @param start index of the first character of the identifier (inclusive). 779 * @param end index of the last character of the identifier (exclusive). -1 780 * is equivalent to <tt>name.length()</tt> if name is not 781 * <tt>null</tt>. 782 * @param msg a message to be used in case of error. 783 */ 784 static void checkInternalName( 785 final String name, 786 final int start, 787 final int end, 788 final String msg) 789 { 790 if (name == null || name.length() == 0) { 791 throw new IllegalArgumentException("Invalid " + msg 792 + " (must not be null or empty)"); 793 } 794 int max = (end == -1 ? name.length() : end); 795 try { 796 int begin = start; 797 int slash; 798 do { 799 slash = name.indexOf('/', begin + 1); 800 if (slash == -1 || slash > max) { 801 slash = max; 802 } 803 checkIdentifier(name, begin, slash, null); 804 begin = slash + 1; 805 } while (slash != max); 806 } catch (IllegalArgumentException ex) { 807 throw new IllegalArgumentException("Invalid " 808 + msg 809 + " (must be a fully qualified class name in internal form): " 810 + name); 811 } 812 } 813 814 /** 815 * Checks that the given string is a valid type descriptor. 816 * 817 * @param desc the string to be checked. 818 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 819 */ 820 static void checkDesc(final String desc, final boolean canBeVoid) { 821 int end = checkDesc(desc, 0, canBeVoid); 822 if (end != desc.length()) { 823 throw new IllegalArgumentException("Invalid descriptor: " + desc); 824 } 825 } 826 827 /** 828 * Checks that a the given substring is a valid type descriptor. 829 * 830 * @param desc the string to be checked. 831 * @param start index of the first character of the identifier (inclusive). 832 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 833 * @return the index of the last character of the type decriptor, plus one. 834 */ 835 static int checkDesc( 836 final String desc, 837 final int start, 838 final boolean canBeVoid) 839 { 840 if (desc == null || start >= desc.length()) { 841 throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); 842 } 843 int index; 844 switch (desc.charAt(start)) { 845 case 'V': 846 if (canBeVoid) { 847 return start + 1; 848 } else { 849 throw new IllegalArgumentException("Invalid descriptor: " 850 + desc); 851 } 852 case 'Z': 853 case 'C': 854 case 'B': 855 case 'S': 856 case 'I': 857 case 'F': 858 case 'J': 859 case 'D': 860 return start + 1; 861 case '[': 862 index = start + 1; 863 while (index < desc.length() && desc.charAt(index) == '[') { 864 ++index; 865 } 866 if (index < desc.length()) { 867 return checkDesc(desc, index, false); 868 } else { 869 throw new IllegalArgumentException("Invalid descriptor: " 870 + desc); 871 } 872 case 'L': 873 index = desc.indexOf(';', start); 874 if (index == -1 || index - start < 2) { 875 throw new IllegalArgumentException("Invalid descriptor: " 876 + desc); 877 } 878 try { 879 checkInternalName(desc, start + 1, index, null); 880 } catch (IllegalArgumentException ex) { 881 throw new IllegalArgumentException("Invalid descriptor: " 882 + desc); 883 } 884 return index + 1; 885 default: 886 throw new IllegalArgumentException("Invalid descriptor: " 887 + desc); 888 } 889 } 890 891 /** 892 * Checks that the given string is a valid method descriptor. 893 * 894 * @param desc the string to be checked. 895 */ 896 static void checkMethodDesc(final String desc) { 897 if (desc == null || desc.length() == 0) { 898 throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); 899 } 900 if (desc.charAt(0) != '(' || desc.length() < 3) { 901 throw new IllegalArgumentException("Invalid descriptor: " + desc); 902 } 903 int start = 1; 904 if (desc.charAt(start) != ')') { 905 do { 906 if (desc.charAt(start) == 'V') { 907 throw new IllegalArgumentException("Invalid descriptor: " 908 + desc); 909 } 910 start = checkDesc(desc, start, false); 911 } while (start < desc.length() && desc.charAt(start) != ')'); 912 } 913 start = checkDesc(desc, start + 1, true); 914 if (start != desc.length()) { 915 throw new IllegalArgumentException("Invalid descriptor: " + desc); 916 } 917 } 918 919 /** 920 * Checks that the given label is not null. This method can also check that 921 * the label has been visited. 922 * 923 * @param label the label to be checked. 924 * @param checkVisited <tt>true</tt> to check that the label has been 925 * visited. 926 * @param msg a message to be used in case of error. 927 */ 928 void checkLabel( 929 final Label label, 930 final boolean checkVisited, 931 final String msg) 932 { 933 if (label == null) { 934 throw new IllegalArgumentException("Invalid " + msg 935 + " (must not be null)"); 936 } 937 if (checkVisited && labels.get(label) == null) { 938 throw new IllegalArgumentException("Invalid " + msg 939 + " (must be visited first)"); 940 } 941 } 942} 943