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 | ||||
| 1692748800000 | ||||
| /abc/gimsuy | ||||
| [object ArrayBuffer] | ||||
| ERROR: DataCloneError: Cannot serialize Symbol | ||||
|  | @ -10,6 +10,21 @@ | |||
|         println(structuredClone(Date.UTC(2023, 7, 23))); | ||||
|         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 { | ||||
|             structuredClone(Symbol("foo")); | ||||
|             println("FAILED") | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <AK/String.h> | ||||
| #include <AK/Vector.h> | ||||
| #include <LibJS/Forward.h> | ||||
| #include <LibJS/Runtime/ArrayBuffer.h> | ||||
| #include <LibJS/Runtime/BigInt.h> | ||||
| #include <LibJS/Runtime/BigIntObject.h> | ||||
| #include <LibJS/Runtime/BooleanObject.h> | ||||
|  | @ -70,6 +71,14 @@ enum ValueTag { | |||
| 
 | ||||
|     RegExpObject, | ||||
| 
 | ||||
|     GrowableSharedArrayBuffer, | ||||
| 
 | ||||
|     SharedArrayBuffer, | ||||
| 
 | ||||
|     ResizeableArrayBuffer, | ||||
| 
 | ||||
|     ArrayBuffer, | ||||
| 
 | ||||
|     // TODO: Define many more types
 | ||||
| 
 | ||||
|     // This tag or higher are understood to be errors
 | ||||
|  | @ -82,9 +91,10 @@ enum ValueTag { | |||
| 
 | ||||
| class Serializer { | ||||
| public: | ||||
|     Serializer(JS::VM& vm, SerializationMemory& memory) | ||||
|     Serializer(JS::VM& vm, SerializationMemory& memory, bool for_storage) | ||||
|         : m_vm(vm) | ||||
|         , 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())))); | ||||
|         } | ||||
| 
 | ||||
|         // 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 { | ||||
|             return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string)); | ||||
|         } | ||||
|  | @ -205,6 +220,7 @@ private: | |||
|     JS::VM& m_vm; | ||||
|     SerializationMemory& m_memory; // JS value -> index
 | ||||
|     SerializationRecord m_serialized; | ||||
|     bool m_for_storage { false }; | ||||
| 
 | ||||
|     WebIDL::ExceptionOr<void> serialize_bytes(Vector<u32>& vector, ReadonlyBytes bytes) | ||||
|     { | ||||
|  | @ -238,6 +254,57 @@ private: | |||
|         TRY(serialize_string(vector, string)); | ||||
|         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 { | ||||
|  | @ -327,6 +394,12 @@ public: | |||
|                 m_memory.append(TRY(JS::regexp_create(m_vm, move(pattern), move(flags)))); | ||||
|                 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: | ||||
|                 m_error = "Unsupported type"_fly_string; | ||||
|                 break; | ||||
|  | @ -404,13 +477,11 @@ WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM | |||
| // 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) | ||||
| { | ||||
|     (void)for_storage; | ||||
| 
 | ||||
|     // 1. If memory was not supplied, let memory be an empty map.
 | ||||
|     if (!memory.has_value()) | ||||
|         memory = SerializationMemory {}; | ||||
| 
 | ||||
|     Serializer serializer(vm, *memory); | ||||
|     Serializer serializer(vm, *memory, for_storage); | ||||
|     return serializer.serialize(value); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andrew Kaster
						Andrew Kaster