Closed Bug 687042 Opened 13 years ago Closed 12 years ago

Lazy property creation on global object makes getOwnPropertyNames inconsistent

Categories

(Core :: DOM: Core & HTML, defect)

defect
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 807222

People

(Reporter: bruant.d, Unassigned)

References

(Depends on 1 open bug)

Details

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.
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.
Cc'ing some people. Is this something WebIDL codifies, and we are out of conformance?

/be
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.
OS: Linux → All
Hardware: x86_64 → All
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.
(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?
(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.
(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)
(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!
(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.
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.
Depends on: ParisBindings
I see this problem is solved in Aurora 19.
Not sure what caused the change, but that's cool :-)
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → WORKSFORME
Resolution: WORKSFORME → DUPLICATE
Summary: Lazy property creation on global object makes getOwnPropertyDescriptor inconsistent → Lazy property creation on global object makes getOwnPropertyNames inconsistent
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.