⚠️本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!
前言
众所周知,Kotlin
团队正在开发新版Kotlin
编译器,并命名为K2
。那么K2
又是什么意思呢?莫非是Kotlin
第二版编译器的意思?
其实K2
指的是乔戈里峰,海拔8611米,仅次于珠穆朗玛峰,为世界第二顶峰。登山者一般称乔戈里峰为K2
,它尽管海拔排名第二,但因方位偏远及山势陡峭,乔戈里峰一般被认为是最难攀登的8000米以上顶峰之一。Kotlin
团队经过K2
这个姓名表明编译器重构作业的难度。
好了,没用的冷知识又增加了,在了解了世界第二顶峰是哪座之后,咱们一起来看下K2
编译器是什么?与老版别编译器有什么差异?
本文首要是学习《K2编译器之路》视频的输出,感兴趣的同学能够直接检查视频,链接在文末
Kotlin
编译器总体介绍
如上图所示,咱们能够认为编译器是一个黑箱,它的输入便是源代码,输出则是机器码或许方针代码。
源代码是人类编写的,一般运用高档言语编写,比方java
或许kotlin
,关于人类来说,易于阅读,了解和修正
机器码则是一系列供机器履行的指令,一般是主动生成的,关于人类来说难以了解,可是关于机器来说却易于了解
当然机器码也是能够手写的,远古时代的程序员便是这样作业的。可是就算是远古程序员,运用机器码开发也不是那么简略,因而程序也难以扩展到杂乱的等级。正是因为这个原因,呈现了一系列的高档言语与编译器,显著简化了编程体会
编译器的效果便是将源代码输出为机器码或许方针代码
如上所示,Kotin
编译器能够将Kotlin
代码编译成jvm
字节码,除此之外,Kotlin
编译器也能够将Kotlin
代码编译成javascript
或许llvm bitcode
总得来说,Kotlin
编译器目前有3个方针渠道,jvm
,javascript
,native
,它们都有着不同的格局,因而需求将源代码编译成三种方针产品
Kotlin
编译器的具体结构
前端与后端
编译器一般能够区分为前端和后端两部分,如下图所示:
当然看到前端与后端你或许会跟业务开发上的前后端产生必定的混杂,但编译器前后端是与之彻底不同的概念
- 编译器前端:效果是构建笼统语法树和语义信息
- 编译器后端:效果是生成机器码或许方针代码
在著名的编译原理龙书中,对编译器前端与后端做了进一步的区分
- 编译器前端被区分为语法解析器(
parser
)和语义分析器 - 编译器后端被区分为中心代码生成器和机器代码生成器,其间中心代码生成器是可选的,没有这个阶段也能够实现编译器,中心代码生成器的产品便是
IR
语法解析器
语法解析器以源代码作为输入,输出笼统语法树,比方下面一段代码
这段代码对咱们来说很简略,便是个if else
的判断,假如条件满足则调用meow
办法,不然打印一段内容。
可是关于编译器来说,这段代码目前还仅仅一段没有语义的文本,目前对编译器毫无意义。
要让编译器知道这段代码,第一步便是给这段文本增加结构,而这些结构便是经过Kotlin
言语的语法界说的。Kotlin
开发者依据界说的语法编写代码,编译器依据语法解析这些文本,得到有结构的数据,这便是语法解析器的效果
比方如上图所示,if
表达式要求必须以if
开头,而且左右各有一个括号,假如咱们编译的代码不符合这个规范的话,编译就会报错。Kotlin
的更多语法界说可检查相关网站:kotlinlang.org/docs/refere…
假如输入的源代码依据语法解析正确,语法解析器将会构建出一个笼统语法树
如上图,在解析成功后,解析器了解了代码的结构,它知道if
表达式有三个部分,if
表达式,then
子句与else
子句,并将结果存储在笼统语法树中
需求注意的是,在这一阶段,编译器还没有了解语义,解析器的方针是遵从语法了解代码结构,但在目前,它还不能分辨出节点内究竟存储了什么,它仅仅将cat
,pet
这些存储为字符串,这些字符串还没有语义,这个时分就需求语义分析器开端发挥效果了
语义分析器
下一阶段,便是语义分析器以笼统语法树为输入,并向其间的节点增加语义信息,那么问题来了,什么是语义信息?
语义信息便是代码中用到的函数,变量和类型的一切详细信息,它能回答“这个函数从哪里来?”,“这两个字符串是否引证同一变量?”,“这是什么类型?”等问题
- 这段代码中
pet
呈现了3次,都指向同一个形参,在语法树中,这些pet
是没有关联互相独立的,语义信息的效果是让编译器让解这3个字符串引证的是同一个变量 - 语义信息同样包含类型信息,比方
pet
参数是Pet
类型的,语义信息需求解析一切运用的类型,并找到他们引证的类或许接口,然后以相同的方式进行解析 - 如图调用了
meow
函数,语义信息的方针是了解在这种情况下该运用哪个函数,比方能够是类中的成员函数,也能够是同名的扩展函数,函数类型的特点,语义分析器需求选择出最合适的那个 - 语义分析器还有一个重要效果是类型揣度,有时咱们在声明特点时不需求指定类型,编译器能够揣度出特点的类型,这也是由语义分析器来完结的
- 当语法不正确时,语法解析器会抛出过错,当语义产生过错,比方调用了不存在的函数,或许调用函数传递的参数个数不对时,语义分析器也会抛出过错
- 语义分析器分析出语义信息,并将这些信息存储在一个表里,这张表是包含语法树一切节点的额外信息的一个
map
- 比方语法树中存储的第一个
pet
字符串,表中存储了它是example.pets.Pet
类型的函数参数,Cat
字符串在表中也记录了它的类型 - 这也适用于第二个
pet
字符串,这时编译器了解了两个pet
字符串其实是引证了同一个参数,而且被智能转化成了Cat
类型 - 关于办法也是相同的,在表中存储了
meow
与println
办法的方位
到了这个阶段,这张表存储了各个节点的信息,每个字符串都有了语义,编译器前端的作业也就完结了。
编译器前端的方针是给源代码转化为有结构和语义的数据结构,有了这些信息,编译器后端生成方针代码也就容易多了,比方Kotlin jvm
后端将语法树和语义信息作为输入,生成Jvm
字节码
编译器后端
咱们知道,Kotlin
能够将源代码编译成3个渠道的方针代码,因而也有着3个不同的编译器后端,为不同的方针渠道转化语法树和信息
上文提到,编译器后端包含一个可选的中心代码生成器,在Kotlin
刚开端开发时,为了加快开发速度,以及在前期阶段的快速开展,没有运用任何的IR
因而老版的Jvm
后端与JavaScript
后端是不包含IR
的,可是因为Kotlin
编译器有着3个后端,明显一切后端能够同享一些代码表明的一些逻辑,简化和转化。因而Kotlin
团队在开发Native
后端时引进了IR
能够看到Native
后端遵从了龙书的经典办法,将生成中心代码的阶段和依据IR
生成方针代码的阶段分离,这一设计的目的是考虑到IR
将来或许能够在不同的后端之间复用
K2
编译器是什么?
如上图所示,K2
编译器首要包含两个部分,新后端与新前端,其间新的Jvm
后端与Js
后端现已正式发布了(Native
后端一开端就引进了IR
),而新的编译器前端还在开发中
新的编译器后端
- 能够看出,新的编译器后端都运用了
IR
,并同享构建和操作它的逻辑 - 引进
IR
的首要目的便是在不同的后端之间同享逻辑 - 引进
IR
同时简化了支撑新的言语特性所需的作业 - 需求指出的是,功能改善不是新的后端的方针(功能改善首要经过一个新的前端来完结)
新的编译器前端
能够看出,新的编译器前端仍是做了相同的作业,经过语法分析与语义分析,获得语法树与语意信息,可是会得到不同的数据结构,也便是FIR
(前端中心表明)
在老版前端中,最终的输出是语法树和一个包含语义信息的表,其间语法树经过PSI
(程序结构接口)表明,PSI
的代码最早来源于IDEA
。而带有语义信息的表称作BindingContext
,它是一个特殊的map
,存储了PSI
元素的一切语义信息
而新版前端与之不同,如下图所示:
FIR
= 前端中心表明 = 带有语义信息的树
新前端运用FIR
表明输出,它本质上也是一棵语法树,可是带有语义信息。树包含其节点中的一切语义信息,不再运用独自的数据来表明
所以新前端的主意其实很简略,旧前端产生两个数据结构,而新前端只产生一个数据结构
同时,新的编译器前端将给编译器和IDE
都带来更好的功能,也将为Kotlin
编译器插件供给揭露api
FIR
与IR
的差异
-
FIR
即前端中心表明,坐落编译器前端,而IR
即中心表明,坐落编译器后端 -
FIR
为调用解析而设计和优化,而IR
则为代码生成而设计和优化,IR
运用FIR
构建而成 - 得益于
FIR
单一的数据结构,它的生成和更新过程能够并行履行,因而它能够带来更好的功能,FIR
也会做一些脱糖的作业,将杂乱的言语结构替换为更简略的结构 - 而
IR
的设计方针则不包含功能上的改善,它的首要方针是在不同的后端之间同享逻辑,并降低支撑新的言语特性的成本
总结
-
Kotlin
编译器能够分为前端与后端两部分,前端担任将源代码转化成语法树与语义信息,后端担任依据这些信息生成方针代码 - 新的编译器后端引进了
IR
,一切的后端同享IR
以简化生成方针代码的过程 - 新的编译器前端引进了
FIR
,将语法树与语义信息存储在一个数据结构中,同时带来必定的功能提升
目前K2
编译器现已发布了alpha
包,或许明年底就能够正式发布,应该也是时分了解一下K2
编译器了,同时假如你需求开发Kotlin
编译器插件,也有必要了解一下Kotlin
编译器,希望本文对你有所帮助~
参考资料
K2 编译器之路