-
-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathstructured-clone.js
More file actions
124 lines (111 loc) · 4.17 KB
/
Copy pathstructured-clone.js
File metadata and controls
124 lines (111 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"use strict";
// WHATWG structuredClone(value, { transfer }): the argument coercion and the
// WebIDL sequence handling for `transfer`; the clone itself is native
// (v8::ValueSerializer round-tripped in this isolate).
//
// Deviation from the HTML spec, forced by the platform: ArrayBuffers and
// MessagePorts are transferable, nothing else is. ImageBitmap and the
// native/interop wrapper objects have no serialization form in this runtime,
// so they are rejected rather than half-supported. Clone failures are "DataCloneError"
// DOMExceptions, from here and from the native serializer alike
// (StructuredSerialization.cpp builds the same class).
const { clone } = binding;
const {
ArrayBufferPrototypeGetByteLength,
ArrayPrototypePush,
FunctionPrototypeCall,
ObjectPrototypeIsPrototypeOf,
SymbolIterator,
TypeError,
} = primordials;
var g = globalThis;
let DOMException;
function dataCloneError(message) {
if (DOMException === undefined) {
({ DOMException } = require("internal/dom-exception"));
}
return new DOMException(message, "DataCloneError");
}
// Brand check through the captured byteLength getter: it is the one thing only
// a real ArrayBuffer has, and it cannot be faked by a `Symbol.toStringTag` or a
// forged prototype. SharedArrayBuffer has its own getter and so fails here,
// which is what the spec wants — a SAB is not transferable.
function isArrayBuffer(value) {
if (value === null || typeof value !== "object") {
return false;
}
try {
ArrayBufferPrototypeGetByteLength(value);
return true;
} catch (notAnArrayBuffer) {
return false;
}
}
// The port check runs only for entries the ArrayBuffer test already rejected,
// so a transfer list of buffers never runs the messaging builtin.
let MessagePort;
function isMessagePort(value) {
if (value === null || typeof value !== "object") {
return false;
}
if (MessagePort === undefined) {
({ MessagePort } = require("internal/message-channel"));
}
return ObjectPrototypeIsPrototypeOf(MessagePort.prototype, value);
}
// WebIDL `sequence<object>` conversion: only an object with a callable
// @@iterator qualifies, which is why a string primitive is a TypeError even
// though strings are iterable.
function toTransferList(value) {
if (value === null || (typeof value !== "object" && typeof value !== "function")) {
throw new TypeError("structuredClone: transfer is not iterable");
}
var method = value[SymbolIterator];
if (typeof method !== "function") {
throw new TypeError("structuredClone: transfer is not iterable");
}
var iterator = FunctionPrototypeCall(method, value);
if (iterator === null || typeof iterator !== "object") {
throw new TypeError("structuredClone: transfer is not iterable");
}
// The iterator record captures `next` once, when it is created — re-reading
// it per step would expose a `next` that changes mid-iteration.
var next = iterator.next;
if (typeof next !== "function") {
throw new TypeError("structuredClone: transfer is not iterable");
}
var list = [];
for (;;) {
var step = FunctionPrototypeCall(next, iterator);
if (step === null || typeof step !== "object") {
throw new TypeError("structuredClone: transfer iterator returned a non-object");
}
if (step.done) {
break;
}
var item = step.value;
if (!isArrayBuffer(item) && !isMessagePort(item)) {
throw dataCloneError("structuredClone: value in transfer list is not transferable");
}
ArrayPrototypePush(list, item);
}
return list;
}
// `options` is defaulted rather than merely optional so that the function's
// reported arity is 1, as the IDL requires.
g.structuredClone = function structuredClone(value, options = undefined) {
if (arguments.length < 1) {
throw new TypeError("structuredClone: 1 argument required, but only 0 present");
}
var transfer;
if (options !== undefined && options !== null) {
if (typeof options !== "object" && typeof options !== "function") {
throw new TypeError("structuredClone: options is not an object");
}
var requested = options.transfer;
if (requested !== undefined) {
transfer = toTransferList(requested);
}
}
return clone(value, transfer);
};