From 1d24e08934188e9676ef3127e1c67d8b4a811b16 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Sat, 11 Nov 2023 01:14:56 +0200 Subject: [PATCH] LibWeb: Support [de]serialization for Error objects --- .../StructuredClone-object-primitives.txt | 2 + .../StructuredClone-object-primitives.html | 2 + .../LibWeb/HTML/StructuredSerialize.cpp | 83 ++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt b/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt index 01f1f25aa5..bcc689ffd6 100644 --- a/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt +++ b/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt @@ -6,5 +6,7 @@ This is a String object 9007199254740991 1692748800000 /abc/gimsuy +Error +URIError: hello [object ArrayBuffer] ERROR: DataCloneError: Cannot serialize Symbol \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html b/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html index ae3dd770d8..b692902669 100644 --- a/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html +++ b/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html @@ -9,6 +9,8 @@ println(structuredClone(BigInt("0x1fffffffffffff"))); println(structuredClone(Date.UTC(2023, 7, 23))); println(structuredClone(/abc/gimsuy)); + println(structuredClone(new Error())); + println(structuredClone(new URIError("hello"))); { let arrayBuffer = new ArrayBuffer(6); diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp index 829a1d7ecd..a2f17bc108 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -85,12 +85,32 @@ enum ValueTag { ArrayBufferView, + ErrorObject, + // TODO: Define many more types // This tag or higher are understood to be errors ValueTagMax, }; +enum ErrorType { + Error, +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ + ClassName, + JS_ENUMERATE_NATIVE_ERRORS +#undef __JS_ENUMERATE +}; + +static ErrorType error_name_to_type(String const& name) +{ +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ + if (name == #ClassName##sv) \ + return ErrorType::ClassName; + JS_ENUMERATE_NATIVE_ERRORS +#undef __JS_ENUMERATE + return Error; +} + // Serializing and deserializing are each two passes: // 1. Fill up the memory with all the values, but without translating references // 2. Translate all the references into the appropriate form @@ -215,7 +235,36 @@ public: TRY(serialize_viewed_array_buffer(m_serialized, static_cast(value.as_object()))); } - // 15 - 24: FIXME: Serialize other data types + // 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then: + else if (value.is_object() && is(value.as_object()) && !is(value.as_object())) { + // 1. Let name be ? Get(value, "name"). + auto name_property = TRY(value.as_object().get(m_vm.names.name)); + + // FIXME: Spec bug - https://github.com/whatwg/html/issues/9923 + // MISSING STEP: Set name to ? ToString(name). + auto name = TRY(name_property.to_string(m_vm)); + + // 2. If name is not one of "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", or "URIError", then set name to "Error". + auto type = error_name_to_type(name); + + // 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message"). + auto value_message_descriptor = TRY(value.as_object().internal_get_own_property(m_vm.names.message)); + + // 4. Let message be undefined if IsDataDescriptor(valueMessageDesc) is false, and ? ToString(valueMessageDesc.[[Value]]) otherwise. + Optional message; + if (value_message_descriptor.has_value() && value_message_descriptor->is_data_descriptor()) + message = TRY(value_message_descriptor->value->to_string(m_vm)); + + // 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }. + // FIXME: 6. User agents should attach a serialized representation of any interesting accompanying data which are not yet specified, notably the stack property, to serialized. + m_serialized.append(ValueTag::ErrorObject); + m_serialized.append(type); + m_serialized.append(message.has_value()); + if (message.has_value()) + TRY(serialize_string(m_serialized, *message)); + } + + // 15, 16, 18 - 24: FIXME: Serialize other data types else { return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string)); } @@ -501,6 +550,38 @@ public: } break; } + case ValueTag::ErrorObject: { + auto& realm = *m_vm.current_realm(); + auto type = static_cast(m_vector[position++]); + auto has_message = static_cast(m_vector[position++]); + if (has_message) { + auto message = TRY(deserialize_string(m_vm, m_vector, position)); + switch (type) { + case ErrorType::Error: + m_memory.append(JS::Error::create(realm, message)); + break; +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ + case ErrorType::ClassName: \ + m_memory.append(JS::ClassName::create(realm, message)); \ + break; + JS_ENUMERATE_NATIVE_ERRORS +#undef __JS_ENUMERATE + } + } else { + switch (type) { + case ErrorType::Error: + m_memory.append(JS::Error::create(realm)); + break; +#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ + case ErrorType::ClassName: \ + m_memory.append(JS::ClassName::create(realm)); \ + break; + JS_ENUMERATE_NATIVE_ERRORS +#undef __JS_ENUMERATE + } + } + break; + } default: m_error = "Unsupported type"_fly_string; break;