Files
LuaCangjia_api/lib/lua_runner.cpp
JianFeeeee 42e8b7676d 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 辅助编程标识及预加载函数模式说明
2026-08-23 12:03:04 +08:00

400 lines
9.5 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

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

#include "lua_runner.hpp"
#include <cstring>
extern "C"
{
#include <errno.h>
#include "errors.h"
}
Lua_runner::Lua_runner(const char *path)
{
this->L =NULL;
this->L = luaL_newstate();
errno = 0;//清除老错误信息
if(this->L == NULL){
errno = NAPI_LUA_STATE_ERROR;
return ;
}
luaL_openlibs(this->L);//创建lua状态机打开标准库
const char* save_stdlib_code =
"local std = {} "
"for k, v in pairs(_G) do std[k] = true end "
"_G.__STD_LIBS = std"; // 将列表存到全局变量 __STD_LIBS 中
luaL_dostring(this->L, save_stdlib_code);
/*if(luaL_dostring(this->L,
"package.path = \";/data/storage/el2/luapkg/?/init.lua\"") != LUA_OK)//TODO 修改加载库的默认路径指向沙盒内路径
errno = NAPI_LUA_INITFAIL;//加载默认路径*/
if(path !=NULL)
{
char buf[1024];
snprintf(buf, sizeof(buf),
"package.path = package.path .. \";%s\"",
path);
if(luaL_dostring(this->L,buf) != LUA_OK)//加载用户路径
errno = NAPI_LUA_INITFAIL;
}
this->pkg_cont = 0;
this->func_cont = 0;
}
Lua_runner::~Lua_runner()
{
lua_close(this->L);
}
bool Lua_runner::check_file_exists(const char *path)
{
FILE* file = fopen(path, "r");
if (file) {
fclose(file);
return true;
}
return false;
}
bool Lua_runner::check_luastatue()
{
/*检查lua状态机状况*/
if(this->L == NULL)
{
errno = NAPI_LUA_STATE_ERROR;
return false;
}
//分为两部分写,防止空指针访问
if(lua_status(this->L) != LUA_OK)
{
errno = NAPI_LUA_STATE_ERROR;
return false;
}
return true;
}
lua_State *Lua_runner::get_lua_State()
{
return this->L;
}
int Lua_runner::load_lib(const char *path,const char *name)
{
//如果引入哈希表会导致删除包时的多次哈希查询,污染缓存。同时性能仅与数组实现相当
if(!this->check_luastatue())
{
return -1;
}
if(!this->check_file_exists(path))
{
errno = NAPI_LOAD_FILE_ERROR;
return -1;
}
if(this->pkg_cont>=MAX_LUA_LIB)
{
errno = NAPI_LUALIB_LOAD_OVER_STACK;
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;
lua_pop(this->L,1);
return -1;
}
this->pkgs[this->pkg_cont].ref = lua_gettop(this->L)+1;
snprintf(pkgs[pkg_cont].name,128,"%s",name);
this->pkg_cont++;
return 0;
}
int Lua_runner::unload_lib(const char *name)
{
int sig = -1;
for(int i=0;i<this->pkg_cont;i++)
{
if(strcmp(this->pkgs[i].name,name) == 0)
{
sig = i;
break;//查找指定的库,查到则将标志位置为库的位置
}
}
if(sig == -1)
{
errno = NAPI_UNLOADLIB_FAIL;
return -1;//没有查到返回异常(尝试卸载未加载的库)
}
/*检查lua状态*/
if(!this->check_luastatue())
{
return -1;
}
lua_remove(this->L,this->pkgs[sig].ref);
for(int j = sig+1;j<this->pkg_cont;j++)
{
this->pkgs[j-1] = this->pkgs[j];//保持数据结构
this->pkgs[j-1].ref--;//虚拟栈位置同步变化
}
this->pkg_cont--;
return 1;
}
int Lua_runner::clean()//进行新一轮调用前一定要先clean清除上个脚本执行的痕迹
{
if(!this->check_luastatue())
{
return -1;
}
const char* clean_globals_code =
"if _G.__STD_LIBS then "
" local std = _G.__STD_LIBS "
" for k, v in pairs(_G) do "
" if not std[k] then _G[k] = nil end "
" end "
"end";
luaL_dostring(this->L, clean_globals_code);//清空全局变量
luaL_dostring(this->L, "package.loaded = {}");//清空当前引用的包
for(int i = 0 ;i<this->pkg_cont;i++)
{
lua_remove(this->L,this->pkgs[i].ref);
}
this->pkg_cont = 0;
// 清理预加载函数
for(int i = 0; i < this->func_cont; i++)
{
luaL_unref(this->L, LUA_REGISTRYINDEX, this->funcs[i].ref);
}
this->func_cont = 0;
this->reslt ="nil";
lua_settop(this->L, 0);//清空栈
return 0;
}
//TODO 支持更多类型的参数
int Lua_runner::loadfunction(const char *path,const char *name)
{
if(!this->check_luastatue())
return -1;
if(!this->check_file_exists(path))
{
errno = 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状态机状态
return -1;
if(path != NULL)//加载脚本到栈
{
if(this->load_lib(path,path) != 0)
return -1;
}
if(arg != NULL)//压参数入栈
{
lua_pushlstring(this->L,arg,strlen(arg));
}
if(this->pkg_cont <= 0)//检查是否有可调用chunk
{
errno = NAPI_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 = NAPI_LUA_STACK_ERROR;
return -1;
}//检查参数数量
if(lua_pcall(this->L,argcount,LUA_MULTRET,0)== LUA_OK)
{
if(lua_isstring(this->L,-1))
this->reslt = lua_tostring(this->L,-1);
this->pkg_cont--;
return 0;
}
else
{
errno = NAPI_SCRIPT_ERROR;
this->reslt = lua_tostring(this->L,-1);
this->pkg_cont--;
lua_pop(L,1);//错误出栈
return -1;
}
}
int Lua_runner::dostring(const char *target)
{
if(target == NULL)
{
errno = NAPI_ERROR_FUNCS;
return -1;
}
if(luaL_dostring(this->L,target) != LUA_OK)
{
errno = NAPI_ERROR_FUNCS;
this->reslt = lua_tostring(this->L,-1);
lua_pop(L,1);//错误出栈
return -1;
}
if(lua_gettop(this->L) == 0) {
errno = NAPI_SCRIPT_BAD_RET;
this->reslt = "";
return -1;
}
if(lua_isstring(this->L,-1)){
this->reslt = lua_tostring(this->L,-1);
lua_pop(L,1);
return 0;
}
else{
errno = NAPI_SCRIPT_BAD_RET;
lua_pop(L,1);
return -1;
}
}
//TODO 实现函数调用 achieve func call via file
/*
int Lua_runner::callfunction()
{
return 0;
}
*/