1、Javac概述

编译器可以将编程语言的代码转换为其他形式,如Javac,将Java语言转换为虚拟机能够识别的.class文件形式。而这种将java源代码(以.java做为文件存储格式)转换为class文件格式的过程一般也称为编译器的前端。要将字节码变为机器码还需要后端编译器,如JIT编译器(Just In Time Compiler)。或者还可以通过AOT编译器直接将Java源代码编译为本地机器代码。本书涉及的主要内容就是Sun的Javac编译器。

javac1.7中没有使用像Lex、YACC这样的生成器工具,词法、语法分析与代码生成全都是手工实现的,具有简单、灵活、高效的特点

在转换的过程中自然要遵循各种各样的规范,涉及到的主要的规范有:

(1)java语法规范The Java Language Specification (JLS)

(2)虚拟机规范 The Java Virtual Machine Specification (JVMS) 所定义

(3)同时,该编译器会处理注解,这是被Pluggable Annotation Processing API (JSR 269). 所定义的. 同样,该编译器还支持 the Java Compiler API (JSR 199)

Javac将Java源代码转变为字节码的过程中涉及到词法分析、语法分析、语义分析及代码生成等阶段,如下图所示。

(1)词法分析

词法分析的主要作用就是将源码转换为Token流,如下示例。

package compile;
package->PACKAGE
compile->IDENTIFIER
;->SEMI

public class TJavac { String v = "helloworld!"; }
public->IDENTIFIER
class ->CLASS
TJavac->IDENTIFIER
{->BRACE
String->STRING
v->IDENTIFIER
=->EQ
"helloworld!"->STRINGLITERAL
;->SEMI
}->	RBRACE

可以看到,词法分析过程将Java源代码按照Java关键字、自定义关键字、符号等按顺序分解为了可识别的Token流。

(2)语法分析

将进行词法分析后形成的Token流中的Token组合成遵循Java语法规范的语法节点,形成一颗基本的语法树。如下图所示。

(3)语义分析

语义分析过程最为复杂,这个过程涉及到的细节众多,除了对代码编写者写出的代码根据JLS规范进行严格的检查外,还必须为后面的代码生成阶段准备各种数据,如符号表、标注抽象语法树节点的符号及类型等。上面例子中是否可将常量字符串"helloworld!"赋值给类型为String的变量v也是在这一阶段做校验。

(4)代码生成

将语义分析后的注解语法树转化成字节码,并将字节码写入*.class文件。

  • 将java的代码块转化为符合JVM语法的命令形式,这就是字节码,然后
  • 按照JVM的文件组织格式将字节码输出到*.class文件中

2、Javac源码与调试

首先需要下载openJDK源码,本书涉及的源码都是基于JDK7的,所以读者也可以到https://download.java.net/openjdk/jdk7下载源代码的zip包,下载的包为

openjdk-7-fcs-src-b147-27_jun_2011.zip

解压后在openjdk/langtools/src/share/classes/com/sun/tools路径下找到javac,在Eclipse中创建一个java项目,然后将javac的源代码复制到该项目中, 如图所示:

还需要com\sun\source包下的类。

由于tools.jar中也会包含Javac编译器的.class文件,所以为了避免API引用的混乱,这里需要将tools.jar从classpath中排除。

Java SE 6 之后自身集成了运行时编译的组件:javax.tools,存放在 tools.jar 包里,可以实现 Java 源代码编译,帮助扩展静态应用程序。该包中提供主要类可以从 Java String、StringBuffer 或其他 CharSequence 中获取源代码并进行编译。

javac源代码结构说明 用官方的一张图进行说明:

在com.sun.tools.javac下有如下几个包,现说明如下:

1. api –> 实现了JavaCompiler 和javax.tools中其他的api

2. code –> 定义了Java程序的语义元素的表示,如符号、作用域和类型,在javax.lang.model.*.中实现.

3.comp –> 编译器的主要处理阶段,如标记、流分析、“解语法糖”和擦除

4. file –> 使用java.nio.file 的api来访问本地的文件系统.

5. jvm –> 读取和写class文件,生成字节码

6. main –> 编译的主要驱动代码,提供了多样的编译步骤选项

7.model –> javax.lang.model.*. 的额外实现类

8.parser –> 读取java源文件生成语法树

9.processing –> 实现了在javax.annotation.processing.*定义的api

10.resources –> 信息本地化和版本信息的资源文件

11. tree –> 编译器的语法树的表示和实用类,实现了com.sun.source.*.中定义的api

12. util –> 工具类

参考文章:https://blog.csdn.net/qq_26000415/article/details/82254426

另外还有javac的测试用例,在openjdk\langtools\test\tools\javac下,可以导入。

4、Javac支持命令及相关实现

Javac提供了一些命令,用于编译Java源文件,如果安装且配置了Java的Path路径,可在Windows的命令行窗口中键入java -help命令查看、或者直接查看Javac源码中的枚举类OptionName,其中定义了Javac支持的所有命令。

下面简单介绍几个命令,其它相关的命令将在后续使用到时再介绍。

-help

-version

-d

-s

这些都是标准且常见的命令,还有另外一些不常用的扩展命令如-Xlint及隐藏命令如-fullversion,这些隐藏命令无法通过-help进行查看。

JavacOption接口定义了对这些命令的一些常用操作,并且通过内部枚举类OptionKind将所有命令分为三类,如下:

enum OptionKind {
        NORMAL,
        EXTENDED,
        HIDDEN,
}

JavacOption接口中定义的方法如下:

public interface JavacOption {

    OptionKind getKind();

    /** Does this option take a (separate) operand?
     *  @return true if this option takes a separate operand
     */
    boolean hasArg();

    /** Does argument string match option pattern?
     *  @param arg   the command line argument string
     *  @return true if {@code arg} matches this option
     */
    boolean matches(String arg);

    /** Process an option with an argument.
     *  @param options the accumulated set of analyzed options
     *  @param option  the option to be processed
     *  @param arg     the arg for the option to be processed
     *  @return true if an error was detected
     */
    boolean process(Options options, String option, String arg);

    /** Process the option with no argument.
     *  @param options the accumulated set of analyzed options
     *  @param option  the option to be processed
     *  @return true if an error was detected
     */
    boolean process(Options options, String option);

}

相关类对这个接口进行了实现,如下:

调用Option的getKind方法返回为NORMAL,HiddenOption为HIDDEN,XOption为EXTENDED。所有命令可通过继承这三个实现类,选择性的实现相关的方法。Javac具体的实现在RecognizedOptions类中的getAll()方法,

通过匿名类来改写实现类中方法的默认行为,如:

new Option(VERSION,"opt.version") {
            @Override
            public boolean process(Options options, String option) {
                helper.printVersion();
                return super.process(options, option);
            }
}

对version命令的process()方法进行了实现,通过调用helper对象的prinVersion()方法打印当前的JDK版本。 

每个命令对应不同的匿名类,这些匿名类最终会存储到Option数组中返回给getAll()方法的调用者。

还记得上面在实现version命令时传入的helper对象吗?这个对象的接口类型为OptionHelper,从名字也不难看出,它是用来辅助实现命令的,下面来具体看看这个接口的实现,如下代码:

public interface OptionHelper {
    void setOut(PrintWriter out);
    void error(String key, Object... args);
    void printVersion();
    void printFullVersion();
    void printHelp();
    void printXhelp();
    void addFile(File f);
    void addClassName(String s);
} 

当我们有了这样一个辅助类后就可以调用getAll()方法获取所有的命令了,Bootstrap类中定义了一个私有的recognizedOptions属性,如下:

OptionHelper optionHelper = new OptionHelper() {
        public void setOut(PrintWriter out) {
            Bootstrap.this.out = out;
        }
        public void error(String key, Object... args) {
            Bootstrap.this.error(key, args);
        }
        public void printVersion() {
            Log.printLines(out, getLocalizedString("version", ownName,  JavaCompiler.version()));
        }
        public void printFullVersion() {
            Log.printLines(out, getLocalizedString("fullVersion", ownName,  JavaCompiler.fullVersion()));
        }
        public void printHelp() {
            help();
        }
        public void printXhelp() {
            xhelp();
        }
        public void addFile(File f) {
            filenames.add(f);
        }
        public void addClassName(String s) {
            classnames.append(s);
        }
    };
    private Option[] recognizedOptions = RecognizedOptions.getJavaCompilerOptions(optionHelper);

调用getJavaCompilerOptions()方法其实也是间接调用了getAll()方法。现在我们可以传入命令-version来查看Javac是如何处理用户传递过来的命令的。

调用时最终会调用如下方法:

public int compile(String[] args,
   String[] classNames,
   Context context,
   List<JavaFileObject> fileObjects,
   Iterable<? extends Processor> processors)

这个方法传递的参数有点多,不过我们的-version命令是在数组args中,这个方法通过调用processArgs(args,classNames)方法来执行-version命令,此就去的实现逻辑也很简单,通过循环找到-version在recognizedOptions中的匿名实现类并调用process()方法,最终通过调用helper对象的printVersion()方法来实现JDK版本号的打印。

  

  

 

  

第一章-Javac编译器介绍的更多相关文章

  1. [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍

    注:为了看上去比较清晰这里只转载了中文 原地址:  [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍 本章将引导您完成安装和设置开发环境,然后你就可 ...

  2. 深入Java虚拟机读书笔记第一章Java体系结构介绍

    第1章 Java体系结构介绍 Java技术核心:Java虚拟机 Java:安全(先天防bug的设计.内存).健壮.平台无关.网络无关(底层结构上,对象序列化和RMI为分布式系统中各个部分共享对象提供了 ...

  3. Ionic 入门与实战之第一章:Ionic 介绍与相关学习资源

    原文发表于我的技术博客 本文是「Ionic 入门与实战」系列连载的第一章,主要对 Ionic 的概念.发展历程.适配的移动平台等知识进行了介绍,并分享了 Ionic 相关的学习资源. 原文发表于我的技 ...

  4. 第一章001-003课程介绍、计算机网络概述、Internet概述

    计算机网络概述 课程安排: 第一章:概述 第二章:物理层 第三章:数据链路层 第四章:网络层 第五章:运输层 第六章:应用层 第七章:网络安全 第八章:因特网上的音频/视频服务 第九章:无线网络 第十 ...

  5. 第一章 : Android Studio 介绍 [Learn Android Studio 汉化教程]

    摘自:http://ask.android-studio.org/?/question/789,为便于学习重新整理.. 本章将引导您完成安装和设置开发环境,然后你就可以跟随本书的例子和课程学习. 首先 ...

  6. Netty In Action中文版 - 第一章:Netty介绍

    本章介绍 Netty介绍 为什么要使用non-blocking IO(NIO) 堵塞IO(blocking IO)和非堵塞IO(non-blocking IO)对照 Java NIO的问题和在Nett ...

  7. [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)

    <<返回目录 Performance Counters(性能计数器) 性能计数器是监视应用程序和系统性能的最简单的方法之一.它有几十个类别数百个计数器在,包括一些.net特有的计数器.要访 ...

  8. 第一章 flume架构介绍

    1.flume概念介绍 1.1 常见的分布式日志收集系统                             Scribe是facebook开源的日志收集系统,在facebook内部已经得到大量的 ...

  9. [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio

    <<返回目录 Visual Studio vs虽然不是全宇宙唯一的IDE,但它是.net开发人员最常用的开发工具.它自带一个性能分析工具,你可以使用它来做开发,不同的vs版本在工具上会略有 ...

随机推荐

  1. nginx调优操作之nginx隐藏其版本号

    1.nginx下载 下载网址:nginx.org 2.解压nginx [root@iZwz9cl4i8oy1reej7o8pmZ soft]# ls nginx-.tar.gz [root@iZwz9 ...

  2. 强制DataNode向NameNode上报blocks

    正常情况下,什么时候上报blocks,是由NameNode通过回复心跳响应的方式触发的. 一次机房搬迁中,原机房hadoop版本为2.7.2,新机房版本为2.8.0,采用先扩容再缩容的方式搬迁.由于新 ...

  3. mysql-5.7.19 压缩安装 设置密码

    初始化完毕后,如果没使用新版本的客户端登入,还会报告类似下面的错误: mysql -uroot -p Enter password: ERROR 1862 (HY000): Your password ...

  4. 机器学习之Apriori算法和FP-growth算法

    1 关联分析 无监督机器学习方法中的关联分析问题.关联分析可以用于回答"哪些商品经常被同时购买?"之类的问题. 2 Apriori算法   频繁项集即出现次数多的数据集   支持度 ...

  5. 用jquery制作一个二级导航下拉菜单

    1使用$(function(){...})获取到想要作用的HTML元素. 2通过使用children()方法寻找子元素.       3通过使用show()方法来显示HTML元素.       4通过 ...

  6. 20155326 2016-2017-2 《Java程序设计》第十周学习总结

    20155326 2016-2017-2 <Java程序设计>第十周学习总结 教材学习内容总结 计算机网络基础 1.计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的 ...

  7. 20155326 2016-2017-2 《Java程序设计》第九周学习总结

    20155326 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 1.撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的增删查找. 2.JDBC目的 ...

  8. Mybatis类型转换介绍

    1.1     目录 1.2     建立TypeHandler 1.2.1    TypeHandler接口 1.2.2    BaseTypeHandler抽象类 1.3     注册TypeHa ...

  9. Swagger生成WebAPI文档

    WebAPI2.0 项目可以使用Swagger能够轻易查看API文档,查看以下配置 1.打开程序包管理控制台输入: Install-Package Swashbuckle 2.在对应项目里的App_S ...

  10. Hdu4687 Boke and Tsukkomi

    Boke and Tsukkomi                                                                               Time ...