• 《lua设计与实现》第6章 指令的解析与执行--6.4 函数相关的操作指令


        在下面的示例中,(3)的prev成员指向(2),(2)的prev成员指向(1),(1)的prev为NULL。

    g = 10              -- (1) 全局环境的 FuncState: 其 prev 为 NULL
    function fun ()     -- (2) fun  的 FuncState: 其 prev 指向(1)
        local a = 2
        function test() -- (3) test 的 FuncState: 其 prev 指向(2)
            local b = 1
            -- g -> test -> fun -> 全局环境 -> VGLOBAL
            -- b -> test ->VLOCAL
            -- a -> test -> fun -> VUPVAL
            print(a,b,g)
        end
        test()
    end
    fun()

        Lua解释器在解析完某个环境(比如全局环境)中定义的函数之后,生成的 Proto 结构体数据必然也会存放在该环境的 Proto结构体数组中,最后一并返回。测试代码:

    function test()
    end

         ChunkSpy编译出来的指令如下。

    ; function [0] definition (level 1)
    ; 0 upvalues, 0 params, 2 stacks
    .function  0 0 2 2
    .const  "test"  ; 0
    
    ; function [0] definition (level 2)
    ; 0 upvalues, 0 params, 2 stacks
    .function  0 0 0 2
    [1] return     0   1      
    ; end of function
    
    [1] closure    0   0        ; 0 upvalues #第一层函数即全局环境,用于定义函数;
    [2] setglobal  0   0        ; test       #用于将函数体的信息赋值给第二层的函数test。
    [3] return     0   1      
    ; end of function

        函数定义相关指令 OP_CLOSURE 格式如下:

    typedef enum {
    /*A: 存放函数的寄存器  B: Proto数组的索引  C: 无 */
    // ......
    OP_CLOSURE,/*A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))*/
    // ......
    } OpCode;

          解释器会依次走过以下函数(箭头左方),右边是其对应的EBNF表示

    chunk    -> {stat [';']}
    statement-> body
    body     -> funcstat
    funcstat -> FUNCTION funcname body

         函数调用堆栈,这里的重点是 body 和 funcstat两个函数。

    int dofile(const char *filename, const char *cmd);
    LUALIB_API int luaL_loadfile (lua_State *L, const char *filename);
    LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname);
    int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
    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);
    static void f_parser (lua_State *L, void *ud);
    Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name);
    // EBNF 对应函数
    static void chunk (LexState *ls);
    static int statement (LexState *ls);
    static void body (LexState *ls, expdesc *e, int needself, int line);
    static void funcstat (LexState *ls, int line);

         函数body处理函数体的解析

    static void body (LexState *ls, expdesc *e, int needself, int line) {
      /* body ->  `(' parlist `)' chunk END */
      FuncState new_fs;
      open_func(ls, &new_fs); //主要用于初始化 new_fs
    
      //完成函数体的解析,解析结果保存在局部变量 new_fs 的 Proto *f 中 。
      new_fs.f->linedefined = line;
      checknext(ls, '(');
      if (needself) {
        new_localvarliteral(ls, "self", 0);
        adjustlocalvars(ls, 1);
      }
      parlist(ls);
      checknext(ls, ')');
      chunk(ls);
      new_fs.f->lastlinedefined = ls->linenumber;
      check_match(ls, TK_END, TK_FUNCTION, line);
    
      //将最后分析的结果保存到 new_fs 的 Proto 中
      close_func(ls);
      // 主要功能是把 new_fs 的 Proto 指针保存到父函数 FuncState 的 Proto指针的 p 数组中
      pushclosure(ls, &new_fs, e);
    }
    pushclosure
    static void pushclosure (LexState *ls, FuncState *func, expdesc *v) {
      FuncState *fs = ls->fs;
      Proto *f = fs->f;
      int oldsize = f->sizep;
      int i;
      luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,
                      MAXARG_Bx, "constant table overflow");
      while (oldsize < f->sizep) f->p[oldsize++] = NULL;
      f->p[fs->np++] = func->f; //将解析函数体生成的 Proto 指针赋值到父函数 FuncState 的 Proto数组中
      luaC_objbarrier(ls->L, f, func->f);
      // 生成一个OP CLOSURE 指令。这里的类型是需要重定向的,因为后面需要将这个函数体与具体的变量进行绑定
      init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));
      for (i=0; i<func->f->nups; i++) {//将外部引用的对象赋值到当前函数棋中
        OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
        luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);
      }
    }

        用于存放函数信息的结构体是Closure

    #define ClosureHeader 
        CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; 
        struct Table *env
    
    typedef struct CClosure {
      ClosureHeader;
      lua_CFunction f;
      TValue upvalue[1];
    } CClosure;
    
    
    typedef struct LClosure {
      ClosureHeader;
      struct Proto *p;  //用于存放解析函数体代码之后的指令
      UpVal *upvals[1]; //用于保存外部引用的局部变量
    } LClosure;
    
    
    typedef union Closure {
      CClosure c;
      LClosure l;
    } Closure;

        funcstat 处理函数的定义,即如何把函数体信息和变量结合在一起。 

    static void funcstat (LexState *ls, int line) {
      /* funcstat -> FUNCTION funcname body */
      int needself;
      expdesc v, b; // v用来保存函数名信息, b用来保存函数体信息
      luaX_next(ls);  /* skip FUNCTION */
      needself = funcname(ls, &v);   // 解析函数名
      body(ls, &b, needself, line);  // 解析函数体
      luaK_storevar(ls->fs, &v, &b); // 前面解析出来的 b 与 v对应上
      luaK_fixline(ls->fs, line);  /* definition `happens' in the first line */
    }

         根据变量不同的作用域来生成保存变量的语句

    void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
      switch (var->k) {
        case VLOCAL: {
          freeexp(fs, ex);
          exp2reg(fs, ex, var->u.s.info);
          return;
        }
        case VUPVAL: {
          int e = luaK_exp2anyreg(fs, ex);
          luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
          break;
        }
        case VGLOBAL: {
          int e = luaK_exp2anyreg(fs, ex);
          // 此例的 test 是全局的,所以会生成一个 OP_SETGLOBAL 语句
          luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
          break;
        }
        case VINDEXED: {
          int e = luaK_exp2RK(fs, ex);
          luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
          break;
        }
        default: {
          lua_assert(0);  /* invalid var kind to store */
          break;
        }
      }
      freeexp(fs, ex);
    }
    luaK_fixline
  • 相关阅读:
    finalShell 文件上传拖拽失败
    centos6.x 启动docker报错
    笔记本查看当前登录用户
    保存文件到D盘时显示“你没有权限在此文件夹中保存文件,请联系管理员“其他文件夹正常?
    关于MongoDB配置文件的一个小细节
    ubuntu: mongoDB安装,无需下载
    Java 连接虚拟机中MongoDB 所需依赖
    信息知识竞赛刷题助手
    python超多常用知识记录
    python字典键或值去重
  • 原文地址:https://www.cnblogs.com/yyqng/p/14773866.html
Copyright © 2020-2023  润新知