当前位置: 首页 > article >正文

LuaJit分析(八)LuaJit预编译库函数加载过程

在luajit的pmain函数中,在执行命令行输入的参数之前,会执行如下函数:

luaL_openlibs(L);

该函数位于lib_init.c中,实现如下:

LUALIB_API void luaL_openlibs(lua_State *L)
{
  const luaL_Reg *lib;
  for (lib = lj_lib_load; lib->func; lib++) {
    lua_pushcfunction(L, lib->func);
    lua_pushstring(L, lib->name);
    lua_call(L, 1, 0);
  }
  luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD",
     sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1);
  for (lib = lj_lib_preload; lib->func; lib++) {
    lua_pushcfunction(L, lib->func);
    lua_setfield(L, -2, lib->name);
  }
  lua_pop(L, 1);
}

它通过编遍历lj_lib_load,加载所有的库函数,lj_lib_load定义如下:

static const luaL_Reg lj_lib_load[] = {
  { "",     luaopen_base },
  { LUA_LOADLIBNAME,  luaopen_package },
  { LUA_TABLIBNAME, luaopen_table },
  { LUA_IOLIBNAME,  luaopen_io },
  { LUA_OSLIBNAME,  luaopen_os },
  { LUA_STRLIBNAME, luaopen_string },
  { LUA_MATHLIBNAME,  luaopen_math },
  { LUA_DBLIBNAME,  luaopen_debug },
  { LUA_BITLIBNAME, luaopen_bit },
  { LUA_JITLIBNAME, luaopen_jit },
  { LUA_FFILIBNAME, luaopen_ffi },
  { NULL,   NULL }
};

这里定义了luajit中的库函数类型,第二个参数是定义的C函数,以luaopen_base为例,定义如下,位于lib_base.c中:

LUALIB_API int luaopen_base(lua_State *L)
{
  /* NOBARRIER: Table and value are the same. */
  GCtab *env = tabref(L->env);
  settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);
  lua_pushliteral(L, LUA_VERSION);  /* top-3. */
  newproxy_weaktable(L);  /* top-2. */
  LJ_LIB_REG(L, "_G", base);
  LJ_LIB_REG(L, LUA_COLIBNAME, coroutine);
  return 2;
}

其中有一个宏定义 LJ_LIB_REG 用于注册库函数,宏定义如下:

#define LJ_LIB_REG(L, regname, name) \
  lj_lib_register(L, regname, lj_lib_init_##name, lj_lib_cf_##name)

从函数名即可看出是注册函数用,lj_lib_register位于lj_lib.c中,实现如下:

void lj_lib_register(lua_State *L, const char *libname,
         const uint8_t *p, const lua_CFunction *cf)
{
  GCtab *env = tabref(L->env);
  GCfunc *ofn = NULL;
  int ffid = *p++;
  BCIns *bcff = &L2GG(L)->bcff[*p++];
  GCtab *tab = lib_create_table(L, libname, *p++);
  ptrdiff_t tpos = L->top - L->base;
  /* Avoid barriers further down. */
  lj_gc_anybarriert(L, tab);
  tab->nomm = 0;
  for (;;) {
    uint32_t tag = *p++;
    MSize len = tag & LIBINIT_LENMASK;
    tag &= LIBINIT_TAGMASK;
    if (tag != LIBINIT_STRING) {
      const char *name;
      MSize nuv = (MSize)(L->top - L->base - tpos);
      GCfunc *fn = lj_func_newC(L, nuv, env);
      if (nuv) {
  L->top = L->base + tpos;
  memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv);
      }
      fn->c.ffid = (uint8_t)(ffid++);
      name = (const char *)p;
      p += len;
      if (tag == LIBINIT_CF)
  setmref(fn->c.pc, &G(L)->bc_cfunc_int);
      else
  setmref(fn->c.pc, bcff++);
      if (tag == LIBINIT_ASM_)
  fn->c.f = ofn->c.f;  /* Copy handler from previous function. */
      else
  fn->c.f = *cf++;  /* Get cf or handler from C function table. */
      if (len) {
  /* NOBARRIER: See above for common barrier. */
  setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn);
      }
      ofn = fn;
    } else {
      switch (tag | len) {
      case LIBINIT_LUA:
  p = lib_read_lfunc(L, p, tab);
  break;
      case LIBINIT_SET:
  L->top -= 2;
  if (tvisstr(L->top+1) && strV(L->top+1)->len == 0)
    env = tabV(L->top);
  else  /* NOBARRIER: See above for common barrier. */
    copyTV(L, lj_tab_set(L, tab, L->top+1), L->top);
  break;
      case LIBINIT_NUMBER:
  memcpy(&L->top->n, p, sizeof(double));
  L->top++;
  p += sizeof(double);
  break;
      case LIBINIT_COPY:
  copyTV(L, L->top, L->top - *p++);
  L->top++;
  break;
      case LIBINIT_LASTCL:
  setfuncV(L, L->top++, ofn);
  break;
      case LIBINIT_FFID:
  ffid++;
  break;
      case LIBINIT_END:
  return;
      default:
  setstrV(L, L->top++, lj_str_new(L, (const char *)p, len));
  p += len;
  break;
      }
    }
  }
}

其中下面这句:

switch (tag | len) {
 case LIBINIT_LUA:
  p = lib_read_lfunc(L, p, tab);
  break;

当是一个LUA的库函数时,使用lib_read_lfunc函数读取,即预编译的那么几个库函数,lib_read_lfunc函数实现如下:

static const uint8_t *lib_read_lfunc(lua_State *L, const uint8_t *p, GCtab *tab)
{
  int len = *p++;
  GCstr *name = lj_str_new(L, (const char *)p, len);
  LexState ls;
  GCproto *pt;
  GCfunc *fn;
  memset(&ls, 0, sizeof(ls));
  ls.L = L;
  ls.p = (const char *)(p+len);
  ls.pe = (const char *)~(uintptr_t)0;
  ls.c = -1;
  ls.level = (BCDUMP_F_STRIP|(LJ_BE*BCDUMP_F_BE));
  ls.chunkname = name;
  pt = lj_bcread_proto(&ls);
  pt->firstline = ~(BCLine)0;
  fn = lj_func_newL_empty(L, pt, tabref(L->env));
  /* NOBARRIER: See below for common barrier. */
  setfuncV(L, lj_tab_setstr(L, tab, name), fn);
  return (const uint8_t *)ls.p;
}

可以看到,该函数通过调用lj_bcread_proto读取luajit字节码的原型数据,接着使用原型创建一个新函数,然后保存该函数变量


http://www.kler.cn/a/283287.html

相关文章:

  • Python多进程间通讯(包含共享内存方式)
  • 字节、快手、Vidu“打野”升级,AI视频小步快跑
  • 翼鸥教育:从OceanBase V3.1.4 到 V4.2.1,8套核心集群升级实践
  • 运行WHTools批量启动游戏房间工具提示要安装.Net Framework3.5解决
  • 管家婆财贸ERP BB059.银行流水导入对账
  • 【webrtc】 RTP 中的 MID(Media Stream Identifier)
  • 【秋招笔试】8.21华为秋招-三语言题解
  • 算法训练营|图论第4天 110.字符串接龙 105.有向图的完全可达性 106.岛屿的周长
  • 网络原理 TCP与UDP协议
  • 本地构建spotbugs,替换gradle的默认仓库地址。
  • chapter08-面向对象编程——(Object类详解)——day09
  • 【C++ Primer Plus习题】7.5
  • Docker方式部署K8s集群
  • 灵神算法题单——不定长滑动窗口(求最长最大)
  • C#入门(13)if语句
  • HTML简单了解和基础知识记录
  • 《机器学习》 基于GANs构建数字图像生成器 探索深度学习世界
  • 群晖(Docker Compose)配置 frp 服务
  • 移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——8.stackqueuepriority_queue(模拟实现)
  • zset使用lua实现取最高分数中的随机成员
  • 使用notepad++将shell脚本转为UNIX格式方法(主要差别在换行符)
  • MySQL中的锁详解
  • AndroidStudio无线连接Android手机进行调试
  • 利润暴涨507%的携程,做对了什么?
  • C++/Qt 多媒体(续三)
  • 酒店管理系统小程序(包含源码C++实现)