Structured cloning algorithm defines the semantics of copying a well-defined subset of ECMAScript objects between Code Realms. This algorithm is extensible by host enviroment to support cloning of host objects.
Optionally, some kinds of objects may support a "transfer" operation, the effect of which is to transfer "ownership" of some resource associated with an object to a different Code Realm. The object then becomes unusable in the source Code Realm.
This specification combines and subsumes http://www.whatwg.org/specs/web-apps/current-work/#dom-messageport-postmessage and http://www.whatwg.org/specs/web-apps/current-work/#structured-clone as they really belong together.
HTML spec will be updated to refer to this specification of the StructuredClone algorithm.
We introduce a StructuredClone operator.
Transferable objects carry a [[Transfer]] internal data property that is either a transfer operator or "neutered", and an [[OnSuccessfulTransfer]] internal method.
Objects defined outside ECMAScript need to define a [[Clone]] internal method that returns a copy of the object.
Note: The first iteration is not user-pluggable. It is about moving the semantics into ECMAScript proper and tying them down.
The operator StructuredClone either returns a structured clone of input or throws an exception. A structured clone of an object input is an object in Code Realm targetRealm. transferList is a list of objects that should be transferred during cloning of input.
- Let memory be a map of source-to-destination object mappings.
- For each object transferable in transferList:
- If transferable does not have a [[Transfer]] internal data property whose value is an operator, throw a DataCloneError exception.
- Let transferResult be a result of a call to a transferable's internal method [[Transfer]] with argument targetRealm.
- ReturnIfAbrupt( transferResult )
- Append a mapping from transferable to transferResult to memory.
- Let clone be the result of InternalStructuredClone( input, memory, targetRealm ).
- ReturnIfAbrupt( clone ).
- For each object transferable in transferList:
- Let transferResult be a target of mapping from transferable in memory.
- Run transferable's internal method [[OnSuccessfulTransfer]](transferResult, targetRealm).
- Return clone.
The operator InternalStructuredClone either returns a structured clone of input in Code Realm targetRealm or throws an exception.
- If input is the source object of a pair of objects in memory, then return the destination object in that pair of objects.
- If input's [[Transfer]] is “neutered”, throw a DataCloneError exception.
- If input is a primitive value, return input.
- Let deepClone be false.
- If input has a [[BooleanData]] internal data property:
- Let output be a new Boolean object in targetRealm whose [[BooleanData]] is [[BooleanData]] of input.
- If input has a [[NumberData]] internal data property:
- Let output be a new Number object in targetRealm whose [[NumberData]] is [[NumberData]] of input.
- If input has a [[StringData]] internal data property:
- Let output be a new String object in targetRealm whose [[StringData]] is [[StringData]] of input.
- If input has a [[DateValue]] internal data property:
- Let output be a new Date object in targetRealm whose [[DateValue]] is [[DateValue]] of input.
- If input.[[RegExpMatcher]] exists:
- Let output be new RegExp object r in targetRealm such that:
- [[RegExpMatcher]] of r is [[RegExpMatcher]] of input.
- [[OriginalSource]] of r is [[OriginalSource]] of input.
- [[OriginalFlags]] of r is [[OriginalFlags]] of input.
- Let output be new RegExp object r in targetRealm such that:
- If input has [[ArrayBufferData]] internal data property:
- Set output to CopyArrayBufferToRealm(input, targetRealm).
- If input has [[ViewedArrayBuffer]] internal data property, then:
- let arrayBuffer be a value of input's [[ViewedArrayBuffer]] internal data property.
- let arrayBufferClone be InternalStructuredClone(arrayBuffer, memory, targetRealm)
- ReturnIfAbrupt(arrayBufferClone)
- if input instanceof %DataView% intrinsic object in current realm:
- Let output be an instance of %DataView% intrinsic object in targetRealm.
- Set output's [[ViewedArrayBuffer]] to arrayBufferClone.
- Set output's [[ByteOffset]] to input's [[ByteOffset]].
- Set output's [[ByteLength]] to input's [[ByteLength]].
- Otherwise, if input instanceof %TypedArray% for one of typed arrays' intrinsics TypedArray in
current code realm:
- Let output be an instance of %TypedArray% intrinsic object in targetRealm.
- Set output's [[ByteOffset]] to input's [[ByteOffset]].
- Set output's [[ByteLength]] to input's [[ByteLength]].
- Set output's [[ArrayLength]] to input's [[ArrayLength]].
- If input has [[MapData]] internal data property, ...
- If input has [[SetData]] internal data property, ...
- If input is an exotic Array object:
- Let output be a new Array in targetRealm.
- Set output.length to input.length.
- Set deepClone to true.
- Otherwise, if IsCallable( input), throw a DataCloneError exception.
- Otherwise, if input has [[ErrorData]] propety, throw a DataCloneError exception.
- Otherwise, if input has [[Clone]] internal method:
- Set output to a result of input.[[Clone]]( targetRealm )
- Otherwise, if input is an exotic object, throw a DataCloneError exception.
- Otherwise:
- Let object be a new Object in targetRealm.
- set deepClone to true.
- Add a mapping from input (the source object) to output (the destination object) to memory.
- If deepClone is true:
- Let keys be input.[[OwnPropertyKeys]]().
- For each key in keys:
- If key is a primitive String value, set outputKey to key
- TODO: Symbols
- Let sourceValue be a result of a call to input's internal method [[Get]]( key, input).
- ReturnIfAbrupt( sourceValue).
- Let clonedValue be InternalStructuredClone( sourceValue, memory).
- ReturnIfAbrupt( clonedValue).
- Let outputSet be a result of a call to output's internal method [[Set]]( outputKey, clonedValue, output).
- ReturnIfAbrupt( outputSet )
- Return output.
Definition of object.[[Transfer]]( targetRealm ):
- If object has an [[ArrayBufferData]] internal data property then:
- Return CopyArrayBufferToRealm(object, targetRealm).
- Let result be a new ArrayBuffer arrayBuffer in targetRealm.
- Let length be a value of arrayBuffer's [[ArrayBufferByteLength]] internal slot.
- Let srcBlock be the value of arrayBuffer's [[ArrayBufferData]] internal slot.
- Let setStatus be a result of SetArrayBufferData(result,length).
- ReturnIfAbrupt(setStatus).
- Let targetBlock be a value of result's [[ArrayBufferData]] internal slot.
- Perform CopyDataBlock(targetBlock, 0, srcBlock, 0, length).
- Return result.
Definition of internal method object.[[OnSuccessfulTransfer]]( transferResult, targetRealm ):
- If object has an [[ArrayBufferData]] internal data property then:
- Let neuteringResult be SetArrayBufferData( object, 0 ).
- ReturnIfAbrupt( neuteringResult ).
- Set value of object's [[Transfer]] internal data property to "neutered".
Indicates failure of the structured clone algorithm.
{Rationale: typically, ECMAScript operations throw RangeError for similar failures, but we need to preserve DOM compatibnility}