From c54b9a692011d8094f20f6852ddac0f77edddc24 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Mon, 14 Jun 2021 01:57:15 +0300 Subject: [PATCH] LibJS: Add all of the DataView.prototype.get* methods --- .../LibCrypto/BigInt/SignedBigInteger.h | 13 +++ .../LibCrypto/BigInt/UnsignedBigInteger.h | 10 ++ .../Libraries/LibJS/Runtime/ArrayBuffer.h | 57 ++++++++++ .../LibJS/Runtime/CommonPropertyNames.h | 10 ++ .../LibJS/Runtime/DataViewPrototype.cpp | 105 ++++++++++++++++++ .../LibJS/Runtime/DataViewPrototype.h | 11 ++ 6 files changed, 206 insertions(+) diff --git a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h index 5ac98d9662..b110eab85d 100644 --- a/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h @@ -47,6 +47,19 @@ public: static SignedBigInteger import_data(const StringView& data) { return import_data((const u8*)data.characters_without_null_termination(), data.length()); } static SignedBigInteger import_data(const u8* ptr, size_t length); + static SignedBigInteger create_from(i64 value) + { + auto sign = false; + u64 unsigned_value; + if (value < 0) { + unsigned_value = static_cast(-(value + 1)) + 1; + sign = true; + } else { + unsigned_value = value; + } + return SignedBigInteger { UnsignedBigInteger::create_from(unsigned_value), sign }; + } + size_t export_data(Bytes, bool remove_leading_zeros = false) const; static SignedBigInteger from_base10(StringView str); diff --git a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h index ff5bad7897..d1a4d8e270 100644 --- a/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h +++ b/Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h @@ -41,6 +41,16 @@ public: return UnsignedBigInteger(ptr, length); } + static UnsignedBigInteger create_from(u64 value) + { + VERIFY(sizeof(Word) == 4); + UnsignedBigInteger integer; + integer.m_words.resize(2); + integer.m_words[0] = static_cast(value & 0xFFFFFFFF); + integer.m_words[1] = static_cast((value >> 32) & 0xFFFFFFFF); + return integer; + } + size_t export_data(Bytes, bool remove_leading_zeros = false) const; static UnsignedBigInteger from_base10(const String& str); diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h index 82e886fbcf..cc9b5f1a49 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h @@ -8,6 +8,7 @@ #include #include +#include #include namespace JS { @@ -33,6 +34,13 @@ public: void detach_buffer() { m_buffer = Empty {}; } bool is_detached() const { return m_buffer.has(); } + enum Order { + SeqCst, + Unordered + }; + template + Value get_value(size_t byte_index, bool is_typed_array, Order, bool is_little_endian = true); + private: virtual void visit_edges(Visitor&) override; @@ -51,4 +59,53 @@ private: Value m_detach_key; }; +// 25.1.2.9 RawBytesToNumeric ( type, rawBytes, isLittleEndian ), https://tc39.es/ecma262/#sec-rawbytestonumeric +template +static Value raw_bytes_to_numeric(GlobalObject& global_object, ByteBuffer raw_value, bool is_little_endian) +{ + if (!is_little_endian) { + VERIFY(raw_value.size() % 2 == 0); + for (size_t i = 0; i < raw_value.size() / 2; ++i) + swap(raw_value[i], raw_value[raw_value.size() - 1 - i]); + } + if constexpr (IsSame) { + float value; + raw_value.span().copy_to({ &value, sizeof(float) }); + if (isnan(value)) + return js_nan(); + return Value(value); + } + if constexpr (IsSame) { + double value; + raw_value.span().copy_to({ &value, sizeof(double) }); + if (isnan(value)) + return js_nan(); + return Value(value); + } + if constexpr (!IsIntegral) + VERIFY_NOT_REACHED(); + T int_value = 0; + raw_value.span().copy_to({ &int_value, sizeof(T) }); + if constexpr (sizeof(T) == 8) { + if constexpr (IsSigned) + return js_bigint(global_object.heap(), Crypto::SignedBigInteger::create_from(int_value)); + else + return js_bigint(global_object.heap(), Crypto::SignedBigInteger { Crypto::UnsignedBigInteger::create_from(int_value) }); + } else { + return Value(int_value); + } +} + +// 25.1.2.10 GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] ), https://tc39.es/ecma262/#sec-getvaluefrombuffer +template +Value ArrayBuffer::get_value(size_t byte_index, [[maybe_unused]] bool is_typed_array, Order, bool is_little_endian) +{ + auto element_size = sizeof(T); + + // FIXME: Check for shared buffer + + auto raw_value = buffer_impl().slice(byte_index, element_size); + return raw_bytes_to_numeric(global_object(), move(raw_value), is_little_endian); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index b8c88aed25..8d8cb3b850 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -128,10 +128,17 @@ namespace JS { P(fround) \ P(gc) \ P(get) \ + P(getBigInt64) \ + P(getBigUint64) \ P(getDate) \ P(getDay) \ + P(getFloat32) \ + P(getFloat64) \ P(getFullYear) \ P(getHours) \ + P(getInt8) \ + P(getInt16) \ + P(getInt32) \ P(getMilliseconds) \ P(getMinutes) \ P(getMonth) \ @@ -142,6 +149,9 @@ namespace JS { P(getSeconds) \ P(getTime) \ P(getTimezoneOffset) \ + P(getUint8) \ + P(getUint16) \ + P(getUint32) \ P(getUTCDate) \ P(getUTCDay) \ P(getUTCFullYear) \ diff --git a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp index 731f8053aa..7f39f768a8 100644 --- a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.cpp @@ -17,6 +17,18 @@ void DataViewPrototype::initialize(GlobalObject& global_object) { auto& vm = this->vm(); Object::initialize(global_object); + u8 attr = Attribute::Writable | Attribute::Configurable; + + define_native_function(vm.names.getBigInt64, get_big_int_64, 1, attr); + define_native_function(vm.names.getBigUint64, get_big_uint_64, 1, attr); + define_native_function(vm.names.getFloat32, get_float_32, 1, attr); + define_native_function(vm.names.getFloat64, get_float_64, 1, attr); + define_native_function(vm.names.getInt8, get_int_8, 1, attr); + define_native_function(vm.names.getInt16, get_int_16, 1, attr); + define_native_function(vm.names.getInt32, get_int_32, 1, attr); + 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_accessor(vm.names.buffer, buffer_getter, {}, Attribute::Configurable); define_native_accessor(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable); @@ -40,6 +52,99 @@ static DataView* typed_this(VM& vm, GlobalObject& global_object) return static_cast(&this_value.as_object()); } +// 25.3.1.1 GetViewValue ( view, requestIndex, isLittleEndian, type ), https://tc39.es/ecma262/#sec-getviewvalue +template +static Value get_view_value(GlobalObject& global_object, Value request_index, Value is_little_endian) +{ + 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 {}; + 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->get_value(buffer_index, 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) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.6 DataView.prototype.getBigUint64 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getbiguint64 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_big_uint_64) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.7 DataView.prototype.getFloat32 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getfloat32 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_float_32) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.8 DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getfloat64 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_float_64) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.9 DataView.prototype.getInt8 ( byteOffset ), https://tc39.es/ecma262/#sec-dataview.prototype.getint8 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_int_8) +{ + return get_view_value(global_object, vm.argument(0), Value(true)); +} + +// 25.3.4.10 DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getint16 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_int_16) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.11 DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getint32 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_int_32) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.12 DataView.prototype.getUint8 ( byteOffset ), https://tc39.es/ecma262/#sec-dataview.prototype.getuint8 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_uint_8) +{ + return get_view_value(global_object, vm.argument(0), Value(true)); +} + +// 25.3.4.13 DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getuint16 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_uint_16) +{ + return get_view_value(global_object, vm.argument(0), vm.argument(1)); +} + +// 25.3.4.14 DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] ), https://tc39.es/ecma262/#sec-dataview.prototype.getuint32 +JS_DEFINE_NATIVE_FUNCTION(DataViewPrototype::get_uint_32) +{ + return get_view_value(global_object, vm.argument(0), 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 604e3c17c3..8d5caebce9 100644 --- a/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/DataViewPrototype.h @@ -19,6 +19,17 @@ public: virtual ~DataViewPrototype() override; private: + JS_DECLARE_NATIVE_FUNCTION(get_big_int_64); + JS_DECLARE_NATIVE_FUNCTION(get_big_uint_64); + JS_DECLARE_NATIVE_FUNCTION(get_float_32); + JS_DECLARE_NATIVE_FUNCTION(get_float_64); + JS_DECLARE_NATIVE_FUNCTION(get_int_8); + JS_DECLARE_NATIVE_FUNCTION(get_int_16); + JS_DECLARE_NATIVE_FUNCTION(get_int_32); + JS_DECLARE_NATIVE_FUNCTION(get_uint_8); + JS_DECLARE_NATIVE_FUNCTION(get_uint_16); + JS_DECLARE_NATIVE_FUNCTION(get_uint_32); + JS_DECLARE_NATIVE_GETTER(buffer_getter); JS_DECLARE_NATIVE_GETTER(byte_length_getter); JS_DECLARE_NATIVE_GETTER(byte_offset_getter);