feat: structuredClone global (HTML structured clone, ArrayBuffer transfer) - #2000
Open
edusperoni wants to merge 2 commits into
Open
feat: structuredClone global (HTML structured clone, ArrayBuffer transfer)#2000edusperoni wants to merge 2 commits into
edusperoni wants to merge 2 commits into
Conversation
…sfer) Android port of NativeScript/ios#431, in lockstep with the iOS runtime. Implements the WHATWG structuredClone(value, { transfer }) global as a post-context JS builtin: js/structured-clone.js (shared unchanged with iOS) owns the WebIDL argument coercion, and a thin native binding runs a v8::ValueSerializer -> ValueDeserializer round-trip in the one isolate. The serialization machinery is consolidated into StructuredSerialization.{h,cpp} (tns::serialization) so structuredClone and worker postMessage run on one core: delegate pair, DataCloneError construction (an Error carrying that name -- previously the worker path threw a plain Error with a message prefix), transfer-list validation, and the register->write->claim->detach ordering that V8 14.9 requires (Detach() aborts on non-detachable buffers; Release() must be claimed even after a failed write). WorkerMessage.cpp is deleted and WorkerMessage.h reduced to an alias. postMessage gains the ArrayBuffer transfer list on both entry points. Host objects stay intentionally asymmetric via HostObjectPolicy: structuredClone rejects (spec), postMessage keeps the shipped degrade-to-{} behavior. Tests: shared cross-runtime suite (common-runtime-tests-app#26, 54 specs) wired via shared.runStructuredCloneTests(), plus an unguarded canary in testRuntimeImplementedAPIs.js. Documented in docs/structured-clone.md.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Android port of NativeScript/ios#431 — implements the WHATWG
structuredClone(value, { transfer })global, in lockstep with the iOS runtime.Architecture
Follows the post-context JS builtin pattern from the js-builtins stack (#1989–#1992), which is why this PR is based on
feat/ns-util:StructuredClone.{h,cpp}exposes a one-function binding bag (clone(value, transferArray)) and runsBuiltinId::kStructuredClone. The clone is av8::ValueSerializer→v8::ValueDeserializerround-trip inside the one isolate, which is whatStructuredDeserialize(StructuredSerializeWithTransfer(...))reduces to when there is no second agent.js/structured-clone.jsowns the argument checks and thesequence<object>conversion fortransfer. It is byte-for-byte the same file as the iOS runtime's — nothing platform-specific in it.ArrayBuffermembership in the transfer list is brand-checked from JS through the capturedArrayBuffer.prototype.byteLengthgetter (tamper-proof, and correctly excludesSharedArrayBuffer, which is not transferable); the native side re-checks withIsArrayBuffer().Adds the
ErrorandSymbolIteratorprimordials.Serialization consolidation
Mirrors the iOS PR's consolidation so
structuredCloneand workerpostMessagecan never diverge:StructuredSerialization.{h,cpp}(tns::serialization) owns the serializer/deserializer delegate pair,DataCloneErrorconstruction, transfer-list validation (duplicates, detached/non-detachable), and the register→write→claim→detach ordering.SerializedValuekeeps serialize/deserialize as separate halves because a worker message deserializes on a different isolate.WorkerMessage.cppis deleted (WorkerMessage.his a one-line alias), and theMallocedBufferscaffolding it carried goes with it.postMessagegains the ArrayBuffer transfer list on both entry points (ArrayofArrayBuffers,TypeErrorotherwise), using the shared validation. Both callbacks now accept 1 or 2 arguments.HostObjectPolicy { kReject, kDegrade }.structuredClonerejects withDataCloneError(spec);postMessagekeeps the shipped degrade-to-{}behavior that the cross-runtime worker suite asserts on both runtimes. Unifying on reject is a coordinated follow-up with the iOS runtime; it will be a one-line policy change here.DataCloneErrorgains its name: worker clone failures previously threw a plainErrorwith a"DataCloneError: ..."message prefix; both entry points now throw anErrorwhosenameis"DataCloneError", matching iOS and the shared suite'se.namedetection.V8 14.9 notes (same as iOS)
ArrayBuffer::Detach()on a non-detachable buffer aborts the process — it does not throw. TheIsDetachable()pre-check before detaching is therefore load-bearing, not defensive.DataCloneErrorname — with a delegate installed, V8's base implementations throw a plainErrordirectly onto the isolate.Release()must be called and its bufferfree()d even after a failed write — the memory is owned by the caller afterRelease()regardless of whether serialization succeeded.TransferArrayBuffermakes the serializer skip itswas_detachedcheck, so detaching after a successfulWriteValue(rather than before) is the correct ordering.Deviations from the specification
Same set as iOS, documented in
docs/structured-clone.md:DataCloneErroris anErrorwith.name = "DataCloneError", not aDOMException(this runtime has noDOMException).ArrayBufferis the only transferable type.SharedArrayBuffershares (a new SAB over the same backing store) and is not transferable.structuredClone;postMessagedegrades them (see above).Tests
Shared cross-runtime suite: NativeScript/common-runtime-tests-app#26 (54 specs — clone semantics, graph identity and cycles, transfer, SharedArrayBuffer sharing, worker message transfer,
DataCloneErrorcases), wired up viashared.runStructuredCloneTests()inmainpage.js. An unguarded canary intestRuntimeImplementedAPIs.jsasserts the global exists, so the suite's self-gating cannot hide a regression.Full suite green on a Pixel 3a API 36 emulator (arm64): 701 specs, 0 failures, 5 skipped, 11 disabled.
Merge sequencing
feat/ns-util(feat: ns:util builtin module (inspect, format) #1992) — the builtin loader, primordials and js2c infrastructure this builds on. Merges after the stack.master(2eee85b4), which now includes the squash-merged test: add structuredClone suite (opt-in via runStructuredCloneTests) common-runtime-tests-app#26. TheStructuredClone/content is byte-identical to the branch commit the device run above was verified against;masteradditionally carries the Performance suite (resolved issue #20 - Hidden JSinstance info ... raised #25), whichmainpage.jsdoes not invoke.