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