mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:32:45 +00:00 
			
		
		
		
	LibWeb: Add {de}serialization steps for ArrayBuffers
This commit is contained in:
		
							parent
							
								
									a527f55768
								
							
						
					
					
						commit
						642802d339
					
				
					 3 changed files with 92 additions and 5 deletions
				
			
		|  | @ -6,4 +6,5 @@ This is a String object | ||||||
| 9007199254740991 | 9007199254740991 | ||||||
| 1692748800000 | 1692748800000 | ||||||
| /abc/gimsuy | /abc/gimsuy | ||||||
|  | [object ArrayBuffer] | ||||||
| ERROR: DataCloneError: Cannot serialize Symbol | ERROR: DataCloneError: Cannot serialize Symbol | ||||||
|  | @ -10,6 +10,21 @@ | ||||||
|         println(structuredClone(Date.UTC(2023, 7, 23))); |         println(structuredClone(Date.UTC(2023, 7, 23))); | ||||||
|         println(structuredClone(/abc/gimsuy)); |         println(structuredClone(/abc/gimsuy)); | ||||||
| 
 | 
 | ||||||
|  |         { | ||||||
|  |             let arrayBuffer = new ArrayBuffer(6); | ||||||
|  |             for (let i = 0; i < arrayBuffer.byteLength; ++i) { | ||||||
|  |                 arrayBuffer[i] = i; | ||||||
|  |             } | ||||||
|  |             let arrayClone = structuredClone(arrayBuffer); | ||||||
|  |             for (let i = 0; i < arrayBuffer.byteLength; ++i) { | ||||||
|  |                 if (arrayBuffer[i] !== arrayBuffer[i]) { | ||||||
|  |                     println("FAILED"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // FIXME: This should print something like ArrayBuffer { byteLength: 6 } | ||||||
|  |             println(arrayClone); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         try { |         try { | ||||||
|             structuredClone(Symbol("foo")); |             structuredClone(Symbol("foo")); | ||||||
|             println("FAILED") |             println("FAILED") | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <AK/String.h> | #include <AK/String.h> | ||||||
| #include <AK/Vector.h> | #include <AK/Vector.h> | ||||||
| #include <LibJS/Forward.h> | #include <LibJS/Forward.h> | ||||||
|  | #include <LibJS/Runtime/ArrayBuffer.h> | ||||||
| #include <LibJS/Runtime/BigInt.h> | #include <LibJS/Runtime/BigInt.h> | ||||||
| #include <LibJS/Runtime/BigIntObject.h> | #include <LibJS/Runtime/BigIntObject.h> | ||||||
| #include <LibJS/Runtime/BooleanObject.h> | #include <LibJS/Runtime/BooleanObject.h> | ||||||
|  | @ -70,6 +71,14 @@ enum ValueTag { | ||||||
| 
 | 
 | ||||||
|     RegExpObject, |     RegExpObject, | ||||||
| 
 | 
 | ||||||
|  |     GrowableSharedArrayBuffer, | ||||||
|  | 
 | ||||||
|  |     SharedArrayBuffer, | ||||||
|  | 
 | ||||||
|  |     ResizeableArrayBuffer, | ||||||
|  | 
 | ||||||
|  |     ArrayBuffer, | ||||||
|  | 
 | ||||||
|     // TODO: Define many more types
 |     // TODO: Define many more types
 | ||||||
| 
 | 
 | ||||||
|     // This tag or higher are understood to be errors
 |     // This tag or higher are understood to be errors
 | ||||||
|  | @ -82,9 +91,10 @@ enum ValueTag { | ||||||
| 
 | 
 | ||||||
| class Serializer { | class Serializer { | ||||||
| public: | public: | ||||||
|     Serializer(JS::VM& vm, SerializationMemory& memory) |     Serializer(JS::VM& vm, SerializationMemory& memory, bool for_storage) | ||||||
|         : m_vm(vm) |         : m_vm(vm) | ||||||
|         , m_memory(memory) |         , m_memory(memory) | ||||||
|  |         , m_for_storage(for_storage) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -187,7 +197,12 @@ public: | ||||||
|             TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_deprecated_string(regexp_object.flags())))); |             TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_deprecated_string(regexp_object.flags())))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // 13 - 24: FIXME: Serialize other data types
 |         // 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()))); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 14 - 24: FIXME: Serialize other data types
 | ||||||
|         else { |         else { | ||||||
|             return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string)); |             return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string)); | ||||||
|         } |         } | ||||||
|  | @ -205,6 +220,7 @@ private: | ||||||
|     JS::VM& m_vm; |     JS::VM& m_vm; | ||||||
|     SerializationMemory& m_memory; // JS value -> index
 |     SerializationMemory& m_memory; // JS value -> index
 | ||||||
|     SerializationRecord m_serialized; |     SerializationRecord m_serialized; | ||||||
|  |     bool m_for_storage { false }; | ||||||
| 
 | 
 | ||||||
|     WebIDL::ExceptionOr<void> serialize_bytes(Vector<u32>& vector, ReadonlyBytes bytes) |     WebIDL::ExceptionOr<void> serialize_bytes(Vector<u32>& vector, ReadonlyBytes bytes) | ||||||
|     { |     { | ||||||
|  | @ -238,6 +254,57 @@ private: | ||||||
|         TRY(serialize_string(vector, string)); |         TRY(serialize_string(vector, string)); | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     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).
 | ||||||
|  |             JS::copy_data_block_bytes(data_copy, 0, array_buffer.buffer(), 0, size); | ||||||
|  | 
 | ||||||
|  |             // 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); | ||||||
|  |                 TRY(serialize_bytes(vector, data_copy.bytes())); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Deserializer { | class Deserializer { | ||||||
|  | @ -327,6 +394,12 @@ public: | ||||||
|                 m_memory.append(TRY(JS::regexp_create(m_vm, move(pattern), move(flags)))); |                 m_memory.append(TRY(JS::regexp_create(m_vm, move(pattern), move(flags)))); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |             case ValueTag::ArrayBuffer: { | ||||||
|  |                 auto* realm = m_vm.current_realm(); | ||||||
|  |                 auto bytes = TRY(deserialize_bytes(m_vm, m_vector, position)); | ||||||
|  |                 m_memory.append(JS::ArrayBuffer::create(*realm, move(bytes))); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|             default: |             default: | ||||||
|                 m_error = "Unsupported type"_fly_string; |                 m_error = "Unsupported type"_fly_string; | ||||||
|                 break; |                 break; | ||||||
|  | @ -404,13 +477,11 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM | ||||||
| // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
 | // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
 | ||||||
| WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory) | WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory) | ||||||
| { | { | ||||||
|     (void)for_storage; |  | ||||||
| 
 |  | ||||||
|     // 1. If memory was not supplied, let memory be an empty map.
 |     // 1. If memory was not supplied, let memory be an empty map.
 | ||||||
|     if (!memory.has_value()) |     if (!memory.has_value()) | ||||||
|         memory = SerializationMemory {}; |         memory = SerializationMemory {}; | ||||||
| 
 | 
 | ||||||
|     Serializer serializer(vm, *memory); |     Serializer serializer(vm, *memory, for_storage); | ||||||
|     return serializer.serialize(value); |     return serializer.serialize(value); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Kaster
						Andrew Kaster