1 /*
2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
26 
27 import com.sun.tools.classfile.AccessFlags;
28 import com.sun.tools.classfile.Annotation;
29 import com.sun.tools.classfile.Annotation.*;
30 import com.sun.tools.classfile.AnnotationDefault_attribute;
31 import com.sun.tools.classfile.Attribute;
32 import com.sun.tools.classfile.Attributes;
33 import com.sun.tools.classfile.BootstrapMethods_attribute;
34 import com.sun.tools.classfile.CharacterRangeTable_attribute;
35 import com.sun.tools.classfile.ClassFile;
36 import com.sun.tools.classfile.Code_attribute;
37 import com.sun.tools.classfile.CompilationID_attribute;
38 import com.sun.tools.classfile.ConstantPool;
39 import com.sun.tools.classfile.ConstantPool.*;
40 import com.sun.tools.classfile.ConstantPoolException;
41 import com.sun.tools.classfile.ConstantValue_attribute;
42 import com.sun.tools.classfile.DefaultAttribute;
43 import com.sun.tools.classfile.Deprecated_attribute;
44 import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
45 import com.sun.tools.classfile.EnclosingMethod_attribute;
46 import com.sun.tools.classfile.Exceptions_attribute;
47 import com.sun.tools.classfile.Field;
48 import com.sun.tools.classfile.InnerClasses_attribute;
49 import com.sun.tools.classfile.InnerClasses_attribute.Info;
50 import com.sun.tools.classfile.Instruction;
51 import com.sun.tools.classfile.Instruction.TypeKind;
52 import com.sun.tools.classfile.LineNumberTable_attribute;
53 import com.sun.tools.classfile.LocalVariableTable_attribute;
54 import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
55 import com.sun.tools.classfile.Method;
56 import com.sun.tools.classfile.MethodParameters_attribute;
57 import com.sun.tools.classfile.Opcode;
58 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
59 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
60 import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
61 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
62 import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
63 import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
64 import com.sun.tools.classfile.Signature_attribute;
65 import com.sun.tools.classfile.SourceDebugExtension_attribute;
66 import com.sun.tools.classfile.SourceFile_attribute;
67 import com.sun.tools.classfile.SourceID_attribute;
68 import com.sun.tools.classfile.StackMapTable_attribute;
69 import com.sun.tools.classfile.StackMapTable_attribute.*;
70 import com.sun.tools.classfile.StackMap_attribute;
71 import com.sun.tools.classfile.Synthetic_attribute;
72 import com.sun.tools.classfile.TypeAnnotation;
73 import com.sun.tools.classfile.TypeAnnotation.Position;
74 import static com.sun.tools.classfile.TypeAnnotation.TargetType.THROWS;
75 import java.util.*;
76 import java.io.*;
77 import java.util.jar.JarEntry;
78 import java.util.jar.JarFile;
79 import xmlkit.XMLKit.Element;
80 
81 /*
82  * @author jrose, ksrini
83  */
84 public class ClassReader {
85 
86     private static final CommandLineParser CLP = new CommandLineParser(""
87             + "-source:     +> = \n"
88             + "-dest:       +> = \n"
89             + "-encoding:   +> = \n"
90             + "-jcov           $ \n   -nojcov         !-jcov        \n"
91             + "-verbose        $ \n   -noverbose      !-verbose     \n"
92             + "-keepPath       $ \n   -nokeepPath     !-keepPath    \n"
93             + "-keepCP         $ \n   -nokeepCP       !-keepCP      \n"
94             + "-keepOrder      $ \n   -nokeepOrder    !-keepOrder   \n"
95             + "-continue       $ \n   -nocontinue     !-continue    \n"
96             + "-@         >-@  . \n"
97             + "-              +? \n"
98             + "\n");
99 
100 
101     // Protected state for representing the class file.
102     protected Element cfile;          // <ClassFile ...>
103     protected Element cpool;          // <ConstantPool ...>
104     protected Element klass;          // <Class ...>
105     protected List<String> thePool;    // stringified flattened Constant Pool
106 
main(String[] ava)107     public static void main(String[] ava) throws IOException {
108         ArrayList<String> av = new ArrayList<>(Arrays.asList(ava));
109         HashMap<String, String> props = new HashMap<>();
110         props.put("-encoding:", "UTF8");  // default
111         props.put("-keepOrder", null);    // CLI default
112         props.put("-pretty", "1");     // CLI default
113         props.put("-continue", "1");     // CLI default
114         CLP.parse(av, props);
115         //System.out.println(props+" ++ "+av);
116         File source = asFile(props.get("-source:"));
117         File dest = asFile(props.get("-dest:"));
118         String encoding = props.get("-encoding:");
119         boolean contError = props.containsKey("-continue");
120         ClassReader options = new ClassReader();
121         options.copyOptionsFrom(props);
122         /*
123         if (dest == null && av.size() > 1) {
124         dest = File.createTempFile("TestOut", ".dir", new File("."));
125         dest.delete();
126         if (!dest.mkdir())
127         throw new RuntimeException("Cannot create "+dest);
128         System.out.println("Writing results to "+dest);
129         }
130          */
131         if (av.isEmpty()) {
132             av.add("");  //to enter this loop
133         }
134         boolean readList = false;
135         for (String a : av) {
136             if (readList) {
137                 readList = false;
138                 InputStream fin;
139                 if (a.equals("-")) {
140                     fin = System.in;
141                 } else {
142                     fin = new FileInputStream(a);
143                 }
144 
145                 BufferedReader files = makeReader(fin, encoding);
146                 for (String file; (file = files.readLine()) != null;) {
147                     doFile(file, source, dest, options, encoding, contError);
148                 }
149                 if (fin != System.in) {
150                     fin.close();
151                 }
152             } else if (a.equals("-@")) {
153                 readList = true;
154             } else if (a.startsWith("-")) {
155                 throw new RuntimeException("Bad flag argument: " + a);
156             } else if (source.getName().endsWith(".jar")) {
157                 doJar(a, source, dest, options, encoding, contError);
158             } else {
159                 doFile(a, source, dest, options, encoding, contError);
160             }
161         }
162     }
163 
asFile(String str)164     private static File asFile(String str) {
165         return (str == null) ? null : new File(str);
166     }
167 
doFile(String a, File source, File dest, ClassReader options, String encoding, boolean contError)168     private static void doFile(String a,
169             File source, File dest,
170             ClassReader options, String encoding,
171             boolean contError) throws IOException  {
172         if (!contError) {
173             doFile(a, source, dest, options, encoding);
174         } else {
175             try {
176                 doFile(a, source, dest, options, encoding);
177             } catch (Exception ee) {
178                 System.out.println("Error processing " + source + ": " + ee);
179                 ee.printStackTrace();
180             }
181         }
182     }
183 
doJar(String a, File source, File dest, ClassReader options, String encoding, Boolean contError)184     private static void doJar(String a, File source, File dest,
185                               ClassReader options, String encoding,
186                               Boolean contError) throws IOException {
187         try {
188             JarFile jf = new JarFile(source);
189             for (JarEntry je : Collections.list(jf.entries())) {
190                 String name = je.getName();
191                 if (!name.endsWith(".class")) {
192                     continue;
193                 }
194                 try {
195                     doStream(name, jf.getInputStream(je), dest, options, encoding);
196                 } catch (Exception e) {
197                     if (contError) {
198                         System.out.println("Error processing " + source + ": " + e);
199                         e.printStackTrace();
200                         continue;
201                     }
202                 }
203             }
204         } catch (IOException ioe) {
205             throw ioe;
206         }
207     }
208 
doStream(String a, InputStream in, File dest, ClassReader options, String encoding)209     private static void doStream(String a, InputStream in, File dest,
210                                  ClassReader options, String encoding) throws IOException {
211 
212         File f = new File(a);
213         ClassReader cr = new ClassReader(options);
214         Element e;
215         if (options.verbose) {
216             System.out.println("Reading " + f);
217         }
218         e = cr.readFrom(in);
219 
220         OutputStream out;
221         if (dest == null) {
222             out = System.out;
223         } else {
224             File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
225             String outName = outf.getName();
226             File outSubdir = outf.getParentFile();
227             outSubdir.mkdirs();
228             int extPos = outName.lastIndexOf('.');
229             if (extPos > 0) {
230                 outf = new File(outSubdir, outName.substring(0, extPos) + ".xml");
231             }
232             out = new FileOutputStream(outf);
233         }
234 
235         Writer outw = makeWriter(out, encoding);
236         if (options.pretty || !options.keepOrder) {
237             e.writePrettyTo(outw);
238         } else {
239             e.writeTo(outw);
240         }
241         if (out == System.out) {
242             outw.write("\n");
243             outw.flush();
244         } else {
245             outw.close();
246         }
247     }
248 
doFile(String a, File source, File dest, ClassReader options, String encoding)249     private static void doFile(String a,
250             File source, File dest,
251             ClassReader options, String encoding) throws IOException {
252         File inf = new File(source, a);
253         if (dest != null && options.verbose) {
254             System.out.println("Reading " + inf);
255         }
256 
257         BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf));
258 
259         doStream(a, in, dest, options, encoding);
260 
261     }
262 
makeReader(InputStream in, String encoding)263     public static BufferedReader makeReader(InputStream in,
264                                             String encoding) throws IOException {
265         Reader inw;
266         in = new BufferedInputStream(in);  // add buffering
267         if (encoding == null) {
268             inw = new InputStreamReader(in);
269         } else {
270             inw = new InputStreamReader(in, encoding);
271         }
272         return new BufferedReader(inw);  // add buffering
273     }
274 
makeWriter(OutputStream out, String encoding)275     public static Writer makeWriter(OutputStream out,
276                                     String encoding) throws IOException {
277         Writer outw;
278         if (encoding == null) {
279             outw = new OutputStreamWriter(out);
280         } else {
281             outw = new OutputStreamWriter(out, encoding);
282         }
283         return new BufferedWriter(outw);  // add buffering
284     }
285 
result()286     public Element result() {
287         return cfile;
288     }
289 
290     protected InputStream in;
291     protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
292     // input options
293     public boolean pretty = false;
294     public boolean verbose = false;
295     public boolean keepPath = false;
296     public boolean keepCP = false;
297     public boolean keepBytes = false;
298     public boolean parseBytes = true;
299     public boolean resolveRefs = true;
300     public boolean keepOrder = true;
301     public boolean keepSizes = false;
302 
ClassReader()303     public ClassReader() {
304         cfile = new Element("ClassFile");
305     }
306 
ClassReader(ClassReader options)307     public ClassReader(ClassReader options) {
308         this();
309         copyOptionsFrom(options);
310     }
311 
copyOptionsFrom(ClassReader options)312     public void copyOptionsFrom(ClassReader options) {
313         pretty = options.pretty;
314         verbose = options.verbose;
315         keepPath = options.keepPath;
316         keepCP = options.keepCP;
317         keepOrder = options.keepOrder;
318     }
319 
copyOptionsFrom(Map<String, String> options)320     public void copyOptionsFrom(Map<String, String> options) {
321         if (options.containsKey("-pretty")) {
322             pretty = (options.get("-pretty") != null);
323         }
324         if (options.containsKey("-verbose")) {
325             verbose = (options.get("-verbose") != null);
326         }
327         if (options.containsKey("-keepPath")) {
328             keepPath = (options.get("-keepPath") != null);
329         }
330         if (options.containsKey("-keepCP")) {
331             keepCP = (options.get("-keepCP") != null);
332         }
333         if (options.containsKey("-keepOrder")) {
334             keepOrder = (options.get("-keepOrder") != null);
335         }
336     }
337 
getCpString(int i)338     protected String getCpString(int i) {
339         return thePool.get(i);
340     }
341 
readFrom(InputStream in)342     public Element readFrom(InputStream in) throws IOException {
343         try {
344             this.in = in;
345             ClassFile c = ClassFile.read(in);
346             // read the file header
347             if (c.magic != 0xCAFEBABE) {
348                 throw new RuntimeException("bad magic number " +
349                         Integer.toHexString(c.magic));
350             }
351             cfile.setAttr("magic", "" + c.magic);
352             int minver = c.minor_version;
353             int majver = c.major_version;
354             cfile.setAttr("minver", "" + minver);
355             cfile.setAttr("majver", "" + majver);
356             readCP(c);
357             readClass(c);
358             return result();
359         } catch (InvalidDescriptor | ConstantPoolException ex) {
360             throw new IOException("Fatal error", ex);
361         }
362     }
363 
readFrom(File file)364     public Element readFrom(File file) throws IOException {
365         try (InputStream strm = new FileInputStream(file)) {
366             Element e = readFrom(new BufferedInputStream(strm));
367             if (keepPath) {
368                 e.setAttr("path", file.toString());
369             }
370             return e;
371         }
372     }
373 
readClass(ClassFile c)374     private void readClass(ClassFile c) throws IOException,
375                                                ConstantPoolException,
376                                                InvalidDescriptor {
377         klass = new Element("Class");
378         cfile.add(klass);
379         String thisk = c.getName();
380 
381         klass.setAttr("name", thisk);
382 
383         AccessFlags af = new AccessFlags(c.access_flags.flags);
384         klass.setAttr("flags", flagString(af, klass));
385         if (!"java/lang/Object".equals(thisk)) {
386             klass.setAttr("super", c.getSuperclassName());
387         }
388         for (int i : c.interfaces) {
389             klass.add(new Element("Interface", "name", getCpString(i)));
390         }
391         readFields(c, klass);
392         readMethods(c, klass);
393         readAttributesFor(c, c.attributes, klass);
394         klass.trimToSize();
395     }
396 
readFields(ClassFile c, Element klass)397     private void readFields(ClassFile c, Element klass) throws IOException {
398         int len = c.fields.length;
399         Element fields = new Element(len);
400         for (Field f : c.fields) {
401             Element field = new Element("Field");
402             field.setAttr("name", getCpString(f.name_index));
403             field.setAttr("type", getCpString(f.descriptor.index));
404             field.setAttr("flags", flagString(f.access_flags.flags, field));
405             readAttributesFor(c, f.attributes, field);
406 
407             field.trimToSize();
408             fields.add(field);
409         }
410         if (!keepOrder) {
411             fields.sort();
412         }
413         klass.addAll(fields);
414     }
415 
416 
readMethods(ClassFile c, Element klass)417     private void readMethods(ClassFile c, Element klass) throws IOException {
418         int len = c.methods.length;
419         Element methods = new Element(len);
420         for (Method m : c.methods) {
421             Element member = new Element("Method");
422             member.setAttr("name", getCpString(m.name_index));
423             member.setAttr("type", getCpString(m.descriptor.index));
424             member.setAttr("flags", flagString(m.access_flags.flags, member));
425             readAttributesFor(c, m.attributes, member);
426 
427             member.trimToSize();
428             methods.add(member);
429         }
430         if (!keepOrder) {
431             methods.sort();
432         }
433         klass.addAll(methods);
434     }
435 
getKind(Element e)436     private AccessFlags.Kind getKind(Element e) {
437         switch(e.getName()) {
438             case "Class":
439                 return AccessFlags.Kind.Class;
440             case "InnerClass":
441                 return AccessFlags.Kind.InnerClass;
442             case "Field":
443                 return AccessFlags.Kind.Field ;
444             case "Method":
445                 return AccessFlags.Kind.Method;
446             default: throw new RuntimeException("should not reach here");
447         }
448     }
449 
flagString(int flags, Element holder)450     protected String flagString(int flags, Element holder) {
451         return flagString(new AccessFlags(flags), holder);
452     }
flagString(AccessFlags af, Element holder)453     protected String flagString(AccessFlags af, Element holder) {
454         return flagString(af, holder.getName());
455     }
flagString(int flags, String kind)456     protected String flagString(int flags, String kind) {
457         return flagString(new AccessFlags(flags), kind);
458     }
flagString(AccessFlags af, String kind)459     protected String flagString(AccessFlags af, String kind) {
460         Set<String> mods = null;
461         switch (kind) {
462             case "Class":
463                 mods = af.getClassFlags();
464                 break;
465             case "InnerClass":
466                 mods = af.getInnerClassFlags();
467                 break;
468             case "Field":
469                 mods = af.getFieldFlags();
470                 break;
471             case "Method":
472                 mods = af.getMethodFlags();
473                 break;
474             default:
475                 throw new RuntimeException("should not reach here");
476         }
477         StringBuilder sb = new StringBuilder();
478         for (String x : mods) {
479             sb.append(x.substring(x.indexOf('_') + 1).toLowerCase()).append(" ");
480         }
481         return sb.toString().trim();
482     }
483 
484 
readAttributesFor(ClassFile c, Attributes attrs, Element x)485     protected  void readAttributesFor(ClassFile c, Attributes attrs, Element x) {
486         Element container = new Element();
487         AttributeVisitor av = new AttributeVisitor(this, c);
488         for (Attribute a : attrs) {
489             av.visit(a, container);
490         }
491         if (!keepOrder) {
492             container.sort();
493         }
494         x.addAll(container);
495     }
496 
497     private int fileSize = 0;
498     private HashMap<String, int[]> attrSizes = new HashMap<>();
499 
attachTo(Element x, Object aval0)500     private void attachTo(Element x, Object aval0) {
501         if (aval0 == null) {
502             return;
503         }
504         if (!(aval0 instanceof Element)) {
505             x.add(aval0);
506             return;
507         }
508         Element aval = (Element) aval0;
509         if (!aval.isAnonymous()) {
510             x.add(aval);
511             return;
512         }
513         for (int imax = aval.attrSize(), i = 0; i < imax; i++) {
514             //%%
515             attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i));
516         }
517         x.addAll(aval);
518     }
519 
attachAttrTo(Element x, String aname, String aval)520     private void attachAttrTo(Element x, String aname, String aval) {
521         String aval0 = x.getAttr(aname);
522         if (aval0 != null) {
523             aval = aval0 + " " + aval;
524         }
525         x.setAttr(aname, aval);
526     }
527 
readCP(ClassFile c)528     private void readCP(ClassFile c) throws IOException {
529         cpool = new Element("ConstantPool", c.constant_pool.size());
530         ConstantPoolVisitor cpv = new ConstantPoolVisitor(cpool, c,
531                 c.constant_pool.size());
532         for (int i = 1 ; i < c.constant_pool.size() ; i++) {
533             try {
534                 cpv.visit(c.constant_pool.get(i), i);
535             } catch (InvalidIndex ex) {
536                 // can happen periodically when accessing doubles etc. ignore it
537                 // ex.printStackTrace();
538             }
539         }
540         thePool = cpv.getPoolList();
541         if (verbose) {
542             for (int i = 0; i < thePool.size(); i++) {
543                 System.out.println("[" + i + "]: " + thePool.get(i));
544             }
545         }
546         if (keepCP) {
547             cfile.add(cpool);
548         }
549     }
550 }
551 
552 class ConstantPoolVisitor implements ConstantPool.Visitor<String, Integer> {
553     final List<String> slist;
554     final Element xpool;
555     final ClassFile cf;
556     final ConstantPool cfpool;
557     final List<String> bsmlist;
558 
559 
ConstantPoolVisitor(Element xpool, ClassFile cf, int size)560     public ConstantPoolVisitor(Element xpool, ClassFile cf, int size) {
561         slist = new ArrayList<>(size);
562         for (int i = 0 ; i < size; i++) {
563             slist.add(null);
564         }
565         this.xpool = xpool;
566         this.cf = cf;
567         this.cfpool = cf.constant_pool;
568         bsmlist = readBSM();
569     }
570 
getPoolList()571     public List<String> getPoolList() {
572         return Collections.unmodifiableList(slist);
573     }
574 
getBSMList()575     public List<String> getBSMList() {
576         return Collections.unmodifiableList(bsmlist);
577     }
578 
visit(CPInfo c, int index)579     public String visit(CPInfo c, int index) {
580         return c.accept(this, index);
581     }
582 
readBSM()583     private List<String> readBSM() {
584         BootstrapMethods_attribute bsmAttr =
585                 (BootstrapMethods_attribute) cf.getAttribute(Attribute.BootstrapMethods);
586         if (bsmAttr != null) {
587             List<String> out =
588                     new ArrayList<>(bsmAttr.bootstrap_method_specifiers.length);
589             for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsms :
590                     bsmAttr.bootstrap_method_specifiers) {
591                 int index = bsms.bootstrap_method_ref;
592                 try {
593                     String value = slist.get(index);
594                     String bsmStr = value;
595                     if (value == null) {
596                         value = visit(cfpool.get(index), index);
597                         slist.set(index, value);
598                     }
599                     bsmStr = value;
600                     for (int idx : bsms.bootstrap_arguments) {
601                         value = slist.get(idx);
602                         if (value == null) {
603                             value = visit(cfpool.get(idx), idx);
604                             slist.set(idx, value);
605                         }
606                         bsmStr = bsmStr.concat("," + value);
607                     }
608                     out.add(bsmStr);
609                 } catch (InvalidIndex ex) {
610                     ex.printStackTrace();
611                 }
612             }
613             return out;
614         }
615         return new ArrayList<>(0);
616     }
617 
618     @Override
visitClass(CONSTANT_Class_info c, Integer p)619     public String visitClass(CONSTANT_Class_info c, Integer p) {
620         String value = slist.get(p);
621         if (value == null) {
622             try {
623                 value = visit(cfpool.get(c.name_index), c.name_index);
624                 slist.set(p, value);
625                 xpool.add(new Element("CONSTANT_Class",
626                         new String[]{"id", p.toString()},
627                         value));
628             } catch (ConstantPoolException ex) {
629                 ex.printStackTrace();
630             }
631         }
632         return value;
633     }
634 
635     @Override
visitDouble(CONSTANT_Double_info c, Integer p)636     public String visitDouble(CONSTANT_Double_info c, Integer p) {
637         String value = slist.get(p);
638         if (value == null) {
639             value = Double.toString(c.value);
640             slist.set(p, value);
641             xpool.add(new Element("CONSTANT_Double",
642                       new String[]{"id", p.toString()},
643                       value));
644         }
645         return value;
646     }
647 
648     @Override
visitFieldref(CONSTANT_Fieldref_info c, Integer p)649     public String visitFieldref(CONSTANT_Fieldref_info c, Integer p) {
650     String value = slist.get(p);
651         if (value == null) {
652             try {
653                 value = visit(cfpool.get(c.class_index), c.class_index);
654                 value = value.concat(" " + visit(cfpool.get(c.name_and_type_index),
655                                      c.name_and_type_index));
656                 slist.set(p, value);
657                 xpool.add(new Element("CONSTANT_Fieldref",
658                           new String[]{"id", p.toString()},
659                           value));
660             } catch (ConstantPoolException ex) {
661                 ex.printStackTrace();
662             }
663         }
664         return value;
665     }
666 
667     @Override
visitFloat(CONSTANT_Float_info c, Integer p)668     public String visitFloat(CONSTANT_Float_info c, Integer p) {
669         String value = slist.get(p);
670         if (value == null) {
671             value = Float.toString(c.value);
672             slist.set(p, value);
673             xpool.add(new Element("CONSTANT_Float",
674                       new String[]{"id", p.toString()},
675                       value));
676         }
677         return value;
678     }
679 
680     @Override
visitInteger(CONSTANT_Integer_info cnstnt, Integer p)681     public String visitInteger(CONSTANT_Integer_info cnstnt, Integer p) {
682         String value = slist.get(p);
683         if (value == null) {
684             value = Integer.toString(cnstnt.value);
685             slist.set(p, value);
686             xpool.add(new Element("CONSTANT_Integer",
687                       new String[]{"id", p.toString()},
688                       value));
689         }
690         return value;
691     }
692 
693     @Override
visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c, Integer p)694     public String visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info c,
695                                           Integer p) {
696         String value = slist.get(p);
697         if (value == null) {
698             try {
699                 value = visit(cfpool.get(c.class_index), c.class_index);
700                 value = value.concat(" " +
701                                      visit(cfpool.get(c.name_and_type_index),
702                                      c.name_and_type_index));
703                 slist.set(p, value);
704                 xpool.add(new Element("CONSTANT_InterfaceMethodref",
705                           new String[]{"id", p.toString()},
706                           value));
707 
708             } catch (ConstantPoolException ex) {
709                 ex.printStackTrace();
710             }
711         }
712         return value;
713     }
714 
715     @Override
visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p)716     public String visitInvokeDynamic(CONSTANT_InvokeDynamic_info c, Integer p) {
717         String value = slist.get(p);
718         if (value == null) {
719             try {
720                 value = bsmlist.get(c.bootstrap_method_attr_index) + " "
721                         + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
722                 slist.set(p, value);
723                 xpool.add(new Element("CONSTANT_InvokeDynamic",
724                           new String[]{"id", p.toString()},
725                           value));
726 
727             } catch (ConstantPoolException ex) {
728                 ex.printStackTrace();
729             }
730         }
731         return value;
732     }
733 
734     @Override
visitLong(CONSTANT_Long_info c, Integer p)735     public String visitLong(CONSTANT_Long_info c, Integer p) {
736         String value = slist.get(p);
737         if (value == null) {
738             value = Long.toString(c.value);
739             slist.set(p, value);
740             xpool.add(new Element("CONSTANT_Long",
741                       new String[]{"id", p.toString()},
742                       value));
743         }
744         return value;
745     }
746 
747     @Override
visitNameAndType(CONSTANT_NameAndType_info c, Integer p)748     public String visitNameAndType(CONSTANT_NameAndType_info c, Integer p) {
749         String value = slist.get(p);
750         if (value == null) {
751             try {
752                 value = visit(cfpool.get(c.name_index), c.name_index);
753                 value = value.concat(" " +
754                         visit(cfpool.get(c.type_index), c.type_index));
755                 slist.set(p, value);
756                 xpool.add(new Element("CONSTANT_NameAndType",
757                           new String[]{"id", p.toString()},
758                           value));
759             } catch (InvalidIndex ex) {
760                 ex.printStackTrace();
761             }
762         }
763         return value;
764     }
765 
766     @Override
visitMethodref(CONSTANT_Methodref_info c, Integer p)767     public String visitMethodref(CONSTANT_Methodref_info c, Integer p) {
768         String value = slist.get(p);
769         if (value == null) {
770             try {
771                 value = visit(cfpool.get(c.class_index), c.class_index);
772                 value = value.concat(" " +
773                                      visit(cfpool.get(c.name_and_type_index),
774                                      c.name_and_type_index));
775                 slist.set(p, value);
776                 xpool.add(new Element("CONSTANT_Methodref",
777                           new String[]{"id", p.toString()},
778                           value));
779 
780             } catch (ConstantPoolException ex) {
781                 ex.printStackTrace();
782             }
783         }
784         return value;
785     }
786 
787     @Override
visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p)788     public String visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p) {
789     String value = slist.get(p);
790         if (value == null) {
791             try {
792                 value = c.reference_kind.name();
793                 value = value.concat(" "
794                         + visit(cfpool.get(c.reference_index), c.reference_index));
795                 slist.set(p, value);
796                 xpool.add(new Element("CONSTANT_MethodHandle",
797                           new String[]{"id", p.toString()},
798                           value));
799 
800             } catch (ConstantPoolException ex) {
801                 ex.printStackTrace();
802             }
803         }
804         return value;
805     }
806 
807     @Override
visitMethodType(CONSTANT_MethodType_info c, Integer p)808     public String visitMethodType(CONSTANT_MethodType_info c, Integer p) {
809         String value = slist.get(p);
810         if (value == null) {
811             try {
812                 value = visit(cfpool.get(c.descriptor_index), c.descriptor_index);
813                 slist.set(p, value);
814                 xpool.add(new Element("CONSTANT_MethodType",
815                           new String[]{"id", p.toString()},
816                           value));
817             } catch (ConstantPoolException ex) {
818                 ex.printStackTrace();
819             }
820         }
821         return value;
822     }
823 
824     @Override
visitString(CONSTANT_String_info c, Integer p)825     public String visitString(CONSTANT_String_info c, Integer p) {
826         try {
827 
828             String value = slist.get(p);
829             if (value == null) {
830                 value = c.getString();
831                 slist.set(p, value);
832                 xpool.add(new Element("CONSTANT_String",
833                           new String[]{"id", p.toString()},
834                           value));
835             }
836             return value;
837         } catch (ConstantPoolException ex) {
838             throw new RuntimeException("Fatal error", ex);
839         }
840     }
841 
842     @Override
visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p)843     public String  visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) {
844         String value = slist.get(p);
845         if (value == null) {
846             value = cnstnt.value;
847             slist.set(p, value);
848             xpool.add(new Element("CONSTANT_Utf8",
849                       new String[]{"id", p.toString()},
850                       value));
851         }
852         return value;
853 
854     }
855 }
856 
857 
858 class AttributeVisitor implements Attribute.Visitor<Element, Element> {
859     final ClassFile cf;
860     final ClassReader x;
861     final AnnotationsElementVisitor aev;
862     final InstructionVisitor iv;
863 
AttributeVisitor(ClassReader x, ClassFile cf)864     public AttributeVisitor(ClassReader x, ClassFile cf) {
865         this.x = x;
866         this.cf = cf;
867         iv =  new InstructionVisitor(x, cf);
868         aev = new AnnotationsElementVisitor(x, cf);
869     }
870 
visit(Attribute a, Element parent)871     public void visit(Attribute a, Element parent) {
872         a.accept(this, parent);
873     }
874 
875     @Override
visitBootstrapMethods(BootstrapMethods_attribute bm, Element p)876     public Element visitBootstrapMethods(BootstrapMethods_attribute bm, Element p) {
877         Element e = new Element(x.getCpString(bm.attribute_name_index));
878         for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : bm.bootstrap_method_specifiers) {
879             Element be = new Element("BootstrapMethodSpecifier");
880             be.setAttr("ref", x.getCpString(bsm.bootstrap_method_ref));
881             if (bsm.bootstrap_arguments.length > 0) {
882                 Element bme = new Element("MethodArguments");
883                 for (int index : bsm.bootstrap_arguments) {
884                     bme.add(x.getCpString(index));
885                 }
886                 bme.trimToSize();
887                 be.add(bme);
888             }
889             be.trimToSize();
890             e.add(be);
891         }
892         e.trimToSize();
893         if (!x.keepOrder) {
894             e.sort();
895         }
896         p.add(e);
897         return null;
898     }
899 
900     @Override
visitDefault(DefaultAttribute da, Element p)901     public Element visitDefault(DefaultAttribute da, Element p) {
902         Element e = new Element(x.getCpString(da.attribute_name_index));
903         StringBuilder sb = new StringBuilder();
904         for (byte x : da.info) {
905             sb.append("0x").append(Integer.toHexString(x)).append(" ");
906         }
907         e.setAttr("bytes", sb.toString().trim());
908         e.trimToSize();
909         p.add(e);
910         return null;
911     }
912 
913     @Override
visitAnnotationDefault(AnnotationDefault_attribute ad, Element p)914     public Element visitAnnotationDefault(AnnotationDefault_attribute ad, Element p) {
915         Element e = new Element(x.getCpString(ad.attribute_name_index));
916         e.setAttr("tag", "" + ad.default_value.tag);
917         Element child = aev.visit(ad.default_value, e);
918         if (child != null) {
919             e.add(child);
920         }
921         e.trimToSize();
922         p.add(e);
923         return null;
924     }
925 
926     @Override
visitCharacterRangeTable(CharacterRangeTable_attribute crt, Element p)927     public Element visitCharacterRangeTable(CharacterRangeTable_attribute crt,
928                                             Element p) {
929         Element e = new Element(x.getCpString(crt.attribute_name_index));
930         for (CharacterRangeTable_attribute.Entry ce : crt.character_range_table) {
931             e.setAttr("start_pc", "" + ce.start_pc);
932             e.setAttr("end_pc", "" + ce.end_pc);
933             e.setAttr("range_start", "" + ce.character_range_start);
934             e.setAttr("range_end", "" + ce.character_range_end);
935             e.setAttr("flags", x.flagString(ce.flags, "Method"));
936         }
937         e.trimToSize();
938         p.add(e);
939         return null;
940     }
941 
instructions(Element code, Code_attribute c)942     private Element instructions(Element code, Code_attribute c) {
943         Element ielement = new Element("Instructions");
944         for (Instruction ins : c.getInstructions()) {
945             ielement.add(iv.visit(ins));
946         }
947         ielement.trimToSize();
948         return ielement;
949     }
950 
951     @Override
visitCode(Code_attribute c, Element p)952     public Element visitCode(Code_attribute c, Element p) {
953         Element e = null;
954 
955         e = new Element(x.getCpString(c.attribute_name_index),
956                 "stack", "" + c.max_stack,
957                 "local", "" + c.max_locals);
958 
959         e.add(instructions(e, c));
960 
961         for (Code_attribute.Exception_data edata : c.exception_table) {
962             e.add(new Element("Handler",
963                     "start", "" + edata.start_pc,
964                     "end", "" + edata.end_pc,
965                     "catch", "" + edata.handler_pc,
966                     "class", x.getCpString(edata.catch_type)));
967 
968         }
969         this.x.readAttributesFor(cf, c.attributes, e);
970         e.trimToSize();
971         p.add(e);
972         return null;
973     }
974 
975     @Override
visitCompilationID(CompilationID_attribute cid, Element p)976     public Element visitCompilationID(CompilationID_attribute cid, Element p) {
977         Element e = new Element(x.getCpString(cid.attribute_name_index),
978                 x.getCpString(cid.compilationID_index));
979         p.add(e);
980         return null;
981     }
982 
983     @Override
visitConstantValue(ConstantValue_attribute cv, Element p)984     public Element visitConstantValue(ConstantValue_attribute cv, Element p) {
985         Element e = new Element(x.getCpString(cv.attribute_name_index));
986         e.add(x.getCpString(cv.constantvalue_index));
987         p.add(e);
988         return null;
989     }
990 
991     @Override
visitDeprecated(Deprecated_attribute d, Element p)992     public Element visitDeprecated(Deprecated_attribute d, Element p) {
993         Element e = new Element(x.getCpString(d.attribute_name_index));
994         p.add(e);
995         return null;
996     }
997 
998     @Override
visitEnclosingMethod(EnclosingMethod_attribute em, Element p)999     public Element visitEnclosingMethod(EnclosingMethod_attribute em, Element p) {
1000         Element e = new Element(x.getCpString(em.attribute_name_index));
1001         e.setAttr("class", x.getCpString(em.class_index));
1002         e.setAttr("desc", x.getCpString(em.method_index));
1003         e.trimToSize();
1004         p.add(e);
1005         return null;
1006     }
1007 
1008     @Override
visitExceptions(Exceptions_attribute e, Element p)1009     public Element visitExceptions(Exceptions_attribute e, Element p) {
1010         Element ee = new Element(x.getCpString(e.attribute_name_index));
1011         for (int idx : e.exception_index_table) {
1012             Element n = new Element("Item");
1013             n.setAttr("class", x.getCpString(idx));
1014             ee.add(n);
1015         }
1016         ee.trimToSize();
1017         p.add(ee);
1018         return null;
1019     }
1020 
1021     @Override
visitInnerClasses(InnerClasses_attribute ic, Element p)1022     public Element visitInnerClasses(InnerClasses_attribute ic, Element p) {
1023         for (Info info : ic.classes) {
1024             Element e = new Element(x.getCpString(ic.attribute_name_index));
1025             e.setAttr("class", x.getCpString(info.inner_class_info_index));
1026             e.setAttr("outer", x.getCpString(info.outer_class_info_index));
1027             e.setAttr("name", x.getCpString(info.inner_name_index));
1028             e.setAttr("flags", x.flagString(info.inner_class_access_flags,
1029                     "InnerClass"));
1030             e.trimToSize();
1031             p.add(e);
1032         }
1033         return null;
1034     }
1035 
1036     @Override
visitLineNumberTable(LineNumberTable_attribute lnt, Element p)1037     public Element visitLineNumberTable(LineNumberTable_attribute lnt, Element p) {
1038         String name = x.getCpString(lnt.attribute_name_index);
1039         for (LineNumberTable_attribute.Entry e : lnt.line_number_table) {
1040             Element l = new Element(name);
1041             l.setAttr("bci", "" + e.start_pc);
1042             l.setAttr("line", "" + e.line_number);
1043             l.trimToSize();
1044             p.add(l);
1045         }
1046         return null; // already added to parent
1047     }
1048 
1049     @Override
visitLocalVariableTable(LocalVariableTable_attribute lvt, Element p)1050     public Element visitLocalVariableTable(LocalVariableTable_attribute lvt,
1051                                                 Element p) {
1052         String name = x.getCpString(lvt.attribute_name_index);
1053         for (LocalVariableTable_attribute.Entry e : lvt.local_variable_table) {
1054             Element l = new Element(name);
1055             l.setAttr("bci", "" + e.start_pc);
1056             l.setAttr("span", "" + e.length);
1057             l.setAttr("name", x.getCpString(e.name_index));
1058             l.setAttr("type", x.getCpString(e.descriptor_index));
1059             l.setAttr("slot", "" + e.index);
1060             l.trimToSize();
1061             p.add(l);
1062         }
1063         return null; // already added to parent
1064     }
1065 
1066     @Override
visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt, Element p)1067     public Element visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt,
1068                                                     Element p) {
1069         String name = x.getCpString(lvtt.attribute_name_index);
1070         for (LocalVariableTypeTable_attribute.Entry e : lvtt.local_variable_table) {
1071             Element l = new Element(name);
1072             l.setAttr("bci", "" + e.start_pc);
1073             l.setAttr("span", "" + e.length);
1074             l.setAttr("name", x.getCpString(e.name_index));
1075             l.setAttr("type", x.getCpString(e.signature_index));
1076             l.setAttr("slot", "" + e.index);
1077             l.trimToSize();
1078             p.add(l);
1079         }
1080         return null; // already added to parent
1081     }
1082 
1083     @Override
visitMethodParameters(MethodParameters_attribute mp, Element p)1084     public Element visitMethodParameters(MethodParameters_attribute mp, Element p) {
1085         String name = x.getCpString(mp.attribute_name_index);
1086         for (MethodParameters_attribute.Entry e : mp.method_parameter_table) {
1087             Element l = new Element(name);
1088             l.setAttr("name", x.getCpString(e.name_index));
1089             l.setAttr("flag", "" + e.flags);
1090             l.trimToSize();
1091             p.add(l);
1092         }
1093         return null; // already added to parent
1094     }
parseAnnotation(Annotation anno, Element p)1095     private void parseAnnotation(Annotation anno, Element p) {
1096         Element ea = new Element("Annotation");
1097         ea.setAttr("name", "" + x.getCpString(anno.type_index));
1098         for (Annotation.element_value_pair evp : anno.element_value_pairs) {
1099             Element evpe = new Element("Element");
1100             evpe.setAttr("tag", "" + evp.value.tag);
1101             evpe.setAttr("value", x.getCpString(evp.element_name_index));
1102             Element child = aev.visit(evp.value, evpe);
1103             if (child != null) {
1104                 evpe.add(child);
1105             }
1106             ea.add(evpe);
1107         }
1108         ea.trimToSize();
1109         p.add(ea);
1110     }
1111 
parseAnnotations(Annotation[] ra, Element p)1112     private void parseAnnotations(Annotation[] ra, Element p) {
1113         for (Annotation anno : ra) {
1114             parseAnnotation(anno, p);
1115         }
1116     }
1117 
1118     @Override
visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva, Element p)1119     public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva,
1120                                                   Element p) {
1121         Element e = new Element(x.getCpString(rva.attribute_name_index));
1122         parseAnnotations(rva.annotations, e);
1123         e.trimToSize();
1124         p.add(e);
1125         return null;
1126     }
1127 
1128     @Override
visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria, Element p)1129     public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria,
1130                                                     Element p) {
1131         Element e = new Element(x.getCpString(ria.attribute_name_index));
1132         parseAnnotations(ria.annotations, e);
1133         e.trimToSize();
1134         p.add(e);
1135         return null;
1136     }
1137 
1138     @Override
visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa, Element p)1139     public Element visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa,
1140                                                            Element p) {
1141         Element e = new Element(x.getCpString(rvpa.attribute_name_index));
1142         for (Annotation[] pa : rvpa.parameter_annotations) {
1143            parseAnnotations(pa, e);
1144         }
1145         p.add(e);
1146         return null;
1147     }
1148 
1149     @Override
visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa, Element p)1150     public Element visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa,
1151                                                              Element p) {
1152         Element e = new Element(x.getCpString(ripa.attribute_name_index));
1153         for (Annotation[] pa : ripa.parameter_annotations) {
1154             parseAnnotations(pa, e);
1155         }
1156         p.add(e);
1157         return null;
1158     }
1159 
parsePosition(Position ap, Element p)1160     private void parsePosition(Position ap, Element p) {
1161         Element te = new Element();
1162         switch (ap.type) {
1163             case CLASS_TYPE_PARAMETER: // 0x00
1164                 te.setName("CLASS_TYPE_PARAMETER");
1165                 te.setAttr("idx", "" + ap.parameter_index);
1166                 break;
1167             case METHOD_TYPE_PARAMETER: // 0x01
1168                 te.setName("METHOD_TYPE_PARAMETER");
1169                 te.setAttr("idx", "" + ap.parameter_index);
1170                 break;
1171             case CLASS_EXTENDS: // 0x10
1172                 te.setName("CLASS_EXTENDS");
1173                 te.setAttr("idx", "" + ap.type_index);
1174                 break;
1175             case CLASS_TYPE_PARAMETER_BOUND: // 0x11
1176                 te.setName("CLASS_TYPE_PARAMETER_BOUND");
1177                 te.setAttr("idx1", "" + ap.parameter_index);
1178                 te.setAttr("idx2", "" + ap.bound_index);
1179                 break;
1180             case METHOD_TYPE_PARAMETER_BOUND: // 0x12
1181                 te.setName("METHOD_TYPE_PARAMETER_BOUND");
1182                 te.setAttr("idx1", "" + ap.parameter_index);
1183                 te.setAttr("idx2", "" + ap.bound_index);
1184                 break;
1185             case FIELD: // 0x13
1186                 te.setName("FIELD");
1187                 break;
1188             case METHOD_RETURN: // 0x14
1189                 te.setName("METHOD_RETURN");
1190                 break;
1191             case METHOD_RECEIVER: // 0x15
1192                 te.setName("METHOD_RECEIVER");
1193                 break;
1194             case METHOD_FORMAL_PARAMETER: // 0x16
1195                 te.setName("METHOD_FORMAL_PARAMETER");
1196                 te.setAttr("idx", "" + ap.parameter_index);
1197                 break;
1198             case THROWS: // 0x17
1199                 te.setName("THROWS");
1200                 te.setAttr("idx", "" + ap.type_index);
1201                 break;
1202             case LOCAL_VARIABLE: // 0x40
1203                 te.setName("LOCAL_VARIABLE");
1204                 for (int i = 0; i < ap.lvarIndex.length; i++) {
1205                     te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
1206                     te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
1207                     te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
1208                 }
1209                 break;
1210             case RESOURCE_VARIABLE: // 0x41
1211                 te.setName("RESOURCE_VARIABLE");
1212                 for (int i = 0; i < ap.lvarIndex.length ; i++) {
1213                     te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
1214                     te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
1215                     te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
1216                 }
1217                 break;
1218             case EXCEPTION_PARAMETER: // 0x42
1219                 te.setName("EXCEPTION_PARAMETER");
1220                 te.setAttr("idx", "" + ap.exception_index);
1221                 break;
1222             case INSTANCEOF: // 0x43
1223                 te.setName("INSTANCE_OF");
1224                 te.setAttr("off", "" + ap.offset);
1225                 break;
1226             case NEW: // 0x44
1227                 te.setName("NEW");
1228                 te.setAttr("off", "" + ap.offset);
1229                 break;
1230             case CONSTRUCTOR_REFERENCE: // 0x45
1231                 te.setName("CONSTRUCTOR_REFERENCE_RECEIVER");
1232                 te.setAttr("off", "" + ap.offset);
1233                 break;
1234             case METHOD_REFERENCE: // 0x46
1235                 te.setName("METHOD_REFERENCE_RECEIVER");
1236                 te.setAttr("off", "" + ap.offset);
1237                 break;
1238             case CAST: // 0x47
1239                 te.setName("CAST");
1240                 te.setAttr("off", "" + ap.offset);
1241                 te.setAttr("idx", "" + ap.type_index);
1242                 break;
1243             case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: // 0x48
1244                 te.setName("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT");
1245                 te.setAttr("off", "" + ap.offset);
1246                 te.setAttr("idx", "" + ap.type_index);
1247                 break;
1248             case METHOD_INVOCATION_TYPE_ARGUMENT: // 0x49
1249                 te.setName("METHOD_INVOCATION_TYPE_ARGUMENT");
1250                 te.setAttr("off", "" + ap.offset);
1251                 te.setAttr("idx", "" + ap.type_index);
1252                 break;
1253             case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: // 0x4A
1254                 te.setName("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT");
1255                 te.setAttr("off", "" + ap.offset);
1256                 te.setAttr("idx", "" + ap.type_index);
1257                 break;
1258             case METHOD_REFERENCE_TYPE_ARGUMENT: // 0x4B
1259                 te.setName("METHOD_REFERENCE_TYPE_ARGUMENT");
1260                 te.setAttr("off", "" + ap.offset);
1261                 te.setAttr("idx", "" + ap.type_index);
1262                 break;
1263             default:
1264                 throw new RuntimeException("not implemented");
1265         }
1266         te.trimToSize();
1267         p.add(te);
1268     }
parseTypeAnnotations(TypeAnnotation pa, Element p)1269     private void parseTypeAnnotations(TypeAnnotation pa, Element p) {
1270         Element pta = new Element("RuntimeVisibleTypeAnnotation");
1271         p.add(pta);
1272         Position pos = pa.position;
1273         parsePosition(pos, pta);
1274         parseAnnotation(pa.annotation, pta);
1275     }
1276 
1277     @Override
visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute rvta, Element p)1278     public Element visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute rvta, Element p) {
1279         Element e = new Element(x.getCpString(rvta.attribute_name_index));
1280         for (TypeAnnotation pa : rvta.annotations) {
1281             parseTypeAnnotations(pa, e);
1282         }
1283         e.sort();
1284         p.add(e);
1285         return null;
1286     }
1287 
1288     @Override
visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute rita, Element p)1289     public Element visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute rita, Element p) {
1290         Element e = new Element(x.getCpString(rita.attribute_name_index));
1291         for (TypeAnnotation pa : rita.annotations) {
1292             parseTypeAnnotations(pa, e);
1293         }
1294         e.sort();
1295         p.add(e);
1296         return null;
1297     }
1298 
1299     @Override
visitSignature(Signature_attribute s, Element p)1300     public Element visitSignature(Signature_attribute s, Element p) {
1301         String aname = x.getCpString(s.attribute_name_index);
1302         String sname = x.getCpString(s.signature_index);
1303         Element se = new Element(aname);
1304         se.add(sname);
1305         se.trimToSize();
1306         p.add(se);
1307         return null;
1308     }
1309 
1310     @Override
visitSourceDebugExtension(SourceDebugExtension_attribute sde, Element p)1311     public Element visitSourceDebugExtension(SourceDebugExtension_attribute sde,
1312                                                 Element p) {
1313         String aname = x.getCpString(sde.attribute_name_index);
1314         Element se = new Element(aname);
1315         se.setAttr("val", sde.getValue());
1316         se.trimToSize();
1317         p.add(se);
1318         return null;
1319     }
1320 
1321     @Override
visitSourceFile(SourceFile_attribute sf, Element p)1322     public Element visitSourceFile(SourceFile_attribute sf, Element p) {
1323         String aname = x.getCpString(sf.attribute_name_index);
1324         String sname = x.getCpString(sf.sourcefile_index);
1325         Element se = new Element(aname);
1326         se.add(sname);
1327         se.trimToSize();
1328         p.add(se);
1329         return null;
1330     }
1331 
1332     @Override
visitSourceID(SourceID_attribute sid, Element p)1333     public Element visitSourceID(SourceID_attribute sid, Element p) {
1334         Element e = new Element(x.getCpString(sid.attribute_name_index));
1335         e.add(x.getCpString(sid.sourceID_index));
1336         e.trimToSize();
1337         p.add(e);
1338         return null;
1339     }
1340 
1341     @Override
visitStackMap(StackMap_attribute sm, Element p)1342     public Element visitStackMap(StackMap_attribute sm, Element p) {
1343         throw new UnsupportedOperationException("Not supported yet.");
1344     }
1345 
1346     @Override
visitStackMapTable(StackMapTable_attribute smt, Element p)1347     public Element visitStackMapTable(StackMapTable_attribute smt, Element p) {
1348         Element stackmap = new Element(x.getCpString(smt.attribute_name_index));
1349         for (StackMapTable_attribute.stack_map_frame f : smt.entries) {
1350            StackMapVisitor smv = new StackMapVisitor(x, cf, stackmap);
1351            stackmap.add(smv.visit(f));
1352         }
1353         stackmap.trimToSize();
1354         p.add(stackmap);
1355         return null;
1356     }
1357 
1358     @Override
visitSynthetic(Synthetic_attribute s, Element p)1359     public Element visitSynthetic(Synthetic_attribute s, Element p) {
1360         Element e = new Element(x.getCpString(s.attribute_name_index));
1361         e.trimToSize();
1362         p.add(e);
1363         return null;
1364     }
1365 }
1366 
1367 class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor<Element, Void> {
1368 
1369     final ClassFile cf;
1370     final ClassReader x;
1371     final Element parent;
1372 
StackMapVisitor(ClassReader x, ClassFile cf, Element parent)1373     public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) {
1374         this.x = x;
1375         this.cf = cf;
1376         this.parent = parent;
1377     }
1378 
visit(StackMapTable_attribute.stack_map_frame frame)1379     public Element visit(StackMapTable_attribute.stack_map_frame frame) {
1380         return frame.accept(this, null);
1381     }
1382 
1383     @Override
visit_same_frame(same_frame sm_frm, Void p)1384     public Element visit_same_frame(same_frame sm_frm, Void p) {
1385         Element e = new Element("SameFrame");
1386         e.setAttr("tag", "" + sm_frm.frame_type);
1387         return e;
1388     }
1389 
1390     @Override
visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p)1391     public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) {
1392         Element e = new Element("SameLocals1StackItemFrame");
1393         e.setAttr("tag", "" + s.frame_type);
1394         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1395         e.trimToSize();
1396         return e;
1397     }
1398 
1399     @Override
visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p)1400     public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) {
1401         Element e = new Element("SameLocals1StackItemFrameExtended");
1402         e.setAttr("tag", "" + s.frame_type);
1403         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1404         e.trimToSize();
1405         return e;
1406     }
1407 
1408     @Override
visit_chop_frame(chop_frame c, Void p)1409     public Element visit_chop_frame(chop_frame c, Void p) {
1410         Element e = new Element("Chop" + (251 - c.frame_type));
1411         e.setAttr("tag", "" + c.frame_type);
1412         e.setAttr("offset", "" + c.offset_delta);
1413         return e;
1414     }
1415 
1416     @Override
visit_same_frame_extended(same_frame_extended s, Void p)1417     public Element visit_same_frame_extended(same_frame_extended s, Void p) {
1418         Element e = new Element("SameFrameExtended");
1419         e.setAttr("tag", "" + s.frame_type);
1420         e.setAttr("offset", "" + s.offset_delta);
1421         return e;
1422     }
1423 
1424     @Override
visit_append_frame(append_frame a, Void p)1425     public Element visit_append_frame(append_frame a, Void p) {
1426        Element e = new Element("AppendFrame" + (a.frame_type - 251));
1427        e.setAttr("tag", "" + a.frame_type);
1428        e.addAll(getVerificationTypeInfo("Local", a.locals));
1429        e.trimToSize();
1430        return e;
1431     }
1432 
1433     @Override
visit_full_frame(full_frame fl_frm, Void p)1434     public Element visit_full_frame(full_frame fl_frm, Void p) {
1435          Element e = new Element("FullFrame");
1436          e.setAttr("tag", "" + fl_frm.frame_type);
1437          e.addAll(getVerificationTypeInfo("Local", fl_frm.locals));
1438          e.trimToSize();
1439          return e;
1440     }
1441 
getVerificationTypeInfo(String kind, StackMapTable_attribute.verification_type_info velems[])1442     private Element getVerificationTypeInfo(String kind,
1443             StackMapTable_attribute.verification_type_info velems[]) {
1444         Element container = new Element(velems.length);
1445         for (StackMapTable_attribute.verification_type_info v : velems) {
1446             Element ve = null;
1447             int offset = 0;
1448             int index = 0;
1449             switch (v.tag) {
1450                 case StackMapTable_attribute.verification_type_info.ITEM_Top:
1451                     ve = new Element("ITEM_Top");
1452                     break;
1453                 case StackMapTable_attribute.verification_type_info.ITEM_Integer:
1454                     ve = new Element("ITEM_Integer");
1455                     break;
1456                 case StackMapTable_attribute.verification_type_info.ITEM_Float:
1457                     ve = new Element("ITEM_Float");
1458                     break;
1459                 case StackMapTable_attribute.verification_type_info.ITEM_Long:
1460                     ve = new Element("ITEM_Long");
1461                     break;
1462                 case StackMapTable_attribute.verification_type_info.ITEM_Double:
1463                     ve = new Element("ITEM_Double");
1464                     break;
1465                 case StackMapTable_attribute.verification_type_info.ITEM_Null:
1466                     ve = new Element("ITEM_Null");
1467                     break;
1468                 case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
1469                     ve = new Element("ITEM_Uninitialized");
1470                     offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
1471                     ve.setAttr("offset", "" + offset);
1472                     break;
1473                 case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
1474                     ve = new Element("ITEM_UnitializedtThis");
1475                     break;
1476                 case StackMapTable_attribute.verification_type_info.ITEM_Object:
1477                     ve = new Element("ITEM_Object");
1478                     index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
1479                     ve.setAttr("class", x.getCpString(index));
1480                     break;
1481                 default:
1482                     ve = new Element("Unknown");
1483             }
1484             Element kindE = new Element(kind);
1485             kindE.setAttr("tag", "" + v.tag);
1486             container.add(kindE);
1487             kindE.add(ve);
1488         }
1489         container.trimToSize();
1490         return container;
1491     }
1492 }
1493 
1494 class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {
1495 
1496     final ClassReader x;
1497     final ClassFile cf;
1498 
InstructionVisitor(ClassReader x, ClassFile cf)1499     public InstructionVisitor(ClassReader x, ClassFile cf) {
1500         this.x = x;
1501         this.cf = cf;
1502     }
1503 
visit(Instruction i)1504     public Element visit(Instruction i) {
1505         Element ie =  i.accept(this, null);
1506         ie.trimToSize();
1507         return ie;
1508     }
1509 
1510     @Override
visitNoOperands(Instruction i, Void p)1511     public Element visitNoOperands(Instruction i, Void p) {
1512         Opcode o = i.getOpcode();
1513         Element e = new Element(i.getMnemonic());
1514         if (o.opcode > 0xab && o.opcode <= 0xb1) {
1515             e.setAttr("pc", "" + i.getPC());
1516         }
1517         return e;
1518     }
1519 
1520     @Override
visitArrayType(Instruction i, TypeKind tk, Void p)1521     public Element visitArrayType(Instruction i, TypeKind tk, Void p) {
1522         Element ie = new Element(i.getMnemonic());
1523         ie.setAttr("num", "" + tk.value);
1524         ie.setAttr("val", tk.name);
1525         return ie;
1526     }
1527 
1528     @Override
visitBranch(Instruction i, int i1, Void p)1529     public Element visitBranch(Instruction i, int i1, Void p) {
1530         Element ie = new Element(i.getMnemonic());
1531         ie.setAttr("lab", "" + (i.getPC() + i1));
1532         return ie;
1533     }
1534 
1535     @Override
visitConstantPoolRef(Instruction i, int i1, Void p)1536     public Element visitConstantPoolRef(Instruction i, int i1, Void p) {
1537         Element ie = new Element(i.getMnemonic());
1538         ie.setAttr("ref", x.getCpString(i1));
1539         return ie;
1540     }
1541 
1542     @Override
visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p)1543     public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
1544         // workaround for a potential bug in classfile
1545         Element ie = new Element(i.getMnemonic());
1546         if (i.getOpcode().equals(Opcode.IINC_W)) {
1547             ie.setAttr("loc", "" + i1);
1548             ie.setAttr("num", "" + i2);
1549         } else {
1550             ie.setAttr("ref", x.getCpString(i1));
1551             ie.setAttr("val", "" + i2);
1552         }
1553         return ie;
1554     }
1555 
1556     @Override
visitLocal(Instruction i, int i1, Void p)1557     public Element visitLocal(Instruction i, int i1, Void p) {
1558         Element ie = new Element(i.getMnemonic());
1559         ie.setAttr("loc", "" + i1);
1560         return ie;
1561     }
1562 
1563     @Override
visitLocalAndValue(Instruction i, int i1, int i2, Void p)1564     public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) {
1565         Element ie = new Element(i.getMnemonic());
1566         ie.setAttr("loc", "" + i1);
1567         ie.setAttr("num", "" + i2);
1568         return ie;
1569     }
1570 
1571     @Override
visitLookupSwitch(Instruction i, int i1, int i2, int[] ints, int[] ints1, Void p)1572     public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints,
1573                                      int[] ints1, Void p) {
1574         Element ie = new Element(i.getMnemonic());
1575         int pc = i.getPC();
1576         ie.setAttr("lab", "" + (pc + i1));
1577         for (int k = 0 ; k < i2 ; k++) {
1578             Element c = new Element("Case");
1579             c.setAttr("num", "" + (ints[k]));
1580             c.setAttr("lab", "" + (pc + ints1[k]));
1581             c.trimToSize();
1582             ie.add(c);
1583         }
1584         return ie;
1585     }
1586 
1587     @Override
visitTableSwitch(Instruction i, int i1, int i2, int i3, int[] ints, Void p)1588     public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
1589                                     int[] ints, Void p) {
1590         Element ie = new Element(i.getMnemonic());
1591         int pc = i.getPC();
1592         ie.setAttr("lab", "" + (pc + i1));
1593         for (int k : ints) {
1594             Element c = new Element("Case");
1595             c.setAttr("num", "" + (k + i2));
1596             c.setAttr("lab", "" + (pc + k));
1597             c.trimToSize();
1598             ie.add(c);
1599         }
1600         return ie;
1601     }
1602 
1603     @Override
visitValue(Instruction i, int i1, Void p)1604     public Element visitValue(Instruction i, int i1, Void p) {
1605         Element ie = new Element(i.getMnemonic());
1606         ie.setAttr("num", "" + i1);
1607         return ie;
1608     }
1609 
1610     @Override
visitUnknown(Instruction i, Void p)1611     public Element visitUnknown(Instruction i, Void p) {
1612         Element e = new Element(i.getMnemonic());
1613         e.setAttr("pc", "" + i.getPC());
1614         e.setAttr("opcode", "" + i.getOpcode().opcode);
1615         return e;
1616     }
1617 }
1618 
1619 class AnnotationsElementVisitor implements Annotation.element_value.Visitor<Element, Element> {
1620     final ClassReader x;
1621     final ClassFile cf;
1622 
AnnotationsElementVisitor(ClassReader x, ClassFile cf)1623     public AnnotationsElementVisitor(ClassReader x, ClassFile cf) {
1624         this.x = x;
1625         this.cf = cf;
1626     }
1627 
visit(Annotation.element_value v, Element p)1628     public Element visit(Annotation.element_value v, Element p) {
1629         return v.accept(this, p);
1630     }
1631 
1632     @Override
visitPrimitive(Primitive_element_value e, Element p)1633     public Element visitPrimitive(Primitive_element_value e, Element p) {
1634         Element el = new Element("String");
1635         el.setAttr("val", x.getCpString(e.const_value_index));
1636         el.trimToSize();
1637         return el;
1638     }
1639 
1640     @Override
visitEnum(Enum_element_value e, Element p)1641     public Element visitEnum(Enum_element_value e, Element p) {
1642         Element el = new Element("Enum");
1643         el.setAttr("name", x.getCpString(e.const_name_index));
1644         el.setAttr("type", x.getCpString(e.type_name_index));
1645         el.trimToSize();
1646         return el;
1647     }
1648 
1649     @Override
visitClass(Class_element_value c, Element p)1650     public Element visitClass(Class_element_value c, Element p) {
1651         Element el = new Element("Class");
1652         el.setAttr("name", x.getCpString(c.class_info_index));
1653         el.trimToSize();
1654         return el;
1655     }
1656 
1657     @Override
visitAnnotation(Annotation_element_value a, Element p)1658     public Element visitAnnotation(Annotation_element_value a, Element p) {
1659         Element el = new Element("Annotation");
1660         Annotation anno = a.annotation_value;
1661         for (Annotation.element_value_pair evp : anno.element_value_pairs) {
1662             Element child = visit(evp.value, el);
1663             if (child != null) {
1664                 el.add(child);
1665             }
1666         }
1667         el.trimToSize();
1668         return el;
1669     }
1670 
1671     @Override
visitArray(Array_element_value a, Element p)1672     public Element visitArray(Array_element_value a, Element p) {
1673      Element el = new Element("Array");
1674         for (Annotation.element_value v : a.values) {
1675            Element child = visit(v, el);
1676            if (child != null) {
1677                el.add(child);
1678            }
1679         }
1680         el.trimToSize();
1681         return el;
1682     }
1683 }
1684