Last Comment Bug 687042 - Lazy property creation on global object makes getOwnPropertyNames inconsistent
: Lazy property creation on global object makes getOwnPropertyNames inconsistent
Status: RESOLVED DUPLICATE of bug 807222
:
Product: Core
Classification: Components
Component: DOM (show other bugs)
: unspecified
: All All
: -- normal with 3 votes (vote)
: ---
Assigned To: Nobody; OK to take it and work on it
:
Mentors:
Depends on: ParisBindings
Blocks:
  Show dependency treegraph
 
Reported: 2011-09-16 04:09 PDT by David Bruant
Modified: 2013-08-20 03:00 PDT (History)
13 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments

Description David Bruant 2011-09-16 04:09:30 PDT
Run the following code in an environment where no <object> element is created (like about:blank):
-----
function allPropNames(obj){
  var o = obj;
  var keys = [];

  while(o !== null){
    keys = keys.concat(Object.getOwnPropertyNames(o));
    o = Object.getPrototypeOf(o);
  }

  return keys;
}

console.log(allPropNames(window).length); // something
HTMLObjectElement;
console.log(allPropNames(window).length); // something + 1 due to addition of "HTMLObjectElement"
-----
Of course, this is not related to <object> but could be reproduced with other elements.

Point is, Firefox lazily adds properties to the global object. This makes Object.getOwnPropertyNames (and certainly property enumeration methods) inconsistent. Seeing a property being created after a useless statement is a weird behavior.

I assume there are certainly very good reasons to lazily create the properties, I won't question this. However, would it be possible to create the properties all at once whenever Object.getOwnPropertyNames (and other enumeration methods) is called on an object? Chances are that the programmer wants to have the full list in order to access each property afterward as I do for instance in http://davidbruant.github.com/ObjectViz/

For the record, Chrome is consistent regarding Object.getOwnPropertyNames, Opera (12 with ES5 support) has the same behavior than Firefox (HTMLAnchorElement instead of HTMLObjectElement). I'm on Linux so I can't test on IE9/10 and Safari 5.1 for now.
Comment 1 David Bruant 2011-09-16 04:13:27 PDT
Also, from the pure ES5.1 perspective, nothing forbids lazy property creation on the global object. The inconsistency between what could be expected from Object.getOwnPropertyNames and actual results are only what i would like to fix.

Finally, maybe that creating all properties at once would be too much. I assume that somewhere in the source code can be found the full list of lazily created properties. This appended to the list of already created properties could be returned by Object.getOwnPropertyNames.
Comment 2 Brendan Eich [:brendan] 2011-09-20 13:37:04 PDT
Cc'ing some people. Is this something WebIDL codifies, and we are out of conformance?

/be
Comment 3 David Flanagan [:djf] 2011-09-20 13:49:55 PDT
WebIDL §4.5 says: 

"For every interface that is not declared with the [NoInterfaceObject] extended attribute, a corresponding property MUST exist on the interface’s relevant namespace object. The name of the property is the identifier of the interface, and its value is an object called the interface object. The property has the attributes { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }."

So properties like HTMLObjectElement are required to exist.  Note that they are non-enumerable, so this lazy initialization was not observable until Object.getOwnPropertyNames() was added in ES5.

In dom.js properties like these are lazy in a different way.  They always exist, but are initially defined as a getter.  When invoked, that getter initializes the required object, redefines the property as a data property and then returns the object.  Perhaps Gecko could do something like that.
Comment 4 Peter Van der Beken [:peterv] - away till Aug 1st 2011-09-20 14:09:10 PDT
We could fix this in the Enumerate hook for nsWindowSH.

(In reply to David Bruant from comment #1)
> I assume that somewhere in the source code can be found the full list of
> lazily created properties. This appended to the list of already created
> properties could be returned by Object.getOwnPropertyNames.

It's not that simple, there are multiple lists and they are extensible (eg by addons). It'll be a little tricky to do this but it should be possible.
Comment 5 David Bruant 2011-09-20 15:32:45 PDT
(In reply to David Flanagan from comment #3)
> WebIDL §4.5 says: 
> 
> (...) The name of the property is the identifier of the
> interface, and its value is an object called the interface object. The
> property has the attributes { [[Writable]]: true, [[Enumerable]]: false,
> [[Configurable]]: true }."
> 
> (...)
>
> In dom.js properties like these are lazy in a different way.  They always
> exist, but are initially defined as a getter.  When invoked, that getter
> initializes the required object, redefines the property as a data property
> and then returns the object.  Perhaps Gecko could do something like that.
Apparently, WebIDL defines a value for the "writable" property, consequently implying that properties have to be data property. By defining a getter for the laziness, the property becomes an accessor property and by being so, non-compliant to WebIDL.

So either dom.js or WebIDL should change for that regard. Do we know why WebIDL imposes a data descriptor?
Comment 6 David Flanagan [:djf] 2011-09-20 21:51:00 PDT
(In reply to David Bruant from comment #5)
> Apparently, WebIDL defines a value for the "writable" property, consequently
> implying that properties have to be data property. By defining a getter for
> the laziness, the property becomes an accessor property and by being so,
> non-compliant to WebIDL.
> 
> So either dom.js or WebIDL should change for that regard. Do we know why
> WebIDL imposes a data descriptor?

You're right.  Thanks for pointing that out.  I've asked on public-script-coord if we can relax the WebIDL requirement.  It seems like quite an implementation burden to require all IDL interfaces to be defined ahead of time, especially when HTML defines so many infrequently used ones.
Comment 7 David Bruant 2011-09-21 02:15:36 PDT
(In reply to David Flanagan from comment #6)
> (In reply to David Bruant from comment #5)
> > Apparently, WebIDL defines a value for the "writable" property, consequently
> > implying that properties have to be data property. By defining a getter for
> > the laziness, the property becomes an accessor property and by being so,
> > non-compliant to WebIDL.
> > 
> > So either dom.js or WebIDL should change for that regard. Do we know why
> > WebIDL imposes a data descriptor?
> 
> You're right.  Thanks for pointing that out.  I've asked on
> public-script-coord if we can relax the WebIDL requirement.  It seems like
> quite an implementation burden to require all IDL interfaces to be defined
> ahead of time, especially when HTML defines so many infrequently used ones.
It is not that much of an implementation burden in C++ since properties can both be lazily constructed and described as data property.
But this option does not exists for a JS implementation (unless the global object was a proxy which we could have access to the handler of)
Comment 8 Jason Orendorff [:jorendorff] 2011-09-30 13:52:42 PDT
(In reply to David Flanagan from comment #6)
> (In reply to David Bruant from comment #5)
> > Apparently, WebIDL defines a value for the "writable" property, consequently
> > implying that properties have to be data property. By defining a getter for
> > the laziness, the property becomes an accessor property and by being so,
> > non-compliant to WebIDL.
> > 
> > So either dom.js or WebIDL should change for that regard. Do we know why
> > WebIDL imposes a data descriptor?
> 
> You're right.  Thanks for pointing that out.  I've asked on
> public-script-coord if we can relax the WebIDL requirement.  It seems like
> quite an implementation burden to require all IDL interfaces to be defined
> ahead of time, especially when HTML defines so many infrequently used ones.

This conversation is a bit misguided, I think. The global object only needs to behave as if the properties are created eagerly. We can and do create them lazily. In fact, the bug here is that the optimization isn't quite as transparent as it should be.

But I think the fix is to make the optimization correct, not to change the spec!
Comment 9 David Flanagan [:djf] 2011-09-30 14:02:20 PDT
(In reply to Jason Orendorff [:jorendorff] from comment #8)
> This conversation is a bit misguided, I think. The global object only needs
> to behave as if the properties are created eagerly. We can and do create
> them lazily. In fact, the bug here is that the optimization isn't quite as
> transparent as it should be.
> 
> But I think the fix is to make the optimization correct, not to change the
> spec!

You're right; David B and I were off on a tangent.  Please ignore the last paragraph of comment 3 and also comments 5, 6 and 7.
Comment 10 Cameron McCormack (:heycam) 2011-10-15 16:49:31 PDT
Yes, just to chime in a bit late: Web IDL does require those properties to exist from the start, but of course an implementation could lazily create them when resolving properties on window or calling Object.getOwnPropertyNames.

The window object already needs to be a proxy in a JS implementation because it is crazy.  In dom.js the data properties for interface objects could easily be exposed lazily.
Comment 11 David Bruant 2012-12-28 01:07:54 PST
I see this problem is solved in Aurora 19.
Not sure what caused the change, but that's cool :-)
Comment 12 Masatoshi Kimura [:emk] 2013-08-20 03:00:43 PDT

*** This bug has been marked as a duplicate of bug 807222 ***

Note You need to log in before you can comment on or make changes to this bug.