From d7a70eb77ce7bc3baf8a03800e3cda3771f3e71e Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Mon, 14 Jun 2021 02:02:53 +0300 Subject: [PATCH] LibJS: Add all of the DataView.prototype.set* methods --- .../LibCrypto/BigInt/SignedBigInteger.cpp | 8 ++ .../LibCrypto/BigInt/SignedBigInteger.h | 2 + .../LibCrypto/BigInt/UnsignedBigInteger.cpp | 11 ++ .../LibCrypto/BigInt/UnsignedBigInteger.h | 2 + .../Libraries/LibJS/Runtime/ArrayBuffer.h | 57 ++++++++++ .../LibJS/Runtime/CommonPropertyNames.h | 10 ++ .../LibJS/Runtime/DataViewPrototype.cpp | 103 ++++++++++++++++++ .../LibJS/Runtime/DataViewPrototype.h | 10 ++ 8 files changed, 203 insertions(+) diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp index 98b91e4c3d..1381be6ba0 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp @@ -55,6 +55,14 @@ String SignedBigInteger::to_base10() const return builder.to_string(); } +u64 SignedBigInteger::to_u64() const +{ + u64 unsigned_value = m_unsigned_data.to_u64(); + if (!m_sign) + return unsigned_value; + return ~(unsigned_value - 1); // equivalent to `-unsigned_value`, but doesnt trigger UBSAN +} + FLATTEN SignedBigInteger SignedBigInteger::plus(const SignedBigInteger& other) const { // If both are of the same sign, just add the unsigned data and return. diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h index b110eab85d..21582656f1 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -65,6 +65,8 @@ public: static SignedBigInteger from_base10(StringView str); String to_base10() const; + u64 to_u64() const; + const UnsignedBigInteger& unsigned_value() const { return m_unsigned_data; } const Vector words() const { return m_unsigned_data.words(); } bool is_negative() const { return m_sign; } diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp index 1039811ed3..56bf73b445 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp @@ -102,6 +102,17 @@ String UnsignedBigInteger::to_base10() const return builder.to_string(); } +u64 UnsignedBigInteger::to_u64() const +{ + VERIFY(sizeof(Word) == 4); + if (!length()) + return 0; + u64 value = m_words[0]; + if (length() > 1) + value |= static_cast(m_words[1]) << 32; + return value; +} + void UnsignedBigInteger::set_to_0() { m_words.clear_with_capacity(); diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index d1a4d8e270..58e5531a6a 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -56,6 +56,8 @@ public: static UnsignedBigInteger from_base10(const String& str); String to_base10() const; + u64 to_u64() const; + const Vector& words() const { return m_words; } void set_to_0(); diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index cc9b5f1a49..c2ee642ca3 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -40,6 +40,8 @@ public: }; template Value get_value(size_t byte_index, bool is_typed_array, Order, bool is_little_endian = true); + template + Value set_value(size_t byte_index, Value value, bool is_typed_array, Order, bool is_little_endian = true); private: virtual void visit_edges(Visitor&) override; @@ -108,4 +110,59 @@ Value ArrayBuffer::get_value(size_t byte_index, [[maybe_unused]] bool is_typed_a return raw_bytes_to_numeric(global_object(), move(raw_value), is_little_endian); } +// 25.1.2.11 NumericToRawBytes ( type, value, isLittleEndian ), https://tc39.es/ecma262/#sec-numerictorawbytes +template +static ByteBuffer numeric_to_raw_bytes(GlobalObject& global_object, Value value, bool is_little_endian) +{ + ByteBuffer raw_bytes = ByteBuffer::create_uninitialized(sizeof(T)); + auto flip_if_needed = [&]() { + if (is_little_endian) + return; + VERIFY(sizeof(T) % 2 == 0); + for (size_t i = 0; i < sizeof(T) / 2; ++i) + swap(raw_bytes[i], raw_bytes[sizeof(T) - 1 - i]); + }; + if constexpr (IsSame) { + float raw_value = value.to_double(global_object); + ReadonlyBytes { &raw_value, sizeof(float) }.copy_to(raw_bytes); + flip_if_needed(); + return raw_bytes; + } + if constexpr (IsSame) { + double raw_value = value.to_double(global_object); + ReadonlyBytes { &raw_value, sizeof(double) }.copy_to(raw_bytes); + flip_if_needed(); + return raw_bytes; + } + if constexpr (!IsIntegral) + VERIFY_NOT_REACHED(); + if constexpr (sizeof(T) == 8) { + u64 int_value = value.as_bigint().big_integer().to_u64(); + ReadonlyBytes { &int_value, sizeof(u64) }.copy_to(raw_bytes); + flip_if_needed(); + return raw_bytes; + } else { + T int_value; + if constexpr (IsSigned) + int_value = value.to_i32(global_object); + else + int_value = value.to_u32(global_object); + ReadonlyBytes { &int_value, sizeof(T) }.copy_to(raw_bytes); + flip_if_needed(); + return raw_bytes; + } +} + +// 25.1.2.12 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-setvalueinbuffer +template +Value ArrayBuffer::set_value(size_t byte_index, Value value, [[maybe_unused]] bool is_typed_array, Order, bool is_little_endian) +{ + auto raw_bytes = numeric_to_raw_bytes(global_object(), value, is_little_endian); + + // FIXME: Check for shared buffer + + raw_bytes.span().copy_to(buffer_impl().span().slice(byte_index)); + return js_undefined(); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 8d8cb3b850..795d0816b9 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -234,15 +234,25 @@ namespace JS { P(round) \ P(seal) \ P(set) \ + P(setBigInt64) \ + P(setBigUint64) \ P(setDate) \ + P(setFloat32) \ + P(setFloat64) \ P(setFullYear) \ P(setHours) \ + P(setInt8) \ + P(setInt16) \ + P(setInt32) \ P(setMilliseconds) \ P(setMinutes) \ P(setMonth) \ P(setPrototypeOf) \ P(setSeconds) \ P(setTime) \ + P(setUint8) \ + P(setUint16) \ + P(setUint32) \ P(setUTCDate) \ P(setUTCFullYear) \ P(setUTCHours) \ diff --git a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp index 7f39f768a8..b221f172d8 100644 --- a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp @@ -29,6 +29,16 @@ void DataViewPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.getUint8, get_uint_8, 1, attr); define_native_function(vm.names.getUint16, get_uint_16, 1, attr); define_native_function(vm.names.getUint32, get_uint_32, 1, attr); + define_native_function(vm.names.setBigInt64, set_big_int_64, 2, attr); + define_native_function(vm.names.setBigUint64, set_big_uint_64, 2, attr); + define_native_function(vm.names.setFloat32, set_float_32, 2, attr); + define_native_function(vm.names.setFloat64, set_float_64, 2, attr); + define_native_function(vm.names.setInt8, set_int_8, 2, attr); + define_native_function(vm.names.setInt16, set_int_16, 2, attr); + define_native_function(vm.names.setInt32, set_int_32, 2, attr); + define_native_function(vm.names.setUint8, set_uint_8, 2, attr); + define_native_function(vm.names.setUint16, set_uint_16, 2, attr); + define_native_function(vm.names.setUint32, set_uint_32, 2, attr); define_native_accessor(vm.names.buffer, buffer_getter, {}, Attribute::Configurable); define_native_accessor(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable); @@ -85,6 +95,48 @@ static Value get_view_value(GlobalObject& global_object, Value request_index, Va return buffer->get_value(buffer_index, false, ArrayBuffer::Order::Unordered, little_endian); } +// 25.3.1.2 SetViewValue ( view, requestIndex, isLittleEndian, type, value ), https://tc39.es/ecma262/#sec-setviewvalue +template +static Value set_view_value(GlobalObject& global_object, Value request_index, Value is_little_endian, Value value) +{ + auto& vm = global_object.vm(); + auto* view = typed_this(vm, global_object); + if (!view) + return {}; + + auto get_index = request_index.to_index(global_object); + if (vm.exception()) + return {}; + + Value number_value; + if constexpr (IsIntegral && sizeof(T) == 8) + number_value = value.to_bigint(global_object); + else + number_value = value.to_number(global_object); + if (vm.exception()) + return {}; + + auto little_endian = is_little_endian.to_boolean(); + + auto buffer = view->viewed_array_buffer(); + if (buffer->is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return {}; + } + + auto view_offset = view->byte_offset(); + auto view_size = view->byte_length(); + + auto element_size = sizeof(T); + if (get_index + element_size > view_size) { + vm.throw_exception(global_object, ErrorType::DataViewOutOfRangeByteOffset, get_index, view_size); + return {}; + } + + auto buffer_index = get_index + view_offset; + return buffer->set_value(buffer_index, number_value, false, ArrayBuffer::Order::Unordered, little_endian); +} + // 25.3.4.5 DataView.prototype.getBigInt64 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getbigint64 JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_big_int_64) { @@ -145,6 +197,57 @@ JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_uint_32) return get_view_value(global_object, vm.argument(0), vm.argument(1)); } +// 25.3.4.15 DataView.prototype.setBigInt64 ( byteOffset, value [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.setbigint64 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_big_int_64) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_big_uint_64) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_float_32) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_float_64) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_int_8) +{ + return set_view_value(global_object, vm.argument(0), Value(true), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_int_16) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_int_32) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_uint_8) +{ + return set_view_value(global_object, vm.argument(0), Value(true), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_uint_16) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::set_uint_32) +{ + return set_view_value(global_object, vm.argument(0), vm.argument(2), vm.argument(1)); +} + // 25.3.4.1 get DataView.prototype.buffer, https://tc39.es/ecma262/#sec-get-dataview.prototype.buffer JS_DEFINE_NATIVE_GETTER(DataViewPrototype::buffer_getter) { diff --git a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h index 8d5caebce9..4a7444adf6 100644 --- a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h @@ -29,6 +29,16 @@ private: JS_DECLARE_NATIVE_FUNCTION(get_uint_8); JS_DECLARE_NATIVE_FUNCTION(get_uint_16); JS_DECLARE_NATIVE_FUNCTION(get_uint_32); + JS_DECLARE_NATIVE_FUNCTION(set_big_int_64); + JS_DECLARE_NATIVE_FUNCTION(set_big_uint_64); + JS_DECLARE_NATIVE_FUNCTION(set_float_32); + JS_DECLARE_NATIVE_FUNCTION(set_float_64); + JS_DECLARE_NATIVE_FUNCTION(set_int_8); + JS_DECLARE_NATIVE_FUNCTION(set_int_16); + JS_DECLARE_NATIVE_FUNCTION(set_int_32); + JS_DECLARE_NATIVE_FUNCTION(set_uint_8); + JS_DECLARE_NATIVE_FUNCTION(set_uint_16); + JS_DECLARE_NATIVE_FUNCTION(set_uint_32); JS_DECLARE_NATIVE_GETTER(buffer_getter); JS_DECLARE_NATIVE_GETTER(byte_length_getter);