diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index 20e7cee7fd..6de6ed238b 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -2599,7 +2599,7 @@ JS::ThrowCompletionOr> @class_name@::legacy_pla if (interface.supports_indexed_properties()) { // ...and P is an array index, then: get_own_property_generator.append(R"~~~( - if (IDL::is_an_array_index(global_object, property_name)) { + if (property_name.is_number()) { // 1. Let index be the result of calling ToUint32(P). u32 index = property_name.as_number(); @@ -2874,7 +2874,7 @@ JS::ThrowCompletionOr @class_name@::internal_set(JS::PropertyKey const& pr if (interface.indexed_property_setter.has_value()) { // ...and P is an array index, then: scoped_generator.append(R"~~~( - if (IDL::is_an_array_index(global_object, property_name)) { + if (property_name.is_number()) { // 1. Invoke the indexed property setter on O with P and V. TRY(invoke_indexed_property_setter(global_object, impl(), property_name, value)); @@ -2919,14 +2919,14 @@ JS::ThrowCompletionOr @class_name@::internal_set(JS::PropertyKey const& pr JS::ThrowCompletionOr @class_name@::internal_define_own_property(JS::PropertyKey const& property_name, JS::PropertyDescriptor const& property_descriptor) { [[maybe_unused]] auto& vm = this->vm(); - auto& global_object = this->global_object(); + [[maybe_unused]] auto& global_object = this->global_object(); )~~~"); // 1. If O supports indexed properties... if (interface.supports_indexed_properties()) { // ...and P is an array index, then: scoped_generator.append(R"~~~( - if (IDL::is_an_array_index(global_object, property_name)) { + if (property_name.is_number()) { // 1. If the result of calling IsDataDescriptor(Desc) is false, then return false. if (!property_descriptor.is_data_descriptor()) return false; @@ -3038,14 +3038,14 @@ JS::ThrowCompletionOr @class_name@::internal_define_own_property(JS::Prope scoped_generator.append(R"~~~( JS::ThrowCompletionOr @class_name@::internal_delete(JS::PropertyKey const& property_name) { - auto& global_object = this->global_object(); + [[maybe_unused]] auto& global_object = this->global_object(); )~~~"); // 1. If O supports indexed properties... if (interface.supports_indexed_properties()) { // ...and P is an array index, then: scoped_generator.append(R"~~~( - if (IDL::is_an_array_index(global_object, property_name)) { + if (property_name.is_number()) { // 1. Let index be the result of calling ToUint32(P). u32 index = property_name.as_number(); @@ -3362,7 +3362,7 @@ JS::ThrowCompletionOr @constructor_class@::construct(FunctionObject generator.append(R"~~~( [[maybe_unused]] auto& vm = this->vm(); - auto& global_object = this->global_object(); + [[maybe_unused]] auto& global_object = this->global_object(); auto& window = static_cast(global_object); )~~~"); diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index aaf45b36f2..f275d58c84 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -1031,7 +1031,7 @@ Object* create_mapped_arguments_object(GlobalObject& global_object, FunctionObje } // 7.1.21 CanonicalNumericIndexString ( argument ), https://tc39.es/ecma262/#sec-canonicalnumericindexstring -Value canonical_numeric_index_string(GlobalObject& global_object, PropertyKey const& property_key) +CanonicalIndex canonical_numeric_index_string(PropertyKey const& property_key, CanonicalIndexMode mode) { // NOTE: If the property name is a number type (An implementation-defined optimized // property key type), it can be treated as a string property that has already been @@ -1040,24 +1040,56 @@ Value canonical_numeric_index_string(GlobalObject& global_object, PropertyKey co VERIFY(property_key.is_string() || property_key.is_number()); if (property_key.is_number()) - return Value(property_key.as_number()); + return CanonicalIndex(CanonicalIndex::Type::Index, property_key.as_number()); + + if (mode != CanonicalIndexMode::DetectNumericRoundtrip) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); // 1. Assert: Type(argument) is String. - auto argument = Value(js_string(global_object.vm(), property_key.as_string())); + auto& argument = property_key.as_string(); - // 2. If argument is "-0", return -0𝔽. - if (argument.as_string().string() == "-0") - return Value(-0.0); + // Handle trivial cases without a full round trip test + // We do not need to check for argument == "0" at this point because we + // already covered it with the is_number() == true path. + if (argument.is_empty()) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); + u32 current_index = 0; + if (argument.characters()[current_index] == '-') { + current_index++; + if (current_index == argument.length()) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); + } + if (argument.characters()[current_index] == '0') { + current_index++; + if (current_index == argument.length()) + return CanonicalIndex(CanonicalIndex::Type::Numeric, 0); + if (argument.characters()[current_index] != '.') + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); + current_index++; + if (current_index == argument.length()) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); + } + + // Short circuit a few common cases + if (argument == "Infinity" || argument == "-Infinity" || argument == "NaN") + return CanonicalIndex(CanonicalIndex::Type::Numeric, 0); + + // Short circuit any string that doesn't start with digits + if (char first_non_zero = argument.characters()[current_index]; first_non_zero < '0' || first_non_zero > '9') + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); // 3. Let n be ! ToNumber(argument). - auto n = MUST(argument.to_number(global_object)); + char* endptr; + auto n = Value(strtod(argument.characters(), &endptr)); + if (endptr != argument.characters() + argument.length()) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); // 4. If SameValue(! ToString(n), argument) is false, return undefined. - if (!same_value(MUST(n.to_primitive_string(global_object)), argument)) - return js_undefined(); + if (n.to_string_without_side_effects() != argument) + return CanonicalIndex(CanonicalIndex::Type::Undefined, 0); // 5. Return n. - return n; + return CanonicalIndex(CanonicalIndex::Type::Numeric, 0); } // 22.1.3.17.1 GetSubstitution ( matched, str, position, captures, namedCaptures, replacement ), https://tc39.es/ecma262/#sec-getsubstitution diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/AbstractOperations.h index 9e0035baac..1406027da4 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -39,7 +40,12 @@ bool validate_and_apply_property_descriptor(Object*, PropertyKey const&, bool ex ThrowCompletionOr get_prototype_from_constructor(GlobalObject&, FunctionObject const& constructor, Object* (GlobalObject::*intrinsic_default_prototype)()); Object* create_unmapped_arguments_object(GlobalObject&, Span arguments); Object* create_mapped_arguments_object(GlobalObject&, FunctionObject&, Vector const&, Span arguments, Environment&); -Value canonical_numeric_index_string(GlobalObject&, PropertyKey const&); + +enum class CanonicalIndexMode { + DetectNumericRoundtrip, + IgnoreNumericRoundtrip, +}; +CanonicalIndex canonical_numeric_index_string(PropertyKey const&, CanonicalIndexMode needs_numeric); ThrowCompletionOr get_substitution(GlobalObject&, Utf16View const& matched, Utf16View const& str, size_t position, Span captures, Value named_captures, Value replacement); enum class CallerMode { diff --git a/Userland/Libraries/LibJS/Runtime/CanonicalIndex.h b/Userland/Libraries/LibJS/Runtime/CanonicalIndex.h new file mode 100644 index 0000000000..71f81d5155 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/CanonicalIndex.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +class CanonicalIndex { +public: + enum class Type { + Index, + Numeric, + Undefined, + }; + + CanonicalIndex(Type type, u32 index) + : m_type(type) + , m_index(index) + { + } + + u32 as_index() const + { + VERIFY(is_index()); + return m_index; + } + + bool is_index() const { return m_type == Type::Index; } + bool is_undefined() const { return m_type == Type::Undefined; } + +private: + Type m_type; + u32 m_index; +}; diff --git a/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp index a113dc8f76..d518073def 100644 --- a/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp +++ b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp @@ -65,16 +65,14 @@ Optional PrimitiveString::get(GlobalObject& global_object, PropertyKey co return Value(static_cast(length)); } } - auto index = canonical_numeric_index_string(global_object, property_key); - if (index.type() != JS::Value::Type::Int32) - return {}; - if (index.as_i32() < 0) + auto index = canonical_numeric_index_string(property_key, CanonicalIndexMode::IgnoreNumericRoundtrip); + if (!index.is_index()) return {}; auto str = utf16_string_view(); auto length = str.length_in_code_units(); - if (static_cast(length) <= index.as_i32()) + if (length <= index.as_index()) return {}; - return js_string(vm(), str.substring_view(index.as_i32(), 1)); + return js_string(vm(), str.substring_view(index.as_index(), 1)); } PrimitiveString* js_string(Heap& heap, Utf16View const& view) diff --git a/Userland/Libraries/LibJS/Runtime/StringObject.cpp b/Userland/Libraries/LibJS/Runtime/StringObject.cpp index f92a65eb43..4434ee1e40 100644 --- a/Userland/Libraries/LibJS/Runtime/StringObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/StringObject.cpp @@ -44,7 +44,7 @@ void StringObject::visit_edges(Cell::Visitor& visitor) } // 10.4.3.5 StringGetOwnProperty ( S, P ), https://tc39.es/ecma262/#sec-stringgetownproperty -static Optional string_get_own_property(GlobalObject& global_object, StringObject const& string, PropertyKey const& property_key) +static Optional string_get_own_property(StringObject const& string, PropertyKey const& property_key) { // 1. Assert: S is an Object that has a [[StringData]] internal slot. // 2. Assert: IsPropertyKey(P) is true. @@ -57,15 +57,12 @@ static Optional string_get_own_property(GlobalObject& global return {}; // 4. Let index be ! CanonicalNumericIndexString(P). - auto index = canonical_numeric_index_string(global_object, property_key); + auto index = canonical_numeric_index_string(property_key, CanonicalIndexMode::IgnoreNumericRoundtrip); + // 5. If index is undefined, return undefined. - if (index.is_undefined()) - return {}; // 6. If IsIntegralNumber(index) is false, return undefined. - if (!index.is_integral_number()) - return {}; // 7. If index is -0𝔽, return undefined. - if (index.is_negative_zero()) + if (!index.is_index()) return {}; // 8. Let str be S.[[StringData]]. @@ -76,11 +73,11 @@ static Optional string_get_own_property(GlobalObject& global auto length = str.length_in_code_units(); // 11. If ℝ(index) < 0 or len ≤ ℝ(index), return undefined. - if (index.as_double() < 0 || length <= index.as_double()) + if (length <= index.as_index()) return {}; // 12. Let resultStr be the String value of length 1, containing one code unit from str, specifically the code unit at index ℝ(index). - auto result_str = js_string(string.vm(), str.substring_view(index.as_double(), 1)); + auto result_str = js_string(string.vm(), str.substring_view(index.as_index(), 1)); // 13. Return the PropertyDescriptor { [[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false }. return PropertyDescriptor { @@ -104,7 +101,7 @@ ThrowCompletionOr> StringObject::internal_get_own_p return descriptor; // 4. Return ! StringGetOwnProperty(S, P). - return string_get_own_property(global_object(), *this, property_key); + return string_get_own_property(*this, property_key); } // 10.4.3.2 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-string-exotic-objects-defineownproperty-p-desc @@ -114,7 +111,7 @@ ThrowCompletionOr StringObject::internal_define_own_property(PropertyKey c VERIFY(property_key.is_valid()); // 2. Let stringDesc be ! StringGetOwnProperty(S, P). - auto string_descriptor = string_get_own_property(global_object(), *this, property_key); + auto string_descriptor = string_get_own_property(*this, property_key); // 3. If stringDesc is not undefined, then if (string_descriptor.has_value()) { diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.h b/Userland/Libraries/LibJS/Runtime/TypedArray.h index 640e516457..4e81d0eb38 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArray.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArray.h @@ -74,31 +74,28 @@ private: }; // 10.4.5.9 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex -inline bool is_valid_integer_index(TypedArrayBase const& typed_array, Value property_index) +inline bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index) { + // 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false. if (typed_array.viewed_array_buffer()->is_detached()) return false; - // TODO: This can be optimized by skipping the following 3 out of 4 checks if property_index - // came from a number-type PropertyKey instead of a canonicalized string-type PropertyKey - - // If ! IsIntegralNumber(index) is false, return false. - if (!property_index.is_integral_number()) - return false; - // If index is -0𝔽, return false. - if (property_index.is_negative_zero()) + // 2. If ! IsIntegralNumber(index) is false, return false. + // 3. If index is -0𝔽, return false. + if (!property_index.is_index()) return false; - // If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false. - if (property_index.as_double() < 0 || property_index.as_double() >= typed_array.array_length()) + // 4. If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false. + if (property_index.as_index() >= typed_array.array_length()) return false; + // 5. Return true. return true; } // 10.4.5.10 IntegerIndexedElementGet ( O, index ), https://tc39.es/ecma262/#sec-integerindexedelementget template -inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, Value property_index) +inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, CanonicalIndex property_index) { // 1. Assert: O is an Integer-Indexed exotic object. @@ -112,7 +109,7 @@ inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, Valu // 4. Let arrayTypeName be the String value of O.[[TypedArrayName]]. // 5. Let elementSize be the Element Size value specified in Table 64 for arrayTypeName. // 6. Let indexedPosition be (ℝ(index) × elementSize) + offset. - Checked indexed_position = (i64)property_index.as_double(); + Checked indexed_position = property_index.as_index(); indexed_position *= typed_array.element_size(); indexed_position += offset; // FIXME: Not exactly sure what we should do when overflow occurs. @@ -130,7 +127,7 @@ inline Value integer_indexed_element_get(TypedArrayBase const& typed_array, Valu // 10.4.5.11 IntegerIndexedElementSet ( O, index, value ), https://tc39.es/ecma262/#sec-integerindexedelementset // NOTE: In error cases, the function will return as if it succeeded. template -inline ThrowCompletionOr integer_indexed_element_set(TypedArrayBase& typed_array, Value property_index, Value value) +inline ThrowCompletionOr integer_indexed_element_set(TypedArrayBase& typed_array, CanonicalIndex property_index, Value value) { VERIFY(!value.is_empty()); auto& global_object = typed_array.global_object(); @@ -157,7 +154,7 @@ inline ThrowCompletionOr integer_indexed_element_set(TypedArrayBase& typed // b. Let arrayTypeName be the String value of O.[[TypedArrayName]]. // c. Let elementSize be the Element Size value specified in Table 64 for arrayTypeName. // d. Let indexedPosition be (ℝ(index) × elementSize) + offset. - Checked indexed_position = (i64)property_index.as_double(); + Checked indexed_position = property_index.as_index(); indexed_position *= typed_array.element_size(); indexed_position += offset; // FIXME: Not exactly sure what we should do when overflow occurs. @@ -198,7 +195,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, then if (!numeric_index.is_undefined()) { // i. Let value be ! IntegerIndexedElementGet(O, numericIndex). @@ -238,7 +235,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, return ! IsValidIntegerIndex(O, numericIndex). if (!numeric_index.is_undefined()) return is_valid_integer_index(*this, numeric_index); @@ -264,7 +261,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, then if (!numeric_index.is_undefined()) { // i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false. @@ -315,7 +312,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, then if (!numeric_index.is_undefined()) { // i. Return ! IntegerIndexedElementGet(O, numericIndex). @@ -343,7 +340,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, then if (!numeric_index.is_undefined()) { // i. Perform ? IntegerIndexedElementSet(O, numericIndex, V). @@ -373,7 +370,7 @@ public: // NOTE: This includes an implementation-defined optimization, see note above! if (property_key.is_string() || property_key.is_number()) { // a. Let numericIndex be ! CanonicalNumericIndexString(P). - auto numeric_index = canonical_numeric_index_string(global_object(), property_key); + auto numeric_index = canonical_numeric_index_string(property_key, CanonicalIndexMode::DetectNumericRoundtrip); // b. If numericIndex is not undefined, then if (!numeric_index.is_undefined()) { // i. If ! IsValidIntegerIndex(O, numericIndex) is false, return true; else return false. diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js index 041faf7be9..5cdb93e715 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.js @@ -273,3 +273,89 @@ test("TypedArray is abstract", () => { new TypedArray(); }).toThrowWithMessage(TypeError, "Abstract class TypedArray cannot be constructed directly"); }); + +TYPED_ARRAYS.forEach(T => { + test(`all numeric indices are valid on ${T.name}`, () => { + const newTypedArray = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + expect(newTypedArray).toHaveLength(10); + + function PoisonError() {} + + const poisonedObject = { + valueOf() { + throw new PoisonError(); + }, + extraValue: 4, + }; + + // valueOf should only be called if the string is a valid numeric index + expect(() => (newTypedArray["0"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["-0"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["Infinity"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["-Infinity"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["NaN"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["1"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["1.1"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["0.3"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["-1"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["-1.1"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray["-0.3"] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[0] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[-0] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[Infinity] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[-Infinity] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[NaN] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[1] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[1.1] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[0.3] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[-1] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[-1.1] = poisonedObject)).toThrow(PoisonError); + expect(() => (newTypedArray[-0.3] = poisonedObject)).toThrow(PoisonError); + + function expectValueSet(property) { + newTypedArray[property] = poisonedObject; + expect(newTypedArray).toHaveLength(10); + expect(newTypedArray[property].extraValue).toBe(4); + expect(delete newTypedArray[property]).toBeTrue(); + } + + expectValueSet("a"); + expectValueSet(" 1"); + expectValueSet("a"); + expectValueSet("a"); + expectValueSet(" 1"); + expectValueSet("+Infinity"); + expectValueSet("00"); + expectValueSet("01"); + expectValueSet("-01"); + expectValueSet("-"); + expectValueSet("."); + expectValueSet("-."); + expectValueSet("1e"); + expectValueSet("1e"); + expectValueSet("1e0"); + expectValueSet("5."); + expectValueSet(".5"); + expectValueSet("-.5"); + expectValueSet("1e1"); + expectValueSet("1e+1"); + expectValueSet("0.0000001"); // ToString = "1e-7" + + // Things that can round trip as numbers get consumed + function expectValueNotSet(property) { + expect(() => { + newTypedArray[property] = poisonedObject; + }).toThrow(PoisonError); + expect(newTypedArray[property]).toBeUndefined(); + expect(delete newTypedArray[property]).toBeTrue(); + } + expectValueNotSet("-2"); + expectValueNotSet(1.5); + expectValueNotSet("-0"); + expectValueNotSet(-1.5); + expectValueNotSet("-Infinity"); + expectValueNotSet("Infinity"); + expectValueNotSet("NaN"); + expectValueNotSet("1e-10"); + }); +}); diff --git a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.cpp b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.cpp index 180d936d39..8c75a1c2dd 100644 --- a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.cpp @@ -16,45 +16,6 @@ namespace Web::Bindings::IDL { -// https://webidl.spec.whatwg.org/#is-an-array-index -bool is_an_array_index(JS::GlobalObject& global_object, JS::PropertyKey const& property_name) -{ - // 1. If Type(P) is not String, then return false. - if (!property_name.is_number()) - return false; - - // 2. Let index be ! CanonicalNumericIndexString(P). - auto index = JS::canonical_numeric_index_string(global_object, property_name); - - // 3. If index is undefined, then return false. - if (index.is_undefined()) - return false; - - // 4. If IsInteger(index) is false, then return false. - // NOTE: IsInteger is the old name of IsIntegralNumber. - if (!index.is_integral_number()) - return false; - - // 5. If index is −0, then return false. - if (index.is_negative_zero()) - return false; - - // FIXME: I'm not sure if this is correct. - auto index_as_double = index.as_double(); - - // 6. If index < 0, then return false. - if (index_as_double < 0) - return false; - - // 7. If index ≥ 2 ** 32 − 1, then return false. - // Note: 2 ** 32 − 1 is the maximum array length allowed by ECMAScript. - if (index_as_double >= NumericLimits::max()) - return false; - - // 8. Return true. - return true; -} - // https://webidl.spec.whatwg.org/#dfn-get-buffer-source-copy Optional get_buffer_source_copy(JS::Object const& buffer_source) { diff --git a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h index 0960a029ea..9804363a0a 100644 --- a/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h +++ b/Userland/Libraries/LibWeb/Bindings/IDLAbstractOperations.h @@ -16,7 +16,6 @@ namespace Web::Bindings::IDL { -bool is_an_array_index(JS::GlobalObject&, JS::PropertyKey const&); Optional get_buffer_source_copy(JS::Object const& buffer_source); // https://webidl.spec.whatwg.org/#call-user-object-operation-return