Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Temporal ToIntegerIfIntegral, ToIntegerWithTruncation, and ToPositiveIntegerWithTruncation implementation #4081

Merged
merged 5 commits into from
Dec 23, 2024

Conversation

nekevss
Copy link
Member

@nekevss nekevss commented Dec 11, 2024

Posting this as a draft to hopefully get comments.

TLDR: I'm convinced the way we currently handle converting JsValue to integers in the Temporal implementation is wrong and this is a way to update it.

Baseline issue

Currently, we are using to_integer_with_truncation, to_positive_integer_with_truncation and to_integer_if_integral to convert values into i32. These methods are still hold over from the initial prototype of Temporal prior to temporal_rs being split off. At the time, I think it was ultimately an optimistic approach, but I'm now fairly certain it's wrong long term and should probably addressed (hence the draft PR) ASAP.

Also, every argument being truncated to i32 negatively impacts temporal_rs's API by forcing us to keep the values at i32. This change would allow us to change temporal_rs's API, and move it away from only i32.

General approach / Order of Operations

So the general approach of this PR is to first get the value and transform it to a FiniteF64 by calling ToNumber() and checking that it's finite, which was essentially all the previous functions were doing anyways besides the truncation.

Once the values are made FiniteF64s, we then get the options object and determine the ArithmeticOverflow from the options object. This is needed for the observability tests which may check for the finite check throwing prior to a wrong options object throwing.

After the options object, we then truncate the value into the integer type that is expected by temporal_rs with the user defined ArithmeticOverflow. If ArithmeticOverflow::Reject and the value is not contained within T::MIN and T::MAX, we throw an early far exceeds range RangeError. If ArithemeticOverflow::Constrain, then we clamp the value to T::MAX or T::MIN.

There is also an argument to just always silently constrain the value to T::MAX or T::MIN and let temporal_rs handle the error. I think I may like this option more, because it does allow us to truncate early without ArithmeticOverflow and deferring truncation to after reading options.

Anyways, general thoughts would be appreciated. Technically, this is probably more of a temporal_rs leaning PR as I think the current truncate function in this PR should live in temporal_rs, but it does handle specifically the Boa's glue code to the library.

Copy link

github-actions bot commented Dec 11, 2024

Test262 conformance changes

Test result main count PR count difference
Total 48,625 48,625 0
Passed 43,616 43,711 +95
Ignored 1,471 1,471 0
Failed 3,538 3,443 -95
Panics 0 0 0
Conformance 89.70% 89.89% +0.20%
Fixed tests (111):
test/intl402/Temporal/PlainDate/from/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDate/compare/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainYearMonth/prototype/subtract/options-undefined.js (previously Failed)
test/intl402/Temporal/PlainYearMonth/prototype/add/options-undefined.js (previously Failed)
test/intl402/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js (previously Failed)
test/intl402/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/calendar-iso-string.js (previously Failed)
test/built-ins/Temporal/PlainDate/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/compare/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/equals/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/with/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units-basic.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/until/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units-basic.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainDate/prototype/since/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/with/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-2.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/subtract/precision-exact-mathematical-values-1.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-precision-exact-numerical-values.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-2.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/add/precision-exact-mathematical-values-1.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainTime/prototype/add/argument-duration-precision-exact-numerical-values.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Instant/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainMonthDay/calendar-iso-string.js (previously Failed)
test/built-ins/Temporal/PlainMonthDay/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/from/order-of-operations.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/round/largestunit-correct-rebalancing.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/round/result-out-of-range.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/with/order-of-operations.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-1.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/result-out-of-range-2.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/result-out-of-range-1.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/basic.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/result-out-of-range-2.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js (previously Failed)
test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/ZonedDateTime/calendar-iso-string.js (previously Failed)
test/built-ins/Temporal/ZonedDateTime/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/ZonedDateTime/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/calendar-iso-string.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/calendar-invalid.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/calendar-iso-string.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/from/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/from/argument-string-minus-sign.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/compare/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/compare/argument-string-minus-sign.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/equals/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-minus-sign.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/with/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/subtract/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/subtract/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/until/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/until/argument-string-minus-sign.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/add/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/add/negative-infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/since/infinity-throws-rangeerror.js (previously Failed)
test/built-ins/Temporal/PlainDateTime/prototype/since/argument-string-minus-sign.js (previously Failed)
Broken tests (16):
test/built-ins/Temporal/PlainDate/from/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDate/compare/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/equals/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/until/calendar-id-match.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/until/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/since/calendar-id-match.js (previously Passed)
test/built-ins/Temporal/PlainDate/prototype/since/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainTime/from/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainTime/compare/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainTime/prototype/equals/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainTime/prototype/until/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainTime/prototype/since/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/Duration/out-of-range.js (previously Passed)
test/built-ins/Temporal/PlainYearMonth/from/argument-string-minus-sign.js (previously Passed)
test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-minus-sign.js (previously Passed)

@raskad
Copy link
Member

raskad commented Dec 15, 2024

I'm all for having the more specific / representative types in the temporal_rs API.

There is also an argument to just always silently constrain the value to T::MAX or T::MIN and let temporal_rs handle the error. I think I may like this option more, because it does allow us to truncate early without ArithmeticOverflow and deferring truncation to after reading options.

I'm not 100% updated on how the current APIs look, but I think instead of silently doing this, there should be helper APIs / types provided by temporal_rs that deal with this most of the way.
I would imagine a worflow like this:

  1. (boa) Get values.
  2. (temporal_rs helper) Transform values into FiniteF64.
  3. (boa) Get options.
  4. (temporal_rs helper) Transform FiniteF64 values together with options into the expected temporal_rs API types.
  5. (temporal_rs) "Real" temporal_rs API call with the fitting types.

Does that make sense?

@nekevss
Copy link
Member Author

nekevss commented Dec 15, 2024

Definitely agreed! 😄 That's why boa-dev/temporal#131 exists and this is still a draft. The helper functions were primarily to double check the approach was working how I thought it would be in Boa before committing to it.

Right now, I'm mostly waiting for reviews on the temporal_rs PR and then this can be updated with the change and switched off draft.

nekevss added a commit to boa-dev/temporal that referenced this pull request Dec 16, 2024
…ntegerIfIntegral methods to `FiniteF64` (#131)

This PR adds the `truncate` method to `FiniteF64`. The main goal of this
PR is to provide engines with a better way of truncating and clamping
JavaScript numbers. For reference, please see boa-dev/boa#4081.

EDIT: Also, add positive truncation method and to integer if integral
method.
@nekevss nekevss marked this pull request as ready for review December 16, 2024 04:03
@nekevss
Copy link
Member Author

nekevss commented Dec 16, 2024

After looking at the broken tests, one appears to be a regression (case insensitive calendar ids) with a PR submitted on temporal_rs, one is a false positive that will require a patch to remove MINUS support from ixdtf, and a third (duration/out-of-range) is most likely a false positive that will need to be tested and potentially updated in temporal_rs.

@nekevss nekevss changed the title Reference draft of Temporal FiniteF64 truncation implementation Update Temporal ToIntegerIfIntegral, ToIntegerWithTruncation, and ToPositiveIntegerWithTruncation implementation Dec 16, 2024
Copy link
Member

@raskad raskad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

core/engine/src/builtins/temporal/duration/mod.rs Outdated Show resolved Hide resolved
core/engine/src/builtins/temporal/duration/mod.rs Outdated Show resolved Hide resolved
@nekevss nekevss requested review from hansl and raskad December 17, 2024 00:01
@nekevss nekevss force-pushed the update-integer-conversion branch from 0e743b0 to 156ad7c Compare December 19, 2024 05:09
Copy link
Member

@jedel1043 jedel1043 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, the new APIs do look a lot nicer! Nice cleanup!

@jedel1043 jedel1043 added this pull request to the merge queue Dec 23, 2024
Merged via the queue into main with commit 1ca6b81 Dec 23, 2024
13 checks passed
@jedel1043 jedel1043 deleted the update-integer-conversion branch December 23, 2024 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants