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 * Number of visited frames in expanded form. 147 */ 148 private int expandedFrames; 149 150 /** 151 * Number of visited frames in compressed form. 152 */ 153 private int compressedFrames; 154 155 /** 156 * Number of instructions before the last visited frame. 157 */ 158 private int lastFrame = -1; 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 * @throws IllegalStateException 425 * If a subclass calls this constructor. 426 */ 427 public CheckMethodAdapter(final MethodVisitor mv, 428 final Map<Label, Integer> labels) { 429 this(Opcodes.ASM5, mv, labels); 430 if (getClass() != CheckMethodAdapter.class) { 431 throw new IllegalStateException(); 432 } 433 } 434 435 /** 436 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 437 * will not perform any data flow check (see 438 * {@link #CheckMethodAdapter(int,String,String,MethodVisitor,Map)}). 439 * 440 * @param mv 441 * the method visitor to which this adapter must delegate calls. 442 * @param labels 443 * a map of already visited labels (in other methods). 444 */ 445 protected CheckMethodAdapter(final int api, final MethodVisitor mv, 446 final Map<Label, Integer> labels) { 447 super(api, mv); 448 this.labels = labels; 449 this.usedLabels = new HashSet<Label>(); 450 this.handlers = new ArrayList<Label>(); 451 } 452 453 /** 454 * Constructs a new {@link CheckMethodAdapter} object. This method adapter 455 * will perform basic data flow checks. For instance in a method whose 456 * signature is <tt>void m ()</tt>, the invalid instruction IRETURN, or the 457 * invalid sequence IADD L2I will be detected. 458 * 459 * @param access 460 * the method's access flags. 461 * @param name 462 * the method's name. 463 * @param desc 464 * the method's descriptor (see {@link Type Type}). 465 * @param cmv 466 * the method visitor to which this adapter must delegate calls. 467 * @param labels 468 * a map of already visited labels (in other methods). 469 */ 470 public CheckMethodAdapter(final int access, final String name, 471 final String desc, final MethodVisitor cmv, 472 final Map<Label, Integer> labels) { 473 this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) { 474 @Override 475 public void visitEnd() { 476 Analyzer<BasicValue> a = new Analyzer<BasicValue>( 477 new BasicVerifier()); 478 try { 479 a.analyze("dummy", this); 480 } catch (Exception e) { 481 if (e instanceof IndexOutOfBoundsException 482 && maxLocals == 0 && maxStack == 0) { 483 throw new RuntimeException( 484 "Data flow checking option requires valid, non zero maxLocals and maxStack values."); 485 } 486 e.printStackTrace(); 487 StringWriter sw = new StringWriter(); 488 PrintWriter pw = new PrintWriter(sw, true); 489 CheckClassAdapter.printAnalyzerResult(this, a, pw); 490 pw.close(); 491 throw new RuntimeException(e.getMessage() + ' ' 492 + sw.toString()); 493 } 494 accept(cmv); 495 } 496 }, labels); 497 this.access = access; 498 } 499 500 @Override 501 public void visitParameter(String name, int access) { 502 if (name != null) { 503 checkUnqualifiedName(version, name, "name"); 504 } 505 CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL 506 + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); 507 super.visitParameter(name, access); 508 } 509 510 @Override 511 public AnnotationVisitor visitAnnotation(final String desc, 512 final boolean visible) { 513 checkEndMethod(); 514 checkDesc(desc, false); 515 return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); 516 } 517 518 @Override 519 public AnnotationVisitor visitTypeAnnotation(final int typeRef, 520 final TypePath typePath, final String desc, final boolean visible) { 521 checkEndMethod(); 522 int sort = typeRef >>> 24; 523 if (sort != TypeReference.METHOD_TYPE_PARAMETER 524 && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND 525 && sort != TypeReference.METHOD_RETURN 526 && sort != TypeReference.METHOD_RECEIVER 527 && sort != TypeReference.METHOD_FORMAL_PARAMETER 528 && sort != TypeReference.THROWS) { 529 throw new IllegalArgumentException("Invalid type reference sort 0x" 530 + Integer.toHexString(sort)); 531 } 532 CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); 533 CheckMethodAdapter.checkDesc(desc, false); 534 return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, 535 typePath, desc, visible)); 536 } 537 538 @Override 539 public AnnotationVisitor visitAnnotationDefault() { 540 checkEndMethod(); 541 return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); 542 } 543 544 @Override 545 public AnnotationVisitor visitParameterAnnotation(final int parameter, 546 final String desc, final boolean visible) { 547 checkEndMethod(); 548 checkDesc(desc, false); 549 return new CheckAnnotationAdapter(super.visitParameterAnnotation( 550 parameter, desc, visible)); 551 } 552 553 @Override 554 public void visitAttribute(final Attribute attr) { 555 checkEndMethod(); 556 if (attr == null) { 557 throw new IllegalArgumentException( 558 "Invalid attribute (must not be null)"); 559 } 560 super.visitAttribute(attr); 561 } 562 563 @Override 564 public void visitCode() { 565 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 566 throw new RuntimeException("Abstract methods cannot have code"); 567 } 568 startCode = true; 569 super.visitCode(); 570 } 571 572 @Override 573 public void visitFrame(final int type, final int nLocal, 574 final Object[] local, final int nStack, final Object[] stack) { 575 if (insnCount == lastFrame) { 576 throw new IllegalStateException( 577 "At most one frame can be visited at a given code location."); 578 } 579 lastFrame = insnCount; 580 int mLocal; 581 int mStack; 582 switch (type) { 583 case Opcodes.F_NEW: 584 case Opcodes.F_FULL: 585 mLocal = Integer.MAX_VALUE; 586 mStack = Integer.MAX_VALUE; 587 break; 588 589 case Opcodes.F_SAME: 590 mLocal = 0; 591 mStack = 0; 592 break; 593 594 case Opcodes.F_SAME1: 595 mLocal = 0; 596 mStack = 1; 597 break; 598 599 case Opcodes.F_APPEND: 600 case Opcodes.F_CHOP: 601 mLocal = 3; 602 mStack = 0; 603 break; 604 605 default: 606 throw new IllegalArgumentException("Invalid frame type " + type); 607 } 608 609 if (nLocal > mLocal) { 610 throw new IllegalArgumentException("Invalid nLocal=" + nLocal 611 + " for frame type " + type); 612 } 613 if (nStack > mStack) { 614 throw new IllegalArgumentException("Invalid nStack=" + nStack 615 + " for frame type " + type); 616 } 617 618 if (type != Opcodes.F_CHOP) { 619 if (nLocal > 0 && (local == null || local.length < nLocal)) { 620 throw new IllegalArgumentException( 621 "Array local[] is shorter than nLocal"); 622 } 623 for (int i = 0; i < nLocal; ++i) { 624 checkFrameValue(local[i]); 625 } 626 } 627 if (nStack > 0 && (stack == null || stack.length < nStack)) { 628 throw new IllegalArgumentException( 629 "Array stack[] is shorter than nStack"); 630 } 631 for (int i = 0; i < nStack; ++i) { 632 checkFrameValue(stack[i]); 633 } 634 if (type == Opcodes.F_NEW) { 635 ++expandedFrames; 636 } else { 637 ++compressedFrames; 638 } 639 if (expandedFrames > 0 && compressedFrames > 0) { 640 throw new RuntimeException( 641 "Expanded and compressed frames must not be mixed."); 642 } 643 super.visitFrame(type, nLocal, local, nStack, stack); 644 } 645 646 @Override 647 public void visitInsn(final int opcode) { 648 checkStartCode(); 649 checkEndCode(); 650 checkOpcode(opcode, 0); 651 super.visitInsn(opcode); 652 ++insnCount; 653 } 654 655 @Override 656 public void visitIntInsn(final int opcode, final int operand) { 657 checkStartCode(); 658 checkEndCode(); 659 checkOpcode(opcode, 1); 660 switch (opcode) { 661 case Opcodes.BIPUSH: 662 checkSignedByte(operand, "Invalid operand"); 663 break; 664 case Opcodes.SIPUSH: 665 checkSignedShort(operand, "Invalid operand"); 666 break; 667 // case Constants.NEWARRAY: 668 default: 669 if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { 670 throw new IllegalArgumentException( 671 "Invalid operand (must be an array type code T_...): " 672 + operand); 673 } 674 } 675 super.visitIntInsn(opcode, operand); 676 ++insnCount; 677 } 678 679 @Override 680 public void visitVarInsn(final int opcode, final int var) { 681 checkStartCode(); 682 checkEndCode(); 683 checkOpcode(opcode, 2); 684 checkUnsignedShort(var, "Invalid variable index"); 685 super.visitVarInsn(opcode, var); 686 ++insnCount; 687 } 688 689 @Override 690 public void visitTypeInsn(final int opcode, final String type) { 691 checkStartCode(); 692 checkEndCode(); 693 checkOpcode(opcode, 3); 694 checkInternalName(type, "type"); 695 if (opcode == Opcodes.NEW && type.charAt(0) == '[') { 696 throw new IllegalArgumentException( 697 "NEW cannot be used to create arrays: " + type); 698 } 699 super.visitTypeInsn(opcode, type); 700 ++insnCount; 701 } 702 703 @Override 704 public void visitFieldInsn(final int opcode, final String owner, 705 final String name, final String desc) { 706 checkStartCode(); 707 checkEndCode(); 708 checkOpcode(opcode, 4); 709 checkInternalName(owner, "owner"); 710 checkUnqualifiedName(version, name, "name"); 711 checkDesc(desc, false); 712 super.visitFieldInsn(opcode, owner, name, desc); 713 ++insnCount; 714 } 715 716 @Deprecated 717 @Override 718 public void visitMethodInsn(int opcode, String owner, String name, 719 String desc) { 720 if (api >= Opcodes.ASM5) { 721 super.visitMethodInsn(opcode, owner, name, desc); 722 return; 723 } 724 doVisitMethodInsn(opcode, owner, name, desc, 725 opcode == Opcodes.INVOKEINTERFACE); 726 } 727 728 @Override 729 public void visitMethodInsn(int opcode, String owner, String name, 730 String desc, boolean itf) { 731 if (api < Opcodes.ASM5) { 732 super.visitMethodInsn(opcode, owner, name, desc, itf); 733 return; 734 } 735 doVisitMethodInsn(opcode, owner, name, desc, itf); 736 } 737 738 private void doVisitMethodInsn(int opcode, final String owner, 739 final String name, final String desc, final boolean itf) { 740 checkStartCode(); 741 checkEndCode(); 742 checkOpcode(opcode, 5); 743 if (opcode != Opcodes.INVOKESPECIAL || !"<init>".equals(name)) { 744 checkMethodIdentifier(version, name, "name"); 745 } 746 checkInternalName(owner, "owner"); 747 checkMethodDesc(desc); 748 if (opcode == Opcodes.INVOKEVIRTUAL && itf) { 749 throw new IllegalArgumentException( 750 "INVOKEVIRTUAL can't be used with interfaces"); 751 } 752 if (opcode == Opcodes.INVOKEINTERFACE && !itf) { 753 throw new IllegalArgumentException( 754 "INVOKEINTERFACE can't be used with classes"); 755 } 756 // Calling super.visitMethodInsn requires to call the correct version 757 // depending on this.api (otherwise infinite loops can occur). To 758 // simplify and to make it easier to automatically remove the backward 759 // compatibility code, we inline the code of the overridden method here. 760 if (mv != null) { 761 mv.visitMethodInsn(opcode, owner, name, desc, itf); 762 } 763 ++insnCount; 764 } 765 766 @Override 767 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, 768 Object... bsmArgs) { 769 checkStartCode(); 770 checkEndCode(); 771 checkMethodIdentifier(version, name, "name"); 772 checkMethodDesc(desc); 773 if (bsm.getTag() != Opcodes.H_INVOKESTATIC 774 && bsm.getTag() != Opcodes.H_NEWINVOKESPECIAL) { 775 throw new IllegalArgumentException("invalid handle tag " 776 + bsm.getTag()); 777 } 778 for (int i = 0; i < bsmArgs.length; i++) { 779 checkLDCConstant(bsmArgs[i]); 780 } 781 super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); 782 ++insnCount; 783 } 784 785 @Override 786 public void visitJumpInsn(final int opcode, final Label label) { 787 checkStartCode(); 788 checkEndCode(); 789 checkOpcode(opcode, 6); 790 checkLabel(label, false, "label"); 791 checkNonDebugLabel(label); 792 super.visitJumpInsn(opcode, label); 793 usedLabels.add(label); 794 ++insnCount; 795 } 796 797 @Override 798 public void visitLabel(final Label label) { 799 checkStartCode(); 800 checkEndCode(); 801 checkLabel(label, false, "label"); 802 if (labels.get(label) != null) { 803 throw new IllegalArgumentException("Already visited label"); 804 } 805 labels.put(label, insnCount); 806 super.visitLabel(label); 807 } 808 809 @Override 810 public void visitLdcInsn(final Object cst) { 811 checkStartCode(); 812 checkEndCode(); 813 checkLDCConstant(cst); 814 super.visitLdcInsn(cst); 815 ++insnCount; 816 } 817 818 @Override 819 public void visitIincInsn(final int var, final int increment) { 820 checkStartCode(); 821 checkEndCode(); 822 checkUnsignedShort(var, "Invalid variable index"); 823 checkSignedShort(increment, "Invalid increment"); 824 super.visitIincInsn(var, increment); 825 ++insnCount; 826 } 827 828 @Override 829 public void visitTableSwitchInsn(final int min, final int max, 830 final Label dflt, final Label... labels) { 831 checkStartCode(); 832 checkEndCode(); 833 if (max < min) { 834 throw new IllegalArgumentException("Max = " + max 835 + " must be greater than or equal to min = " + min); 836 } 837 checkLabel(dflt, false, "default label"); 838 checkNonDebugLabel(dflt); 839 if (labels == null || labels.length != max - min + 1) { 840 throw new IllegalArgumentException( 841 "There must be max - min + 1 labels"); 842 } 843 for (int i = 0; i < labels.length; ++i) { 844 checkLabel(labels[i], false, "label at index " + i); 845 checkNonDebugLabel(labels[i]); 846 } 847 super.visitTableSwitchInsn(min, max, dflt, labels); 848 for (int i = 0; i < labels.length; ++i) { 849 usedLabels.add(labels[i]); 850 } 851 ++insnCount; 852 } 853 854 @Override 855 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, 856 final Label[] labels) { 857 checkEndCode(); 858 checkStartCode(); 859 checkLabel(dflt, false, "default label"); 860 checkNonDebugLabel(dflt); 861 if (keys == null || labels == null || keys.length != labels.length) { 862 throw new IllegalArgumentException( 863 "There must be the same number of keys and labels"); 864 } 865 for (int i = 0; i < labels.length; ++i) { 866 checkLabel(labels[i], false, "label at index " + i); 867 checkNonDebugLabel(labels[i]); 868 } 869 super.visitLookupSwitchInsn(dflt, keys, labels); 870 usedLabels.add(dflt); 871 for (int i = 0; i < labels.length; ++i) { 872 usedLabels.add(labels[i]); 873 } 874 ++insnCount; 875 } 876 877 @Override 878 public void visitMultiANewArrayInsn(final String desc, final int dims) { 879 checkStartCode(); 880 checkEndCode(); 881 checkDesc(desc, false); 882 if (desc.charAt(0) != '[') { 883 throw new IllegalArgumentException( 884 "Invalid descriptor (must be an array type descriptor): " 885 + desc); 886 } 887 if (dims < 1) { 888 throw new IllegalArgumentException( 889 "Invalid dimensions (must be greater than 0): " + dims); 890 } 891 if (dims > desc.lastIndexOf('[') + 1) { 892 throw new IllegalArgumentException( 893 "Invalid dimensions (must not be greater than dims(desc)): " 894 + dims); 895 } 896 super.visitMultiANewArrayInsn(desc, dims); 897 ++insnCount; 898 } 899 900 @Override 901 public AnnotationVisitor visitInsnAnnotation(final int typeRef, 902 final TypePath typePath, final String desc, final boolean visible) { 903 checkStartCode(); 904 checkEndCode(); 905 int sort = typeRef >>> 24; 906 if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW 907 && sort != TypeReference.CONSTRUCTOR_REFERENCE 908 && sort != TypeReference.METHOD_REFERENCE 909 && sort != TypeReference.CAST 910 && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT 911 && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT 912 && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT 913 && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { 914 throw new IllegalArgumentException("Invalid type reference sort 0x" 915 + Integer.toHexString(sort)); 916 } 917 CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); 918 CheckMethodAdapter.checkDesc(desc, false); 919 return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, 920 typePath, desc, visible)); 921 } 922 923 @Override 924 public void visitTryCatchBlock(final Label start, final Label end, 925 final Label handler, final String type) { 926 checkStartCode(); 927 checkEndCode(); 928 checkLabel(start, false, "start label"); 929 checkLabel(end, false, "end label"); 930 checkLabel(handler, false, "handler label"); 931 checkNonDebugLabel(start); 932 checkNonDebugLabel(end); 933 checkNonDebugLabel(handler); 934 if (labels.get(start) != null || labels.get(end) != null 935 || labels.get(handler) != null) { 936 throw new IllegalStateException( 937 "Try catch blocks must be visited before their labels"); 938 } 939 if (type != null) { 940 checkInternalName(type, "type"); 941 } 942 super.visitTryCatchBlock(start, end, handler, type); 943 handlers.add(start); 944 handlers.add(end); 945 } 946 947 @Override 948 public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, 949 final TypePath typePath, final String desc, final boolean visible) { 950 checkStartCode(); 951 checkEndCode(); 952 int sort = typeRef >>> 24; 953 if (sort != TypeReference.EXCEPTION_PARAMETER) { 954 throw new IllegalArgumentException("Invalid type reference sort 0x" 955 + Integer.toHexString(sort)); 956 } 957 CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); 958 CheckMethodAdapter.checkDesc(desc, false); 959 return new CheckAnnotationAdapter(super.visitTryCatchAnnotation( 960 typeRef, typePath, desc, visible)); 961 } 962 963 @Override 964 public void visitLocalVariable(final String name, final String desc, 965 final String signature, final Label start, final Label end, 966 final int index) { 967 checkStartCode(); 968 checkEndCode(); 969 checkUnqualifiedName(version, name, "name"); 970 checkDesc(desc, false); 971 checkLabel(start, true, "start label"); 972 checkLabel(end, true, "end label"); 973 checkUnsignedShort(index, "Invalid variable index"); 974 int s = labels.get(start).intValue(); 975 int e = labels.get(end).intValue(); 976 if (e < s) { 977 throw new IllegalArgumentException( 978 "Invalid start and end labels (end must be greater than start)"); 979 } 980 super.visitLocalVariable(name, desc, signature, start, end, index); 981 } 982 983 @Override 984 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, 985 TypePath typePath, Label[] start, Label[] end, int[] index, 986 String desc, boolean visible) { 987 checkStartCode(); 988 checkEndCode(); 989 int sort = typeRef >>> 24; 990 if (sort != TypeReference.LOCAL_VARIABLE 991 && sort != TypeReference.RESOURCE_VARIABLE) { 992 throw new IllegalArgumentException("Invalid type reference sort 0x" 993 + Integer.toHexString(sort)); 994 } 995 CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); 996 checkDesc(desc, false); 997 if (start == null || end == null || index == null 998 || end.length != start.length || index.length != start.length) { 999 throw new IllegalArgumentException( 1000 "Invalid start, end and index arrays (must be non null and of identical length"); 1001 } 1002 for (int i = 0; i < start.length; ++i) { 1003 checkLabel(start[i], true, "start label"); 1004 checkLabel(end[i], true, "end label"); 1005 checkUnsignedShort(index[i], "Invalid variable index"); 1006 int s = labels.get(start[i]).intValue(); 1007 int e = labels.get(end[i]).intValue(); 1008 if (e < s) { 1009 throw new IllegalArgumentException( 1010 "Invalid start and end labels (end must be greater than start)"); 1011 } 1012 } 1013 return super.visitLocalVariableAnnotation(typeRef, typePath, start, 1014 end, index, desc, visible); 1015 } 1016 1017 @Override 1018 public void visitLineNumber(final int line, final Label start) { 1019 checkStartCode(); 1020 checkEndCode(); 1021 checkUnsignedShort(line, "Invalid line number"); 1022 checkLabel(start, true, "start label"); 1023 super.visitLineNumber(line, start); 1024 } 1025 1026 @Override 1027 public void visitMaxs(final int maxStack, final int maxLocals) { 1028 checkStartCode(); 1029 checkEndCode(); 1030 endCode = true; 1031 for (Label l : usedLabels) { 1032 if (labels.get(l) == null) { 1033 throw new IllegalStateException("Undefined label used"); 1034 } 1035 } 1036 for (int i = 0; i < handlers.size();) { 1037 Integer start = labels.get(handlers.get(i++)); 1038 Integer end = labels.get(handlers.get(i++)); 1039 if (start == null || end == null) { 1040 throw new IllegalStateException( 1041 "Undefined try catch block labels"); 1042 } 1043 if (end.intValue() <= start.intValue()) { 1044 throw new IllegalStateException( 1045 "Emty try catch block handler range"); 1046 } 1047 } 1048 checkUnsignedShort(maxStack, "Invalid max stack"); 1049 checkUnsignedShort(maxLocals, "Invalid max locals"); 1050 super.visitMaxs(maxStack, maxLocals); 1051 } 1052 1053 @Override 1054 public void visitEnd() { 1055 checkEndMethod(); 1056 endMethod = true; 1057 super.visitEnd(); 1058 } 1059 1060 // ------------------------------------------------------------------------- 1061 1062 /** 1063 * Checks that the visitCode method has been called. 1064 */ 1065 void checkStartCode() { 1066 if (!startCode) { 1067 throw new IllegalStateException( 1068 "Cannot visit instructions before visitCode has been called."); 1069 } 1070 } 1071 1072 /** 1073 * Checks that the visitMaxs method has not been called. 1074 */ 1075 void checkEndCode() { 1076 if (endCode) { 1077 throw new IllegalStateException( 1078 "Cannot visit instructions after visitMaxs has been called."); 1079 } 1080 } 1081 1082 /** 1083 * Checks that the visitEnd method has not been called. 1084 */ 1085 void checkEndMethod() { 1086 if (endMethod) { 1087 throw new IllegalStateException( 1088 "Cannot visit elements after visitEnd has been called."); 1089 } 1090 } 1091 1092 /** 1093 * Checks a stack frame value. 1094 * 1095 * @param value 1096 * the value to be checked. 1097 */ 1098 void checkFrameValue(final Object value) { 1099 if (value == Opcodes.TOP || value == Opcodes.INTEGER 1100 || value == Opcodes.FLOAT || value == Opcodes.LONG 1101 || value == Opcodes.DOUBLE || value == Opcodes.NULL 1102 || value == Opcodes.UNINITIALIZED_THIS) { 1103 return; 1104 } 1105 if (value instanceof String) { 1106 checkInternalName((String) value, "Invalid stack frame value"); 1107 return; 1108 } 1109 if (!(value instanceof Label)) { 1110 throw new IllegalArgumentException("Invalid stack frame value: " 1111 + value); 1112 } else { 1113 usedLabels.add((Label) value); 1114 } 1115 } 1116 1117 /** 1118 * Checks that the type of the given opcode is equal to the given type. 1119 * 1120 * @param opcode 1121 * the opcode to be checked. 1122 * @param type 1123 * the expected opcode type. 1124 */ 1125 static void checkOpcode(final int opcode, final int type) { 1126 if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { 1127 throw new IllegalArgumentException("Invalid opcode: " + opcode); 1128 } 1129 } 1130 1131 /** 1132 * Checks that the given value is a signed byte. 1133 * 1134 * @param value 1135 * the value to be checked. 1136 * @param msg 1137 * an message to be used in case of error. 1138 */ 1139 static void checkSignedByte(final int value, final String msg) { 1140 if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { 1141 throw new IllegalArgumentException(msg 1142 + " (must be a signed byte): " + value); 1143 } 1144 } 1145 1146 /** 1147 * Checks that the given value is a signed short. 1148 * 1149 * @param value 1150 * the value to be checked. 1151 * @param msg 1152 * an message to be used in case of error. 1153 */ 1154 static void checkSignedShort(final int value, final String msg) { 1155 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 1156 throw new IllegalArgumentException(msg 1157 + " (must be a signed short): " + value); 1158 } 1159 } 1160 1161 /** 1162 * Checks that the given value is an unsigned short. 1163 * 1164 * @param value 1165 * the value to be checked. 1166 * @param msg 1167 * an message to be used in case of error. 1168 */ 1169 static void checkUnsignedShort(final int value, final String msg) { 1170 if (value < 0 || value > 65535) { 1171 throw new IllegalArgumentException(msg 1172 + " (must be an unsigned short): " + value); 1173 } 1174 } 1175 1176 /** 1177 * Checks that the given value is an {@link Integer}, a{@link Float}, a 1178 * {@link Long}, a {@link Double} or a {@link String}. 1179 * 1180 * @param cst 1181 * the value to be checked. 1182 */ 1183 static void checkConstant(final Object cst) { 1184 if (!(cst instanceof Integer) && !(cst instanceof Float) 1185 && !(cst instanceof Long) && !(cst instanceof Double) 1186 && !(cst instanceof String)) { 1187 throw new IllegalArgumentException("Invalid constant: " + cst); 1188 } 1189 } 1190 1191 void checkLDCConstant(final Object cst) { 1192 if (cst instanceof Type) { 1193 int s = ((Type) cst).getSort(); 1194 if (s != Type.OBJECT && s != Type.ARRAY && s != Type.METHOD) { 1195 throw new IllegalArgumentException("Illegal LDC constant value"); 1196 } 1197 if (s != Type.METHOD && (version & 0xFFFF) < Opcodes.V1_5) { 1198 throw new IllegalArgumentException( 1199 "ldc of a constant class requires at least version 1.5"); 1200 } 1201 if (s == Type.METHOD && (version & 0xFFFF) < Opcodes.V1_7) { 1202 throw new IllegalArgumentException( 1203 "ldc of a method type requires at least version 1.7"); 1204 } 1205 } else if (cst instanceof Handle) { 1206 if ((version & 0xFFFF) < Opcodes.V1_7) { 1207 throw new IllegalArgumentException( 1208 "ldc of a handle requires at least version 1.7"); 1209 } 1210 int tag = ((Handle) cst).getTag(); 1211 if (tag < Opcodes.H_GETFIELD || tag > Opcodes.H_INVOKEINTERFACE) { 1212 throw new IllegalArgumentException("invalid handle tag " + tag); 1213 } 1214 } else { 1215 checkConstant(cst); 1216 } 1217 } 1218 1219 /** 1220 * Checks that the given string is a valid unqualified name. 1221 * 1222 * @param version 1223 * the class version. 1224 * @param name 1225 * the string to be checked. 1226 * @param msg 1227 * a message to be used in case of error. 1228 */ 1229 static void checkUnqualifiedName(int version, final String name, 1230 final String msg) { 1231 if ((version & 0xFFFF) < Opcodes.V1_5) { 1232 checkIdentifier(name, msg); 1233 } else { 1234 for (int i = 0; i < name.length(); ++i) { 1235 if (".;[/".indexOf(name.charAt(i)) != -1) { 1236 throw new IllegalArgumentException("Invalid " + msg 1237 + " (must be a valid unqualified name): " + name); 1238 } 1239 } 1240 } 1241 } 1242 1243 /** 1244 * Checks that the given string is a valid Java identifier. 1245 * 1246 * @param name 1247 * the string to be checked. 1248 * @param msg 1249 * a message to be used in case of error. 1250 */ 1251 static void checkIdentifier(final String name, final String msg) { 1252 checkIdentifier(name, 0, -1, msg); 1253 } 1254 1255 /** 1256 * Checks that the given substring is a valid Java identifier. 1257 * 1258 * @param name 1259 * the string to be checked. 1260 * @param start 1261 * index of the first character of the identifier (inclusive). 1262 * @param end 1263 * index of the last character of the identifier (exclusive). -1 1264 * is equivalent to <tt>name.length()</tt> if name is not 1265 * <tt>null</tt>. 1266 * @param msg 1267 * a message to be used in case of error. 1268 */ 1269 static void checkIdentifier(final String name, final int start, 1270 final int end, final String msg) { 1271 if (name == null || (end == -1 ? name.length() <= start : end <= start)) { 1272 throw new IllegalArgumentException("Invalid " + msg 1273 + " (must not be null or empty)"); 1274 } 1275 if (!Character.isJavaIdentifierStart(name.charAt(start))) { 1276 throw new IllegalArgumentException("Invalid " + msg 1277 + " (must be a valid Java identifier): " + name); 1278 } 1279 int max = end == -1 ? name.length() : end; 1280 for (int i = start + 1; i < max; ++i) { 1281 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 1282 throw new IllegalArgumentException("Invalid " + msg 1283 + " (must be a valid Java identifier): " + name); 1284 } 1285 } 1286 } 1287 1288 /** 1289 * Checks that the given string is a valid Java identifier. 1290 * 1291 * @param version 1292 * the class version. 1293 * @param name 1294 * the string to be checked. 1295 * @param msg 1296 * a message to be used in case of error. 1297 */ 1298 static void checkMethodIdentifier(int version, final String name, 1299 final String msg) { 1300 if (name == null || name.length() == 0) { 1301 throw new IllegalArgumentException("Invalid " + msg 1302 + " (must not be null or empty)"); 1303 } 1304 if ((version & 0xFFFF) >= Opcodes.V1_5) { 1305 for (int i = 0; i < name.length(); ++i) { 1306 if (".;[/<>".indexOf(name.charAt(i)) != -1) { 1307 throw new IllegalArgumentException("Invalid " + msg 1308 + " (must be a valid unqualified name): " + name); 1309 } 1310 } 1311 return; 1312 } 1313 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 1314 throw new IllegalArgumentException( 1315 "Invalid " 1316 + msg 1317 + " (must be a '<init>', '<clinit>' or a valid Java identifier): " 1318 + name); 1319 } 1320 for (int i = 1; i < name.length(); ++i) { 1321 if (!Character.isJavaIdentifierPart(name.charAt(i))) { 1322 throw new IllegalArgumentException( 1323 "Invalid " 1324 + msg 1325 + " (must be '<init>' or '<clinit>' or a valid Java identifier): " 1326 + name); 1327 } 1328 } 1329 } 1330 1331 /** 1332 * Checks that the given string is a valid internal class name. 1333 * 1334 * @param name 1335 * the string to be checked. 1336 * @param msg 1337 * a message to be used in case of error. 1338 */ 1339 static void checkInternalName(final String name, final String msg) { 1340 if (name == null || name.length() == 0) { 1341 throw new IllegalArgumentException("Invalid " + msg 1342 + " (must not be null or empty)"); 1343 } 1344 if (name.charAt(0) == '[') { 1345 checkDesc(name, false); 1346 } else { 1347 checkInternalName(name, 0, -1, msg); 1348 } 1349 } 1350 1351 /** 1352 * Checks that the given substring is a valid internal class name. 1353 * 1354 * @param name 1355 * the string to be checked. 1356 * @param start 1357 * index of the first character of the identifier (inclusive). 1358 * @param end 1359 * index of the last character of the identifier (exclusive). -1 1360 * is equivalent to <tt>name.length()</tt> if name is not 1361 * <tt>null</tt>. 1362 * @param msg 1363 * a message to be used in case of error. 1364 */ 1365 static void checkInternalName(final String name, final int start, 1366 final int end, final String msg) { 1367 int max = end == -1 ? name.length() : end; 1368 try { 1369 int begin = start; 1370 int slash; 1371 do { 1372 slash = name.indexOf('/', begin + 1); 1373 if (slash == -1 || slash > max) { 1374 slash = max; 1375 } 1376 checkIdentifier(name, begin, slash, null); 1377 begin = slash + 1; 1378 } while (slash != max); 1379 } catch (IllegalArgumentException unused) { 1380 throw new IllegalArgumentException( 1381 "Invalid " 1382 + msg 1383 + " (must be a fully qualified class name in internal form): " 1384 + name); 1385 } 1386 } 1387 1388 /** 1389 * Checks that the given string is a valid type descriptor. 1390 * 1391 * @param desc 1392 * the string to be checked. 1393 * @param canBeVoid 1394 * <tt>true</tt> if <tt>V</tt> can be considered valid. 1395 */ 1396 static void checkDesc(final String desc, final boolean canBeVoid) { 1397 int end = checkDesc(desc, 0, canBeVoid); 1398 if (end != desc.length()) { 1399 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1400 } 1401 } 1402 1403 /** 1404 * Checks that a the given substring is a valid type descriptor. 1405 * 1406 * @param desc 1407 * the string to be checked. 1408 * @param start 1409 * index of the first character of the identifier (inclusive). 1410 * @param canBeVoid 1411 * <tt>true</tt> if <tt>V</tt> can be considered valid. 1412 * @return the index of the last character of the type decriptor, plus one. 1413 */ 1414 static int checkDesc(final String desc, final int start, 1415 final boolean canBeVoid) { 1416 if (desc == null || start >= desc.length()) { 1417 throw new IllegalArgumentException( 1418 "Invalid type descriptor (must not be null or empty)"); 1419 } 1420 int index; 1421 switch (desc.charAt(start)) { 1422 case 'V': 1423 if (canBeVoid) { 1424 return start + 1; 1425 } else { 1426 throw new IllegalArgumentException("Invalid descriptor: " 1427 + desc); 1428 } 1429 case 'Z': 1430 case 'C': 1431 case 'B': 1432 case 'S': 1433 case 'I': 1434 case 'F': 1435 case 'J': 1436 case 'D': 1437 return start + 1; 1438 case '[': 1439 index = start + 1; 1440 while (index < desc.length() && desc.charAt(index) == '[') { 1441 ++index; 1442 } 1443 if (index < desc.length()) { 1444 return checkDesc(desc, index, false); 1445 } else { 1446 throw new IllegalArgumentException("Invalid descriptor: " 1447 + desc); 1448 } 1449 case 'L': 1450 index = desc.indexOf(';', start); 1451 if (index == -1 || index - start < 2) { 1452 throw new IllegalArgumentException("Invalid descriptor: " 1453 + desc); 1454 } 1455 try { 1456 checkInternalName(desc, start + 1, index, null); 1457 } catch (IllegalArgumentException unused) { 1458 throw new IllegalArgumentException("Invalid descriptor: " 1459 + desc); 1460 } 1461 return index + 1; 1462 default: 1463 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1464 } 1465 } 1466 1467 /** 1468 * Checks that the given string is a valid method descriptor. 1469 * 1470 * @param desc 1471 * the string to be checked. 1472 */ 1473 static void checkMethodDesc(final String desc) { 1474 if (desc == null || desc.length() == 0) { 1475 throw new IllegalArgumentException( 1476 "Invalid method descriptor (must not be null or empty)"); 1477 } 1478 if (desc.charAt(0) != '(' || desc.length() < 3) { 1479 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1480 } 1481 int start = 1; 1482 if (desc.charAt(start) != ')') { 1483 do { 1484 if (desc.charAt(start) == 'V') { 1485 throw new IllegalArgumentException("Invalid descriptor: " 1486 + desc); 1487 } 1488 start = checkDesc(desc, start, false); 1489 } while (start < desc.length() && desc.charAt(start) != ')'); 1490 } 1491 start = checkDesc(desc, start + 1, true); 1492 if (start != desc.length()) { 1493 throw new IllegalArgumentException("Invalid descriptor: " + desc); 1494 } 1495 } 1496 1497 /** 1498 * Checks that the given label is not null. This method can also check that 1499 * the label has been visited. 1500 * 1501 * @param label 1502 * the label to be checked. 1503 * @param checkVisited 1504 * <tt>true</tt> to check that the label has been visited. 1505 * @param msg 1506 * a message to be used in case of error. 1507 */ 1508 void checkLabel(final Label label, final boolean checkVisited, 1509 final String msg) { 1510 if (label == null) { 1511 throw new IllegalArgumentException("Invalid " + msg 1512 + " (must not be null)"); 1513 } 1514 if (checkVisited && labels.get(label) == null) { 1515 throw new IllegalArgumentException("Invalid " + msg 1516 + " (must be visited first)"); 1517 } 1518 } 1519 1520 /** 1521 * Checks that the given label is not a label used only for debug purposes. 1522 * 1523 * @param label 1524 * the label to be checked. 1525 */ 1526 private static void checkNonDebugLabel(final Label label) { 1527 Field f = getLabelStatusField(); 1528 int status = 0; 1529 try { 1530 status = f == null ? 0 : ((Integer) f.get(label)).intValue(); 1531 } catch (IllegalAccessException e) { 1532 throw new Error("Internal error"); 1533 } 1534 if ((status & 0x01) != 0) { 1535 throw new IllegalArgumentException( 1536 "Labels used for debug info cannot be reused for control flow"); 1537 } 1538 } 1539 1540 /** 1541 * Returns the Field object corresponding to the Label.status field. 1542 * 1543 * @return the Field object corresponding to the Label.status field. 1544 */ 1545 private static Field getLabelStatusField() { 1546 if (labelStatusField == null) { 1547 labelStatusField = getLabelField("a"); 1548 if (labelStatusField == null) { 1549 labelStatusField = getLabelField("status"); 1550 } 1551 } 1552 return labelStatusField; 1553 } 1554 1555 /** 1556 * Returns the field of the Label class whose name is given. 1557 * 1558 * @param name 1559 * a field name. 1560 * @return the field of the Label class whose name is given, or null. 1561 */ 1562 private static Field getLabelField(final String name) { 1563 try { 1564 Field f = Label.class.getDeclaredField(name); 1565 f.setAccessible(true); 1566 return f; 1567 } catch (NoSuchFieldException e) { 1568 return null; 1569 } 1570 } 1571 }