1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 17:35:07 +00:00

LibWeb: Move serialize_* methods outside scope of Serializer class

These methods are useful independent of the class Serializer, so let's
move their declarations to the header file and and outside the scope of
the Serializer class.
This commit is contained in:
Kenneth Myhra 2024-02-23 18:30:50 +01:00 committed by Andrew Kaster
parent 985d0dd270
commit dcf5ff5178
2 changed files with 173 additions and 160 deletions

View file

@ -1,7 +1,7 @@
/*
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
* Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
* Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -170,10 +170,10 @@ public:
} else if (value.is_bigint()) {
m_serialized.append(ValueTag::BigIntPrimitive);
auto& val = value.as_bigint();
TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, val.to_string())));
TRY(serialize_string(m_vm, m_serialized, TRY_OR_THROW_OOM(m_vm, val.to_string())));
} else if (value.is_string()) {
m_serialized.append(ValueTag::StringPrimitive);
TRY(serialize_string(m_serialized, value.as_string()));
TRY(serialize_string(m_vm, m_serialized, value.as_string()));
} else {
return_primitive_type = false;
}
@ -206,14 +206,14 @@ public:
else if (value.is_object() && is<JS::BigIntObject>(value.as_object())) {
m_serialized.append(ValueTag::BigIntObject);
auto& bigint_object = static_cast<JS::BigIntObject&>(value.as_object());
TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, bigint_object.bigint().to_string())));
TRY(serialize_string(m_vm, m_serialized, TRY_OR_THROW_OOM(m_vm, bigint_object.bigint().to_string())));
}
// 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
else if (value.is_object() && is<JS::StringObject>(value.as_object())) {
m_serialized.append(ValueTag::StringObject);
auto& string_object = static_cast<JS::StringObject&>(value.as_object());
TRY(serialize_string(m_serialized, string_object.primitive_string()));
TRY(serialize_string(m_vm, m_serialized, string_object.primitive_string()));
}
// 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
@ -233,20 +233,20 @@ public:
// Note: A Regex<ECMA262> object is perfectly happy to be reconstructed with just the source+flags
// In the future, we could optimize the work being done on the deserialize step by serializing
// more of the internal state (the [[RegExpMatcher]] internal slot)
TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_byte_string(regexp_object.pattern()))));
TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_byte_string(regexp_object.flags()))));
TRY(serialize_string(m_vm, m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_byte_string(regexp_object.pattern()))));
TRY(serialize_string(m_vm, m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_byte_string(regexp_object.flags()))));
}
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
else if (value.is_object() && is<JS::ArrayBuffer>(value.as_object())) {
TRY(serialize_array_buffer(m_serialized, static_cast<JS::ArrayBuffer&>(value.as_object())));
TRY(serialize_array_buffer(m_vm, m_serialized, static_cast<JS::ArrayBuffer&>(value.as_object()), m_for_storage));
}
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
else if (value.is_object() && is<JS::TypedArrayBase>(value.as_object())) {
TRY(serialize_viewed_array_buffer(m_serialized, static_cast<JS::TypedArrayBase&>(value.as_object())));
TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::TypedArrayBase&>(value.as_object()), m_for_storage, m_memory));
} else if (value.is_object() && is<JS::DataView>(value.as_object())) {
TRY(serialize_viewed_array_buffer(m_serialized, static_cast<JS::DataView&>(value.as_object())));
TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::DataView&>(value.as_object()), m_for_storage, m_memory));
}
// 15. Otherwise, if value has [[MapData]] internal slot, then:
@ -291,7 +291,7 @@ public:
m_serialized.append(type);
m_serialized.append(message.has_value());
if (message.has_value())
TRY(serialize_string(m_serialized, *message));
TRY(serialize_string(m_vm, m_serialized, *message));
}
// 18. Otherwise, if value is an Array exotic object, then:
@ -407,7 +407,7 @@ public:
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()));
TRY(serialize_string(m_vm, m_serialized, key.as_string()));
m_serialized.extend(output_value);
property_count++;
@ -427,12 +427,13 @@ private:
u32 m_next_id { 0 };
SerializationRecord m_serialized;
bool m_for_storage { false };
};
WebIDL::ExceptionOr<void> serialize_bytes(Vector<u32>& vector, ReadonlyBytes bytes)
{
WebIDL::ExceptionOr<void> serialize_bytes(JS::VM& vm, Vector<u32>& vector, ReadonlyBytes bytes)
{
// Append size of the buffer to the serialized structure.
u64 const size = bytes.size();
TRY_OR_THROW_OOM(m_vm, vector.try_append(bit_cast<u32*>(&size), 2));
TRY_OR_THROW_OOM(vm, vector.try_append(bit_cast<u32*>(&size), 2));
// Append the bytes of the buffer to the serialized structure.
u64 byte_position = 0;
while (byte_position < size) {
@ -444,30 +445,30 @@ private:
if (byte_position == size)
break;
}
TRY_OR_THROW_OOM(m_vm, vector.try_append(combined_value));
TRY_OR_THROW_OOM(vm, vector.try_append(combined_value));
}
return {};
}
}
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, DeprecatedFlyString const& string)
{
return serialize_bytes(vector, string.view().bytes());
}
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, DeprecatedFlyString const& string)
{
return serialize_bytes(vm, vector, string.view().bytes());
}
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
{
return serialize_bytes(vector, { string.code_points().bytes(), string.code_points().byte_length() });
}
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, String const& string)
{
return serialize_bytes(vm, vector, { string.code_points().bytes(), string.code_points().byte_length() });
}
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
{
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
{
auto string = primitive_string.utf8_string();
TRY(serialize_string(vector, string));
TRY(serialize_string(vm, vector, string));
return {};
}
}
WebIDL::ExceptionOr<void> serialize_array_buffer(Vector<u32>& vector, JS::ArrayBuffer const& array_buffer)
{
WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::ArrayBuffer const& array_buffer, bool for_storage)
{
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
// FIXME: 1. If IsSharedArrayBuffer(value) is true, then:
@ -476,11 +477,11 @@ private:
// NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
// over time and a SharedArrayBuffer cannot leave an agent cluster.
if (current_settings_object().cross_origin_isolated_capability() == CanUseCrossOriginIsolatedAPIs::No)
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize SharedArrayBuffer when cross-origin isolated"_fly_string);
return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer when cross-origin isolated"_fly_string);
// 2. If forStorage is true, then throw a "DataCloneError" DOMException.
if (m_for_storage)
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_fly_string);
if (for_storage)
return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_fly_string);
// FIXME: 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
// [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
@ -492,14 +493,14 @@ private:
else {
// 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
if (array_buffer.is_detached())
return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize detached ArrayBuffer"_fly_string);
return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize detached ArrayBuffer"_fly_string);
// 2. Let size be value.[[ArrayBufferByteLength]].
auto size = array_buffer.byte_length();
// 3. Let dataCopy be ? CreateByteDataBlock(size).
// NOTE: This can throw a RangeError exception upon allocation failure.
auto data_copy = TRY(JS::create_byte_data_block(m_vm, size));
auto data_copy = TRY(JS::create_byte_data_block(vm, size));
// 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size);
@ -511,15 +512,15 @@ private:
// 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
else {
vector.append(ValueTag::ArrayBuffer);
TRY(serialize_bytes(vector, data_copy.buffer().bytes()));
TRY(serialize_bytes(vm, vector, data_copy.buffer().bytes()));
}
}
return {};
}
}
template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(Vector<u32>& vector, ViewType const& view)
{
template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, ViewType const& view, bool for_storage, SerializationMemory& memory)
{
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
auto view_record = [&]() {
@ -533,17 +534,17 @@ private:
// 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
if constexpr (IsSame<ViewType, JS::DataView>) {
if (JS::is_view_out_of_bounds(view_record))
return WebIDL::DataCloneError::create(*m_vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "DataView"sv)));
return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "DataView"sv)));
} else {
if (JS::is_typed_array_out_of_bounds(view_record))
return WebIDL::DataCloneError::create(*m_vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "TypedArray"sv)));
return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "TypedArray"sv)));
}
// 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
auto* buffer = view.viewed_array_buffer();
// 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
auto buffer_serialized = TRY(structured_serialize_internal(m_vm, JS::Value(buffer), m_for_storage, m_memory));
auto buffer_serialized = TRY(structured_serialize_internal(vm, JS::Value(buffer), for_storage, memory));
// 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
// NOTE: We currently only implement this for ArrayBuffer
@ -554,7 +555,7 @@ private:
if constexpr (IsSame<ViewType, JS::DataView>) {
vector.append(ValueTag::ArrayBufferView);
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
TRY(serialize_string(vector, "DataView"_string)); // [[Constructor]]
TRY(serialize_string(vm, vector, "DataView"_string)); // [[Constructor]]
vector.append(JS::get_view_byte_length(view_record));
vector.append(view.byte_offset());
}
@ -568,14 +569,15 @@ private:
// [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
vector.append(ValueTag::ArrayBufferView);
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
TRY(serialize_string(vector, view.element_name())); // [[Constructor]]
TRY(serialize_string(vm, vector, view.element_name())); // [[Constructor]]
vector.append(JS::typed_array_byte_length(view_record));
vector.append(view.byte_offset());
vector.append(JS::typed_array_length(view_record));
}
return {};
}
};
}
template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::TypedArrayBase const& view, bool for_storage, SerializationMemory& memory);
template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::DataView const& view, bool for_storage, SerializationMemory& memory);
class Deserializer {
public:

View file

@ -1,6 +1,7 @@
/*
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -12,6 +13,8 @@
#include <AK/Vector.h>
#include <LibIPC/Forward.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
// Structured serialize is an entirely different format from IPC because:
@ -50,6 +53,14 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& v
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory>);
WebIDL::ExceptionOr<void> serialize_bytes(JS::VM& vm, Vector<u32>& vector, ReadonlyBytes bytes);
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, DeprecatedFlyString const& string);
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, String const& string);
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, JS::PrimitiveString const& primitive_string);
WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::ArrayBuffer const& array_buffer, bool for_storage);
template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, ViewType const& view, bool for_storage, SerializationMemory& memory);
WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);