Browse Source

LibWeb: Add primitive support to Structured{Serialize,Deserialize}

This adds support for Undefined, Null, Boolean, BigInt, and String to
AOs StructuredSerializeInternal and StructuredDeserialize.
Kenneth Myhra 1 year ago
parent
commit
8c88e8f896
1 changed files with 118 additions and 10 deletions
  1. 118 10
      Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp

+ 118 - 10
Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp

@@ -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;
             }
             }
         }
         }
-
-        // Second pass: Update the objects to point to other objects in memory
+        return {};
     }
     }
 
 
     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();
 }
 }