跳到主要内容
文档站副本

本页为语义契约的发布副本;请在上游 ZTSTest/Docs/spec 修改后执行 npm run sync-spec。(源:marshal\09-FUNCTION.md

Delegate / 函数 Marshal

规范性: C# Delegate 与 JavaScript function 之间的双向 Marshal。 相关: ByObj → 06-CLASS.mdGetFunction../01-HOST-API.md;byref → 03-BYREF.md04-OPAQUE.mdto_delegate../05-LIB.md

平台原则: Mono 与 Il2Cpp JS 可见语义一致;Il2Cpp 构建期 C++ bridge;Mono Expression Emit。

无 Event 专用 Marshal;用 add_* / remove_*../02-TYPE-SYSTEM.md §4.5)。


1. 问题与目标

方向需求
C# delegate → JS默认 Delegate exotic;若 targetJsMethod(JS 回调源)→ Push 原 JS function
JS function → C# delegate带 delegate 形参的 C# 方法 隐式 marshal
目标说明
无感JS 传 function,marshal 层转换
性能Il2Cpp 零反射;Mono 按 Invoke 签名缓存 Emit
统一C#→JS 经 GetFunction / delegate bridge 共用 JS_Call + marshal 规则
安全funcRef 与 delegate 生命周期绑定
可控未覆盖签名 → 明确 throw(Mono 禁止 静默 Method.Invoke 热路径)

2. 总体架构

flowchart TB
subgraph CSharpToJs["C# delegate → JS"]
D1[C# MulticastDelegate]
CHK{target 是否为 JsMethod?}
UD1[Delegate exotic + [[Call]]]
LF0[registry funcRef → JS function]
D1 --> CHK
CHK -->|否| UD1
CHK -->|是| LF0
end

subgraph JsToCSharp["JS function → C# delegate"]
LF1[JS function]
REF1[JS 侧 ref / 内部持有]
JM1[JsMethod target]
BR[Delegate bridge]
DEL[C# Delegate]
LF1 --> REF1 --> JM1 --> DEL
BR --> DEL
DEL --> BR --> LF1
end
组件职责
JsMethodJS→C# delegate 的 closed target;持有 funcRef
DelegateBridges(Il2Cpp)构建期 C++ bridge,按 Invoke 签名
DynamicBridgeFactory(Mono)运行时 Expression 编译,按签名缓存
JsDelegateBinderJsMethod + bridge → delegate
ReadDelegateJS callable → delegate
JsCallInvokerfuncRef + push + JS_Call + pop;与 GetFunction 共用

3. C# delegate → JavaScript

3.1 分流规则

判定C# → JS 形态
targetJsMethodJS functionfuncRef 解引用)
其它Delegate exotic(ByObj + [[Call]]
PushDelegate(d):
if d == null → push null; return
if IsJsBoundDelegate(d):
PushRef(d.JsMethod.funcRef) // → JS function
return
PushByObjExotic(d)
规则
多播仅当整条 invocation list 可还原为 单一 JS 源时走 function;否则 exotic
往返JS function → C# → 再 Push → 同一 function
[TsMarshalAs(Object)] 覆盖分流
nullnull

3.2 Delegate exotic 调用

推荐 d(a, b)[[Call]]Invoke)。

写法适用
d(a, b)function 与 Delegate exotic
d.Invoke(a, b) exotic;对 function 无效
obj.RegisterCallback((v) => console.log(v));
const f = obj.Callback;
console.assert(typeof f === "function");
f(1);

3.3 [[Call]] 语义(Delegate exotic)

  • 保持 C# 多播语义
  • null 调用 → throw
  • 开放 delegatetarget == null):MVP 不支持

4. JavaScript function → C# delegate

4.1 隐式 marshal(默认)

obj.RegisterCallback((v) => console.log(v));
1. 解析形参类型 delegateType
2. 栈上为 JS callable(或 null → null delegate)
3. ReadDelegate(ctx, arg, delegateType)
→ 持有 funcRef
→ JsDelegateBinder.Create(delegateType, funcRef)
4. Invoke C# 方法
5. delegate 未被长期持有 → GC 回收 JsMethod 并释放 ref(§6)
JS 实参C# delegate
functionJsDelegateBinder.Create(...)
nullnull
undefined必选 → throw;可空 delegate → null(与 01-OVERVIEW.md §2 一致)
Delegate exotic直接传递
其它throw

4.2 JsMethod + closed delegate

字段
targetJsMethod
入口平台 bridge

4.3 Il2Cpp:DelegateBridges

按每种 Invoke 签名生成 C++ 入口:JS_Call + marshal。

  • ref/out/in(bridge C#→JS):默认 OpaqueValue
  • [TsMarshalAs] 非默认:与普通方法同一解析
  • 未注册签名 → throw + Codegen 提示

4.4 Mono:Expression Emit

  • 首次遇签名 emit;缓存
  • 无法 marshal → NotSupportedException
  • ref/out/in支持
  • 禁止 DynamicMethod + Delegate.CreateDelegate(Unity Mono SIGSEGV 风险)

4.5 显式 zts.to_delegate

const d = zts.to_delegate((a) => a, FuncIntIntType);
obj.RegisterCallback(d);

同一 JsDelegateBinder.Create;返回 Delegate exotic


5. GetFunction 与 delegate bridge

GetFunction<T> closed delegate其它 C#→JS closed delegate
方向C# → JSC# → JS
绑定import 模块 + 命名导出 + marshal 为 T隐式 marshal / to_delegate
调用T.InvokeJS_Call / JS_Eval同左
ref/out/in(C#→JS)默认 OpaqueValue同左
params不支持不支持
缓存调用方负责持有方决定

Reset 后旧 delegate 作废../01-HOST-API.md §1.2)。

不属于 C#→JS bridge: JS 调 C# 时 delegate 形参隐式 marshal → MethodBridge → ReadDelegate


6. 生命周期与 GC

事件行为
隐式 marshal / to_delegate登记 funcRef;delegate 持有 JsMethod
delegate 被 C# GCJsMethod 终结 → 排队释放 ref
JS function 无其它引用ref 仍保活直至 delegate 释放
失效后调用throw

帧泵: TsAppDomain.ProcessPendingRefReleases()TsFramePump)在主线程批量 unref../10-LIFETIME.md)。

C# delegate → JS(JS 源): Push function; 新建 ref。

C# delegate → JS(原生): exotic __gcObjectRegistry.Unregister pin JS function。


7. Mono / Il2Cpp

Il2CppMono
C#→JS 分流§3.1相同
JS→C# bridgeC++ DelegateBridgesExpression Emit
未支持签名运行时查表失败NotSupportedException
JS 可见语义权威须一致

8. Codegen(Il2Cpp)

MethodBridges 同源扫描:带 delegate 形参 的 public 方法 → 推导 Invoke 签名。

签名键示例:

void(System.Int32) → Action<int>
System.Int32(System.Int32) → Func<int,int>

9. 边界情况

场景MVP
Action / Func<> / 自定义 delegateInvoke 签名
C#→JS§3.1 分流
byref03-BYREF.md04-OPAQUE.md
params on bridge不支持
开放 delegate可不支持
Multicast + JS 回调单播
协变 / 逆变精确 delegate 类型
Event 专用;add_*/remove_*

10. 相关文档

文档内容
06-CLASS.mdDelegate exotic
../01-HOST-API.mdGetFunction
../05-LIB.mdto_delegate
../10-LIFETIME.mdref 释放