JAVAC原理「建议收藏」

JAVAC原理「建议收藏」前言本文是对compilation-overview的翻译.如有翻译不对的地方,还望海涵.正文将一组源文件编译成相应的一组类文件的过程并不简单,但是通常可以分为三个阶段。源文件的不同部分可以在“按需”的基础上以不同的速率进行处理。这个过程是由JavaCompiler类来处理的:将命令行上指定的源文件进行读取,解析为语法树,然后将所有外部可见的定义都输入到编译器的…

大家好,又见面了,我是你们的朋友全栈君。

前言

本文是对 compilation-overview 的翻译. 如有翻译不对的地方,还望海涵.

正文

将一组源文件编译成相应的一组类文件的过程并不简单,但是通常可以分为三个阶段。源文件的不同部分可以在“按需”的基础上以不同的速率进行处理。

javac原理

这个过程是由JavaCompiler类来处理的:

  1. 将命令行上指定的源文件进行读取,解析为语法树,然后将所有外部可见的定义都输入到编译器的符号表中
  2. 在编译的过程中会调用适当的注解处理器.如果在调用过程中,生成了新的资源文件或者类文件,则重新进行编译,直到没有新文件创建为止
  3. 最后,对语法分析器生成的语法树进行分析,并将其转换为类文件。在分析过程中,可以找到对其他类的引用。编译器将检查这些类的源和类路径;如果在源路径上找到它们,那么这些文件也将被编译,尽管它们不会受到注释处理的影响。

解析和输入

源文件是被Scanner按照Unicode 编码来处理的同时转化为token流.

token流由解析器读取,使用TreeMaker创建语法树。语法树是从JCTree的子类型构建的,它实现了com.sun.source.Tree 和它的子类.

每一个树都被交给Enter类来进行处理,它会将所遇到的符号都输入到符号表中.这必须在分析树是否会引用这些符号前完成.这个阶段的输出是一个待办事项列表(TO DO list),包含需要分析并生成类文件的树.

输入由多个阶段组成;类通过队列从一个阶段迁移到下一个阶段.

Enter

  1. 在第一个阶段,所有类符号都被输入到它们的封闭范围(enclosing scope,也许可以翻译为定义域?)中,对于内部类,它们沿着树递归下降的输入到封闭范围。类符号被赋予一个成员对象作为完成符。
    此外,如果找到包含包注释的package-info.java文件,则该文件的顶级树节点也放在To Do列表中

  2. 在第二个阶段,类通过MemberEnter.complete()方法来进行处理(completed , 使类变得完整).使类变得完整可能是按需发生的,但是任何未以这种方式完成的类最终将通过处理未完成队列来完成.使一个类完成需要如下条件:

    1. 确定类的泛型参数,父类,接口
    2. 将该类的所有符号输入到它所对应的scope,当该类在第一点已经输入时会产生错误.

    第2点依赖于第一点已经完成了一个类及其所有的超类和外部类.这就是为什么在做(1)之后,我们把类放在一个半完成的队列中。只有当我们对一个类进行了(1)和它的所有超类和外部类时,我们才进行(2).

  3. 在输入所有符号之后,将对这些符号上遇到的任何注解进行分析和验证。

虽然第一阶段被组织成扫描所有编译的语法树,但第二阶段是按需进行的。类的成员是在第一次访问类的内容时输入的。这是通过在编译后的类的类符号中安装completer对象来实现的,编译后的类为对应的类树调用MemberEnter阶段。

注解处理

这部分是由JavacProcessingEnvironment 来进行处理的

从概念上讲,注释处理是编译前的一个准备步骤.这个准备步骤包括一系列循环,每个循环用于解析和输入源文件,然后确定和调用任何适当的注解处理器.在第一次循环之后,如果任何被调用的注解处理器生成任何需要成为最终编译一部分的新源文件或类文件,则将执行后续循环。最后,当完成所有必要的循环时,执行实际编译。

3

实际上,在解析要编译的文件并确定它们包含的声明之前,可能不知道需要调用哪个注解处理器。因此,为了避免在没有执行注释处理的情况下不必要地解析和输入源文件,JavacProcessingEnvironment与概念模型执行“不同步”,同时仍然满足注解处理作为一个整体在实际编译之前发生的概念要求。

4

JavacProcessingEnvironment在文件已经被解析并输入之后被调用。它决定是否需要加载任何注解处理器,并调用任何正在编译的文件.通常,如果在整个编译过程中出现任何错误,则在下一个convenient point停止该过程.但是,如果在Enter阶段检测到任何丢失的符号,则会发生异常,因为调用注解处理器可以生成这些符号的定义。

如果要运行注解处理器,则在单独的类加载器加载并运行它们.

在运行注解处理器时,JavacProcessingEnvironment确定是否需要另一轮注解处理.如果是,它创建一个新的JavaCompiler对象,读取需要解析的任何新生成的源文件,并重用任何以前解析过的语法树。所有这些树都被输入到这个新编译器实例的符号表中,并在必要时调用注解处理器。重复这一步骤,直到不再需要更多的注解处理。

最后,JavacProcessingEnvironment返回用于编译其余部分的JavaCompiler对象。这将是用于解析和输入初始文件集的原始实例,或者它将是用于开始最后一轮编译JavacProcessingEnvironment创建的最新实例.

分析和生成

一旦命令行上指定的所有文件都被解析并输入到编译器的符号表中,并且注解处理完之后,JavaCompiler就可以继续分析被解析的语法树,以便生成相应的类文件.

在分析树时,可以找到对成功编译所需的类(但未明确指定用于编译的类)的引用。根据编译选项,将根据搜索源路径和类路径来搜索这些类的定义.如果定义在类文件中找到,则读取类文件以确定该类中的定义;如果定义在源文件中找到,则源文件将自动解析、输入并放到“待办事项”列表中。这是通过注册JavaCompiler作为Attr.SourceCompleter的一个实现来完成的。

分析树和生成类文件的工作是由一系列visitor 执行的,这些访问者处理编译器的To Do列表上的条目。唯一的要求是,To Do列表中的每个条目最终都应该由这些visitor来处理,除非由于错误而提前终止编译。

Attr(属性标记)

顶层类认为是"属性标记过的",当使用Attr,将语法树中的名称、表达式和其他元素被解析并与相应的类型和符号相关联.许多语义错误可以在这里被检测,无论是通过Attr,还是通过检查。

Flow(数据流分析)

如果到目前为止还没有错误,则使用数据流分析对类进行分析。数据流分析用于检查变量的确定分配和不可到达的语句,这可能导致额外的错误 

TransTypes

使用TransTypes将泛型类型的代码转换为没有泛型类型的代码,

Lower(强度削弱)

“语法糖”是使用Lower进行处理的,它重写语法树以通过替换等价的、简单的树来消除特定类型的子树。这需要处理嵌套和内部类、类中的字面量、断言、Frach循环等等。对于处理的每个类,Lower返回已转换的类及其所有已转换的嵌套类和内部类的树列表

虽然Lower通常处理顶级类,但它也将处理顶级的package-info.java。对于这样的树,Lower将创建一个合成类来包含包的任何注解

Gen(字节码生成)

方法的代码由Gen生成,它创建包含JVM执行方法所需的字节码的Code属性。如果这一步成功了,则这个类是由ClassWriter写出来的

一旦类被写成一个类文件,就不再需要它的语法树和生成的字节码。为了节省内存,对树和符号的这些部分的引用将被取消,以允许垃圾收集器恢复内存。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/140324.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月8日 上午11:00
下一篇 2022年5月8日 上午11:20


相关推荐

  • python进制转换代码_python十六进制转换成十进制

    python进制转换代码_python十六进制转换成十进制本文实例讲述了Python实现的十进制小数与二进制小数相互转换功能。分享给大家供大家参考,具体如下:十进制小数⇒二进制小数乘2取整对十进制小数乘2得到的整数部分和小数部分,整数部分即是相应的二进制数码,再用2乘小数部分(之前乘后得到新的小数部分),又得到整数和小数部分。如此不断重复,直到小数部分为0或达到精度要求为止.第一次所得到为最高位,最后一次得到为最低位如:0.25的二进制0.25*2=…

    2025年11月30日
    6
  • 上行带宽和下行带宽是什么意思?各有什么作用?

    上行带宽和下行带宽是什么意思?各有什么作用?上行带宽和下行带宽 或者说上行速度和下行速度是什么意思 在设置路由器的限速 以及配置其它一些软件的时候经常遇到上行速度和下行速度 很多用户根本就不知道这两个所代表的意识 更不说配置了 下文将为大家进行详细的介绍 我们访问互联网的过程中存在这两种行为 一是上传数据 二是下载数据 上行宽带 速度 指的是上传的速度 而下行宽带 速度 指的是下载数据是的数度 上行宽带 速度 和下行宽带 速度 是不对称的 一般是下行速度大于上行的速度 我们平时所使用的宽带说多少 M 都是指的下行宽带 因为我们上网主要是从互联网上下

    2026年3月16日
    2
  • 数论狄利克雷定理_shx函数

    数论狄利克雷定理_shx函数狄利克雷函数dirac在Matlab中使用Syntaxd=dirac(x)d=dirac(n,x)d=dirac(x)representstheDiracdeltafunctionofx.d=dirac(n,x)representsthenthderivativeoftheDiracdeltafunctionatx.dirac(t)这表示关于ttt的狄利克雷函数dirac(1,t)dirac(2,t)因此,这两个分别表示关于

    2025年9月5日
    5
  • tail命令用法举例

    tail命令用法举例tail命令从指定点开始将文件写到标准输出.tail-ffilename可以方便的查阅正在改变的日志文件,会把filename里最新的内容显示在屏幕上1.命令格式:tail[必要参数][选择参数][文件]  2.命令功能:用于显示指定文件末尾内容,不指定文件时,作为输入信息进行处理。常用查看日志文件。3.命令参数:-f 循环读取-q 不显示文件名-v 显示文件…

    2022年6月4日
    72
  • Java引用类型分类以及详解

    Java引用类型分类以及详解Java 引用类型分类以及详解 Java 引用类型概述在 JVM 之中再好的算法 也敌不过一个好烂的程序员 一个程序要想写好有两点 按照开发标准进行 请写有用代码 而对于垃圾的产生与回收的处理之中 要想进行更好的控制 就必须清楚的掌握 Java 中的四种引用方式 强引用 StrongRefere 即使进行了多次的 GC 回收 即使 JVM 真的已经不够用了 即使 JVM 最终不得已抛出了 O

    2026年3月26日
    1
  • php openssl 怎么升级,OpenSSL升级和php加扩展模板openssl

    php openssl 怎么升级,OpenSSL升级和php加扩展模板openssl升级 openssl 版本 以版本 1 0 2l 为例子方法如下 1 下载最新版本的 openssl 源码包 wgetftp ftp openssl org source openssl 1 0 2l tar gzhttps www openssl org source 2 安装 openssl1 tar xzvfopenssl 1 0 2l tar gz2 cdopenssl 1 0 2l

    2025年7月1日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号