diff --git a/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int-expected.txt b/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int-expected.txt
new file mode 100644
index 0000000000000..8b137891791fe
--- /dev/null
+++ b/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int-expected.txt
@@ -0,0 +1 @@
+
diff --git a/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int.html b/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int.html
new file mode 100644
index 0000000000000..edbc3fecc7ff3
--- /dev/null
+++ b/LayoutTests/js/structuredClone/structured-clone-validation-with-big-int.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
diff --git a/Source/JavaScriptCore/runtime/OptionsList.h b/Source/JavaScriptCore/runtime/OptionsList.h
index 3b22571a99ff3..c0238d61ffcb1 100644
--- a/Source/JavaScriptCore/runtime/OptionsList.h
+++ b/Source/JavaScriptCore/runtime/OptionsList.h
@@ -237,6 +237,7 @@ bool canUseWebAssemblyFastMemory();
v(Unsigned, defaultB3OptLevel, 2, Normal, nullptr) \
v(Bool, b3AlwaysFailsBeforeCompile, false, Normal, nullptr) \
v(Bool, b3AlwaysFailsBeforeLink, false, Normal, nullptr) \
+ v(Bool, validateSerializedValue, false, Normal, nullptr) /* tests CloneSerializer/Deserializer */ \
v(Bool, ftlCrashes, false, Normal, nullptr) /* fool-proof way of checking that you ended up in the FTL. ;-) */\
v(Bool, clobberAllRegsInFTLICSlowPath, ASSERT_ENABLED, Normal, nullptr) \
v(Bool, enableJITDebugAssertions, ASSERT_ENABLED, Normal, nullptr) \
diff --git a/Source/WebCore/bindings/js/SerializedScriptValue.cpp b/Source/WebCore/bindings/js/SerializedScriptValue.cpp
index 35d713f86a7f9..33057f6518357 100644
--- a/Source/WebCore/bindings/js/SerializedScriptValue.cpp
+++ b/Source/WebCore/bindings/js/SerializedScriptValue.cpp
@@ -92,6 +92,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -102,6 +103,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -130,6 +132,13 @@ namespace WebCore {
using namespace JSC;
+namespace SerializationHelper {
+
+static constexpr bool verboseTrace = false;
+
+} // namespace SerializationHelper
+
+
DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(SerializedScriptValue);
static constexpr unsigned maximumFilterRecursion = 40000;
@@ -248,6 +257,141 @@ enum ArrayBufferViewSubtag {
BigUint64ArrayTag = 11,
};
+static const char* name(SerializationTag tag)
+{
+ switch (tag) {
+ case ArrayTag: return "ArrayTag";
+ case ObjectTag: return "ObjectTag";
+ case UndefinedTag: return "UndefinedTag";
+ case NullTag: return "NullTag";
+ case IntTag: return "IntTag";
+ case ZeroTag: return "ZeroTag";
+ case OneTag: return "OneTag";
+ case FalseTag: return "FalseTag";
+ case TrueTag: return "TrueTag";
+ case DoubleTag: return "DoubleTag";
+ case DateTag: return "DateTag";
+ case FileTag: return "FileTag";
+ case FileListTag: return "FileListTag";
+ case ImageDataTag: return "ImageDataTag";
+ case BlobTag: return "BlobTag";
+ case StringTag: return "StringTag";
+ case EmptyStringTag: return "EmptyStringTag";
+ case RegExpTag: return "RegExpTag";
+ case ObjectReferenceTag: return "ObjectReferenceTag";
+ case MessagePortReferenceTag: return "MessagePortReferenceTag";
+ case ArrayBufferTag: return "ArrayBufferTag";
+ case ArrayBufferViewTag: return "ArrayBufferViewTag";
+ case ArrayBufferTransferTag: return "ArrayBufferTransferTag";
+ case TrueObjectTag: return "TrueObjectTag";
+ case FalseObjectTag: return "FalseObjectTag";
+ case StringObjectTag: return "StringObjectTag";
+ case EmptyStringObjectTag: return "EmptyStringObjectTag";
+ case NumberObjectTag: return "NumberObjectTag";
+ case SetObjectTag: return "SetObjectTag";
+ case MapObjectTag: return "MapObjectTag";
+ case NonMapPropertiesTag: return "NonMapPropertiesTag";
+ case NonSetPropertiesTag: return "NonSetPropertiesTag";
+#if ENABLE(WEB_CRYPTO)
+ case CryptoKeyTag: return "CryptoKeyTag";
+#endif
+ case SharedArrayBufferTag: return "SharedArrayBufferTag";
+#if ENABLE(WEBASSEMBLY)
+ case WasmModuleTag: return "WasmModuleTag";
+#endif
+ case DOMPointReadOnlyTag: return "DOMPointReadOnlyTag";
+ case DOMPointTag: return "DOMPointTag";
+ case DOMRectReadOnlyTag: return "DOMRectReadOnlyTag";
+ case DOMRectTag: return "DOMRectTag";
+ case DOMMatrixReadOnlyTag: return "DOMMatrixReadOnlyTag";
+ case DOMMatrixTag: return "DOMMatrixTag";
+ case DOMQuadTag: return "DOMQuadTag";
+ case ImageBitmapTransferTag: return "ImageBitmapTransferTag";
+#if ENABLE(WEB_RTC)
+ case RTCCertificateTag: return "RTCCertificateTag";
+#endif
+ case ImageBitmapTag: return "ImageBitmapTag";
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ case OffscreenCanvasTransferTag: return "OffscreenCanvasTransferTag";
+#endif
+ case BigIntTag: return "BigIntTag";
+ case BigIntObjectTag: return "BigIntObjectTag";
+#if ENABLE(WEBASSEMBLY)
+ case WasmMemoryTag: return "WasmMemoryTag";
+#endif
+#if ENABLE(WEB_RTC)
+ case RTCDataChannelTransferTag: return "RTCDataChannelTransferTag";
+#endif
+ case DOMExceptionTag: return "DOMExceptionTag";
+#if ENABLE(WEB_CODECS)
+ case WebCodecsEncodedVideoChunkTag: return "WebCodecsEncodedVideoChunkTag";
+ case WebCodecsVideoFrameTag: return "WebCodecsVideoFrameTag";
+#endif
+ case ResizableArrayBufferTag: return "ResizableArrayBufferTag";
+ case ErrorInstanceTag: return "ErrorInstanceTag";
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ case InMemoryOffscreenCanvasTag: return "InMemoryOffscreenCanvasTag";
+#endif
+ case InMemoryMessagePortTag: return "InMemoryMessagePortTag";
+#if ENABLE(WEB_CODECS)
+ case WebCodecsEncodedAudioChunkTag: return "WebCodecsEncodedAudioChunkTag";
+ case WebCodecsAudioDataTag: return "WebCodecsAudioDataTag";
+#endif
+ case ErrorTag: return "ErrorTag";
+ }
+}
+
+} // namespace WebCore
+
+namespace WTF {
+
+void printInternal(PrintStream&, WebCore::SerializationTag);
+
+void printInternal(PrintStream& out, WebCore::SerializationTag tag)
+{
+ const char* tagName = WebCore::name(tag);
+ if (tagName[0] != '<')
+ out.print(tagName);
+ else
+ out.print("(tag), ">");
+}
+
+} // namespace WTF
+
+namespace WebCore {
+
+// This function is only used for a sanity check mechanism used in
+// CloneSerializer::addToObjectPoolIfNotDupe() and CloneDeserializer::addToObjectPool().
+static constexpr bool canBeAddedToObjectPool(SerializationTag tag)
+{
+ // If you add a type to the allow ist (i.e. returns true) here, it means
+ // that both the serializer and deserializer will push objects of this
+ // type onto their m_objectPool. This is important because the order of
+ // the objects in the m_objectPool must match for both the serializer and
+ // deserializer.
+ switch (tag) {
+ case ArrayTag:
+ case ArrayBufferTag:
+ case ArrayBufferViewTag:
+ case BigIntObjectTag:
+ case EmptyStringObjectTag:
+ case FalseObjectTag:
+ case MapObjectTag:
+ case NumberObjectTag:
+ case ObjectTag:
+ case ResizableArrayBufferTag:
+ case SetObjectTag:
+ case SharedArrayBufferTag:
+ case StringObjectTag:
+ case TrueObjectTag:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+
static bool isTypeExposedToGlobalObject(JSC::JSGlobalObject& globalObject, SerializationTag tag)
{
#if ENABLE(WEB_AUDIO)
@@ -435,6 +579,12 @@ enum DestinationColorSpaceTag {
#endif
};
+#define SERIALIZE_TRACE(...) do { \
+ if constexpr (SerializationHelper::verboseTrace) \
+ dataLogLn("TRACE ", __VA_ARGS__, " @ ", __LINE__); \
+ } while (false)
+
+
#if ENABLE(WEBASSEMBLY)
static String agentClusterIDFromGlobalObject(JSGlobalObject& globalObject)
{
@@ -751,9 +901,25 @@ class CloneBase {
m_failed = true;
}
+#if ASSERT_ENABLED
+public:
+ const Vector& objectPoolTags() const { return m_objectPoolTags; }
+
+protected:
+ void appendObjectPoolTag(SerializationTag tag)
+ {
+ m_objectPoolTags.append(tag);
+ }
+#else
+ ALWAYS_INLINE void appendObjectPoolTag(SerializationTag) { }
+#endif
+
JSGlobalObject* const m_lexicalGlobalObject;
bool m_failed;
- MarkedArgumentBuffer m_gcBuffer;
+ MarkedArgumentBuffer m_objectPool;
+#if ASSERT_ENABLED
+ Vector m_objectPoolTags;
+#endif
};
#if ENABLE(WEB_CRYPTO)
@@ -815,7 +981,12 @@ template <> bool writeLittleEndian(Vector& buffer, const uint8
return true;
}
-class CloneSerializer : CloneBase {
+class CloneSerializer;
+#if ASSERT_ENABLED
+static void validateSerializedResult(CloneSerializer&, SerializationReturnCode, Vector& result, JSGlobalObject*, Vector>&, ArrayBufferContentsArray&, ArrayBufferContentsArray& sharedBuffers, Vector>&);
+#endif
+
+class CloneSerializer : public CloneBase {
WTF_FORBID_HEAP_ALLOCATION;
public:
static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector>& messagePorts, Vector>& arrayBuffers, const Vector>& imageBitmaps,
@@ -840,6 +1011,11 @@ class CloneSerializer : CloneBase {
Vector& blobHandles, Vector& out, SerializationContext context, ArrayBufferContentsArray& sharedBuffers,
SerializationForStorage forStorage)
{
+#if ASSERT_ENABLED
+ auto& vm = lexicalGlobalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+#endif
+
CloneSerializer serializer(lexicalGlobalObject, messagePorts, arrayBuffers, imageBitmaps,
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
offscreenCanvases,
@@ -860,7 +1036,12 @@ class CloneSerializer : CloneBase {
wasmMemoryHandles,
#endif
blobHandles, out, context, sharedBuffers, forStorage);
- return serializer.serialize(value);
+ auto code = serializer.serialize(value);
+#if ASSERT_ENABLED
+ RETURN_IF_EXCEPTION(scope, code);
+ validateSerializedResult(serializer, code, out, lexicalGlobalObject, messagePorts, sharedBuffers, sharedBuffers, inMemoryMessagePorts);
+#endif
+ return code;
}
static bool serialize(StringView string, Vector& out)
@@ -879,8 +1060,15 @@ class CloneSerializer : CloneBase {
return writeLittleEndian(out, string.characters16(), string.length());
}
+#if ASSERT_ENABLED
+ bool didSeeComplexCases() const { return m_didSeeComplexCases; }
+ void setDidSeeComplexCases() { m_didSeeComplexCases = true; }
+#else
+ ALWAYS_INLINE void setDidSeeComplexCases() { }
+#endif
+
private:
- typedef HashMap ObjectPool;
+ using ObjectPoolMap = HashMap;
CloneSerializer(JSGlobalObject* lexicalGlobalObject, Vector>& messagePorts, Vector>& arrayBuffers, const Vector>& imageBitmaps,
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
@@ -937,7 +1125,7 @@ class CloneSerializer : CloneBase {
}
template
- void fillTransferMap(const Vector& input, ObjectPool& result)
+ void fillTransferMap(const Vector& input, ObjectPoolMap& result)
{
if (input.isEmpty())
return;
@@ -974,71 +1162,54 @@ class CloneSerializer : CloneBase {
return object->inherits();
}
- bool checkForDuplicate(JSObject* object)
+ template
+ bool writeObjectReferenceIfDupe(JSObject* object)
{
+ static_assert(canBeAddedToObjectPool(tag1)
+ && (canBeAddedToObjectPool(tag2) || tag2 == ErrorTag)
+ && (canBeAddedToObjectPool(tag3) || tag3 == ErrorTag));
+
// Record object for graph reconstruction
- ObjectPool::const_iterator found = m_objectPool.find(object);
+ auto found = m_objectPoolMap.find(object);
// Handle duplicate references
- if (found != m_objectPool.end()) {
+ if (found != m_objectPoolMap.end()) {
write(ObjectReferenceTag);
- ASSERT(found->value < m_objectPool.size());
+ ASSERT(found->value < m_objectPoolMap.size());
writeObjectIndex(found->value);
- return true;
+ return true; // is dupe.
}
-
- return false;
+ return false; // not dupe.
}
- void recordObject(JSObject* object)
+ template
+ bool addToObjectPool(JSObject* object)
{
- m_objectPool.add(object, m_objectPool.size());
- m_gcBuffer.appendWithCrashOnOverflow(object);
- }
+ static_assert(canBeAddedToObjectPool(tag1)
+ && (canBeAddedToObjectPool(tag2) || tag2 == ErrorTag)
+ && (canBeAddedToObjectPool(tag3) || tag3 == ErrorTag));
- bool startObjectInternal(JSObject* object)
- {
- if (checkForDuplicate(object))
- return false;
- recordObject(object);
- return true;
- }
+ m_objectPoolMap.add(object, m_objectPoolMap.size());
+ m_objectPool.appendWithCrashOnOverflow(object);
- bool startObject(JSObject* object)
- {
- if (!startObjectInternal(object))
- return false;
- write(ObjectTag);
- return true;
- }
+ if constexpr (tag2 == ErrorTag)
+ appendObjectPoolTag(tag1);
- bool startArray(JSArray* array)
- {
- if (!startObjectInternal(array))
- return false;
-
- unsigned length = array->length();
- write(ArrayTag);
- write(length);
- return true;
+ return true; // new object added.
}
- bool startSet(JSSet* set)
+ template
+ bool addToObjectPoolIfNotDupe(JSObject* object)
{
- if (!startObjectInternal(set))
- return false;
+ static_assert(canBeAddedToObjectPool(tag1)
+ && (canBeAddedToObjectPool(tag2) || tag2 == ErrorTag)
+ && (canBeAddedToObjectPool(tag3) || tag3 == ErrorTag));
- write(SetObjectTag);
- return true;
- }
-
- bool startMap(JSMap* map)
- {
- if (!startObjectInternal(map))
- return false;
+ if (writeObjectReferenceIfDupe(object))
+ return false; // new object NOT added. It's a dupe.
- write(MapObjectTag);
- return true;
+ addToObjectPool(object);
+ return true; // new object added.
}
void endObject()
@@ -1111,9 +1282,11 @@ class CloneSerializer : CloneBase {
void dumpStringObject(const String& string)
{
- if (string.isEmpty())
+ if (string.isEmpty()) {
+ appendObjectPoolTag(EmptyStringObjectTag);
write(EmptyStringObjectTag);
- else {
+ } else {
+ appendObjectPoolTag(StringObjectTag);
write(StringObjectTag);
write(string);
}
@@ -1216,8 +1389,12 @@ class CloneSerializer : CloneBase {
write(BigInt64ArrayTag);
else if (obj->inherits())
write(BigUint64ArrayTag);
- else
- return false;
+ else {
+ // We need to return true here because the client only checks for the error condition if
+ // the return value is true (same as all the error cases below).
+ code = SerializationReturnCode::DataCloneError;
+ return true;
+ }
if (UNLIKELY(jsCast(obj)->isOutOfBounds())) {
code = SerializationReturnCode::DataCloneError;
@@ -1521,31 +1698,33 @@ class CloneSerializer : CloneBase {
return true;
}
if (auto* booleanObject = jsDynamicCast(obj)) {
- if (!startObjectInternal(booleanObject)) // handle duplicates
+ if (!addToObjectPoolIfNotDupe(booleanObject))
return true;
- write(booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag);
+ auto tag = booleanObject->internalValue().toBoolean(m_lexicalGlobalObject) ? TrueObjectTag : FalseObjectTag;
+ write(tag);
+ appendObjectPoolTag(tag);
return true;
}
if (auto* stringObject = jsDynamicCast(obj)) {
- if (!startObjectInternal(stringObject)) // handle duplicates
+ if (!addToObjectPoolIfNotDupe(stringObject))
return true;
String str = asString(stringObject->internalValue())->value(m_lexicalGlobalObject);
dumpStringObject(str);
return true;
}
if (auto* numberObject = jsDynamicCast(obj)) {
- if (!startObjectInternal(numberObject)) // handle duplicates
+ if (!addToObjectPoolIfNotDupe(numberObject))
return true;
write(NumberObjectTag);
write(numberObject->internalValue().asNumber());
return true;
}
if (auto* bigIntObject = jsDynamicCast(obj)) {
- if (!startObjectInternal(bigIntObject)) // handle duplicates
+ if (!addToObjectPoolIfNotDupe(bigIntObject))
return true;
+ write(BigIntObjectTag);
JSValue bigIntValue = bigIntObject->internalValue();
ASSERT(bigIntValue.isBigInt());
- write(BigIntObjectTag);
dumpBigIntData(bigIntValue);
return true;
}
@@ -1683,7 +1862,7 @@ class CloneSerializer : CloneBase {
write(index->value);
return true;
}
- if (!startObjectInternal(obj)) // handle duplicates
+ if (!addToObjectPoolIfNotDupe(obj))
return true;
if (arrayBuffer->isShared() && m_context == SerializationContext::WorkerPostMessage) {
@@ -1695,6 +1874,7 @@ class CloneSerializer : CloneBase {
uint32_t index = m_sharedBuffers.size();
ArrayBufferContents contents;
if (arrayBuffer->shareWith(contents)) {
+ appendObjectPoolTag(SharedArrayBufferTag);
write(SharedArrayBufferTag);
m_sharedBuffers.append(WTFMove(contents));
write(index);
@@ -1703,6 +1883,7 @@ class CloneSerializer : CloneBase {
}
if (arrayBuffer->isResizableOrGrowableShared()) {
+ appendObjectPoolTag(ResizableArrayBufferTag);
write(ResizableArrayBufferTag);
uint64_t byteLength = arrayBuffer->byteLength();
write(byteLength);
@@ -1712,6 +1893,7 @@ class CloneSerializer : CloneBase {
return true;
}
+ appendObjectPoolTag(ArrayBufferTag);
write(ArrayBufferTag);
uint64_t byteLength = arrayBuffer->byteLength();
write(byteLength);
@@ -1719,10 +1901,14 @@ class CloneSerializer : CloneBase {
return true;
}
if (obj->inherits()) {
- if (checkForDuplicate(obj))
+ // Note: we can't just use addToObjectPoolIfNotDupe() here because the deserializer
+ // expects to deserialize the children before it deserializes the JSArrayBufferView.
+ // We need to make the serializer follow the same serialization order here by doing
+ // this dance with writeObjectReferenceIfDupe() and addToObjectPool().
+ if (writeObjectReferenceIfDupe(obj))
return true;
bool success = dumpArrayBufferView(obj, code);
- recordObject(obj);
+ addToObjectPool(obj);
return success;
}
#if ENABLE(WEB_CRYPTO)
@@ -1894,6 +2080,7 @@ class CloneSerializer : CloneBase {
void write(SerializationTag tag)
{
+ SERIALIZE_TRACE("serialize ", tag);
writeLittleEndian(m_buffer, static_cast(tag));
}
@@ -1986,7 +2173,7 @@ class CloneSerializer : CloneBase {
void writeObjectIndex(unsigned i)
{
- writeConstantPoolIndex(m_objectPool, i);
+ writeConstantPoolIndex(m_objectPoolMap, i);
}
template void writeConstantPoolIndex(const T& constantPool, unsigned i)
@@ -2357,15 +2544,15 @@ class CloneSerializer : CloneBase {
Vector& m_buffer;
Vector& m_blobHandles;
- ObjectPool m_objectPool;
- ObjectPool m_transferredMessagePorts;
- ObjectPool m_transferredArrayBuffers;
- ObjectPool m_transferredImageBitmaps;
+ ObjectPoolMap m_objectPoolMap;
+ ObjectPoolMap m_transferredMessagePorts;
+ ObjectPoolMap m_transferredArrayBuffers;
+ ObjectPoolMap m_transferredImageBitmaps;
#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
- ObjectPool m_transferredOffscreenCanvases;
+ ObjectPoolMap m_transferredOffscreenCanvases;
#endif
#if ENABLE(WEB_RTC)
- ObjectPool m_transferredRTCDataChannels;
+ ObjectPoolMap m_transferredRTCDataChannels;
#endif
typedef HashMap, uint32_t, IdentifierRepHash> StringConstantPool;
StringConstantPool m_constantPool;
@@ -2389,6 +2576,11 @@ class CloneSerializer : CloneBase {
Vector>& m_serializedAudioData;
#endif
SerializationForStorage m_forStorage;
+
+ MarkedArgumentBuffer m_keepAliveBuffer;
+#if ASSERT_ENABLED
+ bool m_didSeeComplexCases { false };
+#endif
};
SerializationReturnCode CloneSerializer::serialize(JSValue in)
@@ -2415,8 +2607,10 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
JSArray* inArray = asArray(inValue);
unsigned length = inArray->length();
- if (!startArray(inArray))
+ if (!addToObjectPoolIfNotDupe(inArray))
break;
+ write(ArrayTag);
+ write(length);
inputObjectStack.append(inArray);
indexStack.append(0);
lengthStack.append(length);
@@ -2475,8 +2669,9 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSObject* inObject = asObject(inValue);
- if (!startObject(inObject))
+ if (!addToObjectPoolIfNotDupe(inObject))
break;
+ write(ObjectTag);
// 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
@@ -2538,11 +2733,11 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSMap* inMap = jsCast(inValue);
- if (!startMap(inMap))
+ if (!addToObjectPoolIfNotDupe(inMap))
break;
+ write(MapObjectTag);
JSMapIterator* iterator = JSMapIterator::create(vm, m_lexicalGlobalObject->mapIteratorStructure(), inMap, IterationKind::Entries);
- m_gcBuffer.appendWithCrashOnOverflow(inMap);
- m_gcBuffer.appendWithCrashOnOverflow(iterator);
+ m_keepAliveBuffer.appendWithCrashOnOverflow(iterator);
mapIteratorStack.append(iterator);
inputObjectStack.append(inMap);
goto mapDataStartVisitEntry;
@@ -2564,7 +2759,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
goto objectStartVisitMember;
}
inValue = key;
- m_gcBuffer.appendWithCrashOnOverflow(value);
+ m_keepAliveBuffer.appendWithCrashOnOverflow(value);
mapIteratorValueStack.append(value);
stateStack.append(MapDataEndVisitKey);
goto stateUnknown;
@@ -2584,11 +2779,11 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
if (inputObjectStack.size() > maximumFilterRecursion)
return SerializationReturnCode::StackOverflowError;
JSSet* inSet = jsCast(inValue);
- if (!startSet(inSet))
+ if (!addToObjectPoolIfNotDupe(inSet))
break;
+ write(SetObjectTag);
JSSetIterator* iterator = JSSetIterator::create(vm, m_lexicalGlobalObject->setIteratorStructure(), inSet, IterationKind::Keys);
- m_gcBuffer.appendWithCrashOnOverflow(inSet);
- m_gcBuffer.appendWithCrashOnOverflow(iterator);
+ m_keepAliveBuffer.appendWithCrashOnOverflow(iterator);
setIteratorStack.append(iterator);
inputObjectStack.append(inSet);
goto setDataStartVisitEntry;
@@ -2647,7 +2842,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in)
return SerializationReturnCode::SuccessfullyCompleted;
}
-class CloneDeserializer : CloneBase {
+class CloneDeserializer : public CloneBase {
WTF_FORBID_HEAP_ALLOCATION;
public:
enum class ShouldAtomize : bool { No, Yes };
@@ -2889,9 +3084,18 @@ class CloneDeserializer : CloneBase {
bool isValid() const { return m_version <= CurrentVersion; }
+ template
+ inline void addToObjectPool(JSValue object)
+ {
+ static_assert(canBeAddedToObjectPool(tag));
+ m_objectPool.appendWithCrashOnOverflow(object);
+ appendObjectPoolTag(tag);
+ }
+
template bool readLittleEndian(T& value)
{
if (m_failed || !readLittleEndian(m_ptr, m_end, value)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return false;
}
@@ -3086,6 +3290,7 @@ class CloneDeserializer : CloneBase {
if (length == StringPoolTag) {
auto index = readStringIndex();
if (!index || *index >= m_constantPool.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return false;
}
@@ -3096,6 +3301,7 @@ class CloneDeserializer : CloneBase {
length &= ~StringDataIs8BitFlag;
String str;
if (!readString(m_ptr, m_end, str, length, is8Bit, shouldAtomize)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return false;
}
@@ -3106,9 +3312,13 @@ class CloneDeserializer : CloneBase {
SerializationTag readTag()
{
- if (m_ptr >= m_end)
+ if (m_ptr >= m_end) {
+ SERIALIZE_TRACE("FAIL deserialize");
return ErrorTag;
- return static_cast(*m_ptr++);
+ }
+ auto tag = static_cast(*m_ptr++);
+ SERIALIZE_TRACE("deserialize ", tag);
+ return tag;
}
bool readArrayBufferViewSubtag(ArrayBufferViewSubtag& tag)
@@ -3956,6 +4166,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_backingStores.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -3975,6 +4186,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_detachedOffscreenCanvases.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -3991,6 +4203,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_inMemoryOffscreenCanvases.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4003,21 +4216,25 @@ class CloneDeserializer : CloneBase {
{
double expires;
if (!read(expires)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
CachedStringRef certificate;
if (!readStringData(certificate)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
CachedStringRef origin;
if (!readStringData(origin)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
CachedStringRef keyedMaterial;
if (!readStringData(keyedMaterial)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4049,6 +4266,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_detachedRTCDataChannels.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4068,6 +4286,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_serializedVideoChunks.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4082,6 +4301,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_serializedVideoFrames.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4096,6 +4316,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_serializedAudioChunks.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4110,6 +4331,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_serializedAudioData.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4131,6 +4353,7 @@ class CloneDeserializer : CloneBase {
RefPtr arrayBuffer;
if (!read(serializationState) || !read(logicalWidth) || !read(logicalHeight) || !read(resolutionScale) || (m_version > 8 && !read(colorSpace)) || !readArrayBufferImpl(arrayBuffer)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4141,6 +4364,7 @@ class CloneDeserializer : CloneBase {
auto buffer = ImageBitmap::createImageBuffer(*executionContext(m_lexicalGlobalObject), logicalSize, RenderingMode::Unaccelerated, colorSpace, resolutionScale);
if (!buffer) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4148,6 +4372,7 @@ class CloneDeserializer : CloneBase {
PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, colorSpace };
auto pixelBuffer = ByteArrayPixelBuffer::tryCreate(format, imageDataSize, arrayBuffer.releaseNonNull());
if (!pixelBuffer) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4185,10 +4410,10 @@ class CloneDeserializer : CloneBase {
#else
JSBigInt* bigInt = JSBigInt::tryCreateZero(m_lexicalGlobalObject->vm());
if (UNLIKELY(!bigInt)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- m_gcBuffer.appendWithCrashOnOverflow(bigInt);
return bigInt;
#endif
}
@@ -4209,6 +4434,7 @@ class CloneDeserializer : CloneBase {
ASSERT(digit64 != 0);
JSBigInt* bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), 1);
if (!bigInt) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4216,10 +4442,10 @@ class CloneDeserializer : CloneBase {
bigInt->setSign(sign);
bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm());
if (!bigInt) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- m_gcBuffer.appendWithCrashOnOverflow(bigInt);
return tryConvertToBigInt32(bigInt);
}
#endif
@@ -4227,6 +4453,7 @@ class CloneDeserializer : CloneBase {
if constexpr (sizeof(JSBigInt::Digit) == sizeof(uint64_t)) {
bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), numberOfUint64Elements);
if (!bigInt) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4240,11 +4467,13 @@ class CloneDeserializer : CloneBase {
ASSERT(sizeof(JSBigInt::Digit) == sizeof(uint32_t));
auto actualBigIntLength = WTF::checkedProduct(numberOfUint64Elements, 2);
if (actualBigIntLength.hasOverflowed()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
bigInt = JSBigInt::tryCreateWithLength(m_lexicalGlobalObject->vm(), actualBigIntLength.value());
if (!bigInt) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4259,10 +4488,10 @@ class CloneDeserializer : CloneBase {
bigInt->setSign(sign);
bigInt = bigInt->tryRightTrim(m_lexicalGlobalObject->vm());
if (!bigInt) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- m_gcBuffer.appendWithCrashOnOverflow(bigInt);
return tryConvertToBigInt32(bigInt);
}
@@ -4270,6 +4499,7 @@ class CloneDeserializer : CloneBase {
{
SerializationTag tag = readTag();
if (!isTypeExposedToGlobalObject(*m_globalObject, tag)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4295,13 +4525,13 @@ class CloneDeserializer : CloneBase {
case FalseObjectTag: {
BooleanObject* obj = BooleanObject::create(m_lexicalGlobalObject->vm(), m_globalObject->booleanObjectStructure());
obj->setInternalValue(m_lexicalGlobalObject->vm(), jsBoolean(false));
- m_gcBuffer.appendWithCrashOnOverflow(obj);
+ addToObjectPool(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);
+ addToObjectPool(obj);
return obj;
}
case DoubleTag: {
@@ -4317,7 +4547,7 @@ class CloneDeserializer : CloneBase {
if (!read(d))
return JSValue();
NumberObject* obj = constructNumber(m_globalObject, jsNumber(d));
- m_gcBuffer.appendWithCrashOnOverflow(obj);
+ addToObjectPool(obj);
return obj;
}
case BigIntObjectTag: {
@@ -4326,7 +4556,7 @@ class CloneDeserializer : CloneBase {
return JSValue();
ASSERT(bigInt.isBigInt());
BigIntObject* obj = BigIntObject::create(m_lexicalGlobalObject->vm(), m_globalObject, bigInt);
- m_gcBuffer.appendWithCrashOnOverflow(obj);
+ addToObjectPool(obj);
return obj;
}
case DateTag: {
@@ -4367,6 +4597,7 @@ class CloneDeserializer : CloneBase {
if (width == ImageDataPoolTag) {
auto index = readImageDataIndex();
if (!index || *index >= m_imageDataPool.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4379,6 +4610,7 @@ class CloneDeserializer : CloneBase {
if (!read(length))
return JSValue();
if (static_cast(m_end - m_ptr) < length) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4392,6 +4624,7 @@ class CloneDeserializer : CloneBase {
}
if (length && (IntSize(width, height).area() * 4) != length) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4401,6 +4634,7 @@ class CloneDeserializer : CloneBase {
auto result = ImageData::createUninitialized(width, height, resultColorSpace);
if (result.hasException()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4441,13 +4675,13 @@ class CloneDeserializer : CloneBase {
if (!readStringData(cachedString))
return JSValue();
StringObject* obj = constructString(m_lexicalGlobalObject->vm(), m_globalObject, cachedString->jsString(m_lexicalGlobalObject));
- m_gcBuffer.appendWithCrashOnOverflow(obj);
+ addToObjectPool(obj);
return obj;
}
case EmptyStringObjectTag: {
VM& vm = m_lexicalGlobalObject->vm();
StringObject* obj = constructString(vm, m_globalObject, jsEmptyString(vm));
- m_gcBuffer.appendWithCrashOnOverflow(obj);
+ addToObjectPool(obj);
return obj;
}
case RegExpTag: {
@@ -4466,48 +4700,56 @@ class CloneDeserializer : CloneBase {
case ErrorInstanceTag: {
SerializableErrorType serializedErrorType;
if (!read(serializedErrorType)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
String message;
if (!readNullableString(message)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
uint32_t line;
if (!read(line)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
uint32_t column;
if (!read(column)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
String sourceURL;
if (!readNullableString(sourceURL)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
String stackString;
if (!readNullableString(stackString)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
return ErrorInstance::create(m_lexicalGlobalObject, WTFMove(message), toErrorType(serializedErrorType), line, column, WTFMove(sourceURL), WTFMove(stackString));
}
case ObjectReferenceTag: {
- auto index = readConstantPoolIndex(m_gcBuffer);
+ auto index = readConstantPoolIndex(m_objectPool);
if (!index) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- return m_gcBuffer.at(*index);
+ return m_objectPool.at(*index);
}
case MessagePortReferenceTag: {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_messagePorts.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4517,6 +4759,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_inMemoryMessagePorts.size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4529,6 +4772,7 @@ class CloneDeserializer : CloneBase {
CachedStringRef agentClusterID;
bool agentClusterIDSuccessfullyRead = readStringData(agentClusterID);
if (!agentClusterIDSuccessfullyRead || agentClusterID->string() != agentClusterIDFromGlobalObject(*m_globalObject)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4536,6 +4780,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || !m_wasmModules || index >= m_wasmModules->size()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4545,7 +4790,6 @@ class CloneDeserializer : CloneBase {
// module to not have been a valid module. Therefore, createStub should
// not throw.
scope.releaseAssertNoException();
- m_gcBuffer.appendWithCrashOnOverflow(result);
return result;
}
case WasmMemoryTag: {
@@ -4553,6 +4797,7 @@ class CloneDeserializer : CloneBase {
CachedStringRef agentClusterID;
bool agentClusterIDSuccessfullyRead = readStringData(agentClusterID);
if (!agentClusterIDSuccessfullyRead || agentClusterID->string() != agentClusterIDFromGlobalObject(*m_globalObject)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4560,6 +4805,7 @@ class CloneDeserializer : CloneBase {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || !m_wasmMemoryHandles || index >= m_wasmMemoryHandles->size() || !JSC::Options::useSharedArrayBuffer()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4576,6 +4822,7 @@ class CloneDeserializer : CloneBase {
auto handler = [&vm, result] (Wasm::Memory::GrowSuccess, PageCount oldPageCount, PageCount newPageCount) { result->growSuccessCallback(vm, oldPageCount, newPageCount); };
if (RefPtr contents = m_wasmMemoryHandles->at(index)) {
if (!contents->memoryHandle()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4586,13 +4833,13 @@ class CloneDeserializer : CloneBase {
}
result->adopt(memory.releaseNonNull());
- m_gcBuffer.appendWithCrashOnOverflow(result);
return result;
}
#endif
case ArrayBufferTag: {
RefPtr arrayBuffer;
if (!readArrayBuffer(arrayBuffer)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4600,16 +4847,18 @@ class CloneDeserializer : CloneBase {
// 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)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
JSValue result = JSArrayBuffer::create(m_lexicalGlobalObject->vm(), structure, WTFMove(arrayBuffer));
- m_gcBuffer.appendWithCrashOnOverflow(result);
+ addToObjectPool(result);
return result;
}
case ResizableArrayBufferTag: {
RefPtr arrayBuffer;
if (!readResizableNonSharedArrayBuffer(arrayBuffer)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4617,17 +4866,19 @@ class CloneDeserializer : CloneBase {
// 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)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
JSValue result = JSArrayBuffer::create(m_lexicalGlobalObject->vm(), structure, WTFMove(arrayBuffer));
- m_gcBuffer.appendWithCrashOnOverflow(result);
+ addToObjectPool(result);
return result;
}
case ArrayBufferTransferTag: {
uint32_t index;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || index >= m_arrayBuffers.size()) {
+ SERIALIZE_TRACE("FAIL deserialize ArrayBufferTransferTag: indexSuccessfullyRead ", indexSuccessfullyRead, " index ", index, " m_arrayBuffers.size() ", m_arrayBuffers.size());
fail();
return JSValue();
}
@@ -4642,6 +4893,7 @@ class CloneDeserializer : CloneBase {
uint32_t index = UINT_MAX;
bool indexSuccessfullyRead = read(index);
if (!indexSuccessfullyRead || !m_sharedBuffers || index >= m_sharedBuffers->size() || !JSC::Options::useSharedArrayBuffer()) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4651,27 +4903,30 @@ class CloneDeserializer : CloneBase {
m_sharedBuffers->at(index).shareWith(arrayBufferContents);
auto buffer = ArrayBuffer::create(WTFMove(arrayBufferContents));
JSValue result = getJSValue(buffer.get());
- m_gcBuffer.appendWithCrashOnOverflow(result);
+ addToObjectPool(result);
return result;
}
case ArrayBufferViewTag: {
JSValue arrayBufferView;
if (!readArrayBufferView(m_lexicalGlobalObject->vm(), arrayBufferView)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- m_gcBuffer.appendWithCrashOnOverflow(arrayBufferView);
+ addToObjectPool(arrayBufferView);
return arrayBufferView;
}
#if ENABLE(WEB_CRYPTO)
case CryptoKeyTag: {
Vector wrappedKey;
if (!read(wrappedKey)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
Vector serializedKey;
if (!unwrapCryptoKey(m_lexicalGlobalObject, wrappedKey, serializedKey)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
@@ -4679,10 +4934,10 @@ class CloneDeserializer : CloneBase {
Vector> dummyMessagePorts;
CloneDeserializer rawKeyDeserializer(m_lexicalGlobalObject, m_globalObject, dummyMessagePorts, nullptr, { }, serializedKey);
if (!rawKeyDeserializer.readCryptoKey(cryptoKey)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
return JSValue();
}
- m_gcBuffer.appendWithCrashOnOverflow(cryptoKey);
return cryptoKey;
}
#endif
@@ -4733,6 +4988,7 @@ class CloneDeserializer : CloneBase {
return readDOMException();
default:
+ SERIALIZE_TRACE("push back ", tag);
m_ptr--; // Push the tag back
return JSValue();
}
@@ -4798,6 +5054,10 @@ class CloneDeserializer : CloneBase {
return i < m_blobURLs.size() ? m_blobFilePaths[i] : String();
}
+
+#if ASSERT_ENABLED
+ friend void validateSerializedResult(CloneSerializer&, SerializationReturnCode, Vector&, JSGlobalObject*, Vector>&, ArrayBufferContentsArray&, ArrayBufferContentsArray&, Vector>&);
+#endif
};
DeserializationResult CloneDeserializer::deserialize()
@@ -4821,13 +5081,16 @@ DeserializationResult CloneDeserializer::deserialize()
case ArrayStartState: {
uint32_t length;
if (!read(length)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
goto error;
}
JSArray* outArray = constructEmptyArray(m_globalObject, static_cast(nullptr), length);
- if (UNLIKELY(scope.exception()))
+ if (UNLIKELY(scope.exception())) {
+ SERIALIZE_TRACE("FAIL deserialize");
goto error;
- m_gcBuffer.appendWithCrashOnOverflow(outArray);
+ }
+ addToObjectPool(outArray);
outputObjectStack.append(outArray);
}
arrayStartVisitMember:
@@ -4835,6 +5098,7 @@ DeserializationResult CloneDeserializer::deserialize()
case ArrayStartVisitMember: {
uint32_t index;
if (!read(index)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
goto error;
}
@@ -4843,6 +5107,7 @@ DeserializationResult CloneDeserializer::deserialize()
if (index == TerminatorTag) {
// We reached the end of the indexed properties section.
if (!read(index)) {
+ SERIALIZE_TRACE("FAIL deserialize");
fail();
goto error;
}
@@ -4888,7 +5153,7 @@ DeserializationResult CloneDeserializer::deserialize()
if (outputObjectStack.size() > maximumFilterRecursion)
return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
JSObject* outObject = constructEmptyObject(m_lexicalGlobalObject, m_globalObject->objectPrototype());
- m_gcBuffer.appendWithCrashOnOverflow(outObject);
+ addToObjectPool(outObject);
outputObjectStack.append(outObject);
}
objectStartVisitMember:
@@ -4897,8 +5162,10 @@ DeserializationResult CloneDeserializer::deserialize()
CachedStringRef cachedString;
bool wasTerminator = false;
if (!readStringData(cachedString, wasTerminator, ShouldAtomize::Yes)) {
- if (!wasTerminator)
+ if (!wasTerminator) {
+ SERIALIZE_TRACE("FAIL deserialize");
goto error;
+ }
JSObject* outObject = outputObjectStack.last();
outValue = outObject;
@@ -4919,11 +5186,13 @@ DeserializationResult CloneDeserializer::deserialize()
propertyNameStack.removeLast();
goto objectStartVisitMember;
}
- mapObjectStartState: {
- if (outputObjectStack.size() > maximumFilterRecursion)
+ mapStartState: {
+ if (outputObjectStack.size() > maximumFilterRecursion) {
+ SERIALIZE_TRACE("FAIL deserialize");
return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
+ }
JSMap* map = JSMap::create(m_lexicalGlobalObject->vm(), m_globalObject->mapStructure());
- m_gcBuffer.appendWithCrashOnOverflow(map);
+ addToObjectPool(map);
outputObjectStack.append(map);
mapStack.append(map);
goto mapDataStartVisitEntry;
@@ -4948,11 +5217,13 @@ DeserializationResult CloneDeserializer::deserialize()
goto mapDataStartVisitEntry;
}
- setObjectStartState: {
- if (outputObjectStack.size() > maximumFilterRecursion)
+ setStartState: {
+ if (outputObjectStack.size() > maximumFilterRecursion) {
+ SERIALIZE_TRACE("FAIL deserialize");
return std::make_pair(JSValue(), SerializationReturnCode::StackOverflowError);
+ }
JSSet* set = JSSet::create(m_lexicalGlobalObject->vm(), m_globalObject->setStructure());
- m_gcBuffer.appendWithCrashOnOverflow(set);
+ addToObjectPool(set);
outputObjectStack.append(set);
setStack.append(set);
goto setDataStartVisitEntry;
@@ -4984,9 +5255,9 @@ DeserializationResult CloneDeserializer::deserialize()
if (tag == ObjectTag)
goto objectStartState;
if (tag == MapObjectTag)
- goto mapObjectStartState;
+ goto mapStartState;
if (tag == SetObjectTag)
- goto setObjectStartState;
+ goto setStartState;
goto error;
}
if (stateStack.isEmpty())
@@ -5003,6 +5274,116 @@ DeserializationResult CloneDeserializer::deserialize()
return std::make_pair(JSValue(), SerializationReturnCode::ValidationError);
}
+#if ASSERT_ENABLED
+void validateSerializedResult(CloneSerializer& serializer, SerializationReturnCode code, Vector& result, JSGlobalObject* lexicalGlobalObject, Vector>& messagePorts, ArrayBufferContentsArray& arrayBufferContentsArray, ArrayBufferContentsArray& sharedBuffers, Vector>& inMemoryMessagePorts)
+{
+ if (!JSC::Options::validateSerializedValue())
+ return;
+ if (serializer.didSeeComplexCases())
+ return;
+ if (code != SerializationReturnCode::SuccessfullyCompleted)
+ return;
+
+ SERIALIZE_TRACE("validation start");
+
+ VM& vm = lexicalGlobalObject->vm();
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ JSGlobalObject* globalObject = lexicalGlobalObject;
+ Vector blobURLs;
+ Vector blobFilePaths;
+ Vector> backingStores;
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ Vector> detachedOffscreenCanvases;
+ Vector> inMemoryOffscreenCanvases;
+#endif
+#if ENABLE(WEB_RTC)
+ Vector> detachedRTCDataChannels;
+#endif
+#if ENABLE(WEBASSEMBLY)
+ WasmModuleArray* wasmModules = nullptr;
+ WasmMemoryHandleArray* wasmMemoryHandles = nullptr;
+#endif
+#if ENABLE(WEB_CODECS)
+ Vector> serializedVideoChunks;
+ Vector serializedVideoFrames;
+ Vector> serializedAudioChunks;
+ Vector serializedAudioData;
+#endif
+
+ CloneDeserializer deserializer(lexicalGlobalObject, globalObject, messagePorts, &arrayBufferContentsArray, result, blobURLs, blobFilePaths, &sharedBuffers, WTFMove(backingStores)
+#if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS)
+ , WTFMove(detachedOffscreenCanvases)
+ , inMemoryOffscreenCanvases
+#endif
+ , inMemoryMessagePorts
+#if ENABLE(WEB_RTC)
+ , WTFMove(detachedRTCDataChannels)
+#endif
+#if ENABLE(WEBASSEMBLY)
+ , wasmModules
+ , wasmMemoryHandles
+#endif
+#if ENABLE(WEB_CODECS)
+ , WTFMove(serializedVideoChunks)
+ , WTFMove(serializedVideoFrames)
+ , WTFMove(serializedAudioChunks)
+ , WTFMove(serializedAudioData)
+#endif
+ );
+ RELEASE_ASSERT(deserializer.isValid());
+ auto deserializationResult = deserializer.deserialize();
+ if (scope.exception()) {
+ scope.clearException();
+ SERIALIZE_TRACE("validation abort due to exception");
+ return;
+ }
+
+ if (deserializationResult.second != SerializationReturnCode::SuccessfullyCompleted) {
+ SERIALIZE_TRACE("validation abort due to deserialization failure");
+ return;
+ }
+
+ unsigned numChecks = 0;
+ unsigned numMismatches = 0;
+#define VALIDATE_EQ(a, b, ...) do { \
+ if ((a) != (b)) \
+ numMismatches++; \
+ } while (false)
+
+ auto& serializerTags = serializer.objectPoolTags();
+ auto& deserializerTags = deserializer.objectPoolTags();
+ VALIDATE_EQ(serializerTags.size(), deserializerTags.size(), "");
+ size_t commonMinSize = std::min(serializerTags.size(), deserializerTags.size());
+ size_t maxSize = std::max(serializerTags.size(), deserializerTags.size());
+ for (size_t i = 0; i < commonMinSize; ++i)
+ VALIDATE_EQ(serializerTags[i], deserializerTags[i], " at i ", i);
+ numChecks = 1 + maxSize;
+ if (commonMinSize != maxSize)
+ numMismatches += maxSize - commonMinSize;
+
+ if (numMismatches) {
+ dataLogLn("\n\nERROR: serialization / deserialization mismatch:");
+ dataLogLn("\n");
+ dataLogLn(" # of serializerTags = ", serializerTags.size());
+ dataLogLn(" # of deserializerTags = ", deserializerTags.size());
+ dataLogLn(" FOUND ", numMismatches, " mismatches out of ", numChecks, " checks");
+
+ for (size_t i = 0; i < commonMinSize; ++i)
+ dataLogLn(" serializerTags[", i, "] (", serializerTags[i], ") deserializerTags[", i, "] (", deserializerTags[i], ")", serializerTags[i] == deserializerTags[i] ? "" : " DIFFERENT");
+ for (size_t i = commonMinSize; i < serializerTags.size(); ++i)
+ dataLogLn(" serializerTags[", i, "] (", serializerTags[i], ") DIFFERENT");
+ for (size_t i = commonMinSize; i < deserializerTags.size(); ++i)
+ dataLogLn(" deserializerTags[", i, "] (", deserializerTags[i], ") DIFFERENT");
+ dataLogLn("\n\n");
+ }
+#undef VALIDATE_EQ
+ RELEASE_ASSERT(!numMismatches);
+
+ SERIALIZE_TRACE("validation done");
+}
+#endif // ASSERT_ENABLED
+
SerializedScriptValue::~SerializedScriptValue() = default;
SerializedScriptValue::SerializedScriptValue(Vector&& buffer, std::unique_ptr&& arrayBufferContentsArray