
语言运行时编译器移动开发【免费下载链接】hermesA JavaScript engine optimized for running React Native.项目地址https://gitcode.com/gh_mirrors/hermes/hermes点击查看免费下载导读ISerialization 是 Hermes 为跨运行时传递 JS 值而设计的高效二进制序列化机制它把任意 JS 值编码为一个不透明的二进制表示并可在同一个或多个运行时实例中反序列化即深拷贝还额外支持带所有权转移transfer的序列化模式。本文以 doc/blog/2025-12-02-iserialization.md 为骨架结合 JSI 接口、VM 层实现与单元测试讲解它的设计动机、API 语义、底层二进制布局以及它在 Web Workers 消息传递场景中的实际落地方式。一、从问题出发为什么 Hermes 需要 ISerialization在 JavaScript 引擎的日常使用中存在一类反复出现但难以高效解决的诉求把一个运行时里创建的 JS 值原样地搬运到另一个运行时实例中使用。典型场景包括多运行时/多线程架构主线程运行时与 Worker 线程运行时各自独立消息传递需要一种能跨运行时重建对象图的机制深度克隆在不共享可变状态的前提下复制复杂对象数组、Map、Set、Date、RegExp、ArrayBuffer 等序列化录制与回放把一段执行过程中的值完整记录下来供另一个时刻或另一个进程重放。传统的 JSON 序列化只能表达部分类型丢失 Date、RegExp、Map、Set、BigInt、ArrayBuffer 等信息而逐字段深拷贝在跨运行时场景下又无法直接搬运内部指针。ISerialization 正是为填补这一空白而设计它将一个 JS 值编码为高效、不透明的二进制表示之后可以在一个或多个运行时实例中被反序列化deeply cloned并可选地支持带所有权转移的序列化。从仓库演进历史看这一特性最初在static_h分支落地随后进入稳定发布流程后续稳定版本发布说明见 doc/blog/2026-06-05-new-hermes-stable-release.md。它的长期定位非常明确——作为 Hermes 实现 Web Workers 时消息传递message passing的基础设施。二、核心设计不透明二进制表示与深拷贝语义ISerialization 的设计围绕三个关键词展开高效efficient序列化产物是紧凑的二进制形式而不是 JSON 之类的文本表示字符串、类型标签、嵌套值都以二进制编码避免文本解析开销。不透明opaque序列化产物对调用方是不可见、不可直接解读的。JSI 层通过jsi::Serialized这一不透明句柄持有它只有运行时内部才能通过校验后的私有指针读取真正的数据。可重复反序列化deeply cloned in one or more runtimes一个Serialized对象可以被反序列化多次每次都在目标运行时中重建出一份全新的对象图深拷贝语义原始值与序列化产物的生命周期互不依赖。JSI 头文件对Serialized的注释明确写道“用于存放从某个运行时序列化而来的对象该对象的生命周期与原始运行时对象正交可以比原始对象活得更久”见 API/jsi/jsi/jsi.h。这是它与“转移所有权”模式的关键区别普通序列化是可复用的、非破坏性的。三、JSI 层 APIISerialization 接口与 Serialized 对象3.1 特性门控JSI_UNSTABLEISerialization 相关的接口定义在JSI_UNSTABLE宏门控之内见 API/jsi/jsi/jsi.h。头文件对JSI_UNSTABLE的语义说明是它门控那些将随 Hermes 某个未来版本发布的功能发布前这些 API 可能发生变化发布后会移出JSI_UNSTABLE并冻结。因此集成方若在当前分支使用这些接口需要在构建时启用JSI_UNSTABLE并关注后续版本的 API 稳定性变化。3.2 Serialized不透明数据容器class JSI_EXPORT Serialized { public: /// 使用 secretAddr 校验序列化数据是否受支持。 /// 若支持返回底层序列化数据的指针否则返回 nullptr。 virtual void* getPrivate(const void* secretAddr) 0; virtual ~Serialized(); };Serialized的核心方法是getPrivate运行时通过一个“秘密地址”secret来校验这份序列化数据是否由自己产生。Hermes 的HermesRuntimeImpl在反序列化时会先调用serialized-getPrivate(HermesSerialized::getHermesSerializedSecret())校验若返回空指针则抛出Cannot deserialize non-Hermes serialized objects见 API/hermes/hermes.cpp。也就是说序列化产物与运行时类型强绑定Hermes 序列化的对象只能由 Hermes 运行时反序列化跨引擎例如从 V8 序列化的数据无法互通。3.3 ISerialization四个核心方法ISerialization继承自ICast并拥有一个全局唯一的 UUID0xd40fe0ec-a47c-42c9-8c09-661aeab832d8见 API/jsi/jsi/jsi.h。它定义四个纯虚方法方法签名语义serializestd::shared_ptrSerialized serialize(const Value value)按结构化克隆算法序列化值返回可被反序列化多次的共享指针deserializeValue deserialize(const std::shared_ptrSerialized serialized)将序列化对象重建为当前运行时的 JS 值serializeWithTransferstd::unique_ptrSerialized serializeWithTransfer(const Value value, const Array transferList)序列化并转移transferList中索引[0, length-1]的所有值原运行时中这些值将不再可用返回只能反序列化一次的独占指针deserializeWithTransferArray deserializeWithTransfer(std::unique_ptrSerialized serialized)反序列化并执行转移消耗掉serialized返回的 JS 数组首元素为反序列化后的消息值后续元素为被转移的各值注意两组方法在所有权语义上的差异serialize返回std::shared_ptr可deserialize多次是“复制”语义serializeWithTransfer返回std::unique_ptr只能被deserializeWithTransfer消费一次是“移动”语义——反序列化成功后serialized被整体消耗serialized.reset()转移的值归目标运行时所有见 API/hermes/hermes.cpp。3.4 如何获取接口castInterface 与 UUID调用方通过jsi::castInterfacejsi::ISerialization(runtime)获取接口。Hermes 在HermesRuntimeImpl::getInterface中按 UUID 匹配命中ISerialization::uuid时返回接口实现见 API/hermes/hermes.cpp。TracingRuntime同样实现了该接口并在内部委托给被包装的底层运行时见 API/hermes/TracingRuntime.cpp因此录制/回放synth trace环境也能走同一套序列化路径。四、所有权转移serializeWithTransfer / deserializeWithTransfer“所有权转移”是 ISerialization 区别于普通深拷贝的关键能力其语义与 HTML 结构化克隆规范中的StructuredSerializeWithTransfer/StructuredDeserializeWithTransfer一致序列化端调用serializeWithTransfer(value, transferList)时transferList必须是一个 JS 数组。以数组length为准索引[0, length-1]上的所有值会被从当前运行时“转移”出去转移后这些值在原始运行时中不再可用。返回值是一个独占的Serialized对象只能被反序列化一次。反序列化端deserializeWithTransfer将数据重建到当前运行时被转移的值典型如 ArrayBuffer 底层数据块直接归目标运行时所有而不再复制。由于转移是破坏性的同一个Serialized对象不能被再次反序列化。失败回滚如果当前运行时不支持Serialized中的序列化方案deserializeWithTransfer会抛出异常且serialized保持不变见 API/jsi/jsi/jsi.h保证异常路径不会丢失数据。在 Hermes 实现中serializeWithTransfer还会校验transferList必须是真实数组而非 Proxy——Transfer array must not be a Proxy.见 API/hermes/hermes.cpp防止经 Proxy 拦截产生语义绕行。五、底层实现vm::SerializedValue 的二进制布局JSI 层的Serialized只是外壳真正的序列化数据由 VM 层的hermes::vm::SerializedValue承载见 include/hermes/VM/SerializedValue.h。理解它的内部结构才能理解“高效二进制编码”到底高效在哪里。5.1 四个数据缓冲区SerializedValue用四个向量存放不同类别的二进制数据字段类型内容offsetsstd::vectoruint32_t索引表。每个元素是某条记录的偏移量例如序列化 ID 为 X 的字符串其编码位于strings[stringOffset]而offsets[X]存放该stringOffsetcontentstd::vectoruint8_t序列化值的主记录区含根值记录与所有嵌套值记录每条记录形如(type tag, content)stringsstd::vectoruint8_t反序列化所需的全部字符串每条按(string serialization ID, isAscii, length, data)编码internalBuffers/externalBuffers指针向量ArrayBuffer 底层数据块的转移信息内部数据块存指针外部数据块存指针加负责清理的shared_ptr上下文这种“主记录区 字符串池 偏移索引”的设计让字符串只需存储一次即可被多处引用避免了重复编码。5.2 Type 标签体系与 Reference 记录每条content记录的第一个字节是Type标签见 include/hermes/VM/SerializedValue.h。完整的标签枚举包括原始类型Undefined、Null、PrimitiveBoolean、PrimitiveNumber、PrimitiveBigInt、PrimitiveString对象类型Boolean、Number、BigInt、String、Date、RegExp、ArrayBuffer及细分的ArrayBufferInternal/ArrayBufferExternal、DataView、所有 TypedArray由TypedArrays.def展开为Int8Array、Uint8Array、Float64Array等、Map、Set、Error、Array、Object特殊类型Reference。Reference是对象图共享的关键嵌套值既可以内联编码在父记录中也可以只存一个Reference记录指向此前已序列化的某个值。这样同一对象被多处引用时反序列化后会还原为同一个对象而不是多份拷贝——这是结构化克隆算法保持对象图一致性的体现。5.3 错误类型编码SerializedValue::ErrorType枚举覆盖 JS 的七类错误Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError见 include/hermes/VM/SerializedValue.h。序列化错误对象时类型标签与具体错误子类都会被保留反序列化后仍能还原为对应子类的错误实例。5.4 与 HTML 结构化克隆算法的对应关系SerializedValue.h的头文件注释给出了明确的规范依据serialize_RJS遵循 WHATWG HTML 规范中的StructuredSerializeInternaldeserialize遵循StructuredDeserializeserializeWithTransfer_RJS与deserializeWithTransfer分别对应StructuredSerializeWithTransfer与StructuredDeserializeWithTransfer。也就是说ISerialization 的语义边界以 Web 平台的结构化克隆算法为蓝本这也为其未来支撑 Web Workers 的消息传递postMessage铺平了语义道路。四个核心函数签名如下见 include/hermes/VM/SerializedValue.h// 普通序列化可多次反序列化 CallResultSerializedValue serialize_RJS(Runtime runtime, Handle value); CallResultHermesValue deserialize(Runtime runtime, const SerializedValue serialized); // 带所有权转移只能反序列化一次 CallResultSerializedValue serializeWithTransfer_RJS( Runtime runtime, Handle value, HandleJSArray transferList); CallResultPseudoHandleJSArray deserializeWithTransfer( Runtime runtime, SerializedValue serialized);HermesRuntimeImpl的四个 JSI 方法正是这些 VM 函数的薄封装见 API/hermes/hermes.cpp进入前创建vm::GCScope管理 GC 根通过checkStatus把 VM 层的错误状态转译为 JSI 异常。六、实战场景Worker 消息传递中的 ISerializationISerialization 被设计为 Web Workers 消息传递的基础仓库中API/hermes/extensions/Worker.cpp已经完整展示了这一模式的端到端实现。6.1 消息发送端postMessageWorker 运行时安装的全局postMessage是一个 Host Function见 API/hermes/extensions/Worker.cpp。其处理逻辑为通过jsi::castInterfacejsi::ISerialization(runtime)获取序列化接口若只传入一个参数调用serialize(message)走普通序列化若传入第二个参数transfers先校验它必须是数组否则抛出Must provide an Array of transferable arguments再调用serializeWithTransfer(message, transfers)走转移序列化将序列化结果放入fromWorkerQueue队列并通过ISetEventLoopControl在集成方事件循环上调度processMessageFromWorker任务。值得注意的是源码注释特别指出序列化必须在锁外执行——因为序列化过程中可能运行 JS如触发 getter而运行 JS 可能反向尝试获取同一把锁在锁内执行会死锁见 API/hermes/extensions/Worker.cpp。这个细节对任何“持锁执行 JS”的多线程宿主都是重要的工程教训。6.2 消息接收端onmessage接收端processMessageWithHandler按消息类型分派见 API/hermes/extensions/Worker.cpp普通序列化消息deserialize得到值后直接调用onmessage处理器转移序列化消息deserializeWithTransfer返回的数组首个元素是消息值处理器拿到它源码断言该数组非空并明确约定“反序列化后的消息必须位于数组索引 0”。至此主运行时与 Worker 运行时之间交换的既不是 JSON 字符串也不是跨运行时共享的内部指针而是自包含的二进制序列化块——这正是 ISerialization 作为消息传递基座的价值所在。七、测试与验证单元测试是理解 ISerialization 语义边界最直观的入口unittests/API/APITest.cpp中有一个专门的测试组见 unittests/API/APITest.cpp通过两个运行时实例rt、rt2验证跨运行时往返原始值往返undefined、null、true/false、100.99、BigInt 字面量18446744073709551615n、ASCII 字符串、含 emoji 的 UTF-16 字符串\ud83d\udc4d均能正确还原包装对象往返new Boolean(true)、new Number(123.456)、Object(18446744073709551615n)、new String(...)反序列化后仍是对象且valueOf、toString等行为保持一致Date 与 RegExpnew Date(2025, 5, 4)的toDateString结果一致RegExp 的test与exec行为一致数组与对象数组索引与普通属性如1、foo均正确恢复异常路径对 Symbol 调用serialize会抛出JSErrorSymbol 不在结构化克隆算法可表达的类型集合内。这些用例同时印证了序列化产物可在另一个运行时实例中重建且重建结果在语义上与源值等价。八、使用限制与注意事项综合接口定义与实现使用 ISerialization 时有以下几点需要特别留意运行时类型绑定Hermes 序列化的数据只能由 Hermes 运行时反序列化getPrivate的 secret 校验保证了这一点跨引擎互操作不被支持。Serialized不可拷贝vm::SerializedValue的拷贝构造函数与拷贝赋值被显式删除因为它可能持有数据缓冲区的所有权见 include/hermes/VM/SerializedValue.h只能移动。可表达类型有限遵循结构化克隆算法函数、Symbol、Proxy 等不可序列化serialize会抛错BigInt、Date、RegExp、Map、Set、ArrayBuffer、TypedArray 等则在支持之列。转移是一次性的serializeWithTransfer的产物只能被deserializeWithTransfer消费一次消费后不可复用失败时数据保留以便重试。transferList必须为真实数组Hermes 显式拒绝 Proxy 作为转移列表避免语义绕行。API 处于演进期当前分支中相关 JSI 接口位于JSI_UNSTABLE门控内后续随稳定版本发布可能调整集成时应以所用版本的头文件为准。线程与锁参考 Worker 实现序列化可能执行 JS多线程宿主应避免在持锁状态下调用序列化接口。结语ISerialization 以 WHATWG 结构化克隆算法为语义基准用“类型标签 记录内容 字符串池 偏移索引”的紧凑二进制布局为 Hermes 提供了一套可跨运行时深拷贝、可选所有权转移的值传递方案。它既是多运行时架构下高效传递 JS 值的通用工具也是仓库中 Web Workers 消息传递实现的直接基石。若要继续深入建议按以下顺序阅读仓库源码接口定义API/jsi/jsi/jsi.hSerialized与ISerialization数据布局include/hermes/VM/SerializedValue.hvm::SerializedValue与四个序列化函数运行时实现API/hermes/hermes.cppHermesRuntimeImpl的四个方法实战调用API/hermes/extensions/Worker.cpppostMessage/onmessage全流程行为验证unittests/API/APITest.cpp跨运行时往返测试。赞分享语言运行时编译器移动开发【免费下载链接】hermesA JavaScript engine optimized for running React Native.项目地址https://gitcode.com/gh_mirrors/hermes/hermes点击查看免费下载相关推荐突破PHP序列化性能瓶颈二进制序列化技术深度解析突破PHP序列化性能瓶颈二进制序列化技术深度解析 你是否曾经为PHP序列化数据过大而烦恼在分布式系统和高并发场景下传统的文本序列化方式往往成为性能瓶颈。今OpenCloud 运行时Runtime深度解析基于 Suture 的服务监督体系与单二进制部署OpenCloud 运行时Runtime深度解析基于 Suture 的服务监督体系与单二进制部署 导读 OpenCloud 是一个面向文件管理、共享与协作后端微服务存储认证鉴权BentoPDF PDF to JSON 工具深度解析基于 CPDF WASM 的 PDF 内部结构序列化BentoPDF PDF to JSON 工具深度解析基于 CPDF WASM 的 PDF 内部结构序列化 导读 BentoPDF 是一款宣称 The Pr前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考