Skip to content

Commit

Permalink
Normative: Remove [[Enumerate]] and associated reflective capabilities
Browse files Browse the repository at this point in the history
Summary of changes:

* 6.1.7.2 Removed [[Enumerate]] from the table and list of invariants
* 9.5 Proxy Object Internal Methods and Interal Slots, [[Enumerate]] table entry removed.
* 9.4.6.1.1 Module Namespace Object [[Enumerate]] moved to 26.3.2 module namespace object [ @@iterator ]
* 9.5.11 Proxy [[Enumerate]] deleted
* Moved ordinary [[Enumerate]] section to end of 13.7.5 for-in, renamed to EnumerateObjectProperties.
  * Add an assert to step 1 to signal callers we expect they'll only pass objects
  * Updated informative definition of EnumerateObjectProperties
* 13.7.5.12 for-in head evaluation calls EnumerateObjectProperties(_obj_) instead of _obj_.[[Enumerate]]
* EnumerableOwnNames: the _names_ are always ordered the same as EnumerateObjectProperties (since we don't have to worry about this not being enforceable with proxies anymore)
* 26.1.5 Reflect.enumerate deleted

Fixes #367.
  • Loading branch information
bterlson committed Feb 12, 2016
1 parent 7960e54 commit d96e60a
Showing 1 changed file with 39 additions and 112 deletions.
151 changes: 39 additions & 112 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -1293,17 +1293,6 @@ <h1>Object Internal Methods and Internal Slots</h1>
Create or alter the own property, whose key is _propertyKey_, to have the state described by _PropertyDescriptor_. Return *true* if that property was successfully created/updated or *false* if the property could not be created or updated.
</td>
</tr>
<tr>
<td>
[[Enumerate]]
</td>
<td>
()<b>&rarr;</b>Object
</td>
<td>
Return an iterator object that produces the keys of the string-keyed enumerable properties of the object.
</td>
</tr>
<tr>
<td>
[[OwnPropertyKeys]]
Expand Down Expand Up @@ -1504,12 +1493,6 @@ <h2>[[Delete]] ( _P_ )</h2>
If P was previously observed to be a non-configurable own data or accessor property of the target, [[Delete]] must return false.
</li>
</ul>
<h2>[[Enumerate]] ( )</h2>
<ul>
<li>
The Type of the return value must be Object.
</li>
</ul>
<h2>[[OwnPropertyKeys]] ( )</h2>
<ul>
<li>
Expand Down Expand Up @@ -4397,13 +4380,9 @@ <h1>EnumerableOwnNames (_O_)</h1>
1. Let _desc_ be ? _O_.[[GetOwnProperty]](_key_).
1. If _desc_ is not *undefined*, then
1. If _desc_.[[Enumerable]] is *true*, append _key_ to _names_.
1. If _O_.[[Enumerate]] is the ordinary object [[Enumerate]] internal method (<emu-xref href="#sec-ordinary-object-internal-methods-and-internal-slots-enumerate"></emu-xref>), then
1. Order the elements of _names_ so they are in the same relative order as would be produced by the Iterator that would be returned if the [[Enumerate]] internal method was invoked on _O_.
1. Order the elements of _names_ so they are in the same relative order as would be produced by the Iterator that would be returned if the EnumerateObjectProperties internal method was invoked with _O_.
1. Return _names_.
</emu-alg>
<emu-note>
<p>The order of elements in the returned list is the same as the enumeration order that is used by a for-in statement.</p>
</emu-note>
</emu-clause>

<!-- es6num="7.3.22" -->
Expand Down Expand Up @@ -6558,39 +6537,6 @@ <h1>OrdinaryDelete (_O_, _P_)</h1>
</emu-clause>
</emu-clause>

<!-- es6num="9.1.11" -->
<emu-clause id="sec-ordinary-object-internal-methods-and-internal-slots-enumerate">
<h1>[[Enumerate]] ()</h1>
<p>When the [[Enumerate]] internal method of _O_ is called, the following steps are taken:</p>
<emu-alg>
1. Return an Iterator object (<emu-xref href="#sec-iterator-interface"></emu-xref>) whose `next` method iterates over all the String-valued keys of enumerable properties of _O_. The Iterator object must inherit from %IteratorPrototype% (<emu-xref href="#sec-%iteratorprototype%-object"></emu-xref>). The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below.
</emu-alg>
<p>The iterator's `next` method processes object properties to determine whether the property key should be returned as an iterator value. Returned property keys do not include keys that are Symbols. Properties of the target object may be deleted during enumeration. A property that is deleted before it is processed by the iterator's `next` method is ignored. If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration. A property name will be returned by the iterator's `next` method at most once in any enumeration.</p>
<p>Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively; but a property of a prototype is not processed if it has the same name as a property that has already been processed by the iterator's `next` method. The values of [[Enumerable]] attributes are not considered when determining if a property of a prototype object has already been processed. The enumerable property names of prototype objects must be obtained as if by invoking the prototype object's [[Enumerate]] internal method. [[Enumerate]] must obtain the own property keys of the target object as if by calling its [[OwnPropertyKeys]] internal method. Property attributes of the target object must be obtained as if by calling its [[GetOwnProperty]] internal method.</p>
<emu-note>
<p>The following is an informative definition of an ECMAScript generator function that conforms to these rules:</p>
<pre><code class="javascript">
function* enumerate(obj) {
let visited = new Set;
for (let key of Reflect.ownKeys(obj)) {
if (typeof key === "string") {
let desc = Reflect.getOwnPropertyDescriptor(obj, key);
if (desc) {
visited.add(key);
if (desc.enumerable) yield key;
}
}
}
let proto = Reflect.getPrototypeOf(obj)
if (proto === null) return;
for (let protoName of Reflect.enumerate(proto)) {
if (!visited.has(protoName)) yield protoName;
}
}
</code></pre>
</emu-note>
</emu-clause>

<!-- es6num="9.1.12" -->
<emu-clause id="sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys">
<h1>[[OwnPropertyKeys]] ( )</h1>
Expand Down Expand Up @@ -8128,16 +8074,6 @@ <h1>[[Delete]] (_P_)</h1>
</emu-alg>
</emu-clause>

<!-- es6num="9.4.6.11" -->
<emu-clause id="sec-module-namespace-exotic-objects-enumerate">
<h1>[[Enumerate]] ()</h1>
<p>When the [[Enumerate]] internal method of a module namespace exotic object _O_ is called, the following steps are taken:</p>
<emu-alg>
1. Let _exports_ be the value of _O_'s [[Exports]] internal slot.
1. Return CreateListIterator(_exports_).
</emu-alg>
</emu-clause>

<!-- es6num="9.4.6.12" -->
<emu-clause id="sec-module-namespace-exotic-objects-ownpropertykeys">
<h1>[[OwnPropertyKeys]] ( )</h1>
Expand Down Expand Up @@ -8281,14 +8217,6 @@ <h1>Proxy Object Internal Methods and Internal Slots</h1>
`defineProperty`
</td>
</tr>
<tr>
<td>
[[Enumerate]]
</td>
<td>
`enumerate`
</td>
</tr>
<tr>
<td>
[[OwnPropertyKeys]]
Expand Down Expand Up @@ -8697,32 +8625,6 @@ <h1>[[Delete]] (_P_)</h1>
</emu-note>
</emu-clause>

<!-- es6num="9.5.11" -->
<emu-clause id="sec-proxy-object-internal-methods-and-internal-slots-enumerate">
<h1>[[Enumerate]] ()</h1>
<p>When the [[Enumerate]] internal method of a Proxy exotic object _O_ is called, the following steps are taken:</p>
<emu-alg>
1. Let _handler_ be the value of the [[ProxyHandler]] internal slot of _O_.
1. If _handler_ is *null*, throw a *TypeError* exception.
1. Assert: Type(_handler_) is Object.
1. Let _target_ be the value of the [[ProxyTarget]] internal slot of _O_.
1. Let _trap_ be ? GetMethod(_handler_, `"enumerate"`).
1. If _trap_ is *undefined*, then
1. Return ? _target_.[[Enumerate]]().
1. Let _trapResult_ be ? Call(_trap_, _handler_, &laquo; _target_ &raquo;).
1. If Type(_trapResult_) is not Object, throw a *TypeError* exception.
1. Return _trapResult_.
</emu-alg>
<emu-note>
<p>[[Enumerate]] for proxy objects enforces the following invariants:</p>
<ul>
<li>
The result of [[Enumerate]] must be an Object.
</li>
</ul>
</emu-note>
</emu-clause>

<!-- es6num="9.5.12" -->
<emu-clause id="sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys">
<h1>[[OwnPropertyKeys]] ( )</h1>
Expand Down Expand Up @@ -16143,7 +16045,7 @@ <h1>Runtime Semantics: ForIn/OfHeadEvaluation ( _TDZnames_, _expr_, _iterationKi
1. If _exprValue_.[[value]] is *null* or *undefined*, then
1. Return Completion{[[type]]: ~break~, [[value]]: ~empty~, [[target]]: ~empty~}.
1. Let _obj_ be ToObject(_exprValue_).
1. Return ? _obj_.[[Enumerate]]().
1. Return ? EnumerateObjectProperties(_obj_).
1. Else,
1. Assert: _iterationKind_ is ~iterate~.
1. Return ? GetIterator(_exprValue_).
Expand Down Expand Up @@ -16214,6 +16116,40 @@ <h1>Runtime Semantics: Evaluation</h1>
1. Return ? ResolveBinding(_bindingId_).
</emu-alg>
</emu-clause>

<!-- es6num="9.1.11" -->
<emu-clause id="sec-enumerate-object-properties" aoid="EnumerateObjectProperties">
<h1>EnumerateObjectProperties (_O_)</h1>
<p>When the abstract operation EnumerateObjectProperties is called with argument _O_, the following steps are taken:</p>
<emu-alg>
1. Assert: Type(_O_) is Object.
1. Return an Iterator object (<emu-xref href="#sec-iterator-interface"></emu-xref>) whose `next` method iterates over all the String-valued keys of enumerable properties of _O_. The iterator object is never directly accessible to ECMAScript code. The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below.
</emu-alg>
<p>The iterator's `throw` and `return` methods are *null* and are never invoked. The iterator's `next` method processes object properties to determine whether the property key should be returned as an iterator value. Returned property keys do not include keys that are Symbols. Properties of the target object may be deleted during enumeration. A property that is deleted before it is processed by the iterator's `next` method is ignored. If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration. A property name will be returned by the iterator's `next` method at most once in any enumeration.</p>
<p>Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively; but a property of a prototype is not processed if it has the same name as a property that has already been processed by the iterator's `next` method. The values of [[Enumerable]] attributes are not considered when determining if a property of a prototype object has already been processed. The enumerable property names of prototype objects must be obtained by invoking EnumerateObjectProperties passing the prototype object as the argument. EnumerateObjectProperties must obtain the own property keys of the target object by calling its [[OwnPropertyKeys]] internal method. Property attributes of the target object must be obtained by calling its [[GetOwnProperty]] internal method.</p>
<emu-note>
<p>The following is an informative definition of an ECMAScript generator function that conforms to these rules:</p>
<pre><code class="javascript">
function* EnumerateObjectProperties(obj) {
let visited = new Set;
for (let key of Reflect.ownKeys(obj)) {
if (typeof key === "string") {
let desc = Reflect.getOwnPropertyDescriptor(obj, key);
if (desc) {
visited.add(key);
if (desc.enumerable) yield key;
}
}
}
let proto = Reflect.getPrototypeOf(obj)
if (proto === null) return;
for (let protoName of EnumerateObjectProperties(proto)) {
if (!visited.has(protoName)) yield protoName;
}
}
</code></pre>
</emu-note>
</emu-clause>
</emu-clause>
</emu-clause>

Expand Down Expand Up @@ -35153,16 +35089,6 @@ <h1>Reflect.deleteProperty ( _target_, _propertyKey_ )</h1>
</emu-alg>
</emu-clause>

<!-- es6num="26.1.5" -->
<emu-clause id="sec-reflect.enumerate">
<h1>Reflect.enumerate ( _target_ )</h1>
<p>When the `enumerate` function is called with argument _target_, the following steps are taken:</p>
<emu-alg>
1. If Type(_target_) is not Object, throw a *TypeError* exception.
1. Return ? _target_.[[Enumerate]]().
</emu-alg>
</emu-clause>

<!-- es6num="26.1.6" -->
<emu-clause id="sec-reflect.get">
<h1>Reflect.get ( _target_, _propertyKey_ [ , _receiver_ ])</h1>
Expand Down Expand Up @@ -35346,8 +35272,9 @@ <h1>[ @@iterator ] ( )</h1>
<p>When the @@iterator method is called with no arguments, the following steps are taken:</p>
<emu-alg>
1. Let _N_ be the *this* value.
1. If Type(_N_) is not Object, throw a *TypeError* exception.
1. Return ? _N_.[[Enumerate]]().
1. If _N_ is not a module namespace exotic object, throw a *TypeError* exception.
1. Let _exports_ be the value of _N_'s [[Exports]] internal slot.
1. Return ! CreateListIterator(_N_).
</emu-alg>
<p>The value of the `name` property of this function is `"[Symbol.iterator]"`.</p>
</emu-clause>
Expand Down

8 comments on commit d96e60a

@jdalton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bterlson
Is there a way to get something similar to Reflect.enumerate?
Specifically a way to key own and inherited enumerable keys of an object.

Lodash uses the Reflect.enumerate shim as a _.keysIn compat route for folks wanting less buggy for-in style iteration in older enviros.

@leobalter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the best solution, and it requires Symbol.iterator or Array.prototype.keys:

enumerate = function(obj) {
  // Reflect.ownKeys does not filter enumerable keys
  return Object.keys(obj)[Symbol.iterator](); // or Object.keys(obj).keys();
};

@jdalton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leobalter

Not the best solution, and it requires Symbol.iterator or Array.prototype.keys

That's what I'm going for. I'm looking for something like:

function Foo() { this.a = 1 }
Foo.prototype.b = 2;

Object.keysIn(new Foo);
// => ['a', 'b']

I was able to get their by way of Reflect.enumerate.

@leobalter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the enumerate example on ES2015 seems the most precise approach.

looks like an opportunity to bring in Object.enumerate (or even the keysIn) to esdiscuss (as Reflect wouldn't be appropriated without [[Enumerate]]).

Seems like it's already introduced there and it's a good idea: https://esdiscuss.org/topic/property-ordering-of-enumerate-getownpropertynames

@ljharb
Copy link
Member

@ljharb ljharb commented on d96e60a Feb 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdalton I asked this in the meeting, and was told function* enumerate(obj) { for (const key in obj) { yield [key, obj[key]]; } } would be sufficient.

@jdalton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not shimmable (right?). So they took something away without a proper replacement. 😧

@ljharb
Copy link
Member

@ljharb ljharb commented on d96e60a Feb 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, it's not shimmable without materializing an array, and then returning an iterator off that array. Something like function enumerate(obj) { var entries = []; for (var key in obj) { entries.push([key, obj[key]]); } return entries.values(); } would work tho.

@jdalton
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb

Something like function enumerate(obj) { ... } would work tho.

Work how? What built-in is that anchored to? Which es-shim would it fall under?

I'm aware of how to create a for-in loop y'all. I want the compat path back that you nixed. That's what I want. I want to be able to point to an API that can be shimmed for folks that want better object iteration in older environments so that I can use it to power things like _.keysIn.

Please sign in to comment.