Java Class類文件結構

感謝原作者:  http://blog.csdn.net/zhy05/article/details/1800309


Class文件格式

Class文件格式ClassFile結構體的C語言描述如下:

struct ClassFile

{

              u4 magic;                                 //識別Class文件格式,具體值爲0xCAFEBABE

              u2 minor_version;            // Class文件格式副版本號,

              u2 major_version;            // Class文件格式主版本號,

              u2 constant_pool_count; //  常數表項個數,

              cp_info **constant_pool;// 常數表,又稱變長符號表,

              u2 access_flags;               //Class的聲明中使用的修飾符掩碼,

              u2 this_class;                   //常數表索引,索引內保存類名或接口名,

              u2 super_class;                //常數表索引,索引內保存父類名,

              u2 interfaces_count;        //超接口個數,

              u2 *interfaces;                 //常數表索引,各超接口名稱,

              u2 fields_count;       //類的域個數,

              field_info **fields;          //域數據,包括屬性名稱索引,

//域修飾符掩碼等,

              u2 methods_count;          //方法個數,

              method_info **methods;//方法數據,包括方法名稱索引,方法修飾符掩碼等,

              u2 attributes_count;        //類附加屬性個數,

              attribute_info **attributes; //類附加屬性數據,包括源文件名等。

};

 

其中u2unsigned shortu4unsigned long

typedef unsigned char   u1;

typedef unsigned short  u2;

typedef unsigned long   u4;

 

cp_info **constant_pool是常量表的指針數組,指針數組個數爲constant_pool_count,結構體cp_info

struct cp_info

{

              u1 tag;       //常數表數據類型

              u1 *info;   //常數表數據

};

常數表數據類型Tag定義如下:

#define CONSTANT_Class                                         7     

#define CONSTANT_Fieldref                                     9

#define CONSTANT_Methodref                                10

#define CONSTANT_InterfaceMethodref                  11

#define CONSTANT_String                                                      8

#define CONSTANT_Integer                                                  3

#define CONSTANT_Float                                                       4

#define CONSTANT_Long                                                       5

#define CONSTANT_Double                                      6

#define CONSTANT_NameAndType                         12

#define CONSTANT_Utf8                                                        1

每種類型對應一個結構體保存該類型數據,例如CONSTANT_Class info指針指向的數據類型應爲CONSTANT_Class_info

struct CONSTANT_Class_info

{

              u1 tag;

              u2 name_index;

};

2

CONSTANT_Utf8info指針指向的數據類型應爲CONSTANT_Utf8_info

struct CONSTANT_Utf8_info

{

              u1 tag;

              u2 length;

              u1 *bytes;

};

Taginfo的詳細說明參考《Java虛擬機規範》第四章4.4節。

access_flags爲類修飾符掩碼,域與方法都有各自的修飾符掩碼。

#define ACC_PUBLIC                                0x0001 

#define ACC_PRIVATE                             0x0002

#define ACC_PROTECTED                                   0x0004

#define ACC_STATIC                                0x0008

#define ACC_FINAL                                              0x0010

#define ACC_SYNCHRONIZED                         0x0020

#define ACC_SUPER                                                0x0020

#define ACC_VOLATILE                                        0x0040

#define ACC_TRANSIENT                                      0x0080 

#define ACC_NATIVE                               0x0100

#define ACC_INTERFACE                                      0x0200 

#define ACC_ABSTRACT                                       0x0400 

#define ACC_STRICT                                      0x0800

例如類的修飾符爲public abstractaccess_flags的值爲ACC_PUBLIC | ACC_ABSTRACT=0x0401

this_class的值是常數表的索引,索引的info內保存類或接口名。例如類名爲com.sum.java.swing.SwingUtitlities2info保存爲com/sum/java/swing/SwingUtitlities2

super_class的值是常數表的索引,索引的info內保存超類名,在info內保存形式和類名相同。

interfaces是數組,數組個數爲interfaces_count,數組內的元素爲常數表的索引,索引的info內保存超接口名,在info內保存形式和類名相同。

field_info **fields是類域數據的指針數組,指針數組個數爲fields_count,結構體field_info定義如下:

struct field_info

{

              u2 access_flags;                 //域修飾符掩碼

              u2 name_index;                 //域名在常數表內的索引

              u2 descriptor_index;          //域的描述符,其值是常數表內的索引

              u2 attributes_count;           //域的屬性個數

              attribute_info **attributes; //域的屬性數據,即域的值

 

};

例如一個域定義如下:

private final static byte UNSET=127;

則該域的修飾符掩碼值爲:ACC_PRIVATE | ACC_STATIC | ACC_FINAL=0x001A

常數表內name_index索引內保存數據爲UNSET,常數表內descriptor_index索引內保存的數據爲BB表示byte, 其他類型參考《Java虛擬機規範》第四章4.3.2節)。attributes_count的值爲1,其中attributes是指針數組。指針數組個數爲attributes_count,在此爲1attribute_info結構體如下:

struct attribute_info

{

              u2 attribute_name_index;   //常數表內索引

              u4 attribute_length;            //屬性長度

              u1 *info;                             //根據屬性類型不同而值不同

}; 

attribute_info可以轉換(cast)爲多種類型ConstantValue_attributeExceptions_attributeLineNumberTable_attributeLocalVariableTable_attributeCode_attribute等。

因爲域的屬性只有一種:ConstantValue_attribute,因此此結構體轉換爲

struct ConstantValue_attribute

{

              u2 attribute_name_index;        //常數表內索引

              u4 attribute_length;                 //屬性長度值,永遠爲2

              u2 constantvalue_index;         //常數表內索引,保存域的值

//在此例中,常數表內保存的值爲127

};

method_info **methods是方法數據的指針數組,指針數組個數爲methods_count,結構體method_info定義如下:

struct method_info

{

              u2 access_flags;                   //方法修飾符掩碼

              u2 name_index;                   //方法名在常數表內的索引

              u2 descriptor_index;            //方法描述符,其值是常數表內的索引

              u2 attributes_count;             //方法的屬性個數

              attribute_info **attributes;  //方法的屬性數據,

//保存方法實現的Bytecode和異常處理

};

例如一個方法定義如下:

public static boolean canAccessSystemClipboard(){

              ...

}

access_flags的值爲 ACC_PUBLIC | ACC_STATIC =0x0009,常數表內name_index索引內保存數據爲canAccessSystemClipboard,常數表內descriptor_index索引內保存數據爲()Z(括號表示方法參數,Z表示返回值爲布爾型,詳細說明參照《Java虛擬機規範》第四章4.3.2)attribute_info **attributes是方法的屬性指針數組,個數爲attributes_count,數組內保存的是常數表索引,infoCode_attributeExceptions_attribute

本文不解析方法內容,因此忽略Code_attributeExceptions_attribute的內容。

 

ClassFile結構體中的attribute_info **attributes是附加屬性數組指針,個數爲attributes_count,本文只識別SourceFile屬性。

struct SourceFile_attribute

{

              u2 attribute_name_index; //常數表內索引

              u4 attribute_length;          //屬性長度值,永遠爲2

              u2 sourcefile_index;         //常數表內索引,info保存源文件名

};

例如com.sum.java.swing.SwingUtitlities2類的源文件名爲SwingUtitlities2.java

              以上是本文需要解析的Class文件格式。

2.1.2 讀取數據

定義CJavaClass類完成解析Class文件,生成Java源程序字符串。使用VC++MFCCFileClass文件讀取數據。例如:用16進制編輯器打開Class文件,如圖3,前4byte分別是CA FE BA BE,使用CFile::Read(tmp,sizeof(u4))讀取後,tmp的值爲0xBEBAFECA,所以需要位轉換。定義以下方法從文件讀取定長數據:

                            void readu1(u1 *buff);

  void readu2(u2 *buff);

  void readu4(u4 *buff);

定義如下方法讀取變長數據。

void readun(void *buff,u4 len)

讀取的u2u4的數據需要位轉換:

U1  [0]

U1 [1]

U1   [1]

U1 [0]

U2

U1  [0]

U1 [1]

U1   [3]

U4

U1 [2]

U1  [3]

U1 [2]

U1   [0]

U1 [1]

調用void readu4(u4 *buff);buff的值爲0xCAFEBABE,該值爲ClassFilemagic,識別該文件是Java Class文件。

3

              magic的後面是Class格式的版本號,圖3的版本爲0x00000030=0.48。版本後面是常數表的元素個數,圖3的常數表的元素個數爲0xD2=210個。常數表的元素個數之後如ClassFile結構體定義的常數表,類信息,接口信息,域信息,方法信息和附加屬性等。

2.2 生成Java源文件

              解析Class文件後,生產ClassFile結構體。遍歷該結構體數據,則可根據Java語言規範生成Java源文件。例如根據ClassFileaccess_flags值獲得Java類的修飾符,其中accessCArray<CString,CString&>,保存類所有的修飾符:

              if((flag & ACC_PUBLIC )==ACC_PUBLIC)

              {

                                          access.Add(CString("public"));

              }

              if((flag & ACC_PRIVATE)==ACC_PRIVATE)

              {

                                          access.Add(CString("private"));

 

              }

              if((flag & ACC_PROTECTED)==ACC_PROTECTED)

              {

                                          access.Add(CString("protected"));

 

              }

2.3顯示視圖

              將獲得的Java源代碼顯示在MFCCScrollView視圖非常簡單,可以添加一些關鍵字顏色,例如註釋顯示爲草綠色等,如圖4

4

發佈了115 篇原創文章 · 獲贊 13 · 訪問量 63萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章