/* * Copyright (c) 2002, 2004, 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 sun.tools.javap; import java.util.*; import java.io.*; /** * Central data repository of the Java Disassembler. * Stores all the information in java class file. * * @author Sucheta Dambalkar (Adopted code from jdis) */ public class ClassData implements RuntimeConstants { private int magic; private int minor_version; private int major_version; private int cpool_count; private Object cpool[]; private int access; private int this_class = 0;; private int super_class; private int interfaces_count; private int[] interfaces = new int[0];; private int fields_count; private FieldData[] fields; private int methods_count; private MethodData[] methods; private InnerClassData[] innerClasses; private int attributes_count; private AttrData[] attrs; private String classname; private String superclassname; private int source_cpx=0; private byte tags[]; private Hashtable indexHashAscii = new Hashtable(); private String pkgPrefix=""; private int pkgPrefixLen=0; /** * Read classfile to disassemble. */ public ClassData(InputStream infile){ try{ this.read(new DataInputStream(infile)); }catch (FileNotFoundException ee) { error("cant read file"); }catch (Error ee) { ee.printStackTrace(); error("fatal error"); } catch (Exception ee) { ee.printStackTrace(); error("fatal exception"); } } /** * Reads and stores class file information. */ public void read(DataInputStream in) throws IOException { // Read the header magic = in.readInt(); if (magic != JAVA_MAGIC) { throw new ClassFormatError("wrong magic: " + toHex(magic) + ", expected " + toHex(JAVA_MAGIC)); } minor_version = in.readShort(); major_version = in.readShort(); if (major_version != JAVA_VERSION) { } // Read the constant pool readCP(in); access = in.readUnsignedShort(); this_class = in.readUnsignedShort(); super_class = in.readUnsignedShort(); //Read interfaces. interfaces_count = in.readUnsignedShort(); if(interfaces_count > 0){ interfaces = new int[interfaces_count]; } for (int i = 0; i < interfaces_count; i++) { interfaces[i]=in.readShort(); } // Read the fields readFields(in); // Read the methods readMethods(in); // Read the attributes attributes_count = in.readUnsignedShort(); attrs=new AttrData[attributes_count]; for (int k = 0; k < attributes_count; k++) { int name_cpx=in.readUnsignedShort(); if (getTag(name_cpx)==CONSTANT_UTF8 && getString(name_cpx).equals("SourceFile") ){ if (in.readInt()!=2) throw new ClassFormatError("invalid attr length"); source_cpx=in.readUnsignedShort(); AttrData attr=new AttrData(this); attr.read(name_cpx); attrs[k]=attr; } else if (getTag(name_cpx)==CONSTANT_UTF8 && getString(name_cpx).equals("InnerClasses") ){ int length=in.readInt(); int num=in.readUnsignedShort(); if (2+num*8 != length) throw new ClassFormatError("invalid attr length"); innerClasses=new InnerClassData[num]; for (int j = 0; j < num; j++) { InnerClassData innerClass=new InnerClassData(this); innerClass.read(in); innerClasses[j]=innerClass; } AttrData attr=new AttrData(this); attr.read(name_cpx); attrs[k]=attr; } else { AttrData attr=new AttrData(this); attr.read(name_cpx, in); attrs[k]=attr; } } in.close(); } // end ClassData.read() /** * Reads and stores constant pool info. */ void readCP(DataInputStream in) throws IOException { cpool_count = in.readUnsignedShort(); tags = new byte[cpool_count]; cpool = new Object[cpool_count]; for (int i = 1; i < cpool_count; i++) { byte tag = in.readByte(); switch(tags[i] = tag) { case CONSTANT_UTF8: String str=in.readUTF(); indexHashAscii.put(cpool[i] = str, new Integer(i)); break; case CONSTANT_INTEGER: cpool[i] = new Integer(in.readInt()); break; case CONSTANT_FLOAT: cpool[i] = new Float(in.readFloat()); break; case CONSTANT_LONG: cpool[i++] = new Long(in.readLong()); break; case CONSTANT_DOUBLE: cpool[i++] = new Double(in.readDouble()); break; case CONSTANT_CLASS: case CONSTANT_STRING: cpool[i] = new CPX(in.readUnsignedShort()); break; case CONSTANT_FIELD: case CONSTANT_METHOD: case CONSTANT_INTERFACEMETHOD: case CONSTANT_NAMEANDTYPE: cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort()); break; case 0: default: throw new ClassFormatError("invalid constant type: " + (int)tags[i]); } } } /** * Reads and strores field info. */ protected void readFields(DataInputStream in) throws IOException { int fields_count = in.readUnsignedShort(); fields=new FieldData[fields_count]; for (int k = 0; k < fields_count; k++) { FieldData field=new FieldData(this); field.read(in); fields[k]=field; } } /** * Reads and strores Method info. */ protected void readMethods(DataInputStream in) throws IOException { int methods_count = in.readUnsignedShort(); methods=new MethodData[methods_count]; for (int k = 0; k < methods_count ; k++) { MethodData method=new MethodData(this); method.read(in); methods[k]=method; } } /** * get a string */ public String getString(int n) { return (n == 0) ? null : (String)cpool[n]; } /** * get the type of constant given an index */ public byte getTag(int n) { try{ return tags[n]; } catch (ArrayIndexOutOfBoundsException e) { return (byte)100; } } static final String hexString="0123456789ABCDEF"; public static char hexTable[]=hexString.toCharArray(); static String toHex(long val, int width) { StringBuffer s = new StringBuffer(); for (int i=width-1; i>=0; i--) s.append(hexTable[((int)(val>>(4*i)))&0xF]); return "0x"+s.toString(); } static String toHex(long val) { int width; for (width=16; width>0; width--) { if ((val>>(width-1)*4)!=0) break; } return toHex(val, width); } static String toHex(int val) { int width; for (width=8; width>0; width--) { if ((val>>(width-1)*4)!=0) break; } return toHex(val, width); } public void error(String msg) { System.err.println("ERROR:" +msg); } /** * Returns the name of this class. */ public String getClassName() { String res=null; if (this_class==0) { return res; } int tcpx; try { if (tags[this_class]!=CONSTANT_CLASS) { return res; //"<CP["+cpx+"] is not a Class> "; } tcpx=((CPX)cpool[this_class]).cpx; } catch (ArrayIndexOutOfBoundsException e) { return res; // "#"+cpx+"// invalid constant pool index"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } try { return (String)(cpool[tcpx]); } catch (ArrayIndexOutOfBoundsException e) { return res; // "class #"+scpx+"// invalid constant pool index"; } catch (ClassCastException e) { return res; // "class #"+scpx+"// invalid constant pool reference"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } } /** * Returns the name of class at perticular index. */ public String getClassName(int cpx) { String res="#"+cpx; if (cpx==0) { return res; } int scpx; try { if (tags[cpx]!=CONSTANT_CLASS) { return res; //"<CP["+cpx+"] is not a Class> "; } scpx=((CPX)cpool[cpx]).cpx; } catch (ArrayIndexOutOfBoundsException e) { return res; // "#"+cpx+"// invalid constant pool index"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } res="#"+scpx; try { return (String)(cpool[scpx]); } catch (ArrayIndexOutOfBoundsException e) { return res; // "class #"+scpx+"// invalid constant pool index"; } catch (ClassCastException e) { return res; // "class #"+scpx+"// invalid constant pool reference"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } } /** * Returns true if it is a class */ public boolean isClass() { if((access & ACC_INTERFACE) == 0) return true; return false; } /** * Returns true if it is a interface. */ public boolean isInterface(){ if((access & ACC_INTERFACE) != 0) return true; return false; } /** * Returns true if this member is public, false otherwise. */ public boolean isPublic(){ return (access & ACC_PUBLIC) != 0; } /** * Returns the access of this class or interface. */ public String[] getAccess(){ Vector v = new Vector(); if ((access & ACC_PUBLIC) !=0) v.addElement("public"); if ((access & ACC_FINAL) !=0) v.addElement("final"); if ((access & ACC_ABSTRACT) !=0) v.addElement("abstract"); String[] accflags = new String[v.size()]; v.copyInto(accflags); return accflags; } /** * Returns list of innerclasses. */ public InnerClassData[] getInnerClasses(){ return innerClasses; } /** * Returns list of attributes. */ public AttrData[] getAttributes(){ return attrs; } /** * Returns true if superbit is set. */ public boolean isSuperSet(){ if ((access & ACC_SUPER) !=0) return true; return false; } /** * Returns super class name. */ public String getSuperClassName(){ String res=null; if (super_class==0) { return res; } int scpx; try { if (tags[super_class]!=CONSTANT_CLASS) { return res; //"<CP["+cpx+"] is not a Class> "; } scpx=((CPX)cpool[super_class]).cpx; } catch (ArrayIndexOutOfBoundsException e) { return res; // "#"+cpx+"// invalid constant pool index"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } try { return (String)(cpool[scpx]); } catch (ArrayIndexOutOfBoundsException e) { return res; // "class #"+scpx+"// invalid constant pool index"; } catch (ClassCastException e) { return res; // "class #"+scpx+"// invalid constant pool reference"; } catch (Throwable e) { return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } } /** * Returns list of super interfaces. */ public String[] getSuperInterfaces(){ String interfacenames[] = new String[interfaces.length]; int interfacecpx = -1; for(int i = 0; i < interfaces.length; i++){ interfacecpx=((CPX)cpool[interfaces[i]]).cpx; interfacenames[i] = (String)(cpool[interfacecpx]); } return interfacenames; } /** * Returns string at prticular constant pool index. */ public String getStringValue(int cpoolx) { try { return ((String)cpool[cpoolx]); } catch (ArrayIndexOutOfBoundsException e) { return "//invalid constant pool index:"+cpoolx; } catch (ClassCastException e) { return "//invalid constant pool ref:"+cpoolx; } } /** * Returns list of field info. */ public FieldData[] getFields(){ return fields; } /** * Returns list of method info. */ public MethodData[] getMethods(){ return methods; } /** * Returns constant pool entry at that index. */ public CPX2 getCpoolEntry(int cpx){ return ((CPX2)(cpool[cpx])); } public Object getCpoolEntryobj(int cpx){ return (cpool[cpx]); } /** * Returns index of this class. */ public int getthis_cpx(){ return this_class; } public String TagString (int tag) { String res=Tables.tagName(tag); if (res==null) return "BOGUS_TAG:"+tag; return res; } /** * Returns string at that index. */ public String StringValue(int cpx) { if (cpx==0) return "#0"; int tag; Object x; String suffix=""; try { tag=tags[cpx]; x=cpool[cpx]; } catch (IndexOutOfBoundsException e) { return "<Incorrect CP index:"+cpx+">"; } if (x==null) return "<NULL>"; switch (tag) { case CONSTANT_UTF8: { StringBuffer sb=new StringBuffer(); String s=(String)x; for (int k=0; k<s.length(); k++) { char c=s.charAt(k); switch (c) { case '\t': sb.append('\\').append('t'); break; case '\n': sb.append('\\').append('n'); break; case '\r': sb.append('\\').append('r'); break; case '\"': sb.append('\\').append('\"'); break; default: sb.append(c); } } return sb.toString(); } case CONSTANT_DOUBLE: { Double d=(Double)x; String sd=d.toString(); return sd+"d"; } case CONSTANT_FLOAT: { Float f=(Float)x; String sf=(f).toString(); return sf+"f"; } case CONSTANT_LONG: { Long ln = (Long)x; return ln.toString()+'l'; } case CONSTANT_INTEGER: { Integer in = (Integer)x; return in.toString(); } case CONSTANT_CLASS: return javaName(getClassName(cpx)); case CONSTANT_STRING: return StringValue(((CPX)x).cpx); case CONSTANT_FIELD: case CONSTANT_METHOD: case CONSTANT_INTERFACEMETHOD: //return getShortClassName(((CPX2)x).cpx1)+"."+StringValue(((CPX2)x).cpx2); return javaName(getClassName(((CPX2)x).cpx1))+"."+StringValue(((CPX2)x).cpx2); case CONSTANT_NAMEANDTYPE: return getName(((CPX2)x).cpx1)+":"+StringValue(((CPX2)x).cpx2); default: return "UnknownTag"; //TBD } } /** * Returns resolved java type name. */ public String javaName(String name) { if( name==null) return "null"; int len=name.length(); if (len==0) return "\"\""; int cc='/'; fullname: { // xxx/yyy/zzz int cp; for (int k=0; k<len; k += Character.charCount(cp)) { cp=name.codePointAt(k); if (cc=='/') { if (!Character.isJavaIdentifierStart(cp)) break fullname; } else if (cp!='/') { if (!Character.isJavaIdentifierPart(cp)) break fullname; } cc=cp; } return name; } return "\""+name+"\""; } public String getName(int cpx) { String res; try { return javaName((String)cpool[cpx]); //.replace('/','.'); } catch (ArrayIndexOutOfBoundsException e) { return "<invalid constant pool index:"+cpx+">"; } catch (ClassCastException e) { return "<invalid constant pool ref:"+cpx+">"; } } /** * Returns unqualified class name. */ public String getShortClassName(int cpx) { String classname=javaName(getClassName(cpx)); pkgPrefixLen=classname.lastIndexOf("/")+1; if (pkgPrefixLen!=0) { pkgPrefix=classname.substring(0,pkgPrefixLen); if (classname.startsWith(pkgPrefix)) { return classname.substring(pkgPrefixLen); } } return classname; } /** * Returns source file name. */ public String getSourceName(){ return getName(source_cpx); } /** * Returns package name. */ public String getPkgName(){ String classname=getClassName(this_class); pkgPrefixLen=classname.lastIndexOf("/")+1; if (pkgPrefixLen!=0) { pkgPrefix=classname.substring(0,pkgPrefixLen); return("package "+pkgPrefix.substring(0,pkgPrefixLen-1)+";\n"); }else return null; } /** * Returns total constant pool entry count. */ public int getCpoolCount(){ return cpool_count; } public String StringTag(int cpx) { byte tag=0; String str=null; try { if (cpx==0) throw new IndexOutOfBoundsException(); tag=tags[cpx]; return TagString(tag); } catch (IndexOutOfBoundsException e) { str="Incorrect CP index:"+cpx; } return str; } /** * Returns minor version of class file. */ public int getMinor_version(){ return minor_version; } /** * Returns major version of class file. */ public int getMajor_version(){ return major_version; } }