2668N/A/*
2668N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2668N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2668N/A *
2668N/A * This code is free software; you can redistribute it and/or modify it
2668N/A * under the terms of the GNU General Public License version 2 only, as
2668N/A * published by the Free Software Foundation. Oracle designates this
2668N/A * particular file as subject to the "Classpath" exception as provided
2668N/A * by Oracle in the LICENSE file that accompanied this code.
2668N/A *
2668N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2668N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2668N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2668N/A * version 2 for more details (a copy is included in the LICENSE file that
2668N/A * accompanied this code).
2668N/A *
2668N/A * You should have received a copy of the GNU General Public License version
2668N/A * 2 along with this work; if not, write to the Free Software Foundation,
2668N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2668N/A *
2668N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2668N/A * or visit www.oracle.com if you need additional information or have any
2668N/A * questions.
2668N/A */
2668N/Apackage xmlkit; // -*- mode: java; indent-tabs-mode: nil -*-
2668N/A
2668N/Aimport java.util.*;
2668N/Aimport java.util.jar.*;
2668N/Aimport java.lang.reflect.*;
2668N/Aimport java.io.*;
2668N/Aimport xmlkit.XMLKit.Element;
2668N/A
2668N/A/*
2668N/A * @author jrose
2668N/A */
2668N/Apublic class ClassReader extends ClassSyntax {
2668N/A
2668N/A private static final CommandLineParser CLP = new CommandLineParser(""
2668N/A + "-source: +> = \n"
2668N/A + "-dest: +> = \n"
2668N/A + "-encoding: +> = \n"
2668N/A + "-jcov $ \n -nojcov !-jcov \n"
2668N/A + "-verbose $ \n -noverbose !-verbose \n"
2668N/A + "-pretty $ \n -nopretty !-pretty \n"
2668N/A + "-keepPath $ \n -nokeepPath !-keepPath \n"
2668N/A + "-keepCP $ \n -nokeepCP !-keepCP \n"
2668N/A + "-keepBytes $ \n -nokeepBytes !-keepBytes \n"
2668N/A + "-parseBytes $ \n -noparseBytes !-parseBytes \n"
2668N/A + "-resolveRefs $ \n -noresolveRefs !-resolveRefs \n"
2668N/A + "-keepOrder $ \n -nokeepOrder !-keepOrder \n"
2668N/A + "-keepSizes $ \n -nokeepSizes !-keepSizes \n"
2668N/A + "-continue $ \n -nocontinue !-continue \n"
2668N/A + "-attrDef & \n"
2668N/A + "-@ >-@ . \n"
2668N/A + "- +? \n"
2668N/A + "\n");
2668N/A
2668N/A public static void main(String[] ava) throws IOException {
2668N/A ArrayList<String> av = new ArrayList<String>(Arrays.asList(ava));
2668N/A HashMap<String, String> props = new HashMap<String, String>();
2668N/A props.put("-encoding:", "UTF8"); // default
2668N/A props.put("-keepOrder", null); // CLI default
2668N/A props.put("-pretty", "1"); // CLI default
2668N/A props.put("-continue", "1"); // CLI default
2668N/A CLP.parse(av, props);
2668N/A //System.out.println(props+" ++ "+av);
2668N/A File source = asFile(props.get("-source:"));
2668N/A File dest = asFile(props.get("-dest:"));
2668N/A String encoding = props.get("-encoding:");
2668N/A boolean contError = props.containsKey("-continue");
2668N/A ClassReader options = new ClassReader();
2668N/A options.copyOptionsFrom(props);
2668N/A /*
2668N/A if (dest == null && av.size() > 1) {
2668N/A dest = File.createTempFile("TestOut", ".dir", new File("."));
2668N/A dest.delete();
2668N/A if (!dest.mkdir())
2668N/A throw new RuntimeException("Cannot create "+dest);
2668N/A System.out.println("Writing results to "+dest);
2668N/A }
2668N/A */
2668N/A if (av.isEmpty()) {
2668N/A av.add("doit"); //to enter this loop
2668N/A }
2668N/A boolean readList = false;
2668N/A for (String a : av) {
2668N/A if (readList) {
2668N/A readList = false;
2668N/A InputStream fin;
2668N/A if (a.equals("-")) {
2668N/A fin = System.in;
2668N/A } else {
2668N/A fin = new FileInputStream(a);
2668N/A }
2668N/A
2668N/A BufferedReader files = makeReader(fin, encoding);
2668N/A for (String file; (file = files.readLine()) != null;) {
2668N/A doFile(file, source, dest, options, encoding, contError);
2668N/A }
2668N/A if (fin != System.in) {
2668N/A fin.close();
2668N/A }
2668N/A } else if (a.equals("-@")) {
2668N/A readList = true;
2668N/A } else if (a.startsWith("-")) {
2668N/A throw new RuntimeException("Bad flag argument: " + a);
2668N/A } else if (source.getName().endsWith(".jar")) {
2668N/A doJar(a, source, dest, options, encoding, contError);
2668N/A } else {
2668N/A doFile(a, source, dest, options, encoding, contError);
2668N/A }
2668N/A }
2668N/A }
2668N/A
2668N/A private static File asFile(String str) {
2668N/A return (str == null) ? null : new File(str);
2668N/A }
2668N/A
2668N/A private static void doFile(String a,
2668N/A File source, File dest,
2668N/A ClassReader options, String encoding,
2668N/A boolean contError) throws IOException {
2668N/A if (!contError) {
2668N/A doFile(a, source, dest, options, encoding);
2668N/A } else {
2668N/A try {
2668N/A doFile(a, source, dest, options, encoding);
2668N/A } catch (Exception ee) {
2668N/A System.out.println("Error processing " + source + ": " + ee);
2668N/A }
2668N/A }
2668N/A }
2668N/A
2668N/A private static void doJar(String a, File source, File dest, ClassReader options,
2668N/A String encoding, Boolean contError) throws IOException {
2668N/A try {
2668N/A JarFile jf = new JarFile(source);
2668N/A for (JarEntry je : Collections.list((Enumeration<JarEntry>) jf.entries())) {
2668N/A String name = je.getName();
2668N/A if (!name.endsWith(".class")) {
2668N/A continue;
2668N/A }
2668N/A doStream(name, jf.getInputStream(je), dest, options, encoding);
2668N/A }
2668N/A } catch (IOException ioe) {
2668N/A if (contError) {
2668N/A System.out.println("Error processing " + source + ": " + ioe);
2668N/A } else {
2668N/A throw ioe;
2668N/A }
2668N/A }
2668N/A }
2668N/A
2668N/A private static void doStream(String a, InputStream in, File dest,
2668N/A ClassReader options, String encoding) throws IOException {
2668N/A
2668N/A File f = new File(a);
2668N/A ClassReader cr = new ClassReader(options);
2668N/A Element e = cr.readFrom(in);
2668N/A
2668N/A OutputStream out;
2668N/A if (dest == null) {
2668N/A //System.out.println(e.prettyString());
2668N/A out = System.out;
2668N/A } else {
2668N/A File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath());
2668N/A String outName = outf.getName();
2668N/A File outSubdir = outf.getParentFile();
2668N/A outSubdir.mkdirs();
2668N/A int extPos = outName.lastIndexOf('.');
2668N/A if (extPos > 0) {
2668N/A outf = new File(outSubdir, outName.substring(0, extPos) + ".xml");
2668N/A }
2668N/A out = new FileOutputStream(outf);
2668N/A }
2668N/A
2668N/A Writer outw = makeWriter(out, encoding);
2668N/A if (options.pretty || !options.keepOrder) {
2668N/A e.writePrettyTo(outw);
2668N/A } else {
2668N/A e.writeTo(outw);
2668N/A }
2668N/A if (out == System.out) {
2668N/A outw.write("\n");
2668N/A outw.flush();
2668N/A } else {
2668N/A outw.close();
2668N/A }
2668N/A }
2668N/A
2668N/A private static void doFile(String a,
2668N/A File source, File dest,
2668N/A ClassReader options, String encoding) throws IOException {
2668N/A File inf = new File(source, a);
2668N/A if (dest != null && options.verbose) {
2668N/A System.out.println("Reading " + inf);
2668N/A }
2668N/A
2668N/A BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf));
2668N/A
2668N/A doStream(a, in, dest, options, encoding);
2668N/A
2668N/A }
2668N/A
2668N/A public static BufferedReader makeReader(InputStream in, String encoding) throws IOException {
2668N/A // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
2668N/A if (encoding.equals("8BIT")) {
2668N/A encoding = EIGHT_BIT_CHAR_ENCODING;
2668N/A }
2668N/A if (encoding.equals("UTF8")) {
2668N/A encoding = UTF8_ENCODING;
2668N/A }
2668N/A if (encoding.equals("DEFAULT")) {
2668N/A encoding = null;
2668N/A }
2668N/A if (encoding.equals("-")) {
2668N/A encoding = null;
2668N/A }
2668N/A Reader inw;
2668N/A in = new BufferedInputStream(in); // add buffering
2668N/A if (encoding == null) {
2668N/A inw = new InputStreamReader(in);
2668N/A } else {
2668N/A inw = new InputStreamReader(in, encoding);
2668N/A }
2668N/A return new BufferedReader(inw); // add buffering
2668N/A }
2668N/A
2668N/A public static Writer makeWriter(OutputStream out, String encoding) throws IOException {
2668N/A // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
2668N/A if (encoding.equals("8BIT")) {
2668N/A encoding = EIGHT_BIT_CHAR_ENCODING;
2668N/A }
2668N/A if (encoding.equals("UTF8")) {
2668N/A encoding = UTF8_ENCODING;
2668N/A }
2668N/A if (encoding.equals("DEFAULT")) {
2668N/A encoding = null;
2668N/A }
2668N/A if (encoding.equals("-")) {
2668N/A encoding = null;
2668N/A }
2668N/A Writer outw;
2668N/A if (encoding == null) {
2668N/A outw = new OutputStreamWriter(out);
2668N/A } else {
2668N/A outw = new OutputStreamWriter(out, encoding);
2668N/A }
2668N/A return new BufferedWriter(outw); // add buffering
2668N/A }
2668N/A
2668N/A public Element result() {
2668N/A return cfile;
2668N/A }
2668N/A protected InputStream in;
2668N/A protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024);
2668N/A protected byte cpTag[];
2668N/A protected String cpName[];
2668N/A protected String[] callables; // varies
2668N/A public static final String REF_PREFIX = "#";
2668N/A // input options
2668N/A public boolean pretty = false;
2668N/A public boolean verbose = false;
2668N/A public boolean keepPath = false;
2668N/A public boolean keepCP = false;
2668N/A public boolean keepBytes = false;
2668N/A public boolean parseBytes = true;
2668N/A public boolean resolveRefs = true;
2668N/A public boolean keepOrder = true;
2668N/A public boolean keepSizes = false;
2668N/A
2668N/A public ClassReader() {
2668N/A super.cfile = new Element("ClassFile");
2668N/A }
2668N/A
2668N/A public ClassReader(ClassReader options) {
2668N/A this();
2668N/A copyOptionsFrom(options);
2668N/A }
2668N/A
2668N/A public void copyOptionsFrom(ClassReader options) {
2668N/A pretty = options.pretty;
2668N/A verbose = options.verbose;
2668N/A keepPath = options.keepPath;
2668N/A keepCP = options.keepCP;
2668N/A keepBytes = options.keepBytes;
2668N/A parseBytes = options.parseBytes;
2668N/A resolveRefs = options.resolveRefs;
2668N/A keepSizes = options.keepSizes;
2668N/A keepOrder = options.keepOrder;
2668N/A attrTypes = options.attrTypes;
2668N/A }
2668N/A
2668N/A public void copyOptionsFrom(Map<String, String> options) {
2668N/A if (options.containsKey("-pretty")) {
2668N/A pretty = (options.get("-pretty") != null);
2668N/A }
2668N/A if (options.containsKey("-verbose")) {
2668N/A verbose = (options.get("-verbose") != null);
2668N/A }
2668N/A if (options.containsKey("-keepPath")) {
2668N/A keepPath = (options.get("-keepPath") != null);
2668N/A }
2668N/A if (options.containsKey("-keepCP")) {
2668N/A keepCP = (options.get("-keepCP") != null);
2668N/A }
2668N/A if (options.containsKey("-keepBytes")) {
2668N/A keepBytes = (options.get("-keepBytes") != null);
2668N/A }
2668N/A if (options.containsKey("-parseBytes")) {
2668N/A parseBytes = (options.get("-parseBytes") != null);
2668N/A }
2668N/A if (options.containsKey("-resolveRefs")) {
2668N/A resolveRefs = (options.get("-resolveRefs") != null);
2668N/A }
2668N/A if (options.containsKey("-keepSizes")) {
2668N/A keepSizes = (options.get("-keepSizes") != null);
2668N/A }
2668N/A if (options.containsKey("-keepOrder")) {
2668N/A keepOrder = (options.get("-keepOrder") != null);
2668N/A }
2668N/A if (options.containsKey("-attrDef")) {
2668N/A addAttrTypes(options.get("-attrDef").split(" "));
2668N/A }
2668N/A if (options.get("-jcov") != null) {
2668N/A addJcovAttrTypes();
2668N/A }
2668N/A }
2668N/A
2668N/A public Element readFrom(InputStream in) throws IOException {
2668N/A this.in = in;
2668N/A // read the file header
2668N/A int magic = u4();
2668N/A if (magic != 0xCAFEBABE) {
2668N/A throw new RuntimeException("bad magic number " + Integer.toHexString(magic));
2668N/A }
2668N/A cfile.setAttr("magic", "" + magic);
2668N/A int minver = u2();
2668N/A int majver = u2();
2668N/A cfile.setAttr("minver", "" + minver);
2668N/A cfile.setAttr("majver", "" + majver);
2668N/A readCP();
2668N/A readClass();
2668N/A return result();
2668N/A }
2668N/A
2668N/A public Element readFrom(File file) throws IOException {
2668N/A InputStream in = null;
2668N/A try {
2668N/A in = new FileInputStream(file);
2668N/A Element e = readFrom(new BufferedInputStream(in));
2668N/A if (keepPath) {
2668N/A e.setAttr("path", file.toString());
2668N/A }
2668N/A return e;
2668N/A } finally {
2668N/A if (in != null) {
2668N/A in.close();
2668N/A }
2668N/A }
2668N/A }
2668N/A
2668N/A private void readClass() throws IOException {
2668N/A klass = new Element("Class");
2668N/A cfile.add(klass);
2668N/A int flags = u2();
2668N/A String thisk = cpRef();
2668N/A String superk = cpRef();
2668N/A klass.setAttr("name", thisk);
2668N/A boolean flagsSync = ((flags & Modifier.SYNCHRONIZED) != 0);
2668N/A flags &= ~Modifier.SYNCHRONIZED;
2668N/A String flagString = flagString(flags, klass);
2668N/A if (!flagsSync) {
2668N/A if (flagString.length() > 0) {
2668N/A flagString += " ";
2668N/A }
2668N/A flagString += "!synchronized";
2668N/A }
2668N/A klass.setAttr("flags", flagString);
2668N/A klass.setAttr("super", superk);
2668N/A for (int len = u2(), i = 0; i < len; i++) {
2668N/A String interk = cpRef();
2668N/A klass.add(new Element("Interface", "name", interk));
2668N/A }
2668N/A Element fields = readMembers("Field");
2668N/A klass.addAll(fields);
2668N/A Element methods = readMembers("Method");
2668N/A if (!keepOrder) {
2668N/A methods.sort();
2668N/A }
2668N/A klass.addAll(methods);
2668N/A readAttributesFor(klass);
2668N/A klass.trimToSize();
2668N/A if (keepSizes) {
2668N/A attachTo(cfile, formatAttrSizes());
2668N/A }
2668N/A if (paddingSize != 0) {
2668N/A cfile.setAttr("padding", "" + paddingSize);
2668N/A }
2668N/A }
2668N/A
2668N/A private Element readMembers(String kind) throws IOException {
2668N/A int len = u2();
2668N/A Element members = new Element(len);
2668N/A for (int i = 0; i < len; i++) {
2668N/A Element member = new Element(kind);
2668N/A int flags = u2();
2668N/A String name = cpRef();
2668N/A String type = cpRef();
2668N/A member.setAttr("name", name);
2668N/A member.setAttr("type", type);
2668N/A member.setAttr("flags", flagString(flags, member));
2668N/A readAttributesFor(member);
2668N/A member.trimToSize();
2668N/A members.add(member);
2668N/A }
2668N/A return members;
2668N/A }
2668N/A
2668N/A protected String flagString(int flags, Element holder) {
2668N/A // Superset of Modifier.toString.
2668N/A int kind = 0;
2668N/A if (holder.getName() == "Field") {
2668N/A kind = 1;
2668N/A }
2668N/A if (holder.getName() == "Method") {
2668N/A kind = 2;
2668N/A }
2668N/A StringBuffer sb = new StringBuffer();
2668N/A for (int i = 0; flags != 0; i++, flags >>>= 1) {
2668N/A if ((flags & 1) != 0) {
2668N/A if (sb.length() > 0) {
2668N/A sb.append(' ');
2668N/A }
2668N/A if (i < modifierNames.length) {
2668N/A String[] names = modifierNames[i];
2668N/A String name = (kind < names.length) ? names[kind] : null;
2668N/A for (String name2 : names) {
2668N/A if (name != null) {
2668N/A break;
2668N/A }
2668N/A name = name2;
2668N/A }
2668N/A sb.append(name);
2668N/A } else {
2668N/A sb.append("#").append(1 << i);
2668N/A }
2668N/A }
2668N/A }
2668N/A return sb.toString();
2668N/A }
2668N/A
2668N/A private void readAttributesFor(Element x) throws IOException {
2668N/A Element prevCurrent;
2668N/A Element y = new Element();
2668N/A if (x.getName() == "Code") {
2668N/A prevCurrent = currentCode;
2668N/A currentCode = x;
2668N/A } else {
2668N/A prevCurrent = currentMember;
2668N/A currentMember = x;
2668N/A }
2668N/A for (int len = u2(), i = 0; i < len; i++) {
2668N/A int ref = u2();
2668N/A String uname = cpName(ref).intern();
2668N/A String refName = uname;
2668N/A if (!resolveRefs) {
2668N/A refName = (REF_PREFIX + ref).intern();
2668N/A }
2668N/A String qname = (x.getName() + "." + uname).intern();
2668N/A String wname = ("*." + uname).intern();
2668N/A String type = attrTypes.get(qname);
2668N/A if (type == null || "".equals(type)) {
2668N/A type = attrTypes.get(wname);
2668N/A }
2668N/A if ("".equals(type)) {
2668N/A type = null;
2668N/A }
2668N/A int size = u4();
2668N/A int[] countVar = attrSizes.get(qname);
2668N/A if (countVar == null) {
2668N/A attrSizes.put(qname, countVar = new int[2]);
2668N/A }
2668N/A countVar[0] += 1;
2668N/A countVar[1] += size;
2668N/A buf.reset();
2668N/A for (int j = 0; j < size; j++) {
2668N/A buf.write(u1());
2668N/A }
2668N/A if (type == null && size == 0) {
2668N/A y.add(new Element(uname)); // <Bridge>, etc.
2668N/A } else if (type == null) {
2668N/A //System.out.println("Warning: No attribute type description: "+qname);
2668N/A // write cdata attribute
2668N/A Element a = new Element("Attribute",
2668N/A new String[]{"Name", refName},
2668N/A buf.toString(EIGHT_BIT_CHAR_ENCODING));
2668N/A a.addContent(getCPDigest());
2668N/A y.add(a);
2668N/A } else if (type.equals("")) {
2668N/A // ignore this attribute...
2668N/A } else {
2668N/A InputStream in0 = in;
2668N/A int fileSize0 = fileSize;
2668N/A ByteArrayInputStream in1 = new ByteArrayInputStream(buf.toByteArray());
2668N/A boolean ok = false;
2668N/A try {
2668N/A in = in1;
2668N/A // parse according to type desc.
2668N/A Element aval;
2668N/A if (type.equals("<Code>...")) {
2668N/A // delve into Code attribute
2668N/A aval = readCode();
2668N/A } else if (type.equals("<Frame>...")) {
2668N/A // delve into StackMap attribute
2668N/A aval = readStackMap(false);
2668N/A } else if (type.equals("<FrameX>...")) {
2668N/A // delve into StackMap attribute
2668N/A aval = readStackMap(true);
2668N/A } else if (type.startsWith("[")) {
2668N/A aval = readAttributeCallables(type);
2668N/A } else {
2668N/A aval = readAttribute(type);
2668N/A }
2668N/A //System.out.println("attachTo 1 "+y+" <- "+aval);
2668N/A attachTo(y, aval);
2668N/A if (false
2668N/A && in1.available() != 0) {
2668N/A throw new RuntimeException("extra bytes in " + qname + " :" + in1.available());
2668N/A }
2668N/A ok = true;
2668N/A } finally {
2668N/A in = in0;
2668N/A fileSize = fileSize0;
2668N/A if (!ok) {
2668N/A System.out.println("*** Failed to read " + type);
2668N/A }
2668N/A }
2668N/A }
2668N/A }
2668N/A if (x.getName() == "Code") {
2668N/A currentCode = prevCurrent;
2668N/A } else {
2668N/A currentMember = prevCurrent;
2668N/A }
2668N/A if (!keepOrder) {
2668N/A y.sort();
2668N/A y.sortAttrs();
2668N/A }
2668N/A //System.out.println("attachTo 2 "+x+" <- "+y);
2668N/A attachTo(x, y);
2668N/A }
2668N/A private int fileSize = 0;
2668N/A private int paddingSize = 0;
2668N/A private HashMap<String, int[]> attrSizes = new HashMap<String, int[]>();
2668N/A
2668N/A private Element formatAttrSizes() {
2668N/A Element e = new Element("Sizes");
2668N/A e.setAttr("fileSize", "" + fileSize);
2668N/A for (Map.Entry<String, int[]> ie : attrSizes.entrySet()) {
2668N/A int[] countVar = ie.getValue();
2668N/A e.add(new Element("AttrSize",
2668N/A "name", ie.getKey().toString(),
2668N/A "count", "" + countVar[0],
2668N/A "size", "" + countVar[1]));
2668N/A }
2668N/A return e;
2668N/A }
2668N/A
2668N/A private void attachTo(Element x, Object aval0) {
2668N/A if (aval0 == null) {
2668N/A return;
2668N/A }
2668N/A //System.out.println("attachTo "+x+" : "+aval0);
2668N/A if (!(aval0 instanceof Element)) {
2668N/A x.add(aval0);
2668N/A return;
2668N/A }
2668N/A Element aval = (Element) aval0;
2668N/A if (!aval.isAnonymous()) {
2668N/A x.add(aval);
2668N/A return;
2668N/A }
2668N/A for (int imax = aval.attrSize(), i = 0; i < imax; i++) {
2668N/A //%%
2668N/A attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i));
2668N/A }
2668N/A x.addAll(aval);
2668N/A }
2668N/A
2668N/A private void attachAttrTo(Element x, String aname, String aval) {
2668N/A //System.out.println("attachAttrTo "+x+" : "+aname+"="+aval);
2668N/A String aval0 = x.getAttr(aname);
2668N/A if (aval0 != null) {
2668N/A aval = aval0 + " " + aval;
2668N/A }
2668N/A x.setAttr(aname, aval);
2668N/A }
2668N/A
2668N/A private Element readAttributeCallables(String type) throws IOException {
2668N/A assert (callables == null);
2668N/A callables = getBodies(type);
2668N/A Element res = readAttribute(callables[0]);
2668N/A callables = null;
2668N/A return res;
2668N/A }
2668N/A
2668N/A private Element readAttribute(String type) throws IOException {
2668N/A //System.out.println("readAttribute "+type);
2668N/A Element aval = new Element();
2668N/A String nextAttrName = null;
2668N/A for (int len = type.length(), next, i = 0; i < len; i = next) {
2668N/A String value;
2668N/A switch (type.charAt(i)) {
2668N/A case '<':
2668N/A assert (nextAttrName == null);
2668N/A next = type.indexOf('>', ++i);
2668N/A String form = type.substring(i, next++);
2668N/A if (form.indexOf('=') < 0) {
2668N/A // elem_placement = '<' elemname '>'
2668N/A assert (aval.attrSize() == 0);
2668N/A assert (aval.isAnonymous());
2668N/A aval.setName(form.intern());
2668N/A } else {
2668N/A // attr_placement = '<' attrname '=' (value)? '>'
2668N/A int eqPos = form.indexOf('=');
2668N/A nextAttrName = form.substring(0, eqPos).intern();
2668N/A if (eqPos != form.length() - 1) {
2668N/A value = form.substring(eqPos + 1);
2668N/A attachAttrTo(aval, nextAttrName, value);
2668N/A nextAttrName = null;
2668N/A }
2668N/A // ...else subsequent type parsing will find the attr value
2668N/A // and add it as "nextAttrName".
2668N/A }
2668N/A continue;
2668N/A case '(':
2668N/A next = type.indexOf(')', ++i);
2668N/A int callee = Integer.parseInt(type.substring(i, next++));
2668N/A attachTo(aval, readAttribute(callables[callee]));
2668N/A continue;
2668N/A case 'N': // replication = 'N' int '[' type ... ']'
2668N/A {
2668N/A int count = getInt(type.charAt(i + 1), false);
2668N/A assert (count >= 0);
2668N/A next = i + 2;
2668N/A String type1 = getBody(type, next);
2668N/A next += type1.length() + 2; // skip body and brackets
2668N/A for (int j = 0; j < count; j++) {
2668N/A attachTo(aval, readAttribute(type1));
2668N/A }
2668N/A }
2668N/A continue;
2668N/A case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']'
2668N/A int tagValue;
2668N/A if (type.charAt(++i) == 'S') {
2668N/A tagValue = getInt(type.charAt(++i), true);
2668N/A } else {
2668N/A tagValue = getInt(type.charAt(i), false);
2668N/A }
2668N/A attachAttrTo(aval, "tag", "" + tagValue); // always named "tag"
2668N/A ++i; // skip the int type char
2668N/A // union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']'
2668N/A // uc_tag = ('-')? digit+
2668N/A for (boolean foundCase = false;; i = next) {
2668N/A assert (type.charAt(i) == '(');
2668N/A next = type.indexOf(')', ++i);
2668N/A assert (next >= i);
2668N/A if (type.charAt(next - 1) == '\\'
2668N/A && type.charAt(next - 2) != '\\') // Skip an escaped paren.
2668N/A {
2668N/A next = type.indexOf(')', next + 1);
2668N/A }
2668N/A String caseStr = type.substring(i, next++);
2668N/A String type1 = getBody(type, next);
2668N/A next += type1.length() + 2; // skip body and brackets
2668N/A boolean lastCase = (caseStr.length() == 0);
2668N/A if (!foundCase
2668N/A && (lastCase || matchTag(tagValue, caseStr))) {
2668N/A foundCase = true;
2668N/A // Execute this body.
2668N/A attachTo(aval, readAttribute(type1));
2668N/A }
2668N/A if (lastCase) {
2668N/A break;
2668N/A }
2668N/A }
2668N/A continue;
2668N/A case 'B':
2668N/A case 'H':
2668N/A case 'I': // int = oneof "BHI"
2668N/A next = i + 1;
2668N/A value = "" + getInt(type.charAt(i), false);
2668N/A break;
2668N/A case 'K':
2668N/A assert ("IJFDLQ".indexOf(type.charAt(i + 1)) >= 0);
2668N/A assert (type.charAt(i + 2) == 'H'); // only H works for now
2668N/A next = i + 3;
2668N/A value = cpRef();
2668N/A break;
2668N/A case 'R':
2668N/A assert ("CSDFMIU?".indexOf(type.charAt(i + 1)) >= 0);
2668N/A assert (type.charAt(i + 2) == 'H'); // only H works for now
2668N/A next = i + 3;
2668N/A value = cpRef();
2668N/A break;
2668N/A case 'P': // bci = 'P' int
2668N/A next = i + 2;
2668N/A value = "" + getInt(type.charAt(i + 1), false);
2668N/A break;
2668N/A case 'S': // signed_int = 'S' int
2668N/A next = i + 2;
2668N/A value = "" + getInt(type.charAt(i + 1), true);
2668N/A break;
2668N/A case 'F':
2668N/A next = i + 2;
2668N/A value = flagString(getInt(type.charAt(i + 1), false), currentMember);
2668N/A break;
2668N/A default:
2668N/A throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type);
2668N/A }
2668N/A // store the value
2668N/A if (nextAttrName != null) {
2668N/A attachAttrTo(aval, nextAttrName, value);
2668N/A nextAttrName = null;
2668N/A } else {
2668N/A attachTo(aval, value);
2668N/A }
2668N/A }
2668N/A //System.out.println("readAttribute => "+aval);
2668N/A assert (nextAttrName == null);
2668N/A return aval;
2668N/A }
2668N/A
2668N/A private int getInt(char ch, boolean signed) throws IOException {
2668N/A if (signed) {
2668N/A switch (ch) {
2668N/A case 'B':
2668N/A return (byte) u1();
2668N/A case 'H':
2668N/A return (short) u2();
2668N/A case 'I':
2668N/A return (int) u4();
2668N/A }
2668N/A } else {
2668N/A switch (ch) {
2668N/A case 'B':
2668N/A return u1();
2668N/A case 'H':
2668N/A return u2();
2668N/A case 'I':
2668N/A return u4();
2668N/A }
2668N/A }
2668N/A assert ("BHIJ".indexOf(ch) >= 0);
2668N/A return 0;
2668N/A }
2668N/A
2668N/A private Element readCode() throws IOException {
2668N/A int stack = u2();
2668N/A int local = u2();
2668N/A int length = u4();
2668N/A StringBuilder sb = new StringBuilder(length);
2668N/A for (int i = 0; i < length; i++) {
2668N/A sb.append((char) u1());
2668N/A }
2668N/A String bytecodes = sb.toString();
2668N/A Element e = new Element("Code",
2668N/A "stack", "" + stack,
2668N/A "local", "" + local);
2668N/A Element bytes = new Element("Bytes", (String[]) null, bytecodes);
2668N/A if (keepBytes) {
2668N/A e.add(bytes);
2668N/A }
2668N/A if (parseBytes) {
2668N/A e.add(parseByteCodes(bytecodes));
2668N/A }
2668N/A for (int len = u2(), i = 0; i < len; i++) {
2668N/A int start = u2();
2668N/A int end = u2();
2668N/A int catsh = u2();
2668N/A String clasz = cpRef();
2668N/A e.add(new Element("Handler",
2668N/A "start", "" + start,
2668N/A "end", "" + end,
2668N/A "catch", "" + catsh,
2668N/A "class", clasz));
2668N/A }
2668N/A readAttributesFor(e);
2668N/A e.trimToSize();
2668N/A return e;
2668N/A }
2668N/A
2668N/A private Element parseByteCodes(String bytecodes) {
2668N/A Element e = InstructionSyntax.parse(bytecodes);
2668N/A for (Element ins : e.elements()) {
2668N/A Number ref = ins.getAttrNumber("ref");
2668N/A if (ref != null && resolveRefs) {
2668N/A int id = ref.intValue();
2668N/A String val = cpName(id);
2668N/A if (ins.getName().startsWith("ldc")) {
2668N/A // Yuck: Arb. string cannot be an XML attribute.
2668N/A ins.add(val);
2668N/A val = "";
2668N/A byte tag = (id >= 0 && id < cpTag.length) ? cpTag[id] : 0;
2668N/A if (tag != 0) {
2668N/A ins.setAttrLong("tag", tag);
2668N/A }
2668N/A }
2668N/A if (ins.getName() == "invokeinterface"
2668N/A && computeInterfaceNum(val) == ins.getAttrLong("num")) {
2668N/A ins.setAttr("num", null); // garbage bytes
2668N/A }
2668N/A ins.setAttr("ref", null);
2668N/A ins.setAttr("val", val);
2668N/A }
2668N/A }
2668N/A return e;
2668N/A }
2668N/A
2668N/A private Element readStackMap(boolean hasXOption) throws IOException {
2668N/A Element result = new Element();
2668N/A Element bytes = currentCode.findElement("Bytes");
2668N/A assert (bytes != null && bytes.size() == 1);
2668N/A int byteLength = ((String) bytes.get(0)).length();
2668N/A boolean uoffsetIsU4 = (byteLength >= (1 << 16));
2668N/A boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16);
2668N/A boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16);
2668N/A if (hasXOption || uoffsetIsU4 || ulocalvarIsU4 || ustackIsU4) {
2668N/A Element flags = new Element("StackMapFlags");
2668N/A if (hasXOption) {
2668N/A flags.setAttr("hasXOption", "true");
2668N/A }
2668N/A if (uoffsetIsU4) {
2668N/A flags.setAttr("uoffsetIsU4", "true");
2668N/A }
2668N/A if (ulocalvarIsU4) {
2668N/A flags.setAttr("ulocalvarIsU4", "true");
2668N/A }
2668N/A if (ustackIsU4) {
2668N/A flags.setAttr("ustackIsU4", "true");
2668N/A }
2668N/A currentCode.add(flags);
2668N/A }
2668N/A int frame_count = (uoffsetIsU4 ? u4() : u2());
2668N/A for (int i = 0; i < frame_count; i++) {
2668N/A int bci = (uoffsetIsU4 ? u4() : u2());
2668N/A int flags = (hasXOption ? u1() : 0);
2668N/A Element frame = new Element("Frame");
2668N/A result.add(frame);
2668N/A if (flags != 0) {
2668N/A frame.setAttr("flags", "" + flags);
2668N/A }
2668N/A frame.setAttr("bci", "" + bci);
2668N/A // Scan local and stack types in this frame:
2668N/A final int LOCALS = 0, STACK = 1;
2668N/A for (int j = LOCALS; j <= STACK; j++) {
2668N/A int typeSize;
2668N/A if (j == LOCALS) {
2668N/A typeSize = (ulocalvarIsU4 ? u4() : u2());
2668N/A } else { // STACK
2668N/A typeSize = (ustackIsU4 ? u4() : u2());
2668N/A }
2668N/A Element types = new Element(j == LOCALS ? "Local" : "Stack");
2668N/A for (int k = 0; k < typeSize; k++) {
2668N/A int tag = u1();
2668N/A Element type = new Element(itemTagName(tag));
2668N/A types.add(type);
2668N/A switch (tag) {
2668N/A case ITEM_Object:
2668N/A type.setAttr("class", cpRef());
2668N/A break;
2668N/A case ITEM_Uninitialized:
2668N/A case ITEM_ReturnAddress:
2668N/A type.setAttr("bci", "" + (uoffsetIsU4 ? u4() : u2()));
2668N/A break;
2668N/A }
2668N/A }
2668N/A if (types.size() > 0) {
2668N/A frame.add(types);
2668N/A }
2668N/A }
2668N/A }
2668N/A return result;
2668N/A }
2668N/A
2668N/A private void readCP() throws IOException {
2668N/A int cpLen = u2();
2668N/A cpTag = new byte[cpLen];
2668N/A cpName = new String[cpLen];
2668N/A int cpTem[][] = new int[cpLen][];
2668N/A for (int i = 1; i < cpLen; i++) {
2668N/A cpTag[i] = (byte) u1();
2668N/A switch (cpTag[i]) {
2668N/A case CONSTANT_Utf8:
2668N/A buf.reset();
2668N/A for (int len = u2(), j = 0; j < len; j++) {
2668N/A buf.write(u1());
2668N/A }
2668N/A cpName[i] = buf.toString(UTF8_ENCODING);
2668N/A break;
2668N/A case CONSTANT_Integer:
2668N/A cpName[i] = String.valueOf((int) u4());
2668N/A break;
2668N/A case CONSTANT_Float:
2668N/A cpName[i] = String.valueOf(Float.intBitsToFloat(u4()));
2668N/A break;
2668N/A case CONSTANT_Long:
2668N/A cpName[i] = String.valueOf(u8());
2668N/A i += 1;
2668N/A break;
2668N/A case CONSTANT_Double:
2668N/A cpName[i] = String.valueOf(Double.longBitsToDouble(u8()));
2668N/A i += 1;
2668N/A break;
2668N/A case CONSTANT_Class:
2668N/A case CONSTANT_String:
2668N/A cpTem[i] = new int[]{u2()};
2668N/A break;
2668N/A case CONSTANT_Fieldref:
2668N/A case CONSTANT_Methodref:
2668N/A case CONSTANT_InterfaceMethodref:
2668N/A case CONSTANT_NameAndType:
2668N/A cpTem[i] = new int[]{u2(), u2()};
2668N/A break;
2668N/A }
2668N/A }
2668N/A for (int i = 1; i < cpLen; i++) {
2668N/A switch (cpTag[i]) {
2668N/A case CONSTANT_Class:
2668N/A case CONSTANT_String:
2668N/A cpName[i] = cpName[cpTem[i][0]];
2668N/A break;
2668N/A case CONSTANT_NameAndType:
2668N/A cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]];
2668N/A break;
2668N/A }
2668N/A }
2668N/A // do fieldref et al after nameandtype are all resolved
2668N/A for (int i = 1; i < cpLen; i++) {
2668N/A switch (cpTag[i]) {
2668N/A case CONSTANT_Fieldref:
2668N/A case CONSTANT_Methodref:
2668N/A case CONSTANT_InterfaceMethodref:
2668N/A cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]];
2668N/A break;
2668N/A }
2668N/A }
2668N/A cpool = new Element("ConstantPool", cpName.length);
2668N/A for (int i = 0; i < cpName.length; i++) {
2668N/A if (cpName[i] == null) {
2668N/A continue;
2668N/A }
2668N/A cpool.add(new Element(cpTagName(cpTag[i]),
2668N/A new String[]{"id", "" + i},
2668N/A cpName[i]));
2668N/A }
2668N/A if (keepCP) {
2668N/A cfile.add(cpool);
2668N/A }
2668N/A }
2668N/A
2668N/A private String cpRef() throws IOException {
2668N/A int ref = u2();
2668N/A if (resolveRefs) {
2668N/A return cpName(ref);
2668N/A } else {
2668N/A return REF_PREFIX + ref;
2668N/A }
2668N/A }
2668N/A
2668N/A private String cpName(int id) {
2668N/A if (id >= 0 && id < cpName.length) {
2668N/A return cpName[id];
2668N/A } else {
2668N/A return "[CP#" + Integer.toHexString(id) + "]";
2668N/A }
2668N/A }
2668N/A
2668N/A private long u8() throws IOException {
2668N/A return ((long) u4() << 32) + (((long) u4() << 32) >>> 32);
2668N/A }
2668N/A
2668N/A private int u4() throws IOException {
2668N/A return (u2() << 16) + u2();
2668N/A }
2668N/A
2668N/A private int u2() throws IOException {
2668N/A return (u1() << 8) + u1();
2668N/A }
2668N/A
2668N/A private int u1() throws IOException {
2668N/A int x = in.read();
2668N/A if (x < 0) {
2668N/A paddingSize++;
2668N/A return 0; // error recovery
2668N/A }
2668N/A fileSize++;
2668N/A assert (x == (x & 0xFF));
2668N/A return x;
2668N/A }
2668N/A}
2668N/A