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