mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 15:55:07 +00:00
LibWeb: Support [de]serialization for {Map, Set, Object, Array} objects
This commit is contained in:
parent
4e89ec7fd5
commit
20734ac335
8 changed files with 447 additions and 190 deletions
|
@ -8,5 +8,10 @@ This is a String object
|
||||||
/abc/gimsuy
|
/abc/gimsuy
|
||||||
Error
|
Error
|
||||||
URIError: hello
|
URIError: hello
|
||||||
|
{"1":2,"a":"b"}
|
||||||
|
1,4,aaaa
|
||||||
|
true
|
||||||
|
1
|
||||||
|
true
|
||||||
[object ArrayBuffer]
|
[object ArrayBuffer]
|
||||||
ERROR: DataCloneError: Cannot serialize Symbol
|
ERROR: DataCloneError: Cannot serialize Symbol
|
|
@ -8,7 +8,7 @@ originParsedBeforeSerializeError.message: Invalid URL for targetOrigin: 'aaaa'
|
||||||
originParsedBeforeSerializeError.constructor === window.DOMException: true
|
originParsedBeforeSerializeError.constructor === window.DOMException: true
|
||||||
serializeError instanceof DOMException: true
|
serializeError instanceof DOMException: true
|
||||||
serializeError.name: DataCloneError
|
serializeError.name: DataCloneError
|
||||||
serializeError.message: Unsupported type
|
serializeError.message: Cannot serialize platform objects
|
||||||
serializeError.constructor === window.DOMException: true
|
serializeError.constructor === window.DOMException: true
|
||||||
originIframeError instanceof DOMException: false
|
originIframeError instanceof DOMException: false
|
||||||
originIframeError instanceof iframe.contentWindow.DOMException: true
|
originIframeError instanceof iframe.contentWindow.DOMException: true
|
||||||
|
@ -25,7 +25,7 @@ originParsedBeforeSerializeIframeError.constructor === iframe.contentWindow.DOME
|
||||||
serializeIframeError instanceof DOMException: false
|
serializeIframeError instanceof DOMException: false
|
||||||
serializeIframeError instanceof iframe.contentWindow.DOMException: true
|
serializeIframeError instanceof iframe.contentWindow.DOMException: true
|
||||||
serializeIframeError.name: DataCloneError
|
serializeIframeError.name: DataCloneError
|
||||||
serializeIframeError.message: Unsupported type
|
serializeIframeError.message: Cannot serialize platform objects
|
||||||
serializeIframeError.constructor === DOMException: false
|
serializeIframeError.constructor === DOMException: false
|
||||||
serializeIframeError.constructor === iframe.contentWindow.DOMException: true
|
serializeIframeError.constructor === iframe.contentWindow.DOMException: true
|
||||||
Message 1 data: undefined
|
Message 1 data: undefined
|
||||||
|
|
|
@ -11,6 +11,15 @@
|
||||||
println(structuredClone(/abc/gimsuy));
|
println(structuredClone(/abc/gimsuy));
|
||||||
println(structuredClone(new Error()));
|
println(structuredClone(new Error()));
|
||||||
println(structuredClone(new URIError("hello")));
|
println(structuredClone(new URIError("hello")));
|
||||||
|
println(structuredClone(JSON.stringify({"a": "b", 1: 2})));
|
||||||
|
println(structuredClone([1, 4, "aaaa"]));
|
||||||
|
println(structuredClone(new Set(["a", "b", "c"])).has("b"));
|
||||||
|
println(structuredClone(new Map([["a", 0], ["b", 1], ["c", 2]])).get("b"));
|
||||||
|
|
||||||
|
const obj = {"a": 1, "c": 3};
|
||||||
|
obj["b"] = obj;
|
||||||
|
const result = structuredClone(obj);
|
||||||
|
println(result === result["b"]);
|
||||||
|
|
||||||
{
|
{
|
||||||
let arrayBuffer = new ArrayBuffer(6);
|
let arrayBuffer = new ArrayBuffer(6);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
|
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
|
||||||
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
||||||
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
|
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||||
|
* Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -10,15 +11,18 @@
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
|
#include <LibJS/Runtime/Array.h>
|
||||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||||
#include <LibJS/Runtime/BigInt.h>
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
#include <LibJS/Runtime/BigIntObject.h>
|
#include <LibJS/Runtime/BigIntObject.h>
|
||||||
#include <LibJS/Runtime/BooleanObject.h>
|
#include <LibJS/Runtime/BooleanObject.h>
|
||||||
#include <LibJS/Runtime/DataView.h>
|
#include <LibJS/Runtime/DataView.h>
|
||||||
#include <LibJS/Runtime/Date.h>
|
#include <LibJS/Runtime/Date.h>
|
||||||
|
#include <LibJS/Runtime/Map.h>
|
||||||
#include <LibJS/Runtime/NumberObject.h>
|
#include <LibJS/Runtime/NumberObject.h>
|
||||||
#include <LibJS/Runtime/PrimitiveString.h>
|
#include <LibJS/Runtime/PrimitiveString.h>
|
||||||
#include <LibJS/Runtime/RegExpObject.h>
|
#include <LibJS/Runtime/RegExpObject.h>
|
||||||
|
#include <LibJS/Runtime/Set.h>
|
||||||
#include <LibJS/Runtime/StringObject.h>
|
#include <LibJS/Runtime/StringObject.h>
|
||||||
#include <LibJS/Runtime/TypedArray.h>
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
#include <LibJS/Runtime/VM.h>
|
#include <LibJS/Runtime/VM.h>
|
||||||
|
@ -28,8 +32,6 @@
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<JS::Value> structured_deserialize_impl(JS::VM& vm, ReadonlySpan<u32> serialized, JS::Realm& target_realm, SerializationMemory& memory);
|
|
||||||
|
|
||||||
// Binary format:
|
// Binary format:
|
||||||
// A list of adjacent shallow values, which may contain references to other
|
// A list of adjacent shallow values, which may contain references to other
|
||||||
// values (noted by their position in the list, one value following another).
|
// values (noted by their position in the list, one value following another).
|
||||||
|
@ -85,8 +87,18 @@ enum ValueTag {
|
||||||
|
|
||||||
ArrayBufferView,
|
ArrayBufferView,
|
||||||
|
|
||||||
|
MapObject,
|
||||||
|
|
||||||
|
SetObject,
|
||||||
|
|
||||||
ErrorObject,
|
ErrorObject,
|
||||||
|
|
||||||
|
ArrayObject,
|
||||||
|
|
||||||
|
Object,
|
||||||
|
|
||||||
|
ObjectReference,
|
||||||
|
|
||||||
// TODO: Define many more types
|
// TODO: Define many more types
|
||||||
|
|
||||||
// This tag or higher are understood to be errors
|
// This tag or higher are understood to be errors
|
||||||
|
@ -128,14 +140,13 @@ public:
|
||||||
WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value value)
|
WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value value)
|
||||||
{
|
{
|
||||||
// 2. If memory[value] exists, then return memory[value].
|
// 2. If memory[value] exists, then return memory[value].
|
||||||
// FIXME: Do callers actually need a copy? or can they get away with a range?
|
|
||||||
if (m_memory.contains(value)) {
|
if (m_memory.contains(value)) {
|
||||||
auto range = m_memory.get(value).value();
|
auto index = m_memory.get(value).value();
|
||||||
return m_serialized.span().slice(range.start, range.end);
|
return Vector<u32> { ValueTag::ObjectReference, index };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Let deep be false.
|
// 3. Let deep be false.
|
||||||
[[maybe_unused]] bool deep = false;
|
auto deep = false;
|
||||||
|
|
||||||
bool return_primitive_type = true;
|
bool return_primitive_type = true;
|
||||||
// 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
|
// 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
|
||||||
|
@ -169,9 +180,6 @@ public:
|
||||||
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"_fly_string);
|
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"_fly_string);
|
||||||
|
|
||||||
// 6. Let serialized be an uninitialized value.
|
// 6. Let serialized be an uninitialized value.
|
||||||
// NOTE: We use the range of the soon-to-be-serialized value in our serialized data buffer
|
|
||||||
// to be the `serialized` spec value.
|
|
||||||
auto serialized_start = m_serialized.size();
|
|
||||||
|
|
||||||
// 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
|
// 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
|
||||||
if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
|
if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
|
||||||
|
@ -235,6 +243,22 @@ public:
|
||||||
TRY(serialize_viewed_array_buffer(m_serialized, static_cast<JS::DataView&>(value.as_object())));
|
TRY(serialize_viewed_array_buffer(m_serialized, static_cast<JS::DataView&>(value.as_object())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 15. Otherwise, if value has [[MapData]] internal slot, then:
|
||||||
|
else if (value.is_object() && is<JS::Map>(value.as_object())) {
|
||||||
|
// 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
|
||||||
|
m_serialized.append(ValueTag::MapObject);
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16. Otherwise, if value has [[SetData]] internal slot, then:
|
||||||
|
else if (value.is_object() && is<JS::Set>(value.as_object())) {
|
||||||
|
// 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
|
||||||
|
m_serialized.append(ValueTag::SetObject);
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
}
|
||||||
|
|
||||||
// 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
|
// 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
|
||||||
else if (value.is_object() && is<JS::Error>(value.as_object()) && !is<Bindings::PlatformObject>(value.as_object())) {
|
else if (value.is_object() && is<JS::Error>(value.as_object()) && !is<Bindings::PlatformObject>(value.as_object())) {
|
||||||
// 1. Let name be ? Get(value, "name").
|
// 1. Let name be ? Get(value, "name").
|
||||||
|
@ -264,6 +288,23 @@ public:
|
||||||
TRY(serialize_string(m_serialized, *message));
|
TRY(serialize_string(m_serialized, *message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 18. Otherwise, if value is an Array exotic object, then:
|
||||||
|
else if (value.is_object() && is<JS::Array>(value.as_object())) {
|
||||||
|
// 1. Let valueLenDescriptor be ? OrdinaryGetOwnProperty(value, "length").
|
||||||
|
// 2. Let valueLen be valueLenDescriptor.[[Value]].
|
||||||
|
// NON-STANDARD: Array objects in LibJS do not have a real length property, so it must be accessed the usual way
|
||||||
|
u64 length = MUST(JS::length_of_array_like(m_vm, value.as_object()));
|
||||||
|
|
||||||
|
// 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }.
|
||||||
|
m_serialized.append(ValueTag::ArrayObject);
|
||||||
|
m_serialized.append(bit_cast<u32*>(&length), 2);
|
||||||
|
|
||||||
|
// 4. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: 19. Otherwise, if value is a platform object that is a serializable object:
|
||||||
|
|
||||||
// 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
|
// 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
|
||||||
else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object())) {
|
else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object())) {
|
||||||
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize platform objects"_fly_string));
|
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize platform objects"_fly_string));
|
||||||
|
@ -278,23 +319,106 @@ public:
|
||||||
|
|
||||||
// FIXME: 23. Otherwise, if value is an exotic object and value is not the %Object.prototype% intrinsic object associated with any realm, then throw a "DataCloneError" DOMException.
|
// FIXME: 23. Otherwise, if value is an exotic object and value is not the %Object.prototype% intrinsic object associated with any realm, then throw a "DataCloneError" DOMException.
|
||||||
|
|
||||||
// 15, 16, 18, 19, 24: FIXME: Serialize other data types
|
// 24. Otherwise:
|
||||||
else {
|
else {
|
||||||
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string));
|
// 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
|
||||||
|
m_serialized.append(ValueTag::Object);
|
||||||
|
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 25. Set memory[value] to serialized.
|
// 25. Set memory[value] to serialized.
|
||||||
auto serialized_end = m_serialized.size();
|
m_memory.set(make_handle(value), m_next_id++);
|
||||||
m_memory.set(make_handle(value), { serialized_start, serialized_end });
|
|
||||||
|
|
||||||
// Second pass: Update the objects to point to other objects in memory
|
// 26. If deep is true, then:
|
||||||
|
if (deep) {
|
||||||
|
// 1. If value has a [[MapData]] internal slot, then:
|
||||||
|
if (value.is_object() && is<JS::Map>(value.as_object())) {
|
||||||
|
auto const& map = static_cast<JS::Map const&>(value.as_object());
|
||||||
|
// 1. Let copiedList be a new empty List.
|
||||||
|
Vector<JS::Value> copied_list;
|
||||||
|
copied_list.ensure_capacity(map.map_size() * 2);
|
||||||
|
// 2. For each Record { [[Key]], [[Value]] } entry of value.[[MapData]]:
|
||||||
|
for (auto const& entry : static_cast<JS::Map const&>(value.as_object())) {
|
||||||
|
// 1. Let copiedEntry be a new Record { [[Key]]: entry.[[Key]], [[Value]]: entry.[[Value]] }.
|
||||||
|
// 2. If copiedEntry.[[Key]] is not the special value empty, append copiedEntry to copiedList.
|
||||||
|
copied_list.append(entry.key);
|
||||||
|
copied_list.append(entry.value);
|
||||||
|
}
|
||||||
|
u64 size = map.map_size();
|
||||||
|
m_serialized.append(bit_cast<u32*>(&size), 2);
|
||||||
|
// 3. For each Record { [[Key]], [[Value]] } entry of copiedList:
|
||||||
|
for (auto copied_value : copied_list) {
|
||||||
|
// 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory).
|
||||||
|
// 2. Let serializedValue be ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory).
|
||||||
|
auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
|
||||||
|
|
||||||
|
// 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
|
||||||
|
m_serialized.extend(serialized_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Otherwise, if value has a [[SetData]] internal slot, then:
|
||||||
|
else if (value.is_object() && is<JS::Set>(value.as_object())) {
|
||||||
|
auto const& set = static_cast<JS::Set const&>(value.as_object());
|
||||||
|
// 1. Let copiedList be a new empty List.
|
||||||
|
Vector<JS::Value> copied_list;
|
||||||
|
copied_list.ensure_capacity(set.set_size());
|
||||||
|
// 2. For each entry of value.[[SetData]]:
|
||||||
|
for (auto const& entry : static_cast<JS::Set const&>(value.as_object())) {
|
||||||
|
// 1. If entry is not the special value empty, append entry to copiedList.
|
||||||
|
copied_list.append(entry.key);
|
||||||
|
}
|
||||||
|
u64 size = set.set_size();
|
||||||
|
m_serialized.append(bit_cast<u32*>(&size), 2);
|
||||||
|
// 3. For each entry of copiedList:
|
||||||
|
for (auto copied_value : copied_list) {
|
||||||
|
// 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory).
|
||||||
|
auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
|
||||||
|
|
||||||
|
// 2. Append serializedEntry to serialized.[[SetData]].
|
||||||
|
m_serialized.extend(serialized_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: 3. Otherwise, if value is a platform object that is a serializable object, then perform the serialization steps for value's primary interface, given value, serialized, and forStorage.
|
||||||
|
|
||||||
|
// 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
|
||||||
|
else {
|
||||||
|
u64 property_count = 0;
|
||||||
|
auto count_offset = m_serialized.size();
|
||||||
|
m_serialized.append(bit_cast<u32*>(&property_count), 2);
|
||||||
|
for (auto key : MUST(value.as_object().enumerable_own_property_names(JS::Object::PropertyKind::Key))) {
|
||||||
|
auto property_key = MUST(JS::PropertyKey::from_value(m_vm, key));
|
||||||
|
|
||||||
|
// 1. If ! HasOwnProperty(value, key) is true, then:
|
||||||
|
if (MUST(value.as_object().has_own_property(property_key))) {
|
||||||
|
// 1. Let inputValue be ? value.[[Get]](key, value).
|
||||||
|
auto input_value = TRY(value.as_object().internal_get(property_key, value));
|
||||||
|
|
||||||
|
// 2. Let outputValue be ? StructuredSerializeInternal(inputValue, forStorage, memory).
|
||||||
|
auto output_value = TRY(structured_serialize_internal(m_vm, input_value, m_for_storage, m_memory));
|
||||||
|
|
||||||
|
// 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]].
|
||||||
|
TRY(serialize_string(m_serialized, key.as_string()));
|
||||||
|
m_serialized.extend(output_value);
|
||||||
|
|
||||||
|
property_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(m_serialized.data() + count_offset, &property_count, sizeof(property_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 27. Return serialized.
|
||||||
return m_serialized;
|
return m_serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JS::VM& m_vm;
|
JS::VM& m_vm;
|
||||||
SerializationMemory& m_memory; // JS value -> index
|
SerializationMemory& m_memory; // JS value -> index
|
||||||
|
u32 m_next_id { 0 };
|
||||||
SerializationRecord m_serialized;
|
SerializationRecord m_serialized;
|
||||||
bool m_for_storage { false };
|
bool m_for_storage { false };
|
||||||
|
|
||||||
|
@ -408,8 +532,6 @@ private:
|
||||||
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
|
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
|
||||||
if constexpr (IsSame<ViewType, JS::DataView>) {
|
if constexpr (IsSame<ViewType, JS::DataView>) {
|
||||||
vector.append(ValueTag::ArrayBufferView);
|
vector.append(ValueTag::ArrayBufferView);
|
||||||
u64 const serialized_buffer_size = buffer_serialized.size();
|
|
||||||
vector.append(bit_cast<u32*>(&serialized_buffer_size), 2);
|
|
||||||
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
||||||
TRY(serialize_string(vector, "DataView"_string)); // [[Constructor]]
|
TRY(serialize_string(vector, "DataView"_string)); // [[Constructor]]
|
||||||
vector.append(view.byte_length());
|
vector.append(view.byte_length());
|
||||||
|
@ -424,8 +546,6 @@ private:
|
||||||
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
|
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
|
||||||
// [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
|
// [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
|
||||||
vector.append(ValueTag::ArrayBufferView);
|
vector.append(ValueTag::ArrayBufferView);
|
||||||
u64 const serialized_buffer_size = buffer_serialized.size();
|
|
||||||
vector.append(bit_cast<u32*>(&serialized_buffer_size), 2);
|
|
||||||
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
||||||
TRY(serialize_string(vector, view.element_name())); // [[Constructor]]
|
TRY(serialize_string(vector, view.element_name())); // [[Constructor]]
|
||||||
vector.append(view.byte_length());
|
vector.append(view.byte_length());
|
||||||
|
@ -438,116 +558,145 @@ private:
|
||||||
|
|
||||||
class Deserializer {
|
class Deserializer {
|
||||||
public:
|
public:
|
||||||
Deserializer(JS::VM& vm, JS::Realm& target_realm, ReadonlySpan<u32> v, SerializationMemory& serialization_memory)
|
Deserializer(JS::VM& vm, JS::Realm& target_realm, ReadonlySpan<u32> serialized, DeserializationMemory& memory)
|
||||||
: m_vm(vm)
|
: m_vm(vm)
|
||||||
, m_vector(v)
|
, m_serialized(serialized)
|
||||||
, m_memory(target_realm.heap())
|
, m_memory(memory)
|
||||||
, m_serialization_memory(serialization_memory)
|
|
||||||
{
|
{
|
||||||
VERIFY(vm.current_realm() == &target_realm);
|
VERIFY(vm.current_realm() == &target_realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> deserialize()
|
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
|
||||||
|
WebIDL::ExceptionOr<JS::Value> deserialize()
|
||||||
{
|
{
|
||||||
// First pass: fill up the memory with new values
|
auto tag = m_serialized[m_position++];
|
||||||
u32 position = 0;
|
|
||||||
while (position < m_vector.size()) {
|
// 2. If memory[serialized] exists, then return memory[serialized].
|
||||||
switch (m_vector[position++]) {
|
if (tag == ValueTag::ObjectReference) {
|
||||||
|
auto index = m_serialized[m_position++];
|
||||||
|
return m_memory[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Let deep be false.
|
||||||
|
auto deep = false;
|
||||||
|
|
||||||
|
// 4. Let value be an uninitialized value.
|
||||||
|
JS::Value value;
|
||||||
|
|
||||||
|
auto is_primitive = false;
|
||||||
|
switch (tag) {
|
||||||
|
// 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]].
|
||||||
case ValueTag::UndefinedPrimitive: {
|
case ValueTag::UndefinedPrimitive: {
|
||||||
m_memory.append(JS::js_undefined());
|
value = JS::js_undefined();
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueTag::NullPrimitive: {
|
case ValueTag::NullPrimitive: {
|
||||||
m_memory.append(JS::js_null());
|
value = JS::js_null();
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueTag::BooleanPrimitive: {
|
case ValueTag::BooleanPrimitive: {
|
||||||
m_memory.append(JS::Value(static_cast<bool>(m_vector[position++])));
|
value = JS::Value(static_cast<bool>(m_serialized[m_position++]));
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueTag::NumberPrimitive: {
|
case ValueTag::NumberPrimitive: {
|
||||||
u32 bits[2];
|
u32 bits[2] = {};
|
||||||
bits[0] = m_vector[position++];
|
bits[0] = m_serialized[m_position++];
|
||||||
bits[1] = m_vector[position++];
|
bits[1] = m_serialized[m_position++];
|
||||||
double value = *bit_cast<double*>(&bits);
|
auto double_value = *bit_cast<double*>(&bits);
|
||||||
m_memory.append(JS::Value(value));
|
value = JS::Value(double_value);
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueTag::BigIntPrimitive: {
|
case ValueTag::BigIntPrimitive: {
|
||||||
auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
|
auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position));
|
||||||
m_memory.append(JS::Value { big_int });
|
value = JS::Value { big_int };
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueTag::StringPrimitive: {
|
case ValueTag::StringPrimitive: {
|
||||||
auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
|
auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
|
||||||
m_memory.append(JS::Value { string });
|
value = JS::Value { string };
|
||||||
|
is_primitive = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 6. Otherwise, if serialized.[[Type]] is "Boolean", then set value to a new Boolean object in targetRealm whose [[BooleanData]] internal slot value is serialized.[[BooleanData]].
|
||||||
case BooleanObject: {
|
case BooleanObject: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
bool const value = static_cast<bool>(m_vector[position++]);
|
auto bool_value = static_cast<bool>(m_serialized[m_position++]);
|
||||||
m_memory.append(JS::BooleanObject::create(*realm, value));
|
value = JS::BooleanObject::create(*realm, bool_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 7. Otherwise, if serialized.[[Type]] is "Number", then set value to a new Number object in targetRealm whose [[NumberData]] internal slot value is serialized.[[NumberData]].
|
||||||
case ValueTag::NumberObject: {
|
case ValueTag::NumberObject: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
u32 bits[2];
|
u32 bits[2];
|
||||||
bits[0] = m_vector[position++];
|
bits[0] = m_serialized[m_position++];
|
||||||
bits[1] = m_vector[position++];
|
bits[1] = m_serialized[m_position++];
|
||||||
double const value = *bit_cast<double*>(&bits);
|
auto double_value = *bit_cast<double*>(&bits);
|
||||||
m_memory.append(JS::NumberObject::create(*realm, value));
|
value = JS::NumberObject::create(*realm, double_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 8. Otherwise, if serialized.[[Type]] is "BigInt", then set value to a new BigInt object in targetRealm whose [[BigIntData]] internal slot value is serialized.[[BigIntData]].
|
||||||
case ValueTag::BigIntObject: {
|
case ValueTag::BigIntObject: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
|
auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position));
|
||||||
m_memory.append(JS::BigIntObject::create(*realm, big_int));
|
value = JS::BigIntObject::create(*realm, big_int);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 9. Otherwise, if serialized.[[Type]] is "String", then set value to a new String object in targetRealm whose [[StringData]] internal slot value is serialized.[[StringData]].
|
||||||
case ValueTag::StringObject: {
|
case ValueTag::StringObject: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
|
auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
|
||||||
m_memory.append(JS::StringObject::create(*realm, string, realm->intrinsics().string_prototype()));
|
value = JS::StringObject::create(*realm, string, realm->intrinsics().string_prototype());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 10. Otherwise, if serialized.[[Type]] is "Date", then set value to a new Date object in targetRealm whose [[DateValue]] internal slot value is serialized.[[DateValue]].
|
||||||
case ValueTag::DateObject: {
|
case ValueTag::DateObject: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
u32 bits[2];
|
u32 bits[2];
|
||||||
bits[0] = m_vector[position++];
|
bits[0] = m_serialized[m_position++];
|
||||||
bits[1] = m_vector[position++];
|
bits[1] = m_serialized[m_position++];
|
||||||
double const value = *bit_cast<double*>(&bits);
|
auto double_value = *bit_cast<double*>(&bits);
|
||||||
m_memory.append(JS::Date::create(*realm, value));
|
value = JS::Date::create(*realm, double_value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]],
|
||||||
|
// whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]].
|
||||||
case ValueTag::RegExpObject: {
|
case ValueTag::RegExpObject: {
|
||||||
auto pattern = TRY(deserialize_string_primitive(m_vm, m_vector, position));
|
auto pattern = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
|
||||||
auto flags = TRY(deserialize_string_primitive(m_vm, m_vector, position));
|
auto flags = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
|
||||||
m_memory.append(TRY(JS::regexp_create(m_vm, move(pattern), move(flags))));
|
value = TRY(JS::regexp_create(m_vm, move(pattern), move(flags)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
|
||||||
|
// FIXME: 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:
|
||||||
|
// 14. Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
|
||||||
case ValueTag::ArrayBuffer: {
|
case ValueTag::ArrayBuffer: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
auto bytes = TRY(deserialize_bytes(m_vm, m_vector, position));
|
// If this throws an exception, catch it, and then throw a "DataCloneError" DOMException.
|
||||||
m_memory.append(JS::ArrayBuffer::create(*realm, move(bytes)));
|
auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position);
|
||||||
|
if (bytes_or_error.is_error())
|
||||||
|
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "out of memory"_fly_string);
|
||||||
|
value = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// FIXME: 15. Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal slot value is a serialized.[[ArrayBufferMaxByteLength]].
|
||||||
|
// 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:
|
||||||
case ValueTag::ArrayBufferView: {
|
case ValueTag::ArrayBufferView: {
|
||||||
auto* realm = m_vm.current_realm();
|
auto* realm = m_vm.current_realm();
|
||||||
u32 size_bits[2];
|
auto array_buffer_value = TRY(deserialize());
|
||||||
size_bits[0] = m_vector[position++];
|
|
||||||
size_bits[1] = m_vector[position++];
|
|
||||||
u64 const array_buffer_size = *bit_cast<u64*>(&size_bits);
|
|
||||||
auto array_buffer_value = TRY(structured_deserialize_impl(m_vm, m_vector.slice(position, array_buffer_size), *realm, m_serialization_memory));
|
|
||||||
auto& array_buffer = verify_cast<JS::ArrayBuffer>(array_buffer_value.as_object());
|
auto& array_buffer = verify_cast<JS::ArrayBuffer>(array_buffer_value.as_object());
|
||||||
position += array_buffer_size;
|
auto constructor_name = TRY(deserialize_string(m_vm, m_serialized, m_position));
|
||||||
auto constructor_name = TRY(deserialize_string(m_vm, m_vector, position));
|
u32 byte_length = m_serialized[m_position++];
|
||||||
u32 byte_length = m_vector[position++];
|
u32 byte_offset = m_serialized[m_position++];
|
||||||
u32 byte_offset = m_vector[position++];
|
|
||||||
|
|
||||||
if (constructor_name == "DataView"sv) {
|
if (constructor_name == "DataView"sv) {
|
||||||
m_memory.append(JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset));
|
value = JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset);
|
||||||
} else {
|
} else {
|
||||||
u32 array_length = m_vector[position++];
|
u32 array_length = m_serialized[m_position++];
|
||||||
JS::GCPtr<JS::TypedArrayBase> typed_array_ptr;
|
JS::GCPtr<JS::TypedArrayBase> typed_array_ptr;
|
||||||
#define CREATE_TYPED_ARRAY(ClassName) \
|
#define CREATE_TYPED_ARRAY(ClassName) \
|
||||||
if (constructor_name == #ClassName##sv) \
|
if (constructor_name == #ClassName##sv) \
|
||||||
|
@ -560,23 +709,62 @@ public:
|
||||||
VERIFY(typed_array_ptr != nullptr); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
|
VERIFY(typed_array_ptr != nullptr); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
|
||||||
typed_array_ptr->set_byte_length(byte_length);
|
typed_array_ptr->set_byte_length(byte_length);
|
||||||
typed_array_ptr->set_byte_offset(byte_offset);
|
typed_array_ptr->set_byte_offset(byte_offset);
|
||||||
m_memory.append(typed_array_ptr);
|
value = typed_array_ptr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 17. Otherwise, if serialized.[[Type]] is "Map", then:
|
||||||
|
case ValueTag::MapObject: {
|
||||||
|
auto& realm = *m_vm.current_realm();
|
||||||
|
// 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.
|
||||||
|
value = JS::Map::create(realm);
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 18. Otherwise, if serialized.[[Type]] is "Set", then:
|
||||||
|
case ValueTag::SetObject: {
|
||||||
|
auto& realm = *m_vm.current_realm();
|
||||||
|
// 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List.
|
||||||
|
value = JS::Set::create(realm);
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 19. Otherwise, if serialized.[[Type]] is "Array", then:
|
||||||
|
case ValueTag::ArrayObject: {
|
||||||
|
auto& realm = *m_vm.current_realm();
|
||||||
|
// 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]].
|
||||||
|
// 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto).
|
||||||
|
auto length = read_u64();
|
||||||
|
value = MUST(JS::Array::create(realm, length));
|
||||||
|
// 3. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 20. Otherwise, if serialized.[[Type]] is "Object", then:
|
||||||
|
case ValueTag::Object: {
|
||||||
|
auto& realm = *m_vm.current_realm();
|
||||||
|
// 1. Set value to a new Object in targetRealm.
|
||||||
|
value = JS::Object::create(realm, realm.intrinsics().object_prototype());
|
||||||
|
// 2. Set deep to true.
|
||||||
|
deep = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 21. Otherwise, if serialized.[[Type]] is "Error", then:
|
||||||
case ValueTag::ErrorObject: {
|
case ValueTag::ErrorObject: {
|
||||||
auto& realm = *m_vm.current_realm();
|
auto& realm = *m_vm.current_realm();
|
||||||
auto type = static_cast<ErrorType>(m_vector[position++]);
|
auto type = static_cast<ErrorType>(m_serialized[m_position++]);
|
||||||
auto has_message = static_cast<bool>(m_vector[position++]);
|
auto has_message = static_cast<bool>(m_serialized[m_position++]);
|
||||||
if (has_message) {
|
if (has_message) {
|
||||||
auto message = TRY(deserialize_string(m_vm, m_vector, position));
|
auto message = TRY(deserialize_string(m_vm, m_serialized, m_position));
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ErrorType::Error:
|
case ErrorType::Error:
|
||||||
m_memory.append(JS::Error::create(realm, message));
|
value = JS::Error::create(realm, message);
|
||||||
break;
|
break;
|
||||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||||
case ErrorType::ClassName: \
|
case ErrorType::ClassName: \
|
||||||
m_memory.append(JS::ClassName::create(realm, message)); \
|
value = JS::ClassName::create(realm, message); \
|
||||||
break;
|
break;
|
||||||
JS_ENUMERATE_NATIVE_ERRORS
|
JS_ENUMERATE_NATIVE_ERRORS
|
||||||
#undef __JS_ENUMERATE
|
#undef __JS_ENUMERATE
|
||||||
|
@ -584,11 +772,11 @@ public:
|
||||||
} else {
|
} else {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ErrorType::Error:
|
case ErrorType::Error:
|
||||||
m_memory.append(JS::Error::create(realm));
|
value = JS::Error::create(realm);
|
||||||
break;
|
break;
|
||||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||||
case ErrorType::ClassName: \
|
case ErrorType::ClassName: \
|
||||||
m_memory.append(JS::ClassName::create(realm)); \
|
value = JS::ClassName::create(realm); \
|
||||||
break;
|
break;
|
||||||
JS_ENUMERATE_NATIVE_ERRORS
|
JS_ENUMERATE_NATIVE_ERRORS
|
||||||
#undef __JS_ENUMERATE
|
#undef __JS_ENUMERATE
|
||||||
|
@ -596,29 +784,94 @@ public:
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 22. Otherwise:
|
||||||
default:
|
default:
|
||||||
m_error = "Unsupported type"_fly_string;
|
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::Value> result()
|
// 23. Set memory[serialized] to value.
|
||||||
{
|
// IMPLEMENTATION DEFINED: We don't add primitive values to the memory to match the serialization indices (which also doesn't add them)
|
||||||
if (!m_error.has_value())
|
if (!is_primitive)
|
||||||
return m_memory[0];
|
m_memory.append(value);
|
||||||
return WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error.value());
|
|
||||||
|
// 24. If deep is true, then:
|
||||||
|
if (deep) {
|
||||||
|
// 1. If serialized.[[Type]] is "Map", then:
|
||||||
|
if (tag == ValueTag::MapObject) {
|
||||||
|
auto& map = static_cast<JS::Map&>(value.as_object());
|
||||||
|
auto length = read_u64();
|
||||||
|
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]:
|
||||||
|
for (u64 i = 0u; i < length; ++i) {
|
||||||
|
// 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory).
|
||||||
|
auto deserialized_key = TRY(deserialize());
|
||||||
|
|
||||||
|
// 2. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
|
||||||
|
auto deserialized_value = TRY(deserialize());
|
||||||
|
|
||||||
|
// 3. Append { [[Key]]: deserializedKey, [[Value]]: deserializedValue } to value.[[MapData]].
|
||||||
|
map.map_set(deserialized_key, deserialized_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Otherwise, if serialized.[[Type]] is "Set", then:
|
||||||
|
else if (tag == ValueTag::SetObject) {
|
||||||
|
auto& set = static_cast<JS::Set&>(value.as_object());
|
||||||
|
auto length = read_u64();
|
||||||
|
// 1. For each entry of serialized.[[SetData]]:
|
||||||
|
for (u64 i = 0u; i < length; ++i) {
|
||||||
|
// 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory).
|
||||||
|
auto deserialized_entry = TRY(deserialize());
|
||||||
|
|
||||||
|
// 2. Append deserializedEntry to value.[[SetData]].
|
||||||
|
set.set_add(deserialized_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then:
|
||||||
|
else if (tag == ValueTag::ArrayObject || tag == ValueTag::Object) {
|
||||||
|
auto& object = value.as_object();
|
||||||
|
auto length = read_u64();
|
||||||
|
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]:
|
||||||
|
for (u64 i = 0u; i < length; ++i) {
|
||||||
|
auto key = TRY(deserialize_string(m_vm, m_serialized, m_position));
|
||||||
|
|
||||||
|
// 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
|
||||||
|
auto deserialized_value = TRY(deserialize());
|
||||||
|
|
||||||
|
// 2. Let result be ! CreateDataProperty(value, entry.[[Key]], deserializedValue).
|
||||||
|
auto result = MUST(object.create_data_property(key.to_deprecated_string(), deserialized_value));
|
||||||
|
|
||||||
|
// 3. Assert: result is true.
|
||||||
|
VERIFY(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Otherwise:
|
||||||
|
else {
|
||||||
|
// FIXME: 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25. Return value.
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JS::VM& m_vm;
|
JS::VM& m_vm;
|
||||||
ReadonlySpan<u32> m_vector;
|
ReadonlySpan<u32> m_serialized;
|
||||||
|
size_t m_position { 0 };
|
||||||
JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
|
JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
|
||||||
Optional<FlyString> m_error;
|
|
||||||
SerializationMemory& m_serialization_memory;
|
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, u32& position)
|
u64 read_u64()
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
memcpy(&value, m_serialized.offset_pointer(m_position), sizeof(value));
|
||||||
|
m_position += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
|
||||||
{
|
{
|
||||||
u32 size_bits[2];
|
u32 size_bits[2];
|
||||||
size_bits[0] = vector[position++];
|
size_bits[0] = vector[position++];
|
||||||
|
@ -638,13 +891,13 @@ private:
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, u32& position)
|
static WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
|
||||||
{
|
{
|
||||||
auto bytes = TRY(deserialize_bytes(vm, vector, position));
|
auto bytes = TRY(deserialize_bytes(vm, vector, position));
|
||||||
return TRY_OR_THROW_OOM(vm, String::from_utf8(StringView { bytes }));
|
return TRY_OR_THROW_OOM(vm, String::from_utf8(StringView { bytes }));
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, u32& position)
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
|
||||||
{
|
{
|
||||||
auto bytes = TRY(deserialize_bytes(vm, vector, position));
|
auto bytes = TRY(deserialize_bytes(vm, vector, position));
|
||||||
|
|
||||||
|
@ -653,7 +906,7 @@ private:
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan<u32> vector, u32& position)
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
|
||||||
{
|
{
|
||||||
auto string = TRY(deserialize_string_primitive(vm, vector, position));
|
auto string = TRY(deserialize_string_primitive(vm, vector, position));
|
||||||
auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
|
auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
|
||||||
|
@ -689,21 +942,14 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& v
|
||||||
return serializer.serialize(value);
|
return serializer.serialize(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::Value> structured_deserialize_impl(JS::VM& vm, ReadonlySpan<u32> serialized, JS::Realm& target_realm, SerializationMemory& memory)
|
|
||||||
{
|
|
||||||
// FIXME: Do the spec steps
|
|
||||||
Deserializer deserializer(vm, target_realm, serialized, memory);
|
|
||||||
TRY(deserializer.deserialize());
|
|
||||||
return deserializer.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
|
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
|
||||||
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory> memory)
|
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory> memory)
|
||||||
{
|
{
|
||||||
if (!memory.has_value())
|
if (!memory.has_value())
|
||||||
memory = SerializationMemory {};
|
memory = DeserializationMemory { vm.heap() };
|
||||||
|
|
||||||
return structured_deserialize_impl(vm, serialized.span(), target_realm, *memory);
|
Deserializer deserializer(vm, target_realm, serialized.span(), *memory);
|
||||||
|
return deserializer.deserialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,17 +21,14 @@
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
|
||||||
using SerializationRecord = Vector<u32>;
|
using SerializationRecord = Vector<u32>;
|
||||||
struct SerializationRange {
|
using SerializationMemory = HashMap<JS::Handle<JS::Value>, u32>;
|
||||||
u64 start = 0;
|
using DeserializationMemory = JS::MarkedVector<JS::Value>;
|
||||||
u64 end = 0;
|
|
||||||
};
|
|
||||||
using SerializationMemory = HashMap<JS::Handle<JS::Value>, SerializationRange>;
|
|
||||||
|
|
||||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value);
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value);
|
||||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value);
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value);
|
||||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value, bool for_storage, SerializationMemory&);
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value, bool for_storage, SerializationMemory&);
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory>);
|
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory>);
|
||||||
|
|
||||||
// TODO: structured_[de]serialize_with_transfer
|
// TODO: structured_[de]serialize_with_transfer
|
||||||
|
|
||||||
|
|
|
@ -1057,7 +1057,7 @@ WebIDL::ExceptionOr<void> Window::window_post_message_steps(JS::Value message, W
|
||||||
// FIXME: Don't use a temporary execution context here.
|
// FIXME: Don't use a temporary execution context here.
|
||||||
auto& settings_object = Bindings::host_defined_environment_settings_object(target_realm);
|
auto& settings_object = Bindings::host_defined_environment_settings_object(target_realm);
|
||||||
auto temporary_execution_context = TemporaryExecutionContext { settings_object };
|
auto temporary_execution_context = TemporaryExecutionContext { settings_object };
|
||||||
auto deserialize_record_or_error = structured_deserialize(vm(), serialize_with_transfer_result, target_realm, Optional<HTML::SerializationMemory> {});
|
auto deserialize_record_or_error = structured_deserialize(vm(), serialize_with_transfer_result, target_realm, Optional<HTML::DeserializationMemory> {});
|
||||||
|
|
||||||
// If this throws an exception, catch it, fire an event named messageerror at targetWindow, using MessageEvent,
|
// If this throws an exception, catch it, fire an event named messageerror at targetWindow, using MessageEvent,
|
||||||
// with the origin attribute initialized to origin and the source attribute initialized to source, and then return.
|
// with the origin attribute initialized to origin and the source attribute initialized to source, and then return.
|
||||||
|
|
|
@ -283,7 +283,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<UserTiming::PerformanceMeasure>> Performanc
|
||||||
auto record = TRY(HTML::structured_serialize(vm, start_or_measure_options_dictionary_object->detail));
|
auto record = TRY(HTML::structured_serialize(vm, start_or_measure_options_dictionary_object->detail));
|
||||||
|
|
||||||
// 2. Set entry's detail to the result of calling the StructuredDeserialize algorithm on record and the current realm.
|
// 2. Set entry's detail to the result of calling the StructuredDeserialize algorithm on record and the current realm.
|
||||||
detail = TRY(HTML::structured_deserialize(vm, record, realm, Optional<HTML::SerializationMemory> {}));
|
detail = TRY(HTML::structured_deserialize(vm, record, realm, Optional<HTML::DeserializationMemory> {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Otherwise, set it to null.
|
// 2. Otherwise, set it to null.
|
||||||
|
|
|
@ -84,7 +84,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PerformanceMark>> PerformanceMark::construc
|
||||||
auto record = TRY(HTML::structured_serialize(vm, mark_options.detail));
|
auto record = TRY(HTML::structured_serialize(vm, mark_options.detail));
|
||||||
|
|
||||||
// 2. Set entry's detail to the result of calling the StructuredDeserialize algorithm on record and the current realm.
|
// 2. Set entry's detail to the result of calling the StructuredDeserialize algorithm on record and the current realm.
|
||||||
detail = TRY(HTML::structured_deserialize(vm, record, realm, Optional<HTML::SerializationMemory> {}));
|
detail = TRY(HTML::structured_deserialize(vm, record, realm, Optional<HTML::DeserializationMemory> {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Create a new PerformanceMark object (entry) with the current global object's realm.
|
// 2. Create a new PerformanceMark object (entry) with the current global object's realm.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue