2022-04-05 02:24:10 +02:00
/*
* Copyright ( c ) 2022 , Daniel Ehrenberg < dan @ littledan . dev >
* Copyright ( c ) 2022 , Andrew Kaster < akaster @ serenityos . org >
2024-02-23 18:30:50 +01:00
* Copyright ( c ) 2023 - 2024 , Kenneth Myhra < kennethmyhra @ serenityos . org >
2023-11-11 20:14:46 +02:00
* Copyright ( c ) 2023 , Idan Horowitz < idan . horowitz @ serenityos . org >
2022-04-05 02:24:10 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-07-30 18:34:56 +02:00
# include <AK/StdLibExtras.h>
2023-07-21 21:19:52 +02:00
# include <AK/String.h>
2022-04-05 02:24:10 +02:00
# include <AK/Vector.h>
2023-12-19 15:28:56 -07:00
# include <LibIPC/Decoder.h>
# include <LibIPC/Encoder.h>
2023-12-07 15:53:38 -07:00
# include <LibIPC/File.h>
2022-04-05 02:24:10 +02:00
# include <LibJS/Forward.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Array.h>
2023-09-11 18:06:43 -06:00
# include <LibJS/Runtime/ArrayBuffer.h>
2024-11-24 14:20:57 +01:00
# include <LibJS/Runtime/ArrayBufferConstructor.h>
2023-07-21 21:19:52 +02:00
# include <LibJS/Runtime/BigInt.h>
2023-08-21 17:39:44 -06:00
# include <LibJS/Runtime/BigIntObject.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/BooleanObject.h>
2023-09-12 16:56:35 -06:00
# include <LibJS/Runtime/DataView.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/Date.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Map.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/NumberObject.h>
2023-07-21 21:19:52 +02:00
# include <LibJS/Runtime/PrimitiveString.h>
2023-09-11 16:32:13 -06:00
# include <LibJS/Runtime/RegExpObject.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Set.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/StringObject.h>
2023-09-12 16:56:35 -06:00
# include <LibJS/Runtime/TypedArray.h>
2023-07-09 11:40:17 +03:30
# include <LibJS/Runtime/VM.h>
2023-07-21 21:19:52 +02:00
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-02-23 18:32:27 +01:00
# include <LibWeb/Bindings/Serializable.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/Bindings/Transferable.h>
2024-03-10 20:48:00 +01:00
# include <LibWeb/Crypto/CryptoKey.h>
2024-02-23 18:32:27 +01:00
# include <LibWeb/FileAPI/Blob.h>
2024-02-23 22:14:18 +01:00
# include <LibWeb/FileAPI/File.h>
2024-03-18 21:58:25 +01:00
# include <LibWeb/FileAPI/FileList.h>
2024-03-09 22:41:26 +01:00
# include <LibWeb/Geometry/DOMMatrix.h>
2024-03-09 22:07:22 +01:00
# include <LibWeb/Geometry/DOMMatrixReadOnly.h>
2024-03-09 23:10:11 +01:00
# include <LibWeb/Geometry/DOMPoint.h>
2024-03-09 23:02:22 +01:00
# include <LibWeb/Geometry/DOMPointReadOnly.h>
2024-03-12 21:08:46 +01:00
# include <LibWeb/Geometry/DOMQuad.h>
2024-03-10 09:43:00 +01:00
# include <LibWeb/Geometry/DOMRect.h>
2024-03-10 09:02:54 +01:00
# include <LibWeb/Geometry/DOMRectReadOnly.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/HTML/MessagePort.h>
2022-04-05 02:24:10 +02:00
# include <LibWeb/HTML/StructuredSerialize.h>
2024-11-22 12:17:18 +01:00
# include <LibWeb/WebIDL/DOMException.h>
2022-04-05 02:24:10 +02:00
# include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web : : HTML {
// Binary format:
// A list of adjacent shallow values, which may contain references to other
// values (noted by their position in the list, one value following another).
// This list represents the "memory" in the StructuredSerialize algorithm.
// The first item in the list is the root, i.e., the value of everything.
// The format is generally u32-aligned (hence this leaking out into the type)
// Each value has a length based on its type, as defined below.
//
// (Should more redundancy be added, e.g., for lengths/positions of values?)
enum ValueTag {
2023-07-21 21:19:52 +02:00
// Unused, for ease of catching bugs.
2022-04-05 02:24:10 +02:00
Empty ,
2023-07-21 21:19:52 +02:00
// 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.
2022-04-05 02:24:10 +02:00
NumberPrimitive ,
2023-07-21 21:19:52 +02:00
// 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 ,
2023-07-24 21:03:21 +02:00
BooleanObject ,
NumberObject ,
2023-08-21 17:39:44 -06:00
BigIntObject ,
2023-07-24 21:03:21 +02:00
StringObject ,
DateObject ,
2023-09-11 16:32:13 -06:00
RegExpObject ,
2023-09-11 18:06:43 -06:00
GrowableSharedArrayBuffer ,
SharedArrayBuffer ,
ResizeableArrayBuffer ,
ArrayBuffer ,
2023-09-12 16:56:35 -06:00
ArrayBufferView ,
2023-11-11 20:14:46 +02:00
MapObject ,
SetObject ,
2023-11-11 01:14:56 +02:00
ErrorObject ,
2023-11-11 20:14:46 +02:00
ArrayObject ,
Object ,
ObjectReference ,
2024-02-23 18:32:27 +01:00
SerializableObject ,
2022-04-05 02:24:10 +02:00
// TODO: Define many more types
// This tag or higher are understood to be errors
ValueTagMax ,
} ;
2023-11-11 01:14:56 +02:00
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 ;
}
2022-04-05 02:24:10 +02:00
// 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
class Serializer {
public :
2023-09-11 18:06:43 -06:00
Serializer ( JS : : VM & vm , SerializationMemory & memory , bool for_storage )
2022-04-05 02:24:10 +02:00
: m_vm ( vm )
2023-08-21 15:15:05 -06:00
, m_memory ( memory )
2024-11-24 14:20:57 +01:00
, m_next_id ( memory . size ( ) )
2023-09-11 18:06:43 -06:00
, m_for_storage ( for_storage )
2022-04-05 02:24:10 +02:00
{
}
2023-08-21 15:15:05 -06:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
2024-10-21 13:35:24 +13:00
// https://whatpr.org/html/9893/structured-data.html#structuredserializeinternal
2023-08-21 15:15:05 -06:00
WebIDL : : ExceptionOr < SerializationRecord > serialize ( JS : : Value value )
2022-04-05 02:24:10 +02:00
{
2023-08-21 15:15:05 -06:00
// 2. If memory[value] exists, then return memory[value].
if ( m_memory . contains ( value ) ) {
2023-11-11 20:14:46 +02:00
auto index = m_memory . get ( value ) . value ( ) ;
return Vector < u32 > { ValueTag : : ObjectReference , index } ;
2023-08-21 15:15:05 -06:00
}
// 3. Let deep be false.
2023-11-11 20:14:46 +02:00
auto deep = false ;
2023-08-21 15:15:05 -06:00
bool return_primitive_type = true ;
2024-10-31 11:34:22 +00:00
// 4. If value is undefined, null, a Boolean, a Number, a BigInt, or a String, then return { [[Type]]: "primitive", [[Value]]: value }.
2023-07-21 21:19:52 +02:00
if ( value . is_undefined ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : UndefinedPrimitive ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_null ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : NullPrimitive ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_boolean ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : BooleanPrimitive ) ;
2024-03-02 16:45:12 +01:00
serialize_boolean_primitive ( m_serialized , value ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_number ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : NumberPrimitive ) ;
2024-03-02 16:45:12 +01:00
serialize_number_primitive ( m_serialized , value ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_bigint ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : BigIntPrimitive ) ;
2024-03-02 16:45:12 +01:00
TRY ( serialize_big_int_primitive ( m_vm , m_serialized , value ) ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_string ( ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : StringPrimitive ) ;
2024-03-02 16:45:12 +01:00
TRY ( serialize_string_primitive ( m_vm , m_serialized , value ) ) ;
2023-08-21 15:15:05 -06:00
} else {
return_primitive_type = false ;
}
if ( return_primitive_type )
return m_serialized ;
2024-10-31 11:34:22 +00:00
// 5. If value is a Symbol, then throw a "DataCloneError" DOMException.
2023-08-21 17:39:44 -06:00
if ( value . is_symbol ( ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize Symbol " _string ) ;
2023-08-21 15:15:05 -06:00
// 6. Let serialized be an uninitialized value.
// 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 ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : BooleanObject ) ;
2024-03-02 16:45:12 +01:00
serialize_boolean_object ( m_serialized , value ) ;
2023-08-21 15:15:05 -06:00
}
// 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 ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : NumberObject ) ;
2024-03-02 16:45:12 +01:00
serialize_number_object ( m_serialized , value ) ;
2023-08-21 15:15:05 -06:00
}
2023-08-21 17:39:44 -06:00
// 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
else if ( value . is_object ( ) & & is < JS : : BigIntObject > ( value . as_object ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : BigIntObject ) ;
2024-03-02 16:45:12 +01:00
TRY ( serialize_big_int_object ( m_vm , m_serialized , value ) ) ;
2023-08-21 17:39:44 -06:00
}
2023-08-21 15:15:05 -06:00
// 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 ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : StringObject ) ;
2024-03-02 16:45:12 +01:00
TRY ( serialize_string_object ( m_vm , m_serialized , value ) ) ;
2023-08-21 15:15:05 -06:00
}
// 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 ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : DateObject ) ;
2024-03-02 16:45:12 +01:00
serialize_date_object ( m_serialized , value ) ;
2022-04-05 02:24:10 +02:00
}
2023-07-21 21:19:52 +02:00
2023-09-11 16:32:13 -06:00
// 12. Otherwise, if value has a [[RegExpMatcher]] internal slot, then set serialized to
// { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]],
// [[OriginalFlags]]: value.[[OriginalFlags]] }.
else if ( value . is_object ( ) & & is < JS : : RegExpObject > ( value . as_object ( ) ) ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : RegExpObject ) ;
2024-03-02 16:45:12 +01:00
TRY ( serialize_reg_exp_object ( m_vm , m_serialized , value ) ) ;
2023-09-11 16:32:13 -06:00
}
2023-09-11 18:06:43 -06:00
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
else if ( value . is_object ( ) & & is < JS : : ArrayBuffer > ( value . as_object ( ) ) ) {
2024-02-23 18:30:50 +01:00
TRY ( serialize_array_buffer ( m_vm , m_serialized , static_cast < JS : : ArrayBuffer & > ( value . as_object ( ) ) , m_for_storage ) ) ;
2023-09-11 18:06:43 -06:00
}
2023-09-12 16:56:35 -06:00
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
else if ( value . is_object ( ) & & is < JS : : TypedArrayBase > ( value . as_object ( ) ) ) {
2024-02-23 18:30:50 +01:00
TRY ( serialize_viewed_array_buffer ( m_vm , m_serialized , static_cast < JS : : TypedArrayBase & > ( value . as_object ( ) ) , m_for_storage , m_memory ) ) ;
2023-09-12 16:56:35 -06:00
} else if ( value . is_object ( ) & & is < JS : : DataView > ( value . as_object ( ) ) ) {
2024-02-23 18:30:50 +01:00
TRY ( serialize_viewed_array_buffer ( m_vm , m_serialized , static_cast < JS : : DataView & > ( value . as_object ( ) ) , m_for_storage , m_memory ) ) ;
2023-09-12 16:56:35 -06:00
}
2023-11-11 20:14:46 +02:00
// 15. Otherwise, if value has [[MapData]] internal slot, then:
else if ( value . is_object ( ) & & is < JS : : Map > ( value . as_object ( ) ) ) {
// 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : MapObject ) ;
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
}
// 16. Otherwise, if value has [[SetData]] internal slot, then:
else if ( value . is_object ( ) & & is < JS : : Set > ( value . as_object ( ) ) ) {
// 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : SetObject ) ;
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
}
2023-11-11 01:14:56 +02:00
// 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
else if ( value . is_object ( ) & & is < JS : : Error > ( value . as_object ( ) ) & & ! is < Bindings : : PlatformObject > ( 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 < String > 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.
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : ErrorObject ) ;
serialize_enum ( m_serialized , type ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( m_serialized , message . has_value ( ) ) ;
2023-11-11 01:14:56 +02:00
if ( message . has_value ( ) )
2024-02-23 18:30:50 +01:00
TRY ( serialize_string ( m_vm , m_serialized , * message ) ) ;
2023-11-11 01:14:56 +02:00
}
2023-11-11 20:14:46 +02:00
// 18. Otherwise, if value is an Array exotic object, then:
else if ( value . is_object ( ) & & is < JS : : Array > ( value . as_object ( ) ) ) {
// 1. Let valueLenDescriptor be ? OrdinaryGetOwnProperty(value, "length").
// 2. Let valueLen be valueLenDescriptor.[[Value]].
// NON-STANDARD: Array objects in LibJS do not have a real length property, so it must be accessed the usual way
u64 length = MUST ( JS : : length_of_array_like ( m_vm , value . as_object ( ) ) ) ;
// 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }.
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : ArrayObject ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( m_serialized , length ) ;
2023-11-11 20:14:46 +02:00
// 4. Set deep to true.
deep = true ;
}
2024-03-10 21:04:33 +01:00
// 19. Otherwise, if value is a platform object that is a serializable object:
2024-02-23 18:32:27 +01:00
else if ( value . is_object ( ) & & is < Bindings : : Serializable > ( value . as_object ( ) ) ) {
auto & serializable = dynamic_cast < Bindings : : Serializable & > ( value . as_object ( ) ) ;
2024-03-27 07:56:00 +01:00
// FIXME: 1. If value has a [[Detached]] internal slot whose value is true, then throw a "DataCloneError" DOMException.
2024-02-23 18:32:27 +01:00
2024-03-27 07:56:00 +01:00
// 2. Let typeString be the identifier of the primary interface of value.
// 3. Set serialized to { [[Type]]: typeString }.
serialize_enum ( m_serialized , ValueTag : : SerializableObject ) ;
2024-03-02 22:32:50 +01:00
TRY ( serialize_string ( m_vm , m_serialized , serializable . interface_name ( ) ) ) ;
2024-03-27 07:56:00 +01:00
// 4. Set deep to true
deep = true ;
2024-02-23 18:32:27 +01:00
}
2023-11-11 20:14:46 +02:00
2023-11-11 01:23:09 +02:00
// 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
else if ( value . is_object ( ) & & is < Bindings : : PlatformObject > ( value . as_object ( ) ) ) {
2024-10-12 20:56:21 +02:00
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize platform objects " _string ) ) ;
2023-11-11 01:23:09 +02:00
}
// 21. Otherwise, if IsCallable(value) is true, then throw a "DataCloneError" DOMException.
else if ( value . is_function ( ) ) {
2024-10-12 20:56:21 +02:00
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize functions " _string ) ) ;
2023-11-11 01:23:09 +02:00
}
// FIXME: 22. Otherwise, if value has any internal slot other than [[Prototype]] or [[Extensible]], then throw a "DataCloneError" DOMException.
// FIXME: 23. Otherwise, if value is an exotic object and value is not the %Object.prototype% intrinsic object associated with any realm, then throw a "DataCloneError" DOMException.
2023-11-11 20:14:46 +02:00
// 24. Otherwise:
2023-08-21 15:15:05 -06:00
else {
2023-11-11 20:14:46 +02:00
// 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
2024-03-15 21:32:51 +01:00
serialize_enum ( m_serialized , ValueTag : : Object ) ;
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
2023-08-21 15:15:05 -06:00
}
2023-07-21 21:19:52 +02:00
2023-08-21 15:15:05 -06:00
// 25. Set memory[value] to serialized.
2024-11-15 04:01:23 +13:00
m_memory . set ( make_root ( value ) , m_next_id + + ) ;
2023-11-11 20:14:46 +02:00
// 26. If deep is true, then:
if ( deep ) {
// 1. If value has a [[MapData]] internal slot, then:
if ( value . is_object ( ) & & is < JS : : Map > ( value . as_object ( ) ) ) {
auto const & map = static_cast < JS : : Map const & > ( value . as_object ( ) ) ;
// 1. Let copiedList be a new empty List.
Vector < JS : : Value > copied_list ;
copied_list . ensure_capacity ( map . map_size ( ) * 2 ) ;
// 2. For each Record { [[Key]], [[Value]] } entry of value.[[MapData]]:
for ( auto const & entry : static_cast < JS : : Map const & > ( value . as_object ( ) ) ) {
// 1. Let copiedEntry be a new Record { [[Key]]: entry.[[Key]], [[Value]]: entry.[[Value]] }.
// 2. If copiedEntry.[[Key]] is not the special value empty, append copiedEntry to copiedList.
copied_list . append ( entry . key ) ;
copied_list . append ( entry . value ) ;
}
u64 size = map . map_size ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & size ) , 2 ) ;
// 3. For each Record { [[Key]], [[Value]] } entry of copiedList:
for ( auto copied_value : copied_list ) {
// 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory).
// 2. Let serializedValue be ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory).
auto serialized_value = TRY ( structured_serialize_internal ( m_vm , copied_value , m_for_storage , m_memory ) ) ;
// 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
m_serialized . extend ( serialized_value ) ;
}
}
// 2. Otherwise, if value has a [[SetData]] internal slot, then:
else if ( value . is_object ( ) & & is < JS : : Set > ( value . as_object ( ) ) ) {
auto const & set = static_cast < JS : : Set const & > ( value . as_object ( ) ) ;
// 1. Let copiedList be a new empty List.
Vector < JS : : Value > copied_list ;
copied_list . ensure_capacity ( set . set_size ( ) ) ;
// 2. For each entry of value.[[SetData]]:
for ( auto const & entry : static_cast < JS : : Set const & > ( value . as_object ( ) ) ) {
// 1. If entry is not the special value empty, append entry to copiedList.
copied_list . append ( entry . key ) ;
}
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( m_serialized , set . set_size ( ) ) ;
2023-11-11 20:14:46 +02:00
// 3. For each entry of copiedList:
for ( auto copied_value : copied_list ) {
// 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory).
auto serialized_value = TRY ( structured_serialize_internal ( m_vm , copied_value , m_for_storage , m_memory ) ) ;
// 2. Append serializedEntry to serialized.[[SetData]].
m_serialized . extend ( serialized_value ) ;
}
}
2024-03-27 07:56:00 +01:00
// 3. Otherwise, if value is a platform object that is a serializable object, then perform the serialization steps for value's primary interface, given value, serialized, and forStorage.
else if ( value . is_object ( ) & & is < Bindings : : Serializable > ( value . as_object ( ) ) ) {
auto & serializable = dynamic_cast < Bindings : : Serializable & > ( value . as_object ( ) ) ;
TRY ( serializable . serialization_steps ( m_serialized , m_for_storage , m_memory ) ) ;
}
2023-11-11 20:14:46 +02:00
// 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
else {
u64 property_count = 0 ;
auto count_offset = m_serialized . size ( ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( m_serialized , property_count ) ;
2023-11-11 20:14:46 +02:00
for ( auto key : MUST ( value . as_object ( ) . enumerable_own_property_names ( JS : : Object : : PropertyKind : : Key ) ) ) {
auto property_key = MUST ( JS : : PropertyKey : : from_value ( m_vm , key ) ) ;
// 1. If ! HasOwnProperty(value, key) is true, then:
if ( MUST ( value . as_object ( ) . has_own_property ( property_key ) ) ) {
// 1. Let inputValue be ? value.[[Get]](key, value).
auto input_value = TRY ( value . as_object ( ) . internal_get ( property_key , value ) ) ;
2022-04-05 02:24:10 +02:00
2023-11-11 20:14:46 +02:00
// 2. Let outputValue be ? StructuredSerializeInternal(inputValue, forStorage, memory).
auto output_value = TRY ( structured_serialize_internal ( m_vm , input_value , m_for_storage , m_memory ) ) ;
2023-08-21 15:15:05 -06:00
2023-11-11 20:14:46 +02:00
// 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]].
2024-02-23 18:30:50 +01:00
TRY ( serialize_string ( m_vm , m_serialized , key . as_string ( ) ) ) ;
2023-11-11 20:14:46 +02:00
m_serialized . extend ( output_value ) ;
property_count + + ;
}
}
memcpy ( m_serialized . data ( ) + count_offset , & property_count , sizeof ( property_count ) ) ;
}
}
// 27. Return serialized.
2023-08-21 15:15:05 -06:00
return m_serialized ;
2022-04-05 02:24:10 +02:00
}
private :
JS : : VM & m_vm ;
2023-08-21 15:15:05 -06:00
SerializationMemory & m_memory ; // JS value -> index
2023-11-11 20:14:46 +02:00
u32 m_next_id { 0 } ;
2023-08-21 15:15:05 -06:00
SerializationRecord m_serialized ;
2023-09-11 18:06:43 -06:00
bool m_for_storage { false } ;
2024-02-23 18:30:50 +01:00
} ;
2023-07-21 21:19:52 +02:00
2024-03-02 16:45:12 +01:00
void serialize_boolean_primitive ( SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_boolean ( ) ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( serialized , value . as_bool ( ) ) ;
2024-03-02 16:45:12 +01:00
}
void serialize_number_primitive ( SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_number ( ) ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( serialized , value . as_double ( ) ) ;
2024-03-02 16:45:12 +01:00
}
WebIDL : : ExceptionOr < void > serialize_big_int_primitive ( JS : : VM & vm , SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_bigint ( ) ) ;
auto & val = value . as_bigint ( ) ;
TRY ( serialize_string ( vm , serialized , TRY_OR_THROW_OOM ( vm , val . to_string ( ) ) ) ) ;
return { } ;
}
WebIDL : : ExceptionOr < void > serialize_string_primitive ( JS : : VM & vm , SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_string ( ) ) ;
TRY ( serialize_string ( vm , serialized , value . as_string ( ) ) ) ;
return { } ;
}
void serialize_boolean_object ( SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : BooleanObject > ( value . as_object ( ) ) ) ;
auto & boolean_object = static_cast < JS : : BooleanObject & > ( value . as_object ( ) ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( serialized , boolean_object . boolean ( ) ) ;
2024-03-02 16:45:12 +01:00
}
void serialize_number_object ( SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : NumberObject > ( value . as_object ( ) ) ) ;
auto & number_object = static_cast < JS : : NumberObject & > ( value . as_object ( ) ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( serialized , number_object . number ( ) ) ;
2024-03-02 16:45:12 +01:00
}
WebIDL : : ExceptionOr < void > serialize_big_int_object ( JS : : VM & vm , SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : BigIntObject > ( value . as_object ( ) ) ) ;
auto & bigint_object = static_cast < JS : : BigIntObject & > ( value . as_object ( ) ) ;
TRY ( serialize_string ( vm , serialized , TRY_OR_THROW_OOM ( vm , bigint_object . bigint ( ) . to_string ( ) ) ) ) ;
return { } ;
}
WebIDL : : ExceptionOr < void > serialize_string_object ( JS : : VM & vm , SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : StringObject > ( value . as_object ( ) ) ) ;
auto & string_object = static_cast < JS : : StringObject & > ( value . as_object ( ) ) ;
TRY ( serialize_string ( vm , serialized , string_object . primitive_string ( ) ) ) ;
return { } ;
}
void serialize_date_object ( SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : Date > ( value . as_object ( ) ) ) ;
auto & date_object = static_cast < JS : : Date & > ( value . as_object ( ) ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( serialized , date_object . date_value ( ) ) ;
2024-03-02 16:45:12 +01:00
}
WebIDL : : ExceptionOr < void > serialize_reg_exp_object ( JS : : VM & vm , SerializationRecord & serialized , JS : : Value & value )
{
VERIFY ( value . is_object ( ) & & is < JS : : RegExpObject > ( value . as_object ( ) ) ) ;
auto & regexp_object = static_cast < JS : : RegExpObject & > ( value . as_object ( ) ) ;
// Note: A Regex<ECMA262> object is perfectly happy to be reconstructed with just the source+flags
// In the future, we could optimize the work being done on the deserialize step by serializing
// more of the internal state (the [[RegExpMatcher]] internal slot)
TRY ( serialize_string ( vm , serialized , TRY_OR_THROW_OOM ( vm , String : : from_byte_string ( regexp_object . pattern ( ) ) ) ) ) ;
TRY ( serialize_string ( vm , serialized , TRY_OR_THROW_OOM ( vm , String : : from_byte_string ( regexp_object . flags ( ) ) ) ) ) ;
return { } ;
}
2024-02-23 18:30:50 +01:00
WebIDL : : ExceptionOr < void > serialize_bytes ( JS : : VM & vm , Vector < u32 > & vector , ReadonlyBytes bytes )
{
// Append size of the buffer to the serialized structure.
u64 const size = bytes . size ( ) ;
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( vector , size ) ;
2024-02-23 18:30:50 +01:00
// Append the bytes of the buffer to the serialized structure.
u64 byte_position = 0 ;
while ( byte_position < size ) {
u32 combined_value = 0 ;
for ( u8 i = 0 ; i < 4 ; + + i ) {
u8 const byte = bytes [ byte_position ] ;
combined_value | = byte < < ( i * 8 ) ;
byte_position + + ;
if ( byte_position = = size )
break ;
2023-07-21 21:19:52 +02:00
}
2024-02-23 18:30:50 +01:00
TRY_OR_THROW_OOM ( vm , vector . try_append ( combined_value ) ) ;
2023-07-21 21:19:52 +02:00
}
2024-02-23 18:30:50 +01:00
return { } ;
}
2023-07-21 21:19:52 +02:00
2024-02-23 18:30:50 +01:00
WebIDL : : ExceptionOr < void > serialize_string ( JS : : VM & vm , Vector < u32 > & vector , DeprecatedFlyString const & string )
{
return serialize_bytes ( vm , vector , string . view ( ) . bytes ( ) ) ;
}
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
WebIDL : : ExceptionOr < void > serialize_string ( JS : : VM & vm , Vector < u32 > & vector , String const & string )
{
return serialize_bytes ( vm , vector , { string . code_points ( ) . bytes ( ) , string . code_points ( ) . byte_length ( ) } ) ;
}
2023-09-11 18:05:30 -06:00
2024-02-23 18:30:50 +01:00
WebIDL : : ExceptionOr < void > serialize_string ( JS : : VM & vm , Vector < u32 > & vector , JS : : PrimitiveString const & primitive_string )
{
auto string = primitive_string . utf8_string ( ) ;
TRY ( serialize_string ( vm , vector , string ) ) ;
return { } ;
}
WebIDL : : ExceptionOr < void > serialize_array_buffer ( JS : : VM & vm , Vector < u32 > & vector , JS : : ArrayBuffer const & array_buffer , bool for_storage )
{
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
// FIXME: 1. If IsSharedArrayBuffer(value) is true, then:
if ( false ) {
2024-10-21 13:35:24 +13:00
// 1. If the current principal settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
2024-02-23 18:30:50 +01:00
// NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
// over time and a SharedArrayBuffer cannot leave an agent cluster.
2024-10-21 13:35:24 +13:00
if ( current_principal_settings_object ( ) . cross_origin_isolated_capability ( ) = = CanUseCrossOriginIsolatedAPIs : : No )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer when cross-origin isolated " _string ) ;
2024-02-23 18:30:50 +01:00
// 2. If forStorage is true, then throw a "DataCloneError" DOMException.
if ( for_storage )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer for storage " _string ) ;
2024-02-23 18:30:50 +01:00
// FIXME: 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
// [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
// [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
// FIXME: 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]],
// [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
2023-07-21 21:19:52 +02:00
}
2024-02-23 18:30:50 +01:00
// 2. Otherwise:
else {
// 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
if ( array_buffer . is_detached ( ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize detached ArrayBuffer " _string ) ;
2023-09-11 18:06:43 -06:00
2024-02-23 18:30:50 +01:00
// 2. Let size be value.[[ArrayBufferByteLength]].
auto size = array_buffer . byte_length ( ) ;
// 3. Let dataCopy be ? CreateByteDataBlock(size).
// NOTE: This can throw a RangeError exception upon allocation failure.
auto data_copy = TRY ( JS : : create_byte_data_block ( vm , size ) ) ;
// 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
JS : : copy_data_block_bytes ( data_copy . buffer ( ) , 0 , array_buffer . buffer ( ) , 0 , size ) ;
2023-09-11 18:06:43 -06:00
2024-02-23 18:30:50 +01:00
// FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
// [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
2023-09-11 18:06:43 -06:00
if ( false ) {
}
2024-02-23 18:30:50 +01:00
// 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
2023-09-11 18:06:43 -06:00
else {
2024-03-15 21:32:51 +01:00
serialize_enum ( vector , ValueTag : : ArrayBuffer ) ;
2024-02-23 18:30:50 +01:00
TRY ( serialize_bytes ( vm , vector , data_copy . buffer ( ) . bytes ( ) ) ) ;
2023-09-11 18:06:43 -06:00
}
}
2024-02-23 18:30:50 +01:00
return { } ;
}
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
template < OneOf < JS : : TypedArrayBase , JS : : DataView > ViewType >
WebIDL : : ExceptionOr < void > serialize_viewed_array_buffer ( JS : : VM & vm , Vector < u32 > & vector , ViewType const & view , bool for_storage , SerializationMemory & memory )
{
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
2023-10-15 09:46:09 -04:00
2024-02-23 18:30:50 +01:00
auto view_record = [ & ] ( ) {
2023-10-15 09:46:09 -04:00
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
2024-02-23 18:30:50 +01:00
return JS : : make_data_view_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
2023-10-15 09:46:09 -04:00
} else {
2024-02-23 18:30:50 +01:00
return JS : : make_typed_array_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
2023-10-15 09:46:09 -04:00
}
2024-02-23 18:30:50 +01:00
} ( ) ;
// 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
if ( JS : : is_view_out_of_bounds ( view_record ) )
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , MUST ( String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . message ( ) , " DataView " sv ) ) ) ;
} else {
if ( JS : : is_typed_array_out_of_bounds ( view_record ) )
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , MUST ( String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . message ( ) , " TypedArray " sv ) ) ) ;
}
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
// 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
2024-11-24 14:20:57 +01:00
JS : : Value buffer = view . viewed_array_buffer ( ) ;
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
// 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
2024-11-24 14:20:57 +01:00
auto buffer_serialized = TRY ( structured_serialize_internal ( vm , buffer , for_storage , memory ) ) ;
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
// 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
// NOTE: We currently only implement this for ArrayBuffer
2024-11-24 14:20:57 +01:00
// NOTE: Object reference + memory check is required when ArrayBuffer is transfered.
auto tag = buffer_serialized [ 0 ] ;
VERIFY ( tag = = ValueTag : : ArrayBuffer | | ( tag = = ValueTag : : ObjectReference & & memory . contains ( buffer ) ) ) ;
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
// 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView",
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
2024-03-15 21:32:51 +01:00
serialize_enum ( vector , ValueTag : : ArrayBufferView ) ;
2024-02-23 18:30:50 +01:00
vector . extend ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
TRY ( serialize_string ( vm , vector , " DataView " _string ) ) ; // [[Constructor]]
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( vector , JS : : get_view_byte_length ( view_record ) ) ;
serialize_primitive_type ( vector , view . byte_offset ( ) ) ;
2024-02-23 18:30:50 +01:00
}
2023-09-12 16:56:35 -06:00
2024-02-23 18:30:50 +01:00
// 6. Otherwise:
else {
// 1. Assert: value has a [[TypedArrayName]] internal slot.
// NOTE: Handled by constexpr check and template constraints
// 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]],
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
// [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
2024-03-15 21:32:51 +01:00
serialize_enum ( vector , ValueTag : : ArrayBufferView ) ;
2024-02-23 18:30:50 +01:00
vector . extend ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
TRY ( serialize_string ( vm , vector , view . element_name ( ) ) ) ; // [[Constructor]]
2024-03-09 21:49:15 +01:00
serialize_primitive_type ( vector , JS : : typed_array_byte_length ( view_record ) ) ;
serialize_primitive_type ( vector , view . byte_offset ( ) ) ;
serialize_primitive_type ( vector , JS : : typed_array_length ( view_record ) ) ;
2023-09-12 16:56:35 -06:00
}
2024-02-23 18:30:50 +01:00
return { } ;
}
template WebIDL : : ExceptionOr < void > serialize_viewed_array_buffer ( JS : : VM & vm , Vector < u32 > & vector , JS : : TypedArrayBase const & view , bool for_storage , SerializationMemory & memory ) ;
template WebIDL : : ExceptionOr < void > serialize_viewed_array_buffer ( JS : : VM & vm , Vector < u32 > & vector , JS : : DataView const & view , bool for_storage , SerializationMemory & memory ) ;
2022-04-05 02:24:10 +02:00
class Deserializer {
public :
2024-03-10 20:37:41 +01:00
Deserializer ( JS : : VM & vm , JS : : Realm & target_realm , ReadonlySpan < u32 > serialized , DeserializationMemory & memory , Optional < size_t > position = { } )
2022-04-05 02:24:10 +02:00
: m_vm ( vm )
2023-11-11 20:14:46 +02:00
, m_serialized ( serialized )
, m_memory ( memory )
2024-03-10 20:37:41 +01:00
, m_position ( position . value_or ( 0 ) )
2022-04-05 02:24:10 +02:00
{
2023-09-12 16:41:13 -06:00
VERIFY ( vm . current_realm ( ) = = & target_realm ) ;
2022-04-05 02:24:10 +02:00
}
2024-03-10 20:37:41 +01:00
size_t position ( ) const { return m_position ; }
2023-11-11 20:14:46 +02:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
WebIDL : : ExceptionOr < JS : : Value > deserialize ( )
2022-04-05 02:24:10 +02:00
{
2024-03-15 21:32:51 +01:00
auto tag = deserialize_primitive_type < ValueTag > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
// 2. If memory[serialized] exists, then return memory[serialized].
if ( tag = = ValueTag : : ObjectReference ) {
auto index = m_serialized [ m_position + + ] ;
2023-12-12 13:30:16 -07:00
if ( index = = NumericLimits < u32 > : : max ( ) ) {
2023-12-19 15:25:33 -07:00
return JS : : Object : : create ( * m_vm . current_realm ( ) , nullptr ) ;
2023-12-12 13:30:16 -07:00
}
2023-11-11 20:14:46 +02:00
return m_memory [ index ] ;
}
// 3. Let deep be false.
auto deep = false ;
// 4. Let value be an uninitialized value.
JS : : Value value ;
auto is_primitive = false ;
switch ( tag ) {
// 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]].
case ValueTag : : UndefinedPrimitive : {
value = JS : : js_undefined ( ) ;
is_primitive = true ;
break ;
}
case ValueTag : : NullPrimitive : {
value = JS : : js_null ( ) ;
is_primitive = true ;
break ;
}
case ValueTag : : BooleanPrimitive : {
2024-03-02 20:39:45 +01:00
value = JS : : Value { deserialize_boolean_primitive ( m_serialized , m_position ) } ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
}
case ValueTag : : NumberPrimitive : {
2024-03-02 20:39:45 +01:00
value = JS : : Value { deserialize_number_primitive ( m_serialized , m_position ) } ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
}
case ValueTag : : BigIntPrimitive : {
auto big_int = TRY ( deserialize_big_int_primitive ( m_vm , m_serialized , m_position ) ) ;
value = JS : : Value { big_int } ;
is_primitive = true ;
break ;
}
case ValueTag : : StringPrimitive : {
auto string = TRY ( deserialize_string_primitive ( m_vm , m_serialized , m_position ) ) ;
value = JS : : Value { string } ;
is_primitive = true ;
break ;
}
// 6. Otherwise, if serialized.[[Type]] is "Boolean", then set value to a new Boolean object in targetRealm whose [[BooleanData]] internal slot value is serialized.[[BooleanData]].
case BooleanObject : {
2024-03-02 20:39:45 +01:00
value = deserialize_boolean_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// 7. Otherwise, if serialized.[[Type]] is "Number", then set value to a new Number object in targetRealm whose [[NumberData]] internal slot value is serialized.[[NumberData]].
case ValueTag : : NumberObject : {
2024-03-02 20:39:45 +01:00
value = deserialize_number_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// 8. Otherwise, if serialized.[[Type]] is "BigInt", then set value to a new BigInt object in targetRealm whose [[BigIntData]] internal slot value is serialized.[[BigIntData]].
case ValueTag : : BigIntObject : {
2024-03-02 20:39:45 +01:00
value = TRY ( deserialize_big_int_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// 9. Otherwise, if serialized.[[Type]] is "String", then set value to a new String object in targetRealm whose [[StringData]] internal slot value is serialized.[[StringData]].
case ValueTag : : StringObject : {
2024-03-02 20:39:45 +01:00
value = TRY ( deserialize_string_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// 10. Otherwise, if serialized.[[Type]] is "Date", then set value to a new Date object in targetRealm whose [[DateValue]] internal slot value is serialized.[[DateValue]].
case ValueTag : : DateObject : {
2024-03-02 20:39:45 +01:00
value = deserialize_date_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]],
// whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]].
case ValueTag : : RegExpObject : {
2024-03-02 20:39:45 +01:00
value = TRY ( deserialize_reg_exp_object ( * m_vm . current_realm ( ) , m_serialized , m_position ) ) ;
2023-11-11 20:14:46 +02:00
break ;
}
// FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
// FIXME: 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:
// 14. Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
case ValueTag : : ArrayBuffer : {
auto * realm = m_vm . current_realm ( ) ;
// If this throws an exception, catch it, and then throw a "DataCloneError" DOMException.
auto bytes_or_error = deserialize_bytes ( m_vm , m_serialized , m_position ) ;
if ( bytes_or_error . is_error ( ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " out of memory " _string ) ;
2023-11-11 20:14:46 +02:00
value = JS : : ArrayBuffer : : create ( * realm , bytes_or_error . release_value ( ) ) ;
break ;
}
// FIXME: 15. Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal slot value is a serialized.[[ArrayBufferMaxByteLength]].
// 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:
case ValueTag : : ArrayBufferView : {
auto * realm = m_vm . current_realm ( ) ;
auto array_buffer_value = TRY ( deserialize ( ) ) ;
auto & array_buffer = verify_cast < JS : : ArrayBuffer > ( array_buffer_value . as_object ( ) ) ;
auto constructor_name = TRY ( deserialize_string ( m_vm , m_serialized , m_position ) ) ;
2024-03-09 21:49:15 +01:00
u32 byte_length = deserialize_primitive_type < u32 > ( m_serialized , m_position ) ;
u32 byte_offset = deserialize_primitive_type < u32 > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
if ( constructor_name = = " DataView " sv ) {
value = JS : : DataView : : create ( * realm , & array_buffer , byte_length , byte_offset ) ;
} else {
2024-03-09 21:49:15 +01:00
u32 array_length = deserialize_primitive_type < u32 > ( m_serialized , m_position ) ;
2024-11-15 04:01:23 +13:00
GC : : Ptr < JS : : TypedArrayBase > typed_array_ptr ;
2023-09-12 16:56:35 -06:00
# define CREATE_TYPED_ARRAY(ClassName) \
if ( constructor_name = = # ClassName # # sv ) \
typed_array_ptr = JS : : ClassName : : create ( * realm , array_length , array_buffer ) ;
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
CREATE_TYPED_ARRAY ( ClassName )
2023-11-11 20:14:46 +02:00
JS_ENUMERATE_TYPED_ARRAYS
2023-09-12 16:56:35 -06:00
# undef __JS_ENUMERATE
# undef CREATE_TYPED_ARRAY
2023-11-11 20:14:46 +02:00
VERIFY ( typed_array_ptr ! = nullptr ) ; // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
typed_array_ptr - > set_byte_length ( byte_length ) ;
typed_array_ptr - > set_byte_offset ( byte_offset ) ;
value = typed_array_ptr ;
2023-09-12 16:56:35 -06:00
}
2023-11-11 20:14:46 +02:00
break ;
}
// 17. Otherwise, if serialized.[[Type]] is "Map", then:
case ValueTag : : MapObject : {
auto & realm = * m_vm . current_realm ( ) ;
// 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.
value = JS : : Map : : create ( realm ) ;
// 2. Set deep to true.
deep = true ;
break ;
}
// 18. Otherwise, if serialized.[[Type]] is "Set", then:
case ValueTag : : SetObject : {
auto & realm = * m_vm . current_realm ( ) ;
// 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List.
value = JS : : Set : : create ( realm ) ;
// 2. Set deep to true.
deep = true ;
break ;
}
// 19. Otherwise, if serialized.[[Type]] is "Array", then:
case ValueTag : : ArrayObject : {
auto & realm = * m_vm . current_realm ( ) ;
// 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]].
// 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto).
2024-03-09 21:49:15 +01:00
auto length = deserialize_primitive_type < u64 > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
value = MUST ( JS : : Array : : create ( realm , length ) ) ;
// 3. Set deep to true.
deep = true ;
break ;
}
// 20. Otherwise, if serialized.[[Type]] is "Object", then:
case ValueTag : : Object : {
auto & realm = * m_vm . current_realm ( ) ;
// 1. Set value to a new Object in targetRealm.
value = JS : : Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
// 2. Set deep to true.
deep = true ;
break ;
}
// 21. Otherwise, if serialized.[[Type]] is "Error", then:
case ValueTag : : ErrorObject : {
auto & realm = * m_vm . current_realm ( ) ;
2024-03-15 21:32:51 +01:00
auto type = deserialize_primitive_type < ErrorType > ( m_serialized , m_position ) ;
2024-03-09 21:49:15 +01:00
auto has_message = deserialize_primitive_type < bool > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
if ( has_message ) {
auto message = TRY ( deserialize_string ( m_vm , m_serialized , m_position ) ) ;
switch ( type ) {
case ErrorType : : Error :
value = JS : : Error : : create ( realm , message ) ;
break ;
2023-11-11 01:14:56 +02:00
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
case ErrorType : : ClassName : \
2023-11-11 20:14:46 +02:00
value = JS : : ClassName : : create ( realm , message ) ; \
2023-11-11 01:14:56 +02:00
break ;
2023-11-11 20:14:46 +02:00
JS_ENUMERATE_NATIVE_ERRORS
2023-11-11 01:14:56 +02:00
# undef __JS_ENUMERATE
2023-11-11 20:14:46 +02:00
}
} else {
switch ( type ) {
case ErrorType : : Error :
value = JS : : Error : : create ( realm ) ;
break ;
2023-11-11 01:14:56 +02:00
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
case ErrorType : : ClassName : \
2023-11-11 20:14:46 +02:00
value = JS : : ClassName : : create ( realm ) ; \
2023-11-11 01:14:56 +02:00
break ;
2023-11-11 20:14:46 +02:00
JS_ENUMERATE_NATIVE_ERRORS
2023-11-11 01:14:56 +02:00
# undef __JS_ENUMERATE
}
}
2023-11-11 20:14:46 +02:00
break ;
}
// 22. Otherwise:
default :
2024-02-23 18:32:27 +01:00
VERIFY ( tag = = ValueTag : : SerializableObject ) ;
auto & realm = * m_vm . current_realm ( ) ;
// 1. Let interfaceName be serialized.[[Type]].
auto interface_name = TRY ( deserialize_string ( m_vm , m_serialized , m_position ) ) ;
// 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
if ( ! is_interface_exposed_on_target_realm ( interface_name , realm ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( realm , " Unsupported type " _string ) ;
2024-02-23 18:32:27 +01:00
// 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
2024-11-23 15:36:22 +01:00
value = create_serialized_type ( interface_name , realm ) ;
2024-02-23 18:32:27 +01:00
// 4. Set deep to true.
deep = true ;
2023-11-11 20:14:46 +02:00
}
// 23. Set memory[serialized] to value.
// IMPLEMENTATION DEFINED: We don't add primitive values to the memory to match the serialization indices (which also doesn't add them)
if ( ! is_primitive )
m_memory . append ( value ) ;
// 24. If deep is true, then:
if ( deep ) {
// 1. If serialized.[[Type]] is "Map", then:
if ( tag = = ValueTag : : MapObject ) {
auto & map = static_cast < JS : : Map & > ( value . as_object ( ) ) ;
2024-03-09 21:49:15 +01:00
auto length = deserialize_primitive_type < u64 > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]:
for ( u64 i = 0u ; i < length ; + + i ) {
// 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory).
auto deserialized_key = TRY ( deserialize ( ) ) ;
// 2. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
auto deserialized_value = TRY ( deserialize ( ) ) ;
// 3. Append { [[Key]]: deserializedKey, [[Value]]: deserializedValue } to value.[[MapData]].
map . map_set ( deserialized_key , deserialized_value ) ;
}
}
// 2. Otherwise, if serialized.[[Type]] is "Set", then:
else if ( tag = = ValueTag : : SetObject ) {
auto & set = static_cast < JS : : Set & > ( value . as_object ( ) ) ;
2024-03-09 21:49:15 +01:00
auto length = deserialize_primitive_type < u64 > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
// 1. For each entry of serialized.[[SetData]]:
for ( u64 i = 0u ; i < length ; + + i ) {
// 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory).
auto deserialized_entry = TRY ( deserialize ( ) ) ;
// 2. Append deserializedEntry to value.[[SetData]].
set . set_add ( deserialized_entry ) ;
}
}
// 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then:
else if ( tag = = ValueTag : : ArrayObject | | tag = = ValueTag : : Object ) {
auto & object = value . as_object ( ) ;
2024-03-09 21:49:15 +01:00
auto length = deserialize_primitive_type < u64 > ( m_serialized , m_position ) ;
2023-11-11 20:14:46 +02:00
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]:
for ( u64 i = 0u ; i < length ; + + i ) {
auto key = TRY ( deserialize_string ( m_vm , m_serialized , m_position ) ) ;
// 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
auto deserialized_value = TRY ( deserialize ( ) ) ;
// 2. Let result be ! CreateDataProperty(value, entry.[[Key]], deserializedValue).
2023-12-16 17:49:34 +03:30
auto result = MUST ( object . create_data_property ( key . to_byte_string ( ) , deserialized_value ) ) ;
2023-11-11 20:14:46 +02:00
// 3. Assert: result is true.
VERIFY ( result ) ;
}
}
// 4. Otherwise:
else {
2024-02-23 18:32:27 +01:00
// 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
auto & serializable = dynamic_cast < Bindings : : Serializable & > ( value . as_object ( ) ) ;
2024-03-10 19:48:33 +01:00
TRY ( serializable . deserialization_steps ( m_serialized , m_position , m_memory ) ) ;
2022-04-05 02:24:10 +02:00
}
}
2023-11-11 20:14:46 +02:00
// 25. Return value.
return value ;
2022-04-05 02:24:10 +02:00
}
private :
JS : : VM & m_vm ;
2023-11-11 20:14:46 +02:00
ReadonlySpan < u32 > m_serialized ;
2024-11-15 04:01:23 +13:00
GC : : MarkedVector < JS : : Value > m_memory ; // Index -> JS value
2024-03-10 20:37:41 +01:00
size_t m_position { 0 } ;
2023-07-21 21:19:52 +02:00
2024-11-23 15:36:22 +01:00
static GC : : Ref < Bindings : : PlatformObject > create_serialized_type ( StringView interface_name , JS : : Realm & realm )
2024-02-23 18:32:27 +01:00
{
if ( interface_name = = " Blob " sv )
return FileAPI : : Blob : : create ( realm ) ;
2024-02-23 22:14:18 +01:00
if ( interface_name = = " File " sv )
return FileAPI : : File : : create ( realm ) ;
2024-03-18 21:58:25 +01:00
if ( interface_name = = " FileList " sv )
return FileAPI : : FileList : : create ( realm ) ;
2024-11-22 12:17:18 +01:00
if ( interface_name = = " DOMException " sv )
return WebIDL : : DOMException : : create ( realm ) ;
2024-03-09 22:07:22 +01:00
if ( interface_name = = " DOMMatrixReadOnly " sv )
return Geometry : : DOMMatrixReadOnly : : create ( realm ) ;
2024-03-09 22:41:26 +01:00
if ( interface_name = = " DOMMatrix " sv )
return Geometry : : DOMMatrix : : create ( realm ) ;
2024-03-09 23:02:22 +01:00
if ( interface_name = = " DOMPointReadOnly " sv )
return Geometry : : DOMPointReadOnly : : create ( realm ) ;
2024-03-09 23:10:11 +01:00
if ( interface_name = = " DOMPoint " sv )
return Geometry : : DOMPoint : : create ( realm ) ;
2024-03-10 09:02:54 +01:00
if ( interface_name = = " DOMRectReadOnly " sv )
return Geometry : : DOMRectReadOnly : : create ( realm ) ;
2024-03-10 09:43:00 +01:00
if ( interface_name = = " DOMRect " sv )
return Geometry : : DOMRect : : create ( realm ) ;
2024-03-10 20:48:00 +01:00
if ( interface_name = = " CryptoKey " sv )
return Crypto : : CryptoKey : : create ( realm ) ;
2024-03-12 21:08:46 +01:00
if ( interface_name = = " DOMQuad " sv )
return Geometry : : DOMQuad : : create ( realm ) ;
2024-02-23 18:32:27 +01:00
VERIFY_NOT_REACHED ( ) ;
}
// FIXME: Consolidate this function with the similar is_interface_exposed_on_target_realm() used when transferring objects.
// Also, the name parameter would be better off being the interface name (as a string) so that we don't need a switch statement.
static bool is_interface_exposed_on_target_realm ( StringView interface_name , JS : : Realm & realm )
{
auto const & intrinsics = Bindings : : host_defined_intrinsics ( realm ) ;
return intrinsics . is_exposed ( interface_name ) ;
}
2024-02-22 18:28:34 +01:00
} ;
2023-11-11 20:14:46 +02:00
2024-03-02 20:39:45 +01:00
bool deserialize_boolean_primitive ( ReadonlySpan < u32 > const & serialized , size_t & position )
{
2024-03-09 21:49:15 +01:00
return deserialize_primitive_type < bool > ( serialized , position ) ;
2024-03-02 20:39:45 +01:00
}
double deserialize_number_primitive ( ReadonlySpan < u32 > const & serialized , size_t & position )
{
2024-03-09 21:49:15 +01:00
return deserialize_primitive_type < double > ( serialized , position ) ;
2024-03-02 20:39:45 +01:00
}
2024-11-15 04:01:23 +13:00
GC : : Ref < JS : : BooleanObject > deserialize_boolean_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
auto boolean_primitive = deserialize_boolean_primitive ( serialized , position ) ;
return JS : : BooleanObject : : create ( realm , boolean_primitive ) ;
}
2024-11-15 04:01:23 +13:00
GC : : Ref < JS : : NumberObject > deserialize_number_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
auto number_primitive = deserialize_number_primitive ( serialized , position ) ;
return JS : : NumberObject : : create ( realm , number_primitive ) ;
}
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < JS : : BigIntObject > > deserialize_big_int_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
auto big_int_primitive = TRY ( deserialize_big_int_primitive ( realm . vm ( ) , serialized , position ) ) ;
return JS : : BigIntObject : : create ( realm , big_int_primitive ) ;
}
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < JS : : StringObject > > deserialize_string_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
auto string_primitive = TRY ( deserialize_string_primitive ( realm . vm ( ) , serialized , position ) ) ;
return JS : : StringObject : : create ( realm , string_primitive , realm . intrinsics ( ) . string_prototype ( ) ) ;
}
2024-11-15 04:01:23 +13:00
GC : : Ref < JS : : Date > deserialize_date_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
2024-03-09 21:49:15 +01:00
auto double_value = deserialize_primitive_type < double > ( serialized , position ) ;
2024-03-02 20:39:45 +01:00
return JS : : Date : : create ( realm , double_value ) ;
}
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < JS : : RegExpObject > > deserialize_reg_exp_object ( JS : : Realm & realm , ReadonlySpan < u32 > const & serialized , size_t & position )
2024-03-02 20:39:45 +01:00
{
auto pattern = TRY ( deserialize_string_primitive ( realm . vm ( ) , serialized , position ) ) ;
auto flags = TRY ( deserialize_string_primitive ( realm . vm ( ) , serialized , position ) ) ;
return TRY ( JS : : regexp_create ( realm . vm ( ) , move ( pattern ) , move ( flags ) ) ) ;
}
2024-02-22 18:28:34 +01:00
WebIDL : : ExceptionOr < ByteBuffer > deserialize_bytes ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
{
2024-03-09 21:49:15 +01:00
u64 const size = deserialize_primitive_type < u64 > ( vector , position ) ;
2024-02-22 18:28:34 +01:00
auto bytes = TRY_OR_THROW_OOM ( vm , ByteBuffer : : create_uninitialized ( size ) ) ;
u64 byte_position = 0 ;
while ( position < vector . size ( ) & & byte_position < size ) {
for ( u8 i = 0 ; i < 4 ; + + i ) {
bytes [ byte_position + + ] = ( vector [ position ] > > ( i * 8 ) & 0xFF ) ;
if ( byte_position = = size )
break ;
}
position + + ;
2023-09-11 18:05:30 -06:00
}
2024-02-22 18:28:34 +01:00
return bytes ;
}
2023-09-11 18:05:30 -06:00
2024-02-22 18:28:34 +01:00
WebIDL : : ExceptionOr < String > deserialize_string ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
{
auto bytes = TRY ( deserialize_bytes ( vm , vector , position ) ) ;
return TRY_OR_THROW_OOM ( vm , String : : from_utf8 ( StringView { bytes } ) ) ;
}
2023-09-12 16:41:13 -06:00
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < JS : : PrimitiveString > > deserialize_string_primitive ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2024-02-22 18:28:34 +01:00
{
auto bytes = TRY ( deserialize_bytes ( vm , vector , position ) ) ;
2023-07-21 21:19:52 +02:00
2024-02-22 18:28:34 +01:00
return TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & vm , & bytes ] ( ) {
return JS : : PrimitiveString : : create ( vm , StringView { bytes } ) ;
} ) ) ;
}
2023-07-21 21:19:52 +02:00
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < JS : : BigInt > > deserialize_big_int_primitive ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2024-02-22 18:28:34 +01:00
{
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 ( ) ;
} ) ) ;
auto bigint = MUST ( : : Crypto : : SignedBigInteger : : from_base ( 10 , string_view . substring_view ( 0 , string_view . length ( ) - 1 ) ) ) ;
return JS : : BigInt : : create ( vm , bigint ) ;
}
2022-04-05 02:24:10 +02:00
2023-12-07 15:53:38 -07:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < SerializedTransferRecord > structured_serialize_with_transfer ( JS : : VM & vm , JS : : Value value , Vector < GC : : Root < JS : : Object > > const & transfer_list )
2023-12-07 15:53:38 -07:00
{
// 1. Let memory be an empty map.
SerializationMemory memory = { } ;
// 2. For each transferable of transferList:
for ( auto const & transferable : transfer_list ) {
2024-11-24 14:20:57 +01:00
auto is_array_buffer = is < JS : : ArrayBuffer > ( * transferable ) ;
2023-12-07 15:53:38 -07:00
// 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException.
2024-11-24 14:20:57 +01:00
// FIXME: Handle transferring objects with [[Detached]] internal slot.
if ( ! is_array_buffer & & ! is < Bindings : : Transferable > ( * transferable ) ) {
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer type " _string ) ;
2023-12-07 15:53:38 -07:00
}
2024-11-24 14:20:57 +01:00
// 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
if ( is_array_buffer & & dynamic_cast < JS : : ArrayBuffer & > ( * transferable ) . is_shared_array_buffer ( ) ) {
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer shared array buffer " _string ) ;
}
2023-12-07 15:53:38 -07:00
// 3. If memory[transferable] exists, then throw a "DataCloneError" DOMException.
2023-12-18 14:01:39 -07:00
auto transferable_value = JS : : Value ( transferable ) ;
2023-12-12 13:30:16 -07:00
if ( memory . contains ( transferable_value ) ) {
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer value twice " _string ) ;
2023-12-07 15:53:38 -07:00
}
// 4. Set memory[transferable] to { [[Type]]: an uninitialized value }.
2024-11-24 14:20:57 +01:00
memory . set ( GC : : make_root ( transferable_value ) , memory . size ( ) ) ;
2023-12-07 15:53:38 -07:00
}
// 3. Let serialized be ? StructuredSerializeInternal(value, false, memory).
auto serialized = TRY ( structured_serialize_internal ( vm , value , false , memory ) ) ;
// 4. Let transferDataHolders be a new empty List.
Vector < TransferDataHolder > transfer_data_holders ;
transfer_data_holders . ensure_capacity ( transfer_list . size ( ) ) ;
// 5. For each transferable of transferList:
for ( auto & transferable : transfer_list ) {
2024-11-24 14:20:57 +01:00
auto is_array_buffer = is < JS : : ArrayBuffer > ( * transferable ) ;
auto is_detached = is_array_buffer & & dynamic_cast < JS : : ArrayBuffer & > ( * transferable ) . is_detached ( ) ;
// 1. If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
if ( is_detached ) {
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer detached buffer " _string ) ;
}
2023-12-07 15:53:38 -07:00
// 2. If transferable has a [[Detached]] internal slot and transferable.[[Detached]] is true, then throw a "DataCloneError" DOMException.
2023-12-12 13:30:16 -07:00
if ( is < Bindings : : Transferable > ( * transferable ) ) {
auto & transferable_object = dynamic_cast < Bindings : : Transferable & > ( * transferable ) ;
2023-12-07 15:53:38 -07:00
if ( transferable_object . is_detached ( ) ) {
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Value already transferred " _string ) ;
2023-12-07 15:53:38 -07:00
}
}
// 3. Let dataHolder be memory[transferable].
// IMPLEMENTATION DEFINED: We just create a data holder here, our memory holds indices into the SerializationRecord
TransferDataHolder data_holder ;
2024-11-24 14:20:57 +01:00
// 4. If transferable has an [[ArrayBufferData]] internal slot, then:
if ( is_array_buffer ) {
// 1. If transferable has an [[ArrayBufferMaxByteLength]] internal slot, then:
auto & array_buffer = dynamic_cast < JS : : ArrayBuffer & > ( * transferable ) ;
if ( ! array_buffer . is_fixed_length ( ) ) {
// 1. Set dataHolder.[[Type]] to "ResizableArrayBuffer".
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
// 4. Set dataHolder.[[ArrayBufferMaxByteLength]] to transferable.[[ArrayBufferMaxByteLength]].
serialize_enum < TransferType > ( data_holder . data , TransferType : : ResizableArrayBuffer ) ;
MUST ( serialize_bytes ( vm , data_holder . data , array_buffer . buffer ( ) . bytes ( ) ) ) ; // serializes both byte length and bytes
serialize_primitive_type < size_t > ( data_holder . data , array_buffer . max_byte_length ( ) ) ;
}
// 2. Otherwise:
else {
// 1. Set dataHolder.[[Type]] to "ArrayBuffer".
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
serialize_enum < TransferType > ( data_holder . data , TransferType : : ArrayBuffer ) ;
MUST ( serialize_bytes ( vm , data_holder . data , array_buffer . buffer ( ) . bytes ( ) ) ) ; // serializes both byte length and bytes
}
// 3. Perform ? DetachArrayBuffer(transferable).
// NOTE: Specifications can use the [[ArrayBufferDetachKey]] internal slot to prevent ArrayBuffers from being detached. This is used in WebAssembly JavaScript Interface, for example. See: https://html.spec.whatwg.org/multipage/references.html#refsWASMJS
TRY ( JS : : detach_array_buffer ( vm , array_buffer ) ) ;
2023-12-07 15:53:38 -07:00
}
// 5. Otherwise:
else {
// 1. Assert: transferable is a platform object that is a transferable object.
2023-12-12 13:30:16 -07:00
auto & transferable_object = dynamic_cast < Bindings : : Transferable & > ( * transferable ) ;
VERIFY ( is < Bindings : : PlatformObject > ( * transferable ) ) ;
2023-12-07 15:53:38 -07:00
// 2. Let interfaceName be the identifier of the primary interface of transferable.
auto interface_name = transferable_object . primary_interface ( ) ;
// 3. Set dataHolder.[[Type]] to interfaceName.
2024-11-24 14:20:57 +01:00
serialize_enum < TransferType > ( data_holder . data , interface_name ) ;
2023-12-07 15:53:38 -07:00
// 4. Perform the appropriate transfer steps for the interface identified by interfaceName, given transferable and dataHolder.
TRY ( transferable_object . transfer_steps ( data_holder ) ) ;
// 5. Set transferable.[[Detached]] to true.
transferable_object . set_detached ( true ) ;
}
// 6. Append dataHolder to transferDataHolders.
transfer_data_holders . append ( move ( data_holder ) ) ;
}
// 6. Return { [[Serialized]]: serialized, [[TransferDataHolders]]: transferDataHolders }.
return SerializedTransferRecord { . serialized = move ( serialized ) , . transfer_data_holders = move ( transfer_data_holders ) } ;
}
2024-11-24 14:20:57 +01:00
static bool is_interface_exposed_on_target_realm ( TransferType name , JS : : Realm & realm )
2023-12-07 15:53:38 -07:00
{
auto const & intrinsics = Bindings : : host_defined_intrinsics ( realm ) ;
2024-11-24 14:20:57 +01:00
switch ( name ) {
2023-12-07 15:53:38 -07:00
case TransferType : : MessagePort :
return intrinsics . is_exposed ( " MessagePort " sv ) ;
break ;
default :
2024-11-24 14:20:57 +01:00
dbgln ( " Unknown interface type for transfer: {} " , to_underlying ( name ) ) ;
2023-12-07 15:53:38 -07:00
break ;
}
return false ;
}
2024-11-15 04:01:23 +13:00
static WebIDL : : ExceptionOr < GC : : Ref < Bindings : : PlatformObject > > create_transferred_value ( TransferType name , JS : : Realm & target_realm , TransferDataHolder & transfer_data_holder )
2023-12-07 15:53:38 -07:00
{
switch ( name ) {
case TransferType : : MessagePort : {
auto message_port = HTML : : MessagePort : : create ( target_realm ) ;
TRY ( message_port - > transfer_receiving_steps ( transfer_data_holder ) ) ;
return message_port ;
}
2024-11-24 14:20:57 +01:00
case TransferType : : ArrayBuffer :
case TransferType : : ResizableArrayBuffer :
dbgln ( " ArrayBuffer ({}) is not a platform object. " , to_underlying ( name ) ) ;
break ;
2023-12-07 15:53:38 -07:00
}
VERIFY_NOT_REACHED ( ) ;
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserializewithtransfer
2023-12-18 17:24:31 -07:00
WebIDL : : ExceptionOr < DeserializedTransferRecord > structured_deserialize_with_transfer ( JS : : VM & vm , SerializedTransferRecord & serialize_with_transfer_result )
2023-12-07 15:53:38 -07:00
{
auto & target_realm = * vm . current_realm ( ) ;
// 1. Let memory be an empty map.
auto memory = DeserializationMemory ( vm . heap ( ) ) ;
// 2. Let transferredValues be a new empty List.
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < JS : : Object > > transferred_values ;
2023-12-07 15:53:38 -07:00
// 3. For each transferDataHolder of serializeWithTransferResult.[[TransferDataHolders]]:
for ( auto & transfer_data_holder : serialize_with_transfer_result . transfer_data_holders ) {
2024-11-24 14:20:57 +01:00
if ( transfer_data_holder . data . is_empty ( ) )
continue ;
2023-12-07 15:53:38 -07:00
// 1. Let value be an uninitialized value.
JS : : Value value ;
2024-11-24 14:20:57 +01:00
size_t data_holder_position = 0 ;
auto type = deserialize_primitive_type < TransferType > ( transfer_data_holder . data . span ( ) , data_holder_position ) ;
// 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm
2023-12-07 15:53:38 -07:00
// whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and
// whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]].
// NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization,
// this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by
// [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example,
// when both the source and target realms are in the same process.
2024-11-24 14:20:57 +01:00
if ( type = = TransferType : : ArrayBuffer ) {
auto bytes = TRY ( deserialize_bytes ( vm , transfer_data_holder . data , data_holder_position ) ) ;
JS : : ArrayBuffer * data = TRY ( JS : : allocate_array_buffer ( vm , target_realm . intrinsics ( ) . array_buffer_constructor ( ) , bytes . size ( ) ) ) ;
bytes . span ( ) . copy_to ( data - > buffer ( ) . span ( ) ) ;
value = JS : : Value ( data ) ;
2023-12-07 15:53:38 -07:00
}
2024-11-24 14:20:57 +01:00
// 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object
2023-12-07 15:53:38 -07:00
// in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose
// [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose
// [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]].
// NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception.
2024-11-24 14:20:57 +01:00
else if ( type = = TransferType : : ResizableArrayBuffer ) {
auto bytes = TRY ( deserialize_bytes ( vm , transfer_data_holder . data , data_holder_position ) ) ;
auto max_byte_length = deserialize_primitive_type < size_t > ( transfer_data_holder . data , data_holder_position ) ;
JS : : ArrayBuffer * data = TRY ( JS : : allocate_array_buffer ( vm , target_realm . intrinsics ( ) . array_buffer_constructor ( ) , bytes . size ( ) ) ) ;
data - > set_max_byte_length ( max_byte_length ) ;
bytes . span ( ) . copy_to ( data - > buffer ( ) . span ( ) ) ;
value = JS : : Value ( data ) ;
2023-12-07 15:53:38 -07:00
}
// 4. Otherwise:
else {
// 1. Let interfaceName be transferDataHolder.[[Type]].
// 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
2024-11-24 14:20:57 +01:00
if ( ! is_interface_exposed_on_target_realm ( type , target_realm ) )
2024-10-12 20:56:21 +02:00
return WebIDL : : DataCloneError : : create ( target_realm , " Unknown type transferred " _string ) ;
2023-12-07 15:53:38 -07:00
// 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
// 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value.
2024-11-24 14:20:57 +01:00
transfer_data_holder . data . remove ( 0 , data_holder_position ) ;
value = TRY ( create_transferred_value ( type , target_realm , transfer_data_holder ) ) ;
2023-12-07 15:53:38 -07:00
}
// 5. Set memory[transferDataHolder] to value.
memory . append ( value ) ;
// 6. Append value to transferredValues.
2024-11-15 04:01:23 +13:00
transferred_values . append ( GC : : make_root ( value . as_object ( ) ) ) ;
2023-12-07 15:53:38 -07:00
}
// 4. Let deserialized be ? StructuredDeserialize(serializeWithTransferResult.[[Serialized]], targetRealm, memory).
auto deserialized = TRY ( structured_deserialize ( vm , serialize_with_transfer_result . serialized , target_realm , memory ) ) ;
// 5. Return { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }.
return DeserializedTransferRecord { . deserialized = move ( deserialized ) , . transferred_values = move ( transferred_values ) } ;
}
2022-04-05 02:24:10 +02:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize ( JS : : VM & vm , JS : : Value value )
{
// 1. Return ? StructuredSerializeInternal(value, false).
2023-09-12 16:41:13 -06:00
SerializationMemory memory = { } ;
return structured_serialize_internal ( vm , value , false , memory ) ;
2022-04-05 02:24:10 +02:00
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize_for_storage ( JS : : VM & vm , JS : : Value value )
{
// 1. Return ? StructuredSerializeInternal(value, true).
2023-09-12 16:41:13 -06:00
SerializationMemory memory = { } ;
return structured_serialize_internal ( vm , value , true , memory ) ;
2022-04-05 02:24:10 +02:00
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
2023-09-12 16:41:13 -06:00
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize_internal ( JS : : VM & vm , JS : : Value value , bool for_storage , SerializationMemory & memory )
2022-04-05 02:24:10 +02:00
{
2023-08-21 15:15:05 -06:00
// 1. If memory was not supplied, let memory be an empty map.
2023-09-12 16:41:13 -06:00
// IMPLEMENTATION DEFINED: We move this requirement up to the callers to make recursion easier
2023-08-21 15:15:05 -06:00
2023-09-12 16:41:13 -06:00
Serializer serializer ( vm , memory , for_storage ) ;
2023-08-21 15:15:05 -06:00
return serializer . serialize ( value ) ;
2022-04-05 02:24:10 +02:00
}
2023-09-12 16:41:13 -06:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
2023-11-11 20:14:46 +02:00
WebIDL : : ExceptionOr < JS : : Value > structured_deserialize ( JS : : VM & vm , SerializationRecord const & serialized , JS : : Realm & target_realm , Optional < DeserializationMemory > memory )
2023-09-12 16:41:13 -06:00
{
if ( ! memory . has_value ( ) )
2023-11-11 20:14:46 +02:00
memory = DeserializationMemory { vm . heap ( ) } ;
2023-09-12 16:41:13 -06:00
2023-09-27 22:36:50 -06:00
// IMPLEMENTATION DEFINED: We need to make sure there's an execution context for target_realm on the stack before constructing these JS objects
2024-10-21 20:09:02 +13:00
prepare_to_run_script ( target_realm ) ;
2023-09-27 22:36:50 -06:00
2024-03-10 20:37:41 +01:00
auto result = TRY ( structured_deserialize_internal ( vm , serialized . span ( ) , target_realm , * memory ) ) ;
2023-09-27 22:36:50 -06:00
2024-10-21 16:18:25 +13:00
clean_up_after_running_script ( target_realm ) ;
2024-03-10 20:37:41 +01:00
VERIFY ( result . value . has_value ( ) ) ;
return * result . value ;
}
WebIDL : : ExceptionOr < DeserializedRecord > structured_deserialize_internal ( JS : : VM & vm , ReadonlySpan < u32 > const & serialized , JS : : Realm & target_realm , DeserializationMemory & memory , Optional < size_t > position )
{
Deserializer deserializer ( vm , target_realm , serialized , memory , move ( position ) ) ;
auto value = TRY ( deserializer . deserialize ( ) ) ;
auto deserialized_record = DeserializedRecord {
. value = value ,
. position = deserializer . position ( ) ,
} ;
return deserialized_record ;
2023-09-12 16:41:13 -06:00
}
2022-04-05 02:24:10 +02:00
}
2023-12-19 15:28:56 -07:00
namespace IPC {
template < >
ErrorOr < void > encode ( Encoder & encoder , : : Web : : HTML : : TransferDataHolder const & data_holder )
{
TRY ( encoder . encode ( data_holder . data ) ) ;
TRY ( encoder . encode ( data_holder . fds ) ) ;
return { } ;
}
template < >
ErrorOr < void > encode ( Encoder & encoder , : : Web : : HTML : : SerializedTransferRecord const & record )
{
TRY ( encoder . encode ( record . serialized ) ) ;
TRY ( encoder . encode ( record . transfer_data_holders ) ) ;
return { } ;
}
template < >
ErrorOr < : : Web : : HTML : : TransferDataHolder > decode ( Decoder & decoder )
{
2024-11-24 14:20:57 +01:00
auto data = TRY ( decoder . decode < Vector < u32 > > ( ) ) ;
2023-12-19 15:28:56 -07:00
auto fds = TRY ( decoder . decode < Vector < IPC : : File > > ( ) ) ;
return : : Web : : HTML : : TransferDataHolder { move ( data ) , move ( fds ) } ;
}
template < >
ErrorOr < : : Web : : HTML : : SerializedTransferRecord > decode ( Decoder & decoder )
{
auto serialized = TRY ( decoder . decode < Vector < u32 > > ( ) ) ;
auto transfer_data_holders = TRY ( decoder . decode < Vector < : : Web : : HTML : : TransferDataHolder > > ( ) ) ;
return : : Web : : HTML : : SerializedTransferRecord { move ( serialized ) , move ( transfer_data_holders ) } ;
}
}