summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Runtime/Value.cpp
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2020-12-02 13:23:51 +0000
committerAndreas Kling <kling@serenityos.org>2020-12-02 23:49:00 +0100
commiteaa85969c4ea8c579972e68733e10cef17f12069 (patch)
tree846533d46d8d940dfb1302fd72e36cb938d52240 /Libraries/LibJS/Runtime/Value.cpp
parent6de4f1fcb382fa7c9b2d2c4c3dc69085575cbe82 (diff)
downloadserenity-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.cpp63
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);