Stage 0
Classification: API Change
Human Validated: KW
Title: Structured Clone
Authors: Dmitry Lomov
Champions: Dmitry Lomov
Last Presented: Jan 2014
Stage Upgrades:
Stage 1: NA
Stage 2: NA
Stage 2.7: NA
Stage 3: NA
Stage 4: NA
Last Commit: 2014-01-21
Topics: realms others objects
Keywords: realm operator cloning
GitHub Link: https://github.com/dslomov/ecmascript-structured-clone
GitHub Note Link: https://github.com/tc39/notes/blob/HEAD/meetings/2014-01/jan-30.md#structured-clone
Proposal Description:
Structured cloning and transfer
Overview
Structured cloning algorithm defines the semantics of copying a well-defined subset of ECMAScript objects between Code Realms. This algorithm is extensible by host environment 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.
StructuredClone(input, transferList, targetRealm)
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.
InternalStructuredClone(input, memory, targetRealm)
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:
2. let arrayBuffer be a value of input’s ViewedArrayBuffer internal data property.
3. let arrayBufferClone be InternalStructuredClone(arrayBuffer, memory, targetRealm) 4. ReturnIfAbrupt(arrayBufferClone) 5. if input instanceof %DataView% intrinsic object in current realm: 1. Let output be an instance of %DataView% intrinsic object in targetRealm. 2. Set output’s ViewedArrayBuffer to arrayBufferClone. 3. Set output’s ByteOffset to input’s ByteOffset. 4. Set output’s ByteLength to input’s ByteLength. 6. 5. erwise, if input instanceof %TypedArray% for one of typed arrays’ intrinsics TypedArray in current code realm: 6. Let output be an instance of %TypedArray% intrinsic object in targetRealm. 7. Set output’s ByteOffset to input’s ByteOffset. 8. Set output’s ByteLength to input’s ByteLength. 9. 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: 4. Set output to a result of input.Clone( targetRealm )
- Otherwise, if input is an exotic object, throw a DataCloneError exception.
- Otherwise: 5. Let object be a new Object in targetRealm. 6. 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 Transfer(targetRealm) on ECMAScript exotic objects.
Definition of object.Transfer ( targetRealm ):
- If object has an ArrayBufferData internal data property then:
- . Return CopyArrayBufferToRealm(object, targetRealm).
Definition of CopyArrayBufferToRealm(arrayBuffer, 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 OnSuccessfulTransfer() on ECMAScript exotic objects.
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”.
DataCloneError error object
Indicates failure of the structured clone algorithm.
{Rationale: typically, ECMAScript operations throw RangeError for similar failures, but we need to preserve DOM compatibnility}