From b3f8779c76de7718bc970fdbccda18a5164d2f98 Mon Sep 17 00:00:00 2001
From: Anne van Kesteren
The exception to this general design principle is the JavaScript SharedArrayBuffer
+ class. Using SharedArrayBuffer
objects, it can in fact be observed that scripts in
+ other agents are executing simultaneously. Furthermore, due to the
+ JavaScript memory model, there are situations which not only are un-representable via serialized
+ script execution, but also un-representable via serialized statement execution
+ among those scripts.
A set of steps that serializes the data in input into fields of serialized. The resulting data serialized into serialized must be @@ -7661,6 +7670,9 @@ interface DOMStringList {
These steps may perform a sub-serialization to serialize nested data structures. They should not call StructuredSerialize directly, as doing so will omit the important memory argument.
+ +The introduction of these steps should omit mention of the forStorage argument if + it is not relevant to the algorithm.
Objects defined in the JavaScript specification are handled by the StructuredSerializeWithTransfer abstract operation directly.
-The StructuredSerialize abstract operation takes as input a JavaScript value - value and serializes it to a Realm-independent - form, represented here as a Record. This serialized form has all the information - necessary to later deserialize into a new JavaScript value in a different Realm.
+The StructuredSerializeInternal abstract operation takes as input a JavaScript + value value and serializes it to a Realm-independent form, represented here as a Record. This serialized + form has all the information necessary to later deserialize into a new JavaScript value in a + different Realm.
This process can throw an exception, for example when trying to serialize un-serializable objects.
@@ -7861,23 +7874,43 @@ interface DOMStringList {Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
If IsDetachedBuffer(value) is true, then throw a
- "DataCloneError
" DOMException
.
Let size be value.[[ArrayBufferByteLength]].
Let dataCopy be ? CreateByteDataBlock(size).
+If ! IsSharedArrayBuffer(value) is true, then: + +
If forStorage is true, then throw a
+ "DataCloneError
" DOMException
.
This can throw a RangeError
exception upon
- allocation failure.
Set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: + value.[[ArrayBufferData]], [[ArrayBufferByteLength]]: size, + [[AgentCluster]]: the current Realm Record's corresponding agent + cluster }.
Perform ! CopyDataBlockBytes(dataCopy, 0, - value.[[ArrayBufferData]], 0, size).
Otherwise:
+ +If ! IsDetachedBuffer(value) is true, then throw a
+ "DataCloneError
" DOMException
.
Set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: - dataCopy, [[ArrayBufferByteLength]]: size }.
Let dataCopy be ? CreateByteDataBlock(size).
+ +This can throw a RangeError
exception
+ upon allocation failure.
Perform ! CopyDataBlockBytes(dataCopy, 0, + value.[[ArrayBufferData]], 0, size).
Set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: + dataCopy, [[ArrayBufferByteLength]]: size }.
Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
Let bufferSerialized be ? StructuredSerialize(buffer, +
Let bufferSerialized be ? + StructuredSerializeInternal(buffer, forStorage, memory).
Assert: bufferSerialized.[[Type]] is "ArrayBuffer".
Let serializedKey be ? - StructuredSerialize(entry.[[Key]], memory).
Let serializedValue be ? - StructuredSerialize(entry.[[Value]], memory).
Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
Let serializedEntry be ? - StructuredSerialize(entry, memory).
Append serializedEntry to serialized.[[SetData]].
Otherwise, if value is a platform object that is a serializable object, then perform the appropriate - serialization steps given value and serialized.
+ serialization steps given value, serialized, and + forStorage.The serialization steps may need to perform a sub-serialization. This is an operation which takes as input a value - subValue, and returns StructuredSerialize(subValue, - memory). (In other words, a sub-serialization is a specialization of - StructuredSerialize to be consistent within this invocation.)
+ subValue, and returns StructuredSerializeInternal(subValue, + forStorage, memory). (In other words, a sub-serialization + is a specialization of StructuredSerializeInternal to be consistent within this + invocation.)Let outputValue be ? - StructuredSerialize(inputValue, targetRealm, + StructuredSerializeInternal(inputValue, forStorage, memory).
Append { [[Key]]: key, [[Value]]: @@ -8146,9 +8185,9 @@ interface DOMStringList {
It's important to realize that the Records - produced by StructuredSerialize might contain "pointers" to other records that - create circular references. For example, when we pass the following JavaScript object into - StructuredSerialize:
+ produced by StructuredSerializeInternal might contain "pointers" to other records + that create circular references. For example, when we pass the following JavaScript object into + StructuredSerializeInternal:const o = {}; o.myself = o;@@ -8167,13 +8206,30 @@ o.myself = o;
Return ? StructuredSerializeInternal(value, false).
Return ? StructuredSerializeInternal(value, true).
The StructuredDeserialize abstract operation takes as input a Record - serialized, which was previously produced by StructuredSerialize, and - deserializes it into a new JavaScript value, created in targetRealm.
+ serialized, which was previously produced by StructuredSerialize or + StructuredSerializeForStorage, and deserializes it into a new JavaScript value, + created in targetRealm.This process can throw an exception, for example when trying to allocate memory for the new
objects (especially ArrayBuffer
objects).
Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer - object in targetRealm whose [[ArrayBufferData]] internal slot value is - serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot +
Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
+ +If targetRealm's corresponding agent cluster is not
+ serialized.[[AgentCluster]], then then throw a
+ "DataCloneError
" DOMException
.
Otherwise, set value to a new SharedArrayBuffer object in + targetRealm whose [[ArrayBufferData]] internal slot value is + serialized.[[ArrayBufferData]] and whose [[ArrayBufferByteLength]] internal slot + value is serialized.[[ArrayBufferByteLength]].
Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a + new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value + is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
+If this throws an exception, then throw a "DataCloneError
"
+ DOMException
.
This step might throw an exception if there is not enough memory available to create such an ArrayBuffer object.
Let memory be an empty map.
-In addition to how it is used normally by StructuredSerialize, in - this algorithm memory is also used to ensure that StructuredSerialize - ignores items in transferList, and let us do our own handling instead.
+In addition to how it is used normally by + StructuredSerializeInternal, in this algorithm memory is also used to + ensure that StructuredSerializeInternal ignores items in transferList, + and let us do our own handling instead.
DataCloneError
" DOMException
.If transferable has an [[ArrayBufferData]] internal slot and ! +
If transferable has an [[ArrayBufferData]] internal slot and either !
+ IsSharedArrayBuffer(transferable) is true or !
IsDetachedBuffer(transferable) is true, then throw a
"DataCloneError
" DOMException
.
Let serialized be ? StructuredSerialize(input, - memory).
Let serialized be ? StructuredSerializeInternal(input, + false, memory).
Let transferDataHolders be a new empty List.
Creating a JavaScript Realm-independent snapshot of a given value which can be saved for an indefinite amount of time, and then reified back into a JavaScript value later, possibly multiple times.
+StructuredSerializeForStorage can be used for situations where the serialization
+ is anticipated to be stored in a persistent manner, instead of passed between Realms. It throws
+ when attempting to serialize SharedArrayBuffer
objects, since storing shared memory
+ does not make sense. Similarly, it can throw or possibly have different behavior when given a
+ platform object with custom serialization steps when the
+ forStorage argument is true.
history.pushState()
and history.replaceState()
use
- StructuredSerialize on author-supplied state objects, storing them as
+ StructuredSerializeForStorage on author-supplied state objects, storing them as
serialized state in the appropriate session history entry. Then,
StructuredDeserialize is used so that the history.state
property can return a clone of the
@@ -8627,8 +8711,9 @@ o.myself = o;
multiple times on the result to produce a fresh clone for each destination being broadcast
to. Note that transferring does not make sense in multi-destination situations.
Any API for persisting JavaScript values to the filesystem would likely use - these.
+Any API for persisting JavaScript values to the filesystem would also use + StructuredSerializeForStorage on its input and StructuredDeserialize + on its output.
Call sites that are not invoked as a result of author code synchronously calling into a user agent method must take care to properly prepare to run script and prepare to - run a callback before invoking StructuredSerialize or - StructuredSerializeWithTransfer abstract operations, if they are being performed on - arbitrary objects. This is necessary because the serialization process can invoke author-defined - accessors as part of its final deep-serialization steps, and these accessors could call into - operations that rely on the entry and before invoking StructuredSerialize, + StructuredSerializeForStorage, or StructuredSerializeWithTransfer + abstract operations, if they are being performed on arbitrary objects. This is necessary because + the serialization process can invoke author-defined accessors as part of its final + deep-serialization steps, and these accessors could call into operations that rely on the entry and incumbent concepts being properly set up.
window.postMessage()
performs
- StructuredSerialize on its arguments, but is careful to do so immediately, inside the
- synchronous portion of its algorithm. Thus it is able to use the structured cloning algorithms
- without needing to prepare to run script and prepare to run a
+ StructuredSerializeWithTransfer on its arguments, but is careful to do so
+ immediately, inside the synchronous portion of its algorithm. Thus it is able to use the
+ algorithms without needing to prepare to run script and prepare to run a
callback.
In contrast, a hypothetical API that used StructuredSerialize to @@ -80497,9 +80583,9 @@ callback FrameRequestCallback = void (DOMHighResTimeStamp
Serialized state is a serialization (via - StructuredSerialize) of an object representing a user interface state. We sometimes - informally refer to "state objects", which are the objects representing user interface state - supplied by the author, or alternately the objects created by deserializing (via + StructuredSerializeForStorage) of an object representing a user interface state. We + sometimes informally refer to "state objects", which are the objects representing user interface + state supplied by the author, or alternately the objects created by deserializing (via StructuredDeserialize) serialized state.
Pages can add serialized state to the @@ -80888,8 +80974,8 @@ interface History {
Let targetRealm be this History
object's relevant Realm.
Let serializedData be StructuredSerialize(data). - Rethrow any exceptions.
Let serializedData be + StructuredSerializeForStorage(data). Rethrow any exceptions.