编译器的设计与实现ppt课件6_异常处理.ppt_第1页
编译器的设计与实现ppt课件6_异常处理.ppt_第2页
编译器的设计与实现ppt课件6_异常处理.ppt_第3页
编译器的设计与实现ppt课件6_异常处理.ppt_第4页
编译器的设计与实现ppt课件6_异常处理.ppt_第5页
已阅读5页,还剩54页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

编译器的设计与实现 -异常处理,制作:张云 时间:2008-04,内容,目标 什么是异常处理? 异常处理模型的确立:终止模型?唤醒模型? 需要处理哪些情况? 异常的表示:标准异常与异常层次结构 异常的捕获 异常的处理:unwind 设计与实现 符号表的设计 中间表示的设计 目标代码生成,异常处理,目标: 在前面的基础上增加对异常的支持以及能够进行异常处理 问题:什么是异常处理? 异常处理:一种错误处理方式 exception handling is a programming language construct or computer hardware mechanism designed to handle the occurrence of some condition that changes the normal flow of execution.,传统的错误处理方式,在函数中返回错误信息or设置一个全局的错误状态标志 使用标准c库中的函数:signal() (用于推断事件发生时出现了什么情况)& raise()(产生一个事件) 使用标准c库中的非局部跳转函数:setjmp() & longjmp()。使用setjmp()在程序中保存一个已知的无错误状态,一旦发生错误,就可以通过调用longjmp()返回到该状态。,异常处理,异常处理是c+的主要特征之一,是考虑问题和排除错误的一种更好的处理方式。使用异常处理: 1)错误处理代码的编写不再冗长乏味,并且不再与“正常的”代码混和在一起。 2)错误不能被忽略。如果一个函数必须向调用者发送一条错误消息,它将”抛出”一个描述这个错误的对象。如果调用者没有“捕获”并处理它,错误对象将进入上一层封装的动态范围,并且一直持续下去,直到该错误被捕获or因为程序中没有异常处理器捕获这种类型的异常而导致程序终止。,程序举例,抛出异常,class myerror char* const data; public: myerror(char* const msg = 0) : data (msg) ; void f() / 抛出异常 throw myerror(“something bad happened“); int main() f(); ,捕获异常,try块 异常处理器 捕获所有可能的异常,try / 可能会产生异常的代码 catch(type1 id1) / 处理异常1 catch(type2 id2) / 处理异常2 catch(typen idn) / 处理异常n catch(.) / 继续正常程序.,异常匹配,class except1 ; class except2 public: except2(const except1 /:,异常处理模型,终止模型 c采用的就是终止模型:如果某个函数的执行中抛出一个异常,不能在该函数的内部处理,就会导致一系列函数终止,并使控制最终转移到调用链上的某个函数中适当的异常处理器。如果找不到这样的处理器,整个程序将最终结束运行。 恢复模型 pl/i语言首先引入该模型。使用恢复模型意味着异常处理器希望能够校正这种情况,然后自动地重新执行发生错误地代码,并希望第二次执行成功。,c+异常小结,c+的exception handling主要由3部分构成: 1 一个throw子句。它在程序某处发出一个exception,被抛出的异常可以是内建类型,也可以是用户自定义类型。 2 一个try区域。它包含了一系列的语句,这些语句可能会引发异常。 3 一个或多个catch子句。每一个catch子句都是一个exception handler。即,每个子句准备处理某种类型的异常,提供相应的处理程序。,c+异常小结(cont.),当一个exception被抛出以后,控制权会从当前函数中释放出来,并寻找一个吻合的catch子句。如果没有吻合者,则调用默认的处理例程terminate()。找到匹配的catch以后,还需要进行相应的堆栈反解(unwinding the stack)。在这个过程中,需要对函数中的local class objects进行析构。,对exception handling的支持,当一个exception发生时,编译系统需要做以下工作: 1.检验发生throw操作的函数,构建抛出的异常对象; 2.决定throw操作是否发生在try区域中; 3.若是,需要把抛出的exception类型同每一个catch子句进行比较; 4.如果匹配成功,流程控制交给catch子句中; 5.如果throw的发生不在try区域中,或者没有一个catch子句吻合,那么系统将要(a)摧毁所有的active local objects,(b)从堆栈中将当前的函数unwind掉,(c)进行到程序堆栈中的下一个函数中去,重复步骤25。,需要解决哪些问题?,1.异常对象的表示? 2.决定throw操作是否发生在try区域中? 3.将抛出的exception类型同每一个catch子句进行比较? 4.unwind操作?,问题的解决,异常对象的表示,前面提到:被抛出的异常可以是内建类型,也可以是用户自定义类型。 一个异常对象就是一个普通的变量,或者对象变量 潜在问题:异常对象为子类对象的情况。在与catch子句比较的时候,如果遇到父类参数,能否匹配?如何实现?,决定throw操作是否发生在try区域中,try /begin_try /throw an exception /end_try catch(exception1&) catch(exception2&) ,解决:构造pc_range表格,记录try块所对应的目标代码的起始位置和结束位置。 当throw操作发生时,当前的pc值同对应的pc_range表格进行比较,以决定当前作用中的区域是否在一个try区段中。 如果是,找出相关的catch子句; 如果不是,怎么办?,有try-catch的时候,构建异常表; 有throw的时候进行异常表处理,如果不在当前的try区域内,怎么办?,如果不是,说明这个exception不能被当前的函数处理,这时将这个函数从函数栈中推出(popped),将pc设置为调用端地址,然后重复前面的操作。(当前的pc值同对应的pc_range表格进行比较,决定当前指令是否在该函数的try区段中。) 如果最终都没有找到相应的异常处理区段,则调用默认的terminate()函数。,将抛出抛出的exception类型同每一个catch子句的类型比较?,异常处理程序还有一个难题就是“如何根据catch块的相关数据结构判断这个catch块是否愿意处理当前异常”。这是通过比较异常的类型和catch块的参数的类型来完成的。例如下面这个程序:,void foo() try throw e(); catch(h) /. ,异常捕获,找到try块后,处理程序就遍历与其关联的catch块表,看是否有对当前异常感兴趣的catch块。 如何根据catch块的相关数据结构判断这个catch块是否愿意处理当前异常?-比较异常的类型和catch块的参数的类型! 问题: 如何在运行时进行exception类型的比较? 子类父类问题?,解决:编译器为所有的exception的类型进行编码,根据类型编码比较类型是否相同。如果是一个derived type,则编码内容还必须包括所有base class的类型信息。,实现:每一个函数会产生出一个exception表格,它描述与函数相关的各区域(try块起始位置与结束位置等),任何必要的善后码(cleanup code,被local class object destructors调用),以及catch子句的参数类型以及位置等。 问题: 运行时如何能够知道当前函数符号表中的内容?,解决: 在函数栈中增加指向符号表中相应函数信息的指针,unwind操作?,找到吻合的catch子句以后,对于那些已经不再需要的函数退栈,并且将其中存在的local class object析构掉。 问题: 如何正确的退栈?(即设置正确的bp、sp指针) 需要析构哪些局部类对象?,解决: 记录主调函数的bp、sp的内容; 记录当前语句处所有已经出现的局部类对象,查找对应的析构函数目标代码入口地址,异常在编译器中的实现,语言文法的修改,statement expression-stmt |compound-stmt|selection-stmt |iteration-stmt|delete-stmt|return-stmt | try-stmt | catch-stmt | throw-stmt try-stmt try statement catch-stmt catch(param) statement throw-stmt throw id;,示例1,class myerror int data; myerror(int msg) data = msg; ; void f() throw myerror(0); void main() try f(); catch(myerror e) e.data = 1; ,示例2,class ball int b; int c; ball(int x,int y) b = x; c = y; void testex() if(b!=0) throw ball(b); void botch() b = 0; void setx(int x) b = x; ;,void main(void) ball ball(2,3); try ball.testex(); catch (ball ex) ex.botch(); catch(int i) i=0; catch() ball.botch(); ball.setx(0); try ball.testex(); try ball.setx(7); ball.testex(); catch(int i) i=0; catch() ball.botch(); ,class ball int b; int c; ball(int x,int y) b = x; c = y; void testex() if(b!=0) throw ball(b); void botch() b = 0; void setx(int x) b = x; ;,说明,在这个例子中给出了抛出异常以后, 捕获到异常以及没有捕获到异常的情况; try块的嵌套; 内层抛出的异常如何被外层的处理器捕获到并进行处理; 当前函数没有捕获到异常以后如何将其跳转到上层的调用函数进行异常的处理; 等。,对词法分析器的修改,添加相应的节点类型: public enum tokentype try,catch,throw, ;,中间表示的设计以及对语法分析器的修改,添加相应的语法树节点类型: public enum nodetype trystm,catchstm,throwstm, ;,中间表示形式,try,语句1,语句2,catch,param,语句,try节点,catch节点,catch,throw,语句,throw节点,示例,try ball.testex(); catch (ball ex) ex.botch(); catch(int i) i=0; catch() ball.botch(); ,try,ball.testex(),catch,ex,ball.botch(),ex.botch();,=,catch,catch,i,i,0,符号表的设计,增加了哪些符号表? 枚举异常类型 运行时抛出的异常对象的信息 try块初始位置与结束位置 catch块参数信息以及入口地址 unwind操作,所有局部类对象信息,1.extypetable异常类型表,/hashtable * extype_table; public class extypeitem public int extype_id; /0:any; 1:int; 2-n:用户自定义类型 public string extype_name; public int size;/表示当前异常类型的占内存空间的大小 public int constructor_addr; public int destructor_addr; ; 前面三项内容可以在遇到类定义的时候进行创建并赋值;后面三项的内容必须在代码生成结束以后才能赋值。,2.exceptinfo,异常对象信息,遇到throw语句的时候可以创建一个exceptinfo结构用来保存抛出的异常的信息;在代码生成处理throw语句的时候创建exceptinfo结构。 public class exceptinfo public int extype_id; /异常类型标识 /public int extype_name; public exceptinfo next_exinfo;/指明父类信息 ,exceptinfo,or:,3.funinfo,函数信息结构,public class funinfo public int fun_id; /函数的唯一标识 public string fun_name; /记录函数中的try块链 public int tryblock_count; public tryblock first_tryblock; /在需要栈反解的时候使用该表中记录的信息 public int unwind_count; public hashtable unwind_table; ,4.unwind表,进行堆栈unwind的时候,需要析构所有已经创建的对象。对每一个需要unwind的对象变量记录其相对偏移以及析构函数的入口地址。 public class unwind public string objectvar_name; public string objectvar_type; public int destructor_addr; public int rva; ,5.try块,public class tryblock public int start_id; /起始位置 public int end_id; /结束位置 /当前try块对应的所有catch子句的信息 public int catchblock_count; public catchblock first_catchblock; /记录try块之间的次序以及嵌套关系 public tryblock prev_tryblock; public tryblock next_tryblock; ,6.catch块,public class catchblock public int catch_id; public varinfo para; /记录catch的参数信息! /用来指明异常类型的唯一标识id,用于进行异常匹配 public int extype_id; public int catchblock_addr;/catch处理异常的入口地址 public catchblock next_catchblock; ,tryblock_1,catchblock_1,funinfo,目标代码生成,代码生成部分除了需要将符号表中的内容补充完善以外,还需要处理对try-throw-catch结构设有专门的代码指令: 增加throw指令,throw,1) throw 47; 2) throw e(); 3) e e; throw e; 遇到throw语句以后,代码生成的时候针对后面的不同的异常对象的情况生成对应的except_info结构,同时将异常对象本身or异常对象的指针放到栈顶,throw;,try&catch,try /try_begin: start_id /try_end: end_id jmp _tryend_fun catch(exception e) /target: catchblock_addr jmp _tryend_fun catch(exception e) /target: catchblock_addr _tryend_fun: /_tryend_fun在代码生成的过程中可以静态确定 other statement,try块之前的目标代码 try块初始位置: try块内部的目标代码 try块结束位置 jmp tryend catch块入口位置: catch内部目标代码 jmp tryend catch2块入口位置: catch2块内目标代码 tryend: 其它语句的目标代码,虚拟机中运行的情况,try throw exception; catch(exception& e) 目标代码生成阶段,遇到try-catch的时候,构建异常表; 运行时,遇到throw的时候进行异常处理。,函数栈,指向当前函数的funcinfo结构;由fun_id给出,指向上一层函数的表示异常处理的部分;判断是否到达栈底(一般栈底函数为main函数,不妨设main函数中prev=-1 )。,虚拟机中执行throw语句,虚拟机执行的时候,会调用 throw (),在这个函数中完成异常的匹配、栈的反解以及跳转到合适的catch

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论