fix(compilation): deep-clone Date/RegExp/TypedArray/Buffer/Error in compiled structuredClone; throw DataCloneError on uncloneable#1257
Merged
Conversation
…ompiled structuredClone and throw DataCloneError on uncloneable values (#1255) Compiled StructuredCloneCore previously only deep-cloned List/Dictionary/ HashSet and aliased everything else by reference (Date, RegExp, TypedArray, ArrayBuffer, Buffer, Error) or silently passed through uncloneable values (functions, symbols, class instances, Promises) instead of throwing, unlike the interpreter's StructuredClone.Clone. Extends cloneCore to deep-clone the former and throw a new standalone $DataCloneError for the latter — at any nesting depth, not just top-level — flipping the previous alias-fallback to throw by default so unrecognized constructs fail closed like the interpreter's exhaustive switch. $MessagePort/$BroadcastChannel now catch $DataCloneError around their clone call and convert it to the existing receiver-side 'messageerror' sentinel instead of relying on a narrower top-level typeof pre-check. WrapException recognizes $DataCloneError so a caught value is the raw message string, matching the interpreter's non- guest-throw catch binding. Also fixes two bugs found along the way: a value whose CLR type lives outside the emitted assembly (interpreter runtime values crossing via CompiledMessagePortBridge) must pass through unchanged rather than throw, and SharpTSBroadcastChannel's onmessageerror property handler was never invoked (only the EventEmitter event fired) and dropped delivery if the receiver closed before the scheduled notification ran.
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.
Summary
Fixes #1255: the compiled/emitted structured-clone core (
$Runtime.StructuredClone→StructuredCloneCore) was shallow — it only deep-clonedList<object?>/Dictionary<string,object?>/Dictionary<object,object?>/HashSet<object?>and returned everything else by reference, diverging from the interpreter'sStructuredClone.Clonein two ways:Date,RegExp, TypedArrays,ArrayBuffer,Buffer,Errorinstances.DataCloneError— functions/closures, symbols, class instances,Promise, etc., both at the top level and nested inside objects/arrays.Fix
StructuredCloneCorenow deep-clones$TSDate,$RegExp,$TypedArray/$ArrayBuffer(independent copy, or a shared view when backed by aSharedArrayBuffer),$Buffer,$Object(recurses into its Fields dict), and the built-in$Errorhierarchy (name-based reconstruction +Stackpreservation).$DataCloneError : Exception(noSharpTS.dllreference — stays fully standalone), matching the interpreter's exhaustive switch. This now correctly rejects uncloneable values at any nesting depth, not just the top level.CompiledMessagePortBridge, which already structured-clones on the interpreter side before handing off) passes through unchanged rather than throwing — only the compiled program's own uncloneable constructs throw.$MessagePort.PostMessage/$BroadcastChannel.PostMessagenow catch$DataCloneErroraround the clone call and convert it to the existing receiver-side'messageerror'sentinel, replacing the old top-leveltypeofpre-check (which missed nested uncloneables).$BroadcastChannelgainedmessageerrorsupport end-to-end (sentinel +Drainhandling), matching$MessagePort.WrapExceptionrecognizes$DataCloneErrorand returns its raw message string, matching the interpreter's catch binding for a non-guest-throw exception.Bugs found and fixed along the way
SharpTSBroadcastChannel.EnqueueMessageError's scheduled closure never invoked the property-styleonmessageerrorhandler (only the EventEmitter event fired), and re-checked_closedat fire time — dropping an already-enqueued notification if the receiver closed before the timer ran, inconsistent withDrainPendingMessages' "in-flight deliveries complete after Close()" behavior.Test plan
dotnet test— 15452/15452 passing (22 new tests added)SharpTS.Test262— 22/0 (4 skipped diagnostics), unchangedSharpTS.TypeScriptConformance— 31/0, unchanged--compile --verifyand--compile --standalone --verifyboth pass (noSharpTS.dlldependency introduced)$MessagePort/$BroadcastChannelmessageerror(including a nested-uncloneable case) and structuredClone deep-clone/throw behavior inSharpTS.Tests/SharedTests/WorkerThreadsTests.csandBroadcastChannelTests.cs