Bug 14547 - typeof document.body.childNodes=="function" ("object" expected)
Summary: typeof document.body.childNodes=="function" ("object" expected)
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: 523.x (Safari 3)
Hardware: Mac (PowerPC) OS X 10.4
: P2 Normal
Assignee: Nobody
URL: javascript:alert(typeof document.body...
Keywords: HasReduction, InRadar
: 10647 15089 33716 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-07-07 04:44 PDT by Doeke Zanstra
Modified: 2022-06-14 17:44 PDT (History)
17 users (show)

See Also:


Attachments
approach (7.84 KB, patch)
2010-10-08 21:28 PDT, Adam Barth
no flags Details | Formatted Diff | Diff
Patch (13.43 KB, patch)
2010-10-08 22:13 PDT, Adam Barth
ap: review-
ap: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Doeke Zanstra 2007-07-07 04:44:21 PDT
Javascript's type of an NodeList is "function", where "object" is expected.

testcase: typeof document.body.childNodes
expected result: "object"
actual result: "function"

This also goes for the XML DOM, though I don't have a testcase. I encountered the same behaviour with XMLHttpRequest.responseXML

Rationale: Firefox 2, IE6 and Opera9 all report "object"
Comment 1 David Kilzer (:ddkilzer) 2007-07-07 13:45:10 PDT
Confirmed with a local debug build of WebKit r24089 on Safari 3.0 (522.12) using Mac OS X 10.4.10 (8R218).

Comment 2 mitz 2007-08-27 02:44:42 PDT
*** Bug 15089 has been marked as a duplicate of this bug. ***
Comment 3 Mark Rowe (bdash) 2007-08-27 02:56:10 PDT
<rdar://problem/5439389>
Comment 4 John Sullivan 2007-08-30 14:49:25 PDT
Do we know of any real-world sites that this affects? Do we know if it's a regression from earlier WebKit behavior?
Comment 5 Mark Rowe (bdash) 2007-08-30 15:02:01 PDT
This affects the jQuery JavaScript library which is widly used, though they currently have a workaround in place.
Comment 6 David Kilzer (:ddkilzer) 2007-08-30 21:31:24 PDT
(In reply to comment #4)
> Do we know of any real-world sites that this affects? Do we know if it's a
> regression from earlier WebKit behavior?

It is not a regression since Safari 2.0.4 (419.3) with original WebKit also returns "function" for the test URL.

Comment 7 Sam Weinig 2007-08-30 23:32:19 PDT
The reason NodeLists report there type as function is that they implement call() and the implementation of typeof checks to see if implementsCall() returns true (which it does).   (see http://trac.webkit.org/projects/webkit/browser/trunk/JavaScriptCore/kjs/nodes.cpp#L977).  
Comment 8 Mark Rowe (bdash) 2007-08-30 23:44:58 PDT
Why does it implement call?  document.body.childNodes() throws an exception in Firefox (Error: document.body.childNodes is not a function) while we return undefined.
Comment 9 Mark Rowe (bdash) 2007-08-30 23:51:30 PDT
Sam informs me that it does so because Internet Explorer does so.  document.body.childNodes(1) is the same as document.body.childNodes[1].  Ugh.
Comment 10 Sam Weinig 2007-08-31 00:33:32 PDT
This also is true for HTMLCollections.  For instance, typeof document.images == "function". 
Comment 11 Garrett Smith 2007-10-10 17:23:34 PDT
Objects that do not support call or construct are not functions.

typeof window.constructor == "function"

Typeof should return something other than function.

This makes it harder to typecheck.

Comment 12 Garrett Smith 2007-10-10 17:29:56 PDT
A workaround:
var isFunction = function( fn ) {
  return typeof fn == "function" && 
    typeof fn.hasOwnProperty == "function" && fn.hasOwnProperty('prototype');
};

More on this:
http://dhtmlkitchen.com/?category=/JavaScript/&date=2007/10/10/&entry=How-to-Tell-if-an-Object-is-a-Function#safari-anwser

Comment 13 Garrett Smith 2007-10-10 20:29:51 PDT
The error I get is:

"TypeError: Value undefined (result of expression document.body.childNodes.call) is not object.
undefined

http://trac.webkit.org/projects/webkit/browser/trunk/JavaScriptCore/kjs/nodes.cpp#L636
636 	  if (!v->isObject()) {
637 	    return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, expr.get());
638 	  }


The jQuery patch will return incorrect results under several obvious conditions that I have posted on my blog entry.
Comment 14 Garrett Smith 2007-10-13 13:34:33 PDT
Updated test case. Try & break it.
http://dhtmlkitchen.com/jstest/isFunctionTest.html


I noticed several problems:

1) constructor property missing:

"constructor" in document.getElementsByTagName("div"); // false

2)
toString reflects the [[class]] as NodeList:
document.getElementsByTagName("div").toString();// [object NodeList]
document.getElementsByTagName("div").__proto__ === null; // true

"NodeList" in window; // false

3)
prototype property is missing from built-in functions.
Function.prototype.prototype // undefined, should be an object.
parseInt.prototype; // undefined, should be an object.


Comment 15 Richard Cornford 2007-10-13 18:41:09 PDT
(In reply to comment #14)

Updated test case. Try & break it.
http://dhtmlkitchen.com/jstest/isFunctionTest.html

> I noticed several problems:

> 1) constructor property missing:

> "constructor" in document.getElementsByTagName("div"); // false

In ECMAScript terms the object retuned from a call to - getElementsByTagName - is a host object, and host objects are not required to have a - constructor - property. The W3C DOM specification for the NodeList interface also does not define a - constructor - property. So there is no technical justification for anyone to expect the object returned from a call to - getElementsByTagName - to have a - constructor - property. Thus the absence of such a property cannot be regarded as in any way erroneous. Instead any error would be in the thinking of anyone who expected/demanded it.

> 2)
> toString reflects the [[class]] as NodeList:

Why would that be a problem? There is nothing that says it should not.

> document.getElementsByTagName("div").toString();// [object NodeList]

Why would that be a problem? There is nothing that says it should not.

> document.getElementsByTagName("div").__proto__ === null; // true

Why would that be a problem? There is nothing that says it should not.

> "NodeList" in window; // false

Why would that be a problem? There is nothing that says it should not.

> 3)
> prototype property is missing from built-in functions.

Not all of them, and ECMA 262 3rd Ed, Section 15 paragraph 9 says:-

"None of the built-in functions described in this section shall implement the internal [[Construct]] method unless otherwise specified in the description of a particular function. None of the built-in functions described in this section shall initially have a prototype property unless otherwise specified in the description of a particular function."

- so where not "otherwise specified" no prototype property should be expected.

> Function.prototype.prototype // undefined, should be an object.

That actually would be a bug, but an utterly insignificant one as nobody is likely to be interested in Function.prototype.prototype.

> parseInt.prototype; // undefined, should be an object.

No it should not as for - parseInt - there is nothing to 'otherwise specify' and so section 15 paragraph 9 applies.

Generally this entire 'bug report' is bogus. The W3C specifies NodeList, HTMLCollection, etc., as interfaces. For ECMAScript the implications of this are not that there should be a NodeList class in the environment but instead that when you get an object that "is a NodeList" what you actually get is an object that implements the NodeList interface (nothing more than an object that has all of the methods and properties defined in the ECMAScript binding for the interface), and that can be any object so long as it implements the interface correctly. In ECMAScript/javascript functions are objects, so there is no reason for the object implementing the NodeList interface not being a function object. That may not be required but it certainly is not excluded, or particularly unexpected by anyone with any experience of scripting web browsers. Historically the typeof operator would return 'function' for all objects implanting NodeList, NamedNodeMap, HTMLCollection and even all pre-DOM collections in Mac IE, Konqueror, Opera versions up 7, IceBrowser versions up to 5 and probably at least some others. And in all cases these object were callable.
Comment 16 Garrett Smith 2007-10-13 20:09:25 PDT
(In reply to comment #15)
> (In reply to comment #14)
>
> Updated test case. Try & break it.
> http://dhtmlkitchen.com/jstest/isFunctionTest.html
>
> > I noticed several problems:
>
> > 1) constructor property missing:
>
> > "constructor" in document.getElementsByTagName("div"); // false
>
> In ECMAScript terms the object retuned from a call to - getElementsByTagName -
> is a host object, and host objects are not required to have a - constructor -
> property.
The constructor property would be on the object's [[Prototype]]


The W3C DOM specification for the NodeList interface also does not
> define a - constructor - property. So there is no technical justification for
> anyone to expect the object returned from a call to - getElementsByTagName - to
> have a - constructor - property. Thus the absence of such a property cannot be
> regarded as in any way erroneous. Instead any error would be in the thinking of
> anyone who expected/demanded it.
>

A NodeList is an object. Error in thinking, huh?


> > 2)
> > toString reflects the [[class]] as NodeList:
>
> Why would that be a problem? There is nothing that says it should not.
>
The fact that constructor is null.

[[class]] comes from the constructor.

So it sounds like you're trying to say that an object's [[class]] doesn't imply that an object's has the same constructor. If that's what you're trying to say, that is technically correct. The class doesn't imply the constructor, it's the other way around.


Ab object (nodeList in this case) COULD get its [[class]]
from an internal private function, where private is implementation
defined. 



If the class property is NodeList, the object should be a NodeList.


> > document.getElementsByTagName("div").toString();// [object NodeList]
>
> Why would that be a problem? There is nothing that says it should not.
>
> > document.getElementsByTagName("div").__proto__ === null; // true
>
> Why would that be a problem? There is nothing that says it should not.
>

javascript:alert(document.body.childNodes[1].toString ===
Object.prototype.toString)

We got Object.prototype.toString and all the other methods off Object.prototype. 

What happened to the constructor? 

The [[Prototype]] was not set, but all the methods are there.

To me, that seems strange.


> > "NodeList" in window; // false
>
> Why would that be a problem? There is nothing that says it should not.
>
That's true. 

> > 3)
> > prototype property is missing from built-in functions.
>
> Not all of them, and ECMA 262 3rd Ed, Section 15 paragraph 9 says:-
>
> "None of the built-in functions described in this section shall implement the
> internal [[Construct]] method unless otherwise specified in the description of
> a particular function. None of the built-in functions described in this section
> shall initially have a prototype property unless otherwise specified in the
> description of a particular function."
>
You're right.


>
> Generally this entire 'bug report' is bogus.

THAT is not true. This bug is valid.

document.body.childNodes IS NOT a function.


The W3C specifies NodeList,
> HTMLCollection, etc., as interfaces. For ECMAScript the implications of this
> are not that there should be a NodeList class in the environment but instead
> that when you get an object that "is a NodeList" what you actually get is an
> object that implements the NodeList interface (nothing more than an object that
> has all of the methods and properties defined in the ECMAScript binding for the
> interface), and that can be any object so long as it implements the interface
> correctly. In ECMAScript/javascript functions are objects, so there is no
> reason for the object implementing the NodeList interface not being a function
> object. That may not be required but it certainly is not excluded, or
> particularly unexpected by anyone with any experience of scripting web
> browsers. Historically the typeof operator would return 'function' for all
> objects implanting NodeList, NamedNodeMap, HTMLCollection and even all pre-DOM
> collections in Mac IE, Konqueror, Opera versions up 7, IceBrowser versions up
> to 5 and probably at least some others. And in all cases these object were
> callable.
>

Do you meant objects implementing NodeList? 



document.body.childNodes does not implement NodeList. It's
constructor, however, does. 

document.body.childNodes is an instance of a NodeList.

Thanks for bringing relevant javascript knowledge here to straighten out the prototype property. You seem to have the manual clearly implanted in your brain.

>
Comment 17 Maciej Stachowiak 2007-10-13 21:17:12 PDT
Please, ladies & gentlemen, let's try to remain courteous in these discussions.

It's probably true that many of the issues noted by the test are not required by the relevant specs. However, we may have to implement some of those things for compatibility. The best thing to do with this bug report would be to split off a separate bug for each individual issue, rather than making this a master bug for dozens of DOM binding quirks. For each individual issue, it would be great to know:

1) Do the relevant specs require this behavior? (Probably the answer is no in most cases, since the ECMAScript spec has pretty loose rules for host objects, and the DOM specs are not very detailed in specifying how JS bindings should work.)

2) Do the relevant specs forbid this behavior? We're somewhat hesitant to outright violate specs, even in cases where there might be some effect on compatibility.

3) Is the behavior required for compatibility, or just a minor corner case?

In the case of the original function vs. object thing for NodeLists, it's clear that neither the ECMA-262 spec or the DOM spec require typeof to return "object". It's also clear that this is not forbidden - the ECMAScript spec allows host objects to return anything whatsoever from "typeof". And it's clear that returning "object" matters to web compatibility. Therefore we should give a typeof result of object. This may require objects to gain an isFunction() method in addition to implementsCall(), which can default to calling implementsCall() but which is overridable.

I'm not sure what the answer is for the many other issues mentioned in this bug. They deserve separate bug reports and someone should do the analogous research.
Comment 18 Garrett Smith 2007-10-13 22:50:29 PDT
(In reply to comment #17)
> Please, ladies & gentlemen, let's try to remain courteous in these discussions.
> 
Point taken. My post sounded sarcastic. Richard did correctly correct me on Function.prototype.prototype point, clearing up confusion that I caused. 


> It's probably true that many of the issues noted by the test are not required
> by the relevant specs. However, we may have to implement some of those things
> for compatibility. The best thing to do with this bug report would be to split
> off a separate bug for each individual issue, rather than making this a master
> bug for dozens of DOM binding quirks. For each individual issue, it would be
> great to know:
> 
> 1) Do the relevant specs require this behavior? (Probably the answer is no in
> most cases, since the ECMAScript spec has pretty loose rules for host objects,
> and the DOM specs are not very detailed in specifying how JS bindings should
> work.)
> 
document.images is not a function, it is a NodeList instance.  

> 2) Do the relevant specs forbid this behavior? We're somewhat hesitant to
> outright violate specs, even in cases where there might be some effect on
> compatibility.
> 
ECMA and W3 do not forbid any custom behavior, AFAIK.
 
> 3) Is the behavior required for compatibility, or just a minor corner case?
> 

Minor case, AFAIK. most uses of nodelist/collection won't try to invoke them with arguments. Please correct this if it is not right.

> In the case of the original function vs. object thing for NodeLists, it's clear
> that neither the ECMA-262 spec or the DOM spec require typeof to return
> "object". It's also clear that this is not forbidden - the ECMAScript spec
> allows host objects to return anything whatsoever from "typeof". And it's clear
> that returning "object" matters to web compatibility. Therefore we should give
> a typeof result of object. This may require objects to gain an isFunction()
> method in addition to implementsCall(), which can default to calling
> implementsCall() but which is overridable.
> 
I don't think it's a good idea to implement conflicting interfaces.

typeof it == "function" should mean that I can use the call method or any other methods on Function.prototype. If it's a function, I expect it to be one, completely.

Function.prototype.constructor is Function.

What is the constructor of such an object?

If typeof it == "function", I expect that. 

How do you resolve document.images.length?
1) function.length
2) collection length

Implementing Function for a Collection/NodeList conflicts with NodeList interface.
Partially implementing Function makes for a confusing interface. 

It is not desirable to implement Function for document.images. Collection, not Function.

Can this feature removed? Is it being heavily relied on? 


> I'm not sure what the answer is for the many other issues mentioned in this
> bug. They deserve separate bug reports and someone should do the analogous
> research.
> 

Comment 19 Mark Rowe (bdash) 2007-10-13 23:04:45 PDT
document.images behaves as both a NodeList and a function for compatibility with Internet Explorer.  It is unlikely that this dual behaviour could be removed without causing compatibility issues with real-world websites.  See <http://msdn2.microsoft.com/en-us/library/ms537461.aspx> for Microsoft's documentation of their bizarre behaviour of document.images.
Comment 20 Richard Cornford 2007-10-14 10:48:29 PDT
> Please, ladies & gentlemen, let's try to remain courteous in these
> discussions.
> 
> It's probably true that many of the issues noted by the test are not
> required by the relevant specs. However, we may have to implement
> some of those things for compatibility.

By which you mean compatibility with IE, because, as I have mentioned, historically many browsers had collections that could be called and would report 'function' when tested with - typeof -, so the status quo is already computable with all of those.

A pragmatic acceptance of the real world would tend to indicate that compatibility with IE has a lot to be said for it.

> The best thing to do with
> this bug report would be to split off a separate bug for each
> individual issue, rather than making this a master bug for dozens
> of DOM binding quirks. For each individual issue, it would be great
> to know:
> 
> 1) Do the relevant specs require this behavior? (Probably the answer
> is no in most cases, since the ECMAScript spec has pretty loose
> rules for host objects, and the DOM specs are not very detailed in
> specifying how JS bindings should work.)

The relevant specs nether require that a 'collection' (taken as shorthand for NodeList, HTMLCollection, NamedNodeMap etc and anything outside DOM such as window.frames) be callable, nor do they preclude the possibility (at least in ECMAScript/javascript contexts (and Java itself is the limiting factor in the Java context, not the DOM bindings)).

These 'collections' are all host objects so ECMA 262 allows them to report anything they like from a - typeof - operation. Though historically almost no host objects have stepped outside of the limited set of - typeof - responses defined in ECMA 262 (the only notable exceptions being methods of ActiveX objects which often report 'unknown'). 

> 2) Do the relevant specs forbid this behavior?

Nothing described in this bug report is forbidden by any pertinent specification.

> We're somewhat
> hesitant to outright violate specs, even in cases where there
> might be some effect on compatibility.

That is not an issue here. All of:-

1. Callable collections that report 'object' with typeof.
2. Non-callable collections that report 'object' with typeof
3. Callable collections that report 'function' with typoeof
4. Non-callable collections that report 'function' with typeof

- are allowed by the pertinent specifications. Though 1 and 4 are of questionable rationality. 4 would be absolutely stupid, but it is still worth noting that when the team creating Mac IE looked at what the Windows IE team had done even they perceived option 1 as wrong and chose option 3 in its place.
 
> 3) Is the behavior required for compatibility, or just a minor
> corner case?

Microsoft's own documentation is littered with JScript examples which call collections, to the exclusion of using property accessors for the task. Virtually anyone coming into browser scripting via IE only scripting and Microsoft documentation will have been exposed to these bad examples.

The result will be that if you don't allow collections to be called probably about 50% of the sites that are "best viewed with IE" will be non-functional with Safari. The situation will be worse with "IE only" intranets and it may extend to .NET components.

So the question of whether the collections should be callable comes down to; do you care about that? And the pragmatic answer is probably 'yes'.
 
> In the case of the original function vs. object thing for NodeLists,
> it's clear that neither the ECMA-262 spec or the DOM spec require
> typeof to return "object". It's also clear that this is not
> forbidden - the ECMAScript spec allows host objects to return
> anything whatsoever from "typeof". And it's clear that returning
> "object" matters to web compatibility.

That last is not clear. If you are aiming for 'like for like' compatibility with Windows IE then the implications go well beyond the 'collections'. On Windows IE all of (or at least most of) the DOM methods also report "object" from - typeof -, as do other callable objects such as - alert -, - setTimeout - and - attachEvent -. And perversely the - item - method of 'collections' reports "string" from - typeof -, of all things (at least up to version 6. I have not checked to see if that insanity was carried through to version 7 yet). All of that is allowed, but it certainly is not a good idea and should not be reproduced lightly.

The issue here is not one of compatibility but rather one of expectations. From the first description: "Javascript's type of an NodeList is "function", where "object" is expected." But why is that expected when there is no standard that asserts that anything specific should be expected? Anyone who had any experience stretching back a couple of versions with Opera, or included Konqueror or Mac IE would have no such expectation as it would have been disappointed years ago.

It is an expectation that comes from looking at a couple of recent browsers and assuming that what they do should be definitive (despite the evidence that even recent browsers are a long way from being consistent).

The problem with pandering to such uniformed expectations is that while you can give in to the expectations of the first individual who presents them the next individual's expectations stand a very good chance of being different, and sooner or later there will be expectations that are incompatible with earlier ones.

Indeed the situation here stems from people having uniformed and incompatible expectations. The W3C DOM specifications gives a solid justification for an expected outcome from:-

document.images.item(1);

The ECMAScript bindings, by asserting that the bracket notation is to be mapped to the - item - method, justifies the same expected outcome for:-

document.image[1];

While there is nothing in the world to suggest that:-

document.images(1);

- should do anything but throw an exception, beyond Microsoft's documentation and the fact that it does not throw an exception in most actual web browsers but instead produces the same outcome as the previous two.

The only way out of that is to totally give in to creating a like-for-like imitation of one (and only one) of the browsers that are producing these expectations, and so in your case reproducing all of the features of IE, regardless of how perverse and irrational they may be (That would not be exclusive; there is no reason for not providing things that do not feature in IE at all in addition, and handling them in any why seen as desirable).

> Therefore we should give a
> typeof result of object. This may require objects to gain an
> isFunction() method in addition to implementsCall(), which can
> default to calling implementsCall() but which is overridable.

Just remember that you will have to generate a list of all of the object/function properties available in Safari and then test Windows IE to see precisely what it does return from - typeof - for its equivalents. Otherwise the problem is not solved, only moved a little more out of sight.
Comment 21 Alexey Proskuryakov 2008-03-19 00:20:45 PDT
*** Bug 10647 has been marked as a duplicate of this bug. ***
Comment 22 Ian 'Hixie' Hickson 2009-01-13 00:31:23 PST
HTML5 now requires HTMLCollection objects to be callable.
Comment 23 Eric Seidel (no email) 2009-08-06 21:26:12 PDT
It seems this should be a "relatively simple" fix.

We can use TypeInfo like we do for Strings which masquerade as undefined.

JSValue jsTypeStringForValue(CallFrame* callFrame, JSValue v)
is the function where this check is made:

    if (v.isObject()) {
        // Return "undefined" for objects that should be treated
        // as null when doing comparisons.
        if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined())
            return jsNontrivialString(callFrame, "undefined");
        CallData callData;
        if (asObject(v)->getCallData(callData) != CallTypeNone)
            return jsNontrivialString(callFrame, "function");
    }
    return jsNontrivialString(callFrame, "object");

we just add a callableObject() accessor to typeInfo or some such.  And we add a special property to the IDL files.  The idls already have a CustomCall for these cases.  We can change it to be UndetectableCustomCall or something.

Sam, Oliver?  Sound like a good plan to you?
Comment 24 Alexey Proskuryakov 2010-01-15 13:41:47 PST
*** Bug 33716 has been marked as a duplicate of this bug. ***
Comment 25 Adam Barth 2010-10-08 20:41:51 PDT
Sadly, there does not appear to be any space left in JSC::TypeInfo.
Comment 26 Adam Barth 2010-10-08 21:28:25 PDT
Created attachment 70337 [details]
approach
Comment 27 Adam Barth 2010-10-08 22:13:07 PDT
Created attachment 70341 [details]
Patch
Comment 28 Sam Weinig 2010-10-08 22:25:17 PDT
(In reply to comment #25)
> Sadly, there does not appear to be any space left in JSC::TypeInfo.

I am not sure that is strictly true. The way TypeInfo is used in Structure, there is quite a bit of padding after it, so expanding its size should ok, though it may require some JIT changes.
Comment 29 Adam Barth 2010-10-08 22:39:20 PDT
(In reply to comment #28)
> (In reply to comment #25)
> > Sadly, there does not appear to be any space left in JSC::TypeInfo.
> 
> I am not sure that is strictly true. The way TypeInfo is used in Structure, there is quite a bit of padding after it, so expanding its size should ok, though it may require some JIT changes.

Do you think that's better than the approach in this patch?  I tried changing the field from a char to a short, but then I ran into some ASSERTs about the inline capacity of objects.  Given that the typeof operator isn't called that often, it seemed better to add a virtual function call than to add a byte to the structure, but I'm not nearly an expert on the tradeoffs here.  (Part of the fun of working on these ancient bugs is to learn about this kind of thing.)
Comment 30 Garrett Smith 2010-10-08 23:20:15 PDT
(wincing at my previous comments)

ECMA 5 requires callable host object to result "function" when supplied to typeof operator. And so if document.images is remain callable then typeof document.images is required to result "function".

See also comment 20.
Comment 31 Alexey Proskuryakov 2010-10-09 00:49:45 PDT
Comment on attachment 70341 [details]
Patch

Eric, Adam, Sam - I don't understand why everyone is discussing how to fix this. Isn't the bug clearly INVALID at this point?

r-, because there is no explanation of why this is even being changed.
Comment 32 Adam Barth 2010-10-09 08:17:20 PDT
> Eric, Adam, Sam - I don't understand why everyone is discussing how to fix this. Isn't the bug clearly INVALID at this point?

4 of 5 browsers agree that the typeof these objects is "object".

> ECMA 5 requires callable host object to result "function" when supplied to typeof operator. And so if document.images is remain callable then typeof document.images is required to result "function".

Then the spec is wrong and should be fixed.  The folks in the working group must not have considered this case because their operating rule was that if 4 of 5 browsers agree, then that's what the spec should say.
Comment 33 Adam Barth 2010-10-09 08:18:10 PDT
Comment on attachment 70341 [details]
Patch

IMHO, Safari being the only browser that fails this test is a good reason to converge to the consensus behavior.
Comment 34 Alexey Proskuryakov 2010-10-09 12:35:43 PDT
Comment on attachment 70341 [details]
Patch

Please bring this up for discussion on relevant working groups.
Comment 35 Adam Barth 2010-10-09 19:44:31 PDT
> Please bring this up for discussion on relevant working groups.

That seems overly conservative.  Why wouldn't we want to match every other browser?
Comment 36 Alexey Proskuryakov 2010-10-09 20:59:19 PDT
It doesn't seem that there is any urgency now - we lived with this behavior before, why rush to change it when all the specs reportedly agree with us?

ECMAScript is a complicated spec with many interdependencies. If we're going to violate a requirement saying that typeof of a callable object must be "function", we should at least consult experts who can tell us what else needs to change in accordance.
Comment 37 Adam Barth 2010-10-09 23:46:32 PDT
@oliver: Thoughts on what we should do here?
Comment 38 Erik Arvidsson 2010-10-11 10:00:22 PDT
With my TC39 hat on...

The ES5 spec is clear (11.4.3) that any object that has a [[Call]] should return "function" for typeof.

The problem comes from our willingness to follow the IE VBS-ism where collections are callable.

In ES3 this was fine since ES3 did not mandate that [[Call]] implied typeof obj === "function".

I think there are 2 possible paths here.

1. Make collections non callable, at least not in standards mode. Other browsers do not do this (IE9 stoppee supporting it).
2. Keep them callable but hack typeof to return "object" like this patch does. This is no worse than the document.all hack and just like that one this is a win for compatibility.
Comment 39 Adam Barth 2010-10-11 10:24:41 PDT
> 1. Make collections non callable, at least not in standards mode. Other browsers do not do this (IE9 stopped supporting it).

Oh, I didn't know IE9 stopped making these callable.  Unfortunately, IE9 has tons of compatibility modes, which makes it difficult to know whether following suit will work for us.  I suspect it would cost noticeable compatibility.
Comment 40 Oliver Hunt 2010-10-11 11:28:07 PDT
I would rather not jump through hoops to make things that are callable claim not to be functions, as it would seem likely to lead to interesting bugs.  For instance JSON serialisation skips 'functions' it does this by skipping things that are callable, yet this means it would need to be updated to check for these special cases, ditto for postMessage serialisation.  Special casing these so that typeof returns "object" would lead (i think) to many subtle differences in behaviour.

If we want to stop these from claiming to be "function"s they should stop being callable.
Comment 41 Adam Barth 2010-10-11 11:44:07 PDT
> I would rather not jump through hoops to make things that are callable claim not to be functions, as it would seem likely to lead to interesting bugs.  For instance JSON serialisation skips 'functions' it does this by skipping things that are callable, yet this means it would need to be updated to check for these special cases, ditto for postMessage serialisation.  Special casing these so that typeof returns "object" would lead (i think) to many subtle differences in behaviour.

How do these cases interoperate across browsers now?  It seems that we'll need to make sure these cases behave properly regardless of what string we return from this function.

> If we want to stop these from claiming to be "function"s they should stop being callable.

I suspect we'll have compatibility problems removing their callability.
Comment 42 Garrett Smith 2010-10-11 12:20:20 PDT
(In reply to comment #38)
> With my TC39 hat on...
> 
> The ES5 spec is clear (11.4.3) that any object that has a [[Call]] should return "function" for typeof.
> 
> The problem comes from our willingness to follow the IE VBS-ism where collections are callable.
> 
> In ES3 this was fine since ES3 did not mandate that [[Call]] implied typeof obj === "function".
> 
> I think there are 2 possible paths here.
> 
> 1. Make collections non callable, at least not in standards mode. Other browsers do not do this (IE9 stoppee supporting it).

That's a fair proposal, though you've supported it with false information :-(.

In Gecko, collections aren't callable in any mode. Never have been. If collections aren't callable in Gecko then all of those pages are erring in Gecko. How many pages that is might be hard to determine because many "IE only" sites are internal applications. Probably Mozilla developers would have more insight into this (Boris comes to mind).

Collections are callable in Opera 10.6 (both modes) and in the IE9 beta I have, version 9.0.7390 (all modes).
>>typeof document.images(0)
"object"
>>clientInformation
[object Navigator] {
	appCodeName : "Mozilla",
	appMinorVersion : "beta",
	browserLanguage : "en-us",
	cookieEnabled : true,
	cpuClass : "x86",
	mimeTypes : [object MSMimeTypesCollection],
	plugins : [object MSPluginsCollection],
	systemLanguage : "en-us",
	userLanguage : "en-us",
	appName : "Microsoft Internet Explorer"
	...
}
>>document.documentMode
9
>>typeof document.images
"object"
:-(

> 2. Keep them callable but hack typeof to return "object" like this patch does. This is no worse than the document.all hack and just like that one this is a win for compatibility.

Another spec violation. That weakens the spec and causes interoperability problems for other violations that don't follow your cowpath (which was originally IE's cowpath). I suggest instead that you propose such language extensions to es-discuss.
Comment 43 Erik Arvidsson 2010-10-11 12:36:08 PDT
(In reply to comment #42)
> (In reply to comment #38)
> Collections are callable in Opera 10.6 (both modes) and in the IE9 beta I have, version 9.0.7390 (all modes).
> >>typeof document.images(0)
> "object"

I did try this but my page had no images in it which causes a "Member not found" error. It was a bad test. I apologize for giving false hope.

typeof collection === 'function' is a hazard since these collections are not behaving like functions. The do not have the right [[Class]], instanceof function is false, there is no bind but more serious is the lack of call and apply.

I'm sorry for making this discussion even less on topic than it already is. It is clear that the WebKit/JSC bindings are the exception amongst the browsers and that this is causing compatibility issues.
Comment 44 Geoffrey Garen 2010-10-11 13:18:55 PDT
I don't think a new virtual function -- "hasFunctionType()" -- for asking an object about its callability, in addition to the existing virtual function -- "getCallData()" -- is the best solution. When the two disagree, how do I know which one is right?

I think a better solution is to add a bit to the CallType returned by getCallData, which specifies the behavior of typeof.

I say "I think" because I'm not sure what behavior we're ultimately trying to implement. Are we just trying to bludgeon typeof into matching the behavior of other browsers for a fixed set of DOM objects? If so, that's probably the right solution.

But maybe we're trying to implement a different rule -- for example, that only objects that are callable and descendants of the original value of Function.prototype return "function" to typeof. In that case, a completely different solution is appropriate.

Adam, (In reply to comment #35)
> > Please bring this up for discussion on relevant working groups.
> 
> That seems overly conservative.  Why wouldn't we want to match every other browser?

Since we need to know what behavior we're trying to implement in order to judge whether the code communicates that behavior well, and whether it's fully tested, I think the best way forward is to discuss this with the relevant working groups and decide on a behavior.

I think my comments above are a reason to use a different approach than the one you've used, and to require a certain set of test cases, but I'm just not sure.
Comment 45 Adam Barth 2010-10-11 13:58:37 PDT
> I think a better solution is to add a bit to the CallType returned by getCallData, which specifies the behavior of typeof.

Ok.  That seems to have further reaching implications, but I can study them and see what the consequences would be.

> I say "I think" because I'm not sure what behavior we're ultimately trying to implement. Are we just trying to bludgeon typeof into matching the behavior of other browsers for a fixed set of DOM objects? If so, that's probably the right solution.

I believe bludgeon is a very appropriate verb here.  :)

> But maybe we're trying to implement a different rule -- for example, that only objects that are callable and descendants of the original value of Function.prototype return "function" to typeof. In that case, a completely different solution is appropriate.

I don't believe that's the behavior we want.  For example, just because you mutate an object's prototype chain doesn't mean it's typeof behavior should change.  We could test other browsers to see, but I think this behavior is something specific for these host objects.

> Adam, (In reply to comment #35)
> > > Please bring this up for discussion on relevant working groups.
> > 
> > That seems overly conservative.  Why wouldn't we want to match every other browser?
> 
> Since we need to know what behavior we're trying to implement in order to judge whether the code communicates that behavior well, and whether it's fully tested, I think the best way forward is to discuss this with the relevant working groups and decide on a behavior.

I didn't understand this comment.

> I think my comments above are a reason to use a different approach than the one you've used, and to require a certain set of test cases, but I'm just not sure.

There are two threads to this discussion:

1) What observable behavior do we want?
2) How should we implement that behavior?

It sounds like you agree that our observable behavior should match other browsers and that your comments are more directed at the second question.
Comment 46 Geoffrey Garen 2010-10-13 10:20:33 PDT
> There are two threads to this discussion:
> 
> 1) What observable behavior do we want?
> 2) How should we implement that behavior?

I think the questions are linked. A good implementation depends on a good definition of the desired behavior.
Comment 47 Michał Gołębiowski-Owczarek 2014-04-29 01:53:15 PDT
Both Safari & WebKit nightly have:
typeof document.body.childNodes === 'object'

Therefore, the bug as reported is no longer present.
Comment 48 Ahmad Saleem 2022-06-14 15:40:46 PDT
Using Wayback Archive, I tested "DHTML" test case and now all browsers (Firefox Nightly 103, Chrome Canary 105 and Safari 15.5) aligns and give same output.

Link - https://web.archive.org/web/20101207041718/http://dhtmlkitchen.com/jstest/isFunctionTest.html

Can we close this bug based on all browser behave same now. Thanks!