1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-23 18:55:08 +00:00

LibWeb: Add spec steps to StructuredSerialize

In preparation for serializing more types, let's move the implementation
closer to the steps in the spec.
This commit is contained in:
Andrew Kaster 2023-08-21 15:15:05 -06:00 committed by Andreas Kling
parent 96600e77c2
commit f3cf7496a1
2 changed files with 70 additions and 27 deletions

View file

@ -76,13 +76,27 @@ enum ValueTag {
class Serializer {
public:
Serializer(JS::VM& vm)
Serializer(JS::VM& vm, SerializationMemory& memory)
: m_vm(vm)
, m_memory(memory)
{
}
WebIDL::ExceptionOr<void> serialize(JS::Value value)
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value 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)) {
auto range = m_memory.get(value).value();
return m_serialized.span().slice(range.start, range.end);
}
// 3. Let deep be false.
[[maybe_unused]] bool deep = false;
bool return_primitive_type = true;
// 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
if (value.is_undefined()) {
m_serialized.append(ValueTag::UndefinedPrimitive);
} else if (value.is_null()) {
@ -101,46 +115,70 @@ public:
} else if (value.is_string()) {
m_serialized.append(ValueTag::StringPrimitive);
TRY(serialize_string(m_serialized, value.as_string()));
} else if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
} else {
return_primitive_type = false;
}
if (return_primitive_type)
return m_serialized;
// FIXME 5. If Type(value) is Symbol, then throw a "DataCloneError" DOMException.
// 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]] }.
if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
m_serialized.append(ValueTag::BooleanObject);
auto& boolean_object = static_cast<JS::BooleanObject&>(value.as_object());
m_serialized.append(bit_cast<u32>(static_cast<u32>(boolean_object.boolean())));
} else if (value.is_object() && is<JS::NumberObject>(value.as_object())) {
}
// 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }.
else if (value.is_object() && is<JS::NumberObject>(value.as_object())) {
m_serialized.append(ValueTag::NumberObject);
auto& number_object = static_cast<JS::NumberObject&>(value.as_object());
double const number = number_object.number();
m_serialized.append(bit_cast<u32*>(&number), 2);
} else if (value.is_object() && is<JS::StringObject>(value.as_object())) {
}
// FIXME 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
// 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()));
} else if (value.is_object() && is<JS::Date>(value.as_object())) {
}
// 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
else if (value.is_object() && is<JS::Date>(value.as_object())) {
m_serialized.append(ValueTag::DateObject);
auto& date_object = static_cast<JS::Date&>(value.as_object());
double const date_value = date_object.date_value();
m_serialized.append(bit_cast<u32*>(&date_value), 2);
} else {
// TODO: Define many more types
m_error = "Unsupported type"sv;
}
// 12 - 24: FIXME: Serialize other data types
else {
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"sv));
}
// 25. Set memory[value] to serialized.
auto serialized_end = m_serialized.size();
m_memory.set(make_handle(value), { serialized_start, serialized_end });
// Second pass: Update the objects to point to other objects in memory
return {};
}
WebIDL::ExceptionOr<Vector<u32>> result()
{
if (m_error.is_null())
return m_serialized;
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
return m_serialized;
}
private:
AK::StringView m_error;
SerializationMemory m_memory; // JS value -> index
SerializationRecord m_serialized;
JS::VM& m_vm;
SerializationMemory& m_memory; // JS value -> index
SerializationRecord m_serialized;
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
{
@ -259,7 +297,7 @@ public:
{
if (m_error.is_null())
return m_memory[0];
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
return WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error);
}
private:
@ -320,13 +358,14 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory)
{
// FIXME: Do the spec steps
(void)for_storage;
(void)memory;
Serializer serializer(vm);
TRY(serializer.serialize(value));
return serializer.result(); // TODO: Avoid several copies of vector
// 1. If memory was not supplied, let memory be an empty map.
if (!memory.has_value())
memory = SerializationMemory {};
Serializer serializer(vm, *memory);
return serializer.serialize(value);
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize