mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibWeb: Add {de}serialization steps for TypedArrayBuffers and DataViews
This commit is contained in:
parent
bddf3fbf2d
commit
5ae9b2fdaf
Notes:
sideshowbarker
2024-07-17 02:28:18 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/5ae9b2fdaf Pull-request: https://github.com/SerenityOS/serenity/pull/21052
3 changed files with 161 additions and 1 deletions
|
@ -0,0 +1,12 @@
|
|||
Uint8Array 30,40,50,60
|
||||
Uint8ClampedArray 30,40,50,60
|
||||
Uint16Array 30,40,50,60
|
||||
Uint32Array 30,40,50,60
|
||||
Int8Array 30,40,50,60
|
||||
Int16Array 30,40,50,60
|
||||
Int32Array 30,40,50,60
|
||||
Float32Array 30,40,50,60
|
||||
Float64Array 30,40,50,60
|
||||
BigUint64Array 30,40,50,60
|
||||
BigInt64Array 30,40,50,60
|
||||
DataView [object DataView]
|
|
@ -0,0 +1,51 @@
|
|||
<script src="../include.js"></script>
|
||||
<script>
|
||||
const TYPED_ARRAYS = [
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Uint16Array,
|
||||
Uint32Array,
|
||||
Int8Array,
|
||||
Int16Array,
|
||||
Int32Array,
|
||||
Float32Array,
|
||||
Float64Array,
|
||||
];
|
||||
|
||||
const BIGINT_TYPED_ARRAYS = [BigUint64Array, BigInt64Array];
|
||||
|
||||
test(() => {
|
||||
TYPED_ARRAYS.forEach(T => {
|
||||
const a = new T([30, 40, 50, 60]);
|
||||
let b = structuredClone(a)
|
||||
if (!a.every((value, index) => value === b[index]))
|
||||
println("FAIL")
|
||||
println(`${b[Symbol.toStringTag]} ${b}`)
|
||||
});
|
||||
|
||||
BIGINT_TYPED_ARRAYS.forEach(T => {
|
||||
const a = new T([30n, 40n, 50n, 60n]);
|
||||
let b = structuredClone(a)
|
||||
if (!a.every((value, index) => value === b[index]))
|
||||
println("FAIL")
|
||||
|
||||
println(`${b[Symbol.toStringTag]} ${b}`)
|
||||
});
|
||||
|
||||
// Test DataView clone
|
||||
let a = new ArrayBuffer(64);
|
||||
for(let i = 0; i < a.byteLength; ++i) {
|
||||
a[i] = i;
|
||||
}
|
||||
let view = new DataView(a);
|
||||
let view2 = structuredClone(view);
|
||||
if (view.buffer === view2.buffer)
|
||||
println("FAIL: Buffer not deep copied");
|
||||
|
||||
for (let i = 0; i < view.byteLength; ++i) {
|
||||
if (!view[i] === view2[i])
|
||||
println("FAIL: Data not copied")
|
||||
}
|
||||
println(`${view2[Symbol.toStringTag]} ${view2}`)
|
||||
});
|
||||
</script>
|
|
@ -14,11 +14,13 @@
|
|||
#include <LibJS/Runtime/BigInt.h>
|
||||
#include <LibJS/Runtime/BigIntObject.h>
|
||||
#include <LibJS/Runtime/BooleanObject.h>
|
||||
#include <LibJS/Runtime/DataView.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/NumberObject.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||
|
@ -81,6 +83,8 @@ enum ValueTag {
|
|||
|
||||
ArrayBuffer,
|
||||
|
||||
ArrayBufferView,
|
||||
|
||||
// TODO: Define many more types
|
||||
|
||||
// This tag or higher are understood to be errors
|
||||
|
@ -204,7 +208,14 @@ public:
|
|||
TRY(serialize_array_buffer(m_serialized, static_cast<JS::ArrayBuffer&>(value.as_object())));
|
||||
}
|
||||
|
||||
// 14 - 24: FIXME: Serialize other data types
|
||||
// 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())));
|
||||
}
|
||||
|
||||
// 15 - 24: FIXME: Serialize other data types
|
||||
else {
|
||||
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string));
|
||||
}
|
||||
|
@ -245,6 +256,11 @@ private:
|
|||
return {};
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, DeprecatedFlyString const& string)
|
||||
{
|
||||
return serialize_bytes(vector, string.view().bytes());
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
|
||||
{
|
||||
return serialize_bytes(vector, { string.code_points().bytes(), string.code_points().byte_length() });
|
||||
|
@ -307,6 +323,54 @@ private:
|
|||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
// FIXME: 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
|
||||
|
||||
// 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);
|
||||
u64 const serialized_buffer_size = buffer_serialized.size();
|
||||
vector.append(bit_cast<u32*>(&serialized_buffer_size), 2);
|
||||
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
||||
TRY(serialize_string(vector, "DataView"_string)); // [[Constructor]]
|
||||
vector.append(view.byte_length());
|
||||
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);
|
||||
u64 const serialized_buffer_size = buffer_serialized.size();
|
||||
vector.append(bit_cast<u32*>(&serialized_buffer_size), 2);
|
||||
vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
|
||||
TRY(serialize_string(vector, view.element_name())); // [[Constructor]]
|
||||
vector.append(view.byte_length());
|
||||
vector.append(view.byte_offset());
|
||||
vector.append(view.array_length());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class Deserializer {
|
||||
|
@ -404,6 +468,39 @@ public:
|
|||
m_memory.append(JS::ArrayBuffer::create(*realm, move(bytes)));
|
||||
break;
|
||||
}
|
||||
case ValueTag::ArrayBufferView: {
|
||||
auto* realm = m_vm.current_realm();
|
||||
u32 size_bits[2];
|
||||
size_bits[0] = m_vector[position++];
|
||||
size_bits[1] = m_vector[position++];
|
||||
u64 const array_buffer_size = *bit_cast<u64*>(&size_bits);
|
||||
auto array_buffer_value = TRY(structured_deserialize_impl(m_vm, m_vector.slice(position, array_buffer_size), *realm, m_serialization_memory));
|
||||
auto& array_buffer = verify_cast<JS::ArrayBuffer>(array_buffer_value.as_object());
|
||||
position += array_buffer_size;
|
||||
auto constructor_name = TRY(deserialize_string(m_vm, m_vector, position));
|
||||
u32 byte_length = m_vector[position++];
|
||||
u32 byte_offset = m_vector[position++];
|
||||
|
||||
if (constructor_name == "DataView"sv) {
|
||||
m_memory.append(JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset));
|
||||
} else {
|
||||
u32 array_length = m_vector[position++];
|
||||
JS::GCPtr<JS::TypedArrayBase> typed_array_ptr;
|
||||
#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)
|
||||
JS_ENUMERATE_TYPED_ARRAYS
|
||||
#undef __JS_ENUMERATE
|
||||
#undef CREATE_TYPED_ARRAY
|
||||
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);
|
||||
m_memory.append(typed_array_ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_error = "Unsupported type"_fly_string;
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue