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

@ -20,6 +20,31 @@ foreign func loadfunction(selfd: CPointer<Unit>,path: CString, name: CString): I
foreign func unloadfunction(selfd: CPointer<Unit>,name: CString): Int32
@C
foreign func callfunction(selfd: CPointer<Unit>,name: CString, arg: CString): Int32
// typed interopv0.2.2):原始类型直通映射
@C
foreign func callfunction_int(selfd: CPointer<Unit>, name: CString, val: Int64): Int32
@C
foreign func callfunction_num(selfd: CPointer<Unit>, name: CString, val: Float64): Int32
@C
foreign func callfunction_bool(selfd: CPointer<Unit>, name: CString, val: Int32): Int32
@C
foreign func callfunction_void(selfd: CPointer<Unit>, name: CString): Int32
@C
foreign func run_int(selfd: CPointer<Unit>, path: CString, val: Int64): Int32
@C
foreign func run_num(selfd: CPointer<Unit>, path: CString, val: Float64): Int32
@C
foreign func run_bool(selfd: CPointer<Unit>, path: CString, val: Int32): Int32
@C
foreign func run_void(selfd: CPointer<Unit>, path: CString): Int32
@C
foreign func result_type(selfd: CPointer<Unit>): Int32
@C
foreign func result_int(selfd: CPointer<Unit>): Int64
@C
foreign func result_num(selfd: CPointer<Unit>): Float64
@C
foreign func result_bool(selfd: CPointer<Unit>): Int32
@C
foreign func cleanup(selfd: CPointer<Unit>): Int32
@C
@ -78,31 +103,22 @@ public class LuaError <: Exception {
/// 根据错误码获取错误描述
public static func getErrorMessage(code: Int32): String {
match (code) {
case 5001 => "JSON file load failed"
case 5002 => "Memory allocation failed"
case 5003 => "File load error (path, permission, or compile-time syntax issue)"
case 5004 => "Script runner initialization failed"
case 5005 => "Lua state machine initialization failed or corrupted"
case 5006 => "JSON parse error (format issue)"
case 5007 => "JSON format error (does not match MCP format)"
case 5008 => "Missing argument in JSON"
case 5009 => "Lua stack space insufficient"
case 5010 => "Script internal error, check result for details"
case 5011 => "Script return value error"
case 5012 => "Library not loaded before unload"
case 5013 => "Function call violates single-input-single-output convention"
case 5014 => "Stack size below minimum during function call"
case 5015 => "Lua state machine initialization error"
case 5016 => "Too many libraries loaded"
case 5017 => "Lua runner object lost"
case 5018 => "Missing redirect file path"
case 5019 => "Reset input file failed, may cause input pollution"
case 5020 => "Error in callback functions"
case 5021 => "change workdir error"
case 5022 => "No callable chunk found"
case 5023 => "Function not found"
case 5024 => "Too many preloaded functions"
case 5025 => "Preloaded file must return a function"
case 5001 => "File load error (path, permission, or compile-time syntax issue)"
case 5002 => "Lua state machine initialization failed or corrupted"
case 5003 => "Script internal error, check result for details"
case 5004 => "Script return value type not supported"
case 5005 => "Library not loaded before unload"
case 5006 => "Stack layout mismatch during function call"
case 5007 => "Lua state machine initialization error"
case 5008 => "Too many libraries loaded"
case 5009 => "Lua runner object lost"
case 5010 => "Reset input file failed, may cause input pollution"
case 5011 => "Error in callback functions (including doString syntax error)"
case 5012 => "No callable chunk found"
case 5013 => "Function not found"
case 5014 => "Too many preloaded functions"
case 5015 => "Preloaded file must return a function"
case 5016 => "Result type not supported for requested conversion"
case _ => "Unknown error"
}
}
@ -315,6 +331,109 @@ public class LuaRunner {
return this.cachedResult
}
// ==================== typed interopv0.2.2====================
// 原始类型直通Int64/Float64/Bool 与 Lua integer/number/boolean 直接互转,
// 不经过字符串序列化。结果类型严格校验,不匹配抛 5016。
// 结果类型常量resultType 返回值0=nil 1=bool 2=int 3=num 4=str
/// 获取最近一次调用的结果类型0=nil 1=bool 2=int 3=num 4=str
public func resultType(): Int32 {
unsafe { result_type(this.handle) }
}
/// 调用预加载函数,传 Int64 参数Lua integer
///
/// @param name loadFunction 时指定的函数名称
/// @param val 整数参数
/// @return 函数返回的整数(结果为 Lua number 时自动截断转换)
/// @throws LuaError 函数不存在/执行出错时抛出对应错误码;
/// 结果不是数值类型nil/string等时抛 5016
public func callFunctionInt(name: String, val: Int64): Int64 {
let namePtr = toCStr(name)
let code = unsafe { callfunction_int(this.handle, namePtr, val) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) } // 期望 int/num
unsafe { result_int(this.handle) }
}
/// 调用预加载函数,传 Float64 参数Lua number
///
/// @throws LuaError 同 callFunctionInt结果不是数值类型时抛 5016
public func callFunctionNum(name: String, val: Float64): Float64 {
let namePtr = toCStr(name)
let code = unsafe { callfunction_num(this.handle, namePtr, val) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) } // 期望 int/num
unsafe { result_num(this.handle) }
}
/// 调用预加载函数,传 Bool 参数Lua boolean
///
/// @throws LuaError 同 callFunctionInt结果为 nil 或 string 时抛 5016
public func callFunctionBool(name: String, val: Bool): Bool {
let namePtr = toCStr(name)
let vi = if (val) { 1i32 } else { 0i32 }
let code = unsafe { callfunction_bool(this.handle, namePtr, vi) }
unsafe { if (!namePtr.isNull()) { LibC.free(namePtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt == 0 || rt == 4) { throw LuaError(5016) } // 拒绝 nil/string
unsafe { result_bool(this.handle) == 1 }
}
/// 运行脚本,传 Int64 参数(独立调用,非管道模式)
///
/// 与 runScript 不同typed 变体执行后弹出返回值、保持虚拟栈清洁;
/// 管道模式请继续使用字符串版 runScript。
///
/// @throws LuaError 同 callFunctionInt结果不是数值类型时抛 5016
public func runScriptInt(path: String, val: Int64): Int64 {
let pathPtr = toCStr(path)
let code = unsafe { run_int(this.handle, pathPtr, val) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) }
unsafe { result_int(this.handle) }
}
/// 运行脚本,传 Float64 参数(独立调用,非管道模式)
///
/// @throws LuaError 同 runScriptInt结果不是数值类型时抛 5016
public func runScriptNum(path: String, val: Float64): Float64 {
let pathPtr = toCStr(path)
let code = unsafe { run_num(this.handle, pathPtr, val) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt != 2 && rt != 3) { throw LuaError(5016) }
unsafe { result_num(this.handle) }
}
/// 运行脚本,传 Bool 参数(独立调用,非管道模式)
///
/// @throws LuaError 同 runScriptInt结果为 nil 或 string 时抛 5016
public func runScriptBool(path: String, val: Bool): Bool {
let pathPtr = toCStr(path)
let vi = if (val) { 1i32 } else { 0i32 }
let code = unsafe { run_bool(this.handle, pathPtr, vi) }
unsafe { if (!pathPtr.isNull()) { LibC.free(pathPtr) } }
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
let rt = unsafe { result_type(this.handle) }
if (rt == 0 || rt == 4) { throw LuaError(5016) }
unsafe { result_bool(this.handle) == 1 }
}
/// 获取缓存的结果
public func result(): String { this.cachedResult }