diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index dec2998ca9..63f3467a24 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -41,20 +41,14 @@ const impl = {}; export class Calendar { constructor(id) { - // Note: if the argument is not passed, IsBuiltinCalendar("undefined") will fail. This check - // exists only to improve the error message. - if (arguments.length < 1) { - throw new RangeError('missing argument: id is required'); - } - - id = ES.ToString(id); - if (!ES.IsBuiltinCalendar(id)) throw new RangeError(`invalid calendar identifier ${id}`); + const stringId = ES.RequireString(id); + if (!ES.IsBuiltinCalendar(stringId)) throw new RangeError(`invalid calendar identifier ${stringId}`); CreateSlots(this); - SetSlot(this, CALENDAR_ID, ES.ASCIILowercase(id)); + SetSlot(this, CALENDAR_ID, ES.ASCIILowercase(stringId)); if (typeof __debug__ !== 'undefined' && __debug__) { Object.defineProperty(this, '_repr_', { - value: `${this[Symbol.toStringTag]} <${id}>`, + value: `${this[Symbol.toStringTag]} <${stringId}>`, writable: false, enumerable: false, configurable: false diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 25d54d96f2..404e5a111b 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -18,6 +18,7 @@ const ObjectCreate = Object.create; const ObjectDefineProperty = Object.defineProperty; const ObjectEntries = Object.entries; const ObjectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +const StringCtor = String; const StringFromCharCode = String.fromCharCode; const StringPrototypeCharCodeAt = String.prototype.charCodeAt; const StringPrototypeMatchAll = String.prototype.matchAll; @@ -146,10 +147,27 @@ export function ToIntegerIfIntegral(value) { return number; } +// This convenience function isn't in the spec, but is useful in the polyfill +// for DRY and better error messages. +export function RequireString(value) { + if (Type(value) !== 'String') { + // Use String() to ensure that Symbols won't throw + throw new TypeError(`expected a string, not ${StringCtor(value)}`); + } + return value; +} + +// This function is an enum in the spec, but it's helpful to make it a +// function in the polyfill. +function ToPrimitiveAndRequireString(value) { + value = ToPrimitive(value, StringCtor); + return RequireString(value); +} + const BUILTIN_CASTS = new Map([ ['year', ToIntegerWithTruncation], ['month', ToPositiveIntegerWithTruncation], - ['monthCode', ToString], + ['monthCode', ToPrimitiveAndRequireString], ['day', ToPositiveIntegerWithTruncation], ['hour', ToIntegerWithTruncation], ['minute', ToIntegerWithTruncation], @@ -167,9 +185,9 @@ const BUILTIN_CASTS = new Map([ ['milliseconds', ToIntegerIfIntegral], ['microseconds', ToIntegerIfIntegral], ['nanoseconds', ToIntegerIfIntegral], - ['era', ToString], + ['era', ToPrimitiveAndRequireString], ['eraYear', ToIntegerOrInfinity], - ['offset', ToString] + ['offset', ToPrimitiveAndRequireString] ]); const BUILTIN_DEFAULTS = new Map([ @@ -704,7 +722,7 @@ export function RegulateISOYearMonth(year, month, overflow) { export function ToTemporalDurationRecord(item) { if (Type(item) !== 'Object') { - return ParseTemporalDurationString(ToString(item)); + return ParseTemporalDurationString(RequireString(item)); } if (IsTemporalDuration(item)) { return { @@ -986,7 +1004,7 @@ export function ToRelativeTemporalObject(options) { } else { let tzName, z; ({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, tzName, offset, z } = - ParseISODateTime(ToString(relativeTo))); + ParseISODateTime(RequireString(relativeTo))); if (tzName) { timeZone = ToTemporalTimeZoneSlotValue(tzName); if (z) { @@ -1134,7 +1152,7 @@ export function ToTemporalDate(item, options) { return CalendarDateFromFields(calendar, fields, options); } ToTemporalOverflow(options); // validate and ignore - let { year, month, day, calendar, z } = ParseTemporalDateString(ToString(item)); + let { year, month, day, calendar, z } = ParseTemporalDateString(RequireString(item)); if (z) throw new RangeError('Z designator not supported for PlainDate'); if (!calendar) calendar = 'iso8601'; if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`); @@ -1208,7 +1226,7 @@ export function ToTemporalDateTime(item, options) { ToTemporalOverflow(options); // validate and ignore let z; ({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, z } = - ParseTemporalDateTimeString(ToString(item))); + ParseTemporalDateTimeString(RequireString(item))); if (z) throw new RangeError('Z designator not supported for PlainDateTime'); RejectDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond); if (!calendar) calendar = 'iso8601'; @@ -1243,7 +1261,8 @@ export function ToTemporalInstant(item) { const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); return new TemporalInstant(GetSlot(item, EPOCHNANOSECONDS)); } - const ns = ParseTemporalInstant(ToString(item)); + item = ToPrimitive(item, StringCtor); + const ns = ParseTemporalInstant(RequireString(item)); const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); return new TemporalInstant(ns); } @@ -1273,7 +1292,7 @@ export function ToTemporalMonthDay(item, options) { } ToTemporalOverflow(options); // validate and ignore - let { month, day, referenceISOYear, calendar } = ParseTemporalMonthDayString(ToString(item)); + let { month, day, referenceISOYear, calendar } = ParseTemporalMonthDayString(RequireString(item)); if (calendar === undefined) calendar = 'iso8601'; if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`); calendar = ASCIILowercase(calendar); @@ -1315,7 +1334,7 @@ export function ToTemporalTime(item, overflow = 'constrain') { overflow )); } else { - ({ hour, minute, second, millisecond, microsecond, nanosecond } = ParseTemporalTimeString(ToString(item))); + ({ hour, minute, second, millisecond, microsecond, nanosecond } = ParseTemporalTimeString(RequireString(item))); RejectTime(hour, minute, second, millisecond, microsecond, nanosecond); } const TemporalPlainTime = GetIntrinsic('%Temporal.PlainTime%'); @@ -1332,7 +1351,7 @@ export function ToTemporalYearMonth(item, options) { } ToTemporalOverflow(options); // validate and ignore - let { year, month, referenceISODay, calendar } = ParseTemporalYearMonthString(ToString(item)); + let { year, month, referenceISODay, calendar } = ParseTemporalYearMonthString(RequireString(item)); if (calendar === undefined) calendar = 'iso8601'; if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`); calendar = ASCIILowercase(calendar); @@ -1453,7 +1472,7 @@ export function ToTemporalZonedDateTime(item, options) { } else { let tzName, z; ({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, tzName, offset, z, calendar } = - ParseTemporalZonedDateTimeString(ToString(item))); + ParseTemporalZonedDateTimeString(RequireString(item))); timeZone = ToTemporalTimeZoneSlotValue(tzName); if (z) { offsetBehaviour = 'exact'; @@ -1979,7 +1998,7 @@ export function ToTemporalCalendarSlotValue(calendarLike) { } return calendarLike; } - const identifier = ToString(calendarLike); + const identifier = RequireString(calendarLike); if (IsBuiltinCalendar(identifier)) return ASCIILowercase(identifier); let calendar; try { @@ -2098,7 +2117,7 @@ export function ToTemporalTimeZoneSlotValue(temporalTimeZoneLike) { } return temporalTimeZoneLike; } - const identifier = ToString(temporalTimeZoneLike); + const identifier = RequireString(temporalTimeZoneLike); return ParseTemporalTimeZone(identifier); } diff --git a/polyfill/lib/timezone.mjs b/polyfill/lib/timezone.mjs index 9796e980f6..36224634fc 100644 --- a/polyfill/lib/timezone.mjs +++ b/polyfill/lib/timezone.mjs @@ -20,13 +20,8 @@ import { } from './slots.mjs'; export class TimeZone { - constructor(identifier) { - // Note: if the argument is not passed, GetCanonicalTimeZoneIdentifier(undefined) will throw. - // This check exists only to improve the error message. - if (arguments.length < 1) { - throw new RangeError('missing argument: identifier is required'); - } - let stringIdentifier = ES.ToString(identifier); + constructor(id) { + let stringIdentifier = ES.RequireString(id); if (ES.IsTimeZoneOffsetString(stringIdentifier)) { stringIdentifier = ES.CanonicalizeTimeZoneOffsetString(stringIdentifier); } else { diff --git a/polyfill/test262 b/polyfill/test262 index c5b24c64c3..1eac172956 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit c5b24c64c3c27544f15e1c18ef274924cff1e32c +Subproject commit 1eac1729565c1cba26fada21d9df14f44634b93e diff --git a/spec/abstractops.html b/spec/abstractops.html index e68e49ac68..48f1041386 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -567,16 +567,21 @@

- -

ToRelativeTemporalObject ( _options_ )

-

- The abstract operation ToRelativeTemporalObject examines the value of the `relativeTo` property of its _options_ argument. - If this is not present, it returns *undefined*. - Otherwise, it attempts to return a Temporal.ZonedDateTime instance or Temporal.PlainDate instance, in order of preference, by converting the value. - If neither of those are possible, the operation throws a *RangeError*. -

+ +

ToRelativeTemporalObject ( + _options_: an Object + ): either a normal completion containing either a Temporal.ZonedDateTime object or a Temporal.PlainDate object, or a throw completion

+
+
description
+
+ It examines the value of the `relativeTo` property of its _options_ argument. + If the value is *undefined*, it returns *undefined*. + If the value is not a String or an Object, it throws a *TypeError*. + Otherwise, it attempts to return a Temporal.ZonedDateTime instance or Temporal.PlainDate instance, in order of preference, by converting the value. + If neither of those are possible, it throws a *RangeError*. +
+
- 1. Assert: Type(_options_) is Object. 1. Let _value_ be ? Get(_options_, *"relativeTo"*). 1. If _value_ is *undefined*, then 1. Return _value_. @@ -602,8 +607,8 @@

ToRelativeTemporalObject ( _options_ )

1. If _offsetString_ is *undefined*, then 1. Set _offsetBehaviour_ to ~wall~. 1. Else, - 1. Let _string_ be ? ToString(_value_). - 1. Let _result_ be ? ParseTemporalRelativeToString(_string_). + 1. If _value_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalRelativeToString(_value_). 1. Let _offsetString_ be _result_.[[TimeZone]].[[OffsetString]]. 1. Let _timeZoneName_ be _result_.[[TimeZone]].[[Name]]. 1. If _timeZoneName_ is *undefined*, then @@ -1807,8 +1812,10 @@

1. Set _value_ to ? ToPositiveIntegerWithTruncation(_value_). 1. Set _value_ to 𝔽(_value_). 1. Else, - 1. Assert: _Conversion_ is ~ToString~. - 1. Set _value_ to ? ToString(_value_). + 1. Assert: _Conversion_ is ~ToPrimitiveAndRequireString~. + 1. NOTE: Non-primitive values are supported here for consistency with other fields, but such values must coerce to Strings. + 1. Set _value_ to ? ToPrimitive(_value_, ~string~). + 1. If _value_ is not a String, throw a *TypeError* exception. 1. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_). 1. Else if _requiredFields_ is a List, then 1. If _requiredFields_ contains _property_, then @@ -1843,7 +1850,7 @@

*"monthCode"* - ~ToString~ + ~ToPrimitiveAndRequireString~ *undefined* @@ -1883,12 +1890,12 @@

*"offset"* - ~ToString~ + ~ToPrimitiveAndRequireString~ *undefined* *"era"* - ~ToString~ + ~ToPrimitiveAndRequireString~ *undefined* diff --git a/spec/calendar.html b/spec/calendar.html index 062253ab33..eb196039d3 100644 --- a/spec/calendar.html +++ b/spec/calendar.html @@ -525,8 +525,8 @@

1. Return _temporalCalendarLike_.[[Calendar]]. 1. If ? ObjectImplementsTemporalCalendarProtocol(_temporalCalendarLike_) is *false*, throw a *TypeError* exception. 1. Return _temporalCalendarLike_. - 1. Let _identifier_ be ? ToString(_temporalCalendarLike_). - 1. Set _identifier_ to ? ParseTemporalCalendarString(_identifier_). + 1. If _temporalCalendarLike_ is not a String, throw a *TypeError* exception. + 1. Let _identifier_ be ? ParseTemporalCalendarString(_temporalCalendarLike_). 1. If IsBuiltinCalendar(_identifier_) is *false*, throw a *RangeError* exception. 1. Return the ASCII-lowercase of _identifier_. @@ -1020,7 +1020,7 @@

Temporal.Calendar ( _id_ )

1. If NewTarget is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _id_ to ? ToString(_id_). + 1. If _id_ is not a String, throw a *TypeError* exception. 1. If IsBuiltinCalendar(_id_) is *false*, then 1. Throw a *RangeError* exception. 1. Return ? CreateTemporalCalendar(_id_, NewTarget). diff --git a/spec/duration.html b/spec/duration.html index 6a2ef5d75c..2aad4c2e50 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -932,8 +932,8 @@

1. If Type(_temporalDurationLike_) is not Object, then - 1. Let _string_ be ? ToString(_temporalDurationLike_). - 1. Return ? ParseTemporalDurationString(_string_). + 1. If _temporalDurationLike_ is not a String, throw a *TypeError* exception. + 1. Return ? ParseTemporalDurationString(_temporalDurationLike_). 1. If _temporalDurationLike_ has an [[InitializedTemporalDuration]] internal slot, then 1. Return ! CreateDurationRecord(_temporalDurationLike_.[[Years]], _temporalDurationLike_.[[Months]], _temporalDurationLike_.[[Weeks]], _temporalDurationLike_.[[Days]], _temporalDurationLike_.[[Hours]], _temporalDurationLike_.[[Minutes]], _temporalDurationLike_.[[Seconds]], _temporalDurationLike_.[[Milliseconds]], _temporalDurationLike_.[[Microseconds]], _temporalDurationLike_.[[Nanoseconds]]). 1. Let _result_ be a new Duration Record with each field set to 0. diff --git a/spec/instant.html b/spec/instant.html index 1445a41ba1..6a215389d9 100644 --- a/spec/instant.html +++ b/spec/instant.html @@ -518,8 +518,10 @@

ToTemporalInstant ( _item_ )

1. Return _item_. 1. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then 1. Return ! CreateTemporalInstant(_item_.[[Nanoseconds]]). - 1. Let _string_ be ? ToString(_item_). - 1. Let _epochNanoseconds_ be ? ParseTemporalInstant(_string_). + 1. NOTE: This use of ToPrimitive allows Instant-like objects to be converted. + 1. Set _item_ to ? ToPrimitive(_item_, ~string~). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _epochNanoseconds_ be ? ParseTemporalInstant(_item_). 1. Return ! CreateTemporalInstant(_epochNanoseconds_).
diff --git a/spec/plaindate.html b/spec/plaindate.html index 253fad169a..8e9ca60965 100644 --- a/spec/plaindate.html +++ b/spec/plaindate.html @@ -756,8 +756,8 @@

ToTemporalDate ( _item_ [ , _options_ ] )

1. Let _fields_ be ? PrepareTemporalFields(_item_, _fieldNames_, «»). 1. Return ? CalendarDateFromFields(_calendar_, _fields_, _options_). 1. Perform ? ToTemporalOverflow(_options_). - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalDateString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalDateString(_item_). 1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*. 1. Let _calendar_ be _result_.[[Calendar]]. 1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*. diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html index 94b8db76bf..8810a5381d 100644 --- a/spec/plaindatetime.html +++ b/spec/plaindatetime.html @@ -916,8 +916,8 @@

ToTemporalDateTime ( _item_ [ , _options_ ] )

1. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). 1. Else, 1. Perform ? ToTemporalOverflow(_options_). - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalDateTimeString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalDateTimeString(_item_). 1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*. 1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*. 1. Let _calendar_ be _result_.[[Calendar]]. diff --git a/spec/plainmonthday.html b/spec/plainmonthday.html index 7aaad254f8..55b5ea7b06 100644 --- a/spec/plainmonthday.html +++ b/spec/plainmonthday.html @@ -383,8 +383,8 @@

ToTemporalMonthDay ( _item_ [ , _options_ ] )

1. Perform ! CreateDataPropertyOrThrow(_fields_, *"year"*, 𝔽(_referenceISOYear_)). 1. Return ? CalendarMonthDayFromFields(_calendar_, _fields_, _options_). 1. Perform ? ToTemporalOverflow(_options_). - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalMonthDayString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalMonthDayString(_item_). 1. Let _calendar_ be _result_.[[Calendar]]. 1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*. 1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception. diff --git a/spec/plaintime.html b/spec/plaintime.html index b7540f5626..60245d0829 100644 --- a/spec/plaintime.html +++ b/spec/plaintime.html @@ -580,8 +580,8 @@

ToTemporalTime ( _item_ [ , _overflow_ ] )

1. Let _result_ be ? ToTemporalTimeRecord(_item_). 1. Set _result_ to ? RegulateTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _overflow_). 1. Else, - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalTimeString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalTimeString(_item_). 1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*. 1. Return ! CreateTemporalTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]).
diff --git a/spec/plainyearmonth.html b/spec/plainyearmonth.html index 0fe384686e..1586823e90 100644 --- a/spec/plainyearmonth.html +++ b/spec/plainyearmonth.html @@ -497,8 +497,8 @@

ToTemporalYearMonth ( _item_ [ , _options_ ] )

1. Let _fields_ be ? PrepareTemporalFields(_item_, _fieldNames_, «»). 1. Return ? CalendarYearMonthFromFields(_calendar_, _fields_, _options_). 1. Perform ? ToTemporalOverflow(_options_). - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalYearMonthString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalYearMonthString(_item_). 1. Let _calendar_ be _result_.[[Calendar]]. 1. If _calendar_ is *undefined*, set _calendar_ to *"iso8601"*. 1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception. diff --git a/spec/timezone.html b/spec/timezone.html index 5ad6a4e1a3..cd302892b1 100644 --- a/spec/timezone.html +++ b/spec/timezone.html @@ -31,7 +31,7 @@

Temporal.TimeZone ( _identifier_ )

1. If NewTarget is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _identifier_ to ? ToString(_identifier_). + 1. If _identifier_ is not a String, throw a *TypeError* exception. 1. If IsTimeZoneOffsetString(_identifier_) is *false*, then 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_identifier_). 1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception. @@ -562,8 +562,8 @@

1. Return _temporalTimeZoneLike_.[[TimeZone]]. 1. If ? ObjectImplementsTemporalTimeZoneProtocol(_temporalTimeZoneLike_) is *false*, throw a *TypeError* exception. 1. Return _temporalTimeZoneLike_. - 1. Let _identifier_ be ? ToString(_temporalTimeZoneLike_). - 1. Let _parseResult_ be ? ParseTemporalTimeZoneString(_identifier_). + 1. If _temporalTimeZoneLike_ is not a String, throw a *TypeError* exception. + 1. Let _parseResult_ be ? ParseTemporalTimeZoneString(_temporalTimeZoneLike_). 1. If _parseResult_.[[Name]] is not *undefined*, then 1. Let _name_ be _parseResult_.[[Name]]. 1. If IsTimeZoneOffsetString(_name_) is *true*, return CanonicalizeTimeZoneOffsetString(_name_). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index 4da9e62485..cbaa495bc3 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -1169,8 +1169,8 @@

1. Let _offsetOption_ be ? ToTemporalOffset(_options_, *"reject"*). 1. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). 1. Else, - 1. Let _string_ be ? ToString(_item_). - 1. Let _result_ be ? ParseTemporalZonedDateTimeString(_string_). + 1. If _item_ is not a String, throw a *TypeError* exception. + 1. Let _result_ be ? ParseTemporalZonedDateTimeString(_item_). 1. Let _timeZoneName_ be _result_.[[TimeZone]].[[Name]]. 1. Assert: _timeZoneName_ is not *undefined*. 1. Let _timeZone_ be ? ToTemporalTimeZoneSlotValue(_timeZoneName_).