添加单元测试,修复部分错误

This commit is contained in:
2026-03-21 11:13:13 +08:00
parent a5eb303c4b
commit a4196e53f9
14 changed files with 555 additions and 4246 deletions

View File

@ -1,7 +1,5 @@
package LuaCangjie_api
import std.collection.*
// ==================== FFI底层 ====================
type IOCallback = CFunc<() -> Int32>
@ -31,35 +29,76 @@ func defaultIO(): Int32 {
0
}
// 生成NULL指针
private func nullCString(): CString {
unsafe { CString(CPointer<UInt8>()) }
}
private func toCStr(s: String): CString{
// StringCString空字符串转为NULL
private func toCStr(s: String): CString {
if (s.isEmpty()) {
var tmp = CString(CPointer<UInt8>());
return tmp
}
else {
nullCString()
} else {
// LibC.mallocCString 返回 Option<CString>,需要解包
unsafe { LibC.mallocCString(s) }
}
}
// CString转String:通过 CPointer<UInt8> 构造 CString 后调用 toString()
// CString转String
private func toString(ptr: CString): String {
if (ptr.isNull()) { return "" }
// 将 CPointer<Int8> 强制转换为 CPointer<UInt8>
// 通过 CPointer<UInt8> 构造 CString
return ptr.toString()
ptr.toString()
}
// ==================== 异常 ====================
public class LuaError <: Exception {
public let code: Int32
public init(code: Int32, msg: String) { super(msg); this.code = code }
public init(code: Int32, msg: String) {
super(msg)
this.code = code
}
// 便捷构造函数:只传入错误码,自动获取描述
public init(code: Int32) {
super(getErrorMessage(code))
this.code = code
}
// 根据错误码获取错误描述
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 (permission or path 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 _ => "Unknown error"
}
}
// 获取当前错误的描述
public func getMessage(): String {
getErrorMessage(this.code)
}
}
// ==================== 主类 ====================
public class LuaRunner {
@ -67,23 +106,29 @@ public class LuaRunner {
private var cachedResult: String = ""
private var ioInput: IOCallback
private var ioOutput: IOCallback
//pkgpath 重定向lua状态机包搜索路径
//pathio 重定向io输出到pathio路径
//@param output 类型function 用于lua内部触发输出时调用的回调函数
//@param input 类型function 用于lua内部触发输入时的调用的回调函数
//@brief 构造lua状态机
public init( input!: IOCallback = defaultIO, output!: IOCallback = defaultIO,pathio!: String='' , pkgpath!: String='') {
// pkgpath 重定向lua状态机包搜索路径
// pathio 重定向io输出到pathio路径下
// @param output 类型function 用于lua内部触发输出时调用的回调函数
// @param input 类型function 用于lua内部触发输入时的调用的回调函数
// @brief 构造lua状态机
public init(input!: IOCallback = defaultIO, output!: IOCallback = defaultIO, pathio!: String = "", pkgpath!: String = "") {
this.ioInput = input
this.ioOutput = output
var tmpio = toCStr(pathio);
var tmppkg = toCStr(pkgpath);
let tmpio = toCStr(pathio)
let tmppkg = toCStr(pkgpath)
this.handle = unsafe { init_lua_runner(tmpio,tmppkg, input, output) }
unsafe{
LibC.free(tmpio);
LibC.free(tmppkg);
this.handle = unsafe { init_lua_runner(tmpio, tmppkg, input, output) }
// 释放非NULL的C字符串内存
unsafe {
if (!tmpio.isNull()) { LibC.free(tmpio) }
if (!tmppkg.isNull()) { LibC.free(tmppkg) }
}
if (this.handle.isNull()) {
throw LuaError(-1, "LuaRunner initialization failed: handle is null")
}
if (this.handle.isNull()) { throw LuaError(-1, "Init failed") }
}
~init() {
@ -94,25 +139,53 @@ public class LuaRunner {
}
public func load(path: String, name: String): This {
let result = unsafe { load_lib(this.handle, toCStr(path), toCStr(name)) }
let pathPtr = toCStr(path)
let namePtr = toCStr(name)
let result = unsafe { load_lib(this.handle, pathPtr, namePtr) }
// 释放临时分配的C字符串
unsafe {
if (!pathPtr.isNull()) { LibC.free(pathPtr) }
if (!namePtr.isNull()) { LibC.free(namePtr) }
}
if (result != 0) {
throw LuaError(unsafe { get_errno() }, toString(unsafe { getresult(this.handle) }))
let errMsg = toString(unsafe { getresult(this.handle) })
throw LuaError(unsafe { get_errno() }, errMsg)
}
return this
}
public func unload(name: String): This {
let result = unsafe { unload_lib(this.handle, toCStr(name)) }
let namePtr = toCStr(name)
let result = unsafe { unload_lib(this.handle, namePtr) }
unsafe {
if (!namePtr.isNull()) { LibC.free(namePtr) }
}
if (result != 0) {
throw LuaError(unsafe { get_errno() }, toString(unsafe { getresult(this.handle) }))
let errMsg = toString(unsafe { getresult(this.handle) })
throw LuaError(unsafe { get_errno() }, errMsg)
}
return this
}
public func runScript(path: String, arg: String): String {
let code = unsafe { run(this.handle, toCStr(path), toCStr(arg)) }
let pathPtr = toCStr(path)
let argPtr = toCStr(arg)
let code = unsafe { run(this.handle, pathPtr, argPtr) }
unsafe {
if (!pathPtr.isNull()) { LibC.free(pathPtr) }
if (!argPtr.isNull()) { LibC.free(argPtr) }
}
this.cachedResult = toString(unsafe { getresult(this.handle) })
if (code != 0) { throw LuaError(unsafe { get_errno() }, this.cachedResult) }
if (code != 0) {
throw LuaError(unsafe { get_errno() }, this.cachedResult)
}
return this.cachedResult
}
@ -122,7 +195,9 @@ public class LuaRunner {
public func clear(): This {
let result = unsafe { cleanup(this.handle) }
if (result != 0) { throw LuaError(unsafe { get_errno() }, "Cleanup failed") }
if (result != 0) {
throw LuaError(unsafe { get_errno() }, "Cleanup failed")
}
return this
}
}

View File

@ -1,16 +1,10 @@
package LuaCangjie_api
import std.console.*
// ==================== @C 修饰的回调函数 ====================
// ==================== main ====================
main(): Int64 {
try {
// 创建 LuaRunner 实例,使用 @C 函数作为回调
let runner = LuaRunner()
let runner = LuaRunner(pathio:"test")
println("LuaRunner initialized successfully")