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

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

cpp 复制代码
luaL_openlibs(L);

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

cpp 复制代码
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定义如下:

cpp 复制代码
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中:

cpp 复制代码
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 用于注册库函数,宏定义如下:

cpp 复制代码
#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中,实现如下:

cpp 复制代码
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;
      }
    }
  }
}

其中下面这句:

cpp 复制代码
switch (tag | len) {
 case LIBINIT_LUA:
  p = lib_read_lfunc(L, p, tab);
  break;

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

cpp 复制代码
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字节码的原型数据,接着使用原型创建一个新函数,然后保存该函数变量

相关推荐
字节程序员1 天前
使用JUnit进行集成测试
jmeter·junit·单元测试·集成测试·压力测试
两水先木示3 天前
【Unity3D】无限循环列表(扩展版)
java·开发语言·junit
小白学大数据3 天前
优化Lua-cURL:减少网络请求延迟的实用方法
开发语言·网络·爬虫·junit·lua
璀若星辰4 天前
Spring Boot 项目中 JUnit 使用总结
spring boot·后端·junit
烟草的香味.4 天前
nginx 记录完整的 request 及 response
java·nginx·junit
小诸葛的博客4 天前
wrk如何测试post请求
junit
睡睡怪7 天前
【Java学习笔记】JUnit
笔记·学习·junit
小白学大数据13 天前
Lua中实现HTTP请求的User-Agent自定义
爬虫·http·junit·数据分析·lua
帅B猪13 天前
Lua面向对象 实现 超详细注释 实现构造函数,析构函数,只读类模板等功能
java·c++·算法·junit·lua
爱代码的猴子13 天前
【记录】用JUnit 4的@Test注解时报错java.lang.NullPointerException的原因与解决方法
java·数据库·junit