feat: 实现loadFunction预加载函数能力 + AI辅助编程标识

- 新增 loadFunction/callFunction/unloadFunction 预加载函数 API
- 预加载语义:加载文件→顶层执行一次→返回函数存入Lua Registry→可多次调用
- 与管道模式(load/runScript)存储完全解耦:funcs[]+Registry vs pkgs[]+虚拟栈
- 新增错误码 5023/5024/5025
- 新增单元测试:仓颉侧 8 个 + C++ GTest 侧 11 个
- 本机以 Cangjie 1.1.0 实编译并通过功能运行验证
- README/doc 增加 AI 辅助编程标识及预加载函数模式说明
This commit is contained in:
JianFeeeee
2026-08-23 12:03:04 +08:00
parent 207082a1a0
commit 42e8b7676d
14 changed files with 759 additions and 6 deletions

View File

@ -23,5 +23,8 @@
#define NAPI_ERROR_FUNCS 5020 //函数调用错误
#define NAPI_CHDIR_ERROR 5021 //切换工作目录失败,可能导致包搜索错误,日志写入路径错误等
#define NAPI_NOCHUNK_FOUND 5022 //没有找到待调用的函数
#define NAPI_FUNCTION_NOT_FOUND 5023 //通过callfunction调用时未找到指定函数
#define NAPI_LOAD_FUNCTION_OVER 5024 //预加载函数数量超过上限
#define NAPI_FUNCTION_NOT_VALID 5025 //预加载文件顶层执行后返回的不是函数
#endif

View File

@ -86,6 +86,65 @@ int run(void *selfd,const char *path,const char *arg)
return 0;
}
/// @brief 预加载lua文件到内存加载文件、执行顶层代码、捕获返回的函数并存入 Registry供后续 callfunction 多次调用
/// @param selfd init_lua_runner返回的void指针
/// @param path 需要加载的文件路径
/// @param name 函数名称callfunction/unloadfunction时使用
/// @return 0代表正常结束-1表示异常退出
int loadfunction(void *selfd,const char *path,const char *name)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
return -1;
}
if(runner->loadfunction(path,name)==-1)
return -1;
return 0;
}
/// @brief 卸载预加载的函数(释放 Registry 引用)
/// @param selfd init_lua_runner返回的void指针
/// @param name loadfunction加载时的函数名称
/// @return 0代表正常结束-1表示异常退出
int unloadfunction(void *selfd,const char *name)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;//还原对象
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
return -1;
}
if(runner->unloadfunction(name)==-1)
return -1;
return 0;
}
/// @brief 按名称调用已预加载的函数(从 Registry 取出,不消耗该函数,可重复调用)
/// @param selfd init_lua_runner返回的void指针
/// @param name loadfunction加载时的函数名称
/// @param arg 传递给函数的单参数可为NULL
/// @return 成功返回0,失败返回-1,并将lua出错原因拷贝至result中
int callfunction(void *selfd,const char *name,const char *arg)
{
lua_runner* self = (lua_runner*)selfd;
Lua_runner *runner = (Lua_runner*)self->lua_obj;
if(runner == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
return -1;
}
if(runner->callfunction(name,arg)==-1){
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return -1;
}
snprintf(self->result,MAX_RESULT,"%s",runner->reslt.c_str());
return 0;
}
/// @brief 执行单句lua语句
/// @param selfd init_lua_runner返回的void指针
/// @param target 执行语句(字符串类型)

View File

@ -55,6 +55,26 @@ int load_lib(void *selfd,const char *path,const char *name);
/// @return 成功返回0,失败返回-1
int unload_lib(void *selfd,const char *name);
/// @brief 预加载lua文件到内存加载文件、执行顶层代码、捕获返回的函数并存入 Registry供后续 callfunction 多次调用
/// @param selfd init_lua_runner返回的void指针
/// @param path 需要加载的文件路径
/// @param name 函数名称callfunction/unloadfunction时使用
/// @return 0代表正常结束-1表示异常退出
int loadfunction(void *selfd,const char *path,const char *name);
/// @brief 卸载预加载的函数(释放 Registry 引用)
/// @param selfd init_lua_runner返回的void指针
/// @param name loadfunction加载时的函数名称
/// @return 0代表正常结束-1表示异常退出
int unloadfunction(void *selfd,const char *name);
/// @brief 按名称调用已预加载的函数(从 Registry 取出,不消耗该函数,可重复调用)
/// @param selfd init_lua_runner返回的void指针
/// @param name loadfunction加载时的函数名称
/// @param arg 传递给函数的单参数可为NULL
/// @return 成功返回0,失败返回-1,并将lua出错原因拷贝至result中
int callfunction(void *selfd,const char *name,const char *arg);
/// @brief 运行lua脚本
/// @param selfd init_lua_runner返回的void指针
/// @param path 脚本路径

View File

@ -25,7 +25,7 @@ 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;//加载默认路径*/
errno = NAPI_LUA_INITFAIL;//加载默认路径*/
if(path !=NULL)
{
char buf[1024];
@ -36,6 +36,7 @@ Lua_runner::Lua_runner(const char *path)
errno = NAPI_LUA_INITFAIL;
}
this->pkg_cont = 0;
this->func_cont = 0;
}
Lua_runner::~Lua_runner()
@ -160,12 +161,161 @@ int Lua_runner::clean()//进行新一轮调用前一定要先clean清除上个
lua_remove(this->L,this->pkgs[i].ref);
}
this->pkg_cont = 0;
// 清理预加载函数
for(int i = 0; i < this->func_cont; i++)
{
luaL_unref(this->L, LUA_REGISTRYINDEX, this->funcs[i].ref);
}
this->func_cont = 0;
this->reslt ="nil";
lua_settop(this->L, 0);//清空栈
return 0;
}
//TODO 支持更多类型的参数
int Lua_runner::loadfunction(const char *path,const char *name)
{
if(!this->check_luastatue())
return -1;
if(!this->check_file_exists(path))
{
errno = NAPI_LOAD_FILE_ERROR;
return -1;
}
if(this->func_cont >= MAX_LUA_FUNC)
{
errno = NAPI_LOAD_FUNCTION_OVER;
return -1;
}
// 检查是否已存在同名函数
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
errno = NAPI_FUNCTION_NOT_FOUND;
return -1;
}
}
// 1. 加载文件到栈顶(编译成 chunk
if(luaL_loadfile(this->L, path) != LUA_OK)
{
const char *ret = lua_tostring(this->L, -1);
this->reslt = ret;
errno = NAPI_LOAD_FILE_ERROR;
lua_pop(this->L, 1);
return -1;
}
// 2. 执行 chunk顶层代码期望返回一个函数
if(lua_pcall(this->L, 0, 1, 0) != LUA_OK)
{
errno = NAPI_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
return -1;
}
// 3. 检查栈顶是否为函数
if(!lua_isfunction(this->L, -1))
{
errno = NAPI_FUNCTION_NOT_VALID;
lua_pop(this->L, 1);
return -1;
}
// 4. 存入 Lua Registry绝对引用不受栈变化影响
int ref = luaL_ref(this->L, LUA_REGISTRYINDEX);
// 5. 记录到 funcs 表
snprintf(this->funcs[this->func_cont].name, 128, "%s", name);
this->funcs[this->func_cont].ref = ref;
this->func_cont++;
return 0;
}
int Lua_runner::unloadfunction(const char *name)
{
int sig = -1;
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
sig = i;
break;
}
}
if(sig == -1)
{
errno = NAPI_FUNCTION_NOT_FOUND;
return -1;
}
if(!this->check_luastatue())
return -1;
// 释放 Registry 引用
luaL_unref(this->L, LUA_REGISTRYINDEX, this->funcs[sig].ref);
// 从 funcs 表中移除
for(int j = sig + 1; j < this->func_cont; j++)
{
this->funcs[j - 1] = this->funcs[j];
}
this->func_cont--;
return 0;
}
int Lua_runner::callfunction(const char *name,const char *arg)
{
if(!this->check_luastatue())
return -1;
// 在 funcs 表中查找(预加载函数表,独立于管道栈 pkgs[]
int sig = -1;
for(int i = 0; i < this->func_cont; i++)
{
if(strcmp(this->funcs[i].name, name) == 0)
{
sig = i;
break;
}
}
if(sig == -1)
{
errno = NAPI_FUNCTION_NOT_FOUND;
return -1;
}
// 从 Lua Registry 中取出函数(绝对引用,不受栈变化影响)
lua_rawgeti(this->L, LUA_REGISTRYINDEX, this->funcs[sig].ref);
// 如果有参数,压入参数
if(arg != NULL)
{
lua_pushlstring(this->L, arg, strlen(arg));
}
int argcount = (arg != NULL) ? 1 : 0;
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);
return 0;
}
else
{
errno = NAPI_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L, -1);
lua_pop(this->L, 1);
return -1;
}
}
int Lua_runner::run(const char *path,const char *arg)
{
if(!this->check_luastatue())//检查lua状态机状态

View File

@ -15,17 +15,28 @@ typedef struct loaded_package
}ld_pkg;
// 预加载函数表:使用 luaL_ref 存入 Lua Registryref 为绝对索引,不受栈变化影响
#define MAX_LUA_FUNC 20
typedef struct loaded_function
{
char name[128];
int ref; // luaL_ref 返回的绝对索引
}ld_func;
#ifdef __cplusplus
class Lua_runner//lua运行器对象
{
public:
Lua_runner(const char *path = NULL);//创建lua状态机并持久化持有
~Lua_runner();//销毁状态机
int load_lib(const char *path,const char *name);//加载库
int load_lib(const char *path,const char *name);//加载库(块压栈)
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 clean();//清除状态机缓存
//int callfunction();
int dostring(const char *target);
std::string reslt;
lua_State *get_lua_State();
@ -33,6 +44,8 @@ class Lua_runner//lua运行器对象
lua_State *L;
int pkg_cont;
ld_pkg pkgs[MAX_LUA_LIB];
int func_cont;
ld_func funcs[MAX_LUA_FUNC];
bool check_luastatue();
bool check_file_exists(const char *path);
};