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。
实际上,要考虑的不仅仅是栈深度,还要考虑栈占用一个位置还是两个位置。比如doublelong就会占用两个栈的位置,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相关推荐

  1. 从零开始开发JVM语言(七)语义分析的起步

    2019独角兽企业重金招聘Python工程师标准>>> 目录戳这里 语法分析结束后,编译才刚刚开始.接下来是语义分析. 语义分析的功能大体来说,是对AST加入类型信息.不过不仅是类型 ...

  2. 从零开始开发JVM语言(十一)Lambda

    2019独角兽企业重金招聘Python工程师标准>>> 目录戳这里 这篇说说"内部方法"和"Lambda"如何解析. 先看看java是怎么实现 ...

  3. java开发C语言编译器:把C实现的快速排序算法编译成jvm字节码

    有了前面一系列的铺垫和准备后,我们终于能走到至关重要的一刻.在本节,我们将用C语言开发快速排序算法,然后利用我们的编译器把它编译成java字节码,让C语言编写的快速排序算法能在java虚拟机上顺利执行 ...

  4. 第1章 JVM语言家族概览 《Kotin 编程思想·实战》

    第1章 JVM语言家族概览 天地和而万物生,阴阳接而变化起.<荀子·礼记> 1.1 编程语言简述 1.1.1 编程语言是什么 所谓编程语言只是一个抽象的规范,而编译器是这个规范的实现,它是 ...

  5. SAP UI5 应用开发教程之六十三 - 基于 OData V4 的本地 Mock Server 实现的深入介绍试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  6. SAP UI5 应用开发教程之五十三 - 如何自定义 SAP UI5 数据类型(Data Type)试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  7. SAP UI5 应用开发教程之三十三 - SAP UI5 应用的响应式布局特性(Responsiveness)试读版

    一套适合 SAP UI5 初学者循序渐进的学习教程 教程目录 SAP UI5 本地开发环境的搭建 SAP UI5 应用开发教程之一:Hello World SAP UI5 应用开发教程之二:SAP U ...

  8. 如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)

    如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习)_转 来源:www.armjishu.com 作者:jesse 转载请注明出处 首先,如果你有幸看到这篇文章,千万不要试图在2个小时内阅读完 ...

  9. java程序设置jvm_Java程序员应在2018年学习的3种JVM语言

    java程序设置jvm 如果您是Java程序员,并且想学习更多的编程语言以扩展您的知识和技能,但是不确定选择哪种编程语言,那么您来对地方了. 在本文中,我将分享Java程序员可以在2018年学习的3种 ...

最新文章

  1. 新浪微博的“独立”与互联网社区的“群居”
  2. [转]VS2010安装说明及所有安装出错的解决办法
  3. [Java基础]Stream流的收集操作
  4. 表示微型计算机系统稳定性,计算机基础知识(三)135
  5. Oil Deposit
  6. vantUI应用(Tabbar标签页)返回上一页的失效问题
  7. 以色列网络安全初创企业Cronus获350万美元A轮融资
  8. 大龄技术人的出路在哪里?6月20日长沙-中国技术开放日邀请你一起探讨
  9. mysql explain select_type
  10. 批量word转pdf——VBS脚本,在office宏中运行即可
  11. 解放前端工程师——手把手教你开发自己的自定义列表和自定义表单系列之三表格
  12. 视觉+机械手-delta并联机械手
  13. 未找到导入的项目“C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\VC\VCT
  14. c++将文件保存至txt文件的方法
  15. 400集高并发分布式超级电商项目实战
  16. html中JSON格式化输出
  17. Mysql 5.7 表名大写遇到的坑 error: 1146: Table 'your_table' doesn't exist
  18. 使用 Webmin+bind9快速搭建私有DNS服务器
  19. unity3d学习之镜头耀斑
  20. VUEcli3设置页签图标

热门文章

  1. 芯片焊接和PCB设计引脚的长度及位置对于焊接质量的教训
  2. 实现windows标准的选择文件夹功能
  3. python计算运动会某个参赛选手的得分。数据保存在文件中_Python基础教程之第二章---变量和字符串(1) 搬运...
  4. 两个整数相加 相乘 有符号与无符号 溢出判断条件 移位与2的幂
  5. 【C 语言】文件操作 ( 配置文件读写 | 框架搭建 | 写出或更新配置文件 | 读取配置文件 )
  6. 【开发环境】Windows 中安装 Python 各个版本 ( 下载 Python 各版本 SDK | 安装 Python )
  7. 【Flutter】Flutter 布局组件 ( 布局组件简介 | Row 组件 | Column 组件 | SizedBox 组件 | ClipOval 组件 )
  8. Python与用户的交互 ,格式化输出的三种方式
  9. Bugku——Web——矛盾
  10. bzoj 4711 小奇挖矿 ——“承诺”类树形dp