天天看点

Javac 字节码指令生成Javac 字节码指令生成

Javac 字节码指令生成

根据标注语法树进行指令翻译,将方法中一系列的语句翻译为 Java 虚拟机能够执行的一系列指令.

生成字节码指令

JVM 在执行 Class 文件中的字节码指令时,会以入栈与出栈的方式执行.

Java 虚拟机会为每个新创建的线程分配一个 Java 栈,同时分配一个 PC 寄存器,这个 PC 寄存器保存了 Java 虚拟机正在执行的字节码指令的地址。

当调用一个方法时会在这个栈上新创建一个栈桢。

每个栈桢内部都包含一组称为局部变量表的变量列表,同时还包含一个称为操作数栈的栈。

Javac 在生成字节码指令时尽可能地模拟 Java 虚拟机运行时的过程,用来进行类型验证及更好地生成字节码指令.

实现本地变量表

Javac 在 Code 类中定义了本地变量表,用来模拟 Java 虚拟机运行时的本地变量表。

每个 LocalVar 对象表示一个具体的本地变量

而 nextreg 表示 lvar 数组中下一个可使用的存储位置,初始值为 0,表示本地变量表存储的索引位置是从 0 开始的

因此 VarSymbol 类中定义的 adr 变量在字节码生成阶段保存的是当前的变量在本地变量表中存储的索引位置。

实现操作数栈

State 类是定义在 Code 类中的成员类,可以用来模拟操作数栈

com.sun.tools.javac.jvm.Code.State
Type[] stack;
public int stacksize;
           

在 State 类的构造方法中初始化 stack 数组,默认初始化大小为 16,如果栈的深度超时 16 还会进行扩容.

stacksize 指的就是当前 stack 数组的大小,由于数组索引是从 0 开始,因此 stacksize-1 就是当前栈的栈顶位置。

为了进行类型校验,Javac 使用 Type 类型的数组模拟运行时类型的入栈与出栈,这样就可以在编译期间发现更多类型相关的错误,除此之外还能得出字节码指令在运行过程中需要使用的最大栈空间 max_stack 等信息。

常见的操作操作数栈的一些方法。

1.dup() 方法

dup() 方法可以复制操作数栈

2.push() 方法

push() 方法可以向栈中压入一个类型

3.pop(Type t) 方法、pop(int n) 方法、pop1() 方法与 pop2() 方法

pop(Type t)、pop(int n)、pop1() 与 pop2() 方法都可以进行弹栈操作

常量池信息的存储

Javac 中的 Pool 类代表常量池,可以存储常量池相关的信息,为后续 Class 字节码文件中常量池的生成提供必要的数据.

定义在 Pool 类中的常见操作常量池的方法.

1.put() 方法

put() 方法向常量池中放入某个对象并返回这个对象在常量池中存储的索引

2.get() 方法

get() 方法可以获取常量池中某个对象的常量池索引.