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.FileInputStream; 62 import java.io.PrintWriter; 63 import java.util.ArrayList; 64 import java.util.HashMap; 65 import java.util.Iterator; 66 import java.util.List; 67 import java.util.Map; 68 69 import jdk.internal.org.objectweb.asm.AnnotationVisitor; 70 import jdk.internal.org.objectweb.asm.Attribute; 71 import jdk.internal.org.objectweb.asm.ClassReader; 72 import jdk.internal.org.objectweb.asm.ClassVisitor; 73 import jdk.internal.org.objectweb.asm.FieldVisitor; 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.ClassNode; 81 import jdk.internal.org.objectweb.asm.tree.MethodNode; 82 import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer; 83 import jdk.internal.org.objectweb.asm.tree.analysis.BasicValue; 84 import jdk.internal.org.objectweb.asm.tree.analysis.Frame; 85 import jdk.internal.org.objectweb.asm.tree.analysis.SimpleVerifier; 86 87 /** 88 * A {@link ClassVisitor} that checks that its methods are properly used. More 89 * precisely this class adapter checks each method call individually, based 90 * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i> 91 * of method calls. For example, the invalid sequence 92 * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC, 93 * "i", "D", null)</tt> will <i>not</i> be detected by this class adapter. 94 * 95 * <p> 96 * <code>CheckClassAdapter</code> can be also used to verify bytecode 97 * transformations in order to make sure transformed bytecode is sane. For 98 * example: 99 * 100 * <pre> 101 * InputStream is = ...; // get bytes for the source class 102 * ClassReader cr = new ClassReader(is); 103 * ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS); 104 * ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw)); 105 * cr.accept(cv, 0); 106 * 107 * StringWriter sw = new StringWriter(); 108 * PrintWriter pw = new PrintWriter(sw); 109 * CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw); 110 * assertTrue(sw.toString(), sw.toString().length()==0); 111 * </pre> 112 * 113 * Above code runs transformed bytecode trough the 114 * <code>CheckClassAdapter</code>. It won't be exactly the same verification as 115 * JVM does, but it run data flow analysis for the code of each method and 116 * checks that expectations are met for each method instruction. 117 * 118 * <p> 119 * If method bytecode has errors, assertion text will show the erroneous 120 * instruction number and dump of the failed method with information about 121 * locals and stack slot for each instruction. For example (format is - 122 * insnNumber locals : stack): 123 * 124 * <pre> 125 * jdk.internal.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found . 126 * at jdk.internal.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289) 127 * at jdk.internal.org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135) 128 * ... 129 * remove()V 130 * 00000 LinkedBlockingQueue$Itr . . . . . . . . : 131 * ICONST_0 132 * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I 133 * ISTORE 2 134 * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : 135 * ... 136 * 137 * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : 138 * ILOAD 1 139 * 00072 <b>?</b> 140 * INVOKESPECIAL java/lang/Integer.<init> (I)V 141 * ... 142 * </pre> 143 * 144 * In the above output you can see that variable 1 loaded by 145 * <code>ILOAD 1</code> instruction at position <code>00071</code> is not 146 * initialized. You can also see that at the beginning of the method (code 147 * inserted by the transformation) variable 2 is initialized. 148 * 149 * <p> 150 * Note that when used like that, <code>CheckClassAdapter.verify()</code> can 151 * trigger additional class loading, because it is using 152 * <code>SimpleVerifier</code>. 153 * 154 * @author Eric Bruneton 155 */ 156 public class CheckClassAdapter extends ClassVisitor { 157 158 /** 159 * The class version number. 160 */ 161 private int version; 162 163 /** 164 * <tt>true</tt> if the visit method has been called. 165 */ 166 private boolean start; 167 168 /** 169 * <tt>true</tt> if the visitSource method has been called. 170 */ 171 private boolean source; 172 173 /** 174 * <tt>true</tt> if the visitOuterClass method has been called. 175 */ 176 private boolean outer; 177 178 /** 179 * <tt>true</tt> if the visitEnd method has been called. 180 */ 181 private boolean end; 182 183 /** 184 * The already visited labels. This map associate Integer values to Label 185 * keys. 186 */ 187 private Map<Label, Integer> labels; 188 189 /** 190 * <tt>true</tt> if the method code must be checked with a BasicVerifier. 191 */ 192 private boolean checkDataFlow; 193 194 /** 195 * Checks a given class. 196 * <p> 197 * Usage: CheckClassAdapter <binary class name or class file name> 198 * 199 * @param args 200 * the command line arguments. 201 * 202 * @throws Exception 203 * if the class cannot be found, or if an IO exception occurs. 204 */ 205 public static void main(final String[] args) throws Exception { 206 if (args.length != 1) { 207 System.err.println("Verifies the given class."); 208 System.err.println("Usage: CheckClassAdapter " 209 + "<fully qualified class name or class file name>"); 210 return; 211 } 212 ClassReader cr; 213 if (args[0].endsWith(".class")) { 214 cr = new ClassReader(new FileInputStream(args[0])); 215 } else { 216 cr = new ClassReader(args[0]); 217 } 218 219 verify(cr, false, new PrintWriter(System.err)); 220 } 221 222 /** 223 * Checks a given class. 224 * 225 * @param cr 226 * a <code>ClassReader</code> that contains bytecode for the 227 * analysis. 228 * @param loader 229 * a <code>ClassLoader</code> which will be used to load 230 * referenced classes. This is useful if you are verifiying 231 * multiple interdependent classes. 232 * @param dump 233 * true if bytecode should be printed out not only when errors 234 * are found. 235 * @param pw 236 * write where results going to be printed 237 */ 238 public static void verify(final ClassReader cr, final ClassLoader loader, 239 final boolean dump, final PrintWriter pw) { 240 ClassNode cn = new ClassNode(); 241 cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG); 242 243 Type syperType = cn.superName == null ? null : Type 244 .getObjectType(cn.superName); 245 List<MethodNode> methods = cn.methods; 246 247 List<Type> interfaces = new ArrayList<Type>(); 248 for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) { 249 interfaces.add(Type.getObjectType(i.next())); 250 } 251 252 for (int i = 0; i < methods.size(); ++i) { 253 MethodNode method = methods.get(i); 254 SimpleVerifier verifier = new SimpleVerifier( 255 Type.getObjectType(cn.name), syperType, interfaces, 256 (cn.access & Opcodes.ACC_INTERFACE) != 0); 257 Analyzer<BasicValue> a = new Analyzer<BasicValue>(verifier); 258 if (loader != null) { 259 verifier.setClassLoader(loader); 260 } 261 try { 262 a.analyze(cn.name, method); 263 if (!dump) { 264 continue; 265 } 266 } catch (Exception e) { 267 e.printStackTrace(pw); 268 } 269 printAnalyzerResult(method, a, pw); 270 } 271 pw.flush(); 272 } 273 274 /** 275 * Checks a given class 276 * 277 * @param cr 278 * a <code>ClassReader</code> that contains bytecode for the 279 * analysis. 280 * @param dump 281 * true if bytecode should be printed out not only when errors 282 * are found. 283 * @param pw 284 * write where results going to be printed 285 */ 286 public static void verify(final ClassReader cr, final boolean dump, 287 final PrintWriter pw) { 288 verify(cr, null, dump, pw); 289 } 290 291 static void printAnalyzerResult(MethodNode method, Analyzer<BasicValue> a, 292 final PrintWriter pw) { 293 Frame<BasicValue>[] frames = a.getFrames(); 294 Textifier t = new Textifier(); 295 TraceMethodVisitor mv = new TraceMethodVisitor(t); 296 297 pw.println(method.name + method.desc); 298 for (int j = 0; j < method.instructions.size(); ++j) { 299 method.instructions.get(j).accept(mv); 300 301 StringBuffer s = new StringBuffer(); 302 Frame<BasicValue> f = frames[j]; 303 if (f == null) { 304 s.append('?'); 305 } else { 306 for (int k = 0; k < f.getLocals(); ++k) { 307 s.append(getShortName(f.getLocal(k).toString())) 308 .append(' '); 309 } 310 s.append(" : "); 311 for (int k = 0; k < f.getStackSize(); ++k) { 312 s.append(getShortName(f.getStack(k).toString())) 313 .append(' '); 314 } 315 } 316 while (s.length() < method.maxStack + method.maxLocals + 1) { 317 s.append(' '); 318 } 319 pw.print(Integer.toString(j + 100000).substring(1)); 320 pw.print(" " + s + " : " + t.text.get(t.text.size() - 1)); 321 } 322 for (int j = 0; j < method.tryCatchBlocks.size(); ++j) { 323 method.tryCatchBlocks.get(j).accept(mv); 324 pw.print(" " + t.text.get(t.text.size() - 1)); 325 } 326 pw.println(); 327 } 328 329 private static String getShortName(final String name) { 330 int n = name.lastIndexOf('/'); 331 int k = name.length(); 332 if (name.charAt(k - 1) == ';') { 333 k--; 334 } 335 return n == -1 ? name : name.substring(n + 1, k); 336 } 337 338 /** 339 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use 340 * this constructor</i>. Instead, they must use the 341 * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version. 342 * 343 * @param cv 344 * the class visitor to which this adapter must delegate calls. 345 */ 346 public CheckClassAdapter(final ClassVisitor cv) { 347 this(cv, true); 348 } 349 350 /** 351 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use 352 * this constructor</i>. Instead, they must use the 353 * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version. 354 * 355 * @param cv 356 * the class visitor to which this adapter must delegate calls. 357 * @param checkDataFlow 358 * <tt>true</tt> to perform basic data flow checks, or 359 * <tt>false</tt> to not perform any data flow check (see 360 * {@link CheckMethodAdapter}). This option requires valid 361 * maxLocals and maxStack values. 362 * @throws IllegalStateException 363 * If a subclass calls this constructor. 364 */ 365 public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) { 366 this(Opcodes.ASM5, cv, checkDataFlow); 367 if (getClass() != CheckClassAdapter.class) { 368 throw new IllegalStateException(); 369 } 370 } 371 372 /** 373 * Constructs a new {@link CheckClassAdapter}. 374 * 375 * @param api 376 * the ASM API version implemented by this visitor. Must be one 377 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. 378 * @param cv 379 * the class visitor to which this adapter must delegate calls. 380 * @param checkDataFlow 381 * <tt>true</tt> to perform basic data flow checks, or 382 * <tt>false</tt> to not perform any data flow check (see 383 * {@link CheckMethodAdapter}). This option requires valid 384 * maxLocals and maxStack values. 385 */ 386 protected CheckClassAdapter(final int api, final ClassVisitor cv, 387 final boolean checkDataFlow) { 388 super(api, cv); 389 this.labels = new HashMap<Label, Integer>(); 390 this.checkDataFlow = checkDataFlow; 391 } 392 393 // ------------------------------------------------------------------------ 394 // Implementation of the ClassVisitor interface 395 // ------------------------------------------------------------------------ 396 397 @Override 398 public void visit(final int version, final int access, final String name, 399 final String signature, final String superName, 400 final String[] interfaces) { 401 if (start) { 402 throw new IllegalStateException("visit must be called only once"); 403 } 404 start = true; 405 checkState(); 406 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL 407 + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE 408 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC 409 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM 410 + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE 411 if (name == null || !name.endsWith("package-info")) { 412 CheckMethodAdapter.checkInternalName(name, "class name"); 413 } 414 if ("java/lang/Object".equals(name)) { 415 if (superName != null) { 416 throw new IllegalArgumentException( 417 "The super class name of the Object class must be 'null'"); 418 } 419 } else { 420 CheckMethodAdapter.checkInternalName(superName, "super class name"); 421 } 422 if (signature != null) { 423 checkClassSignature(signature); 424 } 425 if ((access & Opcodes.ACC_INTERFACE) != 0) { 426 if (!"java/lang/Object".equals(superName)) { 427 throw new IllegalArgumentException( 428 "The super class name of interfaces must be 'java/lang/Object'"); 429 } 430 } 431 if (interfaces != null) { 432 for (int i = 0; i < interfaces.length; ++i) { 433 CheckMethodAdapter.checkInternalName(interfaces[i], 434 "interface name at index " + i); 435 } 436 } 437 this.version = version; 438 super.visit(version, access, name, signature, superName, interfaces); 439 } 440 441 @Override 442 public void visitSource(final String file, final String debug) { 443 checkState(); 444 if (source) { 445 throw new IllegalStateException( 446 "visitSource can be called only once."); 447 } 448 source = true; 449 super.visitSource(file, debug); 450 } 451 452 @Override 453 public void visitOuterClass(final String owner, final String name, 454 final String desc) { 455 checkState(); 456 if (outer) { 457 throw new IllegalStateException( 458 "visitOuterClass can be called only once."); 459 } 460 outer = true; 461 if (owner == null) { 462 throw new IllegalArgumentException("Illegal outer class owner"); 463 } 464 if (desc != null) { 465 CheckMethodAdapter.checkMethodDesc(desc); 466 } 467 super.visitOuterClass(owner, name, desc); 468 } 469 470 @Override 471 public void visitInnerClass(final String name, final String outerName, 472 final String innerName, final int access) { 473 checkState(); 474 CheckMethodAdapter.checkInternalName(name, "class name"); 475 if (outerName != null) { 476 CheckMethodAdapter.checkInternalName(outerName, "outer class name"); 477 } 478 if (innerName != null) { 479 int start = 0; 480 while (start < innerName.length() 481 && Character.isDigit(innerName.charAt(start))) { 482 start++; 483 } 484 if (start == 0 || start < innerName.length()) { 485 CheckMethodAdapter.checkIdentifier(innerName, start, -1, 486 "inner class name"); 487 } 488 } 489 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE 490 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC 491 + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE 492 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC 493 + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM); 494 super.visitInnerClass(name, outerName, innerName, access); 495 } 496 497 @Override 498 public FieldVisitor visitField(final int access, final String name, 499 final String desc, final String signature, final Object value) { 500 checkState(); 501 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE 502 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC 503 + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE 504 + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC 505 + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE 506 CheckMethodAdapter.checkUnqualifiedName(version, name, "field name"); 507 CheckMethodAdapter.checkDesc(desc, false); 508 if (signature != null) { 509 checkFieldSignature(signature); 510 } 511 if (value != null) { 512 CheckMethodAdapter.checkConstant(value); 513 } 514 FieldVisitor av = super 515 .visitField(access, name, desc, signature, value); 516 return new CheckFieldAdapter(av); 517 } 518 519 @Override 520 public MethodVisitor visitMethod(final int access, final String name, 521 final String desc, final String signature, final String[] exceptions) { 522 checkState(); 523 checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE 524 + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC 525 + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED 526 + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE 527 + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT 528 + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE 529 if (!"<init>".equals(name) && !"<clinit>".equals(name)) { 530 CheckMethodAdapter.checkMethodIdentifier(version, name, 531 "method name"); 532 } 533 CheckMethodAdapter.checkMethodDesc(desc); 534 if (signature != null) { 535 checkMethodSignature(signature); 536 } 537 if (exceptions != null) { 538 for (int i = 0; i < exceptions.length; ++i) { 539 CheckMethodAdapter.checkInternalName(exceptions[i], 540 "exception name at index " + i); 541 } 542 } 543 CheckMethodAdapter cma; 544 if (checkDataFlow) { 545 cma = new CheckMethodAdapter(access, name, desc, super.visitMethod( 546 access, name, desc, signature, exceptions), labels); 547 } else { 548 cma = new CheckMethodAdapter(super.visitMethod(access, name, desc, 549 signature, exceptions), labels); 550 } 551 cma.version = version; 552 return cma; 553 } 554 555 @Override 556 public AnnotationVisitor visitAnnotation(final String desc, 557 final boolean visible) { 558 checkState(); 559 CheckMethodAdapter.checkDesc(desc, false); 560 return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible)); 561 } 562 563 @Override 564 public AnnotationVisitor visitTypeAnnotation(final int typeRef, 565 final TypePath typePath, final String desc, final boolean visible) { 566 checkState(); 567 int sort = typeRef >>> 24; 568 if (sort != TypeReference.CLASS_TYPE_PARAMETER 569 && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND 570 && sort != TypeReference.CLASS_EXTENDS) { 571 throw new IllegalArgumentException("Invalid type reference sort 0x" 572 + Integer.toHexString(sort)); 573 } 574 checkTypeRefAndPath(typeRef, typePath); 575 CheckMethodAdapter.checkDesc(desc, false); 576 return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, 577 typePath, desc, visible)); 578 } 579 580 @Override 581 public void visitAttribute(final Attribute attr) { 582 checkState(); 583 if (attr == null) { 584 throw new IllegalArgumentException( 585 "Invalid attribute (must not be null)"); 586 } 587 super.visitAttribute(attr); 588 } 589 590 @Override 591 public void visitEnd() { 592 checkState(); 593 end = true; 594 super.visitEnd(); 595 } 596 597 // ------------------------------------------------------------------------ 598 // Utility methods 599 // ------------------------------------------------------------------------ 600 601 /** 602 * Checks that the visit method has been called and that visitEnd has not 603 * been called. 604 */ 605 private void checkState() { 606 if (!start) { 607 throw new IllegalStateException( 608 "Cannot visit member before visit has been called."); 609 } 610 if (end) { 611 throw new IllegalStateException( 612 "Cannot visit member after visitEnd has been called."); 613 } 614 } 615 616 /** 617 * Checks that the given access flags do not contain invalid flags. This 618 * method also checks that mutually incompatible flags are not set 619 * simultaneously. 620 * 621 * @param access 622 * the access flags to be checked 623 * @param possibleAccess 624 * the valid access flags. 625 */ 626 static void checkAccess(final int access, final int possibleAccess) { 627 if ((access & ~possibleAccess) != 0) { 628 throw new IllegalArgumentException("Invalid access flags: " 629 + access); 630 } 631 int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1; 632 int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1; 633 int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1; 634 if (pub + pri + pro > 1) { 635 throw new IllegalArgumentException( 636 "public private and protected are mutually exclusive: " 637 + access); 638 } 639 int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1; 640 int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1; 641 if (fin + abs > 1) { 642 throw new IllegalArgumentException( 643 "final and abstract are mutually exclusive: " + access); 644 } 645 } 646 647 /** 648 * Checks a class signature. 649 * 650 * @param signature 651 * a string containing the signature that must be checked. 652 */ 653 public static void checkClassSignature(final String signature) { 654 // ClassSignature: 655 // FormalTypeParameters? ClassTypeSignature ClassTypeSignature* 656 657 int pos = 0; 658 if (getChar(signature, 0) == '<') { 659 pos = checkFormalTypeParameters(signature, pos); 660 } 661 pos = checkClassTypeSignature(signature, pos); 662 while (getChar(signature, pos) == 'L') { 663 pos = checkClassTypeSignature(signature, pos); 664 } 665 if (pos != signature.length()) { 666 throw new IllegalArgumentException(signature + ": error at index " 667 + pos); 668 } 669 } 670 671 /** 672 * Checks a method signature. 673 * 674 * @param signature 675 * a string containing the signature that must be checked. 676 */ 677 public static void checkMethodSignature(final String signature) { 678 // MethodTypeSignature: 679 // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) ( 680 // ^ClassTypeSignature | ^TypeVariableSignature )* 681 682 int pos = 0; 683 if (getChar(signature, 0) == '<') { 684 pos = checkFormalTypeParameters(signature, pos); 685 } 686 pos = checkChar('(', signature, pos); 687 while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) { 688 pos = checkTypeSignature(signature, pos); 689 } 690 pos = checkChar(')', signature, pos); 691 if (getChar(signature, pos) == 'V') { 692 ++pos; 693 } else { 694 pos = checkTypeSignature(signature, pos); 695 } 696 while (getChar(signature, pos) == '^') { 697 ++pos; 698 if (getChar(signature, pos) == 'L') { 699 pos = checkClassTypeSignature(signature, pos); 700 } else { 701 pos = checkTypeVariableSignature(signature, pos); 702 } 703 } 704 if (pos != signature.length()) { 705 throw new IllegalArgumentException(signature + ": error at index " 706 + pos); 707 } 708 } 709 710 /** 711 * Checks a field signature. 712 * 713 * @param signature 714 * a string containing the signature that must be checked. 715 */ 716 public static void checkFieldSignature(final String signature) { 717 int pos = checkFieldTypeSignature(signature, 0); 718 if (pos != signature.length()) { 719 throw new IllegalArgumentException(signature + ": error at index " 720 + pos); 721 } 722 } 723 724 /** 725 * Checks the reference to a type in a type annotation. 726 * 727 * @param typeRef 728 * a reference to an annotated type. 729 * @param typePath 730 * the path to the annotated type argument, wildcard bound, array 731 * element type, or static inner type within 'typeRef'. May be 732 * <tt>null</tt> if the annotation targets 'typeRef' as a whole. 733 */ 734 static void checkTypeRefAndPath(int typeRef, TypePath typePath) { 735 int mask = 0; 736 switch (typeRef >>> 24) { 737 case TypeReference.CLASS_TYPE_PARAMETER: 738 case TypeReference.METHOD_TYPE_PARAMETER: 739 case TypeReference.METHOD_FORMAL_PARAMETER: 740 mask = 0xFFFF0000; 741 break; 742 case TypeReference.FIELD: 743 case TypeReference.METHOD_RETURN: 744 case TypeReference.METHOD_RECEIVER: 745 case TypeReference.LOCAL_VARIABLE: 746 case TypeReference.RESOURCE_VARIABLE: 747 case TypeReference.INSTANCEOF: 748 case TypeReference.NEW: 749 case TypeReference.CONSTRUCTOR_REFERENCE: 750 case TypeReference.METHOD_REFERENCE: 751 mask = 0xFF000000; 752 break; 753 case TypeReference.CLASS_EXTENDS: 754 case TypeReference.CLASS_TYPE_PARAMETER_BOUND: 755 case TypeReference.METHOD_TYPE_PARAMETER_BOUND: 756 case TypeReference.THROWS: 757 case TypeReference.EXCEPTION_PARAMETER: 758 mask = 0xFFFFFF00; 759 break; 760 case TypeReference.CAST: 761 case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: 762 case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: 763 case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: 764 case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: 765 mask = 0xFF0000FF; 766 break; 767 default: 768 throw new IllegalArgumentException("Invalid type reference sort 0x" 769 + Integer.toHexString(typeRef >>> 24)); 770 } 771 if ((typeRef & ~mask) != 0) { 772 throw new IllegalArgumentException("Invalid type reference 0x" 773 + Integer.toHexString(typeRef)); 774 } 775 if (typePath != null) { 776 for (int i = 0; i < typePath.getLength(); ++i) { 777 int step = typePath.getStep(i); 778 if (step != TypePath.ARRAY_ELEMENT 779 && step != TypePath.INNER_TYPE 780 && step != TypePath.TYPE_ARGUMENT 781 && step != TypePath.WILDCARD_BOUND) { 782 throw new IllegalArgumentException( 783 "Invalid type path step " + i + " in " + typePath); 784 } 785 if (step != TypePath.TYPE_ARGUMENT 786 && typePath.getStepArgument(i) != 0) { 787 throw new IllegalArgumentException( 788 "Invalid type path step argument for step " + i 789 + " in " + typePath); 790 } 791 } 792 } 793 } 794 795 /** 796 * Checks the formal type parameters of a class or method signature. 797 * 798 * @param signature 799 * a string containing the signature that must be checked. 800 * @param pos 801 * index of first character to be checked. 802 * @return the index of the first character after the checked part. 803 */ 804 private static int checkFormalTypeParameters(final String signature, int pos) { 805 // FormalTypeParameters: 806 // < FormalTypeParameter+ > 807 808 pos = checkChar('<', signature, pos); 809 pos = checkFormalTypeParameter(signature, pos); 810 while (getChar(signature, pos) != '>') { 811 pos = checkFormalTypeParameter(signature, pos); 812 } 813 return pos + 1; 814 } 815 816 /** 817 * Checks a formal type parameter of a class or method signature. 818 * 819 * @param signature 820 * a string containing the signature that must be checked. 821 * @param pos 822 * index of first character to be checked. 823 * @return the index of the first character after the checked part. 824 */ 825 private static int checkFormalTypeParameter(final String signature, int pos) { 826 // FormalTypeParameter: 827 // Identifier : FieldTypeSignature? (: FieldTypeSignature)* 828 829 pos = checkIdentifier(signature, pos); 830 pos = checkChar(':', signature, pos); 831 if ("L[T".indexOf(getChar(signature, pos)) != -1) { 832 pos = checkFieldTypeSignature(signature, pos); 833 } 834 while (getChar(signature, pos) == ':') { 835 pos = checkFieldTypeSignature(signature, pos + 1); 836 } 837 return pos; 838 } 839 840 /** 841 * Checks a field type signature. 842 * 843 * @param signature 844 * a string containing the signature that must be checked. 845 * @param pos 846 * index of first character to be checked. 847 * @return the index of the first character after the checked part. 848 */ 849 private static int checkFieldTypeSignature(final String signature, int pos) { 850 // FieldTypeSignature: 851 // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature 852 // 853 // ArrayTypeSignature: 854 // [ TypeSignature 855 856 switch (getChar(signature, pos)) { 857 case 'L': 858 return checkClassTypeSignature(signature, pos); 859 case '[': 860 return checkTypeSignature(signature, pos + 1); 861 default: 862 return checkTypeVariableSignature(signature, pos); 863 } 864 } 865 866 /** 867 * Checks a class type signature. 868 * 869 * @param signature 870 * a string containing the signature that must be checked. 871 * @param pos 872 * index of first character to be checked. 873 * @return the index of the first character after the checked part. 874 */ 875 private static int checkClassTypeSignature(final String signature, int pos) { 876 // ClassTypeSignature: 877 // L Identifier ( / Identifier )* TypeArguments? ( . Identifier 878 // TypeArguments? )* ; 879 880 pos = checkChar('L', signature, pos); 881 pos = checkIdentifier(signature, pos); 882 while (getChar(signature, pos) == '/') { 883 pos = checkIdentifier(signature, pos + 1); 884 } 885 if (getChar(signature, pos) == '<') { 886 pos = checkTypeArguments(signature, pos); 887 } 888 while (getChar(signature, pos) == '.') { 889 pos = checkIdentifier(signature, pos + 1); 890 if (getChar(signature, pos) == '<') { 891 pos = checkTypeArguments(signature, pos); 892 } 893 } 894 return checkChar(';', signature, pos); 895 } 896 897 /** 898 * Checks the type arguments in a class type signature. 899 * 900 * @param signature 901 * a string containing the signature that must be checked. 902 * @param pos 903 * index of first character to be checked. 904 * @return the index of the first character after the checked part. 905 */ 906 private static int checkTypeArguments(final String signature, int pos) { 907 // TypeArguments: 908 // < TypeArgument+ > 909 910 pos = checkChar('<', signature, pos); 911 pos = checkTypeArgument(signature, pos); 912 while (getChar(signature, pos) != '>') { 913 pos = checkTypeArgument(signature, pos); 914 } 915 return pos + 1; 916 } 917 918 /** 919 * Checks a type argument in a class type signature. 920 * 921 * @param signature 922 * a string containing the signature that must be checked. 923 * @param pos 924 * index of first character to be checked. 925 * @return the index of the first character after the checked part. 926 */ 927 private static int checkTypeArgument(final String signature, int pos) { 928 // TypeArgument: 929 // * | ( ( + | - )? FieldTypeSignature ) 930 931 char c = getChar(signature, pos); 932 if (c == '*') { 933 return pos + 1; 934 } else if (c == '+' || c == '-') { 935 pos++; 936 } 937 return checkFieldTypeSignature(signature, pos); 938 } 939 940 /** 941 * Checks a type variable signature. 942 * 943 * @param signature 944 * a string containing the signature that must be checked. 945 * @param pos 946 * index of first character to be checked. 947 * @return the index of the first character after the checked part. 948 */ 949 private static int checkTypeVariableSignature(final String signature, 950 int pos) { 951 // TypeVariableSignature: 952 // T Identifier ; 953 954 pos = checkChar('T', signature, pos); 955 pos = checkIdentifier(signature, pos); 956 return checkChar(';', signature, pos); 957 } 958 959 /** 960 * Checks a type signature. 961 * 962 * @param signature 963 * a string containing the signature that must be checked. 964 * @param pos 965 * index of first character to be checked. 966 * @return the index of the first character after the checked part. 967 */ 968 private static int checkTypeSignature(final String signature, int pos) { 969 // TypeSignature: 970 // Z | C | B | S | I | F | J | D | FieldTypeSignature 971 972 switch (getChar(signature, pos)) { 973 case 'Z': 974 case 'C': 975 case 'B': 976 case 'S': 977 case 'I': 978 case 'F': 979 case 'J': 980 case 'D': 981 return pos + 1; 982 default: 983 return checkFieldTypeSignature(signature, pos); 984 } 985 } 986 987 /** 988 * Checks an identifier. 989 * 990 * @param signature 991 * a string containing the signature that must be checked. 992 * @param pos 993 * index of first character to be checked. 994 * @return the index of the first character after the checked part. 995 */ 996 private static int checkIdentifier(final String signature, int pos) { 997 if (!Character.isJavaIdentifierStart(getChar(signature, pos))) { 998 throw new IllegalArgumentException(signature 999 + ": identifier expected at index " + pos); 1000 } 1001 ++pos; 1002 while (Character.isJavaIdentifierPart(getChar(signature, pos))) { 1003 ++pos; 1004 } 1005 return pos; 1006 } 1007 1008 /** 1009 * Checks a single character. 1010 * 1011 * @param signature 1012 * a string containing the signature that must be checked. 1013 * @param pos 1014 * index of first character to be checked. 1015 * @return the index of the first character after the checked part. 1016 */ 1017 private static int checkChar(final char c, final String signature, int pos) { 1018 if (getChar(signature, pos) == c) { 1019 return pos + 1; 1020 } 1021 throw new IllegalArgumentException(signature + ": '" + c 1022 + "' expected at index " + pos); 1023 } 1024 1025 /** 1026 * Returns the signature car at the given index. 1027 * 1028 * @param signature 1029 * a signature. 1030 * @param pos 1031 * an index in signature. 1032 * @return the character at the given index, or 0 if there is no such 1033 * character. 1034 */ 1035 private static char getChar(final String signature, int pos) { 1036 return pos < signature.length() ? signature.charAt(pos) : (char) 0; 1037 } 1038 }