`

LUA源码分析四:通过luaL_dofile分析IO载入流程和内部函数调用结构

    博客分类:
  • LUA
阅读更多

版本整理日期: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) 

1
1
分享到:
评论

相关推荐

    lua调用C++函数

    6. **运行Lua代码**:使用`luaL_dostring`或`luaL_dofile`执行Lua脚本中的代码,这将调用之前注册的C++函数。 7. **内存管理**:C++函数中可能需要分配或释放内存,需要注意与Lua的垃圾回收机制协调。例如,使用`...

    use_Lua.rar_c++ lua_lua_lua C++ 嵌入_lua vc

    5. **执行Lua脚本**:通过`luaL_dostring()`或`luaL_dofile()`加载并执行Lua源码或文件。这使得你可以从C++程序中直接运行Lua代码。 6. **交互操作**:你可以通过`lua_push*()`系列函数将C++数据类型推送到Lua栈上...

    Lua编程事例:调用Lua有参函数

    在本文中,我们将深入探讨如何在C++环境中,特别是使用Visual C++ 6.0,调用Lua脚本中的有参数函数。Lua是一种轻量级的脚本语言,常用于游戏开发、配置管理以及嵌入式系统中。通过C++与Lua的交互,我们可以将业务...

    Lua的使用入门之在C++程序中调用lua函数1

    使用`luaL_dofile()`或`luaL_dostring()`函数加载并执行Lua脚本。例如,你可以有一个名为`script.lua`的文件,其中定义了一个名为`move`的函数,你可以这样执行它: ```cpp luaL_dofile(L, "script.lua"); ``` ...

    c++ 调用lua函数简单案例

    4. **加载Lua脚本**: 使用`luaL_dofile()`或`luaL_dostring()`加载并执行Lua脚本。例如,如果你有一个名为`test.lua`的文件,你可以这样做: ```cpp luaL_dofile(L, "test.lua"); ``` 5. **调用Lua函数**: 在Lua...

    Lua的最基本使用 C++与lua的互相调用

    4. 调用Lua函数:通过`lua_getglobal`获取全局函数,如`lua_getglobal(L, "myFunction")`。然后,传递参数并调用`lua_pcall`执行函数,如`lua_pcall(L, nArguments, nResults, 0)`。 5. 传递数据:C++与Lua之间可以...

    Lua调用C++函数实现

    这主要通过 Lua 的 C API 实现,它提供了一套 C 函数供外部语言(如 C++)调用,同时也允许外部代码注册函数给 Lua 使用。在 C++ 中,我们可以创建一个 Lua 共享库,其中包含用于与 Lua 交互的函数。 1. **创建 Lua...

    Test_Lua.rar_VS2010_VS2010 Lua_VS2010调用LUA脚本基础_lua_lua脚本

    5. **调用Lua函数和传递参数**:通过lua_push*系列函数将C++数据转换为Lua值,然后通过lua_setglobal或lua_call将它们作为参数传递给Lua函数。同样,可以从Lua返回值使用lua_to*系列函数转换回C++类型。 6. **错误...

    lua模块调用测试

    3. 打开脚本:使用`luaL_dofile()`或`luaL_dostring()`加载并执行Lua源文件。这里以`lua_test.lua`为例,加载模块。 ```cpp int result = luaL_dofile(L, "lua_test.lua"); if (result != 0) { printf("Error: %...

    lua_add调用

    3. **打开Lua脚本文件**:使用`luaL_dofile`或`luaL_loadfile`加载`.lua`文件,如`lua_test.lua`,其中可能包含了调用`add`函数的代码。 ```cpp luaL_dofile(L, "lua_test.lua"); ``` 4. **执行Lua脚本**:加载完...

    c++调用lua方式

    首先,C++调用Lua主要通过Lua的C API来实现,这个API提供了一系列的函数,允许C++代码加载、执行和交互Lua脚本。要开始这个过程,你需要包含Lua头文件`lua.h`并链接Lua库到你的C++项目中。 1. **初始化Lua环境**: ...

    C++ lua Kaguya 应用

    Kaguya是一个高效的lua绑定库,它通过提供简洁的API使C++开发者能够轻松地将lua函数和对象暴露给lua脚本,同时也允许lua调用C++的函数和操作C++的对象。这使得C++程序可以利用lua的灵活性和易读性来编写部分逻辑,...

    进行C++与LUA交互编程的LUA库

    这些函数会被LUA调用,参数和返回值都通过LUA栈进行传递。 - **lua_State**:LUA的状态机,是所有LUA操作的核心。你可以通过`lua_State`实例创建新的LUA环境,加载脚本,执行函数,以及管理LUA栈上的数据。 - **...

    c++和lua互相调用实例及讲解

    2. **加载Lua脚本**: 使用`luaL_loadbuffer`或`luaL_loadfile`函数读取和解析Lua脚本。这些函数将脚本内容加载到Lua虚拟机的堆栈中。 3. **执行Lua脚本**: 使用`lua_pcall`函数执行加载的脚本。这个函数会调用堆栈...

    lua_test.rar_C++_c# lua_c++ lua_lua_lua调用C++

    本示例中的"lua_test.rar"文件集是一个关于C++与Lua交互的实践案例,主要探讨了如何通过Lua脚本调用C++编写的函数。下面我们将详细探讨这个主题。 首先,Lua是一种轻量级的脚本语言,它简洁、易学,常被用于游戏...

    c++ 调用Lua

    4. **调用Lua函数**:通过 `lua_getglobal` 获取全局函数,再配合 `lua_pcall` 执行。 5. **传递参数**:将C++数据类型转换为Lua值,然后压入栈中。 6. **接收返回值**:执行函数后,栈顶会保留返回值,用 `lua_to...

    C\C++中调用Lua函数的接口封装

    它提供了一系列的函数,如`luaL_newstate`用于创建一个新的Lua环境,`luaL_openlibs`打开默认的库,以及`luaL_loadbuffer`或`luaL_dofile`用于加载和运行Lua脚本。 2. **进栈和出栈**: - **进栈**:将C/C++的数据...

    lua_code.zip_Lua嵌入_exe-hreader_lua_lua c_lua c++

    3. **注册C函数到Lua**:通过`lua_pushcfunction()`和`lua_setglobal()`,你可以将C/C++函数暴露给Lua,使其在Lua脚本中可调用。 4. **执行Lua脚本**:使用`luaL_loadbufferx()`或`luaL_loadfile()`加载Lua脚本,...

    luascript_source_lua_Tibia_zip_

    3. **执行 Lua 代码**:使用 `luaL_dofile` 或 `luaL_dostring` 加载并执行 Lua 文件或字符串。 4. **错误处理**:在执行过程中,需要检查 `lua_pcall` 的返回值,以捕获和处理错误。 5. **数据交换**:使用 `lua_...

    C++ 中调用 Lua 函数

    需要注意的是,每次调用 `lua_pcall()` 后,都要检查是否发生错误,通过 `lua_iserror()` 或 `lua_tostring()` 获取错误信息。同时,别忘了清理栈,防止内存泄漏,可以使用 `lua_pop(state, n)` 清除栈上 n 个元素。...

Global site tag (gtag.js) - Google Analytics