You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2018/11/29 00:33:58 UTC
[11/46] tapestry-5 git commit: TAP5-2588: upgrading from ASM 6 to 7
for Java 9+ support
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1c71aec7/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
old mode 100644
new mode 100755
index f66971c..c0b129c
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/util/CheckClassAdapter.java
@@ -1,1035 +1,1066 @@
-/***
- * ASM: a very small and fast Java bytecode manipulation framework
- * Copyright (c) 2000-2011 INRIA, France Telecom
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 INRIA, France Telecom
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// 3. Neither the name of the copyright holders nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
package org.apache.tapestry5.internal.plastic.asm.util;
+import org.apache.tapestry5.internal.plastic.asm.*;
+import org.apache.tapestry5.internal.plastic.asm.tree.ClassNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.*;
+import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Frame;
+
import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
-import org.apache.tapestry5.internal.plastic.asm.Attribute;
-import org.apache.tapestry5.internal.plastic.asm.ClassReader;
-import org.apache.tapestry5.internal.plastic.asm.ClassVisitor;
-import org.apache.tapestry5.internal.plastic.asm.FieldVisitor;
-import org.apache.tapestry5.internal.plastic.asm.Label;
-import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
-import org.apache.tapestry5.internal.plastic.asm.ModuleVisitor;
-import org.apache.tapestry5.internal.plastic.asm.Opcodes;
-import org.apache.tapestry5.internal.plastic.asm.Type;
-import org.apache.tapestry5.internal.plastic.asm.TypePath;
-import org.apache.tapestry5.internal.plastic.asm.TypeReference;
-import org.apache.tapestry5.internal.plastic.asm.tree.ClassNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.MethodNode;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Analyzer;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.BasicValue;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.Frame;
-import org.apache.tapestry5.internal.plastic.asm.tree.analysis.SimpleVerifier;
-
/**
- * A {@link ClassVisitor} that checks that its methods are properly used. More
- * precisely this class adapter checks each method call individually, based
- * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
- * of method calls. For example, the invalid sequence
- * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
- * "i", "D", null)</tt> will <i>not</i> be detected by this class adapter.
- *
- * <p>
- * <code>CheckClassAdapter</code> can be also used to verify bytecode
- * transformations in order to make sure transformed bytecode is sane. For
- * example:
- *
+ * A {@link ClassVisitor} that checks that its methods are properly used. More precisely this class
+ * adapter checks each method call individually, based <i>only</i> on its arguments, but does
+ * <i>not</i> check the <i>sequence</i> of method calls. For example, the invalid sequence {@code
+ * visitField(ACC_PUBLIC, "i", "I", null)} {@code visitField(ACC_PUBLIC, "i", "D", null)} will
+ * <i>not</i> be detected by this class adapter.
+ *
+ * <p><code>CheckClassAdapter</code> can be also used to verify bytecode transformations in order to
+ * make sure that the transformed bytecode is sane. For example:
+ *
* <pre>
- * InputStream is = ...; // get bytes for the source class
- * ClassReader cr = new ClassReader(is);
- * ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
- * ClassVisitor cv = new <b>MyClassAdapter</b>(new CheckClassAdapter(cw));
- * cr.accept(cv, 0);
- *
- * StringWriter sw = new StringWriter();
- * PrintWriter pw = new PrintWriter(sw);
- * CheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false, pw);
- * assertTrue(sw.toString(), sw.toString().length()==0);
+ * InputStream inputStream = ...; // get bytes for the source class
+ * ClassReader classReader = new ClassReader(inputStream);
+ * ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
+ * ClassVisitor classVisitor = new <b>MyClassAdapter</b>(new CheckClassAdapter(classWriter, true));
+ * classReader.accept(classVisitor, 0);
+ *
+ * StringWriter stringWriter = new StringWriter();
+ * PrintWriter printWriter = new PrintWriter(stringWriter);
+ * CheckClassAdapter.verify(new ClassReader(classWriter.toByteArray()), false, printWriter);
+ * assertTrue(stringWriter.toString().isEmpty());
* </pre>
- *
- * Above code runs transformed bytecode trough the
- * <code>CheckClassAdapter</code>. It won't be exactly the same verification as
- * JVM does, but it run data flow analysis for the code of each method and
- * checks that expectations are met for each method instruction.
- *
- * <p>
- * If method bytecode has errors, assertion text will show the erroneous
- * instruction number and dump of the failed method with information about
- * locals and stack slot for each instruction. For example (format is -
- * insnNumber locals : stack):
- *
+ *
+ * <p>The above code pass the transformed bytecode through a <code>CheckClassAdapter</code>, with
+ * data flow checks enabled. These checks are not exactly the same as the JVM verification, but
+ * provide some basic type checking for each method instruction. If the bytecode has errors, the
+ * output text shows the erroneous instruction number, and a dump of the failed method with
+ * information about the type of the local variables and of the operand stack slots for each
+ * instruction. For example (format is - insnNumber locals : stack):
+ *
* <pre>
- * org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
- * at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289)
- * at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135)
+ * org.apache.tapestry5.internal.plastic.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found .
+ * at org.apache.tapestry5.internal.plastic.asm.tree.analysis.Analyzer.analyze(Analyzer.java:...)
+ * at org.apache.tapestry5.internal.plastic.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:...)
* ...
* remove()V
- * 00000 LinkedBlockingQueue$Itr . . . . . . . . :
- * ICONST_0
- * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I
- * ISTORE 2
+ * 00000 LinkedBlockingQueue$Itr . . . . . . . . : ICONST_0
+ * 00001 LinkedBlockingQueue$Itr . . . . . . . . : I ISTORE 2
* 00001 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
* ...
- *
- * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . :
- * ILOAD 1
- * 00072 <b>?</b>
- * INVOKESPECIAL java/lang/Integer.<init> (I)V
+ * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : ILOAD 1
+ * 00072 <b>?</b> INVOKESPECIAL java/lang/Integer.<init> (I)V
* ...
* </pre>
- *
- * In the above output you can see that variable 1 loaded by
- * <code>ILOAD 1</code> instruction at position <code>00071</code> is not
- * initialized. You can also see that at the beginning of the method (code
- * inserted by the transformation) variable 2 is initialized.
- *
- * <p>
- * Note that when used like that, <code>CheckClassAdapter.verify()</code> can
- * trigger additional class loading, because it is using
- * <code>SimpleVerifier</code>.
- *
+ *
+ * <p>The above output shows that the local variable 1, loaded by the <code>ILOAD 1</code>
+ * instruction at position <code>00071</code> is not initialized, whereas the local variable 2 is
+ * initialized and contains an int value.
+ *
* @author Eric Bruneton
*/
public class CheckClassAdapter extends ClassVisitor {
- /**
- * The class version number.
- */
- private int version;
-
- /**
- * <tt>true</tt> if the visit method has been called.
- */
- private boolean start;
-
- /**
- * <tt>true</tt> if the visitSource method has been called.
- */
- private boolean source;
-
- /**
- * <tt>true</tt> if the visitOuterClass method has been called.
- */
- private boolean outer;
-
- /**
- * <tt>true</tt> if the visitEnd method has been called.
- */
- private boolean end;
-
- /**
- * <tt>true</tt> if the visitModule method has been called.
- */
- private boolean module;
-
- /**
- * The already visited labels. This map associate Integer values to Label
- * keys.
- */
- private Map<Label, Integer> labels;
-
- /**
- * <tt>true</tt> if the method code must be checked with a BasicVerifier.
- */
- private boolean checkDataFlow;
-
- /**
- * Checks a given class.
- * <p>
- * Usage: CheckClassAdapter <binary class name or class file name>
- *
- * @param args
- * the command line arguments.
- *
- * @throws Exception
- * if the class cannot be found, or if an IO exception occurs.
- */
- public static void main(final String[] args) throws Exception {
- if (args.length != 1) {
- System.err.println("Verifies the given class.");
- System.err.println("Usage: CheckClassAdapter "
- + "<fully qualified class name or class file name>");
- return;
- }
- ClassReader cr;
- if (args[0].endsWith(".class")) {
- cr = new ClassReader(new FileInputStream(args[0]));
- } else {
- cr = new ClassReader(args[0]);
- }
+ private static final String ERROR_AT = ": error at index ";
- verify(cr, false, new PrintWriter(System.err));
- }
-
- /**
- * Checks a given class.
- *
- * @param cr
- * a <code>ClassReader</code> that contains bytecode for the
- * analysis.
- * @param loader
- * a <code>ClassLoader</code> which will be used to load
- * referenced classes. This is useful if you are verifiying
- * multiple interdependent classes.
- * @param dump
- * true if bytecode should be printed out not only when errors
- * are found.
- * @param pw
- * write where results going to be printed
- */
- public static void verify(final ClassReader cr, final ClassLoader loader,
- final boolean dump, final PrintWriter pw) {
- ClassNode cn = new ClassNode();
- cr.accept(new CheckClassAdapter(cn, false), ClassReader.SKIP_DEBUG);
-
- Type syperType = cn.superName == null ? null : Type
- .getObjectType(cn.superName);
- List<MethodNode> methods = cn.methods;
-
- List<Type> interfaces = new ArrayList<Type>();
- for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) {
- interfaces.add(Type.getObjectType(i.next()));
- }
+ /** Whether the bytecode must be checked with a BasicVerifier. */
+ private boolean checkDataFlow;
- for (int i = 0; i < methods.size(); ++i) {
- MethodNode method = methods.get(i);
- SimpleVerifier verifier = new SimpleVerifier(
- Type.getObjectType(cn.name), syperType, interfaces,
- (cn.access & Opcodes.ACC_INTERFACE) != 0);
- Analyzer<BasicValue> a = new Analyzer<BasicValue>(verifier);
- if (loader != null) {
- verifier.setClassLoader(loader);
- }
- try {
- a.analyze(cn.name, method);
- if (!dump) {
- continue;
- }
- } catch (Exception e) {
- e.printStackTrace(pw);
- }
- printAnalyzerResult(method, a, pw);
- }
- pw.flush();
- }
-
- /**
- * Checks a given class
- *
- * @param cr
- * a <code>ClassReader</code> that contains bytecode for the
- * analysis.
- * @param dump
- * true if bytecode should be printed out not only when errors
- * are found.
- * @param pw
- * write where results going to be printed
- */
- public static void verify(final ClassReader cr, final boolean dump,
- final PrintWriter pw) {
- verify(cr, null, dump, pw);
- }
-
- static void printAnalyzerResult(MethodNode method, Analyzer<BasicValue> a,
- final PrintWriter pw) {
- Frame<BasicValue>[] frames = a.getFrames();
- Textifier t = new Textifier();
- TraceMethodVisitor mv = new TraceMethodVisitor(t);
-
- pw.println(method.name + method.desc);
- for (int j = 0; j < method.instructions.size(); ++j) {
- method.instructions.get(j).accept(mv);
-
- StringBuilder sb = new StringBuilder();
- Frame<BasicValue> f = frames[j];
- if (f == null) {
- sb.append('?');
- } else {
- for (int k = 0; k < f.getLocals(); ++k) {
- sb.append(getShortName(f.getLocal(k).toString()))
- .append(' ');
- }
- sb.append(" : ");
- for (int k = 0; k < f.getStackSize(); ++k) {
- sb.append(getShortName(f.getStack(k).toString()))
- .append(' ');
- }
- }
- while (sb.length() < method.maxStack + method.maxLocals + 1) {
- sb.append(' ');
- }
- pw.print(Integer.toString(j + 100000).substring(1));
- pw.print(" " + sb + " : " + t.text.get(t.text.size() - 1));
- }
- for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
- method.tryCatchBlocks.get(j).accept(mv);
- pw.print(" " + t.text.get(t.text.size() - 1));
- }
- pw.println();
+ /** The class version number. */
+ private int version;
+
+ /** Whether the {@link #visit} method has been called. */
+ private boolean visitCalled;
+
+ /** Whether the {@link #visitModule} method has been called. */
+ private boolean visitModuleCalled;
+
+ /** Whether the {@link #visitSource} method has been called. */
+ private boolean visitSourceCalled;
+
+ /** Whether the {@link #visitOuterClass} method has been called. */
+ private boolean visitOuterClassCalled;
+
+ /** Whether the {@link #visitNestHost} method has been called. */
+ private boolean visitNestHostCalled;
+
+ /**
+ * The common package of all the nest members. Not {@literal null} if the visitNestMember method
+ * has been called.
+ */
+ private String nestMemberPackageName;
+
+ /** Whether the {@link #visitEnd} method has been called. */
+ private boolean visitEndCalled;
+
+ /** The index of the instruction designated by each visited label so far. */
+ private Map<Label, Integer> labelInsnIndices;
+
+ // -----------------------------------------------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>.
+ * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
+ *
+ * @param classVisitor the class visitor to which this adapter must delegate calls.
+ */
+ public CheckClassAdapter(final ClassVisitor classVisitor) {
+ this(classVisitor, true);
+ }
+
+ /**
+ * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use this constructor</i>.
+ * Instead, they must use the {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
+ *
+ * @param classVisitor the class visitor to which this adapter must delegate calls.
+ * @param checkDataFlow whether to perform basic data flow checks. This option requires valid
+ * maxLocals and maxStack values.
+ * @throws IllegalStateException If a subclass calls this constructor.
+ */
+ public CheckClassAdapter(final ClassVisitor classVisitor, final boolean checkDataFlow) {
+ this(Opcodes.ASM7, classVisitor, checkDataFlow);
+ if (getClass() != CheckClassAdapter.class) {
+ throw new IllegalStateException();
}
+ }
- private static String getShortName(final String name) {
- int n = name.lastIndexOf('/');
- int k = name.length();
- if (name.charAt(k - 1) == ';') {
- k--;
- }
- return n == -1 ? name : name.substring(n + 1, k);
- }
-
- /**
- * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
- * this constructor</i>. Instead, they must use the
- * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
- *
- * @param cv
- * the class visitor to which this adapter must delegate calls.
- */
- public CheckClassAdapter(final ClassVisitor cv) {
- this(cv, true);
- }
-
- /**
- * Constructs a new {@link CheckClassAdapter}. <i>Subclasses must not use
- * this constructor</i>. Instead, they must use the
- * {@link #CheckClassAdapter(int, ClassVisitor, boolean)} version.
- *
- * @param cv
- * the class visitor to which this adapter must delegate calls.
- * @param checkDataFlow
- * <tt>true</tt> to perform basic data flow checks, or
- * <tt>false</tt> to not perform any data flow check (see
- * {@link CheckMethodAdapter}). This option requires valid
- * maxLocals and maxStack values.
- * @throws IllegalStateException
- * If a subclass calls this constructor.
- */
- public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) {
- this(Opcodes.ASM6, cv, checkDataFlow);
- if (getClass() != CheckClassAdapter.class) {
- throw new IllegalStateException();
- }
+ /**
+ * Constructs a new {@link CheckClassAdapter}.
+ *
+ * @param api the ASM API version implemented by this visitor. Must be one of {@link
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+ * @param classVisitor the class visitor to which this adapter must delegate calls.
+ * @param checkDataFlow {@literal true} to perform basic data flow checks, or {@literal false} to
+ * not perform any data flow check (see {@link CheckMethodAdapter}). This option requires
+ * valid maxLocals and maxStack values.
+ */
+ protected CheckClassAdapter(
+ final int api, final ClassVisitor classVisitor, final boolean checkDataFlow) {
+ super(api, classVisitor);
+ this.labelInsnIndices = new HashMap<Label, Integer>();
+ this.checkDataFlow = checkDataFlow;
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Implementation of the ClassVisitor interface
+ // -----------------------------------------------------------------------------------------------
+
+ @Override
+ public void visit(
+ final int version,
+ final int access,
+ final String name,
+ final String signature,
+ final String superName,
+ final String[] interfaces) {
+ if (visitCalled) {
+ throw new IllegalStateException("visit must be called only once");
+ }
+ visitCalled = true;
+ checkState();
+ checkAccess(
+ access,
+ Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_FINAL
+ | Opcodes.ACC_SUPER
+ | Opcodes.ACC_INTERFACE
+ | Opcodes.ACC_ABSTRACT
+ | Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_ANNOTATION
+ | Opcodes.ACC_ENUM
+ | Opcodes.ACC_DEPRECATED
+ | Opcodes.ACC_MODULE);
+ if (name == null) {
+ throw new IllegalArgumentException("Illegal class name (null)");
+ }
+ if (!name.endsWith("package-info") && !name.endsWith("module-info")) {
+ CheckMethodAdapter.checkInternalName(version, name, "class name");
+ }
+ if ("java/lang/Object".equals(name)) {
+ if (superName != null) {
+ throw new IllegalArgumentException(
+ "The super class name of the Object class must be 'null'");
+ }
+ } else if (name.endsWith("module-info")) {
+ if (superName != null) {
+ throw new IllegalArgumentException(
+ "The super class name of a module-info class must be 'null'");
+ }
+ } else {
+ CheckMethodAdapter.checkInternalName(version, superName, "super class name");
}
+ if (signature != null) {
+ checkClassSignature(signature);
+ }
+ if ((access & Opcodes.ACC_INTERFACE) != 0 && !"java/lang/Object".equals(superName)) {
+ throw new IllegalArgumentException(
+ "The super class name of interfaces must be 'java/lang/Object'");
+ }
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; ++i) {
+ CheckMethodAdapter.checkInternalName(
+ version, interfaces[i], "interface name at index " + i);
+ }
+ }
+ this.version = version;
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
- /**
- * Constructs a new {@link CheckClassAdapter}.
- *
- * @param api
- * the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
- * @param cv
- * the class visitor to which this adapter must delegate calls.
- * @param checkDataFlow
- * <tt>true</tt> to perform basic data flow checks, or
- * <tt>false</tt> to not perform any data flow check (see
- * {@link CheckMethodAdapter}). This option requires valid
- * maxLocals and maxStack values.
- */
- protected CheckClassAdapter(final int api, final ClassVisitor cv,
- final boolean checkDataFlow) {
- super(api, cv);
- this.labels = new HashMap<Label, Integer>();
- this.checkDataFlow = checkDataFlow;
- }
-
- // ------------------------------------------------------------------------
- // Implementation of the ClassVisitor interface
- // ------------------------------------------------------------------------
-
- @Override
- public void visit(final int version, final int access, final String name,
- final String signature, final String superName,
- final String[] interfaces) {
- if (start) {
- throw new IllegalStateException("visit must be called only once");
- }
- start = true;
- checkState();
- checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
- + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
- + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
- + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
- + Opcodes.ACC_DEPRECATED + Opcodes.ACC_MODULE
- + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
- if (name == null) {
- throw new IllegalArgumentException("Illegal class name (null)");
- }
- if (!name.endsWith("package-info")) {
- CheckMethodAdapter.checkInternalName(name, "class name");
- }
- if ("java/lang/Object".equals(name)) {
- if (superName != null) {
- throw new IllegalArgumentException(
- "The super class name of the Object class must be 'null'");
- }
- } else {
- CheckMethodAdapter.checkInternalName(superName, "super class name");
- }
- if (signature != null) {
- checkClassSignature(signature);
- }
- if ((access & Opcodes.ACC_INTERFACE) != 0) {
- if (!"java/lang/Object".equals(superName)) {
- throw new IllegalArgumentException(
- "The super class name of interfaces must be 'java/lang/Object'");
- }
- }
- if (interfaces != null) {
- for (int i = 0; i < interfaces.length; ++i) {
- CheckMethodAdapter.checkInternalName(interfaces[i],
- "interface name at index " + i);
- }
- }
- this.version = version;
- super.visit(version, access, name, signature, superName, interfaces);
+ @Override
+ public void visitSource(final String file, final String debug) {
+ checkState();
+ if (visitSourceCalled) {
+ throw new IllegalStateException("visitSource can be called only once.");
}
+ visitSourceCalled = true;
+ super.visitSource(file, debug);
+ }
- @Override
- public void visitSource(final String file, final String debug) {
- checkState();
- if (source) {
- throw new IllegalStateException(
- "visitSource can be called only once.");
- }
- source = true;
- super.visitSource(file, debug);
+ @Override
+ public ModuleVisitor visitModule(final String name, final int access, final String version) {
+ checkState();
+ if (visitModuleCalled) {
+ throw new IllegalStateException("visitModule can be called only once.");
}
+ visitModuleCalled = true;
+ checkFullyQualifiedName(this.version, name, "module name");
+ checkAccess(access, Opcodes.ACC_OPEN | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
+ CheckModuleAdapter checkModuleAdapter =
+ new CheckModuleAdapter(
+ api, super.visitModule(name, access, version), (access & Opcodes.ACC_OPEN) != 0);
+ checkModuleAdapter.classVersion = this.version;
+ return checkModuleAdapter;
+ }
- @Override
- public ModuleVisitor visitModule(String name, int access, String version) {
- checkState();
- if (module) {
- throw new IllegalStateException(
- "visitModule can be called only once.");
- }
- module = true;
- if (name == null) {
- throw new IllegalArgumentException("Illegal module name (null)");
- }
- checkAccess(access, Opcodes.ACC_OPEN | Opcodes.ACC_SYNTHETIC);
- return new CheckModuleAdapter(super.visitModule(name, access, version),
- (access & Opcodes.ACC_OPEN) != 0);
- }
-
- @Override
- public void visitOuterClass(final String owner, final String name,
- final String desc) {
- checkState();
- if (outer) {
- throw new IllegalStateException(
- "visitOuterClass can be called only once.");
- }
- outer = true;
- if (owner == null) {
- throw new IllegalArgumentException("Illegal outer class owner");
- }
- if (desc != null) {
- CheckMethodAdapter.checkMethodDesc(desc);
- }
- super.visitOuterClass(owner, name, desc);
+ @Override
+ public void visitNestHost(final String nestHost) {
+ checkState();
+ CheckMethodAdapter.checkInternalName(version, nestHost, "nestHost");
+ if (visitNestHostCalled) {
+ throw new IllegalStateException("visitNestHost can be called only once.");
}
+ if (nestMemberPackageName != null) {
+ throw new IllegalStateException("visitNestHost and visitNestMember are mutually exclusive.");
+ }
+ visitNestHostCalled = true;
+ super.visitNestHost(nestHost);
+ }
- @Override
- public void visitInnerClass(final String name, final String outerName,
- final String innerName, final int access) {
- checkState();
- CheckMethodAdapter.checkInternalName(name, "class name");
- if (outerName != null) {
- CheckMethodAdapter.checkInternalName(outerName, "outer class name");
- }
- if (innerName != null) {
- int start = 0;
- while (start < innerName.length()
- && Character.isDigit(innerName.charAt(start))) {
- start++;
- }
- if (start == 0 || start < innerName.length()) {
- CheckMethodAdapter.checkIdentifier(innerName, start, -1,
- "inner class name");
- }
- }
- checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
- + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
- + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
- + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
- + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
- super.visitInnerClass(name, outerName, innerName, access);
- }
-
- @Override
- public FieldVisitor visitField(final int access, final String name,
- final String desc, final String signature, final Object value) {
- checkState();
- checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
- + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
- + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
- + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
- + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
- CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
- CheckMethodAdapter.checkDesc(desc, false);
- if (signature != null) {
- checkFieldSignature(signature);
- }
- if (value != null) {
- CheckMethodAdapter.checkConstant(value);
- }
- FieldVisitor av = super
- .visitField(access, name, desc, signature, value);
- return new CheckFieldAdapter(av);
- }
-
- @Override
- public MethodVisitor visitMethod(final int access, final String name,
- final String desc, final String signature, final String[] exceptions) {
- checkState();
- checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
- + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
- + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
- + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
- + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
- + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED + 0x40000); // ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
- if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
- CheckMethodAdapter.checkMethodIdentifier(version, name,
- "method name");
- }
- CheckMethodAdapter.checkMethodDesc(desc);
- if (signature != null) {
- checkMethodSignature(signature);
- }
- if (exceptions != null) {
- for (int i = 0; i < exceptions.length; ++i) {
- CheckMethodAdapter.checkInternalName(exceptions[i],
- "exception name at index " + i);
- }
- }
- CheckMethodAdapter cma;
- if (checkDataFlow) {
- cma = new CheckMethodAdapter(access, name, desc, super.visitMethod(
- access, name, desc, signature, exceptions), labels);
- } else {
- cma = new CheckMethodAdapter(super.visitMethod(access, name, desc,
- signature, exceptions), labels);
- }
- cma.version = version;
- return cma;
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(final String desc,
- final boolean visible) {
- checkState();
- CheckMethodAdapter.checkDesc(desc, false);
- return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
- }
-
- @Override
- public AnnotationVisitor visitTypeAnnotation(final int typeRef,
- final TypePath typePath, final String desc, final boolean visible) {
- checkState();
- int sort = typeRef >>> 24;
- if (sort != TypeReference.CLASS_TYPE_PARAMETER
- && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND
- && sort != TypeReference.CLASS_EXTENDS) {
- throw new IllegalArgumentException("Invalid type reference sort 0x"
- + Integer.toHexString(sort));
- }
- checkTypeRefAndPath(typeRef, typePath);
- CheckMethodAdapter.checkDesc(desc, false);
- return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef,
- typePath, desc, visible));
- }
-
- @Override
- public void visitAttribute(final Attribute attr) {
- checkState();
- if (attr == null) {
- throw new IllegalArgumentException(
- "Invalid attribute (must not be null)");
- }
- super.visitAttribute(attr);
+ @Override
+ public void visitNestMember(final String nestMember) {
+ checkState();
+ CheckMethodAdapter.checkInternalName(version, nestMember, "nestMember");
+ if (visitNestHostCalled) {
+ throw new IllegalStateException(
+ "visitMemberOfNest and visitNestHost are mutually exclusive.");
+ }
+ String packageName = packageName(nestMember);
+ if (nestMemberPackageName == null) {
+ nestMemberPackageName = packageName;
+ } else if (!nestMemberPackageName.equals(packageName)) {
+ throw new IllegalStateException(
+ "nest member " + nestMember + " should be in the package " + nestMemberPackageName);
}
+ super.visitNestMember(nestMember);
+ }
- @Override
- public void visitEnd() {
- checkState();
- end = true;
- super.visitEnd();
+ @Override
+ public void visitOuterClass(final String owner, final String name, final String descriptor) {
+ checkState();
+ if (visitOuterClassCalled) {
+ throw new IllegalStateException("visitOuterClass can be called only once.");
+ }
+ visitOuterClassCalled = true;
+ if (owner == null) {
+ throw new IllegalArgumentException("Illegal outer class owner");
}
+ if (descriptor != null) {
+ CheckMethodAdapter.checkMethodDescriptor(version, descriptor);
+ }
+ super.visitOuterClass(owner, name, descriptor);
+ }
- // ------------------------------------------------------------------------
- // Utility methods
- // ------------------------------------------------------------------------
+ @Override
+ public void visitInnerClass(
+ final String name, final String outerName, final String innerName, final int access) {
+ checkState();
+ CheckMethodAdapter.checkInternalName(version, name, "class name");
+ if (outerName != null) {
+ CheckMethodAdapter.checkInternalName(version, outerName, "outer class name");
+ }
+ if (innerName != null) {
+ int startIndex = 0;
+ while (startIndex < innerName.length() && Character.isDigit(innerName.charAt(startIndex))) {
+ startIndex++;
+ }
+ if (startIndex == 0 || startIndex < innerName.length()) {
+ CheckMethodAdapter.checkIdentifier(version, innerName, startIndex, -1, "inner class name");
+ }
+ }
+ checkAccess(
+ access,
+ Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_PROTECTED
+ | Opcodes.ACC_STATIC
+ | Opcodes.ACC_FINAL
+ | Opcodes.ACC_INTERFACE
+ | Opcodes.ACC_ABSTRACT
+ | Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_ANNOTATION
+ | Opcodes.ACC_ENUM);
+ super.visitInnerClass(name, outerName, innerName, access);
+ }
- /**
- * Checks that the visit method has been called and that visitEnd has not
- * been called.
- */
- private void checkState() {
- if (!start) {
- throw new IllegalStateException(
- "Cannot visit member before visit has been called.");
- }
- if (end) {
- throw new IllegalStateException(
- "Cannot visit member after visitEnd has been called.");
- }
+ @Override
+ public FieldVisitor visitField(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final Object value) {
+ checkState();
+ checkAccess(
+ access,
+ Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_PROTECTED
+ | Opcodes.ACC_STATIC
+ | Opcodes.ACC_FINAL
+ | Opcodes.ACC_VOLATILE
+ | Opcodes.ACC_TRANSIENT
+ | Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_ENUM
+ | Opcodes.ACC_DEPRECATED);
+ CheckMethodAdapter.checkUnqualifiedName(version, name, "field name");
+ CheckMethodAdapter.checkDescriptor(version, descriptor, /* canBeVoid = */ false);
+ if (signature != null) {
+ checkFieldSignature(signature);
}
+ if (value != null) {
+ CheckMethodAdapter.checkConstant(value);
+ }
+ return new CheckFieldAdapter(api, super.visitField(access, name, descriptor, signature, value));
+ }
- /**
- * Checks that the given access flags do not contain invalid flags. This
- * method also checks that mutually incompatible flags are not set
- * simultaneously.
- *
- * @param access
- * the access flags to be checked
- * @param possibleAccess
- * the valid access flags.
- */
- static void checkAccess(final int access, final int possibleAccess) {
- if ((access & ~possibleAccess) != 0) {
- throw new IllegalArgumentException("Invalid access flags: "
- + access);
- }
- int pub = (access & Opcodes.ACC_PUBLIC) == 0 ? 0 : 1;
- int pri = (access & Opcodes.ACC_PRIVATE) == 0 ? 0 : 1;
- int pro = (access & Opcodes.ACC_PROTECTED) == 0 ? 0 : 1;
- if (pub + pri + pro > 1) {
- throw new IllegalArgumentException(
- "public private and protected are mutually exclusive: "
- + access);
- }
- int fin = (access & Opcodes.ACC_FINAL) == 0 ? 0 : 1;
- int abs = (access & Opcodes.ACC_ABSTRACT) == 0 ? 0 : 1;
- if (fin + abs > 1) {
- throw new IllegalArgumentException(
- "final and abstract are mutually exclusive: " + access);
- }
+ @Override
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ checkState();
+ checkAccess(
+ access,
+ Opcodes.ACC_PUBLIC
+ | Opcodes.ACC_PRIVATE
+ | Opcodes.ACC_PROTECTED
+ | Opcodes.ACC_STATIC
+ | Opcodes.ACC_FINAL
+ | Opcodes.ACC_SYNCHRONIZED
+ | Opcodes.ACC_BRIDGE
+ | Opcodes.ACC_VARARGS
+ | Opcodes.ACC_NATIVE
+ | Opcodes.ACC_ABSTRACT
+ | Opcodes.ACC_STRICT
+ | Opcodes.ACC_SYNTHETIC
+ | Opcodes.ACC_DEPRECATED);
+ if (!"<init>".equals(name) && !"<clinit>".equals(name)) {
+ CheckMethodAdapter.checkMethodIdentifier(version, name, "method name");
+ }
+ CheckMethodAdapter.checkMethodDescriptor(version, descriptor);
+ if (signature != null) {
+ checkMethodSignature(signature);
+ }
+ if (exceptions != null) {
+ for (int i = 0; i < exceptions.length; ++i) {
+ CheckMethodAdapter.checkInternalName(
+ version, exceptions[i], "exception name at index " + i);
+ }
+ }
+ CheckMethodAdapter checkMethodAdapter;
+ if (checkDataFlow) {
+ checkMethodAdapter =
+ new CheckMethodAdapter(
+ api,
+ access,
+ name,
+ descriptor,
+ super.visitMethod(access, name, descriptor, signature, exceptions),
+ labelInsnIndices);
+ } else {
+ checkMethodAdapter =
+ new CheckMethodAdapter(
+ api,
+ super.visitMethod(access, name, descriptor, signature, exceptions),
+ labelInsnIndices);
}
+ checkMethodAdapter.version = version;
+ return checkMethodAdapter;
+ }
- /**
- * Checks a class signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- */
- public static void checkClassSignature(final String signature) {
- // ClassSignature:
- // FormalTypeParameters? ClassTypeSignature ClassTypeSignature*
+ @Override
+ public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+ checkState();
+ CheckMethodAdapter.checkDescriptor(version, descriptor, false);
+ return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
+ }
- int pos = 0;
- if (getChar(signature, 0) == '<') {
- pos = checkFormalTypeParameters(signature, pos);
- }
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(
+ final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+ checkState();
+ int sort = new TypeReference(typeRef).getSort();
+ if (sort != TypeReference.CLASS_TYPE_PARAMETER
+ && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND
+ && sort != TypeReference.CLASS_EXTENDS) {
+ throw new IllegalArgumentException(
+ "Invalid type reference sort 0x" + Integer.toHexString(sort));
+ }
+ checkTypeRef(typeRef);
+ CheckMethodAdapter.checkDescriptor(version, descriptor, false);
+ return new CheckAnnotationAdapter(
+ super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
+ }
+
+ @Override
+ public void visitAttribute(final Attribute attribute) {
+ checkState();
+ if (attribute == null) {
+ throw new IllegalArgumentException("Invalid attribute (must not be null)");
+ }
+ super.visitAttribute(attribute);
+ }
+
+ @Override
+ public void visitEnd() {
+ checkState();
+ visitEndCalled = true;
+ super.visitEnd();
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Utility methods
+ // -----------------------------------------------------------------------------------------------
+
+ /** Checks that the visit method has been called and that visitEnd has not been called. */
+ private void checkState() {
+ if (!visitCalled) {
+ throw new IllegalStateException("Cannot visit member before visit has been called.");
+ }
+ if (visitEndCalled) {
+ throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
+ }
+ }
+
+ /**
+ * Checks that the given access flags do not contain invalid flags. This method also checks that
+ * mutually incompatible flags are not set simultaneously.
+ *
+ * @param access the access flags to be checked.
+ * @param possibleAccess the valid access flags.
+ */
+ static void checkAccess(final int access, final int possibleAccess) {
+ if ((access & ~possibleAccess) != 0) {
+ throw new IllegalArgumentException("Invalid access flags: " + access);
+ }
+ int publicProtectedPrivate = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE;
+ if (Integer.bitCount(access & publicProtectedPrivate) > 1) {
+ throw new IllegalArgumentException(
+ "public, protected and private are mutually exclusive: " + access);
+ }
+ if (Integer.bitCount(access & (Opcodes.ACC_FINAL | Opcodes.ACC_ABSTRACT)) > 1) {
+ throw new IllegalArgumentException("final and abstract are mutually exclusive: " + access);
+ }
+ }
+
+ /**
+ * Checks that the given name is a fully qualified name, using dots.
+ *
+ * @param version the class version.
+ * @param name the name to be checked.
+ * @param source the source of 'name' (e.g 'module' for a module name).
+ */
+ static void checkFullyQualifiedName(final int version, final String name, final String source) {
+ try {
+ int startIndex = 0;
+ int dotIndex;
+ while ((dotIndex = name.indexOf('.', startIndex + 1)) != -1) {
+ CheckMethodAdapter.checkIdentifier(version, name, startIndex, dotIndex, null);
+ startIndex = dotIndex + 1;
+ }
+ CheckMethodAdapter.checkIdentifier(version, name, startIndex, name.length(), null);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException(
+ "Invalid " + source + " (must be a fully qualified name): " + name, e);
+ }
+ }
+
+ /**
+ * Checks a class signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ public static void checkClassSignature(final String signature) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // ClassSignature:
+ // [TypeParameters] SuperclassSignature SuperinterfaceSignature*
+ // SuperclassSignature:
+ // ClassTypeSignature
+ // SuperinterfaceSignature:
+ // ClassTypeSignature
+ int pos = 0;
+ if (getChar(signature, 0) == '<') {
+ pos = checkTypeParameters(signature, pos);
+ }
+ pos = checkClassTypeSignature(signature, pos);
+ while (getChar(signature, pos) == 'L') {
+ pos = checkClassTypeSignature(signature, pos);
+ }
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ERROR_AT + pos);
+ }
+ }
+
+ /**
+ * Checks a method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ public static void checkMethodSignature(final String signature) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // MethodSignature:
+ // [TypeParameters] ( JavaTypeSignature* ) Result ThrowsSignature*
+ // Result:
+ // JavaTypeSignature
+ // VoidDescriptor
+ // ThrowsSignature:
+ // ^ ClassTypeSignature
+ // ^ TypeVariableSignature
+ int pos = 0;
+ if (getChar(signature, 0) == '<') {
+ pos = checkTypeParameters(signature, pos);
+ }
+ pos = checkChar('(', signature, pos);
+ while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
+ pos = checkJavaTypeSignature(signature, pos);
+ }
+ pos = checkChar(')', signature, pos);
+ if (getChar(signature, pos) == 'V') {
+ ++pos;
+ } else {
+ pos = checkJavaTypeSignature(signature, pos);
+ }
+ while (getChar(signature, pos) == '^') {
+ ++pos;
+ if (getChar(signature, pos) == 'L') {
pos = checkClassTypeSignature(signature, pos);
- while (getChar(signature, pos) == 'L') {
- pos = checkClassTypeSignature(signature, pos);
- }
- if (pos != signature.length()) {
- throw new IllegalArgumentException(signature + ": error at index "
- + pos);
- }
+ } else {
+ pos = checkTypeVariableSignature(signature, pos);
+ }
+ }
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ERROR_AT + pos);
}
+ }
- /**
- * Checks a method signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- */
- public static void checkMethodSignature(final String signature) {
- // MethodTypeSignature:
- // FormalTypeParameters? ( TypeSignature* ) ( TypeSignature | V ) (
- // ^ClassTypeSignature | ^TypeVariableSignature )*
-
- int pos = 0;
- if (getChar(signature, 0) == '<') {
- pos = checkFormalTypeParameters(signature, pos);
- }
- pos = checkChar('(', signature, pos);
- while ("ZCBSIFJDL[T".indexOf(getChar(signature, pos)) != -1) {
- pos = checkTypeSignature(signature, pos);
- }
- pos = checkChar(')', signature, pos);
- if (getChar(signature, pos) == 'V') {
- ++pos;
- } else {
- pos = checkTypeSignature(signature, pos);
- }
- while (getChar(signature, pos) == '^') {
- ++pos;
- if (getChar(signature, pos) == 'L') {
- pos = checkClassTypeSignature(signature, pos);
- } else {
- pos = checkTypeVariableSignature(signature, pos);
- }
- }
- if (pos != signature.length()) {
- throw new IllegalArgumentException(signature + ": error at index "
- + pos);
- }
+ /**
+ * Checks a field signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ */
+ public static void checkFieldSignature(final String signature) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // FieldSignature:
+ // ReferenceTypeSignature
+ int pos = checkReferenceTypeSignature(signature, 0);
+ if (pos != signature.length()) {
+ throw new IllegalArgumentException(signature + ERROR_AT + pos);
}
+ }
- /**
- * Checks a field signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- */
- public static void checkFieldSignature(final String signature) {
- int pos = checkFieldTypeSignature(signature, 0);
- if (pos != signature.length()) {
- throw new IllegalArgumentException(signature + ": error at index "
- + pos);
- }
+ /**
+ * Checks the type parameters of a class or method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeParameters(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // TypeParameters:
+ // < TypeParameter TypeParameter* >
+ int pos = startPos;
+ pos = checkChar('<', signature, pos);
+ pos = checkTypeParameter(signature, pos);
+ while (getChar(signature, pos) != '>') {
+ pos = checkTypeParameter(signature, pos);
}
+ return pos + 1;
+ }
- /**
- * Checks the reference to a type in a type annotation.
- *
- * @param typeRef
- * a reference to an annotated type.
- * @param typePath
- * the path to the annotated type argument, wildcard bound, array
- * element type, or static inner type within 'typeRef'. May be
- * <tt>null</tt> if the annotation targets 'typeRef' as a whole.
- */
- static void checkTypeRefAndPath(int typeRef, TypePath typePath) {
- int mask = 0;
- switch (typeRef >>> 24) {
- case TypeReference.CLASS_TYPE_PARAMETER:
- case TypeReference.METHOD_TYPE_PARAMETER:
- case TypeReference.METHOD_FORMAL_PARAMETER:
- mask = 0xFFFF0000;
- break;
- case TypeReference.FIELD:
- case TypeReference.METHOD_RETURN:
- case TypeReference.METHOD_RECEIVER:
- case TypeReference.LOCAL_VARIABLE:
- case TypeReference.RESOURCE_VARIABLE:
- case TypeReference.INSTANCEOF:
- case TypeReference.NEW:
- case TypeReference.CONSTRUCTOR_REFERENCE:
- case TypeReference.METHOD_REFERENCE:
- mask = 0xFF000000;
- break;
- case TypeReference.CLASS_EXTENDS:
- case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
- case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
- case TypeReference.THROWS:
- case TypeReference.EXCEPTION_PARAMETER:
- mask = 0xFFFFFF00;
- break;
- case TypeReference.CAST:
- case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
- case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
- case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
- case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
- mask = 0xFF0000FF;
- break;
- default:
- throw new IllegalArgumentException("Invalid type reference sort 0x"
- + Integer.toHexString(typeRef >>> 24));
- }
- if ((typeRef & ~mask) != 0) {
- throw new IllegalArgumentException("Invalid type reference 0x"
- + Integer.toHexString(typeRef));
- }
- if (typePath != null) {
- for (int i = 0; i < typePath.getLength(); ++i) {
- int step = typePath.getStep(i);
- if (step != TypePath.ARRAY_ELEMENT
- && step != TypePath.INNER_TYPE
- && step != TypePath.TYPE_ARGUMENT
- && step != TypePath.WILDCARD_BOUND) {
- throw new IllegalArgumentException(
- "Invalid type path step " + i + " in " + typePath);
- }
- if (step != TypePath.TYPE_ARGUMENT
- && typePath.getStepArgument(i) != 0) {
- throw new IllegalArgumentException(
- "Invalid type path step argument for step " + i
- + " in " + typePath);
- }
- }
- }
+ /**
+ * Checks a type parameter of a class or method signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeParameter(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // TypeParameter:
+ // Identifier ClassBound InterfaceBound*
+ // ClassBound:
+ // : [ReferenceTypeSignature]
+ // InterfaceBound:
+ // : ReferenceTypeSignature
+ int pos = startPos;
+ pos = checkSignatureIdentifier(signature, pos);
+ pos = checkChar(':', signature, pos);
+ if ("L[T".indexOf(getChar(signature, pos)) != -1) {
+ pos = checkReferenceTypeSignature(signature, pos);
+ }
+ while (getChar(signature, pos) == ':') {
+ pos = checkReferenceTypeSignature(signature, pos + 1);
}
+ return pos;
+ }
- /**
- * Checks the formal type parameters of a class or method signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkFormalTypeParameters(final String signature, int pos) {
- // FormalTypeParameters:
- // < FormalTypeParameter+ >
-
- pos = checkChar('<', signature, pos);
- pos = checkFormalTypeParameter(signature, pos);
- while (getChar(signature, pos) != '>') {
- pos = checkFormalTypeParameter(signature, pos);
- }
- return pos + 1;
+ /**
+ * Checks a reference type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkReferenceTypeSignature(final String signature, final int pos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // ReferenceTypeSignature:
+ // ClassTypeSignature
+ // TypeVariableSignature
+ // ArrayTypeSignature
+ // ArrayTypeSignature:
+ // [ JavaTypeSignature
+ switch (getChar(signature, pos)) {
+ case 'L':
+ return checkClassTypeSignature(signature, pos);
+ case '[':
+ return checkJavaTypeSignature(signature, pos + 1);
+ default:
+ return checkTypeVariableSignature(signature, pos);
}
+ }
- /**
- * Checks a formal type parameter of a class or method signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkFormalTypeParameter(final String signature, int pos) {
- // FormalTypeParameter:
- // Identifier : FieldTypeSignature? (: FieldTypeSignature)*
-
- pos = checkIdentifier(signature, pos);
- pos = checkChar(':', signature, pos);
- if ("L[T".indexOf(getChar(signature, pos)) != -1) {
- pos = checkFieldTypeSignature(signature, pos);
- }
- while (getChar(signature, pos) == ':') {
- pos = checkFieldTypeSignature(signature, pos + 1);
- }
- return pos;
- }
-
- /**
- * Checks a field type signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkFieldTypeSignature(final String signature, int pos) {
- // FieldTypeSignature:
- // ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature
- //
- // ArrayTypeSignature:
- // [ TypeSignature
-
- switch (getChar(signature, pos)) {
- case 'L':
- return checkClassTypeSignature(signature, pos);
- case '[':
- return checkTypeSignature(signature, pos + 1);
- default:
- return checkTypeVariableSignature(signature, pos);
- }
+ /**
+ * Checks a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkClassTypeSignature(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // ClassTypeSignature:
+ // L [PackageSpecifier] SimpleClassTypeSignature ClassTypeSignatureSuffix* ;
+ // PackageSpecifier:
+ // Identifier / PackageSpecifier*
+ // SimpleClassTypeSignature:
+ // Identifier [TypeArguments]
+ // ClassTypeSignatureSuffix:
+ // . SimpleClassTypeSignature
+ int pos = startPos;
+ pos = checkChar('L', signature, pos);
+ pos = checkSignatureIdentifier(signature, pos);
+ while (getChar(signature, pos) == '/') {
+ pos = checkSignatureIdentifier(signature, pos + 1);
}
+ if (getChar(signature, pos) == '<') {
+ pos = checkTypeArguments(signature, pos);
+ }
+ while (getChar(signature, pos) == '.') {
+ pos = checkSignatureIdentifier(signature, pos + 1);
+ if (getChar(signature, pos) == '<') {
+ pos = checkTypeArguments(signature, pos);
+ }
+ }
+ return checkChar(';', signature, pos);
+ }
- /**
- * Checks a class type signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkClassTypeSignature(final String signature, int pos) {
- // ClassTypeSignature:
- // L Identifier ( / Identifier )* TypeArguments? ( . Identifier
- // TypeArguments? )* ;
-
- pos = checkChar('L', signature, pos);
- pos = checkIdentifier(signature, pos);
- while (getChar(signature, pos) == '/') {
- pos = checkIdentifier(signature, pos + 1);
- }
- if (getChar(signature, pos) == '<') {
- pos = checkTypeArguments(signature, pos);
- }
- while (getChar(signature, pos) == '.') {
- pos = checkIdentifier(signature, pos + 1);
- if (getChar(signature, pos) == '<') {
- pos = checkTypeArguments(signature, pos);
- }
- }
- return checkChar(';', signature, pos);
- }
-
- /**
- * Checks the type arguments in a class type signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkTypeArguments(final String signature, int pos) {
- // TypeArguments:
- // < TypeArgument+ >
-
- pos = checkChar('<', signature, pos);
- pos = checkTypeArgument(signature, pos);
- while (getChar(signature, pos) != '>') {
- pos = checkTypeArgument(signature, pos);
- }
+ /**
+ * Checks the type arguments in a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeArguments(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // TypeArguments:
+ // < TypeArgument TypeArgument* >
+ int pos = startPos;
+ pos = checkChar('<', signature, pos);
+ pos = checkTypeArgument(signature, pos);
+ while (getChar(signature, pos) != '>') {
+ pos = checkTypeArgument(signature, pos);
+ }
+ return pos + 1;
+ }
+
+ /**
+ * Checks a type argument in a class type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeArgument(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // TypeArgument:
+ // [WildcardIndicator] ReferenceTypeSignature
+ // *
+ // WildcardIndicator:
+ // +
+ // -
+ int pos = startPos;
+ char c = getChar(signature, pos);
+ if (c == '*') {
+ return pos + 1;
+ } else if (c == '+' || c == '-') {
+ pos++;
+ }
+ return checkReferenceTypeSignature(signature, pos);
+ }
+
+ /**
+ * Checks a type variable signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkTypeVariableSignature(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // TypeVariableSignature:
+ // T Identifier ;
+ int pos = startPos;
+ pos = checkChar('T', signature, pos);
+ pos = checkSignatureIdentifier(signature, pos);
+ return checkChar(';', signature, pos);
+ }
+
+ /**
+ * Checks a Java type signature.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkJavaTypeSignature(final String signature, final int startPos) {
+ // From https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.9.1:
+ // JavaTypeSignature:
+ // ReferenceTypeSignature
+ // BaseType
+ // BaseType:
+ // (one of)
+ // B C D F I J S Z
+ int pos = startPos;
+ switch (getChar(signature, pos)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
return pos + 1;
+ default:
+ return checkReferenceTypeSignature(signature, pos);
}
+ }
- /**
- * Checks a type argument in a class type signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkTypeArgument(final String signature, int pos) {
- // TypeArgument:
- // * | ( ( + | - )? FieldTypeSignature )
-
- char c = getChar(signature, pos);
- if (c == '*') {
- return pos + 1;
- } else if (c == '+' || c == '-') {
- pos++;
- }
- return checkFieldTypeSignature(signature, pos);
- }
-
- /**
- * Checks a type variable signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkTypeVariableSignature(final String signature,
- int pos) {
- // TypeVariableSignature:
- // T Identifier ;
-
- pos = checkChar('T', signature, pos);
- pos = checkIdentifier(signature, pos);
- return checkChar(';', signature, pos);
- }
-
- /**
- * Checks a type signature.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkTypeSignature(final String signature, int pos) {
- // TypeSignature:
- // Z | C | B | S | I | F | J | D | FieldTypeSignature
-
- switch (getChar(signature, pos)) {
- case 'Z':
- case 'C':
- case 'B':
- case 'S':
- case 'I':
- case 'F':
- case 'J':
- case 'D':
- return pos + 1;
- default:
- return checkFieldTypeSignature(signature, pos);
- }
+ /**
+ * Checks an identifier.
+ *
+ * @param signature a string containing the signature that must be checked.
+ * @param startPos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkSignatureIdentifier(final String signature, final int startPos) {
+ int pos = startPos;
+ while (pos < signature.length() && ".;[/<>:".indexOf(signature.codePointAt(pos)) == -1) {
+ pos = signature.offsetByCodePoints(pos, 1);
+ }
+ if (pos == startPos) {
+ throw new IllegalArgumentException(signature + ": identifier expected at index " + startPos);
}
+ return pos;
+ }
- /**
- * Checks an identifier.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkIdentifier(final String signature, int pos) {
- if (!Character.isJavaIdentifierStart(getChar(signature, pos))) {
- throw new IllegalArgumentException(signature
- + ": identifier expected at index " + pos);
- }
- ++pos;
- while (Character.isJavaIdentifierPart(getChar(signature, pos))) {
- ++pos;
+ /**
+ * Checks a single character.
+ *
+ * @param c a character.
+ * @param signature a string containing the signature that must be checked.
+ * @param pos index of first character to be checked.
+ * @return the index of the first character after the checked part.
+ */
+ private static int checkChar(final char c, final String signature, final int pos) {
+ if (getChar(signature, pos) == c) {
+ return pos + 1;
+ }
+ throw new IllegalArgumentException(signature + ": '" + c + "' expected at index " + pos);
+ }
+
+ /**
+ * Returns the string character at the given index, or 0.
+ *
+ * @param string a string.
+ * @param pos an index in 'string'.
+ * @return the character at the given index, or 0 if there is no such character.
+ */
+ private static char getChar(final String string, final int pos) {
+ return pos < string.length() ? string.charAt(pos) : (char) 0;
+ }
+
+ /**
+ * Checks the reference to a type in a type annotation.
+ *
+ * @param typeRef a reference to an annotated type.
+ */
+ static void checkTypeRef(final int typeRef) {
+ int mask = 0;
+ switch (typeRef >>> 24) {
+ case TypeReference.CLASS_TYPE_PARAMETER:
+ case TypeReference.METHOD_TYPE_PARAMETER:
+ case TypeReference.METHOD_FORMAL_PARAMETER:
+ mask = 0xFFFF0000;
+ break;
+ case TypeReference.FIELD:
+ case TypeReference.METHOD_RETURN:
+ case TypeReference.METHOD_RECEIVER:
+ case TypeReference.LOCAL_VARIABLE:
+ case TypeReference.RESOURCE_VARIABLE:
+ case TypeReference.INSTANCEOF:
+ case TypeReference.NEW:
+ case TypeReference.CONSTRUCTOR_REFERENCE:
+ case TypeReference.METHOD_REFERENCE:
+ mask = 0xFF000000;
+ break;
+ case TypeReference.CLASS_EXTENDS:
+ case TypeReference.CLASS_TYPE_PARAMETER_BOUND:
+ case TypeReference.METHOD_TYPE_PARAMETER_BOUND:
+ case TypeReference.THROWS:
+ case TypeReference.EXCEPTION_PARAMETER:
+ mask = 0xFFFFFF00;
+ break;
+ case TypeReference.CAST:
+ case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+ case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT:
+ case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+ case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT:
+ mask = 0xFF0000FF;
+ break;
+ default:
+ throw new AssertionError();
+ }
+ if ((typeRef & ~mask) != 0) {
+ throw new IllegalArgumentException(
+ "Invalid type reference 0x" + Integer.toHexString(typeRef));
+ }
+ }
+
+ /**
+ * Returns the package name of an internal name.
+ *
+ * @param name an internal name.
+ * @return the package name or "" if there is no package.
+ */
+ private static String packageName(final String name) {
+ int index = name.lastIndexOf('/');
+ if (index == -1) {
+ return "";
+ }
+ return name.substring(0, index);
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Static verification methods
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Checks the given class.
+ *
+ * <p>Usage: CheckClassAdapter <binary class name or class file name>
+ *
+ * @param args the command line arguments.
+ * @throws IOException if the class cannot be found, or if an IO exception occurs.
+ */
+ public static void main(final String[] args) throws IOException {
+ if (args.length != 1) {
+ System.err.println(
+ "Verifies the given class.\n"
+ + "Usage: CheckClassAdapter <fully qualified class name or class file name>");
+ return;
+ }
+
+ ClassReader classReader;
+ if (args[0].endsWith(".class")) {
+ InputStream inputStream =
+ new FileInputStream(args[0]); // NOPMD(AvoidFileStream): can't fix for 1.5 compatibility
+ classReader = new ClassReader(inputStream);
+ } else {
+ classReader = new ClassReader(args[0]);
+ }
+
+ verify(classReader, false, new PrintWriter(System.err));
+ }
+
+ /**
+ * Checks the given class.
+ *
+ * @param classReader the class to be checked.
+ * @param printResults whether to print the results of the bytecode verification.
+ * @param printWriter where the results (or the stack trace in case of error) must be printed.
+ */
+ public static void verify(
+ final ClassReader classReader, final boolean printResults, final PrintWriter printWriter) {
+ verify(classReader, null, printResults, printWriter);
+ }
+
+ /**
+ * Checks the given class.
+ *
+ * @param classReader the class to be checked.
+ * @param loader a <code>ClassLoader</code> which will be used to load referenced classes. May be
+ * {@literal null}.
+ * @param printResults whether to print the results of the bytecode verification.
+ * @param printWriter where the results (or the stack trace in case of error) must be printed.
+ */
+ public static void verify(
+ final ClassReader classReader,
+ final ClassLoader loader,
+ final boolean printResults,
+ final PrintWriter printWriter) {
+ ClassNode classNode = new ClassNode();
+ classReader.accept(
+ new CheckClassAdapter(Opcodes.ASM7, classNode, false) {}, ClassReader.SKIP_DEBUG);
+
+ Type syperType = classNode.superName == null ? null : Type.getObjectType(classNode.superName);
+ List<MethodNode> methods = classNode.methods;
+
+ List<Type> interfaces = new ArrayList<Type>();
+ for (String interfaceName : classNode.interfaces) {
+ interfaces.add(Type.getObjectType(interfaceName));
+ }
+
+ for (MethodNode method : methods) {
+ SimpleVerifier verifier =
+ new SimpleVerifier(
+ Type.getObjectType(classNode.name),
+ syperType,
+ interfaces,
+ (classNode.access & Opcodes.ACC_INTERFACE) != 0);
+ Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(verifier);
+ if (loader != null) {
+ verifier.setClassLoader(loader);
+ }
+ try {
+ analyzer.analyze(classNode.name, method);
+ } catch (AnalyzerException e) {
+ e.printStackTrace(printWriter);
+ }
+ if (printResults) {
+ printAnalyzerResult(method, analyzer, printWriter);
+ }
+ }
+ printWriter.flush();
+ }
+
+ static void printAnalyzerResult(
+ final MethodNode method, final Analyzer<BasicValue> analyzer, final PrintWriter printWriter) {
+ Textifier textifier = new Textifier();
+ TraceMethodVisitor traceMethodVisitor = new TraceMethodVisitor(textifier);
+
+ printWriter.println(method.name + method.desc);
+ for (int i = 0; i < method.instructions.size(); ++i) {
+ method.instructions.get(i).accept(traceMethodVisitor);
+
+ StringBuilder stringBuilder = new StringBuilder();
+ Frame<BasicValue> frame = analyzer.getFrames()[i];
+ if (frame == null) {
+ stringBuilder.append('?');
+ } else {
+ for (int j = 0; j < frame.getLocals(); ++j) {
+ stringBuilder.append(getUnqualifiedName(frame.getLocal(j).toString())).append(' ');
}
- return pos;
- }
-
- /**
- * Checks a single character.
- *
- * @param signature
- * a string containing the signature that must be checked.
- * @param pos
- * index of first character to be checked.
- * @return the index of the first character after the checked part.
- */
- private static int checkChar(final char c, final String signature, int pos) {
- if (getChar(signature, pos) == c) {
- return pos + 1;
+ stringBuilder.append(" : ");
+ for (int j = 0; j < frame.getStackSize(); ++j) {
+ stringBuilder.append(getUnqualifiedName(frame.getStack(j).toString())).append(' ');
}
- throw new IllegalArgumentException(signature + ": '" + c
- + "' expected at index " + pos);
- }
-
- /**
- * Returns the signature car at the given index.
- *
- * @param signature
- * a signature.
- * @param pos
- * an index in signature.
- * @return the character at the given index, or 0 if there is no such
- * character.
- */
- private static char getChar(final String signature, int pos) {
- return pos < signature.length() ? signature.charAt(pos) : (char) 0;
+ }
+ while (stringBuilder.length() < method.maxStack + method.maxLocals + 1) {
+ stringBuilder.append(' ');
+ }
+ printWriter.print(Integer.toString(i + 100000).substring(1));
+ printWriter.print(
+ " " + stringBuilder + " : " + textifier.text.get(textifier.text.size() - 1));
+ }
+ for (TryCatchBlockNode tryCatchBlock : method.tryCatchBlocks) {
+ tryCatchBlock.accept(traceMethodVisitor);
+ printWriter.print(" " + textifier.text.get(textifier.text.size() - 1));
+ }
+ printWriter.println();
+ }
+
+ private static String getUnqualifiedName(final String name) {
+ int lastSlashIndex = name.lastIndexOf('/');
+ if (lastSlashIndex == -1) {
+ return name;
+ } else {
+ int endIndex = name.length();
+ if (name.charAt(endIndex - 1) == ';') {
+ endIndex--;
+ }
+ return name.substring(lastSlashIndex + 1, endIndex);
}
+ }
}