Files
LuaCangjia_api/lib/lua_runner.cpp
JianFeeeee 237aab3f36 feat: typed interop 原始类型直通映射 (Int64/Float64/Bool)
- C++ 层: callfunction_int/num/bool/void + run_int/num/bool/void
- 新增 capture_result()/finish_call() 正确计算 lua_pcall 实际返回值数量,
  修复无返回值场景下误读栈残留的问题
- C FFI 层: 11 个新导出函数 + lua_runner 结构体 tresult_* 镜像字段
- 仓颉层: callFunctionInt/Num/Bool, runScriptInt/Num/Bool, resultType()
  带严格类型校验(不匹配抛 LUAERR_RESULT_TYPE)
- 大整数跨 2^53 保真(字符串路径做不到)
- 管道模式语义保留给字符串 API; typed run_* 为独立调用保持栈清洁
- GTest 新增 10 个 typed 测试用例(共 44 个全部通过)
- 独立程序验证仓颉层运行时正确性(int/num/bool直通/大整数保真/错误路径)
2026-08-25 09:29:24 +08:00

722 lines
18 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "lua_runner.hpp"
#include <cstring>
#include <functional>
extern "C"
{
#include <errno.h>
#include "errors.h"
}
Lua_runner::Lua_runner(const char *path)
{
this->L =NULL;
this->L = luaL_newstate();
errno = 0;//清除老错误信息
if(this->L == NULL){
errno = LUAERR_LUA_STATE;
return ;
}
luaL_openlibs(this->L);//创建lua状态机打开标准库
const char* save_stdlib_code =
"local std = {} "
"for k, v in pairs(_G) do std[k] = true end "
"_G.__STD_LIBS = std"; // 将列表存到全局变量 __STD_LIBS 中
luaL_dostring(this->L, save_stdlib_code);
/*if(luaL_dostring(this->L,
"package.path = \";./luapkg/?/init.lua\"") != LUA_OK)//TODO 修改加载库的默认路径
errno = LUAERR_INIT_FAIL;//加载默认路径*/
if(path !=NULL)
{
char buf[1024];
snprintf(buf, sizeof(buf),
"package.path = package.path .. \";%s\"",
path);
if(luaL_dostring(this->L,buf) != LUA_OK)//加载用户路径
errno = LUAERR_INIT_FAIL;
}
this->pkg_cont = 0;
this->func_cont = 0;
// typed interop 初始状态
this->last_result_type = CJT_NIL;
this->last_result_int = 0;
this->last_result_num = 0.0;
this->last_result_bool = 0;
}
Lua_runner::~Lua_runner()
{
lua_close(this->L);
}
bool Lua_runner::check_file_exists(const char *path)
{
FILE* file = fopen(path, "r");
if (file) {
fclose(file);
return true;
}
return false;
}
bool Lua_runner::check_luastatue()
{
/*检查lua状态机状况*/
if(this->L == NULL)
{
errno = LUAERR_LUA_STATE;
return false;
}
//分为两部分写,防止空指针访问
if(lua_status(this->L) != LUA_OK)
{
errno = LUAERR_LUA_STATE;
return false;
}
return true;
}
lua_State *Lua_runner::get_lua_State()
{
return this->L;
}
int Lua_runner::load_lib(const char *path,const char *name)
{
//如果引入哈希表会导致删除包时的多次哈希查询,污染缓存。同时性能仅与数组实现相当
if(!this->check_luastatue())
{
return -1;
}
if(!this->check_file_exists(path))
{
errno = LUAERR_LOAD_FILE;
return -1;
}
if(this->pkg_cont>=MAX_LUA_LIB)
{
errno = LUAERR_LIB_OVERFLOW;
return -1;
}
if(luaL_loadfile(this->L,path)!=LUA_OK)
{
const char *ret = lua_tostring(this->L, -1);
this->reslt = ret;//存储返回值
errno =LUAERR_LOAD_FILE;
lua_pop(this->L,1);
return -1;
}
this->pkgs[this->pkg_cont].ref = lua_gettop(this->L)+1;
snprintf(pkgs[pkg_cont].name,128,"%s",name);
this->pkg_cont++;
return 0;
}
int Lua_runner::unload_lib(const char *name)
{
int sig = -1;
for(int i=0;i<this->pkg_cont;i++)
{
if(strcmp(this->pkgs[i].name,name) == 0)
{
sig = i;
break;//查找指定的库,查到则将标志位置为库的位置
}
}
if(sig == -1)
{
errno = LUAERR_UNLOADLIB_FAIL;
return -1;//没有查到返回异常(尝试卸载未加载的库)
}
/*检查lua状态*/
if(!this->check_luastatue())
{
return -1;
}
lua_remove(this->L,this->pkgs[sig].ref);
for(int j = sig+1;j<this->pkg_cont;j++)
{
this->pkgs[j-1] = this->pkgs[j];//保持数据结构
this->pkgs[j-1].ref--;//虚拟栈位置同步变化
}
this->pkg_cont--;
return 1;
}
int Lua_runner::clean()//进行新一轮调用前一定要先clean清除上个脚本执行的痕迹
{
if(!this->check_luastatue())
{
return -1;
}
const char* clean_globals_code =
"if _G.__STD_LIBS then "
" local std = _G.__STD_LIBS "
" for k, v in pairs(_G) do "
" if not std[k] then _G[k] = nil end "
" end "
"end";
luaL_dostring(this->L, clean_globals_code);//清空全局变量
luaL_dostring(this->L, "package.loaded = {}");//清空当前引用的包
for(int i = 0 ;i<this->pkg_cont;i++)
{
lua_remove(this->L,this->pkgs[i].ref);
}
this->pkg_cont = 0;
// 清理预加载函数
for(int i = 0; i < this->func_cont; i++)
{
luaL_unref(this->L, LUA_REGISTRYINDEX, this->funcs[i].ref);
}
this->func_cont = 0;
this->reslt ="nil";
lua_settop(this->L, 0);//清空栈
return 0;
}
//TODO 支持更多类型的参数
int Lua_runner::loadfunction(const char *path,const char *name)
{
if(!this->check_luastatue())
return -1;
if(!this->check_file_exists(path))
{
errno = LUAERR_LOAD_FILE;
return -1;
}
if(this->func_cont >= MAX_LUA_FUNC)
{
errno = LUAERR_FUNC_OVERFLOW;
return -1;
}
// 检查是否已存在同名函数
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
}
// 1. 加载文件到栈顶(编译成 chunk
if(luaL_loadfile(this->L, path) != LUA_OK)
{
const char *ret = lua_tostring(this->L, -1);
this->reslt = ret;
errno = LUAERR_LOAD_FILE;
lua_pop(this->L, 1);
return -1;
}
// 2. 执行 chunk顶层代码期望返回一个函数
if(lua_pcall(this->L, 0, 1, 0) != LUA_OK)
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
return -1;
}
// 3. 检查栈顶是否为函数
if(!lua_isfunction(this->L, -1))
{
errno = LUAERR_FUNCTION_INVALID;
lua_pop(this->L, 1);
return -1;
}
// 4. 存入 Lua Registry绝对引用不受栈变化影响
int ref = luaL_ref(this->L, LUA_REGISTRYINDEX);
// 5. 记录到 funcs 表
snprintf(this->funcs[this->func_cont].name, 128, "%s", name);
this->funcs[this->func_cont].ref = ref;
this->func_cont++;
return 0;
}
int Lua_runner::unloadfunction(const char *name)
{
int sig = -1;
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
sig = i;
break;
}
}
if(sig == -1)
{
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
if(!this->check_luastatue())
return -1;
// 释放 Registry 引用
luaL_unref(this->L, LUA_REGISTRYINDEX, this->funcs[sig].ref);
// 从 funcs 表中移除
for(int j = sig + 1; j < this->func_cont; j++)
{
this->funcs[j - 1] = this->funcs[j];
}
this->func_cont--;
return 0;
}
// ============ typed interop 内部辅助 ============
// 读取 Lua 栈顶值,记录类型到 last_result_*,并把字符串形式同步到 reslt。
// 注意:不弹出栈顶(由调用方决定是否 pop
// 计算 lua_pcall 的实际返回值数量并记录结果。
// pre_top: pcall 前的栈顶含函数与参数nargs: 传入参数数。
// pcall 后栈顶 = pre_top - nargs - 1(函数) + nres故 nres = post_top - pre_top + nargs + 1
int Lua_runner::finish_call(int pre_top, int nargs, bool pop_results)
{
int nres = lua_gettop(this->L) - pre_top + nargs + 1;
if(nres <= 0)
{
// 无返回值:显式置 NIL不读栈避免读到上一次调用残留
this->last_result_type = CJT_NIL;
this->reslt = "nil";
this->last_result_int = 0;
this->last_result_num = 0.0;
this->last_result_bool = 0;
return nres;
}
capture_result();
if(pop_results)
{
// 弹出全部返回值capture_result 不弹栈)
for(int i = 0; i < nres; i++)
lua_pop(this->L, 1);
}
return nres;
}
void Lua_runner::capture_result()
{
int top = lua_gettop(this->L);
if(top == 0)
{
// 栈为空:没有返回值
this->last_result_type = CJT_NIL;
this->reslt = "nil";
this->last_result_int = 0;
this->last_result_num = 0.0;
this->last_result_bool = 0;
return;
}
int t = lua_type(this->L, -1);
switch (t)
{
case LUA_TBOOLEAN:
this->last_result_type = CJT_BOOL;
this->last_result_bool = lua_toboolean(this->L, -1) ? 1 : 0;
this->last_result_int = this->last_result_bool;
this->last_result_num = this->last_result_bool;
this->reslt = this->last_result_bool ? "true" : "false";
break;
case LUA_TNUMBER:
{
// lua_isinteger 区分整型和浮点型
if (lua_isinteger(this->L, -1))
{
this->last_result_type = CJT_INT;
this->last_result_int = (long long)lua_tointeger(this->L, -1);
this->last_result_num = (double)this->last_result_int;
this->last_result_bool = this->last_result_int != 0;
}
else
{
this->last_result_type = CJT_NUM;
this->last_result_num = lua_tonumber(this->L, -1);
this->last_result_int = (long long)this->last_result_num;
this->last_result_bool = this->last_result_num != 0.0;
}
this->reslt = lua_tostring(this->L, -1);
break;
}
case LUA_TSTRING:
this->last_result_type = CJT_STR;
this->reslt = lua_tostring(this->L, -1);
this->last_result_int = 0;
this->last_result_num = 0.0;
this->last_result_bool = false;
break;
case LUA_TNIL:
default:
// nil 及 table/function 等复杂类型统一按 CJT_NIL 处理
this->last_result_type = CJT_NIL;
this->reslt = "nil";
this->last_result_int = 0;
this->last_result_num = 0.0;
this->last_result_bool = 0;
break;
}
}
int Lua_runner::callfunction(const char *name,const char *arg)
{
if(!this->check_luastatue())
return -1;
// 在 funcs 表中查找(预加载函数表,独立于管道栈 pkgs[]
int sig = -1;
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
sig = i;
break;
}
}
if(sig == -1)
{
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
// 从 Lua Registry 中取出函数(绝对引用,不受栈变化影响)
lua_rawgeti(this->L, LUA_REGISTRYINDEX, this->funcs[sig].ref);
// 如果有参数,压入参数
if(arg != NULL)
{
lua_pushlstring(this->L, arg, strlen(arg));
}
int argcount = (arg != NULL) ? 1 : 0;
int pre_top = lua_gettop(this->L);
if(lua_pcall(this->L, argcount, LUA_MULTRET, 0) == LUA_OK)
{
finish_call(pre_top, argcount, true);//记录结果并弹出(预加载函数不参与管道)
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
return -1;
}
}
// typed 变体共用骨架查函数→压参数→pcall→finish_call
static int callfunc_impl(Lua_runner *r, lua_State *L, const ld_func *funcs,
int func_cont, const char *name,
std::function<void(lua_State *)> push_args, int nargs)
{
int sig = -1;
for(int i = 0; i < func_cont; i++)
{
if(strcmp(funcs[i].name, name) == 0)
{
sig = i;
break;
}
}
if(sig == -1)
{
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, funcs[sig].ref);
if(nargs > 0)
push_args(L);
int pre_top = lua_gettop(L);
if(lua_pcall(L, nargs, LUA_MULTRET, 0) != LUA_OK)
{
errno = LUAERR_SCRIPT_ERROR;
r->reslt = lua_tostring(L, -1);
lua_pop(L, 1);
return -1;
}
r->finish_call(pre_top, nargs, true);//预加载函数不参与管道,弹出结果
return 0;
}
int Lua_runner::callfunction_int(const char *name, long long val)
{
if(!this->check_luastatue())
return -1;
return callfunc_impl(this, this->L, this->funcs, this->func_cont, name,
[val](lua_State *LL) { lua_pushinteger(LL, (lua_Integer)val); }, 1);
}
int Lua_runner::callfunction_num(const char *name, double val)
{
if(!this->check_luastatue())
return -1;
return callfunc_impl(this, this->L, this->funcs, this->func_cont, name,
[val](lua_State *LL) { lua_pushnumber(LL, (lua_Number)val); }, 1);
}
int Lua_runner::callfunction_bool(const char *name, int val)
{
if(!this->check_luastatue())
return -1;
return callfunc_impl(this, this->L, this->funcs, this->func_cont, name,
[val](lua_State *LL) { lua_pushboolean(LL, val ? 1 : 0); }, 1);
}
int Lua_runner::callfunction_void(const char *name)
{
if(!this->check_luastatue())
return -1;
return callfunc_impl(this, this->L, this->funcs, this->func_cont, name,
[](lua_State *) {}, 0);
}
int Lua_runner::run(const char *path,const char *arg)
{
if(!this->check_luastatue())//检查lua状态机状态
return -1;
if(path != NULL)//加载脚本到栈
{
if(this->load_lib(path,path) != 0)
return -1;
}
if(arg != NULL)//压参数入栈
{
lua_pushlstring(this->L,arg,strlen(arg));
}
if(this->pkg_cont <= 0)//检查是否有可调用chunk
{
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
int top = lua_gettop(this->L);
int argcount = top - func_index;//获取入参数量
if(argcount<0)
{
errno = LUAERR_LUA_STACK;
return -1;
}//检查参数数量
int pre_top = top;
if(lua_pcall(this->L,argcount,LUA_MULTRET,0)== LUA_OK)
{
//管道模式:结果留在栈上供下一个节点使用,仅记录类型
finish_call(pre_top, argcount, false);
this->pkg_cont--;
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L,-1);
this->pkg_cont--;
lua_pop(L,1);//错误出栈
return -1;
}
}
int Lua_runner::run_int(const char *path, long long val)
{
if(!this->check_luastatue())
return -1;
if(path != NULL)
{
if(this->load_lib(path, path) != 0)
return -1;
}
lua_pushinteger(this->L, (lua_Integer)val);
if(this->pkg_cont <= 0)
{
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
int top = lua_gettop(this->L);
int argcount = top - func_index;
if(argcount < 0)
{
errno = LUAERR_LUA_STACK;
return -1;
}
int pre_top = top;
if(lua_pcall(this->L, argcount, LUA_MULTRET, 0) == LUA_OK)
{
finish_call(pre_top, argcount, true);//独立调用,弹出结果
this->pkg_cont--;
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
this->pkg_cont--;
lua_pop(this->L, 1);
return -1;
}
}
int Lua_runner::run_num(const char *path, double val)
{
if(!this->check_luastatue())
return -1;
if(path != NULL)
{
if(this->load_lib(path, path) != 0)
return -1;
}
lua_pushnumber(this->L, (lua_Number)val);
if(this->pkg_cont <= 0)
{
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
int top = lua_gettop(this->L);
int argcount = top - func_index;
if(argcount < 0)
{
errno = LUAERR_LUA_STACK;
return -1;
}
int pre_top = top;
if(lua_pcall(this->L, argcount, LUA_MULTRET, 0) == LUA_OK)
{
finish_call(pre_top, argcount, true);
this->pkg_cont--;
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
this->pkg_cont--;
lua_pop(this->L, 1);
return -1;
}
}
int Lua_runner::run_bool(const char *path, int val)
{
if(!this->check_luastatue())
return -1;
if(path != NULL)
{
if(this->load_lib(path, path) != 0)
return -1;
}
lua_pushboolean(this->L, val ? 1 : 0);
if(this->pkg_cont <= 0)
{
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
int top = lua_gettop(this->L);
int argcount = top - func_index;
if(argcount < 0)
{
errno = LUAERR_LUA_STACK;
return -1;
}
int pre_top = top;
if(lua_pcall(this->L, argcount, LUA_MULTRET, 0) == LUA_OK)
{
finish_call(pre_top, argcount, true);
this->pkg_cont--;
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
this->pkg_cont--;
lua_pop(this->L, 1);
return -1;
}
}
int Lua_runner::run_void(const char *path)
{
if(!this->check_luastatue())
return -1;
if(path != NULL)
{
if(this->load_lib(path, path) != 0)
return -1;
}
if(this->pkg_cont <= 0)
{
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
int top = lua_gettop(this->L);
int argcount = top - func_index;
if(argcount < 0)
{
errno = LUAERR_LUA_STACK;
return -1;
}
if(lua_pcall(this->L, argcount, LUA_MULTRET, 0) == LUA_OK)
{
finish_call(top, argcount, true);
this->pkg_cont--;
return 0;
}
else
{
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
this->pkg_cont--;
lua_pop(this->L, 1);
return -1;
}
}
int Lua_runner::dostring(const char *target)
{
if(target == NULL)
{
errno = LUAERR_CALLBACK;
return -1;
}
if(luaL_dostring(this->L,target) != LUA_OK)
{
errno = LUAERR_CALLBACK;
this->reslt = lua_tostring(this->L,-1);
lua_pop(L,1);//错误出栈
return -1;
}
if(lua_gettop(this->L) == 0) {
errno = LUAERR_SCRIPT_BAD_RET;
this->reslt = "";
return -1;
}
if(lua_isstring(this->L,-1)){
this->reslt = lua_tostring(this->L,-1);
lua_pop(L,1);
return 0;
}
else{
errno = LUAERR_SCRIPT_BAD_RET;
lua_pop(L,1);
return -1;
}
}
//TODO 实现函数调用 achieve func call via file
/*
int Lua_runner::callfunction()
{
return 0;
}
*/