javac编译原理进阶(对此篇的完善):http://blog.csdn.net/dhcao1112/article/details/79133792
从Java源代码经过编译器编译为jvm虚拟机认识的字节码文件:.java -- .class 。
javac编译器:能将一种语言规范转化为另一种语言规范。
步骤:*.java文件--读取源代码--整理成Token流--分析Token流--整理成语法树--语法转化--代码生成器--.class
四大组件:词法分析器,语法分析器,语义分析器,代码生成器。
词法分析器:读取Java源代码,将每个词读取出来整理成对应的Token流。
语法分析器:检查Token流中的关键词组合起来是否符合Java语言规范,形成一个结构化的语法表达方式:抽象语法树(符合Java语言规范)。
语义分析器:将复杂的语法转换为简单的、基本的语法:foreach转换为for循环,静态变量的确定值,注解的分析。形成一个注解过后的抽象语法树:注解语法树。
代码生成器:进一步将注解过的抽象语法树分析,生成符合JVM语言规范的.class字节码。
例如:package aaa;
当一个简单的Java类经过编译器时。由词法分析器(主要接口类:com.sun.tools.javac.parser.Lexer)逐个读取Java源代码,一个一个字符的分析,解析成符合Java语言规范的Token流。
代码过程:主要两个实现类:Scanner和JavaParser。Scanner:读取和归类不同词法。JavacParser:规定了哪些词是符合Java语言规定的。1、在Token(com.sun.tools.javac.parser.Token)中:有许多的元素,什么Token.PACKAGE,Token.PUBLIC等等。2、Java源码中的所有字符集合都创建成一个Name对象,所有的Name对象存在Name.Table这个内部类中。3、有个类叫做Keywords,它保存Name.Table和Token中的对应关系,所有的Java关键字在这个类中都能找到,像什么package,public,int....等等,这个关键字就在Token中对应Token.PACKAGE,Token.PUBLIC,Token.INT...等等。4、词法分析就是让Scanner类将Java源代码中的字符集转换为Token流。
更细节:例如:在读取到package时,会对应的创建Token.PACKAGE,然后会进入package处理环节,连续的读取Token,读取到Token.COMPILE就是aaa是包名,直到读取“;”表示结束。保证读取到的package后面必须接着包名,而且多级结构package a.b.c.d ; 必须中间由"."连接,读取到这个之后,就会形成一个符合Java规范的Token流,如果读取不符合规范呢?那就会编译报错,在编译器的直接体现就是会有 ‘红线’ 提示。
语法分析器:将词法分析器的Token流组建成更加结构化的语法树,可以理解为将一个个单独的单词组装为一句句带有明确意义的话。
javac语法树:可以理解为结构化的java语法组成的集合,其组成节点成为语法节点,例如:IfTree(if语法节点)表示的是一个if类型的表达式,for循环的表达式称作for语法节点。每个语法节点都是com.sun.tools.javac.tree.JCTree 的一个实例。
代码过程:例子还是 package aaa;
在进行词法分析这句代码时,解析到词:package。会调用TreeMaker(所有的语法节点都在这个类中生成,跟词法分析器中的JavacParser一样,它定义了所有的语法节点的生成规则)类:根据Name对象构建一个语法节点:JCIdent语法节点。如果包名是多级目录,将构建成JCFieldAccess语法节点(允许嵌套)。因为package语法规则相对简单,如果:package aaa.b.c.*;这种多级目录,那么建立的package语法节点就是包含多级目录的节点,可以想象成一个树枝的枝丫。这个package语法节点具有一定的嵌套结构,但一定是符合java的package语法规则的。否则报错。
语法数的建立:
一个Java类。对应一棵语法树。编译器在读取类的时候,在词法分析的同时,根据词法分析的结果构建对应的语法节点,当把所有的源代码解析完毕,就形成了一个更加结构话的语法树。它用另外的方式表达java语言的含义,但更加的趋近JVM所能识别。
在经过词法分析器和语法分析器之后,那么这段代码就能生成字节码了么,肯定是不行的,还要经过语义分析器。关于语义分析器的作用,不需要多说。直接贴出各个类的功能就能理解。
com.sun.tools.javac.comp.Enter:
1、将所有类中出现的符号输入到类自身的符号表中,例如:类名称,变量名称和方法名等。包含定义在其父类或者超类的符号!
2、从上往下解析语法树,将所有的符号都输入到符号表。
com.sun.tools.javac.procession.JavacProcessingEnvironment:
1、处理annotation注解。
com.sun.tools.javac.comp.Attr及其相关辅助类:标注检查
1、变量的类型是否匹配。
2、变量在使用前是否已经初始化。
3、能够推导出泛型方法的参数列表。
4、字符串常量的合并。
com.sun.tools.javac.comp.Flow:分析数据流
1、检查变量使用前是否被正确赋值。
2、保证final修饰的变量不会被重复赋值。
3、确定方法的返回值类型。
4、所有的Checked Exception都要捕获或者向上抛出。
5、所有的语句都要被执行到。
6、去掉无用的代码,比如 if(false) 后的语句。
7、变量的自动转换:包装类的自动拆封装。
8、去除语法糖:比如讲foreach转换为简单的for语句。
在语义分析器之后,语法树已经相当完善,而且较为接近JVM可操作的规范了。接下来,javac会调用com.sun.tools.javac.jvm.Gen类。
com.sun.tools.javac.jvm.Gen:遍历语法树,生成以.class为后缀的字节码文件。
总结:可见这四个过程都会遍历语法树,每次都对对语法树进行各自的操作,以完善语法树的结构,使得语法树最后达到可以生成字节码的程度。