自己动手写Java虚拟机
上QQ阅读APP看书,第一时间看更新

icon1

第2章介绍了Java虚拟机从哪里搜索class文件,并且实现了类路径功能,已经可以把class文件读取到内存中。本章将详细讨论class文件格式,编写代码解析class文件,为下一步真正实现Java虚拟机做好准备。

在开始阅读本章之前,先把目录结构准备好。复制ch02目录,并改名为ch03,然后编辑ch03\main.go等文件,把import语句中的ch02都改成ch03这个过程比较无趣,也容易出错。可以使用编辑器提供的“搜索和替换”功能来完成这项工作。,最后在ch03目录中创建classfile子目录。现在的目录结构看起来应该如下所示:

D:\go\workspace\src
—-jvmgo
—-ch01
—-ch02
—-ch03
  —-classfile
  —-classpath
  —-cmd.go
  —-main.go

3.1 class文件

作为类(或者接口)本章后面提到的类,如无特别说明,均泛指类或者接口。信息的载体,每个class文件都完整地定义了一个类。为了使Java程序可以“编写一次,处处运行”, Java虚拟机规范对class文件格式进行了严格的规定。但是另一方面,对于从哪里加载class文件,给了足够多的自由。由第2章可知,Java虚拟机实现可以从文件系统读取和从JAR(或ZIP)压缩包中提取class文件。除此之外,也可以通过网络下载、从数据库加载,甚至是在运行中直接生成class文件。Java虚拟机规范(和本书)中所指的class文件,并非特指位于磁盘中的.class文件,而是泛指任何格式符合规范的class数据。

构成class文件的基本数据单位是字节,可以把整个class文件当成一个字节流来处理。稍大一些的数据由连续多个字节构成,这些数据在class文件中以大端(big-endian)方式存储。为了描述class文件格式,Java虚拟机规范定义了u1、u2和u4三种数据类型来表示1、2和4字节无符号整数,分别对应Go语言的uint8、uint16和uint32类型。相同类型的多条数据一般按表(table)的形式存储在class文件中。表由表头和表项(item)构成,表头是u2或u4整数。假设表头是n,后面就紧跟着n个表项数据。

Java虚拟机规范使用一种类似C语言的结构体语法来描述class文件格式。整个class文件被描述为一个ClassFile结构,代码如下:

ClassFile {
u4                          magic;
u2                          minor_version;
u2                          major_version;
u2                          constant_pool_count;
cp_info                   constant_pool[constant_pool_count-1];
u2                          access_flags;
u2                          this_class;
u2                          super_class;
u2                          interfaces_count;
u2                          interfaces[interfaces_count];
u2                          fields_count;
field_info                 fields[fields_count];
u2                          methods_count;
method_info              methods[methods_count];
u2                          attributes_count;
attribute_info          attributes[attributes_count];
}

JDK提供了一个功能强大的命令行工具javap,可以用它反编译class文件。不过从控制台观察javap的输出并不是很直观,因此笔者用JavaFX编写了一个图形化的工具,叫作classpy。有兴趣的读者可以去GitHub网站https://github.com/zxh0/classpy。下载classpy的源代码或者打包好的JAR可执行文件。后面的小节中将以ClassFileTest类为例,使用classpy程序分析class文件格式。ClassFileTest的代码如下:

package jvmgo.book.ch03;

public class ClassFileTest {
public static final boolean FLAG = true;
public static final byte BYTE = 123;
public static final char X = ' X' ;
public static final short SHORT = 12345;
public static final int INT = 123456789;
public static final long LONG = 12345678901L;
public static final float PI = 3.14f;
public static final double E = 2.71828;

public static void main(String[] args) throws RuntimeException {
  System.out.println("Hello, World! ");
}
}