diff options
-rw-r--r-- | js/public/Equality.h | 55 | ||||
-rw-r--r-- | js/src/builtin/MapObject.cpp | 1 | ||||
-rw-r--r-- | js/src/builtin/Object.cpp | 1 | ||||
-rw-r--r-- | js/src/jit/VMFunctions.cpp | 1 | ||||
-rw-r--r-- | js/src/jsapi-tests/testLooselyEqual.cpp | 10 | ||||
-rw-r--r-- | js/src/jsapi-tests/testSameValue.cpp | 5 | ||||
-rw-r--r-- | js/src/jsapi-tests/tests.h | 7 | ||||
-rw-r--r-- | js/src/jsapi.cpp | 30 | ||||
-rw-r--r-- | js/src/jsapi.h | 9 | ||||
-rw-r--r-- | js/src/moz.build | 2 | ||||
-rw-r--r-- | js/src/proxy/ScriptedProxyHandler.cpp | 2 | ||||
-rw-r--r-- | js/src/shell/js.cpp | 3 | ||||
-rw-r--r-- | js/src/vm/EqualityOperations.cpp | 215 | ||||
-rw-r--r-- | js/src/vm/EqualityOperations.h | 44 | ||||
-rw-r--r-- | js/src/vm/Interpreter.cpp | 165 | ||||
-rw-r--r-- | js/src/vm/Interpreter.h | 10 | ||||
-rw-r--r-- | js/src/vm/NativeObject.cpp | 1 |
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" |