Berry 异常处理 1: 语法和字节码设计
语法
最近在实现 Berry 的异常处理特性,进过初步的调查后决定使用类似 Python 的 try-except
异常处理模式,为此要引入三个新的关键字:
try
:表示异常捕获块的开始,位于异常捕获块中的代码抛出的异常将会被捕获,并由except
语句指定的代码来处理。except
:由该关键字构成的语句后跟随一个用于处理指定异常的代码块。raise
:该语句用于抛出一个异常。
异常处理的常见写法类似这样:
try
...
raise error
except ErrorName:
...
end
在 Berry 中,raise
语句后允许跟 1 到 2 个表达式,第一个表达式为抛出的异常值,第二个可选参数为额外的参数。except
语句的写法则比较多:
excpet Exception1 {, Exception2}:
:捕获Exception1
、Exception2
等异常。excpet Exception1 {, Exception2} as e [, arg]
:捕获Exception1
、Exception2
等异常。捕获到的异常对象存储在变量e
中,同时获取一个可选的额外参数arg
。except .. as e [, arg]:
:将捕获的异常对象存入变量e
,任何异常都会被捕获。同时获取一个可选的额外参数arg
。except:
:捕获所有异常。
字节码设计
功能定义
我们新增了 3 个指令用于运行时的异常处理特性支持。这三个指令是:
EXBLK
:开辟或关闭异常捕获块。所有在异常捕获块中的发生的异常都会被该块捕获,之后会跳转到指定的异常处理代码中。CATCH
:检查被捕获的异常是否在给定的捕获异常值列表中,如果是则应执行相应的处理代码。RAISE
:抛出异常值及其附加参数。
这里可能要解释一些概念:“异常值”是指用于表示某种异常的值,它可以是一个数、字符串或者是类,在进行异常捕获时,只有抛出的异常值和捕获列表中的异常值存在匹配时才能执行相应的异常处理代码;“异常附加参数”是指在抛出异常值的同时可以额外传递的一个值,通常用来说明异常的详细信息。
指令参数定义
EXBLK
该指令存在两种模式:
EXBLK 0 sBx
在这个模式下,指令的 A 操作数为 0,该模式下
EXBLK
指令会创建一个异常捕获块。如果在该块中捕获到异常,VM 将会恢复到执行该指令时的状态并跳转到地址为 pc + sBx 的位置执行代码。
EXBLK 1 Bx
在该模式下,指令的 A 操作数为 1,该模式将关闭 Bx 个异常捕获块。使用该指令即表明异常捕获块结束。
在没有 break
和 return
一类的跳转语句时,EXBLK 0 sBx
和 EXBLK 1 Bx
大致分别出现在 try
和第一个 except
语句之间(这是一个异常捕获快的范围)。
CATCH
CATCH
指令对应源码中的 except
语句。其指令格式为:
CATCH A B C
CATCH
指令会到由 A 到 A + B - 1 索引的寄存器中查找是否有匹配的异常,如果有,则:
- 从栈顶开始拷贝 C 个值到从 A 开始的寄存器中,这些值就是异常值和异常参数(因此最多有两个)。
- 跳过下一条指令(通常是一个跳转到下一个
CATCH
块的指令)。
如果匹配不成功,则不会执行上述操作,因此 CATCH
指令后的一条指令将被执行。
RAISE
该指令对应于脚本中的 raise
语句,其指令格式为:
RAISE 0 B
RAISE 1 B C
RAISE 2
第一种模式中,操作数 A 的值为 0,此时会将 B 操作数索引的寄存器中的值作为异常值抛出。第二种模式中,操作数A为 1,此时会将 B 寄存器中的值作为异常值抛出,同时将 C 寄存器中的值作为额外参数抛出。第三种模式下,RAISE
指令会将现有的异常值和额外参数抛出(它们通常由先前的 RAISE
指令产生)。
字节码的使用
现在,我们通过一段简单的代码来说明字节码的生成方式:
try
raise 'my_except', 'test'
except 'my_except' as e, v:
print(e, v)
end
这段代码会生成下面的字节码(假设该代码段在一个函数中):
Line 1 0: EXBLK 0 [4] ; jump to 4
Line 2 1: RAISE 1 R256 R257 ; R256: 'my_except', R257: 'test'
2: EXBLK 1 1
3: JMP [13] ; jump to 13
Line 3 4: MOVE R0 R256 ; R256: 'my_except'
5: CATCH R0 1 2
6: JMP [12] ; jump to 12
Line 4 7: GETGBL R2 G:14 ; G14: <function: print>
8: MOVE R3 R0
9: MOVE R4 R1
10: CALL R2 2
11: JMP [13] ; jump to 13
12: RAISE 2
Line 5 13: RET 0 R0
其中第 0,1,2,5,12 条指令是为异常处理新增的指令。
- 第 0 条指令由
try
语句翻译而成,它开辟一个异常处理块并继续向下执行,如果在该块被关闭前发生了异常,VM 状态将回到该条指令并跳转到第 4 条指令。 - 第 1 条指令由
raise
语句翻译成,它抛出一个异常,异常值和异常参数分别位于 R256 和 R257 中(也就是常量 0 和常量 1)。异常抛出后,VM 将会返回第 0 条EXBLK
指令并跳转到第 4 条指令。而以下指令不会被执行。- 第 2 条用于实现在离开异常处理块时对其的销毁。这里的指令对应于顺序流程中异常处理块的退出,在跳出外层循环或者函数返回时也要使用该指令退出异常处理块(如果嵌套了多级
try
语句则要退出多层异常处理快)。 - 第 3 条指令用于跳过和该
try
语句配合的所有except
语句块。代码如果执行到第 2,3 条指令则说明没有发生异常,因此不必进行异常捕获。
- 第 2 条用于实现在离开异常处理块时对其的销毁。这里的指令对应于顺序流程中异常处理块的退出,在跳出外层循环或者函数返回时也要使用该指令退出异常处理块(如果嵌套了多级
- Line 3 中的 3 行指令由源代码中的
except
行翻译成,首先使用MOVE
指令将函数常量表中的'my_except'
字符串加载到寄存器 R0 中,随后CATCH
指令会进行异常捕获。- 从第 5 条的
CATCH
指令参数中可以看出,第 1 个待匹配异常值存储在R0
中,总共有 1 个待匹配的异常值(也就是'my_except'
字符串)。该指令还获取 2 个捕获值(由操作数 C 给出),它们分别对应变量e
和v
。 - 代码运行时,由于这个
excpet
分支能成功捕获到一个'my_except'
异常值,因此会被执行。异常捕获的整个过程是:- 第 1 条
RAISE
指令抛出异常 - VM 状态重置到 第 0 条
EXBLK
处,并跳转到第 4 条指令(由第 0 条指令的 sBx 操作数给出) - 第 4 ~ 5 条指令被执行,后者匹配到
'my_except'
异常,因此将异常值和异常参数存储到变量e
和v
- 第 5 条指令
CATCH
匹配成功后会跳过下一条指令,因此 VM 接下来执行指令 7,这里对应源代码第 4 行的异常信息打印代码 - 执行到第 11 条指令,跳出当前的
except
分支,也就是跳出整个异常捕获语句。异常捕获和处理的流程结束
- 第 1 条
- 从第 5 条的
这里简单说一下异常处理过程中捕获失败时的情况(从第 5 条指令开始):
CATCH
指令匹配异常值失败,不会获取异常变量和异常参数,也不跳过下一条指令- 第 6 条的
JMP
语句跳转到第 12 条语句 - 此时分几种情况:
- 例子中只有一个
excpet
分支,因此第 12 条指令直处接用RAISE
重新把当前异常抛给上级异常处理机制。 - 实际上可能存在多个
except
分支,此时第 12 条指令对应下一个分支的开始。在最后一条分支后总会有一个用于捕获失败时重新抛出异常的RAISE
指令。
- 例子中只有一个
总结
到此,字节码层面和源码之间对应的转换关系已经说完。接下来我们需要根据这些关系来设计编译器的相关部分。当然,异常处理机制的实现还离不开运行时的支持,因此我们还要实现这三条字节码的运行时功能。这些内容将在后面的文章中讲解。
Berry 异常处理 1: 语法和字节码设计的更多相关文章
- Java虚拟机-字节码指令
目录 字节码指令 字节码与数据类型 加载和存储指令 运算指令 类型转换指令 对象创建与访问指令 操作数栈管理指令 控制转移指令 方法调用和返回指令 异常处理指令 同步指令 字节码指令 Java虚拟机的 ...
- AOP AspectJ 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Javassist 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析
[WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...
- Java 编程的动态性,第 7 部分: 用 BCEL 设计字节码--转载
在本系列的最后三篇文章中,我展示了如何用 Javassist 框架操作类.这次我将用一种很不同的方法操纵字节码——使用 Apache Byte Code Engineering Library (BC ...
- [WebKit内核] JavaScriptCore深度解析--基础篇(一)字节码生成及语法树的构建
看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>,写的很好,深受启发.想补充一些Horkey没有写到的 ...
- 通过字节码分析Java异常处理机制
在上一次[https://www.cnblogs.com/webor2006/p/9691523.html]初步对异常表相关的概念进行了了解,先来回顾一下: 其源代码也贴一下: 下面来看一下jclas ...
- 字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相对于小傅哥之前编写的字节码编程: ASM.Javassist 系列,Byte Bu ...
- 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的
概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...
随机推荐
- 16-vim-查找字符或单词-01-查找
1.常规查找 查找到指定内容之后,使用n查找下一个出现的位置. 命令 功能 /str 查找str 例:/python n 查找下一个 N 查找上一个 2.单词快速匹配(常用) 命令 功能 * 向下查找 ...
- ant的安装和使用
1.ant的安装 1.1 添加环境变量:ANT_HOME=D:\software\ant\apache-ant-1.10.1 在path中添加:%ANT_HOME%\bin 1.2 测试是否安装成功 ...
- TurtleBOT3
ubuntu更换源 sudo cp /etc/apt/sources.list /etc/apt/sources_backup.list sudo gedit /etc/apt/sources.lis ...
- linux每日命令(4):解压命令
1) Ubuntu 16.04 已经自动安装了unzip 软件,解压命令: unzip FileName.zip 2) 如果没有安装unzip,可以使用下面的命令安装: sudo apt instal ...
- idea设置忽略svn的文件或目录
1. 这个地方可以设置忽略的文件和目录,但是这里设置之后,我们在工程里面就看不到了 2. 这里设置提交是后要忽略的文件,比如我忽略的target目录,*.iml 我再1中设置了target目录发现我的 ...
- 【记录】linux中不同颜色代表的含义
下面是linux约定不同类型文件默认的颜色 白色:表示普通文件 蓝色:表示目录 绿色:表示可执行文件 红色:表示压缩文件 浅蓝色:链接文件 红色闪烁:表示链接的文件有问题 黄色:表示设备文件 灰色:表 ...
- Centos7命令行安装Tomcat以及配置防火墙开放端口
[转载]Centos 7 yum安装tomcat 命令: 系统环境CentOS Linux release 7.2.1511 (Core) 一.搭建准备:1.先到tomcat官网https://tom ...
- 深入理解MAGENTO – 第九章 – 数据集合瓦瑞恩
本来,作为一个PHP程序员,如果你想攒一组变量的相关你有一个选择,古老的 阵列 . 尽管共享一个地址的名称与C存储器的阵列,一个PHP数组是一种通用的字典可变数组索引像数值对象结合行为的影响. 在其他 ...
- INNODB存储引擎之缓冲池
以下的资料总结自:官方文档和<MySQL技术内幕-INNODB存储引擎>一书. 对INNODB存储引擎缓冲池的那一段描述来自博文:http://www.ywnds.com/?p=9886说 ...
- Yii2 使用十一 在设置enablePrettyUrl时候,defaultAction的设置方法
启用美化Url的功能 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'enableS ...