Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

加载与字节码

除了 luaL_loadstringluaL_loadfile,Lua C API 提供了更底层的加载接口,支持从任意来源读取代码、加载预编译字节码、以及将 Lua 函数序列化为字节码。

底层加载:lua_load

int lua_load(lua_State *L, lua_Reader reader, void *data,
             const char *chunkname, const char *mode);
参数说明
reader回调函数,每次提供一块源代码/字节码
data用户数据,传给 reader
chunkname用于错误信息和调试信息的名字
mode"t"(文本)、"b"(字节码)、"bt"(两者)

返回值:LUA_OKLUA_ERRSYNTAXLUA_ERRMEMLUA_ERRGCMM

LUA_ERRGCMM 表示在解析过程中 GC 的 __gc 元方法出错。luaL_loadfilex 在文件打开/读取失败时还可能返回 LUA_ERRFILE

Reader 回调

typedef const char * (*lua_Reader)(lua_State *L, void *ud, size_t *sz);
  • 返回指向数据块的指针,并通过 *sz 设置大小
  • 没有更多数据时返回 NULL

辅助库加载函数

int luaL_loadfilex(lua_State *L, const char *filename, const char *mode);
int luaL_loadbufferx(lua_State *L, const char *buff, size_t sz,
                     const char *name, const char *mode);
  • luaL_loadfilexluaL_loadfile 的可指定模式版本
  • luaL_loadbufferx 从内存缓冲区加载,可指定模式

出于安全考虑,默认模式通常是 "bt"。如果你只信任文本源码,可以强制 "t"

序列化:lua_dump

int lua_dump(lua_State *L, lua_Writer writer, void *data, int strip);

将栈顶的 Lua 函数序列化为字节码。strip 为真时移除调试信息(减小体积但错误信息会变差)。

Writer 回调

typedef int (*lua_Writer)(lua_State *L, const void *p, size_t sz, void *ud);

返回 0 表示成功,非 0 表示出错(会触发 lua_dump 返回错误码)。

典型应用场景

  1. 自定义加载器:从加密文件、网络流、内存映射文件加载 Lua 代码
  2. 字节码缓存:将常用脚本预编译为字节码,运行时直接加载二进制 buffer
  3. 沙箱:限制只允许加载文本源码("t"),防止加载不可信字节码

字节码兼容性

Lua 字节码不保证跨版本兼容。Lua 5.4 的字节码不能直接在 5.3 运行,反之亦然。即使是 5.4 的小版本之间,也建议重新编译。

完整代码


#include <assert.h>
#include <lauxlib.h>
#include <lua.h>
#include <lualib.h>
#include <stdio.h>
#include <string.h>

static char bytecode[4096];
static size_t bytecode_len = 0;

static int writer(lua_State *L, const void *p, size_t sz, void *ud) {
    (void)L;
    (void)ud;
    if (bytecode_len + sz > sizeof(bytecode)) {
        return 1;
    }
    memcpy(bytecode + bytecode_len, p, sz);
    bytecode_len += sz;
    return 0;
}

static const char *reader(lua_State *L, void *ud, size_t *sz) {
    (void)L;
    (void)ud;
    if (bytecode_len == 0) {
        *sz = 0;
        return NULL;
    }
    *sz = bytecode_len;
    bytecode_len = 0;
    return bytecode;
}

int main(void) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    const char *code = "return function(x) return x * 2 end";
    luaL_loadbufferx(L, code, strlen(code), "inline", "t");
    lua_pcall(L, 0, 1, 0);
    lua_pushinteger(L, 21);
    lua_pcall(L, 1, 1, 0);
    assert(lua_tointeger(L, -1) == 42);
    lua_pop(L, 1);

    luaL_loadstring(L, "return function(a,b) return a+b end");
    lua_pcall(L, 0, 1, 0);

    bytecode_len = 0;
    lua_dump(L, writer, NULL, 0);
    assert(bytecode_len > 0);
    lua_pop(L, 1);

    lua_load(L, reader, NULL, "from_mem", "b");
    lua_pushinteger(L, 10);
    lua_pushinteger(L, 32);
    lua_pcall(L, 2, 1, 0);
    assert(lua_tointeger(L, -1) == 42);
    lua_pop(L, 1);

    lua_close(L);
    puts("14-load: ok");
    return 0;
}