aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-04-09 19:53:17 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-04-09 19:53:17 -0700
commitc5d637fbb15874caa0d9669ac8cc79912ed4d5d9 (patch)
treef20a9b0047ba763b0003a60032d6c4ae1f9dcb47 /src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp
parentfb82e2bf86a13211f08b407c3ec80fc0db45a77c (diff)
downloadbun-jarred/workers.tar.gz
bun-jarred/workers.tar.zst
bun-jarred/workers.zip
Diffstat (limited to 'src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp')
-rw-r--r--src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp4617
1 files changed, 4617 insertions, 0 deletions
diff --git a/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp
new file mode 100644
index 000000000..ec6d040f9
--- /dev/null
+++ b/src/javascript/jsc/bindings/webcore/SerializedScriptValue.cpp
@@ -0,0 +1,4617 @@
+/*
+ * Copyright (C) 2009-2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+#include "SerializedScriptValue.h"
+
+// #include "BlobRegistry.h"
+// #include "CryptoKeyAES.h"
+// #include "CryptoKeyEC.h"
+// #include "CryptoKeyHMAC.h"
+// #include "CryptoKeyRSA.h"
+// #include "CryptoKeyRSAComponents.h"
+// #include "CryptoKeyRaw.h"
+// #include "IDBValue.h"
+// #include "ImageBitmapBacking.h"
+// #include "JSBlob.h"
+// #include "JSCryptoKey.h"
+#include "JSDOMBinding.h"
+#include "JSDOMConvertBufferSource.h"
+#include "JSDOMException.h"
+#include "JSDOMGlobalObject.h"
+#include "JSDOMMatrix.h"
+#include "JSDOMPoint.h"
+#include "JSDOMQuad.h"
+#include "JSDOMRect.h"
+#include "JSExecState.h"
+#include "JSFile.h"
+#include "JSFileList.h"
+// #include "JSIDBSerializationGlobalObject.h"
+// #include "JSImageBitmap.h"
+// #include "JSImageData.h"
+// #include "JSMessagePort.h"
+#include "JSNavigator.h"
+#include "JSRTCCertificate.h"
+#include "JSRTCDataChannel.h"
+#include "ScriptExecutionContext.h"
+#include "SharedBuffer.h"
+#include "WebCoreJSClientData.h"
+#include <JavaScriptCore/APICast.h>
+#include <JavaScriptCore/BigIntObject.h>
+#include <JavaScriptCore/BooleanObject.h>
+#include <JavaScriptCore/CatchScope.h>
+#include <JavaScriptCore/DateInstance.h>
+#include <JavaScriptCore/Error.h>
+#include <JavaScriptCore/Exception.h>
+#include <JavaScriptCore/ExceptionHelpers.h>
+#include <JavaScriptCore/IterationKind.h>
+#include <JavaScriptCore/JSArrayBuffer.h>
+#include <JavaScriptCore/JSArrayBufferView.h>
+#include <JavaScriptCore/JSCInlines.h>
+#include <JavaScriptCore/JSDataView.h>
+#include <JavaScriptCore/JSMapInlines.h>
+#include <JavaScriptCore/JSMapIterator.h>
+#include <JavaScriptCore/JSSetInlines.h>
+#include <JavaScriptCore/JSSetIterator.h>
+#include <JavaScriptCore/JSTypedArrays.h>
+#include <JavaScriptCore/JSWebAssemblyMemory.h>
+#include <JavaScriptCore/JSWebAssemblyModule.h>
+#include <JavaScriptCore/ObjectConstructor.h>
+#include <JavaScriptCore/PropertyNameArray.h>
+#include <JavaScriptCore/RegExp.h>
+#include <JavaScriptCore/RegExpObject.h>
+#include <JavaScriptCore/TypedArrayInlines.h>
+#include <JavaScriptCore/TypedArrays.h>
+#include <JavaScriptCore/WasmModule.h>
+#include <JavaScriptCore/YarrFlags.h>
+#include <limits>
+#include <wtf/CheckedArithmetic.h>
+#include <wtf/CompletionHandler.h>
+#include <wtf/MainThread.h>
+#include <wtf/RunLoop.h>
+#include <wtf/Vector.h>
+#include <wtf/threads/BinarySemaphore.h>
+
+#if USE(CG)
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+#if PLATFORM(COCOA)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+#include "JSOffscreenCanvas.h"
+#include "OffscreenCanvas.h"
+#endif
+
+#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
+#define ASSUME_LITTLE_ENDIAN 0
+#else
+#define ASSUME_LITTLE_ENDIAN 1
+#endif
+
+namespace WebCore {
+
+using namespace JSC;
+
+DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SerializedScriptValue);
+
+static const unsigned maximumFilterRecursion = 40000;
+
+enum class SerializationReturnCode {
+ SuccessfullyCompleted,
+ StackOverflowError,
+ InterruptedExecutionError,
+ ValidationError,
+ ExistingExceptionError,
+ DataCloneError,
+ UnspecifiedError
+};
+
+enum WalkerState { StateUnknown,
+ ArrayStartState,
+ ArrayStartVisitMember,
+ ArrayEndVisitMember,
+ ObjectStartState,
+ ObjectStartVisitMember,
+ ObjectEndVisitMember,
+ MapDataStartVisitEntry,
+ MapDataEndVisitKey,
+ MapDataEndVisitValue,
+ SetDataStartVisitEntry,
+ SetDataEndVisitKey };
+
+// These can't be reordered, and any new types must be added to the end of the list
+// When making changes to these lists please cover your new type(s) in the API test "IndexedDB.StructuredCloneBackwardCompatibility"
+enum SerializationTag {
+ ArrayTag = 1,
+ ObjectTag = 2,
+ UndefinedTag = 3,
+ NullTag = 4,
+ IntTag = 5,
+ ZeroTag = 6,
+ OneTag = 7,
+ FalseTag = 8,
+ TrueTag = 9,
+ DoubleTag = 10,
+ DateTag = 11,
+ FileTag = 12,
+ FileListTag = 13,
+ ImageDataTag = 14,
+ BlobTag = 15,
+ StringTag = 16,
+ EmptyStringTag = 17,
+ RegExpTag = 18,
+ ObjectReferenceTag = 19,
+ MessagePortReferenceTag = 20,
+ ArrayBufferTag = 21,
+ ArrayBufferViewTag = 22,
+ ArrayBufferTransferTag = 23,
+ TrueObjectTag = 24,
+ FalseObjectTag = 25,
+ StringObjectTag = 26,
+ EmptyStringObjectTag = 27,
+ NumberObjectTag = 28,
+ SetObjectTag = 29,
+ MapObjectTag = 30,
+ NonMapPropertiesTag = 31,
+ NonSetPropertiesTag = 32,
+#if ENABLE(WEB_CRYPTO)
+ CryptoKeyTag = 33,
+#endif
+ SharedArrayBufferTag = 34,
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleTag = 35,
+#endif
+ DOMPointReadOnlyTag = 36,
+ DOMPointTag = 37,
+ DOMRectReadOnlyTag = 38,
+ DOMRectTag = 39,
+ DOMMatrixReadOnlyTag = 40,
+ DOMMatrixTag = 41,
+ DOMQuadTag = 42,
+ ImageBitmapTransferTag = 43,
+#if ENABLE(WEB_RTC)
+ RTCCertificateTag = 44,
+#endif
+ ImageBitmapTag = 45,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ OffscreenCanvasTransferTag = 46,
+#endif
+ BigIntTag = 47,
+ BigIntObjectTag = 48,
+#if ENABLE(WEBASSEMBLY)
+ WasmMemoryTag = 49,
+#endif
+#if ENABLE(WEB_RTC)
+ RTCDataChannelTransferTag = 50,
+#endif
+ DOMExceptionTag = 51,
+ ErrorTag = 255
+};
+
+enum ArrayBufferViewSubtag {
+ DataViewTag = 0,
+ Int8ArrayTag = 1,
+ Uint8ArrayTag = 2,
+ Uint8ClampedArrayTag = 3,
+ Int16ArrayTag = 4,
+ Uint16ArrayTag = 5,
+ Int32ArrayTag = 6,
+ Uint32ArrayTag = 7,
+ Float32ArrayTag = 8,
+ Float64ArrayTag = 9,
+ BigInt64ArrayTag = 10,
+ BigUint64ArrayTag = 11,
+};
+
+static unsigned typedArrayElementSize(ArrayBufferViewSubtag tag)
+{
+ switch (tag) {
+ case DataViewTag:
+ case Int8ArrayTag:
+ case Uint8ArrayTag:
+ case Uint8ClampedArrayTag:
+ return 1;
+ case Int16ArrayTag:
+ case Uint16ArrayTag:
+ return 2;
+ case Int32ArrayTag:
+ case Uint32ArrayTag:
+ case Float32ArrayTag:
+ return 4;
+ case Float64ArrayTag:
+ case BigInt64ArrayTag:
+ case BigUint64ArrayTag:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+enum class PredefinedColorSpaceTag : uint8_t {
+ SRGB = 0
+#if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3)
+ ,
+ DisplayP3 = 1
+#endif
+};
+
+enum DestinationColorSpaceTag {
+ DestinationColorSpaceSRGBTag = 0,
+#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB)
+ DestinationColorSpaceLinearSRGBTag = 1,
+#endif
+#if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3)
+ DestinationColorSpaceDisplayP3Tag = 2,
+#endif
+#if PLATFORM(COCOA)
+ DestinationColorSpaceCGColorSpaceNameTag = 3,
+ DestinationColorSpaceCGColorSpacePropertyListTag = 4,
+#endif
+};
+
+#if ENABLE(WEB_CRYPTO)
+
+const uint32_t currentKeyFormatVersion = 1;
+
+enum class CryptoKeyClassSubtag {
+ HMAC = 0,
+ AES = 1,
+ RSA = 2,
+ EC = 3,
+ Raw = 4,
+};
+const uint8_t cryptoKeyClassSubtagMaximumValue = 4;
+
+enum class CryptoKeyAsymmetricTypeSubtag {
+ Public = 0,
+ Private = 1
+};
+const uint8_t cryptoKeyAsymmetricTypeSubtagMaximumValue = 1;
+
+enum class CryptoKeyUsageTag {
+ Encrypt = 0,
+ Decrypt = 1,
+ Sign = 2,
+ Verify = 3,
+ DeriveKey = 4,
+ DeriveBits = 5,
+ WrapKey = 6,
+ UnwrapKey = 7
+};
+const uint8_t cryptoKeyUsageTagMaximumValue = 7;
+
+enum class CryptoAlgorithmIdentifierTag {
+ RSAES_PKCS1_v1_5 = 0,
+ RSASSA_PKCS1_v1_5 = 1,
+ RSA_PSS = 2,
+ RSA_OAEP = 3,
+ ECDSA = 4,
+ ECDH = 5,
+ AES_CTR = 6,
+ AES_CBC = 7,
+ AES_GCM = 9,
+ AES_CFB = 10,
+ AES_KW = 11,
+ HMAC = 12,
+ SHA_1 = 14,
+ SHA_224 = 15,
+ SHA_256 = 16,
+ SHA_384 = 17,
+ SHA_512 = 18,
+ HKDF = 20,
+ PBKDF2 = 21,
+};
+const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 21;
+
+static unsigned countUsages(CryptoKeyUsageBitmap usages)
+{
+ // Fast bit count algorithm for sparse bit maps.
+ unsigned count = 0;
+ while (usages) {
+ usages = usages & (usages - 1);
+ ++count;
+ }
+ return count;
+}
+
+#endif
+
+/* CurrentVersion tracks the serialization version so that persistent stores
+ * are able to correctly bail out in the case of encountering newer formats.
+ *
+ * Initial version was 1.
+ * Version 2. added the ObjectReferenceTag and support for serialization of cyclic graphs.
+ * Version 3. added the FalseObjectTag, TrueObjectTag, NumberObjectTag, StringObjectTag
+ * and EmptyStringObjectTag for serialization of Boolean, Number and String objects.
+ * Version 4. added support for serializing non-index properties of arrays.
+ * Version 5. added support for Map and Set types.
+ * Version 6. added support for 8-bit strings.
+ * Version 7. added support for File's lastModified attribute.
+ * Version 8. added support for ImageData's colorSpace attribute.
+ * Version 9. added support for ImageBitmap color space.
+ * Version 10. changed the length (and offsets) of ArrayBuffers (and ArrayBufferViews) from 32 to 64 bits
+ */
+static const unsigned CurrentVersion = 10;
+static const unsigned TerminatorTag = 0xFFFFFFFF;
+static const unsigned StringPoolTag = 0xFFFFFFFE;
+static const unsigned NonIndexPropertiesTag = 0xFFFFFFFD;
+
+// The high bit of a StringData's length determines the character size.
+static const unsigned StringDataIs8BitFlag = 0x80000000;
+
+/*
+ * Object serialization is performed according to the following grammar, all tags
+ * are recorded as a single uint8_t.
+ *
+ * IndexType (used for the object pool and StringData's constant pool) is the
+ * minimum sized unsigned integer type required to represent the maximum index
+ * in the constant pool.
+ *
+ * SerializedValue :- <CurrentVersion:uint32_t> Value
+ * Value :- Array | Object | Map | Set | Terminal
+ *
+ * Array :-
+ * ArrayTag <length:uint32_t>(<index:uint32_t><value:Value>)* TerminatorTag
+ *
+ * Object :-
+ * ObjectTag (<name:StringData><value:Value>)* TerminatorTag
+ *
+ * Map :- MapObjectTag MapData
+ *
+ * Set :- SetObjectTag SetData
+ *
+ * MapData :- (<key:Value><value:Value>)* NonMapPropertiesTag (<name:StringData><value:Value>)* TerminatorTag
+ * SetData :- (<key:Value>)* NonSetPropertiesTag (<name:StringData><value:Value>)* TerminatorTag
+ *
+ * Terminal :-
+ * UndefinedTag
+ * | NullTag
+ * | IntTag <value:int32_t>
+ * | ZeroTag
+ * | OneTag
+ * | FalseTag
+ * | TrueTag
+ * | FalseObjectTag
+ * | TrueObjectTag
+ * | DoubleTag <value:double>
+ * | NumberObjectTag <value:double>
+ * | DateTag <value:double>
+ * | String
+ * | EmptyStringTag
+ * | EmptyStringObjectTag
+ * | BigInt
+ * | File
+ * | FileList
+ * | ImageData
+ * | Blob
+ * | ObjectReference
+ * | MessagePortReferenceTag <value:uint32_t>
+ * | ArrayBuffer
+ * | ArrayBufferViewTag ArrayBufferViewSubtag <byteOffset:uint64_t> <byteLength:uint64_t> (ArrayBuffer | ObjectReference)
+ * | CryptoKeyTag <wrappedKeyLength:uint32_t> <factor:byte{wrappedKeyLength}>
+ * | DOMPoint
+ * | DOMRect
+ * | DOMMatrix
+ * | DOMQuad
+ * | ImageBitmapTransferTag <value:uint32_t>
+ * | RTCCertificateTag
+ * | ImageBitmapTag <originClean:uint8_t> <logicalWidth:int32_t> <logicalHeight:int32_t> <resolutionScale:double> DestinationColorSpace <byteLength:uint32_t>(<imageByteData:uint8_t>)
+ * | OffscreenCanvasTransferTag <value:uint32_t>
+ * | WasmMemoryTag <value:uint32_t>
+ * | RTCDataChannelTransferTag <processIdentifier:uint64_t><rtcDataChannelIdentifier:uint64_t><label:String>
+ * | DOMExceptionTag <message:String> <name:String>
+ *
+ * Inside certificate, data is serialized in this format as per spec:
+ *
+ * <expires:double> <certificate:StringData> <origin:StringData> <keyingMaterial:StringData>
+ * We also add fingerprints to make sure we expose to JavaScript the same information.
+ *
+ * Inside wrapped crypto key, data is serialized in this format:
+ *
+ * <keyFormatVersion:uint32_t> <extractable:int32_t> <usagesCount:uint32_t> <usages:byte{usagesCount}> CryptoKeyClassSubtag (CryptoKeyHMAC | CryptoKeyAES | CryptoKeyRSA)
+ *
+ * String :-
+ * EmptyStringTag
+ * StringTag StringData
+ *
+ * StringObject:
+ * EmptyStringObjectTag
+ * StringObjectTag StringData
+ *
+ * StringData :-
+ * StringPoolTag <cpIndex:IndexType>
+ * (not (TerminatorTag | StringPoolTag))<is8Bit:uint32_t:1><length:uint32_t:31><characters:CharType{length}> // Added to constant pool when seen, string length 0xFFFFFFFF is disallowed
+ *
+ * BigInt :-
+ * BigIntTag BigIntData
+ * BigIntObjectTag BigIntData
+ *
+ * BigIntData :-
+ * <sign:uint8_t> <lengthInUint64:uint32_t> <contents:uint64_t{lengthInUint64}>
+ *
+ * File :-
+ * FileTag FileData
+ *
+ * FileData :-
+ * <path:StringData> <url:StringData> <type:StringData> <name:StringData> <lastModified:double>
+ *
+ * FileList :-
+ * FileListTag <length:uint32_t>(<file:FileData>){length}
+ *
+ * ImageData :-
+ * ImageDataTag <width:int32_t> <height:int32_t> <length:uint32_t> <data:uint8_t{length}> <colorSpace:PredefinedColorSpaceTag>
+ *
+ * Blob :-
+ * BlobTag <url:StringData><type:StringData><size:long long>
+ *
+ * RegExp :-
+ * RegExpTag <pattern:StringData><flags:StringData>
+ *
+ * ObjectReference :-
+ * ObjectReferenceTag <opIndex:IndexType>
+ *
+ * ArrayBuffer :-
+ * ArrayBufferTag <length:uint64_t> <contents:byte{length}>
+ * ArrayBufferTransferTag <value:uint32_t>
+ * SharedArrayBufferTag <value:uint32_t>
+ *
+ * CryptoKeyHMAC :-
+ * <keySize:uint32_t> <keyData:byte{keySize}> CryptoAlgorithmIdentifierTag // Algorithm tag inner hash function.
+ *
+ * CryptoKeyAES :-
+ * CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}>
+ *
+ * CryptoKeyRSA :-
+ * CryptoAlgorithmIdentifierTag <isRestrictedToHash:int32_t> CryptoAlgorithmIdentifierTag? CryptoKeyAsymmetricTypeSubtag CryptoKeyRSAPublicComponents CryptoKeyRSAPrivateComponents?
+ *
+ * CryptoKeyRSAPublicComponents :-
+ * <modulusSize:uint32_t> <modulus:byte{modulusSize}> <exponentSize:uint32_t> <exponent:byte{exponentSize}>
+ *
+ * CryptoKeyRSAPrivateComponents :-
+ * <privateExponentSize:uint32_t> <privateExponent:byte{privateExponentSize}> <primeCount:uint32_t> FirstPrimeInfo? PrimeInfo{primeCount - 1}
+ *
+ * // CRT data could be computed from prime factors. It is only serialized to reuse a code path that's needed for JWK.
+ * FirstPrimeInfo :-
+ * <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}>
+ *
+ * PrimeInfo :-
+ * <factorSize:uint32_t> <factor:byte{factorSize}> <crtExponentSize:uint32_t> <crtExponent:byte{crtExponentSize}> <crtCoefficientSize:uint32_t> <crtCoefficient:byte{crtCoefficientSize}>
+ *
+ * CryptoKeyEC :-
+ * CryptoAlgorithmIdentifierTag <namedCurve:StringData> CryptoKeyAsymmetricTypeSubtag <keySize:uint32_t> <keyData:byte{keySize}>
+ *
+ * CryptoKeyRaw :-
+ * CryptoAlgorithmIdentifierTag <keySize:uint32_t> <keyData:byte{keySize}>
+ *
+ * DOMPoint :-
+ * DOMPointReadOnlyTag DOMPointData
+ * | DOMPointTag DOMPointData
+ *
+ * DOMPointData :-
+ * <x:double> <y:double> <z:double> <w:double>
+ *
+ * DOMRect :-
+ * DOMRectReadOnlyTag DOMRectData
+ * | DOMRectTag DOMRectData
+ *
+ * DOMRectData :-
+ * <x:double> <y:double> <width:double> <height:double>
+ *
+ * DOMMatrix :-
+ * DOMMatrixReadOnlyTag DOMMatrixData
+ * | DOMMatrixTag DOMMatrixData
+ *
+ * DOMMatrixData :-
+ * <is2D:uint8_t:true> <m11:double> <m12:double> <m21:double> <m22:double> <m41:double> <m42:double>
+ * | <is2D:uint8_t:false> <m11:double> <m12:double> <m13:double> <m14:double> <m21:double> <m22:double> <m23:double> <m24:double> <m31:double> <m32:double> <m33:double> <m34:double> <m41:double> <m42:double> <m43:double> <m44:double>
+ *
+ * DOMQuad :-
+ * DOMQuadTag DOMQuadData
+ *
+ * DOMQuadData :-
+ * <p1:DOMPointData> <p2:DOMPointData> <p3:DOMPointData> <p4:DOMPointData>
+ *
+ * DestinationColorSpace :-
+ * DestinationColorSpaceSRGBTag
+ * | DestinationColorSpaceLinearSRGBTag
+ * | DestinationColorSpaceDisplayP3Tag
+ * | DestinationColorSpaceCGColorSpaceNameTag <nameDataLength:uint32_t> <nameData:uint8_t>{nameDataLength}
+ * | DestinationColorSpaceCGColorSpacePropertyListTag <propertyListDataLength:uint32_t> <propertyListData:uint8_t>{propertyListDataLength}
+ */
+
+using DeserializationResult = std::pair<JSC::JSValue, SerializationReturnCode>;
+
+class CloneBase {
+protected:
+ CloneBase(JSGlobalObject* lexicalGlobalObject)
+ : m_lexicalGlobalObject(lexicalGlobalObject)
+ , m_failed(false)
+ {
+ }
+
+ void fail()
+ {
+ m_failed = true;
+ }
+
+ JSGlobalObject* m_lexicalGlobalObject;
+ bool m_failed;
+ MarkedArgumentBuffer m_gcBuffer;
+};
+
+#if ENABLE(WEB_CRYPTO)
+static bool wrapCryptoKey(JSGlobalObject* lexicalGlobalObject, const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey)
+{
+ auto context = executionContext(lexicalGlobalObject);
+ return context && context->wrapCryptoKey(key, wrappedKey);
+}
+
+static bool unwrapCryptoKey(JSGlobalObject* lexicalGlobalObject, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
+{
+ auto context = executionContext(lexicalGlobalObject);
+ return context && context->unwrapCryptoKey(wrappedKey, key);
+}
+#endif
+
+#if ASSUME_LITTLE_ENDIAN
+template<typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
+{
+ buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
+}
+#else
+template<typename T> static void writeLittleEndian(Vector<uint8_t>& buffer, T value)
+{
+ for (unsigned i = 0; i < sizeof(T); i++) {
+ buffer.append(value & 0xFF);
+ value >>= 8;
+ }
+}
+#endif
+
+template<> void writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, uint8_t value)
+{
+ buffer.append(value);
+}
+
+template<typename T> static bool writeLittleEndian(Vector<uint8_t>& buffer, const T* values, uint32_t length)
+{
+ if (length > std::numeric_limits<uint32_t>::max() / sizeof(T))
+ return false;
+
+#if ASSUME_LITTLE_ENDIAN
+ buffer.append(reinterpret_cast<const uint8_t*>(values), length * sizeof(T));
+#else
+ for (unsigned i = 0; i < length; i++) {
+ T value = values[i];
+ for (unsigned j = 0; j < sizeof(T); j++) {
+ buffer.append(static_cast<uint8_t>(value & 0xFF));
+ value >>= 8;
+ }
+ }
+#endif
+ return true;
+}
+
+template<> bool writeLittleEndian<uint8_t>(Vector<uint8_t>& buffer, const uint8_t* values, uint32_t length)
+{
+ buffer.append(values, length);
+ return true;
+}
+
+class CloneSerializer : CloneBase {
+public:
+ static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, const Vector<RefPtr<ImageBitmap>>& imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ const Vector<Ref<RTCDataChannel>>& rtcDataChannels,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray& wasmModules,
+ WasmMemoryHandleArray& wasmMemoryHandles,
+#endif
+ /* Vector<BlobURLHandle>& blobHandles,*/ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers)
+ {
+ CloneSerializer serializer(lexicalGlobalObject, messagePorts, arrayBuffers, imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ rtcDataChannels,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ wasmModules,
+ wasmMemoryHandles,
+#endif
+ blobHandles, out, context, sharedBuffers);
+ return serializer.serialize(value);
+ }
+
+ static bool serialize(StringView string, Vector<uint8_t>& out)
+ {
+ writeLittleEndian(out, CurrentVersion);
+ if (string.isEmpty()) {
+ writeLittleEndian<uint8_t>(out, EmptyStringTag);
+ return true;
+ }
+ writeLittleEndian<uint8_t>(out, StringTag);
+ if (string.is8Bit()) {
+ writeLittleEndian(out, string.length() | StringDataIs8BitFlag);
+ return writeLittleEndian(out, string.characters8(), string.length());
+ }
+ writeLittleEndian(out, string.length());
+ return writeLittleEndian(out, string.characters16(), string.length());
+ }
+
+private:
+ typedef HashMap<JSObject*, uint32_t> ObjectPool;
+
+ CloneSerializer(JSGlobalObject* lexicalGlobalObject, Vector<RefPtr<MessagePort>>& messagePorts, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers, /*const Vector<RefPtr<ImageBitmap>>& imageBitmaps,*/
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ const Vector<Ref<RTCDataChannel>>& rtcDataChannels,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray& wasmModules,
+ WasmMemoryHandleArray& wasmMemoryHandles,
+#endif
+ /*Vector<BlobURLHandle>& blobHandles,*/ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers)
+ : CloneBase(lexicalGlobalObject)
+ , m_buffer(out)
+ // , m_blobHandles(blobHandles)
+ , m_emptyIdentifier(Identifier::fromString(lexicalGlobalObject->vm(), emptyString()))
+ , m_context(context)
+ , m_sharedBuffers(sharedBuffers)
+#if ENABLE(WEBASSEMBLY)
+ , m_wasmModules(wasmModules)
+ , m_wasmMemoryHandles(wasmMemoryHandles)
+#endif
+ {
+ write(CurrentVersion);
+ fillTransferMap(messagePorts, m_transferredMessagePorts);
+ fillTransferMap(arrayBuffers, m_transferredArrayBuffers);
+ // fillTransferMap(imageBitmaps, m_transferredImageBitmaps);
+ // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ // fillTransferMap(offscreenCanvases, m_transferredOffscreenCanvases);
+ // #endif
+ // #if ENABLE(WEB_RTC)
+ // fillTransferMap(rtcDataChannels, m_transferredRTCDataChannels);
+ // #endif
+ }
+
+ template<class T>
+ void fillTransferMap(const Vector<RefPtr<T>>& input, ObjectPool& result)
+ {
+ if (input.isEmpty())
+ return;
+ JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(m_lexicalGlobalObject);
+ for (size_t i = 0; i < input.size(); i++) {
+ JSC::JSValue value = toJS(m_lexicalGlobalObject, globalObject, input[i].get());
+ JSC::JSObject* obj = value.getObject();
+ if (obj && !result.contains(obj))
+ result.add(obj, i);
+ }
+ }
+ template<class T>
+ void fillTransferMap(const Vector<Ref<T>>& input, ObjectPool& result)
+ {
+ if (input.isEmpty())
+ return;
+ JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(m_lexicalGlobalObject);
+ for (size_t i = 0; i < input.size(); i++) {
+ JSC::JSValue value = toJS(m_lexicalGlobalObject, globalObject, input[i].get());
+ JSC::JSObject* obj = value.getObject();
+ if (obj && !result.contains(obj))
+ result.add(obj, i);
+ }
+ }
+
+ SerializationReturnCode serialize(JSValue in);
+
+ bool isArray(VM& vm, JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSArray>(vm);
+ }
+
+ bool isMap(VM& vm, JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSMap>(vm);
+ }
+ bool isSet(VM& vm, JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSSet>(vm);
+ }
+
+ bool checkForDuplicate(JSObject* object)
+ {
+ // Record object for graph reconstruction
+ ObjectPool::const_iterator found = m_objectPool.find(object);
+
+ // Handle duplicate references
+ if (found != m_objectPool.end()) {
+ write(ObjectReferenceTag);
+ ASSERT(found->value < m_objectPool.size());
+ writeObjectIndex(found->value);
+ return true;
+ }
+
+ return false;
+ }
+
+ void recordObject(JSObject* object)
+ {
+ m_objectPool.add(object, m_objectPool.size());
+ m_gcBuffer.appendWithCrashOnOverflow(object);
+ }
+
+ bool startObjectInternal(JSObject* object)
+ {
+ if (checkForDuplicate(object))
+ return false;
+ recordObject(object);
+ return true;
+ }
+
+ bool startObject(JSObject* object)
+ {
+ if (!startObjectInternal(object))
+ return false;
+ write(ObjectTag);
+ return true;
+ }
+
+ bool startArray(JSArray* array)
+ {
+ if (!startObjectInternal(array))
+ return false;
+
+ unsigned length = array->length();
+ write(ArrayTag);
+ write(length);
+ return true;
+ }
+
+ bool startSet(JSSet* set)
+ {
+ if (!startObjectInternal(set))
+ return false;
+
+ write(SetObjectTag);
+ return true;
+ }
+
+ bool startMap(JSMap* map)
+ {
+ if (!startObjectInternal(map))
+ return false;
+
+ write(MapObjectTag);
+ return true;
+ }
+
+ void endObject()
+ {
+ write(TerminatorTag);
+ }
+
+ JSValue getProperty(VM& vm, JSObject* object, const Identifier& propertyName)
+ {
+ PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
+ if (object->methodTable(vm)->getOwnPropertySlot(object, m_lexicalGlobalObject, propertyName, slot))
+ return slot.getValue(m_lexicalGlobalObject, propertyName);
+ return JSValue();
+ }
+
+ void dumpImmediate(JSValue value, SerializationReturnCode& code)
+ {
+ if (value.isNull()) {
+ write(NullTag);
+ return;
+ }
+ if (value.isUndefined()) {
+ write(UndefinedTag);
+ return;
+ }
+ if (value.isNumber()) {
+ if (value.isInt32()) {
+ if (!value.asInt32())
+ write(ZeroTag);
+ else if (value.asInt32() == 1)
+ write(OneTag);
+ else {
+ write(IntTag);
+ write(static_cast<uint32_t>(value.asInt32()));
+ }
+ } else {
+ write(DoubleTag);
+ write(value.asDouble());
+ }
+ return;
+ }
+ if (value.isBoolean()) {
+ if (value.isTrue())
+ write(TrueTag);
+ else
+ write(FalseTag);
+ return;
+ }
+#if USE(BIGINT32)
+ if (value.isBigInt32()) {
+ write(BigIntTag);
+ dumpBigIntData(value);
+ return;
+ }
+#endif
+
+ // Make any new primitive extension safe by throwing an error.
+ code = SerializationReturnCode::DataCloneError;
+ }
+
+ void dumpString(const String& string)
+ {
+ if (string.isEmpty())
+ write(EmptyStringTag);
+ else {
+ write(StringTag);
+ write(string);
+ }
+ }
+
+ void dumpStringObject(const String& string)
+ {
+ if (string.isEmpty())
+ write(EmptyStringObjectTag);
+ else {
+ write(StringObjectTag);
+ write(string);
+ }
+ }
+
+ void dumpBigIntData(JSValue value)
+ {
+ ASSERT(value.isBigInt());
+#if USE(BIGINT32)
+ if (value.isBigInt32()) {
+ dumpBigInt32Data(value.bigInt32AsInt32());
+ return;
+ }
+#endif
+ dumpHeapBigIntData(jsCast<JSBigInt*>(value));
+ }
+
+#if USE(BIGINT32)
+ void dumpBigInt32Data(int32_t integer)
+ {
+ write(static_cast<uint8_t>(integer < 0));
+ if (!integer) {
+ write(static_cast<uint32_t>(0)); // Length-in-uint64_t
+ return;
+ }
+ write(static_cast<uint32_t>(1)); // Length-in-uint64_t
+ int64_t value = static_cast<int64_t>(integer);
+ if (value < 0)
+ value = -value;
+ write(static_cast<uint64_t>(value));
+ }
+#endif
+
+ void dumpHeapBigIntData(JSBigInt* bigInt)
+ {
+ write(static_cast<uint8_t>(bigInt->sign()));
+ if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
+ write(static_cast<uint32_t>(bigInt->length()));
+ for (unsigned index = 0; index < bigInt->length(); ++index)
+ write(static_cast<uint64_t>(bigInt->digit(index)));
+ } else {
+ ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
+ uint32_t lengthInUint64 = bigInt->length() / 2;
+ if (bigInt->length() & 0x1)
+ ++lengthInUint64;
+ write(lengthInUint64);
+ uint64_t value = 0;
+ for (unsigned index = 0; index < bigInt->length(); ++index) {
+ if (!(index & 0x1))
+ value = bigInt->digit(index);
+ else {
+ value = (static_cast<uint64_t>(bigInt->digit(index)) << 32) | value;
+ write(static_cast<uint64_t>(value));
+ value = 0;
+ }
+ }
+ if (bigInt->length() & 0x1)
+ write(static_cast<uint64_t>(value));
+ }
+ }
+
+ JSC::JSValue toJSArrayBuffer(ArrayBuffer& arrayBuffer)
+ {
+ auto& vm = m_lexicalGlobalObject->vm();
+ auto* globalObject = m_lexicalGlobalObject;
+ if (globalObject->inherits<JSDOMGlobalObject>(vm))
+ return toJS(globalObject, jsCast<JSDOMGlobalObject*>(globalObject), &arrayBuffer);
+
+ if (auto* buffer = arrayBuffer.m_wrapper.get())
+ return buffer;
+
+ return JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(arrayBuffer.sharingMode()), &arrayBuffer);
+ }
+
+ bool dumpArrayBufferView(JSObject* obj, SerializationReturnCode& code)
+ {
+ VM& vm = m_lexicalGlobalObject->vm();
+ write(ArrayBufferViewTag);
+ if (obj->inherits<JSDataView>(vm))
+ write(DataViewTag);
+ else if (obj->inherits<JSUint8ClampedArray>(vm))
+ write(Uint8ClampedArrayTag);
+ else if (obj->inherits<JSInt8Array>(vm))
+ write(Int8ArrayTag);
+ else if (obj->inherits<JSUint8Array>(vm))
+ write(Uint8ArrayTag);
+ else if (obj->inherits<JSInt16Array>(vm))
+ write(Int16ArrayTag);
+ else if (obj->inherits<JSUint16Array>(vm))
+ write(Uint16ArrayTag);
+ else if (obj->inherits<JSInt32Array>(vm))
+ write(Int32ArrayTag);
+ else if (obj->inherits<JSUint32Array>(vm))
+ write(Uint32ArrayTag);
+ else if (obj->inherits<JSFloat32Array>(vm))
+ write(Float32ArrayTag);
+ else if (obj->inherits<JSFloat64Array>(vm))
+ write(Float64ArrayTag);
+ else if (obj->inherits<JSBigInt64Array>(vm))
+ write(BigInt64ArrayTag);
+ else if (obj->inherits<JSBigUint64Array>(vm))
+ write(BigUint64ArrayTag);
+ else
+ return false;
+
+ RefPtr<ArrayBufferView> arrayBufferView = toPossiblySharedArrayBufferView(vm, obj);
+ uint64_t byteOffset = arrayBufferView->byteOffset();
+ write(byteOffset);
+ uint64_t byteLength = arrayBufferView->byteLength();
+ write(byteLength);
+ RefPtr<ArrayBuffer> arrayBuffer = arrayBufferView->possiblySharedBuffer();
+ if (!arrayBuffer) {
+ code = SerializationReturnCode::ValidationError;
+ return true;
+ }
+
+ return dumpIfTerminal(toJSArrayBuffer(*arrayBuffer), code);
+ }
+
+ // void dumpDOMPoint(const DOMPointReadOnly& point)
+ // {
+ // write(point.x());
+ // write(point.y());
+ // write(point.z());
+ // write(point.w());
+ // }
+
+ // void dumpDOMPoint(JSObject* obj)
+ // {
+ // VM& vm = m_lexicalGlobalObject->vm();
+ // if (obj->inherits<JSDOMPoint>(vm))
+ // write(DOMPointTag);
+ // else
+ // write(DOMPointReadOnlyTag);
+
+ // dumpDOMPoint(jsCast<JSDOMPointReadOnly*>(obj)->wrapped());
+ // }
+
+ // void dumpDOMRect(JSObject* obj)
+ // {
+ // VM& vm = m_lexicalGlobalObject->vm();
+ // if (obj->inherits<JSDOMRect>(vm))
+ // write(DOMRectTag);
+ // else
+ // write(DOMRectReadOnlyTag);
+
+ // auto& rect = jsCast<JSDOMRectReadOnly*>(obj)->wrapped();
+ // write(rect.x());
+ // write(rect.y());
+ // write(rect.width());
+ // write(rect.height());
+ // }
+
+ // void dumpDOMMatrix(JSObject* obj)
+ // {
+ // VM& vm = m_lexicalGlobalObject->vm();
+ // if (obj->inherits<JSDOMMatrix>(vm))
+ // write(DOMMatrixTag);
+ // else
+ // write(DOMMatrixReadOnlyTag);
+
+ // auto& matrix = jsCast<JSDOMMatrixReadOnly*>(obj)->wrapped();
+ // bool is2D = matrix.is2D();
+ // write(static_cast<uint8_t>(is2D));
+ // if (is2D) {
+ // write(matrix.m11());
+ // write(matrix.m12());
+ // write(matrix.m21());
+ // write(matrix.m22());
+ // write(matrix.m41());
+ // write(matrix.m42());
+ // } else {
+ // write(matrix.m11());
+ // write(matrix.m12());
+ // write(matrix.m13());
+ // write(matrix.m14());
+ // write(matrix.m21());
+ // write(matrix.m22());
+ // write(matrix.m23());
+ // write(matrix.m24());
+ // write(matrix.m31());
+ // write(matrix.m32());
+ // write(matrix.m33());
+ // write(matrix.m34());
+ // write(matrix.m41());
+ // write(matrix.m42());
+ // write(matrix.m43());
+ // write(matrix.m44());
+ // }
+ // }
+
+ // void dumpDOMQuad(JSObject* obj)
+ // {
+ // write(DOMQuadTag);
+
+ // auto& quad = jsCast<JSDOMQuad*>(obj)->wrapped();
+ // dumpDOMPoint(quad.p1());
+ // dumpDOMPoint(quad.p2());
+ // dumpDOMPoint(quad.p3());
+ // dumpDOMPoint(quad.p4());
+ // }
+
+ // void dumpImageBitmap(JSObject* obj, SerializationReturnCode& code)
+ // {
+ // auto index = m_transferredImageBitmaps.find(obj);
+ // if (index != m_transferredImageBitmaps.end()) {
+ // write(ImageBitmapTransferTag);
+ // write(index->value);
+ // return;
+ // }
+
+ // auto& imageBitmap = jsCast<JSImageBitmap*>(obj)->wrapped();
+ // auto* buffer = imageBitmap.buffer();
+
+ // if (!buffer) {
+ // code = SerializationReturnCode::ValidationError;
+ // return;
+ // }
+
+ // // FIXME: We should try to avoid converting pixel format.
+ // PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, buffer->colorSpace() };
+ // const IntSize& logicalSize = buffer->truncatedLogicalSize();
+ // auto pixelBuffer = buffer->getPixelBuffer(format, { IntPoint::zero(), logicalSize });
+ // if (!pixelBuffer) {
+ // code = SerializationReturnCode::ValidationError;
+ // return;
+ // }
+
+ // auto arrayBuffer = pixelBuffer->data().possiblySharedBuffer();
+ // if (!arrayBuffer) {
+ // code = SerializationReturnCode::ValidationError;
+ // return;
+ // }
+
+ // write(ImageBitmapTag);
+ // write(static_cast<uint8_t>(imageBitmap.serializationState().toRaw()));
+ // write(static_cast<int32_t>(logicalSize.width()));
+ // write(static_cast<int32_t>(logicalSize.height()));
+ // write(static_cast<double>(buffer->resolutionScale()));
+ // write(buffer->colorSpace());
+
+ // CheckedUint32 byteLength = arrayBuffer->byteLength();
+ // if (byteLength.hasOverflowed()) {
+ // code = SerializationReturnCode::ValidationError;
+ // return;
+ // }
+ // write(byteLength);
+ // write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength);
+ // }
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ void dumpOffscreenCanvas(JSObject* obj, SerializationReturnCode& code)
+ {
+ auto index = m_transferredOffscreenCanvases.find(obj);
+ if (index != m_transferredOffscreenCanvases.end()) {
+ write(OffscreenCanvasTransferTag);
+ write(index->value);
+ return;
+ }
+
+ code = SerializationReturnCode::DataCloneError;
+ }
+#endif
+
+#if ENABLE(WEB_RTC)
+ void dumpRTCDataChannel(JSObject* obj, SerializationReturnCode& code)
+ {
+ auto index = m_transferredRTCDataChannels.find(obj);
+ if (index != m_transferredRTCDataChannels.end()) {
+ write(RTCDataChannelTransferTag);
+ write(index->value);
+ return;
+ }
+
+ code = SerializationReturnCode::DataCloneError;
+ }
+#endif
+
+ void dumpDOMException(JSObject* obj, SerializationReturnCode& code)
+ {
+ if (auto* exception = JSDOMException::toWrapped(m_lexicalGlobalObject->vm(), obj)) {
+ write(DOMExceptionTag);
+ write(exception->message());
+ write(exception->name());
+ return;
+ }
+
+ code = SerializationReturnCode::DataCloneError;
+ }
+
+ bool dumpIfTerminal(JSValue value, SerializationReturnCode& code)
+ {
+ if (!value.isCell()) {
+ dumpImmediate(value, code);
+ return true;
+ }
+ ASSERT(value.isCell());
+
+ if (value.isString()) {
+ dumpString(asString(value)->value(m_lexicalGlobalObject));
+ return true;
+ }
+
+ if (value.isHeapBigInt()) {
+ write(BigIntTag);
+ dumpBigIntData(value);
+ return true;
+ }
+
+ if (value.isSymbol()) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+
+ VM& vm = m_lexicalGlobalObject->vm();
+ if (isArray(vm, value))
+ return false;
+
+ if (value.isObject()) {
+ auto* obj = asObject(value);
+ if (auto* dateObject = jsDynamicCast<DateInstance*>(vm, obj)) {
+ write(DateTag);
+ write(dateObject->internalNumber());
+ return true;
+ }
+ if (auto* booleanObject = jsDynamicCast<BooleanObject*>(vm, obj)) {
+ if (!startObjectInternal(booleanObject)) // handle duplicates
+ return true;
+ write(booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag);
+ return true;
+ }
+ if (auto* stringObject = jsDynamicCast<StringObject*>(vm, obj)) {
+ if (!startObjectInternal(stringObject)) // handle duplicates
+ return true;
+ String str = asString(stringObject->internalValue())->value(m_lexicalGlobalObject);
+ dumpStringObject(str);
+ return true;
+ }
+ if (auto* numberObject = jsDynamicCast<NumberObject*>(vm, obj)) {
+ if (!startObjectInternal(numberObject)) // handle duplicates
+ return true;
+ write(NumberObjectTag);
+ write(numberObject->internalValue().asNumber());
+ return true;
+ }
+ if (auto* bigIntObject = jsDynamicCast<BigIntObject*>(vm, obj)) {
+ if (!startObjectInternal(bigIntObject)) // handle duplicates
+ return true;
+ JSValue bigIntValue = bigIntObject->internalValue();
+ ASSERT(bigIntValue.isBigInt());
+ write(BigIntObjectTag);
+ dumpBigIntData(bigIntValue);
+ return true;
+ }
+ if (auto* file = JSFile::toWrapped(vm, obj)) {
+ write(FileTag);
+ write(*file);
+ return true;
+ }
+ if (auto* list = JSFileList::toWrapped(vm, obj)) {
+ write(FileListTag);
+ write(list->length());
+ for (auto& file : list->files())
+ write(file.get());
+ return true;
+ }
+ // if (auto* blob = JSBlob::toWrapped(vm, obj)) {
+ // write(BlobTag);
+ // m_blobHandles.append(blob->handle());
+ // write(blob->url().string());
+ // write(blob->type());
+ // static_assert(sizeof(uint64_t) == sizeof(decltype(blob->size())));
+ // uint64_t size = blob->size();
+ // write(size);
+ // return true;
+ // }
+ if (auto* data = JSImageData::toWrapped(vm, obj)) {
+ write(ImageDataTag);
+ write(data->width());
+ write(data->height());
+ CheckedUint32 dataLength = data->data().length();
+ if (dataLength.hasOverflowed()) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+ write(dataLength);
+ write(data->data().data(), dataLength);
+ write(data->colorSpace());
+ return true;
+ }
+ if (auto* regExp = jsDynamicCast<RegExpObject*>(vm, obj)) {
+ write(RegExpTag);
+ write(regExp->regExp()->pattern());
+ write(String(JSC::Yarr::flagsString(regExp->regExp()->flags()).data()));
+ return true;
+ }
+ if (obj->inherits<JSMessagePort>(vm)) {
+ auto index = m_transferredMessagePorts.find(obj);
+ if (index != m_transferredMessagePorts.end()) {
+ write(MessagePortReferenceTag);
+ write(index->value);
+ return true;
+ }
+ // MessagePort object could not be found in transferred message ports
+ code = SerializationReturnCode::ValidationError;
+ return true;
+ }
+ if (auto* arrayBuffer = toPossiblySharedArrayBuffer(vm, obj)) {
+ if (arrayBuffer->isDetached()) {
+ code = SerializationReturnCode::ValidationError;
+ return true;
+ }
+ auto index = m_transferredArrayBuffers.find(obj);
+ if (index != m_transferredArrayBuffers.end()) {
+ write(ArrayBufferTransferTag);
+ write(index->value);
+ return true;
+ }
+ if (!startObjectInternal(obj)) // handle duplicates
+ return true;
+
+ if (arrayBuffer->isShared() && m_context == SerializationContext::WorkerPostMessage) {
+ uint32_t index = m_sharedBuffers.size();
+ ArrayBufferContents contents;
+ if (arrayBuffer->shareWith(contents)) {
+ write(SharedArrayBufferTag);
+ m_sharedBuffers.append(WTFMove(contents));
+ write(index);
+ return true;
+ }
+ }
+
+ write(ArrayBufferTag);
+ uint64_t byteLength = arrayBuffer->byteLength();
+ write(byteLength);
+ write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength);
+ return true;
+ }
+ if (obj->inherits<JSArrayBufferView>(vm)) {
+ if (checkForDuplicate(obj))
+ return true;
+ bool success = dumpArrayBufferView(obj, code);
+ recordObject(obj);
+ return success;
+ }
+#if ENABLE(WEB_CRYPTO)
+ if (auto* key = JSCryptoKey::toWrapped(vm, obj)) {
+ write(CryptoKeyTag);
+ Vector<uint8_t> serializedKey;
+ Vector<BlobURLHandle> dummyBlobHandles;
+ Vector<RefPtr<MessagePort>> dummyMessagePorts;
+ Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers;
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray dummyModules;
+ WasmMemoryHandleArray dummyMemoryHandles;
+#endif
+ ArrayBufferContentsArray dummySharedBuffers;
+ CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyMessagePorts, dummyArrayBuffers, {},
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ {},
+#endif
+#if ENABLE(WEB_RTC)
+ {},
+#endif
+#if ENABLE(WEBASSEMBLY)
+ dummyModules,
+ dummyMemoryHandles,
+#endif
+ dummyBlobHandles, serializedKey, SerializationContext::Default, dummySharedBuffers);
+ rawKeySerializer.write(key);
+ Vector<uint8_t> wrappedKey;
+ if (!wrapCryptoKey(m_lexicalGlobalObject, serializedKey, wrappedKey))
+ return false;
+ write(wrappedKey);
+ return true;
+ }
+#endif
+#if ENABLE(WEB_RTC)
+ if (auto* rtcCertificate = JSRTCCertificate::toWrapped(vm, obj)) {
+ write(RTCCertificateTag);
+ write(rtcCertificate->expires());
+ write(rtcCertificate->pemCertificate());
+ write(rtcCertificate->origin().toString());
+ write(rtcCertificate->pemPrivateKey());
+ write(static_cast<unsigned>(rtcCertificate->getFingerprints().size()));
+ for (const auto& fingerprint : rtcCertificate->getFingerprints()) {
+ write(fingerprint.algorithm);
+ write(fingerprint.value);
+ }
+ return true;
+ }
+#endif
+#if ENABLE(WEBASSEMBLY)
+ if (JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, obj)) {
+ if (m_context != SerializationContext::WorkerPostMessage && m_context != SerializationContext::WindowPostMessage)
+ return false;
+
+ uint32_t index = m_wasmModules.size();
+ m_wasmModules.append(&module->module());
+ write(WasmModuleTag);
+ write(index);
+ return true;
+ }
+ if (JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, obj)) {
+ if (memory->memory().sharingMode() != JSC::Wasm::MemorySharingMode::Shared) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+ if (m_context != SerializationContext::WorkerPostMessage) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+ uint32_t index = m_wasmMemoryHandles.size();
+ m_wasmMemoryHandles.append(&memory->memory().handle());
+ write(WasmMemoryTag);
+ write(index);
+ return true;
+ }
+#endif
+ // if (obj->inherits<JSDOMPointReadOnly>(vm)) {
+ // dumpDOMPoint(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMRectReadOnly>(vm)) {
+ // dumpDOMRect(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMMatrixReadOnly>(vm)) {
+ // dumpDOMMatrix(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMQuad>(vm)) {
+ // dumpDOMQuad(obj);
+ // return true;
+ // }
+ // if (obj->inherits(vm, JSImageBitmap::info())) {
+ // dumpImageBitmap(obj, code);
+ // return true;
+ // }
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ if (obj->inherits(vm, JSOffscreenCanvas::info())) {
+ dumpOffscreenCanvas(obj, code);
+ return true;
+ }
+#endif
+#if ENABLE(WEB_RTC)
+ if (obj->inherits(vm, JSRTCDataChannel::info())) {
+ dumpRTCDataChannel(obj, code);
+ return true;
+ }
+#endif
+ if (obj->inherits(vm, JSDOMException::info())) {
+ dumpDOMException(obj, code);
+ return true;
+ }
+
+ return false;
+ }
+ // Any other types are expected to serialize as null.
+ write(NullTag);
+ return true;
+ }
+
+ void write(SerializationTag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+
+ void write(ArrayBufferViewSubtag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+
+ // void write(DestinationColorSpaceTag tag)
+ // {
+ // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ // }
+
+#if ENABLE(WEB_CRYPTO)
+ void write(CryptoKeyClassSubtag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+
+ void write(CryptoKeyAsymmetricTypeSubtag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+
+ void write(CryptoKeyUsageTag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+
+ void write(CryptoAlgorithmIdentifierTag tag)
+ {
+ writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(tag));
+ }
+#endif
+
+ void write(uint8_t c)
+ {
+ writeLittleEndian(m_buffer, c);
+ }
+
+ void write(uint32_t i)
+ {
+ writeLittleEndian(m_buffer, i);
+ }
+
+ void write(double d)
+ {
+ union {
+ double d;
+ int64_t i;
+ } u;
+ u.d = d;
+ writeLittleEndian(m_buffer, u.i);
+ }
+
+ void write(int32_t i)
+ {
+ writeLittleEndian(m_buffer, i);
+ }
+
+ void write(uint64_t i)
+ {
+ writeLittleEndian(m_buffer, i);
+ }
+
+ void write(uint16_t ch)
+ {
+ writeLittleEndian(m_buffer, ch);
+ }
+
+ void writeStringIndex(unsigned i)
+ {
+ writeConstantPoolIndex(m_constantPool, i);
+ }
+
+ void writeObjectIndex(unsigned i)
+ {
+ writeConstantPoolIndex(m_objectPool, i);
+ }
+
+ template<class T> void writeConstantPoolIndex(const T& constantPool, unsigned i)
+ {
+ ASSERT(i < constantPool.size());
+ if (constantPool.size() <= 0xFF)
+ write(static_cast<uint8_t>(i));
+ else if (constantPool.size() <= 0xFFFF)
+ write(static_cast<uint16_t>(i));
+ else
+ write(static_cast<uint32_t>(i));
+ }
+
+ void write(const Identifier& ident)
+ {
+ const String& str = ident.string();
+ StringConstantPool::AddResult addResult = m_constantPool.add(ident.impl(), m_constantPool.size());
+ if (!addResult.isNewEntry) {
+ write(StringPoolTag);
+ writeStringIndex(addResult.iterator->value);
+ return;
+ }
+
+ unsigned length = str.length();
+
+ // Guard against overflow
+ if (length > (std::numeric_limits<uint32_t>::max() - sizeof(uint32_t)) / sizeof(UChar)) {
+ fail();
+ return;
+ }
+
+ if (str.is8Bit())
+ writeLittleEndian<uint32_t>(m_buffer, length | StringDataIs8BitFlag);
+ else
+ writeLittleEndian<uint32_t>(m_buffer, length);
+
+ if (!length)
+ return;
+ if (str.is8Bit()) {
+ if (!writeLittleEndian(m_buffer, str.characters8(), length))
+ fail();
+ return;
+ }
+ if (!writeLittleEndian(m_buffer, str.characters16(), length))
+ fail();
+ }
+
+ void write(const String& str)
+ {
+ if (str.isNull())
+ write(m_emptyIdentifier);
+ else
+ write(Identifier::fromString(m_lexicalGlobalObject->vm(), str));
+ }
+
+ void write(const Vector<uint8_t>& vector)
+ {
+ uint32_t size = vector.size();
+ write(size);
+ writeLittleEndian(m_buffer, vector.data(), size);
+ }
+
+ // void write(const File& file)
+ // {
+ // // m_blobHandles.append(file.handle());
+ // write(file.path());
+ // write(file.url().string());
+ // write(file.type());
+ // write(file.name());
+ // write(static_cast<double>(file.lastModifiedOverride().value_or(-1)));
+ // }
+
+ // void write(PredefinedColorSpace colorSpace)
+ // {
+ // switch (colorSpace) {
+ // case PredefinedColorSpace::SRGB:
+ // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(PredefinedColorSpaceTag::SRGB));
+ // break;
+ // #if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3)
+ // case PredefinedColorSpace::DisplayP3:
+ // writeLittleEndian<uint8_t>(m_buffer, static_cast<uint8_t>(PredefinedColorSpaceTag::DisplayP3));
+ // break;
+ // #endif
+ // }
+ // }
+
+#if PLATFORM(COCOA)
+ void write(const RetainPtr<CFDataRef>& data)
+ {
+ uint32_t dataLength = CFDataGetLength(data.get());
+ write(dataLength);
+ write(CFDataGetBytePtr(data.get()), dataLength);
+ }
+#endif
+
+ // void write(DestinationColorSpace destinationColorSpace)
+ // {
+ // if (destinationColorSpace == DestinationColorSpace::SRGB()) {
+ // write(DestinationColorSpaceSRGBTag);
+ // return;
+ // }
+
+ // #if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB)
+ // if (destinationColorSpace == DestinationColorSpace::LinearSRGB()) {
+ // write(DestinationColorSpaceLinearSRGBTag);
+ // return;
+ // }
+ // #endif
+
+ // #if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3)
+ // if (destinationColorSpace == DestinationColorSpace::DisplayP3()) {
+ // write(DestinationColorSpaceDisplayP3Tag);
+ // return;
+ // }
+ // #endif
+
+ // #if PLATFORM(COCOA)
+ // auto colorSpace = destinationColorSpace.platformColorSpace();
+
+ // if (auto name = CGColorSpaceGetName(colorSpace)) {
+ // auto data = adoptCF(CFStringCreateExternalRepresentation(nullptr, name, kCFStringEncodingUTF8, 0));
+ // if (!data) {
+ // write(DestinationColorSpaceSRGBTag);
+ // return;
+ // }
+
+ // write(DestinationColorSpaceCGColorSpaceNameTag);
+ // write(data);
+ // return;
+ // }
+
+ // if (auto propertyList = adoptCF(CGColorSpaceCopyPropertyList(colorSpace))) {
+ // auto data = adoptCF(CFPropertyListCreateData(nullptr, propertyList.get(), kCFPropertyListBinaryFormat_v1_0, 0, nullptr));
+ // if (!data) {
+ // write(DestinationColorSpaceSRGBTag);
+ // return;
+ // }
+
+ // write(DestinationColorSpaceCGColorSpacePropertyListTag);
+ // write(data);
+ // return;
+ // }
+ // #endif
+
+ // ASSERT_NOT_REACHED();
+ // write(DestinationColorSpaceSRGBTag);
+ // }
+
+#if ENABLE(WEB_CRYPTO)
+ void write(CryptoAlgorithmIdentifier algorithm)
+ {
+ switch (algorithm) {
+ case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
+ write(CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5);
+ break;
+ case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
+ write(CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5);
+ break;
+ case CryptoAlgorithmIdentifier::RSA_PSS:
+ write(CryptoAlgorithmIdentifierTag::RSA_PSS);
+ break;
+ case CryptoAlgorithmIdentifier::RSA_OAEP:
+ write(CryptoAlgorithmIdentifierTag::RSA_OAEP);
+ break;
+ case CryptoAlgorithmIdentifier::ECDSA:
+ write(CryptoAlgorithmIdentifierTag::ECDSA);
+ break;
+ case CryptoAlgorithmIdentifier::ECDH:
+ write(CryptoAlgorithmIdentifierTag::ECDH);
+ break;
+ case CryptoAlgorithmIdentifier::AES_CTR:
+ write(CryptoAlgorithmIdentifierTag::AES_CTR);
+ break;
+ case CryptoAlgorithmIdentifier::AES_CBC:
+ write(CryptoAlgorithmIdentifierTag::AES_CBC);
+ break;
+ case CryptoAlgorithmIdentifier::AES_GCM:
+ write(CryptoAlgorithmIdentifierTag::AES_GCM);
+ break;
+ case CryptoAlgorithmIdentifier::AES_CFB:
+ write(CryptoAlgorithmIdentifierTag::AES_CFB);
+ break;
+ case CryptoAlgorithmIdentifier::AES_KW:
+ write(CryptoAlgorithmIdentifierTag::AES_KW);
+ break;
+ case CryptoAlgorithmIdentifier::HMAC:
+ write(CryptoAlgorithmIdentifierTag::HMAC);
+ break;
+ case CryptoAlgorithmIdentifier::SHA_1:
+ write(CryptoAlgorithmIdentifierTag::SHA_1);
+ break;
+ case CryptoAlgorithmIdentifier::SHA_224:
+ write(CryptoAlgorithmIdentifierTag::SHA_224);
+ break;
+ case CryptoAlgorithmIdentifier::SHA_256:
+ write(CryptoAlgorithmIdentifierTag::SHA_256);
+ break;
+ case CryptoAlgorithmIdentifier::SHA_384:
+ write(CryptoAlgorithmIdentifierTag::SHA_384);
+ break;
+ case CryptoAlgorithmIdentifier::SHA_512:
+ write(CryptoAlgorithmIdentifierTag::SHA_512);
+ break;
+ case CryptoAlgorithmIdentifier::HKDF:
+ write(CryptoAlgorithmIdentifierTag::HKDF);
+ break;
+ case CryptoAlgorithmIdentifier::PBKDF2:
+ write(CryptoAlgorithmIdentifierTag::PBKDF2);
+ break;
+ }
+ }
+
+ void write(CryptoKeyRSAComponents::Type type)
+ {
+ switch (type) {
+ case CryptoKeyRSAComponents::Type::Public:
+ write(CryptoKeyAsymmetricTypeSubtag::Public);
+ return;
+ case CryptoKeyRSAComponents::Type::Private:
+ write(CryptoKeyAsymmetricTypeSubtag::Private);
+ return;
+ }
+ }
+
+ void write(const CryptoKeyRSAComponents& key)
+ {
+ write(key.type());
+ write(key.modulus());
+ write(key.exponent());
+ if (key.type() == CryptoKeyRSAComponents::Type::Public)
+ return;
+
+ write(key.privateExponent());
+
+ unsigned primeCount = key.hasAdditionalPrivateKeyParameters() ? key.otherPrimeInfos().size() + 2 : 0;
+ write(primeCount);
+ if (!primeCount)
+ return;
+
+ write(key.firstPrimeInfo().primeFactor);
+ write(key.firstPrimeInfo().factorCRTExponent);
+ write(key.secondPrimeInfo().primeFactor);
+ write(key.secondPrimeInfo().factorCRTExponent);
+ write(key.secondPrimeInfo().factorCRTCoefficient);
+ for (unsigned i = 2; i < primeCount; ++i) {
+ write(key.otherPrimeInfos()[i].primeFactor);
+ write(key.otherPrimeInfos()[i].factorCRTExponent);
+ write(key.otherPrimeInfos()[i].factorCRTCoefficient);
+ }
+ }
+
+ void write(const CryptoKey* key)
+ {
+ write(currentKeyFormatVersion);
+
+ write(key->extractable());
+
+ CryptoKeyUsageBitmap usages = key->usagesBitmap();
+ write(countUsages(usages));
+ if (usages & CryptoKeyUsageEncrypt)
+ write(CryptoKeyUsageTag::Encrypt);
+ if (usages & CryptoKeyUsageDecrypt)
+ write(CryptoKeyUsageTag::Decrypt);
+ if (usages & CryptoKeyUsageSign)
+ write(CryptoKeyUsageTag::Sign);
+ if (usages & CryptoKeyUsageVerify)
+ write(CryptoKeyUsageTag::Verify);
+ if (usages & CryptoKeyUsageDeriveKey)
+ write(CryptoKeyUsageTag::DeriveKey);
+ if (usages & CryptoKeyUsageDeriveBits)
+ write(CryptoKeyUsageTag::DeriveBits);
+ if (usages & CryptoKeyUsageWrapKey)
+ write(CryptoKeyUsageTag::WrapKey);
+ if (usages & CryptoKeyUsageUnwrapKey)
+ write(CryptoKeyUsageTag::UnwrapKey);
+
+ switch (key->keyClass()) {
+ case CryptoKeyClass::HMAC:
+ write(CryptoKeyClassSubtag::HMAC);
+ write(downcast<CryptoKeyHMAC>(*key).key());
+ write(downcast<CryptoKeyHMAC>(*key).hashAlgorithmIdentifier());
+ break;
+ case CryptoKeyClass::AES:
+ write(CryptoKeyClassSubtag::AES);
+ write(key->algorithmIdentifier());
+ write(downcast<CryptoKeyAES>(*key).key());
+ break;
+ case CryptoKeyClass::EC:
+ write(CryptoKeyClassSubtag::EC);
+ write(key->algorithmIdentifier());
+ write(downcast<CryptoKeyEC>(*key).namedCurveString());
+ switch (key->type()) {
+ case CryptoKey::Type::Public: {
+ write(CryptoKeyAsymmetricTypeSubtag::Public);
+ auto result = downcast<CryptoKeyEC>(*key).exportRaw();
+ ASSERT(!result.hasException());
+ write(result.releaseReturnValue());
+ break;
+ }
+ case CryptoKey::Type::Private: {
+ write(CryptoKeyAsymmetricTypeSubtag::Private);
+ // Use the standard complied method is not very efficient, but simple/reliable.
+ auto result = downcast<CryptoKeyEC>(*key).exportPkcs8();
+ ASSERT(!result.hasException());
+ write(result.releaseReturnValue());
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ break;
+ case CryptoKeyClass::Raw:
+ write(CryptoKeyClassSubtag::Raw);
+ write(key->algorithmIdentifier());
+ write(downcast<CryptoKeyRaw>(*key).key());
+ break;
+ case CryptoKeyClass::RSA:
+ write(CryptoKeyClassSubtag::RSA);
+ write(key->algorithmIdentifier());
+ CryptoAlgorithmIdentifier hash;
+ bool isRestrictedToHash = downcast<CryptoKeyRSA>(*key).isRestrictedToHash(hash);
+ write(isRestrictedToHash);
+ if (isRestrictedToHash)
+ write(hash);
+ write(*downcast<CryptoKeyRSA>(*key).exportData());
+ break;
+ }
+ }
+#endif
+
+ void write(const uint8_t* data, unsigned length)
+ {
+ m_buffer.append(data, length);
+ }
+
+ Vector<uint8_t>& m_buffer;
+ // Vector<BlobURLHandle>& m_blobHandles;
+ ObjectPool m_objectPool;
+ ObjectPool m_transferredMessagePorts;
+ ObjectPool m_transferredArrayBuffers;
+ // ObjectPool m_transferredImageBitmaps;
+ // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ // ObjectPool m_transferredOffscreenCanvases;
+ // #endif
+ // #if ENABLE(WEB_RTC)
+ // ObjectPool m_transferredRTCDataChannels;
+ // #endif
+ typedef HashMap<RefPtr<UniquedStringImpl>, uint32_t, IdentifierRepHash> StringConstantPool;
+ StringConstantPool m_constantPool;
+ Identifier m_emptyIdentifier;
+ SerializationContext m_context;
+ ArrayBufferContentsArray& m_sharedBuffers;
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray& m_wasmModules;
+ WasmMemoryHandleArray& m_wasmMemoryHandles;
+#endif
+};
+
+SerializationReturnCode CloneSerializer::serialize(JSValue in)
+{
+ VM& vm = m_lexicalGlobalObject->vm();
+ Vector<uint32_t, 16> indexStack;
+ Vector<uint32_t, 16> lengthStack;
+ Vector<PropertyNameArray, 16> propertyStack;
+ Vector<JSObject*, 32> inputObjectStack;
+ Vector<JSMapIterator*, 4> mapIteratorStack;
+ Vector<JSSetIterator*, 4> setIteratorStack;
+ Vector<JSValue, 4> mapIteratorValueStack;
+ Vector<WalkerState, 16> stateStack;
+ WalkerState lexicalGlobalObject = StateUnknown;
+ JSValue inValue = in;
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ while (1) {
+ switch (lexicalGlobalObject) {
+ arrayStartState:
+ case ArrayStartState: {
+ ASSERT(isArray(vm, inValue));
+ if (inputObjectStack.size() > maximumFilterRecursion)
+ return SerializationReturnCode::StackOverflowError;
+
+ JSArray* inArray = asArray(inValue);
+ unsigned length = inArray->length();
+ if (!startArray(inArray))
+ break;
+ inputObjectStack.append(inArray);
+ indexStack.append(0);
+ lengthStack.append(length);
+ }
+ arrayStartVisitMember:
+ FALLTHROUGH;
+ case ArrayStartVisitMember: {
+ JSObject* array = inputObjectStack.last();
+ uint32_t index = indexStack.last();
+ if (index == lengthStack.last()) {
+ indexStack.removeLast();
+ lengthStack.removeLast();
+
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ array->getOwnNonIndexPropertyNames(m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+ if (propertyStack.last().size()) {
+ write(NonIndexPropertiesTag);
+ indexStack.append(0);
+ goto objectStartVisitMember;
+ }
+ propertyStack.removeLast();
+
+ endObject();
+ inputObjectStack.removeLast();
+ break;
+ }
+ inValue = array->getDirectIndex(m_lexicalGlobalObject, index);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+ if (!inValue) {
+ indexStack.last()++;
+ goto arrayStartVisitMember;
+ }
+
+ write(index);
+ auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
+ if (dumpIfTerminal(inValue, terminalCode)) {
+ if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
+ return terminalCode;
+ indexStack.last()++;
+ goto arrayStartVisitMember;
+ }
+ stateStack.append(ArrayEndVisitMember);
+ goto stateUnknown;
+ }
+ case ArrayEndVisitMember: {
+ indexStack.last()++;
+ goto arrayStartVisitMember;
+ }
+ objectStartState:
+ case ObjectStartState: {
+ ASSERT(inValue.isObject());
+ if (inputObjectStack.size() > maximumFilterRecursion)
+ return SerializationReturnCode::StackOverflowError;
+ JSObject* inObject = asObject(inValue);
+ if (!startObject(inObject))
+ break;
+ // At this point, all supported objects other than Object
+ // objects have been handled. If we reach this point and
+ // the input is not an Object object then we should throw
+ // a DataCloneError.
+ if (inObject->classInfo(vm) != JSFinalObject::info())
+ return SerializationReturnCode::DataCloneError;
+ inputObjectStack.append(inObject);
+ indexStack.append(0);
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ inObject->methodTable(vm)->getOwnPropertyNames(inObject, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+ }
+ objectStartVisitMember:
+ FALLTHROUGH;
+ case ObjectStartVisitMember: {
+ JSObject* object = inputObjectStack.last();
+ uint32_t index = indexStack.last();
+ PropertyNameArray& properties = propertyStack.last();
+ if (index == properties.size()) {
+ endObject();
+ inputObjectStack.removeLast();
+ indexStack.removeLast();
+ propertyStack.removeLast();
+ break;
+ }
+ inValue = getProperty(vm, object, properties[index]);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+
+ if (!inValue) {
+ // Property was removed during serialisation
+ indexStack.last()++;
+ goto objectStartVisitMember;
+ }
+ write(properties[index]);
+
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+
+ auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
+ if (!dumpIfTerminal(inValue, terminalCode)) {
+ stateStack.append(ObjectEndVisitMember);
+ goto stateUnknown;
+ }
+ if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
+ return terminalCode;
+ FALLTHROUGH;
+ }
+ case ObjectEndVisitMember: {
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+
+ indexStack.last()++;
+ goto objectStartVisitMember;
+ }
+ mapStartState : {
+ ASSERT(inValue.isObject());
+ if (inputObjectStack.size() > maximumFilterRecursion)
+ return SerializationReturnCode::StackOverflowError;
+ JSMap* inMap = jsCast<JSMap*>(inValue);
+ if (!startMap(inMap))
+ break;
+ JSMapIterator* iterator = JSMapIterator::create(vm, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries);
+ m_gcBuffer.appendWithCrashOnOverflow(inMap);
+ m_gcBuffer.appendWithCrashOnOverflow(iterator);
+ mapIteratorStack.append(iterator);
+ inputObjectStack.append(inMap);
+ goto mapDataStartVisitEntry;
+ }
+ mapDataStartVisitEntry:
+ case MapDataStartVisitEntry: {
+ JSMapIterator* iterator = mapIteratorStack.last();
+ JSValue key, value;
+ if (!iterator->nextKeyValue(m_lexicalGlobalObject, key, value)) {
+ mapIteratorStack.removeLast();
+ JSObject* object = inputObjectStack.last();
+ ASSERT(jsDynamicCast<JSMap*>(vm, object));
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ object->methodTable(vm)->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+ write(NonMapPropertiesTag);
+ indexStack.append(0);
+ goto objectStartVisitMember;
+ }
+ inValue = key;
+ m_gcBuffer.appendWithCrashOnOverflow(value);
+ mapIteratorValueStack.append(value);
+ stateStack.append(MapDataEndVisitKey);
+ goto stateUnknown;
+ }
+ case MapDataEndVisitKey: {
+ inValue = mapIteratorValueStack.last();
+ mapIteratorValueStack.removeLast();
+ stateStack.append(MapDataEndVisitValue);
+ goto stateUnknown;
+ }
+ case MapDataEndVisitValue: {
+ goto mapDataStartVisitEntry;
+ }
+
+ setStartState : {
+ ASSERT(inValue.isObject());
+ if (inputObjectStack.size() > maximumFilterRecursion)
+ return SerializationReturnCode::StackOverflowError;
+ JSSet* inSet = jsCast<JSSet*>(inValue);
+ if (!startSet(inSet))
+ break;
+ JSSetIterator* iterator = JSSetIterator::create(vm, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys);
+ m_gcBuffer.appendWithCrashOnOverflow(inSet);
+ m_gcBuffer.appendWithCrashOnOverflow(iterator);
+ setIteratorStack.append(iterator);
+ inputObjectStack.append(inSet);
+ goto setDataStartVisitEntry;
+ }
+ setDataStartVisitEntry:
+ case SetDataStartVisitEntry: {
+ JSSetIterator* iterator = setIteratorStack.last();
+ JSValue key;
+ if (!iterator->next(m_lexicalGlobalObject, key)) {
+ setIteratorStack.removeLast();
+ JSObject* object = inputObjectStack.last();
+ ASSERT(jsDynamicCast<JSSet*>(vm, object));
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ object->methodTable(vm)->getOwnPropertyNames(object, m_lexicalGlobalObject, propertyStack.last(), DontEnumPropertiesMode::Exclude);
+ if (UNLIKELY(scope.exception()))
+ return SerializationReturnCode::ExistingExceptionError;
+ write(NonSetPropertiesTag);
+ indexStack.append(0);
+ goto objectStartVisitMember;
+ }
+ inValue = key;
+ stateStack.append(SetDataEndVisitKey);
+ goto stateUnknown;
+ }
+ case SetDataEndVisitKey: {
+ goto setDataStartVisitEntry;
+ }
+
+ stateUnknown:
+ case StateUnknown: {
+ auto terminalCode = SerializationReturnCode::SuccessfullyCompleted;
+ if (dumpIfTerminal(inValue, terminalCode)) {
+ if (terminalCode != SerializationReturnCode::SuccessfullyCompleted)
+ return terminalCode;
+ break;
+ }
+
+ if (isArray(vm, inValue))
+ goto arrayStartState;
+ if (isMap(vm, inValue))
+ goto mapStartState;
+ if (isSet(vm, inValue))
+ goto setStartState;
+ goto objectStartState;
+ }
+ }
+ if (stateStack.isEmpty())
+ break;
+
+ lexicalGlobalObject = stateStack.last();
+ stateStack.removeLast();
+ }
+ if (m_failed)
+ return SerializationReturnCode::UnspecifiedError;
+
+ return SerializationReturnCode::SuccessfullyCompleted;
+}
+
+class CloneDeserializer : CloneBase {
+public:
+ static String deserializeString(const Vector<uint8_t>& buffer)
+ {
+ if (buffer.isEmpty())
+ return String();
+ const uint8_t* ptr = buffer.begin();
+ const uint8_t* end = buffer.end();
+ uint32_t version;
+ if (!readLittleEndian(ptr, end, version) || version > CurrentVersion)
+ return String();
+ uint8_t tag;
+ if (!readLittleEndian(ptr, end, tag) || tag != StringTag)
+ return String();
+ uint32_t length;
+ if (!readLittleEndian(ptr, end, length))
+ return String();
+ bool is8Bit = length & StringDataIs8BitFlag;
+ length &= ~StringDataIs8BitFlag;
+ String str;
+ if (!readString(ptr, end, str, length, is8Bit))
+ return String();
+ return str;
+ }
+
+ static DeserializationResult deserialize(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores*/
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels
+#endif
+ ,
+ ArrayBufferContentsArray* arrayBufferContentsArray, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers
+#if ENABLE(WEBASSEMBLY)
+ ,
+ WasmModuleArray* wasmModules, WasmMemoryHandleArray* wasmMemoryHandles
+#endif
+ )
+ {
+ if (!buffer.size())
+ return std::make_pair(jsNull(), SerializationReturnCode::UnspecifiedError);
+ CloneDeserializer deserializer(lexicalGlobalObject, globalObject, messagePorts, arrayBufferContentsArray, buffer, blobURLs, blobFilePaths, sharedBuffers, WTFMove(backingStores)
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ WTFMove(detachedOffscreenCanvases)
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ WTFMove(detachedRTCDataChannels)
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ wasmModules, wasmMemoryHandles
+#endif
+ );
+ if (!deserializer.isValid())
+ return std::make_pair(JSValue(), SerializationReturnCode::ValidationError);
+ return deserializer.deserialize();
+ }
+
+private:
+ struct CachedString {
+ CachedString(const String& string)
+ : m_string(string)
+ {
+ }
+
+ JSValue jsString(JSGlobalObject* lexicalGlobalObject)
+ {
+ if (!m_jsString)
+ m_jsString = JSC::jsString(lexicalGlobalObject->vm(), m_string);
+ return m_jsString;
+ }
+ const String& string() { return m_string; }
+ String takeString() { return WTFMove(m_string); }
+
+ private:
+ String m_string;
+ JSValue m_jsString;
+ };
+
+ struct CachedStringRef {
+ CachedStringRef()
+ : m_base(0)
+ , m_index(0)
+ {
+ }
+ CachedStringRef(Vector<CachedString>* base, size_t index)
+ : m_base(base)
+ , m_index(index)
+ {
+ }
+
+ CachedString* operator->()
+ {
+ ASSERT(m_base);
+ return &m_base->at(m_index);
+ }
+
+ private:
+ Vector<CachedString>* m_base;
+ size_t m_index;
+ };
+
+ CloneDeserializer(
+ JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores,*/ const Vector<uint8_t>& buffer
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases = {}
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels = {}
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ WasmModuleArray* wasmModules = nullptr, WasmMemoryHandleArray* wasmMemoryHandles = nullptr
+#endif
+ )
+ : CloneBase(lexicalGlobalObject)
+ , m_globalObject(globalObject)
+ , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>(globalObject->vm()))
+ , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>(globalObject->vm()))
+ , m_ptr(buffer.data())
+ , m_end(buffer.data() + buffer.size())
+ , m_version(0xFFFFFFFF)
+ , m_messagePorts(messagePorts)
+ , m_arrayBufferContents(arrayBufferContents)
+ , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
+ , m_backingStores(WTFMove(backingStores))
+ , m_imageBitmaps(m_backingStores.size())
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+ , m_offscreenCanvases(m_detachedOffscreenCanvases.size())
+#endif
+#if ENABLE(WEB_RTC)
+ , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels))
+ , m_rtcDataChannels(m_detachedRTCDataChannels.size())
+#endif
+#if ENABLE(WEBASSEMBLY)
+ , m_wasmModules(wasmModules)
+ , m_wasmMemoryHandles(wasmMemoryHandles)
+#endif
+ {
+ if (!read(m_version))
+ m_version = 0xFFFFFFFF;
+ }
+
+ CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers, /*Vector<std::optional<ImageBitmapBacking>>&& backingStores*/
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ WasmModuleArray* wasmModules, WasmMemoryHandleArray* wasmMemoryHandles
+#endif
+ )
+ : CloneBase(lexicalGlobalObject)
+ , m_globalObject(globalObject)
+ , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>(globalObject->vm()))
+ , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>(globalObject->vm()))
+ , m_ptr(buffer.data())
+ , m_end(buffer.data() + buffer.size())
+ , m_version(0xFFFFFFFF)
+ , m_messagePorts(messagePorts)
+ , m_arrayBufferContents(arrayBufferContents)
+ , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
+ // , m_blobURLs(blobURLs)
+ // , m_blobFilePaths(blobFilePaths)
+ , m_sharedBuffers(sharedBuffers)
+ // , m_backingStores(WTFMove(backingStores))
+ // , m_imageBitmaps(m_backingStores.size())
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+ , m_offscreenCanvases(m_detachedOffscreenCanvases.size())
+#endif
+#if ENABLE(WEB_RTC)
+ , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels))
+ , m_rtcDataChannels(m_detachedRTCDataChannels.size())
+#endif
+#if ENABLE(WEBASSEMBLY)
+ , m_wasmModules(wasmModules)
+ , m_wasmMemoryHandles(wasmMemoryHandles)
+#endif
+ {
+ if (!read(m_version))
+ m_version = 0xFFFFFFFF;
+ }
+
+ DeserializationResult deserialize();
+
+ bool isValid() const { return m_version <= CurrentVersion; }
+
+ template<typename T> bool readLittleEndian(T& value)
+ {
+ if (m_failed || !readLittleEndian(m_ptr, m_end, value)) {
+ fail();
+ return false;
+ }
+ return true;
+ }
+#if ASSUME_LITTLE_ENDIAN
+ template<typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
+ {
+ if (ptr > end - sizeof(value))
+ return false;
+
+ if (sizeof(T) == 1)
+ value = *ptr++;
+ else {
+ value = *reinterpret_cast<const T*>(ptr);
+ ptr += sizeof(T);
+ }
+ return true;
+ }
+#else
+ template<typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
+ {
+ if (ptr > end - sizeof(value))
+ return false;
+
+ if (sizeof(T) == 1)
+ value = *ptr++;
+ else {
+ value = 0;
+ for (unsigned i = 0; i < sizeof(T); i++)
+ value += ((T)*ptr++) << (i * 8);
+ }
+ return true;
+ }
+#endif
+
+ bool read(uint32_t& i)
+ {
+ return readLittleEndian(i);
+ }
+
+ bool read(int32_t& i)
+ {
+ return readLittleEndian(*reinterpret_cast<uint32_t*>(&i));
+ }
+
+ bool read(uint16_t& i)
+ {
+ return readLittleEndian(i);
+ }
+
+ bool read(uint8_t& i)
+ {
+ return readLittleEndian(i);
+ }
+
+ bool read(double& d)
+ {
+ union {
+ double d;
+ uint64_t i64;
+ } u;
+ if (!readLittleEndian(u.i64))
+ return false;
+ d = u.d;
+ return true;
+ }
+
+ bool read(uint64_t& i)
+ {
+ return readLittleEndian(i);
+ }
+
+ bool readStringIndex(uint32_t& i)
+ {
+ return readConstantPoolIndex(m_constantPool, i);
+ }
+
+ template<class T> bool readConstantPoolIndex(const T& constantPool, uint32_t& i)
+ {
+ if (constantPool.size() <= 0xFF) {
+ uint8_t i8;
+ if (!read(i8))
+ return false;
+ i = i8;
+ return true;
+ }
+ if (constantPool.size() <= 0xFFFF) {
+ uint16_t i16;
+ if (!read(i16))
+ return false;
+ i = i16;
+ return true;
+ }
+ return read(i);
+ }
+
+ static bool readString(const uint8_t*& ptr, const uint8_t* end, String& str, unsigned length, bool is8Bit)
+ {
+ if (length >= std::numeric_limits<int32_t>::max() / sizeof(UChar))
+ return false;
+
+ if (is8Bit) {
+ if ((end - ptr) < static_cast<int>(length))
+ return false;
+ str = String { ptr, length };
+ ptr += length;
+ return true;
+ }
+
+ unsigned size = length * sizeof(UChar);
+ if ((end - ptr) < static_cast<int>(size))
+ return false;
+
+#if ASSUME_LITTLE_ENDIAN
+ str = String(reinterpret_cast<const UChar*>(ptr), length);
+ ptr += length * sizeof(UChar);
+#else
+ UChar* characters;
+ str = String::createUninitialized(length, characters);
+ for (unsigned i = 0; i < length; ++i) {
+ uint16_t c;
+ readLittleEndian(ptr, end, c);
+ characters[i] = c;
+ }
+#endif
+ return true;
+ }
+
+ bool readStringData(CachedStringRef& cachedString)
+ {
+ bool scratch;
+ return readStringData(cachedString, scratch);
+ }
+
+ bool readStringData(CachedStringRef& cachedString, bool& wasTerminator)
+ {
+ if (m_failed)
+ return false;
+ uint32_t length = 0;
+ if (!read(length))
+ return false;
+ if (length == TerminatorTag) {
+ wasTerminator = true;
+ return false;
+ }
+ if (length == StringPoolTag) {
+ unsigned index = 0;
+ if (!readStringIndex(index)) {
+ fail();
+ return false;
+ }
+ if (index >= m_constantPool.size()) {
+ fail();
+ return false;
+ }
+ cachedString = CachedStringRef(&m_constantPool, index);
+ return true;
+ }
+ bool is8Bit = length & StringDataIs8BitFlag;
+ length &= ~StringDataIs8BitFlag;
+ String str;
+ if (!readString(m_ptr, m_end, str, length, is8Bit)) {
+ fail();
+ return false;
+ }
+ m_constantPool.append(str);
+ cachedString = CachedStringRef(&m_constantPool, m_constantPool.size() - 1);
+ return true;
+ }
+
+ SerializationTag readTag()
+ {
+ if (m_ptr >= m_end)
+ return ErrorTag;
+ return static_cast<SerializationTag>(*m_ptr++);
+ }
+
+ bool readArrayBufferViewSubtag(ArrayBufferViewSubtag& tag)
+ {
+ if (m_ptr >= m_end)
+ return false;
+ tag = static_cast<ArrayBufferViewSubtag>(*m_ptr++);
+ return true;
+ }
+
+ void putProperty(JSObject* object, unsigned index, JSValue value)
+ {
+ object->putDirectIndex(m_lexicalGlobalObject, index, value);
+ }
+
+ void putProperty(JSObject* object, const Identifier& property, JSValue value)
+ {
+ object->putDirectMayBeIndex(m_lexicalGlobalObject, property, value);
+ }
+
+ // bool readFile(RefPtr<File>& file)
+ // {
+ // CachedStringRef path;
+ // if (!readStringData(path))
+ // return false;
+ // CachedStringRef url;
+ // if (!readStringData(url))
+ // return false;
+ // CachedStringRef type;
+ // if (!readStringData(type))
+ // return false;
+ // CachedStringRef name;
+ // if (!readStringData(name))
+ // return false;
+ // std::optional<int64_t> optionalLastModified;
+ // if (m_version > 6) {
+ // double lastModified;
+ // if (!read(lastModified))
+ // return false;
+ // if (lastModified >= 0)
+ // optionalLastModified = lastModified;
+ // }
+
+ // // If the blob URL for this file has an associated blob file path, prefer that one over the "built-in" path.
+ // String filePath = blobFilePathForBlobURL(url->string());
+ // if (filePath.isEmpty())
+ // filePath = path->string();
+
+ // if (!m_canCreateDOMObject)
+ // return true;
+
+ // file = File::deserialize(executionContext(m_lexicalGlobalObject), filePath, URL { url->string() }, type->string(), name->string(), optionalLastModified);
+ // return true;
+ // }
+
+ template<typename LengthType>
+ bool readArrayBufferImpl(RefPtr<ArrayBuffer>& arrayBuffer)
+ {
+ LengthType length;
+ if (!read(length))
+ return false;
+ if (m_ptr + length > m_end)
+ return false;
+ arrayBuffer = ArrayBuffer::tryCreate(m_ptr, length);
+ if (!arrayBuffer)
+ return false;
+ m_ptr += length;
+ return true;
+ }
+
+ bool readArrayBuffer(RefPtr<ArrayBuffer>& arrayBuffer)
+ {
+ if (m_version < 10)
+ return readArrayBufferImpl<uint32_t>(arrayBuffer);
+ return readArrayBufferImpl<uint64_t>(arrayBuffer);
+ }
+
+ template<typename LengthType>
+ bool readArrayBufferViewImpl(VM& vm, JSValue& arrayBufferView)
+ {
+ ArrayBufferViewSubtag arrayBufferViewSubtag;
+ if (!readArrayBufferViewSubtag(arrayBufferViewSubtag))
+ return false;
+ LengthType byteOffset;
+ if (!read(byteOffset))
+ return false;
+ LengthType byteLength;
+ if (!read(byteLength))
+ return false;
+ JSObject* arrayBufferObj = asObject(readTerminal());
+ if (!arrayBufferObj || !arrayBufferObj->inherits<JSArrayBuffer>(vm))
+ return false;
+
+ unsigned elementSize = typedArrayElementSize(arrayBufferViewSubtag);
+ if (!elementSize)
+ return false;
+ LengthType length = byteLength / elementSize;
+ if (length * elementSize != byteLength)
+ return false;
+
+ RefPtr<ArrayBuffer> arrayBuffer = toPossiblySharedArrayBuffer(vm, arrayBufferObj);
+ switch (arrayBufferViewSubtag) {
+ case DataViewTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, DataView::create(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Int8ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int8Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Uint8ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Uint8ClampedArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8ClampedArray::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Int16ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int16Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Uint16ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint16Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Int32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Uint32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Float32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float32Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case Float64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case BigInt64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigInt64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ case BigUint64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigUint64Array::tryCreate(WTFMove(arrayBuffer), byteOffset, length).get());
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool readArrayBufferView(VM& vm, JSValue& arrayBufferView)
+ {
+ if (m_version < 10)
+ return readArrayBufferViewImpl<uint32_t>(vm, arrayBufferView);
+ return readArrayBufferViewImpl<uint64_t>(vm, arrayBufferView);
+ }
+
+ bool read(Vector<uint8_t>& result)
+ {
+ ASSERT(result.isEmpty());
+ uint32_t size;
+ if (!read(size))
+ return false;
+ if (m_ptr + size > m_end)
+ return false;
+ result.append(m_ptr, size);
+ m_ptr += size;
+ return true;
+ }
+
+ // bool read(PredefinedColorSpace& result)
+ // {
+ // uint8_t tag;
+ // if (!read(tag))
+ // return false;
+
+ // switch (static_cast<PredefinedColorSpaceTag>(tag)) {
+ // case PredefinedColorSpaceTag::SRGB:
+ // result = PredefinedColorSpace::SRGB;
+ // return true;
+ // #if ENABLE(PREDEFINED_COLOR_SPACE_DISPLAY_P3)
+ // case PredefinedColorSpaceTag::DisplayP3:
+ // result = PredefinedColorSpace::DisplayP3;
+ // return true;
+ // #endif
+ // default:
+ // return false;
+ // }
+ // }
+
+ // bool read(DestinationColorSpaceTag& tag)
+ // {
+ // if (m_ptr >= m_end)
+ // return false;
+ // tag = static_cast<DestinationColorSpaceTag>(*m_ptr++);
+ // return true;
+ // }
+
+#if PLATFORM(COCOA)
+ bool read(RetainPtr<CFDataRef>& data)
+ {
+ uint32_t dataLength;
+ if (!read(dataLength) || static_cast<uint32_t>(m_end - m_ptr) < dataLength)
+ return false;
+
+ data = adoptCF(CFDataCreateWithBytesNoCopy(nullptr, m_ptr, dataLength, kCFAllocatorNull));
+ if (!data)
+ return false;
+
+ m_ptr += dataLength;
+ return true;
+ }
+#endif
+
+ bool read(DestinationColorSpace& destinationColorSpace)
+ {
+ DestinationColorSpaceTag tag;
+ if (!read(tag))
+ return false;
+
+ switch (tag) {
+ case DestinationColorSpaceSRGBTag:
+ destinationColorSpace = DestinationColorSpace::SRGB();
+ return true;
+#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB)
+ case DestinationColorSpaceLinearSRGBTag:
+ destinationColorSpace = DestinationColorSpace::LinearSRGB();
+ return true;
+#endif
+#if ENABLE(DESTINATION_COLOR_SPACE_DISPLAY_P3)
+ case DestinationColorSpaceDisplayP3Tag:
+ destinationColorSpace = DestinationColorSpace::DisplayP3();
+ return true;
+#endif
+#if PLATFORM(COCOA)
+ case DestinationColorSpaceCGColorSpaceNameTag: {
+ RetainPtr<CFDataRef> data;
+ if (!read(data))
+ return false;
+
+ auto name = adoptCF(CFStringCreateFromExternalRepresentation(nullptr, data.get(), kCFStringEncodingUTF8));
+ if (!name)
+ return false;
+
+ auto colorSpace = adoptCF(CGColorSpaceCreateWithName(name.get()));
+ if (!colorSpace)
+ return false;
+
+ destinationColorSpace = DestinationColorSpace(colorSpace.get());
+ return true;
+ }
+ case DestinationColorSpaceCGColorSpacePropertyListTag: {
+ RetainPtr<CFDataRef> data;
+ if (!read(data))
+ return false;
+
+ auto propertyList = adoptCF(CFPropertyListCreateWithData(nullptr, data.get(), kCFPropertyListImmutable, nullptr, nullptr));
+ if (!propertyList)
+ return false;
+
+ auto colorSpace = adoptCF(CGColorSpaceCreateWithPropertyList(propertyList.get()));
+ if (!colorSpace)
+ return false;
+
+ destinationColorSpace = DestinationColorSpace(colorSpace.get());
+ return true;
+ }
+#endif
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+
+#if ENABLE(WEB_CRYPTO)
+ bool read(CryptoAlgorithmIdentifier& result)
+ {
+ uint8_t algorithmTag;
+ if (!read(algorithmTag))
+ return false;
+ if (algorithmTag > cryptoAlgorithmIdentifierTagMaximumValue)
+ return false;
+ switch (static_cast<CryptoAlgorithmIdentifierTag>(algorithmTag)) {
+ case CryptoAlgorithmIdentifierTag::RSAES_PKCS1_v1_5:
+ result = CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5;
+ break;
+ case CryptoAlgorithmIdentifierTag::RSASSA_PKCS1_v1_5:
+ result = CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5;
+ break;
+ case CryptoAlgorithmIdentifierTag::RSA_PSS:
+ result = CryptoAlgorithmIdentifier::RSA_PSS;
+ break;
+ case CryptoAlgorithmIdentifierTag::RSA_OAEP:
+ result = CryptoAlgorithmIdentifier::RSA_OAEP;
+ break;
+ case CryptoAlgorithmIdentifierTag::ECDSA:
+ result = CryptoAlgorithmIdentifier::ECDSA;
+ break;
+ case CryptoAlgorithmIdentifierTag::ECDH:
+ result = CryptoAlgorithmIdentifier::ECDH;
+ break;
+ case CryptoAlgorithmIdentifierTag::AES_CTR:
+ result = CryptoAlgorithmIdentifier::AES_CTR;
+ break;
+ case CryptoAlgorithmIdentifierTag::AES_CBC:
+ result = CryptoAlgorithmIdentifier::AES_CBC;
+ break;
+ case CryptoAlgorithmIdentifierTag::AES_GCM:
+ result = CryptoAlgorithmIdentifier::AES_GCM;
+ break;
+ case CryptoAlgorithmIdentifierTag::AES_CFB:
+ result = CryptoAlgorithmIdentifier::AES_CFB;
+ break;
+ case CryptoAlgorithmIdentifierTag::AES_KW:
+ result = CryptoAlgorithmIdentifier::AES_KW;
+ break;
+ case CryptoAlgorithmIdentifierTag::HMAC:
+ result = CryptoAlgorithmIdentifier::HMAC;
+ break;
+ case CryptoAlgorithmIdentifierTag::SHA_1:
+ result = CryptoAlgorithmIdentifier::SHA_1;
+ break;
+ case CryptoAlgorithmIdentifierTag::SHA_224:
+ result = CryptoAlgorithmIdentifier::SHA_224;
+ break;
+ case CryptoAlgorithmIdentifierTag::SHA_256:
+ result = CryptoAlgorithmIdentifier::SHA_256;
+ break;
+ case CryptoAlgorithmIdentifierTag::SHA_384:
+ result = CryptoAlgorithmIdentifier::SHA_384;
+ break;
+ case CryptoAlgorithmIdentifierTag::SHA_512:
+ result = CryptoAlgorithmIdentifier::SHA_512;
+ break;
+ case CryptoAlgorithmIdentifierTag::HKDF:
+ result = CryptoAlgorithmIdentifier::HKDF;
+ break;
+ case CryptoAlgorithmIdentifierTag::PBKDF2:
+ result = CryptoAlgorithmIdentifier::PBKDF2;
+ break;
+ }
+ return true;
+ }
+
+ bool read(CryptoKeyClassSubtag& result)
+ {
+ uint8_t tag;
+ if (!read(tag))
+ return false;
+ if (tag > cryptoKeyClassSubtagMaximumValue)
+ return false;
+ result = static_cast<CryptoKeyClassSubtag>(tag);
+ return true;
+ }
+
+ bool read(CryptoKeyUsageTag& result)
+ {
+ uint8_t tag;
+ if (!read(tag))
+ return false;
+ if (tag > cryptoKeyUsageTagMaximumValue)
+ return false;
+ result = static_cast<CryptoKeyUsageTag>(tag);
+ return true;
+ }
+
+ bool read(CryptoKeyAsymmetricTypeSubtag& result)
+ {
+ uint8_t tag;
+ if (!read(tag))
+ return false;
+ if (tag > cryptoKeyAsymmetricTypeSubtagMaximumValue)
+ return false;
+ result = static_cast<CryptoKeyAsymmetricTypeSubtag>(tag);
+ return true;
+ }
+
+ bool readHMACKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ Vector<uint8_t> keyData;
+ if (!read(keyData))
+ return false;
+ CryptoAlgorithmIdentifier hash;
+ if (!read(hash))
+ return false;
+ result = CryptoKeyHMAC::importRaw(0, hash, WTFMove(keyData), extractable, usages);
+ return true;
+ }
+
+ bool readAESKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ CryptoAlgorithmIdentifier algorithm;
+ if (!read(algorithm))
+ return false;
+ if (!CryptoKeyAES::isValidAESAlgorithm(algorithm))
+ return false;
+ Vector<uint8_t> keyData;
+ if (!read(keyData))
+ return false;
+ result = CryptoKeyAES::importRaw(algorithm, WTFMove(keyData), extractable, usages);
+ return true;
+ }
+
+ bool readRSAKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ CryptoAlgorithmIdentifier algorithm;
+ if (!read(algorithm))
+ return false;
+
+ int32_t isRestrictedToHash;
+ CryptoAlgorithmIdentifier hash = CryptoAlgorithmIdentifier::SHA_1;
+ if (!read(isRestrictedToHash))
+ return false;
+ if (isRestrictedToHash && !read(hash))
+ return false;
+
+ CryptoKeyAsymmetricTypeSubtag type;
+ if (!read(type))
+ return false;
+
+ Vector<uint8_t> modulus;
+ if (!read(modulus))
+ return false;
+ Vector<uint8_t> exponent;
+ if (!read(exponent))
+ return false;
+
+ if (type == CryptoKeyAsymmetricTypeSubtag::Public) {
+ auto keyData = CryptoKeyRSAComponents::createPublic(modulus, exponent);
+ auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages);
+ result = WTFMove(key);
+ return true;
+ }
+
+ Vector<uint8_t> privateExponent;
+ if (!read(privateExponent))
+ return false;
+
+ uint32_t primeCount;
+ if (!read(primeCount))
+ return false;
+
+ if (!primeCount) {
+ auto keyData = CryptoKeyRSAComponents::createPrivate(modulus, exponent, privateExponent);
+ auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages);
+ result = WTFMove(key);
+ return true;
+ }
+
+ if (primeCount < 2)
+ return false;
+
+ CryptoKeyRSAComponents::PrimeInfo firstPrimeInfo;
+ CryptoKeyRSAComponents::PrimeInfo secondPrimeInfo;
+ Vector<CryptoKeyRSAComponents::PrimeInfo> otherPrimeInfos(primeCount - 2);
+
+ if (!read(firstPrimeInfo.primeFactor))
+ return false;
+ if (!read(firstPrimeInfo.factorCRTExponent))
+ return false;
+ if (!read(secondPrimeInfo.primeFactor))
+ return false;
+ if (!read(secondPrimeInfo.factorCRTExponent))
+ return false;
+ if (!read(secondPrimeInfo.factorCRTCoefficient))
+ return false;
+ for (unsigned i = 2; i < primeCount; ++i) {
+ if (!read(otherPrimeInfos[i].primeFactor))
+ return false;
+ if (!read(otherPrimeInfos[i].factorCRTExponent))
+ return false;
+ if (!read(otherPrimeInfos[i].factorCRTCoefficient))
+ return false;
+ }
+
+ auto keyData = CryptoKeyRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
+ auto key = CryptoKeyRSA::create(algorithm, hash, isRestrictedToHash, *keyData, extractable, usages);
+ result = WTFMove(key);
+ return true;
+ }
+
+ bool readECKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ CryptoAlgorithmIdentifier algorithm;
+ if (!read(algorithm))
+ return false;
+ if (!CryptoKeyEC::isValidECAlgorithm(algorithm))
+ return false;
+ CachedStringRef curve;
+ if (!readStringData(curve))
+ return false;
+ CryptoKeyAsymmetricTypeSubtag type;
+ if (!read(type))
+ return false;
+ Vector<uint8_t> keyData;
+ if (!read(keyData))
+ return false;
+
+ switch (type) {
+ case CryptoKeyAsymmetricTypeSubtag::Public:
+ result = CryptoKeyEC::importRaw(algorithm, curve->string(), WTFMove(keyData), extractable, usages);
+ break;
+ case CryptoKeyAsymmetricTypeSubtag::Private:
+ result = CryptoKeyEC::importPkcs8(algorithm, curve->string(), WTFMove(keyData), extractable, usages);
+ break;
+ }
+
+ return true;
+ }
+
+ bool readRawKey(CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ CryptoAlgorithmIdentifier algorithm;
+ if (!read(algorithm))
+ return false;
+ Vector<uint8_t> keyData;
+ if (!read(keyData))
+ return false;
+ result = CryptoKeyRaw::create(algorithm, WTFMove(keyData), usages);
+ return true;
+ }
+
+ bool readCryptoKey(JSValue& cryptoKey)
+ {
+ uint32_t keyFormatVersion;
+ if (!read(keyFormatVersion) || keyFormatVersion > currentKeyFormatVersion)
+ return false;
+
+ int32_t extractable;
+ if (!read(extractable))
+ return false;
+
+ uint32_t usagesCount;
+ if (!read(usagesCount))
+ return false;
+
+ CryptoKeyUsageBitmap usages = 0;
+ for (uint32_t i = 0; i < usagesCount; ++i) {
+ CryptoKeyUsageTag usage;
+ if (!read(usage))
+ return false;
+ switch (usage) {
+ case CryptoKeyUsageTag::Encrypt:
+ usages |= CryptoKeyUsageEncrypt;
+ break;
+ case CryptoKeyUsageTag::Decrypt:
+ usages |= CryptoKeyUsageDecrypt;
+ break;
+ case CryptoKeyUsageTag::Sign:
+ usages |= CryptoKeyUsageSign;
+ break;
+ case CryptoKeyUsageTag::Verify:
+ usages |= CryptoKeyUsageVerify;
+ break;
+ case CryptoKeyUsageTag::DeriveKey:
+ usages |= CryptoKeyUsageDeriveKey;
+ break;
+ case CryptoKeyUsageTag::DeriveBits:
+ usages |= CryptoKeyUsageDeriveBits;
+ break;
+ case CryptoKeyUsageTag::WrapKey:
+ usages |= CryptoKeyUsageWrapKey;
+ break;
+ case CryptoKeyUsageTag::UnwrapKey:
+ usages |= CryptoKeyUsageUnwrapKey;
+ break;
+ }
+ }
+
+ CryptoKeyClassSubtag cryptoKeyClass;
+ if (!read(cryptoKeyClass))
+ return false;
+ RefPtr<CryptoKey> result;
+ switch (cryptoKeyClass) {
+ case CryptoKeyClassSubtag::HMAC:
+ if (!readHMACKey(extractable, usages, result))
+ return false;
+ break;
+ case CryptoKeyClassSubtag::AES:
+ if (!readAESKey(extractable, usages, result))
+ return false;
+ break;
+ case CryptoKeyClassSubtag::RSA:
+ if (!readRSAKey(extractable, usages, result))
+ return false;
+ break;
+ case CryptoKeyClassSubtag::EC:
+ if (!readECKey(extractable, usages, result))
+ return false;
+ break;
+ case CryptoKeyClassSubtag::Raw:
+ if (!readRawKey(usages, result))
+ return false;
+ break;
+ }
+ cryptoKey = getJSValue(result.get());
+ return true;
+ }
+#endif
+
+ template<class T>
+ JSValue getJSValue(T&& nativeObj)
+ {
+ return toJS(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), std::forward<T>(nativeObj));
+ }
+
+ // template<class T>
+ // JSValue readDOMPoint()
+ // {
+ // double x;
+ // if (!read(x))
+ // return {};
+ // double y;
+ // if (!read(y))
+ // return {};
+ // double z;
+ // if (!read(z))
+ // return {};
+ // double w;
+ // if (!read(w))
+ // return {};
+
+ // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(x, y, z, w));
+ // }
+
+ // template<class T>
+ // JSValue readDOMMatrix()
+ // {
+ // uint8_t is2D;
+ // if (!read(is2D))
+ // return {};
+
+ // if (is2D) {
+ // double m11;
+ // if (!read(m11))
+ // return {};
+ // double m12;
+ // if (!read(m12))
+ // return {};
+ // double m21;
+ // if (!read(m21))
+ // return {};
+ // double m22;
+ // if (!read(m22))
+ // return {};
+ // double m41;
+ // if (!read(m41))
+ // return {};
+ // double m42;
+ // if (!read(m42))
+ // return {};
+
+ // TransformationMatrix matrix(m11, m12, m21, m22, m41, m42);
+ // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(WTFMove(matrix), DOMMatrixReadOnly::Is2D::Yes));
+ // } else {
+ // double m11;
+ // if (!read(m11))
+ // return {};
+ // double m12;
+ // if (!read(m12))
+ // return {};
+ // double m13;
+ // if (!read(m13))
+ // return {};
+ // double m14;
+ // if (!read(m14))
+ // return {};
+ // double m21;
+ // if (!read(m21))
+ // return {};
+ // double m22;
+ // if (!read(m22))
+ // return {};
+ // double m23;
+ // if (!read(m23))
+ // return {};
+ // double m24;
+ // if (!read(m24))
+ // return {};
+ // double m31;
+ // if (!read(m31))
+ // return {};
+ // double m32;
+ // if (!read(m32))
+ // return {};
+ // double m33;
+ // if (!read(m33))
+ // return {};
+ // double m34;
+ // if (!read(m34))
+ // return {};
+ // double m41;
+ // if (!read(m41))
+ // return {};
+ // double m42;
+ // if (!read(m42))
+ // return {};
+ // double m43;
+ // if (!read(m43))
+ // return {};
+ // double m44;
+ // if (!read(m44))
+ // return {};
+
+ // TransformationMatrix matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
+ // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(WTFMove(matrix), DOMMatrixReadOnly::Is2D::No));
+ // }
+ // }
+
+ // template<class T>
+ // JSValue readDOMRect()
+ // {
+ // double x;
+ // if (!read(x))
+ // return {};
+ // double y;
+ // if (!read(y))
+ // return {};
+ // double width;
+ // if (!read(width))
+ // return {};
+ // double height;
+ // if (!read(height))
+ // return {};
+
+ // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), T::create(x, y, width, height));
+ // }
+
+ // std::optional<DOMPointInit> readDOMPointInit()
+ // {
+ // DOMPointInit point;
+ // if (!read(point.x))
+ // return std::nullopt;
+ // if (!read(point.y))
+ // return std::nullopt;
+ // if (!read(point.z))
+ // return std::nullopt;
+ // if (!read(point.w))
+ // return std::nullopt;
+
+ // return point;
+ // }
+
+ // JSValue readDOMQuad()
+ // {
+ // auto p1 = readDOMPointInit();
+ // if (!p1)
+ // return JSValue();
+ // auto p2 = readDOMPointInit();
+ // if (!p2)
+ // return JSValue();
+ // auto p3 = readDOMPointInit();
+ // if (!p3)
+ // return JSValue();
+ // auto p4 = readDOMPointInit();
+ // if (!p4)
+ // return JSValue();
+
+ // return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), DOMQuad::create(p1.value(), p2.value(), p3.value(), p4.value()));
+ // }
+
+ // JSValue readTransferredImageBitmap()
+ // {
+ // uint32_t index;
+ // bool indexSuccessfullyRead = read(index);
+ // if (!indexSuccessfullyRead || index >= m_backingStores.size()) {
+ // fail();
+ // return JSValue();
+ // }
+
+ // if (!m_imageBitmaps[index])
+ // m_imageBitmaps[index] = ImageBitmap::create(WTFMove(m_backingStores.at(index)));
+
+ // auto bitmap = m_imageBitmaps[index].get();
+ // return getJSValue(bitmap);
+ // }
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ JSValue readOffscreenCanvas()
+ {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_detachedOffscreenCanvases.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_offscreenCanvases[index])
+ m_offscreenCanvases[index] = OffscreenCanvas::create(*executionContext(m_lexicalGlobalObject), WTFMove(m_detachedOffscreenCanvases.at(index)));
+
+ auto offscreenCanvas = m_offscreenCanvases[index].get();
+ return getJSValue(offscreenCanvas);
+ }
+#endif
+
+#if ENABLE(WEB_RTC)
+ JSValue readRTCCertificate()
+ {
+ double expires;
+ if (!read(expires)) {
+ fail();
+ return JSValue();
+ }
+ CachedStringRef certificate;
+ if (!readStringData(certificate)) {
+ fail();
+ return JSValue();
+ }
+ CachedStringRef origin;
+ if (!readStringData(origin)) {
+ fail();
+ return JSValue();
+ }
+ CachedStringRef keyedMaterial;
+ if (!readStringData(keyedMaterial)) {
+ fail();
+ return JSValue();
+ }
+ unsigned size = 0;
+ if (!read(size))
+ return JSValue();
+
+ Vector<RTCCertificate::DtlsFingerprint> fingerprints;
+ fingerprints.reserveInitialCapacity(size);
+ for (unsigned i = 0; i < size; i++) {
+ CachedStringRef algorithm;
+ if (!readStringData(algorithm))
+ return JSValue();
+ CachedStringRef value;
+ if (!readStringData(value))
+ return JSValue();
+ fingerprints.uncheckedAppend(RTCCertificate::DtlsFingerprint { algorithm->string(), value->string() });
+ }
+
+ if (!m_canCreateDOMObject)
+ return constructEmptyObject(m_lexicalGlobalObject, m_globalObject->objectPrototype());
+
+ auto rtcCertificate = RTCCertificate::create(SecurityOrigin::createFromString(origin->string()), expires, WTFMove(fingerprints), certificate->takeString(), keyedMaterial->takeString());
+ return toJSNewlyCreated(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), WTFMove(rtcCertificate));
+ }
+
+ JSValue readRTCDataChannel()
+ {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_detachedRTCDataChannels.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_rtcDataChannels[index]) {
+ auto detachedChannel = WTFMove(m_detachedRTCDataChannels.at(index));
+ m_rtcDataChannels[index] = RTCDataChannel::create(*executionContext(m_lexicalGlobalObject), detachedChannel->identifier, WTFMove(detachedChannel->label), WTFMove(detachedChannel->options), detachedChannel->state);
+ }
+
+ return getJSValue(m_rtcDataChannels[index].get());
+ }
+#endif
+
+ JSValue readImageBitmap()
+ {
+ uint8_t serializationState;
+ int32_t logicalWidth;
+ int32_t logicalHeight;
+ double resolutionScale;
+ auto colorSpace = DestinationColorSpace::SRGB();
+ RefPtr<ArrayBuffer> arrayBuffer;
+
+ if (!read(serializationState) || !read(logicalWidth) || !read(logicalHeight) || !read(resolutionScale) || (m_version > 8 && !read(colorSpace)) || !readArrayBufferImpl<uint32_t>(arrayBuffer)) {
+ fail();
+ return JSValue();
+ }
+
+ auto logicalSize = IntSize(logicalWidth, logicalHeight);
+ auto imageDataSize = logicalSize;
+ imageDataSize.scale(resolutionScale);
+
+ auto buffer = ImageBitmap::createImageBuffer(*executionContext(m_lexicalGlobalObject), logicalSize, RenderingMode::Unaccelerated, colorSpace, resolutionScale);
+ if (!buffer) {
+ fail();
+ return JSValue();
+ }
+
+ PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, colorSpace };
+ auto pixelBuffer = PixelBuffer::tryCreate(format, imageDataSize, arrayBuffer.releaseNonNull());
+ if (!pixelBuffer) {
+ fail();
+ return JSValue();
+ }
+
+ buffer->putPixelBuffer(WTFMove(*pixelBuffer), { IntPoint::zero(), logicalSize });
+
+ auto bitmap = ImageBitmap::create(ImageBitmapBacking(WTFMove(buffer), OptionSet<SerializationState>::fromRaw(serializationState)));
+ return getJSValue(bitmap);
+ }
+
+ JSValue readDOMException()
+ {
+ CachedStringRef message;
+ if (!readStringData(message))
+ return JSValue();
+ CachedStringRef name;
+ if (!readStringData(name))
+ return JSValue();
+ auto exception = DOMException::create(message->string(), name->string());
+ return getJSValue(exception);
+ }
+
+ JSValue readBigInt()
+ {
+ uint8_t sign = 0;
+ if (!read(sign))
+ return JSValue();
+ uint32_t lengthInUint64 = 0;
+ if (!read(lengthInUint64))
+ return JSValue();
+
+ if (!lengthInUint64) {
+#if USE(BIGINT32)
+ return jsBigInt32(0);
+#else
+ JSBigInt* bigInt = JSBigInt::tryCreateZero(m_lexicalGlobalObject->vm());
+ if (UNLIKELY(!bigInt)) {
+ fail();
+ return JSValue();
+ }
+ m_gcBuffer.appendWithCrashOnOverflow(bigInt);
+ return bigInt;
+#endif
+ }
+
+#if USE(BIGINT32)
+ static_assert(sizeof(JSBigInt::Digit) == sizeof(uint64_t));
+ if (lengthInUint64 == 1) {
+ uint64_t digit64 = 0;
+ if (!read(digit64))
+ return JSValue();
+ if (sign) {
+ if (digit64 <= static_cast<uint64_t>(-static_cast<int64_t>(INT32_MIN)))
+ return jsBigInt32(static_cast<int32_t>(-static_cast<int64_t>(digit64)));
+ } else {
+ if (digit64 <= INT32_MAX)
+ return jsBigInt32(static_cast<int32_t>(digit64));
+ }
+ ASSERT(digit64 != 0);
+ JSBigInt* bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), 1);
+ if (!bigInt) {
+ fail();
+ return JSValue();
+ }
+ bigInt->setDigit(0, digit64);
+ bigInt->setSign(sign);
+ bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm());
+ if (!bigInt) {
+ fail();
+ return JSValue();
+ }
+ m_gcBuffer.appendWithCrashOnOverflow(bigInt);
+ return tryConvertToBigInt32(bigInt);
+ }
+#endif
+ JSBigInt* bigInt = nullptr;
+ if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
+ bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64);
+ if (!bigInt) {
+ fail();
+ return JSValue();
+ }
+ for (uint32_t index = 0; index < lengthInUint64; ++index) {
+ uint64_t digit64 = 0;
+ if (!read(digit64))
+ return JSValue();
+ bigInt->setDigit(index, digit64);
+ }
+ } else {
+ ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
+ bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), lengthInUint64 * 2);
+ if (!bigInt) {
+ fail();
+ return JSValue();
+ }
+ for (uint32_t index = 0; index < lengthInUint64; ++index) {
+ uint64_t digit64 = 0;
+ if (!read(digit64))
+ return JSValue();
+ bigInt->setDigit(index * 2, static_cast<uint32_t>(digit64));
+ bigInt->setDigit(index * 2 + 1, static_cast<uint32_t>(digit64 >> 32));
+ }
+ }
+ bigInt->setSign(sign);
+ bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm());
+ if (!bigInt) {
+ fail();
+ return JSValue();
+ }
+ m_gcBuffer.appendWithCrashOnOverflow(bigInt);
+ return tryConvertToBigInt32(bigInt);
+ }
+
+ JSValue readTerminal()
+ {
+ SerializationTag tag = readTag();
+ switch (tag) {
+ case UndefinedTag:
+ return jsUndefined();
+ case NullTag:
+ return jsNull();
+ case IntTag: {
+ int32_t i;
+ if (!read(i))
+ return JSValue();
+ return jsNumber(i);
+ }
+ case ZeroTag:
+ return jsNumber(0);
+ case OneTag:
+ return jsNumber(1);
+ case FalseTag:
+ return jsBoolean(false);
+ case TrueTag:
+ return jsBoolean(true);
+ case FalseObjectTag: {
+ BooleanObject* obj = BooleanObject::create(m_lexicalGlobalObject->vm(), m_globalObject->booleanObjectStructure());
+ obj->setInternalValue(m_lexicalGlobalObject->vm(), jsBoolean(false));
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case TrueObjectTag: {
+ BooleanObject* obj = BooleanObject::create(m_lexicalGlobalObject->vm(), m_globalObject->booleanObjectStructure());
+ obj->setInternalValue(m_lexicalGlobalObject->vm(), jsBoolean(true));
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case DoubleTag: {
+ double d;
+ if (!read(d))
+ return JSValue();
+ return jsNumber(d);
+ }
+ case BigIntTag:
+ return readBigInt();
+ case NumberObjectTag: {
+ double d;
+ if (!read(d))
+ return JSValue();
+ NumberObject* obj = constructNumber(m_globalObject, jsNumber(d));
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case BigIntObjectTag: {
+ JSValue bigInt = readBigInt();
+ if (!bigInt)
+ return JSValue();
+ ASSERT(bigInt.isBigInt());
+ BigIntObject* obj = BigIntObject::create(m_lexicalGlobalObject->vm(), m_globalObject, bigInt);
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case DateTag: {
+ double d;
+ if (!read(d))
+ return JSValue();
+ return DateInstance::create(m_lexicalGlobalObject->vm(), m_globalObject->dateStructure(), d);
+ }
+ case FileTag: {
+ RefPtr<File> file;
+ if (!readFile(file))
+ return JSValue();
+ if (!m_canCreateDOMObject)
+ return jsNull();
+ return toJS(m_lexicalGlobalObject, jsCast<JSDOMGlobalObject*>(m_globalObject), file.get());
+ }
+ case FileListTag: {
+ unsigned length = 0;
+ if (!read(length))
+ return JSValue();
+ ASSERT(m_globalObject->inherits<JSDOMGlobalObject>(m_globalObject->vm()));
+ Vector<Ref<File>> files;
+ for (unsigned i = 0; i < length; i++) {
+ RefPtr<File> file;
+ if (!readFile(file))
+ return JSValue();
+ if (m_canCreateDOMObject)
+ files.append(file.releaseNonNull());
+ }
+ if (!m_canCreateDOMObject)
+ return jsNull();
+ return getJSValue(FileList::create(WTFMove(files)).get());
+ }
+ case ImageDataTag: {
+ uint32_t width;
+ if (!read(width))
+ return JSValue();
+ uint32_t height;
+ if (!read(height))
+ return JSValue();
+ uint32_t length;
+ if (!read(length))
+ return JSValue();
+ if (static_cast<uint32_t>(m_end - m_ptr) < length) {
+ fail();
+ return JSValue();
+ }
+ auto bufferStart = m_ptr;
+ m_ptr += length;
+
+ auto resultColorSpace = PredefinedColorSpace::SRGB;
+ if (m_version > 7) {
+ if (!read(resultColorSpace))
+ return JSValue();
+ }
+
+ if (length && (IntSize(width, height).area() * 4) != length) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_isDOMGlobalObject)
+ return jsNull();
+
+ auto result = ImageData::createUninitialized(width, height, resultColorSpace);
+ if (result.hasException()) {
+ fail();
+ return JSValue();
+ }
+ if (length)
+ memcpy(result.returnValue()->data().data(), bufferStart, length);
+ else
+ result.returnValue()->data().zeroFill();
+ return getJSValue(result.releaseReturnValue());
+ }
+ case BlobTag: {
+ CachedStringRef url;
+ if (!readStringData(url))
+ return JSValue();
+ CachedStringRef type;
+ if (!readStringData(type))
+ return JSValue();
+ uint64_t size = 0;
+ if (!read(size))
+ return JSValue();
+ if (!m_canCreateDOMObject)
+ return jsNull();
+ return getJSValue(Blob::deserialize(executionContext(m_lexicalGlobalObject), URL { url->string() }, type->string(), size, blobFilePathForBlobURL(url->string())).get());
+ }
+ case StringTag: {
+ CachedStringRef cachedString;
+ if (!readStringData(cachedString))
+ return JSValue();
+ return cachedString->jsString(m_lexicalGlobalObject);
+ }
+ case EmptyStringTag:
+ return jsEmptyString(m_lexicalGlobalObject->vm());
+ case StringObjectTag: {
+ CachedStringRef cachedString;
+ if (!readStringData(cachedString))
+ return JSValue();
+ StringObject* obj = constructString(m_lexicalGlobalObject->vm(), m_globalObject, cachedString->jsString(m_lexicalGlobalObject));
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case EmptyStringObjectTag: {
+ VM& vm = m_lexicalGlobalObject->vm();
+ StringObject* obj = constructString(vm, m_globalObject, jsEmptyString(vm));
+ m_gcBuffer.appendWithCrashOnOverflow(obj);
+ return obj;
+ }
+ case RegExpTag: {
+ CachedStringRef pattern;
+ if (!readStringData(pattern))
+ return JSValue();
+ CachedStringRef flags;
+ if (!readStringData(flags))
+ return JSValue();
+ auto reFlags = Yarr::parseFlags(flags->string());
+ ASSERT(reFlags.has_value());
+ VM& vm = m_lexicalGlobalObject->vm();
+ RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags.value());
+ return RegExpObject::create(vm, m_globalObject->regExpStructure(), regExp);
+ }
+ case ObjectReferenceTag: {
+ unsigned index = 0;
+ if (!readConstantPoolIndex(m_gcBuffer, index)) {
+ fail();
+ return JSValue();
+ }
+ return m_gcBuffer.at(index);
+ }
+ case MessagePortReferenceTag: {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_messagePorts.size()) {
+ fail();
+ return JSValue();
+ }
+ return getJSValue(m_messagePorts[index].get());
+ }
+#if ENABLE(WEBASSEMBLY)
+ case WasmModuleTag: {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || !m_wasmModules || index >= m_wasmModules->size()) {
+ fail();
+ return JSValue();
+ }
+ auto scope = DECLARE_THROW_SCOPE(m_lexicalGlobalObject->vm());
+ JSValue result = JSC::JSWebAssemblyModule::createStub(m_lexicalGlobalObject->vm(), m_lexicalGlobalObject, m_globalObject->webAssemblyModuleStructure(), m_wasmModules->at(index));
+ // Since we are cloning a JSWebAssemblyModule, it's impossible for that
+ // module to not have been a valid module. Therefore, createStub should
+ // not throw.
+ scope.releaseAssertNoException();
+ m_gcBuffer.appendWithCrashOnOverflow(result);
+ return result;
+ }
+ case WasmMemoryTag: {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || !m_wasmMemoryHandles || index >= m_wasmMemoryHandles->size()) {
+ fail();
+ return JSValue();
+ }
+ RefPtr<Wasm::MemoryHandle> handle = m_wasmMemoryHandles->at(index);
+ if (!handle) {
+ fail();
+ return JSValue();
+ }
+ auto& vm = m_lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSWebAssemblyMemory* result = JSC::JSWebAssemblyMemory::tryCreate(m_lexicalGlobalObject, vm, m_globalObject->webAssemblyMemoryStructure());
+ // Since we are cloning a JSWebAssemblyMemory, it's impossible for that
+ // module to not have been a valid module. Therefore, createStub should
+ // not throw.
+ scope.releaseAssertNoException();
+ Ref<Wasm::Memory> memory = Wasm::Memory::create(
+ handle.releaseNonNull(),
+ [&vm](Wasm::Memory::NotifyPressure) { vm.heap.collectAsync(CollectionScope::Full); },
+ [&vm](Wasm::Memory::SyncTryToReclaim) { vm.heap.collectSync(CollectionScope::Full); },
+ [&vm, result](Wasm::Memory::GrowSuccess, Wasm::PageCount oldPageCount, Wasm::PageCount newPageCount) { result->growSuccessCallback(vm, oldPageCount, newPageCount); });
+ result->adopt(WTFMove(memory));
+ m_gcBuffer.appendWithCrashOnOverflow(result);
+ return result;
+ }
+#endif
+ case ArrayBufferTag: {
+ RefPtr<ArrayBuffer> arrayBuffer;
+ if (!readArrayBuffer(arrayBuffer)) {
+ fail();
+ return JSValue();
+ }
+ Structure* structure = m_globalObject->arrayBufferStructure(arrayBuffer->sharingMode());
+ // A crazy RuntimeFlags mismatch could mean that we are not equipped to handle shared
+ // array buffers while the sender is. In that case, we would see a null structure here.
+ if (!structure) {
+ fail();
+ return JSValue();
+ }
+ JSValue result = JSArrayBuffer::create(m_lexicalGlobalObject->vm(), structure, WTFMove(arrayBuffer));
+ m_gcBuffer.appendWithCrashOnOverflow(result);
+ return result;
+ }
+ case ArrayBufferTransferTag: {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_arrayBuffers.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_arrayBuffers[index])
+ m_arrayBuffers[index] = ArrayBuffer::create(WTFMove(m_arrayBufferContents->at(index)));
+
+ return getJSValue(m_arrayBuffers[index].get());
+ }
+ case SharedArrayBufferTag: {
+ uint32_t index = UINT_MAX;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || !m_sharedBuffers || index >= m_sharedBuffers->size()) {
+ fail();
+ return JSValue();
+ }
+
+ RELEASE_ASSERT(m_sharedBuffers->at(index));
+ auto buffer = ArrayBuffer::create(WTFMove(m_sharedBuffers->at(index)));
+ JSValue result = getJSValue(buffer.get());
+ m_gcBuffer.appendWithCrashOnOverflow(result);
+ return result;
+ }
+ case ArrayBufferViewTag: {
+ JSValue arrayBufferView;
+ if (!readArrayBufferView(m_lexicalGlobalObject->vm(), arrayBufferView)) {
+ fail();
+ return JSValue();
+ }
+ m_gcBuffer.appendWithCrashOnOverflow(arrayBufferView);
+ return arrayBufferView;
+ }
+#if ENABLE(WEB_CRYPTO)
+ case CryptoKeyTag: {
+ Vector<uint8_t> wrappedKey;
+ if (!read(wrappedKey)) {
+ fail();
+ return JSValue();
+ }
+ Vector<uint8_t> serializedKey;
+ if (!unwrapCryptoKey(m_lexicalGlobalObject, wrappedKey, serializedKey)) {
+ fail();
+ return JSValue();
+ }
+ JSValue cryptoKey;
+ Vector<RefPtr<MessagePort>> dummyMessagePorts;
+ CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, dummyMessagePorts, nullptr, {}, serializedKey);
+ if (!rawKeyDeserializer.readCryptoKey(cryptoKey)) {
+ fail();
+ return JSValue();
+ }
+ m_gcBuffer.appendWithCrashOnOverflow(cryptoKey);
+ return cryptoKey;
+ }
+#endif
+ // case DOMPointReadOnlyTag:
+ // return readDOMPoint<DOMPointReadOnly>();
+ // case DOMPointTag:
+ // return readDOMPoint<DOMPoint>();
+ // case DOMRectReadOnlyTag:
+ // return readDOMRect<DOMRectReadOnly>();
+ // case DOMRectTag:
+ // return readDOMRect<DOMRect>();
+ // case DOMMatrixReadOnlyTag:
+ // return readDOMMatrix<DOMMatrixReadOnly>();
+ // case DOMMatrixTag:
+ // return readDOMMatrix<DOMMatrix>();
+ // case DOMQuadTag:
+ // return readDOMQuad();
+ // case ImageBitmapTransferTag:
+ // return readTransferredImageBitmap();
+#if ENABLE(WEB_RTC)
+ case RTCCertificateTag:
+ return readRTCCertificate();
+
+#endif
+ // case ImageBitmapTag:
+ // return readImageBitmap();
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ case OffscreenCanvasTransferTag:
+ return readOffscreenCanvas();
+#endif
+#if ENABLE(WEB_RTC)
+ case RTCDataChannelTransferTag:
+ return readRTCDataChannel();
+#endif
+ case DOMExceptionTag:
+ return readDOMException();
+
+ default:
+ m_ptr--; // Push the tag back
+ return JSValue();
+ }
+ }
+
+ template<SerializationTag Tag>
+ bool consumeCollectionDataTerminationIfPossible()
+ {
+ if (readTag() == Tag)
+ return true;
+ m_ptr--;
+ return false;
+ }
+
+ JSGlobalObject* m_globalObject;
+ bool m_isDOMGlobalObject;
+ bool m_canCreateDOMObject;
+ const uint8_t* m_ptr;
+ const uint8_t* m_end;
+ unsigned m_version;
+ Vector<CachedString> m_constantPool;
+ const Vector<RefPtr<MessagePort>>& m_messagePorts;
+ ArrayBufferContentsArray* m_arrayBufferContents;
+ Vector<RefPtr<JSC::ArrayBuffer>> m_arrayBuffers;
+ // Vector<String> m_blobURLs;
+ // Vector<String> m_blobFilePaths;
+ ArrayBufferContentsArray* m_sharedBuffers;
+ // Vector<std::optional<ImageBitmapBacking>> m_backingStores;
+ // Vector<RefPtr<ImageBitmap>> m_imageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases;
+ Vector<RefPtr<OffscreenCanvas>> m_offscreenCanvases;
+#endif
+#if ENABLE(WEB_RTC)
+ Vector<std::unique_ptr<DetachedRTCDataChannel>> m_detachedRTCDataChannels;
+ Vector<RefPtr<RTCDataChannel>> m_rtcDataChannels;
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray* m_wasmModules;
+ WasmMemoryHandleArray* m_wasmMemoryHandles;
+#endif
+
+ // String blobFilePathForBlobURL(const String& blobURL)
+ // {
+ // size_t i = 0;
+ // for (; i < m_blobURLs.size(); ++i) {
+ // if (m_blobURLs[i] == blobURL)
+ // break;
+ // }
+
+ // return i < m_blobURLs.size() ? m_blobFilePaths[i] : String();
+ // }
+};
+
+DeserializationResult CloneDeserializer::deserialize()
+{
+ VM& vm = m_lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Vector<uint32_t, 16> indexStack;
+ Vector<Identifier, 16> propertyNameStack;
+ Vector<JSObject*, 32> outputObjectStack;
+ Vector<JSValue, 4> mapKeyStack;
+ Vector<JSMap*, 4> mapStack;
+ Vector<JSSet*, 4> setStack;
+ Vector<WalkerState, 16> stateStack;
+ WalkerState lexicalGlobalObject = StateUnknown;
+ JSValue outValue;
+
+ while (1) {
+ switch (lexicalGlobalObject) {
+ arrayStartState:
+ case ArrayStartState: {
+ uint32_t length;
+ if (!read(length)) {
+ fail();
+ goto error;
+ }
+ JSArray* outArray = constructEmptyArray(m_globalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), length);
+ if (UNLIKELY(scope.exception()))
+ goto error;
+ m_gcBuffer.appendWithCrashOnOverflow(outArray);
+ outputObjectStack.append(outArray);
+ }
+ arrayStartVisitMember:
+ FALLTHROUGH;
+ case ArrayStartVisitMember: {
+ uint32_t index;
+ if (!read(index)) {
+ fail();
+ goto error;
+ }
+ if (index == TerminatorTag) {
+ JSObject* outArray = outputObjectStack.last();
+ outValue = outArray;
+ outputObjectStack.removeLast();
+ break;
+ } else if (index == NonIndexPropertiesTag) {
+ goto objectStartVisitMember;
+ }
+
+ if (JSValue terminal = readTerminal()) {
+ putProperty(outputObjectStack.last(), index, terminal);
+ goto arrayStartVisitMember;
+ }
+ if (m_failed)
+ goto error;
+ indexStack.append(index);
+ stateStack.append(ArrayEndVisitMember);
+ goto stateUnknown;
+ }
+ case ArrayEndVisitMember: {
+ JSObject* outArray = outputObjectStack.last();
+ putProperty(outArray, indexStack.last(), outValue);
+ indexStack.removeLast();
+ goto arrayStartVisitMember;
+ }
+ objectStartState:
+ case ObjectStartState: {
+ if (outputObjectStack.size() > maximumFilterRecursion)
+ return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
+ JSObject* outObject = constructEmptyObject(m_lexicalGlobalObject, m_globalObject->objectPrototype());
+ m_gcBuffer.appendWithCrashOnOverflow(outObject);
+ outputObjectStack.append(outObject);
+ }
+ objectStartVisitMember:
+ FALLTHROUGH;
+ case ObjectStartVisitMember: {
+ CachedStringRef cachedString;
+ bool wasTerminator = false;
+ if (!readStringData(cachedString, wasTerminator)) {
+ if (!wasTerminator)
+ goto error;
+
+ JSObject* outObject = outputObjectStack.last();
+ outValue = outObject;
+ outputObjectStack.removeLast();
+ break;
+ }
+
+ if (JSValue terminal = readTerminal()) {
+ putProperty(outputObjectStack.last(), Identifier::fromString(vm, cachedString->string()), terminal);
+ goto objectStartVisitMember;
+ }
+ stateStack.append(ObjectEndVisitMember);
+ propertyNameStack.append(Identifier::fromString(vm, cachedString->string()));
+ goto stateUnknown;
+ }
+ case ObjectEndVisitMember: {
+ putProperty(outputObjectStack.last(), propertyNameStack.last(), outValue);
+ propertyNameStack.removeLast();
+ goto objectStartVisitMember;
+ }
+ mapObjectStartState : {
+ if (outputObjectStack.size() > maximumFilterRecursion)
+ return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
+ JSMap* map = JSMap::create(m_lexicalGlobalObject, m_lexicalGlobalObject->vm(), m_globalObject->mapStructure());
+ if (UNLIKELY(scope.exception()))
+ goto error;
+ m_gcBuffer.appendWithCrashOnOverflow(map);
+ outputObjectStack.append(map);
+ mapStack.append(map);
+ goto mapDataStartVisitEntry;
+ }
+ mapDataStartVisitEntry:
+ case MapDataStartVisitEntry: {
+ if (consumeCollectionDataTerminationIfPossible<NonMapPropertiesTag>()) {
+ mapStack.removeLast();
+ goto objectStartVisitMember;
+ }
+ stateStack.append(MapDataEndVisitKey);
+ goto stateUnknown;
+ }
+ case MapDataEndVisitKey: {
+ mapKeyStack.append(outValue);
+ stateStack.append(MapDataEndVisitValue);
+ goto stateUnknown;
+ }
+ case MapDataEndVisitValue: {
+ mapStack.last()->set(m_lexicalGlobalObject, mapKeyStack.last(), outValue);
+ mapKeyStack.removeLast();
+ goto mapDataStartVisitEntry;
+ }
+
+ setObjectStartState : {
+ if (outputObjectStack.size() > maximumFilterRecursion)
+ return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
+ JSSet* set = JSSet::create(m_lexicalGlobalObject, m_lexicalGlobalObject->vm(), m_globalObject->setStructure());
+ if (UNLIKELY(scope.exception()))
+ goto error;
+ m_gcBuffer.appendWithCrashOnOverflow(set);
+ outputObjectStack.append(set);
+ setStack.append(set);
+ goto setDataStartVisitEntry;
+ }
+ setDataStartVisitEntry:
+ case SetDataStartVisitEntry: {
+ if (consumeCollectionDataTerminationIfPossible<NonSetPropertiesTag>()) {
+ setStack.removeLast();
+ goto objectStartVisitMember;
+ }
+ stateStack.append(SetDataEndVisitKey);
+ goto stateUnknown;
+ }
+ case SetDataEndVisitKey: {
+ JSSet* set = setStack.last();
+ set->add(m_lexicalGlobalObject, outValue);
+ goto setDataStartVisitEntry;
+ }
+
+ stateUnknown:
+ case StateUnknown:
+ if (JSValue terminal = readTerminal()) {
+ outValue = terminal;
+ break;
+ }
+ SerializationTag tag = readTag();
+ if (tag == ArrayTag)
+ goto arrayStartState;
+ if (tag == ObjectTag)
+ goto objectStartState;
+ if (tag == MapObjectTag)
+ goto mapObjectStartState;
+ if (tag == SetObjectTag)
+ goto setObjectStartState;
+ goto error;
+ }
+ if (stateStack.isEmpty())
+ break;
+
+ lexicalGlobalObject = stateStack.last();
+ stateStack.removeLast();
+ }
+ ASSERT(outValue);
+ ASSERT(!m_failed);
+ return std::make_pair(outValue, SerializationReturnCode::SuccessfullyCompleted);
+error:
+ fail();
+ return std::make_pair(JSValue(), SerializationReturnCode::ValidationError);
+}
+
+SerializedScriptValue::~SerializedScriptValue() = default;
+
+SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray>&& arrayBufferContentsArray
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels
+#endif
+ )
+ : m_data(WTFMove(buffer))
+ , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
+#if ENABLE(WEB_RTC)
+ , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels))
+#endif
+{
+ m_memoryCost = computeMemoryCost();
+}
+
+SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, /* const Vector<BlobURLHandle>& blobHandles, */ std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray, Vector<std::optional<ImageBitmapBacking>>&& backingStores
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& detachedOffscreenCanvases
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& detachedRTCDataChannels
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ std::unique_ptr<WasmModuleArray> wasmModulesArray, std::unique_ptr<WasmMemoryHandleArray> wasmMemoryHandlesArray
+#endif
+ )
+ : m_data(WTFMove(buffer))
+ , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
+ , m_sharedBufferContentsArray(WTFMove(sharedBufferContentsArray))
+ , m_backingStores(WTFMove(backingStores))
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ , m_detachedOffscreenCanvases(WTFMove(detachedOffscreenCanvases))
+#endif
+#if ENABLE(WEB_RTC)
+ , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels))
+#endif
+#if ENABLE(WEBASSEMBLY)
+ , m_wasmModulesArray(WTFMove(wasmModulesArray))
+ , m_wasmMemoryHandlesArray(WTFMove(wasmMemoryHandlesArray))
+#endif
+// , m_blobHandles(blobHandles)
+{
+ m_memoryCost = computeMemoryCost();
+}
+
+size_t SerializedScriptValue::computeMemoryCost() const
+{
+ size_t cost = m_data.size();
+
+ if (m_arrayBufferContentsArray) {
+ for (auto& content : *m_arrayBufferContentsArray)
+ cost += content.sizeInBytes();
+ }
+
+ if (m_sharedBufferContentsArray) {
+ for (auto& content : *m_sharedBufferContentsArray)
+ cost += content.sizeInBytes();
+ }
+
+ for (auto& backingStore : m_backingStores) {
+ if (auto buffer = backingStore ? backingStore->buffer() : nullptr)
+ cost += buffer->memoryCost();
+ }
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ for (auto& canvas : m_detachedOffscreenCanvases) {
+ if (canvas)
+ cost += canvas->memoryCost();
+ }
+#endif
+#if ENABLE(WEB_RTC)
+ for (auto& channel : m_detachedRTCDataChannels) {
+ if (channel)
+ cost += channel->memoryCost();
+ }
+#endif
+
+#if ENABLE(WEBASSEMBLY)
+ // We are not supporting WebAssembly Module memory estimation yet.
+ if (m_wasmMemoryHandlesArray) {
+ for (auto& content : *m_wasmMemoryHandlesArray)
+ cost += content->size();
+ }
+#endif
+
+ // for (auto& handle : m_blobHandles)
+ // cost += handle.url().string().sizeInBytes();
+
+ return cost;
+}
+
+static ExceptionOr<std::unique_ptr<ArrayBufferContentsArray>> transferArrayBuffers(VM& vm, const Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers)
+{
+ if (arrayBuffers.isEmpty())
+ return nullptr;
+
+ auto contents = makeUnique<ArrayBufferContentsArray>(arrayBuffers.size());
+
+ HashSet<JSC::ArrayBuffer*> visited;
+ for (size_t arrayBufferIndex = 0; arrayBufferIndex < arrayBuffers.size(); arrayBufferIndex++) {
+ if (visited.contains(arrayBuffers[arrayBufferIndex].get()))
+ continue;
+ visited.add(arrayBuffers[arrayBufferIndex].get());
+
+ bool result = arrayBuffers[arrayBufferIndex]->transferTo(vm, contents->at(arrayBufferIndex));
+ if (!result)
+ return Exception { TypeError };
+ }
+
+ return contents;
+}
+
+static void maybeThrowExceptionIfSerializationFailed(JSGlobalObject& lexicalGlobalObject, SerializationReturnCode code)
+{
+ auto& vm = lexicalGlobalObject.vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ switch (code) {
+ case SerializationReturnCode::SuccessfullyCompleted:
+ break;
+ case SerializationReturnCode::StackOverflowError:
+ throwException(&lexicalGlobalObject, scope, createStackOverflowError(&lexicalGlobalObject));
+ break;
+ case SerializationReturnCode::ValidationError:
+ throwTypeError(&lexicalGlobalObject, scope, "Unable to deserialize data."_s);
+ break;
+ case SerializationReturnCode::DataCloneError:
+ throwDataCloneError(lexicalGlobalObject, scope);
+ break;
+ case SerializationReturnCode::ExistingExceptionError:
+ case SerializationReturnCode::UnspecifiedError:
+ break;
+ case SerializationReturnCode::InterruptedExecutionError:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+static Exception exceptionForSerializationFailure(SerializationReturnCode code)
+{
+ ASSERT(code != SerializationReturnCode::SuccessfullyCompleted);
+
+ switch (code) {
+ case SerializationReturnCode::StackOverflowError:
+ return Exception { StackOverflowError };
+ case SerializationReturnCode::ValidationError:
+ return Exception { TypeError };
+ case SerializationReturnCode::DataCloneError:
+ return Exception { DataCloneError };
+ case SerializationReturnCode::ExistingExceptionError:
+ return Exception { ExistingExceptionError };
+ case SerializationReturnCode::UnspecifiedError:
+ return Exception { TypeError };
+ case SerializationReturnCode::SuccessfullyCompleted:
+ case SerializationReturnCode::InterruptedExecutionError:
+ ASSERT_NOT_REACHED();
+ return Exception { TypeError };
+ }
+ ASSERT_NOT_REACHED();
+ return Exception { TypeError };
+}
+
+static bool containsDuplicates(const Vector<RefPtr<ImageBitmap>>& imageBitmaps)
+{
+ HashSet<ImageBitmap*> visited;
+ for (auto& imageBitmap : imageBitmaps) {
+ if (!visited.add(imageBitmap.get()))
+ return true;
+ }
+ return false;
+}
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+static bool canOffscreenCanvasesDetach(const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases)
+{
+ HashSet<OffscreenCanvas*> visited;
+ for (auto& offscreenCanvas : offscreenCanvases) {
+ if (!offscreenCanvas->canDetach())
+ return false;
+ // Check the return value of add, we should not encounter duplicates.
+ if (!visited.add(offscreenCanvas.get()))
+ return false;
+ }
+ return true;
+}
+#endif
+
+#if ENABLE(WEB_RTC)
+static bool canDetachRTCDataChannels(const Vector<Ref<RTCDataChannel>>& channels)
+{
+ HashSet<RTCDataChannel*> visited;
+ for (auto& channel : channels) {
+ if (!channel->canDetach())
+ return false;
+ // Check the return value of add, we should not encounter duplicates.
+ if (!visited.add(channel.ptr()))
+ return false;
+ }
+ return true;
+}
+#endif
+
+RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSC::JSGlobalObject& globalObject, JSC::JSValue value, SerializationErrorMode throwExceptions, SerializationContext serializationContext)
+{
+ Vector<RefPtr<MessagePort>> dummyPorts;
+ auto result = create(globalObject, value, {}, dummyPorts, throwExceptions, serializationContext);
+ if (result.hasException())
+ return nullptr;
+ return result.releaseReturnValue();
+}
+
+ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& globalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationContext serializationContext)
+{
+ return create(globalObject, value, WTFMove(transferList), messagePorts, SerializationErrorMode::NonThrowing, serializationContext);
+}
+
+ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& lexicalGlobalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationErrorMode throwExceptions, SerializationContext context)
+{
+ VM& vm = lexicalGlobalObject.vm();
+ Vector<RefPtr<JSC::ArrayBuffer>> arrayBuffers;
+ Vector<RefPtr<ImageBitmap>> imageBitmaps;
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ Vector<RefPtr<OffscreenCanvas>> offscreenCanvases;
+#endif
+#if ENABLE(WEB_RTC)
+ Vector<Ref<RTCDataChannel>> dataChannels;
+#endif
+ HashSet<JSC::JSObject*> uniqueTransferables;
+ for (auto& transferable : transferList) {
+ if (!uniqueTransferables.add(transferable.get()).isNewEntry)
+ return Exception { DataCloneError, "Duplicate transferable for structured clone"_s };
+
+ if (auto arrayBuffer = toPossiblySharedArrayBuffer(vm, transferable.get())) {
+ if (arrayBuffer->isDetached() || arrayBuffer->isShared())
+ return Exception { DataCloneError };
+ if (arrayBuffer->isLocked()) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ throwVMTypeError(&lexicalGlobalObject, scope, errorMesasgeForTransfer(arrayBuffer));
+ return Exception { ExistingExceptionError };
+ }
+ arrayBuffers.append(WTFMove(arrayBuffer));
+ continue;
+ }
+ if (auto port = JSMessagePort::toWrapped(vm, transferable.get())) {
+ // FIXME: This should check if the port is detached as per https://html.spec.whatwg.org/multipage/infrastructure.html#istransferable.
+ messagePorts.append(WTFMove(port));
+ continue;
+ }
+
+ if (auto imageBitmap = JSImageBitmap::toWrapped(vm, transferable.get())) {
+ if (imageBitmap->isDetached())
+ return Exception { DataCloneError };
+
+ imageBitmaps.append(WTFMove(imageBitmap));
+ continue;
+ }
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ if (auto offscreenCanvas = JSOffscreenCanvas::toWrapped(vm, transferable.get())) {
+ offscreenCanvases.append(WTFMove(offscreenCanvas));
+ continue;
+ }
+#endif
+
+#if ENABLE(WEB_RTC)
+ if (auto channel = JSRTCDataChannel::toWrapped(vm, transferable.get())) {
+ dataChannels.append(*channel);
+ continue;
+ }
+#endif
+
+ return Exception { DataCloneError };
+ }
+
+ if (containsDuplicates(imageBitmaps))
+ return Exception { DataCloneError };
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ if (!canOffscreenCanvasesDetach(offscreenCanvases))
+ return Exception { InvalidStateError };
+#endif
+#if ENABLE(WEB_RTC)
+ if (!canDetachRTCDataChannels(dataChannels))
+ return Exception { DataCloneError };
+#endif
+
+ Vector<uint8_t> buffer;
+ // Vector<BlobURLHandle> blobHandles;
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray wasmModules;
+ WasmMemoryHandleArray wasmMemoryHandles;
+#endif
+ std::unique_ptr<ArrayBufferContentsArray> sharedBuffers = makeUnique<ArrayBufferContentsArray>();
+ auto code = CloneSerializer::serialize(&lexicalGlobalObject, value, messagePorts, arrayBuffers, imageBitmaps,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ dataChannels,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ wasmModules,
+ wasmMemoryHandles,
+#endif
+ /*blobHandles,*/ buffer, context, *sharedBuffers);
+
+ if (throwExceptions == SerializationErrorMode::Throwing)
+ maybeThrowExceptionIfSerializationFailed(lexicalGlobalObject, code);
+
+ if (code != SerializationReturnCode::SuccessfullyCompleted)
+ return exceptionForSerializationFailure(code);
+
+ auto arrayBufferContentsArray = transferArrayBuffers(vm, arrayBuffers);
+ if (arrayBufferContentsArray.hasException())
+ return arrayBufferContentsArray.releaseException();
+
+ auto backingStores = ImageBitmap::detachBitmaps(WTFMove(imageBitmaps));
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> detachedCanvases;
+ for (auto offscreenCanvas : offscreenCanvases)
+ detachedCanvases.append(offscreenCanvas->detach());
+#endif
+#if ENABLE(WEB_RTC)
+ Vector<std::unique_ptr<DetachedRTCDataChannel>> detachedRTCDataChannels;
+ for (auto& channel : dataChannels)
+ detachedRTCDataChannels.append(channel->detach());
+#endif
+
+ return adoptRef(*new SerializedScriptValue(WTFMove(buffer), /*blobHandles,*/ arrayBufferContentsArray.releaseReturnValue(), context == SerializationContext::WorkerPostMessage ? WTFMove(sharedBuffers) : nullptr, WTFMove(backingStores)
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ WTFMove(detachedCanvases)
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ WTFMove(detachedRTCDataChannels)
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ makeUnique<WasmModuleArray>(wasmModules), context == SerializationContext::WorkerPostMessage ? makeUnique<WasmMemoryHandleArray>(wasmMemoryHandles) : nullptr
+#endif
+ ));
+}
+
+RefPtr<SerializedScriptValue> SerializedScriptValue::create(StringView string)
+{
+ Vector<uint8_t> buffer;
+ if (!CloneSerializer::serialize(string, buffer))
+ return nullptr;
+ return adoptRef(*new SerializedScriptValue(WTFMove(buffer)));
+}
+
+RefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef originContext, JSValueRef apiValue, JSValueRef* exception)
+{
+ JSGlobalObject* lexicalGlobalObject = toJS(originContext);
+ VM& vm = lexicalGlobalObject->vm();
+ JSLockHolder locker(vm);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ JSValue value = toJS(lexicalGlobalObject, apiValue);
+ auto serializedValue = SerializedScriptValue::create(*lexicalGlobalObject, value);
+ if (UNLIKELY(scope.exception())) {
+ if (exception)
+ *exception = toRef(lexicalGlobalObject, scope.exception()->value());
+ scope.clearException();
+ return nullptr;
+ }
+ ASSERT(serializedValue);
+ return serializedValue;
+}
+
+String SerializedScriptValue::toString() const
+{
+ return CloneDeserializer::deserializeString(m_data);
+}
+
+JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, SerializationErrorMode throwExceptions)
+{
+ return deserialize(lexicalGlobalObject, globalObject, {}, throwExceptions);
+}
+
+JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, SerializationErrorMode throwExceptions)
+{
+ Vector<String> dummyBlobs;
+ Vector<String> dummyPaths;
+ return deserialize(lexicalGlobalObject, globalObject, messagePorts, dummyBlobs, dummyPaths, throwExceptions);
+}
+
+JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions)
+{
+ DeserializationResult result = CloneDeserializer::deserialize(&lexicalGlobalObject, globalObject, messagePorts, WTFMove(m_backingStores)
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ WTFMove(m_detachedOffscreenCanvases)
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ WTFMove(m_detachedRTCDataChannels)
+#endif
+ ,
+ m_arrayBufferContentsArray.get(), m_data, blobURLs, blobFilePaths, m_sharedBufferContentsArray.get()
+#if ENABLE(WEBASSEMBLY)
+ ,
+ m_wasmModulesArray.get(), m_wasmMemoryHandlesArray.get()
+#endif
+ );
+ if (throwExceptions == SerializationErrorMode::Throwing)
+ maybeThrowExceptionIfSerializationFailed(lexicalGlobalObject, result.second);
+ return result.first ? result.first : jsNull();
+}
+
+JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception)
+{
+ JSGlobalObject* lexicalGlobalObject = toJS(destinationContext);
+ VM& vm = lexicalGlobalObject->vm();
+ JSLockHolder locker(vm);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ JSValue value = deserialize(*lexicalGlobalObject, lexicalGlobalObject);
+ if (UNLIKELY(scope.exception())) {
+ if (exception)
+ *exception = toRef(lexicalGlobalObject, scope.exception()->value());
+ scope.clearException();
+ return nullptr;
+ }
+ ASSERT(value);
+ return toRef(lexicalGlobalObject, value);
+}
+
+Ref<SerializedScriptValue> SerializedScriptValue::nullValue()
+{
+ return adoptRef(*new SerializedScriptValue(Vector<uint8_t>()));
+}
+
+uint32_t SerializedScriptValue::wireFormatVersion()
+{
+ return CurrentVersion;
+}
+
+// Vector<String> SerializedScriptValue::blobURLs() const
+// {
+// return m_blobHandles.map([](auto& handle) {
+// return handle.url().string();
+// });
+// }
+
+// void SerializedScriptValue::writeBlobsToDiskForIndexedDB(CompletionHandler<void(IDBValue&&)>&& completionHandler)
+// {
+// ASSERT(isMainThread());
+// ASSERT(hasBlobURLs());
+
+// blobRegistry().writeBlobsToTemporaryFilesForIndexedDB(blobURLs(), [completionHandler = WTFMove(completionHandler), this, protectedThis = Ref { *this }](auto&& blobFilePaths) mutable {
+// ASSERT(isMainThread());
+
+// if (blobFilePaths.isEmpty()) {
+// // We should have successfully written blobs to temporary files.
+// // If we failed, then we can't successfully store this record.
+// completionHandler({});
+// return;
+// }
+
+// ASSERT(m_blobHandles.size() == blobFilePaths.size());
+
+// completionHandler({ *this, blobURLs(), blobFilePaths });
+// });
+// }
+
+// IDBValue SerializedScriptValue::writeBlobsToDiskForIndexedDBSynchronously()
+// {
+// ASSERT(!isMainThread());
+
+// BinarySemaphore semaphore;
+// IDBValue value;
+// callOnMainThread([this, &semaphore, &value] {
+// writeBlobsToDiskForIndexedDB([&semaphore, &value](IDBValue&& result) {
+// ASSERT(isMainThread());
+// value.setAsIsolatedCopy(result);
+
+// semaphore.signal();
+// });
+// });
+// semaphore.wait();
+
+// return value;
+// }
+
+} // namespace WebCore