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 */ 30 package org.objectweb.asm.util; 31 32 import org.objectweb.asm.AnnotationVisitor; 33 import org.objectweb.asm.Label; 34 import org.objectweb.asm.MethodAdapter; 35 import org.objectweb.asm.MethodVisitor; 36 import org.objectweb.asm.Opcodes; 37 import org.objectweb.asm.Attribute; 38 import org.objectweb.asm.Type; 39 40 import 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 */ 54 public 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 */ CheckMethodAdapter(final MethodVisitor cv)310 public CheckMethodAdapter(final MethodVisitor cv) { 311 super(cv); 312 this.labels = new HashMap(); 313 } 314 visitAnnotation( final String desc, final boolean visible)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 visitAnnotationDefault()324 public AnnotationVisitor visitAnnotationDefault() { 325 checkEndMethod(); 326 return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false); 327 } 328 visitParameterAnnotation( final int parameter, final String desc, final boolean visible)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 visitAttribute(final Attribute attr)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 visitCode()349 public void visitCode() { 350 startCode = true; 351 mv.visitCode(); 352 } 353 visitInsn(final int opcode)354 public void visitInsn(final int opcode) { 355 checkStartCode(); 356 checkEndCode(); 357 checkOpcode(opcode, 0); 358 mv.visitInsn(opcode); 359 } 360 visitIntInsn(final int opcode, final int operand)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 visitVarInsn(final int opcode, final int var)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 visitTypeInsn(final int opcode, final String desc)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 visitFieldInsn( final int opcode, final String owner, final String name, final String desc)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 visitMethodInsn( final int opcode, final String owner, final String name, final String desc)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 visitJumpInsn(final int opcode, final Label label)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 visitLabel(final Label label)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 visitLdcInsn(final Object cst)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 visitIincInsn(final int var, final int increment)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 visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label labels[])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 visitLookupSwitchInsn( final Label dflt, final int keys[], final Label labels[])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 visitMultiANewArrayInsn(final String desc, final int dims)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 visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type)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 visitLocalVariable( final String name, final String desc, final String signature, final Label start, final Label end, final int index)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 visitLineNumber(final int line, final Label start)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 visitMaxs(final int maxStack, final int maxLocals)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 visitEnd()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 */ checkStartCode()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 */ checkEndCode()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 */ checkEndMethod()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 */ checkOpcode(final int opcode, final int type)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 */ checkSignedByte(final int value, final String msg)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 */ checkSignedShort(final int value, final String msg)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 */ checkUnsignedShort(final int value, final String msg)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 */ checkConstant(final Object cst)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 */ checkIdentifier(final String name, final String msg)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 */ checkIdentifier( final String name, final int start, final int end, final String msg)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 */ checkMethodIdentifier(final String name, final String msg)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 */ checkInternalName(final String name, final String msg)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 */ checkInternalName( final String name, final int start, final int end, final String msg)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 */ checkDesc(final String desc, final boolean canBeVoid)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 */ checkDesc( final String desc, final int start, final boolean canBeVoid)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 */ checkMethodDesc(final String desc)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 */ checkLabel( final Label label, final boolean checkVisited, final String msg)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