java编译器源码详解_Java 源代码编译成 Class 文件的过程分析

原标题:Java 源代码编译成 Class 文件的过程分析

在上篇文章《》中了解到了它们各有什么优点和缺点,以及前端编译+JIT编译方式的运作过程。

下面我们详细了解Java前端编译:Java源代码编译成Class文件的过程;我们从官方JDK提供的前端编译器javac入手,用javac编译一些测试程序,调试跟踪javac源码,看看javac整个编译过程是如何实现的。

1、javac编译器

1-1、javac源码与调试

javac编译器是官方JDK中提供的前端编译器,JDK/bin目录下的javac只是一个与平台相关的调用入口,具体实现在JDK/lib目录下的tools.jar。此外,JDK6开始提供在运行时进行前端编译,默认也是调用到javac,如图:

javac是由Java语言编写的,而HotSpot虚拟机则是由C++语言编写;标准JDK中并没有提供javac的源码,而在OpenJDK中的提供;我们需要在Eclipse中调试跟踪javac源码,看整个编译过程是如何实现的。

javac编译器源码下载(JDK8):http://hg.openjdk.java.net/jdk8u/jdk8u-dev/langtools/archive/tip.tar.bz2

javac编译器源码目录:**\src\share\classes\com\sun\tools\javac

在Eclipse新建工程导入后,可以看到javac源码的目录结构如下:

javac编译器程序入口:com.sun.tools.javac.Main类中的main()方法;

运行javac程序,先是解析命令行参数,由com.sun.tools.javac.main.Main.compile()方法处理,代码片段如下:

因为没有给参数,可看到输出的是javac用法,如下:

这就是平时我们用JDK/bin/javac的用法,更多javac选项用法请参考:

调试编译文件,需要右键工程 -> Debug As -> Debug Configurations ->切换到Arguments选项卡,在Program arguments中输入我们要用javac编译的Java程序文件的路径即可;然后就可以打断点Debug运行调试了,如图:

1-2、javac编译过程

JVM规范定义了Class文件结构格式,但没有定义如何从java程序文件转化为Class文件,所以不同编译器可以有不同实现。

从javac编译器源码来看,其编译过程可以分为3个子过程:

1、解析与填充符号表过程:解析主要包括词法分析和语法分析两个过程;

2、插入式注解处理器的注解处理过程;

3、语义分析与字节码的生成过程;

如图所示(来自参考4):

javac编译动作入口: com.sun.tools.javac.main.JavaCompiler类;

3个编译过程逻辑集中在这个类的compile()和compile2()方法;

如图所示:

1-3、javac中的访问者模式

访问者模式可以将数据结构和对数据结构的操作解耦,使得增加对数据结构的操作不需要修改数据结构,也不必修改原有的操作,而执行时再定义新的Visitor实现者就行了。

Javac经过第一步解析(词法分析和语法分析),会生成用来一棵描述程序代码语法结构的抽象语法树,每个节点都代表程序代码中的一个语法结构,包括:包、类型、修饰符、运算符、接口、返回值、甚至注释等;而后的不同编译阶段都定义了不同的访问者去处理该语法树(节点)。

了解这些更容易理解javac的编译过程实现,而后面分析过程中会再对访问者模式的实现作相关说明。

2、解析与填充符号表

2-1、解析:词法、语法分析

解析包括:词法分析和语法分析两个过程;

2-1-1、词法分析

1、概念解理

词法分析是将源代码的字符流转变为标记(Token)集合;

标记:

标记是编译过程的最小元素;

包括关键字、变量名、字面量、运算符(甚至一个”.”)等;

2、源码分析:

由com.sun.tools.javac.parser.Scanner类实现对外部提供服务;

由com.sun.tools.javac.parser.JavaTokenizer类实现具体的Token分析动作(JavaTokenizer.readToken()方法);

Scanner.nextToken()调用JavaTokenizer.readToken()方法读取下一个Token;

返回com.sun.tools.javac.parser.Tokens.Token类实例表示的一个Token;

Scanner.nextToken()方法如下:

注意,下面语法分析时才会不断调用Scanner.nextToken()读取一个个Token进来解析。

2-1-2、语法分析

1、概念解理

语法分析是根据Token序列构造抽象语法树的过程;

抽象语法树(Abstract Syntax Tree,AST):

是一种用来描述程序代码语法结构的树形表示方式;

每个节点都代表程序代码中的一个语法结构;

语法结构(Construct)包括:包、类型、修饰符、运算符、接口、返回值、甚至注释等;

2、源码分析:

由com.sun.tools.javac.parser.JavacParser类完成整个过程,该类实现com.sun.tools.javac.parser.Parser接口;

一个类文件解析产生的抽象语法树的所有内容保存在JCCompilationUnit类实例里,JCCompilationUnit类是由com.sun.tools.javac.tree.JCTree类扩展;

JCTree是个抽象类,实现了Tree接口,Tree接口里有一个” R accept(TreeVisitor visitor, D data)”方法用来接收访问者,所以Tree接口是访问者模式中的抽象节点元素;

JCTree类中有一个Visitor内部类,同时也是一个抽象类,作为访问者模式中的抽象访问者;

一个JCTree类实例相当于抽象语法树的一个节点,它会扩展许多类型,对应不同语法结构类型的树节点,如JCStatement,JCClassDecl,JCMethodDecl,JCBlock等等,这些类是访问者模式中的具体节点元素;

JCTree扩展的JCMethodDecl方法类型节点结构如下:

代码执行的解析过程,如下:

1)、由JavaCompiler.compile()方法调用JavaCompiler.parseFiles()方法完成参数输入的所有文件的编译;

2)、JavaCompiler.parseFiles()方法中又调用本类中的parse()方法对其中一个文件进行编译;

该方法中生成JavacParser类实例,然后调用该实例的parseCompilationUnit()方法开始进行整个文件的解析(包括”package”包名),如下:

Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, lineDebugInfo); tree = parser.parseCompilationUnit();

返回的tree是JCCompilationUnit类型实例,保存了一个类文件解析产生的抽象语法树的所有内容,也可以说是抽象语法树的根节点;

3)、JavacParser.parseCompilationUnit()方法中调用JavacParser.typeDeclaration()进行文件中所有类型定义的解析;

JavacParser.typeDeclaration()又调用JavacParser.classOrInterfaceOrEnumDeclaration()进行类或接口的解析;

如果是类又调用classDeclaration()对该类进行解析….

JCTree def = typeDeclaration(mods, docComment);

返回一个JCTree类实例表示文件中所有类型定义定义的语法树(不包括”package”包名);

这期间会不断调用Scanner.nextToken()读取一个个Token进来解析;

3、编译测试:

下面我们用javac编译JavacTest.java文件来跟踪整个解析过程,测试文件代码如下:

package com.jvmtest; publicclassJavacTest{ privateint i; publicintgetI(){ return i; } publicvoidsetI(int i){ this.i = i; } }

对于解析JavacTest.java文件生成的抽象语法树,由返回的JCCompilationUnit类实例表示,如下图所示:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值