From f35fc58d04d0ed90ff0f41b173588ab4eb08e970 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 16 Jan 2023 15:31:06 -0800 Subject: [PATCH] Normative: Validate receiver fields before fields of any param objects Changes to the order of observable operations in - PlainDate.p.with - PlainDateTime.p.with - PlainMonthDay.p.with - PlainTime.p.with - PlainYearMonth.p.since - PlainYearMonth.p.until - PlainYearMonth.p.with - ZonedDateTime.p.with Swapping the PrepareTemporalFields calls in ZonedDateTime.p.with exposed another unnecessary user call: the 'timeZone' field was observably read from the receiver and from the object returned from mergeFields(), but never used. This is fixed as well. Closes: #2462 --- polyfill/lib/ecmascript.mjs | 6 +++--- polyfill/lib/plaindate.mjs | 5 ++--- polyfill/lib/plaindatetime.mjs | 2 +- polyfill/lib/plainmonthday.mjs | 4 ++-- polyfill/lib/plaintime.mjs | 4 ++-- polyfill/lib/plainyearmonth.mjs | 5 ++--- polyfill/lib/zoneddatetime.mjs | 4 ++-- spec/plaindate.html | 4 ++-- spec/plaindatetime.html | 4 ++-- spec/plainmonthday.html | 4 ++-- spec/plaintime.html | 2 +- spec/plainyearmonth.html | 10 +++++----- spec/zoneddatetime.html | 4 ++-- 13 files changed, 28 insertions(+), 30 deletions(-) diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 7048552235..606423b9ba 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -3797,12 +3797,12 @@ export const ES = ObjectAssign({}, ES2022, { const settings = ES.GetDifferenceSettings(operation, options, 'date', ['week', 'day'], 'month', 'year'); const fieldNames = ES.CalendarFields(calendar, ['monthCode', 'year']); - const otherFields = ES.PrepareTemporalFields(other, fieldNames, []); - otherFields.day = 1; - const otherDate = ES.CalendarDateFromFields(calendar, otherFields); const thisFields = ES.PrepareTemporalFields(yearMonth, fieldNames, []); thisFields.day = 1; const thisDate = ES.CalendarDateFromFields(calendar, thisFields); + const otherFields = ES.PrepareTemporalFields(other, fieldNames, []); + otherFields.day = 1; + const otherDate = ES.CalendarDateFromFields(calendar, otherFields); const untilOptions = ObjectCreate(null); ES.CopyDataProperties(untilOptions, options, []); diff --git a/polyfill/lib/plaindate.mjs b/polyfill/lib/plaindate.mjs index e76cc9636e..875a559600 100644 --- a/polyfill/lib/plaindate.mjs +++ b/polyfill/lib/plaindate.mjs @@ -95,16 +95,15 @@ export class PlainDate { throw new TypeError('invalid argument'); } ES.RejectObjectWithCalendarOrTimeZone(temporalDateLike); + options = ES.GetOptionsObject(options); const calendar = GetSlot(this, CALENDAR); const fieldNames = ES.CalendarFields(calendar, ['day', 'month', 'monthCode', 'year']); - const partialDate = ES.PrepareTemporalFields(temporalDateLike, fieldNames, 'partial'); let fields = ES.PrepareTemporalFields(this, fieldNames, []); + const partialDate = ES.PrepareTemporalFields(temporalDateLike, fieldNames, 'partial'); fields = ES.CalendarMergeFields(calendar, fields, partialDate); fields = ES.PrepareTemporalFields(fields, fieldNames, []); - options = ES.GetOptionsObject(options); - return ES.CalendarDateFromFields(calendar, fields, options); } withCalendar(calendar) { diff --git a/polyfill/lib/plaindatetime.mjs b/polyfill/lib/plaindatetime.mjs index d76e345235..25f146906e 100644 --- a/polyfill/lib/plaindatetime.mjs +++ b/polyfill/lib/plaindatetime.mjs @@ -166,8 +166,8 @@ export class PlainDateTime { 'second', 'year' ]); - const partialDateTime = ES.PrepareTemporalFields(temporalDateTimeLike, fieldNames, 'partial'); let fields = ES.PrepareTemporalFields(this, fieldNames, []); + const partialDateTime = ES.PrepareTemporalFields(temporalDateTimeLike, fieldNames, 'partial'); fields = ES.CalendarMergeFields(calendar, fields, partialDateTime); fields = ES.PrepareTemporalFields(fields, fieldNames, []); const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = diff --git a/polyfill/lib/plainmonthday.mjs b/polyfill/lib/plainmonthday.mjs index eac51011a7..045063c48c 100644 --- a/polyfill/lib/plainmonthday.mjs +++ b/polyfill/lib/plainmonthday.mjs @@ -37,15 +37,15 @@ export class PlainMonthDay { throw new TypeError('invalid argument'); } ES.RejectObjectWithCalendarOrTimeZone(temporalMonthDayLike); + options = ES.GetOptionsObject(options); const calendar = GetSlot(this, CALENDAR); const fieldNames = ES.CalendarFields(calendar, ['day', 'month', 'monthCode', 'year']); - const partialMonthDay = ES.PrepareTemporalFields(temporalMonthDayLike, fieldNames, 'partial'); let fields = ES.PrepareTemporalFields(this, fieldNames, []); + const partialMonthDay = ES.PrepareTemporalFields(temporalMonthDayLike, fieldNames, 'partial'); fields = ES.CalendarMergeFields(calendar, fields, partialMonthDay); fields = ES.PrepareTemporalFields(fields, fieldNames, []); - options = ES.GetOptionsObject(options); return ES.CalendarMonthDayFromFields(calendar, fields, options); } equals(other) { diff --git a/polyfill/lib/plaintime.mjs b/polyfill/lib/plaintime.mjs index 4dd284f845..23b547a014 100644 --- a/polyfill/lib/plaintime.mjs +++ b/polyfill/lib/plaintime.mjs @@ -117,11 +117,11 @@ export class PlainTime { throw new TypeError('invalid argument'); } ES.RejectObjectWithCalendarOrTimeZone(temporalTimeLike); - - const partialTime = ES.ToTemporalTimeRecord(temporalTimeLike, 'partial'); options = ES.GetOptionsObject(options); const overflow = ES.ToTemporalOverflow(options); + const partialTime = ES.ToTemporalTimeRecord(temporalTimeLike, 'partial'); + const fields = ES.ToTemporalTimeRecord(this); let { hour, minute, second, millisecond, microsecond, nanosecond } = ObjectAssign(fields, partialTime); ({ hour, minute, second, millisecond, microsecond, nanosecond } = ES.RegulateTime( diff --git a/polyfill/lib/plainyearmonth.mjs b/polyfill/lib/plainyearmonth.mjs index cefe3b2245..61481bc9e4 100644 --- a/polyfill/lib/plainyearmonth.mjs +++ b/polyfill/lib/plainyearmonth.mjs @@ -63,16 +63,15 @@ export class PlainYearMonth { throw new TypeError('invalid argument'); } ES.RejectObjectWithCalendarOrTimeZone(temporalYearMonthLike); + options = ES.GetOptionsObject(options); const calendar = GetSlot(this, CALENDAR); const fieldNames = ES.CalendarFields(calendar, ['month', 'monthCode', 'year']); - const partialYearMonth = ES.PrepareTemporalFields(temporalYearMonthLike, fieldNames, 'partial'); let fields = ES.PrepareTemporalFields(this, fieldNames, []); + const partialYearMonth = ES.PrepareTemporalFields(temporalYearMonthLike, fieldNames, 'partial'); fields = ES.CalendarMergeFields(calendar, fields, partialYearMonth); fields = ES.PrepareTemporalFields(fields, fieldNames, []); - options = ES.GetOptionsObject(options); - return ES.CalendarYearMonthFromFields(calendar, fields, options); } add(temporalDurationLike, options = undefined) { diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index e0f5bd5952..92170c6f35 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -180,6 +180,7 @@ export class ZonedDateTime { throw new TypeError('invalid zoned-date-time-like'); } ES.RejectObjectWithCalendarOrTimeZone(temporalZonedDateTimeLike); + options = ES.GetOptionsObject(options); const calendar = GetSlot(this, CALENDAR); const fieldNames = ES.CalendarFields(calendar, [ @@ -195,12 +196,11 @@ export class ZonedDateTime { 'year' ]); ES.Call(ArrayPrototypePush, fieldNames, ['offset']); - const partialZonedDateTime = ES.PrepareTemporalFields(temporalZonedDateTimeLike, fieldNames, 'partial'); let fields = ES.PrepareTemporalFields(this, fieldNames, ['offset']); + const partialZonedDateTime = ES.PrepareTemporalFields(temporalZonedDateTimeLike, fieldNames, 'partial'); fields = ES.CalendarMergeFields(calendar, fields, partialZonedDateTime); fields = ES.PrepareTemporalFields(fields, fieldNames, ['offset']); - options = ES.GetOptionsObject(options); const disambiguation = ES.ToTemporalDisambiguation(options); const offset = ES.ToTemporalOffset(options, 'prefer'); diff --git a/spec/plaindate.html b/spec/plaindate.html index 1991f5ee5b..e42bb308f9 100644 --- a/spec/plaindate.html +++ b/spec/plaindate.html @@ -391,11 +391,11 @@

Temporal.PlainDate.prototype.with ( _temporalDateLike_ [ , _options_ ] )

Temporal.PlainDateTime.prototype.with ( _temporalDateTimeLike_ [ , _options_ 1. If Type(_temporalDateTimeLike_) is not Object, then 1. Throw a *TypeError* exception. 1. Perform ? RejectObjectWithCalendarOrTimeZone(_temporalDateTimeLike_). + 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _calendar_ be _dateTime_.[[Calendar]]. 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). - 1. Let _partialDateTime_ be ? PrepareTemporalFields(_temporalDateTimeLike_, _fieldNames_, ~partial~). - 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _fields_ be ? PrepareTemporalFields(_dateTime_, _fieldNames_, «»). + 1. Let _partialDateTime_ be ? PrepareTemporalFields(_temporalDateTimeLike_, _fieldNames_, ~partial~). 1. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialDateTime_). 1. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»). 1. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). diff --git a/spec/plainmonthday.html b/spec/plainmonthday.html index 58388b4ca2..fcbb95cbd8 100644 --- a/spec/plainmonthday.html +++ b/spec/plainmonthday.html @@ -146,11 +146,11 @@

Temporal.PlainMonthDay.prototype.with ( _temporalMonthDayLike_ [ , _options_ 1. If Type(_temporalMonthDayLike_) is not Object, then 1. Throw a *TypeError* exception. 1. Perform ? RejectObjectWithCalendarOrTimeZone(_temporalMonthDayLike_). + 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _calendar_ be _monthDay_.[[Calendar]]. 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). - 1. Let _partialMonthDay_ be ? PrepareTemporalFields(_temporalMonthDayLike_, _fieldNames_, ~partial~). - 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _fields_ be ? PrepareTemporalFields(_monthDay_, _fieldNames_, «»). + 1. Let _partialMonthDay_ be ? PrepareTemporalFields(_temporalMonthDayLike_, _fieldNames_, ~partial~). 1. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialMonthDay_). 1. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»). 1. Return ? CalendarMonthDayFromFields(_calendar_, _fields_, _options_). diff --git a/spec/plaintime.html b/spec/plaintime.html index e9f60f2560..c38ccfdf53 100644 --- a/spec/plaintime.html +++ b/spec/plaintime.html @@ -233,9 +233,9 @@

Temporal.PlainTime.prototype.with ( _temporalTimeLike_ [ , _options_ ] )

Temporal.PlainYearMonth.prototype.with ( _temporalYearMonthLike_ [ , _option 1. If Type(_temporalYearMonthLike_) is not Object, then 1. Throw a *TypeError* exception. 1. Perform ? RejectObjectWithCalendarOrTimeZone(_temporalYearMonthLike_). + 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _calendar_ be _yearMonth_.[[Calendar]]. 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"month"*, *"monthCode"*, *"year"* »). - 1. Let _partialYearMonth_ be ? PrepareTemporalFields(_temporalYearMonthLike_, _fieldNames_, ~partial~). - 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»). + 1. Let _partialYearMonth_ be ? PrepareTemporalFields(_temporalYearMonthLike_, _fieldNames_, ~partial~). 1. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialYearMonth_). 1. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, «»). 1. Return ? CalendarYearMonthFromFields(_calendar_, _fields_, _options_). @@ -611,12 +611,12 @@

1. If ? CalendarEquals(_calendar_, _other_.[[Calendar]]) is *false*, throw a *RangeError* exception. 1. Let _settings_ be ? GetDifferenceSettings(_operation_, _options_, ~date~, « *"week"*, *"day"* », *"month"*, *"year"*). 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"monthCode"*, *"year"* »). - 1. Let _otherFields_ be ? PrepareTemporalFields(_other_, _fieldNames_, «»). - 1. Perform ! CreateDataPropertyOrThrow(_otherFields_, *"day"*, *1*𝔽). - 1. Let _otherDate_ be ? CalendarDateFromFields(_calendar_, _otherFields_). 1. Let _thisFields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»). 1. Perform ! CreateDataPropertyOrThrow(_thisFields_, *"day"*, *1*𝔽). 1. Let _thisDate_ be ? CalendarDateFromFields(_calendar_, _thisFields_). + 1. Let _otherFields_ be ? PrepareTemporalFields(_other_, _fieldNames_, «»). + 1. Perform ! CreateDataPropertyOrThrow(_otherFields_, *"day"*, *1*𝔽). + 1. Let _otherDate_ be ? CalendarDateFromFields(_calendar_, _otherFields_). 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ? CopyDataProperties(_untilOptions_, _settings_.[[Options]], « »). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, _settings_.[[LargestUnit]]). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index e77a561614..2996294e15 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -581,14 +581,14 @@

Temporal.ZonedDateTime.prototype.with ( _temporalZonedDateTimeLike_ [ , _opt 1. If Type(_temporalZonedDateTimeLike_) is not Object, then 1. Throw a *TypeError* exception. 1. Perform ? RejectObjectWithCalendarOrTimeZone(_temporalZonedDateTimeLike_). + 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _calendar_ be _zonedDateTime_.[[Calendar]]. 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"month"*, *"monthCode"*, *"nanosecond"*, *"second"*, *"year"* »). 1. Append *"offset"* to _fieldNames_. - 1. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~). 1. Let _fields_ be ? PrepareTemporalFields(_zonedDateTime_, _fieldNames_, « *"offset"* »). + 1. Let _partialZonedDateTime_ be ? PrepareTemporalFields(_temporalZonedDateTimeLike_, _fieldNames_, ~partial~). 1. Set _fields_ to ? CalendarMergeFields(_calendar_, _fields_, _partialZonedDateTime_). 1. Set _fields_ to ? PrepareTemporalFields(_fields_, _fieldNames_, « *"offset"* »). - 1. Set _options_ to ? GetOptionsObject(_options_). 1. NOTE: The following steps read options and perform independent validation in alphabetical order (ToTemporalDisambiguation reads *"disambiguation"*, ToTemporalOffset reads *"offset"*, and InterpretTemporalDateTimeFields reads *"overflow"*). 1. Let _disambiguation_ be ? ToTemporalDisambiguation(_options_). 1. Let _offset_ be ? ToTemporalOffset(_options_, *"prefer"*).