Closed
Bug 304376
Opened 19 years ago
Closed 19 years ago
String = Array changes strings to have Array's prototype
Categories
(Core :: JavaScript Engine, defect, P1)
Core
JavaScript Engine
Tracking
()
VERIFIED
FIXED
mozilla1.9alpha1
People
(Reporter: mrbkap, Assigned: brendan)
References
Details
(Keywords: verified1.8.1, Whiteboard: [patch])
Attachments
(6 files, 2 obsolete files)
(deleted),
patch
|
brendan
:
review+
brendan
:
approval1.8b4+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
mrbkap
:
review+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
mrbkap
:
review+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
mrbkap
:
review+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
mrbkap
:
review+
|
Details | Diff | Splinter Review |
(deleted),
text/plain
|
Details |
This actually causes a crash in one instance. I'll attach a wallpaper for the
crash in a second.
According to Ecma-262, 15.5.2.1, the string constructor creates a new object
with "original String prototype object, the one that is the initial value of
String.prototype (15.5.3.1)." When I set |String = Array|, subsequent string
objects have Array prototypes. Brendan interprets this to mean that we're not
compliant with the spec.
Reporter | ||
Comment 1•19 years ago
|
||
Steps to reproduce the crash:
js> String = Array
function Array() {
[native code]
}
js> "".join()
<crash>
This is caused by js_EnterSharpObject not incrementing map->depth when it
creates the hash table. The problem is that we end up re-entering it when
enumerating the properties while marking the incoming object as sharp. Its
subsequent attempts to used the destroyed (null'd out) hash table cause a
segfault.
Attachment #192446 -
Flags: review?(brendan)
Reporter | ||
Comment 2•19 years ago
|
||
(timeless gets credit for influencing the testcase that found this crash).
Assignee | ||
Comment 3•19 years ago
|
||
Comment on attachment 192446 [details] [diff] [review]
wallpaper over the crash
r=me, but I would not say this is a Hack unless you can show a cleaner patch
that forks the initial condition earlier, and sets map->depth to 1 right away.
I bet that's not a cleaner patch.
If you agree, remove the "Hack: ". Oh, and s/since/because/ (or s/since/as/),
usage nit of the day.
This particular fix is safe for 1.8b4. Go fast!
/be
Attachment #192446 -
Flags: review?(brendan)
Attachment #192446 -
Flags: review+
Attachment #192446 -
Flags: approval1.8b4+
Reporter | ||
Comment 4•19 years ago
|
||
Attachment 192446 [details] [diff] is checked in. I'm leaving this bug open to solve the more
general issue of String's prototype getting replaced.
Assignee | ||
Comment 5•19 years ago
|
||
(In reply to comment #4)
> I'm leaving this bug open to solve the more
> general issue of String's prototype getting replaced.
String.prototype is readonly and permanent, so it's really String that is
getting replaced, along with all the other standard class constructors defined
in section 15 of ECMA-262 Edition 3.
The fix here will entail some kind of per-global-object peer data structure
containing the initial value of the standard class prototypes. We could stick
'em in the cx used to init standard classes, but API compatibility for JS_Get-
and JS_SetGlobalObject/JS_InitStandardClasses says we have to be prepared to
swap out the prototype pointers, swapping in those associated with the new obj
being set.
What's more, it is possible to JS_SetGlobalObject with a non-null obj, then call
JS_InitStandardClasses with a different obj param (perhaps one related by proto
or parent linkage; perhaps not). So we can't just store the root-by-definition
proto pointers in cx -- we need a runtime-wide mapping from "global" obj (scope
obj, really) to proto-roots.
/be
OS: Linux → All
Priority: -- → P3
Hardware: PC → All
Target Milestone: --- → mozilla1.9alpha
Assignee | ||
Comment 6•19 years ago
|
||
The reason JS_InitStandardClasses (actually, InitFunctionAndObjectClasses) calls
JS_SetGlobalObject if (!cx->globalObject) is so that js_FindConstructor can find
a class-named constructor or prototype without any frames active on cx. This is
a concession to ancient code, but it could be fixed by making JS_Init*Class*
push a dummy frame with scopeChain == obj.
That might not cover other APIs that can run on a cx with no frames active and
want to resolve a standard class, though. Pushing dummy frames for a bunch of
APIs is going to bloat code and runtime, so let's stick with the august and
venerable cx->globalObject hack, I say.
We could optimize to avoid creating a proto-root peer data structure for common
cases such as Mozilla's DOM, by inlining a proto-roots struct in JSContext and
using that if (obj == cx->globalObject || !cx->globalObject). Comments?
/be
Comment 7•19 years ago
|
||
Checking in regress-304376.js;
/cvsroot/mozilla/js/tests/ecma_3/String/regress-304376.js,v <-- regress-304376.js
initial revision: 1.1
done
Flags: testcase+
Assignee | ||
Comment 8•19 years ago
|
||
Patch coming in the big one for bug 326466.
/be
Assignee: general → brendan
Depends on: geniter
Assignee | ||
Updated•19 years ago
|
Status: NEW → ASSIGNED
Priority: P3 → P1
Summary: Setting String = Array changes strings to have Array's prototype → String = Array changes strings to have Array's prototype
Assignee | ||
Comment 9•19 years ago
|
||
- Conform to ECMA-262 verbiage about "the original value of Object.prototype"
as the per-instance prototype for objects created during runtime, e.g. (same
for Array, etc.). This patch implements a cache per JSContext of prototypes
indexed by a well-known int id (dense index). Care required to avoid dynamic
scope bugs. Well-known native classes include JSCLASS_CACHED_PROTO_KEY(key)
in their flags initializer to associate their int prototype cache id. Using
this flags field, js_NewObject can avoid a global object lookup.
- Fix JS_ValueToId to return object-tagged XML ids given QName, AttributeName,
and AnyName object values.
- Add JSCLASS_IS_ANONYMOUS for initialized classes that should not bind their
class name to a constructor or prototype.
- Split out JS_GetMethodById API from JS_GetMethod (former takes a jsid id,
latter takes a const char *name).
- Factor js_MarkStackFrame from js_GC, for future use from generator_mark.
/be
Attachment #218767 -
Flags: review?(mrbkap)
Assignee | ||
Comment 10•19 years ago
|
||
Comment on attachment 218767 [details] [diff] [review]
proposed fix
Urgh, forgot to re-diff after my own review.
/be
Attachment #218767 -
Attachment is obsolete: true
Attachment #218767 -
Flags: review?(mrbkap)
Assignee | ||
Comment 11•19 years ago
|
||
See next-to-last comment for proposed checkin message.
/be
Attachment #218768 -
Flags: review?(mrbkap)
Assignee | ||
Comment 12•19 years ago
|
||
Comment on attachment 218768 [details] [diff] [review]
proposed fix
Igor, feel free to review if you have time. This follows up on some recently patched areas of common interest.
/be
Attachment #218768 -
Flags: review?(igor.bukanov)
Assignee | ||
Updated•19 years ago
|
Whiteboard: [patch]
Assignee | ||
Comment 13•19 years ago
|
||
Attachment #218768 -
Attachment is obsolete: true
Attachment #218777 -
Flags: review?(mrbkap)
Attachment #218768 -
Flags: review?(mrbkap)
Attachment #218768 -
Flags: review?(igor.bukanov)
Assignee | ||
Updated•19 years ago
|
Attachment #218777 -
Flags: review?(igor.bukanov)
Assignee | ||
Comment 14•19 years ago
|
||
This is going in first, so reversing dependency.
/be
Reporter | ||
Comment 15•19 years ago
|
||
Comment on attachment 218777 [details] [diff] [review]
consarnit, left out two files
Nice! r=mrbkap
Attachment #218777 -
Flags: review?(mrbkap) → review+
Comment 16•19 years ago
|
||
I run the test from bug 325724 comment 1. The patch for this bug slows down js shell by 2% while the patch from bug 325724 makes things faster by 10%. (Be aware of the patch from bug 334261 that optimises aways object creation and makes test useless when applied!)
But this is strange as it seems the patch should make the same speedup as the idea behind bug 325724 even for the single threaded case. Any reason for this?
Comment 17•19 years ago
|
||
String= Array, "".join()
still doesn't work as expected (should throw TypeError because the original
String prototype has no callable property named "join").
Another case:
js> String= Number, "".valueOf()
typein:1: strict warning: assignment to undeclared variable String
typein:1: TypeError: Number.prototype.valueOf called on incompatible String
Both with and without the patch for bug 334261. (Is the warning a bug?)
Assignee | ||
Comment 18•19 years ago
|
||
A bunch of JSCLASS_HAS_CACHED_PROTO(JSProto_...) flag setting is missing. Plus, jsproto.tbl is bereft of init functions. This explains the performance loss due to futile extra checking of js_GetCachedPrototype. Fixing, should be much better soon.
/be
Assignee | ||
Comment 20•19 years ago
|
||
Comment on attachment 218777 [details] [diff] [review]
consarnit, left out two files
Igor, please feel free to review the sum of the two latest patches -- sorry for the mess.
/be
Attachment #218777 -
Flags: review?(igor.bukanov)
Assignee | ||
Comment 21•19 years ago
|
||
(In reply to comment #17)
> String= Array, "".join()
>
> still doesn't work as expected (should throw TypeError because the original
> String prototype has no callable property named "join").
Works now:
js> String = Array, "".join()
typein:1: TypeError: "".join is not a function
> Another case:
>
> js> String= Number, "".valueOf()
> typein:1: strict warning: assignment to undeclared variable String
> typein:1: TypeError: Number.prototype.valueOf called on incompatible String
Works also:
js> String = Number, "".valueOf()
js>
> Both with and without the patch for bug 334261. (Is the warning a bug?)
The warning is an odd interaction between lazy standard class initialization via JS_ResolveStandardClass, done only if the name being resolved is not on the left side of assignment. Perhaps (and this bug would have been helped by it) the global_resolve code, and similar code in the Gecko DOM, should resolve whether or not the name is on the left of assignment.
Igor, with your test from bug 325724 comment 1, with 1000*1000 passed instead of 100*1000, I see a best-of-three speedup from 942 to 871 with the patches for this bug applied.
/be
Reporter | ||
Comment 22•19 years ago
|
||
Comment on attachment 219076 [details] [diff] [review]
the rest of the story...
r=mrbkap
Attachment #219076 -
Flags: review?(mrbkap) → review+
Assignee | ||
Comment 23•19 years ago
|
||
Final patch checked in. Anything still not quite right should be reported in a followup bug.
/be
Status: ASSIGNED → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
Assignee | ||
Comment 24•19 years ago
|
||
Sloppy, late night hacking -- no excuse. This fixes the orange tinderboxes.
/be
Attachment #219088 -
Flags: review?(mrbkap)
Reporter | ||
Updated•19 years ago
|
Attachment #219088 -
Flags: review?(mrbkap) → review+
Assignee | ||
Comment 25•19 years ago
|
||
Attachment #219090 -
Flags: review?(mrbkap)
Reporter | ||
Comment 26•19 years ago
|
||
Comment on attachment 219090 [details] [diff] [review]
XDR is bi-directional, data flow is hard
But it's so symetric or something...
Attachment #219090 -
Flags: review?(mrbkap) → review+
Comment 27•19 years ago
|
||
I filed bug 334807 for the arguments object still being affected by
assignment to Object, but I do not know what should happen with object
and array literals. The spec says:
Create a new object/array as if by the expression new Object()/Array().
It is unclear if this simply refers to section 15.2.2.1/15.4.2.1 or means
to use the value of Object resp. Array at the time of evaluation. Current
behaviour is the latter.
Furthermore, the description of the Object constructor doesn't refer to the
/original/ value of Object.prototype, unlike all the other constructors.
Comment 28•19 years ago
|
||
> Igor, with your test from bug 325724 comment 1, with 1000*1000 passed instead
> of 100*1000, I see a best-of-three speedup from 942 to 871 with the patches for
> this bug applied.
In fact I run already the test with N set to 10**6. My numbers for the optimized build of js shell under 2.6 Ghz Pentium-4 box with Ubuntu 5.10/GCC 4.0:
Without the patch (CVS tree from 2006-04-17): 1185
With the patch (Current CVS HEAD): 1133
With the patch from bug 325724 apllied for CVS tree from 2006-04-17: 1059
That is, the speedup from the last patch is less then halve then the speedup from the patch in bug 325724. The question is why?
Comment 29•19 years ago
|
||
Such a small testcase measures much random noise. My similar testcase from
bug 334261 has this results for str.length (without the patch for bug 334261):
before now
compiled with -O1: 3791 4109
compiled with -O2: 3133 2885
(This is on an Athlon, where performance depends very much on how good jump
targets happen to be aligned, and compiled without -march set)
Comment 30•19 years ago
|
||
(In reply to comment #29)
> Such a small testcase measures much random noise. My similar testcase from
> bug 334261 has this results for str.length (without the patch for bug 334261):
>
> before now
> compiled with -O1: 3791 4109
> compiled with -O2: 3133 2885
>
> (This is on an Athlon, where performance depends very much on how good jump
> targets happen to be aligned, and compiled without -march set)
Could you also check the patch from the patch from bug 325724 apllied for CVS tree from 2006-04-17 ?
Comment 31•19 years ago
|
||
Here is my data with -O2 (2.6 Ghz Pentium-4 box with Ubuntu 5.10/GCC
4.0):
before now bug 334261
compiled with -O: 1185 1133 1059
compiled with -O2: 1111 1033 951
So the conclusion is the same: the speedup from bug 334261 is 2 times greater that this patch.
It seems that for the single threaded case this patch brings too much code to look for cached classes compared to property lookup for known atoms.
Comment 32•19 years ago
|
||
Here is stats for jsshell compiled with JS_THREADSAFE=1 (note that to compile against CVS 2006-04-17 config.mk and Makefile.ref should be kept from HEAD as fixes to allow such compilation was committed after the patch):
before now bug 334261
With JS_THREADSAFE:
compiled with -O: 1761 1586 1505
compiled with -O2: 1670 1493 1491
Without JS_THREADSAFE:
compiled with -O: 1185 1133 1059
compiled with -O2: 1111 1033 951
I interpret this as the code in the patch is not optimized for the single threaded case while its more complex nature requires more efforts for the compiler to optimize with JS_THREADSAFE defined.
Assignee | ||
Comment 33•19 years ago
|
||
I'll try to get VS8 numbers, but anyone with similar hardware who can use MS's fine compilers, please report results.
/be
Assignee | ||
Comment 34•19 years ago
|
||
Just for the record, Blake and I at least are pretty put off by ECMA-262's weird "the original Foo prototype object" language. Note how 15.11.7.2 does *not* say to use the original value of, e.g., SyntaxError.prototype. Other lacunae noted by Andreas just beg the question: why? If the goal is to support optimization by avoiding reflection at runtime on each construction, it would be better to try to make the global properties bound to standard constructors readonly and dontdelete.
If not for this ECMA-262 language, I would prefer Igor's patch to parameterize internal constructor calls with atoms, and not this bug's patches. But the spec is the standard, and we probably should follow it, until we can fix it. Anyway we should figure out how to have both patches and best performance.
I will take this up with ECMA TG1 today or tomorrow.
/be
Comment 35•19 years ago
|
||
With the patch applied GCC complains:
jsobj.c:1810: warning: 'With' defined but not used
Before it was used in js_InitObjectClass via:
#if JS_HAS_OBJ_PROTO_PROP
if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
NULL, NULL, NULL, NULL)) {
return NULL;
}
#endif
but now that code is gone.
Comment 36•19 years ago
|
||
Comment on attachment 219076 [details] [diff] [review]
the rest of the story...
> /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
> if (OBJ_GET_CLASS(cx, ctor) == clasp) {
>- /* XXXMLM - this fails in framesets that are writing over
>- * themselves!
>- * JS_ASSERT(!OBJ_GET_PROTO(cx, ctor));
>- */
>+ JS_ASSERT(!OBJ_GET_PROTO(cx, ctor));
> OBJ_SET_PROTO(cx, ctor, proto);
> }
> }
This assertion, which was introduced as a comment in June 1998 and never was actually live until today, now fires and causes problems, see bug 334834
Comment 37•19 years ago
|
||
(In reply to comment #30)
> Could you also check the patch from the patch from bug 325724 apllied for CVS
> tree from 2006-04-17 ?
Loop overhead and total time, minimum of 8:
2006-04-18 2006-04-20 bug 325724 bug 334261
-O0 261 6394 266 4440 256 4454 260 567
-O1 183 3775 178 4150 188 3005 179 369
-O2 175 2951 175 2726 178 2611 173 337
-O2a 169 3171 169 2515 172 2474 172 343
-O3a 141 2759 143 2299 141 2315 139 288
2006-04-18 := CVS 2006-04-18 12:00 UTC
2006-04-20 := CVS synced early today
bug 325724 := 2006-04-18 + attachment 218944 [details] [diff] [review]
bug 334261 := 2006-04-20 + attachment 219038 [details] [diff] [review]
-O0 := -O0
-O1 := -O1
-O2 := -O2 -fomit-frame-pointer
-O2a := -O2 -fomit-frame-pointer -march=athlon
-O3a := -O3 -fomit-frame-pointer -march=athlon
Testcase used (to minimize loop overhead):
var n= 1e6, m= 8;
function test0() {
var i= n, str= "str", now= Date.now, t= now();
do str; while ( --i );
return now() - t;
}
function test() {
var i= n, str= "str", now= Date.now, t= now();
do str.length; while ( --i );
return now() - t;
}
for ( var a0= [], a= [], i= 0; i < m; ++i )
a0.push( test0() ), a.push( test() );
print( Math.min.apply( null, a0 ), Math.min.apply( null, a ) );
Comment 38•19 years ago
|
||
The times above were probably without any GC. Forcing GC to be included by
setting ulimit -v 4096, I get these results:
2006-04-18 2006-04-20 bug 325724 bug 334261
-O0 258 7052 265 5169 254 5144 258 566
-O1 183 4217 176 4619 186 3441 178 365
-O2 174 3361 174 3183 177 3044 172 335
-O2a 168 3584 168 2974 171 2894 171 341
-O3a 140 3178 141 2785 140 2712 139 284
Comment 39•19 years ago
|
||
This broke compiling with JS_VERSION < 160 because of missing
#if JS_HAS_XML_SUPPORT in jsproto.tbl.
Comment 40•19 years ago
|
||
(In reply to comment #39)
> This broke compiling with JS_VERSION < 160 because of missing
> #if JS_HAS_XML_SUPPORT in jsproto.tbl.
Filed bug 335001.
Depends on: 336054
Comment 41•19 years ago
|
||
ecma_3/String/regress-304376.js fails in the browser with
Expected value 'String', Actual value 'Array' FAILED!:
and in the shell with TypeError: "".join is not a function
The TypeError looks right. I'll modify the test to catch the exception. Not sure why the browser test fails.
Comment 42•19 years ago
|
||
browser:
BUGNUMBER: 304376
STATUS: String.prototype should be readonly and permanent
FAILED!: String.prototype should be readonly and permanent
FAILED!: Expected value 'String', Actual value 'Array'
FAILED!:
FAILED!: String.prototype should be readonly and permanent
FAILED!: Expected value 'TypeError', Actual value 'No Error'
FAILED!:
shell:
Testcase ecma_3/String/regress-304376.js failed Bug Number 304376
[ Top of Page ]
STATUS: String.prototype should be readonly and permanent
STATUS: TypeError: "".join is not a function
Failure messages were:
FAILED!: String.prototype should be readonly and permanent
FAILED!: Expected value 'String', Actual value 'Array'
FAILED!:
Updated•19 years ago
|
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Assignee | ||
Comment 43•19 years ago
|
||
mrbkap is all over this. The browser embedding has inner and outer globals, and the cached class object logic is blissfully ignorant of the rules for innerizing and outerizing.
/be
Assignee: brendan → mrbkap
Status: REOPENED → NEW
Comment 44•19 years ago
|
||
remove bogus check for the prototype name
Checking in regress-304376.js;
/cvsroot/mozilla/js/tests/ecma_3/String/regress-304376.js,v <-- regress-304376.js
new revision: 1.3; previous revision: 1.2
Reporter | ||
Updated•19 years ago
|
Status: NEW → RESOLVED
Closed: 19 years ago → 19 years ago
Resolution: --- → FIXED
Comment 46•19 years ago
|
||
it looks ok. I'll verify when I run it again with the fixed test case.
Checking in regress-304376.js;
/cvsroot/mozilla/js/tests/ecma_3/String/regress-304376.js,v <-- regress-304376.js
new revision: 1.4; previous revision: 1.3
done
Comment 47•19 years ago
|
||
Note the test case fails in the browser on the trunk with string.replace is not a function Page: http://test.mozilla.com/tests/mozilla.org/js/ecma_3/browser.js Line: 80 but it does pass in the shell. Note the test tries to reset the String constructor prior to any calls to a String method....
expect = 'TypeError';
var saveString = String;
String = Array;
try
{
// see if we can crash...
"".join();
String = saveString;
actual = 'No Error';
}
catch(ex)
{
String = saveString;
actual = ex.name;
printStatus(ex + '');
}
reportCompare(expect, actual, summary);
Comment 48•19 years ago
|
||
Brendan, Blake: any idea what is wrong with my test case or if the failure to reset the String constructor is this or a new bug?
Comment 49•19 years ago
|
||
something changed. verified fixed win/macppc/linux 2006070603 1.9a1
Status: RESOLVED → VERIFIED
Comment 50•19 years ago
|
||
fixed by Bug 336373 on the 1.8.1 branch.
verified fixed 1.8.1 with windows/macppc/linux 20060707
Keywords: verified1.8.1
You need to log in
before you can comment on or make changes to this bug.
Description
•