http://blog.youkuaiyun.com/larrylgq/article/details/7395261
java源码级编译器的任务是将符合java语言规范的源码编译为符合jvm规范的Class文件,对不符合java语言规范的报错
在sun的jdk中源码编译器是使用java写的javac
javac的工作流程:
1 解析(parse)和输入到符号表(enter)
2 注解处理(annotation processing)
3 分析与代码的生成
解析:
词法分析:使用com.sun.tools.javac.parser.Scanner类,将代码字符串变为Token序列
eg:
int y = 1;
->Token.INT(name:int)->Token.IDENTIFIER(name:y))+ Token.EQ(name:=))+ Token.INTLITERAL(stringVal:1))+ Token.SEMI(name:;))
语法分析:是一个递归下降且运算符优先级的语法分析器,使用com.sun.tools.javac.parser.Parser类,根据语法由Token序列生成抽象语法树
eg:
输入到符号表:
使用com.sun.tools.javac.comp.Enter类,此过程每个编译单元的抽象语法树的顶局节点都先被放到待处理列表中并逐个处理列表中的节点
所有的类符号被输入到外围作用域的符号表中
确定类的参数(对泛型类型而言)、超类型和接口
如果需要添加默认构造器
将类中出现的符号输入到类自身的符号表中
分析和检查代码中的annotation
注解处理:annotation processing
使用com.sun.tools.javac.processing.JavacProcessingEnvironment类,并且支持用户自定义的annotation
编译时lombook对java文件进行编译之后会再次进入Parse and Enter步骤
标注(Attr)和检查(Check):
是语义分析的一个步骤,使用com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类
主要作用有:
将语法树中名字、表达式等元素与变量、方法、类型等联系到一起
检查变量使用前是否已声明
推导泛型方法的类型参数
检查类型匹配性
进行常量折叠(将常量语法树合并)
数据流分析(Flow):
使用com.sun.tools.javac.comp.Flow类
检查所有语句都可到达
检查所有checked exception都被捕获或抛出
检查所有局部变量在使用前必项确定性赋值
检查有返回值的方法必须确定性返回值
检查变量的确定性且不重复赋值
转换类型:(TransTypes)
使用com.sun.tools.javac.comp.TransTypes主要作用是将泛型java代码转换成普通的java代码
eg:
List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);
转换后:
List list = Arrays.asList(1, 2, 3);
int i = (Integer)list.get(0);为了保证泛型的语义,增加了强制类型转换
解除语法糖:(Lower)
使用com.sun.tools.javac.comp.Lower类
消除if(false){...}形式的无用代码
将含有语法糖的语法树改写为含有简单语言结构的语法树
eg
具名内部类,匿名内部类,类字面量
自动装箱拆箱
断言
foreach循环
enum或String类型的switch(java7)
eg:
List<Integer> list = Arrays.asList(1, 2, 3);
int i = list.get(0);
转换后:
List list = Arrays.asList(//自动装箱拆箱
new Integer[]{
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3)
}
);
int i = ((Integer)list.get(0)).intValue();
生成class文件:(Gen)
使用com.sun.tool.javac.jvm.Gen
将实例成员初始化器收集到构造器中成为<init>()
将静态成员初始化器收集为<clinit>()
从抽象语法树生成字节码
后序遍历语法树
进行最后的少量代码转换
String的+操作被生成为StringBuilder操作
x++/x--在条件允许时被优化为++x/--x
etc …
从符号表生成Class文件
生成Class文件的结构信息
生成元数据(包括常量池)
class文件所记录的信息:
结构信息
Class文件格式版本号
各部分的数量及大小
元数据
类、继承的超类、实现接口的声明信息
域与方法声明信息
常量池
用户自定义的、RetentionPolicy为CLASS或RUNTIME的注解
方法信息
字节码(程序逻辑)
异常处理器表
操作数栈与局部变量区大小
操作数栈的类型记录(java6开始 StackMapTable)
调试用符号信息(LineNumberTable,LocalVariableTable)
class文件例子:
import java.io.Serializable;
public class Foo implements Serializable {
public void bar() {
int i = 31;
if (i > 0) {
int j = 42;
}
}
}
执行编译:javac -g Foo.java
执行反编译:javap -c -s -l -verbose Foo
类声明:
public class Foo extends java.lang.Object implements java.io.Serializable
源文件名:
SourceFile: "Foo.java"
Class文件结构信息:
minor version: 0
major version: 50
常量池:
Constant pool:
const #1 = Method #3.#19; // java/lang/Object."<init>":()V
const #2 = class #20; // Foo
const #3 = class #21; // java/lang/Object
const #4 = class #22; // java/io/Serializable
const #5 = Asciz <init>;
const #6 = Asciz ()V;
const #7 = Asciz Code;
const #8 = Asciz LineNumberTable;
const #9 = Asciz LocalVariableTable;
const #10 = Asciz this;
const #11 = Asciz LFoo;;
const #12 = Asciz bar;
const #13 = Asciz j;
const #14 = Asciz I;
const #15 = Asciz i;
const #16 = Asciz StackMapTable;
const #17 = Asciz SourceFile;
const #18 = Asciz Foo.java;
const #19 = NameAndType #5:#6;// "<init>":()V
const #20 = Asciz Foo;
const #21 = Asciz java/lang/Object;
const #22 = Asciz java/io/Serializable;//引入包时使用*通配,从Class文件来看没有任何不同,只不过使用通配符增加了源码中名字冲突的可能性而已。
方法元数据:
public Foo();
Signature: ()V
LineNumberTable:
line 2: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LFoo;
Code:
Stack=1, Locals=1, Args_size=1
字节码:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
方法元数据:
public void bar();
Signature: ()V
LineNumberTable:
line 4: 0
line 5: 3
line 6: 7
line 8: 10
LocalVariableTable: // 参数名跟局部变量名都保存在LocalVariableTable;返个属性表跟方法体联系在一起,而接口的方法没有方法体,
Start Length Slot Name Signature //所以ASM无法从接口类型上的方法取得参数的名称
10 0 2 j I
0 11 0 this LFoo;
3 8 1 i I
StackMapTable: number_of_entries = 1
frame_type = 252 /* append */
offset_delta = 10
locals = [ int ]
Code:
Stack=1, Locals=3, Args_size=1
字节码:
0: bipush 31
2: istore_1
3: iload_1
4: ifle 10
7: bipush 42
9: istore_2
10: return
Class文件与jdk版本比较:
JDK版本Class文件版本(major.minor)
1.0 --------45.3
1.1 --------45.3
1.2 --------46.0
1.3 --------47.0
1.4 --------48.0
5 --------49.0
6 --------50.0
7 --------51.0