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