Javac编译过程

简介:   Javac编译过程大致分为4个过程,分别是:词法分析语法分析语义分析代码生成词法分析  词法分析是将源代码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键字、变量名、字面量、运算符都可以成为编辑,如“int a+b=2”这句代码中包含了6个标记,分别是int、a、=、b、+、2,虽然关键字int由三个字符构成,但是它只是一个Token,不可再拆分。

  Javac编译过程大致分为4个过程,分别是:

  1. 词法分析
  2. 语法分析
  3. 语义分析
  4. 代码生成

词法分析

  词法分析是将源代码的字符流转变为标记(Token)集合,单个字符是程序编写过程的最小元素,而标记则是编译过程的最小元素,关键字、变量名、字面量、运算符都可以成为编辑,如“int a+b=2”这句代码中包含了6个标记,分别是int、a、=、b、+、2,虽然关键字int由三个字符构成,但是它只是一个Token,不可再拆分。在Javac的源码中,词法分析过程由com.sun.tools.javac.parser.Scanner类来实现。


语法分析

  词法分析器的作用是将Java源文件的字符流转变成对应的Token流。而语法分析器是将词法分析器分的Token流组件成更加结构化的语法树,也就是将一个个单词组装成一句话,一个完整的语句。哪些词语组合在一起是主语,哪些是谓语、哪些是宾语、哪些是定语等没要做进一步区分。
  语法分析是根据Token序列构造抽象语法树的过程,抽象语法树是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序代码中的一个语法结构,例如包、类型、修饰符、运算符、接口、返回值甚至代码注释等都可以是一个语法结构。语法分析过程由com.sun.tools.javac.parser.Parser类实现,这个阶段产出的抽象语法树由com.sun.tools.javc.tree.JCTree类表示,经过这个步骤之后,编译器就基本不会再对源码文件进行操作了,后续的操作都是建立在抽象语法树上。


语义分析

  语法分析之后,编译器获得了程序代码的抽象语法树表示,语法树能表示一个结构正确的源程序的抽象,但无法保证源程序是符合逻辑的。语义分析是要在语法树的基础上再做一些处理,如给类添加默认的构造函数,检查变量在使用前是否已经初始化,将一些常量进行合并处理,检查操作变量类型是否匹配,检查所有的操作语句是否可达,检查checked exception是否正确处理。
  语义分析阶段分为:填充符号表、标注检查、数据及控制流分析。

填充符号表
  符号表是由一组符号地址和符号信息构成的表格,读者可以把它想象成哈希表K-V值对的形式。符号表中所登记的信息在编译的不同阶段都要用到。在语义分析中,符号表所登记的内容将用于语义检测和产生中间代码。在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。在Javac源码中,填充符号表的过程由com.sun.tools.javac.comp.Enter类实现。
  一个类除了类本身会定义一些符号变量如类名称、变量名称和方法名称等,还有一些符号是引用其它类的,这些符号会调用其它类的方法或者变量等,还有一些类可能会继承或者实现超类和接口等。这些符号都是在其他类中定义的,那么就需要将这些类的符号也解析到符号表中。
  在Enter类解析这一步骤中,还有一个重要的步骤就是添加默认的构造函数。如果代码中没有提供任何构造函数,那么编译器将会添加一个没有参数、访问下与当前一致的默认构造函数。

标注检查
  检查的内容包括诸如变量的类型是否匹配、变量在使用前是否已经初始化、能够推导出泛型方法的参数类型、字符串常量的合并(常量折叠)。在标注检查步骤中一个重要的动作称为常量折叠,如果我们在代码中写了如下定义:

int a=1+2;

  那么在语法树上仍然能看到字面量1、2以及操作符+,但是在进过常量折叠之后,他们将会被折叠为字面量3.实现的类是com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类。

数据流分析
  数据流主要完成如下工作:

  • 检查变量在使用前是否都已经被正确赋值。
  • 保证final修饰的变量不会被重复赋值。
  • 要确定方法的返回值类型。这里需要检查方法的返回值类型是否确定,并检查接受这个方法返回值的引用类型是否匹配,如果没有返回值,则不能有任何引用类型指向方法的这个返回值。
  • 所有的Checked Exception都要捕获或者向上抛出。
  • 所有的语句都要被执行到。这里会检查是否有语句出现在一个return方法的后面,因为在return方法后面的语句永远也不会被执行到。

控制流分析
  控制流主要完成如下工作:

  • 去掉无用的代码,比如永假的if代码块。
  • 变量的自动转换,比如自动装箱拆箱。
  • 去除语法糖。解语法糖的过程由desugar()方法触发,在com.sun.tools.javac.comp.TransTypes和com.sun.tools.javac.comp.Lower类中完成。
    数据流及控制流的分析入口是flow()方法,具体操作由com.sun.tools.javac.comp.Flow类来完成。

字节码生成

  由com.sun.tools.javac.jvm.Gen类来完成。字节码阶段不仅仅把前面各个步骤所生成的信息(语法树、符号表)转化成字节码写到磁盘中,编译器还进行了少量的代码添加和转换工作。
实例构造器<init>方法和类构造器<clinit>方法就是在这个阶段添加到语法树中的。
  生成java字节码需要经过以下两个步骤:

  • 将java方法中的代码块转化成符合JVM语法的命令形式,JVM的操作都是基于栈的,所有的操作都必须经过出栈和进栈来完成。
  • 按照JVM的文件组织格式将字节码输出到以class为扩展名的文件中。

  在jdk1.5之后,java语言提供了对注解(Annotation)的支持,这些注解与普通的Java代码一样,是在运行期间发挥作用的。在Jdk1.6中提供了一组插入式注解处理器的标准API在编译期间对注解进行处理,我们可以把它看做是一组编译器的插件,在这些插件里面,可以读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止。对注解的处理是在填充符号表之后及在标注注解之前发生的。


参考

  1. 《深入理解Java虚拟机》周志明著。
  2. 《深入分析Java Web技术内幕》许令波著。
目录
相关文章
|
6月前
|
机器学习/深度学习 运维 自然语言处理
系统程序的编译与处理
系统程序的编译与处理
|
10月前
|
缓存 Java Shell
ThingsBoard详细编译指南2.4.3
ThingsBoard详细编译指南2.4.3
319 0
|
10月前
|
C++
C++程序的编译过程
C++程序的编译过程
|
自然语言处理 编译器 C语言
C/C++程序的编译过程
C/C++程序的编译过程
178 0
C/C++程序的编译过程
|
开发工具 C++ git
DCMTK-001-3.6.6编译
DCMTK-001-3.6.6编译
239 0
DCMTK-001-3.6.6编译
使用javac编译多个文件
使用javac编译多个文件
202 0
|
自然语言处理 Java
说说Javac
Java语言有Java语言的规范,,这个规范详细描述了Java语言有哪些词法、语法,而Java虚拟机也有其Java虚拟机的规范,同样Java虚拟机的规范和Java语言规范并不一样,它们都有自己的词法和语法解析规则,而且解析规则也是不同的。
1364 0
|
Python
编译过程
编译系统的运行过程 源代码 --> 机器代码 解释器运行程序的方法 1.直接运行高级编程语言 2.转换高级编程语言码到一些有效率的字节码(Bytecode),并运行这些字节码 Python解释语言特点 "拆解"代码: 首先当用户键入代码交给Python处理的时候会先进行此法分析,例如用户...
772 0