ad

《自己动手写 Python 虚拟机》_更理解虚拟机的意义_6.1.1 栈帧

网友投稿 127 2023-11-07

【摘要】 本书摘自《自己动手写 Python 虚拟机》一书中第6章,第1节,海纳编著。

6.1.1 栈帧

在第2章讲解递归函数的时候,已经讲过在每一次函数调用时,都会有一个栈帧 与之相对应。在执行器里,还没有栈帧的概念,因此,要实现一种数据结构来对函数 的调用过程进行记录,这个数据结构就是 FrameObject 。 每一次调用一个函数,便有 一个这次调用的活动记录(也就是 FrameObject) 被创建,每次函数执行结束,它所对 应的活动记录也会被销毁。

《自己动手写 Python 虚拟机》_更理解虚拟机的意义_6.1.1 栈帧

在 Interpreter 里,有很多变量是为函数执行时的活动记录服务的。例如 pc 记 录了程序当前执行到的位置,locals 表记录了变量的值等。本质上这些变量是与函 数的执行绑定的,所以它们应该被封装到 FrameObject 里,而不是 Interpreter 中。 因此,做的第一件事情就是对 Interpreter 进行重构,将所有这些与执行状态相关的 变量移到 FrameObject 中。

先定义 FrameObject, 代码如下:

class FrameObject(

2 public:

3 FrameObject(CodeObject*codes);

4 FrameObject();

5

6 ArrayList*_stack;

7 ArrayList* _loop_stack;

8

9 ArrayList*_consts;

10 ArrayList*_names;

11

12 Map*_locals;

13

14 CodeObject * _codes;

15 int _pc;

16

17 public:

18 void set_pc(int x) {_pc =x;}

19 int get_pc() (return_pc;)

20

21 ArrayList*stack() {returnstack;)

22 ArrayList*loop_stack() {return_loop_stack;}

23 ArrayList*consts() {return_consts;}

24 ArrayList*names() {return_names;}

25 Map*locals() {return_locals;)

26

27 bool has_more_codes();

28 unsigned char get_op_code();

29 int get_op_arg();

30 };

上述代码已经把 Interpreter 中的相关变量都转移到 FrameObject 中来了。注

意第3行的构造方法,之前要执行一段 Code时,是直接调用Interpreter 的 run 方 法,以 CodeObject 为参数。现在,则要先创建一个 FrameObject,代码执行时,就只 会影响 FrameObject 中的 pc 和 locals 等变量。

这段代码的逻辑很简单,FrameObject 只是对一些状态变量的封装,包括27~ 29 所声明的三个方法,也不过是对一些简单逻辑的封装,具体的实现如代码清单6.2 所示。

//this constructor is used for module only.

2 FrameObject::FrameObject(CodeObject *codes)

3 _consts =codes ->_consts;

4 names =codes->_names;

5

6 locals =new Map();

7

8 _stack =new ArrayList();

9 _loop_stack =new ArrayList();

10

11 _codes =codes;

12 _pc =0;

13 }

14

15 int FrameObject::get_op_arg()(

16 int bytel =_codes->_bytecodes->value()[_pc++]&0xff;

17 int byte2 =_codes →_bytecodes->value()[_pc++]&0xff;

18 return byte2<<8|bytel;

19 }

20

21 unsigned char FrameObject::get_op_code(){

22 return_codes>_bytecodes>value()[_pc++];

23

24

25 bool FrameObject::has_more_codes()(

26 return_pc <._codes>_bytecodes >length();

27

与之相应的,Interpreter 的 run 方法也发生了很多变化,代码如下:

1 //[runtime/interpreter.cpp]

2 //stack has been moved into FrameObject.

3 #define PUSH(x) _frame->stack()->add((x))

4 #define POP() frame->stack()>pop()

5 #define STACK_LEVEL()_frame >stack()->size()

7 void Interpreter::run(CodeObject *codes){

8 frame =new FrameObject(codes);

9 while(_frame>has_more_codes())(

10 unsigned char op_code =_frame->get_op_code()

bool has_argument =(op_code&0xFF)>=ByteCode::HAVE_ARGUMENT;

12

int op_arg =-1;

if(has_argument){

op_arg =_frame->get_op_arg();

)

17

}

19

除了上述代码所展示的修改外,run 方法里还有很多处修改,这里就不一一列出 了,读者可以通过工程提交记录自行比较代码还有哪些变化。

有了FrameObject 这个基础结构以后,终于可以把注意力转向 MAKE_FUNC- TION 和 CALL_FUNCTION 这两个字节码了。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们 18664393530@aliyun.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:跟着一起学《Excel VBA跟卢子一起学 早做完 不加班 基础入门版》_2.4.4 定义数据类型的好处
下一篇:《深入理解 Java 虚拟机 JVM 高级特性与最佳实践(第3版)》_求知之路漫漫_3.5.7 Garbage First收集器
相关文章

 发表评论

暂时没有评论,来抢沙发吧~