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  */
  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.Annotation_element_value;
  30 import com.sun.tools.classfile.Annotation.Array_element_value;
  31 import com.sun.tools.classfile.Annotation.Class_element_value;
  32 import com.sun.tools.classfile.Annotation.Enum_element_value;
  33 import com.sun.tools.classfile.Annotation.Primitive_element_value;
  34 import com.sun.tools.classfile.AnnotationDefault_attribute;
  35 import com.sun.tools.classfile.Attribute;
  36 import com.sun.tools.classfile.Attributes;
  37 import com.sun.tools.classfile.BootstrapMethods_attribute;
  38 import com.sun.tools.classfile.CharacterRangeTable_attribute;
  39 import com.sun.tools.classfile.ClassFile;
  40 import com.sun.tools.classfile.Code_attribute;
  41 import com.sun.tools.classfile.CompilationID_attribute;
  42 import com.sun.tools.classfile.ConstantPool;
  43 import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
  44 import com.sun.tools.classfile.ConstantPool.CONSTANT_Module_info;
  45 import com.sun.tools.classfile.ConstantPool.CONSTANT_Package_info;
  46 import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
  47 import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
  48 import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
  49 import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
  50 import com.sun.tools.classfile.ConstantPool.CONSTANT_InterfaceMethodref_info;
  51 import com.sun.tools.classfile.ConstantPool.CONSTANT_InvokeDynamic_info;
  52 import com.sun.tools.classfile.ConstantPool.CONSTANT_Long_info;
  53 import com.sun.tools.classfile.ConstantPool.CONSTANT_MethodHandle_info;
  54 import com.sun.tools.classfile.ConstantPool.CONSTANT_MethodType_info;
  55 import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
  56 import com.sun.tools.classfile.ConstantPool.CONSTANT_NameAndType_info;
  57 import com.sun.tools.classfile.ConstantPool.CONSTANT_String_info;
  58 import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
  59 import com.sun.tools.classfile.ConstantPool.CPInfo;
  60 import com.sun.tools.classfile.ConstantPool.InvalidIndex;
  61 import com.sun.tools.classfile.ConstantPoolException;
  62 import com.sun.tools.classfile.ConstantValue_attribute;
  63 import com.sun.tools.classfile.DefaultAttribute;
  64 import com.sun.tools.classfile.Deprecated_attribute;
  65 import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
  66 import com.sun.tools.classfile.EnclosingMethod_attribute;
  67 import com.sun.tools.classfile.Exceptions_attribute;
  68 import com.sun.tools.classfile.Field;
  69 import com.sun.tools.classfile.InnerClasses_attribute;
  70 import com.sun.tools.classfile.InnerClasses_attribute.Info;
  71 import com.sun.tools.classfile.Instruction;
  72 import com.sun.tools.classfile.Instruction.TypeKind;
  73 import com.sun.tools.classfile.LineNumberTable_attribute;
  74 import com.sun.tools.classfile.LocalVariableTable_attribute;
  75 import com.sun.tools.classfile.LocalVariableTypeTable_attribute;
  76 import com.sun.tools.classfile.Method;
  77 import com.sun.tools.classfile.MethodParameters_attribute;
  78 import com.sun.tools.classfile.Module_attribute;
  79 import com.sun.tools.classfile.Module_attribute.ExportsEntry;
  80 import com.sun.tools.classfile.Module_attribute.ProvidesEntry;
  81 import com.sun.tools.classfile.Module_attribute.RequiresEntry;
  82 import com.sun.tools.classfile.ModuleHashes_attribute;
  83 import com.sun.tools.classfile.ModuleHashes_attribute.Entry;
  84 import com.sun.tools.classfile.ModuleMainClass_attribute;
  85 import com.sun.tools.classfile.ModuleResolution_attribute;
  86 import com.sun.tools.classfile.ModuleTarget_attribute;
  87 import com.sun.tools.classfile.ModulePackages_attribute;
  88 import com.sun.tools.classfile.Opcode;
  89 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
  90 import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute;
  91 import com.sun.tools.classfile.RuntimeInvisibleTypeAnnotations_attribute;
  92 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
  93 import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
  94 import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
  95 import com.sun.tools.classfile.Signature_attribute;
  96 import com.sun.tools.classfile.SourceDebugExtension_attribute;
  97 import com.sun.tools.classfile.SourceFile_attribute;
  98 import com.sun.tools.classfile.SourceID_attribute;
  99 import com.sun.tools.classfile.StackMapTable_attribute;
 100 import com.sun.tools.classfile.StackMapTable_attribute.append_frame;
 101 import com.sun.tools.classfile.StackMapTable_attribute.chop_frame;
 102 import com.sun.tools.classfile.StackMapTable_attribute.full_frame;
 103 import com.sun.tools.classfile.StackMapTable_attribute.same_frame;
 104 import com.sun.tools.classfile.StackMapTable_attribute.same_frame_extended;
 105 import com.sun.tools.classfile.StackMapTable_attribute.same_locals_1_stack_item_frame;
 106 import com.sun.tools.classfile.StackMapTable_attribute.same_locals_1_stack_item_frame_extended;
 107 import com.sun.tools.classfile.StackMap_attribute;
 108 import com.sun.tools.classfile.Synthetic_attribute;
 109 import com.sun.tools.classfile.TypeAnnotation;
 110 import com.sun.tools.classfile.TypeAnnotation.Position;
 111 import static com.sun.tools.classfile.TypeAnnotation.TargetType.THROWS;
 112 import java.io.*;
 113 import java.util.*;
 114 import java.util.jar.JarEntry;
 115 import java.util.jar.JarFile;
 116 import xmlkit.XMLKit.Element;
 117 
 118 /*
 119  * @author jrose, ksrini
 120  */
 121 public 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 
 591 class 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 
 930 class 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.os_name_index));
1546         e.add(x.getCpString(attr.os_arch_index));
1547         e.add(x.getCpString(attr.os_version_index));
1548         e.trimToSize();
1549         p.add(e);
1550         return null;
1551     }
1552 }
1553 
1554 class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor<Element, Void> {
1555 
1556     final ClassFile cf;
1557     final ClassReader x;
1558     final Element parent;
1559 
1560     public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) {
1561         this.x = x;
1562         this.cf = cf;
1563         this.parent = parent;
1564     }
1565 
1566     public Element visit(StackMapTable_attribute.stack_map_frame frame) {
1567         return frame.accept(this, null);
1568     }
1569 
1570     @Override
1571     public Element visit_same_frame(same_frame sm_frm, Void p) {
1572         Element e = new Element("SameFrame");
1573         e.setAttr("tag", "" + sm_frm.frame_type);
1574         return e;
1575     }
1576 
1577     @Override
1578     public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) {
1579         Element e = new Element("SameLocals1StackItemFrame");
1580         e.setAttr("tag", "" + s.frame_type);
1581         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1582         e.trimToSize();
1583         return e;
1584     }
1585 
1586     @Override
1587     public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) {
1588         Element e = new Element("SameLocals1StackItemFrameExtended");
1589         e.setAttr("tag", "" + s.frame_type);
1590         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1591         e.trimToSize();
1592         return e;
1593     }
1594 
1595     @Override
1596     public Element visit_chop_frame(chop_frame c, Void p) {
1597         Element e = new Element("Chop" + (251 - c.frame_type));
1598         e.setAttr("tag", "" + c.frame_type);
1599         e.setAttr("offset", "" + c.offset_delta);
1600         return e;
1601     }
1602 
1603     @Override
1604     public Element visit_same_frame_extended(same_frame_extended s, Void p) {
1605         Element e = new Element("SameFrameExtended");
1606         e.setAttr("tag", "" + s.frame_type);
1607         e.setAttr("offset", "" + s.offset_delta);
1608         return e;
1609     }
1610 
1611     @Override
1612     public Element visit_append_frame(append_frame a, Void p) {
1613        Element e = new Element("AppendFrame" + (a.frame_type - 251));
1614        e.setAttr("tag", "" + a.frame_type);
1615        e.addAll(getVerificationTypeInfo("Local", a.locals));
1616        e.trimToSize();
1617        return e;
1618     }
1619 
1620     @Override
1621     public Element visit_full_frame(full_frame fl_frm, Void p) {
1622          Element e = new Element("FullFrame");
1623          e.setAttr("tag", "" + fl_frm.frame_type);
1624          e.addAll(getVerificationTypeInfo("Local", fl_frm.locals));
1625          e.trimToSize();
1626          return e;
1627     }
1628 
1629     private Element getVerificationTypeInfo(String kind,
1630             StackMapTable_attribute.verification_type_info velems[]) {
1631         Element container = new Element(velems.length);
1632         for (StackMapTable_attribute.verification_type_info v : velems) {
1633             Element ve = null;
1634             int offset = 0;
1635             int index = 0;
1636             switch (v.tag) {
1637                 case StackMapTable_attribute.verification_type_info.ITEM_Top:
1638                     ve = new Element("ITEM_Top");
1639                     break;
1640                 case StackMapTable_attribute.verification_type_info.ITEM_Integer:
1641                     ve = new Element("ITEM_Integer");
1642                     break;
1643                 case StackMapTable_attribute.verification_type_info.ITEM_Float:
1644                     ve = new Element("ITEM_Float");
1645                     break;
1646                 case StackMapTable_attribute.verification_type_info.ITEM_Long:
1647                     ve = new Element("ITEM_Long");
1648                     break;
1649                 case StackMapTable_attribute.verification_type_info.ITEM_Double:
1650                     ve = new Element("ITEM_Double");
1651                     break;
1652                 case StackMapTable_attribute.verification_type_info.ITEM_Null:
1653                     ve = new Element("ITEM_Null");
1654                     break;
1655                 case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
1656                     ve = new Element("ITEM_Uninitialized");
1657                     offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
1658                     ve.setAttr("offset", "" + offset);
1659                     break;
1660                 case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
1661                     ve = new Element("ITEM_UnitializedtThis");
1662                     break;
1663                 case StackMapTable_attribute.verification_type_info.ITEM_Object:
1664                     ve = new Element("ITEM_Object");
1665                     index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
1666                     ve.setAttr("class", x.getCpString(index));
1667                     break;
1668                 default:
1669                     ve = new Element("Unknown");
1670             }
1671             Element kindE = new Element(kind);
1672             kindE.setAttr("tag", "" + v.tag);
1673             container.add(kindE);
1674             kindE.add(ve);
1675         }
1676         container.trimToSize();
1677         return container;
1678     }
1679 }
1680 
1681 class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {
1682 
1683     final ClassReader x;
1684     final ClassFile cf;
1685 
1686     public InstructionVisitor(ClassReader x, ClassFile cf) {
1687         this.x = x;
1688         this.cf = cf;
1689     }
1690 
1691     public Element visit(Instruction i) {
1692         Element ie =  i.accept(this, null);
1693         ie.trimToSize();
1694         return ie;
1695     }
1696 
1697     @Override
1698     public Element visitNoOperands(Instruction i, Void p) {
1699         Opcode o = i.getOpcode();
1700         Element e = new Element(i.getMnemonic());
1701         if (o.opcode > 0xab && o.opcode <= 0xb1) {
1702             e.setAttr("pc", "" + i.getPC());
1703         }
1704         return e;
1705     }
1706 
1707     @Override
1708     public Element visitArrayType(Instruction i, TypeKind tk, Void p) {
1709         Element ie = new Element(i.getMnemonic());
1710         ie.setAttr("num", "" + tk.value);
1711         ie.setAttr("val", tk.name);
1712         return ie;
1713     }
1714 
1715     @Override
1716     public Element visitBranch(Instruction i, int i1, Void p) {
1717         Element ie = new Element(i.getMnemonic());
1718         ie.setAttr("lab", "" + (i.getPC() + i1));
1719         return ie;
1720     }
1721 
1722     @Override
1723     public Element visitConstantPoolRef(Instruction i, int i1, Void p) {
1724         Element ie = new Element(i.getMnemonic());
1725         ie.setAttr("ref", x.getCpString(i1));
1726         return ie;
1727     }
1728 
1729     @Override
1730     public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
1731         // workaround for a potential bug in classfile
1732         Element ie = new Element(i.getMnemonic());
1733         if (i.getOpcode().equals(Opcode.IINC_W)) {
1734             ie.setAttr("loc", "" + i1);
1735             ie.setAttr("num", "" + i2);
1736         } else {
1737             ie.setAttr("ref", x.getCpString(i1));
1738             ie.setAttr("val", "" + i2);
1739         }
1740         return ie;
1741     }
1742 
1743     @Override
1744     public Element visitLocal(Instruction i, int i1, Void p) {
1745         Element ie = new Element(i.getMnemonic());
1746         ie.setAttr("loc", "" + i1);
1747         return ie;
1748     }
1749 
1750     @Override
1751     public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) {
1752         Element ie = new Element(i.getMnemonic());
1753         ie.setAttr("loc", "" + i1);
1754         ie.setAttr("num", "" + i2);
1755         return ie;
1756     }
1757 
1758     @Override
1759     public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints,
1760                                      int[] ints1, Void p) {
1761         Element ie = new Element(i.getMnemonic());
1762         int pc = i.getPC();
1763         ie.setAttr("lab", "" + (pc + i1));
1764         for (int k = 0 ; k < i2 ; k++) {
1765             Element c = new Element("Case");
1766             c.setAttr("num", "" + (ints[k]));
1767             c.setAttr("lab", "" + (pc + ints1[k]));
1768             c.trimToSize();
1769             ie.add(c);
1770         }
1771         return ie;
1772     }
1773 
1774     @Override
1775     public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
1776                                     int[] ints, Void p) {
1777         Element ie = new Element(i.getMnemonic());
1778         int pc = i.getPC();
1779         ie.setAttr("lab", "" + (pc + i1));
1780         for (int k : ints) {
1781             Element c = new Element("Case");
1782             c.setAttr("num", "" + (k + i2));
1783             c.setAttr("lab", "" + (pc + k));
1784             c.trimToSize();
1785             ie.add(c);
1786         }
1787         return ie;
1788     }
1789 
1790     @Override
1791     public Element visitValue(Instruction i, int i1, Void p) {
1792         Element ie = new Element(i.getMnemonic());
1793         ie.setAttr("num", "" + i1);
1794         return ie;
1795     }
1796 
1797     @Override
1798     public Element visitUnknown(Instruction i, Void p) {
1799         Element e = new Element(i.getMnemonic());
1800         e.setAttr("pc", "" + i.getPC());
1801         e.setAttr("opcode", "" + i.getOpcode().opcode);
1802         return e;
1803     }
1804 }
1805 
1806 class AnnotationsElementVisitor implements Annotation.element_value.Visitor<Element, Element> {
1807     final ClassReader x;
1808     final ClassFile cf;
1809 
1810     public AnnotationsElementVisitor(ClassReader x, ClassFile cf) {
1811         this.x = x;
1812         this.cf = cf;
1813     }
1814 
1815     public Element visit(Annotation.element_value v, Element p) {
1816         return v.accept(this, p);
1817     }
1818 
1819     @Override
1820     public Element visitPrimitive(Primitive_element_value e, Element p) {
1821         Element el = new Element("String");
1822         el.setAttr("val", x.getCpString(e.const_value_index));
1823         el.trimToSize();
1824         return el;
1825     }
1826 
1827     @Override
1828     public Element visitEnum(Enum_element_value e, Element p) {
1829         Element el = new Element("Enum");
1830         el.setAttr("name", x.getCpString(e.const_name_index));
1831         el.setAttr("type", x.getCpString(e.type_name_index));
1832         el.trimToSize();
1833         return el;
1834     }
1835 
1836     @Override
1837     public Element visitClass(Class_element_value c, Element p) {
1838         Element el = new Element("Class");
1839         el.setAttr("name", x.getCpString(c.class_info_index));
1840         el.trimToSize();
1841         return el;
1842     }
1843 
1844     @Override
1845     public Element visitAnnotation(Annotation_element_value a, Element p) {
1846         Element el = new Element("Annotation");
1847         Annotation anno = a.annotation_value;
1848         for (Annotation.element_value_pair evp : anno.element_value_pairs) {
1849             Element child = visit(evp.value, el);
1850             if (child != null) {
1851                 el.add(child);
1852             }
1853         }
1854         el.trimToSize();
1855         return el;
1856     }
1857 
1858     @Override
1859     public Element visitArray(Array_element_value a, Element p) {
1860         Element el = new Element("Array");
1861         for (Annotation.element_value v : a.values) {
1862            Element child = visit(v, el);
1863            if (child != null) {
1864                el.add(child);
1865            }
1866         }
1867         el.trimToSize();
1868         return el;
1869     }
1870 }