javap/src/main/java/org/apidesign/javap/ClassData.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 11 Dec 2012 11:05:06 +0100
branchlazyvm
changeset 303 c12342170235
parent 274 81f6e7778135
child 355 eea0065bcc1a
permissions -rw-r--r--
VM in VM properly processes class constants
     1 /*
     2  * Copyright (c) 2002, 2004, 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 
    26 
    27 package org.apidesign.javap;
    28 
    29 import java.io.*;
    30 
    31 /**
    32  * Central data repository of the Java Disassembler.
    33  * Stores all the information in java class file.
    34  *
    35  * @author  Sucheta Dambalkar (Adopted code from jdis)
    36  */
    37 public final class ClassData implements RuntimeConstants {
    38 
    39     private int magic;
    40     private int minor_version;
    41     private int major_version;
    42     private int cpool_count;
    43     private Object cpool[];
    44     private int access;
    45     private int this_class = 0;;
    46     private int super_class;
    47     private int interfaces_count;
    48     private int[] interfaces = new int[0];;
    49     private int fields_count;
    50     private FieldData[] fields;
    51     private int methods_count;
    52     private MethodData[] methods;
    53     private InnerClassData[] innerClasses;
    54     private int attributes_count;
    55     private AttrData[] attrs;
    56     private String classname;
    57     private String superclassname;
    58     private int source_cpx=0;
    59     private byte tags[];
    60     private Hashtable indexHashAscii = new Hashtable();
    61     private String pkgPrefix="";
    62     private int pkgPrefixLen=0;
    63 
    64     /**
    65      * Read classfile to disassemble.
    66      */
    67     public ClassData(InputStream infile) throws IOException {
    68         this.read(new DataInputStream(infile));
    69     }
    70 
    71     /**
    72      * Reads and stores class file information.
    73      */
    74     public void read(DataInputStream in) throws IOException {
    75         // Read the header
    76         magic = in.readInt();
    77         if (magic != JAVA_MAGIC) {
    78             throw new ClassFormatError("wrong magic: " +
    79                                        toHex(magic) + ", expected " +
    80                                        toHex(JAVA_MAGIC));
    81         }
    82         minor_version = in.readShort();
    83         major_version = in.readShort();
    84         if (major_version != JAVA_VERSION) {
    85         }
    86 
    87         // Read the constant pool
    88         readCP(in);
    89         access = in.readUnsignedShort();
    90         this_class = in.readUnsignedShort();
    91         super_class = in.readUnsignedShort();
    92 
    93         //Read interfaces.
    94         interfaces_count = in.readUnsignedShort();
    95         if(interfaces_count > 0){
    96             interfaces = new int[interfaces_count];
    97         }
    98         for (int i = 0; i < interfaces_count; i++) {
    99             interfaces[i]=in.readShort();
   100         }
   101 
   102         // Read the fields
   103         readFields(in);
   104 
   105         // Read the methods
   106         readMethods(in);
   107 
   108         // Read the attributes
   109         attributes_count = in.readUnsignedShort();
   110         attrs=new AttrData[attributes_count];
   111         for (int k = 0; k < attributes_count; k++) {
   112             int name_cpx=in.readUnsignedShort();
   113             if (getTag(name_cpx)==CONSTANT_UTF8
   114                 && getString(name_cpx).equals("SourceFile")
   115                 ){      if (in.readInt()!=2)
   116                     throw new ClassFormatError("invalid attr length");
   117                 source_cpx=in.readUnsignedShort();
   118                 AttrData attr=new AttrData(this);
   119                 attr.read(name_cpx);
   120                 attrs[k]=attr;
   121 
   122             } else if (getTag(name_cpx)==CONSTANT_UTF8
   123                        && getString(name_cpx).equals("InnerClasses")
   124                        ){       int length=in.readInt();
   125                        int num=in.readUnsignedShort();
   126                        if (2+num*8 != length)
   127                            throw new ClassFormatError("invalid attr length");
   128                        innerClasses=new InnerClassData[num];
   129                        for (int j = 0; j < num; j++) {
   130                            InnerClassData innerClass=new InnerClassData(this);
   131                            innerClass.read(in);
   132                            innerClasses[j]=innerClass;
   133                        }
   134                        AttrData attr=new AttrData(this);
   135                        attr.read(name_cpx);
   136                        attrs[k]=attr;
   137             } else {
   138                 AttrData attr=new AttrData(this);
   139                 attr.read(name_cpx, in);
   140                 attrs[k]=attr;
   141             }
   142         }
   143         in.close();
   144     } // end ClassData.read()
   145 
   146     /**
   147      * Reads and stores constant pool info.
   148      */
   149     void readCP(DataInputStream in) throws IOException {
   150         cpool_count = in.readUnsignedShort();
   151         tags = new byte[cpool_count];
   152         cpool = new Object[cpool_count];
   153         for (int i = 1; i < cpool_count; i++) {
   154             byte tag = in.readByte();
   155 
   156             switch(tags[i] = tag) {
   157             case CONSTANT_UTF8:
   158                 String str=in.readUTF();
   159                 indexHashAscii.put(cpool[i] = str, new Integer(i));
   160                 break;
   161             case CONSTANT_INTEGER:
   162                 cpool[i] = new Integer(in.readInt());
   163                 break;
   164             case CONSTANT_FLOAT:
   165                 cpool[i] = new Float(in.readFloat());
   166                 break;
   167             case CONSTANT_LONG:
   168                 cpool[i++] = new Long(in.readLong());
   169                 break;
   170             case CONSTANT_DOUBLE:
   171                 cpool[i++] = new Double(in.readDouble());
   172                 break;
   173             case CONSTANT_CLASS:
   174             case CONSTANT_STRING:
   175                 cpool[i] = new CPX(in.readUnsignedShort());
   176                 break;
   177 
   178             case CONSTANT_FIELD:
   179             case CONSTANT_METHOD:
   180             case CONSTANT_INTERFACEMETHOD:
   181             case CONSTANT_NAMEANDTYPE:
   182                 cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort());
   183                 break;
   184 
   185             case 0:
   186             default:
   187                 throw new ClassFormatError("invalid constant type: " + (int)tags[i]);
   188             }
   189         }
   190     }
   191 
   192     /**
   193      * Reads and strores field info.
   194      */
   195     protected void readFields(DataInputStream in) throws IOException {
   196         int fields_count = in.readUnsignedShort();
   197         fields=new FieldData[fields_count];
   198         for (int k = 0; k < fields_count; k++) {
   199             FieldData field=new FieldData(this);
   200             field.read(in);
   201             fields[k]=field;
   202         }
   203     }
   204 
   205     /**
   206      * Reads and strores Method info.
   207      */
   208     protected void readMethods(DataInputStream in) throws IOException {
   209         int methods_count = in.readUnsignedShort();
   210         methods=new MethodData[methods_count];
   211         for (int k = 0; k < methods_count ; k++) {
   212             MethodData method=new MethodData(this);
   213             method.read(in);
   214             methods[k]=method;
   215         }
   216     }
   217 
   218     /**
   219      * get a string
   220      */
   221     public String getString(int n) {
   222         if (n == 0) {
   223             return null; 
   224         } else {
   225             return (String)cpool[n];
   226         }
   227     }
   228 
   229     /**
   230      * get the type of constant given an index
   231      */
   232     public byte getTag(int n) {
   233         try{
   234             return tags[n];
   235         } catch (ArrayIndexOutOfBoundsException e) {
   236             return (byte)100;
   237         }
   238     }
   239 
   240     static final String hexString="0123456789ABCDEF";
   241 
   242     public static char hexTable[]=hexString.toCharArray();
   243 
   244     static String toHex(long val, int width) {
   245         StringBuffer s = new StringBuffer();
   246         for (int i=width-1; i>=0; i--)
   247             s.append(hexTable[((int)(val>>(4*i)))&0xF]);
   248         return "0x"+s.toString();
   249     }
   250 
   251     static String toHex(long val) {
   252         int width;
   253         for (width=16; width>0; width--) {
   254             if ((val>>(width-1)*4)!=0) break;
   255         }
   256         return toHex(val, width);
   257     }
   258 
   259     static String toHex(int val) {
   260         int width;
   261         for (width=8; width>0; width--) {
   262             if ((val>>(width-1)*4)!=0) break;
   263         }
   264         return toHex(val, width);
   265     }
   266 
   267     /**
   268      * Returns the name of this class.
   269      */
   270     public String getClassName() {
   271         String res=null;
   272         if (this_class==0) {
   273             return res;
   274         }
   275         int tcpx;
   276         try {
   277             if (tags[this_class]!=CONSTANT_CLASS) {
   278                 return res; //"<CP["+cpx+"] is not a Class> ";
   279             }
   280             tcpx=((CPX)cpool[this_class]).cpx;
   281         } catch (ArrayIndexOutOfBoundsException e) {
   282             return res; // "#"+cpx+"// invalid constant pool index";
   283         } catch (Throwable e) {
   284             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   285         }
   286 
   287         try {
   288             return (String)(cpool[tcpx]);
   289         } catch (ArrayIndexOutOfBoundsException e) {
   290             return  res; // "class #"+scpx+"// invalid constant pool index";
   291         } catch (ClassCastException e) {
   292             return  res; // "class #"+scpx+"// invalid constant pool reference";
   293         } catch (Throwable e) {
   294             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   295         }
   296 
   297     }
   298 
   299     /**
   300      * Returns the name of class at perticular index.
   301      */
   302     public String getClassName(int cpx) {
   303         String res="#"+cpx;
   304         if (cpx==0) {
   305             return res;
   306         }
   307         int scpx;
   308         try {
   309             if (tags[cpx]!=CONSTANT_CLASS) {
   310                 return res; //"<CP["+cpx+"] is not a Class> ";
   311             }
   312             scpx=((CPX)cpool[cpx]).cpx;
   313         } catch (ArrayIndexOutOfBoundsException e) {
   314             return res; // "#"+cpx+"// invalid constant pool index";
   315         } catch (Throwable e) {
   316             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   317         }
   318         res="#"+scpx;
   319         try {
   320             return (String)(cpool[scpx]);
   321         } catch (ArrayIndexOutOfBoundsException e) {
   322             return  res; // "class #"+scpx+"// invalid constant pool index";
   323         } catch (ClassCastException e) {
   324             return  res; // "class #"+scpx+"// invalid constant pool reference";
   325         } catch (Throwable e) {
   326             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   327         }
   328     }
   329 
   330     /**
   331      * Returns true if it is a class
   332      */
   333     public boolean isClass() {
   334         if((access & ACC_INTERFACE) == 0) return true;
   335         return false;
   336     }
   337 
   338     /**
   339      * Returns true if it is a interface.
   340      */
   341     public boolean isInterface(){
   342         if((access & ACC_INTERFACE) != 0) return true;
   343         return false;
   344     }
   345 
   346     /**
   347      * Returns true if this member is public, false otherwise.
   348      */
   349     public boolean isPublic(){
   350         return (access & ACC_PUBLIC) != 0;
   351     }
   352 
   353     /**
   354      * Returns the access of this class or interface.
   355      */
   356     public String[] getAccess(){
   357         Vector v = new Vector();
   358         if ((access & ACC_PUBLIC)   !=0) v.addElement("public");
   359         if ((access & ACC_FINAL)    !=0) v.addElement("final");
   360         if ((access & ACC_ABSTRACT) !=0) v.addElement("abstract");
   361         String[] accflags = new String[v.size()];
   362         v.copyInto(accflags);
   363         return accflags;
   364     }
   365 
   366     /**
   367      * Returns list of innerclasses.
   368      */
   369     public InnerClassData[] getInnerClasses(){
   370         return innerClasses;
   371     }
   372 
   373     /**
   374      * Returns list of attributes.
   375      */
   376     final AttrData[] getAttributes(){
   377         return attrs;
   378     }
   379     
   380     public byte[] findAnnotationData(boolean classRetention) {
   381         String n = classRetention ?
   382             "RuntimeInvisibleAnnotations" : // NOI18N
   383             "RuntimeVisibleAnnotations"; // NOI18N
   384         return findAttr(n, attrs);
   385     }
   386 
   387     /**
   388      * Returns true if superbit is set.
   389      */
   390     public boolean isSuperSet(){
   391         if ((access & ACC_SUPER)   !=0) return true;
   392         return false;
   393     }
   394 
   395     /**
   396      * Returns super class name.
   397      */
   398     public String getSuperClassName(){
   399         String res=null;
   400         if (super_class==0) {
   401             return res;
   402         }
   403         int scpx;
   404         try {
   405             if (tags[super_class]!=CONSTANT_CLASS) {
   406                 return res; //"<CP["+cpx+"] is not a Class> ";
   407             }
   408             scpx=((CPX)cpool[super_class]).cpx;
   409         } catch (ArrayIndexOutOfBoundsException e) {
   410             return res; // "#"+cpx+"// invalid constant pool index";
   411         } catch (Throwable e) {
   412             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   413         }
   414 
   415         try {
   416             return (String)(cpool[scpx]);
   417         } catch (ArrayIndexOutOfBoundsException e) {
   418             return  res; // "class #"+scpx+"// invalid constant pool index";
   419         } catch (ClassCastException e) {
   420             return  res; // "class #"+scpx+"// invalid constant pool reference";
   421         } catch (Throwable e) {
   422             return res; // "#"+cpx+"// ERROR IN DISASSEMBLER";
   423         }
   424     }
   425 
   426     /**
   427      * Returns list of super interfaces.
   428      */
   429     public String[] getSuperInterfaces(){
   430         String interfacenames[] = new String[interfaces.length];
   431         int interfacecpx = -1;
   432         for(int i = 0; i < interfaces.length; i++){
   433             interfacecpx=((CPX)cpool[interfaces[i]]).cpx;
   434             interfacenames[i] = (String)(cpool[interfacecpx]);
   435         }
   436         return interfacenames;
   437     }
   438 
   439     /**
   440      * Returns string at prticular constant pool index.
   441      */
   442     public String getStringValue(int cpoolx) {
   443         try {
   444             return ((String)cpool[cpoolx]);
   445         } catch (ArrayIndexOutOfBoundsException e) {
   446             return "//invalid constant pool index:"+cpoolx;
   447         } catch (ClassCastException e) {
   448             return "//invalid constant pool ref:"+cpoolx;
   449         }
   450     }
   451 
   452     /**
   453      * Returns list of field info.
   454      */
   455     public  FieldData[] getFields(){
   456         return fields;
   457     }
   458 
   459     /**
   460      * Returns list of method info.
   461      */
   462     public  MethodData[] getMethods(){
   463         return methods;
   464     }
   465 
   466     /**
   467      * Returns constant pool entry at that index.
   468      */
   469     public CPX2 getCpoolEntry(int cpx){
   470         return ((CPX2)(cpool[cpx]));
   471     }
   472 
   473     public Object getCpoolEntryobj(int cpx){
   474         return (cpool[cpx]);
   475     }
   476 
   477     /**
   478      * Returns index of this class.
   479      */
   480     public int getthis_cpx(){
   481         return this_class;
   482     }
   483 
   484     /**
   485      * Returns string at that index.
   486      */
   487     public String StringValue(int cpx) {
   488         return stringValue(cpx, false);
   489     }
   490     public String stringValue(int cpx, boolean textual) {
   491         return stringValue(cpx, textual, null);
   492     }
   493     public String stringValue(int cpx, String[] classRefs) {
   494         return stringValue(cpx, true, classRefs);
   495     }
   496     private String stringValue(int cpx, boolean textual, String[] refs) {
   497         if (cpx==0) return "#0";
   498         int tag;
   499         Object x;
   500         String suffix="";
   501         try {
   502             tag=tags[cpx];
   503             x=cpool[cpx];
   504         } catch (IndexOutOfBoundsException e) {
   505             return "<Incorrect CP index:"+cpx+">";
   506         }
   507 
   508         if (x==null) return "<NULL>";
   509         switch (tag) {
   510         case CONSTANT_UTF8: {
   511             if (!textual) {
   512                 return (String)x;
   513             }
   514             StringBuilder sb=new StringBuilder();
   515             String s=(String)x;
   516             for (int k=0; k<s.length(); k++) {
   517                 char c=s.charAt(k);
   518                 switch (c) {
   519                 case '\\': sb.append('\\').append('\\'); break;
   520                 case '\t': sb.append('\\').append('t'); break;
   521                 case '\n': sb.append('\\').append('n'); break;
   522                 case '\r': sb.append('\\').append('r'); break;
   523                 case '\"': sb.append('\\').append('\"'); break;
   524                 default: sb.append(c);
   525                 }
   526             }
   527             return sb.toString();
   528         }
   529         case CONSTANT_DOUBLE: {
   530             Double d=(Double)x;
   531             String sd=d.toString();
   532             if (textual) {
   533                 return sd;
   534             }
   535             return sd+"d";
   536         }
   537         case CONSTANT_FLOAT: {
   538             Float f=(Float)x;
   539             String sf=(f).toString();
   540             if (textual) {
   541                 return sf;
   542             }
   543             return sf+"f";
   544         }
   545         case CONSTANT_LONG: {
   546             Long ln = (Long)x;
   547             if (textual) {
   548                 return ln.toString();
   549             }
   550             return ln.toString()+'l';
   551         }
   552         case CONSTANT_INTEGER: {
   553             Integer in = (Integer)x;
   554             return in.toString();
   555         }
   556         case CONSTANT_CLASS:
   557             String jn = getClassName(cpx);
   558             if (textual) {
   559                 if (refs != null) {
   560                     refs[0] = jn;
   561                 }
   562                 return jn;
   563             }
   564             return javaName(jn);
   565         case CONSTANT_STRING:
   566             String sv = stringValue(((CPX)x).cpx, textual);
   567             if (textual) {
   568                 return '"' + sv + '"';
   569             } else {
   570                 return sv;
   571             }
   572         case CONSTANT_FIELD:
   573         case CONSTANT_METHOD:
   574         case CONSTANT_INTERFACEMETHOD:
   575             //return getShortClassName(((CPX2)x).cpx1)+"."+StringValue(((CPX2)x).cpx2);
   576              return javaName(getClassName(((CPX2)x).cpx1))+"."+StringValue(((CPX2)x).cpx2);
   577 
   578         case CONSTANT_NAMEANDTYPE:
   579             return getName(((CPX2)x).cpx1)+":"+StringValue(((CPX2)x).cpx2);
   580         default:
   581             return "UnknownTag"; //TBD
   582         }
   583     }
   584 
   585     /**
   586      * Returns resolved java type name.
   587      */
   588     public String javaName(String name) {
   589         if( name==null) return "null";
   590         int len=name.length();
   591         if (len==0) return "\"\"";
   592         int cc='/';
   593     fullname: { // xxx/yyy/zzz
   594             int cp;
   595             for (int k=0; k<len; k += Character.charCount(cp)) {
   596                 cp=name.codePointAt(k);
   597                 if (cc=='/') {
   598                     if (!isJavaIdentifierStart(cp)) break fullname;
   599                 } else if (cp!='/') {
   600                     if (!isJavaIdentifierPart(cp)) break fullname;
   601                 }
   602                 cc=cp;
   603             }
   604             return name;
   605         }
   606         return "\""+name+"\"";
   607     }
   608 
   609     public String getName(int cpx) {
   610         String res;
   611         try {
   612             return javaName((String)cpool[cpx]); //.replace('/','.');
   613         } catch (ArrayIndexOutOfBoundsException e) {
   614             return "<invalid constant pool index:"+cpx+">";
   615         } catch (ClassCastException e) {
   616             return "<invalid constant pool ref:"+cpx+">";
   617         }
   618     }
   619 
   620     /**
   621      * Returns unqualified class name.
   622      */
   623     public String getShortClassName(int cpx) {
   624         String classname=javaName(getClassName(cpx));
   625         pkgPrefixLen=classname.lastIndexOf("/")+1;
   626         if (pkgPrefixLen!=0) {
   627             pkgPrefix=classname.substring(0,pkgPrefixLen);
   628             if (classname.startsWith(pkgPrefix)) {
   629                 return classname.substring(pkgPrefixLen);
   630             }
   631         }
   632         return classname;
   633     }
   634 
   635     /**
   636      * Returns source file name.
   637      */
   638     public String getSourceName(){
   639         return getName(source_cpx);
   640     }
   641 
   642     /**
   643      * Returns package name.
   644      */
   645     public String getPkgName(){
   646         String classname=getClassName(this_class);
   647         pkgPrefixLen=classname.lastIndexOf("/")+1;
   648         if (pkgPrefixLen!=0) {
   649             pkgPrefix=classname.substring(0,pkgPrefixLen);
   650             return("package  "+pkgPrefix.substring(0,pkgPrefixLen-1)+";\n");
   651         }else return null;
   652     }
   653 
   654     /**
   655      * Returns total constant pool entry count.
   656      */
   657     public int getCpoolCount(){
   658         return cpool_count;
   659     }
   660 
   661     /**
   662      * Returns minor version of class file.
   663      */
   664     public int getMinor_version(){
   665         return minor_version;
   666     }
   667 
   668     /**
   669      * Returns major version of class file.
   670      */
   671     public int getMajor_version(){
   672         return major_version;
   673     }
   674 
   675     private boolean isJavaIdentifierStart(int cp) {
   676         return ('a' <= cp && cp <= 'z') || ('A' <= cp && cp <= 'Z');
   677     }
   678 
   679     private boolean isJavaIdentifierPart(int cp) {
   680         return isJavaIdentifierStart(cp) || ('0' <= cp && cp <= '9');
   681     }
   682 
   683     public String[] getNameAndType(int indx) {
   684         return getNameAndType(indx, 0, new String[2]);
   685     }
   686     
   687     private String[] getNameAndType(int indx, int at, String[] arr) {
   688         CPX2 c2 = getCpoolEntry(indx);
   689         arr[at] = StringValue(c2.cpx1);
   690         arr[at + 1] = StringValue(c2.cpx2);
   691         return arr;
   692     }
   693 
   694     public String[] getFieldInfoName(int indx) {
   695         CPX2 c2 = getCpoolEntry(indx);
   696         String[] arr = new String[3];
   697         arr[0] = getClassName(c2.cpx1);
   698         return getNameAndType(c2.cpx2, 1, arr);
   699     }
   700 
   701     static byte[] findAttr(String n, AttrData[] attrs) {
   702         for (AttrData ad : attrs) {
   703             if (n.equals(ad.getAttrName())) {
   704                 return ad.getData();
   705             }
   706         }
   707         return null;
   708     }
   709 }