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写出来的

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 服务器文件句柄数_Linux文件句柄机制

    服务器文件句柄数_Linux文件句柄机制设置文件句柄在配置我们的RedHatLinux服务器时,确保文件句柄的最大数量足够大是非常关键的。文件句柄设置表示您在Linux系统中可以打开的文件数量。使用以下命令来确定整个系统中文件句柄的最大数量:#cat/proc/sys/fs/file-max32768Oracle建议将整个系统的文件句柄值至少设置为65536。通过直接更改/proc文件系统,您可以不必重新启动机…

    2022年10月18日
  • 真正免费的国外PHP建站空间

    真正免费的国外PHP建站空间最近在学习wordpress,找了一个挺不错的国外免费建站空间:http://www.000webhost.com/582659.html空间属性:空间容量 1500MB数据流量 100GB/月附加域名  5个子域名   5个E-mail地址  5个MySQL数据库  2个

  • http接口取参方式 – getParameter 和 getParameterValues[通俗易懂]

    http接口取参方式 – getParameter 和 getParameterValues[通俗易懂]前言:最近写http接口时,有了很多关于接口取参方式的疑问,大家都知道Servlet常用的取参方式有getParameter、getParameterValues、getInputStream(读流形式)。SpringMvc常用的有封装好的@RequestParam,RequestBody。这些取参方式都有什么特点,我都写了测试接口,利用postman做了测试…

  • android 100以内的随机数

    android 100以内的随机数int a = (int)(Math.random()*100);//a是已经生成的随机数

  • Java常见的8种数据结构「建议收藏」

    Java常见的8种数据结构「建议收藏」数据结构是指数据在计算机内存空间中或磁盘中的组织形式算法是完成特定任务的过程二分法查找r=2^ss:查找步数r查找范围幂函数s=log2®已知范围获取需要的次数对数算法复杂度使用O(N)函数进行标示主要是去除常数看运行时间受数据项个数的影响常见排序参考地址https://blog.csdn.net/muranfei/article/details/80923996栈对列优先级对列栈按照“后进先出”、“先进后出”的原则来存储数据,先插入的数…

  • javascript中for/in循环及使用技巧

    javascript中for/in循环及使用技巧

发表回复

您的电子邮箱地址不会被公开。

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