Serenity Operating System
at master 236 lines 9.6 kB view raw
1/* 2 * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include <AK/ByteBuffer.h> 10#include <AK/Function.h> 11#include <AK/Variant.h> 12#include <LibJS/Runtime/BigInt.h> 13#include <LibJS/Runtime/Completion.h> 14#include <LibJS/Runtime/GlobalObject.h> 15#include <LibJS/Runtime/Object.h> 16 17namespace JS { 18 19struct ClampedU8 { 20}; 21 22// 25.1.1 Notation (read-modify-write modification function), https://tc39.es/ecma262/#sec-arraybuffer-notation 23using ReadWriteModifyFunction = Function<ByteBuffer(ByteBuffer, ByteBuffer)>; 24 25class ArrayBuffer : public Object { 26 JS_OBJECT(ArrayBuffer, Object); 27 28public: 29 static ThrowCompletionOr<NonnullGCPtr<ArrayBuffer>> create(Realm&, size_t); 30 static NonnullGCPtr<ArrayBuffer> create(Realm&, ByteBuffer); 31 static NonnullGCPtr<ArrayBuffer> create(Realm&, ByteBuffer*); 32 33 virtual ~ArrayBuffer() override = default; 34 35 size_t byte_length() const { return buffer_impl().size(); } 36 ByteBuffer& buffer() { return buffer_impl(); } 37 ByteBuffer const& buffer() const { return buffer_impl(); } 38 39 // Used by allocate_array_buffer() to attach the data block after construction 40 void set_buffer(ByteBuffer buffer) { m_buffer = move(buffer); } 41 42 Value detach_key() const { return m_detach_key; } 43 void set_detach_key(Value detach_key) { m_detach_key = detach_key; } 44 45 void detach_buffer() { m_buffer = Empty {}; } 46 bool is_detached() const { return m_buffer.has<Empty>(); } 47 48 enum Order { 49 SeqCst, 50 Unordered 51 }; 52 template<typename type> 53 Value get_value(size_t byte_index, bool is_typed_array, Order, bool is_little_endian = true); 54 template<typename type> 55 void set_value(size_t byte_index, Value value, bool is_typed_array, Order, bool is_little_endian = true); 56 template<typename T> 57 Value get_modify_set_value(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian = true); 58 59private: 60 ArrayBuffer(ByteBuffer buffer, Object& prototype); 61 ArrayBuffer(ByteBuffer* buffer, Object& prototype); 62 63 virtual void visit_edges(Visitor&) override; 64 65 ByteBuffer& buffer_impl() 66 { 67 ByteBuffer* ptr { nullptr }; 68 m_buffer.visit([&](Empty) { VERIFY_NOT_REACHED(); }, [&](auto* pointer) { ptr = pointer; }, [&](auto& value) { ptr = &value; }); 69 return *ptr; 70 } 71 72 ByteBuffer const& buffer_impl() const { return const_cast<ArrayBuffer*>(this)->buffer_impl(); } 73 74 Variant<Empty, ByteBuffer, ByteBuffer*> m_buffer; 75 // The various detach related members of ArrayBuffer are not used by any ECMA262 functionality, 76 // but are required to be available for the use of various harnesses like the Test262 test runner. 77 Value m_detach_key; 78}; 79 80ThrowCompletionOr<ArrayBuffer*> allocate_array_buffer(VM&, FunctionObject& constructor, size_t byte_length); 81ThrowCompletionOr<void> detach_array_buffer(VM&, ArrayBuffer& array_buffer, Optional<Value> key = {}); 82ThrowCompletionOr<ArrayBuffer*> clone_array_buffer(VM&, ArrayBuffer& source_buffer, size_t source_byte_offset, size_t source_length); 83 84// 25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian ), https://tc39.es/ecma262/#sec-rawbytestonumeric 85template<typename T> 86static Value raw_bytes_to_numeric(VM& vm, ByteBuffer raw_value, bool is_little_endian) 87{ 88 if (!is_little_endian) { 89 VERIFY(raw_value.size() % 2 == 0); 90 for (size_t i = 0; i < raw_value.size() / 2; ++i) 91 swap(raw_value[i], raw_value[raw_value.size() - 1 - i]); 92 } 93 using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>; 94 if constexpr (IsSame<UnderlyingBufferDataType, float>) { 95 float value; 96 raw_value.span().copy_to({ &value, sizeof(float) }); 97 if (isnan(value)) 98 return js_nan(); 99 return Value(value); 100 } 101 if constexpr (IsSame<UnderlyingBufferDataType, double>) { 102 double value; 103 raw_value.span().copy_to({ &value, sizeof(double) }); 104 if (isnan(value)) 105 return js_nan(); 106 return Value(value); 107 } 108 if constexpr (!IsIntegral<UnderlyingBufferDataType>) 109 VERIFY_NOT_REACHED(); 110 UnderlyingBufferDataType int_value = 0; 111 raw_value.span().copy_to({ &int_value, sizeof(UnderlyingBufferDataType) }); 112 if constexpr (sizeof(UnderlyingBufferDataType) == 8) { 113 if constexpr (IsSigned<UnderlyingBufferDataType>) { 114 static_assert(IsSame<UnderlyingBufferDataType, i64>); 115 return BigInt::create(vm, Crypto::SignedBigInteger { int_value }); 116 } else { 117 static_assert(IsOneOf<UnderlyingBufferDataType, u64, double>); 118 return BigInt::create(vm, Crypto::SignedBigInteger { Crypto::UnsignedBigInteger { int_value } }); 119 } 120 } else { 121 return Value(int_value); 122 } 123} 124 125// Implementation for 25.1.2.10 GetValueFromBuffer, used in TypedArray<T>::get_value_from_buffer(). 126template<typename T> 127Value ArrayBuffer::get_value(size_t byte_index, [[maybe_unused]] bool is_typed_array, Order, bool is_little_endian) 128{ 129 auto& vm = this->vm(); 130 131 auto element_size = sizeof(T); 132 133 // FIXME: Check for shared buffer 134 135 // FIXME: Propagate errors. 136 auto raw_value = MUST(buffer_impl().slice(byte_index, element_size)); 137 return raw_bytes_to_numeric<T>(vm, move(raw_value), is_little_endian); 138} 139 140// 25.1.2.11 NumericToRawBytes ( type, value, isLittleEndian ), https://tc39.es/ecma262/#sec-numerictorawbytes 141template<typename T> 142static ByteBuffer numeric_to_raw_bytes(VM& vm, Value value, bool is_little_endian) 143{ 144 VERIFY(value.is_number() || value.is_bigint()); 145 using UnderlyingBufferDataType = Conditional<IsSame<ClampedU8, T>, u8, T>; 146 ByteBuffer raw_bytes = ByteBuffer::create_uninitialized(sizeof(UnderlyingBufferDataType)).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation. 147 auto flip_if_needed = [&]() { 148 if (is_little_endian) 149 return; 150 VERIFY(sizeof(UnderlyingBufferDataType) % 2 == 0); 151 for (size_t i = 0; i < sizeof(UnderlyingBufferDataType) / 2; ++i) 152 swap(raw_bytes[i], raw_bytes[sizeof(UnderlyingBufferDataType) - 1 - i]); 153 }; 154 if constexpr (IsSame<UnderlyingBufferDataType, float>) { 155 float raw_value = MUST(value.to_double(vm)); 156 ReadonlyBytes { &raw_value, sizeof(float) }.copy_to(raw_bytes); 157 flip_if_needed(); 158 return raw_bytes; 159 } 160 if constexpr (IsSame<UnderlyingBufferDataType, double>) { 161 double raw_value = MUST(value.to_double(vm)); 162 ReadonlyBytes { &raw_value, sizeof(double) }.copy_to(raw_bytes); 163 flip_if_needed(); 164 return raw_bytes; 165 } 166 if constexpr (!IsIntegral<UnderlyingBufferDataType>) 167 VERIFY_NOT_REACHED(); 168 if constexpr (sizeof(UnderlyingBufferDataType) == 8) { 169 UnderlyingBufferDataType int_value; 170 171 if constexpr (IsSigned<UnderlyingBufferDataType>) 172 int_value = MUST(value.to_bigint_int64(vm)); 173 else 174 int_value = MUST(value.to_bigint_uint64(vm)); 175 176 ReadonlyBytes { &int_value, sizeof(UnderlyingBufferDataType) }.copy_to(raw_bytes); 177 flip_if_needed(); 178 return raw_bytes; 179 } else { 180 UnderlyingBufferDataType int_value; 181 if constexpr (IsSigned<UnderlyingBufferDataType>) { 182 if constexpr (sizeof(UnderlyingBufferDataType) == 4) 183 int_value = MUST(value.to_i32(vm)); 184 else if constexpr (sizeof(UnderlyingBufferDataType) == 2) 185 int_value = MUST(value.to_i16(vm)); 186 else 187 int_value = MUST(value.to_i8(vm)); 188 } else { 189 if constexpr (sizeof(UnderlyingBufferDataType) == 4) 190 int_value = MUST(value.to_u32(vm)); 191 else if constexpr (sizeof(UnderlyingBufferDataType) == 2) 192 int_value = MUST(value.to_u16(vm)); 193 else if constexpr (!IsSame<T, ClampedU8>) 194 int_value = MUST(value.to_u8(vm)); 195 else 196 int_value = MUST(value.to_u8_clamp(vm)); 197 } 198 ReadonlyBytes { &int_value, sizeof(UnderlyingBufferDataType) }.copy_to(raw_bytes); 199 if constexpr (sizeof(UnderlyingBufferDataType) % 2 == 0) 200 flip_if_needed(); 201 return raw_bytes; 202 } 203} 204 205// 25.1.2.12 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-setvalueinbuffer 206template<typename T> 207void ArrayBuffer::set_value(size_t byte_index, Value value, [[maybe_unused]] bool is_typed_array, Order, bool is_little_endian) 208{ 209 auto& vm = this->vm(); 210 211 auto raw_bytes = numeric_to_raw_bytes<T>(vm, value, is_little_endian); 212 213 // FIXME: Check for shared buffer 214 215 raw_bytes.span().copy_to(buffer_impl().span().slice(byte_index)); 216} 217 218// 25.1.2.13 GetModifySetValueInBuffer ( arrayBuffer, byteIndex, type, value, op [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-getmodifysetvalueinbuffer 219template<typename T> 220Value ArrayBuffer::get_modify_set_value(size_t byte_index, Value value, ReadWriteModifyFunction operation, bool is_little_endian) 221{ 222 auto& vm = this->vm(); 223 224 auto raw_bytes = numeric_to_raw_bytes<T>(vm, value, is_little_endian); 225 226 // FIXME: Check for shared buffer 227 228 // FIXME: Propagate errors. 229 auto raw_bytes_read = MUST(buffer_impl().slice(byte_index, sizeof(T))); 230 auto raw_bytes_modified = operation(raw_bytes_read, raw_bytes); 231 raw_bytes_modified.span().copy_to(buffer_impl().span().slice(byte_index)); 232 233 return raw_bytes_to_numeric<T>(vm, raw_bytes_read, is_little_endian); 234} 235 236}