| /* |
| * Copyright 2002-2004 Sun Microsystems, Inc. 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.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; |
| } |
| } |