From 9c85a16aebcb60e620288484d02f813545b3dd63 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 28 Apr 2025 19:19:59 -0400 Subject: [PATCH] LibJS: Standardize spec prose for Math.sumPrecise --- Libraries/LibJS/Runtime/MathObject.cpp | 128 +++++++++++++------------ 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/Libraries/LibJS/Runtime/MathObject.cpp b/Libraries/LibJS/Runtime/MathObject.cpp index 31aa683821b..55d4eb66d89 100644 --- a/Libraries/LibJS/Runtime/MathObject.cpp +++ b/Libraries/LibJS/Runtime/MathObject.cpp @@ -1025,25 +1025,15 @@ static TwoSumResult two_sum(double x, double y) return { hi, lo }; } -// https://tc39.es/proposal-math-sum/#sec-math.sumprecise -ThrowCompletionOr MathObject::sum_precise_impl(VM& vm, Value iterable) +// 2 Math.sumPrecise ( items ), https://tc39.es/proposal-math-sum/#sec-math.sumprecise +JS_DEFINE_NATIVE_FUNCTION(MathObject::sumPrecise) { - constexpr double MAX_DOUBLE = 1.79769313486231570815e+308; // std::numeric_limits::max() - constexpr double PENULTIMATE_DOUBLE = 1.79769313486231550856e+308; // std::nextafter(DBL_MAX, 0) - constexpr double MAX_ULP = MAX_DOUBLE - PENULTIMATE_DOUBLE; - constexpr double POW_2_1023 = 8.98846567431158e+307; // 2^1023 + static constexpr double MAX_DOUBLE = 1.79769313486231570815e+308; // std::numeric_limits::max() + static constexpr double PENULTIMATE_DOUBLE = 1.79769313486231550856e+308; // std::nextafter(DBL_MAX, 0) + static constexpr double MAX_ULP = MAX_DOUBLE - PENULTIMATE_DOUBLE; + static constexpr double POW_2_1023 = 8.98846567431158e+307; // 2^1023 - // 1. Perform ? RequireObjectCoercible(items). - TRY(require_object_coercible(vm, iterable)); - - // 2. Let iteratorRecord be ? GetIterator(items, sync). - auto using_iterator = TRY(iterable.get_method(vm, vm.well_known_symbol_iterator())); - if (!using_iterator) - return vm.throw_completion(ErrorType::NotIterable, iterable.to_string_without_side_effects()); - - auto iterator = TRY(get_iterator_from_method(vm, iterable, *using_iterator)); - - enum State { + enum class State { MinusZero, PlusInfinity, MinusInfinity, @@ -1051,7 +1041,15 @@ ThrowCompletionOr MathObject::sum_precise_impl(VM& vm, Value iterable) Finite }; - // 3. Let state be minus-zero. + auto items = vm.argument(0); + + // 1. Perform ? RequireObjectCoercible(items). + TRY(require_object_coercible(vm, items)); + + // 2. Let iteratorRecord be ? GetIterator(items, SYNC). + auto iterator_record = TRY(get_iterator(vm, items, IteratorHint::Sync)); + + // 3. Let state be MINUS-ZERO. State state = State::MinusZero; // 4. Let sum be 0. @@ -1060,56 +1058,66 @@ ThrowCompletionOr MathObject::sum_precise_impl(VM& vm, Value iterable) u64 count = 0; Vector partials; - // 6. Let next be not-started. - // 7. Repeat, while next is not done + // 6. Let next be NOT-STARTED. + // 7. Repeat, while next is not DONE for (;;) { // a. Set next to ? IteratorStepValue(iteratorRecord). - auto next = TRY(iterator_step_value(vm, iterator)); - + auto next = TRY(iterator_step_value(vm, iterator_record)); if (!next.has_value()) break; - // If next is not done, then - // i. Set count to count + 1. - count++; - // ii. If count ≥ 2**53, then - // 1. Let error be ThrowCompletion(a newly created RangeError object). - // 2. Return ? IteratorClose(iteratorRecord, error). - if (count >= (1ULL << 53)) - return iterator_close(vm, iterator, vm.throw_completion(ErrorType::ArrayMaxSize)); + auto next_value = next.value(); - // iii. NOTE: The above case is not expected to be reached in practice and is included only so that implementations may rely on inputs being - // "reasonably sized" without violating this specification. + // b. If next is not DONE, then + // i. Set count to count + 1. + ++count; + + // ii. If count ≥ 2**53, then + if (count >= (1ULL << 53)) { + // 1. Let error be ThrowCompletion(a newly created RangeError object). + auto error = vm.throw_completion(ErrorType::ArrayMaxSize); + + // 2. Return ? IteratorClose(iteratorRecord, error). + return iterator_close(vm, iterator_record, error); + } + + // iii. NOTE: The above case is not expected to be reached in practice and is included only so that implementations + // may rely on inputs being "reasonably sized" without violating this specification. // iv. If next is not a Number, then - auto value = next.value(); - if (!value.is_number()) + if (!next_value.is_number()) { // 1. Let error be ThrowCompletion(a newly created TypeError object). + auto error = vm.throw_completion(ErrorType::IsNotA, next_value, "number"); + // 2. Return ? IteratorClose(iteratorRecord, error). - return iterator_close(vm, iterator, vm.throw_completion(ErrorType::IsNotA, value.to_string_without_side_effects(), "number")); + return iterator_close(vm, iterator_record, error); + } // v. Let n be next. - auto n = value.as_double(); + auto n = next_value.as_double(); - // vi. If state is not not-a-number, then + // vi. If state is not NOT-A-NUMBER, then if (state != State::NotANumber) { // 1. If n is NaN, then - if (isnan(n)) { - // a. Set state to not-a-number. + if (next_value.is_nan()) { + // a. Set state to NOT-A-NUMBER. state = State::NotANumber; - } // 2. Else if n is +∞𝔽, then - else if (Value(n).is_positive_infinity()) { - // a. If state is minus-infinity, set state to not-a-number. - // b. Else, set state to plus-infinity. - state = (state == State::MinusInfinity) ? State::NotANumber : State::PlusInfinity; - } // 3. Else if n is -∞𝔽, then - else if (Value(n).is_negative_infinity()) { - // a. If state is plus-infinity, set state to not-a-number. - // b. Else, set state to minus-infinity. - state = (state == State::PlusInfinity) ? State::NotANumber : State::MinusInfinity; - } // 4. Else if n is not -0𝔽 and state is either minus-zero or finite, then - else if (!Value(n).is_negative_zero() && (state == State::MinusZero || state == State::Finite)) { - // a. Set state to finite. + } + // 2. Else if n is +∞𝔽, then + else if (next_value.is_positive_infinity()) { + // a. If state is MINUS-INFINITY, set state to NOT-A-NUMBER. + // b. Else, set state to PLUS-INFINITY. + state = state == State::MinusInfinity ? State::NotANumber : State::PlusInfinity; + } + // 3. Else if n is -∞𝔽, then + else if (next_value.is_negative_infinity()) { + // a. If state is PLUS-INFINITY, set state to NOT-A-NUMBER. + // b. Else, set state to MINUS-INFINITY. + state = state == State::PlusInfinity ? State::NotANumber : State::MinusInfinity; + } + // 4. Else if n is not -0𝔽 and state is either MINUS-ZERO or FINITE, then + else if (!next_value.is_negative_zero() && (state == State::MinusZero || state == State::Finite)) { + // a. Set state to FINITE. state = State::Finite; // b. Set sum to sum + ℝ(n). @@ -1159,19 +1167,19 @@ ThrowCompletionOr MathObject::sum_precise_impl(VM& vm, Value iterable) } } - // 8. If state is not-a-number, return NaN. + // 8. If state is NOT-A-NUMBER, return NaN. if (state == State::NotANumber) return js_nan(); - // 9. If state is plus-infinity, return +∞𝔽. + // 9. If state is PLUS-INFINITY, return +∞𝔽. if (state == State::PlusInfinity) return js_infinity(); - // 10. If state is minus-infinity, return -∞𝔽. + // 10. If state is MINUS-INFINITY, return -∞𝔽. if (state == State::MinusInfinity) return js_negative_infinity(); - // 11. If state is minus-zero, return -0𝔽. + // 11. If state is MINUS-ZERO, return -0𝔽. if (state == State::MinusZero) return Value(-0.0); @@ -1241,13 +1249,7 @@ ThrowCompletionOr MathObject::sum_precise_impl(VM& vm, Value iterable) } } - return Value(hi); -} - -// https://tc39.es/proposal-math-sum/#sec-math.sumprecise -JS_DEFINE_NATIVE_FUNCTION(MathObject::sumPrecise) -{ - return sum_precise_impl(vm, vm.argument(0)); + return hi; } }