summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Runtime
diff options
context:
space:
mode:
Diffstat (limited to 'Libraries/LibJS/Runtime')
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.cpp6
-rw-r--r--Libraries/LibJS/Runtime/ObjectConstructor.cpp13
-rw-r--r--Libraries/LibJS/Runtime/Value.cpp160
-rw-r--r--Libraries/LibJS/Runtime/Value.h8
4 files changed, 114 insertions, 73 deletions
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 9df20acea3..8a9fa55876 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -344,7 +344,7 @@ Value ArrayPrototype::index_of(Interpreter& interpreter)
auto search_element = interpreter.argument(0);
for (i32 i = from_index; i < array_size; ++i) {
auto& element = array->elements().at(i);
- if (typed_eq(interpreter, element, search_element).as_bool())
+ if (strict_eq(interpreter, element, search_element))
return Value(i);
}
@@ -398,7 +398,7 @@ Value ArrayPrototype::last_index_of(Interpreter& interpreter)
auto search_element = interpreter.argument(0);
for (i32 i = array_size - 1; i >= from_index; --i) {
auto& element = array->elements().at(i);
- if (typed_eq(interpreter, element, search_element).as_bool())
+ if (strict_eq(interpreter, element, search_element))
return Value(i);
}
@@ -432,7 +432,7 @@ Value ArrayPrototype::includes(Interpreter& interpreter)
auto value_to_find = interpreter.argument(0);
for (i32 i = from_index; i < array_size; ++i) {
auto& element = array->elements().at(i);
- if (typed_eq(interpreter, element, value_to_find).as_bool())
+ if (same_value_zero(interpreter, element, value_to_find))
return Value(true);
}
diff --git a/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Libraries/LibJS/Runtime/ObjectConstructor.cpp
index e3f96fe4b7..a7700b0aee 100644
--- a/Libraries/LibJS/Runtime/ObjectConstructor.cpp
+++ b/Libraries/LibJS/Runtime/ObjectConstructor.cpp
@@ -131,18 +131,7 @@ Value ObjectConstructor::define_property(Interpreter& interpreter)
Value ObjectConstructor::is(Interpreter& interpreter)
{
- auto value1 = interpreter.argument(0);
- auto value2 = interpreter.argument(1);
- if (value1.is_nan() && value2.is_nan())
- return Value(true);
- if (value1.is_number() && value1.as_double() == 0 && value2.is_number() && value2.as_double() == 0) {
- if (value1.is_positive_zero() && value2.is_positive_zero())
- return Value(true);
- if (value1.is_negative_zero() && value2.is_negative_zero())
- return Value(true);
- return Value(false);
- }
- return typed_eq(interpreter, value1, value2);
+ return Value(same_value(interpreter, interpreter.argument(0), interpreter.argument(1)));
}
Value ObjectConstructor::keys(Interpreter& interpreter)
diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp
index a2f1cceec5..fbcab42581 100644
--- a/Libraries/LibJS/Runtime/Value.cpp
+++ b/Libraries/LibJS/Runtime/Value.cpp
@@ -156,12 +156,14 @@ Value Value::to_number() const
case Type::Empty:
ASSERT_NOT_REACHED();
return {};
+ case Type::Undefined:
+ return js_nan();
+ case Type::Null:
+ return Value(0);
case Type::Boolean:
return Value(m_value.as_bool ? 1 : 0);
case Type::Number:
return Value(m_value.as_double);
- case Type::Null:
- return Value(0);
case Type::String: {
// FIXME: Trim whitespace beforehand
auto& string = as_string().string();
@@ -179,8 +181,6 @@ Value Value::to_number() const
return js_nan();
}
- case Type::Undefined:
- return js_nan();
case Type::Object:
return m_value.as_object->to_primitive(Object::PreferredType::Number).to_number();
}
@@ -348,84 +348,132 @@ Value exp(Interpreter&, Value lhs, Value rhs)
return Value(pow(lhs.to_number().as_double(), rhs.to_number().as_double()));
}
-Value typed_eq(Interpreter&, Value lhs, Value rhs)
+Value in(Interpreter& interpreter, Value lhs, Value rhs)
+{
+ if (!rhs.is_object())
+ return interpreter.throw_exception<TypeError>("'in' operator must be used on object");
+
+ return Value(!rhs.as_object().get(lhs.to_string()).is_empty());
+}
+
+Value instance_of(Interpreter&, Value lhs, Value rhs)
{
- if (rhs.type() != lhs.type())
+ if (!lhs.is_object() || !rhs.is_object())
+ return Value(false);
+
+ auto constructor_prototype_property = rhs.as_object().get("prototype");
+ if (!constructor_prototype_property.is_object())
return Value(false);
+ return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object()));
+}
+
+const LogStream& operator<<(const LogStream& stream, const Value& value)
+{
+ return stream << value.to_string();
+}
+
+bool same_value(Interpreter& interpreter, Value lhs, Value rhs)
+{
+ if (lhs.type() != rhs.type())
+ return false;
+
+ if (lhs.is_number()) {
+ if (lhs.is_nan() && rhs.is_nan())
+ return true;
+ if (lhs.is_positive_zero() && rhs.is_negative_zero())
+ return false;
+ if (lhs.is_negative_zero() && rhs.is_positive_zero())
+ return false;
+ return lhs.to_double() == rhs.to_double();
+ }
+
+ return same_value_non_numeric(interpreter, lhs, rhs);
+}
+
+bool same_value_zero(Interpreter& interpreter, Value lhs, Value rhs)
+{
+ if (lhs.type() != rhs.type())
+ return false;
+
+ if (lhs.is_number()) {
+ if (lhs.is_nan() && rhs.is_nan())
+ return true;
+ if ((lhs.is_positive_zero() || lhs.is_negative_zero()) && (rhs.is_positive_zero() || rhs.is_negative_zero()))
+ return true;
+ return lhs.to_double() == rhs.to_double();
+ }
+
+ return same_value_non_numeric(interpreter, lhs, rhs);
+}
+
+bool same_value_non_numeric(Interpreter&, Value lhs, Value rhs)
+{
+ ASSERT(!lhs.is_number());
+ ASSERT(lhs.type() == rhs.type());
+
switch (lhs.type()) {
case Value::Type::Empty:
ASSERT_NOT_REACHED();
- return {};
case Value::Type::Undefined:
- return Value(true);
case Value::Type::Null:
- return Value(true);
- case Value::Type::Number:
- return Value(lhs.as_double() == rhs.as_double());
+ return true;
case Value::Type::String:
- return Value(lhs.as_string().string() == rhs.as_string().string());
+ return lhs.as_string().string() == rhs.as_string().string();
case Value::Type::Boolean:
- return Value(lhs.as_bool() == rhs.as_bool());
+ return lhs.as_bool() == rhs.as_bool();
case Value::Type::Object:
- return Value(&lhs.as_object() == &rhs.as_object());
+ return &lhs.as_object() == &rhs.as_object();
+ default:
+ ASSERT_NOT_REACHED();
}
-
- ASSERT_NOT_REACHED();
}
-Value eq(Interpreter& interpreter, Value lhs, Value rhs)
+bool strict_eq(Interpreter& interpreter, Value lhs, Value rhs)
{
- if (lhs.type() == rhs.type())
- return typed_eq(interpreter, lhs, rhs);
-
- if ((lhs.is_undefined() || lhs.is_null()) && (rhs.is_undefined() || rhs.is_null()))
- return Value(true);
-
- if (lhs.is_object() && rhs.is_boolean())
- return eq(interpreter, lhs.as_object().to_primitive(), rhs.to_number());
-
- if (lhs.is_boolean() && rhs.is_object())
- return eq(interpreter, lhs.to_number(), rhs.as_object().to_primitive());
+ if (lhs.type() != rhs.type())
+ return false;
- if (lhs.is_object())
- return eq(interpreter, lhs.as_object().to_primitive(), rhs);
+ if (lhs.is_number()) {
+ if (lhs.is_nan() || rhs.is_nan())
+ return false;
+ if (lhs.to_double() == rhs.to_double())
+ return true;
+ if ((lhs.is_positive_zero() || lhs.is_negative_zero()) && (rhs.is_positive_zero() || rhs.is_negative_zero()))
+ return true;
+ return false;
+ }
- if (rhs.is_object())
- return eq(interpreter, lhs, rhs.as_object().to_primitive());
+ return same_value_non_numeric(interpreter, lhs, rhs);
+}
- if (lhs.is_number() || rhs.is_number())
- return Value(lhs.to_number().as_double() == rhs.to_number().as_double());
+bool abstract_eq(Interpreter& interpreter, Value lhs, Value rhs)
+{
+ if (lhs.type() == rhs.type())
+ return strict_eq(interpreter, lhs, rhs);
- if ((lhs.is_string() && rhs.is_boolean()) || (lhs.is_string() && rhs.is_boolean()))
- return Value(lhs.to_number().as_double() == rhs.to_number().as_double());
+ if ((lhs.is_undefined() || lhs.is_null()) && (rhs.is_undefined() || rhs.is_null()))
+ return true;
- return Value(false);
-}
+ if (lhs.is_number() && rhs.is_string())
+ return abstract_eq(interpreter, lhs, rhs.to_number());
-Value in(Interpreter& interpreter, Value lhs, Value rhs)
-{
- if (!rhs.is_object())
- return interpreter.throw_exception<TypeError>("'in' operator must be used on object");
+ if (lhs.is_string() && rhs.is_number())
+ return abstract_eq(interpreter, lhs.to_number(), rhs);
- return Value(!rhs.as_object().get(lhs.to_string()).is_empty());
-}
+ if (lhs.is_boolean())
+ return abstract_eq(interpreter, lhs.to_number(), rhs);
-Value instance_of(Interpreter&, Value lhs, Value rhs)
-{
- if (!lhs.is_object() || !rhs.is_object())
- return Value(false);
+ if (rhs.is_boolean())
+ return abstract_eq(interpreter, lhs, rhs.to_number());
- auto constructor_prototype_property = rhs.as_object().get("prototype");
- if (!constructor_prototype_property.is_object())
- return Value(false);
+ if ((lhs.is_string() || lhs.is_number()) && rhs.is_object())
+ return abstract_eq(interpreter, lhs, rhs.to_primitive(interpreter));
- return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object()));
-}
+ if (lhs.is_object() && (rhs.is_string() || rhs.is_number()))
+ return abstract_eq(interpreter, lhs.to_primitive(interpreter), rhs);
-const LogStream& operator<<(const LogStream& stream, const Value& value)
-{
- return stream << value.to_string();
+ return false;
}
}
diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h
index 82c4e4478a..a57098ac32 100644
--- a/Libraries/LibJS/Runtime/Value.h
+++ b/Libraries/LibJS/Runtime/Value.h
@@ -233,11 +233,15 @@ Value mul(Interpreter&, Value lhs, Value rhs);
Value div(Interpreter&, Value lhs, Value rhs);
Value mod(Interpreter&, Value lhs, Value rhs);
Value exp(Interpreter&, Value lhs, Value rhs);
-Value eq(Interpreter&, Value lhs, Value rhs);
-Value typed_eq(Interpreter&, Value lhs, Value rhs);
Value in(Interpreter&, Value lhs, Value rhs);
Value instance_of(Interpreter&, Value lhs, Value rhs);
+bool abstract_eq(Interpreter&, Value lhs, Value rhs);
+bool strict_eq(Interpreter&, Value lhs, Value rhs);
+bool same_value(Interpreter&, Value lhs, Value rhs);
+bool same_value_zero(Interpreter&, Value lhs, Value rhs);
+bool same_value_non_numeric(Interpreter&, Value lhs, Value rhs);
+
const LogStream& operator<<(const LogStream&, const Value&);
}