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 /Libraries/LibJS/Runtime/Value.cpp | |
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.
Diffstat (limited to 'Libraries/LibJS/Runtime/Value.cpp')
-rw-r--r-- | Libraries/LibJS/Runtime/Value.cpp | 63 |
1 files changed, 62 insertions, 1 deletions
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); |