Skip to content

Commit

Permalink
Require options bags to be object or undefined
Browse files Browse the repository at this point in the history
See also: tc39/ecma402#480

Co-Authored-By: Philip Chimento <pchimento@igalia.com>
  • Loading branch information
justingrant and ptomato committed Aug 27, 2020
1 parent 059ce0f commit 10d03d9
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 17 deletions.
16 changes: 12 additions & 4 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Call from 'es-abstract/2019/Call.js';
import SpeciesConstructor from 'es-abstract/2019/SpeciesConstructor.js';
import ToInteger from 'es-abstract/2019/ToInteger.js';
import ToNumber from 'es-abstract/2019/ToNumber.js';
import ToObject from 'es-abstract/2019/ToObject.js';
import ToPrimitive from 'es-abstract/2019/ToPrimitive.js';
import ToString from 'es-abstract/2019/ToString.js';

Expand Down Expand Up @@ -55,7 +54,6 @@ const ES2019 = {
SpeciesConstructor,
ToInteger,
ToNumber,
ToObject,
ToPrimitive,
ToString
};
Expand Down Expand Up @@ -466,15 +464,19 @@ export const ES = ObjectAssign({}, ES2019, {
return duration;
},
ToDurationTemporalDisambiguation: (options) => {
options = ES.NormalizeOptionsObject(options);
return ES.GetOption(options, 'disambiguation', ['constrain', 'balance', 'reject'], 'constrain');
},
ToTemporalDisambiguation: (options) => {
options = ES.NormalizeOptionsObject(options);
return ES.GetOption(options, 'disambiguation', ['constrain', 'reject'], 'constrain');
},
ToTimeZoneTemporalDisambiguation: (options) => {
options = ES.NormalizeOptionsObject(options);
return ES.GetOption(options, 'disambiguation', ['compatible', 'earlier', 'later', 'reject'], 'compatible');
},
ToDurationSubtractionTemporalDisambiguation: (options) => {
options = ES.NormalizeOptionsObject(options);
return ES.GetOption(options, 'disambiguation', ['balanceConstrain', 'balance'], 'balanceConstrain');
},
ToLargestTemporalUnit: (options, fallback, disallowedStrings = []) => {
Expand All @@ -493,6 +495,7 @@ export const ES = ObjectAssign({}, ES2019, {
for (const s of disallowedStrings) {
allowed.delete(s);
}
options = ES.NormalizeOptionsObject(options);
return ES.GetOption(options, 'largestUnit', [...allowed], fallback);
},
ToPartialRecord: (bag, fields) => {
Expand Down Expand Up @@ -1581,9 +1584,14 @@ export const ES = ObjectAssign({}, ES2019, {
return new TemporalTimeZone(ES.TemporalTimeZoneFromString(fmt.resolvedOptions().timeZone));
},
ComparisonResult: (value) => (value < 0 ? -1 : value > 0 ? 1 : value),
NormalizeOptionsObject: (options) => {
if (options === undefined) return {};
if (options !== null && (typeof options === 'object' || typeof options === 'function')) {
return options;
}
throw new TypeError(`Options parameter must be an object, not a ${typeof options}`);
},
GetOption: (options, property, allowedValues, fallback) => {
if (options === null || options === undefined) return fallback;
options = ES.ToObject(options);
let value = options[property];
if (value !== undefined) {
value = ES.ToString(value);
Expand Down
10 changes: 9 additions & 1 deletion polyfill/test/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Pretty from '@pipobscure/demitasse-pretty';
const { reporter } = Pretty;

import { strict as assert } from 'assert';
const { deepEqual } = assert;
const { deepEqual, throws } = assert;

import { ES } from '../lib/ecmascript.mjs';
import { GetSlot, TIMEZONE_ID } from '../lib/slots.mjs';
Expand Down Expand Up @@ -398,6 +398,14 @@ describe('ECMAScript', () => {
it(`${nanos} @ ${zone}`, () => deepEqual(ES.GetFormatterParts(zone, nanos), expected));
}
});

describe('NormalizeOptionsObject', () => {
it('Options parameter can only be an object or undefined', () => {
[null, 1, 'hello', true, Symbol('1'), 1n].forEach((options) =>
throws(() => ES.NormalizeOptionsObject(options), TypeError)
);
});
});
});

import { normalize } from 'path';
Expand Down
2 changes: 1 addition & 1 deletion polyfill/test/yearmonth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ describe('YearMonth', () => {
describe('YearMonth.with()', () => {
it('throws on bad disambiguation', () => {
['', 'CONSTRAIN', 'balance', 3, null].forEach((disambiguation) =>
throws(() => YearMonth.from(2019, 1).with({ month: 2 }, { disambiguation }), RangeError)
throws(() => YearMonth.from({ year: 2019, month: 1 }).with({ month: 2 }, { disambiguation }), RangeError)
);
});
});
Expand Down
38 changes: 27 additions & 11 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,67 @@
<emu-clause id="sec-temporal-abstract-ops">
<h1>Abstract operations</h1>

<!-- Based on ECMA-402 9.2.10 GetOption -->
<!-- Based on ECMA-402 9.2.10 NormalizeOptionsObject and GetOption -->
<emu-clause id="sec-normalizeoptionsobject" aoid="NormalizeOptionsObject">
<h1>NormalizeOptionsObject ( _options_ )</h1>
<p>
The abstract operation NormalizeOptionsObject massages _options_ into an Object to be subsequently passed to GetOption.
It throws a TypeError if _options_ is not undefined and not an Object.
</p>
<emu-alg>
1. If _options_ is *undefined*, then
1. Return ! ObjectCreate(*null*).
1. If Type(_options_) is Object, then
1. Return _options_.
1. Throw a *TypeError* exception.
</emu-alg>
</emu-clause>

<emu-clause id="sec-getoption" aoid="GetOption">
<h1>GetOption ( _options_, _property_, _values_, _fallback_ )</h1>

<p>
The abstract operation GetOption extracts the value of the property named _property_ from the provided _options_ object, converts it to a string, checks whether it is one of a List of allowed _values_, and fills in a _fallback_ value if necessary. If _values_ is *undefined*, there is no fixed set of values and any is permitted. If _options_ is *undefined*, then _fallback_ is returned.
The abstract operation GetOption extracts the value of the property named _property_ from the provided _options_ object, converts it to a string, checks whether it is one of a List of allowed _values_, and fills in a _fallback_ value if necessary. If _values_ is *undefined*, there is no fixed set of values and any is permitted.
</p>

<emu-alg>
1. If _options_ is *undefined* or *null*, then
1. Return _fallback_.
1. Set _options_ to ? ToObject(_options_).
1. Assert: Type(_options_) is Object.
1. Let _value_ be ? Get(_options_, _property_).
1. If _value_ is not *undefined*, then
1. Set _value_ to ? ToString(_value_).
1. If _values_ is not *undefined*, then
1. If _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
1. Return _value_.
1. Else, return _fallback_.
1. If _value_ is *undefined*, return _fallback_.
1. Let _value_ be ? ToString(_value_).
1. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception.
1. Return _value_.
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-todurationtemporaldisambiguation" aoid="ToDurationTemporalDisambiguation">
<h1>ToDurationTemporalDisambiguation ( _options_ )</h1>
<emu-alg>
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Return ? GetOption(_options_, *"disambiguation"*, « *"constrain"*, *"balance"*, *"reject"* », *"constrain"*).
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-totemporaldisambiguation" aoid="ToTemporalDisambiguation">
<h1>ToTemporalDisambiguation ( _options_ )</h1>
<emu-alg>
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Return ? GetOption(_options_, *"disambiguation"*, « *"constrain"*, *"reject"* », *"constrain"*).
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-totimezonetemporaldisambiguation" aoid="ToTimeZoneTemporalDisambiguation">
<h1>ToTimeZoneTemporalDisambiguation ( _options_ )</h1>
<emu-alg>
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Return ? GetOption(_options_, *"disambiguation"*, « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*).
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-todurationsubtractiontemporaldisambiguation" aoid="ToDurationSubtractionTemporalDisambiguation">
<h1>ToDurationSubtractionTemporalDisambiguation ( _options_ )</h1>
<emu-alg>
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Return ? GetOption(_options_, *"disambiguation"*, « *"balanceConstrain"*, *"balance"* », *"balanceConstrain"*).
</emu-alg>
</emu-clause>
Expand All @@ -58,6 +73,7 @@ <h1>ToDurationSubtractionTemporalDisambiguation ( _options_ )</h1>
<h1>ToLargestTemporalUnit ( _largestUnit_, _disallowedUnits_, _defaultUnit_ )</h1>
<emu-alg>
1. Assert: _disallowedUnits_ does not contain _defaultUnit_.
1. Set _options_ to ? NormalizeOptionsObject(_options_).
1. Let _largestUnit_ be GetOption(_options_, *"largestUnit"*, « *"years"*, *"months"*, *"days"*, *"hours"*, *"minutes"*, *"seconds"*, *"milliseconds"*, *"microseconds"*, *"nanoseconds"* », _defaultUnit_).
1. If _disallowedUnits_ contains _largestUnit_, then
1. Throw a *RangeError* exception.
Expand Down

0 comments on commit 10d03d9

Please sign in to comment.