diff options
author | Linus Groh <mail@linusgroh.de> | 2020-12-02 13:23:51 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-12-02 23:49:00 +0100 |
commit | eaa85969c4ea8c579972e68733e10cef17f12069 (patch) | |
tree | 846533d46d8d940dfb1302fd72e36cb938d52240 | |
parent | 6de4f1fcb382fa7c9b2d2c4c3dc69085575cbe82 (diff) | |
download | serenity-eaa85969c4ea8c579972e68733e10cef17f12069.zip |
LibJS: Add Value::to_{index,length,integer_or_infinity} abstract operations
We should pay more attention to using the well-defined abstract
operations from the spec rather than making up our own, often slightly
different rules. This is another step in that direction.
-rw-r--r-- | Libraries/LibJS/Runtime/ErrorTypes.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Value.cpp | 63 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Value.h | 3 |
3 files changed, 66 insertions, 1 deletions
diff --git a/Libraries/LibJS/Runtime/ErrorTypes.h b/Libraries/LibJS/Runtime/ErrorTypes.h index a345d1bd4e..501cad3af7 100644 --- a/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Libraries/LibJS/Runtime/ErrorTypes.h @@ -46,6 +46,7 @@ M(InOperatorWithObject, "'in' operator must be used on an object") \ M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not an object") \ M(InvalidAssignToConst, "Invalid assignment to const variable") \ + M(InvalidIndex, "Index must be a positive integer") \ M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \ M(InvalidLength, "Invalid {} length") \ M(InvalidRadix, "Radix must be an integer no less than 2, and no greater than 36") \ diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index ccb3f52039..70b411186c 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -53,6 +53,9 @@ namespace JS { +// Used in various abstract operations to make it obvious when a non-optional return value must be discarded. +static const double INVALID { 0 }; + static const Crypto::SignedBigInteger BIGINT_ZERO { 0 }; static bool is_valid_bigint_value(String string) @@ -362,11 +365,12 @@ i32 Value::to_i32(GlobalObject& global_object) const size_t Value::to_size_t(GlobalObject& global_object) const { + // FIXME: Replace uses of this function with to_length/to_index for correct behaviour and remove this eventually. if (is_empty()) return 0; auto number = to_number(global_object); if (global_object.vm().exception()) - return 0; + return INVALID; if (number.is_nan()) return 0; if (number.as_double() <= 0) @@ -374,6 +378,63 @@ size_t Value::to_size_t(GlobalObject& global_object) const return number.as_size_t(); } +size_t Value::to_length(GlobalObject& global_object) const +{ + // 7.1.20 ToLength, https://tc39.es/ecma262/#sec-tolength + + auto& vm = global_object.vm(); + + auto len = to_integer_or_infinity(global_object); + if (vm.exception()) + return INVALID; + if (len <= 0) + return 0; + return min(len, MAX_ARRAY_LIKE_INDEX); +} + +size_t Value::to_index(GlobalObject& global_object) const +{ + // 7.1.22 ToIndex, https://tc39.es/ecma262/#sec-toindex + + auto& vm = global_object.vm(); + + if (is_undefined()) + return 0; + auto integer_index = to_integer_or_infinity(global_object); + if (vm.exception()) + return INVALID; + if (integer_index < 0) { + vm.throw_exception<RangeError>(global_object, ErrorType::InvalidIndex); + return INVALID; + } + auto index = Value(integer_index).to_length(global_object); + ASSERT(!vm.exception()); + if (integer_index != index) { + vm.throw_exception<RangeError>(global_object, ErrorType::InvalidIndex); + return INVALID; + } + return index; +} + +double Value::to_integer_or_infinity(GlobalObject& global_object) const +{ + // 7.1.5 ToIntegerOrInfinity, https://tc39.es/ecma262/#sec-tointegerorinfinity + + auto& vm = global_object.vm(); + + auto number = to_number(global_object); + if (vm.exception()) + return INVALID; + if (number.is_nan() || number.as_double() == 0) + return 0; + if (number.is_infinity()) + return number.as_double(); + auto integer = floor(abs(number.as_double())); + if (number.as_double() < 0) + integer = -integer; + return integer; +} + Value greater_than(GlobalObject& global_object, Value lhs, Value rhs) { TriState relation = abstract_relation(global_object, false, lhs, rhs); diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index c7a0c3d078..6ad54f12d0 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -251,6 +251,9 @@ public: double to_double(GlobalObject&) const; i32 to_i32(GlobalObject&) const; size_t to_size_t(GlobalObject&) const; + size_t to_length(GlobalObject&) const; + size_t to_index(GlobalObject&) const; + double to_integer_or_infinity(GlobalObject&) const; bool to_boolean() const; String to_string_without_side_effects() const; |