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

@ -0,0 +1,5 @@
-- 支持 typed 参数的加法函数(整数版)
-- 调用方传入一个整数,返回它加 1
return function(n)
return n + 1
end

View File

@ -0,0 +1,4 @@
-- 回声函数:返回传入的参数本身(保留类型)
return function(x)
return x
end

View File

@ -0,0 +1,4 @@
-- 支持 typed 参数的乘法函数(浮点数版)
return function(n)
return n * 2.5
end

View File

@ -0,0 +1,4 @@
-- 返回 nil 的函数
return function()
return nil
end

View File

@ -0,0 +1,4 @@
-- 逻辑非函数(布尔版)
return function(b)
return not b
end

View File

@ -0,0 +1,4 @@
-- 无参函数:返回常量整数
return function()
return 42
end

View File

@ -82,7 +82,7 @@ TEST_F(LuaCjApiTest, LoadLib) {
TEST_F(LuaCjApiTest, LoadLibFileNotFound) {
int ret = load_lib(runner, "/no/such/file.lua", "bad");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
}
// 测试超过最大库数量限制
@ -106,7 +106,7 @@ TEST_F(LuaCjApiTest, LoadLibOverflow) {
fclose(f);
int ret = load_lib(runner, path.c_str(), "extra");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LUALIB_LOAD_OVER_STACK);
EXPECT_EQ(get_errno(), LUAERR_LIB_OVERFLOW);
}
// 测试卸载库
@ -120,7 +120,7 @@ TEST_F(LuaCjApiTest, UnloadLib) {
TEST_F(LuaCjApiTest, UnloadLibNotFound) {
int ret = unload_lib(runner, "nosuch");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_UNLOADLIB_FAIL);
EXPECT_EQ(get_errno(), LUAERR_UNLOADLIB_FAIL);
}
// 测试运行脚本
@ -144,7 +144,7 @@ TEST_F(LuaCjApiTest, RunScriptWithArg) {
TEST_F(LuaCjApiTest, RunScriptSyntaxError) {
int ret = run(runner, get_script_path("syntax_error.lua").c_str(), NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FILE_ERROR);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "'do' expected"), nullptr);
}
@ -153,7 +153,7 @@ TEST_F(LuaCjApiTest, RunScriptSyntaxError) {
TEST_F(LuaCjApiTest, RunScriptRuntimeError) {
int ret = run(runner, get_script_path("runtime_error.lua").c_str(), nullptr);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_ERROR);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_ERROR);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "oops"), nullptr);
}
@ -307,7 +307,7 @@ TEST_F(LuaCjApiTest, DoStringWithGlobal) {
TEST_F(LuaCjApiTest, DoStringSyntaxError) {
int ret = dostring(runner, "if true then");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_ERROR_FUNCS);
EXPECT_EQ(get_errno(), LUAERR_CALLBACK);
char* res = getresult(runner);
EXPECT_NE(strstr(res, "'end' expected"), nullptr);
}
@ -316,14 +316,14 @@ TEST_F(LuaCjApiTest, DoStringSyntaxError) {
TEST_F(LuaCjApiTest, DoStringNoReturn) {
int ret = dostring(runner, "x = 10");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_BAD_RET);
}
// 测试返回 nil
TEST_F(LuaCjApiTest, DoStringNilReturn) {
int ret = dostring(runner, "return nil");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_SCRIPT_BAD_RET);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_BAD_RET);
}
// ==================== loadfunction / callfunction 预加载测试 ====================
@ -364,21 +364,21 @@ TEST_F(LuaCjApiTest, CallFunctionNoArgMultipleTimes) {
TEST_F(LuaCjApiTest, CallFunctionNotFound) {
int ret = callfunction(runner, "nonexistent", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_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);
EXPECT_EQ(get_errno(), LUAERR_LOAD_FILE);
}
// 预加载顶层未返回函数的文件
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);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_INVALID);
}
// 预加载同名函数
@ -386,7 +386,7 @@ 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);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// 运行时错误
@ -394,7 +394,7 @@ 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);
EXPECT_EQ(get_errno(), LUAERR_SCRIPT_ERROR);
}
// unloadfunction 后再调用应报 5023且可重新加载
@ -407,7 +407,7 @@ TEST_F(LuaCjApiTest, CallFunctionWithUnloadFunction) {
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
// 卸载后可重新预加载,计数器重置
EXPECT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
@ -472,7 +472,7 @@ TEST_F(LuaCjApiTest, LoadFunctionOverflow) {
fclose(f);
int ret = loadfunction(runner, path.c_str(), "overflow");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_LOAD_FUNCTION_OVER);
EXPECT_EQ(get_errno(), LUAERR_FUNC_OVERFLOW);
}
// cleanup 后预加载函数应失效
@ -483,7 +483,168 @@ TEST_F(LuaCjApiTest, CallFunctionAfterCleanup) {
int ret = callfunction(runner, "add", NULL);
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), NAPI_FUNCTION_NOT_FOUND);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// ==================== typed interop 测试v0.2.2====================
// 整型参数直通Lua 收到 integer返回 integer+1仓颉侧读回 Int64
TEST_F(LuaCjApiTest, TypedCallInt) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
EXPECT_EQ(callfunction_int(runner, "inc", 41), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 42);
// 字符串形式同步可用(向后兼容)
EXPECT_STREQ(getresult(runner), "42");
}
// 浮点参数直通:返回 number*2.5,读回 Float64
TEST_F(LuaCjApiTest, TypedCallNum) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_mul.lua").c_str(), "mul"), 0);
EXPECT_EQ(callfunction_num(runner, "mul", 4.0), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 10.0);
EXPECT_STREQ(getresult(runner), "10.0");
}
// 布尔参数直通:返回逻辑非,读回 Bool
TEST_F(LuaCjApiTest, TypedCallBool) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_not.lua").c_str(), "lnot"), 0);
EXPECT_EQ(callfunction_bool(runner, "lnot", 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 0);
EXPECT_STREQ(getresult(runner), "false");
EXPECT_EQ(callfunction_bool(runner, "lnot", 0), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
EXPECT_STREQ(getresult(runner), "true");
}
// 无参调用 + 整数返回
TEST_F(LuaCjApiTest, TypedCallVoid) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "forty_two"), 0);
EXPECT_EQ(callfunction_void(runner, "forty_two"), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 42);
}
// 回声函数:验证各类型往返保真(类型不丢失)
TEST_F(LuaCjApiTest, TypedEchoRoundTrip) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_echo.lua").c_str(), "echo"), 0);
// int 往返
EXPECT_EQ(callfunction_int(runner, "echo", -123456789LL), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), -123456789LL);
// 大整数(超过 double 精确表示范围)往返
EXPECT_EQ(callfunction_int(runner, "echo", 9007199254740993LL), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 9007199254740993LL);
// num 往返
EXPECT_EQ(callfunction_num(runner, "echo", 3.14159), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 3.14159);
// bool 往返
EXPECT_EQ(callfunction_bool(runner, "echo", 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
// string 往返(走原 callfunction
EXPECT_EQ(callfunction(runner, "echo", "hello typed"), 0);
EXPECT_EQ(result_type(runner), CJT_STR);
EXPECT_STREQ(getresult(runner), "hello typed");
}
// nil 返回值识别
TEST_F(LuaCjApiTest, TypedNilResult) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_nil.lua").c_str(), "nilret"), 0);
EXPECT_EQ(callfunction_void(runner, "nilret"), 0);
EXPECT_EQ(result_type(runner), CJT_NIL);
EXPECT_EQ(result_int(runner), 0);
EXPECT_EQ(result_bool(runner), 0);
}
// typed 调用不存在的函数仍报 5023
TEST_F(LuaCjApiTest, TypedCallNotFound) {
EXPECT_EQ(callfunction_int(runner, "nonexistent", 1), -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
EXPECT_EQ(callfunction_void(runner, "nonexistent"), -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
// runScript 的 typed 变体args_echo.lua 直接返回入参
TEST_F(LuaCjApiTest, TypedRunVariants) {
std::string path = temp_dir + "/args_echo.lua";
FILE* f = fopen(path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "return (...)");
fclose(f);
// int
EXPECT_EQ(run_int(runner, path.c_str(), 777), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(result_int(runner), 777);
// num
EXPECT_EQ(run_num(runner, path.c_str(), 2.5), 0);
EXPECT_EQ(result_type(runner), CJT_NUM);
EXPECT_DOUBLE_EQ(result_num(runner), 2.5);
// bool
EXPECT_EQ(run_bool(runner, path.c_str(), 1), 0);
EXPECT_EQ(result_type(runner), CJT_BOOL);
EXPECT_EQ(result_bool(runner), 1);
// 无参无返回时栈顶为空reslt 应为 "nil"
std::string void_path = temp_dir + "/no_return.lua";
f = fopen(void_path.c_str(), "w");
ASSERT_NE(f, nullptr);
fprintf(f, "local x = 1");
fclose(f);
EXPECT_EQ(run_void(runner, void_path.c_str()), 0);
EXPECT_EQ(result_type(runner), CJT_NIL);
}
// typed 与字符串 API 混用互不干扰typed 结果同步到字符串缓冲区
TEST_F(LuaCjApiTest, TypedAndStringCoexist) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_add.lua").c_str(), "add"), 0);
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_add.lua").c_str(), "inc"), 0);
// 字符串 API
EXPECT_EQ(callfunction(runner, "add", "a"), 0);
EXPECT_STREQ(getresult(runner), "result: a (call #1)");
EXPECT_EQ(result_type(runner), CJT_STR);
// typed API
EXPECT_EQ(callfunction_int(runner, "inc", 100), 0);
EXPECT_EQ(result_int(runner), 101);
// 字符串函数的计数器不受影响
EXPECT_EQ(callfunction(runner, "add", "b"), 0);
EXPECT_STREQ(getresult(runner), "result: b (call #2)");
}
// cleanup 后 typed 状态重置为 NIL
TEST_F(LuaCjApiTest, TypedStateAfterCleanup) {
ASSERT_EQ(loadfunction(runner, get_script_path("func_typed_void.lua").c_str(), "v"), 0);
EXPECT_EQ(callfunction_void(runner, "v"), 0);
EXPECT_EQ(result_type(runner), CJT_INT);
EXPECT_EQ(cleanup(runner), 0);
int ret = callfunction_void(runner, "v");
EXPECT_EQ(ret, -1);
EXPECT_EQ(get_errno(), LUAERR_FUNCTION_NOT_FOUND);
}
int main(int argc, char **argv) {