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