Python虚拟机字节码之控制流如何实现
Python虚拟机字节码之控制流实现
Python虚拟机采用了一种类栈帧结构,即为每个函数调用创建一个栈帧,每个栈帧包含了函数的参数、局部变量以及函数的执行状态,如程序计数器、返回地址等。栈帧以后入先出的方式存放在虚拟机的栈中,形成了调用链。 Python解释器每次只会执行栈顶的栈帧,栈顶栈帧执行完毕后再弹出并销毁,然后继续执行下一个栈帧。控制流是由字节码指令来实现的。
1、条件跳转
条件跳转主要利用了jumps指令的分支作用。jumps指令专门用于在代码流中跳转。 当某条件成立时,就通过jumps调到指定位置开始执行指定的代码块。如果条件不成立,就可以继续往下执行。示例代码如下:
def compare(a,b): if a>b: return "greater" elif a==b: return "equal" else: return "less"
对应的字节码如下:
2 LOAD_FAST 0 (a) 5 LOAD_FAST 1 (b) 8 COMPARE_OP 4 (>) 11 POP_JUMP_IF_FALSE 20 14 LOAD_CONST 0 ('greater') 17 RETURN_VALUE 18 JUMP_FORWARD 11 (to 32) 20 LOAD_FAST 0 (a) 23 LOAD_FAST 1 (b) 26 COMPARE_OP 2 (==) 29 POP_JUMP_IF_FALSE 38 32 LOAD_CONST 1 ('equal') 35 RETURN_VALUE 36 JUMP_FORWARD 9 (to 48) 38 LOAD_CONST 2 ('less') 41 RETURN_VALUE 44 JUMP_FORWARD 1 (to 48)
2、循环
循环是由jump指令来实现的,包括while、for等,可以使用jumps指令实现循环开始和循环结束条件,从而执行相应的代码块。示例代码如下:
def squareList(n): result = [] for i in range(n): result.append(i*i) return result
对应的字节码如下:
2 BUILD_LIST 0 5 LOAD_GLOBAL 0 (range) 8 LOAD_FAST 0 (n) 11 CALL_FUNCTION 1 14 GET_ITER 15 FOR_ITER 28 (to 46) 18 STORE_FAST 1 (i) 21 LOAD_FAST 1 (i) 24 LOAD_FAST 1 (i) 27 BINARY_MULTIPLY 28 LIST_APPEND 2 31 JUMP_ABSOLUTE 15
3、异常处理
Python支持try/except语句块来处理异常,字节码内部提供了一些指令来支持异常处理。try块包含了可能产生异常的代码,当代码块中发生异常时,就会由对应的exception handler代码来处理异常。对于try/except外的其他代码,也需要设置对应的exception handler,以免出现未处理的异常。示例代码如下:
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except IOError as e: print("I/O error occurred: ", format(e.errno, e.strerror)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise finally: f.close()
对应的字节码如下:
2 SETUP_FINALLY 14 (to 19) 5 LOAD_GLOBAL 0 (open) 8 LOAD_CONST 1 ('myfile.txt') 11 CALL_FUNCTION 1 14 STORE_FAST 0 (f) 17 JUMP_FORWARD 2 (to 22) 20 POP_BLOCK 21 POP_EXCEPT 22 LOAD_FAST 0 (f) 25 LOAD_ATTR 1 (readline) 28 CALL_FUNCTION 0 31 STORE_FAST 1 (s) 34 LOAD_GLOBAL 2 (int) 37 LOAD_FAST 1 (s) 40 LOAD_ATTR 3 (strip) 43 CALL_FUNCTION 0 46 CALL_FUNCTION 1 49 STORE_FAST 2 (i) 52 JUMP_FORWARD 2 (to 57) 55 POP_BLOCK 56 POP_EXCEPT 57 LOAD_FAST 0 (f) 60 LOAD_ATTR 4 (close) 63 CALL_FUNCTION 0 66 POP_BLOCK
4、函数调用
函数调用是字节码的基本操作之一,Python虚拟机为每个函数调用创建一个新的栈帧,并将其入栈。调用将转移到新的栈帧,该栈帧在调用完成后将被移出栈。除了依次将函数的参数推入栈中,函数调用时还必须为函数分配新的帧对象。针对不同的数据类型,Python虚拟机还提供了相应的函数调用指令,如CALL_FUNCTION、RETURN_VALUE等。示例代码如下:
def greet(name): return "Hello " + name def add(x, y): return x + y greeting = greet("John") result = add(2, 3) print(greeting) print(result)
对应的字节码如下:
2 LOAD_CONST 1 ('John') 5 CALL_FUNCTION 1 8 STORE_FAST 1 (greeting) 11 LOAD_CONST 2 (2) 14 LOAD_CONST 3 (3) 17 BINARY_ADD 18 RETURN_VALUE