mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 19:45:08 +00:00
LibWeb: Add primitive support to Structured{Serialize,Deserialize}
This adds support for Undefined, Null, Boolean, BigInt, and String to AOs StructuredSerializeInternal and StructuredDeserialize.
This commit is contained in:
parent
b30a1b957d
commit
8c88e8f896
1 changed files with 118 additions and 10 deletions
|
@ -1,14 +1,19 @@
|
||||||
/*
|
/*
|
||||||
* 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>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
|
#include <LibJS/Runtime/BigInt.h>
|
||||||
|
#include <LibJS/Runtime/PrimitiveString.h>
|
||||||
#include <LibJS/Runtime/VM.h>
|
#include <LibJS/Runtime/VM.h>
|
||||||
|
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
|
@ -25,12 +30,28 @@ namespace Web::HTML {
|
||||||
// (Should more redundancy be added, e.g., for lengths/positions of values?)
|
// (Should more redundancy be added, e.g., for lengths/positions of values?)
|
||||||
|
|
||||||
enum ValueTag {
|
enum ValueTag {
|
||||||
// Unused, for ease of catching bugs
|
// Unused, for ease of catching bugs.
|
||||||
Empty,
|
Empty,
|
||||||
|
|
||||||
// Following two u32s are the double value
|
// UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized.
|
||||||
|
UndefinedPrimitive,
|
||||||
|
|
||||||
|
// NullPrimitive is serialized indicating that the Type is Null, no value is serialized.
|
||||||
|
NullPrimitive,
|
||||||
|
|
||||||
|
// Following u32 is the boolean value.
|
||||||
|
BooleanPrimitive,
|
||||||
|
|
||||||
|
// Following two u32s are the double value.
|
||||||
NumberPrimitive,
|
NumberPrimitive,
|
||||||
|
|
||||||
|
// The BigIntPrimitive is serialized as a string in base 10 representation.
|
||||||
|
// Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
|
||||||
|
BigIntPrimitive,
|
||||||
|
|
||||||
|
// Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
|
||||||
|
StringPrimitive,
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -48,16 +69,34 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(JS::Value value)
|
WebIDL::ExceptionOr<void> serialize(JS::Value value)
|
||||||
{
|
{
|
||||||
if (value.is_number()) {
|
if (value.is_undefined()) {
|
||||||
|
m_serialized.append(ValueTag::UndefinedPrimitive);
|
||||||
|
} else if (value.is_null()) {
|
||||||
|
m_serialized.append(ValueTag::NullPrimitive);
|
||||||
|
} else if (value.is_boolean()) {
|
||||||
|
m_serialized.append(ValueTag::BooleanPrimitive);
|
||||||
|
m_serialized.append(static_cast<u32>(value.as_bool()));
|
||||||
|
} else if (value.is_number()) {
|
||||||
m_serialized.append(ValueTag::NumberPrimitive);
|
m_serialized.append(ValueTag::NumberPrimitive);
|
||||||
double number = value.as_double();
|
double number = value.as_double();
|
||||||
m_serialized.append(bit_cast<u32*>(&number), 2);
|
m_serialized.append(bit_cast<u32*>(&number), 2);
|
||||||
|
} 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())));
|
||||||
|
} else if (value.is_string()) {
|
||||||
|
m_serialized.append(ValueTag::StringPrimitive);
|
||||||
|
TRY(serialize_string(m_serialized, value.as_string()));
|
||||||
} else {
|
} else {
|
||||||
// TODO: Define many more types
|
// TODO: Define many more types
|
||||||
m_error = "Unsupported type"sv;
|
m_error = "Unsupported type"sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second pass: Update the objects to point to other objects in memory
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<Vector<u32>> result()
|
WebIDL::ExceptionOr<Vector<u32>> result()
|
||||||
|
@ -72,6 +111,27 @@ private:
|
||||||
SerializationMemory m_memory; // JS value -> index
|
SerializationMemory m_memory; // JS value -> index
|
||||||
SerializationRecord m_serialized;
|
SerializationRecord m_serialized;
|
||||||
JS::VM& m_vm;
|
JS::VM& m_vm;
|
||||||
|
|
||||||
|
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
|
||||||
|
{
|
||||||
|
u64 const size = string.code_points().length();
|
||||||
|
// Append size of the string to the serialized structure.
|
||||||
|
TRY_OR_THROW_OOM(m_vm, vector.try_append(bit_cast<u32*>(&size), 2));
|
||||||
|
for (auto code_point : string.code_points()) {
|
||||||
|
// Append each code point to the serialized structure.
|
||||||
|
TRY_OR_THROW_OOM(m_vm, vector.try_append(code_point));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
|
||||||
|
{
|
||||||
|
auto string = TRY(Bindings::throw_dom_exception_if_needed(m_vm, [&primitive_string]() {
|
||||||
|
return primitive_string.utf8_string();
|
||||||
|
}));
|
||||||
|
TRY(serialize_string(vector, string));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Deserializer {
|
class Deserializer {
|
||||||
|
@ -83,12 +143,24 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize()
|
WebIDL::ExceptionOr<void> deserialize()
|
||||||
{
|
{
|
||||||
// First pass: fill up the memory with new values
|
// First pass: fill up the memory with new values
|
||||||
u32 position = 0;
|
u32 position = 0;
|
||||||
while (position < m_vector.size()) {
|
while (position < m_vector.size()) {
|
||||||
switch (m_vector[position++]) {
|
switch (m_vector[position++]) {
|
||||||
|
case ValueTag::UndefinedPrimitive: {
|
||||||
|
m_memory.append(JS::js_undefined());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ValueTag::NullPrimitive: {
|
||||||
|
m_memory.append(JS::js_null());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ValueTag::BooleanPrimitive: {
|
||||||
|
m_memory.append(JS::Value(static_cast<bool>(m_vector[position++])));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ValueTag::NumberPrimitive: {
|
case ValueTag::NumberPrimitive: {
|
||||||
u32 bits[2];
|
u32 bits[2];
|
||||||
bits[0] = m_vector[position++];
|
bits[0] = m_vector[position++];
|
||||||
|
@ -97,13 +169,22 @@ public:
|
||||||
m_memory.append(JS::Value(value));
|
m_memory.append(JS::Value(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ValueTag::BigIntPrimitive: {
|
||||||
|
auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
|
||||||
|
m_memory.append(JS::Value { big_int });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ValueTag::StringPrimitive: {
|
||||||
|
auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
|
||||||
|
m_memory.append(JS::Value { string });
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
m_error = "Unsupported type"sv;
|
m_error = "Unsupported type"sv;
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
// Second pass: Update the objects to point to other objects in memory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::Value> result()
|
WebIDL::ExceptionOr<JS::Value> result()
|
||||||
|
@ -118,6 +199,33 @@ private:
|
||||||
SerializationRecord const& m_vector;
|
SerializationRecord const& m_vector;
|
||||||
JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
|
JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
|
||||||
StringView m_error;
|
StringView m_error;
|
||||||
|
|
||||||
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
|
||||||
|
{
|
||||||
|
u32 size_bits[2];
|
||||||
|
size_bits[0] = vector[position++];
|
||||||
|
size_bits[1] = vector[position++];
|
||||||
|
u64 const size = *bit_cast<u64*>(&size_bits);
|
||||||
|
|
||||||
|
u8 bits[size];
|
||||||
|
for (u32 i = 0; i < size; ++i)
|
||||||
|
bits[i] = vector[position++];
|
||||||
|
|
||||||
|
ReadonlyBytes const bytes = { bits, size };
|
||||||
|
|
||||||
|
return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
|
||||||
|
return JS::PrimitiveString::create(vm, StringView { bytes });
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
|
||||||
|
{
|
||||||
|
auto string = TRY(deserialize_string_primitive(vm, vector, position));
|
||||||
|
auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
|
||||||
|
return string->utf8_string_view();
|
||||||
|
}));
|
||||||
|
return JS::BigInt::create(vm, ::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
|
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
|
||||||
|
@ -142,7 +250,7 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& v
|
||||||
(void)memory;
|
(void)memory;
|
||||||
|
|
||||||
Serializer serializer(vm);
|
Serializer serializer(vm);
|
||||||
serializer.serialize(value);
|
TRY(serializer.serialize(value));
|
||||||
return serializer.result(); // TODO: Avoid several copies of vector
|
return serializer.result(); // TODO: Avoid several copies of vector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +261,7 @@ WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationR
|
||||||
(void)memory;
|
(void)memory;
|
||||||
|
|
||||||
Deserializer deserializer(vm, target_realm, serialized);
|
Deserializer deserializer(vm, target_realm, serialized);
|
||||||
deserializer.deserialize();
|
TRY(deserializer.deserialize());
|
||||||
return deserializer.result();
|
return deserializer.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue