lua2.1函数调用过程分析(完善中)

本文深入分析Lua2.1中函数调用的过程,包括指令解析、栈上参数处理、do_call函数的工作原理及返回值调整等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数调用过程在lua2.1中的实现异常难读懂,所以这里专门以一篇文章来分析。

 

 

已有知识:

 

比如func1(a, b) body end

 

func1(1, 2)时依次执行的指令是PUSHGLOBAL index(func1) PUSH1 PUSH2 CALLFUNC 2 1

 

执行到指令CALLFUNC的时候,栈上自底向上有函数对象f,参数1和参数2.

 

我们可以用 (栈顶-1-参数数量)得到函数对象的位置。

 

假设栈顶是位置是4,第二参数的位置是3, 第一个参数的位置是2,那么函数对象的位置就是1。

 

接着再求一个“base”,base的计算公式是(栈顶-栈底) - 参数数量,上面这个例子中base为(4 - 0) - 2 = 2。

 

也就是说base含义是表示第一参数相对于栈顶的偏移量(偏移了多少个对象)。

 

紧接着就是调用函数了,这里会调用do_call函数。

 

do_call的原型如下:

void do_call(Object * func, StkId base, int nResult, StkId whereRes);

 

我们看lua2.1对do_call添加的注释:

 

调用一个函数(C或lua)。

参数必须在栈上,并且在[stack + base, top)域内。

返回值必须也在栈上,在[stack + whereRes, top)域内。

当nResult不等于MULT_RET的时候,表示返回值的数量。

 

我们再次回到CALLFUNC指令的执行过程。它将whereRes置为base - 1,而base - 1恰好是函数对象f,

这就意味着,函数执行结束之后对象f,函数的参数将不复存在,取而代之的是返回值。显然lua2.1中还可

以有意地将返回值放到其他位置,不过现在暂时还没看到这种使用方法。

 

do_call函数有主要的两个部分:

第一部分是找到函数对应的指令通过lua_execute或callC函数来执行指令串。这个部分后面再详细说明。

 

第二部分则是调整返回值:

不管是callC和lua_execute函数,他们都有一个共同点是返回值的含义,都是指第一个返回值的

偏移值。这两个函数虽然有base作为参数传入,但是并非“老老实实”地将返回值放到[stack + whereRes, top)

域内,这主要的原因是他们并不知晓whereRes。do_call函数在目标指令执行完之后将返回值的数量调整到

nResult,多则截断,少则补充nil对象。然后再将调整好的结果移动到以偏移whereRes开始的地方。在移动之后,

栈顶将变成调整之后的返回值中的最后一个返回值的下一个对象。

 


 

callC和lua_execute:

首先看函数lua_execute,这个函数其实就是lua2.1的心脏了,真正地去执行指令的就是这个函数。

它产生的结果是在栈顶,假设调用lua_execute之前的base = 1,栈顶top = 4,调用之后top = 10;那么可

以推断参数有3个,返回值有6个,而它向do_call返回的值为1 + 3 = 4。在这里我们可以看出RETCODE0和

RETCODE指令的作用,他们指出了返回值与base之间的偏移量,千万不要误会诚“指出了返回值的数量”。

 

接着再看函数callC,这个函数是实际上是去调用一个类型为lua_CFunction的C函数,我们先看这个

函数的相关注释:

调用一个C函数,CBase将指向栈顶,CnResult代表参数数量,返回第一个返回值在栈中的偏移。

 

callC中调用的C函数 func可能会有返回值,这个返回值以CBase为开始,所以返回值为CBase,那么CBase

是否会在函数func执行的时候变化呢,通过查代码我们知道在可供C调用的API中有四个会对CBase进行修改:

lua_getsubscript

lua_createtable

lua_getlocked

lua_getglobal

这为了将栈顶对象整合到栈中,都对top和CBase进行加1操作。那么接下来的问题是为什么要整合呢?这个问题没想好,

这个地方会有一个问题,就是当函数func需要向lua返回一个值并调用了lua_pushnumber(2.1)之后,然后再调用

lua_getglobal,lua会得不到这个值,得到的是一个nil对象。所以在C函数开始向lua返回值的过程中,就不能再

出现这四个函数中的任何一个。并且C函数不能向lua返回一个table(第一,通过lua_getglobal获得的table不能

作为返回值;第二,创建一个表也不能作为返回值,因为它被整合到栈中了)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值