深入分析javac编译原理

     2022-04-13     386

关键词:

通常,一个java文件会通过编译器编译成字节码文件.class,再又java虚拟机JVM翻译成计算机可执行的文件。

我们所知道的java语言有它自己的语法规范,同样的JVM也有它的语法规范,如何让java的语法规则去适应语法解析规则,这就是javac的作用,简而言之,javac的作用就是将java源代码转化成class字节码文件。

Javac编译器的基本结构
编译步骤
技术分享图片

  1. 词法分析器:
    1.1作用:
    将源码转化为Token流

1.2流程
读取源代码,从源文件的一个字符开始,按照java语法规范依次找出package,import,类定义,属性,方法定义等,最后构建出一个抽象语法树

1.3举例

package compile;

/**
 * 词法解析器
 */
 public class Cifa{
     int a;
     int c = a + 1;
 }

转化为Token流:
技术分享图片
1.4源码分析
com.sun.tools.javac.parser.JavacParser 规定哪些词符合Java语言规范,具体读取和归类不同词法的操作由scanner完成
com.sun.tools.javac.parser.Scanner 负责逐个读取源代码的单个字符,然后解析符合Java语言规范的Token序列,调用一次nextToken()都构造一个Token
com.sun.tools.javac.parser.Tokens$TokenKind 里面包含了所有token的类型,譬如BOOLEAN,BREAK,BYTE,CASE。
com.sun.tools.javac.util.Names 用来存储和表示解析后的词法,每个字符集合都会是一个Name对象,所有的对象都存储在Name.Table这个内部类中。
com.sun.tools.javac.parser.KeyWords 负责将字符集合对应到token集合中,如,packagezxy.demo.com; Token.PACKAGE = package, Token.IDENTIFIER =zxy.demo.com,(这部分又分为读取第一个token,为zxy,判断下一个token是否为“.”,是的话接着读取下一个Token.IDENTIFIER类型的token,反复直至下一个token不是”.”,也就是说下一个不是Token.IDENIFIER类型的token,Token.SEMI = ;即这个TIDENTIFIER类型的token的Name读完),KeyWords类负责此任务。
1.5问题
Javac是如何分辨这一个个Token呢?例如它时如何直到package是关键词而不是自定义变量呢?
Javac在进行此法分析时会由JavacParser根据Java语言规范来控制什么顺序,地方会出现什么Token,例如package就只能在文件的最开头出现

Javac怎样确定哪些字符组合在一起就是一个Token呢?它如何从一串字符流中划分出Token来?
对于关键字,主要由关键字的语法规则,例如package就是若一个字符串package是连续的,那么他就是关键字

对于自定义变量名称,自定义名称之间用空格隔开,每个语法表达式用分号结束

举例:
int a = 1 + 2;

从package开始

.....

int 就是通过语法关键字判定的TOKEN:INT

int a之间通过空格隔开

a 就是自定义的变量被判定为TOKEN:IDENTIFIER

a =之间通过空格隔开(这时有的小伙伴就会说了, int a=b+c;这句话也不报错啊 ,对的,大多数时候,这种不用空格分开确实能够编译, 这是因为java指出声明变量的时候必须以字母、下划线或者美元符开头,当JavacParser读完a去读=的时候就直到这个=不属于变量了 )将=判定为TOKEN:EQ

1被判定为TOKEN:INTLITERAL

.....

将;识别为TOKEN:SEMI

.....

最后读取到类结束,也就是}被判定为TOKEN:RBRACE

2.语法分析器:
刚才,词法解析器已经将Java源文件解析成了Token流。

现在,语法解析器就要将Token流组建成更加结构化的语法树。也就是将这些Token流中的单词装成一句话,完整的语句。

2.1作用
将进行词法分析后形成的Token流中的一个个Token组成一句句话,检查这一句句话是不是符合Java语言规范。

2.2语法分析三部分
package
import
类(包含class、interface、enum),一下提到的类泛指这三类,并不单单是指class
2.3所用类库
com.sun.tools.javac.tree.TreeMaker 所有语法节点都是由它生成的,根据Name对象构建一个语法节点
com.sun.tools.javac.tree.JCTree$JCIf 所有的节点都会继承jctree和实现**tree,譬如 JCIf extends JCTree.JCStatement implements IfTree
com.sun.tools.javac.tree.JCTree的三个属性
Tree tag:每个语法节点都会以整数的形式表示,下一个节点在上一个节点上加1;
复制代码

pos:也是一个整数,它存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,而-1表示不存在

type:它代表的是这个节点是什么java类型,如int,float,还是string等
2.4 举例

package compile;

/**
 * 语法
 */
public class Yufa {
    int a;
    private int c = a + 1;

    //getter
    public int getC() {
        return c;
    }
    //setter
    public void setC(int c) {
        this.c = c;
    }
}

技术分享图片
每一个包package下的所有类都会放在一个JCCompilationUnit节点下,在该节点下包含:package语法树(作为pid)、各个类的语法树
每一个从JCClassDecl发出的分支都是一个完整的代码块,上述是四个分支,对应我们代码中的两行属性操作语句和两个方法块代码块,这样其实就完成了语法分析器的作用:将一个个Token单词组成了一句句话(或者说成一句句代码块)
在上述的语法树部分,对于属性操作部分是完整的,但是对于两个方法块,省略了一些语法节点,例如:方法修饰符public、方法返回类型、方法参数。
注1:若类中有import关键字则途中还有import的语法节点

注2:所有语法节点的生成都是在TreeMaker类中完成的

3.语法分析器
3.1作用
将语法树转化为注解语法树,即在这颗语法树上做一些处理

3.2步骤
给类添加默认构造函数(由com.sun.tools.javac.comp.Enter类完成)
处理注解(由com.sun.tools.javac.processing.JavacProcessingEnvironment类完成)
检查语义的合法性并进行逻辑判断(由com.sun.tools.javac.comp.Attr完成)
变量的类型是否匹配
变量在使用前是否初始化
能够推导出泛型方法的参数类型
字符串常量合并

数据流分析(由com.sun.tools.javac.comp.Flow类完成)
检验变量是否被正确赋值(eg.有返回值的方法必须确定有返回值)
保证final变量不会被重复修饰
确定方法的返回值类型
所有的检查型异常是否抛出或捕获
所有的语句都要被执行到(return后边的语句就不会被执行到,除了finally块儿)

对语法树进行语义分析(由com.sun.tools.javac.comp.Flow执行)
去掉无用的代码,如只有永假的if代码块
变量的自动转换,如将int自动包装为Integer类型
去除语法糖,将foreach的形式转化为更简单的for循环

最终,生成了注解语法树

3.3所用类库
com.sun.tools.javac.comp.Check,它用来辅助Attr类检查语法树中变量类型是否正确,如方法返回值是否和接收的引用值类型匹配
com.sun.tools.javac.comp.Resolve,用来检查变量,方法或者类的访问是否合法,变量是否是静态变量
com.sun.tools.javac.comp.ConstFold,将一个字符串常量中的多个字符合并成一个字符串
com.sun.tools.javac.comp.Infer,帮助推导泛型方法的参数类型
3.4举例
变量自动转化

public class Yuyi{
    public static void main(String agrs[]){
        Integer i = 1;
        Long l = i + 2L;
        System.out.println(l);
    }
}
//经过自动转换后
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        Integer i = Integer.valueOf(1);
        Long l = Long.valueOf(i.intValue() + 2L);
        System.out.println(l);
    }
}

解除语法

public class Yuyi{
    public static void main(String agrs[]){
        int[] array = {1,2,3};
        for (int i : array){
            System.out.println(i);
        }
    }
}
//解除语法糖后
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        int[] arrays = {1,2,3};
        for (int[] arr$ = array,len$=arr$.length,i$=0; i$<len$; ++i$){
            int i = arr$[i$];
            {
                System.out.println(i);
            }
        }
    }
}

内部类解析

public class Yuyi{
    public static void main(String agrs[]){
        Inner inner = new Inner();
        inner.print();
    }
    class Inner{
        public void print(){
            System.out.println("Yuyi$Inner.print");
        }
    }
}
//转化后的代码如下
public class Yuyi{
    public Yuyi(){
        super();
    }
    public static void main(String agrs[]){
        Yuyi$Inner inner = new Yuyi$Inner(this);
        inner.print();
    }
    {
    }
}
class Yuyi$Inner{
    /*synthetic*/ final Yuyi this$0;

    Yuyi$Inner(/*synthetic*/final Yuyi this$0){
        this.this$0 = this$0;
        super();
    }

    public void print(){
        System.out.println("Yuyi$Inner.print");
    }
}

深入理解java虚拟机(10-13)学习总结(代码片段)

注:本文参考学习周志明老师的《深入理解Java虚拟机(第3版)》第10章前端编译与优化10.1概述前端编译器就是把*.java文件转变成*.class文件过程。也可能是JIT运行期把字节码转变为本地机器码的过程。前端编译器࿱... 查看详情

javac编译原理

...规范转换成java虚拟机语言规范。编译流程:流程:词法分析器:将源码转换为Token流将源代码划分成一个个Token(找出java语言中的if,else, 查看详情

深入分析javaweb技术内幕修订版和原版的区别

参考技术A《深入分析JavaWeb技术内幕(修订版)》新增了淘宝在无线端的应用实践,包括:CDN动态加速、多终端化改造、多终端Session统一,以及在大流量的情况下,如何跨越性能、网络和一个地区的电力瓶颈等内容,并提供了比... 查看详情

java技术专题「编译器专题」深入分析探究“静态编译器”(javaideaecj编译器)是否可以实现代码优化?(代码片段)

技术分析大家都知道Eclipse已经实现了自己的编译器,命名为Eclipse编译器forJava(ECJ)。ECJ是EclipseCompilerforJava的缩写,是JavaTM认可的Java编译工具(类似javac)。可以单独下载使用。IDEA所支持的编译器,也有几种... 查看详情

66.javac编译与jit编译编译过程javac编译词法语法分析填充符号表语义分析字节码生成jit编译

...ac编译与JIT编译66.1.编译过程66.2.javac编译66.2.1.词法、语法分析66.2.2.填充符号表66.2.3.语义分析66.2.4.字节码生成66.3.JIT编译66.javac编译与JIT编译66.1.编译过程不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成... 查看详情

编译原理

...译原理有什么作用:1.可以为今后的考研做准备;2.可以深入的了解别人编译器的操作以致于不会成为别人的 查看详情

深入java虚拟机之七:javac编译与jit编译

转载地址:https://blog.csdn.net/ns_code/article/details/18009455 编译过程   不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个... 查看详情

深入java虚拟机之七:javac编译与jit编译

转载请注明出处:http://blog.csdn.net/ns_code/article/details/18009455编译过程  不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各... 查看详情

jvm技术专题深入分析字节码指令重排序技术「原理篇」(代码片段)

前提概要指令重排序有两类,编译器重排序和处理器重排序。(至于内存系统指令重排较为复杂不是本章重点)重排序分为两类:编译期重排序和运行期重排序,分别对应编译时和运行时环境。编译器重排序... 查看详情

java-深入理解java中的逃逸分析(代码片段)

在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件。第二段编译是把.class转换成机器指令的过程。第一段编译就是javac命令。在... 查看详情

深入浅出java并发编程指南「原理分析篇」深入分析aqs的工作原理(前传)

查看详情

编译原理pdf

...简介  · · · · · ·本书全面、深入地探讨了编译器设计方面的重要主题,包括词法分析、语法分析、语法制导定义和语法制导翻译、运行时刻环境、目标代码生成、代码优化技术、并行性检测以及过程... 查看详情

jvm技术专题深入分析class字节码指令方法调用详解「原理篇」(代码片段)

方法调用详解​调用目标在程序代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析。解析​在Java语言中符合**“编译期可知,运行期不可变”**这个要求的方法,主要包括静态方法和私有方法两... 查看详情

深入分析volatile的实现原理

...改的值。它在某些情况下比synchronized的开销更小,本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助 查看详情

深入分析synchronized的实现原理

深入分析synchronized的实现原理 记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决多线程情况... 查看详情

javac编译器(代码片段)

...注解处理器。解析与填充符号表过程,包括:词法、语法分析,将源代码的字符流转变为标记集合,构造出抽象语法树。填充符号表,产生符号地址和符号信息。插入式注解处理器的注解处理过程。可以把插入式注解处理器看作... 查看详情

转载深入理解phpopcode缓存原理

转载地址:深入理解PHPOpcode缓存原理什么是opcode缓存?当解释器完成对脚本代码的分析后,便将它们生成可以直接运行的中间代码,也称为操作码(OperateCode,opcode)。Opcodecache的目地是避免重复编译,减少CPU和内存开销。如果... 查看详情

flutter实践深入分析之——flutteractivity/fragment原理流程分析(代码片段)

文章目录前言FlutterActivity分析FlutterActivityAndFragmentDelegate.Host分析FlutterActivityAndFragmentDelegate分析onAttach方法onStart方法onCreateView方法FlutterSplashView分析FlutterFragment分析FlutterFragmentActivity分析onCrea 查看详情