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直通/大整数保真/错误路径)
This commit is contained in:
JianFeeeee
2026-08-25 09:29:24 +08:00
parent 42e8b7676d
commit 237aab3f36
13 changed files with 1091 additions and 112 deletions

View File

@ -12,13 +12,13 @@ bool check_luastatue(lua_State *L)
/*检查lua状态机状况*/
if(L == NULL)
{
errno = NAPI_LUA_STACK_ERROR;
errno = LUAERR_LUA_STACK;
return false;
}
//分为两部分写,防止空指针访问
if(lua_status(L) != LUA_OK)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return false;
}
return true;
@ -26,6 +26,15 @@ bool check_luastatue(lua_State *L)
int get_errno(void) { return errno; }//获取错误码
// 同步 typed 结果:把 Lua_runner 的 last_result_* 拷贝到外层 lua_runner 结构体
static void sync_typed_result(lua_runner *self, Lua_runner *runner)
{
self->tresult_type = runner->last_result_type;
self->tresult_int = runner->last_result_int;
self->tresult_num = runner->last_result_num;
self->tresult_bool = runner->last_result_bool;
}
/// @brief 加载lua文件到lua虚拟栈
/// @param self 使用init_lua_runner初始化的lua_runner结构体指针
/// @param path 需要加载的脚本的位置
@ -37,7 +46,7 @@ int load_lib(void *selfd,const char *path,const char *name)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->load_lib(path,name)==-1)
@ -54,7 +63,7 @@ int unload_lib(void *selfd,const char *name)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->unload_lib(name)==-1)
@ -64,7 +73,7 @@ int unload_lib(void *selfd,const char *name)
/// @brief 运行脚本(可指定入参)
/// @param self 使用init_lua_runner初始化的lua_runner结构体指针
/// @param path 脚本的沙盒内路径
/// @param path 脚本路径
/// @param arg 脚本的入参
/// @return 0代表正常结束-1表示异常退出
int run(void *selfd,const char *path,const char *arg)
@ -74,7 +83,7 @@ int run(void *selfd,const char *path,const char *arg)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->run(path,arg)==-1)
@ -97,7 +106,7 @@ int loadfunction(void *selfd,const char *path,const char *name)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->loadfunction(path,name)==-1)
@ -115,7 +124,7 @@ int unloadfunction(void *selfd,const char *name)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->unloadfunction(name)==-1)
@ -134,7 +143,7 @@ int callfunction(void *selfd,const char *name,const char *arg)
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->callfunction(name,arg)==-1){
@ -142,9 +151,162 @@ int callfunction(void *selfd,const char *name,const char *arg)
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
/*-------------typed interopv0.2.2------------------------*/
int callfunction_int(void *selfd,const char *name,long long val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->callfunction_int(name,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int callfunction_num(void *selfd,const char *name,double val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->callfunction_num(name,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int callfunction_bool(void *selfd,const char *name,int val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->callfunction_bool(name,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int callfunction_void(void *selfd,const char *name)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->callfunction_void(name)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int run_int(void *selfd,const char *path,long long val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->run_int(path,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int run_num(void *selfd,const char *path,double val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->run_num(path,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int run_bool(void *selfd,const char *path,int val)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->run_bool(path,val)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int run_void(void *selfd,const char *path)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->run_void(path)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
sync_typed_result(self, runner);
return 0;
}
int result_type(void *selfd){lua_runner*self =(lua_runner*)selfd;return self->tresult_type;}
long long result_int(void *selfd){lua_runner*self =(lua_runner*)selfd;return self->tresult_int;}
double result_num(void *selfd){lua_runner*self =(lua_runner*)selfd;return self->tresult_num;}
int result_bool(void *selfd){lua_runner*self =(lua_runner*)selfd;return self->tresult_bool;}
/*-------------typed interop------------------------*/
/// @brief 执行单句lua语句
/// @param selfd init_lua_runner返回的void指针
/// @param target 执行语句(字符串类型)
@ -155,7 +317,7 @@ int dostring(void *selfd,const char *target){
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->dostring(target)==-1){
@ -175,7 +337,7 @@ int cleanup(void *selfd)
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
if(runner->clean()==-1)
@ -221,7 +383,7 @@ static int l_tty_read(lua_State *L)
fclose(fp);
if(remove(fname) != 0)
{
errno = NAPI_RESET_INPUT_FILE_ERROR;
errno = LUAERR_RESET_INPUT_FILE;
return luaL_error(L, "fail to reset input file: %s", fname);
}
lua_pushlstring(L, tmp, sz);
@ -310,7 +472,7 @@ static void push_tty_file(lua_State *L, int isout, const luaL_Reg *fake_tty_meta
int redirecct_path(lua_State *L)
{
if(!check_luastatue(L)){
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return -1;
}
iopath *p = (iopath*)lua_getextraspace(L);
@ -362,13 +524,18 @@ void *init_lua_runner(const char *pathio,const char *pkgpath,int(*input)(),int(*
{
//分配内存
lua_runner *self = (lua_runner*)malloc(sizeof(lua_runner));
// typed interop 字段初始化
self->tresult_type = CJT_NIL;
self->tresult_int = 0;
self->tresult_num = 0.0;
self->tresult_bool = 0;
//创建对象
self->lua_obj = (void*)new Lua_runner(pkgpath);
if(errno != 0)
return NULL;
if(self->lua_obj == NULL)
{
errno = NAPI_LUA_CLASS_LOST;
errno = LUAERR_CLASS_LOST;
return NULL;
}
//提取对象
@ -377,7 +544,7 @@ void *init_lua_runner(const char *pathio,const char *pkgpath,int(*input)(),int(*
lua_State *L = runner->get_lua_State();
if(!check_luastatue(L)){
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return NULL;
}
iopath *io_path = (iopath*)lua_getextraspace(L);
@ -388,7 +555,7 @@ void *init_lua_runner(const char *pathio,const char *pkgpath,int(*input)(),int(*
}
if(io_path == NULL){
errno = NAPI_ERROR_FUNCS;
errno = LUAERR_CALLBACK;
return NULL;
}
@ -399,7 +566,7 @@ void *init_lua_runner(const char *pathio,const char *pkgpath,int(*input)(),int(*
strcpy(io_path->iopath,pathio);
//注册重定向
if(redirecct_path(L)==-1){
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return NULL;
}
WITHOUT_REDIRECT:

View File

@ -27,6 +27,12 @@ typedef struct lua_runner
void *lua_obj;//cpp对象指向在cpp中封装的luarunner对象
// typed interop最近一次调用的结果类型与值镜像 Lua_runner 同名字段)
int tresult_type; // 0=nil, 1=bool, 2=int, 3=num, 4=str
long long tresult_int;
double tresult_num;
int tresult_bool;
}lua_runner;
/*内部使用结构体,仓颉无需关心*/
@ -75,6 +81,56 @@ int unloadfunction(void *selfd,const char *name);
/// @return 成功返回0,失败返回-1,并将lua出错原因拷贝至result中
int callfunction(void *selfd,const char *name,const char *arg);
/*-------------typed interopv0.2.2------------------------*/
// 原始类型直通映射:仓颉 Int64/Float64/Bool 与 Lua integer/number/boolean
// 直接互转,无需序列化为字符串。调用后用 result_type/result_int/result_num/
// result_bool/getresult 读取结果。
/// @brief 按名称调用预加载函数,传递 Int64 参数Lua integer
/// @return 成功返回0,失败返回-1
int callfunction_int(void *selfd,const char *name,long long val);
/// @brief 按名称调用预加载函数,传递 Float64 参数Lua number
int callfunction_num(void *selfd,const char *name,double val);
/// @brief 按名称调用预加载函数,传递 Bool 参数Lua boolean
int callfunction_bool(void *selfd,const char *name,int val);
/// @brief 按名称调用预加载函数,不传参数
int callfunction_void(void *selfd,const char *name);
/// @brief 运行脚本,传递 Int64 参数
int run_int(void *selfd,const char *path,long long val);
/// @brief 运行脚本,传递 Float64 参数
int run_num(void *selfd,const char *path,double val);
/// @brief 运行脚本,传递 Bool 参数
int run_bool(void *selfd,const char *path,int val);
/// @brief 运行脚本,不带参数
int run_void(void *selfd,const char *path);
#define CJT_NIL 0
#define CJT_BOOL 1
#define CJT_INT 2
#define CJT_NUM 3
#define CJT_STR 4
/// @brief 获取最近一次 typed 调用的结果类型
/// @return CJT_NIL/CJT_BOOL/CJT_INT/CJT_NUM/CJT_STR
int result_type(void *selfd);
/// @brief 获取最近一次 typed 调用的整数结果(非整型结果时为转换值)
long long result_int(void *selfd);
/// @brief 获取最近一次 typed 调用的浮点结果(非浮点结果时为转换值)
double result_num(void *selfd);
/// @brief 获取最近一次 typed 调用的布尔结果
int result_bool(void *selfd);
/*-------------typed interop------------------------*/
/// @brief 运行lua脚本
/// @param selfd init_lua_runner返回的void指针
/// @param path 脚本路径

View File

@ -1,5 +1,6 @@
#include "lua_runner.hpp"
#include <cstring>
#include <functional>
extern "C"
{
@ -13,7 +14,7 @@ Lua_runner::Lua_runner(const char *path)
this->L = luaL_newstate();
errno = 0;//清除老错误信息
if(this->L == NULL){
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return ;
}
luaL_openlibs(this->L);//创建lua状态机打开标准库
@ -24,8 +25,8 @@ Lua_runner::Lua_runner(const char *path)
luaL_dostring(this->L, save_stdlib_code);
/*if(luaL_dostring(this->L,
"package.path = \";/data/storage/el2/luapkg/?/init.lua\"") != LUA_OK)//TODO 修改加载库的默认路径指向沙盒内路径
errno = NAPI_LUA_INITFAIL;//加载默认路径*/
"package.path = \";./luapkg/?/init.lua\"") != LUA_OK)//TODO 修改加载库的默认路径
errno = LUAERR_INIT_FAIL;//加载默认路径*/
if(path !=NULL)
{
char buf[1024];
@ -33,10 +34,15 @@ Lua_runner::Lua_runner(const char *path)
"package.path = package.path .. \";%s\"",
path);
if(luaL_dostring(this->L,buf) != LUA_OK)//加载用户路径
errno = NAPI_LUA_INITFAIL;
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()
@ -59,13 +65,13 @@ bool Lua_runner::check_luastatue()
/*检查lua状态机状况*/
if(this->L == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return false;
}
//分为两部分写,防止空指针访问
if(lua_status(this->L) != LUA_OK)
{
errno = NAPI_LUA_STATE_ERROR;
errno = LUAERR_LUA_STATE;
return false;
}
return true;
@ -86,19 +92,19 @@ int Lua_runner::load_lib(const char *path,const char *name)
}
if(!this->check_file_exists(path))
{
errno = NAPI_LOAD_FILE_ERROR;
errno = LUAERR_LOAD_FILE;
return -1;
}
if(this->pkg_cont>=MAX_LUA_LIB)
{
errno = NAPI_LUALIB_LOAD_OVER_STACK;
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 =NAPI_LOAD_FILE_ERROR;
errno =LUAERR_LOAD_FILE;
lua_pop(this->L,1);
return -1;
}
@ -122,7 +128,7 @@ int Lua_runner::unload_lib(const char *name)
}
if(sig == -1)
{
errno = NAPI_UNLOADLIB_FAIL;
errno = LUAERR_UNLOADLIB_FAIL;
return -1;//没有查到返回异常(尝试卸载未加载的库)
}
/*检查lua状态*/
@ -179,12 +185,12 @@ int Lua_runner::loadfunction(const char *path,const char *name)
return -1;
if(!this->check_file_exists(path))
{
errno = NAPI_LOAD_FILE_ERROR;
errno = LUAERR_LOAD_FILE;
return -1;
}
if(this->func_cont >= MAX_LUA_FUNC)
{
errno = NAPI_LOAD_FUNCTION_OVER;
errno = LUAERR_FUNC_OVERFLOW;
return -1;
}
// 检查是否已存在同名函数
@ -192,7 +198,7 @@ int Lua_runner::loadfunction(const char *path,const char *name)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
errno = NAPI_FUNCTION_NOT_FOUND;
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
}
@ -202,7 +208,7 @@ int Lua_runner::loadfunction(const char *path,const char *name)
{
const char *ret = lua_tostring(this->L, -1);
this->reslt = ret;
errno = NAPI_LOAD_FILE_ERROR;
errno = LUAERR_LOAD_FILE;
lua_pop(this->L, 1);
return -1;
}
@ -210,7 +216,7 @@ int Lua_runner::loadfunction(const char *path,const char *name)
// 2. 执行 chunk顶层代码期望返回一个函数
if(lua_pcall(this->L, 0, 1, 0) != LUA_OK)
{
errno = NAPI_SCRIPT_ERROR;
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
return -1;
@ -219,7 +225,7 @@ int Lua_runner::loadfunction(const char *path,const char *name)
// 3. 检查栈顶是否为函数
if(!lua_isfunction(this->L, -1))
{
errno = NAPI_FUNCTION_NOT_VALID;
errno = LUAERR_FUNCTION_INVALID;
lua_pop(this->L, 1);
return -1;
}
@ -248,7 +254,7 @@ int Lua_runner::unloadfunction(const char *name)
}
if(sig == -1)
{
errno = NAPI_FUNCTION_NOT_FOUND;
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
@ -268,6 +274,98 @@ int Lua_runner::unloadfunction(const char *name)
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())
@ -285,7 +383,7 @@ int Lua_runner::callfunction(const char *name,const char *arg)
}
if(sig == -1)
{
errno = NAPI_FUNCTION_NOT_FOUND;
errno = LUAERR_FUNCTION_NOT_FOUND;
return -1;
}
@ -299,23 +397,88 @@ int Lua_runner::callfunction(const char *name,const char *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)
{
if(lua_isstring(this->L, -1))
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
finish_call(pre_top, argcount, true);//记录结果并弹出(预加载函数不参与管道)
return 0;
}
else
{
errno = NAPI_SCRIPT_ERROR;
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状态机状态
@ -332,7 +495,7 @@ int Lua_runner::run(const char *path,const char *arg)
}
if(this->pkg_cont <= 0)//检查是否有可调用chunk
{
errno = NAPI_NOCHUNK_FOUND;
errno = LUAERR_NOCHUNK_FOUND;
return -1;
}
int func_index = this->pkgs[this->pkg_cont-1].ref - 1;
@ -340,19 +503,20 @@ int Lua_runner::run(const char *path,const char *arg)
int argcount = top - func_index;//获取入参数量
if(argcount<0)
{
errno = NAPI_LUA_STACK_ERROR;
errno = LUAERR_LUA_STACK;
return -1;
}//检查参数数量
int pre_top = top;
if(lua_pcall(this->L,argcount,LUA_MULTRET,0)== LUA_OK)
{
if(lua_isstring(this->L,-1))
this->reslt = lua_tostring(this->L,-1);
//管道模式:结果留在栈上供下一个节点使用,仅记录类型
finish_call(pre_top, argcount, false);
this->pkg_cont--;
return 0;
}
else
{
errno = NAPI_SCRIPT_ERROR;
errno = LUAERR_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L,-1);
this->pkg_cont--;
lua_pop(L,1);//错误出栈
@ -360,22 +524,180 @@ int Lua_runner::run(const char *path,const char *arg)
}
}
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 = NAPI_ERROR_FUNCS;
errno = LUAERR_CALLBACK;
return -1;
}
if(luaL_dostring(this->L,target) != LUA_OK)
{
errno = NAPI_ERROR_FUNCS;
errno = LUAERR_CALLBACK;
this->reslt = lua_tostring(this->L,-1);
lua_pop(L,1);//错误出栈
return -1;
}
if(lua_gettop(this->L) == 0) {
errno = NAPI_SCRIPT_BAD_RET;
errno = LUAERR_SCRIPT_BAD_RET;
this->reslt = "";
return -1;
}
@ -385,7 +707,7 @@ int Lua_runner::dostring(const char *target)
return 0;
}
else{
errno = NAPI_SCRIPT_BAD_RET;
errno = LUAERR_SCRIPT_BAD_RET;
lua_pop(L,1);
return -1;
}

View File

@ -24,6 +24,13 @@ typedef struct loaded_function
}ld_func;
// 结果类型标记typed interopv0.2.2
#define CJT_NIL 0
#define CJT_BOOL 1
#define CJT_INT 2
#define CJT_NUM 3
#define CJT_STR 4
#ifdef __cplusplus
class Lua_runner//lua运行器对象
{
@ -34,12 +41,31 @@ class Lua_runner//lua运行器对象
int unload_lib(const char *name);//解除加载
int loadfunction(const char *path,const char *name);//预加载函数(顶层执行,结果存入独立函数表)
int unloadfunction(const char *name);//卸载预加载函数
int callfunction(const char *name,const char *arg);//按名称调用已预加载的函数
int run(const char *path = NULL,const char *arg = NULL);
int callfunction(const char *name,const char *arg);//按名称调用已预加载的函数(字符串参数)
int callfunction_int(const char *name, long long val);//按名称调用,传 Int64 参数
int callfunction_num(const char *name, double val);//按名称调用,传 Float64 参数
int callfunction_bool(const char *name, int val);//按名称调用,传 Bool 参数
int callfunction_void(const char *name);//按名称调用,不带参数
int run(const char *path = NULL,const char *arg = NULL);//运行脚本(字符串参数)
int run_int(const char *path, long long val);//运行脚本,传 Int64 参数
int run_num(const char *path, double val);//运行脚本,传 Float64 参数
int run_bool(const char *path, int val);//运行脚本,传 Bool 参数
int run_void(const char *path);//运行脚本,不带参数
int clean();//清除状态机缓存
int dostring(const char *target);
std::string reslt;
lua_State *get_lua_State();
// typed interop最近一次调用的结果类型与值callfunction_*/run_* 更新)
int last_result_type; // CJT_NIL/CJT_BOOL/CJT_INT/CJT_NUM/CJT_STR
long long last_result_int;
double last_result_num;
int last_result_bool;
// 内部辅助:读取 Lua 栈顶值并记录类型到 last_result_*
void capture_result();
// 计算 lua_pcall 实际返回值数量并记录结果pop_results=true 时弹出全部返回值
// (管道模式的 run_* 传 false 以保留栈上数据供后续节点使用)
// 返回实际返回值数量
int finish_call(int pre_top, int nargs, bool pop_results);
private:
lua_State *L;
int pkg_cont;

View File

@ -20,6 +20,31 @@ foreign func loadfunction(selfd: CPointer<Unit>,path: CString, name: CString): I
foreign func unloadfunction(selfd: CPointer<Unit>,name: CString): Int32
@C
foreign func callfunction(selfd: CPointer<Unit>,name: CString, arg: CString): Int32
// typed interopv0.2.2):原始类型直通映射
@C
foreign func callfunction_int(selfd: CPointer<Unit>, name: CString, val: Int64): Int32
@C
foreign func callfunction_num(selfd: CPointer<Unit>, name: CString, val: Float64): Int32
@C
foreign func callfunction_bool(selfd: CPointer<Unit>, name: CString, val: Int32): Int32
@C
foreign func callfunction_void(selfd: CPointer<Unit>, name: CString): Int32
@C
foreign func run_int(selfd: CPointer<Unit>, path: CString, val: Int64): Int32
@C
foreign func run_num(selfd: CPointer<Unit>, path: CString, val: Float64): Int32
@C
foreign func run_bool(selfd: CPointer<Unit>, path: CString, val: Int32): Int32
@C
foreign func run_void(selfd: CPointer<Unit>, path: CString): Int32
@C
foreign func result_type(selfd: CPointer<Unit>): Int32
@C
foreign func result_int(selfd: CPointer<Unit>): Int64
@C
foreign func result_num(selfd: CPointer<Unit>): Float64
@C
foreign func result_bool(selfd: CPointer<Unit>): Int32
@C
foreign func cleanup(selfd: CPointer<Unit>): Int32
@C
@ -78,31 +103,22 @@ public class LuaError <: Exception {
/// 根据错误码获取错误描述
public static func getErrorMessage(code: Int32): String {
match (code) {
case 5001 => "JSON file load failed"
case 5002 => "Memory allocation failed"
case 5003 => "File load error (path, permission, or compile-time syntax issue)"
case 5004 => "Script runner initialization failed"
case 5005 => "Lua state machine initialization failed or corrupted"
case 5006 => "JSON parse error (format issue)"
case 5007 => "JSON format error (does not match MCP format)"
case 5008 => "Missing argument in JSON"
case 5009 => "Lua stack space insufficient"
case 5010 => "Script internal error, check result for details"
case 5011 => "Script return value error"
case 5012 => "Library not loaded before unload"
case 5013 => "Function call violates single-input-single-output convention"
case 5014 => "Stack size below minimum during function call"
case 5015 => "Lua state machine initialization error"
case 5016 => "Too many libraries loaded"
case 5017 => "Lua runner object lost"
case 5018 => "Missing redirect file path"
case 5019 => "Reset input file failed, may cause input pollution"
case 5020 => "Error in callback functions"
case 5021 => "change workdir error"
case 5022 => "No callable chunk found"
case 5023 => "Function not found"
case 5024 => "Too many preloaded functions"
case 5025 => "Preloaded file must return a function"
case 5001 => "File load error (path, permission, or compile-time syntax issue)"
case 5002 => "Lua state machine initialization failed or corrupted"
case 5003 => "Script internal error, check result for details"
case 5004 => "Script return value type not supported"
case 5005 => "Library not loaded before unload"
case 5006 => "Stack layout mismatch during function call"
case 5007 => "Lua state machine initialization error"
case 5008 => "Too many libraries loaded"
case 5009 => "Lua runner object lost"
case 5010 => "Reset input file failed, may cause input pollution"
case 5011 => "Error in callback functions (including doString syntax error)"
case 5012 => "No callable chunk found"
case 5013 => "Function not found"
case 5014 => "Too many preloaded functions"
case 5015 => "Preloaded file must return a function"
case 5016 => "Result type not supported for requested conversion"
case _ => "Unknown error"
}
}
@ -315,6 +331,109 @@ public class LuaRunner {
return this.cachedResult
}
// ==================== typed interopv0.2.2====================
// 原始类型直通Int64/Float64/Bool 与 Lua integer/number/boolean 直接互转,
// 不经过字符串序列化。结果类型严格校验,不匹配抛 5016。
// 结果类型常量resultType 返回值0=nil 1=bool 2=int 3=num 4=str
/// 获取最近一次调用的结果类型0=nil 1=bool 2=int 3=num 4=str
public func resultType(): Int32 {
unsafe { result_type(this.handle) }
}
/// 调用预加载函数,传 Int64 参数Lua integer
///
/// @param name loadFunction 时指定的函数名称
/// @param val 整数参数
/// @return 函数返回的整数(结果为 Lua number 时自动截断转换)
/// @throws LuaError 函数不存在/执行出错时抛出对应错误码;
/// 结果不是数值类型nil/string等时抛 5016
public func callFunctionInt(name: String, val: Int64): Int64 {
let namePtr = toCStr(name)
let code = unsafe { callfunction_int(this.handle, namePtr, val) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) } // 期望 int/num
unsafe { result_int(this.handle) }
}
/// 调用预加载函数,传 Float64 参数Lua number
///
/// @throws LuaError 同 callFunctionInt结果不是数值类型时抛 5016
public func callFunctionNum(name: String, val: Float64): Float64 {
let namePtr = toCStr(name)
let code = unsafe { callfunction_num(this.handle, namePtr, val) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) } // 期望 int/num
unsafe { result_num(this.handle) }
}
/// 调用预加载函数,传 Bool 参数Lua boolean
///
/// @throws LuaError 同 callFunctionInt结果为 nil 或 string 时抛 5016
public func callFunctionBool(name: String, val: Bool): Bool {
let namePtr = toCStr(name)
let vi = if (val) { 1i32 } else { 0i32 }
let code = unsafe { callfunction_bool(this.handle, namePtr, vi) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt == 0 || rt == 4) { throw LuaError(5016) } // 拒绝 nil/string
unsafe { result_bool(this.handle) == 1 }
}
/// 运行脚本,传 Int64 参数(独立调用,非管道模式)
///
/// 与 runScript 不同typed 变体执行后弹出返回值、保持虚拟栈清洁;
/// 管道模式请继续使用字符串版 runScript。
///
/// @throws LuaError 同 callFunctionInt结果不是数值类型时抛 5016
public func runScriptInt(path: String, val: Int64): Int64 {
let pathPtr = toCStr(path)
let code = unsafe { run_int(this.handle, pathPtr, val) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) }
unsafe { result_int(this.handle) }
}
/// 运行脚本,传 Float64 参数(独立调用,非管道模式)
///
/// @throws LuaError 同 runScriptInt结果不是数值类型时抛 5016
public func runScriptNum(path: String, val: Float64): Float64 {
let pathPtr = toCStr(path)
let code = unsafe { run_num(this.handle, pathPtr, val) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) }
unsafe { result_num(this.handle) }
}
/// 运行脚本,传 Bool 参数(独立调用,非管道模式)
///
/// @throws LuaError 同 runScriptInt结果为 nil 或 string 时抛 5016
public func runScriptBool(path: String, val: Bool): Bool {
let pathPtr = toCStr(path)
let vi = if (val) { 1i32 } else { 0i32 }
let code = unsafe { run_bool(this.handle, pathPtr, vi) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt == 0 || rt == 4) { throw LuaError(5016) }
unsafe { result_bool(this.handle) == 1 }
}
/// 获取缓存的结果
public func result(): String { this.cachedResult }

View File

@ -55,7 +55,7 @@ func testRunScriptSyntaxError(): Unit {
runner.runScript(path, "")
fail("Should throw syntax error")
} catch (e: LuaError) {
@Expect(e.code, 5003)
@Expect(e.code, 5001)
}
}
@ -67,7 +67,7 @@ func testRunScriptRuntimeError(): Unit {
runner.runScript(path, "")
fail("Should throw runtime error")
} catch (e: LuaError) {
@Expect(e.code, 5010)
@Expect(e.code, 5003)
}
}
@ -89,7 +89,7 @@ func testLoadLibFileNotFound(): Unit {
runner.load("/no/such/file.lua", "badlib")
fail("Should throw file not found error")
} catch (e: LuaError) {
@Expect(e.code, 5003)
@Expect(e.code, 5001)
}
}
@ -108,7 +108,7 @@ func testUnloadLibNotFound(): Unit {
runner.unload("nosuchlib")
fail("Should throw exception")
} catch (e: LuaError) {
@Expect(e.code, 5012)
@Expect(e.code, 5005)
}
}
@ -199,7 +199,7 @@ func testLoadLibOverflow(): Unit {
runner.load(overflowPath, overflowName)
fail("Should throw overflow error")
} catch (e: LuaError) {
@Expect(e.code, 5016)
@Expect(e.code, 5008)
}
}
@ -278,7 +278,7 @@ func testDoStringNoReturn(): Unit {
runner.doString("x = 10")
fail("Should throw bad ret error")
} catch (e: LuaError) {
@Expect(e.code, 5011)
@Expect(e.code, 5004)
}
}
@ -289,7 +289,7 @@ func testDoStringSyntaxError(): Unit {
runner.doString("if true then")
fail("Should throw syntax error")
} catch (e: LuaError) {
@Expect(e.code, 5020)
@Expect(e.code, 5011)
}
}
@ -300,7 +300,7 @@ func testDoStringNilReturn(): Unit {
runner.doString("return nil")
fail("Should throw bad ret error")
} catch (e: LuaError) {
@Expect(e.code, 5011)
@Expect(e.code, 5004)
}
}
@ -310,11 +310,12 @@ func testDoStringNilReturn(): Unit {
@Test
func testErrorCodesMapping(): Unit {
@Expect(LuaError.getErrorMessage(5001), "JSON file load failed")
@Expect(LuaError.getErrorMessage(5002), "Memory allocation failed")
@Expect(LuaError.getErrorMessage(5003), "File load error (path, permission, or compile-time syntax issue)")
@Expect(LuaError.getErrorMessage(5010), "Script internal error, check result for details")
@Expect(LuaError.getErrorMessage(5022), "No callable chunk found")
@Expect(LuaError.getErrorMessage(5001), "File load error (path, permission, or compile-time syntax issue)")
@Expect(LuaError.getErrorMessage(5002), "Lua state machine initialization failed or corrupted")
@Expect(LuaError.getErrorMessage(5003), "Script internal error, check result for details")
@Expect(LuaError.getErrorMessage(5004), "Script return value type not supported")
@Expect(LuaError.getErrorMessage(5013), "Function not found")
@Expect(LuaError.getErrorMessage(5016), "Result type not supported for requested conversion")
@Expect(LuaError.getErrorMessage(9999), "Unknown error")
}
@ -372,7 +373,7 @@ func testCallFunctionNotFound(): Unit {
runner.callFunction("nonexistent", "")
fail("Should throw function not found error")
} catch (e: LuaError) {
@Expect(e.code, 5023)
@Expect(e.code, 5013)
}
}
@ -383,7 +384,7 @@ func testLoadFunctionFileNotFound(): Unit {
runner.loadFunction("/no/such/file.lua", "bad")
fail("Should throw file not found error")
} catch (e: LuaError) {
@Expect(e.code, 5003)
@Expect(e.code, 5001)
}
}
@ -394,7 +395,7 @@ func testLoadFunctionNotAFunction(): Unit {
runner.loadFunction(scriptPath("func_not_a_function.lua"), "bad")
fail("Should throw function not valid error")
} catch (e: LuaError) {
@Expect(e.code, 5025)
@Expect(e.code, 5015)
}
}
@ -406,7 +407,7 @@ func testLoadFunctionDuplicate(): Unit {
runner.loadFunction(scriptPath("func_add.lua"), "dup")
fail("Should throw on duplicate name")
} catch (e: LuaError) {
@Expect(e.code, 5023)
@Expect(e.code, 5013)
}
}
@ -418,7 +419,7 @@ func testCallFunctionRuntimeError(): Unit {
runner.callFunction("bad", "")
fail("Should throw runtime error")
} catch (e: LuaError) {
@Expect(e.code, 5010)
@Expect(e.code, 5003)
}
}
@ -434,7 +435,7 @@ func testCallFunctionWithUnloadFunction(): Unit {
runner.callFunction("add", "")
fail("Should throw function not found after unloadFunction")
} catch (e: LuaError) {
@Expect(e.code, 5023)
@Expect(e.code, 5013)
}
// 卸载后可重新加载同名函数
@ -477,7 +478,7 @@ func testLoadFunctionAndCleanCoexists(): Unit {
runner.callFunction("add", "")
fail("Should throw after clean")
} catch (e: LuaError) {
@Expect(e.code, 5023)
@Expect(e.code, 5013)
}
}
@ -493,8 +494,110 @@ func testLoadFunctionOverflow(): Unit {
try {
runner.loadFunction(path, name)
} catch (e: LuaError) {
@Expect(e.code, 5024)
@Expect(e.code, 5014)
break
}
}
}
// ====================
// 9. typed interop 测试v0.2.2
// ====================
@Test
func testTypedCallInt(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_typed_add.lua"), "inc")
let res = runner.callFunctionInt("inc", 41)
@Expect(res, 42)
// 字符串形式同步可用(向后兼容)
@Expect(runner.result(), "42")
}
@Test
func testTypedCallNum(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_typed_mul.lua"), "mul")
let res = runner.callFunctionNum("mul", 4.0)
@Expect(res, 10.0)
}
@Test
func testTypedCallBool(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_typed_not.lua"), "lnot")
@Expect(runner.callFunctionBool("lnot", true), false)
@Expect(runner.callFunctionBool("lnot", false), true)
}
@Test
func testTypedRoundTrip(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_typed_echo.lua"), "echo")
// int 往返保真
@Expect(runner.callFunctionInt("echo", -123456789), -123456789)
@Expect(runner.callFunctionInt("echo", 9007199254740993), 9007199254740993)
// num 往返保真
@Expect(runner.callFunctionNum("echo", 3.14159), 3.14159)
// bool 往返保真
@Expect(runner.callFunctionBool("echo", true), true)
// string 走原 API类型标记同步为 STR(4)
let s = runner.callFunction("echo", "hello typed")
@Expect(s, "hello typed")
@Expect(runner.resultType(), 4)
}
@Test
func testTypedTypeMismatch(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_typed_void.lua"), "forty_two")
// 函数返回 int 42用 Num 方式可读int→num 兼容)
@Expect(runner.callFunctionNum("forty_two", 0.0), 42.0)
// 返回 string 的函数不能用 Int 读 → 5016
runner.loadFunction(scriptPath("func_add.lua"), "add")
try {
runner.callFunctionInt("add", 1)
fail("Should throw result type error")
} catch (e: LuaError) {
@Expect(e.code, 5016)
}
// 返回 nil 的函数不能用 Int 读 → 5016
runner.loadFunction(scriptPath("func_typed_nil.lua"), "nilret")
try {
runner.callFunctionInt("nilret", 1)
fail("Should throw on nil result")
} catch (e: LuaError) {
@Expect(e.code, 5016)
}
}
@Test
func testTypedRunVariants(): Unit {
ensureGeneratedScriptsDir()
// 注意typed run 变体是独立调用,脚本需直接返回值(非返回函数的预加载式文件)
let echoPath = generatedScriptPath("typed_echo_arg.lua")
File.writeTo(echoPath, "return (...)".toArray())
let runner = LuaRunner()
@Expect(runner.runScriptInt(echoPath, 777), 777)
@Expect(runner.runScriptNum(echoPath, 2.5), 2.5)
@Expect(runner.runScriptBool(echoPath, true), true)
}
@Test
func testTypedNotFound(): Unit {
let runner = LuaRunner()
try {
runner.callFunctionInt("nonexistent", 1)
fail("Should throw function not found")
} catch (e: LuaError) {
@Expect(e.code, 5013)
}
}

View File

@ -0,0 +1,5 @@
-- 支持 typed 参数的加法函数(整数版)
-- 调用方传入一个整数,返回它加 1
return function(n)
return n + 1
end

View File

@ -0,0 +1,4 @@
-- 回声函数:返回传入的参数本身(保留类型)
return function(x)
return x
end

View File

@ -0,0 +1,4 @@
-- 支持 typed 参数的乘法函数(浮点数版)
return function(n)
return n * 2.5
end

View File

@ -0,0 +1,4 @@
-- 返回 nil 的函数
return function()
return nil
end

View File

@ -0,0 +1,4 @@
-- 逻辑非函数(布尔版)
return function(b)
return not b
end

View File

@ -0,0 +1,4 @@
-- 无参函数:返回常量整数
return function()
return 42
end

View File

@ -82,7 +82,7 @@ TEST_F(LuaCjApiTest, LoadLib) {
TEST_F(LuaCjApiTest, LoadLibFileNotFound) {
int ret = load_lib(runner, "/no/such/file.lua", "bad");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
}
// 测试超过最大库数量限制
@ -106,7 +106,7 @@ TEST_F(LuaCjApiTest, LoadLibOverflow) {
fclose(f);
int ret = load_lib(runner, path.c_str(), "extra");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LUALIB_LOAD_OVER_STACK);
EXPECT_EQ(get_errno(), LUAERR_LIB_OVERFLOW);
}
// 测试卸载库
@ -120,7 +120,7 @@ TEST_F(LuaCjApiTest, UnloadLib) {
TEST_F(LuaCjApiTest, UnloadLibNotFound) {
int ret = unload_lib(runner, "nosuch");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_UNLOADLIB_FAIL);
EXPECT_EQ(get_errno(), LUAERR_UNLOADLIB_FAIL);
}
// 测试运行脚本
@ -144,7 +144,7 @@ TEST_F(LuaCjApiTest, RunScriptWithArg) {
TEST_F(LuaCjApiTest, RunScriptSyntaxError) {
int ret = run(runner, get_script_path("syntax_error.lua").c_str(), NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "'do' expected"), nullptr);
}
@ -153,7 +153,7 @@ TEST_F(LuaCjApiTest, RunScriptSyntaxError) {
TEST_F(LuaCjApiTest, RunScriptRuntimeError) {
int ret = run(runner, get_script_path("runtime_error.lua").c_str(), nullptr);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_ERROR);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "oops"), nullptr);
}
@ -307,7 +307,7 @@ TEST_F(LuaCjApiTest, DoStringWithGlobal) {
TEST_F(LuaCjApiTest, DoStringSyntaxError) {
int ret = dostring(runner, "if true then");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_ERROR_FUNCS);
EXPECT_EQ(get_errno(), LUAERR_CALLBACK);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "'end' expected"), nullptr);
}
@ -316,14 +316,14 @@ TEST_F(LuaCjApiTest, DoStringSyntaxError) {
TEST_F(LuaCjApiTest, DoStringNoReturn) {
int ret = dostring(runner, "x = 10");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_BAD_RET);
}
// 测试返回 nil
TEST_F(LuaCjApiTest, DoStringNilReturn) {
int ret = dostring(runner, "return nil");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_BAD_RET);
}
// ==================== loadfunction / callfunction 预加载测试 ====================
@ -364,21 +364,21 @@ TEST_F(LuaCjApiTest, CallFunctionNoArgMultipleTimes) {
TEST_F(LuaCjApiTest, CallFunctionNotFound) {
int ret = callfunction(runner, "nonexistent", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// 预加载不存在的文件
TEST_F(LuaCjApiTest, LoadFunctionFileNotFound) {
int ret = loadfunction(runner, "/no/such/file.lua", "bad");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
}
// 预加载顶层未返回函数的文件
TEST_F(LuaCjApiTest, LoadFunctionNotAFunction) {
int ret = loadfunction(runner, get_script_path("func_not_a_function.lua").c_str(), "bad");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_VALID);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_INVALID);
}
// 预加载同名函数
@ -386,7 +386,7 @@ TEST_F(LuaCjApiTest, LoadFunctionDuplicate) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "dup"), 0);
int ret = loadfunction(runner, get_script_path("func_add.lua").c_str(), "dup");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// 运行时错误
@ -394,7 +394,7 @@ TEST_F(LuaCjApiTest, CallFunctionRuntimeError) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_runtime_error.lua").c_str(), "bad"), 0);
int ret = callfunction(runner, "bad", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_ERROR);
}
// unloadfunction 后再调用应报 5023且可重新加载
@ -407,7 +407,7 @@ TEST_F(LuaCjApiTest, CallFunctionWithUnloadFunction) {
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
// 卸载后可重新预加载,计数器重置
EXPECT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
@ -472,7 +472,7 @@ TEST_F(LuaCjApiTest, LoadFunctionOverflow) {
fclose(f);
int ret = loadfunction(runner, path.c_str(), "overflow");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FUNCTION_OVER);
EXPECT_EQ(get_errno(), LUAERR_FUNC_OVERFLOW);
}
// cleanup 后预加载函数应失效
@ -483,7 +483,168 @@ TEST_F(LuaCjApiTest, CallFunctionAfterCleanup) {
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// ==================== typed interop 测试v0.2.2====================
// 整型参数直通Lua 收到 integer返回 integer+1仓颉侧读回 Int64
TEST_F(LuaCjApiTest, TypedCallInt) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
EXPECT_EQ(callfunction_int(runner, "inc", 41), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 42);
// 字符串形式同步可用(向后兼容)
EXPECT_STREQ(getresult(runner), "42");
}
// 浮点参数直通:返回 number*2.5,读回 Float64
TEST_F(LuaCjApiTest, TypedCallNum) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_mul.lua").c_str(), "mul"), 0);
EXPECT_EQ(callfunction_num(runner, "mul", 4.0), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 10.0);
EXPECT_STREQ(getresult(runner), "10.0");
}
// 布尔参数直通:返回逻辑非,读回 Bool
TEST_F(LuaCjApiTest, TypedCallBool) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_not.lua").c_str(), "lnot"), 0);
EXPECT_EQ(callfunction_bool(runner, "lnot", 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 0);
EXPECT_STREQ(getresult(runner), "false");
EXPECT_EQ(callfunction_bool(runner, "lnot", 0), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
EXPECT_STREQ(getresult(runner), "true");
}
// 无参调用 + 整数返回
TEST_F(LuaCjApiTest, TypedCallVoid) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "forty_two"), 0);
EXPECT_EQ(callfunction_void(runner, "forty_two"), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 42);
}
// 回声函数:验证各类型往返保真(类型不丢失)
TEST_F(LuaCjApiTest, TypedEchoRoundTrip) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_echo.lua").c_str(), "echo"), 0);
// int 往返
EXPECT_EQ(callfunction_int(runner, "echo", -123456789LL), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), -123456789LL);
// 大整数(超过 double 精确表示范围)往返
EXPECT_EQ(callfunction_int(runner, "echo", 9007199254740993LL), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 9007199254740993LL);
// num 往返
EXPECT_EQ(callfunction_num(runner, "echo", 3.14159), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 3.14159);
// bool 往返
EXPECT_EQ(callfunction_bool(runner, "echo", 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
// string 往返(走原 callfunction
EXPECT_EQ(callfunction(runner, "echo", "hello typed"), 0);
EXPECT_EQ(result_type(runner), CJT_STR);
EXPECT_STREQ(getresult(runner), "hello typed");
}
// nil 返回值识别
TEST_F(LuaCjApiTest, TypedNilResult) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_nil.lua").c_str(), "nilret"), 0);
EXPECT_EQ(callfunction_void(runner, "nilret"), 0);
EXPECT_EQ(result_type(runner), CJT_NIL);
EXPECT_EQ(result_int(runner), 0);
EXPECT_EQ(result_bool(runner), 0);
}
// typed 调用不存在的函数仍报 5023
TEST_F(LuaCjApiTest, TypedCallNotFound) {
EXPECT_EQ(callfunction_int(runner, "nonexistent", 1), -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
EXPECT_EQ(callfunction_void(runner, "nonexistent"), -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// runScript 的 typed 变体args_echo.lua 直接返回入参
TEST_F(LuaCjApiTest, TypedRunVariants) {
std::string path = temp_dir + "/args_echo.lua";
FILE* f = fopen(path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "return (...)");
fclose(f);
// int
EXPECT_EQ(run_int(runner, path.c_str(), 777), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 777);
// num
EXPECT_EQ(run_num(runner, path.c_str(), 2.5), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 2.5);
// bool
EXPECT_EQ(run_bool(runner, path.c_str(), 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
// 无参无返回时栈顶为空reslt 应为 "nil"
std::string void_path = temp_dir + "/no_return.lua";
f = fopen(void_path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "local x = 1");
fclose(f);
EXPECT_EQ(run_void(runner, void_path.c_str()), 0);
EXPECT_EQ(result_type(runner), CJT_NIL);
}
// typed 与字符串 API 混用互不干扰typed 结果同步到字符串缓冲区
TEST_F(LuaCjApiTest, TypedAndStringCoexist) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
// 字符串 API
EXPECT_EQ(callfunction(runner, "add", "a"), 0);
EXPECT_STREQ(getresult(runner), "result: a (call #1)");
EXPECT_EQ(result_type(runner), CJT_STR);
// typed API
EXPECT_EQ(callfunction_int(runner, "inc", 100), 0);
EXPECT_EQ(result_int(runner), 101);
// 字符串函数的计数器不受影响
EXPECT_EQ(callfunction(runner, "add", "b"), 0);
EXPECT_STREQ(getresult(runner), "result: b (call #2)");
}
// cleanup 后 typed 状态重置为 NIL
TEST_F(LuaCjApiTest, TypedStateAfterCleanup) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "v"), 0);
EXPECT_EQ(callfunction_void(runner, "v"), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(cleanup(runner), 0);
int ret = callfunction_void(runner, "v");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
int main(int argc, char **argv) {