aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-07-14 19:37:22 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-14 19:37:22 -0700
commitc39c11e1011b682c2c4e48594c7d6110cfc3343c (patch)
tree8808ca4cd6684949423f28f3cd4d90aedb605cb7 /src/bun.js
parent25512104265aa568ca98e5bd89a977203ee261a6 (diff)
downloadbun-c39c11e1011b682c2c4e48594c7d6110cfc3343c.tar.gz
bun-c39c11e1011b682c2c4e48594c7d6110cfc3343c.tar.zst
bun-c39c11e1011b682c2c4e48594c7d6110cfc3343c.zip
structured clone (#3637)
* copy `SerializedScriptValue` * format * make `SerializedScriptValue` compile * add `transfer` option * tests * serialize/deserialize blobs * tests for blobs * serialize/deserialize file blobs * more tests * small cleanup * format * small changes + serialize offset * slice helper * map and set test
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/bindings/ProcessIdentifier.cpp54
-rw-r--r--src/bun.js/bindings/ProcessIdentifier.h41
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.cpp7
-rw-r--r--src/bun.js/bindings/ScriptExecutionContext.h15
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp46
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.h24
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp67
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h3
-rw-r--r--src/bun.js/bindings/generated_classes.zig10
-rw-r--r--src/bun.js/bindings/webcore/SerializedScriptValue.cpp5456
-rw-r--r--src/bun.js/bindings/webcore/SerializedScriptValue.h330
-rw-r--r--src/bun.js/node/types.zig2
-rw-r--r--src/bun.js/scripts/class-definitions.ts3
-rw-r--r--src/bun.js/scripts/generate-classes.ts138
-rw-r--r--src/bun.js/test/expect.zig3
-rw-r--r--src/bun.js/webcore/blob.zig220
-rw-r--r--src/bun.js/webcore/response.classes.ts1
17 files changed, 6411 insertions, 9 deletions
diff --git a/src/bun.js/bindings/ProcessIdentifier.cpp b/src/bun.js/bindings/ProcessIdentifier.cpp
new file mode 100644
index 000000000..5e82f4fe9
--- /dev/null
+++ b/src/bun.js/bindings/ProcessIdentifier.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``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 ITS 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 "ProcessIdentifier.h"
+
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+namespace ProcessIdent {
+
+static std::optional<ProcessIdentifier> globalIdentifier;
+
+void setIdentifier(ProcessIdentifier processIdentifier)
+{
+ ASSERT(isUIThread());
+ globalIdentifier = processIdentifier;
+}
+
+ProcessIdentifier identifier()
+{
+ static std::once_flag onceFlag;
+ std::call_once(onceFlag, [] {
+ if (!globalIdentifier)
+ globalIdentifier = ProcessIdentifier::generate();
+ });
+
+ return *globalIdentifier;
+}
+
+} // namespace ProcessIdent
+} // namespace WebCore
diff --git a/src/bun.js/bindings/ProcessIdentifier.h b/src/bun.js/bindings/ProcessIdentifier.h
new file mode 100644
index 000000000..e6141bdc0
--- /dev/null
+++ b/src/bun.js/bindings/ProcessIdentifier.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``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 ITS 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.
+ */
+
+#pragma once
+
+#include <wtf/ObjectIdentifier.h>
+
+namespace WebCore {
+
+enum ProcessIdentifierType {};
+using ProcessIdentifier = ObjectIdentifier<ProcessIdentifierType>;
+
+namespace ProcessIdent {
+
+WEBCORE_EXPORT void setIdentifier(ProcessIdentifier);
+WEBCORE_EXPORT ProcessIdentifier identifier();
+
+} // namespace ProcessIdent
+} // namespace WebCore
diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp
index 3262bdb5d..0293ecc35 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.cpp
+++ b/src/bun.js/bindings/ScriptExecutionContext.cpp
@@ -137,4 +137,11 @@ void ScriptExecutionContext::removeFromContextsMap()
// allScriptExecutionContextsMap().remove(m_identifier);
}
+ScriptExecutionContext* executionContext(JSC::JSGlobalObject* globalObject)
+{
+ if (!globalObject || !globalObject->inherits<JSDOMGlobalObject>())
+ return nullptr;
+ return JSC::jsCast<JSDOMGlobalObject*>(globalObject)->scriptExecutionContext();
+}
+
}
diff --git a/src/bun.js/bindings/ScriptExecutionContext.h b/src/bun.js/bindings/ScriptExecutionContext.h
index aed7977a5..fcf65f477 100644
--- a/src/bun.js/bindings/ScriptExecutionContext.h
+++ b/src/bun.js/bindings/ScriptExecutionContext.h
@@ -115,6 +115,18 @@ public:
// {
// }
+#if ENABLE(WEB_CRYPTO)
+ // These two methods are used when CryptoKeys are serialized into IndexedDB. As a side effect, it is also
+ // used for things that utilize the same structure clone algorithm, for example, message passing between
+ // worker and document.
+
+ // For now these will return false. In the future, we will want to implement these similar to how WorkerGlobalScope.cpp does.
+ // virtual bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) = 0;
+ // virtual bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) = 0;
+ bool wrapCryptoKey(const Vector<uint8_t>& key, Vector<uint8_t>& wrappedKey) { return false; }
+ bool unwrapCryptoKey(const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key) { return false; }
+#endif
+
static bool postTaskTo(ScriptExecutionContextIdentifier identifier, Function<void(ScriptExecutionContext&)>&& task);
void regenerateIdentifier();
@@ -194,4 +206,7 @@ public:
}
}
};
+
+ScriptExecutionContext* executionContext(JSC::JSGlobalObject*);
+
} \ No newline at end of file
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index ce82dc1f1..c11766926 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -98,6 +98,11 @@ private:
extern "C" void* BlobClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsBlobConstructor);
+
+extern "C" void Blob__onStructuredCloneSerialize(void*, JSC::JSGlobalObject*, void*, void (*)(CloneSerializer*, const uint8_t*, uint32_t));
+
+extern "C" JSC::EncodedJSValue Blob__onStructuredCloneDeserialize(JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);
+
extern "C" void BlobClass__finalize(void*);
extern "C" EncodedJSValue BlobPrototype__getArrayBuffer(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -1250,6 +1255,7 @@ private:
extern "C" void* BuildMessageClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsBuildMessageConstructor);
+
extern "C" void BuildMessageClass__finalize(void*);
extern "C" EncodedJSValue BuildMessagePrototype__toPrimitive(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -1727,6 +1733,7 @@ private:
extern "C" void* CryptoHasherClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsCryptoHasherConstructor);
+
extern "C" void CryptoHasherClass__finalize(void*);
extern "C" JSC::EncodedJSValue CryptoHasherPrototype__getAlgorithm(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -2155,6 +2162,7 @@ private:
extern "C" void* DirentClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsDirentConstructor);
+
extern "C" void DirentClass__finalize(void*);
extern "C" EncodedJSValue DirentPrototype__isBlockDevice(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -2681,6 +2689,7 @@ private:
extern "C" void* ExpectClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsExpectConstructor);
+
extern "C" void ExpectClass__finalize(void*);
extern "C" JSC_DECLARE_HOST_FUNCTION(ExpectClass__call);
@@ -5783,6 +5792,7 @@ private:
extern "C" void* FileSystemRouterClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsFileSystemRouterConstructor);
+
extern "C" void FileSystemRouterClass__finalize(void*);
extern "C" EncodedJSValue FileSystemRouterPrototype__match(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -6191,6 +6201,7 @@ private:
extern "C" void* ListenerClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsListenerConstructor);
+
extern "C" void ListenerClass__finalize(void*);
extern "C" JSC::EncodedJSValue ListenerPrototype__getData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -6640,6 +6651,7 @@ private:
extern "C" void* MD4Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsMD4Constructor);
+
extern "C" void MD4Class__finalize(void*);
extern "C" JSC::EncodedJSValue MD4Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -6967,6 +6979,7 @@ private:
extern "C" void* MD5Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsMD5Constructor);
+
extern "C" void MD5Class__finalize(void*);
extern "C" JSC::EncodedJSValue MD5Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -7257,6 +7270,7 @@ private:
extern "C" void* MatchedRouteClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsMatchedRouteConstructor);
+
extern "C" void MatchedRouteClass__finalize(void*);
extern "C" JSC::EncodedJSValue MatchedRoutePrototype__getFilePath(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -7756,6 +7770,7 @@ private:
extern "C" void* NodeJSFSClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsNodeJSFSConstructor);
+
extern "C" void NodeJSFSClass__finalize(void*);
extern "C" EncodedJSValue NodeJSFSPrototype__access(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -10537,6 +10552,7 @@ private:
extern "C" void* RequestClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsRequestConstructor);
+
extern "C" void RequestClass__finalize(void*);
extern "C" EncodedJSValue RequestPrototype__getArrayBuffer(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -11309,6 +11325,7 @@ private:
extern "C" void* ResolveMessageClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsResolveMessageConstructor);
+
extern "C" void ResolveMessageClass__finalize(void*);
extern "C" EncodedJSValue ResolveMessagePrototype__toPrimitive(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -11898,6 +11915,7 @@ private:
extern "C" void* ResponseClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsResponseConstructor);
+
extern "C" void ResponseClass__finalize(void*);
extern "C" EncodedJSValue ResponsePrototype__getArrayBuffer(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -12599,6 +12617,7 @@ private:
extern "C" void* SHA1Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA1Constructor);
+
extern "C" void SHA1Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA1Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -12926,6 +12945,7 @@ private:
extern "C" void* SHA224Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA224Constructor);
+
extern "C" void SHA224Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA224Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -13253,6 +13273,7 @@ private:
extern "C" void* SHA256Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA256Constructor);
+
extern "C" void SHA256Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA256Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -13580,6 +13601,7 @@ private:
extern "C" void* SHA384Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA384Constructor);
+
extern "C" void SHA384Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA384Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -13907,6 +13929,7 @@ private:
extern "C" void* SHA512Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA512Constructor);
+
extern "C" void SHA512Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA512Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -14234,6 +14257,7 @@ private:
extern "C" void* SHA512_256Class__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSHA512_256Constructor);
+
extern "C" void SHA512_256Class__finalize(void*);
extern "C" JSC::EncodedJSValue SHA512_256Prototype__getByteLength(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -14561,6 +14585,7 @@ private:
extern "C" void* ServerWebSocketClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsServerWebSocketConstructor);
+
extern "C" void ServerWebSocketClass__finalize(void*);
extern "C" JSC::EncodedJSValue ServerWebSocketPrototype__getBinaryType(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -15504,6 +15529,7 @@ private:
extern "C" void* StatsClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsStatsConstructor);
+
extern "C" void StatsClass__finalize(void*);
extern "C" JSC::EncodedJSValue StatsPrototype__atime(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -16433,6 +16459,7 @@ private:
extern "C" void* SubprocessClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsSubprocessConstructor);
+
extern "C" void SubprocessClass__finalize(void*);
extern "C" JSC::EncodedJSValue SubprocessPrototype__getExitCode(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -16963,6 +16990,7 @@ private:
extern "C" void* TCPSocketClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsTCPSocketConstructor);
+
extern "C" void TCPSocketClass__finalize(void*);
extern "C" JSC::EncodedJSValue TCPSocketPrototype__getALPNProtocol(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -17648,6 +17676,7 @@ private:
extern "C" void* TLSSocketClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsTLSSocketConstructor);
+
extern "C" void TLSSocketClass__finalize(void*);
extern "C" JSC::EncodedJSValue TLSSocketPrototype__getALPNProtocol(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
@@ -18370,6 +18399,7 @@ private:
extern "C" void* TextDecoderClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsTextDecoderConstructor);
+
extern "C" void TextDecoderClass__finalize(void*);
extern "C" EncodedJSValue TextDecoderPrototype__decode(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -19081,6 +19111,7 @@ private:
extern "C" void* TranspilerClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
JSC_DECLARE_CUSTOM_GETTER(jsTranspilerConstructor);
+
extern "C" void TranspilerClass__finalize(void*);
extern "C" EncodedJSValue TranspilerPrototype__scan(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
@@ -19376,5 +19407,20 @@ extern "C" EncodedJSValue Transpiler__create(Zig::GlobalObject* globalObject, vo
return JSValue::encode(instance);
}
+std::optional<StructuredCloneableSerialize> StructuredCloneableSerialize::fromJS(JSC::JSValue value)
+{
+ if (auto* result = jsDynamicCast<JSBlob*>(value)) {
+ return StructuredCloneableSerialize { .cppWriteBytes = SerializedScriptValue::writeBytesForBun, .zigFunction = Blob__onStructuredCloneSerialize, .tag = 254, .impl = result->wrapped() };
+ }
+ return std::nullopt;
+}
+
+std::optional<JSC::EncodedJSValue> StructuredCloneableDeserialize::fromTagDeserialize(uint8_t tag, JSC::JSGlobalObject* globalObject, const uint8_t* ptr, const uint8_t* end)
+{
+ if (tag == 254) {
+ return Blob__onStructuredCloneDeserialize(globalObject, ptr, end);
+ }
+ return std::nullopt;
+}
} // namespace WebCore
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h
index 1631f960e..f66c65e40 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses.h
@@ -10,6 +10,7 @@ namespace Zig {
#include "JSDOMWrapper.h"
#include <wtf/NeverDestroyed.h>
+#include "SerializedScriptValue.h"
namespace WebCore {
using namespace Zig;
@@ -1998,4 +1999,27 @@ public:
void finishCreation(JSC::VM&);
};
+class StructuredCloneableSerialize {
+public:
+ void (*cppWriteBytes)(CloneSerializer*, const uint8_t*, uint32_t);
+
+ std::function<void(void*, JSC::JSGlobalObject*, void*, void (*)(CloneSerializer*, const uint8_t*, uint32_t))> zigFunction;
+
+ uint8_t tag;
+
+ // the type from zig
+ void* impl;
+
+ static std::optional<StructuredCloneableSerialize> fromJS(JSC::JSValue);
+ void write(CloneSerializer* serializer, JSC::JSGlobalObject* globalObject)
+ {
+ zigFunction(impl, globalObject, serializer, cppWriteBytes);
+ }
+};
+
+class StructuredCloneableDeserialize {
+public:
+ static std::optional<JSC::EncodedJSValue> fromTagDeserialize(uint8_t tag, JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);
+};
+
}
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 4585b6b84..e6663bfed 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -106,6 +106,8 @@
#include "BunJSCModule.h"
#include "ModuleLoader.h"
#include "NodeVMScript.h"
+#include "ProcessIdentifier.h"
+#include "SerializedScriptValue.h"
#include "ZigGeneratedClasses.h"
#include "JavaScriptCore/DateInstance.h"
@@ -660,6 +662,19 @@ extern "C" bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* g
#define PUT_WEBCORE_GENERATED_CONSTRUCTOR(name, ConstructorName) \
putDirectCustomAccessor(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, name)), JSC::CustomGetterSetter::create(vm, ConstructorName##_getter, ConstructorName##_setter), 0)
+String GlobalObject::defaultAgentClusterID()
+{
+ return makeString(ProcessIdent::identifier().toUInt64(), "-default"_s);
+}
+
+String GlobalObject::agentClusterID() const
+{
+ // TODO: workers
+ // if (is<SharedWorkerGlobalScope>(scriptExecutionContext()))
+ // return makeString(WProcess::identifier().toUInt64(), "-sharedworker");
+ return defaultAgentClusterID();
+}
+
namespace Zig {
using namespace WebCore;
@@ -1211,6 +1226,50 @@ JSC_DEFINE_HOST_FUNCTION(functionClearTimeout,
return Bun__Timer__clearTimeout(globalObject, JSC::JSValue::encode(num));
}
+JSC_DEFINE_HOST_FUNCTION(functionStructuredClone,
+ (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ JSC::VM& vm = globalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+
+ if (callFrame->argumentCount() == 0) {
+ throwTypeError(globalObject, throwScope, "structuredClone requires 1 argument"_s);
+ return JSValue::encode(jsUndefined());
+ }
+
+ JSC::JSValue value = callFrame->argument(0);
+ JSC::JSValue options = callFrame->argument(1);
+
+ Vector<JSC::Strong<JSC::JSObject>> transferList;
+
+ if (options.isObject()) {
+ JSC::JSObject* optionsObject = options.getObject();
+ JSC::JSValue transferListValue = optionsObject->get(globalObject, vm.propertyNames->transfer);
+ if (transferListValue.isObject()) {
+ JSC::JSObject* transferListObject = transferListValue.getObject();
+ if (auto* transferListArray = jsDynamicCast<JSC::JSArray*>(transferListObject)) {
+ for (unsigned i = 0; i < transferListArray->length(); i++) {
+ JSC::JSValue transferListValue = transferListArray->get(globalObject, i);
+ if (transferListValue.isObject()) {
+ JSC::JSObject* transferListObject = transferListValue.getObject();
+ transferList.append(JSC::Strong<JSC::JSObject>(vm, transferListObject));
+ }
+ }
+ }
+ }
+ }
+
+ ExceptionOr<Ref<SerializedScriptValue>> serialized = SerializedScriptValue::create(*globalObject, value, WTFMove(transferList));
+ if (serialized.hasException()) {
+ WebCore::propagateException(*globalObject, throwScope, serialized.releaseException());
+ return JSValue::encode(jsUndefined());
+ }
+
+ JSValue deserialized = serialized.releaseReturnValue()->deserialize(*globalObject, globalObject);
+
+ return JSValue::encode(deserialized);
+}
+
JSC_DEFINE_HOST_FUNCTION(functionBTOA,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -3652,7 +3711,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
auto& builtinNames = WebCore::builtinNames(vm);
WTF::Vector<GlobalPropertyInfo> extraStaticGlobals;
- extraStaticGlobals.reserveCapacity(44);
+ extraStaticGlobals.reserveCapacity(45);
JSC::Identifier queueMicrotaskIdentifier = JSC::Identifier::fromString(vm, "queueMicrotask"_s);
extraStaticGlobals.uncheckedAppend(
@@ -3676,6 +3735,12 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
"clearImmediate"_s, functionClearTimeout, ImplementationVisibility::Public),
JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0 });
+ extraStaticGlobals.uncheckedAppend(
+ GlobalPropertyInfo { JSC::Identifier::fromString(vm, "structuredClone"_s),
+ JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 2,
+ "structuredClone"_s, functionStructuredClone, ImplementationVisibility::Public),
+ JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0 });
+
JSC::Identifier setTimeoutIdentifier = JSC::Identifier::fromString(vm, "setTimeout"_s);
extraStaticGlobals.uncheckedAppend(
GlobalPropertyInfo { setTimeoutIdentifier,
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index 0b5c882f5..081ca33b3 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -373,6 +373,9 @@ public:
JSObject* navigatorObject();
JSFunction* nativeMicrotaskTrampoline() { return m_nativeMicrotaskTrampoline.getInitializedOnMainThread(this); }
+ String agentClusterID() const;
+ static String defaultAgentClusterID();
+
void trackFFIFunction(JSC::JSFunction* function)
{
this->m_ffiFunctions.append(JSC::Strong<JSC::JSFunction> { vm(), function });
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 5f83630c5..10aec1865 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -98,6 +98,14 @@ pub const JSBlob = struct {
extern fn Blob__dangerouslySetPtr(JSC.JSValue, ?*Blob) bool;
comptime {
+ if (@TypeOf(Blob.onStructuredCloneSerialize) != (fn (*Blob, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, writeBytes: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void) callconv(.C) void)) {
+ @compileLog("Blob.onStructuredCloneSerialize is not a structured clone serialize function");
+ }
+
+ if (@TypeOf(Blob.onStructuredCloneDeserialize) != (fn (globalThis: *JSC.JSGlobalObject, ptr: [*]u8, end: [*]u8) callconv(.C) JSC.JSValue)) {
+ @compileLog("Blob.onStructuredCloneDeserialize is not a structured clone deserialize function");
+ }
+
if (@TypeOf(Blob.constructor) != (fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*Blob)) {
@compileLog("Blob.constructor is not a constructor");
}
@@ -149,6 +157,8 @@ pub const JSBlob = struct {
@export(Blob.getText, .{ .name = "BlobPrototype__getText" });
@export(Blob.getType, .{ .name = "BlobPrototype__getType" });
@export(Blob.getWriter, .{ .name = "BlobPrototype__getWriter" });
+ @export(Blob.onStructuredCloneDeserialize, .{ .name = "Blob__onStructuredCloneDeserialize" });
+ @export(Blob.onStructuredCloneSerialize, .{ .name = "Blob__onStructuredCloneSerialize" });
}
}
};
diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.cpp b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp
new file mode 100644
index 000000000..e3a741a02
--- /dev/null
+++ b/src/bun.js/bindings/webcore/SerializedScriptValue.cpp
@@ -0,0 +1,5456 @@
+/*
+ * Copyright (C) 2009-2023 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 "ByteArrayPixelBuffer.h"
+#include "CryptoKeyAES.h"
+#include "CryptoKeyEC.h"
+#include "CryptoKeyHMAC.h"
+#include "CryptoKeyOKP.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 "JSWebCodecsEncodedVideoChunk.h"
+// #include "JSWebCodecsVideoFrame.h"
+#include "ScriptExecutionContext.h"
+#include "SharedBuffer.h"
+// #include "WebCodecsEncodedVideoChunk.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/NumberObject.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>
+
+#include "blob.h"
+#include "ZigGeneratedClasses.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 constexpr unsigned maximumFilterRecursion = 40000;
+static constexpr uint64_t autoLengthMarker = UINT64_MAX;
+
+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,
+#if ENABLE(WEB_CODECS)
+ WebCodecsEncodedVideoChunkTag = 52,
+ WebCodecsVideoFrameTag = 53,
+#endif
+ ResizableArrayBufferTag = 54,
+
+ Bun__BlobTag = 254,
+ // bun types start at 254 and decrease with each addition
+
+ 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(WEBASSEMBLY)
+static String agentClusterIDFromGlobalObject(JSGlobalObject& globalObject)
+{
+ if (!globalObject.inherits<JSDOMGlobalObject>())
+ return JSDOMGlobalObject::defaultAgentClusterID();
+ return jsCast<JSDOMGlobalObject*>(&globalObject)->agentClusterID();
+}
+#endif
+
+#if ENABLE(WEB_CRYPTO)
+
+const uint32_t currentKeyFormatVersion = 1;
+
+enum class CryptoKeyClassSubtag {
+ HMAC = 0,
+ AES = 1,
+ RSA = 2,
+ EC = 3,
+ Raw = 4,
+ OKP = 5,
+};
+const uint8_t cryptoKeyClassSubtagMaximumValue = 5;
+
+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,
+ ED25519 = 22,
+};
+
+const uint8_t cryptoAlgorithmIdentifierTagMaximumValue = 22;
+
+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;
+}
+
+enum class CryptoKeyOKPOpNameTag {
+ X25519 = 0,
+ ED25519 = 1,
+};
+const uint8_t cryptoKeyOKPOpNameTagMaximumValue = 1;
+
+#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.
+ * Version 11. added support for Blob's memory cost.
+ * Version 12. added support for agent cluster ID.
+ */
+static const unsigned CurrentVersion = 12;
+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 <identifier:uint32_t>
+ * | DOMExceptionTag <message:String> <name:String>
+ * | WebCodecsEncodedVideoChunkTag <identifier:uint32_t>
+ *
+ * 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><memoryCost:long long>
+ *
+ * RegExp :-
+ * RegExpTag <pattern:StringData><flags:StringData>
+ *
+ * ObjectReference :-
+ * ObjectReferenceTag <opIndex:IndexType>
+ *
+ * ArrayBuffer :-
+ * ArrayBufferTag <byteLength:uint64_t> <contents:byte{length}>
+ * ResizableArrayBufferTag <byteLength:uint64_t> <maxLength: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 {
+ WTF_FORBID_HEAP_ALLOCATION;
+
+protected:
+ CloneBase(JSGlobalObject* lexicalGlobalObject)
+ : m_lexicalGlobalObject(lexicalGlobalObject)
+ , m_failed(false)
+ {
+ }
+
+ void fail()
+ {
+ m_failed = true;
+ }
+
+ JSGlobalObject* const 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 {
+ WTF_FORBID_HEAP_ALLOCATION;
+
+public:
+ Vector<uint8_t>& m_buffer;
+
+ void write(const uint8_t* data, unsigned length)
+ {
+ writeLittleEndian(m_buffer, data, length);
+ }
+ // 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(WEB_CODECS)
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>& serializedVideoChunks,
+ // Vector<RefPtr<WebCodecsVideoFrame>>& serializedVideoFrames,
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // WasmModuleArray& wasmModules,
+ // WasmMemoryHandleArray& wasmMemoryHandles,
+ // #endif
+ // Vector<URLKeepingBlobAlive>& blobHandles, Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers,
+ // SerializationForStorage forStorage)
+ // {
+ // CloneSerializer serializer(lexicalGlobalObject, messagePorts, arrayBuffers, imageBitmaps,
+ // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ // offscreenCanvases,
+ // #endif
+ // #if ENABLE(WEB_RTC)
+ // rtcDataChannels,
+ // #endif
+ // #if ENABLE(WEB_CODECS)
+ // serializedVideoChunks,
+ // serializedVideoFrames,
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // wasmModules,
+ // wasmMemoryHandles,
+ // #endif
+ // blobHandles, out, context, sharedBuffers, forStorage);
+ // return serializer.serialize(value);
+ // }
+
+ static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ const Vector<Ref<RTCDataChannel>>& rtcDataChannels,
+#endif
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>& serializedVideoChunks,
+ Vector<RefPtr<WebCodecsVideoFrame>>& serializedVideoFrames,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray& wasmModules,
+ WasmMemoryHandleArray& wasmMemoryHandles,
+#endif
+ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers,
+ SerializationForStorage forStorage)
+ {
+ CloneSerializer serializer(lexicalGlobalObject, arrayBuffers,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ rtcDataChannels,
+#endif
+#if ENABLE(WEB_CODECS)
+ serializedVideoChunks,
+ serializedVideoFrames,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ wasmModules,
+ wasmMemoryHandles,
+#endif
+ out, context, sharedBuffers, forStorage);
+ 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(WEB_CODECS)
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>& serializedVideoChunks,
+ // Vector<RefPtr<WebCodecsVideoFrame>>& serializedVideoFrames,
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // WasmModuleArray& wasmModules,
+ // WasmMemoryHandleArray& wasmMemoryHandles,
+ // #endif
+ // Vector<URLKeepingBlobAlive>& blobHandles, Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers, SerializationForStorage forStorage)
+ // : 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
+ // #if ENABLE(WEB_CODECS)
+ // , m_serializedVideoChunks(serializedVideoChunks)
+ // , m_serializedVideoFrames(serializedVideoFrames)
+ // #endif
+ // , m_forStorage(forStorage)
+ // {
+ // 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
+ // }
+
+ CloneSerializer(JSGlobalObject* lexicalGlobalObject, Vector<RefPtr<JSC::ArrayBuffer>>& arrayBuffers,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ const Vector<RefPtr<OffscreenCanvas>>& offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ const Vector<Ref<RTCDataChannel>>& rtcDataChannels,
+#endif
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>& serializedVideoChunks,
+ Vector<RefPtr<WebCodecsVideoFrame>>& serializedVideoFrames,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray& wasmModules,
+ WasmMemoryHandleArray& wasmMemoryHandles,
+#endif
+ Vector<uint8_t>& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers, SerializationForStorage forStorage)
+ : CloneBase(lexicalGlobalObject)
+ , m_buffer(out)
+ , m_emptyIdentifier(Identifier::fromString(lexicalGlobalObject->vm(), emptyString()))
+ , m_context(context)
+ , m_sharedBuffers(sharedBuffers)
+#if ENABLE(WEBASSEMBLY)
+ , m_wasmModules(wasmModules)
+ , m_wasmMemoryHandles(wasmMemoryHandles)
+#endif
+#if ENABLE(WEB_CODECS)
+ , m_serializedVideoChunks(serializedVideoChunks)
+ , m_serializedVideoFrames(serializedVideoFrames)
+#endif
+ , m_forStorage(forStorage)
+ {
+ write(CurrentVersion);
+ fillTransferMap(arrayBuffers, m_transferredArrayBuffers);
+#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(JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSArray>();
+ }
+
+ bool isMap(JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSMap>();
+ }
+ bool isSet(JSValue value)
+ {
+ if (!value.isObject())
+ return false;
+ JSObject* object = asObject(value);
+ return object->inherits<JSSet>();
+ }
+
+ 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(JSObject* object, const Identifier& propertyName)
+ {
+ PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
+ if (object->methodTable()->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>())
+ 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>())
+ write(DataViewTag);
+ else if (obj->inherits<JSUint8ClampedArray>())
+ write(Uint8ClampedArrayTag);
+ else if (obj->inherits<JSInt8Array>())
+ write(Int8ArrayTag);
+ else if (obj->inherits<JSUint8Array>())
+ write(Uint8ArrayTag);
+ else if (obj->inherits<JSInt16Array>())
+ write(Int16ArrayTag);
+ else if (obj->inherits<JSUint16Array>())
+ write(Uint16ArrayTag);
+ else if (obj->inherits<JSInt32Array>())
+ write(Int32ArrayTag);
+ else if (obj->inherits<JSUint32Array>())
+ write(Uint32ArrayTag);
+ else if (obj->inherits<JSFloat32Array>())
+ write(Float32ArrayTag);
+ else if (obj->inherits<JSFloat64Array>())
+ write(Float64ArrayTag);
+ else if (obj->inherits<JSBigInt64Array>())
+ write(BigInt64ArrayTag);
+ else if (obj->inherits<JSBigUint64Array>())
+ write(BigUint64ArrayTag);
+ else
+ return false;
+
+ if (UNLIKELY(jsCast<JSArrayBufferView*>(obj)->isOutOfBounds())) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+
+ RefPtr<ArrayBufferView> arrayBufferView = toPossiblySharedArrayBufferView(vm, obj);
+ if (arrayBufferView->isResizableOrGrowableShared()) {
+ uint64_t byteOffset = arrayBufferView->byteOffsetRaw();
+ write(byteOffset);
+ uint64_t byteLength = arrayBufferView->byteLengthRaw();
+ if (arrayBufferView->isAutoLength())
+ byteLength = autoLengthMarker;
+ write(byteLength);
+ } else {
+ 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)
+ // {
+ // if (obj->inherits<JSDOMPoint>())
+ // write(DOMPointTag);
+ // else
+ // write(DOMPointReadOnlyTag);
+
+ // dumpDOMPoint(jsCast<JSDOMPointReadOnly*>(obj)->wrapped());
+ // }
+
+ // void dumpDOMRect(JSObject* obj)
+ // {
+ // if (obj->inherits<JSDOMRect>())
+ // 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)
+ // {
+ // if (obj->inherits<JSDOMMatrix>())
+ // 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();
+ // if (!imageBitmap.originClean()) {
+ // code = SerializationReturnCode::DataCloneError;
+ // return;
+ // }
+
+ // 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 (!is<ByteArrayPixelBuffer>(pixelBuffer)) {
+ // code = SerializationReturnCode::ValidationError;
+ // return;
+ // }
+
+ // auto arrayBuffer = downcast<ByteArrayPixelBuffer>(*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
+#if ENABLE(WEB_CODECS)
+ void dumpWebCodecsEncodedVideoChunk(JSObject* obj)
+ {
+ auto& videoChunk = jsCast<JSWebCodecsEncodedVideoChunk*>(obj)->wrapped();
+
+ auto index = m_serializedVideoChunks.find(&videoChunk.storage());
+ if (index == notFound) {
+ index = m_serializedVideoChunks.size();
+ m_serializedVideoChunks.append(&videoChunk.storage());
+ }
+
+ write(WebCodecsEncodedVideoChunkTag);
+ write(static_cast<uint32_t>(index));
+ }
+
+ bool dumpWebCodecsVideoFrame(JSObject* obj)
+ {
+ Ref videoFrame = jsCast<JSWebCodecsVideoFrame*>(obj)->wrapped();
+ if (videoFrame->isDetached())
+ return false;
+
+ auto index = m_serializedVideoFrames.find(videoFrame.ptr());
+ if (index == notFound) {
+ index = m_serializedVideoChunks.size();
+ m_serializedVideoFrames.append(WTFMove(videoFrame));
+ }
+ write(WebCodecsVideoFrameTag);
+ write(static_cast<uint32_t>(index));
+ return true;
+ }
+#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(value))
+ return false;
+
+ if (value.isObject()) {
+ auto* obj = asObject(value);
+ if (auto* dateObject = jsDynamicCast<DateInstance*>(obj)) {
+ write(DateTag);
+ write(dateObject->internalNumber());
+ return true;
+ }
+ if (auto* booleanObject = jsDynamicCast<BooleanObject*>(obj)) {
+ if (!startObjectInternal(booleanObject)) // handle duplicates
+ return true;
+ write(booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag);
+ return true;
+ }
+ if (auto* stringObject = jsDynamicCast<StringObject*>(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*>(obj)) {
+ if (!startObjectInternal(numberObject)) // handle duplicates
+ return true;
+ write(NumberObjectTag);
+ write(numberObject->internalValue().asNumber());
+ return true;
+ }
+ if (auto* bigIntObject = jsDynamicCast<BigIntObject*>(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;
+ // }
+
+ // write bun types
+ if (auto _cloneable = StructuredCloneableSerialize::fromJS(value)) {
+ StructuredCloneableSerialize cloneable = WTFMove(_cloneable.value());
+ write(cloneable.tag);
+ cloneable.write(this, m_lexicalGlobalObject);
+ return true;
+ }
+
+ // if (auto* blob = JSBlob::toWrapped(vm, obj)) {
+ // write(BlobTag);
+ // m_blobHandles.append(blob->handle().isolatedCopy());
+ // write(blob->url().string());
+ // write(blob->type());
+ // static_assert(sizeof(uint64_t) == sizeof(decltype(blob->size())));
+ // uint64_t size = blob->size();
+ // write(size);
+ // uint64_t memoryCost = blob->memoryCost();
+ // write(memoryCost);
+ // 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*>(obj)) {
+ write(RegExpTag);
+ write(regExp->regExp()->pattern());
+ write(String::fromLatin1(JSC::Yarr::flagsString(regExp->regExp()->flags()).data()));
+ return true;
+ }
+ // if (obj->inherits<JSMessagePort>()) {
+ // 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) {
+ // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
+ if (!JSC::Options::useSharedArrayBuffer()) {
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
+ uint32_t index = m_sharedBuffers.size();
+ ArrayBufferContents contents;
+ if (arrayBuffer->shareWith(contents)) {
+ write(SharedArrayBufferTag);
+ m_sharedBuffers.append(WTFMove(contents));
+ write(index);
+ return true;
+ }
+ }
+
+ if (arrayBuffer->isResizableOrGrowableShared()) {
+ write(ResizableArrayBufferTag);
+ uint64_t byteLength = arrayBuffer->byteLength();
+ write(byteLength);
+ uint64_t maxByteLength = arrayBuffer->maxByteLength().value_or(0);
+ write(maxByteLength);
+ write(static_cast<const uint8_t*>(arrayBuffer->data()), byteLength);
+ 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>()) {
+ 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<URLKeepingBlobAlive> dummyBlobHandles;
+ // Vector<RefPtr<MessagePort>> dummyMessagePorts;
+ Vector<RefPtr<JSC::ArrayBuffer>> dummyArrayBuffers;
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> dummyVideoChunks;
+ Vector<RefPtr<WebCodecsVideoFrame>> dummyVideoFrames;
+#endif
+#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(WEB_CODECS)
+ // dummyVideoChunks,
+ // dummyVideoFrames,
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // dummyModules,
+ // dummyMemoryHandles,
+ // #endif
+ // dummyBlobHandles, serializedKey, SerializationContext::Default, dummySharedBuffers, m_forStorage);
+ CloneSerializer rawKeySerializer(m_lexicalGlobalObject, dummyArrayBuffers,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ {},
+#endif
+#if ENABLE(WEB_RTC)
+ {},
+#endif
+#if ENABLE(WEB_CODECS)
+ dummyVideoChunks,
+ dummyVideoFrames,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ dummyModules,
+ dummyMemoryHandles,
+#endif
+ serializedKey, SerializationContext::Default, dummySharedBuffers, m_forStorage);
+ 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*>(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(agentClusterIDFromGlobalObject(*m_lexicalGlobalObject));
+ write(index);
+ return true;
+ }
+ if (JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(obj)) {
+ if (!JSC::Options::useSharedArrayBuffer() || memory->memory().sharingMode() != JSC::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().shared());
+ write(WasmMemoryTag);
+ write(agentClusterIDFromGlobalObject(*m_lexicalGlobalObject));
+ write(index);
+ return true;
+ }
+#endif
+ // if (obj->inherits<JSDOMPointReadOnly>()) {
+ // dumpDOMPoint(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMRectReadOnly>()) {
+ // dumpDOMRect(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMMatrixReadOnly>()) {
+ // dumpDOMMatrix(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSDOMQuad>()) {
+ // dumpDOMQuad(obj);
+ // return true;
+ // }
+ // if (obj->inherits<JSImageBitmap>()) {
+ // dumpImageBitmap(obj, code);
+ // return true;
+ // }
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ if (obj->inherits<JSOffscreenCanvas>()) {
+ dumpOffscreenCanvas(obj, code);
+ return true;
+ }
+#endif
+#if ENABLE(WEB_RTC)
+ if (obj->inherits<JSRTCDataChannel>()) {
+ dumpRTCDataChannel(obj, code);
+ return true;
+ }
+#endif
+ if (obj->inherits<JSDOMException>()) {
+ dumpDOMException(obj, code);
+ return true;
+ }
+#if ENABLE(WEB_CODECS)
+ if (obj->inherits<JSWebCodecsEncodedVideoChunk>()) {
+ if (m_forStorage == SerializationForStorage::Yes)
+ return false;
+ dumpWebCodecsEncodedVideoChunk(obj);
+ return true;
+ }
+ if (obj->inherits<JSWebCodecsVideoFrame>()) {
+ if (m_forStorage == SerializationForStorage::Yes)
+ return false;
+ return dumpWebCodecsVideoFrame(obj);
+ }
+#endif
+
+ 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));
+ }
+
+ void write(CryptoKeyOKPOpNameTag 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().isolatedCopy());
+ // 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(CryptoKeyOKP::NamedCurve curve)
+ {
+ switch (curve) {
+ case CryptoKeyOKP::NamedCurve::X25519:
+ write(CryptoKeyOKPOpNameTag::X25519);
+ break;
+ case CryptoKeyOKP::NamedCurve::Ed25519:
+ write(CryptoKeyOKPOpNameTag::ED25519);
+ break;
+ }
+ }
+
+ 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;
+ case CryptoAlgorithmIdentifier::Ed25519:
+ write(CryptoAlgorithmIdentifierTag::ED25519);
+ 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;
+ }
+ case CryptoKeyClass::OKP:
+ write(CryptoKeyClassSubtag::OKP);
+ write(key->algorithmIdentifier());
+ write(downcast<CryptoKeyOKP>(*key).namedCurve());
+ write(downcast<CryptoKeyOKP>(*key).platformKey());
+ break;
+ }
+ }
+#endif
+ // Vector<URLKeepingBlobAlive>& 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
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>& m_serializedVideoChunks;
+ Vector<RefPtr<WebCodecsVideoFrame>>& m_serializedVideoFrames;
+#endif
+ SerializationForStorage m_forStorage;
+};
+
+void SerializedScriptValue::writeBytesForBun(CloneSerializer* ctx, const uint8_t* data, uint32_t size)
+{
+ ctx->write(data, size);
+}
+
+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 state = StateUnknown;
+ JSValue inValue = in;
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ while (1) {
+ switch (state) {
+ arrayStartState:
+ case ArrayStartState: {
+ ASSERT(isArray(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() != JSFinalObject::info())
+ return SerializationReturnCode::DataCloneError;
+ inputObjectStack.append(inObject);
+ indexStack.append(0);
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ inObject->methodTable()->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(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*>(object));
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ object->methodTable()->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*>(object));
+ propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
+ object->methodTable()->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(inValue))
+ goto arrayStartState;
+ if (isMap(inValue))
+ goto mapStartState;
+ if (isSet(inValue))
+ goto setStartState;
+ goto objectStartState;
+ }
+ }
+ if (stateStack.isEmpty())
+ break;
+
+ state = stateStack.last();
+ stateStack.removeLast();
+ }
+ if (m_failed)
+ return SerializationReturnCode::UnspecifiedError;
+
+ return SerializationReturnCode::SuccessfullyCompleted;
+}
+
+class CloneDeserializer : CloneBase {
+ WTF_FORBID_HEAP_ALLOCATION;
+
+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 ENABLE(WEB_CODECS)
+ // ,
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames
+ // #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 ENABLE(WEB_CODECS)
+ // ,
+ // WTFMove(serializedVideoChunks), WTFMove(serializedVideoFrames)
+ // #endif
+ // );
+ // if (!deserializer.isValid())
+ // return std::make_pair(JSValue(), SerializationReturnCode::ValidationError);
+ // return deserializer.deserialize();
+ // }
+
+ static DeserializationResult deserialize(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject
+#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 ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames
+#endif
+ )
+ {
+ if (!buffer.size())
+ return std::make_pair(jsNull(), SerializationReturnCode::UnspecifiedError);
+ CloneDeserializer deserializer(lexicalGlobalObject, globalObject, arrayBufferContentsArray, buffer, blobURLs, blobFilePaths, sharedBuffers
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ WTFMove(detachedOffscreenCanvases)
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ WTFMove(detachedRTCDataChannels)
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ wasmModules, wasmMemoryHandles
+#endif
+#if ENABLE(WEB_CODECS)
+ ,
+ WTFMove(serializedVideoChunks), WTFMove(serializedVideoFrames)
+#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
+ // #if ENABLE(WEB_CODECS)
+ // ,
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks = {}, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames = {}
+ // #endif
+ // )
+ // : CloneBase(lexicalGlobalObject)
+ // , m_globalObject(globalObject)
+ // , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>())
+ // , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>())
+ // , 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 ENABLE(WEB_CODECS)
+ // , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ // , m_videoChunks(m_serializedVideoChunks.size())
+ // , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+ // , m_videoFrames(m_serializedVideoFrames.size())
+ // #endif
+ // {
+ // if (!read(m_version))
+ // m_version = 0xFFFFFFFF;
+ // }
+
+ CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, ArrayBufferContentsArray* arrayBufferContents, 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
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks = {}, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames = {}
+#endif
+ )
+ : CloneBase(lexicalGlobalObject)
+ , m_globalObject(globalObject)
+ , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>())
+ , m_canCreateDOMObject(m_isDOMGlobalObject)
+ , m_ptr(buffer.data())
+ , m_end(buffer.data() + buffer.size())
+ , m_version(0xFFFFFFFF)
+ , m_arrayBufferContents(arrayBufferContents)
+ , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
+#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 ENABLE(WEB_CODECS)
+ , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ , m_videoChunks(m_serializedVideoChunks.size())
+ , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+ , m_videoFrames(m_serializedVideoFrames.size())
+#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
+ // #if ENABLE(WEB_CODECS)
+ // ,
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks = {}, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames = {}
+ // #endif
+ // )
+ // : CloneBase(lexicalGlobalObject)
+ // , m_globalObject(globalObject)
+ // , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>())
+ // , m_canCreateDOMObject(m_isDOMGlobalObject && !globalObject->inherits<JSIDBSerializationGlobalObject>())
+ // , 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 ENABLE(WEB_CODECS)
+ // , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ // , m_videoChunks(m_serializedVideoChunks.size())
+ // , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+ // , m_videoFrames(m_serializedVideoFrames.size())
+ // #endif
+ // {
+ // if (!read(m_version))
+ // m_version = 0xFFFFFFFF;
+ // }
+
+ CloneDeserializer(JSGlobalObject* lexicalGlobalObject, JSGlobalObject* globalObject, ArrayBufferContentsArray* arrayBufferContents, const Vector<uint8_t>& buffer, const Vector<String>& blobURLs, const Vector<String> blobFilePaths, ArrayBufferContentsArray* sharedBuffers
+#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
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks = {}, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames = {}
+#endif
+ )
+ : CloneBase(lexicalGlobalObject)
+ , m_globalObject(globalObject)
+ , m_isDOMGlobalObject(globalObject->inherits<JSDOMGlobalObject>())
+ , m_canCreateDOMObject(m_isDOMGlobalObject)
+ , m_ptr(buffer.data())
+ , m_end(buffer.data() + buffer.size())
+ , m_version(0xFFFFFFFF)
+ , m_arrayBufferContents(arrayBufferContents)
+ , m_arrayBuffers(arrayBufferContents ? arrayBufferContents->size() : 0)
+ , m_blobURLs(blobURLs)
+ , m_blobFilePaths(blobFilePaths)
+ , m_sharedBuffers(sharedBuffers)
+#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 ENABLE(WEB_CODECS)
+ , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ , m_videoChunks(m_serializedVideoChunks.size())
+ , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+ , m_videoFrames(m_serializedVideoFrames.size())
+#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);
+ }
+
+ bool readResizableNonSharedArrayBuffer(RefPtr<ArrayBuffer>& arrayBuffer)
+ {
+ uint64_t byteLength;
+ if (!read(byteLength))
+ return false;
+ uint64_t maxByteLength;
+ if (!read(maxByteLength))
+ return false;
+ if (m_ptr + byteLength > m_end)
+ return false;
+ arrayBuffer = ArrayBuffer::tryCreate(byteLength, 1, maxByteLength);
+ if (!arrayBuffer)
+ return false;
+ ASSERT(arrayBuffer->isResizableNonShared());
+ memcpy(arrayBuffer->data(), m_ptr, byteLength);
+ m_ptr += byteLength;
+ return true;
+ }
+
+ 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;
+ JSValue arrayBufferValue = readTerminal();
+ if (!arrayBufferValue || !arrayBufferValue.inherits<JSArrayBuffer>())
+ return false;
+ JSObject* arrayBufferObj = asObject(arrayBufferValue);
+
+ unsigned elementSize = typedArrayElementSize(arrayBufferViewSubtag);
+ if (!elementSize)
+ return false;
+
+ RefPtr<ArrayBuffer> arrayBuffer = toPossiblySharedArrayBuffer(vm, arrayBufferObj);
+ if (!arrayBuffer) {
+ arrayBufferView = jsNull();
+ return true;
+ }
+
+ std::optional<size_t> length;
+ if (byteLength != autoLengthMarker) {
+ LengthType computedLength = byteLength / elementSize;
+ if (computedLength * elementSize != byteLength)
+ return false;
+ length = computedLength;
+ } else {
+ if (!arrayBuffer->isResizableOrGrowableShared())
+ return false;
+ }
+
+ switch (arrayBufferViewSubtag) {
+ case DataViewTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, DataView::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Int8ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int8Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Uint8ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Uint8ClampedArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint8ClampedArray::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Int16ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int16Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Uint16ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint16Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Int32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Int32Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Uint32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Uint32Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Float32ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float32Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case Float64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, Float64Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case BigInt64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigInt64Array::wrappedAs(arrayBuffer.releaseNonNull(), byteOffset, length).get());
+ return true;
+ case BigUint64ArrayTag:
+ arrayBufferView = toJS(m_lexicalGlobalObject, m_globalObject, BigUint64Array::wrappedAs(arrayBuffer.releaseNonNull(), 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(CryptoKeyOKP::NamedCurve& result)
+ {
+ uint8_t nameTag;
+ if (!read(nameTag))
+ return false;
+ if (nameTag > cryptoKeyOKPOpNameTagMaximumValue)
+ return false;
+
+ switch (static_cast<CryptoKeyOKPOpNameTag>(nameTag)) {
+ case CryptoKeyOKPOpNameTag::X25519:
+ result = CryptoKeyOKP::NamedCurve::X25519;
+ break;
+ case CryptoKeyOKPOpNameTag::ED25519:
+ result = CryptoKeyOKP::NamedCurve::Ed25519;
+ break;
+ }
+
+ return true;
+ }
+
+ 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;
+ case CryptoAlgorithmIdentifierTag::ED25519:
+ result = CryptoAlgorithmIdentifier::Ed25519;
+ 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 readOKPKey(bool extractable, CryptoKeyUsageBitmap usages, RefPtr<CryptoKey>& result)
+ {
+ CryptoAlgorithmIdentifier algorithm;
+ if (!read(algorithm))
+ return false;
+ if (!CryptoKeyOKP::isValidOKPAlgorithm(algorithm))
+ return false;
+ CryptoKeyOKP::NamedCurve namedCurve;
+ if (!read(namedCurve))
+ return false;
+ Vector<uint8_t> keyData;
+ if (!read(keyData))
+ return false;
+
+ result = CryptoKeyOKP::importRaw(algorithm, namedCurve, WTFMove(keyData), extractable, usages);
+ 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;
+ case CryptoKeyClassSubtag::OKP:
+ if (!readOKPKey(extractable, 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_backingStores.at(index)->connect(*executionContext(m_lexicalGlobalObject));
+ // 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
+
+#if ENABLE(WEB_CODECS)
+ JSValue readWebCodecsEncodedVideoChunk()
+ {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_serializedVideoChunks.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_videoChunks[index])
+ m_videoChunks[index] = WebCodecsEncodedVideoChunk::create(m_serializedVideoChunks.at(index).releaseNonNull());
+
+ return getJSValue(m_videoChunks[index].get());
+ }
+ JSValue readWebCodecsVideoFrame()
+ {
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || index >= m_serializedVideoFrames.size()) {
+ fail();
+ return JSValue();
+ }
+
+ if (!m_videoFrames[index])
+ m_videoFrames[index] = WebCodecsVideoFrame::create(*executionContext(m_lexicalGlobalObject), WTFMove(m_serializedVideoFrames.at(index)));
+
+ return getJSValue(m_videoFrames[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 = ByteArrayPixelBuffer::tryCreate(format, imageDataSize, arrayBuffer.releaseNonNull());
+ // if (!pixelBuffer) {
+ // fail();
+ // return JSValue();
+ // }
+
+ // buffer->putPixelBuffer(*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();
+
+ // read bun types
+ if (auto value = StructuredCloneableDeserialize::fromTagDeserialize(tag, m_lexicalGlobalObject, m_ptr, m_end)) {
+ JSValue deserialized = JSValue::decode(value.value());
+ if (deserialized.isEmpty()) {
+ fail();
+ return JSValue();
+ }
+ return deserialized;
+ }
+
+ 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(purifyNaN(d));
+ }
+ case BigIntTag:
+ return readBigInt();
+ case NumberObjectTag: {
+ double d;
+ if (!read(d))
+ return JSValue();
+ NumberObject* obj = constructNumber(m_globalObject, jsNumber(purifyNaN(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>());
+ // 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();
+ // uint64_t memoryCost = 0;
+ // if (m_version >= 11 && !read(memoryCost))
+ // return JSValue();
+ // if (!m_canCreateDOMObject)
+ // return jsNull();
+ // return getJSValue(Blob::deserialize(executionContext(m_lexicalGlobalObject), URL { url->string() }, type->string(), size, memoryCost, 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: {
+ if (m_version >= 12) {
+ // https://webassembly.github.io/spec/web-api/index.html#serialization
+ CachedStringRef agentClusterID;
+ bool agentClusterIDSuccessfullyRead = readStringData(agentClusterID);
+ if (!agentClusterIDSuccessfullyRead || agentClusterID->string() != agentClusterIDFromGlobalObject(*m_globalObject)) {
+ fail();
+ return JSValue();
+ }
+ }
+ 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: {
+ if (m_version >= 12) {
+ CachedStringRef agentClusterID;
+ bool agentClusterIDSuccessfullyRead = readStringData(agentClusterID);
+ if (!agentClusterIDSuccessfullyRead || agentClusterID->string() != agentClusterIDFromGlobalObject(*m_globalObject)) {
+ fail();
+ return JSValue();
+ }
+ }
+ uint32_t index;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || !m_wasmMemoryHandles || index >= m_wasmMemoryHandles->size() || !JSC::Options::useSharedArrayBuffer()) {
+ 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();
+
+ RefPtr<Wasm::Memory> memory;
+ auto handler = [&vm, result](Wasm::Memory::GrowSuccess, PageCount oldPageCount, PageCount newPageCount) { result->growSuccessCallback(vm, oldPageCount, newPageCount); };
+ if (RefPtr<SharedArrayBufferContents> contents = m_wasmMemoryHandles->at(index)) {
+ if (!contents->memoryHandle()) {
+ fail();
+ return JSValue();
+ }
+ memory = Wasm::Memory::create(contents.releaseNonNull(), WTFMove(handler));
+ } else {
+ // zero size & max-size.
+ memory = Wasm::Memory::createZeroSized(JSC::MemorySharingMode::Shared, WTFMove(handler));
+ }
+
+ result->adopt(memory.releaseNonNull());
+ 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 (UNLIKELY(!structure)) {
+ fail();
+ return JSValue();
+ }
+ JSValue result = JSArrayBuffer::create(m_lexicalGlobalObject->vm(), structure, WTFMove(arrayBuffer));
+ m_gcBuffer.appendWithCrashOnOverflow(result);
+ return result;
+ }
+ case ResizableArrayBufferTag: {
+ RefPtr<ArrayBuffer> arrayBuffer;
+ if (!readResizableNonSharedArrayBuffer(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 (UNLIKELY(!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: {
+ // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
+ uint32_t index = UINT_MAX;
+ bool indexSuccessfullyRead = read(index);
+ if (!indexSuccessfullyRead || !m_sharedBuffers || index >= m_sharedBuffers->size() || !JSC::Options::useSharedArrayBuffer()) {
+ 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);
+ CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, 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
+#if ENABLE(WEB_CODECS)
+ case WebCodecsEncodedVideoChunkTag:
+ return readWebCodecsEncodedVideoChunk();
+ case WebCodecsVideoFrameTag:
+ return readWebCodecsVideoFrame();
+#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* const m_globalObject;
+ const bool m_isDOMGlobalObject;
+ const bool m_canCreateDOMObject;
+ const uint8_t* m_ptr;
+ const uint8_t* const 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* const m_wasmModules;
+ WasmMemoryHandleArray* const m_wasmMemoryHandles;
+#endif
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> m_serializedVideoChunks;
+ Vector<RefPtr<WebCodecsEncodedVideoChunk>> m_videoChunks;
+ Vector<WebCodecsVideoFrameData> m_serializedVideoFrames;
+ Vector<RefPtr<WebCodecsVideoFrame>> m_videoFrames;
+#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;
+ MarkedVector<JSObject*, 32> outputObjectStack;
+ MarkedVector<JSValue, 4> mapKeyStack;
+ MarkedVector<JSMap*, 4> mapStack;
+ MarkedVector<JSSet*, 4> setStack;
+ Vector<WalkerState, 16> stateStack;
+ WalkerState state = StateUnknown;
+ JSValue outValue;
+
+ while (1) {
+ switch (state) {
+ 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->vm(), m_globalObject->mapStructure());
+ 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->vm(), m_globalObject->setStructure());
+ 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;
+
+ state = 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
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames
+#endif
+ )
+ : m_data(WTFMove(buffer))
+ , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
+#if ENABLE(WEB_RTC)
+ , m_detachedRTCDataChannels(WTFMove(detachedRTCDataChannels))
+#endif
+#if ENABLE(WEB_CODECS)
+ , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+#endif
+{
+ m_memoryCost = computeMemoryCost();
+}
+
+// SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, Vector<URLKeepingBlobAlive>&& 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
+// #if ENABLE(WEB_CODECS)
+// ,
+// Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames
+// #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
+// #if ENABLE(WEB_CODECS)
+// , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+// , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+// #endif
+// , m_blobHandles(crossThreadCopy(WTFMove(blobHandles)))
+// {
+// m_memoryCost = computeMemoryCost();
+// }
+
+SerializedScriptValue::SerializedScriptValue(Vector<uint8_t>&& buffer, std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray, std::unique_ptr<ArrayBufferContentsArray> sharedBufferContentsArray
+#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
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& serializedVideoChunks, Vector<WebCodecsVideoFrameData>&& serializedVideoFrames
+#endif
+ )
+ : m_data(WTFMove(buffer))
+ , m_arrayBufferContentsArray(WTFMove(arrayBufferContentsArray))
+ , m_sharedBufferContentsArray(WTFMove(sharedBufferContentsArray))
+#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
+#if ENABLE(WEB_CODECS)
+ , m_serializedVideoChunks(WTFMove(serializedVideoChunks))
+ , m_serializedVideoFrames(WTFMove(serializedVideoFrames))
+#endif
+{
+ 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->sizeInBytes(std::memory_order_relaxed);
+ }
+#endif
+#if ENABLE(WEB_CODECS)
+ for (auto& chunk : m_serializedVideoChunks) {
+ if (chunk)
+ cost += chunk->memoryCost();
+ }
+ for (auto& frame : m_serializedVideoFrames)
+ cost += frame.memoryCost();
+#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, SerializationForStorage forStorage, SerializationErrorMode throwExceptions, SerializationContext serializationContext)
+{
+ // Vector<RefPtr<MessagePort>> dummyPorts;
+ // auto result = create(globalObject, value, {}, dummyPorts, forStorage, throwExceptions, serializationContext);
+ auto result = create(globalObject, value, {}, forStorage, 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, SerializationForStorage forStorage, SerializationContext serializationContext)
+// {
+// return create(globalObject, value, WTFMove(transferList), messagePorts, forStorage, SerializationErrorMode::NonThrowing, serializationContext);
+// }
+
+ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& globalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, SerializationForStorage forStorage, SerializationContext serializationContext)
+{
+ return create(globalObject, value, WTFMove(transferList), forStorage, SerializationErrorMode::Throwing, serializationContext);
+}
+
+// ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& lexicalGlobalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, Vector<RefPtr<MessagePort>>& messagePorts, SerializationForStorage forStorage, SerializationErrorMode throwExceptions, SerializationContext context)
+ExceptionOr<Ref<SerializedScriptValue>> SerializedScriptValue::create(JSGlobalObject& lexicalGlobalObject, JSValue value, Vector<JSC::Strong<JSC::JSObject>>&& transferList, SerializationForStorage forStorage, 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
+#if ENABLE(WEB_CODECS)
+ Vector<Ref<WebCodecsVideoFrame>> transferredVideoFrames;
+#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, errorMessageForTransfer(arrayBuffer));
+ return Exception { ExistingExceptionError };
+ }
+ arrayBuffers.append(WTFMove(arrayBuffer));
+ continue;
+ }
+ // if (auto port = JSMessagePort::toWrapped(vm, transferable.get())) {
+ // if (port->isDetached())
+ // return Exception { DataCloneError, "MessagePort is detached"_s };
+ // messagePorts.append(WTFMove(port));
+ // continue;
+ // }
+
+ // if (auto imageBitmap = JSImageBitmap::toWrapped(vm, transferable.get())) {
+ // if (imageBitmap->isDetached())
+ // return Exception { DataCloneError };
+ // if (!imageBitmap->originClean())
+ // 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
+
+#if ENABLE(WEB_CODECS)
+ if (auto videoFrame = JSWebCodecsVideoFrame::toWrapped(vm, transferable.get())) {
+ if (videoFrame->isDetached())
+ return Exception { DataCloneError };
+ transferredVideoFrames.append(*videoFrame);
+ 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<URLKeepingBlobAlive> blobHandles;
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray wasmModules;
+ WasmMemoryHandleArray wasmMemoryHandles;
+#endif
+ std::unique_ptr<ArrayBufferContentsArray> sharedBuffers = makeUnique<ArrayBufferContentsArray>();
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> serializedVideoChunks;
+ Vector<RefPtr<WebCodecsVideoFrame>> serializedVideoFrames;
+#endif
+ // 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(WEB_CODECS)
+ // serializedVideoChunks,
+ // serializedVideoFrames,
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // wasmModules,
+ // wasmMemoryHandles,
+ // #endif
+ // blobHandles, buffer, context, *sharedBuffers, forStorage);
+
+ auto code = CloneSerializer::serialize(&lexicalGlobalObject, value, arrayBuffers,
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ offscreenCanvases,
+#endif
+#if ENABLE(WEB_RTC)
+ dataChannels,
+#endif
+#if ENABLE(WEB_CODECS)
+ serializedVideoChunks,
+ serializedVideoFrames,
+#endif
+#if ENABLE(WEBASSEMBLY)
+ wasmModules,
+ wasmMemoryHandles,
+#endif
+ buffer, context, *sharedBuffers, forStorage);
+
+ 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
+
+#if ENABLE(WEB_CODECS)
+ auto serializedVideoFrameData = map(serializedVideoFrames, [](auto& frame) -> WebCodecsVideoFrameData { return frame->data(); });
+#endif
+#if ENABLE(WEB_CODECS)
+ for (auto& videoFrame : transferredVideoFrames)
+ videoFrame->close();
+#endif
+
+ // return adoptRef(*new SerializedScriptValue(WTFMove(buffer), WTFMove(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
+ // #if ENABLE(WEB_CODECS)
+ // ,
+ // WTFMove(serializedVideoChunks), WTFMove(serializedVideoFrameData)
+ // #endif
+ // ));
+ return adoptRef(*new SerializedScriptValue(WTFMove(buffer), arrayBufferContentsArray.releaseReturnValue(), context == SerializationContext::WorkerPostMessage ? WTFMove(sharedBuffers) : nullptr
+#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
+#if ENABLE(WEB_CODECS)
+ ,
+ WTFMove(serializedVideoChunks), WTFMove(serializedVideoFrameData)
+#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, bool* didFail)
+// {
+// return deserialize(lexicalGlobalObject, globalObject, {}, throwExceptions, didFail);
+// }
+
+// JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, SerializationErrorMode throwExceptions, bool* didFail)
+// {
+// Vector<String> dummyBlobs;
+// Vector<String> dummyPaths;
+// return deserialize(lexicalGlobalObject, globalObject, messagePorts, dummyBlobs, dummyPaths, throwExceptions, didFail);
+// }
+
+JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, SerializationErrorMode throwExceptions, bool* didFail)
+{
+ Vector<String> dummyBlobs;
+ Vector<String> dummyPaths;
+ return deserialize(lexicalGlobalObject, globalObject, dummyBlobs, dummyPaths, throwExceptions, didFail);
+}
+
+// JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<RefPtr<MessagePort>>& messagePorts, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions, bool* didFail)
+// {
+// 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 ENABLE(WEB_CODECS)
+// ,
+// WTFMove(m_serializedVideoChunks), WTFMove(m_serializedVideoFrames)
+// #endif
+// );
+// if (didFail)
+// *didFail = result.second != SerializationReturnCode::SuccessfullyCompleted;
+// if (throwExceptions == SerializationErrorMode::Throwing)
+// maybeThrowExceptionIfSerializationFailed(lexicalGlobalObject, result.second);
+// return result.first ? result.first : jsNull();
+// }
+JSValue SerializedScriptValue::deserialize(JSGlobalObject& lexicalGlobalObject, JSGlobalObject* globalObject, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode throwExceptions, bool* didFail)
+{
+ // 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 ENABLE(WEB_CODECS)
+ // ,
+ // WTFMove(m_serializedVideoChunks), WTFMove(m_serializedVideoFrames)
+ // #endif
+ // );
+ DeserializationResult result = CloneDeserializer::deserialize(&lexicalGlobalObject, globalObject
+#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 ENABLE(WEB_CODECS)
+ ,
+ WTFMove(m_serializedVideoChunks), WTFMove(m_serializedVideoFrames)
+#endif
+ );
+ if (didFail)
+ *didFail = result.second != SerializationReturnCode::SuccessfullyCompleted;
+ 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().isolatedCopy();
+// });
+// }
+
+// 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
diff --git a/src/bun.js/bindings/webcore/SerializedScriptValue.h b/src/bun.js/bindings/webcore/SerializedScriptValue.h
new file mode 100644
index 000000000..6e2cbae86
--- /dev/null
+++ b/src/bun.js/bindings/webcore/SerializedScriptValue.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2009, 2013, 2016 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.
+ *
+ */
+
+#pragma once
+
+// #include "Blob.h"
+// #include "DetachedRTCDataChannel.h"
+#include "ExceptionOr.h"
+#include <JavaScriptCore/ArrayBuffer.h>
+#include <JavaScriptCore/JSCJSValue.h>
+#include <JavaScriptCore/Strong.h>
+#include <wtf/Forward.h>
+#include <wtf/Function.h>
+#include <wtf/Gigacage.h>
+#include <wtf/text/WTFString.h>
+
+#if ENABLE(WEB_CODECS)
+#include "WebCodecsEncodedVideoChunk.h"
+#include "WebCodecsVideoFrame.h"
+#endif
+
+typedef const struct OpaqueJSContext* JSContextRef;
+typedef const struct OpaqueJSValue* JSValueRef;
+
+#if ENABLE(WEBASSEMBLY)
+namespace JSC {
+namespace Wasm {
+class Module;
+class MemoryHandle;
+}
+}
+#endif
+
+namespace WebCore {
+
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+class DetachedOffscreenCanvas;
+#endif
+// class IDBValue;
+// class MessagePort;
+// class ImageBitmapBacking;
+class CloneSerializer;
+class FragmentedSharedBuffer;
+enum class SerializationReturnCode;
+
+enum class SerializationErrorMode { NonThrowing,
+ Throwing };
+enum class SerializationContext { Default,
+ WorkerPostMessage,
+ WindowPostMessage };
+enum class SerializationForStorage : bool { No,
+ Yes };
+
+using ArrayBufferContentsArray = Vector<JSC::ArrayBufferContents>;
+#if ENABLE(WEBASSEMBLY)
+using WasmModuleArray = Vector<RefPtr<JSC::Wasm::Module>>;
+using WasmMemoryHandleArray = Vector<RefPtr<JSC::SharedArrayBufferContents>>;
+#endif
+
+DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SerializedScriptValue);
+class SerializedScriptValue : public ThreadSafeRefCounted<SerializedScriptValue> {
+ WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(SerializedScriptValue);
+
+public:
+ static void writeBytesForBun(CloneSerializer*, const uint8_t*, uint32_t);
+
+ // WEBCORE_EXPORT static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, Vector<RefPtr<MessagePort>>&, SerializationForStorage = SerializationForStorage::No, SerializationContext = SerializationContext::Default);
+ WEBCORE_EXPORT static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, SerializationForStorage = SerializationForStorage::No, SerializationContext = SerializationContext::Default);
+
+ WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(JSC::JSGlobalObject&, JSC::JSValue, SerializationForStorage = SerializationForStorage::No, SerializationErrorMode = SerializationErrorMode::Throwing, SerializationContext = SerializationContext::Default);
+
+ static RefPtr<SerializedScriptValue> convert(JSC::JSGlobalObject& globalObject, JSC::JSValue value) { return create(globalObject, value, SerializationForStorage::Yes); }
+
+ WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(StringView);
+
+ static Ref<SerializedScriptValue> nullValue();
+
+ WEBCORE_EXPORT JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, SerializationErrorMode = SerializationErrorMode::Throwing, bool* didFail = nullptr);
+ // WEBCORE_EXPORT JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, const Vector<RefPtr<MessagePort>>&, SerializationErrorMode = SerializationErrorMode::Throwing, bool* didFail = nullptr);
+
+ // JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, const Vector<RefPtr<MessagePort>>&, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode = SerializationErrorMode::Throwing, bool* didFail = nullptr);
+ JSC::JSValue deserialize(JSC::JSGlobalObject&, JSC::JSGlobalObject*, const Vector<String>& blobURLs, const Vector<String>& blobFilePaths, SerializationErrorMode = SerializationErrorMode::Throwing, bool* didFail = nullptr);
+
+ static uint32_t wireFormatVersion();
+
+ WEBCORE_EXPORT String toString() const;
+
+ // API implementation helpers. These don't expose special behavior for ArrayBuffers or MessagePorts.
+ WEBCORE_EXPORT static RefPtr<SerializedScriptValue> create(JSContextRef, JSValueRef, JSValueRef* exception);
+ WEBCORE_EXPORT JSValueRef deserialize(JSContextRef, JSValueRef* exception);
+
+ // bool hasBlobURLs() const { return !m_blobHandles.isEmpty(); }
+
+ // Vector<String> blobURLs() const;
+ // Vector<URLKeepingBlobAlive> blobHandles() const { return crossThreadCopy(m_blobHandles); }
+ // void writeBlobsToDiskForIndexedDB(CompletionHandler<void(IDBValue&&)>&&);
+ // IDBValue writeBlobsToDiskForIndexedDBSynchronously();
+ static Ref<SerializedScriptValue> createFromWireBytes(Vector<uint8_t>&& data)
+ {
+ return adoptRef(*new SerializedScriptValue(WTFMove(data)));
+ }
+ const Vector<uint8_t>& wireBytes() const { return m_data; }
+
+ template<class Encoder> void encode(Encoder&) const;
+ template<class Decoder> static RefPtr<SerializedScriptValue> decode(Decoder&);
+
+ size_t memoryCost() const { return m_memoryCost; }
+
+ WEBCORE_EXPORT ~SerializedScriptValue();
+
+private:
+ // static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, Vector<RefPtr<MessagePort>>&, SerializationForStorage, SerializationErrorMode, SerializationContext);
+ // WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>&& = nullptr
+ // #if ENABLE(WEB_RTC)
+ // ,
+ // Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {}
+ // #endif
+ // #if ENABLE(WEB_CODECS)
+ // ,
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& = {}, Vector<WebCodecsVideoFrameData>&& = {}
+ // #endif
+ // );
+ static ExceptionOr<Ref<SerializedScriptValue>> create(JSC::JSGlobalObject&, JSC::JSValue, Vector<JSC::Strong<JSC::JSObject>>&& transfer, SerializationForStorage, SerializationErrorMode, SerializationContext);
+ WEBCORE_EXPORT SerializedScriptValue(Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>&& = nullptr
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {}
+#endif
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& = {}, Vector<WebCodecsVideoFrameData>&& = {}
+#endif
+ );
+
+ // SerializedScriptValue(Vector<unsigned char>&&, Vector<URLKeepingBlobAlive>&& blobHandles, std::unique_ptr<ArrayBufferContentsArray>, std::unique_ptr<ArrayBufferContentsArray> sharedBuffers, Vector<std::optional<ImageBitmapBacking>>&& backingStores
+ // #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ // ,
+ // Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& = {}
+ // #endif
+ // #if ENABLE(WEB_RTC)
+ // ,
+ // Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {}
+ // #endif
+ // #if ENABLE(WEBASSEMBLY)
+ // ,
+ // std::unique_ptr<WasmModuleArray> = nullptr, std::unique_ptr<WasmMemoryHandleArray> = nullptr
+ // #endif
+ // #if ENABLE(WEB_CODECS)
+ // ,
+ // Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& = {}, Vector<WebCodecsVideoFrameData>&& = {}
+ // #endif
+ // );
+ SerializedScriptValue(Vector<unsigned char>&&, std::unique_ptr<ArrayBufferContentsArray>, std::unique_ptr<ArrayBufferContentsArray> sharedBuffers
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ ,
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>>&& = {}
+#endif
+#if ENABLE(WEB_RTC)
+ ,
+ Vector<std::unique_ptr<DetachedRTCDataChannel>>&& = {}
+#endif
+#if ENABLE(WEBASSEMBLY)
+ ,
+ std::unique_ptr<WasmModuleArray> = nullptr, std::unique_ptr<WasmMemoryHandleArray> = nullptr
+#endif
+#if ENABLE(WEB_CODECS)
+ ,
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>>&& = {}, Vector<WebCodecsVideoFrameData>&& = {}
+#endif
+ );
+
+ size_t computeMemoryCost() const;
+
+ Vector<unsigned char> m_data;
+ std::unique_ptr<ArrayBufferContentsArray> m_arrayBufferContentsArray;
+ std::unique_ptr<ArrayBufferContentsArray> m_sharedBufferContentsArray;
+ // Vector<std::optional<ImageBitmapBacking>> m_backingStores;
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ Vector<std::unique_ptr<DetachedOffscreenCanvas>> m_detachedOffscreenCanvases;
+#endif
+#if ENABLE(WEB_RTC)
+ Vector<std::unique_ptr<DetachedRTCDataChannel>> m_detachedRTCDataChannels;
+#endif
+#if ENABLE(WEBASSEMBLY)
+ std::unique_ptr<WasmModuleArray> m_wasmModulesArray;
+ std::unique_ptr<WasmMemoryHandleArray> m_wasmMemoryHandlesArray;
+#endif
+#if ENABLE(WEB_CODECS)
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> m_serializedVideoChunks;
+ Vector<WebCodecsVideoFrameData> m_serializedVideoFrames;
+#endif
+ // Vector<URLKeepingBlobAlive> m_blobHandles;
+ size_t m_memoryCost { 0 };
+};
+
+template<class Encoder>
+void SerializedScriptValue::encode(Encoder& encoder) const
+{
+ encoder << m_data;
+
+ auto hasArray = m_arrayBufferContentsArray && m_arrayBufferContentsArray->size();
+ encoder << hasArray;
+
+ if (hasArray) {
+ encoder << static_cast<uint64_t>(m_arrayBufferContentsArray->size());
+ for (const auto& arrayBufferContents : *m_arrayBufferContentsArray)
+ encoder << std::span(reinterpret_cast<const uint8_t*>(arrayBufferContents.data()), arrayBufferContents.sizeInBytes());
+ }
+
+#if ENABLE(WEB_RTC)
+ encoder << static_cast<uint64_t>(m_detachedRTCDataChannels.size());
+ for (const auto& channel : m_detachedRTCDataChannels)
+ encoder << *channel;
+#endif
+
+#if ENABLE(WEB_CODECS)
+ encoder << static_cast<uint64_t>(m_serializedVideoChunks.size());
+ for (const auto& videoChunk : m_serializedVideoChunks)
+ encoder << videoChunk->data();
+
+ // FIXME: encode video frames
+#endif
+}
+
+template<class Decoder>
+RefPtr<SerializedScriptValue> SerializedScriptValue::decode(Decoder& decoder)
+{
+ Vector<uint8_t> data;
+ if (!decoder.decode(data))
+ return nullptr;
+
+ bool hasArray;
+ if (!decoder.decode(hasArray))
+ return nullptr;
+
+ std::unique_ptr<ArrayBufferContentsArray> arrayBufferContentsArray;
+ if (hasArray) {
+ uint64_t arrayLength;
+ if (!decoder.decode(arrayLength))
+ return nullptr;
+ ASSERT(arrayLength);
+
+ arrayBufferContentsArray = makeUnique<ArrayBufferContentsArray>();
+ while (arrayLength--) {
+ std::span<const uint8_t> data;
+ if (!decoder.decode(data))
+ return nullptr;
+
+ auto buffer = Gigacage::tryMalloc(Gigacage::Primitive, data.size_bytes());
+ if (!buffer)
+ return nullptr;
+
+ static_assert(sizeof(std::span<const uint8_t>::element_type) == 1);
+ memcpy(buffer, data.data(), data.size_bytes());
+ JSC::ArrayBufferDestructorFunction destructor = ArrayBuffer::primitiveGigacageDestructor();
+ arrayBufferContentsArray->append({ buffer, data.size_bytes(), std::nullopt, WTFMove(destructor) });
+ }
+ }
+
+#if ENABLE(WEB_RTC)
+ uint64_t detachedRTCDataChannelsSize;
+ if (!decoder.decode(detachedRTCDataChannelsSize))
+ return nullptr;
+
+ Vector<std::unique_ptr<DetachedRTCDataChannel>> detachedRTCDataChannels;
+ while (detachedRTCDataChannelsSize--) {
+ std::optional<DetachedRTCDataChannel> detachedRTCDataChannel;
+ decoder >> detachedRTCDataChannel;
+ if (!detachedRTCDataChannel)
+ return nullptr;
+ detachedRTCDataChannels.append(makeUnique<DetachedRTCDataChannel>(WTFMove(*detachedRTCDataChannel)));
+ }
+#endif
+#if ENABLE(WEB_CODECS)
+ uint64_t serializedVideoChunksSize;
+ if (!decoder.decode(serializedVideoChunksSize))
+ return nullptr;
+
+ Vector<RefPtr<WebCodecsEncodedVideoChunkStorage>> serializedVideoChunks;
+ while (serializedVideoChunksSize--) {
+ std::optional<WebCodecsEncodedVideoChunkData> videoChunkData;
+ decoder >> videoChunkData;
+ if (!videoChunkData)
+ return nullptr;
+ serializedVideoChunks.append(WebCodecsEncodedVideoChunkStorage::create(WTFMove(*videoChunkData)));
+ }
+ // FIXME: decode video frames
+ Vector<WebCodecsVideoFrameData> serializedVideoFrames;
+#endif
+
+ return adoptRef(*new SerializedScriptValue(WTFMove(data), WTFMove(arrayBufferContentsArray)
+#if ENABLE(WEB_RTC)
+ ,
+ WTFMove(detachedRTCDataChannels)
+#endif
+#if ENABLE(WEB_CODECS)
+ ,
+ WTFMove(serializedVideoChunks)
+#endif
+#if ENABLE(WEB_CODECS)
+ ,
+ WTFMove(serializedVideoFrames)
+#endif
+ ));
+}
+
+}
diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig
index 642039ba5..64bd04e94 100644
--- a/src/bun.js/node/types.zig
+++ b/src/bun.js/node/types.zig
@@ -1024,6 +1024,8 @@ pub const PathOrFileDescriptor = union(Tag) {
pub const Tag = enum { fd, path };
+ pub const SerializeTag = enum(u8) { fd, path };
+
/// This will unref() the path string if it is a PathLike.
/// Does nothing for file descriptors, **does not** close file descriptors.
pub fn deinit(this: PathOrFileDescriptor) void {
diff --git a/src/bun.js/scripts/class-definitions.ts b/src/bun.js/scripts/class-definitions.ts
index 59b8f6f05..281cd62c5 100644
--- a/src/bun.js/scripts/class-definitions.ts
+++ b/src/bun.js/scripts/class-definitions.ts
@@ -41,6 +41,7 @@ export interface ClassDefinition {
configurable?: boolean;
enumerable?: boolean;
+ structuredClone?: boolean | { transferrable: boolean; tag: number };
}
export interface CustomField {
@@ -58,6 +59,7 @@ export function define(
estimatedSize = false,
call = false,
construct = false,
+ structuredClone = false,
...rest
} = {} as ClassDefinition,
): ClassDefinition {
@@ -66,6 +68,7 @@ export function define(
call,
construct,
estimatedSize,
+ structuredClone,
values,
klass: Object.fromEntries(Object.entries(klass).sort(([a], [b]) => a.localeCompare(b))),
proto: Object.fromEntries(Object.entries(proto).sort(([a], [b]) => a.localeCompare(b))),
diff --git a/src/bun.js/scripts/generate-classes.ts b/src/bun.js/scripts/generate-classes.ts
index ff6954753..fb4419642 100644
--- a/src/bun.js/scripts/generate-classes.ts
+++ b/src/bun.js/scripts/generate-classes.ts
@@ -345,6 +345,25 @@ ${
JSC_DECLARE_CUSTOM_GETTER(js${typeName}Constructor);`
: ""
}
+
+${
+ obj.structuredClone
+ ? `extern "C" void ${symbolName(
+ typeName,
+ "onStructuredCloneSerialize",
+ )}(void*, JSC::JSGlobalObject*, void*, void (*) (CloneSerializer*, const uint8_t*, uint32_t));`
+ : ""
+}
+
+${
+ obj.structuredClone
+ ? `extern "C" JSC::EncodedJSValue ${symbolName(
+ typeName,
+ "onStructuredCloneDeserialize",
+ )}(JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);`
+ : ""
+}
+
${"finalize" in obj ? `extern "C" void ${classSymbolName(typeName, "finalize")}(void*);` : ""}
${obj.call ? `extern "C" JSC_DECLARE_HOST_FUNCTION(${classSymbolName(typeName, "call")});` : ""}
@@ -1200,6 +1219,7 @@ function generateZig(
call = false,
values = [],
hasPendingActivity = false,
+ structuredClone = false,
} = {} as ClassDefinition,
) {
const exports = new Map<string, string>();
@@ -1226,6 +1246,16 @@ function generateZig(
Object.values(klass).map(a => appendSymbols(exports, name => classSymbolName(typeName, name), a));
Object.values(proto).map(a => appendSymbols(exports, name => protoSymbolName(typeName, name), a));
+ if (structuredClone) {
+ exports.set("onStructuredCloneSerialize", symbolName(typeName, "onStructuredCloneSerialize"));
+
+ if (structuredClone === "transferrable") {
+ exports.set("onStructuredCloneTransfer", symbolName(typeName, "onStructuredCloneTransfer"));
+ }
+
+ exports.set("onStructuredCloneDeserialize", symbolName(typeName, "onStructuredCloneDeserialize"));
+ }
+
const externs = Object.entries({
...proto,
...Object.fromEntries((values || []).map(a => [a, { internal: true }])),
@@ -1269,6 +1299,29 @@ function generateZig(
`;
}
+ if (structuredClone) {
+ output += `
+ if (@TypeOf(${typeName}.onStructuredCloneSerialize) != (fn(*${typeName}, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, writeBytes: *const fn(*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void) callconv(.C) void)) {
+ @compileLog("${typeName}.onStructuredCloneSerialize is not a structured clone serialize function");
+ }
+ `;
+
+ if (structuredClone === "transferrable") {
+ exports.set("structuredClone", symbolName(typeName, "onTransferrableStructuredClone"));
+ output += `
+ if (@TypeOf(${typeName}.onStructuredCloneTransfer) != (fn(*${typeName}, globalThis: *JSC.JSGlobalObject, ctx: *anyopaque, write: *const fn(*anyopaque, ptr: [*]const u8, len: usize) callconv(.C) void) callconv(.C) void)) {
+ @compileLog("${typeName}.onStructuredCloneTransfer is not a structured clone transfer function");
+ }
+ `;
+ }
+
+ output += `
+ if (@TypeOf(${typeName}.onStructuredCloneDeserialize) != (fn(globalThis: *JSC.JSGlobalObject, ptr: [*]u8, end: [*]u8) callconv(.C) JSC.JSValue)) {
+ @compileLog("${typeName}.onStructuredCloneDeserialize is not a structured clone deserialize function");
+ }
+ `;
+ }
+
if (construct && !noConstructor) {
output += `
if (@TypeOf(${typeName}.constructor) != (fn(*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*${typeName})) {
@@ -1532,6 +1585,7 @@ namespace Zig {
#include "JSDOMWrapper.h"
#include <wtf/NeverDestroyed.h>
+#include "SerializedScriptValue.h"
`,
`
@@ -1543,11 +1597,6 @@ using namespace JSC;
`,
];
-const GENERATED_CLASSES_FOOTER = `
-}
-
-`;
-
const GENERATED_CLASSES_IMPL_HEADER = `
// GENERATED CODE - DO NOT MODIFY BY HAND
// Generated by make codegen
@@ -1676,6 +1725,84 @@ function writeAndUnlink(path, content) {
return Bun.write(path, content);
}
+const GENERATED_CLASSES_FOOTER = `
+
+class StructuredCloneableSerialize {
+ public:
+
+ void (*cppWriteBytes)(CloneSerializer*, const uint8_t*, uint32_t);
+
+ std::function<void(void*, JSC::JSGlobalObject*, void*, void (*)(CloneSerializer*, const uint8_t*, uint32_t))> zigFunction;
+
+ uint8_t tag;
+
+ // the type from zig
+ void* impl;
+
+ static std::optional<StructuredCloneableSerialize> fromJS(JSC::JSValue);
+ void write(CloneSerializer* serializer, JSC::JSGlobalObject* globalObject)
+ {
+ zigFunction(impl, globalObject, serializer, cppWriteBytes);
+ }
+};
+
+class StructuredCloneableDeserialize {
+ public:
+ static std::optional<JSC::EncodedJSValue> fromTagDeserialize(uint8_t tag, JSC::JSGlobalObject*, const uint8_t*, const uint8_t*);
+};
+
+}
+
+`;
+
+function writeCppSerializers() {
+ var output = ``;
+
+ var structuredClonable = classes
+ .filter(a => a.structuredClone)
+ .sort((a, b) => a.structuredClone.tag < b.structuredClone.tag);
+
+ function fromJSForEachClass(klass) {
+ return `
+ if (auto* result = jsDynamicCast<${className(klass.name)}*>(value)) {
+ return StructuredCloneableSerialize { .cppWriteBytes = SerializedScriptValue::writeBytesForBun, .zigFunction = ${symbolName(
+ klass.name,
+ "onStructuredCloneSerialize",
+ )}, .tag = ${klass.structuredClone.tag}, .impl = result->wrapped() };
+ }
+ `;
+ }
+
+ function fromTagDeserializeForEachClass(klass) {
+ return `
+ if (tag == ${klass.structuredClone.tag}) {
+ return ${symbolName(klass.name, "onStructuredCloneDeserialize")}(globalObject, ptr, end);
+ }
+ `;
+ }
+
+ output += `
+ std::optional<StructuredCloneableSerialize> StructuredCloneableSerialize::fromJS(JSC::JSValue value)
+ {
+ ${structuredClonable.map(fromJSForEachClass).join("\n").trim()}
+ return std::nullopt;
+ }
+ `;
+
+ output += `
+ std::optional<JSC::EncodedJSValue> StructuredCloneableDeserialize::fromTagDeserialize(uint8_t tag, JSC::JSGlobalObject* globalObject, const uint8_t* ptr, const uint8_t* end)
+ {
+ ${structuredClonable.map(fromTagDeserializeForEachClass).join("\n").trim()}
+ return std::nullopt;
+ }
+ `;
+
+ for (let klass of classes) {
+ }
+
+ return output;
+}
+
await writeAndUnlink(`${import.meta.dir}/../bindings/generated_classes.zig`, [
ZIG_GENERATED_CLASSES_HEADER,
@@ -1699,6 +1826,7 @@ await writeAndUnlink(`${import.meta.dir}/../bindings/ZigGeneratedClasses.h`, [
await writeAndUnlink(`${import.meta.dir}/../bindings/ZigGeneratedClasses.cpp`, [
GENERATED_CLASSES_IMPL_HEADER,
...classes.map(a => generateImpl(a.name, a)),
+ writeCppSerializers(classes),
GENERATED_CLASSES_IMPL_FOOTER,
]);
await writeAndUnlink(
diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig
index e0833f8ed..12f7011fb 100644
--- a/src/bun.js/test/expect.zig
+++ b/src/bun.js/test/expect.zig
@@ -312,9 +312,6 @@ pub const Expect = struct {
const not = this.flags.not;
var pass = right.isSameValue(left, globalObject);
- if (comptime Environment.allow_assert) {
- std.debug.assert(pass == JSC.C.JSValueIsStrictEqual(globalObject, right.asObjectRef(), left.asObjectRef()));
- }
if (not) pass = !pass;
if (pass) return thisValue;
diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig
index ef2520049..0d6dcbc26 100644
--- a/src/bun.js/webcore/blob.zig
+++ b/src/bun.js/webcore/blob.zig
@@ -105,6 +105,9 @@ pub const Blob = struct {
pub const JSTimeType = u52;
pub const init_timestamp = std.math.maxInt(JSTimeType);
+ const serialization_version: u8 = 1;
+ const reserved_space_for_serialization: u32 = 128;
+
pub fn getFormDataEncoding(this: *Blob) ?*bun.FormData.AsyncFormData {
var content_type_slice: ZigString.Slice = this.getContentType() orelse return null;
defer content_type_slice.deinit();
@@ -208,6 +211,192 @@ pub const Blob = struct {
return null;
}
+ const StructuredCloneWriter = struct {
+ ctx: *anyopaque,
+ impl: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void,
+
+ pub const WriteError = error{};
+ pub fn write(this: StructuredCloneWriter, bytes: []const u8) WriteError!usize {
+ this.impl(this.ctx, bytes.ptr, @truncate(u32, bytes.len));
+ return bytes.len;
+ }
+ };
+
+ fn _onStructuredCloneSerialize(
+ this: *Blob,
+ comptime Writer: type,
+ writer: Writer,
+ ) !void {
+ try writer.writeIntNative(u8, serialization_version);
+
+ try writer.writeIntNative(u64, @intCast(u64, this.offset));
+
+ try writer.writeIntNative(u32, @truncate(u32, this.content_type.len));
+ _ = try writer.write(this.content_type);
+ try writer.writeIntNative(u8, @intFromBool(this.content_type_was_set));
+
+ const store_tag: Store.SerializeTag = if (this.store) |store|
+ if (store.data == .file) .file else .bytes
+ else
+ .empty;
+
+ try writer.writeIntNative(u8, @intFromEnum(store_tag));
+
+ this.resolveSize();
+ if (this.store) |store| {
+ try store.serialize(Writer, writer);
+ }
+
+ // reserved space for future use
+ _ = try writer.write(&[_]u8{0} ** reserved_space_for_serialization);
+ }
+
+ pub fn onStructuredCloneSerialize(
+ this: *Blob,
+ globalThis: *JSC.JSGlobalObject,
+ ctx: *anyopaque,
+ writeBytes: *const fn (*anyopaque, ptr: [*]const u8, len: u32) callconv(.C) void,
+ ) callconv(.C) void {
+ _ = globalThis;
+
+ const Writer = std.io.Writer(StructuredCloneWriter, StructuredCloneWriter.WriteError, StructuredCloneWriter.write);
+ var writer = Writer{
+ .context = .{
+ .ctx = ctx,
+ .impl = writeBytes,
+ },
+ };
+
+ _onStructuredCloneSerialize(this, Writer, writer) catch return .zero;
+ }
+
+ pub fn onStructuredCloneTransfer(
+ this: *Blob,
+ globalThis: *JSC.JSGlobalObject,
+ ctx: *anyopaque,
+ write: *const fn (*anyopaque, ptr: [*]const u8, len: usize) callconv(.C) void,
+ ) callconv(.C) void {
+ _ = write;
+ _ = ctx;
+ _ = this;
+ _ = globalThis;
+ }
+
+ fn readSlice(
+ reader: anytype,
+ len: usize,
+ allocator: std.mem.Allocator,
+ ) ![]u8 {
+ var slice = try allocator.alloc(u8, len);
+ slice = slice[0..try reader.read(slice)];
+ if (slice.len != len) return error.TooSmall;
+ return slice;
+ }
+
+ fn _onStructuredCloneDeserialize(
+ globalThis: *JSC.JSGlobalObject,
+ comptime Reader: type,
+ reader: Reader,
+ ) !JSValue {
+ const allocator = globalThis.allocator();
+
+ const version = try reader.readIntNative(u8);
+ _ = version;
+
+ const offset = try reader.readIntNative(u64);
+
+ const content_type_len = try reader.readIntNative(u32);
+
+ const content_type = try readSlice(reader, content_type_len, allocator);
+
+ const content_type_was_set: bool = try reader.readIntNative(u8) != 0;
+
+ const store_tag = try reader.readEnum(Store.SerializeTag, .Little);
+
+ const blob: *Blob = switch (store_tag) {
+ .bytes => brk: {
+ const bytes_len = try reader.readIntNative(u32);
+ const bytes = try readSlice(reader, bytes_len, allocator);
+
+ var blob = Blob.init(bytes, allocator, globalThis);
+ var blob_ = try allocator.create(Blob);
+ blob_.* = blob;
+
+ break :brk blob_;
+ },
+ .file => brk: {
+ const pathlike_tag = try reader.readEnum(JSC.Node.PathOrFileDescriptor.SerializeTag, .Little);
+
+ switch (pathlike_tag) {
+ .fd => {
+ const fd = @intCast(i32, try reader.readIntNative(u32));
+
+ var blob = try allocator.create(Blob);
+ blob.* = Blob.findOrCreateFileFromPath(
+ JSC.Node.PathOrFileDescriptor{
+ .fd = fd,
+ },
+ globalThis,
+ );
+
+ break :brk blob;
+ },
+ .path => {
+ const path_len = try reader.readIntNative(u32);
+
+ const path = try readSlice(reader, path_len, default_allocator);
+
+ var blob = try allocator.create(Blob);
+ blob.* = Blob.findOrCreateFileFromPath(
+ JSC.Node.PathOrFileDescriptor{
+ .path = .{
+ .string = bun.PathString.init(path),
+ },
+ },
+ globalThis,
+ );
+
+ break :brk blob;
+ },
+ }
+
+ return .zero;
+ },
+ .empty => brk: {
+ var blob = try allocator.create(Blob);
+ blob.* = Blob.initEmpty(globalThis);
+ break :brk blob;
+ },
+ };
+ blob.allocator = allocator;
+ blob.offset = @intCast(u52, offset);
+ if (content_type.len > 0) {
+ blob.content_type = content_type;
+ blob.content_type_allocated = true;
+ blob.content_type_was_set = content_type_was_set;
+ }
+
+ return blob.toJS(globalThis);
+ }
+
+ pub fn onStructuredCloneDeserialize(
+ globalThis: *JSC.JSGlobalObject,
+ ptr: [*]u8,
+ end: [*]u8,
+ ) callconv(.C) JSValue {
+ const total_length: usize = @intFromPtr(end) - @intFromPtr(ptr);
+ var buffer_stream = std.io.fixedBufferStream(ptr[0..total_length]);
+ var reader = buffer_stream.reader();
+
+ const blob = _onStructuredCloneDeserialize(globalThis, @TypeOf(reader), reader) catch return .zero;
+
+ if (Environment.allow_assert) {
+ std.debug.assert(total_length - reader.context.pos == reserved_space_for_serialization);
+ }
+
+ return blob;
+ }
+
const URLSearchParamsConverter = struct {
allocator: std.mem.Allocator,
buf: []u8 = "",
@@ -1095,6 +1284,37 @@ pub const Blob = struct {
allocator.destroy(this);
}
+ const SerializeTag = enum(u8) {
+ file = 0,
+ bytes = 1,
+ empty = 2,
+ };
+
+ pub fn serialize(this: *Store, comptime Writer: type, writer: Writer) !void {
+ switch (this.data) {
+ .file => |file| {
+ const pathlike_tag: JSC.Node.PathOrFileDescriptor.SerializeTag = if (file.pathlike == .fd) .fd else .path;
+ try writer.writeIntNative(u8, @intFromEnum(pathlike_tag));
+
+ switch (file.pathlike) {
+ .fd => |fd| {
+ try writer.writeIntNative(u32, @intCast(u32, fd));
+ },
+ .path => |path| {
+ const path_slice = path.slice();
+ try writer.writeIntNative(u32, @truncate(u32, path_slice.len));
+ _ = try writer.write(path_slice);
+ },
+ }
+ },
+ .bytes => |bytes| {
+ const slice = bytes.slice();
+ try writer.writeIntNative(u32, @truncate(u32, slice.len));
+ _ = try writer.write(slice);
+ },
+ }
+ }
+
pub fn fromArrayList(list: std.ArrayListUnmanaged(u8), allocator: std.mem.Allocator) !*Blob.Store {
return try Blob.Store.init(list.items, allocator);
}
diff --git a/src/bun.js/webcore/response.classes.ts b/src/bun.js/webcore/response.classes.ts
index c11cb10b2..f0d6e1451 100644
--- a/src/bun.js/webcore/response.classes.ts
+++ b/src/bun.js/webcore/response.classes.ts
@@ -125,6 +125,7 @@ export default [
JSType: "0b11101110",
klass: {},
configurable: false,
+ structuredClone: { transferrable: false, tag: 254 },
proto: {
text: { fn: "getText" },
json: { fn: "getJSON" },