feat: typed interop 原始类型直通映射 (Int64/Float64/Bool)

- C++ 层: callfunction_int/num/bool/void + run_int/num/bool/void
- 新增 capture_result()/finish_call() 正确计算 lua_pcall 实际返回值数量,
  修复无返回值场景下误读栈残留的问题
- C FFI 层: 11 个新导出函数 + lua_runner 结构体 tresult_* 镜像字段
- 仓颉层: callFunctionInt/Num/Bool, runScriptInt/Num/Bool, resultType()
  带严格类型校验(不匹配抛 LUAERR_RESULT_TYPE)
- 大整数跨 2^53 保真(字符串路径做不到)
- 管道模式语义保留给字符串 API; typed run_* 为独立调用保持栈清洁
- GTest 新增 10 个 typed 测试用例(共 44 个全部通过)
- 独立程序验证仓颉层运行时正确性(int/num/bool直通/大整数保真/错误路径)
This commit is contained in:
JianFeeeee
2026-08-25 09:29:24 +08:00
parent 42e8b7676d
commit 237aab3f36
13 changed files with 1091 additions and 112 deletions

View File

@ -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)
}
}