Question about [[Enumerate]] and property decision timing

# Yusuke SUZUKI (4 years ago)

Recently, we implemented Reflect.enumerate in WebKit JSC. At that time, the question is raised "When are the enumerated keys determined?"1

For example,

var object = ...; var iterator = Reflect.enumerate(object); object.newKey = "hello"; // At that time, iterator.next() is not called yet. for (var key of iterator) { print(key); // Here, "newKey" should be produced or not? }

Seeing the spec, I think it's a little bit ambiguous. (correct?) In the current WebKit implementation, keys are cached when the iterator is created.

Does this violate the spec behavior, "The iterator’s next method processes object properties to determine whether the property key should be returned as an iterator value. "?

Or, is it implementation dependent because "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."?

And is the creation of the iterator by calling [[Enumerate]] included in "active enumeration"?

Best , Yusuke Suzuki

# Jordan Harband (4 years ago)

I interpreted this spec text (and the included generator code example) to mean that the first "next" is the thing that determines the keys to be enumerated - since the generator function body (including the Reflect.ownKeys call) is not executed until that point.

The question was also raised on that linked bug, why does the spec leave behavior undefined for keys added during enumeration? It seems like the spec should either dictate that added keys are not enumerated, or, that they are always enumerated.

# Allen Wirfs-Brock (4 years ago)

On Aug 9, 2015, at 2:34 PM, Yusuke SUZUKI wrote:

Hi forks,

Recently, we implemented Reflect.enumerate in WebKit JSC. At that time, the question is raised "When are the enumerated keys determined?"1

For example,

var object = ...; var iterator = Reflect.enumerate(object); object.newKey = "hello"; // At that time, iterator.next() is not called yet. for (var key of iterator) { print(key); // Here, "newKey" should be produced or not? }

Seeing the spec, I think it's a little bit ambiguous. (correct?)

The ES6 spec. text for ordinary object [[Enumerate]] 2 was directly derived from the ES5.1 spec. text for the for-in statement 3. The ES5.1 spec. text is a minor elaboration upon the corresponding text in the E3 spec. In ES3 and ES5 many details of for-in processing were left unspecified (ie, implementation dependent) because that reflected the reality of web browsers and it did not seem feasible at that time to force browsers to change their for-in implementations.

ES6 moved part of the for-in enumeration into the [[Enumerate]] internal method and and added some additional semantic requirements. But it also did not change the policy established in the previous editions of not fully specifying the semantics. This decisions could certainly be revisited for future editions. My sense is that a fully specified for-in/[[Enumerate]] would have a better chance of finding acceptance today, than it would have in the past.

In the current WebKit implementation, keys are cached when the iterator is created.

Does this violate the spec behavior, "The iterator’s next method processes object properties to determine whether the property key should be returned as an iterator value. "?

This sounds like it is a valid implementation. The quoted sentence was not intended to imply that nothing happens until the first next method call.

Or, is it implementation dependent because "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."?

And is the creation of the iterator by calling [[Enumerate]] included in "active enumeration"?

The "active enumeration" was intended to start with the the call to [[Enumerate]].

Best , Yusuke Suzuki

Allen

# Allen Wirfs-Brock (4 years ago)

On Aug 10, 2015, at 1:35 AM, Jordan Harband wrote:

I interpreted this spec text (and the included generator code example) to mean that the first "next" is the thing that determines the keys to be enumerated - since the generator function body (including the Reflect.ownKeys call) is not executed until that point.

The generator-based definition was intended to only be an informative example of a conforming enumeration algorithm. Anything it does that goes beyond requirements stated in the normative prose should not be considered a normative requirement. The deferral of all setup processing to the first next call is simply an artifact of the how generators works.

The question was also raised on that linked bug, why does the spec leave behavior undefined for keys added during enumeration? It seems like the spec should either dictate that added keys are not enumerated, or, that they are always enumerated.

See my previous reply. When decisions about for-in were made for ES6, TC39 was not yet ready to try to mandate a normative enumeration order or other historically unspecified for-in semantic details. This could be reconsider in a future edition.

# Yusuke SUZUKI (4 years ago)

Thank you so much.

On Mon, Aug 10, 2015 at 8:18 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>

wrote:

On Aug 9, 2015, at 2:34 PM, Yusuke SUZUKI wrote:

Hi forks,

Recently, we implemented Reflect.enumerate in WebKit JSC. At that time, the question is raised "When are the enumerated keys determined?"[1]

For example,

var object = ...; var iterator = Reflect.enumerate(object); object.newKey = "hello"; // At that time, iterator.next() is not called yet. for (var key of iterator) { print(key); // Here, "newKey" should be produced or not? }

Seeing the spec, I think it's a little bit ambiguous. (correct?)

The ES6 spec. text for ordinary object [[Enumerate]] [2] was directly derived from the ES5.1 spec. text for the for-in statement [3]. The ES5.1 spec. text is a minor elaboration upon the corresponding text in the E3 spec. In ES3 and ES5 many details of for-in processing were left unspecified (ie, implementation dependent) because that reflected the reality of web browsers and it did not seem feasible at that time to force browsers to change their for-in implementations.

ES6 moved part of the for-in enumeration into the [[Enumerate]] internal method and and added some additional semantic requirements. But it also did not change the policy established in the previous editions of not fully specifying the semantics. This decisions could certainly be revisited for future editions. My sense is that a fully specified for-in/[[Enumerate]] would have a better chance of finding acceptance today, than it would have in the past.

Make sense. Now, almost all engines have the similar JS object structure (elements and properties, managed by Map/Shape/Structure). So I think making consensus is (relatively) easier than before.

And personally, I think if we can clearly state what the "enumeration" is, the description of the current ES6 spec becomes clear (without hurting the performance).

In the current WebKit implementation, keys are cached when the iterator is created.

Does this violate the spec behavior, "The iterator’s next method processes object properties to determine whether the property key should be returned as an iterator value. "?

This sounds like it is a valid implementation. The quoted sentence was not intended to imply that nothing happens until the first next method call.

Make sense.

Or, is it implementation dependent because "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."?

And is the creation of the iterator by calling [[Enumerate]] included in "active enumeration"?

The "active enumeration" was intended to start with the the call to [[Enumerate]].

Make sense, so the "enumeration" includes [[Enumerate]] call. I agree with this because the for-in statement actually contains the sequence "calling the [[Enumerate]]". If the "active enumeration" contains the [[Enumerate]] call, the spec's description looks clear to me.

  1. The "enumeration" contains "calling the [[Enumerate]]".
  2. "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.".
  3. So, the keys added after creating the iterator by calling [[Enumerate]] may or may not be produced, implementation dependent.