代码流程分析
来源:互联网 发布:excel03显示重复数据 编辑:程序博客网 时间:2024/06/08 18:46
在larva的语义和流程分析上,我偷了很大的懒,基本等于没做,因为觉得做起来太麻烦了
流程分析可以在一段语法上完全没有问题的代码中,找出可能有问题的代码,这个不同编译器支持程度也不同,其实在绝大多数情况下,不做这个问题也不大,不过larva面对了一个相关的不可逃避的问题,因为它和python一样有一个特性:若一个函数或方法最后没有return,则自动return一个空对象,larva中是nil,python中则是None,比如:
根据实现的不同,这个问题也有各种程度的解决办法
最简单的就是编译器不做任何处理,采用解释器执行,无论是解释ast还是字节码,若一个函数执行到最后还没有return,虚拟机就自动return nil
另一个简单方法是,无论何种情况,给函数后面加一个return nil即可,虽然会造成下面这种情况:
不过larva并不能用上述这两种方式,原因有两点:
1 larva的实现可能是(也是目前唯一的方式)转换为其它代码,而其它语言的编译器可能很严格,如果larc不报错,而是转换后的代码报错,会增加排错难度
2 为提高效率,larva引入了类型推导,在第二种做法下,会对类型推导产生干扰,例如,一个函数中所有return都是int型表达式,原本可以将其返回类型推导为int,但增加一个return nil的额外代码会改变这个结果。而另一种方式,如果先推导函数返回类型,再根据返回类型为object或int在代码最后补return nil或return 0,这就和语言规范冲突了,因为larva规定没有显式return的情况下return nil
P.S.更激进一点的意见是,直接改语言规范,在没有显式return的情况下,返回值未定义,不过目前没有往这个方向走,其实将来可以在这方面做努力的,下面会提到
larva在这个问题上采用了一种简单的折中处理,直接检测函数的最后一个语句,若没有return则补一个return nil,不过不能简单检测最后一个语句就是return,比如如下代码:
1 没有嵌套语句块的语句,只需要判断是不是return即可
2 for、while循环,虽然嵌套了语句块,但是不能保证一定能执行语句块,即根本不进入循环,所以也不会“必定”返回,不需判断
3 if语句,需判断
if语句的判断规则是,一个if语句必定return,当且仅当它带else部分,且所有语句块都必定return
这是一个递归定义,所以判断的算法也是递归的,很简单
P.S.目前还没有实现for和while的带else语法,如果像python一样有for...else和while...else语法,上述第2条就不成立了,对于for和while也需要判断,而且算法更复杂些
很明显larva目前这种做法是不完善的,比如对这种代码:
上面这个例子是和return相关的死代码检测,不过还有其它一些死代码检测,比如:
larva在流程分析上做的还有一点不够好的是变量初始化和使用的分析,比如:
有人可能会说,只要所有变量都初始化就行了,这话其实只说对了一半,正确的说法是,只有所有变量都用字面量或按执行顺序有依赖地初始化,才能规避初值未定义的问题,比如这个代码:
如果是上面两个变量的例子,改成这样:
流程分析可以在一段语法上完全没有问题的代码中,找出可能有问题的代码,这个不同编译器支持程度也不同,其实在绝大多数情况下,不做这个问题也不大,不过larva面对了一个相关的不可逃避的问题,因为它和python一样有一个特性:若一个函数或方法最后没有return,则自动return一个空对象,larva中是nil,python中则是None,比如:
func f(): print "hello world"f最后应该隐式return nil
根据实现的不同,这个问题也有各种程度的解决办法
最简单的就是编译器不做任何处理,采用解释器执行,无论是解释ast还是字节码,若一个函数执行到最后还没有return,虚拟机就自动return nil
另一个简单方法是,无论何种情况,给函数后面加一个return nil即可,虽然会造成下面这种情况:
func f(): return 0 return nil但这在语法上是正确的,执行也没有问题,只是最后的return永远执行不到而已,就是占用代码空间
不过larva并不能用上述这两种方式,原因有两点:
1 larva的实现可能是(也是目前唯一的方式)转换为其它代码,而其它语言的编译器可能很严格,如果larc不报错,而是转换后的代码报错,会增加排错难度
2 为提高效率,larva引入了类型推导,在第二种做法下,会对类型推导产生干扰,例如,一个函数中所有return都是int型表达式,原本可以将其返回类型推导为int,但增加一个return nil的额外代码会改变这个结果。而另一种方式,如果先推导函数返回类型,再根据返回类型为object或int在代码最后补return nil或return 0,这就和语言规范冲突了,因为larva规定没有显式return的情况下return nil
P.S.更激进一点的意见是,直接改语言规范,在没有显式return的情况下,返回值未定义,不过目前没有往这个方向走,其实将来可以在这方面做努力的,下面会提到
larva在这个问题上采用了一种简单的折中处理,直接检测函数的最后一个语句,若没有return则补一个return nil,不过不能简单检测最后一个语句就是return,比如如下代码:
func f(a): if a == 0: return 123 else: return 456其实这种嵌套return也只有if语句的情况,编译器需要判断最后一条语句“必定”return,则分三种情况:
1 没有嵌套语句块的语句,只需要判断是不是return即可
2 for、while循环,虽然嵌套了语句块,但是不能保证一定能执行语句块,即根本不进入循环,所以也不会“必定”返回,不需判断
3 if语句,需判断
if语句的判断规则是,一个if语句必定return,当且仅当它带else部分,且所有语句块都必定return
这是一个递归定义,所以判断的算法也是递归的,很简单
P.S.目前还没有实现for和while的带else语法,如果像python一样有for...else和while...else语法,上述第2条就不成立了,对于for和while也需要判断,而且算法更复杂些
很明显larva目前这种做法是不完善的,比如对这种代码:
func f(): return a = 0因为我比较懒,就只检测了最后一条语句,但另一方面我认为这种代码比较少,所以就没详细考虑,不过要做似乎也不是很难,只需要循环检测stmt_list即可,后面有空再改进
上面这个例子是和return相关的死代码检测,不过还有其它一些死代码检测,比如:
if false: print "never execute"或者:
while true: ... //代码块没有breakreturn //永远执行不到或者:
for i in l: break //或continue print "never execute"这类检测很多编译器也都实现了,其实就是对各种情况分别做判断
larva在流程分析上做的还有一点不够好的是变量初始化和使用的分析,比如:
func f(): print a a = 123根据语法规定,a是一个局部变量,若是python,则会在运行时查出错误,但是larva为了运行效率,省掉了这一步,因此规定,所有变量(larva不像python可以动态增减变量,所以编译期可以完全确定)的初始值是未定义的(但实际实现中,一般是赋一个合适的初值,比如nil),这点和其它语言有类似之处,比如C代码:
int a;printf("%d", a);a = 123;只不过,C编译器会对这个流程做分析,提示错误或告警。但这种分析往往是基于代码前后位置的,有时候程序员可能写出语法和语义上都没啥问题的,但编译器比较难分析的代码,比如:
for i in list: if i == 1: print a else: a = 123若list是[0, 1],则这段代码是没啥问题,由于循环和判断,a是先赋值再打印的,但是编译器比较难检测这种情况,因为根本不知道list是什么内容,但有的时候又的确会存在这种代码,所以最好的处理方式也只能是给一个警告了,程序员可以在for之前给a赋值一个nil来消除这个警告,虽然这个赋值没什么用
有人可能会说,只要所有变量都初始化就行了,这话其实只说对了一半,正确的说法是,只有所有变量都用字面量或按执行顺序有依赖地初始化,才能规避初值未定义的问题,比如这个代码:
a = bb = aa和b的确“初始化”了,但它们互相依赖了,甚至对于这种代码:
a = a其实写了等于没写,虽然现代C++编译器已经尽可能检查这类问题,但也有漏的,比方说在vs中的这个代码(a是局部变量):
A a = a + 1;A是一个class,实现一下拷贝构造和operator+(int),就可以看到,这行在a初始化之前就调用了operator+(int),是可能出问题的,而且编译器没有警告
如果是上面两个变量的例子,改成这样:
int a = 1;int b = a;如果执行顺序如此,是没啥问题,但如果这两句执行顺序不固定,就可能有问题,比如这两句在两个不同模块,和运行时模块加载和初始化顺序有关,就可能有问题了
0 0
- Sensor 代码流程分析
- Sensor 代码流程分析
- Unreal 代码流程分析
- 代码流程分析
- openRTSP 代码流程分析
- Sensor 代码流程分析
- dm9000驱动代码流程分析
- MapReduce关键流程代码分析
- MapReduce关键流程代码分析
- A20启动代码流程分析
- A20启动代码流程分析
- MapReduce关键流程代码分析
- EDK2 VfrCompiler 代码流程分析
- APScheduler代码流程简要分析
- A20启动代码流程分析
- linux启动流程分析(代码分析) .
- 代码流程分析二:Settings-蓝牙分析-switch开关分析
- TSE代码爬取流程分析
- 动态表单及动态建表实现原理
- STM32中关于串口通信的printf()函数重定向问题
- x4412开发板&ibox卡片电脑项目实战14-linux驱动的编写步骤
- 用Emacs muse制作幻灯片
- 高性能网络编程(一)----accept建立连接
- 代码流程分析
- 在N9使用TabGround进行页面导航(02)
- malloc,free的简单实现
- Mina、Netty、Twisted一起学(九):异步IO和回调函数
- for 和 foreach 的区别
- 通过网页获取路由器外网IP地址
- webView 报错
- 【Algorithm】关于n!被整除的问题【算法实现】
- linux创建守护进程