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