summaryrefslogtreecommitdiff
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
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.
-rw-r--r--Libraries/LibJS/Runtime/ErrorTypes.h1
-rw-r--r--Libraries/LibJS/Runtime/Value.cpp63
-rw-r--r--Libraries/LibJS/Runtime/Value.h3
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;