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

@ -323,3 +323,178 @@ func testDefaultIOCallback(): Unit {
let result = unsafe { defaultIO() }
@Expect(result, 0)
}
// ====================
// 8. loadFunction / callFunction 预加载测试
// ====================
@Test
func testLoadFunctionAndCall(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_add.lua"), "add")
let res = runner.callFunction("add", "hello")
// 闭包带计数器,第一次调用应返回 call #1
@Expect(res, "result: hello (call #1)")
}
@Test
func testCallFunctionMultipleTimes(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_multi_call.lua"), "multi")
let r1 = runner.callFunction("multi", "")
@Expect(r1, "multi:init-done")
let r2 = runner.callFunction("multi", "")
@Expect(r2, "multi:init-done")
let r3 = runner.callFunction("multi", "")
@Expect(r3, "multi:init-done")
let r4 = runner.callFunction("multi", "")
@Expect(r4, "multi:init-done")
}
@Test
func testCallFunctionCallCount(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_add.lua"), "add")
@Expect(runner.callFunction("add", "a"), "result: a (call #1)")
@Expect(runner.callFunction("add", "b"), "result: b (call #2)")
@Expect(runner.callFunction("add", "c"), "result: c (call #3)")
}
@Test
func testCallFunctionNotFound(): Unit {
let runner = LuaRunner()
try {
runner.callFunction("nonexistent", "")
fail("Should throw function not found error")
} catch (e: LuaError) {
@Expect(e.code, 5023)
}
}
@Test
func testLoadFunctionFileNotFound(): Unit {
let runner = LuaRunner()
try {
runner.loadFunction("/no/such/file.lua", "bad")
fail("Should throw file not found error")
} catch (e: LuaError) {
@Expect(e.code, 5003)
}
}
@Test
func testLoadFunctionNotAFunction(): Unit {
let runner = LuaRunner()
try {
runner.loadFunction(scriptPath("func_not_a_function.lua"), "bad")
fail("Should throw function not valid error")
} catch (e: LuaError) {
@Expect(e.code, 5025)
}
}
@Test
func testLoadFunctionDuplicate(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_add.lua"), "dup")
try {
runner.loadFunction(scriptPath("func_add.lua"), "dup")
fail("Should throw on duplicate name")
} catch (e: LuaError) {
@Expect(e.code, 5023)
}
}
@Test
func testCallFunctionRuntimeError(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_runtime_error.lua"), "bad")
try {
runner.callFunction("bad", "")
fail("Should throw runtime error")
} catch (e: LuaError) {
@Expect(e.code, 5010)
}
}
@Test
func testCallFunctionWithUnloadFunction(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_add.lua"), "add")
@Expect(runner.callFunction("add", "before"), "result: before (call #1)")
runner.unloadFunction("add")
try {
runner.callFunction("add", "")
fail("Should throw function not found after unloadFunction")
} catch (e: LuaError) {
@Expect(e.code, 5023)
}
// 卸载后可重新加载同名函数
runner.loadFunction(scriptPath("func_add.lua"), "add")
@Expect(runner.callFunction("add", "after"), "result: after (call #1)")
}
@Test
func testLoadFunctionAndPipelineNoInterference(): Unit {
let runner = LuaRunner()
// 预加载函数(用独立 func 脚本)
runner.loadFunction(scriptPath("func_add.lua"), "add")
@Expect(runner.callFunction("add", "x"), "result: x (call #1)")
// 管道模式(用 pipeline 脚本,走 pkgs 栈)
runner.load(scriptPath("pipeline_add_suffix.lua"), "add_suffix")
.load(scriptPath("pipeline_to_upper.lua"), "to_upper")
.load(scriptPath("pipeline_add_prefix.lua"), "add_prefix")
let initialData = runner.runScript(scriptPath("pipeline_generate_data.lua"), "hello")
@Expect(initialData, "hello")
@Expect(runner.runScript("", ""), "[PREFIX] hello")
@Expect(runner.runScript("", ""), "[PREFIX] HELLO")
@Expect(runner.runScript("", ""), "[PREFIX] HELLO [SUFFIX]")
// 管道结束后,预加载函数仍可调用,且计数器继续
@Expect(runner.callFunction("add", "y"), "result: y (call #2)")
}
@Test
func testLoadFunctionAndCleanCoexists(): Unit {
let runner = LuaRunner()
runner.loadFunction(scriptPath("func_add.lua"), "add")
@Expect(runner.callFunction("add", "x"), "result: x (call #1)")
runner.clear()
try {
runner.callFunction("add", "")
fail("Should throw after clean")
} catch (e: LuaError) {
@Expect(e.code, 5023)
}
}
@Test
func testLoadFunctionOverflow(): Unit {
ensureGeneratedScriptsDir()
let runner = LuaRunner()
for (i in 0..20) {
let name = "overflow_func_${i}"
let path = generatedScriptPath(name + ".lua")
File.writeTo(path, ("return function() return \"" + name + "\" end").toArray())
try {
runner.loadFunction(path, name)
} catch (e: LuaError) {
@Expect(e.code, 5024)
break
}
}
}