Skip to content

Commit

Permalink
Update Intl.DateTimeFormat to read options only once
Browse files Browse the repository at this point in the history
Implements tc39/ecma402#709

Introduced test failures:
```sh
$ npm run test:test262 -- --extended-tests --run-slow-tests $(
    find $(
      find test/test262/test262/ -type d -a '(' -iname '*intl*' -o -iname '*402*' ')' -prune | \
      tee /dev/stderr \
    ) -iname '*date*' -prune | \
    grep -viE 'temporal|\bdate\b|\bsupportedValuesOf\b|\bDisplayName'
  ) 2>&1 | \
  grep -vE '^[[:space:]]+ at ' | \
  tee /dev/stderr | \
  awk '/^FAILURE/ { a[$2]++ } END { print "\n\nFILES" > "/dev/stderr"; for(f in a) print f }' | \
  sort -u
test/test262/test262/test/staging/Intl402
test/test262/test262/test/intl402
test/test262/test262/implementation-contributed/v8/intl
test/test262/test262/implementation-contributed/v8/test262/local-tests/test/intl402

> @engine262/[email protected] test:test262
> node --enable-source-maps test/test262/test262.js --extended-tests --run-slow-tests test/test262/test262/test/intl402/DateTimeFormat test/test262/test262/test/intl402/Intl/DateTimeFormat test/test262/test262/implementation-contributed/v8/test262/local-tests/test/intl402/DateTimeFormat

 engine262 Test Runner
 Detected 4 CPUs
 Not running on CI

[00:00|:  342|+    0|-    0|»   20] (0.00/s)
FAILURE! intl402/DateTimeFormat/constructor-options-order-dayPeriod.js
Checks the order of getting options of 'dayPeriod' for the DateTimeFormat constructor. (Strict Mode)
Error: Expected [day, dayPeriod, hour] and [day, dayPeriod, hour, day, dayPeriod, hour] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order-dayPeriod.js
Checks the order of getting options of 'dayPeriod' for the DateTimeFormat constructor.
Error: Expected [day, dayPeriod, hour] and [day, dayPeriod, hour, day, dayPeriod, hour] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order-timedate-style.js
Checks the order of getting options for the DateTimeFormat constructor.
Error: Expected [localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher, dateStyle, timeStyle] and [weekday, year, month, day, hour, minute, second, dateStyle, timeStyle, localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher, dateStyle, timeStyle] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order-fractionalSecondDigits.js
Checks the order of getting options of 'fractionalSecondDigits' for the DateTimeFormat constructor. (Strict Mode)
Error: Expected [localeMatcher, second, fractionalSecondDigits, timeZoneName, formatMatcher] and [second, fractionalSecondDigits, localeMatcher, second, fractionalSecondDigits, timeZoneName, formatMatcher] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order.js
Checks the order of getting options for the DateTimeFormat constructor. (Strict Mode)
Error: Expected [localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher] and [weekday, year, month, day, hour, minute, second, localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order.js
Checks the order of getting options for the DateTimeFormat constructor.
Error: Expected [localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher] and [weekday, year, month, day, hour, minute, second, localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order-fractionalSecondDigits.js
Checks the order of getting options of 'fractionalSecondDigits' for the DateTimeFormat constructor.
Error: Expected [localeMatcher, second, fractionalSecondDigits, timeZoneName, formatMatcher] and [second, fractionalSecondDigits, localeMatcher, second, fractionalSecondDigits, timeZoneName, formatMatcher] to have the same contents.

FAILURE! intl402/DateTimeFormat/constructor-options-order-timedate-style.js
Checks the order of getting options for the DateTimeFormat constructor. (Strict Mode)
Error: Expected [localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher, dateStyle, timeStyle] and [weekday, year, month, day, hour, minute, second, dateStyle, timeStyle, localeMatcher, hour12, hourCycle, timeZone, weekday, era, year, month, day, hour, minute, second, timeZoneName, formatMatcher, dateStyle, timeStyle] to have the same contents.
[00:04|:  342|+  314|-    8|»   20] (70.00/s)

FILES
intl402/DateTimeFormat/constructor-options-order-dayPeriod.js
intl402/DateTimeFormat/constructor-options-order-fractionalSecondDigits.js
intl402/DateTimeFormat/constructor-options-order-timedate-style.js
intl402/DateTimeFormat/constructor-options-order.js
```
  • Loading branch information
gibson042 committed Jan 16, 2023
1 parent d2db5d1 commit 85c8079
Showing 1 changed file with 44 additions and 63 deletions.
107 changes: 44 additions & 63 deletions src/intrinsics/Intl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,55 +117,6 @@ const DTF_COMPONENTS = {
timeZoneName: ['short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric'],
};

const ToDateTimeOptions = (options, required, defaults) => {
if (options === Value.undefined) options = Value.null;
else options = Q(ToObject(options));
options = OrdinaryObjectCreate(options);
let needDefaults = Value.true;
const hostRequiredCategory = bridgeableForHost(required);
if (hostRequiredCategory === 'date' || hostRequiredCategory === 'any') {
for (const prop of ['weekday', 'year', 'month', 'day'].map(s => new Value(s))) {
const value = Q(Get(options, prop));
if (value !== Value.undefined) needDefaults = Value.false;
}
}
if (hostRequiredCategory === 'time' || hostRequiredCategory === 'any') {
for (const prop of ['dayPeriod', 'hour', 'minute', 'second', 'fractionalSecondDigits'].map(s => new Value(s))) {
const value = Q(Get(options, prop));
if (value !== Value.undefined) needDefaults = Value.false;
}
}
const dateStyle = Q(Get(options, new Value('dateStyle')));
const timeStyle = Q(Get(options, new Value('timeStyle')));
if (dateStyle !== Value.undefined || timeStyle !== Value.undefined) needDefaults = false;
if (hostRequiredCategory === 'date' && timeStyle !== Value.undefined) {
return surroundingAgent.Throw(
'TypeError',
'IncompatibleOptions',
'date-only formatting prohibits "timeStyle"',
);
}
if (hostRequiredCategory === 'time' && dateStyle !== Value.undefined) {
return surroundingAgent.Throw(
'TypeError',
'IncompatibleOptions',
'time-only formatting prohibits "dateStyle"',
);
}
const hostDefaultsCategory = bridgeableForHost(defaults);
if (needDefaults === Value.true && ['date', 'all'].includes(hostDefaultsCategory)) {
for (const prop of ['year', 'month', 'day'].map(s => new Value(s))) {
Q(CreateDataPropertyOrThrow(options, prop, new Value('numeric')));
}
}
if (needDefaults === Value.true && ['time', 'all'].includes(hostDefaultsCategory)) {
for (const prop of ['hour', 'minute', 'second'].map(s => new Value(s))) {
Q(CreateDataPropertyOrThrow(options, prop, new Value('numeric')));
}
}
return options;
};


export function bootstrapIntl(realmRec) {
if (typeof Intl !== 'object') return;
Expand Down Expand Up @@ -273,16 +224,19 @@ export function bootstrapIntl(realmRec) {
if (NewTarget === Value.undefined) {
NewTarget = surroundingAgent.activeFunctionObject;
}
const dtf = Q(OrdinaryCreateFromConstructor(
NewTarget,

return Q(CreateDateTimeFormat(NewTarget, locales, options, 'any', 'date'));
}

function CreateDateTimeFormat(newTarget, locales, options, requiredCategory, defaultsCategory) {
const dateTimeFormat = Q(OrdinaryCreateFromConstructor(
newTarget,
'%DateTimeFormat.prototype%',
// We defer to a host instance, replacing the need for most internal slots.
['HostDTFInstance', 'BoundFormat'],
));

// InitializeDateTimeFormat
const requestedLocales = Q(limitedCanonicalizeLocaleList(locales));
options = Q(ToDateTimeOptions(options, new Value('any'), new Value('date')));
options = Q(CoerceOptionsToObject(options));
const localeMatcher = Q(hostGetOption(
options,
'localeMatcher',
Expand Down Expand Up @@ -361,22 +315,49 @@ export function bootstrapIntl(realmRec) {
hostOptions.timeStyle = bridgeableForHost(
Q(hostGetOption(options, 'timeStyle', 'string', ['full', 'long', 'medium', 'short']))
);
const broadStyle = hostOptions.dateStyle !== undefined || hostOptions.timeStyle !== undefined;
if (broadStyle && hasExplicitFormatComponents === Value.true) {
return surroundingAgent.Throw(
'TypeError',
'IncompatibleOptions',
'"dateStyle" and "timeStyle" prohibit field-specific formatting',
);
if (hostOptions.dateStyle !== undefined || hostOptions.timeStyle !== undefined) {
let errMessage;
if (hasExplicitFormatComponents === Value.true) {
errMessage = '"dateStyle" and "timeStyle" prohibit component-specific formatting';
} else if (requiredCategory === 'date' && hostOptions.timeStyle !== undefined) {
errMessage = 'date-only formatting prohibits "timeStyle"';
} else if (requiredCategory === 'time' && hostOptions.dateStyle !== undefined) {
errMessage = 'time-only formatting prohibits "dateStyle"';
}
if (errMessage) return surroundingAgent.Throw('TypeError', 'IncompatibleOptions', errMessage);
} else {
let needDefaults = Value.true;
if (requiredCategory === 'date' || requiredCategory === 'any') {
for (const prop of ['weekday', 'year', 'month', 'day']) {
const value = hostOptions[prop];
if (value !== undefined) needDefaults = Value.false;
}
}
if (requiredCategory === 'time' || requiredCategory === 'any') {
for (const prop of ['dayPeriod', 'hour', 'minute', 'second', 'fractionalSecondDigits']) {
const value = hostOptions[prop];
if (value !== undefined) needDefaults = Value.false;
}
}
if (needDefaults === Value.true && ['date', 'all'].includes(defaultsCategory)) {
for (const prop of ['year', 'month', 'day']) {
hostOptions[prop] = 'numeric';
}
}
if (needDefaults === Value.true && ['time', 'all'].includes(defaultsCategory)) {
for (const prop of ['hour', 'minute', 'second']) {
hostOptions[prop] = 'numeric';
}
}
}

const hostRequestedLocales = bridgeableForHost(requestedLocales);
try {
dtf.HostDTFInstance = new hostDTF(hostRequestedLocales, hostOptions);
dateTimeFormat.HostDTFInstance = new hostDTF(hostRequestedLocales, hostOptions);
} catch (err) {
return surroundingAgent.Throw(err.name, 'Raw', err.message);
}
return dtf;
return dateTimeFormat;
}

const DTF = realmRec.Intrinsics['%DateTimeFormat%'] = bootstrapConstructor(
Expand Down

0 comments on commit 85c8079

Please sign in to comment.