`

LUA源码分析八:小总结,完整分析dofile的过程和堆栈

    博客分类:
  • LUA
阅读更多


关于一些语法包装的问题不涉及(可见前面某篇),主要对堆栈排列上的分析。

一路跟调到static void f_parser (lua_State *L, void *ud)函数,堆栈记录如下:


+ L->top 0x003950e8:当前指针

+ L->stack 0x003950a8

+ L->base 0x003950b8

+ L->ci 0x00398e08


跟调到Proto *luaY_parser函数中。该函数主要进行了语法解析,FuncState的建立,最后返回结构体

Proto。里面具体怎么解析语法等我们都不需要关系,我们只需要关注哪些结果被压入了堆栈。

 

Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {

  struct LexState lexstate;
  struct FuncState funcstate;
  lexstate.buff = buff;
  /*
  	不涉及堆栈,只是设置lexstate的一些参数
  */
  luaX_setinput(L, &lexstate, z, luaS_new(L, name));
  /*
  	会在top上压入2个值,Proto(表示函数属性)和一张表。
  	L->top排列如下:
  	L->top	0x003950e8:funcstate->h
  	L->top	0x003950f8:luaF_newproto(L)
  */
  open_func(&lexstate, &funcstate);
  funcstate.f->is_vararg = VARARG_ISVARARG;  /* main func. is always vararg */
  luaX_next(&lexstate);  /* read first token */
  
  chunk(&lexstate);
  check(&lexstate, TK_EOS);
  /*
  	将之前压入的2个值退出,堆栈恢复为
  	+		L->top	0x003950e8
  */
  close_func(&lexstate);
  lua_assert(funcstate.prev == NULL);
  lua_assert(funcstate.f->nups == 0);
  lua_assert(lexstate.fs == NULL);
  return funcstate.f;
}

 

return上层函数f_parser,继续跟入:

static void f_parser (lua_State *L, void *ud) {
  int i;
  Proto *tf;
  Closure *cl;
  struct SParser *p = cast(struct SParser *, ud);
  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);
  /*
  	根据返回回来的函数属性新建一个closure,tf->nups表示多少个upvalues。
  	并且将tf设置回cl,初始化好upvalues值等
  */                                                           
  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);
	/*
		改变了L->top
  	L->top	0x003950e8:cl,
  	cl地址为 0x00473780
	*/  
  incr_top(L);
}

 

一路返回,断点到最初的luaL_loadfile中,该函数最后执行了一句lua_remove(L, fnameindex);

清除了一些参数(不影响到子调用的压栈),现在top里面排列如下:

+ L->top 0x003950d8:cl包

+ L->top 0x003950e8:当前指针

+ L->stack 0x003950a8

+ L->base 0x003950b8

+ L->ci 0x00398e08


根据宏

#define luaL_dofile(L, fn) \
	(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))

 

接下来调用lua_pcall函数:

LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
  struct CallS c;
	/*
		nargs为0,这段地址算出来为0x003950d8,
		也就是我们上文创建的cl包
	*/
  c.func = L->top - (nargs+1);  /* function to be called */
  c.nresults = nresults;
  /*
  	savestack算的是c.func和L->stack的差值。
  	0x003950d8-0x003950a8=48
  */
  status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
  adjustresults(L, nresults);
  lua_unlock(L);
  return status;
}

 

跟入luaD_pcall->luaD_rawrunprotected->f_call->luaD_call->luaD_precall

int luaD_precall (lua_State *L, StkId func, int nresults) {
  LClosure *cl;
  ptrdiff_t funcr;
  if (!ttisfunction(func)) /* `func' is not a function? */
    func = tryfuncTM(L, func);  /* check the `function' tag method */
  /*
  	savestack算的是c.func和L->stack的差值。
  	0x003950d8-0x003950a8=48
  */    
  funcr = savestack(L, func);
  cl = &clvalue(func)->l;
  L->ci->savedpc = L->savedpc;
  if (!cl->isC) {  /* Lua function? prepare its call */
    CallInfo *ci;
    StkId st, base;
    //取出函数属性
    Proto *p = cl->p;
    luaD_checkstack(L, p->maxstacksize);
    //??为什么不直接用func呢?
    func = restorestack(L, funcr);
    if (!p->is_vararg) {  /* no varargs? */
      base = func + 1;
      if (L->top > base + p->numparams)
        L->top = base + p->numparams;
    }
    else {  /* vararg function */
    	/*
    		因为不带参数,所以base返回L->top,姑且是这个值
    	*/
      int nargs = cast_int(L->top - func) - 1;
      base = adjust_varargs(L, p, nargs);
      func = restorestack(L, funcr);  /* previous call may change the stack */
    }
    /*
      设置好ci的堆栈
      ci->func=0x003950d8
      ci->base=0x003950e8
      ci->top= 0x00395108
      L->savedpc=0x00473860  编译出来的虚拟指令
    */
    ci = inc_ci(L);  /* now `enter' new function */
    ci->func = func;
    L->base = ci->base = base;
    ci->top = L->base + p->maxstacksize;
    lua_assert(ci->top <= L->stack_last);
    L->savedpc = p->code;  /* starting point */
    ci->tailcalls = 0;
    ci->nresults = nresults;
    /*
    	清空ci需要的堆栈
    */
    for (st = L->top; st < ci->top; st++)
      setnilvalue(st);
    /*
    	L->top=0x00395108  
    */
    L->top = ci->top;
    if (L->hookmask & LUA_MASKCALL) {
      L->savedpc++;  /* hooks assume 'pc' is already incremented */
      luaD_callhook(L, LUA_HOOKCALL, -1);
      L->savedpc--;  /* correct 'pc' */
    }
    return PCRLUA;
  }
}
 

 

跟入执行luaV_execute,根据 L->savedpc获取执行码

先获取环境表

      case OP_GETGLOBAL: {
        TValue g;
        TValue *rb = KBx(i);
        sethvalue(L, &g, cl->env);
        lua_assert(ttisstring(rb));
        Protect(luaV_gettable(L, &g, rb, ra));
        continue;
      }

 再

      case OP_LOADK: {
        setobj2s(L, ra, KBx(i));
        continue;
      }

 

前面两个的具体意义都可以先不管,最后的调用.如果ci里有多个函数需要执行,则返回值为

PCRC,从下一指令的ci设置完毕,即可马上调用

      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)) {
          case PCRLUA: {
            nexeccalls++;
            goto reentry;  /* restart luaV_execute over new Lua function */
          }
          case PCRC: {
            /* it was a C function (`precall' called it); adjust results */
            if (nresults >= 0) L->top = L->ci->top;
            base = L->base;
            continue;
          }
          default: {
            return;  /* yield */
          }
        }
      }     

 

跟调到最后的luaD_precall.上文已经分析过luaD_precall,不同的是if (!cl->isC)的判断。上文中

为lua function,此次为C function

int luaD_precall (lua_State *L, StkId func, int nresults) {
  LClosure *cl;
  ptrdiff_t funcr;
  if (!ttisfunction(func)) /* `func' is not a function? */
    func = tryfuncTM(L, func);  /* check the `function' tag method */
  funcr = savestack(L, func);
  cl = &clvalue(func)->l;
  L->ci->savedpc = L->savedpc;
  if (!cl->isC) {  /* Lua function? prepare its call */
 
  }
  else {  /* if is a C function, call it */
    CallInfo *ci;
    int n;
    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */
    ci = inc_ci(L);  /* now `enter' new function */
    
    /*
    	设置好ci的环境。要执行的起始栈地址为要执行的函数+1,栈顶为L->top+LUA_MINSTACK
    	ci->func:0x003950e8
    	L->base:0x003950f8
    	ci->top:0x00395248
    */
    ci->func = restorestack(L, funcr);
    L->base = ci->base = ci->func + 1;
    ci->top = L->top + LUA_MINSTACK;
    lua_assert(ci->top <= L->stack_last);
    ci->nresults = nresults;
    
    if (L->hookmask & LUA_MASKCALL)
      luaD_callhook(L, LUA_HOOKCALL, -1);
    lua_unlock(L);
    /*
    	执行到最终的函数
    */
    n = (*curr_func(L)->c.f)(L);  /* do the actual call */
    lua_lock(L);
    if (n < 0)  /* yielding? */
      return PCRYIELD;
    else {
    	/*
    		整理CI的下一次行为.默认结尾时压入一条return的虚拟指令
    	*/
      luaD_poscall(L, L->top - n);
      return PCRC;
    }
  }
}
 
2
4
分享到:
评论

相关推荐

    云风-lua源码欣赏-lua-5.21

    接下来,书中讨论了Lua的虚拟机(VM)实现,包括字节码的翻译和预编译过程。这部分内容对于理解Lua的执行效率和如何优化代码至关重要。此外,还涉及到了内嵌库的实现,这些库是Lua功能的重要组成部分,如数学运算、...

    lua源码分析

    由于Lua的源码相对较为简洁,且注重性能,因此对于想要深入学习C语言、数据结构、算法设计,或者是希望了解语言设计和实现的程序员而言,Lua源码分析可以成为一个非常有价值的学习资源。通过研究Lua的源码,开发者...

    Lua源码分析

    Lua源码分析中会关注到代码翻译的过程,它包括如何将Lua代码转换为字节码,以及这些字节码如何被虚拟机加载和执行。 5. 内嵌库: Lua官方提供了内嵌库来丰富Lua语言的功能。源码分析中会探讨这些库的实现方式以及...

    lua 源码剖析

    `lparser.c`文件包含了词法分析和语法分析的逻辑。 3. **垃圾回收(Garbage Collection)**:lua使用了增量式标记-清除垃圾回收算法,这在`lgc.c`中有详细实现。理解这一机制对于避免内存泄漏和优化性能至关重要。 ...

    lua 源码分析

    《深入剖析:Lua源码分析》 一、引言与背景 Lua,作为一种小巧而强大的脚本语言,凭借其高效性、灵活性以及易嵌入特性,在游戏开发、系统管理、Web应用等多个领域得到了广泛的应用。它由标准C语言编写而成,这不仅...

    Lua源码欣赏中文版

    首先,Lua源码的下载、阅读和分析需要一定的C语言基础,因为Lua是基于C语言实现的。官方提供的Lua5.0版本源码的中文解读文档可以作为理解源码的辅助材料。在开始阅读源码之前,应先了解Lua语言的标准和官方实现的...

    lua源码鉴赏云风

    2. **语法解析**:Lua的词法分析和语法分析是通过Lemon解析器完成的,这是一个小型但功能强大的解析工具。云风会解释如何从源代码字符串解析出抽象语法树(AST),进而转换成虚拟机指令。 3. **垃圾回收**:Lua使用...

    Lua源码剖析,Lua虚拟机的机制分析,硕士论文

    Lua源码剖析、Lua虚拟机的机制分析是硕士论文的主题,本文从Lua语言的特性入手,深入分析Lua虚拟机的实现,涵盖编译过程、线程执行、函数调用和垃圾回收过程。然后,本文将Lua虚拟机中的关键技术与Pyt hon虚拟机的...

    Wireshark 解析插件(lua源码)

    Wireshark 解析插件(lua源码) Wireshark 解析插件(lua源码) Wireshark 解析插件(lua源码) Wireshark 解析插件(lua源码) Wireshark 解析插件(lua源码) Wireshark 解析插件(lua源码) Wireshark 解析插件...

    lua源码下载 Lua-5.3.4 源码 最新 截止2017-3-7

    通过分析和编译源码,你可以了解到Lua是如何高效地实现动态语言特性的,如闭包、元表、弱引用等。同时,也可以根据需求修改源码,创建定制版的Lua解释器,满足特定应用场景的需求。总之,研究Lua源码是提升编程技能...

    lua脚本源码包

    通过分析lua_test的源码,你可以了解到如何在实际项目中嵌入lua,这对于嵌入式系统开发者来说是非常有价值的。你可以学习到如何在有限的资源环境下利用lua的灵活性和高效性,来增强软件的功能和可维护性。同时,lua_...

    《Lua 源码欣赏》

    综上所述,这篇文章不仅对Lua源码进行了深入的分析,而且对Lua语言的设计理念和优化技巧进行了全面的讲解。对于希望深入了解脚本语言实现机制的程序员来说,这篇文章是一份宝贵的资料。通过对Lua源码的解读,我们...

    lua脚本加密工具:简单异或加密

    做了那么多 Lua 脚本破解,我们来尝试写一个不能被破解的加密。 所谓不能被破解,并不是真正不能被破解,只是在没有密码的情况下很难破解。

    lua分析分析

    编译过程中,`lua_load`函数负责实际的词法分析和语法解析,生成字节码。如果文件以Lua的二进制格式(LUA_SIGNATURE)开头,`luaL_loadfile`会将文件重新打开为二进制模式进行处理。 4. 函数与闭包 `luaL_loadfile`...

    所有版本LUA源码

    所有版本LUA源码 lua-5.3.5 lua-5.3.4 lua-5.3.3 lua-5.3.2 lua-5.3.1 lua-5.3.0 lua-5.2.4 lua-5.2.3 lua-5.2.2 lua-5.2.1 lua-5.2.0 lua-5.1.5 lua-5.1.4 lua-5.1.3 lua-5.1.2 lua-5.1.1 lua-5.1 lua-5.0.3 lua-...

    lua源码导读---云风

    总结,《lua源码导读》通过对 Lua 源代码的深度剖析,不仅让读者了解了 Lua 的内部运作机制,也为深入学习和使用 Lua 提供了宝贵的资源。无论是对于初学者还是有一定经验的开发者来说,这本书都极具价值。

    Lua 源码剖析.rar

    《Lua 源码剖析》是由知名IT专家云风精心撰写的关于Lua编程语言源代码解析的著作。这本书深入浅出地探讨了Lua的核心机制,为开发者提供了深入了解和掌握Lua的宝贵资源。以下是对该书内容的详细概述: 1. **Lua简介*...

    云风《Lua源码欣赏》1积分

    第七章深入分析了Lua的虚拟机架构,涵盖了指令结构、字节码的执行细节、寄存器赋值、表处理、表达式运算、分支和跳转、函数调用、不定长参数、生成闭包以及For循环等虚拟机内部的工作原理。 最后,第八章探讨了内置...

    Lua中文教程+源码赏析

    这个资源包包含了“Lua中文教程”和“Lua源码赏析”两部分,旨在帮助初学者在短短两小时内快速掌握Lua编程基础,并通过源码分析深入理解其内部机制。 “Lua中文教程”可能涵盖以下内容: 1. **基础语法**:Lua的...

Global site tag (gtag.js) - Google Analytics