LuaJit分析(七)LuaJit -b 命令分析

Luajit -b 命令用于生成字节码文件,通过之前对 -bl命令的分析:

luajit -bl 命令分析

可知,-b系统的命令都通过执行 bcsave.lua脚本来完成, luajit -b命令最终是执行bcsave.lua脚本中的 bcsave函数,bcsave函数代码如下:

Lua 复制代码
local function bcsave(ctx, input, output)
  local f = readfile(input)
  local s = string.dump(f, ctx.strip)
  local t = ctx.type
  if not t then
    t = detecttype(output)
    ctx.type = t
  end
  if t == "raw" then
    bcsave_raw(output, s)
  else
    if not ctx.modname then ctx.modname = detectmodname(input) end
    if t == "obj" then
      bcsave_obj(ctx, output, s)
    else
      bcsave_c(ctx, output, s)
    end
  end
end

这通过关键的两个函数,来完成代码的转换:

Lua 复制代码
local f = readfile(input)
local s = string.dump(f, ctx.strip)

由之前对luajit -b 命令的分析可知,readfile函数最终返回原型数据,如果是字节码文件,返回读取后的原型数据,否则进行源码分析再返回原型数据。接着通过string.dump返回完整的字节码文件,通过ctx.strip决定是否有调试信息,string.dump为库函数,位于lib_string.c中,实现如下:

cpp 复制代码
LJLIB_CF(string_dump)
{
  GCfunc *fn = lj_lib_checkfunc(L, 1);
  int strip = L->base+1 < L->top && tvistruecond(L->base+1);
  SBuf *sb = lj_buf_tmp_(L);  /* Assumes lj_bcwrite() doesn't use tmpbuf. */
  L->top = L->base+1;
  if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip))
    lj_err_caller(L, LJ_ERR_STRDUMP);
  setstrV(L, L->top-1, lj_buf_str(L, sb));
  lj_gc_check(L);
  return 1;
}

最终是通过lj_bcwrite函数来完成字节码的生成,与lj_bcread对应。Lj_bcwriter位于lj_bcwriter.c文件中,代码如下:

cpp 复制代码
int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data,
        int strip)
{
  BCWriteCtx ctx;
  int status;
  ctx.pt = pt;
  ctx.wfunc = writer;
  ctx.wdata = data;
  ctx.strip = strip;
  ctx.status = 0;
  lj_buf_init(L, &ctx.sb);
  status = lj_vm_cpcall(L, NULL, &ctx, cpwriter);
  if (status == 0) status = ctx.status;
  lj_buf_free(G(sbufL(&ctx.sb)), &ctx.sb);
  return status;
}

实质是调用 cpwriter函数,实现如下:

cpp 复制代码
/* Protected callback for bytecode writer. */
static TValue *cpwriter(lua_State *L, lua_CFunction dummy, void *ud)
{
  BCWriteCtx *ctx = (BCWriteCtx *)ud;
  UNUSED(L); UNUSED(dummy);
  lj_buf_need(&ctx->sb, 1024);  /* Avoids resize for most prototypes. */
  bcwrite_header(ctx);
  bcwrite_proto(ctx, ctx->pt);
  bcwrite_footer(ctx);
  return NULL;
}

从这里就可以明显的看出,它通过分别生成头部、原型等数据来完成字节码的生成。

Bcsave函数中通过detecttype确定要保存的文件类型,代码如下:

Lua 复制代码
local function detecttype(str)
  local ext = string.match(string.lower(str), "%.(%a+)$")
  return map_type[ext] or "raw"
end
local map_type = {
  raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
}

这里通过正则表达式匹配了输出文件后缀名,map_type中定义了几种后缀,除非特别指定了如obj等后缀名,否则都是返回raw.即执行bcsave_raw函数,实现如下:

Lua 复制代码
local function bcsave_raw(output, s)
  local fp = savefile(output, "wb")
  bcsave_tail(fp, output, s)
end

这里最终将整个字节码文件写入到指定的文件名中。

**总结:**luajit -b生成字节码,实际上是调用 bcsave.lua脚本中的bcsave函数,该函数通过readfile函数(最终调用loadfile库函数)来完成lua脚本和字节码统一返回原型数据,并通过string.dump库函数生成完整的字节码文件。

相关推荐
爱瑞瑞5 天前
Junit5测试类编写笔记
junit
黄暄5 天前
分布式锁优化:使用Lua脚本保证释放锁的原子性问题
java·redis·分布式·后端·junit·lua
cxh_陈7 天前
org.junit.runners.model.InvalidTestClassError:此类问题的解决
java·开发语言·junit
.生产的驴10 天前
SpringBoot 执行Lua脚本 服务端执行 减少性能损耗 优化性能 优化连接性能
java·数据库·spring boot·后端·junit·maven·lua
小小数媒成员10 天前
Unity—lua基础语法
unity·junit·lua
奋斗的老史11 天前
利用Lua脚本限制用户的访问频率
开发语言·junit·lua
渡梦酒12 天前
Redis批量删除Key的三种方式
数据库·redis·junit
兰德里的折磨55012 天前
为什么要使用stream流
java·jvm·spring boot·spring·junit·log4j·intellij-idea
i1yo_kiki13 天前
junit单元测试
junit·log4j
啥都想学的又啥都不会的研究生13 天前
log日志最佳实践
java·spring boot·后端·spring·junit·log4j·logback