版本整理日期:2011/3/31
本篇主要说明两个点:
1. 载入的IO流程,
2. lua内部调用函数流程
两个核心的函数
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef)
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
}
//先分析luaL_loadfile(L,fn),实际调用两个函数
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
在luaL_loadfile里,主要是几个结构体的传递初始化,因此一些语法上的逻辑将抛开,主要看结构体之间的IO组织和传递。
luaL_loadfile //获取输入IO
->getF(lua_Reader), LoadF) //表示传递这两个参数给下个调用的函数
Lua_load //构建ZIO
->ZIO(getF, LoadF) //填充一个IO结构
Lua_protectedparser //上下文中保护Sparser资源
->Sparser(ZIO, name(函数名),f_parser)
luaD_pcall //带p开头的,除了调用,还做了一些栈维护的清理工作
->执行f_parser(Sparser) //f_parser是个剖析函数
luaD_rawrunprotected //公共的底层调用函数,这里调用f_parser
f_parser
->Sparser->ZIO->getF(LoadF) //调用ZIO结构体,得到数据
整个过程就是
填充IO来源->调用->调用IO->解析读到的内容
如果有其他解析的需求,整个结构上只要修改ZIO的函数指针,和一些跳转的判断。来看具体代码实现
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
//…
//打开filename文件路径,存放到lf里
LoadF lf;
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
}
关注LoadF这个结构
typedef struct LoadF {
int extraline; //扩展的命令参数,略过
FILE *f; //打开文件指针
char buff[LUAL_BUFFERSIZE]; //读取的缓存
} LoadF;
往下走lua_load(L, getF, &lf, lua_tostring(L, -1));
getF:根据LoadF结构调用fread函数
lua_tostring(L, -1):取到”LuaTest.lua”
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
const char *chunkname) {
ZIO z;
int status;
lua_lock(L);
if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname);
lua_unlock(L);
return status;
}
又是一个ZIO结构体,并且将reader,data都格式化到里面,
struct Zio {
size_t n; /* bytes still unread */
const char *p; /* current position in buffer */
//存储的是reader函数,也就是读文件
lua_Reader reader;
//存储的是LoadF结构体
void* data; /* additional data */
lua_State *L; /* Lua state (for reader) */
};
往下走luaD_protectedparser(L, &z, chunkname);
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
/*
struct SParser { /* data to `f_parser' */
ZIO *z;
Mbuffer buff; /* buffer to be used by the scanner */
/*
typedef struct Mbuffer {
char *buffer;
size_t n;
size_t buffsize;
} Mbuffer;
*/
//存放的是“LuaTest.lua”
const char *name;
};
*/
struct SParser p;
int status;
p.z = z; p.name = name;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
return status;
}
f_parser函数如下
static void f_parser (lua_State *L, void *ud) {
int i;
//Proto结构比较复杂,先略过
Proto *tf;
//
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
//按这里的调试是调用luaY_parser
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
cl->l.upvals[i] = luaF_newupval(L);
setclvalue(L, L->top, cl);
incr_top(L);
}
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef) {
int status;
unsigned short oldnCcalls = L->nCcalls;
ptrdiff_t old_ci = saveci(L, L->ci);
lu_byte old_allowhooks = L->allowhook;
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
/*
func也就是f_parser
u也就是Sparser
luaD_rawrunprotected具体怎么执行先不关心,只知道会执行f_parser,并且参数是u
*/
status = luaD_rawrunprotected(L, func, u);
if (status != 0) { /* an error occurred? */
//恢复栈,略过
}
L->errfunc = old_errfunc;
return status;
}
跟踪f_parser
static void f_parser (lua_State *L, void *ud) {
int i;
Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
/*
核心会调用buff = z->reader(L, z->data, &size);
然后设置ZIO结构的 p和n
返回p的头一个字节,似乎判断是否预编译后的文件.
这里先调用luaY_parser,
这段函数代码就不具体贴了,做的工作大体是语法解析,构建函数等等
*/
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
cl->l.upvals[i] = luaF_newupval(L);
setclvalue(L, L->top, cl);
incr_top(L);
}
接下来分析从lua文件中执行函数。如下代码为例
t={}
table.insert(t,100,13)
在ltablib.c中tinsert下断点,然后观察堆栈
LuaSrc.exe!tinsert(lua_State * L=0x00398c28) 行91 C
LuaSrc.exe!luaD_precall(lua_State * L=0x00398c28, lua_TValue * func=0x003950b8, int nresults=0) 行319 + 0x16 字节 C
LuaSrc.exe!luaV_execute(lua_State * L=0x00398c28, int nexeccalls=1) 行587 + 0x14 字节 C
LuaSrc.exe!luaD_call(lua_State * L=0x00398c28, lua_TValue * func=0x003950a8, int nResults=-1) 行377 + 0xb 字节 C
LuaSrc.exe!f_call(lua_State * L=0x00398c28, void * ud=0x0012fe68) 行800 + 0x16 字节 C
LuaSrc.exe!luaD_rawrunprotected(lua_State * L=0x00398c28, void (lua_State *, void *)* f=0x00420020, void * ud=0x0012fe68) 行118 + 0x1f 字节 C
LuaSrc.exe!luaD_pcall(lua_State * L=0x00398c28, void (lua_State *, void *)* func=0x00420020, void * u=0x0012fe68, int old_top=16, int ef=0) 行463 + 0x11 字节 C
> LuaSrc.exe!lua_pcall(lua_State * L=0x00398c28, int nargs=0, int nresults=-1, int errfunc=0) 行821 + 0x20 字节 C
LuaSrc.exe!wmain(int argc=1, wchar_t * * argv=0x00393250) 行55 + 0x29 字节 C++
LuaSrc.exe!__tmainCRTStartup() 行583 + 0x19 字节 C
LuaSrc.exe!wmainCRTStartup() 行403 C
主要把LUA里调用函数的一个规律总结出来(无关代码先剔除)
LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
/*
struct CallS { /* data to `f_call' */
StkId func;
int nresults;
};
*/
struct CallS c;
//…其他代码省略
//取到要调用的函数体
c.func = L->top - (nargs+1); /* function to be called */
c.nresults = nresults;
/*
对ud的一个调用
static void f_call (lua_State *L, void *ud) {
struct CallS *c = cast(struct CallS *, ud);
luaD_call(L, c->func, c->nresults);
}
*/
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
return status;
}
往下走luaD_pcall
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef) {
//pcall,主要工作是一些堆栈保护,然后调用luaD_rawrunprotected
status = luaD_rawrunprotected(L, func, u);
return status;
}
往下走luaD_rawrunprotected
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
//先设置一些异常的跳转点,然后调用
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
return lj.status;
}
然后就是调用f_call,f_call又调用luaD->pcall,接着走luaD->pcall
void luaD_call (lua_State *L, StkId func, int nResults) {
// luaD_precall做了什么先不关心,走luaV_execute
if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
luaV_execute(L, 1); /* call it */
}
luaV_execute是很根据虚拟机字节码跳转的函数
void luaV_execute (lua_State *L, int nexeccalls) {
const Instruction *pc;
//`savedpc' of current function
pc = L->savedpc;
const Instruction i = *pc++;
StkId ra;
//取出调用的函数
ra = RA(i);
case OP_CALL: {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
L->savedpc = pc;
//终于快到头了
switch (luaD_precall(L, ra, nresults)) {
}
}
}
往下走luaD_precall
int luaD_precall (lua_State *L, StkId func, int nresults) {
LClosure *cl;
// #define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
cl = &clvalue(func)->l;
/*
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
#define curr_func(L) (clvalue(L->ci->func))
*/
n = (*curr_func(L)->c.f)(L); /* do the actual call */
}
接着调用到最终函数tinsert (lua_State *L)
分享到:
相关推荐
6. **运行Lua代码**:使用`luaL_dostring`或`luaL_dofile`执行Lua脚本中的代码,这将调用之前注册的C++函数。 7. **内存管理**:C++函数中可能需要分配或释放内存,需要注意与Lua的垃圾回收机制协调。例如,使用`...
5. **执行Lua脚本**:通过`luaL_dostring()`或`luaL_dofile()`加载并执行Lua源码或文件。这使得你可以从C++程序中直接运行Lua代码。 6. **交互操作**:你可以通过`lua_push*()`系列函数将C++数据类型推送到Lua栈上...
在本文中,我们将深入探讨如何在C++环境中,特别是使用Visual C++ 6.0,调用Lua脚本中的有参数函数。Lua是一种轻量级的脚本语言,常用于游戏开发、配置管理以及嵌入式系统中。通过C++与Lua的交互,我们可以将业务...
使用`luaL_dofile()`或`luaL_dostring()`函数加载并执行Lua脚本。例如,你可以有一个名为`script.lua`的文件,其中定义了一个名为`move`的函数,你可以这样执行它: ```cpp luaL_dofile(L, "script.lua"); ``` ...
4. **加载Lua脚本**: 使用`luaL_dofile()`或`luaL_dostring()`加载并执行Lua脚本。例如,如果你有一个名为`test.lua`的文件,你可以这样做: ```cpp luaL_dofile(L, "test.lua"); ``` 5. **调用Lua函数**: 在Lua...
4. 调用Lua函数:通过`lua_getglobal`获取全局函数,如`lua_getglobal(L, "myFunction")`。然后,传递参数并调用`lua_pcall`执行函数,如`lua_pcall(L, nArguments, nResults, 0)`。 5. 传递数据:C++与Lua之间可以...
这主要通过 Lua 的 C API 实现,它提供了一套 C 函数供外部语言(如 C++)调用,同时也允许外部代码注册函数给 Lua 使用。在 C++ 中,我们可以创建一个 Lua 共享库,其中包含用于与 Lua 交互的函数。 1. **创建 Lua...
5. **调用Lua函数和传递参数**:通过lua_push*系列函数将C++数据转换为Lua值,然后通过lua_setglobal或lua_call将它们作为参数传递给Lua函数。同样,可以从Lua返回值使用lua_to*系列函数转换回C++类型。 6. **错误...
3. 打开脚本:使用`luaL_dofile()`或`luaL_dostring()`加载并执行Lua源文件。这里以`lua_test.lua`为例,加载模块。 ```cpp int result = luaL_dofile(L, "lua_test.lua"); if (result != 0) { printf("Error: %...
3. **打开Lua脚本文件**:使用`luaL_dofile`或`luaL_loadfile`加载`.lua`文件,如`lua_test.lua`,其中可能包含了调用`add`函数的代码。 ```cpp luaL_dofile(L, "lua_test.lua"); ``` 4. **执行Lua脚本**:加载完...
首先,C++调用Lua主要通过Lua的C API来实现,这个API提供了一系列的函数,允许C++代码加载、执行和交互Lua脚本。要开始这个过程,你需要包含Lua头文件`lua.h`并链接Lua库到你的C++项目中。 1. **初始化Lua环境**: ...
Kaguya是一个高效的lua绑定库,它通过提供简洁的API使C++开发者能够轻松地将lua函数和对象暴露给lua脚本,同时也允许lua调用C++的函数和操作C++的对象。这使得C++程序可以利用lua的灵活性和易读性来编写部分逻辑,...
这些函数会被LUA调用,参数和返回值都通过LUA栈进行传递。 - **lua_State**:LUA的状态机,是所有LUA操作的核心。你可以通过`lua_State`实例创建新的LUA环境,加载脚本,执行函数,以及管理LUA栈上的数据。 - **...
2. **加载Lua脚本**: 使用`luaL_loadbuffer`或`luaL_loadfile`函数读取和解析Lua脚本。这些函数将脚本内容加载到Lua虚拟机的堆栈中。 3. **执行Lua脚本**: 使用`lua_pcall`函数执行加载的脚本。这个函数会调用堆栈...
本示例中的"lua_test.rar"文件集是一个关于C++与Lua交互的实践案例,主要探讨了如何通过Lua脚本调用C++编写的函数。下面我们将详细探讨这个主题。 首先,Lua是一种轻量级的脚本语言,它简洁、易学,常被用于游戏...
4. **调用Lua函数**:通过 `lua_getglobal` 获取全局函数,再配合 `lua_pcall` 执行。 5. **传递参数**:将C++数据类型转换为Lua值,然后压入栈中。 6. **接收返回值**:执行函数后,栈顶会保留返回值,用 `lua_to...
它提供了一系列的函数,如`luaL_newstate`用于创建一个新的Lua环境,`luaL_openlibs`打开默认的库,以及`luaL_loadbuffer`或`luaL_dofile`用于加载和运行Lua脚本。 2. **进栈和出栈**: - **进栈**:将C/C++的数据...
3. **注册C函数到Lua**:通过`lua_pushcfunction()`和`lua_setglobal()`,你可以将C/C++函数暴露给Lua,使其在Lua脚本中可调用。 4. **执行Lua脚本**:使用`luaL_loadbufferx()`或`luaL_loadfile()`加载Lua脚本,...
3. **执行 Lua 代码**:使用 `luaL_dofile` 或 `luaL_dostring` 加载并执行 Lua 文件或字符串。 4. **错误处理**:在执行过程中,需要检查 `lua_pcall` 的返回值,以捕获和处理错误。 5. **数据交换**:使用 `lua_...
需要注意的是,每次调用 `lua_pcall()` 后,都要检查是否发生错误,通过 `lua_iserror()` 或 `lua_tostring()` 获取错误信息。同时,别忘了清理栈,防止内存泄漏,可以使用 `lua_pop(state, n)` 清除栈上 n 个元素。...