mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 01:08:39 +09:00
Issue #2241 - Part 1: Move {js::,JS_}{{Strictly,Loosely}Equal,SameValue} into their own header and implementation files
Backported from Mozilla bug 1516742. The .from* methods are going to depend on SameValueZero, which needs to be visible to /dom. This patch provides the foundation for that.
This commit is contained in:
parent
1ae0d6ff7f
commit
ec2fb72568
17 changed files with 338 additions and 223 deletions
55
js/public/Equality.h
Normal file
55
js/public/Equality.h
Normal file
|
|
@ -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 */
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -391,36 +391,6 @@ JS_TypeOfValue(JSContext* cx, HandleValue value)
|
|||
return TypeOfValue(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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "vm/EqualityOperations.h" // js::SameValue
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
215
js/src/vm/EqualityOperations.cpp
Normal file
215
js/src/vm/EqualityOperations.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
44
js/src/vm/EqualityOperations.h
Normal file
44
js/src/vm/EqualityOperations.h
Normal file
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue