jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
changeset 6314 8ab691ddb904
child 12544 5768f2e096de
equal deleted inserted replaced
6313:470912c9e214 6314:8ab691ddb904
       
     1 /*
       
     2  * Copyright (c) 2010, 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 java.util.*;
       
    28 import java.util.jar.*;
       
    29 import java.lang.reflect.*;
       
    30 import java.io.*;
       
    31 import xmlkit.XMLKit.Element;
       
    32 
       
    33 /*
       
    34  * @author jrose
       
    35  */
       
    36 public class ClassReader extends ClassSyntax {
       
    37 
       
    38     private static final CommandLineParser CLP = new CommandLineParser(""
       
    39             + "-source:     +> = \n"
       
    40             + "-dest:       +> = \n"
       
    41             + "-encoding:   +> = \n"
       
    42             + "-jcov           $ \n   -nojcov         !-jcov        \n"
       
    43             + "-verbose        $ \n   -noverbose      !-verbose     \n"
       
    44             + "-pretty         $ \n   -nopretty       !-pretty      \n"
       
    45             + "-keepPath       $ \n   -nokeepPath     !-keepPath    \n"
       
    46             + "-keepCP         $ \n   -nokeepCP       !-keepCP      \n"
       
    47             + "-keepBytes      $ \n   -nokeepBytes    !-keepBytes   \n"
       
    48             + "-parseBytes     $ \n   -noparseBytes   !-parseBytes  \n"
       
    49             + "-resolveRefs    $ \n   -noresolveRefs  !-resolveRefs \n"
       
    50             + "-keepOrder      $ \n   -nokeepOrder    !-keepOrder   \n"
       
    51             + "-keepSizes      $ \n   -nokeepSizes    !-keepSizes   \n"
       
    52             + "-continue       $ \n   -nocontinue     !-continue    \n"
       
    53             + "-attrDef        & \n"
       
    54             + "-@         >-@  . \n"
       
    55             + "-              +? \n"
       
    56             + "\n");
       
    57 
       
    58     public static void main(String[] ava) throws IOException {
       
    59         ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava));
       
    60         HashMap<String, String> props = new HashMap<String, String>();
       
    61         props.put("-encoding:", "UTF8");  // default
       
    62         props.put("-keepOrder", null);    // CLI default
       
    63         props.put("-pretty", "1");     // CLI default
       
    64         props.put("-continue", "1");     // CLI default
       
    65         CLP.parse(av, props);
       
    66         //System.out.println(props+" ++ "+av);
       
    67         File source = asFile(props.get("-source:"));
       
    68         File dest = asFile(props.get("-dest:"));
       
    69         String encoding = props.get("-encoding:");
       
    70         boolean contError = props.containsKey("-continue");
       
    71         ClassReader options = new ClassReader();
       
    72         options.copyOptionsFrom(props);
       
    73         /*
       
    74         if (dest == null && av.size() > 1) {
       
    75         dest = File.createTempFile("TestOut", ".dir", new File("."));
       
    76         dest.delete();
       
    77         if (!dest.mkdir())
       
    78         throw new RuntimeException("Cannot create "+dest);
       
    79         System.out.println("Writing results to "+dest);
       
    80         }
       
    81          */
       
    82         if (av.isEmpty()) {
       
    83             av.add("doit");  //to enter this loop
       
    84         }
       
    85         boolean readList = false;
       
    86         for (String a : av) {
       
    87             if (readList) {
       
    88                 readList = false;
       
    89                 InputStream fin;
       
    90                 if (a.equals("-")) {
       
    91                     fin = System.in;
       
    92                 } else {
       
    93                     fin = new FileInputStream(a);
       
    94                 }
       
    95 
       
    96                 BufferedReader files = makeReader(fin, encoding);
       
    97                 for (String file; (file = files.readLine()) != null;) {
       
    98                     doFile(file, source, dest, options, encoding, contError);
       
    99                 }
       
   100                 if (fin != System.in) {
       
   101                     fin.close();
       
   102                 }
       
   103             } else if (a.equals("-@")) {
       
   104                 readList = true;
       
   105             } else if (a.startsWith("-")) {
       
   106                 throw new RuntimeException("Bad flag argument: " + a);
       
   107             } else if (source.getName().endsWith(".jar")) {
       
   108                 doJar(a, source, dest, options, encoding, contError);
       
   109             } else {
       
   110                 doFile(a, source, dest, options, encoding, contError);
       
   111             }
       
   112         }
       
   113     }
       
   114 
       
   115     private static File asFile(String str) {
       
   116         return (str == null) ? null : new File(str);
       
   117     }
       
   118 
       
   119     private static void doFile(String a,
       
   120             File source, File dest,
       
   121             ClassReader options, String encoding,
       
   122             boolean contError) throws IOException {
       
   123         if (!contError) {
       
   124             doFile(a, source, dest, options, encoding);
       
   125         } else {
       
   126             try {
       
   127                 doFile(a, source, dest, options, encoding);
       
   128             } catch (Exception ee) {
       
   129                 System.out.println("Error processing " + source + ": " + ee);
       
   130             }
       
   131         }
       
   132     }
       
   133 
       
   134     private static void doJar(String a, File source, File dest, ClassReader options,
       
   135             String encoding, Boolean contError) throws IOException {
       
   136         try {
       
   137             JarFile jf = new JarFile(source);
       
   138             for (JarEntry je : Collections.list((Enumeration<JarEntry>) jf.entries())) {
       
   139                 String name = je.getName();
       
   140                 if (!name.endsWith(".class")) {
       
   141                     continue;
       
   142                 }
       
   143                 doStream(name, jf.getInputStream(je), dest, options, encoding);
       
   144             }
       
   145         } catch (IOException ioe) {
       
   146             if (contError) {
       
   147                 System.out.println("Error processing " + source + ": " + ioe);
       
   148             } else {
       
   149                 throw ioe;
       
   150             }
       
   151         }
       
   152     }
       
   153 
       
   154     private static void doStream(String a, InputStream in, File dest,
       
   155             ClassReader options, String encoding) throws IOException {
       
   156 
       
   157         File f = new File(a);
       
   158         ClassReader cr = new ClassReader(options);
       
   159         Element e = cr.readFrom(in);
       
   160 
       
   161         OutputStream out;
       
   162         if (dest == null) {
       
   163             //System.out.println(e.prettyString());
       
   164             out = System.out;
       
   165         } else {
       
   166             File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
       
   167             String outName = outf.getName();
       
   168             File outSubdir = outf.getParentFile();
       
   169             outSubdir.mkdirs();
       
   170             int extPos = outName.lastIndexOf('.');
       
   171             if (extPos > 0) {
       
   172                 outf = new File(outSubdir, outName.substring(0, extPos) + ".xml");
       
   173             }
       
   174             out = new FileOutputStream(outf);
       
   175         }
       
   176 
       
   177         Writer outw = makeWriter(out, encoding);
       
   178         if (options.pretty || !options.keepOrder) {
       
   179             e.writePrettyTo(outw);
       
   180         } else {
       
   181             e.writeTo(outw);
       
   182         }
       
   183         if (out == System.out) {
       
   184             outw.write("\n");
       
   185             outw.flush();
       
   186         } else {
       
   187             outw.close();
       
   188         }
       
   189     }
       
   190 
       
   191     private static void doFile(String a,
       
   192             File source, File dest,
       
   193             ClassReader options, String encoding) throws IOException {
       
   194         File inf = new File(source, a);
       
   195         if (dest != null && options.verbose) {
       
   196             System.out.println("Reading " + inf);
       
   197         }
       
   198 
       
   199         BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf));
       
   200 
       
   201         doStream(a, in, dest, options, encoding);
       
   202 
       
   203     }
       
   204 
       
   205     public static BufferedReader makeReader(InputStream in, String encoding) throws IOException {
       
   206         // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
       
   207         if (encoding.equals("8BIT")) {
       
   208             encoding = EIGHT_BIT_CHAR_ENCODING;
       
   209         }
       
   210         if (encoding.equals("UTF8")) {
       
   211             encoding = UTF8_ENCODING;
       
   212         }
       
   213         if (encoding.equals("DEFAULT")) {
       
   214             encoding = null;
       
   215         }
       
   216         if (encoding.equals("-")) {
       
   217             encoding = null;
       
   218         }
       
   219         Reader inw;
       
   220         in = new BufferedInputStream(in);  // add buffering
       
   221         if (encoding == null) {
       
   222             inw = new InputStreamReader(in);
       
   223         } else {
       
   224             inw = new InputStreamReader(in, encoding);
       
   225         }
       
   226         return new BufferedReader(inw);  // add buffering
       
   227     }
       
   228 
       
   229     public static Writer makeWriter(OutputStream out, String encoding) throws IOException {
       
   230         // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
       
   231         if (encoding.equals("8BIT")) {
       
   232             encoding = EIGHT_BIT_CHAR_ENCODING;
       
   233         }
       
   234         if (encoding.equals("UTF8")) {
       
   235             encoding = UTF8_ENCODING;
       
   236         }
       
   237         if (encoding.equals("DEFAULT")) {
       
   238             encoding = null;
       
   239         }
       
   240         if (encoding.equals("-")) {
       
   241             encoding = null;
       
   242         }
       
   243         Writer outw;
       
   244         if (encoding == null) {
       
   245             outw = new OutputStreamWriter(out);
       
   246         } else {
       
   247             outw = new OutputStreamWriter(out, encoding);
       
   248         }
       
   249         return new BufferedWriter(outw);  // add buffering
       
   250     }
       
   251 
       
   252     public Element result() {
       
   253         return cfile;
       
   254     }
       
   255     protected InputStream in;
       
   256     protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
       
   257     protected byte cpTag[];
       
   258     protected String cpName[];
       
   259     protected String[] callables;     // varies
       
   260     public static final String REF_PREFIX = "#";
       
   261     // input options
       
   262     public boolean pretty = false;
       
   263     public boolean verbose = false;
       
   264     public boolean keepPath = false;
       
   265     public boolean keepCP = false;
       
   266     public boolean keepBytes = false;
       
   267     public boolean parseBytes = true;
       
   268     public boolean resolveRefs = true;
       
   269     public boolean keepOrder = true;
       
   270     public boolean keepSizes = false;
       
   271 
       
   272     public ClassReader() {
       
   273         super.cfile = new Element("ClassFile");
       
   274     }
       
   275 
       
   276     public ClassReader(ClassReader options) {
       
   277         this();
       
   278         copyOptionsFrom(options);
       
   279     }
       
   280 
       
   281     public void copyOptionsFrom(ClassReader options) {
       
   282         pretty = options.pretty;
       
   283         verbose = options.verbose;
       
   284         keepPath = options.keepPath;
       
   285         keepCP = options.keepCP;
       
   286         keepBytes = options.keepBytes;
       
   287         parseBytes = options.parseBytes;
       
   288         resolveRefs = options.resolveRefs;
       
   289         keepSizes = options.keepSizes;
       
   290         keepOrder = options.keepOrder;
       
   291         attrTypes = options.attrTypes;
       
   292     }
       
   293 
       
   294     public void copyOptionsFrom(Map<String, String> options) {
       
   295         if (options.containsKey("-pretty")) {
       
   296             pretty = (options.get("-pretty") != null);
       
   297         }
       
   298         if (options.containsKey("-verbose")) {
       
   299             verbose = (options.get("-verbose") != null);
       
   300         }
       
   301         if (options.containsKey("-keepPath")) {
       
   302             keepPath = (options.get("-keepPath") != null);
       
   303         }
       
   304         if (options.containsKey("-keepCP")) {
       
   305             keepCP = (options.get("-keepCP") != null);
       
   306         }
       
   307         if (options.containsKey("-keepBytes")) {
       
   308             keepBytes = (options.get("-keepBytes") != null);
       
   309         }
       
   310         if (options.containsKey("-parseBytes")) {
       
   311             parseBytes = (options.get("-parseBytes") != null);
       
   312         }
       
   313         if (options.containsKey("-resolveRefs")) {
       
   314             resolveRefs = (options.get("-resolveRefs") != null);
       
   315         }
       
   316         if (options.containsKey("-keepSizes")) {
       
   317             keepSizes = (options.get("-keepSizes") != null);
       
   318         }
       
   319         if (options.containsKey("-keepOrder")) {
       
   320             keepOrder = (options.get("-keepOrder") != null);
       
   321         }
       
   322         if (options.containsKey("-attrDef")) {
       
   323             addAttrTypes(options.get("-attrDef").split(" "));
       
   324         }
       
   325         if (options.get("-jcov") != null) {
       
   326             addJcovAttrTypes();
       
   327         }
       
   328     }
       
   329 
       
   330     public Element readFrom(InputStream in) throws IOException {
       
   331         this.in = in;
       
   332         // read the file header
       
   333         int magic = u4();
       
   334         if (magic != 0xCAFEBABE) {
       
   335             throw new RuntimeException("bad magic number " + Integer.toHexString(magic));
       
   336         }
       
   337         cfile.setAttr("magic", "" + magic);
       
   338         int minver = u2();
       
   339         int majver = u2();
       
   340         cfile.setAttr("minver", "" + minver);
       
   341         cfile.setAttr("majver", "" + majver);
       
   342         readCP();
       
   343         readClass();
       
   344         return result();
       
   345     }
       
   346 
       
   347     public Element readFrom(File file) throws IOException {
       
   348         InputStream in = null;
       
   349         try {
       
   350             in = new FileInputStream(file);
       
   351             Element e = readFrom(new BufferedInputStream(in));
       
   352             if (keepPath) {
       
   353                 e.setAttr("path", file.toString());
       
   354             }
       
   355             return e;
       
   356         } finally {
       
   357             if (in != null) {
       
   358                 in.close();
       
   359             }
       
   360         }
       
   361     }
       
   362 
       
   363     private void readClass() throws IOException {
       
   364         klass = new Element("Class");
       
   365         cfile.add(klass);
       
   366         int flags = u2();
       
   367         String thisk = cpRef();
       
   368         String superk = cpRef();
       
   369         klass.setAttr("name", thisk);
       
   370         boolean flagsSync = ((flags & Modifier.SYNCHRONIZED) != 0);
       
   371         flags &= ~Modifier.SYNCHRONIZED;
       
   372         String flagString = flagString(flags, klass);
       
   373         if (!flagsSync) {
       
   374             if (flagString.length() > 0) {
       
   375                 flagString += " ";
       
   376             }
       
   377             flagString += "!synchronized";
       
   378         }
       
   379         klass.setAttr("flags", flagString);
       
   380         klass.setAttr("super", superk);
       
   381         for (int len = u2(), i = 0; i < len; i++) {
       
   382             String interk = cpRef();
       
   383             klass.add(new Element("Interface", "name", interk));
       
   384         }
       
   385         Element fields = readMembers("Field");
       
   386         klass.addAll(fields);
       
   387         Element methods = readMembers("Method");
       
   388         if (!keepOrder) {
       
   389             methods.sort();
       
   390         }
       
   391         klass.addAll(methods);
       
   392         readAttributesFor(klass);
       
   393         klass.trimToSize();
       
   394         if (keepSizes) {
       
   395             attachTo(cfile, formatAttrSizes());
       
   396         }
       
   397         if (paddingSize != 0) {
       
   398             cfile.setAttr("padding", "" + paddingSize);
       
   399         }
       
   400     }
       
   401 
       
   402     private Element readMembers(String kind) throws IOException {
       
   403         int len = u2();
       
   404         Element members = new Element(len);
       
   405         for (int i = 0; i < len; i++) {
       
   406             Element member = new Element(kind);
       
   407             int flags = u2();
       
   408             String name = cpRef();
       
   409             String type = cpRef();
       
   410             member.setAttr("name", name);
       
   411             member.setAttr("type", type);
       
   412             member.setAttr("flags", flagString(flags, member));
       
   413             readAttributesFor(member);
       
   414             member.trimToSize();
       
   415             members.add(member);
       
   416         }
       
   417         return members;
       
   418     }
       
   419 
       
   420     protected String flagString(int flags, Element holder) {
       
   421         // Superset of Modifier.toString.
       
   422         int kind = 0;
       
   423         if (holder.getName() == "Field") {
       
   424             kind = 1;
       
   425         }
       
   426         if (holder.getName() == "Method") {
       
   427             kind = 2;
       
   428         }
       
   429         StringBuffer sb = new StringBuffer();
       
   430         for (int i = 0; flags != 0; i++, flags >>>= 1) {
       
   431             if ((flags & 1) != 0) {
       
   432                 if (sb.length() > 0) {
       
   433                     sb.append(' ');
       
   434                 }
       
   435                 if (i < modifierNames.length) {
       
   436                     String[] names = modifierNames[i];
       
   437                     String name = (kind < names.length) ? names[kind] : null;
       
   438                     for (String name2 : names) {
       
   439                         if (name != null) {
       
   440                             break;
       
   441                         }
       
   442                         name = name2;
       
   443                     }
       
   444                     sb.append(name);
       
   445                 } else {
       
   446                     sb.append("#").append(1 << i);
       
   447                 }
       
   448             }
       
   449         }
       
   450         return sb.toString();
       
   451     }
       
   452 
       
   453     private void readAttributesFor(Element x) throws IOException {
       
   454         Element prevCurrent;
       
   455         Element y = new Element();
       
   456         if (x.getName() == "Code") {
       
   457             prevCurrent = currentCode;
       
   458             currentCode = x;
       
   459         } else {
       
   460             prevCurrent = currentMember;
       
   461             currentMember = x;
       
   462         }
       
   463         for (int len = u2(), i = 0; i < len; i++) {
       
   464             int ref = u2();
       
   465             String uname = cpName(ref).intern();
       
   466             String refName = uname;
       
   467             if (!resolveRefs) {
       
   468                 refName = (REF_PREFIX + ref).intern();
       
   469             }
       
   470             String qname = (x.getName() + "." + uname).intern();
       
   471             String wname = ("*." + uname).intern();
       
   472             String type = attrTypes.get(qname);
       
   473             if (type == null || "".equals(type)) {
       
   474                 type = attrTypes.get(wname);
       
   475             }
       
   476             if ("".equals(type)) {
       
   477                 type = null;
       
   478             }
       
   479             int size = u4();
       
   480             int[] countVar = attrSizes.get(qname);
       
   481             if (countVar == null) {
       
   482                 attrSizes.put(qname, countVar = new int[2]);
       
   483             }
       
   484             countVar[0] += 1;
       
   485             countVar[1] += size;
       
   486             buf.reset();
       
   487             for (int j = 0; j < size; j++) {
       
   488                 buf.write(u1());
       
   489             }
       
   490             if (type == null && size == 0) {
       
   491                 y.add(new Element(uname)); // <Bridge>, etc.
       
   492             } else if (type == null) {
       
   493                 //System.out.println("Warning:  No attribute type description: "+qname);
       
   494                 // write cdata attribute
       
   495                 Element a = new Element("Attribute",
       
   496                         new String[]{"Name", refName},
       
   497                         buf.toString(EIGHT_BIT_CHAR_ENCODING));
       
   498                 a.addContent(getCPDigest());
       
   499                 y.add(a);
       
   500             } else if (type.equals("")) {
       
   501                 // ignore this attribute...
       
   502             } else {
       
   503                 InputStream in0 = in;
       
   504                 int fileSize0 = fileSize;
       
   505                 ByteArrayInputStream in1 = new ByteArrayInputStream(buf.toByteArray());
       
   506                 boolean ok = false;
       
   507                 try {
       
   508                     in = in1;
       
   509                     // parse according to type desc.
       
   510                     Element aval;
       
   511                     if (type.equals("<Code>...")) {
       
   512                         // delve into Code attribute
       
   513                         aval = readCode();
       
   514                     } else if (type.equals("<Frame>...")) {
       
   515                         // delve into StackMap attribute
       
   516                         aval = readStackMap(false);
       
   517                     } else if (type.equals("<FrameX>...")) {
       
   518                         // delve into StackMap attribute
       
   519                         aval = readStackMap(true);
       
   520                     } else if (type.startsWith("[")) {
       
   521                         aval = readAttributeCallables(type);
       
   522                     } else {
       
   523                         aval = readAttribute(type);
       
   524                     }
       
   525                     //System.out.println("attachTo 1 "+y+" <- "+aval);
       
   526                     attachTo(y, aval);
       
   527                     if (false
       
   528                             && in1.available() != 0) {
       
   529                         throw new RuntimeException("extra bytes in " + qname + " :" + in1.available());
       
   530                     }
       
   531                     ok = true;
       
   532                 } finally {
       
   533                     in = in0;
       
   534                     fileSize = fileSize0;
       
   535                     if (!ok) {
       
   536                         System.out.println("*** Failed to read " + type);
       
   537                     }
       
   538                 }
       
   539             }
       
   540         }
       
   541         if (x.getName() == "Code") {
       
   542             currentCode = prevCurrent;
       
   543         } else {
       
   544             currentMember = prevCurrent;
       
   545         }
       
   546         if (!keepOrder) {
       
   547             y.sort();
       
   548             y.sortAttrs();
       
   549         }
       
   550         //System.out.println("attachTo 2 "+x+" <- "+y);
       
   551         attachTo(x, y);
       
   552     }
       
   553     private int fileSize = 0;
       
   554     private int paddingSize = 0;
       
   555     private HashMap<String, int[]> attrSizes = new HashMap<String, int[]>();
       
   556 
       
   557     private Element formatAttrSizes() {
       
   558         Element e = new Element("Sizes");
       
   559         e.setAttr("fileSize", "" + fileSize);
       
   560         for (Map.Entry<String, int[]> ie : attrSizes.entrySet()) {
       
   561             int[] countVar = ie.getValue();
       
   562             e.add(new Element("AttrSize",
       
   563                     "name", ie.getKey().toString(),
       
   564                     "count", "" + countVar[0],
       
   565                     "size", "" + countVar[1]));
       
   566         }
       
   567         return e;
       
   568     }
       
   569 
       
   570     private void attachTo(Element x, Object aval0) {
       
   571         if (aval0 == null) {
       
   572             return;
       
   573         }
       
   574         //System.out.println("attachTo "+x+" : "+aval0);
       
   575         if (!(aval0 instanceof Element)) {
       
   576             x.add(aval0);
       
   577             return;
       
   578         }
       
   579         Element aval = (Element) aval0;
       
   580         if (!aval.isAnonymous()) {
       
   581             x.add(aval);
       
   582             return;
       
   583         }
       
   584         for (int imax = aval.attrSize(), i = 0; i < imax; i++) {
       
   585             //%%
       
   586             attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i));
       
   587         }
       
   588         x.addAll(aval);
       
   589     }
       
   590 
       
   591     private void attachAttrTo(Element x, String aname, String aval) {
       
   592         //System.out.println("attachAttrTo "+x+" : "+aname+"="+aval);
       
   593         String aval0 = x.getAttr(aname);
       
   594         if (aval0 != null) {
       
   595             aval = aval0 + " " + aval;
       
   596         }
       
   597         x.setAttr(aname, aval);
       
   598     }
       
   599 
       
   600     private Element readAttributeCallables(String type) throws IOException {
       
   601         assert (callables == null);
       
   602         callables = getBodies(type);
       
   603         Element res = readAttribute(callables[0]);
       
   604         callables = null;
       
   605         return res;
       
   606     }
       
   607 
       
   608     private Element readAttribute(String type) throws IOException {
       
   609         //System.out.println("readAttribute "+type);
       
   610         Element aval = new Element();
       
   611         String nextAttrName = null;
       
   612         for (int len = type.length(), next, i = 0; i < len; i = next) {
       
   613             String value;
       
   614             switch (type.charAt(i)) {
       
   615                 case '<':
       
   616                     assert (nextAttrName == null);
       
   617                     next = type.indexOf('>', ++i);
       
   618                     String form = type.substring(i, next++);
       
   619                     if (form.indexOf('=') < 0) {
       
   620                         //  elem_placement = '<' elemname '>'
       
   621                         assert (aval.attrSize() == 0);
       
   622                         assert (aval.isAnonymous());
       
   623                         aval.setName(form.intern());
       
   624                     } else {
       
   625                         //  attr_placement = '<' attrname '=' (value)? '>'
       
   626                         int eqPos = form.indexOf('=');
       
   627                         nextAttrName = form.substring(0, eqPos).intern();
       
   628                         if (eqPos != form.length() - 1) {
       
   629                             value = form.substring(eqPos + 1);
       
   630                             attachAttrTo(aval, nextAttrName, value);
       
   631                             nextAttrName = null;
       
   632                         }
       
   633                         // ...else subsequent type parsing will find the attr value
       
   634                         // and add it as "nextAttrName".
       
   635                     }
       
   636                     continue;
       
   637                 case '(':
       
   638                     next = type.indexOf(')', ++i);
       
   639                     int callee = Integer.parseInt(type.substring(i, next++));
       
   640                     attachTo(aval, readAttribute(callables[callee]));
       
   641                     continue;
       
   642                 case 'N': // replication = 'N' int '[' type ... ']'
       
   643                 {
       
   644                     int count = getInt(type.charAt(i + 1), false);
       
   645                     assert (count >= 0);
       
   646                     next = i + 2;
       
   647                     String type1 = getBody(type, next);
       
   648                     next += type1.length() + 2;  // skip body and brackets
       
   649                     for (int j = 0; j < count; j++) {
       
   650                         attachTo(aval, readAttribute(type1));
       
   651                     }
       
   652                 }
       
   653                 continue;
       
   654                 case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']'
       
   655                     int tagValue;
       
   656                     if (type.charAt(++i) == 'S') {
       
   657                         tagValue = getInt(type.charAt(++i), true);
       
   658                     } else {
       
   659                         tagValue = getInt(type.charAt(i), false);
       
   660                     }
       
   661                     attachAttrTo(aval, "tag", "" + tagValue);  // always named "tag"
       
   662                     ++i;  // skip the int type char
       
   663                     // union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']'
       
   664                     // uc_tag = ('-')? digit+
       
   665                     for (boolean foundCase = false;; i = next) {
       
   666                         assert (type.charAt(i) == '(');
       
   667                         next = type.indexOf(')', ++i);
       
   668                         assert (next >= i);
       
   669                         if (type.charAt(next - 1) == '\\'
       
   670                                 && type.charAt(next - 2) != '\\') // Skip an escaped paren.
       
   671                         {
       
   672                             next = type.indexOf(')', next + 1);
       
   673                         }
       
   674                         String caseStr = type.substring(i, next++);
       
   675                         String type1 = getBody(type, next);
       
   676                         next += type1.length() + 2;  // skip body and brackets
       
   677                         boolean lastCase = (caseStr.length() == 0);
       
   678                         if (!foundCase
       
   679                                 && (lastCase || matchTag(tagValue, caseStr))) {
       
   680                             foundCase = true;
       
   681                             // Execute this body.
       
   682                             attachTo(aval, readAttribute(type1));
       
   683                         }
       
   684                         if (lastCase) {
       
   685                             break;
       
   686                         }
       
   687                     }
       
   688                     continue;
       
   689                 case 'B':
       
   690                 case 'H':
       
   691                 case 'I': // int = oneof "BHI"
       
   692                     next = i + 1;
       
   693                     value = "" + getInt(type.charAt(i), false);
       
   694                     break;
       
   695                 case 'K':
       
   696                     assert ("IJFDLQ".indexOf(type.charAt(i + 1)) >= 0);
       
   697                     assert (type.charAt(i + 2) == 'H');  // only H works for now
       
   698                     next = i + 3;
       
   699                     value = cpRef();
       
   700                     break;
       
   701                 case 'R':
       
   702                     assert ("CSDFMIU?".indexOf(type.charAt(i + 1)) >= 0);
       
   703                     assert (type.charAt(i + 2) == 'H');  // only H works for now
       
   704                     next = i + 3;
       
   705                     value = cpRef();
       
   706                     break;
       
   707                 case 'P':  // bci = 'P' int
       
   708                     next = i + 2;
       
   709                     value = "" + getInt(type.charAt(i + 1), false);
       
   710                     break;
       
   711                 case 'S':  // signed_int = 'S' int
       
   712                     next = i + 2;
       
   713                     value = "" + getInt(type.charAt(i + 1), true);
       
   714                     break;
       
   715                 case 'F':
       
   716                     next = i + 2;
       
   717                     value = flagString(getInt(type.charAt(i + 1), false), currentMember);
       
   718                     break;
       
   719                 default:
       
   720                     throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type);
       
   721             }
       
   722             // store the value
       
   723             if (nextAttrName != null) {
       
   724                 attachAttrTo(aval, nextAttrName, value);
       
   725                 nextAttrName = null;
       
   726             } else {
       
   727                 attachTo(aval, value);
       
   728             }
       
   729         }
       
   730         //System.out.println("readAttribute => "+aval);
       
   731         assert (nextAttrName == null);
       
   732         return aval;
       
   733     }
       
   734 
       
   735     private int getInt(char ch, boolean signed) throws IOException {
       
   736         if (signed) {
       
   737             switch (ch) {
       
   738                 case 'B':
       
   739                     return (byte) u1();
       
   740                 case 'H':
       
   741                     return (short) u2();
       
   742                 case 'I':
       
   743                     return (int) u4();
       
   744             }
       
   745         } else {
       
   746             switch (ch) {
       
   747                 case 'B':
       
   748                     return u1();
       
   749                 case 'H':
       
   750                     return u2();
       
   751                 case 'I':
       
   752                     return u4();
       
   753             }
       
   754         }
       
   755         assert ("BHIJ".indexOf(ch) >= 0);
       
   756         return 0;
       
   757     }
       
   758 
       
   759     private Element readCode() throws IOException {
       
   760         int stack = u2();
       
   761         int local = u2();
       
   762         int length = u4();
       
   763         StringBuilder sb = new StringBuilder(length);
       
   764         for (int i = 0; i < length; i++) {
       
   765             sb.append((char) u1());
       
   766         }
       
   767         String bytecodes = sb.toString();
       
   768         Element e = new Element("Code",
       
   769                 "stack", "" + stack,
       
   770                 "local", "" + local);
       
   771         Element bytes = new Element("Bytes", (String[]) null, bytecodes);
       
   772         if (keepBytes) {
       
   773             e.add(bytes);
       
   774         }
       
   775         if (parseBytes) {
       
   776             e.add(parseByteCodes(bytecodes));
       
   777         }
       
   778         for (int len = u2(), i = 0; i < len; i++) {
       
   779             int start = u2();
       
   780             int end = u2();
       
   781             int catsh = u2();
       
   782             String clasz = cpRef();
       
   783             e.add(new Element("Handler",
       
   784                     "start", "" + start,
       
   785                     "end", "" + end,
       
   786                     "catch", "" + catsh,
       
   787                     "class", clasz));
       
   788         }
       
   789         readAttributesFor(e);
       
   790         e.trimToSize();
       
   791         return e;
       
   792     }
       
   793 
       
   794     private Element parseByteCodes(String bytecodes) {
       
   795         Element e = InstructionSyntax.parse(bytecodes);
       
   796         for (Element ins : e.elements()) {
       
   797             Number ref = ins.getAttrNumber("ref");
       
   798             if (ref != null && resolveRefs) {
       
   799                 int id = ref.intValue();
       
   800                 String val = cpName(id);
       
   801                 if (ins.getName().startsWith("ldc")) {
       
   802                     // Yuck:  Arb. string cannot be an XML attribute.
       
   803                     ins.add(val);
       
   804                     val = "";
       
   805                     byte tag = (id >= 0 && id < cpTag.length) ? cpTag[id] : 0;
       
   806                     if (tag != 0) {
       
   807                         ins.setAttrLong("tag", tag);
       
   808                     }
       
   809                 }
       
   810                 if (ins.getName() == "invokeinterface"
       
   811                         && computeInterfaceNum(val) == ins.getAttrLong("num")) {
       
   812                     ins.setAttr("num", null);  // garbage bytes
       
   813                 }
       
   814                 ins.setAttr("ref", null);
       
   815                 ins.setAttr("val", val);
       
   816             }
       
   817         }
       
   818         return e;
       
   819     }
       
   820 
       
   821     private Element readStackMap(boolean hasXOption) throws IOException {
       
   822         Element result = new Element();
       
   823         Element bytes = currentCode.findElement("Bytes");
       
   824         assert (bytes != null && bytes.size() == 1);
       
   825         int byteLength = ((String) bytes.get(0)).length();
       
   826         boolean uoffsetIsU4 = (byteLength >= (1 << 16));
       
   827         boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16);
       
   828         boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16);
       
   829         if (hasXOption || uoffsetIsU4 || ulocalvarIsU4 || ustackIsU4) {
       
   830             Element flags = new Element("StackMapFlags");
       
   831             if (hasXOption) {
       
   832                 flags.setAttr("hasXOption", "true");
       
   833             }
       
   834             if (uoffsetIsU4) {
       
   835                 flags.setAttr("uoffsetIsU4", "true");
       
   836             }
       
   837             if (ulocalvarIsU4) {
       
   838                 flags.setAttr("ulocalvarIsU4", "true");
       
   839             }
       
   840             if (ustackIsU4) {
       
   841                 flags.setAttr("ustackIsU4", "true");
       
   842             }
       
   843             currentCode.add(flags);
       
   844         }
       
   845         int frame_count = (uoffsetIsU4 ? u4() : u2());
       
   846         for (int i = 0; i < frame_count; i++) {
       
   847             int bci = (uoffsetIsU4 ? u4() : u2());
       
   848             int flags = (hasXOption ? u1() : 0);
       
   849             Element frame = new Element("Frame");
       
   850             result.add(frame);
       
   851             if (flags != 0) {
       
   852                 frame.setAttr("flags", "" + flags);
       
   853             }
       
   854             frame.setAttr("bci", "" + bci);
       
   855             // Scan local and stack types in this frame:
       
   856             final int LOCALS = 0, STACK = 1;
       
   857             for (int j = LOCALS; j <= STACK; j++) {
       
   858                 int typeSize;
       
   859                 if (j == LOCALS) {
       
   860                     typeSize = (ulocalvarIsU4 ? u4() : u2());
       
   861                 } else { // STACK
       
   862                     typeSize = (ustackIsU4 ? u4() : u2());
       
   863                 }
       
   864                 Element types = new Element(j == LOCALS ? "Local" : "Stack");
       
   865                 for (int k = 0; k < typeSize; k++) {
       
   866                     int tag = u1();
       
   867                     Element type = new Element(itemTagName(tag));
       
   868                     types.add(type);
       
   869                     switch (tag) {
       
   870                         case ITEM_Object:
       
   871                             type.setAttr("class", cpRef());
       
   872                             break;
       
   873                         case ITEM_Uninitialized:
       
   874                         case ITEM_ReturnAddress:
       
   875                             type.setAttr("bci", "" + (uoffsetIsU4 ? u4() : u2()));
       
   876                             break;
       
   877                     }
       
   878                 }
       
   879                 if (types.size() > 0) {
       
   880                     frame.add(types);
       
   881                 }
       
   882             }
       
   883         }
       
   884         return result;
       
   885     }
       
   886 
       
   887     private void readCP() throws IOException {
       
   888         int cpLen = u2();
       
   889         cpTag = new byte[cpLen];
       
   890         cpName = new String[cpLen];
       
   891         int cpTem[][] = new int[cpLen][];
       
   892         for (int i = 1; i < cpLen; i++) {
       
   893             cpTag[i] = (byte) u1();
       
   894             switch (cpTag[i]) {
       
   895                 case CONSTANT_Utf8:
       
   896                     buf.reset();
       
   897                     for (int len = u2(), j = 0; j < len; j++) {
       
   898                         buf.write(u1());
       
   899                     }
       
   900                     cpName[i] = buf.toString(UTF8_ENCODING);
       
   901                     break;
       
   902                 case CONSTANT_Integer:
       
   903                     cpName[i] = String.valueOf((int) u4());
       
   904                     break;
       
   905                 case CONSTANT_Float:
       
   906                     cpName[i] = String.valueOf(Float.intBitsToFloat(u4()));
       
   907                     break;
       
   908                 case CONSTANT_Long:
       
   909                     cpName[i] = String.valueOf(u8());
       
   910                     i += 1;
       
   911                     break;
       
   912                 case CONSTANT_Double:
       
   913                     cpName[i] = String.valueOf(Double.longBitsToDouble(u8()));
       
   914                     i += 1;
       
   915                     break;
       
   916                 case CONSTANT_Class:
       
   917                 case CONSTANT_String:
       
   918                     cpTem[i] = new int[]{u2()};
       
   919                     break;
       
   920                 case CONSTANT_Fieldref:
       
   921                 case CONSTANT_Methodref:
       
   922                 case CONSTANT_InterfaceMethodref:
       
   923                 case CONSTANT_NameAndType:
       
   924                     cpTem[i] = new int[]{u2(), u2()};
       
   925                     break;
       
   926             }
       
   927         }
       
   928         for (int i = 1; i < cpLen; i++) {
       
   929             switch (cpTag[i]) {
       
   930                 case CONSTANT_Class:
       
   931                 case CONSTANT_String:
       
   932                     cpName[i] = cpName[cpTem[i][0]];
       
   933                     break;
       
   934                 case CONSTANT_NameAndType:
       
   935                     cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]];
       
   936                     break;
       
   937             }
       
   938         }
       
   939         // do fieldref et al after nameandtype are all resolved
       
   940         for (int i = 1; i < cpLen; i++) {
       
   941             switch (cpTag[i]) {
       
   942                 case CONSTANT_Fieldref:
       
   943                 case CONSTANT_Methodref:
       
   944                 case CONSTANT_InterfaceMethodref:
       
   945                     cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]];
       
   946                     break;
       
   947             }
       
   948         }
       
   949         cpool = new Element("ConstantPool", cpName.length);
       
   950         for (int i = 0; i < cpName.length; i++) {
       
   951             if (cpName[i] == null) {
       
   952                 continue;
       
   953             }
       
   954             cpool.add(new Element(cpTagName(cpTag[i]),
       
   955                     new String[]{"id", "" + i},
       
   956                     cpName[i]));
       
   957         }
       
   958         if (keepCP) {
       
   959             cfile.add(cpool);
       
   960         }
       
   961     }
       
   962 
       
   963     private String cpRef() throws IOException {
       
   964         int ref = u2();
       
   965         if (resolveRefs) {
       
   966             return cpName(ref);
       
   967         } else {
       
   968             return REF_PREFIX + ref;
       
   969         }
       
   970     }
       
   971 
       
   972     private String cpName(int id) {
       
   973         if (id >= 0 && id < cpName.length) {
       
   974             return cpName[id];
       
   975         } else {
       
   976             return "[CP#" + Integer.toHexString(id) + "]";
       
   977         }
       
   978     }
       
   979 
       
   980     private long u8() throws IOException {
       
   981         return ((long) u4() << 32) + (((long) u4() << 32) >>> 32);
       
   982     }
       
   983 
       
   984     private int u4() throws IOException {
       
   985         return (u2() << 16) + u2();
       
   986     }
       
   987 
       
   988     private int u2() throws IOException {
       
   989         return (u1() << 8) + u1();
       
   990     }
       
   991 
       
   992     private int u1() throws IOException {
       
   993         int x = in.read();
       
   994         if (x < 0) {
       
   995             paddingSize++;
       
   996             return 0;  // error recovery
       
   997         }
       
   998         fileSize++;
       
   999         assert (x == (x & 0xFF));
       
  1000         return x;
       
  1001     }
       
  1002 }
       
  1003