1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 package jdk.internal.org.objectweb.asm.util; 60 61 import java.io.PrintWriter; 62 import java.io.StringWriter; 63 import java.lang.reflect.Field; 64 import java.util.ArrayList; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Set; 70 71 import jdk.internal.org.objectweb.asm.AnnotationVisitor; 72 import jdk.internal.org.objectweb.asm.Attribute; 73 import jdk.internal.org.objectweb.asm.Handle; 74 import jdk.internal.org.objectweb.asm.Label; 75 import jdk.internal.org.objectweb.asm.MethodVisitor; 76 import jdk.internal.org.objectweb.asm.Opcodes; 77 import jdk.internal.org.objectweb.asm.Type; 78 import jdk.internal.org.objectweb.asm.tree.MethodNode; 79 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer; 80 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue; 81 import jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier; 82 83 /** 84 * A {@link MethodVisitor} that checks that its methods are properly used. More 85 * precisely this method adapter checks each instruction individually, i.e., 86 * each visit method checks some preconditions based <i>only</i> on its 87 * arguments - such as the fact that the given opcode is correct for a given 88 * visit method. This adapter can also perform some basic data flow checks (more 89 * precisely those that can be performed without the full class hierarchy - see 90 * {@link jdk.internal.org.objectweb.asm.tree.analysis.BasicVerifier}). For instance in a 91 * method whose signature is <tt>void m ()</tt>, the invalid instruction 92 * IRETURN, or the invalid sequence IADD L2I will be detected if the data flow 93 * checks are enabled. These checks are enabled by using the 94 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)} constructor. 95 * They are not performed if any other constructor is used. 96 * 97 * @author Eric Bruneton 98 */ 99 public class CheckMethodAdapter extends MethodVisitor { 100 101 /** 102 * The class version number. 103 */ 104 public int version; 105 106 /** 107 * <tt>true</tt> if the visitCode method has been called. 108 */ 109 private boolean startCode; 110 111 /** 112 * <tt>true</tt> if the visitMaxs method has been called. 113 */ 114 private boolean endCode; 115 116 /** 117 * <tt>true</tt> if the visitEnd method has been called. 118 */ 119 private boolean endMethod; 120 121 /** 122 * Number of visited instructions. 123 */ 124 private int insnCount; 125 126 /** 127 * The already visited labels. This map associate Integer values to pseudo 128 * code offsets. 129 */ 130 private final Map<Label, Integer> labels; 131 132 /** 133 * The labels used in this method. Every used label must be visited with 134 * visitLabel before the end of the method (i.e. should be in #labels). 135 */ 136 private Set<Label> usedLabels; 137 138 /** 139 * The exception handler ranges. Each pair of list element contains the 140 * start and end labels of an exception handler block. 141 */ 142 private List<Label> handlers; 143 144 /** 145 * Code of the visit method to be used for each opcode. 146 */ 147 private static final int[] TYPE; 148 149 /** 150 * The Label.status field. 151 */ 152 private static Field labelStatusField; 153 154 static { 155 String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD" 156 + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" 157 + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD" 158 + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA"; 159 TYPE = new int[s.length()]; 160 for (int i = 0; i < TYPE.length; ++i) { 161 TYPE[i] = s.charAt(i) - 'A' - 1; 162 } 163 } 164 165 // code to generate the above string 166 // public static void main (String[] args) { 167 // int[] TYPE = new int[] { 168 // 0, //NOP 169 // 0, //ACONST_NULL 170 // 0, //ICONST_M1 171 // 0, //ICONST_0 172 // 0, //ICONST_1 173 // 0, //ICONST_2 174 // 0, //ICONST_3 175 // 0, //ICONST_4 176 // 0, //ICONST_5 177 // 0, //LCONST_0 178 // 0, //LCONST_1 179 // 0, //FCONST_0 180 // 0, //FCONST_1 181 // 0, //FCONST_2 182 // 0, //DCONST_0 183 // 0, //DCONST_1 184 // 1, //BIPUSH 185 // 1, //SIPUSH 186 // 7, //LDC 187 // -1, //LDC_W 188 // -1, //LDC2_W 189 // 2, //ILOAD 190 // 2, //LLOAD 191 // 2, //FLOAD 192 // 2, //DLOAD 193 // 2, //ALOAD 194 // -1, //ILOAD_0 195 // -1, //ILOAD_1 196 // -1, //ILOAD_2 197 // -1, //ILOAD_3 198 // -1, //LLOAD_0 199 // -1, //LLOAD_1 200 // -1, //LLOAD_2 201 // -1, //LLOAD_3 202 // -1, //FLOAD_0 203 // -1, //FLOAD_1 204 // -1, //FLOAD_2 205 // -1, //FLOAD_3 206 // -1, //DLOAD_0 207 // -1, //DLOAD_1 208 // -1, //DLOAD_2 209 // -1, //DLOAD_3 210 // -1, //ALOAD_0 211 // -1, //ALOAD_1 212 // -1, //ALOAD_2 213 // -1, //ALOAD_3 214 // 0, //IALOAD 215 // 0, //LALOAD 216 // 0, //FALOAD 217 // 0, //DALOAD 218 // 0, //AALOAD 219 // 0, //BALOAD 220 // 0, //CALOAD 221 // 0, //SALOAD 222 // 2, //ISTORE 223 // 2, //LSTORE 224 // 2, //FSTORE 225 // 2, //DSTORE 226 // 2, //ASTORE 227 // -1, //ISTORE_0 228 // -1, //ISTORE_1 229 // -1, //ISTORE_2 230 // -1, //ISTORE_3 231 // -1, //LSTORE_0 232 // -1, //LSTORE_1 233 // -1, //LSTORE_2 234 // -1, //LSTORE_3 235 // -1, //FSTORE_0 236 // -1, //FSTORE_1 237 // -1, //FSTORE_2 238 // -1, //FSTORE_3 239 // -1, //DSTORE_0 240 // -1, //DSTORE_1 241 // -1, //DSTORE_2 242 // -1, //DSTORE_3 243 // -1, //ASTORE_0 244 // -1, //ASTORE_1 245 // -1, //ASTORE_2 246 // -1, //ASTORE_3 247 // 0, //IASTORE 248 // 0, //LASTORE 249 // 0, //FASTORE 250 // 0, //DASTORE 251 // 0, //AASTORE 252 // 0, //BASTORE 253 // 0, //CASTORE 254 // 0, //SASTORE 255 // 0, //POP 256 // 0, //POP2 257 // 0, //DUP 258 // 0, //DUP_X1 259 // 0, //DUP_X2 260 // 0, //DUP2 261 // 0, //DUP2_X1 262 // 0, //DUP2_X2 263 // 0, //SWAP 264 // 0, //IADD 265 // 0, //LADD 266 // 0, //FADD 267 // 0, //DADD 268 // 0, //ISUB 269 // 0, //LSUB 270 // 0, //FSUB 271 // 0, //DSUB 272 // 0, //IMUL 273 // 0, //LMUL 274 // 0, //FMUL 275 // 0, //DMUL 276 // 0, //IDIV 277 // 0, //LDIV 278 // 0, //FDIV 279 // 0, //DDIV 280 // 0, //IREM 281 // 0, //LREM 282 // 0, //FREM 283 // 0, //DREM 284 // 0, //INEG 285 // 0, //LNEG 286 // 0, //FNEG 287 // 0, //DNEG 288 // 0, //ISHL 289 // 0, //LSHL 290 // 0, //ISHR 291 // 0, //LSHR 292 // 0, //IUSHR 293 // 0, //LUSHR 294 // 0, //IAND 295 // 0, //LAND 296 // 0, //IOR 297 // 0, //LOR 298 // 0, //IXOR 299 // 0, //LXOR 300 // 8, //IINC 301 // 0, //I2L 302 // 0, //I2F 303 // 0, //I2D 304 // 0, //L2I 305 // 0, //L2F 306 // 0, //L2D 307 // 0, //F2I 308 // 0, //F2L 309 // 0, //F2D 310 // 0, //D2I 311 // 0, //D2L 312 // 0, //D2F 313 // 0, //I2B 314 // 0, //I2C 315 // 0, //I2S 316 // 0, //LCMP 317 // 0, //FCMPL 318 // 0, //FCMPG 319 // 0, //DCMPL 320 // 0, //DCMPG 321 // 6, //IFEQ 322 // 6, //IFNE 323 // 6, //IFLT 324 // 6, //IFGE 325 // 6, //IFGT 326 // 6, //IFLE 327 // 6, //IF_ICMPEQ 328 // 6, //IF_ICMPNE 329 // 6, //IF_ICMPLT 330 // 6, //IF_ICMPGE 331 // 6, //IF_ICMPGT 332 // 6, //IF_ICMPLE 333 // 6, //IF_ACMPEQ 334 // 6, //IF_ACMPNE 335 // 6, //GOTO 336 // 6, //JSR 337 // 2, //RET 338 // 9, //TABLESWITCH 339 // 10, //LOOKUPSWITCH 340 // 0, //IRETURN 341 // 0, //LRETURN 342 // 0, //FRETURN 343 // 0, //DRETURN 344 // 0, //ARETURN 345 // 0, //RETURN 346 // 4, //GETSTATIC 347 // 4, //PUTSTATIC 348 // 4, //GETFIELD 349 // 4, //PUTFIELD 350 // 5, //INVOKEVIRTUAL 351 // 5, //INVOKESPECIAL 352 // 5, //INVOKESTATIC 353 // 5, //INVOKEINTERFACE 354 // -1, //INVOKEDYNAMIC 355 // 3, //NEW 356 // 1, //NEWARRAY 357 // 3, //ANEWARRAY 358 // 0, //ARRAYLENGTH 359 // 0, //ATHROW 360 // 3, //CHECKCAST 361 // 3, //INSTANCEOF 362 // 0, //MONITORENTER 363 // 0, //MONITOREXIT 364 // -1, //WIDE 365 // 11, //MULTIANEWARRAY 366 // 6, //IFNULL 367 // 6, //IFNONNULL 368 // -1, //GOTO_W 369 // -1 //JSR_W 370 // }; 371 // for (int i = 0; i < TYPE.length; ++i) { 372 // System.out.print((char)(TYPE[i] + 1 + 'A')); 373 // } 374 // System.out.println(); 375 // } 376 377 /** 378 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 379 * will not perform any data flow check (see 380 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). 381 * <i>Subclasses must not use this constructor</i>. Instead, they must use 382 * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version. 383 * 384 * @param mv the method visitor to which this adapter must delegate calls. 385 */ 386 public CheckMethodAdapter(final MethodVisitor mv) { 387 this(mv, new HashMap<Label, Integer>()); 388 } 389 390 /** 391 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 392 * will not perform any data flow check (see 393 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). 394 * <i>Subclasses must not use this constructor</i>. Instead, they must use 395 * the {@link #CheckMethodAdapter(int, MethodVisitor, Map)} version. 396 * 397 * @param mv the method visitor to which this adapter must delegate calls. 398 * @param labels a map of already visited labels (in other methods). 399 */ 400 public CheckMethodAdapter( 401 final MethodVisitor mv, 402 final Map<Label, Integer> labels) 403 { 404 this(Opcodes.ASM4, mv, labels); 405 } 406 407 /** 408 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 409 * will not perform any data flow check (see 410 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). 411 * 412 * @param mv the method visitor to which this adapter must delegate calls. 413 * @param labels a map of already visited labels (in other methods). 414 */ 415 protected CheckMethodAdapter( 416 final int api, 417 final MethodVisitor mv, 418 final Map<Label, Integer> labels) 419 { 420 super(api, mv); 421 this.labels = labels; 422 this.usedLabels = new HashSet<Label>(); 423 this.handlers = new ArrayList<Label>(); 424 } 425 426 /** 427 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 428 * will perform basic data flow checks. For instance in a method whose 429 * signature is <tt>void m ()</tt>, the invalid instruction IRETURN, or the 430 * invalid sequence IADD L2I will be detected. 431 * 432 * @param access the method's access flags. 433 * @param name the method's name. 434 * @param desc the method's descriptor (see {@link Type Type}). 435 * @param cmv the method visitor to which this adapter must delegate calls. 436 * @param labels a map of already visited labels (in other methods). 437 */ 438 public CheckMethodAdapter( 439 final int access, 440 final String name, 441 final String desc, 442 final MethodVisitor cmv, 443 final Map<Label, Integer> labels) 444 { 445 this(new MethodNode(access, name, desc, null, null) { 446 @Override 447 public void visitEnd() { 448 Analyzer<BasicValue> a = new Analyzer<BasicValue>(new BasicVerifier()); 449 try { 450 a.analyze("dummy", this); 451 } catch (Exception e) { 452 if (e instanceof IndexOutOfBoundsException 453 && maxLocals == 0 && maxStack == 0) 454 { 455 throw new RuntimeException("Data flow checking option requires valid, non zero maxLocals and maxStack values."); 456 } 457 e.printStackTrace(); 458 StringWriter sw = new StringWriter(); 459 PrintWriter pw = new PrintWriter(sw, true); 460 CheckClassAdapter.printAnalyzerResult(this, a, pw); 461 pw.close(); 462 throw new RuntimeException(e.getMessage() + ' ' 463 + sw.toString()); 464 } 465 accept(cmv); 466 } 467 }, 468 labels); 469 } 470 471 @Override 472 public AnnotationVisitor visitAnnotation( 473 final String desc, 474 final boolean visible) 475 { 476 checkEndMethod(); 477 checkDesc(desc, false); 478 return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); 479 } 480 481 @Override 482 public AnnotationVisitor visitAnnotationDefault() { 483 checkEndMethod(); 484 return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); 485 } 486 487 @Override 488 public AnnotationVisitor visitParameterAnnotation( 489 final int parameter, 490 final String desc, 491 final boolean visible) 492 { 493 checkEndMethod(); 494 checkDesc(desc, false); 495 return new CheckAnnotationAdapter(super.visitParameterAnnotation(parameter, 496 desc, 497 visible)); 498 } 499 500 @Override 501 public void visitAttribute(final Attribute attr) { 502 checkEndMethod(); 503 if (attr == null) { 504 throw new IllegalArgumentException("Invalid attribute (must not be null)"); 505 } 506 super.visitAttribute(attr); 507 } 508 509 @Override 510 public void visitCode() { 511 startCode = true; 512 super.visitCode(); 513 } 514 515 @Override 516 public void visitFrame( 517 final int type, 518 final int nLocal, 519 final Object[] local, 520 final int nStack, 521 final Object[] stack) 522 { 523 int mLocal; 524 int mStack; 525 switch (type) { 526 case Opcodes.F_NEW: 527 case Opcodes.F_FULL: 528 mLocal = Integer.MAX_VALUE; 529 mStack = Integer.MAX_VALUE; 530 break; 531 532 case Opcodes.F_SAME: 533 mLocal = 0; 534 mStack = 0; 535 break; 536 537 case Opcodes.F_SAME1: 538 mLocal = 0; 539 mStack = 1; 540 break; 541 542 case Opcodes.F_APPEND: 543 case Opcodes.F_CHOP: 544 mLocal = 3; 545 mStack = 0; 546 break; 547 548 default: 549 throw new IllegalArgumentException("Invalid frame type " + type); 550 } 551 552 if (nLocal > mLocal) { 553 throw new IllegalArgumentException("Invalid nLocal=" + nLocal 554 + " for frame type " + type); 555 } 556 if (nStack > mStack) { 557 throw new IllegalArgumentException("Invalid nStack=" + nStack 558 + " for frame type " + type); 559 } 560 561 if (type != Opcodes.F_CHOP) { 562 if (nLocal > 0 && (local == null || local.length < nLocal)) { 563 throw new IllegalArgumentException("Array local[] is shorter than nLocal"); 564 } 565 for (int i = 0; i < nLocal; ++i) { 566 checkFrameValue(local[i]); 567 } 568 } 569 if (nStack > 0 && (stack == null || stack.length < nStack)) { 570 throw new IllegalArgumentException("Array stack[] is shorter than nStack"); 571 } 572 for (int i = 0; i < nStack; ++i) { 573 checkFrameValue(stack[i]); 574 } 575 576 super.visitFrame(type, nLocal, local, nStack, stack); 577 } 578 579 @Override 580 public void visitInsn(final int opcode) { 581 checkStartCode(); 582 checkEndCode(); 583 checkOpcode(opcode, 0); 584 super.visitInsn(opcode); 585 ++insnCount; 586 } 587 588 @Override 589 public void visitIntInsn(final int opcode, final int operand) { 590 checkStartCode(); 591 checkEndCode(); 592 checkOpcode(opcode, 1); 593 switch (opcode) { 594 case Opcodes.BIPUSH: 595 checkSignedByte(operand, "Invalid operand"); 596 break; 597 case Opcodes.SIPUSH: 598 checkSignedShort(operand, "Invalid operand"); 599 break; 600 // case Constants.NEWARRAY: 601 default: 602 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { 603 throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): " 604 + operand); 605 } 606 } 607 super.visitIntInsn(opcode, operand); 608 ++insnCount; 609 } 610 611 @Override 612 public void visitVarInsn(final int opcode, final int var) { 613 checkStartCode(); 614 checkEndCode(); 615 checkOpcode(opcode, 2); 616 checkUnsignedShort(var, "Invalid variable index"); 617 super.visitVarInsn(opcode, var); 618 ++insnCount; 619 } 620 621 @Override 622 public void visitTypeInsn(final int opcode, final String type) { 623 checkStartCode(); 624 checkEndCode(); 625 checkOpcode(opcode, 3); 626 checkInternalName(type, "type"); 627 if (opcode == Opcodes.NEW && type.charAt(0) == '[') { 628 throw new IllegalArgumentException("NEW cannot be used to create arrays: " 629 + type); 630 } 631 super.visitTypeInsn(opcode, type); 632 ++insnCount; 633 } 634 635 @Override 636 public void visitFieldInsn( 637 final int opcode, 638 final String owner, 639 final String name, 640 final String desc) 641 { 642 checkStartCode(); 643 checkEndCode(); 644 checkOpcode(opcode, 4); 645 checkInternalName(owner, "owner"); 646 checkUnqualifiedName(version, name, "name"); 647 checkDesc(desc, false); 648 super.visitFieldInsn(opcode, owner, name, desc); 649 ++insnCount; 650 } 651 652 @Override 653 public void visitMethodInsn( 654 final int opcode, 655 final String owner, 656 final String name, 657 final String desc) 658 { 659 checkStartCode(); 660 checkEndCode(); 661 checkOpcode(opcode, 5); 662 checkMethodIdentifier(version, name, "name"); 663 checkInternalName(owner, "owner"); 664 checkMethodDesc(desc); 665 super.visitMethodInsn(opcode, owner, name, desc); 666 ++insnCount; 667 } 668 669 @Override 670 public void visitInvokeDynamicInsn( 671 String name, 672 String desc, 673 Handle bsm, 674 Object... bsmArgs) 675 { 676 checkStartCode(); 677 checkEndCode(); 678 checkMethodIdentifier(version, name, "name"); 679 checkMethodDesc(desc); 680 if (bsm.getTag() != Opcodes.H_INVOKESTATIC 681 && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) 682 { 683 throw new IllegalArgumentException("invalid handle tag " 684 + bsm.getTag()); 685 } 686 for (int i = 0; i < bsmArgs.length; i++) { 687 checkLDCConstant(bsmArgs[i]); 688 } 689 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 690 ++insnCount; 691 } 692 693 @Override 694 public void visitJumpInsn(final int opcode, final Label label) { 695 checkStartCode(); 696 checkEndCode(); 697 checkOpcode(opcode, 6); 698 checkLabel(label, false, "label"); 699 checkNonDebugLabel(label); 700 super.visitJumpInsn(opcode, label); 701 usedLabels.add(label); 702 ++insnCount; 703 } 704 705 @Override 706 public void visitLabel(final Label label) { 707 checkStartCode(); 708 checkEndCode(); 709 checkLabel(label, false, "label"); 710 if (labels.get(label) != null) { 711 throw new IllegalArgumentException("Already visited label"); 712 } 713 labels.put(label, new Integer(insnCount)); 714 super.visitLabel(label); 715 } 716 717 @Override 718 public void visitLdcInsn(final Object cst) { 719 checkStartCode(); 720 checkEndCode(); 721 checkLDCConstant(cst); 722 super.visitLdcInsn(cst); 723 ++insnCount; 724 } 725 726 @Override 727 public void visitIincInsn(final int var, final int increment) { 728 checkStartCode(); 729 checkEndCode(); 730 checkUnsignedShort(var, "Invalid variable index"); 731 checkSignedShort(increment, "Invalid increment"); 732 super.visitIincInsn(var, increment); 733 ++insnCount; 734 } 735 736 @Override 737 public void visitTableSwitchInsn( 738 final int min, 739 final int max, 740 final Label dflt, 741 final Label... labels) 742 { 743 checkStartCode(); 744 checkEndCode(); 745 if (max < min) { 746 throw new IllegalArgumentException("Max = " + max 747 + " must be greater than or equal to min = " + min); 748 } 749 checkLabel(dflt, false, "default label"); 750 checkNonDebugLabel(dflt); 751 if (labels == null || labels.length != max - min + 1) { 752 throw new IllegalArgumentException("There must be max - min + 1 labels"); 753 } 754 for (int i = 0; i < labels.length; ++i) { 755 checkLabel(labels[i], false, "label at index " + i); 756 checkNonDebugLabel(labels[i]); 757 } 758 super.visitTableSwitchInsn(min, max, dflt, labels); 759 for (int i = 0; i < labels.length; ++i) { 760 usedLabels.add(labels[i]); 761 } 762 ++insnCount; 763 } 764 765 @Override 766 public void visitLookupSwitchInsn( 767 final Label dflt, 768 final int[] keys, 769 final Label[] labels) 770 { 771 checkEndCode(); 772 checkStartCode(); 773 checkLabel(dflt, false, "default label"); 774 checkNonDebugLabel(dflt); 775 if (keys == null || labels == null || keys.length != labels.length) { 776 throw new IllegalArgumentException("There must be the same number of keys and labels"); 777 } 778 for (int i = 0; i < labels.length; ++i) { 779 checkLabel(labels[i], false, "label at index " + i); 780 checkNonDebugLabel(labels[i]); 781 } 782 super.visitLookupSwitchInsn(dflt, keys, labels); 783 usedLabels.add(dflt); 784 for (int i = 0; i < labels.length; ++i) { 785 usedLabels.add(labels[i]); 786 } 787 ++insnCount; 788 } 789 790 @Override 791 public void visitMultiANewArrayInsn(final String desc, final int dims) { 792 checkStartCode(); 793 checkEndCode(); 794 checkDesc(desc, false); 795 if (desc.charAt(0) != '[') { 796 throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): " 797 + desc); 798 } 799 if (dims < 1) { 800 throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): " 801 + dims); 802 } 803 if (dims > desc.lastIndexOf('[') + 1) { 804 throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): " 805 + dims); 806 } 807 super.visitMultiANewArrayInsn(desc, dims); 808 ++insnCount; 809 } 810 811 @Override 812 public void visitTryCatchBlock( 813 final Label start, 814 final Label end, 815 final Label handler, 816 final String type) 817 { 818 checkStartCode(); 819 checkEndCode(); 820 checkLabel(start, false, "start label"); 821 checkLabel(end, false, "end label"); 822 checkLabel(handler, false, "handler label"); 823 checkNonDebugLabel(start); 824 checkNonDebugLabel(end); 825 checkNonDebugLabel(handler); 826 if (labels.get(start) != null || labels.get(end) != null 827 || labels.get(handler) != null) 828 { 829 throw new IllegalStateException("Try catch blocks must be visited before their labels"); 830 } 831 if (type != null) { 832 checkInternalName(type, "type"); 833 } 834 super.visitTryCatchBlock(start, end, handler, type); 835 handlers.add(start); 836 handlers.add(end); 837 } 838 839 @Override 840 public void visitLocalVariable( 841 final String name, 842 final String desc, 843 final String signature, 844 final Label start, 845 final Label end, 846 final int index) 847 { 848 checkStartCode(); 849 checkEndCode(); 850 checkUnqualifiedName(version, name, "name"); 851 checkDesc(desc, false); 852 checkLabel(start, true, "start label"); 853 checkLabel(end, true, "end label"); 854 checkUnsignedShort(index, "Invalid variable index"); 855 int s = labels.get(start).intValue(); 856 int e = labels.get(end).intValue(); 857 if (e < s) { 858 throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)"); 859 } 860 super.visitLocalVariable(name, desc, signature, start, end, index); 861 } 862 863 @Override 864 public void visitLineNumber(final int line, final Label start) { 865 checkStartCode(); 866 checkEndCode(); 867 checkUnsignedShort(line, "Invalid line number"); 868 checkLabel(start, true, "start label"); 869 super.visitLineNumber(line, start); 870 } 871 872 @Override 873 public void visitMaxs(final int maxStack, final int maxLocals) { 874 checkStartCode(); 875 checkEndCode(); 876 endCode = true; 877 for (Label l : usedLabels) { 878 if (labels.get(l) == null) { 879 throw new IllegalStateException("Undefined label used"); 880 } 881 } 882 for (int i = 0; i < handlers.size(); ) { 883 Integer start = labels.get(handlers.get(i++)); 884 Integer end = labels.get(handlers.get(i++)); 885 if (start == null || end == null) { 886 throw new IllegalStateException("Undefined try catch block labels"); 887 } 888 if (end.intValue() <= start.intValue()) { 889 throw new IllegalStateException("Emty try catch block handler range"); 890 } 891 } 892 checkUnsignedShort(maxStack, "Invalid max stack"); 893 checkUnsignedShort(maxLocals, "Invalid max locals"); 894 super.visitMaxs(maxStack, maxLocals); 895 } 896 897 @Override 898 public void visitEnd() { 899 checkEndMethod(); 900 endMethod = true; 901 super.visitEnd(); 902 } 903 904 // ------------------------------------------------------------------------- 905 906 /** 907 * Checks that the visitCode method has been called. 908 */ 909 void checkStartCode() { 910 if (!startCode) { 911 throw new IllegalStateException("Cannot visit instructions before visitCode has been called."); 912 } 913 } 914 915 /** 916 * Checks that the visitMaxs method has not been called. 917 */ 918 void checkEndCode() { 919 if (endCode) { 920 throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); 921 } 922 } 923 924 /** 925 * Checks that the visitEnd method has not been called. 926 */ 927 void checkEndMethod() { 928 if (endMethod) { 929 throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); 930 } 931 } 932 933 /** 934 * Checks a stack frame value. 935 * 936 * @param value the value to be checked. 937 */ 938 void checkFrameValue(final Object value) { 939 if (value == Opcodes.TOP || value == Opcodes.INTEGER 940 || value == Opcodes.FLOAT || value == Opcodes.LONG 941 || value == Opcodes.DOUBLE || value == Opcodes.NULL 942 || value == Opcodes.UNINITIALIZED_THIS) 943 { 944 return; 945 } 946 if (value instanceof String) { 947 checkInternalName((String) value, "Invalid stack frame value"); 948 return; 949 } 950 if (!(value instanceof Label)) { 951 throw new IllegalArgumentException("Invalid stack frame value: " 952 + value); 953 } else { 954 usedLabels.add((Label) value); 955 } 956 } 957 958 /** 959 * Checks that the type of the given opcode is equal to the given type. 960 * 961 * @param opcode the opcode to be checked. 962 * @param type the expected opcode type. 963 */ 964 static void checkOpcode(final int opcode, final int type) { 965 if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { 966 throw new IllegalArgumentException("Invalid opcode: " + opcode); 967 } 968 } 969 970 /** 971 * Checks that the given value is a signed byte. 972 * 973 * @param value the value to be checked. 974 * @param msg an message to be used in case of error. 975 */ 976 static void checkSignedByte(final int value, final String msg) { 977 if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { 978 throw new IllegalArgumentException(msg 979 + " (must be a signed byte): " + value); 980 } 981 } 982 983 /** 984 * Checks that the given value is a signed short. 985 * 986 * @param value the value to be checked. 987 * @param msg an message to be used in case of error. 988 */ 989 static void checkSignedShort(final int value, final String msg) { 990 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 991 throw new IllegalArgumentException(msg 992 + " (must be a signed short): " + value); 993 } 994 } 995 996 /** 997 * Checks that the given value is an unsigned short. 998 * 999 * @param value the value to be checked. 1000 * @param msg an message to be used in case of error. 1001 */ 1002 static void checkUnsignedShort(final int value, final String msg) { 1003 if (value < 0 || value > 65535) { 1004 throw new IllegalArgumentException(msg 1005 + " (must be an unsigned short): " + value); 1006 } 1007 } 1008 1009 /** 1010 * Checks that the given value is an {@link Integer}, a{@link Float}, a 1011 * {@link Long}, a {@link Double} or a {@link String}. 1012 * 1013 * @param cst the value to be checked. 1014 */ 1015 static void checkConstant(final Object cst) { 1016 if (!(cst instanceof Integer) && !(cst instanceof Float) 1017 && !(cst instanceof Long) && !(cst instanceof Double) 1018 && !(cst instanceof String)) 1019 { 1020 throw new IllegalArgumentException("Invalid constant: " + cst); 1021 } 1022 } 1023 1024 void checkLDCConstant(final Object cst) { 1025 if (cst instanceof Type) { 1026 int s = ((Type) cst).getSort(); 1027 if (s != Type.OBJECT && s != Type.ARRAY && s != Type.METHOD) { 1028 throw new IllegalArgumentException("Illegal LDC constant value"); 1029 } 1030 if (s != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) { 1031 throw new IllegalArgumentException("ldc of a constant class requires at least version 1.5"); 1032 } 1033 if (s == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) { 1034 throw new IllegalArgumentException("ldc of a method type requires at least version 1.7"); 1035 } 1036 } else if (cst instanceof Handle) { 1037 if ((version & 0xFFFF) < Opcodes.V1_7) { 1038 throw new IllegalArgumentException("ldc of a handle requires at least version 1.7"); 1039 } 1040 int tag = ((Handle) cst).getTag(); 1041 if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) { 1042 throw new IllegalArgumentException("invalid handle tag " 1043 + tag); 1044 } 1045 } else { 1046 checkConstant(cst); 1047 } 1048 } 1049 1050 /** 1051 * Checks that the given string is a valid unqualified name. 1052 * 1053 * @param version the class version. 1054 * @param name the string to be checked. 1055 * @param msg a message to be used in case of error. 1056 */ 1057 static void checkUnqualifiedName( 1058 int version, 1059 final String name, 1060 final String msg) 1061 { 1062 if ((version & 0xFFFF) < Opcodes.V1_5) { 1063 checkIdentifier(name, msg); 1064 } else { 1065 for (int i = 0; i < name.length(); ++i) { 1066 if (".;[/".indexOf(name.charAt(i)) != -1) { 1067 throw new IllegalArgumentException("Invalid " + msg 1068 + " (must be a valid unqualified name): " + name); 1069 } 1070 } 1071 } 1072 } 1073 1074 /** 1075 * Checks that the given string is a valid Java identifier. 1076 * 1077 * @param name the string to be checked. 1078 * @param msg a message to be used in case of error. 1079 */ 1080 static void checkIdentifier(final String name, final String msg) { 1081 checkIdentifier(name, 0, -1, msg); 1082 } 1083 1084 /** 1085 * Checks that the given substring is a valid Java identifier. 1086 * 1087 * @param name the string to be checked. 1088 * @param start index of the first character of the identifier (inclusive). 1089 * @param end index of the last character of the identifier (exclusive). -1 1090 * is equivalent to <tt>name.length()</tt> if name is not 1091 * <tt>null</tt>. 1092 * @param msg a message to be used in case of error. 1093 */ 1094 static void checkIdentifier( 1095 final String name, 1096 final int start, 1097 final int end, 1098 final String msg) 1099 { 1100 if (name == null || (end == -1 ? name.length() <= start : end <= start)) 1101 { 1102 throw new IllegalArgumentException("Invalid " + msg 1103 + " (must not be null or empty)"); 1104 } 1105 if (!Character.isJavaIdentifierStart(name.charAt(start))) { 1106 throw new IllegalArgumentException("Invalid " + msg 1107 + " (must be a valid Java identifier): " + name); 1108 } 1109 int max = end == -1 ? name.length() : end; 1110 for (int i = start + 1; i < max; ++i) { 1111 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 1112 throw new IllegalArgumentException("Invalid " + msg 1113 + " (must be a valid Java identifier): " + name); 1114 } 1115 } 1116 } 1117 1118 /** 1119 * Checks that the given string is a valid Java identifier or is equal to 1120 * '<init>' or '<clinit>'. 1121 * 1122 * @param version the class version. 1123 * @param name the string to be checked. 1124 * @param msg a message to be used in case of error. 1125 */ 1126 static void checkMethodIdentifier( 1127 int version, 1128 final String name, 1129 final String msg) 1130 { 1131 if (name == null || name.length() == 0) { 1132 throw new IllegalArgumentException("Invalid " + msg 1133 + " (must not be null or empty)"); 1134 } 1135 if ("<init>".equals(name) || "<clinit>".equals(name)) { 1136 return; 1137 } 1138 if ((version & 0xFFFF) >= Opcodes.V1_5) { 1139 for (int i = 0; i < name.length(); ++i) { 1140 if (".;[/<>".indexOf(name.charAt(i)) != -1) { 1141 throw new IllegalArgumentException("Invalid " + msg 1142 + " (must be a valid unqualified name): " + name); 1143 } 1144 } 1145 return; 1146 } 1147 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 1148 throw new IllegalArgumentException("Invalid " 1149 + msg 1150 + " (must be a '<init>', '<clinit>' or a valid Java identifier): " 1151 + name); 1152 } 1153 for (int i = 1; i < name.length(); ++i) { 1154 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 1155 throw new IllegalArgumentException("Invalid " 1156 + msg 1157 + " (must be '<init>' or '<clinit>' or a valid Java identifier): " 1158 + name); 1159 } 1160 } 1161 } 1162 1163 /** 1164 * Checks that the given string is a valid internal class name. 1165 * 1166 * @param name the string to be checked. 1167 * @param msg a message to be used in case of error. 1168 */ 1169 static void checkInternalName(final String name, final String msg) { 1170 if (name == null || name.length() == 0) { 1171 throw new IllegalArgumentException("Invalid " + msg 1172 + " (must not be null or empty)"); 1173 } 1174 if (name.charAt(0) == '[') { 1175 checkDesc(name, false); 1176 } else { 1177 checkInternalName(name, 0, -1, msg); 1178 } 1179 } 1180 1181 /** 1182 * Checks that the given substring is a valid internal class name. 1183 * 1184 * @param name the string to be checked. 1185 * @param start index of the first character of the identifier (inclusive). 1186 * @param end index of the last character of the identifier (exclusive). -1 1187 * is equivalent to <tt>name.length()</tt> if name is not 1188 * <tt>null</tt>. 1189 * @param msg a message to be used in case of error. 1190 */ 1191 static void checkInternalName( 1192 final String name, 1193 final int start, 1194 final int end, 1195 final String msg) 1196 { 1197 int max = end == -1 ? name.length() : end; 1198 try { 1199 int begin = start; 1200 int slash; 1201 do { 1202 slash = name.indexOf('/', begin + 1); 1203 if (slash == -1 || slash > max) { 1204 slash = max; 1205 } 1206 checkIdentifier(name, begin, slash, null); 1207 begin = slash + 1; 1208 } while (slash != max); 1209 } catch (IllegalArgumentException _) { 1210 throw new IllegalArgumentException("Invalid " 1211 + msg 1212 + " (must be a fully qualified class name in internal form): " 1213 + name); 1214 } 1215 } 1216 1217 /** 1218 * Checks that the given string is a valid type descriptor. 1219 * 1220 * @param desc the string to be checked. 1221 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 1222 */ 1223 static void checkDesc(final String desc, final boolean canBeVoid) { 1224 int end = checkDesc(desc, 0, canBeVoid); 1225 if (end != desc.length()) { 1226 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1227 } 1228 } 1229 1230 /** 1231 * Checks that a the given substring is a valid type descriptor. 1232 * 1233 * @param desc the string to be checked. 1234 * @param start index of the first character of the identifier (inclusive). 1235 * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. 1236 * @return the index of the last character of the type decriptor, plus one. 1237 */ 1238 static int checkDesc( 1239 final String desc, 1240 final int start, 1241 final boolean canBeVoid) 1242 { 1243 if (desc == null || start >= desc.length()) { 1244 throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); 1245 } 1246 int index; 1247 switch (desc.charAt(start)) { 1248 case 'V': 1249 if (canBeVoid) { 1250 return start + 1; 1251 } else { 1252 throw new IllegalArgumentException("Invalid descriptor: " 1253 + desc); 1254 } 1255 case 'Z': 1256 case 'C': 1257 case 'B': 1258 case 'S': 1259 case 'I': 1260 case 'F': 1261 case 'J': 1262 case 'D': 1263 return start + 1; 1264 case '[': 1265 index = start + 1; 1266 while (index < desc.length() && desc.charAt(index) == '[') { 1267 ++index; 1268 } 1269 if (index < desc.length()) { 1270 return checkDesc(desc, index, false); 1271 } else { 1272 throw new IllegalArgumentException("Invalid descriptor: " 1273 + desc); 1274 } 1275 case 'L': 1276 index = desc.indexOf(';', start); 1277 if (index == -1 || index - start < 2) { 1278 throw new IllegalArgumentException("Invalid descriptor: " 1279 + desc); 1280 } 1281 try { 1282 checkInternalName(desc, start + 1, index, null); 1283 } catch (IllegalArgumentException _) { 1284 throw new IllegalArgumentException("Invalid descriptor: " 1285 + desc); 1286 } 1287 return index + 1; 1288 default: 1289 throw new IllegalArgumentException("Invalid descriptor: " 1290 + desc); 1291 } 1292 } 1293 1294 /** 1295 * Checks that the given string is a valid method descriptor. 1296 * 1297 * @param desc the string to be checked. 1298 */ 1299 static void checkMethodDesc(final String desc) { 1300 if (desc == null || desc.length() == 0) { 1301 throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); 1302 } 1303 if (desc.charAt(0) != '(' || desc.length() < 3) { 1304 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1305 } 1306 int start = 1; 1307 if (desc.charAt(start) != ')') { 1308 do { 1309 if (desc.charAt(start) == 'V') { 1310 throw new IllegalArgumentException("Invalid descriptor: " 1311 + desc); 1312 } 1313 start = checkDesc(desc, start, false); 1314 } while (start < desc.length() && desc.charAt(start) != ')'); 1315 } 1316 start = checkDesc(desc, start + 1, true); 1317 if (start != desc.length()) { 1318 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1319 } 1320 } 1321 1322 /** 1323 * Checks a class signature. 1324 * 1325 * @param signature a string containing the signature that must be checked. 1326 */ 1327 static void checkClassSignature(final String signature) { 1328 // ClassSignature: 1329 // FormalTypeParameters? ClassTypeSignature ClassTypeSignature* 1330 1331 int pos = 0; 1332 if (getChar(signature, 0) == '<') { 1333 pos = checkFormalTypeParameters(signature, pos); 1334 } 1335 pos = checkClassTypeSignature(signature, pos); 1336 while (getChar(signature, pos) == 'L') { 1337 pos = checkClassTypeSignature(signature, pos); 1338 } 1339 if (pos != signature.length()) { 1340 throw new IllegalArgumentException(signature + ": error at index " 1341 + pos); 1342 } 1343 } 1344 1345 /** 1346 * Checks a method signature. 1347 * 1348 * @param signature a string containing the signature that must be checked. 1349 */ 1350 static void checkMethodSignature(final String signature) { 1351 // MethodTypeSignature: 1352 // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) ( 1353 // ^ClassTypeSignature | ^TypeVariableSignature )* 1354 1355 int pos = 0; 1356 if (getChar(signature, 0) == '<') { 1357 pos = checkFormalTypeParameters(signature, pos); 1358 } 1359 pos = checkChar('(', signature, pos); 1360 while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) { 1361 pos = checkTypeSignature(signature, pos); 1362 } 1363 pos = checkChar(')', signature, pos); 1364 if (getChar(signature, pos) == 'V') { 1365 ++pos; 1366 } else { 1367 pos = checkTypeSignature(signature, pos); 1368 } 1369 while (getChar(signature, pos) == '^') { 1370 ++pos; 1371 if (getChar(signature, pos) == 'L') { 1372 pos = checkClassTypeSignature(signature, pos); 1373 } else { 1374 pos = checkTypeVariableSignature(signature, pos); 1375 } 1376 } 1377 if (pos != signature.length()) { 1378 throw new IllegalArgumentException(signature + ": error at index " 1379 + pos); 1380 } 1381 } 1382 1383 /** 1384 * Checks a field signature. 1385 * 1386 * @param signature a string containing the signature that must be checked. 1387 */ 1388 static void checkFieldSignature(final String signature) { 1389 int pos = checkFieldTypeSignature(signature, 0); 1390 if (pos != signature.length()) { 1391 throw new IllegalArgumentException(signature + ": error at index " 1392 + pos); 1393 } 1394 } 1395 1396 /** 1397 * Checks the formal type parameters of a class or method signature. 1398 * 1399 * @param signature a string containing the signature that must be checked. 1400 * @param pos index of first character to be checked. 1401 * @return the index of the first character after the checked part. 1402 */ 1403 private static int checkFormalTypeParameters(final String signature, int pos) 1404 { 1405 // FormalTypeParameters: 1406 // < FormalTypeParameter+ > 1407 1408 pos = checkChar('<', signature, pos); 1409 pos = checkFormalTypeParameter(signature, pos); 1410 while (getChar(signature, pos) != '>') { 1411 pos = checkFormalTypeParameter(signature, pos); 1412 } 1413 return pos + 1; 1414 } 1415 1416 /** 1417 * Checks a formal type parameter of a class or method signature. 1418 * 1419 * @param signature a string containing the signature that must be checked. 1420 * @param pos index of first character to be checked. 1421 * @return the index of the first character after the checked part. 1422 */ 1423 private static int checkFormalTypeParameter(final String signature, int pos) 1424 { 1425 // FormalTypeParameter: 1426 // Identifier : FieldTypeSignature? (: FieldTypeSignature)* 1427 1428 pos = checkIdentifier(signature, pos); 1429 pos = checkChar(':', signature, pos); 1430 if ("L[T".indexOf(getChar(signature, pos)) != -1) { 1431 pos = checkFieldTypeSignature(signature, pos); 1432 } 1433 while (getChar(signature, pos) == ':') { 1434 pos = checkFieldTypeSignature(signature, pos + 1); 1435 } 1436 return pos; 1437 } 1438 1439 /** 1440 * Checks a field type signature. 1441 * 1442 * @param signature a string containing the signature that must be checked. 1443 * @param pos index of first character to be checked. 1444 * @return the index of the first character after the checked part. 1445 */ 1446 private static int checkFieldTypeSignature(final String signature, int pos) 1447 { 1448 // FieldTypeSignature: 1449 // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature 1450 // 1451 // ArrayTypeSignature: 1452 // [ TypeSignature 1453 1454 switch (getChar(signature, pos)) { 1455 case 'L': 1456 return checkClassTypeSignature(signature, pos); 1457 case '[': 1458 return checkTypeSignature(signature, pos + 1); 1459 default: 1460 return checkTypeVariableSignature(signature, pos); 1461 } 1462 } 1463 1464 /** 1465 * Checks a class type signature. 1466 * 1467 * @param signature a string containing the signature that must be checked. 1468 * @param pos index of first character to be checked. 1469 * @return the index of the first character after the checked part. 1470 */ 1471 private static int checkClassTypeSignature(final String signature, int pos) 1472 { 1473 // ClassTypeSignature: 1474 // L Identifier ( / Identifier )* TypeArguments? ( . Identifier 1475 // TypeArguments? )* ; 1476 1477 pos = checkChar('L', signature, pos); 1478 pos = checkIdentifier(signature, pos); 1479 while (getChar(signature, pos) == '/') { 1480 pos = checkIdentifier(signature, pos + 1); 1481 } 1482 if (getChar(signature, pos) == '<') { 1483 pos = checkTypeArguments(signature, pos); 1484 } 1485 while (getChar(signature, pos) == '.') { 1486 pos = checkIdentifier(signature, pos + 1); 1487 if (getChar(signature, pos) == '<') { 1488 pos = checkTypeArguments(signature, pos); 1489 } 1490 } 1491 return checkChar(';', signature, pos); 1492 } 1493 1494 /** 1495 * Checks the type arguments in a class type signature. 1496 * 1497 * @param signature a string containing the signature that must be checked. 1498 * @param pos index of first character to be checked. 1499 * @return the index of the first character after the checked part. 1500 */ 1501 private static int checkTypeArguments(final String signature, int pos) { 1502 // TypeArguments: 1503 // < TypeArgument+ > 1504 1505 pos = checkChar('<', signature, pos); 1506 pos = checkTypeArgument(signature, pos); 1507 while (getChar(signature, pos) != '>') { 1508 pos = checkTypeArgument(signature, pos); 1509 } 1510 return pos + 1; 1511 } 1512 1513 /** 1514 * Checks a type argument in a class type signature. 1515 * 1516 * @param signature a string containing the signature that must be checked. 1517 * @param pos index of first character to be checked. 1518 * @return the index of the first character after the checked part. 1519 */ 1520 private static int checkTypeArgument(final String signature, int pos) { 1521 // TypeArgument: 1522 // * | ( ( + | - )? FieldTypeSignature ) 1523 1524 char c = getChar(signature, pos); 1525 if (c == '*') { 1526 return pos + 1; 1527 } else if (c == '+' || c == '-') { 1528 pos++; 1529 } 1530 return checkFieldTypeSignature(signature, pos); 1531 } 1532 1533 /** 1534 * Checks a type variable signature. 1535 * 1536 * @param signature a string containing the signature that must be checked. 1537 * @param pos index of first character to be checked. 1538 * @return the index of the first character after the checked part. 1539 */ 1540 private static int checkTypeVariableSignature( 1541 final String signature, 1542 int pos) 1543 { 1544 // TypeVariableSignature: 1545 // T Identifier ; 1546 1547 pos = checkChar('T', signature, pos); 1548 pos = checkIdentifier(signature, pos); 1549 return checkChar(';', signature, pos); 1550 } 1551 1552 /** 1553 * Checks a type signature. 1554 * 1555 * @param signature a string containing the signature that must be checked. 1556 * @param pos index of first character to be checked. 1557 * @return the index of the first character after the checked part. 1558 */ 1559 private static int checkTypeSignature(final String signature, int pos) { 1560 // TypeSignature: 1561 // Z | C | B | S | I | F | J | D | FieldTypeSignature 1562 1563 switch (getChar(signature, pos)) { 1564 case 'Z': 1565 case 'C': 1566 case 'B': 1567 case 'S': 1568 case 'I': 1569 case 'F': 1570 case 'J': 1571 case 'D': 1572 return pos + 1; 1573 default: 1574 return checkFieldTypeSignature(signature, pos); 1575 } 1576 } 1577 1578 /** 1579 * Checks an identifier. 1580 * 1581 * @param signature a string containing the signature that must be checked. 1582 * @param pos index of first character to be checked. 1583 * @return the index of the first character after the checked part. 1584 */ 1585 private static int checkIdentifier(final String signature, int pos) { 1586 if (!Character.isJavaIdentifierStart(getChar(signature, pos))) { 1587 throw new IllegalArgumentException(signature 1588 + ": identifier expected at index " + pos); 1589 } 1590 ++pos; 1591 while (Character.isJavaIdentifierPart(getChar(signature, pos))) { 1592 ++pos; 1593 } 1594 return pos; 1595 } 1596 1597 /** 1598 * Checks a single character. 1599 * 1600 * @param signature a string containing the signature that must be checked. 1601 * @param pos index of first character to be checked. 1602 * @return the index of the first character after the checked part. 1603 */ 1604 private static int checkChar(final char c, final String signature, int pos) 1605 { 1606 if (getChar(signature, pos) == c) { 1607 return pos + 1; 1608 } 1609 throw new IllegalArgumentException(signature + ": '" + c 1610 + "' expected at index " + pos); 1611 } 1612 1613 /** 1614 * Returns the signature car at the given index. 1615 * 1616 * @param signature a signature. 1617 * @param pos an index in signature. 1618 * @return the character at the given index, or 0 if there is no such 1619 * character. 1620 */ 1621 private static char getChar(final String signature, int pos) { 1622 return pos < signature.length() ? signature.charAt(pos) : (char) 0; 1623 } 1624 1625 /** 1626 * Checks that the given label is not null. This method can also check that 1627 * the label has been visited. 1628 * 1629 * @param label the label to be checked. 1630 * @param checkVisited <tt>true</tt> to check that the label has been 1631 * visited. 1632 * @param msg a message to be used in case of error. 1633 */ 1634 void checkLabel( 1635 final Label label, 1636 final boolean checkVisited, 1637 final String msg) 1638 { 1639 if (label == null) { 1640 throw new IllegalArgumentException("Invalid " + msg 1641 + " (must not be null)"); 1642 } 1643 if (checkVisited && labels.get(label) == null) { 1644 throw new IllegalArgumentException("Invalid " + msg 1645 + " (must be visited first)"); 1646 } 1647 } 1648 1649 /** 1650 * Checks that the given label is not a label used only for debug purposes. 1651 * 1652 * @param label the label to be checked. 1653 */ 1654 private static void checkNonDebugLabel(final Label label) { 1655 Field f = getLabelStatusField(); 1656 int status = 0; 1657 try { 1658 status = f == null ? 0 : ((Integer) f.get(label)).intValue(); 1659 } catch (IllegalAccessException e) { 1660 throw new Error("Internal error"); 1661 } 1662 if ((status & 0x01) != 0) { 1663 throw new IllegalArgumentException("Labels used for debug info cannot be reused for control flow"); 1664 } 1665 } 1666 1667 /** 1668 * Returns the Field object corresponding to the Label.status field. 1669 * 1670 * @return the Field object corresponding to the Label.status field. 1671 */ 1672 private static Field getLabelStatusField() { 1673 if (labelStatusField == null) { 1674 labelStatusField = getLabelField("a"); 1675 if (labelStatusField == null) { 1676 labelStatusField = getLabelField("status"); 1677 } 1678 } 1679 return labelStatusField; 1680 } 1681 1682 /** 1683 * Returns the field of the Label class whose name is given. 1684 * 1685 * @param name a field name. 1686 * @return the field of the Label class whose name is given, or null. 1687 */ 1688 private static Field getLabelField(final String name) { 1689 try { 1690 Field f = Label.class.getDeclaredField(name); 1691 f.setAccessible(true); 1692 return f; 1693 } catch (NoSuchFieldException e) { 1694 return null; 1695 } 1696 } 1697 }