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