Issue #1240 - Part 5g - Implement BigInt comparison operators. Implement BigInt support for equality operators. https://bugzilla.mozilla.org/show_bug.cgi?id=1486173 Part 3. Implement BigInt support for relational comparison operators. https://bugzilla.mozilla.org/show_bug.cgi?id=1492669

This commit is contained in:
Brian Smith 2023-07-19 17:10:33 -05:00 committed by roytam1
commit 1355043d40
2 changed files with 163 additions and 34 deletions

View file

@ -31,6 +31,10 @@ EqualGivenSameType(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bo
*equal = (lval.toDouble() == rval.toDouble());
return true;
}
if (lval.isBigInt()) {
*equal = JS::BigInt::equal(lval.toBigInt(), rval.toBigInt());
return true;
}
if (lval.isGCThing()) { // objects or symbols
*equal = (lval.toGCThing() == rval.toGCThing());
return true;
@ -134,6 +138,22 @@ js::LooselyEqual(JSContext* cx, JS::HandleValue lval, JS::HandleValue rval, bool
return js::LooselyEqual(cx, lvalue, rval, result);
}
if (lval.isBigInt()) {
RootedBigInt lbi(cx, lval.toBigInt());
bool tmpResult;
JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult, BigInt::looselyEqual(cx, lbi, rval));
*result = tmpResult;
return true;
}
if (rval.isBigInt()) {
RootedBigInt rbi(cx, rval.toBigInt());
bool tmpResult;
JS_TRY_VAR_OR_RETURN_FALSE(cx, tmpResult, BigInt::looselyEqual(cx, rbi, lval));
*result = tmpResult;
return true;
}
// Step 12.
*result = false;
return true;

View file

@ -8,6 +8,8 @@
#include "vm/Interpreter.h"
#include "mozilla/Maybe.h"
#include "jscompartment.h"
#include "jsnum.h"
#include "jsstr.h"
@ -660,51 +662,158 @@ ProcessCallSiteObjOperation(JSContext* cx, RootedObject& cso, RootedObject& raw,
return true;
}
#define RELATIONAL_OP(OP) \
JS_BEGIN_MACRO \
/* Optimize for two int-tagged operands (typical loop control). */ \
if (lhs.isInt32() && rhs.isInt32()) { \
*res = lhs.toInt32() OP rhs.toInt32(); \
} else { \
if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) \
return false; \
if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) \
return false; \
if (lhs.isString() && rhs.isString()) { \
JSString* l = lhs.toString(); \
JSString* r = rhs.toString(); \
int32_t result; \
if (!CompareStrings(cx, l, r, &result)) \
return false; \
*res = result OP 0; \
} else { \
double l, r; \
if (!ToNumber(cx, lhs, &l) || !ToNumber(cx, rhs, &r)) \
return false; \
*res = (l OP r); \
} \
} \
return true; \
JS_END_MACRO
// BigInt proposal 3.2.4 Abstract Relational Comparison
// Returns Nothing when at least one operand is a NaN, or when
// ToNumeric or StringToBigInt can't interpret a string as a numeric
// value. (These cases correspond to a NaN result in the spec.)
// Otherwise, return a boolean to indicate whether lhs is less than
// rhs. The operands must be primitives; the caller is responsible for
// evaluating them in the correct order.
static MOZ_ALWAYS_INLINE bool
LessThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
RELATIONAL_OP(<);
LessThanImpl(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
mozilla::Maybe<bool>& res)
{
// Steps 1 and 2 are performed by the caller.
// Step 3.
if (lhs.isString() && rhs.isString()) {
JSString* l = lhs.toString();
JSString* r = rhs.toString();
int32_t result;
if (!CompareStrings(cx, l, r, &result)) {
return false;
}
res = mozilla::Some(result < 0);
return true;
}
// Step 4a.
if (lhs.isBigInt() && rhs.isString()) {
return BigInt::lessThan(cx, lhs, rhs, res);
}
// Step 4b.
if (lhs.isString() && rhs.isBigInt()) {
return BigInt::lessThan(cx, lhs, rhs, res);
}
// Steps 4c and 4d.
if (!ToNumeric(cx, lhs) || !ToNumeric(cx, rhs)) {
return false;
}
// Steps 4e-j.
if (lhs.isBigInt() || rhs.isBigInt()) {
return BigInt::lessThan(cx, lhs, rhs, res);
}
// Step 4e for Number operands.
MOZ_ASSERT(lhs.isNumber() && rhs.isNumber());
double lhsNum = lhs.toNumber();
double rhsNum = rhs.toNumber();
if (mozilla::IsNaN(lhsNum) || mozilla::IsNaN(rhsNum)) {
res = mozilla::Maybe<bool>(mozilla::Nothing());
return true;
}
res = mozilla::Some(lhsNum < rhsNum);
return true;
}
static MOZ_ALWAYS_INLINE bool
LessThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
RELATIONAL_OP(<=);
LessThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
{
if (lhs.isInt32() && rhs.isInt32()) {
*res = lhs.toInt32() < rhs.toInt32();
return true;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) {
return false;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) {
return false;
}
mozilla::Maybe<bool> tmpResult;
if (!LessThanImpl(cx, lhs, rhs, tmpResult)) {
return false;
}
*res = tmpResult.valueOr(false);
return true;
}
static MOZ_ALWAYS_INLINE bool
GreaterThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
RELATIONAL_OP(>);
LessThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
{
if (lhs.isInt32() && rhs.isInt32()) {
*res = lhs.toInt32() <= rhs.toInt32();
return true;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) {
return false;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) {
return false;
}
mozilla::Maybe<bool> tmpResult;
if (!LessThanImpl(cx, rhs, lhs, tmpResult)) {
return false;
}
*res = !tmpResult.valueOr(true);
return true;
}
static MOZ_ALWAYS_INLINE bool
GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res) {
RELATIONAL_OP(>=);
GreaterThanOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
{
if (lhs.isInt32() && rhs.isInt32()) {
*res = lhs.toInt32() > rhs.toInt32();
return true;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) {
return false;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) {
return false;
}
mozilla::Maybe<bool> tmpResult;
if (!LessThanImpl(cx, rhs, lhs, tmpResult)) {
return false;
}
*res = tmpResult.valueOr(false);
return true;
}
static MOZ_ALWAYS_INLINE bool
GreaterThanOrEqualOperation(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
{
if (lhs.isInt32() && rhs.isInt32()) {
*res = lhs.toInt32() >= rhs.toInt32();
return true;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, lhs)) {
return false;
}
if (!ToPrimitive(cx, JSTYPE_NUMBER, rhs)) {
return false;
}
mozilla::Maybe<bool> tmpResult;
if (!LessThanImpl(cx, lhs, rhs, tmpResult)) {
return false;
}
*res = !tmpResult.valueOr(true);
return true;
}
static MOZ_ALWAYS_INLINE bool