Closed Bug 292731 Opened 20 years ago Closed 19 years ago

for-in statement calls valueOf method

Categories

(Core :: JavaScript Engine, defect, P3)

defect

Tracking

()

VERIFIED FIXED
Future

People

(Reporter: stryker330, Assigned: brendan)

References

Details

(Keywords: js1.5, testcase, verified1.8, Whiteboard: [no l10n impact])

Attachments

(3 files, 2 obsolete files)

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b2) Gecko/20050502 Firefox/1.0+ Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b2) Gecko/20050502 Firefox/1.0+ If you run an for-in statement on an object, even an empty for-in statement, the object's valueOf method will be called. I looked in the ECMAScript 3rd ed spec, and I can't see why the valueOf method should be called here. IE and Opera don't call it. I wondered if the evaluating of the object expression in the for-in calls the valueOf, but when I tried an if statement that should do the same type of evaluation (comparing the object reference to window), valueOf isn't called. Reproducible: Always Steps to Reproduce: 1) Create an object that has a custom valueOf method. 2) for (prop in obj); Actual Results: valueOf method is called Expected Results: valueOf method should not be called
Attached file test case (obsolete) —
Keywords: testcase
Reading <http://bclary.com/2004/11/07/ecma-262#a-12.6.4>, the case for valueOf() being called could be made. Brendan?
Attachment #182487 - Attachment is obsolete: true
This is a very old extension (over 8 years?) and I'm loath to change it now. It may be disallowed by ECMA-262 Edition 3 Section 16, but I haven't yet attempted a close reading of that section applied to this specific case. What exactly is the problem it causes? /be
Status: UNCONFIRMED → NEW
Ever confirmed: true
It looks like the with and for-in statements have the exact same processing of the object expression: 1. Evaluate Expression. 2. Call GetValue(Result(1)). 3. Call ToObject(Result(2)). And the with statement does not call valueOf. (In reply to comment #3) > This is a very old extension (over 8 years?) and I'm loath to change it now. It > may be disallowed by ECMA-262 Edition 3 Section 16, but I haven't yet attempted > a close reading of that section applied to this specific case. What exactly is > the problem it causes? > > /be No problem, but why would it need to call valueOf?
The engine is inconsistent about this, as well as old, pre-ECMA. Ok, let's try changing this for 1.8. /be
Assignee: general → brendan
Flags: blocking1.8b3+
Keywords: js1.5
Priority: -- → P3
Target Milestone: --- → mozilla1.8beta3
Flags: testcase?
/cvsroot/mozilla/js/tests/ecma_3/Statements/12.6.3.js,v <-- 12.6.3.js initial revision: 1.1
Flags: testcase? → testcase+
Flags: blocking1.8b4+
Flags: blocking1.8b3-
Flags: blocking1.8b3+
Whiteboard: [no l10n impact]
Actually, I think fixing this will break the patch for ambiguous scriptable plugin members that Pete Zha has prepared in bug 293972. API compatibility means never having to say you're sorry, because you never break old APIs. So I would rather change with's implementation to match for-in's. Comments? /be
Target Milestone: mozilla1.8beta3 → mozilla1.8beta4
OS: Windows XP → All
Hardware: PC → All
Target Milestone: mozilla1.8beta4 → Future
FYI, accessing properties also has the same 3 steps: 3. Evaluate Expression. 4. Call GetValue(Result(3)). 5. Call ToObject(Result(2)). http://bclary.com/2004/11/07/ecma-262#a-11.2.1 (In reply to comment #7) > Actually, I think fixing this will break the patch for ambiguous scriptable > plugin members that Pete Zha has prepared in bug 293972. API compatibility > means never having to say you're sorry, because you never break old APIs. > > So I would rather change with's implementation to match for-in's. Comments? > > /be I'm fine with that. If I were to define a custom valueOf for an object, I would never let it set or do anything and would just return something instead. Since the for-in discards the value returned from valueOf, there should be no side effects, except a bit of inefficiency.
(In reply to comment #8) > Since the for-in discards the value returned from valueOf, there should be no > side effects, except a bit of inefficiency. for-in code does not discard an object reference returned from valueOf("object"), though -- that case works as intended. js> o = {valueOf: function (hint) { if (hint == "object") return [4,5,6] }}; [object Object] js> for (i in o) print(i) 0 1 2 js> for each (v in o) print(v) 4 5 6 /be
brendan, do we still need to block on this?
mrbkap has kindly volunteered to take this. It's easy to fix. /be
Assignee: brendan → mrbkap
Attached patch always call valueOf (obsolete) — Splinter Review
This is, I think, the solution that Brendan and I were talking about earlier. Now, instead of only sometimes calling valueOf, we always call it. Consistancy is the winner with this patch. Now we should always call valueOf.
Attachment #193102 - Flags: review?(brendan)
Comment on attachment 193102 [details] [diff] [review] always call valueOf r+a=me, thanks! /be
Attachment #193102 - Flags: review?(brendan)
Attachment #193102 - Flags: review+
Attachment #193102 - Flags: approval1.8b4+
Fix checked into MOZILLA_1_8_BRANCH and trunk.
Status: NEW → RESOLVED
Closed: 19 years ago
Keywords: fixed1.8
Resolution: --- → FIXED
This looks like a bit of a Tp hit (0.5-1%)
I just backed out the fix for this bug and checked in a fix that did the opposite (applying the optimization in more places), since the original fix caused the Tp hit. Reopening until we know what the numbers from the most recent checkin look like.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Note that ECMA never calls [[DefaultValue]] (which calls valueOf(hint)) with hint "object", so we are now thinking about breaking a SpiderMonkey extension of very long standing, in order to restore the lost Tp perf (and perhaps even gain a bit), and to match ECMA. The latter is really more for future implementations of JS we care about that might have to ship in products using SpiderMonkey. I'm all in favor of reducing the extensions required in such scenarios. Let's not require Narcissus to support the "object" hint. /be
Attached patch new patchSplinter Review
This is the patch that was checked into trunk. It restores the optimization and uses it in more places. Looking for approval.
Attachment #193102 - Attachment is obsolete: true
Attachment #193512 - Flags: review+
Attachment #193512 - Flags: approval1.8b4?
Marking as FIXED and removing fixed1.8, since this there is (for now) an inconsistancy between the trunk and the branch that will be solved with this checkin.
Status: REOPENED → RESOLVED
Closed: 19 years ago19 years ago
Keywords: fixed1.8
Resolution: --- → FIXED
Comment on attachment 193512 [details] [diff] [review] new patch This is better. Let's see if anyone notices (the way for..in called valueOf("object") was odd and did not match other such cases as noted in this bug, so I'd be surprised). /be
Attachment #193512 - Flags: approval1.8b4? → approval1.8b4+
attachment 193512 [details] [diff] [review] checked into MOZILLA_1_8_BRANCH.
Keywords: fixed1.8
(In reply to comment #20) > (From update of attachment 193512 [details] [diff] [review] [edit]) > This is better. Let's see if anyone notice ChatZilla noticed, rather badly. The code often does this: if (typeof foo == "object") for (var p in foo) which obviously breaks now, since |null| is typeof "object", but fails in the for..in with a TypeError. The problem is really a conflict within the ECMA spec itself. It specifically defines |typeof null| as "object" (11.4.3), and yet also specifically says ToObject(null) should throw a TypeError (9.9). That just doesn't make sense. Either the null value is an object or it isn't. Now, I don't expect anyone to care, but really, the spec is only making things worse here. Some parts of the spec, like 15.2.1.1 (calling Object as a function), specifically handle null and undefined separately from the normal code path of calling ToObject. The for..in bit doesn't care. What would it take to just make for..in of null not enumerate anything, like it used to? (or, alternatively, make |typeof null| return something else? I suspect this is very unlikely to ever happen, though)
Would if (foo instanceof Object) work for you? It's false for null (which is not an "instance of" anything, and more importantly for the JS world does not delegate to anything) and true for all the objects I've been able to test with. An alternative might be to enumerate over Object(foo), which will promote null to a new, empty object at some small performance cost. That typeof returns "object" for null has been a source of quite some pain in ECMA and JS, but it's not really something we can change any more. Changing the meaning of ToObject(null) to matching |Object(null)| would have repercussions throughout the spec, including the behaviour of this code: var foo = null; var bar = foo.x; which would then give bar the value |undefined|, instead of throwing an error. But IE and Safari have the pre-292731 behaviour, according to my quick tests and requests on IRC for help, so this might cause us to regress in terms of web compatibility. Thanks to Silver for being the canary in that coal mine.
Thanks for the suggestions, I'll try some of them later. instanceof is not a workable solution, though (I'm not sure yet if it is just the separate window issue, or something else, but it is returning false for critical ChatZilla objects that really are objects :) ). My personal opinion on dealing with the for..in of null would be to add a special case, much like Object(foo) does. Of course, this would be nothing more than a compatibility hack (unless the spec was declared wrong or updated). I also noticed that for..in of undefined used to work - is that also failing now? (I know of some code that only got review because undefined was ok in for..in) Now if only today's Firefox build didn't just die with JS errors on startup. :(
We very recently (last 2-3 days) fixed some issues with wrapper identity and such across scopes that were severed by the split windows, so you might try instanceof again. undefined will throw as well, today, for the same ECMA conformance reason as with null. I really think we should at least take this off the branch until we can assess the compatibility effects.
I'm testing the instanceof stuff in 2005071504, which is pre-split window, but post bug 254067 (which is the thing I was refering to in my comment). That bug really messed up ChatZilla, and made instanceof virtually unusable in it, so it may just be that. I think we have no choice but to take the performance hit of doing |for...in| of |Object(foo)|.
Compatibility is king, and this change seems to have caused bug 305724. We should make for..in tolerate null (and undefined? That would restore the lost compatibility completely, and why take chances at this point?). Silver: sorry this broke you, I'm actually working the ECMA TG1 group in charge of Edition 4 of the JS standard (ECMA-262) to fix it so typeof null === "null". That means Edition 4 for (i in null); will throw, again. But any JS2/ES4 usage will require an explicit, opt-in version selection. /be
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Assignee: mrbkap → brendan
Status: REOPENED → NEW
... while still avoiding valueOf hook-calling (and at the same time not again deoptimizing JSOP_TOOBJECT by losing the !JSVAL_IS_PRIMITIVE(v) fast path). mrbkap says r=him. I'm checking this into the branch and trunk. /be
Attachment #193725 - Flags: review+
Attachment #193725 - Flags: approval1.8b4+
Fixed on trunk and MOZILLA_1_8_BRANCH. /be
Status: NEW → RESOLVED
Closed: 19 years ago19 years ago
Resolution: --- → FIXED
*** Bug 305724 has been marked as a duplicate of this bug. ***
Blocks: 305651
Depends on: 305622
For historical reference & duping purposes, the original patch on the 1.8 branch broke the Account Manager for Thunderbird and suite mail. I've verified that Brendan's patch from this morning fixes that problem. See Bug 305622.
For reference, XPCNativeWrapper is not instanceof Object, and in general a JS object created in one scope is not instanceof Object in another scope. For example: (window.frames[0].document instanceof Object) is false no matter what while (window.frames[0].document instanceof window.frames[0].Object) will generally be true, modulo the split-window stuff and assuming there are no XPCNativeWrappers in sight.
Blocks: 305622
No longer depends on: 305622
Blocks: 305845
v 1.8.0.1, 1.8, 1.9a1 win/linux/mac
Status: RESOLVED → VERIFIED
Keywords: fixed1.8verified1.8
*** Bug 333730 has been marked as a duplicate of this bug. ***
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: