跳到主要内容

ZLua 标准库(zlua)设计规范

本文档描述全局 zlua 表的 Lua API。源码位于:

Packages/com.code-philosophy.zlua/Resources/zlua/zlualib.lua

lua_State 初始化时由 native 嵌入执行(dostring / BuiltinScripts::LoadZLuaLib),与 globals.lua 一并加载。

相关文档:

文档内容
../type-system-spec.mdCSharp 类型访问、元表、数组、泛型方法
../method-overload-spec.mdsignature / get_method / register_method 语义
../marshal/index.md参数编组总览
../marshal/function.mdLua function ↔ C# delegate
../design-spec.md双运行时与总体架构

平台原则: zlua 的 Lua 封装在 Mono 与 Il2Cpp 上签名与语义一致;耗时逻辑在 native __zlua_* 回调中实现,Lua 层保持薄封装。


1. 职责边界

层级职责
CSharp 根表程序集 / 类型懒加载;静态成员、Type() 构造(见 ../type-system-spec.md
zlua类型构造辅助(泛型、数组)、typeof、重载显式绑定、常用类型常量
实例 userdata经类型表 __callnew_* 创建;成员经元表访问,不经 zlua

zlua 替代 CSharp 访问类型;二者配合使用。


2. 加载与全局对象

zlua = zlua or {} -- 全局表,由 zlualib.lua 初始化

Il2Cpp 将脚本嵌入 BuiltinScripts.cpp;Mono 从 Resources/zlua/zlualib.lua 读取。内容应保持一致(或通过构建同步)。


3. 类型实参(typeArg

多处 API 需要描述 C# 类型,统称 类型实参 typeArg,以下形式等价(native 统一解析):

形式示例
zlua.types.*zlua.types.int32
zlua.typeof(typeTable)zlua.typeof(CSharp.AC.Demo)
CSharp 类型表CSharp.mscorlib['System.Int32'](须已解析)

zlua.types.* 见 §4.2:预置常用 corlib 类型的类型全名(如 System.Int32),可直接作为 typeArg 传入。


4. 类型查询与常量

4.1 zlua.typeof

zlua.typeof(typeTable) → typeDescriptor
参数说明
typeTableCSharp 下的类型表(含 __fullname 等元数据)

返回该类型的 System.Type 等价物(Mono:System.Type;Il2Cpp:携带 Il2CppClass* 的类型描述对象)。供需要显式类型对象的场景使用;多数 API 也可直接传 typeArg

local t = zlua.typeof(CSharp.mscorlib['System.Int32'])
local demoType = zlua.typeof(CSharp.AC.Demo) -- 无 namespace 全局类型
local panelType = zlua.typeof(CSharp.AC['MyGame.UI.Panel']) -- 含 namespace

4.2 zlua.types

预置常用 corlib 类型全名,避免重复写长字符串:

zlua.types.int32 -- "System.Int32"
zlua.types.int64
zlua.types.single -- System.Single
zlua.types.double
zlua.types.boolean
zlua.types.string
zlua.types.object
zlua.types.void -- 若暴露;多用于签名场景
-- … 由 zlualib 或 native 初始化补全

可直接作为 typeArg 传入 signaturemake_generic_typemake_szarray_type 等。

命名: 旧称 zlua.corlibtypes 已废弃,统一为 zlua.types


5. 泛型类型

5.1 zlua.make_generic_type

zlua.make_generic_type(genericBaseType, typeArg1, typeArg2, ...) → typeTable
参数说明
genericBaseType未闭合泛型定义的类型表;含 namespace 时必须括号访问
genericParamType…泛型实参;typeArg 规则见 ../type-system-spec.md §2.4
local ListInt = zlua.make_generic_type(
CSharp.mscorlib['System.Collections.Generic.List'],
zlua.types.int32
)
local list = ListInt() -- 构造实例,见 TYPE_SYSTEM_SPEC §4.6
  • 返回闭合泛型的类型表;相同实参多次调用应 intern 为同一表。
  • 实参个数须与泛型定义一致,否则 luaL_error

Native: __zlua_make_generic_type


6. ref 变量(zlua.new_ref

创建 带类型的 StructUserData,作为 C# ref / out / in 形参的 真 ref 实参。规则见 ../marshal/index.md §3、../marshal/struct.md §6.2。

zlua.new_ref(ref_type [, value, ...]) → structUserdata
参数说明
ref_typetypeArg(§3):须为 值类型(基元、enum、struct);引用类型 → 报错
value可选。省略或 nildefault(T) 写入 payload
...struct / enum 时,额外参数同类型表 _ctor / __call 规则

返回值: T 的 StructUserData(挂接 IMT),payload 即 ref 目标存储;C# 经 ref T 修改后 原地 反映到该 userdata。

6.1 与 _ctor 的关系

创建方式传给 ref T
zlua.new_ref(T, ...)真 ref
T._ctor(...) / T(...)真 ref(同一 StructUserData 模型)

by-val 形参仍对 StructUserData 做 拷贝;仅 ref/out/in 绑定 payload 地址。

非 StructUserData 实参(如裸 5)传给 ref int拷贝语义,调用成功但 不回写 Lua 值(../marshal/index.md §3.1)。

6.2 示例

-- ref 基元(须 new_ref 才有真 ref;裸 number 为拷贝语义)
local n = zlua.new_ref(zlua.types.int32, 5)
CS.Demo.Increment(n)

-- ref struct:_ctor 即真 ref
local p = Point2D(1, 2)
CS.Demo.Offset(p, 10, 20)

-- ref struct:new_ref + 默认 / 构造
local q = zlua.new_ref(Point2D)
local r = zlua.new_ref(Point2D, 3, 4)

-- out
local out = zlua.new_ref(zlua.types.int32)
CS.Demo.TryParse("42", out)

6.3 读取 payload(可选辅助)

实现可提供 zlua.deref(refUserdata) 或约定经 IMT 字段访问;MVP 以 struct 实例字段 API 与 enum/基元 payload 读取为准。

Native: __zlua_new_ref

6.4 Opaque 升级(zlua.to_user_data

将 C#→Lua marshal 产生的 opaque lightuserdata 转为可长期持有、可成员访问的 userdata。规则见 ../marshal/index.md §4。

zlua.to_user_data(opaque) → structUserdata | classUserdata
说明
参数须为本次同步调用链内 C# Push 的 lightuserdataValidate 失败 → 报错
struct拷贝 到 StructUserData(与旧 handle ToUserData 相同)
class 槽Il2CppObject**,注册为 ClassUserData
限制opaque : / .不可 跨 pcall / 异步保存后再调用

Native: __zlua_to_user_data


7. 数组类型与实例

../type-system-spec.md §2.4–§2.6、§7 一致。

7.1 数组类型构造

zlua.make_szarray_type(typeArg) → szarrayTypeTable
zlua.make_mdarray_type(typeArg, rank) → mdarrayTypeTable
API说明
make_szarray_type单维 0 基向量数组 T[]elementType../type-system-spec.md §2.4
make_mdarray_typerank 维数组 T[,…]rank ≥ 1elementType../type-system-spec.md §2.4
local IntArray = zlua.make_szarray_type(zlua.types.int32)
local IntMatrix = zlua.make_mdarray_type(zlua.types.int32, 2)

7.2 数组实例创建

zlua.new_szarray_by_element_type(typeArg, length) → szarrayUserdata
zlua.new_szarray_by_szarray_type(szarrayTypeTable, length) → szarrayUserdata

zlua.new_mdarray_by_mdarray_type(mdarrayTypeTable, lowbounds, sizes) → mdarrayUserdata
zlua.new_mdarray_by_spec(typeArg, lowbounds, sizes) → mdarrayUserdata
参数说明
lengthszarray 长度,≥ 0
lowbounds长度为 rank 的 Lua 表,每维下界
sizes长度为 rank 的 Lua 表,每维元素个数

元素初始化为 default(T)。szarray 实例支持 #arr__len,等价 Length)。

7.3 szarray 转换

zlua.to_bytes

zlua.to_bytes(szarray) → string

szarray 实例的底层元素内存按顺序拷贝为 Lua 二进制字符串string 每字节对应一字节,可含 \0)。

约束说明
输入必须是 szarray userdata(不支持 mdarray)
元素类型blittable 基元boolbytesbytecharshortushortintuintlongulongfloatdouble
不支持stringobjectdecimal 及任意引用类型、非 blittable struct

布局:

  • 按 C# 数组下标 0 .. Length-1 顺序拼接;无额外长度头。
  • 多字节数值使用 平台原生字节序(Il2Cpp 目标平台通常为 little-endian),与 Buffer.BlockCopy / 内存逐元素布局一致。
  • bool1 字节 存储:0 / 非 0(与 Il2Cpp 布尔数组内存布局一致)。
local bytes = zlua.to_bytes(int_arr) -- #bytes == #int_arr * 4(int 为 4 字节)

元素类型不在白名单内时 luaL_error

Native: __zlua_to_bytes

zlua.to_table

zlua.to_table(szarray) → table

将 szarray 转为 等长 Lua 表;对元素类型无限制,每个元素按 ../marshal/index.md / ../marshal/class.md 规则转为 Lua 值。

约束说明
输入必须是 szarray userdata
输出长度n = #szarray;返回表在 1 .. n 上连续赋值
下标对应t[i] ↔ C# arr[i - 1](Lua 1 基 ↔ C# 0 基)
local t = zlua.to_table(obj_arr)
-- t[1] 对应 arr[0],t[#t] 对应 arr[Length-1]

引用类型元素转为 userdata;值类型 struct 按 class marshal 规则处理。

Native: __zlua_to_table


Native(§6.1–6.2): __zlua_make_szarray_type__zlua_new_szarray_* 等(名称以实现为准,语义对齐本文档)。


8. 泛型方法实参

仅用于方法自身带泛型参数的情形(如 void Foo<T>(T x)),见 ../type-system-spec.md §6。

8.1 zlua.make_generic_inst

zlua.make_generic_inst(typeArg1, typeArg2, ...) → genericInst

构造泛型方法调用所需的 generic_inst 句柄;native 校验类型参数个数与约束。

8.2 调用约定

local inst = zlua.make_generic_inst(zlua.types.int32)
Type.Foo(inst, value) -- 第一实参必须是 generic_inst

每个泛型方法在 native 维护 inflatedMapgeneric_inst 指纹 → 单态化后的 bridge closure。

每个泛型方法在 native 维护 inflatedMapgeneric_inst 指纹 → 单态化后的 bridge closure。


9. Delegate(可选显式 API)

默认行为: Lua 调用带 delegate 形参的 C# 方法时,直接传入 Lua function 即可,由 方法参数 marshal 隐式转换,与其它形参规则相同。详见 ../marshal/function.md §4.0

obj:RegisterCallback(function(v) print(v) end) -- 无需 to_delegate

8.1 zlua.to_delegate(可选)

仅在需要先构造 delegate、再传递或缓存时使用(非常规路径)。

zlua.to_delegate(func, delegateTypeTable) → delegateUserdata
local d = zlua.to_delegate(function(a) return a end, closedFuncIntIntType)
obj:RegisterCallback(d)
参数说明
funcLua function
delegateTypeTable已闭合的 delegate 类型表(无方法形参上下文时须显式指定)

Native: __zlua_to_delegate(可选,待实现)

C# delegate 传入 Lua 后可直接 d(...)IMT.__call),见 ../marshal/function.md §3。


10. 方法重载辅助

完整语义见 ../method-overload-spec.mdzlua 仅提供薄封装。

9.1 zlua.signature

zlua.signature([typeArg1, typeArg2, ...]) → paramSignature
  • 不包含 方法名;仅参数类型列表。
  • 无参:();有参:(System.Int32,…),使用 Type.FullName 规范格式。
local sig = zlua.signature(zlua.types.int32) -- "(System.Int32)"
local sig2 = zlua.signature(zlua.types.int32, zlua.types.string)

禁止将返回值用作 obj[sig] 查表键;应配合 get_method 或模块级缓存。

Native: __zlua_create_signature(Lua 层不再暴露 create_signature 公共 API;若存在则视为 signature 别名并标记废弃)。

9.2 zlua.get_method

zlua.get_method(target, methodName, signature, is_static) → closure
参数说明
target实例 userdata 或类型表;用于解析声明类型
methodNameC# 方法名
signaturezlua.signature(...) 返回值
is_statictrue 查静态域;false 查实例域
local demo = CSharp.AC.Demo()
local run_i32 = zlua.get_method(demo, "Run", zlua.signature(zlua.types.int32), false)
run_i32(demo, 10)

local add = zlua.get_method(CSharp.AC.Demo, "Add",
zlua.signature(zlua.types.int32, zlua.types.int32), true)
add(3, 5)
  • 实例方法:点号调用并传入 self;静态方法:直接 closure(...)
  • is_static == truetarget 可为实例,仅用于解析类型(见 ../type-system-spec.md §3.2)。

Native: __zlua_get_method(待实现)

9.3 zlua.register_method

zlua.register_method(static_class_mt_or_obj, aliasName, methodOrClosure) → void

将可调用 closure 注册为 methodTable 上的别名键,使 obj:aliasName(...)TypeTable.aliasName(...) 可直接分派。

local Demo = CSharp.AC.Demo
local demo = Demo()

-- 实例方法别名:第一个参数传对象实例
local run_i32 = zlua.get_method(demo, "Run", zlua.signature(zlua.types.int32), false)
zlua.register_method(demo, "run_i32", run_i32)
demo:run_i32(20)

-- 静态方法别名:第一个参数传类型表(静态类元表)
local add = zlua.get_method(Demo, "Add",
zlua.signature(zlua.types.int32, zlua.types.int32), true)
zlua.register_method(Demo, "add_i32", add)
assert.equal(Demo.add_i32(3, 5), 8)
参数说明
static_class_mt_or_obj类型表(静态类元表)或 C# 对象实例 userdata(见下)
aliasName写入 methodTable 的 Lua 键名
methodOrClosurezlua.get_method 返回值,或语义兼容的可调用 closure

第一个参数 static_class_mt_or_obj

传入值写入目标
类型表该类型静态绑定methodTableSMT.__index 的 upvalue)。例如 CSharp['Assembly-CSharp'].MyFoozlua.make_generic_type(...) 的返回值
对象实例 userdata该实例所属类型的 实例元表 IMT 所绑定的 methodTable
  • 静态方法别名须传类型表;实例方法别名须传对象实例(或等价的实例 userdata)。
  • methodTable 为注册期构建的内部表,见 ../meta-table-spec.md §3.1;不得直接 rawset 到类型表根键。

冲突与错误:

  • 注册时若目标 methodTable已存在同名键(值为非 nil),立即报错luaL_error),不覆盖已有条目。
  • aliasName 另须满足 ../method-overload-spec.md §5.1(不得与非别名默认 C# 方法名重复等)。

Native: __zlua_register_method(待实现)


11. 与 CSharp 的配合示例

CSharp.AC = CSharp['Assembly-CSharp']

-- 类型解析(TYPE_SYSTEM_SPEC §2.2)
local Demo = CSharp.AC.Demo -- 无 namespace
local Panel = CSharp.AC['MyGame.UI.Panel'] -- 有 namespace:必须括号

-- typeof / types
local demo = Demo()
local sig = zlua.signature(zlua.types.int32)

-- 泛型
local ListInt = zlua.make_generic_type(
CSharp.mscorlib['System.Collections.Generic.List'],
zlua.types.int32
)

-- 数组
local IntArray = zlua.make_szarray_type(zlua.types.int32)
local arr = zlua.new_szarray_by_szarray_type(IntArray, 4)
print(#arr)
local bytes = zlua.to_bytes(arr)
local t = zlua.to_table(arr)

-- 重载
local run_i32 = zlua.get_method(demo, "Run", sig, false)
zlua.register_method(demo, "run_i32", run_i32)

12. Native 回调一览

Lua APINative 回调状态(参考)
zlua.typeof__zlua_typeofMono / Il2Cpp MVP
zlua.signature__zlua_create_signatureMono(待对齐:仅 typeArg)
zlua.make_generic_type__zlua_make_generic_typeMono;Il2Cpp 占位
zlua.new_ref__zlua_new_ref待实现
zlua.to_user_data__zlua_to_user_data待实现
zlua.make_szarray_type__zlua_make_szarray_type待实现
zlua.make_mdarray_type__zlua_make_mdarray_type待实现
zlua.new_szarray_*__zlua_new_szarray_*待实现
zlua.to_bytes__zlua_to_bytes待实现
zlua.to_table__zlua_to_table待实现
zlua.new_mdarray_*__zlua_new_mdarray_*待实现
zlua.make_generic_inst__zlua_make_generic_inst待实现
zlua.to_delegate__zlua_to_delegate可选,待实现
zlua.get_method__zlua_get_method待实现
zlua.register_method__zlua_register_method待实现

新增 native 回调在 LuaInteropManager::RegisterZLuaApi(Il2Cpp)/ LuaManagerObject(Mono)注册;Lua 侧仅 function zlua.xxx(...) return __zlua_xxx(...) end


13. zlualib.lua 目标骨架

zlua = zlua or {}

function zlua.typeof(typeTable)
return __zlua_typeof(typeTable)
end

function zlua.signature(...)
return __zlua_create_signature(...)
end

function zlua.make_generic_type(genericBase, ...)
return __zlua_make_generic_type(genericBase, ...)
end

function zlua.new_ref(ref_type, ...)
return __zlua_new_ref(ref_type, ...)
end

function zlua.to_user_data(opaque)
return __zlua_to_user_data(opaque)
end

function zlua.make_szarray_type(elementType)
return __zlua_make_szarray_type(elementType)
end

function zlua.make_mdarray_type(elementType, rank)
return __zlua_make_mdarray_type(elementType, rank)
end

function zlua.new_szarray_by_element_type(elementType, length)
return __zlua_new_szarray_by_element_type(elementType, length)
end

function zlua.new_szarray_by_szarray_type(szarrayType, length)
return __zlua_new_szarray_by_szarray_type(szarrayType, length)
end

function zlua.to_bytes(szarray)
return __zlua_to_bytes(szarray)
end

function zlua.to_table(szarray)
return __zlua_to_table(szarray)
end

function zlua.new_mdarray_by_mdarray_type(mdarrayType, lowbounds, sizes)
return __zlua_new_mdarray_by_mdarray_type(mdarrayType, lowbounds, sizes)
end

function zlua.new_mdarray_by_spec(elementType, lowbounds, sizes)
return __zlua_new_mdarray_by_spec(elementType, lowbounds, sizes)
end

function zlua.make_generic_inst(...)
return __zlua_make_generic_inst(...)
end

function zlua.to_delegate(func, delegateType)
return __zlua_to_delegate(func, delegateType)
end

function zlua.get_method(target, methodName, signature, is_static)
return __zlua_get_method(target, methodName, signature, is_static)
end

function zlua.register_method(static_class_mt_or_obj, aliasName, methodOrClosure)
return __zlua_register_method(static_class_mt_or_obj, aliasName, methodOrClosure)
end

zlua.types = zlua.types or {
void = "System.Void",
boolean = "System.Boolean",
int32 = "System.Int32",
int64 = "System.Int64",
single = "System.Single",
double = "System.Double",
string = "System.String",
object = "System.Object",
}

当前仓库内 zlualib.lua 仍为早期原型(含 create_signature(methodName, …)get_method 两参数等),以实现清单为准逐步对齐本规范


14. 实现清单

  • zlua.new_ref / __zlua_new_ref../marshal/index.md §3)
  • zlua.to_user_data / __zlua_to_user_data../marshal/index.md §4)
  • zlua.types 初始化与 corlibtypes 迁移
  • signature 仅接收 typeArg,去掉 methodName
  • get_method(target, methodName, signature, is_static) native 实现
  • register_method(static_class_mt_or_obj, aliasName, methodOrClosure) native 实现
  • 数组 make_* / new_* 全套 API
  • to_bytes / to_table(szarray)
  • Marshaling::ReadDelegate 隐式 marshal(../marshal/function.md §4.0,必做
  • (可选)to_delegate 显式 API
  • make_generic_inst + 泛型方法 inflatedMap
  • Mono / Il2Cpp zlualib.lua 内容同步(嵌入 vs Resources)
  • ../type-system-spec.md §2.2 命名空间括号规则在类型解析回调中强制执行