跳到主要内容

00 — 总览

ZLua 产品目标、双运行时架构、文档地图与初始化流程。
术语见 GLOSSARY.md


1. 产品目标

1.1 使用方式

ZLua 在概念上对齐 P/Invoke、MonoPInvokeCallbackMarshalAs

概念ZLua 对应
P/InvokeL/Invoke — Lua 与 C# 互调
MarshalAs[LuaMarshalAs] — 参数 / 返回值编组
C# 回调[LuaInvoke] — C# 调 Lua

统一交互模型:

  • C#→Lua:标记 [LuaInvoke]static extern 方法;Editor 下由 Weaver 注入桥接,Player 下为 InternalCall → native stub。
  • Lua→C#:类型 懒注册;首次访问 CSharp[assembly][typeFullName] 时绑定成员。静态成员经类型表,实例成员经 obj:Method(),语义贴近 C#。
  • 代码生成:交互桥接在 Editor 生成(Mono:Expression Emit;Il2Cpp:C++ stub + 元数据),对业务开发者透明。

深度集成: 宿主启动时初始化 CLR 与 lua_State,加载 zlua 标准库与 CSharp 根表。

1.2 Player 发布优化(Il2Cpp)

优化说明
Native 桥接热路径为 C++,不经 LuaDLL extern 逐层跳转
Stub 复用相同 ReducedType 签名复用桥接函数,非「每成员一个独立 C 函数」
字段 / 属性Il2Cpp 可走偏移 + methodPointer 直接访问
托管对象userdata 记录对象指针;ObjectRegistry 槽位注册为 GC root,Lua 释放 userdata 后解除

Mono(Editor)允许反射 / Emit 慢路径,但 Lua 可见语义必须与 Il2Cpp 一致

1.3 明确不支持

规范行为
Event 专用元表 { get, set, fire };脚本使用 add_EventName / remove_EventName(与普通方法相同)
__index miss返回 nil
__newindex misserror
实例继承运行时查找;继承成员在 Bind 期扁平化 到当前类型三表(见 02-TYPE-SYSTEM.md §5)

2. 双运行时架构

LuaAppDomain.Initialize(moduleLoader)

┌───────────────┴───────────────┐
▼ ▼
ZLua.Mono (Editor) ZLua.Il2Cpp (Player)
LuaMonoAppDomain LuaIl2CppAppDomain
│ │
三表 Lua indexer / Emit 桥 libil2cpp/zlua (C++)
│ │
└───────────────┬───────────────┘

同一 Lua 可见语义 (spec/**)
MonoIl2Cpp
程序集ZLua.MonoZLua.Il2Cpp(薄 InternalCall 壳)
互操作实现C# + Lua indexerlibil2cpp/zlua/**
桥接每 public 成员 Expression EmitReducedType stub + 生成元数据
Indexer三表 Lua closurenative Dispatch* + MetaBinding / TypeRegistry
共享定义ZLua.CommonLuaInvokeAttributeLuaMarshalAsAttributeLuaAliasAttributeLuaAppDomain同左

Il2Cpp 源码布局(Unity 构建自动编译):

  • libil2cpp/lua — Lua 5.4 源码
  • libil2cpp/zlua — ZLua native 实现

权威参考路径:build-win64/Il2CppOutputProject/IL2CPP/libil2cpp/zlua(包内 ZLua~/libil2cpp-2022 为手动同步副本)。


3. 文档地图

Docs/
├── GLOSSARY.md 术语表
├── spec/
│ ├── 00-OVERVIEW.md ← 本文件
│ ├── 01-HOST-API.md LuaAppDomain、[LuaInvoke]、Weaver
│ ├── 02-TYPE-SYSTEM.md CSharp、类型表、构造、数组
│ ├── 04-METHOD-OVERLOAD.md dispatch、别名、签名
│ ├── 05-LIB.md zlua.* API
│ ├── 10-LIFETIME.md Registry、GC、异常边界
│ ├── metatable/ __index、三表、布局
│ └── marshal/ Push/Pop、[LuaMarshalAs]
├── impl/ 实现说明(不改变 Lua 语义)
├── guides/ 测试、迁移
└── compare/ 与 xLua / toLua / SLua 对比

阅读顺序建议:

  1. 本文件 → 01-HOST-API.md(宿主集成)
  2. 02-TYPE-SYSTEM.md + metatable/README.md(Lua 如何访问 C#)
  3. marshal/README.md(参数如何传递)
  4. 04-METHOD-OVERLOAD.md + 05-LIB.md(重载与标准库)
  5. 10-LIFETIME.md(内存与 GC)

冲突裁决: spec/** > Il2Cpp 源码 > impl/**


4. 初始化流程

4.1 C# 入口

LuaAppDomain.Initialize(moduleName => {
// 返回模块源码 string,或 byte[] 等 loader 约定类型
return LoadLuaModule(moduleName);
});

LuaAppDomainApplication.isEditor 解析后端:

  • Editor → ZLua.LuaMonoAppDomain.Initialize
  • Player → ZLua.LuaIl2CppAppDomain.Initialize → native InitializeInternal

初始化完成后注册 LuaFramePump,在 Unity 帧回调中处理 pending ref 释放等 housekeeping。

4.2 Native / Mono 侧(概念顺序)

步骤动作
1创建主 lua_State单状态模型,见 10-LIFETIME.md
2打开标准库;执行 zlualib.luaZLuaLib::RegisterGlobals 注册 __zlua_*
3初始化 Registry:ObjectRegistryTypeRegistry、Opaque scope 等
4创建全局 CSharp 根表(程序集 / 类型懒加载 __index
5安装模块 loader(__zlua_load_module searcher)
6可选:执行 globals.lua 等项目脚本

4.3 首次类型访问

CSharp.__index(assemblyName)
→ 创建程序集表,rawset 缓存

assembly.__index(typeFullName)
→ CLR 解析 Type,EnsureBinding
→ 构建 SMT / IMT、三表、dispatch
→ PushTypeTable,rawset 缓存

之后 Lua 侧通过类型表 / userdata 元表访问成员,无需 [MonoLuaCallback] 或手动 Export。

4.4 关闭

宿主销毁或域卸载时:

  1. 排空 pending Lua ref 释放队列
  2. ObjectRegistry::Shutdown、Struct registry shutdown
  3. 关闭 lua_State

顺序细节见 10-LIFETIME.mdimpl/IL2CPP.md


5. 与其它文档的边界

主题所在文档
__index / 三表 / miss 语义metatable/
Push / Pop / ref / Opaquemarshal/
zlua.make_* / register_method05-LIB.md
[LuaInvoke] Weaver 规则01-HOST-API.md
ObjectRegistry / GC root10-LIFETIME.md

6. 示例:最小脚本

-- 程序集别名(可选)
CSharp.AC = CSharp['Assembly-CSharp']

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

demo:SetX(10)
print(demo:GetX())

-- 显式重载别名(见 04-METHOD-OVERLOAD)
local run = demo.run_i32 -- [LuaAlias] 或 register_method
run(demo, 42)

C# 侧:

[LuaInvoke("main", "OnStart")]
public static extern void OnStart();

public event Action<int> ValueChanged;
// Lua: demo:add_ValueChanged(function(v) ... end)
// demo:remove_ValueChanged(handler)