2022-04-05 02:24:10 +02:00
/*
* Copyright ( c ) 2022 , Daniel Ehrenberg < dan @ littledan . dev >
* Copyright ( c ) 2022 , Andrew Kaster < akaster @ serenityos . org >
2023-07-21 21:19:52 +02:00
* Copyright ( c ) 2023 , 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>
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>
# include <LibWeb/Bindings/Transferable.h>
# include <LibWeb/HTML/MessagePort.h>
2022-04-05 02:24:10 +02:00
# include <LibWeb/HTML/StructuredSerialize.h>
# 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 ,
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 )
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
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 ;
// 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
2023-07-21 21:19:52 +02:00
if ( value . is_undefined ( ) ) {
m_serialized . append ( ValueTag : : UndefinedPrimitive ) ;
} else if ( value . is_null ( ) ) {
m_serialized . append ( ValueTag : : NullPrimitive ) ;
} else if ( value . is_boolean ( ) ) {
m_serialized . append ( ValueTag : : BooleanPrimitive ) ;
m_serialized . append ( static_cast < u32 > ( value . as_bool ( ) ) ) ;
} else if ( value . is_number ( ) ) {
2022-04-05 02:24:10 +02:00
m_serialized . append ( ValueTag : : NumberPrimitive ) ;
double number = value . as_double ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & number ) , 2 ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_bigint ( ) ) {
m_serialized . append ( ValueTag : : BigIntPrimitive ) ;
auto & val = value . as_bigint ( ) ;
TRY ( serialize_string ( m_serialized , TRY_OR_THROW_OOM ( m_vm , val . to_string ( ) ) ) ) ;
} else if ( value . is_string ( ) ) {
m_serialized . append ( ValueTag : : StringPrimitive ) ;
TRY ( serialize_string ( m_serialized , value . as_string ( ) ) ) ;
2023-08-21 15:15:05 -06:00
} else {
return_primitive_type = false ;
}
if ( return_primitive_type )
return m_serialized ;
2023-08-21 17:39:44 -06:00
// 5. If Type(value) is Symbol, then throw a "DataCloneError" DOMException.
if ( value . is_symbol ( ) )
2023-09-06 16:03:01 +12:00
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize Symbol " _fly_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 ( ) ) ) {
2023-08-01 20:53:43 +02:00
m_serialized . append ( ValueTag : : BooleanObject ) ;
auto & boolean_object = static_cast < JS : : BooleanObject & > ( value . as_object ( ) ) ;
m_serialized . append ( bit_cast < u32 > ( static_cast < u32 > ( boolean_object . boolean ( ) ) ) ) ;
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 ( ) ) ) {
2023-08-01 20:53:43 +02:00
m_serialized . append ( ValueTag : : NumberObject ) ;
auto & number_object = static_cast < JS : : NumberObject & > ( value . as_object ( ) ) ;
double const number = number_object . number ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & number ) , 2 ) ;
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 ( ) ) ) {
m_serialized . append ( ValueTag : : BigIntObject ) ;
auto & bigint_object = static_cast < JS : : BigIntObject & > ( value . as_object ( ) ) ;
TRY ( serialize_string ( m_serialized , TRY_OR_THROW_OOM ( m_vm , bigint_object . bigint ( ) . to_string ( ) ) ) ) ;
}
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 ( ) ) ) {
2023-08-01 20:53:43 +02:00
m_serialized . append ( ValueTag : : StringObject ) ;
auto & string_object = static_cast < JS : : StringObject & > ( value . as_object ( ) ) ;
TRY ( serialize_string ( m_serialized , string_object . primitive_string ( ) ) ) ;
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 ( ) ) ) {
2023-08-01 20:53:43 +02:00
m_serialized . append ( ValueTag : : DateObject ) ;
auto & date_object = static_cast < JS : : Date & > ( value . as_object ( ) ) ;
double const date_value = date_object . date_value ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & date_value ) , 2 ) ;
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 ( ) ) ) {
m_serialized . append ( ValueTag : : RegExpObject ) ;
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)
2023-12-16 17:49:34 +03:30
TRY ( serialize_string ( m_serialized , TRY_OR_THROW_OOM ( m_vm , String : : from_byte_string ( regexp_object . pattern ( ) ) ) ) ) ;
TRY ( serialize_string ( m_serialized , TRY_OR_THROW_OOM ( m_vm , String : : from_byte_string ( regexp_object . flags ( ) ) ) ) ) ;
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 ( ) ) ) {
TRY ( serialize_array_buffer ( m_serialized , static_cast < JS : : ArrayBuffer & > ( value . as_object ( ) ) ) ) ;
}
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 ( ) ) ) {
TRY ( serialize_viewed_array_buffer ( m_serialized , static_cast < JS : : TypedArrayBase & > ( value . as_object ( ) ) ) ) ;
} else if ( value . is_object ( ) & & is < JS : : DataView > ( value . as_object ( ) ) ) {
TRY ( serialize_viewed_array_buffer ( m_serialized , static_cast < JS : : DataView & > ( value . as_object ( ) ) ) ) ;
}
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 }.
m_serialized . append ( ValueTag : : MapObject ) ;
// 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 }.
m_serialized . append ( ValueTag : : SetObject ) ;
// 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.
m_serialized . append ( ValueTag : : ErrorObject ) ;
m_serialized . append ( type ) ;
m_serialized . append ( message . has_value ( ) ) ;
if ( message . has_value ( ) )
TRY ( serialize_string ( m_serialized , * message ) ) ;
}
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 }.
m_serialized . append ( ValueTag : : ArrayObject ) ;
m_serialized . append ( bit_cast < u32 * > ( & length ) , 2 ) ;
// 4. Set deep to true.
deep = true ;
}
// FIXME: 19. Otherwise, if value is a platform object that is a serializable object:
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 ( ) ) ) {
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize platform objects " _fly_string ) ) ;
}
// 21. Otherwise, if IsCallable(value) is true, then throw a "DataCloneError" DOMException.
else if ( value . is_function ( ) ) {
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize functions " _fly_string ) ) ;
}
// 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 }.
m_serialized . append ( ValueTag : : Object ) ;
// 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.
2023-11-11 20:14:46 +02:00
m_memory . set ( make_handle ( value ) , m_next_id + + ) ;
// 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 ) ;
}
u64 size = set . set_size ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & size ) , 2 ) ;
// 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 ) ;
}
}
// FIXME: 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.
// 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
else {
u64 property_count = 0 ;
auto count_offset = m_serialized . size ( ) ;
m_serialized . append ( bit_cast < u32 * > ( & property_count ) , 2 ) ;
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]].
TRY ( serialize_string ( m_serialized , key . as_string ( ) ) ) ;
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 } ;
2023-07-21 21:19:52 +02:00
2023-09-11 18:05:30 -06:00
WebIDL : : ExceptionOr < void > serialize_bytes ( Vector < u32 > & vector , ReadonlyBytes bytes )
2023-07-21 21:19:52 +02:00
{
2023-09-11 18:05:30 -06:00
// Append size of the buffer to the serialized structure.
u64 const size = bytes . size ( ) ;
2023-07-21 21:19:52 +02:00
TRY_OR_THROW_OOM ( m_vm , vector . try_append ( bit_cast < u32 * > ( & size ) , 2 ) ) ;
2023-09-11 18:05:30 -06:00
// Append the bytes of the buffer to the serialized structure.
2023-07-30 18:34:56 +02:00
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 ;
}
TRY_OR_THROW_OOM ( m_vm , vector . try_append ( combined_value ) ) ;
2023-07-21 21:19:52 +02:00
}
return { } ;
}
2023-09-12 16:56:35 -06:00
WebIDL : : ExceptionOr < void > serialize_string ( Vector < u32 > & vector , DeprecatedFlyString const & string )
{
return serialize_bytes ( vector , string . view ( ) . bytes ( ) ) ;
}
2023-09-11 18:05:30 -06:00
WebIDL : : ExceptionOr < void > serialize_string ( Vector < u32 > & vector , String const & string )
{
return serialize_bytes ( vector , { string . code_points ( ) . bytes ( ) , string . code_points ( ) . byte_length ( ) } ) ;
}
2023-07-21 21:19:52 +02:00
WebIDL : : ExceptionOr < void > serialize_string ( Vector < u32 > & vector , JS : : PrimitiveString const & primitive_string )
{
2023-08-08 19:17:55 +02:00
auto string = primitive_string . utf8_string ( ) ;
2023-07-21 21:19:52 +02:00
TRY ( serialize_string ( vector , string ) ) ;
return { } ;
}
2023-09-11 18:06:43 -06:00
WebIDL : : ExceptionOr < void > serialize_array_buffer ( Vector < u32 > & vector , JS : : ArrayBuffer const & array_buffer )
{
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
// FIXME: 1. If IsSharedArrayBuffer(value) is true, then:
if ( false ) {
// 1. If the current settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
// 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.
if ( current_settings_object ( ) . cross_origin_isolated_capability ( ) = = CanUseCrossOriginIsolatedAPIs : : No )
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer when cross-origin isolated " _fly_string ) ;
// 2. If forStorage is true, then throw a "DataCloneError" DOMException.
if ( m_for_storage )
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer for storage " _fly_string ) ;
// 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 }.
}
// 2. Otherwise:
else {
// 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
if ( array_buffer . is_detached ( ) )
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize detached ArrayBuffer " _fly_string ) ;
// 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 ( m_vm , size ) ) ;
// 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
2023-09-11 10:35:03 +02:00
JS : : copy_data_block_bytes ( data_copy . buffer ( ) , 0 , array_buffer . buffer ( ) , 0 , size ) ;
2023-09-11 18:06:43 -06:00
// FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
// [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
if ( false ) {
}
// 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
else {
vector . append ( ValueTag : : ArrayBuffer ) ;
2023-09-11 10:35:03 +02:00
TRY ( serialize_bytes ( vector , data_copy . buffer ( ) . bytes ( ) ) ) ;
2023-09-11 18:06:43 -06:00
}
}
return { } ;
}
2023-09-12 16:56:35 -06:00
template < OneOf < JS : : TypedArrayBase , JS : : DataView > ViewType >
WebIDL : : ExceptionOr < void > serialize_viewed_array_buffer ( Vector < u32 > & vector , ViewType const & view )
{
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
2023-10-15 09:46:09 -04:00
auto view_record = [ & ] ( ) {
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
return JS : : make_data_view_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
} else {
2023-12-24 14:55:10 -05:00
return JS : : make_typed_array_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
2023-10-15 09:46:09 -04: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 ( * m_vm . current_realm ( ) , MUST ( String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . message ( ) , " DataView " sv ) ) ) ;
} else {
2023-12-24 14:55:10 -05:00
if ( JS : : is_typed_array_out_of_bounds ( view_record ) )
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , MUST ( String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . message ( ) , " TypedArray " sv ) ) ) ;
2023-10-15 09:46:09 -04:00
}
2023-09-12 16:56:35 -06:00
// 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
auto * buffer = view . viewed_array_buffer ( ) ;
// 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
auto buffer_serialized = TRY ( structured_serialize_internal ( m_vm , JS : : Value ( buffer ) , m_for_storage , m_memory ) ) ;
// 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
// NOTE: We currently only implement this for ArrayBuffer
VERIFY ( buffer_serialized [ 0 ] = = ValueTag : : ArrayBuffer ) ;
// 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 > ) {
vector . append ( ValueTag : : ArrayBufferView ) ;
vector . extend ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
TRY ( serialize_string ( vector , " DataView " _string ) ) ; // [[Constructor]]
2023-10-15 09:46:09 -04:00
vector . append ( JS : : get_view_byte_length ( view_record ) ) ;
2023-09-12 16:56:35 -06:00
vector . append ( view . byte_offset ( ) ) ;
}
// 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]] }.
vector . append ( ValueTag : : ArrayBufferView ) ;
vector . extend ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
TRY ( serialize_string ( vector , view . element_name ( ) ) ) ; // [[Constructor]]
2023-12-24 14:55:10 -05:00
vector . append ( JS : : typed_array_byte_length ( view_record ) ) ;
2023-09-12 16:56:35 -06:00
vector . append ( view . byte_offset ( ) ) ;
2023-12-24 14:55:10 -05:00
vector . append ( JS : : typed_array_length ( view_record ) ) ;
2023-09-12 16:56:35 -06:00
}
return { } ;
}
2022-04-05 02:24:10 +02:00
} ;
class Deserializer {
public :
2023-11-11 20:14:46 +02:00
Deserializer ( JS : : VM & vm , JS : : Realm & target_realm , ReadonlySpan < u32 > serialized , DeserializationMemory & memory )
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 )
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
}
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
{
2023-11-11 20:14:46 +02:00
auto tag = m_serialized [ m_position + + ] ;
// 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 : {
value = JS : : Value ( static_cast < bool > ( m_serialized [ m_position + + ] ) ) ;
is_primitive = true ;
break ;
}
case ValueTag : : NumberPrimitive : {
u32 bits [ 2 ] = { } ;
bits [ 0 ] = m_serialized [ m_position + + ] ;
bits [ 1 ] = m_serialized [ m_position + + ] ;
auto double_value = * bit_cast < double * > ( & bits ) ;
value = JS : : Value ( double_value ) ;
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 : {
auto * realm = m_vm . current_realm ( ) ;
auto bool_value = static_cast < bool > ( m_serialized [ m_position + + ] ) ;
value = JS : : BooleanObject : : create ( * realm , bool_value ) ;
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 : {
auto * realm = m_vm . current_realm ( ) ;
u32 bits [ 2 ] ;
bits [ 0 ] = m_serialized [ m_position + + ] ;
bits [ 1 ] = m_serialized [ m_position + + ] ;
auto double_value = * bit_cast < double * > ( & bits ) ;
value = JS : : NumberObject : : create ( * realm , double_value ) ;
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 : {
auto * realm = m_vm . current_realm ( ) ;
auto big_int = TRY ( deserialize_big_int_primitive ( m_vm , m_serialized , m_position ) ) ;
value = JS : : BigIntObject : : create ( * realm , big_int ) ;
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 : {
auto * realm = m_vm . current_realm ( ) ;
auto string = TRY ( deserialize_string_primitive ( m_vm , m_serialized , m_position ) ) ;
value = JS : : StringObject : : create ( * realm , string , realm - > intrinsics ( ) . string_prototype ( ) ) ;
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 : {
auto * realm = m_vm . current_realm ( ) ;
u32 bits [ 2 ] ;
bits [ 0 ] = m_serialized [ m_position + + ] ;
bits [ 1 ] = m_serialized [ m_position + + ] ;
auto double_value = * bit_cast < double * > ( & bits ) ;
value = JS : : Date : : create ( * realm , double_value ) ;
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 : {
auto pattern = TRY ( deserialize_string_primitive ( m_vm , m_serialized , m_position ) ) ;
auto flags = TRY ( deserialize_string_primitive ( m_vm , m_serialized , m_position ) ) ;
value = TRY ( JS : : regexp_create ( m_vm , move ( pattern ) , move ( flags ) ) ) ;
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 ( ) )
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " out of memory " _fly_string ) ;
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 ) ) ;
u32 byte_length = m_serialized [ m_position + + ] ;
u32 byte_offset = m_serialized [ m_position + + ] ;
if ( constructor_name = = " DataView " sv ) {
value = JS : : DataView : : create ( * realm , & array_buffer , byte_length , byte_offset ) ;
} else {
u32 array_length = m_serialized [ m_position + + ] ;
JS : : GCPtr < 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).
auto length = read_u64 ( ) ;
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 ( ) ;
auto type = static_cast < ErrorType > ( m_serialized [ m_position + + ] ) ;
auto has_message = static_cast < bool > ( m_serialized [ m_position + + ] ) ;
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 :
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Unsupported type " _fly_string ) ;
}
// 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 ( ) ) ;
auto length = read_u64 ( ) ;
// 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 ( ) ) ;
auto length = read_u64 ( ) ;
// 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 ( ) ;
auto length = read_u64 ( ) ;
// 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 {
// FIXME: 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
VERIFY_NOT_REACHED ( ) ;
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 ;
size_t m_position { 0 } ;
2022-04-05 02:24:10 +02:00
JS : : MarkedVector < JS : : Value > m_memory ; // Index -> JS value
2023-07-21 21:19:52 +02:00
2023-11-11 20:14:46 +02:00
u64 read_u64 ( )
{
u64 value ;
memcpy ( & value , m_serialized . offset_pointer ( m_position ) , sizeof ( value ) ) ;
m_position + = 2 ;
return value ;
}
static WebIDL : : ExceptionOr < ByteBuffer > deserialize_bytes ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2023-07-21 21:19:52 +02:00
{
u32 size_bits [ 2 ] ;
size_bits [ 0 ] = vector [ position + + ] ;
size_bits [ 1 ] = vector [ position + + ] ;
u64 const size = * bit_cast < u64 * > ( & size_bits ) ;
2023-09-11 18:05:30 -06:00
auto bytes = TRY_OR_THROW_OOM ( vm , ByteBuffer : : create_uninitialized ( size ) ) ;
2023-07-30 18:34:56 +02:00
u64 byte_position = 0 ;
2023-09-11 16:29:29 -06:00
while ( position < vector . size ( ) & & byte_position < size ) {
2023-07-30 18:34:56 +02:00
for ( u8 i = 0 ; i < 4 ; + + i ) {
2023-09-11 18:05:30 -06:00
bytes [ byte_position + + ] = ( vector [ position ] > > ( i * 8 ) & 0xFF ) ;
2023-07-30 18:34:56 +02:00
if ( byte_position = = size )
break ;
}
position + + ;
}
2023-09-11 18:05:30 -06:00
return bytes ;
}
2023-11-11 20:14:46 +02:00
static WebIDL : : ExceptionOr < String > deserialize_string ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2023-09-12 16:41:13 -06:00
{
auto bytes = TRY ( deserialize_bytes ( vm , vector , position ) ) ;
return TRY_OR_THROW_OOM ( vm , String : : from_utf8 ( StringView { bytes } ) ) ;
}
2023-11-11 20:14:46 +02:00
static WebIDL : : ExceptionOr < JS : : NonnullGCPtr < JS : : PrimitiveString > > deserialize_string_primitive ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2023-09-11 18:05:30 -06:00
{
auto bytes = TRY ( deserialize_bytes ( vm , vector , position ) ) ;
2023-07-21 21:19:52 +02:00
return TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & vm , & bytes ] ( ) {
return JS : : PrimitiveString : : create ( vm , StringView { bytes } ) ;
} ) ) ;
}
2023-11-11 20:14:46 +02:00
static WebIDL : : ExceptionOr < JS : : NonnullGCPtr < JS : : BigInt > > deserialize_big_int_primitive ( JS : : VM & vm , ReadonlySpan < u32 > vector , size_t & position )
2023-07-21 21:19:52 +02: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 ( ) ;
} ) ) ;
return JS : : BigInt : : create ( vm , : : Crypto : : SignedBigInteger : : from_base ( 10 , string_view . substring_view ( 0 , string_view . length ( ) - 1 ) ) ) ;
}
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
2023-12-12 13:30:16 -07:00
WebIDL : : ExceptionOr < SerializedTransferRecord > structured_serialize_with_transfer ( JS : : VM & vm , JS : : Value value , Vector < JS : : Handle < 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 ) {
// 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException.
// FIXME: Handle transferring ArrayBufferData objects
2023-12-12 13:30:16 -07:00
if ( ! is < Bindings : : Transferable > ( * transferable ) ) {
2023-12-07 15:53:38 -07:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer type " _fly_string ) ;
}
// FIXME: 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
// 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 ) ) {
2023-12-07 15:53:38 -07:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer value twice " _fly_string ) ;
}
// 4. Set memory[transferable] to { [[Type]]: an uninitialized value }.
2023-12-12 13:30:16 -07:00
memory . set ( JS : : make_handle ( transferable_value ) , NumericLimits < u32 > : : max ( ) ) ;
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 ) {
// 1. FIXME: If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
// 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 ( ) ) {
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Value already transferred " _fly_string ) ;
}
}
// 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 ;
// FIXME 4. If transferable has an [[ArrayBufferData]] internal slot, then:
if ( false ) {
}
// 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.
data_holder . data . append ( to_underlying ( interface_name ) ) ;
// 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 ) } ;
}
static bool is_interface_exposed_on_target_realm ( u8 name , JS : : Realm & realm )
{
auto const & intrinsics = Bindings : : host_defined_intrinsics ( realm ) ;
switch ( static_cast < TransferType > ( name ) ) {
case TransferType : : MessagePort :
return intrinsics . is_exposed ( " MessagePort " sv ) ;
break ;
default :
dbgln ( " Unknown interface type for transfer: {} " , name ) ;
break ;
}
return false ;
}
2023-12-18 17:24:31 -07:00
static WebIDL : : ExceptionOr < JS : : NonnullGCPtr < 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 ;
}
}
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.
2023-12-12 13:30:16 -07:00
Vector < JS : : Handle < 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 ) {
// 1. Let value be an uninitialized value.
JS : : Value value ;
// FIXME: 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm
// 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.
if ( false ) {
}
// FIXME: 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object
// 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.
else if ( false ) {
}
// 4. Otherwise:
else {
// 1. Let interfaceName be transferDataHolder.[[Type]].
2023-12-18 17:24:31 -07:00
u8 const interface_name = transfer_data_holder . data . take_first ( ) ;
2023-12-07 15:53:38 -07:00
// 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 , target_realm ) )
return WebIDL : : DataCloneError : : create ( target_realm , " Unknown type transferred " _fly_string ) ;
// 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.
value = TRY ( create_transferred_value ( static_cast < TransferType > ( interface_name ) , target_realm , transfer_data_holder ) ) ;
}
// 5. Set memory[transferDataHolder] to value.
memory . append ( value ) ;
// 6. Append value to transferredValues.
2023-12-12 13:30:16 -07:00
transferred_values . append ( JS : : make_handle ( 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-11-11 20:14:46 +02:00
Deserializer deserializer ( vm , target_realm , serialized . span ( ) , * memory ) ;
return deserializer . deserialize ( ) ;
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 )
{
auto data = TRY ( decoder . decode < Vector < u8 > > ( ) ) ;
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 ) } ;
}
}