StructuredSerialize.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
  3. * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
  4. * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/StdLibExtras.h>
  9. #include <AK/String.h>
  10. #include <AK/Vector.h>
  11. #include <LibJS/Forward.h>
  12. #include <LibJS/Runtime/BigInt.h>
  13. #include <LibJS/Runtime/BigIntObject.h>
  14. #include <LibJS/Runtime/BooleanObject.h>
  15. #include <LibJS/Runtime/Date.h>
  16. #include <LibJS/Runtime/NumberObject.h>
  17. #include <LibJS/Runtime/PrimitiveString.h>
  18. #include <LibJS/Runtime/StringObject.h>
  19. #include <LibJS/Runtime/VM.h>
  20. #include <LibWeb/Bindings/ExceptionOrUtils.h>
  21. #include <LibWeb/HTML/StructuredSerialize.h>
  22. #include <LibWeb/WebIDL/ExceptionOr.h>
  23. namespace Web::HTML {
  24. // Binary format:
  25. // A list of adjacent shallow values, which may contain references to other
  26. // values (noted by their position in the list, one value following another).
  27. // This list represents the "memory" in the StructuredSerialize algorithm.
  28. // The first item in the list is the root, i.e., the value of everything.
  29. // The format is generally u32-aligned (hence this leaking out into the type)
  30. // Each value has a length based on its type, as defined below.
  31. //
  32. // (Should more redundancy be added, e.g., for lengths/positions of values?)
  33. enum ValueTag {
  34. // Unused, for ease of catching bugs.
  35. Empty,
  36. // UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized.
  37. UndefinedPrimitive,
  38. // NullPrimitive is serialized indicating that the Type is Null, no value is serialized.
  39. NullPrimitive,
  40. // Following u32 is the boolean value.
  41. BooleanPrimitive,
  42. // Following two u32s are the double value.
  43. NumberPrimitive,
  44. // The BigIntPrimitive is serialized as a string in base 10 representation.
  45. // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
  46. BigIntPrimitive,
  47. // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
  48. StringPrimitive,
  49. BooleanObject,
  50. NumberObject,
  51. BigIntObject,
  52. StringObject,
  53. DateObject,
  54. // TODO: Define many more types
  55. // This tag or higher are understood to be errors
  56. ValueTagMax,
  57. };
  58. // Serializing and deserializing are each two passes:
  59. // 1. Fill up the memory with all the values, but without translating references
  60. // 2. Translate all the references into the appropriate form
  61. class Serializer {
  62. public:
  63. Serializer(JS::VM& vm, SerializationMemory& memory)
  64. : m_vm(vm)
  65. , m_memory(memory)
  66. {
  67. }
  68. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
  69. WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value value)
  70. {
  71. // 2. If memory[value] exists, then return memory[value].
  72. // FIXME: Do callers actually need a copy? or can they get away with a range?
  73. if (m_memory.contains(value)) {
  74. auto range = m_memory.get(value).value();
  75. return m_serialized.span().slice(range.start, range.end);
  76. }
  77. // 3. Let deep be false.
  78. [[maybe_unused]] bool deep = false;
  79. bool return_primitive_type = true;
  80. // 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
  81. if (value.is_undefined()) {
  82. m_serialized.append(ValueTag::UndefinedPrimitive);
  83. } else if (value.is_null()) {
  84. m_serialized.append(ValueTag::NullPrimitive);
  85. } else if (value.is_boolean()) {
  86. m_serialized.append(ValueTag::BooleanPrimitive);
  87. m_serialized.append(static_cast<u32>(value.as_bool()));
  88. } else if (value.is_number()) {
  89. m_serialized.append(ValueTag::NumberPrimitive);
  90. double number = value.as_double();
  91. m_serialized.append(bit_cast<u32*>(&number), 2);
  92. } else if (value.is_bigint()) {
  93. m_serialized.append(ValueTag::BigIntPrimitive);
  94. auto& val = value.as_bigint();
  95. TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, val.to_string())));
  96. } else if (value.is_string()) {
  97. m_serialized.append(ValueTag::StringPrimitive);
  98. TRY(serialize_string(m_serialized, value.as_string()));
  99. } else {
  100. return_primitive_type = false;
  101. }
  102. if (return_primitive_type)
  103. return m_serialized;
  104. // 5. If Type(value) is Symbol, then throw a "DataCloneError" DOMException.
  105. if (value.is_symbol())
  106. return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"sv);
  107. // 6. Let serialized be an uninitialized value.
  108. // NOTE: We use the range of the soon-to-be-serialized value in our serialized data buffer
  109. // to be the `serialized` spec value.
  110. auto serialized_start = m_serialized.size();
  111. // 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
  112. if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
  113. m_serialized.append(ValueTag::BooleanObject);
  114. auto& boolean_object = static_cast<JS::BooleanObject&>(value.as_object());
  115. m_serialized.append(bit_cast<u32>(static_cast<u32>(boolean_object.boolean())));
  116. }
  117. // 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }.
  118. else if (value.is_object() && is<JS::NumberObject>(value.as_object())) {
  119. m_serialized.append(ValueTag::NumberObject);
  120. auto& number_object = static_cast<JS::NumberObject&>(value.as_object());
  121. double const number = number_object.number();
  122. m_serialized.append(bit_cast<u32*>(&number), 2);
  123. }
  124. // 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
  125. else if (value.is_object() && is<JS::BigIntObject>(value.as_object())) {
  126. m_serialized.append(ValueTag::BigIntObject);
  127. auto& bigint_object = static_cast<JS::BigIntObject&>(value.as_object());
  128. TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, bigint_object.bigint().to_string())));
  129. }
  130. // 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
  131. else if (value.is_object() && is<JS::StringObject>(value.as_object())) {
  132. m_serialized.append(ValueTag::StringObject);
  133. auto& string_object = static_cast<JS::StringObject&>(value.as_object());
  134. TRY(serialize_string(m_serialized, string_object.primitive_string()));
  135. }
  136. // 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
  137. else if (value.is_object() && is<JS::Date>(value.as_object())) {
  138. m_serialized.append(ValueTag::DateObject);
  139. auto& date_object = static_cast<JS::Date&>(value.as_object());
  140. double const date_value = date_object.date_value();
  141. m_serialized.append(bit_cast<u32*>(&date_value), 2);
  142. }
  143. // 12 - 24: FIXME: Serialize other data types
  144. else {
  145. return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"sv));
  146. }
  147. // 25. Set memory[value] to serialized.
  148. auto serialized_end = m_serialized.size();
  149. m_memory.set(make_handle(value), { serialized_start, serialized_end });
  150. // Second pass: Update the objects to point to other objects in memory
  151. return m_serialized;
  152. }
  153. private:
  154. JS::VM& m_vm;
  155. SerializationMemory& m_memory; // JS value -> index
  156. SerializationRecord m_serialized;
  157. WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
  158. {
  159. u64 const size = string.code_points().byte_length();
  160. // Append size of the string to the serialized structure.
  161. TRY_OR_THROW_OOM(m_vm, vector.try_append(bit_cast<u32*>(&size), 2));
  162. // Append the bytes of the string to the serialized structure.
  163. u64 byte_position = 0;
  164. ReadonlyBytes const bytes = { string.code_points().bytes(), string.code_points().byte_length() };
  165. while (byte_position < size) {
  166. u32 combined_value = 0;
  167. for (u8 i = 0; i < 4; ++i) {
  168. u8 const byte = bytes[byte_position];
  169. combined_value |= byte << (i * 8);
  170. byte_position++;
  171. if (byte_position == size)
  172. break;
  173. }
  174. TRY_OR_THROW_OOM(m_vm, vector.try_append(combined_value));
  175. }
  176. return {};
  177. }
  178. WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
  179. {
  180. auto string = primitive_string.utf8_string();
  181. TRY(serialize_string(vector, string));
  182. return {};
  183. }
  184. };
  185. class Deserializer {
  186. public:
  187. Deserializer(JS::VM& vm, JS::Realm& target_realm, SerializationRecord const& v)
  188. : m_vm(vm)
  189. , m_vector(v)
  190. , m_memory(target_realm.heap())
  191. {
  192. }
  193. WebIDL::ExceptionOr<void> deserialize()
  194. {
  195. // First pass: fill up the memory with new values
  196. u32 position = 0;
  197. while (position < m_vector.size()) {
  198. switch (m_vector[position++]) {
  199. case ValueTag::UndefinedPrimitive: {
  200. m_memory.append(JS::js_undefined());
  201. break;
  202. }
  203. case ValueTag::NullPrimitive: {
  204. m_memory.append(JS::js_null());
  205. break;
  206. }
  207. case ValueTag::BooleanPrimitive: {
  208. m_memory.append(JS::Value(static_cast<bool>(m_vector[position++])));
  209. break;
  210. }
  211. case ValueTag::NumberPrimitive: {
  212. u32 bits[2];
  213. bits[0] = m_vector[position++];
  214. bits[1] = m_vector[position++];
  215. double value = *bit_cast<double*>(&bits);
  216. m_memory.append(JS::Value(value));
  217. break;
  218. }
  219. case ValueTag::BigIntPrimitive: {
  220. auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
  221. m_memory.append(JS::Value { big_int });
  222. break;
  223. }
  224. case ValueTag::StringPrimitive: {
  225. auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
  226. m_memory.append(JS::Value { string });
  227. break;
  228. }
  229. case BooleanObject: {
  230. auto* realm = m_vm.current_realm();
  231. bool const value = static_cast<bool>(m_vector[position++]);
  232. m_memory.append(JS::BooleanObject::create(*realm, value));
  233. break;
  234. }
  235. case ValueTag::NumberObject: {
  236. auto* realm = m_vm.current_realm();
  237. u32 bits[2];
  238. bits[0] = m_vector[position++];
  239. bits[1] = m_vector[position++];
  240. double const value = *bit_cast<double*>(&bits);
  241. m_memory.append(JS::NumberObject::create(*realm, value));
  242. break;
  243. }
  244. case ValueTag::BigIntObject: {
  245. auto* realm = m_vm.current_realm();
  246. auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
  247. m_memory.append(JS::BigIntObject::create(*realm, big_int));
  248. break;
  249. }
  250. case ValueTag::StringObject: {
  251. auto* realm = m_vm.current_realm();
  252. auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
  253. m_memory.append(JS::StringObject::create(*realm, string, realm->intrinsics().string_prototype()));
  254. break;
  255. }
  256. case ValueTag::DateObject: {
  257. auto* realm = m_vm.current_realm();
  258. u32 bits[2];
  259. bits[0] = m_vector[position++];
  260. bits[1] = m_vector[position++];
  261. double const value = *bit_cast<double*>(&bits);
  262. m_memory.append(JS::Date::create(*realm, value));
  263. break;
  264. }
  265. default:
  266. m_error = "Unsupported type"sv;
  267. break;
  268. }
  269. }
  270. return {};
  271. }
  272. WebIDL::ExceptionOr<JS::Value> result()
  273. {
  274. if (m_error.is_null())
  275. return m_memory[0];
  276. return WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error);
  277. }
  278. private:
  279. JS::VM& m_vm;
  280. SerializationRecord const& m_vector;
  281. JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
  282. StringView m_error;
  283. static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
  284. {
  285. u32 size_bits[2];
  286. size_bits[0] = vector[position++];
  287. size_bits[1] = vector[position++];
  288. u64 const size = *bit_cast<u64*>(&size_bits);
  289. Vector<u8> bytes;
  290. TRY_OR_THROW_OOM(vm, bytes.try_ensure_capacity(size));
  291. u64 byte_position = 0;
  292. while (position < vector.size()) {
  293. for (u8 i = 0; i < 4; ++i) {
  294. bytes.append(vector[position] >> (i * 8) & 0xFF);
  295. byte_position++;
  296. if (byte_position == size)
  297. break;
  298. }
  299. position++;
  300. }
  301. return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
  302. return JS::PrimitiveString::create(vm, StringView { bytes });
  303. }));
  304. }
  305. static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
  306. {
  307. auto string = TRY(deserialize_string_primitive(vm, vector, position));
  308. auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
  309. return string->utf8_string_view();
  310. }));
  311. return JS::BigInt::create(vm, ::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
  312. }
  313. };
  314. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
  315. WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
  316. {
  317. // 1. Return ? StructuredSerializeInternal(value, false).
  318. return structured_serialize_internal(vm, value, false, {});
  319. }
  320. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
  321. WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
  322. {
  323. // 1. Return ? StructuredSerializeInternal(value, true).
  324. return structured_serialize_internal(vm, value, true, {});
  325. }
  326. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
  327. WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory)
  328. {
  329. (void)for_storage;
  330. // 1. If memory was not supplied, let memory be an empty map.
  331. if (!memory.has_value())
  332. memory = SerializationMemory {};
  333. Serializer serializer(vm, *memory);
  334. return serializer.serialize(value);
  335. }
  336. // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
  337. WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory> memory)
  338. {
  339. // FIXME: Do the spec steps
  340. (void)memory;
  341. Deserializer deserializer(vm, target_realm, serialized);
  342. TRY(deserializer.deserialize());
  343. return deserializer.result();
  344. }
  345. }