编译器知识杂记-javac

简介:

先看一张Javc编译成class文件的时候流程图

image.png

至于什么是token流,语法树相关可以参考我之前的两篇帖子。

https://www.jianshu.com/p/7a9476c65672

https://www.jianshu.com/p/49ca10345080

如何下载JavaC源码?

直接使用openJdk进行下载,下载地址为 https://hg.openjdk.java.net/jdk8/jdk8/langtools/

也可以直接在我的在我的项目库内部进行下载,地址见下文。

Javac主要有四个模块,分别是词法分析器,语法分析器,语义分析器和代码生成器。

JavaC JavaParser

用途:

把Java源码转换成 JavaParser定义的Statement对象,

也就是将java源码解析成一颗语法树,然后基于这棵树对java代码进行分析和修改的工具。

在javac 编译时候使用到

重点类介绍

语法树简介:

在学习之前需要知道什么是语法树,很简单的代码如下,在解析的时候主要分为两步

“1 + 2 * 3”

第一步: +, 1,(2 * 3)

第二步: +, 1,(*, 2, 3)

语法树

com.sun.tools.java.tree.JCTree

用于表示最终的语法树,结构,里面有很多内部类。

比如com.sun.tools.java.tree.JCTree$JCCompilationUnit,在AST中,你可以把这个类看成是AST的根节点

插入式注解器简介

Jdk1.5后引入注解功能,注解是一种应用字节码 属性中类的元数据进行操作的一种编程机制。

处理表形成后 会自动检测是否有注解器需要执行,若有则执行注解处理器。注解处理器实现了在可插入式的编译期改变编译过程的功能。

其本质就是 再次修改 处理表中的语法树。 一旦语法树被修改,则将再次进行 词法,语法分析并填充符号表的过程,直到所有插件对语法树进行修改完为止。

初始化过程入口是 initPorcessAnnotations()

执行过程入口是 processAnnotations()

该方法判断是否有新的注解处理器需要执行,若有的,则通过com.sun.tools.方法生成新的JavaCompiler对象对编译的后续步骤进行处理

JavaC 词法分析

词法分析主要是将代码转换成token

词法分析过程主要是在的JavacParser.parseCompilationUnit()中完成的

入口是parseFiles()

词法分析实现类是com.sun.tools.javac.parser.Scanner类

语法分析实现类是com.sun.tools.javac.parser.Parser

出口由com.sun.tools.javac.tree.JCTree类表示

重点介绍下面几个类,是构成词法分析的关键

先介绍词法分析“大管家”

com.sun.tools.javac.parser.ParserFactory

存放着很多的解析器比如token,源文件,名字注解之类的。

通过静态方法单例进行初始化

//获取实类的单例方法
public static ParserFactory instance(Context context) {
    ParserFactory instance = context.get(parserFactoryKey);
    if (instance == null) {
        instance = new ParserFactory(context);
    }
    return instance;
}

//创建的时候在构造方法内部,将词法分析需要的一些类进行初始化
//在编译的时候全部的解析器包括,生成器,各种工具类,都是全局唯一的单例模式
protected ParserFactory(Context context) {
        super();
        context.put(parserFactoryKey, this);
        this.F = TreeMaker.instance(context);
        this.docTreeMaker = DocTreeMaker.instance(context);
        this.log = Log.instance(context);
        this.names = Names.instance(context);
        this.tokens = Tokens.instance(context);
        this.source = Source.instance(context);
        this.options = Options.instance(context);
        this.scannerFactory = ScannerFactory.instance(context);
        this.locale = context.get(Locale.class);
}


com.sun.tools.javac.parser.JavacParser

规定哪些词符合Java语言规范,具体读取和归类不同词法的操作由scanner完成

/**
 *  Skip forward until a suitable stop token is found.
 *  核心方法,判断是否是我们需要的token
 */
private void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) {

com.sun.tools.javac.parser.Scanner

负责逐个读取源代码的单个字符,然后解析符合Java语言规范的Token序列,调用一次nextToken()都构造一个Token

内部有个List 储存读取到的token信息

  /**
     *  Buffer of saved tokens (used during lookahead)
     *  token的一个缓冲区  
     */
    private List<Token> savedTokens = new ArrayList<Token>();

com.sun.tools.javac.parser.Tokens$TokenKind

里面包含了所有token的类型,譬如BOOLEAN,BREAK,BYTE,CASE。用的是枚举的方式进行保存

类似如下:

public enum TokenKind implements Formattable, Filter<TokenKind> {
        EOF(),
        ERROR(),
        IDENTIFIER(Tag.NAMED),
        ABSTRACT("abstract"),
        ASSERT("assert", Tag.NAMED),
        BOOLEAN("boolean", Tag.NAMED),
        BREAK("break"),
        BYTE("byte", Tag.NAMED),
        CASE("case"),
        CATCH("catch"),
        CHAR("char", Tag.NAMED),
        CLASS("class"),
        CONST("const"),
        ...

com.sun.tools.javac.parser.Tokens

存放全部的定义token

/**
 * The names of all tokens.
 * 存放全部token的数组 
 */
private Name[] tokenName = new Name[TokenKind.values().length];

com.sun.tools.javac.util.Names

用来存储和表示解析后的词法,每个字符集合都会是一个Name对象,所有的对象都存储在Name.Table这个内部类中。

com.sun.tools.javac.parser.KeyWords

负责将字符集合对应到token集合中,如,package zxy.demo.com; Token.PACKAGE = package, Token.IDENTIFIER = zxy.demo.com,(这部分又分为读取第一个token,为zxy,判断下一个token是否为“.”,是的话接着读取下一个Token.IDENTIFIER类型的token,反复直至下一个token不是”.”,也就是说下一个不是Token.IDENIFIER类型的token,Token.SEMI = ;即这个TIDENTIFIER类型的token的Name读完),KeyWords类负责此任务。

package compile;
public class Cifa {
    int a;
    int c = a + 1;
}

转换的token流如下

《转载自https://www.cnblogs.com/wade-luffy/p/5925728.html》

JavaC 语法分析

将token流转换成句子

com.sun.tools.javac.tree.TreeMaker

所有语法节点都是由它生成的,根据Name对象构建一个语法节点

com.sun.tools.javac.tree.JCTree$JCIf

所有的节点都会继承jctree和实现**tree,譬如 JCIf extends JCTree.JCStatement implements IfTree

com.sun.tools.javac.tree.JCTree

重点介绍的三个属性

  • Tree tag:每个语法节点都会以整数的形式表示,下一个节点在上一个节点上加1;
  • pos:也是一个整数,它存储的是这个语法节点在源代码中的起始位置,一个文件的位置是0,而-1表示不存在
  • type:它代表的是这个节点是什么java类型,如int,float,还是string等

例子:

package compile;
public class Yufa {
    int a;
    private int c = a + 1;
    //getter
    public int getC() {
        return c;
    }
    //setter
    public void setC(int c) {
        this.c = c;
    }
}
《转载自https://www.cnblogs.com/wade-luffy/p/5925728.html》

说明:

  • 每一个包package下的所有类都会放在一个JCCompilationUnit节点下,在该节点下包含:package语法树(作为pid)、各个类的语法树
  • 每一个从JCClassDecl发出的分支都是一个完整的代码块,上述是四个分支,对应我们代码中的两行属性操作语句和两个方法块代码块,这样其实就完成了语法分析器的作用:将一个个Token单词组成了一句句话(或者说成一句句代码块)
  • 在上述的语法树部分,对于属性操作部分是完整的,但是对于两个方法块,省略了一些语法节点,例如:方法修饰符public、方法返回类型、方法参数。

JavaC语义分析器

流程:

  • 添加默认的无参构造器(在没有指定任何有参构造器的情况下),把引用其他类的方法或者变量,抑或是继承实现来的变量和方法等输入到类自身的符号表中

  • 处理注解

  • 标注:检查语义合法性、进行逻辑判断

    • 检查语法树中的变量类型是否匹配(eg.String s = 1 + 2;//这样"="两端的类型就不匹配)
    • 检查变量、方法或者类的访问是否合法(eg.一个类无法访问另一个类的private方法)
    • 变量在使用前是否已经声明、是否初始化
    • 常量折叠(eg.代码中:String s = "hello" + "world",语义分析后String s = "helloworld")
    • 推导泛型方法的参数类型
  • 数据流分析

    • 变量的确定性赋值(eg.有返回值的方法必须确定有返回值)
    • final变量只能赋一次值,在编译的时候再赋值的话会报错
    • 所有的检查型异常是否抛出或捕获
    • 所有的语句都要被执行到(return后边的语句就不会被执行到,除了finally块儿)
  • 进一步语义分析

    • 去掉永假代码(eg.if(false))
    • 变量自动转换(eg.int和Integer)自动装箱拆箱
    • 去掉语法糖(eg.foreach转化为for循环,assert转化为if,内部类解析成一个与外部类相关联的外部类)
  • 最后,将经过上述处理的语法树转化为最后的注解语法树

入口是attribute()

实现类是com.sun.tools.javac.comp.Attr类和com.sun.tools.javac.comp.Check类

源码关键:

com.sun.tools.javac.comp.Enter

将java类中的符号输入到符号表中,主要是两个步骤:

  • 将所有类中出现的符号输入到类自身的符号表中,所有类符号、类的参数类型符号(泛型参数类型)、超类符号和继承的接口类型符号等都存储到一个未处理的列表中。
  • 将这个未处理的列表中所有的类都解析到各自的类符号列表中,这个操作是在MemberEnter.complete()中完成(默认构造器也是在这里完成的)。

com.sun.tools.javac.processing.JavacProcessingEnvironment

处理注解

com.sun.tools.javac.comp.Attr

检查语义的合理性并进行逻辑判断,类型是否匹配,是否初始化,泛型是否可推导,字符串常量合并

com.sun.tools.javac.comp.Check

协助attr,变量类型是否正确

com.sun.tools.javac.comp.Resolve

协助attr,变量方法类的访问是否合法,是否是静态变量

com.sun.tools.javac.comp.ConstFold

协助attr,常量折叠

com.sun.tools.javac.comp.Infer

协助attr,推导泛型

com.sun.tools.javac.comp.Flow

数据流分析和替换等价源代码的分析(即上面的进一步语义分析)

字节码生成:

进行了少量的代码添加和转换工作

把生成的信息(语法树、符号表)转化成字节码写到磁盘

“写到磁盘”由com.sun.tools.输出字节码,生成最终Class文件

入口generate()

实现类com.sun.tools.javac.jvm.Gen类

项目地址:

https://github.com/w296488320/OllJavaC

参考:

https://zhuanlan.zhihu.com/p/93939780

https://www.cnblogs.com/wade-luffy/p/5925728.html

https://blog.csdn.net/crabstew/article/details/89547472

https://www.sohu.com/a/212579385_100063030

http://blog.sina.com.cn/s/blog_17c534c120102xwv9.html


安卓逆向百级教程+全网最新js逆向视频+永久小蜜圈+永久售后群=1299

视频下载网盘
http://nas.alienhe.cn:5008/home.html
下载视频账号密码:
账号guest 密码world

Js试看:
http://oss.alienhe.cn/JS%E9%80%86%E5%90%91%E5%85%A5%E9%97%A8-%E5%B8%A6%E6%B0%B4%E5%8D%B0.mp4

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容