1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.util; 29 30 import java.io.FileInputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 import org.objectweb.asm.AnnotationVisitor; 39 import org.objectweb.asm.Attribute; 40 import org.objectweb.asm.ClassReader; 41 import org.objectweb.asm.ClassVisitor; 42 import org.objectweb.asm.ClassWriter; 43 import org.objectweb.asm.FieldVisitor; 44 import org.objectweb.asm.Label; 45 import org.objectweb.asm.MethodVisitor; 46 import org.objectweb.asm.ModuleVisitor; 47 import org.objectweb.asm.Opcodes; 48 import org.objectweb.asm.RecordComponentVisitor; 49 import org.objectweb.asm.Type; 50 import org.objectweb.asm.TypePath; 51 import org.objectweb.asm.TypeReference; 52 import org.objectweb.asm.tree.ClassNode; 53 import org.objectweb.asm.tree.MethodNode; 54 import org.objectweb.asm.tree.TryCatchBlockNode; 55 import org.objectweb.asm.tree.analysis.Analyzer; 56 import org.objectweb.asm.tree.analysis.AnalyzerException; 57 import org.objectweb.asm.tree.analysis.BasicValue; 58 import org.objectweb.asm.tree.analysis.Frame; 59 import org.objectweb.asm.tree.analysis.SimpleVerifier; 60 61 /** 62 * A {@link ClassVisitor} that checks that its methods are properly used. More precisely this class 63 * adapter checks each method call individually, based <i>only</i> on its arguments, but does 64 * <i>not</i> check the <i>sequence</i> of method calls. For example, the invalid sequence {@code 65 * visitField(ACC_PUBLIC, "i", "I", null)} {@code visitField(ACC_PUBLIC, "i", "D", null)} will 66 * <i>not</i> be detected by this class adapter. 67 * 68 * <p><code>CheckClassAdapter</code> can be also used to verify bytecode transformations in order to 69 * make sure that the transformed bytecode is sane. For example: 70 * 71 * <pre> 72 * InputStream inputStream = ...; // get bytes for the source class 73 * ClassReader classReader = new ClassReader(inputStream); 74 * ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); 75 * ClassVisitor classVisitor = new <b>MyClassAdapter</b>(new CheckClassAdapter(classWriter, true)); 76 * classReader.accept(classVisitor, 0); 77 * 78 * StringWriter stringWriter = new StringWriter(); 79 * PrintWriter printWriter = new PrintWriter(stringWriter); 80 * CheckClassAdapter.verify(new ClassReader(classWriter.toByteArray()), false, printWriter); 81 * assertTrue(stringWriter.toString().isEmpty()); 82 * </pre> 83 * 84 * <p>The above code pass the transformed bytecode through a <code>CheckClassAdapter</code>, with 85 * data flow checks enabled. These checks are not exactly the same as the JVM verification, but 86 * provide some basic type checking for each method instruction. If the bytecode has errors, the 87 * output text shows the erroneous instruction number, and a dump of the failed method with 88 * information about the type of the local variables and of the operand stack slots for each 89 * instruction. For example (format is - insnNumber locals : stack): 90 * 91 * <pre> 92 * org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found . 93 * at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:...) 94 * at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:...) 95 * ... 96 * remove()V 97 * 00000 LinkedBlockingQueue$Itr . . . . . . . . : ICONST_0 98 * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I ISTORE 2 99 * 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : 100 * ... 101 * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : ILOAD 1 102 * 00072 <b>?</b> INVOKESPECIAL java/lang/Integer.<init> (I)V 103 * ... 104 * </pre> 105 * 106 * <p>The above output shows that the local variable 1, loaded by the <code>ILOAD 1</code> 107 * instruction at position <code>00071</code> is not initialized, whereas the local variable 2 is 108 * initialized and contains an int value. 109 * 110 * @author Eric Bruneton 111 */ 112 public class CheckClassAdapter extends ClassVisitor { 113 114 /** The help message shown when command line arguments are incorrect. */ 115 private static final String USAGE = 116 "Verifies the given class.\n" 117 + "Usage: CheckClassAdapter <fully qualified class name or class file name>"; 118 119 private static final String ERROR_AT = ": error at index "; 120 121 /** Whether the bytecode must be checked with a BasicVerifier. */ 122 private boolean checkDataFlow; 123 124 /** The class version number. */ 125 private int version; 126 127 /** Whether the {@link #visit} method has been called. */ 128 private boolean visitCalled; 129 130 /** Whether the {@link #visitModule} method has been called. */ 131 private boolean visitModuleCalled; 132 133 /** Whether the {@link #visitSource} method has been called. */ 134 private boolean visitSourceCalled; 135 136 /** Whether the {@link #visitOuterClass} method has been called. */ 137 private boolean visitOuterClassCalled; 138 139 /** Whether the {@link #visitNestHost} method has been called. */ 140 private boolean visitNestHostCalled; 141 142 /** 143 * The common package of all the nest members. Not {@literal null} if the visitNestMember method 144 * has been called. 145 */ 146 private String nestMemberPackageName; 147 148 /** Whether the {@link #visitEnd} method has been called. */ 149 private boolean visitEndCalled; 150 151 /** The index of the instruction designated by each visited label so far. */ 152 private Map<Label, Integer> labelInsnIndices; 153 154 // ----------------------------------------------------------------------------------------------- 155 // Constructors 156 // ----------------------------------------------------------------------------------------------- 157 158 /** 159 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>. 160 * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version. 161 * 162 * @param classVisitor the class visitor to which this adapter must delegate calls. 163 */ CheckClassAdapter(final ClassVisitor classVisitor)164 public CheckClassAdapter(final ClassVisitor classVisitor) { 165 this(classVisitor, /* checkDataFlow = */ true); 166 } 167 168 /** 169 * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>. 170 * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version. 171 * 172 * @param classVisitor the class visitor to which this adapter must delegate calls. 173 * @param checkDataFlow whether to perform basic data flow checks. 174 * @throws IllegalStateException If a subclass calls this constructor. 175 */ CheckClassAdapter(final ClassVisitor classVisitor, final boolean checkDataFlow)176 public CheckClassAdapter(final ClassVisitor classVisitor, final boolean checkDataFlow) { 177 this(/* latest api = */ Opcodes.ASM9, classVisitor, checkDataFlow); 178 if (getClass() != CheckClassAdapter.class) { 179 throw new IllegalStateException(); 180 } 181 } 182 183 /** 184 * Constructs a new {@link CheckClassAdapter}. 185 * 186 * @param api the ASM API version implemented by this visitor. Must be one of the {@code 187 * ASM}<i>x</i> values in {@link Opcodes}. 188 * @param classVisitor the class visitor to which this adapter must delegate calls. 189 * @param checkDataFlow {@literal true} to perform basic data flow checks, or {@literal false} to 190 * not perform any data flow check (see {@link CheckMethodAdapter}). 191 */ CheckClassAdapter( final int api, final ClassVisitor classVisitor, final boolean checkDataFlow)192 protected CheckClassAdapter( 193 final int api, final ClassVisitor classVisitor, final boolean checkDataFlow) { 194 super(api, classVisitor); 195 this.labelInsnIndices = new HashMap<>(); 196 this.checkDataFlow = checkDataFlow; 197 } 198 199 // ----------------------------------------------------------------------------------------------- 200 // Implementation of the ClassVisitor interface 201 // ----------------------------------------------------------------------------------------------- 202 203 @Override visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)204 public void visit( 205 final int version, 206 final int access, 207 final String name, 208 final String signature, 209 final String superName, 210 final String[] interfaces) { 211 if (visitCalled) { 212 throw new IllegalStateException("visit must be called only once"); 213 } 214 visitCalled = true; 215 checkState(); 216 checkAccess( 217 access, 218 Opcodes.ACC_PUBLIC 219 | Opcodes.ACC_FINAL 220 | Opcodes.ACC_SUPER 221 | Opcodes.ACC_INTERFACE 222 | Opcodes.ACC_ABSTRACT 223 | Opcodes.ACC_SYNTHETIC 224 | Opcodes.ACC_ANNOTATION 225 | Opcodes.ACC_ENUM 226 | Opcodes.ACC_DEPRECATED 227 | Opcodes.ACC_RECORD 228 | Opcodes.ACC_MODULE); 229 if (name == null) { 230 throw new IllegalArgumentException("Illegal class name (null)"); 231 } 232 if (!name.endsWith("package-info") && !name.endsWith("module-info")) { 233 CheckMethodAdapter.checkInternalName(version, name, "class name"); 234 } 235 if ("java/lang/Object".equals(name)) { 236 if (superName != null) { 237 throw new IllegalArgumentException( 238 "The super class name of the Object class must be 'null'"); 239 } 240 } else if (name.endsWith("module-info")) { 241 if (superName != null) { 242 throw new IllegalArgumentException( 243 "The super class name of a module-info class must be 'null'"); 244 } 245 } else { 246 CheckMethodAdapter.checkInternalName(version, superName, "super class name"); 247 } 248 if (signature != null) { 249 checkClassSignature(signature); 250 } 251 if ((access & Opcodes.ACC_INTERFACE) != 0 && !"java/lang/Object".equals(superName)) { 252 throw new IllegalArgumentException( 253 "The super class name of interfaces must be 'java/lang/Object'"); 254 } 255 if (interfaces != null) { 256 for (int i = 0; i < interfaces.length; ++i) { 257 CheckMethodAdapter.checkInternalName( 258 version, interfaces[i], "interface name at index " + i); 259 } 260 } 261 this.version = version; 262 super.visit(version, access, name, signature, superName, interfaces); 263 } 264 265 @Override visitSource(final String file, final String debug)266 public void visitSource(final String file, final String debug) { 267 checkState(); 268 if (visitSourceCalled) { 269 throw new IllegalStateException("visitSource can be called only once."); 270 } 271 visitSourceCalled = true; 272 super.visitSource(file, debug); 273 } 274 275 @Override visitModule(final String name, final int access, final String version)276 public ModuleVisitor visitModule(final String name, final int access, final String version) { 277 checkState(); 278 if (visitModuleCalled) { 279 throw new IllegalStateException("visitModule can be called only once."); 280 } 281 visitModuleCalled = true; 282 checkFullyQualifiedName(this.version, name, "module name"); 283 checkAccess(access, Opcodes.ACC_OPEN | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED); 284 CheckModuleAdapter checkModuleAdapter = 285 new CheckModuleAdapter( 286 api, super.visitModule(name, access, version), (access & Opcodes.ACC_OPEN) != 0); 287 checkModuleAdapter.classVersion = this.version; 288 return checkModuleAdapter; 289 } 290 291 @Override visitNestHost(final String nestHost)292 public void visitNestHost(final String nestHost) { 293 checkState(); 294 CheckMethodAdapter.checkInternalName(version, nestHost, "nestHost"); 295 if (visitNestHostCalled) { 296 throw new IllegalStateException("visitNestHost can be called only once."); 297 } 298 if (nestMemberPackageName != null) { 299 throw new IllegalStateException("visitNestHost and visitNestMember are mutually exclusive."); 300 } 301 visitNestHostCalled = true; 302 super.visitNestHost(nestHost); 303 } 304 305 @Override visitNestMember(final String nestMember)306 public void visitNestMember(final String nestMember) { 307 checkState(); 308 CheckMethodAdapter.checkInternalName(version, nestMember, "nestMember"); 309 if (visitNestHostCalled) { 310 throw new IllegalStateException( 311 "visitMemberOfNest and visitNestHost are mutually exclusive."); 312 } 313 String packageName = packageName(nestMember); 314 if (nestMemberPackageName == null) { 315 nestMemberPackageName = packageName; 316 } else if (!nestMemberPackageName.equals(packageName)) { 317 throw new IllegalStateException( 318 "nest member " + nestMember + " should be in the package " + nestMemberPackageName); 319 } 320 super.visitNestMember(nestMember); 321 } 322 323 @Override visitPermittedSubclass(final String permittedSubclass)324 public void visitPermittedSubclass(final String permittedSubclass) { 325 checkState(); 326 CheckMethodAdapter.checkInternalName(version, permittedSubclass, "permittedSubclass"); 327 super.visitPermittedSubclass(permittedSubclass); 328 } 329 330 @Override visitOuterClass(final String owner, final String name, final String descriptor)331 public void visitOuterClass(final String owner, final String name, final String descriptor) { 332 checkState(); 333 if (visitOuterClassCalled) { 334 throw new IllegalStateException("visitOuterClass can be called only once."); 335 } 336 visitOuterClassCalled = true; 337 if (owner == null) { 338 throw new IllegalArgumentException("Illegal outer class owner"); 339 } 340 if (descriptor != null) { 341 CheckMethodAdapter.checkMethodDescriptor(version, descriptor); 342 } 343 super.visitOuterClass(owner, name, descriptor); 344 } 345 346 @Override visitInnerClass( final String name, final String outerName, final String innerName, final int access)347 public void visitInnerClass( 348 final String name, final String outerName, final String innerName, final int access) { 349 checkState(); 350 CheckMethodAdapter.checkInternalName(version, name, "class name"); 351 if (outerName != null) { 352 CheckMethodAdapter.checkInternalName(version, outerName, "outer class name"); 353 } 354 if (innerName != null) { 355 int startIndex = 0; 356 while (startIndex < innerName.length() && Character.isDigit(innerName.charAt(startIndex))) { 357 startIndex++; 358 } 359 if (startIndex == 0 || startIndex < innerName.length()) { 360 CheckMethodAdapter.checkIdentifier(version, innerName, startIndex, -1, "inner class name"); 361 } 362 } 363 checkAccess( 364 access, 365 Opcodes.ACC_PUBLIC 366 | Opcodes.ACC_PRIVATE 367 | Opcodes.ACC_PROTECTED 368 | Opcodes.ACC_STATIC 369 | Opcodes.ACC_FINAL 370 | Opcodes.ACC_INTERFACE 371 | Opcodes.ACC_ABSTRACT 372 | Opcodes.ACC_SYNTHETIC 373 | Opcodes.ACC_ANNOTATION 374 | Opcodes.ACC_ENUM); 375 super.visitInnerClass(name, outerName, innerName, access); 376 } 377 378 @Override visitRecordComponent( final String name, final String descriptor, final String signature)379 public RecordComponentVisitor visitRecordComponent( 380 final String name, final String descriptor, final String signature) { 381 checkState(); 382 CheckMethodAdapter.checkUnqualifiedName(version, name, "record component name"); 383 CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false); 384 if (signature != null) { 385 checkFieldSignature(signature); 386 } 387 return new CheckRecordComponentAdapter( 388 api, super.visitRecordComponent(name, descriptor, signature)); 389 } 390 391 @Override visitField( final int access, final String name, final String descriptor, final String signature, final Object value)392 public FieldVisitor visitField( 393 final int access, 394 final String name, 395 final String descriptor, 396 final String signature, 397 final Object value) { 398 checkState(); 399 checkAccess( 400 access, 401 Opcodes.ACC_PUBLIC 402 | Opcodes.ACC_PRIVATE 403 | Opcodes.ACC_PROTECTED 404 | Opcodes.ACC_STATIC 405 | Opcodes.ACC_FINAL 406 | Opcodes.ACC_VOLATILE 407 | Opcodes.ACC_TRANSIENT 408 | Opcodes.ACC_SYNTHETIC 409 | Opcodes.ACC_ENUM 410 | Opcodes.ACC_MANDATED 411 | Opcodes.ACC_DEPRECATED); 412 CheckMethodAdapter.checkUnqualifiedName(version, name, "field name"); 413 CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false); 414 if (signature != null) { 415 checkFieldSignature(signature); 416 } 417 if (value != null) { 418 CheckMethodAdapter.checkConstant(value); 419 } 420 return new CheckFieldAdapter(api, super.visitField(access, name, descriptor, signature, value)); 421 } 422 423 @Override visitMethod( final int access, final String name, final String descriptor, final String signature, final String[] exceptions)424 public MethodVisitor visitMethod( 425 final int access, 426 final String name, 427 final String descriptor, 428 final String signature, 429 final String[] exceptions) { 430 checkState(); 431 checkMethodAccess( 432 version, 433 access, 434 Opcodes.ACC_PUBLIC 435 | Opcodes.ACC_PRIVATE 436 | Opcodes.ACC_PROTECTED 437 | Opcodes.ACC_STATIC 438 | Opcodes.ACC_FINAL 439 | Opcodes.ACC_SYNCHRONIZED 440 | Opcodes.ACC_BRIDGE 441 | Opcodes.ACC_VARARGS 442 | Opcodes.ACC_NATIVE 443 | Opcodes.ACC_ABSTRACT 444 | Opcodes.ACC_STRICT 445 | Opcodes.ACC_SYNTHETIC 446 | Opcodes.ACC_MANDATED 447 | Opcodes.ACC_DEPRECATED); 448 if (!"<init>".equals(name) && !"<clinit>".equals(name)) { 449 CheckMethodAdapter.checkMethodIdentifier(version, name, "method name"); 450 } 451 CheckMethodAdapter.checkMethodDescriptor(version, descriptor); 452 if (signature != null) { 453 checkMethodSignature(signature); 454 } 455 if (exceptions != null) { 456 for (int i = 0; i < exceptions.length; ++i) { 457 CheckMethodAdapter.checkInternalName( 458 version, exceptions[i], "exception name at index " + i); 459 } 460 } 461 CheckMethodAdapter checkMethodAdapter; 462 MethodVisitor methodVisitor = 463 super.visitMethod(access, name, descriptor, signature, exceptions); 464 if (checkDataFlow) { 465 if (cv instanceof ClassWriter) { 466 methodVisitor = 467 new CheckMethodAdapter.MethodWriterWrapper( 468 api, version, (ClassWriter) cv, methodVisitor); 469 } 470 checkMethodAdapter = 471 new CheckMethodAdapter(api, access, name, descriptor, methodVisitor, labelInsnIndices); 472 } else { 473 checkMethodAdapter = new CheckMethodAdapter(api, methodVisitor, labelInsnIndices); 474 } 475 checkMethodAdapter.version = version; 476 return checkMethodAdapter; 477 } 478 479 @Override visitAnnotation(final String descriptor, final boolean visible)480 public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { 481 checkState(); 482 CheckMethodAdapter.checkDescriptor(version, descriptor, false); 483 return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible)); 484 } 485 486 @Override visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)487 public AnnotationVisitor visitTypeAnnotation( 488 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 489 checkState(); 490 int sort = new TypeReference(typeRef).getSort(); 491 if (sort != TypeReference.CLASS_TYPE_PARAMETER 492 && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND 493 && sort != TypeReference.CLASS_EXTENDS) { 494 throw new IllegalArgumentException( 495 "Invalid type reference sort 0x" + Integer.toHexString(sort)); 496 } 497 checkTypeRef(typeRef); 498 CheckMethodAdapter.checkDescriptor(version, descriptor, false); 499 return new CheckAnnotationAdapter( 500 super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); 501 } 502 503 @Override visitAttribute(final Attribute attribute)504 public void visitAttribute(final Attribute attribute) { 505 checkState(); 506 if (attribute == null) { 507 throw new IllegalArgumentException("Invalid attribute (must not be null)"); 508 } 509 super.visitAttribute(attribute); 510 } 511 512 @Override visitEnd()513 public void visitEnd() { 514 checkState(); 515 visitEndCalled = true; 516 super.visitEnd(); 517 } 518 519 // ----------------------------------------------------------------------------------------------- 520 // Utility methods 521 // ----------------------------------------------------------------------------------------------- 522 523 /** Checks that the visit method has been called and that visitEnd has not been called. */ checkState()524 private void checkState() { 525 if (!visitCalled) { 526 throw new IllegalStateException("Cannot visit member before visit has been called."); 527 } 528 if (visitEndCalled) { 529 throw new IllegalStateException("Cannot visit member after visitEnd has been called."); 530 } 531 } 532 533 /** 534 * Checks that the given access flags do not contain invalid flags. This method also checks that 535 * mutually incompatible flags are not set simultaneously. 536 * 537 * @param access the access flags to be checked. 538 * @param possibleAccess the valid access flags. 539 */ checkAccess(final int access, final int possibleAccess)540 static void checkAccess(final int access, final int possibleAccess) { 541 if ((access & ~possibleAccess) != 0) { 542 throw new IllegalArgumentException("Invalid access flags: " + access); 543 } 544 int publicProtectedPrivate = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE; 545 if (Integer.bitCount(access & publicProtectedPrivate) > 1) { 546 throw new IllegalArgumentException( 547 "public, protected and private are mutually exclusive: " + access); 548 } 549 if (Integer.bitCount(access & (Opcodes.ACC_FINAL | Opcodes.ACC_ABSTRACT)) > 1) { 550 throw new IllegalArgumentException("final and abstract are mutually exclusive: " + access); 551 } 552 } 553 554 /** 555 * Checks that the given access flags do not contain invalid flags for a method. This method also 556 * checks that mutually incompatible flags are not set simultaneously. 557 * 558 * @param version the class version. 559 * @param access the method access flags to be checked. 560 * @param possibleAccess the valid access flags. 561 */ checkMethodAccess( final int version, final int access, final int possibleAccess)562 private static void checkMethodAccess( 563 final int version, final int access, final int possibleAccess) { 564 checkAccess(access, possibleAccess); 565 if ((version & 0xFFFF) < Opcodes.V17 566 && Integer.bitCount(access & (Opcodes.ACC_STRICT | Opcodes.ACC_ABSTRACT)) > 1) { 567 throw new IllegalArgumentException("strictfp and abstract are mutually exclusive: " + access); 568 } 569 } 570 571 /** 572 * Checks that the given name is a fully qualified name, using dots. 573 * 574 * @param version the class version. 575 * @param name the name to be checked. 576 * @param source the source of 'name' (e.g 'module' for a module name). 577 */ checkFullyQualifiedName(final int version, final String name, final String source)578 static void checkFullyQualifiedName(final int version, final String name, final String source) { 579 try { 580 int startIndex = 0; 581 int dotIndex; 582 while ((dotIndex = name.indexOf('.', startIndex + 1)) != -1) { 583 CheckMethodAdapter.checkIdentifier(version, name, startIndex, dotIndex, null); 584 startIndex = dotIndex + 1; 585 } 586 CheckMethodAdapter.checkIdentifier(version, name, startIndex, name.length(), null); 587 } catch (IllegalArgumentException e) { 588 throw new IllegalArgumentException( 589 "Invalid " + source + " (must be a fully qualified name): " + name, e); 590 } 591 } 592 593 /** 594 * Checks a class signature. 595 * 596 * @param signature a string containing the signature that must be checked. 597 */ checkClassSignature(final String signature)598 public static void checkClassSignature(final String signature) { 599 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 600 // ClassSignature: 601 // [TypeParameters] SuperclassSignature SuperinterfaceSignature* 602 // SuperclassSignature: 603 // ClassTypeSignature 604 // SuperinterfaceSignature: 605 // ClassTypeSignature 606 int pos = 0; 607 if (getChar(signature, 0) == '<') { 608 pos = checkTypeParameters(signature, pos); 609 } 610 pos = checkClassTypeSignature(signature, pos); 611 while (getChar(signature, pos) == 'L') { 612 pos = checkClassTypeSignature(signature, pos); 613 } 614 if (pos != signature.length()) { 615 throw new IllegalArgumentException(signature + ERROR_AT + pos); 616 } 617 } 618 619 /** 620 * Checks a method signature. 621 * 622 * @param signature a string containing the signature that must be checked. 623 */ checkMethodSignature(final String signature)624 public static void checkMethodSignature(final String signature) { 625 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 626 // MethodSignature: 627 // [TypeParameters] ( JavaTypeSignature* ) Result ThrowsSignature* 628 // Result: 629 // JavaTypeSignature 630 // VoidDescriptor 631 // ThrowsSignature: 632 // ^ ClassTypeSignature 633 // ^ TypeVariableSignature 634 int pos = 0; 635 if (getChar(signature, 0) == '<') { 636 pos = checkTypeParameters(signature, pos); 637 } 638 pos = checkChar('(', signature, pos); 639 while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) { 640 pos = checkJavaTypeSignature(signature, pos); 641 } 642 pos = checkChar(')', signature, pos); 643 if (getChar(signature, pos) == 'V') { 644 ++pos; 645 } else { 646 pos = checkJavaTypeSignature(signature, pos); 647 } 648 while (getChar(signature, pos) == '^') { 649 ++pos; 650 if (getChar(signature, pos) == 'L') { 651 pos = checkClassTypeSignature(signature, pos); 652 } else { 653 pos = checkTypeVariableSignature(signature, pos); 654 } 655 } 656 if (pos != signature.length()) { 657 throw new IllegalArgumentException(signature + ERROR_AT + pos); 658 } 659 } 660 661 /** 662 * Checks a field signature. 663 * 664 * @param signature a string containing the signature that must be checked. 665 */ checkFieldSignature(final String signature)666 public static void checkFieldSignature(final String signature) { 667 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 668 // FieldSignature: 669 // ReferenceTypeSignature 670 int pos = checkReferenceTypeSignature(signature, 0); 671 if (pos != signature.length()) { 672 throw new IllegalArgumentException(signature + ERROR_AT + pos); 673 } 674 } 675 676 /** 677 * Checks the type parameters of a class or method signature. 678 * 679 * @param signature a string containing the signature that must be checked. 680 * @param startPos index of first character to be checked. 681 * @return the index of the first character after the checked part. 682 */ checkTypeParameters(final String signature, final int startPos)683 private static int checkTypeParameters(final String signature, final int startPos) { 684 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 685 // TypeParameters: 686 // < TypeParameter TypeParameter* > 687 int pos = startPos; 688 pos = checkChar('<', signature, pos); 689 pos = checkTypeParameter(signature, pos); 690 while (getChar(signature, pos) != '>') { 691 pos = checkTypeParameter(signature, pos); 692 } 693 return pos + 1; 694 } 695 696 /** 697 * Checks a type parameter of a class or method signature. 698 * 699 * @param signature a string containing the signature that must be checked. 700 * @param startPos index of first character to be checked. 701 * @return the index of the first character after the checked part. 702 */ checkTypeParameter(final String signature, final int startPos)703 private static int checkTypeParameter(final String signature, final int startPos) { 704 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 705 // TypeParameter: 706 // Identifier ClassBound InterfaceBound* 707 // ClassBound: 708 // : [ReferenceTypeSignature] 709 // InterfaceBound: 710 // : ReferenceTypeSignature 711 int pos = startPos; 712 pos = checkSignatureIdentifier(signature, pos); 713 pos = checkChar(':', signature, pos); 714 if ("L[T".indexOf(getChar(signature, pos)) != -1) { 715 pos = checkReferenceTypeSignature(signature, pos); 716 } 717 while (getChar(signature, pos) == ':') { 718 pos = checkReferenceTypeSignature(signature, pos + 1); 719 } 720 return pos; 721 } 722 723 /** 724 * Checks a reference type signature. 725 * 726 * @param signature a string containing the signature that must be checked. 727 * @param pos index of first character to be checked. 728 * @return the index of the first character after the checked part. 729 */ checkReferenceTypeSignature(final String signature, final int pos)730 private static int checkReferenceTypeSignature(final String signature, final int pos) { 731 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 732 // ReferenceTypeSignature: 733 // ClassTypeSignature 734 // TypeVariableSignature 735 // ArrayTypeSignature 736 // ArrayTypeSignature: 737 // [ JavaTypeSignature 738 switch (getChar(signature, pos)) { 739 case 'L': 740 return checkClassTypeSignature(signature, pos); 741 case '[': 742 return checkJavaTypeSignature(signature, pos + 1); 743 default: 744 return checkTypeVariableSignature(signature, pos); 745 } 746 } 747 748 /** 749 * Checks a class type signature. 750 * 751 * @param signature a string containing the signature that must be checked. 752 * @param startPos index of first character to be checked. 753 * @return the index of the first character after the checked part. 754 */ checkClassTypeSignature(final String signature, final int startPos)755 private static int checkClassTypeSignature(final String signature, final int startPos) { 756 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 757 // ClassTypeSignature: 758 // L [PackageSpecifier] SimpleClassTypeSignature ClassTypeSignatureSuffix* ; 759 // PackageSpecifier: 760 // Identifier / PackageSpecifier* 761 // SimpleClassTypeSignature: 762 // Identifier [TypeArguments] 763 // ClassTypeSignatureSuffix: 764 // . SimpleClassTypeSignature 765 int pos = startPos; 766 pos = checkChar('L', signature, pos); 767 pos = checkSignatureIdentifier(signature, pos); 768 while (getChar(signature, pos) == '/') { 769 pos = checkSignatureIdentifier(signature, pos + 1); 770 } 771 if (getChar(signature, pos) == '<') { 772 pos = checkTypeArguments(signature, pos); 773 } 774 while (getChar(signature, pos) == '.') { 775 pos = checkSignatureIdentifier(signature, pos + 1); 776 if (getChar(signature, pos) == '<') { 777 pos = checkTypeArguments(signature, pos); 778 } 779 } 780 return checkChar(';', signature, pos); 781 } 782 783 /** 784 * Checks the type arguments in a class type signature. 785 * 786 * @param signature a string containing the signature that must be checked. 787 * @param startPos index of first character to be checked. 788 * @return the index of the first character after the checked part. 789 */ checkTypeArguments(final String signature, final int startPos)790 private static int checkTypeArguments(final String signature, final int startPos) { 791 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 792 // TypeArguments: 793 // < TypeArgument TypeArgument* > 794 int pos = startPos; 795 pos = checkChar('<', signature, pos); 796 pos = checkTypeArgument(signature, pos); 797 while (getChar(signature, pos) != '>') { 798 pos = checkTypeArgument(signature, pos); 799 } 800 return pos + 1; 801 } 802 803 /** 804 * Checks a type argument in a class type signature. 805 * 806 * @param signature a string containing the signature that must be checked. 807 * @param startPos index of first character to be checked. 808 * @return the index of the first character after the checked part. 809 */ checkTypeArgument(final String signature, final int startPos)810 private static int checkTypeArgument(final String signature, final int startPos) { 811 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 812 // TypeArgument: 813 // [WildcardIndicator] ReferenceTypeSignature 814 // * 815 // WildcardIndicator: 816 // + 817 // - 818 int pos = startPos; 819 char c = getChar(signature, pos); 820 if (c == '*') { 821 return pos + 1; 822 } else if (c == '+' || c == '-') { 823 pos++; 824 } 825 return checkReferenceTypeSignature(signature, pos); 826 } 827 828 /** 829 * Checks a type variable signature. 830 * 831 * @param signature a string containing the signature that must be checked. 832 * @param startPos index of first character to be checked. 833 * @return the index of the first character after the checked part. 834 */ checkTypeVariableSignature(final String signature, final int startPos)835 private static int checkTypeVariableSignature(final String signature, final int startPos) { 836 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 837 // TypeVariableSignature: 838 // T Identifier ; 839 int pos = startPos; 840 pos = checkChar('T', signature, pos); 841 pos = checkSignatureIdentifier(signature, pos); 842 return checkChar(';', signature, pos); 843 } 844 845 /** 846 * Checks a Java type signature. 847 * 848 * @param signature a string containing the signature that must be checked. 849 * @param startPos index of first character to be checked. 850 * @return the index of the first character after the checked part. 851 */ checkJavaTypeSignature(final String signature, final int startPos)852 private static int checkJavaTypeSignature(final String signature, final int startPos) { 853 // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1: 854 // JavaTypeSignature: 855 // ReferenceTypeSignature 856 // BaseType 857 // BaseType: 858 // (one of) 859 // B C D F I J S Z 860 int pos = startPos; 861 switch (getChar(signature, pos)) { 862 case 'B': 863 case 'C': 864 case 'D': 865 case 'F': 866 case 'I': 867 case 'J': 868 case 'S': 869 case 'Z': 870 return pos + 1; 871 default: 872 return checkReferenceTypeSignature(signature, pos); 873 } 874 } 875 876 /** 877 * Checks an identifier. 878 * 879 * @param signature a string containing the signature that must be checked. 880 * @param startPos index of first character to be checked. 881 * @return the index of the first character after the checked part. 882 */ checkSignatureIdentifier(final String signature, final int startPos)883 private static int checkSignatureIdentifier(final String signature, final int startPos) { 884 int pos = startPos; 885 while (pos < signature.length() && ".;[/<>:".indexOf(signature.codePointAt(pos)) == -1) { 886 pos = signature.offsetByCodePoints(pos, 1); 887 } 888 if (pos == startPos) { 889 throw new IllegalArgumentException(signature + ": identifier expected at index " + startPos); 890 } 891 return pos; 892 } 893 894 /** 895 * Checks a single character. 896 * 897 * @param c a character. 898 * @param signature a string containing the signature that must be checked. 899 * @param pos index of first character to be checked. 900 * @return the index of the first character after the checked part. 901 */ checkChar(final char c, final String signature, final int pos)902 private static int checkChar(final char c, final String signature, final int pos) { 903 if (getChar(signature, pos) == c) { 904 return pos + 1; 905 } 906 throw new IllegalArgumentException(signature + ": '" + c + "' expected at index " + pos); 907 } 908 909 /** 910 * Returns the string character at the given index, or 0. 911 * 912 * @param string a string. 913 * @param pos an index in 'string'. 914 * @return the character at the given index, or 0 if there is no such character. 915 */ getChar(final String string, final int pos)916 private static char getChar(final String string, final int pos) { 917 return pos < string.length() ? string.charAt(pos) : (char) 0; 918 } 919 920 /** 921 * Checks the reference to a type in a type annotation. 922 * 923 * @param typeRef a reference to an annotated type. 924 */ checkTypeRef(final int typeRef)925 static void checkTypeRef(final int typeRef) { 926 int mask = 0; 927 switch (typeRef >>> 24) { 928 case TypeReference.CLASS_TYPE_PARAMETER: 929 case TypeReference.METHOD_TYPE_PARAMETER: 930 case TypeReference.METHOD_FORMAL_PARAMETER: 931 mask = 0xFFFF0000; 932 break; 933 case TypeReference.FIELD: 934 case TypeReference.METHOD_RETURN: 935 case TypeReference.METHOD_RECEIVER: 936 case TypeReference.LOCAL_VARIABLE: 937 case TypeReference.RESOURCE_VARIABLE: 938 case TypeReference.INSTANCEOF: 939 case TypeReference.NEW: 940 case TypeReference.CONSTRUCTOR_REFERENCE: 941 case TypeReference.METHOD_REFERENCE: 942 mask = 0xFF000000; 943 break; 944 case TypeReference.CLASS_EXTENDS: 945 case TypeReference.CLASS_TYPE_PARAMETER_BOUND: 946 case TypeReference.METHOD_TYPE_PARAMETER_BOUND: 947 case TypeReference.THROWS: 948 case TypeReference.EXCEPTION_PARAMETER: 949 mask = 0xFFFFFF00; 950 break; 951 case TypeReference.CAST: 952 case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: 953 case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: 954 case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: 955 case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: 956 mask = 0xFF0000FF; 957 break; 958 default: 959 break; 960 } 961 if (mask == 0 || (typeRef & ~mask) != 0) { 962 throw new IllegalArgumentException( 963 "Invalid type reference 0x" + Integer.toHexString(typeRef)); 964 } 965 } 966 967 /** 968 * Returns the package name of an internal name. 969 * 970 * @param name an internal name. 971 * @return the package name or "" if there is no package. 972 */ packageName(final String name)973 private static String packageName(final String name) { 974 int index = name.lastIndexOf('/'); 975 if (index == -1) { 976 return ""; 977 } 978 return name.substring(0, index); 979 } 980 981 // ----------------------------------------------------------------------------------------------- 982 // Static verification methods 983 // ----------------------------------------------------------------------------------------------- 984 985 /** 986 * Checks the given class. 987 * 988 * <p>Usage: CheckClassAdapter <binary class name or class file name> 989 * 990 * @param args the command line arguments. 991 * @throws IOException if the class cannot be found, or if an IO exception occurs. 992 */ main(final String[] args)993 public static void main(final String[] args) throws IOException { 994 main(args, new PrintWriter(System.err, true)); 995 } 996 997 /** 998 * Checks the given class. 999 * 1000 * @param args the command line arguments. 1001 * @param logger where to log errors. 1002 * @throws IOException if the class cannot be found, or if an IO exception occurs. 1003 */ main(final String[] args, final PrintWriter logger)1004 static void main(final String[] args, final PrintWriter logger) throws IOException { 1005 if (args.length != 1) { 1006 logger.println(USAGE); 1007 return; 1008 } 1009 1010 ClassReader classReader; 1011 if (args[0].endsWith(".class")) { 1012 // Can't fix PMD warning for 1.5 compatibility. 1013 try (InputStream inputStream = new FileInputStream(args[0])) { // NOPMD(AvoidFileStream) 1014 classReader = new ClassReader(inputStream); 1015 } 1016 } else { 1017 classReader = new ClassReader(args[0]); 1018 } 1019 1020 verify(classReader, false, logger); 1021 } 1022 1023 /** 1024 * Checks the given class. 1025 * 1026 * @param classReader the class to be checked. 1027 * @param printResults whether to print the results of the bytecode verification. 1028 * @param printWriter where the results (or the stack trace in case of error) must be printed. 1029 */ verify( final ClassReader classReader, final boolean printResults, final PrintWriter printWriter)1030 public static void verify( 1031 final ClassReader classReader, final boolean printResults, final PrintWriter printWriter) { 1032 verify(classReader, null, printResults, printWriter); 1033 } 1034 1035 /** 1036 * Checks the given class. 1037 * 1038 * @param classReader the class to be checked. 1039 * @param loader a <code>ClassLoader</code> which will be used to load referenced classes. May be 1040 * {@literal null}. 1041 * @param printResults whether to print the results of the bytecode verification. 1042 * @param printWriter where the results (or the stack trace in case of error) must be printed. 1043 */ verify( final ClassReader classReader, final ClassLoader loader, final boolean printResults, final PrintWriter printWriter)1044 public static void verify( 1045 final ClassReader classReader, 1046 final ClassLoader loader, 1047 final boolean printResults, 1048 final PrintWriter printWriter) { 1049 ClassNode classNode = new ClassNode(); 1050 classReader.accept( 1051 new CheckClassAdapter(/*latest*/ Opcodes.ASM10_EXPERIMENTAL, classNode, false) {}, 1052 ClassReader.SKIP_DEBUG); 1053 1054 Type syperType = classNode.superName == null ? null : Type.getObjectType(classNode.superName); 1055 List<MethodNode> methods = classNode.methods; 1056 1057 List<Type> interfaces = new ArrayList<>(); 1058 for (String interfaceName : classNode.interfaces) { 1059 interfaces.add(Type.getObjectType(interfaceName)); 1060 } 1061 1062 for (MethodNode method : methods) { 1063 SimpleVerifier verifier = 1064 new SimpleVerifier( 1065 Type.getObjectType(classNode.name), 1066 syperType, 1067 interfaces, 1068 (classNode.access & Opcodes.ACC_INTERFACE) != 0); 1069 Analyzer<BasicValue> analyzer = new Analyzer<>(verifier); 1070 if (loader != null) { 1071 verifier.setClassLoader(loader); 1072 } 1073 try { 1074 analyzer.analyze(classNode.name, method); 1075 } catch (AnalyzerException e) { 1076 e.printStackTrace(printWriter); 1077 } 1078 if (printResults) { 1079 printAnalyzerResult(method, analyzer, printWriter); 1080 } 1081 } 1082 printWriter.flush(); 1083 } 1084 printAnalyzerResult( final MethodNode method, final Analyzer<BasicValue> analyzer, final PrintWriter printWriter)1085 static void printAnalyzerResult( 1086 final MethodNode method, final Analyzer<BasicValue> analyzer, final PrintWriter printWriter) { 1087 Textifier textifier = new Textifier(); 1088 TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(textifier); 1089 1090 printWriter.println(method.name + method.desc); 1091 for (int i = 0; i < method.instructions.size(); ++i) { 1092 method.instructions.get(i).accept(traceMethodVisitor); 1093 1094 StringBuilder stringBuilder = new StringBuilder(); 1095 Frame<BasicValue> frame = analyzer.getFrames()[i]; 1096 if (frame == null) { 1097 stringBuilder.append('?'); 1098 } else { 1099 for (int j = 0; j < frame.getLocals(); ++j) { 1100 stringBuilder.append(getUnqualifiedName(frame.getLocal(j).toString())).append(' '); 1101 } 1102 stringBuilder.append(" : "); 1103 for (int j = 0; j < frame.getStackSize(); ++j) { 1104 stringBuilder.append(getUnqualifiedName(frame.getStack(j).toString())).append(' '); 1105 } 1106 } 1107 while (stringBuilder.length() < method.maxStack + method.maxLocals + 1) { 1108 stringBuilder.append(' '); 1109 } 1110 printWriter.print(Integer.toString(i + 100000).substring(1)); 1111 printWriter.print( 1112 " " + stringBuilder + " : " + textifier.text.get(textifier.text.size() - 1)); 1113 } 1114 for (TryCatchBlockNode tryCatchBlock : method.tryCatchBlocks) { 1115 tryCatchBlock.accept(traceMethodVisitor); 1116 printWriter.print(" " + textifier.text.get(textifier.text.size() - 1)); 1117 } 1118 printWriter.println(); 1119 } 1120 getUnqualifiedName(final String name)1121 private static String getUnqualifiedName(final String name) { 1122 int lastSlashIndex = name.lastIndexOf('/'); 1123 if (lastSlashIndex == -1) { 1124 return name; 1125 } else { 1126 int endIndex = name.length(); 1127 if (name.charAt(endIndex - 1) == ';') { 1128 endIndex--; 1129 } 1130 int lastBracketIndex = name.lastIndexOf('['); 1131 if (lastBracketIndex == -1) { 1132 return name.substring(lastSlashIndex + 1, endIndex); 1133 } 1134 return name.substring(0, lastBracketIndex + 1) + name.substring(lastSlashIndex + 1, endIndex); 1135 } 1136 } 1137 } 1138