From 7f694d4718a713e065eef5286cedd75b0345ab5e Mon Sep 17 00:00:00 2001 From: Kevin Gibbons <bakkot@gmail.com> Date: Fri, 10 Aug 2018 21:49:01 -0700 Subject: [PATCH] Object.fromEntries: add basic tests --- features.txt | 4 + .../Object/fromEntries/evaluation-order.js | 74 +++++++++++++++++++ .../iterator-closed-for-null-entry.js.js | 39 ++++++++++ .../iterator-closed-for-string-entry.js.js | 39 ++++++++++ ...ator-closed-for-throwing-entry-accessor.js | 45 +++++++++++ ...-closed-for-throwing-entry-key-tostring.js | 47 ++++++++++++ ...ot-closed-for-next-returning-non-object.js | 25 +++++++ ...r-not-closed-for-throwing-done-accessor.js | 32 ++++++++ .../iterator-not-closed-for-throwing-next.js | 27 +++++++ ...iterator-not-closed-for-uncallable-next.js | 23 ++++++ .../built-ins/Object/fromEntries/key-order.js | 22 ++++++ test/built-ins/Object/fromEntries/length.js | 15 ++++ test/built-ins/Object/fromEntries/name.js | 15 ++++ .../Object/fromEntries/requires-argument.js | 12 +++ .../Object/fromEntries/simple-properties.js | 14 ++++ .../string-entry-object-succeeds.js | 11 +++ .../string-entry-primitive-throws.js | 12 +++ .../Object/fromEntries/supports-symbols.js | 12 +++ .../Object/fromEntries/to-property-key.js | 17 +++++ .../fromEntries/uses-define-semantics.js | 20 +++++ .../fromEntries/uses-keys-not-iterator.js | 48 ++++++++++++ 21 files changed, 553 insertions(+) create mode 100644 test/built-ins/Object/fromEntries/evaluation-order.js create mode 100644 test/built-ins/Object/fromEntries/iterator-closed-for-null-entry.js.js create mode 100644 test/built-ins/Object/fromEntries/iterator-closed-for-string-entry.js.js create mode 100644 test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-accessor.js create mode 100644 test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-key-tostring.js create mode 100644 test/built-ins/Object/fromEntries/iterator-not-closed-for-next-returning-non-object.js create mode 100644 test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-done-accessor.js create mode 100644 test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-next.js create mode 100644 test/built-ins/Object/fromEntries/iterator-not-closed-for-uncallable-next.js create mode 100644 test/built-ins/Object/fromEntries/key-order.js create mode 100644 test/built-ins/Object/fromEntries/length.js create mode 100644 test/built-ins/Object/fromEntries/name.js create mode 100644 test/built-ins/Object/fromEntries/requires-argument.js create mode 100644 test/built-ins/Object/fromEntries/simple-properties.js create mode 100644 test/built-ins/Object/fromEntries/string-entry-object-succeeds.js create mode 100644 test/built-ins/Object/fromEntries/string-entry-primitive-throws.js create mode 100644 test/built-ins/Object/fromEntries/supports-symbols.js create mode 100644 test/built-ins/Object/fromEntries/to-property-key.js create mode 100644 test/built-ins/Object/fromEntries/uses-define-semantics.js create mode 100644 test/built-ins/Object/fromEntries/uses-keys-not-iterator.js diff --git a/features.txt b/features.txt index 478e419365..07d42907ec 100644 --- a/features.txt +++ b/features.txt @@ -7,6 +7,10 @@ # # https://github.com/tc39/process-document +# Object.fromEntries +# https://github.com/tc39/proposal-object-from-entries +Object.fromEntries + # BigInt # https://github.com/tc39/proposal-bigint BigInt diff --git a/test/built-ins/Object/fromEntries/evaluation-order.js b/test/built-ins/Object/fromEntries/evaluation-order.js new file mode 100644 index 0000000000..4c9e07a4cb --- /dev/null +++ b/test/built-ins/Object/fromEntries/evaluation-order.js @@ -0,0 +1,74 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Evaluation order is iterator.next(), get '0', get '1', toPropertyKey, repeat. +esid: sec-object.fromentries +includes: [compareArray.js] +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var effects = []; + +function makeEntry(label) { + return { + get '0'() { + effects.push('access property "0" of ' + label + ' entry'); + return { + toString: function() { + effects.push('toString of ' + label + ' key'); + return label + ' key'; + }, + }; + }, + get '1'() { + effects.push('access property "1" of ' + label + ' entry'); + return label + ' value'; + }, + }; +} + +var iterable = { + [Symbol.iterator]: function() { + effects.push('get Symbol.iterator'); + var count = 0; + return { + next: function() { + effects.push('next ' + count); + if (count === 0) { + ++count; + return { + done: false, + value: makeEntry('first', 'first key', 'first value'), + }; + } else if (count === 1) { + ++count; + return { + done: false, + value: makeEntry('second', 'second key', 'second value'), + }; + } else { + return { + done: true, + }; + } + }, + }; + }, +}; + +var result = Object.fromEntries(iterable); +assert.compareArray(effects, [ + 'get Symbol.iterator', + 'next 0', + 'access property "0" of first entry', + 'access property "1" of first entry', + 'toString of first key', + 'next 1', + 'access property "0" of second entry', + 'access property "1" of second entry', + 'toString of second key', + 'next 2', +], 'Object.fromEntries evaluation order'); +assert.sameValue(result['first key'], 'first value'); +assert.sameValue(result['second key'], 'second value'); diff --git a/test/built-ins/Object/fromEntries/iterator-closed-for-null-entry.js.js b/test/built-ins/Object/fromEntries/iterator-closed-for-null-entry.js.js new file mode 100644 index 0000000000..b84d87feac --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-closed-for-null-entry.js.js @@ -0,0 +1,39 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Closes iterators when they return entries which are null. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var returned = false; +var iterable = { + [Symbol.iterator]: function() { + var advanced = false; + return { + next: function() { + if (advanced) { + throw new Test262Error('should only advance once'); + } + advanced = true; + return { + done: false, + value: 'null', + }; + }, + return: function() { + if (returned) { + throw new Test262Error('should only return once'); + } + returned = true; + }, + }; + }, +}; + +assert.throws(TypeError, function() { + Object.fromEntries(iterable); +}); + +assert(returned, 'iterator should be closed when entry is null'); diff --git a/test/built-ins/Object/fromEntries/iterator-closed-for-string-entry.js.js b/test/built-ins/Object/fromEntries/iterator-closed-for-string-entry.js.js new file mode 100644 index 0000000000..b0c5bc9f90 --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-closed-for-string-entry.js.js @@ -0,0 +1,39 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Closes iterators when they return entries which are strings. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var returned = false; +var iterable = { + [Symbol.iterator]: function() { + var advanced = false; + return { + next: function() { + if (advanced) { + throw new Test262Error('should only advance once'); + } + advanced = true; + return { + done: false, + value: 'ab', + }; + }, + return: function() { + if (returned) { + throw new Test262Error('should only return once'); + } + returned = true; + }, + }; + }, +}; + +assert.throws(TypeError, function() { + Object.fromEntries(iterable); +}); + +assert(returned, 'iterator should be closed when entry is a string'); diff --git a/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-accessor.js b/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-accessor.js new file mode 100644 index 0000000000..9e7f9e4a2c --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-accessor.js @@ -0,0 +1,45 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Closes iterators when accessing an entry's properties throws. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +function DummyError() {} + +var returned = false; +var iterable = { + [Symbol.iterator]: function() { + var advanced = false; + return { + next: function() { + if (advanced) { + throw new Test262Error('should only advance once'); + } + advanced = true; + return { + done: false, + value: { + get '0'() { + throw new DummyError(); + }, + }, + }; + }, + return: function() { + if (returned) { + throw new Test262Error('should only return once'); + } + returned = true; + }, + }; + }, +}; + +assert.throws(DummyError, function() { + Object.fromEntries(iterable); +}); + +assert(returned, 'iterator should be closed when entry property access throws'); diff --git a/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-key-tostring.js b/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-key-tostring.js new file mode 100644 index 0000000000..5a845b5b20 --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-closed-for-throwing-entry-key-tostring.js @@ -0,0 +1,47 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Closes iterators when toString on a key throws. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +function DummyError() {} + +var returned = false; +var iterable = { + [Symbol.iterator]: function() { + var advanced = false; + return { + next: function() { + if (advanced) { + throw new Test262Error('should only advance once'); + } + advanced = true; + return { + done: false, + value: { + 0: { + toString: function() { + throw new DummyError(); + }, + }, + }, + }; + }, + return: function() { + if (returned) { + throw new Test262Error('should only return once'); + } + returned = true; + }, + }; + }, +}; + +assert.throws(DummyError, function() { + Object.fromEntries(iterable); +}); + +assert(returned, 'iterator should be closed when key toString throws'); diff --git a/test/built-ins/Object/fromEntries/iterator-not-closed-for-next-returning-non-object.js b/test/built-ins/Object/fromEntries/iterator-not-closed-for-next-returning-non-object.js new file mode 100644 index 0000000000..8f18c974f0 --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-not-closed-for-next-returning-non-object.js @@ -0,0 +1,25 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Does not close iterators with a `next` method which returns a non-object. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var iterable = { + [Symbol.iterator]: function() { + return { + next: function() { + return null; + }, + return: function() { + throw new Test262Error('should not call return'); + }, + }; + }, +}; + +assert.throws(TypeError, function() { + Object.fromEntries(iterable); +}); diff --git a/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-done-accessor.js b/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-done-accessor.js new file mode 100644 index 0000000000..5fd69c116f --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-done-accessor.js @@ -0,0 +1,32 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Does not close iterators with a `done` accessor which throws. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +function DummyError() {} + +var returned = false; +var iterable = { + [Symbol.iterator]: function() { + return { + next: function() { + return { + get done() { + throw new DummyError(); + }, + }; + }, + return: function() { + throw new Test262Error('should not call return'); + }, + }; + }, +}; + +assert.throws(DummyError, function() { + Object.fromEntries(iterable); +}); diff --git a/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-next.js b/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-next.js new file mode 100644 index 0000000000..b5ebcdf3f1 --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-not-closed-for-throwing-next.js @@ -0,0 +1,27 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Does not close iterators with a `next` method which throws. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +function DummyError() {} + +var iterable = { + [Symbol.iterator]: function() { + return { + next: function() { + throw new DummyError(); + }, + return: function() { + throw new Test262Error('should not call return'); + }, + }; + }, +}; + +assert.throws(DummyError, function() { + Object.fromEntries(iterable); +}); diff --git a/test/built-ins/Object/fromEntries/iterator-not-closed-for-uncallable-next.js b/test/built-ins/Object/fromEntries/iterator-not-closed-for-uncallable-next.js new file mode 100644 index 0000000000..f9aa9d23e1 --- /dev/null +++ b/test/built-ins/Object/fromEntries/iterator-not-closed-for-uncallable-next.js @@ -0,0 +1,23 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Does not close iterators with an uncallable `next` property. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var iterable = { + [Symbol.iterator]: function() { + return { + next: null, + return: function() { + throw new Test262Error('should not call return'); + }, + }; + }, +}; + +assert.throws(TypeError, function() { + Object.fromEntries(iterable); +}); diff --git a/test/built-ins/Object/fromEntries/key-order.js b/test/built-ins/Object/fromEntries/key-order.js new file mode 100644 index 0000000000..f8a08d3fae --- /dev/null +++ b/test/built-ins/Object/fromEntries/key-order.js @@ -0,0 +1,22 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Key enumeration order of result objects matches the order of entries in the iterable. +esid: sec-object.fromentries +includes: [compareArray.js] +features: [Object.fromEntries] +---*/ + +var entries = [ + ['z', 1], + ['y', 2], + ['x', 3], + ['y', 4], +]; + +var result = Object.fromEntries(entries); +assert.sameValue(result['z'], 1); +assert.sameValue(result['y'], 4); +assert.sameValue(result['x'], 3); +assert.compareArray(Object.keys(result), ['z', 'y', 'x']); diff --git a/test/built-ins/Object/fromEntries/length.js b/test/built-ins/Object/fromEntries/length.js new file mode 100644 index 0000000000..56615b4d7b --- /dev/null +++ b/test/built-ins/Object/fromEntries/length.js @@ -0,0 +1,15 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Object.fromEntries.length is 1. +esid: sec-object.fromentries +includes: [propertyHelper.js] +features: [Object.fromEntries] +---*/ + +assert.sameValue(Object.fromEntries.length, 1); + +verifyNotEnumerable(Object.fromEntries, "length"); +verifyNotWritable(Object.fromEntries, "length"); +verifyConfigurable(Object.fromEntries, "length"); diff --git a/test/built-ins/Object/fromEntries/name.js b/test/built-ins/Object/fromEntries/name.js new file mode 100644 index 0000000000..aa39c2277d --- /dev/null +++ b/test/built-ins/Object/fromEntries/name.js @@ -0,0 +1,15 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Object.fromEntries.name is "fromEntries". +esid: sec-object.fromentries +includes: [propertyHelper.js] +features: [Object.fromEntries] +---*/ + +assert.sameValue(Object.fromEntries.name, "fromEntries"); + +verifyNotEnumerable(Object.fromEntries, "name"); +verifyNotWritable(Object.fromEntries, "name"); +verifyConfigurable(Object.fromEntries, "name"); diff --git a/test/built-ins/Object/fromEntries/requires-argument.js b/test/built-ins/Object/fromEntries/requires-argument.js new file mode 100644 index 0000000000..b65051b003 --- /dev/null +++ b/test/built-ins/Object/fromEntries/requires-argument.js @@ -0,0 +1,12 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws when called without an argument. +esid: sec-object.fromentries +features: [Object.fromEntries] +---*/ + +assert.throws(TypeError, function() { + Object.fromEntries(); +}); diff --git a/test/built-ins/Object/fromEntries/simple-properties.js b/test/built-ins/Object/fromEntries/simple-properties.js new file mode 100644 index 0000000000..92663304ba --- /dev/null +++ b/test/built-ins/Object/fromEntries/simple-properties.js @@ -0,0 +1,14 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Creates data properties which are enumerable, writable, and configurable. +esid: sec-object.fromentries +includes: [propertyHelper.js] +features: [Object.fromEntries] +---*/ + +var result = Object.fromEntries([['key', 'value']]); +verifyEnumerable(result, 'key'); +verifyWritable(result, 'key'); +verifyConfigurable(result, 'key'); diff --git a/test/built-ins/Object/fromEntries/string-entry-object-succeeds.js b/test/built-ins/Object/fromEntries/string-entry-object-succeeds.js new file mode 100644 index 0000000000..d616f4d47a --- /dev/null +++ b/test/built-ins/Object/fromEntries/string-entry-object-succeeds.js @@ -0,0 +1,11 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Succeeds when an entry object is a boxed string. +esid: sec-object.fromentries +features: [Object.fromEntries] +---*/ + +var result = Object.fromEntries([Object('ab')]); +assert.sameValue(result['a'], 'b'); diff --git a/test/built-ins/Object/fromEntries/string-entry-primitive-throws.js b/test/built-ins/Object/fromEntries/string-entry-primitive-throws.js new file mode 100644 index 0000000000..2a678e3d8a --- /dev/null +++ b/test/built-ins/Object/fromEntries/string-entry-primitive-throws.js @@ -0,0 +1,12 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws when an entry object is a primitive string. +esid: sec-object.fromentries +features: [Object.fromEntries] +---*/ + +assert.throws(TypeError, function() { + Object.fromEntries(['ab']); +}); diff --git a/test/built-ins/Object/fromEntries/supports-symbols.js b/test/built-ins/Object/fromEntries/supports-symbols.js new file mode 100644 index 0000000000..fc72996166 --- /dev/null +++ b/test/built-ins/Object/fromEntries/supports-symbols.js @@ -0,0 +1,12 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Allows symbol keys. +esid: sec-object.fromentries +features: [Symbol, Object.fromEntries] +---*/ + +var key = Symbol(); +var result = Object.fromEntries([[key, 'value']]); +assert.sameValue(result[key], 'value'); diff --git a/test/built-ins/Object/fromEntries/to-property-key.js b/test/built-ins/Object/fromEntries/to-property-key.js new file mode 100644 index 0000000000..a707787588 --- /dev/null +++ b/test/built-ins/Object/fromEntries/to-property-key.js @@ -0,0 +1,17 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Coerces keys to strings using ToPropertyKey. +esid: sec-object.fromentries +features: [Symbol.toPrimitive, Object.fromEntries] +---*/ + +var key = { + [Symbol.toPrimitive]: function(hint) { + assert.sameValue(hint, 'string'); + return 'key'; + }, +}; +var result = Object.fromEntries([[key, 'value']]); +assert.sameValue(result.key, 'value'); diff --git a/test/built-ins/Object/fromEntries/uses-define-semantics.js b/test/built-ins/Object/fromEntries/uses-define-semantics.js new file mode 100644 index 0000000000..b5f56686d1 --- /dev/null +++ b/test/built-ins/Object/fromEntries/uses-define-semantics.js @@ -0,0 +1,20 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Uses [[DefineOwnProperty]] rather than [[Set]]. +esid: sec-object.fromentries +features: [Object.fromEntries] +---*/ + +Object.defineProperty(Object.prototype, 'property', { + get: function() { + throw new Test262Error('should not trigger getter on Object.prototype'); + }, + set: function() { + throw new Test262Error('should not trigger setter on Object.prototype'); + }, +}); + +var result = Object.fromEntries([['property', 'value']]); +assert.sameValue(result['property'], 'value', ''); diff --git a/test/built-ins/Object/fromEntries/uses-keys-not-iterator.js b/test/built-ins/Object/fromEntries/uses-keys-not-iterator.js new file mode 100644 index 0000000000..c4302074cb --- /dev/null +++ b/test/built-ins/Object/fromEntries/uses-keys-not-iterator.js @@ -0,0 +1,48 @@ +// Copyright (C) 2018 Kevin Gibbons. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Reads properties rather than iterating. +esid: sec-object.fromentries +features: [Symbol.iterator, Object.fromEntries] +---*/ + +var iterable = { + [Symbol.iterator]: function() { + var count = 0; + return { + next: function() { + if (count === 0) { + ++count; + return { + done: false, + value: { + '0': 'first key', + '1': 'first value', + get [Symbol.iterator]() { + throw new Test262Error('Object.fromEntries should not access Symbol.iterator on entry objects'); + }, + }, + }; + } else if (count === 1) { + ++count; + Array.prototype[Symbol.iterator] = function() { + throw new Test262Error('Object.fromEntries should not access Symbol.iterator on entry arrays'); + }; + return { + done: false, + value: ['second key', 'second value'], + }; + } else { + return { + done: true, + }; + } + }, + }; + }, +}; + +var result = Object.fromEntries(iterable); +assert.sameValue(result['first key'], 'first value'); +assert.sameValue(result['second key'], 'second value'); -- GitLab