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