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