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

@ -0,0 +1,6 @@
-- func_add.lua: 顶层执行一次(计数),返回闭包函数供 callFunction 多次调用
local callCount = 0
return function(input)
callCount = callCount + 1
return "result: " .. input .. " (call #" .. callCount .. ")"
end

View File

@ -0,0 +1,5 @@
-- func_multi_call.lua: 顶层执行一次(初始化),返回无参闭包
local init = "init-done"
return function()
return "multi:" .. init
end

View File

@ -0,0 +1,2 @@
-- func_not_a_function.lua: 顶层返回非函数loadFunction 应报 5025
return "not a function"

View File

@ -0,0 +1,5 @@
-- func_runtime_error.lua: 返回会在运行时抛错的函数
return function()
local x = nil
return x.nonexistent
end

View File

@ -326,6 +326,166 @@ TEST_F(LuaCjApiTest, DoStringNilReturn) {
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
}
// ==================== loadfunction / callfunction 预加载测试 ====================
// 基础预加载 + 调用
TEST_F(LuaCjApiTest, LoadFunctionAndCall) {
int ret = loadfunction(runner, get_script_path("func_add.lua").c_str(), "add");
EXPECT_EQ(ret, 0);
ret = callfunction(runner, "add", "hello");
EXPECT_EQ(ret, 0);
EXPECT_STREQ(getresult(runner), "result: hello (call #1)");
}
// 同一函数多次调用:顶层只执行一次,计数器持续累加
TEST_F(LuaCjApiTest, CallFunctionMultipleTimes) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
EXPECT_EQ(callfunction(runner, "add", "a"), 0);
EXPECT_STREQ(getresult(runner), "result: a (call #1)");
EXPECT_EQ(callfunction(runner, "add", "b"), 0);
EXPECT_STREQ(getresult(runner), "result: b (call #2)");
EXPECT_EQ(callfunction(runner, "add", "c"), 0);
EXPECT_STREQ(getresult(runner), "result: c (call #3)");
}
// 无参函数多次调用
TEST_F(LuaCjApiTest, CallFunctionNoArgMultipleTimes) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_multi_call.lua").c_str(), "multi"), 0);
for (int i = 0; i < 5; i++) {
EXPECT_EQ(callfunction(runner, "multi", NULL), 0);
EXPECT_STREQ(getresult(runner), "multi:init-done");
}
}
// 调用未预加载的函数
TEST_F(LuaCjApiTest, CallFunctionNotFound) {
int ret = callfunction(runner, "nonexistent", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_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);
}
// 预加载顶层未返回函数的文件
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);
}
// 预加载同名函数
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);
}
// 运行时错误
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);
}
// unloadfunction 后再调用应报 5023且可重新加载
TEST_F(LuaCjApiTest, CallFunctionWithUnloadFunction) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
EXPECT_EQ(callfunction(runner, "add", "before"), 0);
EXPECT_STREQ(getresult(runner), "result: before (call #1)");
EXPECT_EQ(unloadfunction(runner, "add"), 0);
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
// 卸载后可重新预加载,计数器重置
EXPECT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
EXPECT_EQ(callfunction(runner, "add", "after"), 0);
EXPECT_STREQ(getresult(runner), "result: after (call #1)");
}
// 预加载与管道模式共存,互不干扰:
// 预加载函数走独立的 funcs[]+Registry管道走 pkgs[] 栈
TEST_F(LuaCjApiTest, LoadFunctionAndPipelineNoInterference) {
// 预加载一个函数(独立于管道栈)
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
EXPECT_EQ(callfunction(runner, "add", "x"), 0);
EXPECT_STREQ(getresult(runner), "result: x (call #1)");
// 管道模式照常pkgs 栈)
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_add_suffix.lua").c_str(), "add_suffix"), 0);
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_to_upper.lua").c_str(), "to_upper"), 0);
ASSERT_EQ(load_lib(runner, get_script_path("pipeline_add_prefix.lua").c_str(), "add_prefix"), 0);
int ret = run(runner, get_script_path("pipeline_generate_data.lua").c_str(), "hello");
EXPECT_EQ(ret, 0);
EXPECT_STREQ(getresult(runner), "hello");
ret = run(runner, nullptr, nullptr);
EXPECT_EQ(ret, 0);
EXPECT_STREQ(getresult(runner), "[PREFIX] hello");
ret = run(runner, nullptr, nullptr);
EXPECT_EQ(ret, 0);
EXPECT_STREQ(getresult(runner), "[PREFIX] HELLO");
ret = run(runner, nullptr, nullptr);
EXPECT_EQ(ret, 0);
EXPECT_STREQ(getresult(runner), "[PREFIX] HELLO [SUFFIX]");
// 管道跑完后,预加载函数仍可调用,计数器继续
EXPECT_EQ(callfunction(runner, "add", "y"), 0);
EXPECT_STREQ(getresult(runner), "result: y (call #2)");
}
// 预加载数量上限:默认 MAX_LUA_FUNC=20
TEST_F(LuaCjApiTest, LoadFunctionOverflow) {
for (int i = 0; i < MAX_LUA_FUNC; i++) {
char name[20];
snprintf(name, sizeof(name), "func%d", i);
std::string content = "return function() return '" + std::to_string(i) + "' end";
std::string path = temp_dir + "/func" + std::to_string(i) + ".lua";
FILE* f = fopen(path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "%s", content.c_str());
fclose(f);
int ret = loadfunction(runner, path.c_str(), name);
EXPECT_EQ(ret, 0);
}
// 第 21 个应报 5024
std::string path = temp_dir + "/overflow.lua";
FILE* f = fopen(path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "return function() return 'overflow' end");
fclose(f);
int ret = loadfunction(runner, path.c_str(), "overflow");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FUNCTION_OVER);
}
// cleanup 后预加载函数应失效
TEST_F(LuaCjApiTest, CallFunctionAfterCleanup) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
EXPECT_EQ(callfunction(runner, "add", "x"), 0);
EXPECT_EQ(cleanup(runner), 0);
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();