从零开始开发JVM语言(十三)代码生成与ASM
2019独角兽企业重金招聘Python工程师标准>>>
目录戳这里
如果能够做完语义分析,得到带类型的AST,或者更接近于虚拟机字节码的结构,那么你离整个编译器的“落成”就不远了!
在这个步骤,你可以直接操作byte数组,也可以使用第三方的中间结构,也可以使用字节码类库。
当然,最直接的当然是字节码库了,例如大名鼎鼎的ASM。
#ASM ASM库很小,但是功能非常完善。所有的属性都有一套封装来支持,栈的大小,StackMapTable的计算做的都很好。当然也有不好的地方,如果你给出的指令有问题它也不会报错,有时候计算StackMapTable时会抛出IndexOutOfBoundsException,让人摸不着头脑。而且,mvn中下载的是压缩版本,报错不带行数,也没有源码参考。用maven之类的进行管理时还可能出现依赖冲突。
所以正确的做法是像Spring一样,对它做一个repackaging
,其实就是保留版权信息,然后把源代码直接往项目里放,包名调整一下。比如我这么做。
字节码的生成本质上几个循环就可以搞定了。 对类型需要类型名称,修饰符,父类,接口,注解。 各字段,需要名称,descriptor,注解。 构造方法(<init>
),普通方法,static块方法(<clinit>
),它们需要名称,descriptor,注解。相比较语法语义分析来说这一步实在是太简单了。
这里介绍一下ASM库的组成吧。
ASM库主要由各个Visitor构成。而Visitor都是abstract的类型,我们实际需要用到的是Writer。
- 类 ClassWriter
- 注解 AnnotationWriter
- 字段 FieldWriter
- 方法 MethodWriter
其中,只有ClassWriter是需要手动构造的,其他几个都是可以通过方法调用来获取的。
new ClassWriter(ClassWriter.COMPUTE_FRAMES)
建议参数中的COMPUTE_MAXS
不要加上。因为栈的弹出 我们需要在字节码生成步骤手动完成(当然,这取决于你的语义分析输出,我的输出是基本不带POP的,因为POP可以很自然的由当前栈深度分析出来,只有明确需要POP的地方才在语义分析中加上)。
比方说这样一条语句(Integer.valueOf(1)
),它将返回一个Integer
类型的值。但是这个值没有被变量或者字段接收,所以需要将其pop掉。这时可以分析出当前栈深度为1,需要pop一次。于是在此加入一个pop,并将当前栈深度减1。
实际上,要考虑的不仅仅是栈深度,还要考虑栈占用一个位置还是两个位置。比如double
和long
就会占用两个栈的位置,pop时不能用pop
指令,而需要使用pop2
。比如这里的实现,定义了一个结构来指定占用多少的栈深度,并合理的pop
出去。
由于有了这个自动pop的机制,栈最大深度也可以顺便做出来,不必使用自动计算的最大深度了。
#生成器 在字节码生成中,我定义了这样几个工具方法,来使代码逻辑更明确
- int acc(List<SModifier> modifiers) 用来获取Modifier
- String typeToDesc(STypeDef type) 用来获取类型的descriptor
- String typeToInternalName(STypeDef type) 用来获取internal name
- String methodDesc(STypeDef returnType, List<STypeDef> parameters) 用来获取方法的descriptor
这些东西经常需要获取,所以单独拎出来实现一下。
对于字段、方法、注解等结构自然也是各管各的分开实现:
- void buildStatic(ClassWriter classWriter, List<Instruction> staticIns, List<ExceptionTable> exceptionTable) static块
- void buildConstructor(ClassWriter classWriter, List<SConstructorDef> constructors) 构造函数
- void buildField(ClassWriter classWriter, List<SFieldDef> fields) 字段
- void buildMethod(ClassWriter classWriter, List<SMethodDef> methods) 方法
- void buildParameter(MethodVisitor methodVisitor, List<SParameter> params) 参数
- void buildAnnotation(AnnotationVisitor annotationVisitor, SAnno anno) 注解
其中buildAnnotation
经常被调用,因为不管是类,字段还是方法,都可能会有注解,甚至注解中还可以包含注解。所以上述这几个方法内部都有buildAnnotation
的调用。
#指令 我实现的语义分析输出的指令与字节码非常接近,所以对每一个指令加一个if分支,并调用对应的方法构造指令即可。
ASM将指令依据调用所需操作数(不是栈内操作数)进行了分类。
- void visitInsn(int opcode) 不带任何操作数的指令
- void visitIntInsn(int opcode, int operand) 只带一个整数作为操作数的指令
- void visitVarInsn(int opcode, int var) 与局部变量相关的指令
- void visitTypeInsn(int opcode, String type) 接收一个internal name的指令
- void visitFieldInsn(int opcode, String owner, String name, String desc) 接收一个字段的指令。字段由类型/名称/descriptor表示
- void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) 接收一个方法的指令。方法由类型/名称/descriptor/是否为接口方法 表示
- void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) InvokeDynamic特有指令
- void visitJumpInsn(int opcode, Label label) 跳转指令
- void visitLdcInsn(Object cst) 取常量池指令。这里的参数会自动加到常量池中去。
到此,整个编译器主体已经完成(因为是JVM语言,所以编译目标到字节码即可)。然而,接下来要做的工作还有很多,Evaluator,REPL,编译器交互,语法高亮,ide支持等。不过至少最困难的部分终于结束了!
下一篇说说Evaluator和REPL怎么实现~
最后,希望看官能够关注我的编译器哦~Latte
转载于:https://my.oschina.net/wkgcass/blog/704503
从零开始开发JVM语言(十三)代码生成与ASM相关推荐
- 从零开始开发JVM语言(七)语义分析的起步
2019独角兽企业重金招聘Python工程师标准>>> 目录戳这里 语法分析结束后,编译才刚刚开始.接下来是语义分析. 语义分析的功能大体来说,是对AST加入类型信息.不过不仅是类型 ...
- 从零开始开发JVM语言(十一)Lambda
2019独角兽企业重金招聘Python工程师标准>>> 目录戳这里 这篇说说"内部方法"和"Lambda"如何解析. 先看看java是怎么实现 ...
- java开发C语言编译器:把C实现的快速排序算法编译成jvm字节码
有了前面一系列的铺垫和准备后,我们终于能走到至关重要的一刻.在本节,我们将用C语言开发快速排序算法,然后利用我们的编译器把它编译成java字节码,让C语言编写的快速排序算法能在java虚拟机上顺利执行 ...
- 第1章 JVM语言家族概览 《Kotin 编程思想·实战》
第1章 JVM语言家族概览 天地和而万物生,阴阳接而变化起.<荀子·礼记> 1.1 编程语言简述 1.1.1 编程语言是什么 所谓编程语言只是一个抽象的规范,而编译器是这个规范的实现,它是 ...
- SAP UI5 应用开发教程之六十三 - 基于 OData V4 的本地 Mock Server 实现的深入介绍试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- SAP UI5 应用开发教程之五十三 - 如何自定义 SAP UI5 数据类型(Data Type)试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- SAP UI5 应用开发教程之三十三 - SAP UI5 应用的响应式布局特性(Responsiveness)试读版
一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...
- 如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)
如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)_转 来源:www.armjishu.com 作者:jesse 转载请注明出处 首先,如果你有幸看到这篇文章,千万不要试图在2个小时内阅读完 ...
- java程序设置jvm_Java程序员应在2018年学习的3种JVM语言
java程序设置jvm 如果您是Java程序员,并且想学习更多的编程语言以扩展您的知识和技能,但是不确定选择哪种编程语言,那么您来对地方了. 在本文中,我将分享Java程序员可以在2018年学习的3种 ...
最新文章
- 新浪微博的“独立”与互联网社区的“群居”
- [转]VS2010安装说明及所有安装出错的解决办法
- [Java基础]Stream流的收集操作
- 表示微型计算机系统稳定性,计算机基础知识(三)135
- Oil Deposit
- vantUI应用(Tabbar标签页)返回上一页的失效问题
- 以色列网络安全初创企业Cronus获350万美元A轮融资
- 大龄技术人的出路在哪里?6月20日长沙-中国技术开放日邀请你一起探讨
- mysql explain select_type
- 批量word转pdf——VBS脚本,在office宏中运行即可
- 解放前端工程师——手把手教你开发自己的自定义列表和自定义表单系列之三表格
- 视觉+机械手-delta并联机械手
- 未找到导入的项目“C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\VC\VCT
- c++将文件保存至txt文件的方法
- 400集高并发分布式超级电商项目实战
- html中JSON格式化输出
- Mysql 5.7 表名大写遇到的坑 error: 1146: Table 'your_table' doesn't exist
- 使用 Webmin+bind9快速搭建私有DNS服务器
- unity3d学习之镜头耀斑
- VUEcli3设置页签图标
热门文章
- 芯片焊接和PCB设计引脚的长度及位置对于焊接质量的教训
- 实现windows标准的选择文件夹功能
- python计算运动会某个参赛选手的得分。数据保存在文件中_Python基础教程之第二章---变量和字符串(1) 搬运...
- 两个整数相加 相乘 有符号与无符号 溢出判断条件 移位与2的幂
- 【C 语言】文件操作 ( 配置文件读写 | 框架搭建 | 写出或更新配置文件 | 读取配置文件 )
- 【开发环境】Windows 中安装 Python 各个版本 ( 下载 Python 各版本 SDK | 安装 Python )
- 【Flutter】Flutter 布局组件 ( 布局组件简介 | Row 组件 | Column 组件 | SizedBox 组件 | ClipOval 组件 )
- Python与用户的交互 ,格式化输出的三种方式
- Bugku——Web——矛盾
- bzoj 4711 小奇挖矿 ——“承诺”类树形dp