• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.&lt;init&gt; (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 &lt;binary class name or class file name&gt;
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