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