diff --git a/lib/lua_cj_api.cpp b/lib/lua_cj_api.cpp index 6dc05e3..e635305 100755 --- a/lib/lua_cj_api.cpp +++ b/lib/lua_cj_api.cpp @@ -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 interop(v0.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: diff --git a/lib/lua_cj_api.h b/lib/lua_cj_api.h index 77c9dc7..83a51e6 100755 --- a/lib/lua_cj_api.h +++ b/lib/lua_cj_api.h @@ -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 interop(v0.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 脚本路径 diff --git a/lib/lua_runner.cpp b/lib/lua_runner.cpp index fe73bb0..39218a9 100755 --- a/lib/lua_runner.cpp +++ b/lib/lua_runner.cpp @@ -1,5 +1,6 @@ #include "lua_runner.hpp" #include +#include 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 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; } diff --git a/lib/lua_runner.hpp b/lib/lua_runner.hpp index 17b6fd4..6336454 100644 --- a/lib/lua_runner.hpp +++ b/lib/lua_runner.hpp @@ -24,6 +24,13 @@ typedef struct loaded_function }ld_func; +// 结果类型标记(typed interop,v0.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; diff --git a/src/bridge.cj b/src/bridge.cj index ce0e6fb..6dffb0b 100644 --- a/src/bridge.cj +++ b/src/bridge.cj @@ -20,6 +20,31 @@ foreign func loadfunction(selfd: CPointer,path: CString, name: CString): I foreign func unloadfunction(selfd: CPointer,name: CString): Int32 @C foreign func callfunction(selfd: CPointer,name: CString, arg: CString): Int32 +// typed interop(v0.2.2):原始类型直通映射 +@C +foreign func callfunction_int(selfd: CPointer, name: CString, val: Int64): Int32 +@C +foreign func callfunction_num(selfd: CPointer, name: CString, val: Float64): Int32 +@C +foreign func callfunction_bool(selfd: CPointer, name: CString, val: Int32): Int32 +@C +foreign func callfunction_void(selfd: CPointer, name: CString): Int32 +@C +foreign func run_int(selfd: CPointer, path: CString, val: Int64): Int32 +@C +foreign func run_num(selfd: CPointer, path: CString, val: Float64): Int32 +@C +foreign func run_bool(selfd: CPointer, path: CString, val: Int32): Int32 +@C +foreign func run_void(selfd: CPointer, path: CString): Int32 +@C +foreign func result_type(selfd: CPointer): Int32 +@C +foreign func result_int(selfd: CPointer): Int64 +@C +foreign func result_num(selfd: CPointer): Float64 +@C +foreign func result_bool(selfd: CPointer): Int32 @C foreign func cleanup(selfd: CPointer): 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 interop(v0.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 } diff --git a/src/lua_runner_test.cj b/src/lua_runner_test.cj index e46b0de..08a8ffa 100644 --- a/src/lua_runner_test.cj +++ b/src/lua_runner_test.cj @@ -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) + } +} diff --git a/test/scripts/func_typed_add.lua b/test/scripts/func_typed_add.lua new file mode 100644 index 0000000..08b0e7d --- /dev/null +++ b/test/scripts/func_typed_add.lua @@ -0,0 +1,5 @@ +-- 支持 typed 参数的加法函数(整数版) +-- 调用方传入一个整数,返回它加 1 +return function(n) + return n + 1 +end \ No newline at end of file diff --git a/test/scripts/func_typed_echo.lua b/test/scripts/func_typed_echo.lua new file mode 100644 index 0000000..a8826ef --- /dev/null +++ b/test/scripts/func_typed_echo.lua @@ -0,0 +1,4 @@ +-- 回声函数:返回传入的参数本身(保留类型) +return function(x) + return x +end \ No newline at end of file diff --git a/test/scripts/func_typed_mul.lua b/test/scripts/func_typed_mul.lua new file mode 100644 index 0000000..4472224 --- /dev/null +++ b/test/scripts/func_typed_mul.lua @@ -0,0 +1,4 @@ +-- 支持 typed 参数的乘法函数(浮点数版) +return function(n) + return n * 2.5 +end \ No newline at end of file diff --git a/test/scripts/func_typed_nil.lua b/test/scripts/func_typed_nil.lua new file mode 100644 index 0000000..b8abbaa --- /dev/null +++ b/test/scripts/func_typed_nil.lua @@ -0,0 +1,4 @@ +-- 返回 nil 的函数 +return function() + return nil +end \ No newline at end of file diff --git a/test/scripts/func_typed_not.lua b/test/scripts/func_typed_not.lua new file mode 100644 index 0000000..31f9042 --- /dev/null +++ b/test/scripts/func_typed_not.lua @@ -0,0 +1,4 @@ +-- 逻辑非函数(布尔版) +return function(b) + return not b +end \ No newline at end of file diff --git a/test/scripts/func_typed_void.lua b/test/scripts/func_typed_void.lua new file mode 100644 index 0000000..896e88b --- /dev/null +++ b/test/scripts/func_typed_void.lua @@ -0,0 +1,4 @@ +-- 无参函数:返回常量整数 +return function() + return 42 +end \ No newline at end of file diff --git a/test/test_lua_cj_api.cpp b/test/test_lua_cj_api.cpp index e997efb..5741593 100644 --- a/test/test_lua_cj_api.cpp +++ b/test/test_lua_cj_api.cpp @@ -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) {