现在的位置: 首页 > 综合 > 正文

Java Class类文件结构

2013年03月12日 ⁄ 综合 ⁄ 共 7378字 ⁄ 字号 评论关闭

感谢原作者:  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

抱歉!评论已关闭.