webutil: replace cloneValue impl with serialize/deserialize (#10215)

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Elad Keyshawn 2021-06-05 16:57:51 +03:00 committed by GitHub
parent 1d070f3d47
commit 4b3d55b449
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -35,86 +35,44 @@
/** Clone a value in a similar way to structured cloning. It is similar to a
* StructureDeserialize(StructuredSerialize(...)). */
function cloneValue(value) {
switch (typeof value) {
case "number":
case "string":
case "boolean":
case "undefined":
case "bigint":
return value;
case "object": {
if (objectCloneMemo.has(value)) {
return objectCloneMemo.get(value);
}
if (value === null) {
return value;
}
if (value instanceof Date) {
return new Date(value.valueOf());
}
if (value instanceof RegExp) {
return new RegExp(value);
}
if (value instanceof SharedArrayBuffer) {
return value;
}
if (value instanceof ArrayBuffer) {
const cloned = cloneArrayBuffer(
value,
0,
value.byteLength,
ArrayBuffer,
);
objectCloneMemo.set(value, cloned);
return cloned;
}
if (ArrayBuffer.isView(value)) {
const clonedBuffer = cloneValue(value.buffer);
// Use DataViewConstructor type purely for type-checking, can be a
// DataView or TypedArray. They use the same constructor signature,
// only DataView has a length in bytes and TypedArrays use a length in
// terms of elements, so we adjust for that.
let length;
if (value instanceof DataView) {
length = value.byteLength;
} else {
length = value.length;
}
return new (value.constructor)(
clonedBuffer,
value.byteOffset,
length,
);
}
if (value instanceof Map) {
const clonedMap = new Map();
objectCloneMemo.set(value, clonedMap);
value.forEach((v, k) => {
clonedMap.set(cloneValue(k), cloneValue(v));
});
return clonedMap;
}
if (value instanceof Set) {
// assumes that cloneValue still takes only one argument
const clonedSet = new Set([...value].map(cloneValue));
objectCloneMemo.set(value, clonedSet);
return clonedSet;
}
// default for objects
const clonedObj = {};
objectCloneMemo.set(value, clonedObj);
const sourceKeys = Object.getOwnPropertyNames(value);
for (const key of sourceKeys) {
clonedObj[key] = cloneValue(value[key]);
}
Reflect.setPrototypeOf(clonedObj, Reflect.getPrototypeOf(value));
return clonedObj;
// Performance optimization for buffers, otherwise
// `serialize/deserialize` will allocate new buffer.
if (value instanceof ArrayBuffer) {
const cloned = cloneArrayBuffer(
value,
0,
value.byteLength,
ArrayBuffer,
);
objectCloneMemo.set(value, cloned);
return cloned;
}
if (ArrayBuffer.isView(value)) {
const clonedBuffer = cloneValue(value.buffer);
// Use DataViewConstructor type purely for type-checking, can be a
// DataView or TypedArray. They use the same constructor signature,
// only DataView has a length in bytes and TypedArrays use a length in
// terms of elements, so we adjust for that.
let length;
if (value instanceof DataView) {
length = value.byteLength;
} else {
length = value.length;
}
case "symbol":
case "function":
default:
throw new DOMException("Uncloneable value in stream", "DataCloneError");
return new (value.constructor)(
clonedBuffer,
value.byteOffset,
length,
);
}
try {
return Deno.core.deserialize(Deno.core.serialize(value));
} catch (e) {
if (e instanceof TypeError) {
throw new DOMException("Uncloneable value", "DataCloneError");
}
throw e;
}
}