LuaJit分析(七)LuaJit -b 命令分析
Luajit -b 命令用于生成字节码文件,通过之前对 -bl命令的分析:
luajit -bl 命令分析
可知,-b系统的命令都通过执行 bcsave.lua脚本来完成, luajit -b命令最终是执行bcsave.lua脚本中的 bcsave函数,bcsave函数代码如下:
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
这通过关键的两个函数,来完成代码的转换:
local f = readfile(input)
local s = string.dump(f, ctx.strip)
由之前对luajit -b 命令的分析可知,readfile函数最终返回原型数据,如果是字节码文件,返回读取后的原型数据,否则进行源码分析再返回原型数据。接着通过string.dump返回完整的字节码文件,通过ctx.strip决定是否有调试信息,string.dump为库函数,位于lib_string.c中,实现如下:
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文件中,代码如下:
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函数,实现如下:
/* 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确定要保存的文件类型,代码如下:
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函数,实现如下:
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库函数生成完整的字节码文件。