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

Fast-path conversions between Temporal types #1484

Merged
merged 19 commits into from
Apr 16, 2021
Merged

Fast-path conversions between Temporal types #1484

merged 19 commits into from
Apr 16, 2021

Conversation

ptomato
Copy link
Collaborator

@ptomato ptomato commented Apr 13, 2021

Not all the changes have test262 tests yet, but writing them was taking so long that I decided to go ahead and put this up for review anyway. I'll add the undone ones to #1398.

Closes: #1428

This was duplicated in the spec text.
This was clearly meant to be ToTemporalDate, not ToTemporalTime.
@codecov
Copy link

codecov bot commented Apr 13, 2021

Codecov Report

Merging #1484 (5defed6) into main (2b7197f) will decrease coverage by 2.93%.
The diff coverage is 80.37%.

❗ Current head 5defed6 differs from pull request most recent head 12dab2e. Consider uploading reports for the commit 12dab2e to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1484      +/-   ##
==========================================
- Coverage   95.65%   92.71%   -2.94%     
==========================================
  Files          19       17       -2     
  Lines       10897    11181     +284     
  Branches     1730     1643      -87     
==========================================
- Hits        10424    10367      -57     
- Misses        469      801     +332     
- Partials        4       13       +9     
Flag Coverage Δ
test262 ?
tests 91.55% <79.06%> (-0.40%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
polyfill/lib/ecmascript.mjs 94.81% <71.42%> (-1.27%) ⬇️
polyfill/lib/plainmonthday.mjs 83.83% <76.08%> (-6.92%) ⬇️
polyfill/lib/plainyearmonth.mjs 89.65% <76.59%> (-5.70%) ⬇️
polyfill/lib/plaindate.mjs 89.21% <79.62%> (-6.73%) ⬇️
polyfill/lib/zoneddatetime.mjs 91.76% <83.67%> (-6.11%) ⬇️
polyfill/lib/plaintime.mjs 92.16% <84.21%> (-4.38%) ⬇️
polyfill/lib/plaindatetime.mjs 90.87% <90.59%> (-3.92%) ⬇️
polyfill/lib/calendar.mjs 92.74% <100.00%> (-1.71%) ⬇️
polyfill/lib/instant.mjs 87.94% <100.00%> (-6.85%) ⬇️
polyfill/lib/now.mjs 100.00% <100.00%> (ø)
... and 16 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2b7197f...12dab2e. Read the comment docs.

spec/calendar.html Outdated Show resolved Hide resolved
spec/calendar.html Outdated Show resolved Hide resolved
spec/plaintime.html Show resolved Hide resolved
Copy link
Collaborator

@cjtenny cjtenny left a comment

Choose a reason for hiding this comment

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

Part 1: Reviewed everything but the tests. Wanted to offer the feedback sooner since I doubt I'll have changes to suggest for the tests :)

polyfill/lib/calendar.mjs Outdated Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/intl.html Show resolved Hide resolved
spec/timezone.html Outdated Show resolved Hide resolved
spec/zoneddatetime.html Outdated Show resolved Hide resolved
spec/intl.html Outdated Show resolved Hide resolved
spec/plaintime.html Show resolved Hide resolved
polyfill/lib/ecmascript.mjs Show resolved Hide resolved
polyfill/lib/ecmascript.mjs Show resolved Hide resolved
If passing a Temporal object with a [[Calendar]] internal slot where a
calendar is expected, then read that slot instead of doing a Get on the
"calendar" property.
(The Get operation was also not consistently done throughout the existing
spec text, so fix that as well. This means the CalendarFrom operation
becomes redundant, so remove it.)

See: #1428
Left separate for ease of reviewing.
If passing a Temporal.ZonedDateTime where a Temporal.Instant is expected,
then create a Temporal.Instant directly from the ZonedDateTime's
[[Nanoseconds]] internal slot.
Previously, the ZonedDateTime's toString() method would have been called,
and the result would have been parsed into an Instant.

See: #1428
Left separate for ease of reviewing.
If passing a Temporal.PlainDateTime where a Temporal.PlainDate is
expected, then create a Temporal.PlainDate directly from the
PlainDateTime's [[ISOYear]], [[ISOMonth]], and [[ISODay]] internal slots.
Previously, the PlainDateTime's calendar, year, month, monthCode, and day
getters would have been called.

See: #1428
If passing a Temporal.PlainDate where a Temporal.PlainDateTime is
expected, then create a Temporal.PlainDateTime directly from the
PlainDate's [[ISOYear]], [[ISOMonth]], and [[ISODay]] internal slots, and
assume midnight as the time, just as if a property bag with year,
month/monthCode, and day properties had been passed.
Previously, the PlainDate's calendar, year, month, monthCode, and day
getters would have been called, and the time properties would have been
accessed as well.

See: #1428
Left separate for ease of reviewing.
If passing a Temporal.PlainDateTime where a Temporal.PlainTime is
expected, then create a Temporal.PlainTime directly from the
PlainDateTime's [[ISOHour]], [[ISOMinute]], [[ISOSecond]],
[[ISOMillisecond]], [[ISOMicrosecond]], and [[ISONanosecond]] internal
slots.
Previously, the PlainDateTime's hour, minute, second, millisecond,
microsecond, nanosecond, and calendar getters would have been called.

See: #1428
@ptomato ptomato force-pushed the 1428-fast-paths branch 3 times, most recently from c90387b to 71ac595 Compare April 14, 2021 00:43
@ptomato
Copy link
Collaborator Author

ptomato commented Apr 14, 2021

OK, thanks for the reviews! Only the last two commits are substantially different from the previous version.

@ptomato ptomato requested a review from cjtenny April 14, 2021 00:44
Copy link
Collaborator

@cjtenny cjtenny left a comment

Choose a reason for hiding this comment

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

Really clever (not in a bad way - it's very clean and clear, just took a few minutes to wrap my brain around what would get called in what abstract op), well done!

I think fixes are needed to era, eraYear in polyfill, and era, eraYear, and month in Intl spec text. The tests look great, awesome work with the helper. Also optionally can remove redundant [[InitializedTemporalDate]] slot checks in the spec when the alternative is calling ToTemporalDate since ToTemporalDate shortcuts that path anyway, which is why the polyfill doesn't do that check.

Approved once those changes are in!

polyfill/lib/calendar.mjs Outdated Show resolved Hide resolved
polyfill/lib/calendar.mjs Outdated Show resolved Hide resolved
spec/plaintime.html Show resolved Hide resolved
spec/intl.html Outdated Show resolved Hide resolved
spec/intl.html Outdated Show resolved Hide resolved
spec/intl.html Show resolved Hide resolved
spec/intl.html Show resolved Hide resolved
If passing a Temporal.ZonedDateTime (that is, a Temporal object with a
[[TimeZone]] internal slot) where a Temporal.TimeZone is expected, then
read that slot instead of doing a Get on the "timeZone" property.

The Get operation was also not consistently done throughout the existing
spec text, so fix that as well. This means the TimeZoneFrom operation
becomes redundant, so remove it. Additionally, the check for the
"timeZone" property on plain objects was not implemented according to the
consensus in
#925 (comment)
and this change fixes that as well.

See: #1428
The value of a relativeTo property in a property bag is already allowed to
be either a Temporal.PlainDateTime or Temporal.ZonedDateTime. If passing a
Temporal.PlainDate, then create a Temporal.PlainDateTime from its slots as
we do in ToTemporalDateTime.

See: #1428
There are a few places where the `calendar` property of a property bag is
read. If the property bag is actually a Temporal object with a
[[Calendar]] internal slot, then read this slot instead.

See: #1428
…Time

If passing a Temporal.ZonedDateTime where a Temporal.PlainDate,
Temporal.PlainTime, or Temporal.PlainDateTime is expected, then first call
the built-in TimeZone.getPlainDateTimeFor() method to obtain a
Temporal.PlainDateTime object and proceed accordingly.
Previously, the ZonedDateTime's getters would have been called,
potentially calling the getPlainDateTimeFor() method multiple times; very
inefficient.

See: #1428
spec/calendar.html Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/calendar.html Show resolved Hide resolved
spec/plaindate.html Outdated Show resolved Hide resolved
All of the with() methods check that the given object doesn't have a
`calendar` or `timeZone` property. Short-circuit these checks by first
checking if it's a Temporal object with a [[Calendar]] or [[TimeZone]]
internal slot, so that no Gets are performed in that case.

See: #1428
Now that we have a fast-path conversion from PlainDateTime to PlainDate,
it's no longer necessary to check for an [[ISOYear]] internal slot. Make
sure it is consistent whether a particular Calendar method can be called
with a Temporal.PlainYearMonth or Temporal.PlainMonthDay without
triggering a conversion, if appropriate.

The policy is that these Calendar methods can all be called with a
PlainDate, and additionally some of them can be called with a
PlainYearMonth and/or PlainMonthDay. Anything else gets passed through
ToTemporalDate, which includes the fast path for PlainDateTime.

See: #1428
In ParseTemporalTimeZoneString, a time zone name may meet the grammar for
time zone names while still not being a supported time zone name.
Therefore, this should be a throw rather than an assertion. This was
probably left over from when the grammar was defined more loosely as "any
string that is a supported time zone name".
The constructors of PlainDate, PlainDateTime, PlainMonthDay,
PlainYearMonth, and ZonedDateTime call ToTemporalCalendar on the calendar
argument, and the constructor of ZonedDateTime additionally calls
ToTemporalTimeZone on its timeZone argument. Now that these operations
include an observable HasProperty operation, we do not want to call them
more than once on the same calendar or time zone object.

Therefore we add CreateTemporalDate etc. which create an instance without
any observable validation of the arguments.

The spec text already works like this, with abstract operations such as
CreateTemporalZonedDateTime and friends that do not call
ToTemporalCalendar or ToTemporalTimeZone. So this is not a normative
change; it's a compliance bug in the polyfill. It is required for the
test262 tests in 19cd26f to pass, which observe HasProperty(`timeZone`)
operations.

See: #1428
@ptomato
Copy link
Collaborator Author

ptomato commented Apr 16, 2021

I believe that I've addressed the comments, and would like to get this landed before the plenary so I'm going to go ahead and merge it. Happy to address any further comments as they come in.

@ptomato ptomato merged commit 9b7b42a into main Apr 16, 2021
@ptomato ptomato deleted the 1428-fast-paths branch April 16, 2021 00:29
@FrankYFTang
Copy link
Contributor

@ptomato Could you help me to answer this question in https://chromium-review.googlesource.com/c/v8/v8/+/3531554/8/src/objects/js-temporal-objects.cc#6190
?

" why the spec rejects this but allows this kind of object for monthCode? Just strikes me as odd."

This is referring to the fact that Temporal.Calendar.prototype.monthCode will accept a PlainMonthDay object but Temporal.Calendar.prototype.month will throw TypeError exception

The throw is added by you in this PR
See spec text here
step 4 in https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.month
but no such step in
https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthCode

I also have problem to understand the reason behind it, it seems both ISOMonth and ISOMonthCode get the information from [[ISOMonth]] internal slot.

@ptomato
Copy link
Collaborator Author

ptomato commented May 3, 2022

@FrankYFTang I created an account on Gerrit and followed up there.

@justingrant
Copy link
Collaborator

Copying @ptomato's note from the other site to close the loop back here:

Hi, I can help explain this — the reason is because only the monthCode values remain constant from year to year. The value returned by the month() getter is the ordinal number of the month in a particular year, which may change from year to year in lunisolar calendars.

For example today's date in the Hebrew calendar is 2 Iyar, as a PlainMonthDay this is monthCode M08 and day 2. In this year, 5782, Iyar is the 9th month because this year has a leap month. Last year, Iyar was the 8th month. Without the year, we can't determine an ordinal value for the month() getter to return.

(This all doesn't matter for the ISO 8601 calendar which always has 12 months per year, but it would be bad if the month() getter would sometimes return a value and sometimes throw if your code was unexpectedly handed a PlainMonthDay object with a lunisolar calendar.)

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.

delegate review: why are "Temporal-like" objects supported at all?
5 participants