Bug 81588

Summary: Array.prototype.toString should be generic
Product: WebKit Reporter: Erik Arvidsson <arv>
Component: JavaScriptCoreAssignee: Gavin Barraclough <barraclough>
Status: RESOLVED FIXED    
Severity: Normal CC: abarth, andersca, barraclough, hojong.han, oliver, webkit.review.bot
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 83967    
Bug Blocks: 81573, 83787    
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch
none
partial patch
none
Patch
none
Patch
none
Patch
none
Fix
none
Fix inc layout test changes! sam: review+

Description Erik Arvidsson 2012-03-19 16:26:19 PDT
The following fails in JSC

Array.prototype.toString.call(document.getElementsByTagName('*'))

with a TypeError.

The spec is pretty clear here that the above should work.

15.4.4.2	Array.prototype.toString ( )

NOTE	The toString function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the toString function can be applied successfully to a host object is implementation-dependent.
Comment 1 Hojong Han 2012-03-29 22:09:15 PDT
Created attachment 134723 [details]
Patch
Comment 2 Erik Arvidsson 2012-03-30 11:09:33 PDT
Comment on attachment 134723 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=134723&action=review

Please add tests that ensures the correct behavior.

var obj = {__proto__: Array.prototype, 0: 'a', 1: 'b', 2: 'c', length: 3}
shouldBeEqualToString('obj.toString()', 'a,b,c');

var join = {join: function() { return 'join' }}
shouldBeEqualToString('Array.prototype.toString.call(join)', 'join')

shouldBeEqualToString('Array.prototype.toString.call(new Date)', '[object Date]')

> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:260
> +            JSValue value = thisValue.asCell()->toPrimitive(exec, PreferString);

This does not look correct. How does this check for "join" etc?
Comment 3 Hojong Han 2012-04-01 20:24:59 PDT
(In reply to comment #2)
> (From update of attachment 134723 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=134723&action=review
> 
> Please add tests that ensures the correct behavior.
> 
> var obj = {__proto__: Array.prototype, 0: 'a', 1: 'b', 2: 'c', length: 3}
> shouldBeEqualToString('obj.toString()', 'a,b,c');
> 
> var join = {join: function() { return 'join' }}
> shouldBeEqualToString('Array.prototype.toString.call(join)', 'join')
> 
> shouldBeEqualToString('Array.prototype.toString.call(new Date)', '[object Date]')
> 
> > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:260
> > +            JSValue value = thisValue.asCell()->toPrimitive(exec, PreferString);
> 
> This does not look correct. How does this check for "join" etc?

Could you explain how Array.prototype.toString.call(join) results join , not [object Object]?
(and also Array.prototype.toString.call(new Date) results [object Date], not current date information like Mon Apr 02 2012 12:24:05 GMT+0900 (KST))
Comment 4 Erik Arvidsson 2012-04-01 21:11:58 PDT
The spec is pretty clear here. (Sorry for the brevity, I'm using my phone)
Comment 5 Hojong Han 2012-04-01 23:03:20 PDT
(In reply to comment #4)
> The spec is pretty clear here. (Sorry for the brevity, I'm using my phone)

I did misunderstand the spec. It's clear now. thanks, Erik.
Comment 6 Hojong Han 2012-04-02 05:26:33 PDT
Created attachment 135074 [details]
Patch
Comment 7 Darin Adler 2012-04-02 07:49:43 PDT
Comment on attachment 135074 [details]
Patch

A bug fix like this needs test cases. And it needs performance testing.
Comment 8 Darin Adler 2012-04-02 07:50:20 PDT
Comment on attachment 135074 [details]
Patch

Please submit a new patch that adds tests for all the new code paths.
Comment 9 Erik Arvidsson 2012-04-02 09:55:01 PDT
Comment on attachment 135074 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=135074&action=review

> Source/JavaScriptCore/runtime/ArrayPrototype.cpp:270
> +                return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]"));

This does not seem right. You should call the original Object.prototype.toString instead of duplicating its implementation.


...and add tests
Comment 10 Hojong Han 2012-04-02 18:00:55 PDT
(In reply to comment #9)
> (From update of attachment 135074 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=135074&action=review
> > Source/JavaScriptCore/runtime/ArrayPrototype.cpp:270
> > +                return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable()->className(thisObject), "]"));
> This does not seem right. You should call the original Object.prototype.toString instead of duplicating its implementation.
> ...and add tests

Thanks for reveiw. I'll apply what you recommend and tests to the new patch.
Comment 11 Hojong Han 2012-04-04 03:04:13 PDT
Created attachment 135542 [details]
Patch
Comment 12 Early Warning System Bot 2012-04-04 03:08:19 PDT
Comment on attachment 135542 [details]
Patch

Attachment 135542 [details] did not pass qt-wk2-ews (qt):
Output: http://queues.webkit.org/results/12317837
Comment 13 Early Warning System Bot 2012-04-04 03:12:46 PDT
Comment on attachment 135542 [details]
Patch

Attachment 135542 [details] did not pass qt-ews (qt):
Output: http://queues.webkit.org/results/12320742
Comment 14 Hojong Han 2012-04-04 03:13:28 PDT
Created attachment 135546 [details]
Patch
Comment 15 Erik Arvidsson 2012-04-05 16:00:55 PDT
Olli, Gavin: This bug is blocking 81573 so your feedback is highly appreciated.
Comment 16 Erik Arvidsson 2012-04-06 15:19:37 PDT
The reason why this is blocking 81573 is that NodeList now has Array.prototype on its prototype chain. This will cause toString on a NodeList to call Array.prototype.toString which throws in JSC. I'm tempted to submit 81573 without waiting for this but the JSC behavior until this one is fixed is pretty much unacceptable.
Comment 17 Erik Arvidsson 2012-04-06 15:26:04 PDT
I think this patch might not be sufficient. If someone overrides Array.prototype.join, arrays need to reflect that. For example:

Array.prototype.join = function() { return 'join' }
shouldBeEqualToString('[0, 1, 2].toString()', 'join');

It seems like the right thing to do is to not special case real arrays and always call join as the spec says.
Comment 18 Hojong Han 2012-04-07 07:35:44 PDT
(In reply to comment #17)
> I think this patch might not be sufficient. If someone overrides Array.prototype.join, arrays need to reflect that. For example:
> 
> Array.prototype.join = function() { return 'join' }
> shouldBeEqualToString('[0, 1, 2].toString()', 'join');
> 
> It seems like the right thing to do is to not special case real arrays and always call join as the spec says.

I think you are right on the basic principle. It'd better call join as your explanation.
but some considerations, such as arguments, will be necessay if join were called in toString.
I'll patch again with these.
Comment 19 Gavin Barraclough 2012-04-07 19:44:07 PDT
(In reply to comment #17)
> I think this patch might not be sufficient. If someone overrides Array.prototype.join, arrays need to reflect that. For example:
> 
> Array.prototype.join = function() { return 'join' }
> shouldBeEqualToString('[0, 1, 2].toString()', 'join');

Agreed - please add this test case.  Also, due to the 'if (thisValue.isCell()) {'check, I think this will do the wrong think for non-cell this values, e.g.:

Number.prototype.join = function() { return "number join"; }
Array.prototype.toString.call(42);

> It seems like the right thing to do is to not special case real arrays and always call join as the spec says.

From a quick glance it looks like toString is more optimized that join, we could look at similarly optimizing join, but without doing so we wouldn't want to ditch the optimization from toString, and changing functionality and introducing optimizations in the same patch is a bad plan.  So it would be best for this patch to fix toString without either removing optimizations from toString or introducing them to join.

The 'inherits' check in the current patch is also bogus (not all functions inherit from JSFunction).  If we did need to check for functions, the right thing to do would be to would be to check .isFunction - however in this case the spec does not ask for the value to be a function, rather that it is callable (these may be one and the same for objects defined in the ES spec, but is not the same thing in the DOM).  The way to check for this is to check the returned CallType for CallTypeNone.  Rather than creating a new Identifier every time toString is called, one should be added to CommonIdentifiers.  The function call is passing undefined as the this value, this is also wrong.  When implementing something like this, I'd suggest the easiest way is to start by copying the text from the spec, and using this a comments to follow, to help make sure your steps are all correct!
Comment 20 Gavin Barraclough 2012-04-07 19:47:09 PDT
Created attachment 136143 [details]
partial patch

Thought I'd try to help, think this might be a useful starting point.  I haven't run the existing regression tests, the new tests from your patch, Erik's proposed test case, or the one I suggest from my patch.  So there may be bugs. :-)  But hope this is useful.
Comment 21 Hojong Han 2012-04-09 02:45:26 PDT
Created attachment 136203 [details]
Patch
Comment 22 Hojong Han 2012-04-09 03:20:58 PDT
(In reply to comment #20)
> Created an attachment (id=136143) [details]
> partial patch
> 
> Thought I'd try to help, think this might be a useful starting point.  I haven't run the existing regression tests, the new tests from your patch, Erik's proposed test case, or the one I suggest from my patch.  So there may be bugs. :-)  But hope this is useful.

Thanks a lot, Gavin. I patched by steps you'd taught.
Comment 23 Hojong Han 2012-04-09 03:58:10 PDT
Created attachment 136209 [details]
Patch
Comment 24 Hojong Han 2012-04-09 04:26:06 PDT
Created attachment 136211 [details]
Patch
Comment 25 Erik Arvidsson 2012-04-09 09:30:08 PDT
Comment on attachment 136211 [details]
Patch

This looks pretty good to me. I'll let Gavin or someone else that knows JSC take care of the r+
Comment 26 Erik Arvidsson 2012-04-11 18:47:08 PDT
[ArrayClass] landed so when this lands JSC ports need some rebaselines
Comment 27 Hojong Han 2012-04-11 19:59:40 PDT
(In reply to comment #26)
> [ArrayClass] landed so when this lands JSC ports need some rebaselines
When was it landed? I cannot find any landed codes affects this latest patch.
Comment 28 Adam Barth 2012-04-11 23:07:43 PDT
> > [ArrayClass] landed so when this lands JSC ports need some rebaselines
>
> When was it landed? I cannot find any landed codes affects this latest patch.

http://trac.webkit.org/changeset/113931
Comment 29 Erik Arvidsson 2012-04-11 23:11:17 PDT
The following tests probably needs rebaselines:

LayoutTests/fast/dom/HTMLSelectElement/named-options.html
LayoutTests/fast/dom/NodeList/node-list-array-class.html
LayoutTests/fast/dom/NodeList/nodelist-item-call-as-function.html

since they depend on the toString behavior of NodeList.
Comment 30 Erik Arvidsson 2012-04-13 12:32:18 PDT
This is now causing Gmail to not working when building with JSC.
Comment 31 WebKit Review Bot 2012-04-13 17:26:40 PDT
Comment on attachment 136211 [details]
Patch

Clearing flags on attachment: 136211

Committed r114185: <http://trac.webkit.org/changeset/114185>
Comment 32 WebKit Review Bot 2012-04-13 17:26:46 PDT
All reviewed patches have been landed.  Closing bug.
Comment 33 Anders Carlsson 2012-04-13 18:23:07 PDT
Unfortunately, this broke a bunch of JavaScript tests on the Mac bots (probably more bots as well):

fast/dom/HTMLSelectElement/named-options.html
fast/dom/NodeList/nodelist-item-call-as-function.html
fast/dom/everything-to-string.html
fast/js/array-functions-non-arrays.html
fast/js/array-prototype-properties.html
fast/js/recursion-limit-equal.html
fast/js/toString-overrides.html
inspector/console/command-line-api.html
inspector/console/console-dir.html
inspector/console/console-format-collections.html
jquery/traversing.html
sputnik/Conformance/15_Native_Objects/15.4_Array/15.4.4/15.4.4.2_Array_prototype_toString/S15.4.4.2_A2_T1.html
sputnik/Conformance/15_Native_Objects/15.4_Array/15.4.4/15.4.4.3_Array_prototype_toLocaleString/S15.4.4.3_A1_T1.html

I don't know if all of them are progressions so I'm going to roll out the patch.
Comment 34 Alexey Proskuryakov 2012-04-15 00:03:35 PDT
This fix was rolled out in <http://trac.webkit.org/changeset/114195>, so reopening.
Comment 35 Hojong Han 2012-04-15 18:21:49 PDT
(In reply to comment #30)
> This is now causing Gmail to not working when building with JSC.
Erik, could you let me know which one is "This", patch or bug?
Layout test has not been run under my development environmen
so that I've not been able to update the patch with rebaseline...
Sorry for this.
Comment 36 Gavin Barraclough 2012-04-16 10:02:05 PDT
(In reply to comment #35)
> Layout test has not been run under my development environmen

If you want to contribute patches to WebKit, we do require that you run the regression tests on your changes.
Comment 37 Gavin Barraclough 2012-04-16 16:35:37 PDT
Created attachment 137427 [details]
Fix
Comment 38 Gavin Barraclough 2012-04-16 16:36:59 PDT
Created attachment 137430 [details]
Fix inc layout test changes!
Comment 39 Hojong Han 2012-04-16 17:03:23 PDT
(In reply to comment #36)
> (In reply to comment #35)
> > Layout test has not been run under my development environmen
> 
> If you want to contribute patches to WebKit, we do require that you run the regression tests on your changes.

Yes, of course. What I meant is that my system had some problems while running layout tests for rebaseline so that I couldn't patch right away. I should have let people know this before landing.
Comment 40 Gavin Barraclough 2012-04-17 11:59:15 PDT
Fixed in r114405