summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/public/Equality.h55
-rw-r--r--js/src/builtin/MapObject.cpp1
-rw-r--r--js/src/builtin/Object.cpp1
-rw-r--r--js/src/jit/VMFunctions.cpp1
-rw-r--r--js/src/jsapi-tests/testLooselyEqual.cpp10
-rw-r--r--js/src/jsapi-tests/testSameValue.cpp5
-rw-r--r--js/src/jsapi-tests/tests.h7
-rw-r--r--js/src/jsapi.cpp30
-rw-r--r--js/src/jsapi.h9
-rw-r--r--js/src/moz.build2
-rw-r--r--js/src/proxy/ScriptedProxyHandler.cpp2
-rw-r--r--js/src/shell/js.cpp3
-rw-r--r--js/src/vm/EqualityOperations.cpp215
-rw-r--r--js/src/vm/EqualityOperations.h44
-rw-r--r--js/src/vm/Interpreter.cpp165
-rw-r--r--js/src/vm/Interpreter.h10
-rw-r--r--js/src/vm/NativeObject.cpp1
17 files changed, 338 insertions, 223 deletions
diff --git a/js/public/Equality.h b/js/public/Equality.h
new file mode 100644
index 0000000000..03181074de
--- /dev/null
+++ b/js/public/Equality.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* Equality operations. */
+
+#ifndef js_Equality_h
+#define js_Equality_h
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::Value
+
+struct JSContext;
+
+namespace JS {
+
+/**
+ * Store |v1 === v2| to |*equal| -- strict equality, which performs no
+ * conversions on |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API(bool)
+StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Store |v1 == v2| to |*equal| -- loose equality, which may perform
+ * user-modifiable conversions on |v1| or |v2|.
+ *
+ * This operation can fail if a user-modifiable conversion fails *or* if an
+ * internal error occurs. (e.g. OOM while linearizing a string value).
+ */
+extern JS_PUBLIC_API(bool)
+LooselyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
+
+/**
+ * Stores |SameValue(v1, v2)| to |*equal| -- using the SameValue operation
+ * defined in ECMAScript, initially exposed to script as |Object.is|. SameValue
+ * behaves identically to strict equality, except that it equates two NaN values
+ * and does not equate differently-signed zeroes. It performs no conversions on
+ * |v1| or |v2| before comparing.
+ *
+ * This operation can fail only if an internal error occurs (e.g. OOM while
+ * linearizing a string value).
+ */
+extern JS_PUBLIC_API(bool)
+SameValue(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* same);
+
+} // namespace JS
+
+#endif /* js_Equality_h */
diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp
index 9d17acbf8a..893e0448a4 100644
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -12,6 +12,7 @@
#include "ds/OrderedHashTable.h"
#include "gc/Marking.h"
#include "js/Utility.h"
+#include "vm/EqualityOperations.h" // js::SameValue
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp
index d661a222e5..70a21079c0 100644
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -15,6 +15,7 @@
#include "jit/InlinableNatives.h"
#include "js/UniquePtr.h"
#include "vm/AsyncFunction.h"
+#include "vm/EqualityOperations.h" // js::SameValue
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index f191ce7d6d..6e5676f153 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -17,6 +17,7 @@
#include "jit/mips64/Simulator-mips64.h"
#include "vm/ArrayObject.h"
#include "vm/Debugger.h"
+#include "vm/EqualityOperations.h" // js::StrictlyEqual
#include "vm/Interpreter.h"
#include "vm/TraceLogging.h"
diff --git a/js/src/jsapi-tests/testLooselyEqual.cpp b/js/src/jsapi-tests/testLooselyEqual.cpp
index 70f5cf8964..5c017497c9 100644
--- a/js/src/jsapi-tests/testLooselyEqual.cpp
+++ b/js/src/jsapi-tests/testLooselyEqual.cpp
@@ -5,6 +5,8 @@
#include <limits>
#include <math.h>
+#include "js/Equality.h" // JS::LooselyEqual
+
#include "jsapi-tests/tests.h"
using namespace std;
@@ -15,15 +17,15 @@ struct LooseEqualityFixture : public JSAPITest
bool leq(JS::HandleValue x, JS::HandleValue y) {
bool equal;
- CHECK(JS_LooselyEqual(cx, x, y, &equal) && equal);
- CHECK(JS_LooselyEqual(cx, y, x, &equal) && equal);
+ CHECK(JS::LooselyEqual(cx, x, y, &equal) && equal);
+ CHECK(JS::LooselyEqual(cx, y, x, &equal) && equal);
return true;
}
bool nleq(JS::HandleValue x, JS::HandleValue y) {
bool equal;
- CHECK(JS_LooselyEqual(cx, x, y, &equal) && !equal);
- CHECK(JS_LooselyEqual(cx, y, x, &equal) && !equal);
+ CHECK(JS::LooselyEqual(cx, x, y, &equal) && !equal);
+ CHECK(JS::LooselyEqual(cx, y, x, &equal) && !equal);
return true;
}
};
diff --git a/js/src/jsapi-tests/testSameValue.cpp b/js/src/jsapi-tests/testSameValue.cpp
index 666da40360..50d2208d86 100644
--- a/js/src/jsapi-tests/testSameValue.cpp
+++ b/js/src/jsapi-tests/testSameValue.cpp
@@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "js/Equality.h" // JS::SameValue
#include "jsapi-tests/tests.h"
BEGIN_TEST(testSameValue)
@@ -11,7 +12,7 @@ BEGIN_TEST(testSameValue)
/*
* NB: passing a double that fits in an integer jsval is API misuse. As a
- * matter of defense in depth, however, JS_SameValue should return the
+ * matter of defense in depth, however, JS::SameValue should return the
* correct result comparing a positive-zero double to a negative-zero
* double, and this is believed to be the only way to make such a
* comparison possible.
@@ -19,7 +20,7 @@ BEGIN_TEST(testSameValue)
JS::RootedValue v1(cx, JS::DoubleValue(0.0));
JS::RootedValue v2(cx, JS::DoubleValue(-0.0));
bool same;
- CHECK(JS_SameValue(cx, v1, v2, &same));
+ CHECK(JS::SameValue(cx, v1, v2, &same));
CHECK(!same);
return true;
}
diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h
index 4d30ba8c85..dfb9f49887 100644
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -18,6 +18,7 @@
#include "jscntxt.h"
#include "jsgc.h"
+#include "js/Equality.h" // JS::SameValue
#include "js/Vector.h"
/* Note: Aborts on OOM. */
@@ -201,9 +202,9 @@ class JSAPITest
const char* filename, int lineno) {
bool same;
JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg);
- return (JS_SameValue(cx, actual, expected, &same) && same) ||
- fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
- actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
+ return (JS::SameValue(cx, actual, expected, &same) && same) ||
+ fail(JSAPITestString("CHECK_SAME failed: expected JS::SameValue(cx, ") +
+ actualExpr + ", " + expectedExpr + "), got !JS::SameValue(cx, " +
jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno);
}
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 50e3442ae8..9e5853b454 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -392,36 +392,6 @@ JS_TypeOfValue(JSContext* cx, HandleValue value)
}
JS_PUBLIC_API(bool)
-JS_StrictlyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal)
-{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, value1, value2);
- MOZ_ASSERT(equal);
- return StrictlyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API(bool)
-JS_LooselyEqual(JSContext* cx, HandleValue value1, HandleValue value2, bool* equal)
-{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, value1, value2);
- MOZ_ASSERT(equal);
- return LooselyEqual(cx, value1, value2, equal);
-}
-
-JS_PUBLIC_API(bool)
-JS_SameValue(JSContext* cx, HandleValue value1, HandleValue value2, bool* same)
-{
- AssertHeapIsIdle(cx);
- CHECK_REQUEST(cx);
- assertSameCompartment(cx, value1, value2);
- MOZ_ASSERT(same);
- return SameValue(cx, value1, value2, same);
-}
-
-JS_PUBLIC_API(bool)
JS_IsBuiltinEvalFunction(JSFunction* fun)
{
return IsAnyBuiltinEval(fun);
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index a6a5429cf5..923aa2bb05 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -959,15 +959,6 @@ InformalValueTypeName(const JS::Value& v);
} /* namespace JS */
-extern JS_PUBLIC_API(bool)
-JS_StrictlyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
-
-extern JS_PUBLIC_API(bool)
-JS_LooselyEqual(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* equal);
-
-extern JS_PUBLIC_API(bool)
-JS_SameValue(JSContext* cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool* same);
-
/** True iff fun is the global eval function. */
extern JS_PUBLIC_API(bool)
JS_IsBuiltinEvalFunction(JSFunction* fun);
diff --git a/js/src/moz.build b/js/src/moz.build
index b12d0a90cc..0f1302bb93 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -70,6 +70,7 @@ EXPORTS.js += [
'../public/Conversions.h',
'../public/Date.h',
'../public/Debug.h',
+ '../public/Equality.h',
'../public/GCAnnotations.h',
'../public/GCAPI.h',
'../public/GCHashTable.h',
@@ -293,6 +294,7 @@ main_deunified_sources = [
'vm/Debugger.cpp',
'vm/DebuggerMemory.cpp',
'vm/EnvironmentObject.cpp',
+ 'vm/EqualityOperations.cpp',
'vm/ErrorObject.cpp',
'vm/ForOfIterator.cpp',
'vm/GeneratorObject.cpp',
diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
index adb98edbdd..d396b7805c 100644
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -7,6 +7,8 @@
#include "jsapi.h"
+#include "vm/EqualityOperations.h" // js::SameValue
+
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index bab5cbb0b5..eaac23b537 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -71,6 +71,7 @@
#include "jit/JitcodeMap.h"
#include "jit/OptimizationTracking.h"
#include "js/Debug.h"
+#include "js/Equality.h" // JS::SameValue
#include "js/GCAPI.h"
#include "js/Initialization.h"
#include "js/StructuredClone.h"
@@ -2286,7 +2287,7 @@ AssertEq(JSContext* cx, unsigned argc, Value* vp)
}
bool same;
- if (!JS_SameValue(cx, args[0], args[1], &same))
+ if (!JS::SameValue(cx, args[0], args[1], &same))
return false;
if (!same) {
JSAutoByteString bytes0, bytes1;
diff --git a/js/src/vm/EqualityOperations.cpp b/js/src/vm/EqualityOperations.cpp
new file mode 100644
index 0000000000..6f90450b49
--- /dev/null
+++ b/js/src/vm/EqualityOperations.cpp
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/EqualityOperations.h" // js::LooselyEqual, js::StrictlyEqual, js::SameValue
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
+
+#include "jsapi.h" // js::AssertHeapIsIdle, CHECK_REQUEST
+#include "jsnum.h" // js::StringToNumber
+#include "jsobj.h" // js::ToPrimitive
+#include "jsstr.h" // js::EqualStrings
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/Equality.h" // JS::LooselyEqual, JS::StrictlyEqual, JS::SameValue
+#include "js/RootingAPI.h" // JS::Rooted, JS::Handle
+#include "js/Value.h" // JS::Int32Value, JS::SameType, JS::Value
+
+#include "jsboolinlines.h" // js::EmulatesUndefined
+#include "jscntxtinlines.h" // js::assertSameCompartment
+
+static inline bool
+EqualGivenSameType(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal)
+{
+ MOZ_ASSERT(SameType(lval, rval));
+
+ if (lval.isString())
+ return EqualStrings(cx, lval.toString(), rval.toString(), equal);
+ if (lval.isDouble()) {
+ *equal = (lval.toDouble() == rval.toDouble());
+ return true;
+ }
+ if (lval.isGCThing()) { // objects or symbols
+ *equal = (lval.toGCThing() == rval.toGCThing());
+ return true;
+ }
+ *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
+ MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
+ return true;
+}
+
+static inline bool
+LooselyEqualBooleanAndOther(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result)
+{
+ MOZ_ASSERT(!rval.isBoolean());
+ JS::RootedValue lvalue(cx, JS::Int32Value(lval.toBoolean() ? 1 : 0));
+
+ // The tail-call would end up in Step 3.
+ if (rval.isNumber()) {
+ *result = (lvalue.toNumber() == rval.toNumber());
+ return true;
+ }
+ // The tail-call would end up in Step 6.
+ if (rval.isString()) {
+ double num;
+ if (!StringToNumber(cx, rval.toString(), &num))
+ return false;
+ *result = (lvalue.toNumber() == num);
+ return true;
+ }
+
+ return js::LooselyEqual(cx, lvalue, rval, result);
+}
+
+// ES6 draft rev32 7.2.12 Abstract Equality Comparison
+bool
+js::LooselyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result)
+{
+ // Step 3.
+ if (SameType(lval, rval))
+ return EqualGivenSameType(cx, lval, rval, result);
+
+ // Handle int32 x double.
+ if (lval.isNumber() && rval.isNumber()) {
+ *result = (lval.toNumber() == rval.toNumber());
+ return true;
+ }
+
+ // Step 4. This a bit more complex, because of the undefined emulating object.
+ if (lval.isNullOrUndefined()) {
+ // We can return early here, because null | undefined is only equal to the same set.
+ *result = rval.isNullOrUndefined() ||
+ (rval.isObject() && EmulatesUndefined(&rval.toObject()));
+ return true;
+ }
+
+ // Step 5.
+ if (rval.isNullOrUndefined()) {
+ MOZ_ASSERT(!lval.isNullOrUndefined());
+ *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
+ return true;
+ }
+
+ // Step 6.
+ if (lval.isNumber() && rval.isString()) {
+ double num;
+ if (!StringToNumber(cx, rval.toString(), &num))
+ return false;
+ *result = (lval.toNumber() == num);
+ return true;
+ }
+
+ // Step 7.
+ if (lval.isString() && rval.isNumber()) {
+ double num;
+ if (!StringToNumber(cx, lval.toString(), &num))
+ return false;
+ *result = (num == rval.toNumber());
+ return true;
+ }
+
+ // Step 8.
+ if (lval.isBoolean())
+ return LooselyEqualBooleanAndOther(cx, lval, rval, result);
+
+ // Step 9.
+ if (rval.isBoolean())
+ return LooselyEqualBooleanAndOther(cx, rval, lval, result);
+
+ // Step 10.
+ if ((lval.isString() || lval.isNumber() || lval.isSymbol()) && rval.isObject()) {
+ JS::RootedValue rvalue(cx, rval);
+ if (!ToPrimitive(cx, &rvalue))
+ return false;
+ return js::LooselyEqual(cx, lval, rvalue, result);
+ }
+
+ // Step 11.
+ if (lval.isObject() && (rval.isString() || rval.isNumber() || rval.isSymbol())) {
+ JS::RootedValue lvalue(cx, lval);
+ if (!ToPrimitive(cx, &lvalue))
+ return false;
+ return js::LooselyEqual(cx, lvalue, rval, result);
+ }
+
+ // Step 12.
+ *result = false;
+ return true;
+}
+
+JS_PUBLIC_API(bool)
+JS::LooselyEqual(JSContext* cx, Handle<Value> value1, Handle<Value> value2, bool* equal)
+{
+ js::AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ js::assertSameCompartment(cx, value1, value2);
+ MOZ_ASSERT(equal);
+ return js::LooselyEqual(cx, value1, value2, equal);
+}
+
+bool
+js::StrictlyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal)
+{
+ if (SameType(lval, rval))
+ return EqualGivenSameType(cx, lval, rval, equal);
+
+ if (lval.isNumber() && rval.isNumber()) {
+ *equal = (lval.toNumber() == rval.toNumber());
+ return true;
+ }
+
+ *equal = false;
+ return true;
+}
+
+JS_PUBLIC_API(bool)
+JS::StrictlyEqual(JSContext* cx, Handle<Value> value1, Handle<Value> value2, bool* equal)
+{
+ js::AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ js::assertSameCompartment(cx, value1, value2);
+ MOZ_ASSERT(equal);
+ return js::StrictlyEqual(cx, value1, value2, equal);
+}
+
+static inline bool
+IsNegativeZero(const JS::Value& v)
+{
+ return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
+}
+
+static inline bool
+IsNaN(const JS::Value& v)
+{
+ return v.isDouble() && mozilla::IsNaN(v.toDouble());
+}
+
+bool
+js::SameValue(JSContext* cx, JS::HandleValue v1, JS::HandleValue v2, bool* same)
+{
+ if (IsNegativeZero(v1)) {
+ *same = IsNegativeZero(v2);
+ return true;
+ }
+ if (IsNegativeZero(v2)) {
+ *same = false;
+ return true;
+ }
+ if (IsNaN(v1) && IsNaN(v2)) {
+ *same = true;
+ return true;
+ }
+ return js::StrictlyEqual(cx, v1, v2, same);
+}
+
+JS_PUBLIC_API(bool)
+JS::SameValue(JSContext* cx, Handle<Value> value1, Handle<Value> value2, bool* same)
+{
+ js::AssertHeapIsIdle(cx);
+ CHECK_REQUEST(cx);
+ js::assertSameCompartment(cx, value1, value2);
+ MOZ_ASSERT(same);
+ return js::SameValue(cx, value1, value2, same);
+}
diff --git a/js/src/vm/EqualityOperations.h b/js/src/vm/EqualityOperations.h
new file mode 100644
index 0000000000..13cae70b16
--- /dev/null
+++ b/js/src/vm/EqualityOperations.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * The equality comparisons of js/Equality.h, but with extra efficiency for
+ * SpiderMonkey-internal callers.
+ *
+ * These functions, assuming they're passed C++-valid arguments, are identical
+ * to the same-named JS::-namespaced functions -- just with hidden linkage (so
+ * they're more efficient to call), and without various external-caller-focused
+ * JSAPI-usage assertions performed that SpiderMonkey users never come close to
+ * failing.
+ */
+
+#ifndef vm_EqualityOperations_h
+#define vm_EqualityOperations_h
+
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/Value.h" // JS::Value
+
+struct JSContext;
+
+namespace js {
+
+/** Computes |lval === rval|. */
+extern bool
+StrictlyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* equal);
+
+/** Computes |lval == rval|. */
+extern bool
+LooselyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool* result);
+
+/**
+ * Computes |SameValue(v1, v2)| -- strict equality except that NaNs are
+ * considered equal and opposite-signed zeroes are considered unequal.
+ */
+extern bool
+SameValue(JSContext* cx, JS::HandleValue v1, JS::HandleValue v2, bool* same);
+
+} // namespace js
+
+#endif // vm_EqualityOperations_h
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index b17c762eae..d7c1b8e84a 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -41,6 +41,7 @@
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/Debugger.h"
+#include "vm/EqualityOperations.h" // js::StrictlyEqual
#include "vm/GeneratorObject.h"
#include "vm/Opcodes.h"
#include "vm/Scope.h"
@@ -786,170 +787,6 @@ js::HasInstance(JSContext* cx, HandleObject obj, HandleValue v, bool* bp)
return JS::InstanceofOperator(cx, obj, local, bp);
}
-static inline bool
-EqualGivenSameType(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal)
-{
- MOZ_ASSERT(SameType(lval, rval));
-
- if (lval.isString())
- return EqualStrings(cx, lval.toString(), rval.toString(), equal);
- if (lval.isDouble()) {
- *equal = (lval.toDouble() == rval.toDouble());
- return true;
- }
- if (lval.isGCThing()) { // objects or symbols
- *equal = (lval.toGCThing() == rval.toGCThing());
- return true;
- }
- *equal = lval.get().payloadAsRawUint32() == rval.get().payloadAsRawUint32();
- MOZ_ASSERT_IF(lval.isUndefined() || lval.isNull(), *equal);
- return true;
-}
-
-static inline bool
-LooselyEqualBooleanAndOther(JSContext* cx, HandleValue lval, HandleValue rval, bool* result)
-{
- MOZ_ASSERT(!rval.isBoolean());
- RootedValue lvalue(cx, Int32Value(lval.toBoolean() ? 1 : 0));
-
- // The tail-call would end up in Step 3.
- if (rval.isNumber()) {
- *result = (lvalue.toNumber() == rval.toNumber());
- return true;
- }
- // The tail-call would end up in Step 6.
- if (rval.isString()) {
- double num;
- if (!StringToNumber(cx, rval.toString(), &num))
- return false;
- *result = (lvalue.toNumber() == num);
- return true;
- }
-
- return LooselyEqual(cx, lvalue, rval, result);
-}
-
-// ES6 draft rev32 7.2.12 Abstract Equality Comparison
-bool
-js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* result)
-{
- // Step 3.
- if (SameType(lval, rval))
- return EqualGivenSameType(cx, lval, rval, result);
-
- // Handle int32 x double.
- if (lval.isNumber() && rval.isNumber()) {
- *result = (lval.toNumber() == rval.toNumber());
- return true;
- }
-
- // Step 4. This a bit more complex, because of the undefined emulating object.
- if (lval.isNullOrUndefined()) {
- // We can return early here, because null | undefined is only equal to the same set.
- *result = rval.isNullOrUndefined() ||
- (rval.isObject() && EmulatesUndefined(&rval.toObject()));
- return true;
- }
-
- // Step 5.
- if (rval.isNullOrUndefined()) {
- MOZ_ASSERT(!lval.isNullOrUndefined());
- *result = lval.isObject() && EmulatesUndefined(&lval.toObject());
- return true;
- }
-
- // Step 6.
- if (lval.isNumber() && rval.isString()) {
- double num;
- if (!StringToNumber(cx, rval.toString(), &num))
- return false;
- *result = (lval.toNumber() == num);
- return true;
- }
-
- // Step 7.
- if (lval.isString() && rval.isNumber()) {
- double num;
- if (!StringToNumber(cx, lval.toString(), &num))
- return false;
- *result = (num == rval.toNumber());
- return true;
- }
-
- // Step 8.
- if (lval.isBoolean())
- return LooselyEqualBooleanAndOther(cx, lval, rval, result);
-
- // Step 9.
- if (rval.isBoolean())
- return LooselyEqualBooleanAndOther(cx, rval, lval, result);
-
- // Step 10.
- if ((lval.isString() || lval.isNumber() || lval.isSymbol()) && rval.isObject()) {
- RootedValue rvalue(cx, rval);
- if (!ToPrimitive(cx, &rvalue))
- return false;
- return LooselyEqual(cx, lval, rvalue, result);
- }
-
- // Step 11.
- if (lval.isObject() && (rval.isString() || rval.isNumber() || rval.isSymbol())) {
- RootedValue lvalue(cx, lval);
- if (!ToPrimitive(cx, &lvalue))
- return false;
- return LooselyEqual(cx, lvalue, rval, result);
- }
-
- // Step 12.
- *result = false;
- return true;
-}
-
-bool
-js::StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal)
-{
- if (SameType(lval, rval))
- return EqualGivenSameType(cx, lval, rval, equal);
-
- if (lval.isNumber() && rval.isNumber()) {
- *equal = (lval.toNumber() == rval.toNumber());
- return true;
- }
-
- *equal = false;
- return true;
-}
-
-static inline bool
-IsNegativeZero(const Value& v)
-{
- return v.isDouble() && mozilla::IsNegativeZero(v.toDouble());
-}
-
-static inline bool
-IsNaN(const Value& v)
-{
- return v.isDouble() && mozilla::IsNaN(v.toDouble());
-}
-
-bool
-js::SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same)
-{
- if (IsNegativeZero(v1)) {
- *same = IsNegativeZero(v2);
- return true;
- }
- if (IsNegativeZero(v2)) {
- *same = false;
- return true;
- }
- if (IsNaN(v1) && IsNaN(v2)) {
- *same = true;
- return true;
- }
- return StrictlyEqual(cx, v1, v2, same);
-}
-
JSType
js::TypeOfObject(JSObject* obj)
{
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
index df9368e71c..1927e8cc7f 100644
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -305,16 +305,6 @@ class InvokeState final : public RunState
extern bool
RunScript(JSContext* cx, RunState& state);
-extern bool
-StrictlyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal);
-
-extern bool
-LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* equal);
-
-/* === except that NaN is the same as NaN and -0 is not the same as +0. */
-extern bool
-SameValue(JSContext* cx, HandleValue v1, HandleValue v2, bool* same);
-
extern JSType
TypeOfObject(JSObject* obj);
diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp
index 91b7cacb4d..a6bb9826ee 100644
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -11,6 +11,7 @@
#include "gc/Marking.h"
#include "js/Value.h"
#include "vm/Debugger.h"
+#include "vm/EqualityOperations.h" // js::SameValue
#include "vm/TypedArrayCommon.h"
#include "jsobjinlines.h"