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.

  1. Let memory be a map of source-to-destination object mappings.
  2. For each object transferable in transferList:
    1. If transferable does not have a Transfer internal data property whose value is an operator, throw a DataCloneError exception.
    2. Let transferResult be a result of a call to a transferable’s internal method Transfer with argument targetRealm.
    3. ReturnIfAbrupt( transferResult )
    4. Append a mapping from transferable to transferResult to memory.
  3. Let clone be the result of InternalStructuredClone( input, memory, targetRealm ).
  4. ReturnIfAbrupt( clone ).
  5. For each object transferable in transferList:
    1. Let transferResult be a target of mapping from transferable in memory.
    2. Run transferable’s internal method OnSuccessfulTransfer(transferResult, targetRealm).
  6. Return clone.

InternalStructuredClone(input, memory, targetRealm)

The operator InternalStructuredClone either returns a structured clone of input in Code Realm targetRealm or throws an exception.

  1. If input is the source object of a pair of objects in memory, then return the destination object in that pair of objects.
  2. If input’s Transfer is “neutered”, throw a DataCloneError exception.
  3. If input is a primitive value, return input.
  4. Let deepClone be false.
  5. If input has a BooleanData internal data property:
    • Let output be a new Boolean object in targetRealm whose BooleanData is BooleanData of input.
  6. If input has a NumberData internal data property:
    • Let output be a new Number object in targetRealm whose NumberData is NumberData of input.
  7. If input has a StringData internal data property:
    • Let output be a new String object in targetRealm whose StringData is StringData of input.
  8. If input has a DateValue internal data property:
    • Let output be a new Date object in targetRealm whose DateValue is DateValue of input.
  9. 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.
  10. If input has ArrayBufferData internal data property:
    1. Set output to CopyArrayBufferToRealm(input, targetRealm).
  11. 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.
  12. If input has MapData internal data property, …
  13. If input has SetData internal data property, …
  14. If input is an exotic Array object:
    1. Let output be a new Array in targetRealm.
    2. Set output.length to input.length.
    3. Set deepClone to true.
  15. Otherwise, if IsCallable( input), throw a DataCloneError exception.
  16. Otherwise, if input has ErrorData propety, throw a DataCloneError exception.
  17. Otherwise, if input has Clone internal method: 4. Set output to a result of input.Clone( targetRealm )
  18. Otherwise, if input is an exotic object, throw a DataCloneError exception.
  19. Otherwise: 5. Let object be a new Object in targetRealm. 6. set deepClone to true.
  20. Add a mapping from input (the source object) to output (the destination object) to memory.
  21. If deepClone is true:
    1. Let keys be input.OwnPropertyKeys().
    1. For each key in keys:
    2. If key is a primitive String value, set outputKey to key
    3. TODO: Symbols
    4. Let sourceValue be a result of a call to input’s internal method Get( key, input).
    5. ReturnIfAbrupt( sourceValue).
    6. Let clonedValue be InternalStructuredClone( sourceValue, memory).
    7. ReturnIfAbrupt( clonedValue).
    8. Let outputSet be a result of a call to output’s internal method Set( outputKey, clonedValue, output).
    9. ReturnIfAbrupt( outputSet )
  22. Return output.

Definition of Transfer(targetRealm) on ECMAScript exotic objects.

Definition of object.Transfer ( targetRealm ):

  1. If object has an ArrayBufferData internal data property then:
  2. . Return CopyArrayBufferToRealm(object, targetRealm).

Definition of CopyArrayBufferToRealm(arrayBuffer, targetRealm)

  1. Let result be a new ArrayBuffer arrayBuffer in targetRealm.
  2. Let length be a value of arrayBuffer’s ArrayBufferByteLength internal slot.
  3. Let srcBlock be the value of arrayBuffer’s ArrayBufferData internal slot.
  4. Let setStatus be a result of SetArrayBufferData(result,length).
  5. ReturnIfAbrupt(setStatus).
  6. Let targetBlock be a value of result’s ArrayBufferData internal slot.
  7. Perform CopyDataBlock(targetBlock, 0, srcBlock, 0, length).
  8. Return result.

Definition of OnSuccessfulTransfer() on ECMAScript exotic objects.

Definition of internal method object. OnSuccessfulTransfer ( transferResult, targetRealm ):

  1. If object has an ArrayBufferData internal data property then:
    1. Let neuteringResult be SetArrayBufferData( object, 0 ).
    2. ReturnIfAbrupt( neuteringResult ).
    3. 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}