Closed
Bug 97921
Opened 24 years ago
Closed 24 years ago
bad args for heavyweight function called with fewer actuals than formals
Categories
(Core :: JavaScript Engine, defect, P1)
Tracking
()
VERIFIED
FIXED
mozilla0.9.5
People
(Reporter: georg, Assigned: brendan)
Details
(Keywords: js1.5)
Attachments
(6 files, 3 obsolete files)
|
4.81 KB,
text/html
|
Details | |
|
1.18 KB,
text/plain
|
Details | |
|
635 bytes,
text/html
|
Details | |
|
215 bytes,
text/html
|
Details | |
|
8.89 KB,
patch
|
Details | Diff | Splinter Review | |
|
7.58 KB,
patch
|
shaver
:
superreview+
|
Details | Diff | Splinter Review |
A valueOf to a local variable in a with block of the outer function sets the
value of that variable to zero inside the nested inner function.
A valueOf to the function reference of a nested function inside the outer
function sets the value of a local variable of the outer function to zero inside
the nested inner function.
| Reporter | ||
Comment 1•24 years ago
|
||
| Assignee | ||
Comment 2•24 years ago
|
||
Need a reduced testcase. Note also that this has nothing to do with the valueOf
method.
/be
| Reporter | ||
Comment 3•24 years ago
|
||
I know that it does not have to do with the valueOf method available for
JavaScript objects. It has to do with getting the value of something like a
simple number variable, that is not a number object but a simple value, or
getting the value of a function object and may be more objects.
This testcase does *not* produce the problem, it was modified to try it on the
shell but does not produce the bug:
p=[{c:{}},{c:{}}]; doit=function(a1,a2,a3){var sptr = a2 || this;a1 = a1 || 0; a3 =
a3 || 0;if(a3 < this.p.length){if(useWith) with(this.p[a3].c){a3;} a3++; this.cnt = (this.cnt||0)+1;sptr.tf =
function(){print('a3 inside nested function call:\ntype: '+typeof a3+'\nvalue:
'+a3+' ('+this.cnt+')\nuseWith:
'+useWith+'\n');sptr.doit(a1,a2,a3);};if(this.cnt <= this.p.length)sptr.tf();
else print('Yaeh; you got the bug!\n');}else print('final a3: '+a3+'\n');}
useWith=false;cnt=0;doit();
useWith=true;cnt=0;doit();
But this one, which is not compatible with the shell, does produce the bug:
p=[{c:{}},{c:{}}];doit=function(a1,a2,a3){var sptr = a2 || this;a1 = a1 || 0;a3
= a3 || 0;if(a3 < this.p.length){if(useWith)
with(this.p[a3].c){a3;}a3++;this.cnt = (this.cnt||0)+1;sptr.tf =
function(){alert('a3 inside nested function call:\ntype: '+typeof a3+'\nvalue:
'+a3+' ('+this.cnt+')\nuseWith:
'+useWith+'\n');sptr.doit(a1,a2,a3);};if(this.cnt <=
this.p.length)sptr.tf();else alert('Yaeh; you got the bug!\n');}else
alert('final a3: '+a3+'\n');}
useWith=false;cnt=0;doit(); // running without bug
useWith=true;cnt=0;doit(); // running with bug
No nested functions are required. It also works inside the global scope.You
can't test it on the shell, because the shell does not provide setTimeout.
This is a simplified variation of the first test scenario as displayed in the
attached file above.
Comment 4•24 years ago
|
||
Comment 5•24 years ago
|
||
If I load Georg'e JS shell testcase, I DO see the bug, apparently.
Here is the JS shell output:
----------- NOT using with statement ------------
a3 inside nested function call:
type: number
value:1 (1)
useWith:false
a3 inside nested function call:
type: number
value:2 (2)
useWith:false
final a3: 2
---------------------------------------------------
----------- USING with statement ----------------
a3 inside nested function call:
type: number
value:0 (1)
useWith:true
a3 inside nested function call:
type: number
value:1 (2)
useWith:true
Yaeh; you got the bug!
---------------------------------------------------
Georg, is this what you see in the JS shell, too?
Assignee: rogerl → khanson
Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Linux → All
| Reporter | ||
Comment 6•24 years ago
|
||
Yes, now I see this too in the shell test case. May be that I had a miss typing
error, when I did the test yesterday on the shell, which I fixed without
knowledge when entering the test case to this bug forum.
In this test case for the shell it runs with a value, which is one step to less.
In the browser scenario, where I can use setTimeout, it is fixed to zero value.
Is it a real bug, or are my expections wrong about, what the correct behaviour
should be?
Comment 7•24 years ago
|
||
cc'ing jband. This is the unusual testcase I was talking about -
Comment 8•24 years ago
|
||
The testcase can be reduced to the following. I have renamed the parent
test function as 'test' and the child test function as 'test1'.
var USING_WITH = '\n----------- USING with() statement ----------------';
var USING_NO_WITH = '\n----------- NOT using with() statement ------------';
var IN_TEST = 'In test() : a3 = ';
var IN_TEST1 = 'In test1(): a3 = '
var useWith;
var a3;
print(USING_NO_WITH);
useWith=false;
test();
print(USING_WITH);
useWith=true;
test();
function test(a3)
{
a3 = 0;
if(useWith) with(1){a3;}
a3++;
print(IN_TEST + a3);
test1 = function() {print(IN_TEST1 + a3);};
test1();
}
Comment 9•24 years ago
|
||
The output in the SpiderMonkey shell is:
----------- NOT using with() statement ------------
In test() : a3 = 1
In test1(): a3 = 1
----------- USING with() statement ----------------
In test() : a3 = 1
In test1(): a3 = 0
Comment 10•24 years ago
|
||
The output in the Rhino shell is:
----------- NOT using with() statement ------------
In test() : a3 = 1
In test1(): a3 = 1
----------- USING with() statement ----------------
In test() : a3 = 1
In test1(): a3 = 1
Comment 11•24 years ago
|
||
cc'ing Brendan on this one, now that there is a reduced testcase -
Comment 12•24 years ago
|
||
Comment 13•24 years ago
|
||
The output from NN4.7 is:
----------- NOT using with() statement ------------
In test() : a3 = 1
In test1(): a3 = undefined
----------- USING with() statement ----------------
In test() : a3 = 1
In test1(): a3 = undefined
Comment 14•24 years ago
|
||
Of course, the output from Mozilla is the same as the SpiderMonkey shell:
----------- NOT using with() statement ------------
In test() : a3 = 1
In test1(): a3 = 1
----------- USING with() statement ----------------
In test() : a3 = 1
In test1(): a3 = 0
Comment 15•24 years ago
|
||
Here is a testcase reduced even further:
test();
function test(a3)
{
a3 = 0;
with (new Object()) {a3=100};
a3++;
print(a3);
test1 = function() {print(a3);};
test1();
}
OUTPUT:
SpiderMonkey Rhino Mozilla NN4.7 IE4.7
1 101 1 101 101
100 101 100 undefined 101
I will attach the HTML version of this test below -
Comment 16•24 years ago
|
||
| Assignee | ||
Comment 17•24 years ago
|
||
georg, thanks so much for finding this. The bug is peculiar to functions that
have formal parameters, but that are called with fewer actual arguments than the
declared number of formal parameters. Patch coming up.
/be
| Assignee | ||
Comment 18•24 years ago
|
||
| Assignee | ||
Comment 19•24 years ago
|
||
My fault, taking bug. Please r= and sr=, all you cc: list members who care.
/be
| Assignee | ||
Comment 20•24 years ago
|
||
Taking -- anyone try the patch yet?
/be
Severity: normal → critical
Status: NEW → ASSIGNED
Priority: -- → P1
Comment 21•24 years ago
|
||
I've tried the patch on WinNT; it makes both the testcases pass.
Will try the full test suite now -
Comment 22•24 years ago
|
||
The patch seems to be causing regressions. These tests fail on it:
js/tests/ecma/ExecutionContexts/10.1.6.js
js/tests/ecma/ExecutionContexts/10.1.8-1.js
js/tests/ecma/ExecutionContexts/10.1.8-2.js
js/tests/ecma_3/Function/arguments-001.js
For example:
Testcase ecma/ExecutionContexts/10.1.6.js failed
Failure messages were:
(new TestObject(0,1,2,3,4,5))[0] = undefined FAILED! expected: 0
(new TestObject(0,1,2,3,4,5))[1] = undefined FAILED! expected: 1
(new TestObject(0,1,2,3,4,5))[2] = undefined FAILED! expected: 2
(new TestObject(0,1,2,3,4,5))[3] = undefined FAILED! expected: 3
(new TestObject(0,1,2,3,4,5))[4] = undefined FAILED! expected: 4
(new TestObject(0,1,2,3,4,5))[5] = undefined FAILED! expected: 5
Testcase ecma/ExecutionContexts/10.1.8-1.js failed
Failure messages were:
GetArguments(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,5
2,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99)[0]
= undefined FAILED! expected: 0
GetArguments(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,5
2,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99)[1]
= undefined FAILED! expected: 1
etc.
etc.
Testcase ecma/ExecutionContexts/10.1.8-2.js failed
[ Previous Failure | Next Failure | Top of Page ]
Failure messages were:
GetArguments(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,5
2,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99)[0]
= undefined FAILED! expected: 0
GetArguments(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25
,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,5
2,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,
79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99)[1]
= undefined FAILED! expected: 1
etc.
etc.
Testcase ecma_3/Function/arguments-001.js failed Bug Number 72884
STATUS: Testing the arguments object
Failure messages were:
FAILED!: Section B of test
FAILED!: Expected value '1', Actual value 'undefined'
FAILED!: Expected value '2', Actual value 'undefined'
FAILED!: Expected value '3', Actual value 'undefined'
Comment 23•24 years ago
|
||
Testcase added to JS test suite:
mozilla/js/tests/ecma_3/Function/regress-97921.js
This includes both the reduced tests above. It failed in SpiderMonkey
before I applied the above patch , but passed afterwards.
It passes in Rhino, both in compiled and interpreted mode.
| Assignee | ||
Comment 24•24 years ago
|
||
| Assignee | ||
Comment 25•24 years ago
|
||
pschwartau, anyone: how's that latest patch for you?
/be
Summary: Bugs correlated to internal valueOf with side effects onto nested functions accessing variables local to the wrapping function → bad args for heavyweight function called with fewer actuals than formals
| Assignee | ||
Updated•24 years ago
|
Attachment #48921 -
Attachment is obsolete: true
Comment 26•24 years ago
|
||
The latest patch passes the testcase for this bug:
js/tests/ecma_3/Function/regress-97921.js
And also passes the problematic testcases above:
js/tests/ecma/ExecutionContexts/10.1.6.js
js/tests/ecma/ExecutionContexts/10.1.8-1.js
js/tests/ecma/ExecutionContexts/10.1.8-2.js
js/tests/ecma_3/Function/arguments-001.js
I'm now testing it against the rest of the test suite -
Comment 27•24 years ago
|
||
Success - the latest patch introduces no regressions on WinNT or Linux when
run against the full JS testsuite, in both the debug and optimized JS shells.
Comment 28•24 years ago
|
||
I'm *mostly* convinced this is right. I understand most of it.
Unlike you to add the one time use variable in args_resolve :)
My brain may not be up to the task of convincing myself that you got the
args_setProperty bit right.
I'm not sure what to say... convince me?
| Assignee | ||
Comment 29•24 years ago
|
||
You're right, that single-use variable in args_resolve is gone in my tree (I'm
getting sloppy in my old age!).
Let's look at the crucial part of args_setProperty:
- if (0 <= argc && argc < fp->argc) {
+ if (0 <= argc && argc < fp->fun->nargs) {
/* Check whether we need to free the deleted args bitmap. */
if (fp->argc > JSVAL_INT_BITS &&
- (uintN)argc <= JSVAL_INT_BITS) {
+ fp->fun->nargs <= JSVAL_INT_BITS) {
(void) JS_GetReservedSlot(cx, obj, 0, &bmapval);
if (!JSVAL_IS_VOID(bmapval)) {
bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
The outer if ensures that argc < fp->fun->nargs, so the inner if needs to test
fp->fun->nargs <= JSVAL_INT_BITS to know that argc < JSVAL_INT_BITS. The outer
if does not need to test whether argc < JS_MAX(fp->argc, fp->fun->nargs),
because if fp->argc (the old argc) is greater than fp->fun->nargs, the new argc
won't pull the JS_MAX beyond fp->fun->nargs, and the latter will determine
whether we can use a jsval as an inline jsbitmap instead of as a private-tagged
pointer to a jsbitmap vector.
IOW, we can optimize away the out-of-line jsbitmap vector only if fp->fun->nargs
(the number of formal params) is <= JSVAL_INT_BITS throughout, and the old argc
used to be > JSVAL_INT_BITS, while the new one is <= fp->fun->nargs. Oops, that
means the outer if should test argc <= fp->fun->nargs in its 2nd clause.
New patch coming up, thanks for making me dance!
/be
| Assignee | ||
Comment 30•24 years ago
|
||
| Assignee | ||
Updated•24 years ago
|
Attachment #48973 -
Attachment is obsolete: true
Comment 31•24 years ago
|
||
Part of what I'm thinking about is that the outer if controls whether or not
fp->argc changes or not. So if you have a case where...
fp->fun->nargs < argc < fp->argc
...then your logic here is not going to change fp->argc. This seems wrong. Then
again, I'm not clear on what exactly it means to set the length property of the
arguments object.
| Assignee | ||
Comment 32•24 years ago
|
||
Setting arguments.length means setting fp->argc, because getting
arguments.length just returns fp->argc. Now it occurs to me that we haven't
allowed one to increase argc above its last value to one <= nargs. New patch
coming up.
/be
| Assignee | ||
Updated•24 years ago
|
Attachment #49171 -
Attachment is obsolete: true
| Assignee | ||
Comment 33•24 years ago
|
||
Boy, I'm batting 0 today. Setting arguments.length should set fp->argc *not*
because the getter reflects that data member (it doesn't, due to fp->overrides),
but because fp->argc is use throughout the engine for an active frame's actual
argument count, and we want arguments.length assignment to modify that actual
argument count. At least the GC cares -- it won't mark slots at or above index
fp->argc.
Oops, args_getProperty cares too -- it won't reflect fp->argv[slot] if slot is
not in [0, fp->argc) for the current value of fp->argc. That seems to violate
the ECMA spec, which says that the arguments object is created on entry to a
function code execution context, and populated with indexed properties for each
actual argument (10.1.8). The conservative thing to do is to condition all this
confusing fp->argc-reducing code with a !JSVERSION_IS_ECMA(cx->version) test, I
think. Comments?
/be
| Assignee | ||
Comment 34•24 years ago
|
||
Comment 35•24 years ago
|
||
I say nuke it. I don't think anyone will miss it, and it's obviously a
maintenance headache. =)
Comment 36•24 years ago
|
||
I don't really know if anyone cares about the old behavior - I don't. I'm good
with whatever you think is right. I'm just trying to help make sure the code
does what you intend it to do. Is that last patch your final answer for the
'nuke-it' strategy?
| Assignee | ||
Comment 37•24 years ago
|
||
jband: your review is much appreciated, even better if you help me to remove
code and win weight-watchers points! That last patch's description was a bit
confusing -- it does not remove the fp->argc-mutating code, but I'm going to
back off and nuke the site from orbit. Net patch anon.
/be
| Assignee | ||
Comment 38•24 years ago
|
||
| Assignee | ||
Comment 39•24 years ago
|
||
Ok, please r= and sr= attachment 49648 [details] [diff] [review].
/be
Comment 40•24 years ago
|
||
Comment on attachment 49648 [details] [diff] [review]
proposed fix, simplified to leave fp->argc alone when arguments.length is set
r=shaver with the removal of the unused
+ uintN minargs, maxargs;
Attachment #49648 -
Flags: superreview+
Comment 41•24 years ago
|
||
Comment on attachment 49648 [details] [diff] [review]
proposed fix, simplified to leave fp->argc alone when arguments.length is set
you don't need...
+ uintN minargs, maxargs;
otherwise, sr=jband
| Assignee | ||
Comment 42•24 years ago
|
||
I also don't need argc, bmapval, or bitmap -- sleepy today. All better, thanks.
/be
Status: ASSIGNED → RESOLVED
Closed: 24 years ago
Resolution: --- → FIXED
Comment 43•24 years ago
|
||
Marking Verified -
The testcase js/tests/ecma_3/Function/regress-97921.js now passes
in the debug and optimized JS shells on WinNT, Linux, and Mac -
Status: RESOLVED → VERIFIED
Updated•20 years ago
|
Flags: testcase+
You need to log in
before you can comment on or make changes to this bug.
Description
•