blob: 719f91b08c79d393b779d0eb64aab10220ddd601 [file] [log] [blame]
/*
* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
import com.sun.tools.classfile.AccessFlags;
import com.sun.tools.classfile.Annotation;
import com.sun.tools.classfile.Annotation.Annotation_element_value;
import com.sun.tools.classfile.Annotation.Array_element_value;
import com.sun.tools.classfile.Annotation.Class_element_value;
import com.sun.tools.classfile.Annotation.Enum_element_value;
import com.sun.tools.classfile.Annotation.Primitive_element_value;
import com.sun.tools.classfile.AnnotationDefault_attribute;
import com.sun.tools.classfile.Attribute;
import com.sun.tools.classfile.Attributes;
import com.sun.tools.classfile.BootstrapMethods_attribute;
import com.sun.tools.classfile.CharacterRangeTable_attribute;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.Code_attribute;
import com.sun.tools.classfile.CompilationID_attribute;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Module_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Package_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_InterfaceMethodref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_InvokeDynamic_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Long_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_MethodHandle_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_MethodType_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_NameAndType_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_String_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
import com.sun.tools.classfile.ConstantPool.CPInfo;
import com.sun.tools.classfile.ConstantPool.InvalidIndex;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.ConstantValue_attribute;
import com.sun.tools.classfile.DefaultAttribute;
import com.sun.tools.classfile.Deprecated_attribute;
import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
import com.sun.tools.classfile.EnclosingMethod_attribute;
import com.sun.tools.classfile.Exceptions_attribute;
import com.sun.tools.classfile.Field;
import com.sun.tools.classfile.InnerClasses_attribute;
import com.sun.tools.classfile.InnerClasses_attribute.Info;
import com.sun.tools.classfile.Instruction;
import com.sun.tools.classfile.Instruction.TypeKind;
import com.sun.tools.classfile.LineNumberTable_attribute;
import com.sun.tools.classfile.LocalVariableTable_attribute;
import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
import com.sun.tools.classfile.Method;
import com.sun.tools.classfile.MethodParameters_attribute;
import com.sun.tools.classfile.Module_attribute;
import com.sun.tools.classfile.Module_attribute.ExportsEntry;
import com.sun.tools.classfile.Module_attribute.ProvidesEntry;
import com.sun.tools.classfile.Module_attribute.RequiresEntry;
import com.sun.tools.classfile.ModuleHashes_attribute;
import com.sun.tools.classfile.ModuleHashes_attribute.Entry;
import com.sun.tools.classfile.ModuleMainClass_attribute;
import com.sun.tools.classfile.ModuleResolution_attribute;
import com.sun.tools.classfile.ModuleTarget_attribute;
import com.sun.tools.classfile.ModulePackages_attribute;
import com.sun.tools.classfile.Opcode;
import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
import com.sun.tools.classfile.Signature_attribute;
import com.sun.tools.classfile.SourceDebugExtension_attribute;
import com.sun.tools.classfile.SourceFile_attribute;
import com.sun.tools.classfile.SourceID_attribute;
import com.sun.tools.classfile.StackMapTable_attribute;
import com.sun.tools.classfile.StackMapTable_attribute.append_frame;
import com.sun.tools.classfile.StackMapTable_attribute.chop_frame;
import com.sun.tools.classfile.StackMapTable_attribute.full_frame;
import com.sun.tools.classfile.StackMapTable_attribute.same_frame;
import com.sun.tools.classfile.StackMapTable_attribute.same_frame_extended;
import com.sun.tools.classfile.StackMapTable_attribute.same_locals_1_stack_item_frame;
import com.sun.tools.classfile.StackMapTable_attribute.same_locals_1_stack_item_frame_extended;
import com.sun.tools.classfile.StackMap_attribute;
import com.sun.tools.classfile.Synthetic_attribute;
import com.sun.tools.classfile.TypeAnnotation;
import com.sun.tools.classfile.TypeAnnotation.Position;
import static com.sun.tools.classfile.TypeAnnotation.TargetType.THROWS;
import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import xmlkit.XMLKit.Element;
/*
* @author jrose, ksrini
*/
public class ClassReader {
private static final CommandLineParser CLP = new CommandLineParser(""
+ "-source: +> = \n"
+ "-dest: +> = \n"
+ "-encoding: +> = \n"
+ "-jcov $ \n -nojcov !-jcov \n"
+ "-verbose $ \n -noverbose !-verbose \n"
+ "-keepPath $ \n -nokeepPath !-keepPath \n"
+ "-keepCP $ \n -nokeepCP !-keepCP \n"
+ "-keepOrder $ \n -nokeepOrder !-keepOrder \n"
+ "-continue $ \n -nocontinue !-continue \n"
+ "-@ >-@ . \n"
+ "- +? \n"
+ "\n");
// Protected state for representing the class file.
protected Element cfile; // <ClassFile ...>
protected Element cpool; // <ConstantPool ...>
protected Element klass; // <Class ...>
protected List<String> thePool; // stringified flattened Constant Pool
public static void main(String[] ava) throws IOException {
ArrayList<String> av = new ArrayList<>(Arrays.asList(ava));
HashMap<String, String> props = new HashMap<>();
props.put("-encoding:", "UTF8"); // default
props.put("-keepOrder", null); // CLI default
props.put("-pretty", "1"); // CLI default
props.put("-continue", "1"); // CLI default
CLP.parse(av, props);
//System.out.println(props+" ++ "+av);
File source = asFile(props.get("-source:"));
File dest = asFile(props.get("-dest:"));
String encoding = props.get("-encoding:");
boolean contError = props.containsKey("-continue");
ClassReader options = new ClassReader();
options.copyOptionsFrom(props);
/*
if (dest == null && av.size() > 1) {
dest = File.createTempFile("TestOut", ".dir", new File("."));
dest.delete();
if (!dest.mkdir())
throw new RuntimeException("Cannot create "+dest);
System.out.println("Writing results to "+dest);
}
*/
if (av.isEmpty()) {
av.add(""); //to enter this loop
}
boolean readList = false;
for (String a : av) {
if (readList) {
readList = false;
InputStream fin;
if (a.equals("-")) {
fin = System.in;
} else {
fin = new FileInputStream(a);
}
BufferedReader files = makeReader(fin, encoding);
for (String file; (file = files.readLine()) != null;) {
doFile(file, source, dest, options, encoding, contError);
}
if (fin != System.in) {
fin.close();
}
} else if (a.equals("-@")) {
readList = true;
} else if (a.startsWith("-")) {
throw new RuntimeException("Bad flag argument: " + a);
} else if (source.getName().endsWith(".jar")) {
doJar(a, source, dest, options, encoding, contError);
} else {
doFile(a, source, dest, options, encoding, contError);
}
}
}
private static File asFile(String str) {
return (str == null) ? null : new File(str);
}
private static void doFile(String a,
File source, File dest,
ClassReader options, String encoding,
boolean contError) throws IOException {
if (!contError) {
doFile(a, source, dest, options, encoding);
} else {
try {
doFile(a, source, dest, options, encoding);
} catch (Exception ee) {
System.out.println("Error processing " + source + ": " + ee);
ee.printStackTrace();
}
}
}
private static void doJar(String a, File source, File dest,
ClassReader options, String encoding,
Boolean contError) throws IOException {
try {
JarFile jf = new JarFile(source);
for (JarEntry je : Collections.list(jf.entries())) {
String name = je.getName();
if (!name.endsWith(".class")) {
continue;
}
try {
doStream(name, jf.getInputStream(je), dest, options, encoding);
} catch (Exception e) {
if (contError) {
System.out.println("Error processing " + source + ": " + e);
e.printStackTrace();
continue;
}
}
}
} catch (IOException ioe) {
throw ioe;
}
}
private static void doStream(String a, InputStream in, File dest,
ClassReader options, String encoding) throws IOException {
File f = new File(a);
ClassReader cr = new ClassReader(options);
Element e;
if (options.verbose) {
System.out.println("Reading " + f);
}
e = cr.readFrom(in);
OutputStream out;
if (dest == null) {
out = System.out;
} else {
File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
String outName = outf.getName();
File outSubdir = outf.getParentFile();
outSubdir.mkdirs();
int extPos = outName.lastIndexOf('.');
if (extPos > 0) {
outf = new File(outSubdir, outName.substring(0, extPos) + ".xml");
}
out = new FileOutputStream(outf);
}
Writer outw = makeWriter(out, encoding);
if (options.pretty || !options.keepOrder) {
e.writePrettyTo(outw);
} else {
e.writeTo(outw);
}
if (out == System.out) {
outw.write("\n");
outw.flush();
} else {
outw.close();
}
}
private static void doFile(String a,
File source, File dest,
ClassReader options, String encoding) throws IOException {
File inf = new File(source, a);
if (dest != null && options.verbose) {
System.out.println("Reading " + inf);
}
BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf));
doStream(a, in, dest, options, encoding);
}
public static BufferedReader makeReader(InputStream in,
String encoding) throws IOException {
Reader inw;
in = new BufferedInputStream(in); // add buffering
if (encoding == null) {
inw = new InputStreamReader(in);
} else {
inw = new InputStreamReader(in, encoding);
}
return new BufferedReader(inw); // add buffering
}
public static Writer makeWriter(OutputStream out,
String encoding) throws IOException {
Writer outw;
if (encoding == null) {
outw = new OutputStreamWriter(out);
} else {
outw = new OutputStreamWriter(out, encoding);
}
return new BufferedWriter(outw); // add buffering
}
public Element result() {
return cfile;
}
protected InputStream in;
protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
// input options
public boolean pretty = false;
public boolean verbose = false;
public boolean keepPath = false;
public boolean keepCP = false;
public boolean keepBytes = false;
public boolean parseBytes = true;
public boolean resolveRefs = true;
public boolean keepOrder = true;
public boolean keepSizes = false;
public ClassReader() {
cfile = new Element("ClassFile");
}
public ClassReader(ClassReader options) {
this();
copyOptionsFrom(options);
}
public void copyOptionsFrom(ClassReader options) {
pretty = options.pretty;
verbose = options.verbose;
keepPath = options.keepPath;
keepCP = options.keepCP;
keepOrder = options.keepOrder;
}
public void copyOptionsFrom(Map<String, String> options) {
if (options.containsKey("-pretty")) {
pretty = (options.get("-pretty") != null);
}
if (options.containsKey("-verbose")) {
verbose = (options.get("-verbose") != null);
}
if (options.containsKey("-keepPath")) {
keepPath = (options.get("-keepPath") != null);
}
if (options.containsKey("-keepCP")) {
keepCP = (options.get("-keepCP") != null);
}
if (options.containsKey("-keepOrder")) {
keepOrder = (options.get("-keepOrder") != null);
}
}
protected String getCpString(int i) {
return thePool.get(i);
}
public Element readFrom(InputStream in) throws IOException {
try {
this.in = in;
ClassFile c = ClassFile.read(in);
// read the file header
if (c.magic != 0xCAFEBABE) {
throw new RuntimeException("bad magic number " +
Integer.toHexString(c.magic));
}
cfile.setAttr("magic", "" + c.magic);
int minver = c.minor_version;
int majver = c.major_version;
cfile.setAttr("minver", "" + minver);
cfile.setAttr("majver", "" + majver);
readCP(c);
readClass(c);
return result();
} catch (InvalidDescriptor | ConstantPoolException ex) {
throw new IOException("Fatal error", ex);
}
}
public Element readFrom(File file) throws IOException {
try (InputStream strm = new FileInputStream(file)) {
Element e = readFrom(new BufferedInputStream(strm));
if (keepPath) {
e.setAttr("path", file.toString());
}
return e;
}
}
private void readClass(ClassFile c) throws IOException,
ConstantPoolException,
InvalidDescriptor {
klass = new Element("Class");
cfile.add(klass);
String thisk = c.getName();
klass.setAttr("name", thisk);
AccessFlags af = new AccessFlags(c.access_flags.flags);
klass.setAttr("flags", flagString(af, klass));
if (!"java/lang/Object".equals(thisk)) {
if (c.super_class != 0) {
klass.setAttr("super", c.getSuperclassName());
}
}
for (int i : c.interfaces) {
klass.add(new Element("Interface", "name", getCpString(i)));
}
readFields(c, klass);
readMethods(c, klass);
readAttributesFor(c, c.attributes, klass);
klass.trimToSize();
}
private void readFields(ClassFile c, Element klass) throws IOException {
int len = c.fields.length;
Element fields = new Element(len);
for (Field f : c.fields) {
Element field = new Element("Field");
field.setAttr("name", getCpString(f.name_index));
field.setAttr("type", getCpString(f.descriptor.index));
field.setAttr("flags", flagString(f.access_flags.flags, field));
readAttributesFor(c, f.attributes, field);
field.trimToSize();
fields.add(field);
}
if (!keepOrder) {
fields.sort();
}
klass.addAll(fields);
}
private void readMethods(ClassFile c, Element klass) throws IOException {
int len = c.methods.length;
Element methods = new Element(len);
for (Method m : c.methods) {
Element member = new Element("Method");
member.setAttr("name", getCpString(m.name_index));
member.setAttr("type", getCpString(m.descriptor.index));
member.setAttr("flags", flagString(m.access_flags.flags, member));
readAttributesFor(c, m.attributes, member);
member.trimToSize();
methods.add(member);
}
if (!keepOrder) {
methods.sort();
}
klass.addAll(methods);
}
private AccessFlags.Kind getKind(Element e) {
switch(e.getName()) {
case "Class":
return AccessFlags.Kind.Class;
case "InnerClass":
return AccessFlags.Kind.InnerClass;
case "Field":
return AccessFlags.Kind.Field ;
case "Method":
return AccessFlags.Kind.Method;
default: throw new RuntimeException("should not reach here");
}
}
protected String flagString(int flags, Element holder) {
return flagString(new AccessFlags(flags), holder);
}
protected String flagString(AccessFlags af, Element holder) {
return flagString(af, holder.getName());
}
protected String flagString(int flags, String kind) {
return flagString(new AccessFlags(flags), kind);
}
protected String flagString(AccessFlags af, String kind) {
Set<String> mods = null;
switch (kind) {
case "Class":
mods = af.getClassFlags();
break;
case "InnerClass":
mods = af.getInnerClassFlags();
break;
case "Field":
mods = af.getFieldFlags();
break;
case "Method":
mods = af.getMethodFlags();
break;
default:
throw new RuntimeException("should not reach here");
}
StringBuilder sb = new StringBuilder();
for (String x : mods) {
sb.append(x.substring(x.indexOf('_') + 1).toLowerCase()).append(" ");
}
return sb.toString().trim();
}
protected void readAttributesFor(ClassFile c, Attributes attrs, Element x) {
Element container = new Element();
AttributeVisitor av = new AttributeVisitor(this, c);
for (Attribute a : attrs) {
av.visit(a, container);
}
if (!keepOrder) {
container.sort();
}
x.addAll(container);
}
private int fileSize = 0;
private HashMap<String, int[]> attrSizes = new HashMap<>();
private void attachTo(Element x, Object aval0) {
if (aval0 == null) {
return;
}
if (!(aval0 instanceof Element)) {
x.add(aval0);
return;
}
Element aval = (Element) aval0;
if (!aval.isAnonymous()) {
x.add(aval);
return;
}
for (int imax = aval.attrSize(), i = 0; i < imax; i++) {
//%%
attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i));
}
x.addAll(aval);
}
private void attachAttrTo(Element x, String aname, String aval) {
String aval0 = x.getAttr(aname);
if (aval0 != null) {
aval = aval0 + " " + aval;
}
x.setAttr(aname, aval);
}
private void readCP(ClassFile c) throws IOException {
cpool = new Element("ConstantPool", c.constant_pool.size());
ConstantPoolVisitor cpv = new ConstantPoolVisitor(cpool, c,
c.constant_pool.size());
for (int i = 1 ; i < c.constant_pool.size() ; i++) {
try {
cpv.visit(c.constant_pool.get(i), i);
} catch (InvalidIndex ex) {
// can happen periodically when accessing doubles etc. ignore it
// ex.printStackTrace();
}
}
thePool = cpv.getPoolList();
if (verbose) {
for (int i = 0; i < thePool.size(); i++) {
System.out.println("[" + i + "]: " + thePool.get(i));
}
}
if (keepCP) {
cfile.add(cpool);
}
}
}
class ConstantPoolVisitor implements ConstantPool.Visitor<String, Integer> {
final List<String> slist;
final Element xpool;
final ClassFile cf;
final ConstantPool cfpool;
final List<String> bsmlist;
public ConstantPoolVisitor(Element xpool, ClassFile cf, int size) {
slist = new ArrayList<>(size);
for (int i = 0 ; i < size; i++) {
slist.add(null);
}
this.xpool = xpool;
this.cf = cf;
this.cfpool = cf.constant_pool;
bsmlist = readBSM();
}
public List<String> getPoolList() {
return Collections.unmodifiableList(slist);
}
public List<String> getBSMList() {
return Collections.unmodifiableList(bsmlist);
}
public String visit(CPInfo c, int index) {
return c.accept(this, index);
}
private List<String> readBSM() {
BootstrapMethods_attribute bsmAttr =
(BootstrapMethods_attribute) cf.getAttribute(Attribute.BootstrapMethods);
if (bsmAttr != null) {
List<String> out =
new ArrayList<>(bsmAttr.bootstrap_method_specifiers.length);
for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsms :
bsmAttr.bootstrap_method_specifiers) {
int index = bsms.bootstrap_method_ref;
try {
String value = slist.get(index);
String bsmStr = value;
if (value == null) {
value = visit(cfpool.get(index), index);
slist.set(index, value);
}
bsmStr = value;
for (int idx : bsms.bootstrap_arguments) {
value = slist.get(idx);
if (value == null) {
value = visit(cfpool.get(idx), idx);
slist.set(idx, value);
}
bsmStr = bsmStr.concat("," + value);
}
out.add(bsmStr);
} catch (InvalidIndex ex) {
ex.printStackTrace();
}
}
return out;
}
return new ArrayList<>(0);
}
@Override
public String visitClass(CONSTANT_Class_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.name_index), c.name_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Class",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitModule(CONSTANT_Module_info info, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(info.name_index), info.name_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Module",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitPackage(CONSTANT_Package_info info, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(info.name_index), info.name_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Package",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitDouble(CONSTANT_Double_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
value = Double.toString(c.value);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Double",
new String[]{"id", p.toString()},
value));
}
return value;
}
@Override
public String visitFieldref(CONSTANT_Fieldref_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.class_index), c.class_index);
value = value.concat(" " + visit(cfpool.get(c.name_and_type_index),
c.name_and_type_index));
slist.set(p, value);
xpool.add(new Element("CONSTANT_Fieldref",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitFloat(CONSTANT_Float_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
value = Float.toString(c.value);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Float",
new String[]{"id", p.toString()},
value));
}
return value;
}
@Override
public String visitInteger(CONSTANT_Integer_info cnstnt, Integer p) {
String value = slist.get(p);
if (value == null) {
value = Integer.toString(cnstnt.value);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Integer",
new String[]{"id", p.toString()},
value));
}
return value;
}
@Override
public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c,
Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.class_index), c.class_index);
value = value.concat(" " +
visit(cfpool.get(c.name_and_type_index),
c.name_and_type_index));
slist.set(p, value);
xpool.add(new Element("CONSTANT_InterfaceMethodref",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = bsmlist.get(c.bootstrap_method_attr_index) + " "
+ visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_InvokeDynamic",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitLong(CONSTANT_Long_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
value = Long.toString(c.value);
slist.set(p, value);
xpool.add(new Element("CONSTANT_Long",
new String[]{"id", p.toString()},
value));
}
return value;
}
@Override
public String visitNameAndType(CONSTANT_NameAndType_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.name_index), c.name_index);
value = value.concat(" " +
visit(cfpool.get(c.type_index), c.type_index));
slist.set(p, value);
xpool.add(new Element("CONSTANT_NameAndType",
new String[]{"id", p.toString()},
value));
} catch (InvalidIndex ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitMethodref(CONSTANT_Methodref_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.class_index), c.class_index);
value = value.concat(" " +
visit(cfpool.get(c.name_and_type_index),
c.name_and_type_index));
slist.set(p, value);
xpool.add(new Element("CONSTANT_Methodref",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = c.reference_kind.name();
value = value.concat(" "
+ visit(cfpool.get(c.reference_index), c.reference_index));
slist.set(p, value);
xpool.add(new Element("CONSTANT_MethodHandle",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitMethodType(CONSTANT_MethodType_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
try {
value = visit(cfpool.get(c.descriptor_index), c.descriptor_index);
slist.set(p, value);
xpool.add(new Element("CONSTANT_MethodType",
new String[]{"id", p.toString()},
value));
} catch (ConstantPoolException ex) {
ex.printStackTrace();
}
}
return value;
}
@Override
public String visitString(CONSTANT_String_info c, Integer p) {
try {
String value = slist.get(p);
if (value == null) {
value = c.getString();
slist.set(p, value);
xpool.add(new Element("CONSTANT_String",
new String[]{"id", p.toString()},
value));
}
return value;
} catch (ConstantPoolException ex) {
throw new RuntimeException("Fatal error", ex);
}
}
@Override
public String visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) {
String value = slist.get(p);
if (value == null) {
value = cnstnt.value;
slist.set(p, value);
xpool.add(new Element("CONSTANT_Utf8",
new String[]{"id", p.toString()},
value));
}
return value;
}
}
class AttributeVisitor implements Attribute.Visitor<Element, Element> {
final ClassFile cf;
final ClassReader x;
final AnnotationsElementVisitor aev;
final InstructionVisitor iv;
public AttributeVisitor(ClassReader x, ClassFile cf) {
this.x = x;
this.cf = cf;
iv = new InstructionVisitor(x, cf);
aev = new AnnotationsElementVisitor(x, cf);
}
public void visit(Attribute a, Element parent) {
a.accept(this, parent);
}
@Override
public Element visitBootstrapMethods(BootstrapMethods_attribute bm, Element p) {
Element e = new Element(x.getCpString(bm.attribute_name_index));
for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : bm.bootstrap_method_specifiers) {
Element be = new Element("BootstrapMethodSpecifier");
be.setAttr("ref", x.getCpString(bsm.bootstrap_method_ref));
if (bsm.bootstrap_arguments.length > 0) {
Element bme = new Element("MethodArguments");
for (int index : bsm.bootstrap_arguments) {
bme.add(x.getCpString(index));
}
bme.trimToSize();
be.add(bme);
}
be.trimToSize();
e.add(be);
}
e.trimToSize();
if (!x.keepOrder) {
e.sort();
}
p.add(e);
return null;
}
@Override
public Element visitDefault(DefaultAttribute da, Element p) {
Element e = new Element(x.getCpString(da.attribute_name_index));
StringBuilder sb = new StringBuilder();
for (byte x : da.info) {
sb.append("0x").append(Integer.toHexString(x)).append(" ");
}
e.setAttr("bytes", sb.toString().trim());
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitAnnotationDefault(AnnotationDefault_attribute ad, Element p) {
Element e = new Element(x.getCpString(ad.attribute_name_index));
e.setAttr("tag", "" + ad.default_value.tag);
Element child = aev.visit(ad.default_value, e);
if (child != null) {
e.add(child);
}
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitCharacterRangeTable(CharacterRangeTable_attribute crt,
Element p) {
Element e = new Element(x.getCpString(crt.attribute_name_index));
for (CharacterRangeTable_attribute.Entry ce : crt.character_range_table) {
e.setAttr("start_pc", "" + ce.start_pc);
e.setAttr("end_pc", "" + ce.end_pc);
e.setAttr("range_start", "" + ce.character_range_start);
e.setAttr("range_end", "" + ce.character_range_end);
e.setAttr("flags", x.flagString(ce.flags, "Method"));
}
e.trimToSize();
p.add(e);
return null;
}
private Element instructions(Element code, Code_attribute c) {
Element ielement = new Element("Instructions");
for (Instruction ins : c.getInstructions()) {
ielement.add(iv.visit(ins));
}
ielement.trimToSize();
return ielement;
}
@Override
public Element visitCode(Code_attribute c, Element p) {
Element e = null;
e = new Element(x.getCpString(c.attribute_name_index),
"stack", "" + c.max_stack,
"local", "" + c.max_locals);
e.add(instructions(e, c));
for (Code_attribute.Exception_data edata : c.exception_table) {
e.add(new Element("Handler",
"start", "" + edata.start_pc,
"end", "" + edata.end_pc,
"catch", "" + edata.handler_pc,
"class", x.getCpString(edata.catch_type)));
}
this.x.readAttributesFor(cf, c.attributes, e);
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitCompilationID(CompilationID_attribute cid, Element p) {
Element e = new Element(x.getCpString(cid.attribute_name_index),
x.getCpString(cid.compilationID_index));
p.add(e);
return null;
}
@Override
public Element visitModulePackages(ModulePackages_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
for (int i : attr.packages_index) {
Element ee = new Element("Package");
String pkg = x.getCpString(i);
ee.setAttr("package", pkg);
e.add(ee);
}
e.trimToSize();
e.sort();
p.add(e);
return null;
}
@Override
public Element visitConstantValue(ConstantValue_attribute cv, Element p) {
Element e = new Element(x.getCpString(cv.attribute_name_index));
e.add(x.getCpString(cv.constantvalue_index));
p.add(e);
return null;
}
@Override
public Element visitDeprecated(Deprecated_attribute d, Element p) {
Element e = new Element(x.getCpString(d.attribute_name_index));
p.add(e);
return null;
}
@Override
public Element visitEnclosingMethod(EnclosingMethod_attribute em, Element p) {
Element e = new Element(x.getCpString(em.attribute_name_index));
e.setAttr("class", x.getCpString(em.class_index));
e.setAttr("desc", x.getCpString(em.method_index));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitExceptions(Exceptions_attribute e, Element p) {
Element ee = new Element(x.getCpString(e.attribute_name_index));
for (int idx : e.exception_index_table) {
Element n = new Element("Item");
n.setAttr("class", x.getCpString(idx));
ee.add(n);
}
ee.trimToSize();
p.add(ee);
return null;
}
@Override
public Element visitInnerClasses(InnerClasses_attribute ic, Element p) {
for (Info info : ic.classes) {
Element e = new Element(x.getCpString(ic.attribute_name_index));
e.setAttr("class", x.getCpString(info.inner_class_info_index));
e.setAttr("outer", x.getCpString(info.outer_class_info_index));
e.setAttr("name", x.getCpString(info.inner_name_index));
e.setAttr("flags", x.flagString(info.inner_class_access_flags,
"InnerClass"));
e.trimToSize();
p.add(e);
}
return null;
}
@Override
public Element visitLineNumberTable(LineNumberTable_attribute lnt, Element p) {
String name = x.getCpString(lnt.attribute_name_index);
for (LineNumberTable_attribute.Entry e : lnt.line_number_table) {
Element l = new Element(name);
l.setAttr("bci", "" + e.start_pc);
l.setAttr("line", "" + e.line_number);
l.trimToSize();
p.add(l);
}
return null; // already added to parent
}
@Override
public Element visitLocalVariableTable(LocalVariableTable_attribute lvt,
Element p) {
String name = x.getCpString(lvt.attribute_name_index);
for (LocalVariableTable_attribute.Entry e : lvt.local_variable_table) {
Element l = new Element(name);
l.setAttr("bci", "" + e.start_pc);
l.setAttr("span", "" + e.length);
l.setAttr("name", x.getCpString(e.name_index));
l.setAttr("type", x.getCpString(e.descriptor_index));
l.setAttr("slot", "" + e.index);
l.trimToSize();
p.add(l);
}
return null; // already added to parent
}
private void parseModuleRequires(RequiresEntry[] res, Element p) {
for (RequiresEntry re : res) {
Element er = new Element("Requires");
er.setAttr("module", x.getCpString(re.requires_index));
er.setAttr("flags", Integer.toString(re.requires_flags));
p.add(er);
}
}
private void parseModuleExports(ExportsEntry[] exports, Element p) {
Element ex = new Element("Exports");
for (ExportsEntry export : exports) {
Element exto = new Element("exports");
exto.setAttr("package", x.getCpString(export.exports_index));
for (int idx : export.exports_to_index) {
exto.setAttr("module", x.getCpString(idx));
}
ex.add(exto);
}
p.add(ex);
}
private void parseModuleProvides(ProvidesEntry[] provides, Element p) {
Element ex = new Element("Provides");
for (ProvidesEntry provide : provides) {
ex.setAttr("provides", x.getCpString(provide.provides_index));
for (int idx : provide.with_index) {
ex.setAttr("with", x.getCpString(idx));
}
}
p.add(ex);
}
@Override
public Element visitModule(Module_attribute m, Element p) {
Element e = new Element(x.getCpString(m.attribute_name_index));
parseModuleRequires(m.requires, e);
parseModuleExports(m.exports, e);
for (int idx : m.uses_index) {
Element ei = new Element("Uses");
ei.setAttr("used_class", x.getCpString(idx));
e.add(ei);
}
parseModuleProvides(m.provides, e);
p.add(e);
return null;
}
@Override
public Element visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt,
Element p) {
String name = x.getCpString(lvtt.attribute_name_index);
for (LocalVariableTypeTable_attribute.Entry e : lvtt.local_variable_table) {
Element l = new Element(name);
l.setAttr("bci", "" + e.start_pc);
l.setAttr("span", "" + e.length);
l.setAttr("name", x.getCpString(e.name_index));
l.setAttr("type", x.getCpString(e.signature_index));
l.setAttr("slot", "" + e.index);
l.trimToSize();
p.add(l);
}
return null; // already added to parent
}
@Override
public Element visitMethodParameters(MethodParameters_attribute mp, Element p) {
String name = x.getCpString(mp.attribute_name_index);
for (MethodParameters_attribute.Entry e : mp.method_parameter_table) {
Element l = new Element(name);
l.setAttr("name", x.getCpString(e.name_index));
l.setAttr("flag", "" + e.flags);
l.trimToSize();
p.add(l);
}
return null; // already added to parent
}
private void parseAnnotation(Annotation anno, Element p) {
Element ea = new Element("Annotation");
ea.setAttr("name", "" + x.getCpString(anno.type_index));
for (Annotation.element_value_pair evp : anno.element_value_pairs) {
Element evpe = new Element("Element");
evpe.setAttr("tag", "" + evp.value.tag);
evpe.setAttr("value", x.getCpString(evp.element_name_index));
Element child = aev.visit(evp.value, evpe);
if (child != null) {
evpe.add(child);
}
ea.add(evpe);
}
ea.trimToSize();
p.add(ea);
}
private void parseAnnotations(Annotation[] ra, Element p) {
for (Annotation anno : ra) {
parseAnnotation(anno, p);
}
}
@Override
public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva,
Element p) {
Element e = new Element(x.getCpString(rva.attribute_name_index));
parseAnnotations(rva.annotations, e);
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria,
Element p) {
Element e = new Element(x.getCpString(ria.attribute_name_index));
parseAnnotations(ria.annotations, e);
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa,
Element p) {
Element e = new Element(x.getCpString(rvpa.attribute_name_index));
for (Annotation[] pa : rvpa.parameter_annotations) {
parseAnnotations(pa, e);
}
p.add(e);
return null;
}
@Override
public Element visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa,
Element p) {
Element e = new Element(x.getCpString(ripa.attribute_name_index));
for (Annotation[] pa : ripa.parameter_annotations) {
parseAnnotations(pa, e);
}
p.add(e);
return null;
}
private void parsePosition(Position ap, Element p) {
Element te = new Element();
switch (ap.type) {
case CLASS_TYPE_PARAMETER: // 0x00
te.setName("CLASS_TYPE_PARAMETER");
te.setAttr("idx", "" + ap.parameter_index);
break;
case METHOD_TYPE_PARAMETER: // 0x01
te.setName("METHOD_TYPE_PARAMETER");
te.setAttr("idx", "" + ap.parameter_index);
break;
case CLASS_EXTENDS: // 0x10
te.setName("CLASS_EXTENDS");
te.setAttr("idx", "" + ap.type_index);
break;
case CLASS_TYPE_PARAMETER_BOUND: // 0x11
te.setName("CLASS_TYPE_PARAMETER_BOUND");
te.setAttr("idx1", "" + ap.parameter_index);
te.setAttr("idx2", "" + ap.bound_index);
break;
case METHOD_TYPE_PARAMETER_BOUND: // 0x12
te.setName("METHOD_TYPE_PARAMETER_BOUND");
te.setAttr("idx1", "" + ap.parameter_index);
te.setAttr("idx2", "" + ap.bound_index);
break;
case FIELD: // 0x13
te.setName("FIELD");
break;
case METHOD_RETURN: // 0x14
te.setName("METHOD_RETURN");
break;
case METHOD_RECEIVER: // 0x15
te.setName("METHOD_RECEIVER");
break;
case METHOD_FORMAL_PARAMETER: // 0x16
te.setName("METHOD_FORMAL_PARAMETER");
te.setAttr("idx", "" + ap.parameter_index);
break;
case THROWS: // 0x17
te.setName("THROWS");
te.setAttr("idx", "" + ap.type_index);
break;
case LOCAL_VARIABLE: // 0x40
te.setName("LOCAL_VARIABLE");
for (int i = 0; i < ap.lvarIndex.length; i++) {
te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
}
break;
case RESOURCE_VARIABLE: // 0x41
te.setName("RESOURCE_VARIABLE");
for (int i = 0; i < ap.lvarIndex.length ; i++) {
te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
}
break;
case EXCEPTION_PARAMETER: // 0x42
te.setName("EXCEPTION_PARAMETER");
te.setAttr("idx", "" + ap.exception_index);
break;
case INSTANCEOF: // 0x43
te.setName("INSTANCE_OF");
te.setAttr("off", "" + ap.offset);
break;
case NEW: // 0x44
te.setName("NEW");
te.setAttr("off", "" + ap.offset);
break;
case CONSTRUCTOR_REFERENCE: // 0x45
te.setName("CONSTRUCTOR_REFERENCE_RECEIVER");
te.setAttr("off", "" + ap.offset);
break;
case METHOD_REFERENCE: // 0x46
te.setName("METHOD_REFERENCE_RECEIVER");
te.setAttr("off", "" + ap.offset);
break;
case CAST: // 0x47
te.setName("CAST");
te.setAttr("off", "" + ap.offset);
te.setAttr("idx", "" + ap.type_index);
break;
case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: // 0x48
te.setName("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT");
te.setAttr("off", "" + ap.offset);
te.setAttr("idx", "" + ap.type_index);
break;
case METHOD_INVOCATION_TYPE_ARGUMENT: // 0x49
te.setName("METHOD_INVOCATION_TYPE_ARGUMENT");
te.setAttr("off", "" + ap.offset);
te.setAttr("idx", "" + ap.type_index);
break;
case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: // 0x4A
te.setName("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT");
te.setAttr("off", "" + ap.offset);
te.setAttr("idx", "" + ap.type_index);
break;
case METHOD_REFERENCE_TYPE_ARGUMENT: // 0x4B
te.setName("METHOD_REFERENCE_TYPE_ARGUMENT");
te.setAttr("off", "" + ap.offset);
te.setAttr("idx", "" + ap.type_index);
break;
default:
throw new RuntimeException("not implemented");
}
te.trimToSize();
p.add(te);
}
private void parseTypeAnnotations(TypeAnnotation pa, Element p) {
Element pta = new Element("RuntimeVisibleTypeAnnotation");
p.add(pta);
Position pos = pa.position;
parsePosition(pos, pta);
parseAnnotation(pa.annotation, pta);
}
@Override
public Element visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute rvta, Element p) {
Element e = new Element(x.getCpString(rvta.attribute_name_index));
for (TypeAnnotation pa : rvta.annotations) {
parseTypeAnnotations(pa, e);
}
e.sort();
p.add(e);
return null;
}
@Override
public Element visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute rita, Element p) {
Element e = new Element(x.getCpString(rita.attribute_name_index));
for (TypeAnnotation pa : rita.annotations) {
parseTypeAnnotations(pa, e);
}
e.sort();
p.add(e);
return null;
}
@Override
public Element visitSignature(Signature_attribute s, Element p) {
String aname = x.getCpString(s.attribute_name_index);
String sname = x.getCpString(s.signature_index);
Element se = new Element(aname);
se.add(sname);
se.trimToSize();
p.add(se);
return null;
}
@Override
public Element visitSourceDebugExtension(SourceDebugExtension_attribute sde,
Element p) {
String aname = x.getCpString(sde.attribute_name_index);
Element se = new Element(aname);
se.setAttr("val", sde.getValue());
se.trimToSize();
p.add(se);
return null;
}
@Override
public Element visitSourceFile(SourceFile_attribute sf, Element p) {
String aname = x.getCpString(sf.attribute_name_index);
String sname = x.getCpString(sf.sourcefile_index);
Element se = new Element(aname);
se.add(sname);
se.trimToSize();
p.add(se);
return null;
}
@Override
public Element visitSourceID(SourceID_attribute sid, Element p) {
Element e = new Element(x.getCpString(sid.attribute_name_index));
e.add(x.getCpString(sid.sourceID_index));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitStackMap(StackMap_attribute sm, Element p) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Element visitStackMapTable(StackMapTable_attribute smt, Element p) {
Element stackmap = new Element(x.getCpString(smt.attribute_name_index));
for (StackMapTable_attribute.stack_map_frame f : smt.entries) {
StackMapVisitor smv = new StackMapVisitor(x, cf, stackmap);
stackmap.add(smv.visit(f));
}
stackmap.trimToSize();
p.add(stackmap);
return null;
}
@Override
public Element visitSynthetic(Synthetic_attribute s, Element p) {
Element e = new Element(x.getCpString(s.attribute_name_index));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitModuleHashes(ModuleHashes_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
e.setAttr("Algorithm", x.getCpString(attr.algorithm_index));
for (Entry entry : attr.hashes_table) {
Element ee = new Element("Entry");
String mn = x.getCpString(entry.module_name_index);
ee.setAttr("module_name", mn);
ee.setAttr("hash_length", "" + entry.hash.length);
StringBuilder sb = new StringBuilder();
for (byte b: entry.hash) {
sb.append(String.format("%02x", b & 0xff));
}
ee.setAttr("hash", sb.toString());
ee.trimToSize();
e.add(ee);
}
e.trimToSize();
e.sort();
p.add(e);
return null;
}
@Override
public Element visitModuleMainClass(ModuleMainClass_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
e.add(x.getCpString(attr.main_class_index));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitModuleResolution(ModuleResolution_attribute attr, Element p) {
Element e = new Element("ModuleResolution");
e.setAttr("flags", Integer.toString(attr.resolution_flags));
e.trimToSize();
p.add(e);
return null;
}
@Override
public Element visitModuleTarget(ModuleTarget_attribute attr, Element p) {
Element e = new Element(x.getCpString(attr.attribute_name_index));
e.add(x.getCpString(attr.target_platform_index));
e.trimToSize();
p.add(e);
return null;
}
}
class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor<Element, Void> {
final ClassFile cf;
final ClassReader x;
final Element parent;
public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) {
this.x = x;
this.cf = cf;
this.parent = parent;
}
public Element visit(StackMapTable_attribute.stack_map_frame frame) {
return frame.accept(this, null);
}
@Override
public Element visit_same_frame(same_frame sm_frm, Void p) {
Element e = new Element("SameFrame");
e.setAttr("tag", "" + sm_frm.frame_type);
return e;
}
@Override
public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) {
Element e = new Element("SameLocals1StackItemFrame");
e.setAttr("tag", "" + s.frame_type);
e.addAll(getVerificationTypeInfo("Stack", s.stack));
e.trimToSize();
return e;
}
@Override
public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) {
Element e = new Element("SameLocals1StackItemFrameExtended");
e.setAttr("tag", "" + s.frame_type);
e.addAll(getVerificationTypeInfo("Stack", s.stack));
e.trimToSize();
return e;
}
@Override
public Element visit_chop_frame(chop_frame c, Void p) {
Element e = new Element("Chop" + (251 - c.frame_type));
e.setAttr("tag", "" + c.frame_type);
e.setAttr("offset", "" + c.offset_delta);
return e;
}
@Override
public Element visit_same_frame_extended(same_frame_extended s, Void p) {
Element e = new Element("SameFrameExtended");
e.setAttr("tag", "" + s.frame_type);
e.setAttr("offset", "" + s.offset_delta);
return e;
}
@Override
public Element visit_append_frame(append_frame a, Void p) {
Element e = new Element("AppendFrame" + (a.frame_type - 251));
e.setAttr("tag", "" + a.frame_type);
e.addAll(getVerificationTypeInfo("Local", a.locals));
e.trimToSize();
return e;
}
@Override
public Element visit_full_frame(full_frame fl_frm, Void p) {
Element e = new Element("FullFrame");
e.setAttr("tag", "" + fl_frm.frame_type);
e.addAll(getVerificationTypeInfo("Local", fl_frm.locals));
e.trimToSize();
return e;
}
private Element getVerificationTypeInfo(String kind,
StackMapTable_attribute.verification_type_info velems[]) {
Element container = new Element(velems.length);
for (StackMapTable_attribute.verification_type_info v : velems) {
Element ve = null;
int offset = 0;
int index = 0;
switch (v.tag) {
case StackMapTable_attribute.verification_type_info.ITEM_Top:
ve = new Element("ITEM_Top");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Integer:
ve = new Element("ITEM_Integer");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Float:
ve = new Element("ITEM_Float");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Long:
ve = new Element("ITEM_Long");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Double:
ve = new Element("ITEM_Double");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Null:
ve = new Element("ITEM_Null");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
ve = new Element("ITEM_Uninitialized");
offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
ve.setAttr("offset", "" + offset);
break;
case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
ve = new Element("ITEM_UnitializedtThis");
break;
case StackMapTable_attribute.verification_type_info.ITEM_Object:
ve = new Element("ITEM_Object");
index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
ve.setAttr("class", x.getCpString(index));
break;
default:
ve = new Element("Unknown");
}
Element kindE = new Element(kind);
kindE.setAttr("tag", "" + v.tag);
container.add(kindE);
kindE.add(ve);
}
container.trimToSize();
return container;
}
}
class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {
final ClassReader x;
final ClassFile cf;
public InstructionVisitor(ClassReader x, ClassFile cf) {
this.x = x;
this.cf = cf;
}
public Element visit(Instruction i) {
Element ie = i.accept(this, null);
ie.trimToSize();
return ie;
}
@Override
public Element visitNoOperands(Instruction i, Void p) {
Opcode o = i.getOpcode();
Element e = new Element(i.getMnemonic());
if (o.opcode > 0xab && o.opcode <= 0xb1) {
e.setAttr("pc", "" + i.getPC());
}
return e;
}
@Override
public Element visitArrayType(Instruction i, TypeKind tk, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("num", "" + tk.value);
ie.setAttr("val", tk.name);
return ie;
}
@Override
public Element visitBranch(Instruction i, int i1, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("lab", "" + (i.getPC() + i1));
return ie;
}
@Override
public Element visitConstantPoolRef(Instruction i, int i1, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("ref", x.getCpString(i1));
return ie;
}
@Override
public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
// workaround for a potential bug in classfile
Element ie = new Element(i.getMnemonic());
if (i.getOpcode().equals(Opcode.IINC_W)) {
ie.setAttr("loc", "" + i1);
ie.setAttr("num", "" + i2);
} else {
ie.setAttr("ref", x.getCpString(i1));
ie.setAttr("val", "" + i2);
}
return ie;
}
@Override
public Element visitLocal(Instruction i, int i1, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("loc", "" + i1);
return ie;
}
@Override
public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("loc", "" + i1);
ie.setAttr("num", "" + i2);
return ie;
}
@Override
public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints,
int[] ints1, Void p) {
Element ie = new Element(i.getMnemonic());
int pc = i.getPC();
ie.setAttr("lab", "" + (pc + i1));
for (int k = 0 ; k < i2 ; k++) {
Element c = new Element("Case");
c.setAttr("num", "" + (ints[k]));
c.setAttr("lab", "" + (pc + ints1[k]));
c.trimToSize();
ie.add(c);
}
return ie;
}
@Override
public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
int[] ints, Void p) {
Element ie = new Element(i.getMnemonic());
int pc = i.getPC();
ie.setAttr("lab", "" + (pc + i1));
for (int k : ints) {
Element c = new Element("Case");
c.setAttr("num", "" + (k + i2));
c.setAttr("lab", "" + (pc + k));
c.trimToSize();
ie.add(c);
}
return ie;
}
@Override
public Element visitValue(Instruction i, int i1, Void p) {
Element ie = new Element(i.getMnemonic());
ie.setAttr("num", "" + i1);
return ie;
}
@Override
public Element visitUnknown(Instruction i, Void p) {
Element e = new Element(i.getMnemonic());
e.setAttr("pc", "" + i.getPC());
e.setAttr("opcode", "" + i.getOpcode().opcode);
return e;
}
}
class AnnotationsElementVisitor implements Annotation.element_value.Visitor<Element, Element> {
final ClassReader x;
final ClassFile cf;
public AnnotationsElementVisitor(ClassReader x, ClassFile cf) {
this.x = x;
this.cf = cf;
}
public Element visit(Annotation.element_value v, Element p) {
return v.accept(this, p);
}
@Override
public Element visitPrimitive(Primitive_element_value e, Element p) {
Element el = new Element("String");
el.setAttr("val", x.getCpString(e.const_value_index));
el.trimToSize();
return el;
}
@Override
public Element visitEnum(Enum_element_value e, Element p) {
Element el = new Element("Enum");
el.setAttr("name", x.getCpString(e.const_name_index));
el.setAttr("type", x.getCpString(e.type_name_index));
el.trimToSize();
return el;
}
@Override
public Element visitClass(Class_element_value c, Element p) {
Element el = new Element("Class");
el.setAttr("name", x.getCpString(c.class_info_index));
el.trimToSize();
return el;
}
@Override
public Element visitAnnotation(Annotation_element_value a, Element p) {
Element el = new Element("Annotation");
Annotation anno = a.annotation_value;
for (Annotation.element_value_pair evp : anno.element_value_pairs) {
Element child = visit(evp.value, el);
if (child != null) {
el.add(child);
}
}
el.trimToSize();
return el;
}
@Override
public Element visitArray(Array_element_value a, Element p) {
Element el = new Element("Array");
for (Annotation.element_value v : a.values) {
Element child = visit(v, el);
if (child != null) {
el.add(child);
}
}
el.trimToSize();
return el;
}
}