1 /*
   2  * Copyright (c) 2010, 2017, 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 visitDynamicConstant(ConstantPool.CONSTANT_Dynamic_info c, Integer p) {
 809         String value = slist.get(p);
 810         if (value == null) {
 811             try {
 812                 value = bsmlist.get(c.bootstrap_method_attr_index) + " "
 813                         + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
 814                 slist.set(p, value);
 815                 xpool.add(new Element("CONSTANT_Dynamic",
 816                                       new String[]{"id", p.toString()},
 817                                       value));
 818 
 819             } catch (ConstantPoolException ex) {
 820                 ex.printStackTrace();
 821             }
 822         }
 823         return value;
 824     }
 825 
 826     @Override
 827     public String visitLong(CONSTANT_Long_info c, Integer p) {
 828         String value = slist.get(p);
 829         if (value == null) {
 830             value = Long.toString(c.value);
 831             slist.set(p, value);
 832             xpool.add(new Element("CONSTANT_Long",
 833                       new String[]{"id", p.toString()},
 834                       value));
 835         }
 836         return value;
 837     }
 838 
 839     @Override
 840     public String visitNameAndType(CONSTANT_NameAndType_info c, Integer p) {
 841         String value = slist.get(p);
 842         if (value == null) {
 843             try {
 844                 value = visit(cfpool.get(c.name_index), c.name_index);
 845                 value = value.concat(" " +
 846                         visit(cfpool.get(c.type_index), c.type_index));
 847                 slist.set(p, value);
 848                 xpool.add(new Element("CONSTANT_NameAndType",
 849                           new String[]{"id", p.toString()},
 850                           value));
 851             } catch (InvalidIndex ex) {
 852                 ex.printStackTrace();
 853             }
 854         }
 855         return value;
 856     }
 857 
 858     @Override
 859     public String visitMethodref(CONSTANT_Methodref_info c, Integer p) {
 860         String value = slist.get(p);
 861         if (value == null) {
 862             try {
 863                 value = visit(cfpool.get(c.class_index), c.class_index);
 864                 value = value.concat(" " +
 865                                      visit(cfpool.get(c.name_and_type_index),
 866                                      c.name_and_type_index));
 867                 slist.set(p, value);
 868                 xpool.add(new Element("CONSTANT_Methodref",
 869                           new String[]{"id", p.toString()},
 870                           value));
 871 
 872             } catch (ConstantPoolException ex) {
 873                 ex.printStackTrace();
 874             }
 875         }
 876         return value;
 877     }
 878 
 879     @Override
 880     public String visitMethodHandle(CONSTANT_MethodHandle_info c, Integer p) {
 881     String value = slist.get(p);
 882         if (value == null) {
 883             try {
 884                 value = c.reference_kind.name();
 885                 value = value.concat(" "
 886                         + visit(cfpool.get(c.reference_index), c.reference_index));
 887                 slist.set(p, value);
 888                 xpool.add(new Element("CONSTANT_MethodHandle",
 889                           new String[]{"id", p.toString()},
 890                           value));
 891 
 892             } catch (ConstantPoolException ex) {
 893                 ex.printStackTrace();
 894             }
 895         }
 896         return value;
 897     }
 898 
 899     @Override
 900     public String visitMethodType(CONSTANT_MethodType_info c, Integer p) {
 901         String value = slist.get(p);
 902         if (value == null) {
 903             try {
 904                 value = visit(cfpool.get(c.descriptor_index), c.descriptor_index);
 905                 slist.set(p, value);
 906                 xpool.add(new Element("CONSTANT_MethodType",
 907                           new String[]{"id", p.toString()},
 908                           value));
 909             } catch (ConstantPoolException ex) {
 910                 ex.printStackTrace();
 911             }
 912         }
 913         return value;
 914     }
 915 
 916     @Override
 917     public String visitString(CONSTANT_String_info c, Integer p) {
 918         try {
 919 
 920             String value = slist.get(p);
 921             if (value == null) {
 922                 value = c.getString();
 923                 slist.set(p, value);
 924                 xpool.add(new Element("CONSTANT_String",
 925                           new String[]{"id", p.toString()},
 926                           value));
 927             }
 928             return value;
 929         } catch (ConstantPoolException ex) {
 930             throw new RuntimeException("Fatal error", ex);
 931         }
 932     }
 933 
 934     @Override
 935     public String  visitUtf8(CONSTANT_Utf8_info cnstnt, Integer p) {
 936         String value = slist.get(p);
 937         if (value == null) {
 938             value = cnstnt.value;
 939             slist.set(p, value);
 940             xpool.add(new Element("CONSTANT_Utf8",
 941                       new String[]{"id", p.toString()},
 942                       value));
 943         }
 944         return value;
 945 
 946     }
 947 }
 948 
 949 class AttributeVisitor implements Attribute.Visitor<Element, Element> {
 950     final ClassFile cf;
 951     final ClassReader x;
 952     final AnnotationsElementVisitor aev;
 953     final InstructionVisitor iv;
 954 
 955     public AttributeVisitor(ClassReader x, ClassFile cf) {
 956         this.x = x;
 957         this.cf = cf;
 958         iv =  new InstructionVisitor(x, cf);
 959         aev = new AnnotationsElementVisitor(x, cf);
 960     }
 961 
 962     public void visit(Attribute a, Element parent) {
 963         a.accept(this, parent);
 964     }
 965 
 966     @Override
 967     public Element visitBootstrapMethods(BootstrapMethods_attribute bm, Element p) {
 968         Element e = new Element(x.getCpString(bm.attribute_name_index));
 969         for (BootstrapMethods_attribute.BootstrapMethodSpecifier bsm : bm.bootstrap_method_specifiers) {
 970             Element be = new Element("BootstrapMethodSpecifier");
 971             be.setAttr("ref", x.getCpString(bsm.bootstrap_method_ref));
 972             if (bsm.bootstrap_arguments.length > 0) {
 973                 Element bme = new Element("MethodArguments");
 974                 for (int index : bsm.bootstrap_arguments) {
 975                     bme.add(x.getCpString(index));
 976                 }
 977                 bme.trimToSize();
 978                 be.add(bme);
 979             }
 980             be.trimToSize();
 981             e.add(be);
 982         }
 983         e.trimToSize();
 984         if (!x.keepOrder) {
 985             e.sort();
 986         }
 987         p.add(e);
 988         return null;
 989     }
 990 
 991     @Override
 992     public Element visitDefault(DefaultAttribute da, Element p) {
 993         Element e = new Element(x.getCpString(da.attribute_name_index));
 994         StringBuilder sb = new StringBuilder();
 995         for (byte x : da.info) {
 996             sb.append("0x").append(Integer.toHexString(x)).append(" ");
 997         }
 998         e.setAttr("bytes", sb.toString().trim());
 999         e.trimToSize();
1000         p.add(e);
1001         return null;
1002     }
1003 
1004     @Override
1005     public Element visitAnnotationDefault(AnnotationDefault_attribute ad, Element p) {
1006         Element e = new Element(x.getCpString(ad.attribute_name_index));
1007         e.setAttr("tag", "" + ad.default_value.tag);
1008         Element child = aev.visit(ad.default_value, e);
1009         if (child != null) {
1010             e.add(child);
1011         }
1012         e.trimToSize();
1013         p.add(e);
1014         return null;
1015     }
1016 
1017     @Override
1018     public Element visitCharacterRangeTable(CharacterRangeTable_attribute crt,
1019                                             Element p) {
1020         Element e = new Element(x.getCpString(crt.attribute_name_index));
1021         for (CharacterRangeTable_attribute.Entry ce : crt.character_range_table) {
1022             e.setAttr("start_pc", "" + ce.start_pc);
1023             e.setAttr("end_pc", "" + ce.end_pc);
1024             e.setAttr("range_start", "" + ce.character_range_start);
1025             e.setAttr("range_end", "" + ce.character_range_end);
1026             e.setAttr("flags", x.flagString(ce.flags, "Method"));
1027         }
1028         e.trimToSize();
1029         p.add(e);
1030         return null;
1031     }
1032 
1033     private Element instructions(Element code, Code_attribute c) {
1034         Element ielement = new Element("Instructions");
1035         for (Instruction ins : c.getInstructions()) {
1036             ielement.add(iv.visit(ins));
1037         }
1038         ielement.trimToSize();
1039         return ielement;
1040     }
1041 
1042     @Override
1043     public Element visitCode(Code_attribute c, Element p) {
1044         Element e = null;
1045 
1046         e = new Element(x.getCpString(c.attribute_name_index),
1047                 "stack", "" + c.max_stack,
1048                 "local", "" + c.max_locals);
1049 
1050         e.add(instructions(e, c));
1051 
1052         for (Code_attribute.Exception_data edata : c.exception_table) {
1053             e.add(new Element("Handler",
1054                     "start", "" + edata.start_pc,
1055                     "end", "" + edata.end_pc,
1056                     "catch", "" + edata.handler_pc,
1057                     "class", x.getCpString(edata.catch_type)));
1058 
1059         }
1060         this.x.readAttributesFor(cf, c.attributes, e);
1061         e.trimToSize();
1062         p.add(e);
1063         return null;
1064     }
1065 
1066     @Override
1067     public Element visitCompilationID(CompilationID_attribute cid, Element p) {
1068         Element e = new Element(x.getCpString(cid.attribute_name_index),
1069                 x.getCpString(cid.compilationID_index));
1070         p.add(e);
1071         return null;
1072     }
1073 
1074     @Override
1075     public Element visitModulePackages(ModulePackages_attribute attr, Element p) {
1076         Element e = new Element(x.getCpString(attr.attribute_name_index));
1077         for (int i : attr.packages_index) {
1078             Element ee = new Element("Package");
1079             String pkg = x.getCpString(i);
1080             ee.setAttr("package", pkg);
1081             e.add(ee);
1082         }
1083         e.trimToSize();
1084         e.sort();
1085         p.add(e);
1086         return null;
1087     }
1088 
1089     @Override
1090     public Element visitConstantValue(ConstantValue_attribute cv, Element p) {
1091         Element e = new Element(x.getCpString(cv.attribute_name_index));
1092         e.add(x.getCpString(cv.constantvalue_index));
1093         p.add(e);
1094         return null;
1095     }
1096 
1097     @Override
1098     public Element visitDeprecated(Deprecated_attribute d, Element p) {
1099         Element e = new Element(x.getCpString(d.attribute_name_index));
1100         p.add(e);
1101         return null;
1102     }
1103 
1104     @Override
1105     public Element visitEnclosingMethod(EnclosingMethod_attribute em, Element p) {
1106         Element e = new Element(x.getCpString(em.attribute_name_index));
1107         e.setAttr("class", x.getCpString(em.class_index));
1108         e.setAttr("desc", x.getCpString(em.method_index));
1109         e.trimToSize();
1110         p.add(e);
1111         return null;
1112     }
1113 
1114     @Override
1115     public Element visitExceptions(Exceptions_attribute e, Element p) {
1116         Element ee = new Element(x.getCpString(e.attribute_name_index));
1117         for (int idx : e.exception_index_table) {
1118             Element n = new Element("Item");
1119             n.setAttr("class", x.getCpString(idx));
1120             ee.add(n);
1121         }
1122         ee.trimToSize();
1123         p.add(ee);
1124         return null;
1125     }
1126 
1127     @Override
1128     public Element visitInnerClasses(InnerClasses_attribute ic, Element p) {
1129         for (Info info : ic.classes) {
1130             Element e = new Element(x.getCpString(ic.attribute_name_index));
1131             e.setAttr("class", x.getCpString(info.inner_class_info_index));
1132             e.setAttr("outer", x.getCpString(info.outer_class_info_index));
1133             e.setAttr("name", x.getCpString(info.inner_name_index));
1134             e.setAttr("flags", x.flagString(info.inner_class_access_flags,
1135                     "InnerClass"));
1136             e.trimToSize();
1137             p.add(e);
1138         }
1139         return null;
1140     }
1141 
1142     @Override
1143     public Element visitLineNumberTable(LineNumberTable_attribute lnt, Element p) {
1144         String name = x.getCpString(lnt.attribute_name_index);
1145         for (LineNumberTable_attribute.Entry e : lnt.line_number_table) {
1146             Element l = new Element(name);
1147             l.setAttr("bci", "" + e.start_pc);
1148             l.setAttr("line", "" + e.line_number);
1149             l.trimToSize();
1150             p.add(l);
1151         }
1152         return null; // already added to parent
1153     }
1154 
1155     @Override
1156     public Element visitLocalVariableTable(LocalVariableTable_attribute lvt,
1157                                                 Element p) {
1158         String name = x.getCpString(lvt.attribute_name_index);
1159         for (LocalVariableTable_attribute.Entry e : lvt.local_variable_table) {
1160             Element l = new Element(name);
1161             l.setAttr("bci", "" + e.start_pc);
1162             l.setAttr("span", "" + e.length);
1163             l.setAttr("name", x.getCpString(e.name_index));
1164             l.setAttr("type", x.getCpString(e.descriptor_index));
1165             l.setAttr("slot", "" + e.index);
1166             l.trimToSize();
1167             p.add(l);
1168         }
1169         return null; // already added to parent
1170     }
1171 
1172     private void parseModuleRequires(RequiresEntry[] res, Element p) {
1173         for (RequiresEntry re : res) {
1174             Element er = new Element("Requires");
1175             er.setAttr("module", x.getCpString(re.requires_index));
1176             er.setAttr("flags", Integer.toString(re.requires_flags));
1177             p.add(er);
1178         }
1179     }
1180 
1181     private void parseModuleExports(ExportsEntry[] exports, Element p) {
1182         Element ex = new Element("Exports");
1183         for (ExportsEntry export : exports) {
1184             Element exto = new Element("exports");
1185             exto.setAttr("package", x.getCpString(export.exports_index));
1186             for (int idx : export.exports_to_index) {
1187                 exto.setAttr("module", x.getCpString(idx));
1188             }
1189             ex.add(exto);
1190         }
1191         p.add(ex);
1192     }
1193 
1194     private void parseModuleProvides(ProvidesEntry[] provides, Element p) {
1195         Element ex = new Element("Provides");
1196         for (ProvidesEntry provide : provides) {
1197             ex.setAttr("provides", x.getCpString(provide.provides_index));
1198             for (int idx : provide.with_index) {
1199                 ex.setAttr("with", x.getCpString(idx));
1200             }
1201         }
1202         p.add(ex);
1203     }
1204 
1205     @Override
1206     public Element visitModule(Module_attribute m, Element p) {
1207         Element e = new Element(x.getCpString(m.attribute_name_index));
1208         parseModuleRequires(m.requires, e);
1209         parseModuleExports(m.exports, e);
1210         for (int idx : m.uses_index) {
1211             Element ei = new Element("Uses");
1212             ei.setAttr("used_class", x.getCpString(idx));
1213             e.add(ei);
1214         }
1215         parseModuleProvides(m.provides, e);
1216         p.add(e);
1217         return null;
1218     }
1219 
1220     @Override
1221     public Element visitLocalVariableTypeTable(LocalVariableTypeTable_attribute lvtt,
1222                                                     Element p) {
1223         String name = x.getCpString(lvtt.attribute_name_index);
1224         for (LocalVariableTypeTable_attribute.Entry e : lvtt.local_variable_table) {
1225             Element l = new Element(name);
1226             l.setAttr("bci", "" + e.start_pc);
1227             l.setAttr("span", "" + e.length);
1228             l.setAttr("name", x.getCpString(e.name_index));
1229             l.setAttr("type", x.getCpString(e.signature_index));
1230             l.setAttr("slot", "" + e.index);
1231             l.trimToSize();
1232             p.add(l);
1233         }
1234         return null; // already added to parent
1235     }
1236 
1237     @Override
1238     public Element visitMethodParameters(MethodParameters_attribute mp, Element p) {
1239         String name = x.getCpString(mp.attribute_name_index);
1240         for (MethodParameters_attribute.Entry e : mp.method_parameter_table) {
1241             Element l = new Element(name);
1242             l.setAttr("name", x.getCpString(e.name_index));
1243             l.setAttr("flag", "" + e.flags);
1244             l.trimToSize();
1245             p.add(l);
1246         }
1247         return null; // already added to parent
1248     }
1249     private void parseAnnotation(Annotation anno, Element p) {
1250         Element ea = new Element("Annotation");
1251         ea.setAttr("name", "" + x.getCpString(anno.type_index));
1252         for (Annotation.element_value_pair evp : anno.element_value_pairs) {
1253             Element evpe = new Element("Element");
1254             evpe.setAttr("tag", "" + evp.value.tag);
1255             evpe.setAttr("value", x.getCpString(evp.element_name_index));
1256             Element child = aev.visit(evp.value, evpe);
1257             if (child != null) {
1258                 evpe.add(child);
1259             }
1260             ea.add(evpe);
1261         }
1262         ea.trimToSize();
1263         p.add(ea);
1264     }
1265 
1266     private void parseAnnotations(Annotation[] ra, Element p) {
1267         for (Annotation anno : ra) {
1268             parseAnnotation(anno, p);
1269         }
1270     }
1271 
1272     @Override
1273     public Element visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute rva,
1274                                                   Element p) {
1275         Element e = new Element(x.getCpString(rva.attribute_name_index));
1276         parseAnnotations(rva.annotations, e);
1277         e.trimToSize();
1278         p.add(e);
1279         return null;
1280     }
1281 
1282     @Override
1283     public Element visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute ria,
1284                                                     Element p) {
1285         Element e = new Element(x.getCpString(ria.attribute_name_index));
1286         parseAnnotations(ria.annotations, e);
1287         e.trimToSize();
1288         p.add(e);
1289         return null;
1290     }
1291 
1292     @Override
1293     public Element visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute rvpa,
1294                                                            Element p) {
1295         Element e = new Element(x.getCpString(rvpa.attribute_name_index));
1296         for (Annotation[] pa : rvpa.parameter_annotations) {
1297            parseAnnotations(pa, e);
1298         }
1299         p.add(e);
1300         return null;
1301     }
1302 
1303     @Override
1304     public Element visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute ripa,
1305                                                              Element p) {
1306         Element e = new Element(x.getCpString(ripa.attribute_name_index));
1307         for (Annotation[] pa : ripa.parameter_annotations) {
1308             parseAnnotations(pa, e);
1309         }
1310         p.add(e);
1311         return null;
1312     }
1313 
1314     private void parsePosition(Position ap, Element p) {
1315         Element te = new Element();
1316         switch (ap.type) {
1317             case CLASS_TYPE_PARAMETER: // 0x00
1318                 te.setName("CLASS_TYPE_PARAMETER");
1319                 te.setAttr("idx", "" + ap.parameter_index);
1320                 break;
1321             case METHOD_TYPE_PARAMETER: // 0x01
1322                 te.setName("METHOD_TYPE_PARAMETER");
1323                 te.setAttr("idx", "" + ap.parameter_index);
1324                 break;
1325             case CLASS_EXTENDS: // 0x10
1326                 te.setName("CLASS_EXTENDS");
1327                 te.setAttr("idx", "" + ap.type_index);
1328                 break;
1329             case CLASS_TYPE_PARAMETER_BOUND: // 0x11
1330                 te.setName("CLASS_TYPE_PARAMETER_BOUND");
1331                 te.setAttr("idx1", "" + ap.parameter_index);
1332                 te.setAttr("idx2", "" + ap.bound_index);
1333                 break;
1334             case METHOD_TYPE_PARAMETER_BOUND: // 0x12
1335                 te.setName("METHOD_TYPE_PARAMETER_BOUND");
1336                 te.setAttr("idx1", "" + ap.parameter_index);
1337                 te.setAttr("idx2", "" + ap.bound_index);
1338                 break;
1339             case FIELD: // 0x13
1340                 te.setName("FIELD");
1341                 break;
1342             case METHOD_RETURN: // 0x14
1343                 te.setName("METHOD_RETURN");
1344                 break;
1345             case METHOD_RECEIVER: // 0x15
1346                 te.setName("METHOD_RECEIVER");
1347                 break;
1348             case METHOD_FORMAL_PARAMETER: // 0x16
1349                 te.setName("METHOD_FORMAL_PARAMETER");
1350                 te.setAttr("idx", "" + ap.parameter_index);
1351                 break;
1352             case THROWS: // 0x17
1353                 te.setName("THROWS");
1354                 te.setAttr("idx", "" + ap.type_index);
1355                 break;
1356             case LOCAL_VARIABLE: // 0x40
1357                 te.setName("LOCAL_VARIABLE");
1358                 for (int i = 0; i < ap.lvarIndex.length; i++) {
1359                     te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
1360                     te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
1361                     te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
1362                 }
1363                 break;
1364             case RESOURCE_VARIABLE: // 0x41
1365                 te.setName("RESOURCE_VARIABLE");
1366                 for (int i = 0; i < ap.lvarIndex.length ; i++) {
1367                     te.setAttr("lvar_idx_" + i, "" + ap.lvarIndex[i]);
1368                     te.setAttr("lvar_len_" + i, "" + ap.lvarLength[i]);
1369                     te.setAttr("lvar_off_" + i, "" + ap.lvarOffset[i]);
1370                 }
1371                 break;
1372             case EXCEPTION_PARAMETER: // 0x42
1373                 te.setName("EXCEPTION_PARAMETER");
1374                 te.setAttr("idx", "" + ap.exception_index);
1375                 break;
1376             case INSTANCEOF: // 0x43
1377                 te.setName("INSTANCE_OF");
1378                 te.setAttr("off", "" + ap.offset);
1379                 break;
1380             case NEW: // 0x44
1381                 te.setName("NEW");
1382                 te.setAttr("off", "" + ap.offset);
1383                 break;
1384             case CONSTRUCTOR_REFERENCE: // 0x45
1385                 te.setName("CONSTRUCTOR_REFERENCE_RECEIVER");
1386                 te.setAttr("off", "" + ap.offset);
1387                 break;
1388             case METHOD_REFERENCE: // 0x46
1389                 te.setName("METHOD_REFERENCE_RECEIVER");
1390                 te.setAttr("off", "" + ap.offset);
1391                 break;
1392             case CAST: // 0x47
1393                 te.setName("CAST");
1394                 te.setAttr("off", "" + ap.offset);
1395                 te.setAttr("idx", "" + ap.type_index);
1396                 break;
1397             case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: // 0x48
1398                 te.setName("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT");
1399                 te.setAttr("off", "" + ap.offset);
1400                 te.setAttr("idx", "" + ap.type_index);
1401                 break;
1402             case METHOD_INVOCATION_TYPE_ARGUMENT: // 0x49
1403                 te.setName("METHOD_INVOCATION_TYPE_ARGUMENT");
1404                 te.setAttr("off", "" + ap.offset);
1405                 te.setAttr("idx", "" + ap.type_index);
1406                 break;
1407             case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: // 0x4A
1408                 te.setName("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT");
1409                 te.setAttr("off", "" + ap.offset);
1410                 te.setAttr("idx", "" + ap.type_index);
1411                 break;
1412             case METHOD_REFERENCE_TYPE_ARGUMENT: // 0x4B
1413                 te.setName("METHOD_REFERENCE_TYPE_ARGUMENT");
1414                 te.setAttr("off", "" + ap.offset);
1415                 te.setAttr("idx", "" + ap.type_index);
1416                 break;
1417             default:
1418                 throw new RuntimeException("not implemented");
1419         }
1420         te.trimToSize();
1421         p.add(te);
1422     }
1423     private void parseTypeAnnotations(TypeAnnotation pa, Element p) {
1424         Element pta = new Element("RuntimeVisibleTypeAnnotation");
1425         p.add(pta);
1426         Position pos = pa.position;
1427         parsePosition(pos, pta);
1428         parseAnnotation(pa.annotation, pta);
1429     }
1430 
1431     @Override
1432     public Element visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute rvta, Element p) {
1433         Element e = new Element(x.getCpString(rvta.attribute_name_index));
1434         for (TypeAnnotation pa : rvta.annotations) {
1435             parseTypeAnnotations(pa, e);
1436         }
1437         e.sort();
1438         p.add(e);
1439         return null;
1440     }
1441 
1442     @Override
1443     public Element visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute rita, Element p) {
1444         Element e = new Element(x.getCpString(rita.attribute_name_index));
1445         for (TypeAnnotation pa : rita.annotations) {
1446             parseTypeAnnotations(pa, e);
1447         }
1448         e.sort();
1449         p.add(e);
1450         return null;
1451     }
1452 
1453     @Override
1454     public Element visitSignature(Signature_attribute s, Element p) {
1455         String aname = x.getCpString(s.attribute_name_index);
1456         String sname = x.getCpString(s.signature_index);
1457         Element se = new Element(aname);
1458         se.add(sname);
1459         se.trimToSize();
1460         p.add(se);
1461         return null;
1462     }
1463 
1464     @Override
1465     public Element visitSourceDebugExtension(SourceDebugExtension_attribute sde,
1466                                                 Element p) {
1467         String aname = x.getCpString(sde.attribute_name_index);
1468         Element se = new Element(aname);
1469         se.setAttr("val", sde.getValue());
1470         se.trimToSize();
1471         p.add(se);
1472         return null;
1473     }
1474 
1475     @Override
1476     public Element visitSourceFile(SourceFile_attribute sf, Element p) {
1477         String aname = x.getCpString(sf.attribute_name_index);
1478         String sname = x.getCpString(sf.sourcefile_index);
1479         Element se = new Element(aname);
1480         se.add(sname);
1481         se.trimToSize();
1482         p.add(se);
1483         return null;
1484     }
1485 
1486     @Override
1487     public Element visitSourceID(SourceID_attribute sid, Element p) {
1488         Element e = new Element(x.getCpString(sid.attribute_name_index));
1489         e.add(x.getCpString(sid.sourceID_index));
1490         e.trimToSize();
1491         p.add(e);
1492         return null;
1493     }
1494 
1495     @Override
1496     public Element visitStackMap(StackMap_attribute sm, Element p) {
1497         throw new UnsupportedOperationException("Not supported yet.");
1498     }
1499 
1500     @Override
1501     public Element visitStackMapTable(StackMapTable_attribute smt, Element p) {
1502         Element stackmap = new Element(x.getCpString(smt.attribute_name_index));
1503         for (StackMapTable_attribute.stack_map_frame f : smt.entries) {
1504            StackMapVisitor smv = new StackMapVisitor(x, cf, stackmap);
1505            stackmap.add(smv.visit(f));
1506         }
1507         stackmap.trimToSize();
1508         p.add(stackmap);
1509         return null;
1510     }
1511 
1512     @Override
1513     public Element visitSynthetic(Synthetic_attribute s, Element p) {
1514         Element e = new Element(x.getCpString(s.attribute_name_index));
1515         e.trimToSize();
1516         p.add(e);
1517         return null;
1518     }
1519 
1520     @Override
1521     public Element visitModuleHashes(ModuleHashes_attribute attr, Element p) {
1522         Element e = new Element(x.getCpString(attr.attribute_name_index));
1523         e.setAttr("Algorithm", x.getCpString(attr.algorithm_index));
1524         for (Entry entry : attr.hashes_table) {
1525             Element ee = new Element("Entry");
1526             String mn = x.getCpString(entry.module_name_index);
1527             ee.setAttr("module_name", mn);
1528             ee.setAttr("hash_length", "" + entry.hash.length);
1529             StringBuilder sb = new StringBuilder();
1530             for (byte b: entry.hash) {
1531                 sb.append(String.format("%02x", b & 0xff));
1532             }
1533             ee.setAttr("hash", sb.toString());
1534             ee.trimToSize();
1535             e.add(ee);
1536         }
1537         e.trimToSize();
1538         e.sort();
1539         p.add(e);
1540         return null;
1541     }
1542 
1543     @Override
1544     public Element visitModuleMainClass(ModuleMainClass_attribute attr, Element p) {
1545         Element e = new Element(x.getCpString(attr.attribute_name_index));
1546         e.add(x.getCpString(attr.main_class_index));
1547         e.trimToSize();
1548         p.add(e);
1549         return null;
1550     }
1551 
1552     @Override
1553     public Element visitModuleResolution(ModuleResolution_attribute attr, Element p) {
1554         Element e = new Element("ModuleResolution");
1555         e.setAttr("flags", Integer.toString(attr.resolution_flags));
1556         e.trimToSize();
1557         p.add(e);
1558         return null;
1559     }
1560 
1561     @Override
1562     public Element visitModuleTarget(ModuleTarget_attribute attr, Element p) {
1563         Element e = new Element(x.getCpString(attr.attribute_name_index));
1564         e.add(x.getCpString(attr.target_platform_index));
1565         e.trimToSize();
1566         p.add(e);
1567         return null;
1568     }
1569 }
1570 
1571 class StackMapVisitor implements StackMapTable_attribute.stack_map_frame.Visitor<Element, Void> {
1572 
1573     final ClassFile cf;
1574     final ClassReader x;
1575     final Element parent;
1576 
1577     public StackMapVisitor(ClassReader x, ClassFile cf, Element parent) {
1578         this.x = x;
1579         this.cf = cf;
1580         this.parent = parent;
1581     }
1582 
1583     public Element visit(StackMapTable_attribute.stack_map_frame frame) {
1584         return frame.accept(this, null);
1585     }
1586 
1587     @Override
1588     public Element visit_same_frame(same_frame sm_frm, Void p) {
1589         Element e = new Element("SameFrame");
1590         e.setAttr("tag", "" + sm_frm.frame_type);
1591         return e;
1592     }
1593 
1594     @Override
1595     public Element visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame s, Void p) {
1596         Element e = new Element("SameLocals1StackItemFrame");
1597         e.setAttr("tag", "" + s.frame_type);
1598         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1599         e.trimToSize();
1600         return e;
1601     }
1602 
1603     @Override
1604     public Element visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended s, Void p) {
1605         Element e = new Element("SameLocals1StackItemFrameExtended");
1606         e.setAttr("tag", "" + s.frame_type);
1607         e.addAll(getVerificationTypeInfo("Stack", s.stack));
1608         e.trimToSize();
1609         return e;
1610     }
1611 
1612     @Override
1613     public Element visit_chop_frame(chop_frame c, Void p) {
1614         Element e = new Element("Chop" + (251 - c.frame_type));
1615         e.setAttr("tag", "" + c.frame_type);
1616         e.setAttr("offset", "" + c.offset_delta);
1617         return e;
1618     }
1619 
1620     @Override
1621     public Element visit_same_frame_extended(same_frame_extended s, Void p) {
1622         Element e = new Element("SameFrameExtended");
1623         e.setAttr("tag", "" + s.frame_type);
1624         e.setAttr("offset", "" + s.offset_delta);
1625         return e;
1626     }
1627 
1628     @Override
1629     public Element visit_append_frame(append_frame a, Void p) {
1630        Element e = new Element("AppendFrame" + (a.frame_type - 251));
1631        e.setAttr("tag", "" + a.frame_type);
1632        e.addAll(getVerificationTypeInfo("Local", a.locals));
1633        e.trimToSize();
1634        return e;
1635     }
1636 
1637     @Override
1638     public Element visit_full_frame(full_frame fl_frm, Void p) {
1639          Element e = new Element("FullFrame");
1640          e.setAttr("tag", "" + fl_frm.frame_type);
1641          e.addAll(getVerificationTypeInfo("Local", fl_frm.locals));
1642          e.trimToSize();
1643          return e;
1644     }
1645 
1646     private Element getVerificationTypeInfo(String kind,
1647             StackMapTable_attribute.verification_type_info velems[]) {
1648         Element container = new Element(velems.length);
1649         for (StackMapTable_attribute.verification_type_info v : velems) {
1650             Element ve = null;
1651             int offset = 0;
1652             int index = 0;
1653             switch (v.tag) {
1654                 case StackMapTable_attribute.verification_type_info.ITEM_Top:
1655                     ve = new Element("ITEM_Top");
1656                     break;
1657                 case StackMapTable_attribute.verification_type_info.ITEM_Integer:
1658                     ve = new Element("ITEM_Integer");
1659                     break;
1660                 case StackMapTable_attribute.verification_type_info.ITEM_Float:
1661                     ve = new Element("ITEM_Float");
1662                     break;
1663                 case StackMapTable_attribute.verification_type_info.ITEM_Long:
1664                     ve = new Element("ITEM_Long");
1665                     break;
1666                 case StackMapTable_attribute.verification_type_info.ITEM_Double:
1667                     ve = new Element("ITEM_Double");
1668                     break;
1669                 case StackMapTable_attribute.verification_type_info.ITEM_Null:
1670                     ve = new Element("ITEM_Null");
1671                     break;
1672                 case StackMapTable_attribute.verification_type_info.ITEM_Uninitialized:
1673                     ve = new Element("ITEM_Uninitialized");
1674                     offset = ((StackMapTable_attribute.Uninitialized_variable_info) v).offset;
1675                     ve.setAttr("offset", "" + offset);
1676                     break;
1677                 case StackMapTable_attribute.verification_type_info.ITEM_UninitializedThis:
1678                     ve = new Element("ITEM_UnitializedtThis");
1679                     break;
1680                 case StackMapTable_attribute.verification_type_info.ITEM_Object:
1681                     ve = new Element("ITEM_Object");
1682                     index = ((StackMapTable_attribute.Object_variable_info) v).cpool_index;
1683                     ve.setAttr("class", x.getCpString(index));
1684                     break;
1685                 default:
1686                     ve = new Element("Unknown");
1687             }
1688             Element kindE = new Element(kind);
1689             kindE.setAttr("tag", "" + v.tag);
1690             container.add(kindE);
1691             kindE.add(ve);
1692         }
1693         container.trimToSize();
1694         return container;
1695     }
1696 }
1697 
1698 class InstructionVisitor implements Instruction.KindVisitor<Element, Void> {
1699 
1700     final ClassReader x;
1701     final ClassFile cf;
1702 
1703     public InstructionVisitor(ClassReader x, ClassFile cf) {
1704         this.x = x;
1705         this.cf = cf;
1706     }
1707 
1708     public Element visit(Instruction i) {
1709         Element ie =  i.accept(this, null);
1710         ie.trimToSize();
1711         return ie;
1712     }
1713 
1714     @Override
1715     public Element visitNoOperands(Instruction i, Void p) {
1716         Opcode o = i.getOpcode();
1717         Element e = new Element(i.getMnemonic());
1718         if (o.opcode > 0xab && o.opcode <= 0xb1) {
1719             e.setAttr("pc", "" + i.getPC());
1720         }
1721         return e;
1722     }
1723 
1724     @Override
1725     public Element visitArrayType(Instruction i, TypeKind tk, Void p) {
1726         Element ie = new Element(i.getMnemonic());
1727         ie.setAttr("num", "" + tk.value);
1728         ie.setAttr("val", tk.name);
1729         return ie;
1730     }
1731 
1732     @Override
1733     public Element visitBranch(Instruction i, int i1, Void p) {
1734         Element ie = new Element(i.getMnemonic());
1735         ie.setAttr("lab", "" + (i.getPC() + i1));
1736         return ie;
1737     }
1738 
1739     @Override
1740     public Element visitConstantPoolRef(Instruction i, int i1, Void p) {
1741         Element ie = new Element(i.getMnemonic());
1742         ie.setAttr("ref", x.getCpString(i1));
1743         return ie;
1744     }
1745 
1746     @Override
1747     public Element visitConstantPoolRefAndValue(Instruction i, int i1, int i2, Void p) {
1748         // workaround for a potential bug in classfile
1749         Element ie = new Element(i.getMnemonic());
1750         if (i.getOpcode().equals(Opcode.IINC_W)) {
1751             ie.setAttr("loc", "" + i1);
1752             ie.setAttr("num", "" + i2);
1753         } else {
1754             ie.setAttr("ref", x.getCpString(i1));
1755             ie.setAttr("val", "" + i2);
1756         }
1757         return ie;
1758     }
1759 
1760     @Override
1761     public Element visitLocal(Instruction i, int i1, Void p) {
1762         Element ie = new Element(i.getMnemonic());
1763         ie.setAttr("loc", "" + i1);
1764         return ie;
1765     }
1766 
1767     @Override
1768     public Element visitLocalAndValue(Instruction i, int i1, int i2, Void p) {
1769         Element ie = new Element(i.getMnemonic());
1770         ie.setAttr("loc", "" + i1);
1771         ie.setAttr("num", "" + i2);
1772         return ie;
1773     }
1774 
1775     @Override
1776     public Element visitLookupSwitch(Instruction i, int i1, int i2, int[] ints,
1777                                      int[] ints1, Void p) {
1778         Element ie = new Element(i.getMnemonic());
1779         int pc = i.getPC();
1780         ie.setAttr("lab", "" + (pc + i1));
1781         for (int k = 0 ; k < i2 ; k++) {
1782             Element c = new Element("Case");
1783             c.setAttr("num", "" + (ints[k]));
1784             c.setAttr("lab", "" + (pc + ints1[k]));
1785             c.trimToSize();
1786             ie.add(c);
1787         }
1788         return ie;
1789     }
1790 
1791     @Override
1792     public Element visitTableSwitch(Instruction i, int i1, int i2, int i3,
1793                                     int[] ints, Void p) {
1794         Element ie = new Element(i.getMnemonic());
1795         int pc = i.getPC();
1796         ie.setAttr("lab", "" + (pc + i1));
1797         for (int k : ints) {
1798             Element c = new Element("Case");
1799             c.setAttr("num", "" + (k + i2));
1800             c.setAttr("lab", "" + (pc + k));
1801             c.trimToSize();
1802             ie.add(c);
1803         }
1804         return ie;
1805     }
1806 
1807     @Override
1808     public Element visitValue(Instruction i, int i1, Void p) {
1809         Element ie = new Element(i.getMnemonic());
1810         ie.setAttr("num", "" + i1);
1811         return ie;
1812     }
1813 
1814     @Override
1815     public Element visitUnknown(Instruction i, Void p) {
1816         Element e = new Element(i.getMnemonic());
1817         e.setAttr("pc", "" + i.getPC());
1818         e.setAttr("opcode", "" + i.getOpcode().opcode);
1819         return e;
1820     }
1821 }
1822 
1823 class AnnotationsElementVisitor implements Annotation.element_value.Visitor<Element, Element> {
1824     final ClassReader x;
1825     final ClassFile cf;
1826 
1827     public AnnotationsElementVisitor(ClassReader x, ClassFile cf) {
1828         this.x = x;
1829         this.cf = cf;
1830     }
1831 
1832     public Element visit(Annotation.element_value v, Element p) {
1833         return v.accept(this, p);
1834     }
1835 
1836     @Override
1837     public Element visitPrimitive(Primitive_element_value e, Element p) {
1838         Element el = new Element("String");
1839         el.setAttr("val", x.getCpString(e.const_value_index));
1840         el.trimToSize();
1841         return el;
1842     }
1843 
1844     @Override
1845     public Element visitEnum(Enum_element_value e, Element p) {
1846         Element el = new Element("Enum");
1847         el.setAttr("name", x.getCpString(e.const_name_index));
1848         el.setAttr("type", x.getCpString(e.type_name_index));
1849         el.trimToSize();
1850         return el;
1851     }
1852 
1853     @Override
1854     public Element visitClass(Class_element_value c, Element p) {
1855         Element el = new Element("Class");
1856         el.setAttr("name", x.getCpString(c.class_info_index));
1857         el.trimToSize();
1858         return el;
1859     }
1860 
1861     @Override
1862     public Element visitAnnotation(Annotation_element_value a, Element p) {
1863         Element el = new Element("Annotation");
1864         Annotation anno = a.annotation_value;
1865         for (Annotation.element_value_pair evp : anno.element_value_pairs) {
1866             Element child = visit(evp.value, el);
1867             if (child != null) {
1868                 el.add(child);
1869             }
1870         }
1871         el.trimToSize();
1872         return el;
1873     }
1874 
1875     @Override
1876     public Element visitArray(Array_element_value a, Element p) {
1877         Element el = new Element("Array");
1878         for (Annotation.element_value v : a.values) {
1879            Element child = visit(v, el);
1880            if (child != null) {
1881                el.add(child);
1882            }
1883         }
1884         el.trimToSize();
1885         return el;
1886     }
1887 }