Phabricator will be unavailable due to database maintenance from 14:00 UTC until 18:00 UTC on Saturday, October 13, 2018.
Bugzilla will remain up during this time. All users have been logged out of Bugzilla

For-in can hit deleted keys

UNCONFIRMED
Unassigned

Status

()

UNCONFIRMED
2 months ago
a month ago

People

(Reporter: bakkot, Unassigned)

Tracking

Trunk
Points:
---

Firefox Tracking Flags

(Not tracked)

Details

(Reporter)

Description

2 months ago
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36

Steps to reproduce:

// Run the following
if (typeof console === 'undefined') console = { log: print };

let a = {
  x: 0,
  y: 1,
};

let pa = new Proxy(a, {});

for (let key in pa) {
  console.log(key);
  delete a.y;
  if (key === 'y') {
    console.log('reached');
  }
}



Actual results:

Prints `x, y, reached`.


Expected results:

Prints only `x`. It should not reach the step with `y`, since `y` has been deleted before it gets to that iteration of the loop. The spec is pretty loose here, but does not allow this behavior: in #sec-enumerate-object-properties, it says "A property that is deleted before it is processed by the iterator's next method is ignored." No other engine except moddable's embedded XS engine has this behavior.

This doesn't happen if using `a` instead of the proxy `pa`, even though `pa` is a transparent wrapper around `a`.

If you add a trap for `getOwnPropertyDescriptor`, you can observe that `getOwnPropertyDescriptor` is being called for each property before enumerating executing the body of the loop at all, presumably to determine presence and enumerability. But because properties may be deleted before their iteration of the loop is reached, you need to check for presence immediately before the loop step covering them, not ahead of time. (It's not clear whether it's legal to check enumerability ahead of time.)

See also (and please comment on) this open spec bug about more precisely specifying the behavior of for-in, which prompted the investigation which lead me to discovering this: https://github.com/tc39/ecma262/issues/1281
(Reporter)

Updated

2 months ago
Summary: For-in over a proxy prints hits keys → For-in over a proxy prints hits deleted keys
(Reporter)

Comment 1

a month ago
Actually, this can happen even with no proxies:


```
if (typeof console === 'undefined') console = { log: print };


let a = { x: 0 };

let b = { y: 1 };

Object.setPrototypeOf(a, b);

for (let key in a) {
  console.log(key);
  delete b.y;
  if (key === 'y') {
    console.log('reached');
  }
}
```

This prints `x, y, reached`. This is forbidden per the spec text cited above, and no other engine has this behavior.
(Reporter)

Updated

a month ago
Summary: For-in over a proxy prints hits deleted keys → For-in can hit deleted keys
(Reporter)

Comment 2

a month ago
Ah, this is maybe a duplicate of https://bugzilla.mozilla.org/show_bug.cgi?id=862771.
You need to log in before you can comment on or make changes to this bug.