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.typeif not t thent = detecttype(output)ctx.type = tendif t == "raw" thenbcsave_raw(output, s)elseif not ctx.modname then ctx.modname = detectmodname(input) endif t == "obj" thenbcsave_obj(ctx, output, s)elsebcsave_c(ctx, output, s)endend
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库函数生成完整的字节码文件。