From ed7507ebd47ee9ef7c5af1a5dcf29ac92feaa4dc Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 8 Feb 2011 15:45:12 -0600 Subject: [PATCH 01/42] Bug 632612 - More thorough output from dumpObject debug-only function. r=Waldo, a=npotb. --HG-- extra : rebase_source : 70021cdeff6e904519cce542186236e9a4625030 --- js/src/jsobj.cpp | 55 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ab7808742f29..bc7a304731b9 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6794,20 +6794,30 @@ js_DumpId(jsid id) } static void -DumpShape(const Shape &shape) +DumpProperty(JSObject *obj, const Shape &shape) { jsid id = shape.id; uint8 attrs = shape.attributes(); - fprintf(stderr, " "); + fprintf(stderr, " ((Shape *) %p) ", (void *) &shape); if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate "); if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly "); if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent "); - if (attrs & JSPROP_GETTER) fprintf(stderr, "getter "); - if (attrs & JSPROP_SETTER) fprintf(stderr, "setter "); if (attrs & JSPROP_SHARED) fprintf(stderr, "shared "); if (shape.isAlias()) fprintf(stderr, "alias "); - if (shape.isMethod()) fprintf(stderr, "method(%p) ", (void *) &shape.methodObject()); + if (shape.isMethod()) fprintf(stderr, "method=%p ", (void *) &shape.methodObject()); + + if (shape.hasGetterValue()) + fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject()); + else if (!shape.hasDefaultGetter()) + fprintf(stderr, "getterOp=%p ", (void *) shape.getterOp()); + + if (shape.hasSetterValue()) + fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject()); + else if (shape.setterOp() == js_watch_set) + fprintf(stderr, "setterOp=js_watch_set "); + else if (!shape.hasDefaultSetter()) + fprintf(stderr, "setterOp=%p ", (void *) shape.setterOp()); if (JSID_IS_ATOM(id)) dumpString(JSID_TO_STRING(id)); @@ -6816,6 +6826,12 @@ DumpShape(const Shape &shape) else fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id)); fprintf(stderr, ": slot %d", shape.slot); + if (obj->containsSlot(shape.slot)) { + fprintf(stderr, " = "); + dumpValue(obj->getSlot(shape.slot)); + } else if (shape.slot != SHAPE_INVALID_SLOT) { + fprintf(stderr, " (INVALID!)"); + } fprintf(stderr, "\n"); } @@ -6830,7 +6846,7 @@ js_DumpObject(JSObject *obj) uint32 flags = obj->flags; if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate"); if (flags & JSObject::SYSTEM) fprintf(stderr, " system"); - if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not extensible"); + if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not_extensible"); if (flags & JSObject::BRANDED) fprintf(stderr, " branded"); if (flags & JSObject::GENERIC) fprintf(stderr, " generic"); if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier"); @@ -6865,15 +6881,6 @@ js_DumpObject(JSObject *obj) return; } - if (obj->isNative()) { - fprintf(stderr, "properties:\n"); - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) - DumpShape(r.front()); - } else { - if (!obj->isNative()) - fprintf(stderr, "not native\n"); - } - fprintf(stderr, "proto "); dumpValue(ObjectOrNullValue(obj->getProto())); fputc('\n', stderr); @@ -6885,10 +6892,15 @@ js_DumpObject(JSObject *obj) if (clasp->flags & JSCLASS_HAS_PRIVATE) fprintf(stderr, "private %p\n", obj->getPrivate()); - fprintf(stderr, "slots:\n"); + if (!obj->isNative()) + fprintf(stderr, "not native\n"); + unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); unsigned slots = obj->slotSpan(); - for (unsigned i = 0; i < slots; i++) { + unsigned stop = obj->isNative() ? reservedEnd : slots; + if (stop > 0) + fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n"); + for (unsigned i = 0; i < stop; i++) { fprintf(stderr, " %3d ", i); if (i < reservedEnd) fprintf(stderr, "(reserved) "); @@ -6896,6 +6908,15 @@ js_DumpObject(JSObject *obj) dumpValue(obj->getSlot(i)); fputc('\n', stderr); } + + if (obj->isNative()) { + fprintf(stderr, "properties:\n"); + Vector props; + for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) + props.append(&r.front()); + for (size_t i = props.length(); i-- != 0;) + DumpProperty(obj, *props[i]); + } fputc('\n', stderr); } From 7032e92ee5b50961fb9fcc6eae47ff34ab2678f5 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 17 Feb 2011 16:10:10 -0600 Subject: [PATCH 02/42] Remove bogus assertions in the tracer and in ArrayCompPushImpl helper function. Bug 630377, r=brendan. --HG-- extra : rebase_source : 1ed9f873b630f7078f275846d95fbd68b4daeabe --- js/src/jit-test/tests/basic/bug630377.js | 7 +++++++ js/src/jsarray.cpp | 18 ++++++++++++------ js/src/jstracer.cpp | 1 - js/src/tests/js1_8_5/extensions/jstests.list | 1 + .../tests/js1_8_5/extensions/regress-630377.js | 11 +++++++++++ 5 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug630377.js create mode 100644 js/src/tests/js1_8_5/extensions/regress-630377.js diff --git a/js/src/jit-test/tests/basic/bug630377.js b/js/src/jit-test/tests/basic/bug630377.js new file mode 100644 index 000000000000..8fe97e29c0a4 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug630377.js @@ -0,0 +1,7 @@ +var a = []; +for (var i = 0; i < RUNLOOP; i++) + a[i] = 0; +var b = #1=[x === "0" && (#1#.slow = 1) for (x in a)]; +assertEq(b[0], 1); +for (var i = 1; i < RUNLOOP; i++) + assertEq(b[i], false); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 278f24d0931c..8d90650190a9 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2059,28 +2059,34 @@ array_push1_dense(JSContext* cx, JSObject* obj, const Value &v, Value *rval) JS_ALWAYS_INLINE JSBool ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v) { + uint32 length = obj->getArrayLength(); + if (obj->isSlowArray()) { + /* This can happen in one evil case. See bug 630377. */ + jsid id; + return js_IndexToId(cx, length, &id) && + js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE); + } + JS_ASSERT(obj->isDenseArray()); - uint32_t length = obj->getArrayLength(); JS_ASSERT(length <= obj->getDenseArrayCapacity()); if (length == obj->getDenseArrayCapacity()) { if (length > JS_ARGS_LENGTH_MAX) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_ARRAY_INIT_TOO_BIG); - return JS_FALSE; + return false; } /* - * Array comprehension cannot add holes to the array and never leaks - * the array before it is fully initialized. So we can use ensureSlots - * instead of ensureDenseArrayElements. + * An array comprehension cannot add holes to the array. So we can use + * ensureSlots instead of ensureDenseArrayElements. */ if (!obj->ensureSlots(cx, length + 1)) return false; } obj->setArrayLength(length + 1); obj->setDenseArrayElement(length, v); - return JS_TRUE; + return true; } JSBool diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 932c89463498..296f0ed79eb3 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -16309,7 +16309,6 @@ TraceRecorder::record_JSOP_ARRAYPUSH() JS_ASSERT(cx->fp()->slots() + slot < cx->regs->sp - 1); Value &arrayval = cx->fp()->slots()[slot]; JS_ASSERT(arrayval.isObject()); - JS_ASSERT(arrayval.toObject().isDenseArray()); LIns *array_ins = get(&arrayval); Value &elt = stackval(-1); LIns *elt_ins = box_value_for_native_call(elt, get(&elt)); diff --git a/js/src/tests/js1_8_5/extensions/jstests.list b/js/src/tests/js1_8_5/extensions/jstests.list index 6842e5736de8..e60130b9e9cf 100644 --- a/js/src/tests/js1_8_5/extensions/jstests.list +++ b/js/src/tests/js1_8_5/extensions/jstests.list @@ -32,4 +32,5 @@ script regress-627984-4.js script regress-627984-5.js script regress-627984-6.js script regress-627984-7.js +script regress-630377.js script regress-631723.js diff --git a/js/src/tests/js1_8_5/extensions/regress-630377.js b/js/src/tests/js1_8_5/extensions/regress-630377.js new file mode 100644 index 000000000000..8a5c9e0af17a --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-630377.js @@ -0,0 +1,11 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +function f(x) { + x.q = 1; +} +var a = #1=[f(#1#) for (i in [0])]; +assertEq(a.q, 1); +assertEq(a[0], void 0); + +reportCompare(0, 0, 'ok'); From 7ce1ece928cd15460d415dd3265d0aea21646267 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Feb 2011 14:31:52 -0800 Subject: [PATCH 03/42] Bug 632901 - TM: crash when assigning to function.arguments. r=dvander. --- js/src/jit-test/tests/basic/bug632901.js | 9 +++++++++ js/src/jstracer.cpp | 23 +++++++++++++++++++---- js/src/jstracer.h | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug632901.js diff --git a/js/src/jit-test/tests/basic/bug632901.js b/js/src/jit-test/tests/basic/bug632901.js new file mode 100644 index 000000000000..c0d5a4d6a599 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug632901.js @@ -0,0 +1,9 @@ +// don't crash when tracing +function f(o) { + var prop = "arguments"; + f[prop] = f[prop]; +} +for(var i=0; i<10; i++) { + f(); +} + diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 296f0ed79eb3..28418dcff8f6 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10328,7 +10328,7 @@ class BoxArg * argument values into the object as properties in case it is used after * this frame returns. */ -JS_REQUIRES_STACK void +JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::putActivationObjects() { JSStackFrame *const fp = cx->fp(); @@ -10336,7 +10336,20 @@ TraceRecorder::putActivationObjects() bool have_call = fp->isFunctionFrame() && fp->fun()->isHeavyweight(); if (!have_args && !have_call) - return; + return ARECORD_CONTINUE; + + if (have_args && !fp->script()->usesArguments) { + /* + * have_args is true, so |arguments| has been accessed, but + * usesArguments is false, so there's no statically visible access. + * It must have been a dodgy access like |f["arguments"]|; just + * abort. (In the case where the record-time property name is not + * "arguments" but a later run-time property name is, we wouldn't have + * emitted the call to js_PutArgumentsOnTrace(), and js_GetArgsValue() + * will deep bail asking for the top JSStackFrame.) + */ + RETURN_STOP_A("dodgy arguments access"); + } uintN nformal = fp->numFormalArgs(); uintN nactual = fp->numActualArgs(); @@ -10380,6 +10393,8 @@ TraceRecorder::putActivationObjects() w.nameImmi(fp->numFormalArgs()), scopeChain_ins, cx_ins }; w.call(&js_PutCallObjectOnTrace_ci, args); } + + return ARECORD_CONTINUE; } JS_REQUIRES_STACK AbortableRecordingStatus @@ -10572,7 +10587,7 @@ TraceRecorder::record_JSOP_RETURN() return endLoop(); } - putActivationObjects(); + CHECK_STATUS_A(putActivationObjects()); if (Probes::callTrackingActive(cx)) { LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins }; @@ -16219,7 +16234,7 @@ TraceRecorder::record_JSOP_STOP() return ARECORD_CONTINUE; } - putActivationObjects(); + CHECK_STATUS_A(putActivationObjects()); if (Probes::callTrackingActive(cx)) { LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins }; diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 823d3ab161e5..e9f7b4fb0040 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1482,7 +1482,7 @@ class TraceRecorder VMSideExit* exit); JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v); JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker(); - JS_REQUIRES_STACK void putActivationObjects(); + JS_REQUIRES_STACK AbortableRecordingStatus putActivationObjects(); JS_REQUIRES_STACK RecordingStatus createThis(JSObject& ctor, nanojit::LIns* ctor_ins, nanojit::LIns** thisobj_insp); JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee); From 96fdc3dfd9ed48a013337b00042d24e9c1b9aad2 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 11 Feb 2011 14:14:50 -0800 Subject: [PATCH 04/42] Bug 621432 - Properly reconstruct the pcstack when decompiling for an exception thrown during script prolog execution. r=jorendorff, a=beltzner --HG-- extra : rebase_source : ed51ace3c3c045441ea2314e54cb458261d40f19 --- js/src/jsopcode.cpp | 4 +-- .../adding-global-var-nonextensible-error.js | 35 +++++++++++++++++++ js/src/tests/ecma_5/Global/jstests.list | 1 + 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 1c58107abe8b..05da515022f6 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -5489,8 +5489,8 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, return ReconstructImacroPCStack(cx, script, imacstart, target, pcstack); #endif - LOCAL_ASSERT(script->main <= target && target < script->code + script->length); - jsbytecode *pc = script->main; + LOCAL_ASSERT(script->code <= target && target < script->code + script->length); + jsbytecode *pc = script->code; uintN pcdepth = 0; ptrdiff_t oplen; for (; pc < target; pc += oplen) { diff --git a/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js b/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js new file mode 100644 index 000000000000..f4eaf00a293f --- /dev/null +++ b/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js @@ -0,0 +1,35 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 621432; +var summary = + "If a var statement can't create a global property because the global " + + "object isn't extensible, and an error is thrown while decompiling the " + + "global, don't assert"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var toSource = []; +Object.preventExtensions(this); + +try +{ + eval("var x;"); + throw new Error("no error thrown"); +} +catch (e) +{ + assertEq(e instanceof TypeError, true, "expected TypeError, got: " + e); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 1da36470ae4b..5976eb0a6f99 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -1,4 +1,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ +script adding-global-var-nonextensible-error.js script parseInt-01.js script parseFloat-01.js script eval-01.js From 72b24c5e258f3f08fac4be593d7bb814cffdbc59 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Thu, 17 Feb 2011 14:17:24 -0800 Subject: [PATCH 05/42] Bug 633741 - Check result of defineProperty in js_InitFunctionAndObjectClasses. r=jwalden, a=dmandelin --HG-- extra : rebase_source : 25e7e6dce22a5f5eb57a7954bcc39415050b5595 --- js/src/jsapi.cpp | 6 ++++-- js/src/tests/js1_8_5/regress/jstests.list | 1 + js/src/tests/js1_8_5/regress/regress-633741.js | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 js/src/tests/js1_8_5/regress/regress-633741.js diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8e81c59842ed..ca3acefdaf18 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1447,8 +1447,10 @@ js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) ctor = JS_GetConstructor(cx, fun_proto); if (!ctor) return NULL; - obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)), - ObjectValue(*ctor), 0, 0, 0); + if (!obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)), + ObjectValue(*ctor), 0, 0, 0)) { + return NULL; + } } /* Initialize the object class next so Object.prototype works. */ diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index 09493bc26a47..a2f88246a261 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -84,6 +84,7 @@ script regress-624199.js script regress-624547.js script regress-624968.js script regress-626436.js +fails-if(xulRuntime.shell) script regress-633741.js script regress-634210-1.js script regress-634210-2.js script regress-634210-3.js diff --git a/js/src/tests/js1_8_5/regress/regress-633741.js b/js/src/tests/js1_8_5/regress/regress-633741.js new file mode 100644 index 000000000000..d77aba9b43a0 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-633741.js @@ -0,0 +1,17 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributors: Jan de Mooij + */ + +Object.preventExtensions(this); +delete Function; + +try { + /* Don't assert. */ + Object.getOwnPropertyNames(this); +} catch(e) { + reportCompare(true, false, "this shouldn't have thrown"); +} +reportCompare(0, 0, "ok"); + From 20543f0350d5b7b8a8039ab066c30fcda33a3bd3 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 17 Feb 2011 14:21:52 -0800 Subject: [PATCH 06/42] Fix a JSBool-returning function to return false rather than NULL in one place, and use the func-to-pointer macro to avoid two further warnings. r+a=warning-fix --HG-- extra : rebase_source : 8d7ff178c8e6c25c68dd26e1124b66dcbd333627 --- js/src/jsobj.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index bc7a304731b9..b2925942c7cc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4772,7 +4772,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu JS_ASSERT(existingShape->getter() != getter); if (!obj->methodReadBarrier(cx, *existingShape, &valueCopy)) - return NULL; + return false; } } else { adding = true; @@ -6810,14 +6810,14 @@ DumpProperty(JSObject *obj, const Shape &shape) if (shape.hasGetterValue()) fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject()); else if (!shape.hasDefaultGetter()) - fprintf(stderr, "getterOp=%p ", (void *) shape.getterOp()); + fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp())); if (shape.hasSetterValue()) fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject()); else if (shape.setterOp() == js_watch_set) fprintf(stderr, "setterOp=js_watch_set "); else if (!shape.hasDefaultSetter()) - fprintf(stderr, "setterOp=%p ", (void *) shape.setterOp()); + fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp())); if (JSID_IS_ATOM(id)) dumpString(JSID_TO_STRING(id)); From 66d95c44a963e249a41f54decfc561ec66f5ebfc Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 17 Feb 2011 16:52:53 -0800 Subject: [PATCH 07/42] Bug 633741 - Followup to fix an older test that expected non-standard behavior (which we don't quite implement, but it's not so important that it be fixed immediately now). r=orange --- .../ecma_5/Global/adding-global-var-nonextensible-error.js | 2 +- js/src/tests/ecma_5/Global/jstests.list | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js b/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js index f4eaf00a293f..9299797f6db3 100644 --- a/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js +++ b/js/src/tests/ecma_5/Global/adding-global-var-nonextensible-error.js @@ -24,7 +24,7 @@ try } catch (e) { - assertEq(e instanceof TypeError, true, "expected TypeError, got: " + e); + reportCompare(e instanceof TypeError, true, "expected TypeError, got: " + e); } /******************************************************************************/ diff --git a/js/src/tests/ecma_5/Global/jstests.list b/js/src/tests/ecma_5/Global/jstests.list index 5976eb0a6f99..5fc5a14ed6b7 100644 --- a/js/src/tests/ecma_5/Global/jstests.list +++ b/js/src/tests/ecma_5/Global/jstests.list @@ -1,5 +1,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Global/ -script adding-global-var-nonextensible-error.js +fails-if(!xulRuntime.shell) script adding-global-var-nonextensible-error.js script parseInt-01.js script parseFloat-01.js script eval-01.js From 2e75eef92d92daf7bc266f5f5e4a29093c2eeb97 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Feb 2011 17:52:55 -0800 Subject: [PATCH 08/42] Unqualified function invocation doesn't use the global object the property was gotten from as |this| (bug 634590, r=brendan). --- js/src/jit-test/tests/basic/testBug634590.js | 7 ++ js/src/jsinterp.cpp | 73 +++++++++++++------- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 31 ++++++++- js/src/jsobj.h | 14 +++- js/src/jspropertycache.cpp | 4 ++ js/src/jstracer.cpp | 4 +- js/src/methodjit/PolyIC.cpp | 4 +- 8 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug634590.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js new file mode 100644 index 000000000000..da47ed0090df --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -0,0 +1,7 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), + "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index fde50aec9f10..4313e8bff616 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2178,6 +2178,29 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } +static inline bool +SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +{ + if (!funval.isObject() || + (obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && IsCacheableCallee(cx, funval)) { + /* + * We can avoid computing 'this' eagerly and push the implicit 'this' + * value (undefined), as long the scope is cachable and we are not + * crossing into another scope (in which case lazy calculation of 'this' + * would pick up the new and incorrect scope). 'strict' functions are an + * exception. We don't want to eagerly calculate 'this' for them even if + * the callee is in a different scope. + */ + *vp = UndefinedValue(); + return true; + } + + if (!(obj = obj->thisObject(cx))) + return false; + *vp = ObjectValue(*obj); + return true; +} + namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2203,8 +2226,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4792,26 +4815,28 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj) \ - JS_BEGIN_MACRO \ - Class *clasp; \ - JSObject *thisp = obj; \ - if (!thisp->getParent() || \ - (clasp = thisp->getClass()) == &js_CallClass || \ - clasp == &js_BlockClass || \ - clasp == &js_DeclEnvClass) { \ - /* Push the ImplicitThisValue for the Environment Record */ \ - /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ - /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ - /* (Function Calls). */ \ - PUSH_UNDEFINED(); \ - } else { \ - thisp = thisp->thisObject(cx); \ - if (!thisp) \ - goto error; \ - PUSH_OBJECT(*thisp); \ - } \ - JS_END_MACRO +/* + * Eager 'this' coercion slow path. If we end up calculating 'this' eagerly, + * purge the property cache since we don't perform eager 'this' coercion in + * the property cache hit fast path. + */ +#define SLOW_PUSH_THISV(cx, obj, funval) \ + JS_BEGIN_MACRO \ + Value v; \ + if (!SlowThis(cx, obj, funval, &v)) \ + goto error; \ + if (!v.isUndefined()) { \ + PropertyCacheEntry *entry; \ + JSObject *obj2; \ + JSAtom *atom; \ + JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom); \ + if (!atom) { \ + ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); \ + memset(entry, 0, sizeof(*entry)); \ + } \ + } \ + PUSH_COPY(v); \ + JS_END_MACRO \ BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4892,7 +4917,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6395,7 +6420,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c55c868b3feb..c976cdea8da1 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b2925942c7cc..5d2e3385234a 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4445,6 +4445,31 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } +namespace js { + +bool +IsCacheableCallee(JSContext *cx, const Value &funval) +{ + JS_ASSERT(funval.isObject()); + + /* + * Look past any wrappers. If the callee is a strict function it is always + * cacheable because we won't do 'this' coercion in strict mode. Otherwise + * the callee is only cacheable if it is in the current scope. Without this + * restriction lazy 'this' computation would pick up the wrong global in + * the other scope. + */ + JSObject *funobj = funval.toObject().unwrap(); + if (funobj->isFunction()) { + JSFunction *fun = funobj->getFunctionPrivate(); + if (fun->isInterpreted() && fun->inStrictMode()) + return true; + } + return funobj->getGlobal() == cx->fp()->scopeChain().getGlobal(); +} + +} + /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5052,7 +5077,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? js_IsCacheableNonGlobalScope(obj) + ? IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5162,11 +5187,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because js_IsCacheableNonGlobalScope + * The test order here matters because IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || js_IsCacheableNonGlobalScope(obj); + !obj->getParent() || IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 27d59fa02121..88ed1b42096b 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,16 +1661,19 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); +extern JS_FRIEND_DATA(js::Class) js_CallClass; +extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; + +namespace js { + /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ inline bool -js_IsCacheableNonGlobalScope(JSObject *obj) +IsCacheableNonGlobalScope(JSObject *obj) { - extern JS_FRIEND_DATA(js::Class) js_CallClass; - extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1682,6 +1685,11 @@ js_IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } +bool +IsCacheableCallee(JSContext *cx, const Value &funval); + +} + /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jspropertycache.cpp b/js/src/jspropertycache.cpp index bcb22d80c29f..910ea6a91c80 100644 --- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -155,6 +155,8 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI JS_ASSERT(pobj->hasMethodBarrier()); JSObject &funobj = shape->methodObject(); JS_ASSERT(&funobj == &pobj->nativeGetSlot(shape->slot).toObject()); + if (!IsCacheableCallee(cx, ObjectValue(funobj))) + return JS_NO_PROP_CACHE_FILL; vword.setFunObj(funobj); break; } @@ -191,6 +193,8 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI if (!pobj->brand(cx)) return JS_NO_PROP_CACHE_FILL; } + if (!IsCacheableCallee(cx, v)) + return JS_NO_PROP_CACHE_FILL; vword.setFunObj(*funobj); break; } diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 28418dcff8f6..81d958d91f81 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!js_IsCacheableNonGlobalScope(child)) { + if (!IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15189,7 +15189,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!js_IsCacheableNonGlobalScope(obj)) + if (!IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index a954a7536819..aa82dabf6dbf 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From d3f2662ac808e3c7be7a53204c49219bdb0968bd Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Thu, 17 Feb 2011 18:54:05 -0800 Subject: [PATCH 09/42] Backed out changeset b0aa9c20ffe4 -- orange on tinderbox --- js/src/jit-test/tests/basic/testBug634590.js | 7 -- js/src/jsinterp.cpp | 73 +++++++------------- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 31 +-------- js/src/jsobj.h | 14 +--- js/src/jspropertycache.cpp | 4 -- js/src/jstracer.cpp | 4 +- js/src/methodjit/PolyIC.cpp | 4 +- 8 files changed, 35 insertions(+), 104 deletions(-) delete mode 100644 js/src/jit-test/tests/basic/testBug634590.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js deleted file mode 100644 index da47ed0090df..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ /dev/null @@ -1,7 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), - "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 4313e8bff616..fde50aec9f10 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2178,29 +2178,6 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } -static inline bool -SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) -{ - if (!funval.isObject() || - (obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && IsCacheableCallee(cx, funval)) { - /* - * We can avoid computing 'this' eagerly and push the implicit 'this' - * value (undefined), as long the scope is cachable and we are not - * crossing into another scope (in which case lazy calculation of 'this' - * would pick up the new and incorrect scope). 'strict' functions are an - * exception. We don't want to eagerly calculate 'this' for them even if - * the callee is in a different scope. - */ - *vp = UndefinedValue(); - return true; - } - - if (!(obj = obj->thisObject(cx))) - return false; - *vp = ObjectValue(*obj); - return true; -} - namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2226,8 +2203,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4815,28 +4792,26 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -/* - * Eager 'this' coercion slow path. If we end up calculating 'this' eagerly, - * purge the property cache since we don't perform eager 'this' coercion in - * the property cache hit fast path. - */ -#define SLOW_PUSH_THISV(cx, obj, funval) \ - JS_BEGIN_MACRO \ - Value v; \ - if (!SlowThis(cx, obj, funval, &v)) \ - goto error; \ - if (!v.isUndefined()) { \ - PropertyCacheEntry *entry; \ - JSObject *obj2; \ - JSAtom *atom; \ - JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom); \ - if (!atom) { \ - ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); \ - memset(entry, 0, sizeof(*entry)); \ - } \ - } \ - PUSH_COPY(v); \ - JS_END_MACRO \ +#define SLOW_PUSH_THISV(cx, obj) \ + JS_BEGIN_MACRO \ + Class *clasp; \ + JSObject *thisp = obj; \ + if (!thisp->getParent() || \ + (clasp = thisp->getClass()) == &js_CallClass || \ + clasp == &js_BlockClass || \ + clasp == &js_DeclEnvClass) { \ + /* Push the ImplicitThisValue for the Environment Record */ \ + /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ + /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ + /* (Function Calls). */ \ + PUSH_UNDEFINED(); \ + } else { \ + thisp = thisp->thisObject(cx); \ + if (!thisp) \ + goto error; \ + PUSH_OBJECT(*thisp); \ + } \ + JS_END_MACRO BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4917,7 +4892,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_NAME) @@ -6420,7 +6395,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c976cdea8da1..c55c868b3feb 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 5d2e3385234a..b2925942c7cc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4445,31 +4445,6 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } -namespace js { - -bool -IsCacheableCallee(JSContext *cx, const Value &funval) -{ - JS_ASSERT(funval.isObject()); - - /* - * Look past any wrappers. If the callee is a strict function it is always - * cacheable because we won't do 'this' coercion in strict mode. Otherwise - * the callee is only cacheable if it is in the current scope. Without this - * restriction lazy 'this' computation would pick up the wrong global in - * the other scope. - */ - JSObject *funobj = funval.toObject().unwrap(); - if (funobj->isFunction()) { - JSFunction *fun = funobj->getFunctionPrivate(); - if (fun->isInterpreted() && fun->inStrictMode()) - return true; - } - return funobj->getGlobal() == cx->fp()->scopeChain().getGlobal(); -} - -} - /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5077,7 +5052,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? IsCacheableNonGlobalScope(obj) + ? js_IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5187,11 +5162,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because IsCacheableNonGlobalScope + * The test order here matters because js_IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || IsCacheableNonGlobalScope(obj); + !obj->getParent() || js_IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 88ed1b42096b..27d59fa02121 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,19 +1661,16 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); -extern JS_FRIEND_DATA(js::Class) js_CallClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - -namespace js { - /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ inline bool -IsCacheableNonGlobalScope(JSObject *obj) +js_IsCacheableNonGlobalScope(JSObject *obj) { + extern JS_FRIEND_DATA(js::Class) js_CallClass; + extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1685,11 +1682,6 @@ IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } -bool -IsCacheableCallee(JSContext *cx, const Value &funval); - -} - /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jspropertycache.cpp b/js/src/jspropertycache.cpp index 910ea6a91c80..bcb22d80c29f 100644 --- a/js/src/jspropertycache.cpp +++ b/js/src/jspropertycache.cpp @@ -155,8 +155,6 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI JS_ASSERT(pobj->hasMethodBarrier()); JSObject &funobj = shape->methodObject(); JS_ASSERT(&funobj == &pobj->nativeGetSlot(shape->slot).toObject()); - if (!IsCacheableCallee(cx, ObjectValue(funobj))) - return JS_NO_PROP_CACHE_FILL; vword.setFunObj(funobj); break; } @@ -193,8 +191,6 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoI if (!pobj->brand(cx)) return JS_NO_PROP_CACHE_FILL; } - if (!IsCacheableCallee(cx, v)) - return JS_NO_PROP_CACHE_FILL; vword.setFunObj(*funobj); break; } diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 81d958d91f81..28418dcff8f6 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!IsCacheableNonGlobalScope(child)) { + if (!js_IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15189,7 +15189,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!IsCacheableNonGlobalScope(obj)) + if (!js_IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index aa82dabf6dbf..a954a7536819 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From d33de0756616f015b97c145f5c14e1fe086f0084 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Feb 2011 19:02:48 -0800 Subject: [PATCH 10/42] Bug 634444 - Errors in long lines cause memory spikes when a console is in use. r=brendan, a=blocking. --- js/src/jsscan.cpp | 121 +++++++++++++++++++++++++++++++--------------- js/src/jsscan.h | 2 +- 2 files changed, 84 insertions(+), 39 deletions(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 028f4f675b42..1f4556e3b3e1 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -371,8 +371,9 @@ TokenStream::peekChars(intN n, jschar *cp) return i == n; } +/* Finds the next EOL, but stops once 'max' chars past the start of the token have been scanned. */ jschar * -TokenStream::findEOL() +TokenStream::findWlineLimit(jschar *tokptr, int max) { TokenBuf tmpUserbuf = userbuf; jschar *tmpLinebase = linebase; @@ -380,12 +381,23 @@ TokenStream::findEOL() uintN tmpFlags = flags; uintN tmpLineno = lineno; + JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit); + userbuf.ptr = tokptr; /* Start scanning at tokptr. */ + + jschar *wlinelimit; + jschar *wlinelimitmax = tokptr + max + 1; while (true) { - int32 c = getChar(); - if (c == '\n' || c == EOF) + if (userbuf.ptr > wlinelimitmax) { + wlinelimit = wlinelimitmax; break; + } + jschar* next = userbuf.ptr; + int32 c = getChar(); + if (c == '\n' || c == EOF) { + wlinelimit = next; + break; + } } - jschar *linelimit = userbuf.ptr; /* Need to restore everything changed by getChar(). */ userbuf = tmpUserbuf; @@ -394,7 +406,7 @@ TokenStream::findEOL() flags = tmpFlags; lineno = tmpLineno; - return linelimit; + return wlinelimit; } bool @@ -403,10 +415,15 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro { JSErrorReport report; char *message; - size_t linelength; - jschar *linechars; - jschar *linelimit; - char *linebytes; + + /* "wline" is short for "window into line", because we might not show it all. */ + size_t wlinelength; + jschar *wlinechars; + jschar *wlinelimit; + jschar *wlinebase; + jschar *tokptr; + char *wlinebytes; + bool warning; JSBool ok; TokenPos *tp; @@ -426,8 +443,8 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro report.flags = flags; report.errorNumber = errorNumber; message = NULL; - linechars = NULL; - linebytes = NULL; + wlinechars = NULL; + wlinebytes = NULL; MUST_FLOW_THROUGH("out"); ok = js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, @@ -451,29 +468,49 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro } report.lineno = lineno; - linelimit = findEOL(); - linelength = linelimit - linebase; - - linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar)); - if (!linechars) { - warning = false; - goto out; - } - memcpy(linechars, linebase, linelength * sizeof(jschar)); - linechars[linelength] = 0; - linebytes = js_DeflateString(cx, linechars, linelength); - if (!linebytes) { - warning = false; - goto out; - } - report.linebuf = linebytes; /* the offending source line, without final \n */ - index = (tp->begin.lineno == tp->end.lineno) - ? tp->begin.index /* the column number of the start of the bad token */ - : 0; + ? tp->begin.index /* the column number of the start of the bad token */ + : 0; /* the bad token didn't start on this line; don't give a column */ + tokptr = linebase + index; + + /* + * We show only a portion of the line around the erroneous token -- WINDOW + * chars before and after the first char in the token. This is because + * lines can be very long and printing the whole line is (a) not that + * helpful, and (b) can waste a lot of memory. See bug 634444. + */ + static const size_t WINDOW = 100; + + /* Truncate at the front if necessary. */ + if (linebase + WINDOW < tokptr) { + wlinebase = tokptr - WINDOW; + size_t nTrunc = wlinebase - linebase; + index -= nTrunc; + } else { + wlinebase = linebase; + } + + /* Find EOL, or truncate at the back if necessary. */ + wlinelimit = findWlineLimit(tokptr, WINDOW); + + wlinelength = wlinelimit - wlinebase; + JS_ASSERT(wlinelength <= WINDOW * 2 + 1); + wlinechars = (jschar *)cx->malloc((wlinelength + 1) * sizeof(jschar)); + if (!wlinechars) { + warning = false; + goto out; + } + memcpy(wlinechars, wlinebase, wlinelength * sizeof(jschar)); + wlinechars[wlinelength] = 0; + wlinebytes = js_DeflateString(cx, wlinechars, wlinelength); + if (!wlinebytes) { + warning = false; + goto out; + } + report.linebuf = wlinebytes; /* some or all of the offending source line, without final \n */ report.tokenptr = report.linebuf + index; - report.uclinebuf = linechars; + report.uclinebuf = wlinechars; report.uctokenptr = report.uclinebuf + index; /* @@ -530,10 +567,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro (*onError)(cx, message, &report); out: - if (linebytes) - cx->free(linebytes); - if (linechars) - cx->free(linechars); + if (wlinebytes) + cx->free(wlinebytes); + if (wlinechars) + cx->free(wlinechars); if (message) cx->free(message); if (report.ucmessage) @@ -772,6 +809,7 @@ TokenStream::newToken(ptrdiff_t adjust) cursor = (cursor + 1) & ntokensMask; Token *tp = &tokens[cursor]; tp->ptr = userbuf.ptr + adjust; + JS_ASSERT(tp->ptr >= linebase); tp->pos.begin.index = tp->ptr - linebase; tp->pos.begin.lineno = tp->pos.end.lineno = lineno; return tp; @@ -800,6 +838,7 @@ TokenStream::getTokenInternal() Token *tp; JSAtom *atom; bool hadUnicodeEscape; + int adjust; #if JS_HAS_XML_SUPPORT JSBool inTarget; size_t targetLength; @@ -975,22 +1014,28 @@ TokenStream::getTokenInternal() retry: /* - * This gets the next non-space char and starts the token. + * This gets the next non-space char and starts the token. adjust is set + * to -1 because we'll probably scan the first char of the upcoming token + * while chewing up the whitespace. */ + adjust = -1; do { c = getChar(); if (c == '\n') { flags &= ~TSF_DIRTYLINE; - if (flags & TSF_NEWLINES) + if (flags & TSF_NEWLINES) { + adjust = 0; /* early break means we didn't scan an extra char */ break; + } } } while (ScanAsSpace((jschar)c)); - tp = newToken(-1); if (c == EOF) { + tp = newToken(0); /* no -1 here because userbuf.ptr isn't incremented for EOF */ tt = TOK_EOF; goto out; } + tp = newToken(adjust); /* * Look for an identifier. diff --git a/js/src/jsscan.h b/js/src/jsscan.h index b65219e7d947..7157124e52cc 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -476,7 +476,7 @@ class TokenStream bool matchUnicodeEscapeIdent(int32 *c); JSBool peekChars(intN n, jschar *cp); JSBool getXMLEntity(); - jschar *findEOL(); + jschar *findWlineLimit(jschar *tokptr, int max); JSBool matchChar(int32 expect) { int32 c = getChar(); From 30721671f770a814a8e14d7708a99e7aa7a13f84 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Thu, 17 Feb 2011 19:13:28 -0800 Subject: [PATCH 11/42] Fix for bug 634855 (Memory leak with NoScript 2.0.9.8 installed). r=bent, a=sicking. --HG-- extra : transplant_source : %97cf%7D%FCJf%8Dl%0B%E3%CBH%1AN%FF%0C%D7%3E%F6 --- js/src/xpconnect/src/xpcprivate.h | 20 +++++++++++++++++--- js/src/xpconnect/src/xpcwrappednative.cpp | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 6e09edd5474a..357ed32ffade 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -4557,10 +4557,24 @@ struct CompartmentPrivate return expandoMap->Put(wn, expando); } + /** + * This lookup does not change the color of the JSObject meaning that the + * object returned is not guaranteed to be kept alive past the next CC. + * + * This should only be called if you are certain that the return value won't + * be passed into a JS API function and that it won't be stored without + * being rooted (or otherwise signaling the stored value to the CC). + */ + JSObject *LookupExpandoObjectPreserveColor(XPCWrappedNative *wn) { + return expandoMap ? expandoMap->Get(wn) : nsnull; + } + + /** + * This lookup clears the gray bit before handing out the JSObject which + * means that the object is guaranteed to be kept alive past the next CC. + */ JSObject *LookupExpandoObject(XPCWrappedNative *wn) { - if (!expandoMap) - return nsnull; - JSObject *obj = expandoMap->Get(wn); + JSObject *obj = LookupExpandoObjectPreserveColor(wn); xpc_UnmarkGrayObject(obj); return obj; } diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 279a8549a7be..cb4e198fcdd7 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -81,7 +81,7 @@ TraverseExpandoObjects(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compart NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(closure->cb, "XPCWrappedNative expando object"); closure->cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, - priv->LookupExpandoObject(closure->wn)); + priv->LookupExpandoObjectPreserveColor(closure->wn)); return PL_DHASH_NEXT; } From 695f6a01ee20cba4553d87e17beba5aa6563078e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Feb 2011 20:47:24 -0800 Subject: [PATCH 12/42] Avoid assertion failures for bug 634444. r=me, a=blocking. --- js/src/jsscan.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 1f4556e3b3e1..54174b7b6b7d 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -381,7 +381,8 @@ TokenStream::findWlineLimit(jschar *tokptr, int max) uintN tmpFlags = flags; uintN tmpLineno = lineno; - JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit); + /* FIXME: this assertion is commented out due to bug 635144. */ + /*JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit);*/ userbuf.ptr = tokptr; /* Start scanning at tokptr. */ jschar *wlinelimit; From 2c33e0e654856da4f0b776b2541b7130884b5c7f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 18 Feb 2011 11:19:49 -0800 Subject: [PATCH 13/42] Don't trace arguments in strict mode (bug 632924, r=njn). --- js/src/jstracer.cpp | 18 ++++++------------ js/src/jstracer.h | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 28418dcff8f6..79fddf099902 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10654,7 +10654,7 @@ TraceRecorder::record_JSOP_IFNE() } LIns* -TraceRecorder::newArguments(LIns* callee_ins, bool strict) +TraceRecorder::newArguments(LIns* callee_ins) { LIns* global_ins = w.immpObjGC(globalObj); LIns* argc_ins = w.nameImmi(cx->fp()->numActualArgs()); @@ -10663,13 +10663,6 @@ TraceRecorder::newArguments(LIns* callee_ins, bool strict) LIns* argsobj_ins = w.call(&js_NewArgumentsOnTrace_ci, args); guard(false, w.eqp0(argsobj_ins), OOM_EXIT); - if (strict) { - LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); - ptrdiff_t slotsOffset = offsetof(ArgumentsData, slots); - cx->fp()->forEachCanonicalActualArg(BoxArg(this, ArgsSlotOffsetAddress(argsData_ins, - slotsOffset))); - } - return argsobj_ins; } @@ -10683,14 +10676,15 @@ TraceRecorder::record_JSOP_ARGUMENTS() if (fp->hasOverriddenArgs()) RETURN_STOP_A("Can't trace |arguments| if |arguments| is assigned to"); + if (fp->fun()->inStrictMode()) + RETURN_STOP_A("Can't trace strict-mode arguments"); LIns* a_ins = getFrameObjPtr(fp->addressOfArgs()); LIns* args_ins; LIns* callee_ins = get(&fp->calleeValue()); - bool strict = fp->fun()->inStrictMode(); if (a_ins->isImmP()) { // |arguments| is set to 0 by EnterFrame on this trace, so call to create it. - args_ins = newArguments(callee_ins, strict); + args_ins = newArguments(callee_ins); } else { // Generate LIR to create arguments only if it has not already been created. @@ -10700,7 +10694,7 @@ TraceRecorder::record_JSOP_ARGUMENTS() if (isZero_ins->isImmI(0)) { w.stAlloc(a_ins, mem_ins); } else if (isZero_ins->isImmI(1)) { - LIns* call_ins = newArguments(callee_ins, strict); + LIns* call_ins = newArguments(callee_ins); w.stAlloc(call_ins, mem_ins); } else { LIns* br1 = w.jtUnoptimizable(isZero_ins); @@ -10708,7 +10702,7 @@ TraceRecorder::record_JSOP_ARGUMENTS() LIns* br2 = w.j(NULL); w.label(br1); - LIns* call_ins = newArguments(callee_ins, strict); + LIns* call_ins = newArguments(callee_ins); w.stAlloc(call_ins, mem_ins); w.label(br2); } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index e9f7b4fb0040..7246a91da0d5 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1290,7 +1290,7 @@ class TraceRecorder JS_REQUIRES_STACK RecordingStatus makeNumberUint32(nanojit::LIns* d, nanojit::LIns** num_ins); JS_REQUIRES_STACK nanojit::LIns* stringify(const Value& v); - JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins, bool strict); + JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins); JS_REQUIRES_STACK bool canCallImacro() const; JS_REQUIRES_STACK RecordingStatus callImacro(jsbytecode* imacro); From c9c33eb477fdc2913ce734650db1f95824a6e82d Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Wed, 16 Feb 2011 11:13:26 -0800 Subject: [PATCH 14/42] Remove surprisingly-bogus assertion (634593, r=jorendorff). --- js/src/jit-test/tests/basic/bug634593.js | 4 ++++ js/src/jsobjinlines.h | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/basic/bug634593.js diff --git a/js/src/jit-test/tests/basic/bug634593.js b/js/src/jit-test/tests/basic/bug634593.js new file mode 100644 index 000000000000..b4241a8a4ca6 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug634593.js @@ -0,0 +1,4 @@ +this.__defineGetter__("x3", Function); +parseInt = x3; +parseInt.prototype = []; +for (var z = 0; z < 10; ++z) { new parseInt() } \ No newline at end of file diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index e5db574549e5..a536724cbf29 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -915,7 +915,6 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent, gc::FinalizeKind kind) { JS_ASSERT(proto); - JS_ASSERT(proto->isNative()); JS_ASSERT(parent); /* From 068460f52ca5072140c40dd28883fef44ec58740 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 18 Feb 2011 15:14:21 -0800 Subject: [PATCH 15/42] Don't GC nsContentUtils::XPConnect on shutdown if it doesn't exist. r=gal a=blocking2.0:final+,hard --- dom/base/nsJSEnvironment.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index f035dc48f253..2f026e56c986 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3264,7 +3264,9 @@ nsJSContext::GarbageCollectNow() sPendingLoadCount = 0; sLoadingInProgress = PR_FALSE; - nsContentUtils::XPConnect()->GarbageCollect(); + if (nsContentUtils::XPConnect()) { + nsContentUtils::XPConnect()->GarbageCollect(); + } } //Static From 77f31fb7fe7272a42ccb3af3a35a34a9b43f6257 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 8 Feb 2011 17:20:06 -0800 Subject: [PATCH 16/42] Bug 631135 - Objects created by or on behalf of fast natives and property ops (getters or setters) are parented to the wrong proto and global. r=lw,jst,mrbkap,bz, a=jst --- caps/tests/mochitest/test_bug246699.html | 14 +- content/base/src/nsContentUtils.cpp | 8 +- dom/base/nsGlobalWindow.cpp | 23 +- dom/base/nsJSEnvironment.cpp | 20 +- dom/base/nsJSUtils.cpp | 23 +- dom/indexedDB/IDBKeyRange.cpp | 2 +- dom/src/threads/nsDOMWorker.cpp | 5 +- js/src/jsapi-tests/Makefile.in | 1 + js/src/jsapi-tests/testCrossGlobal.cpp | 90 ++++ js/src/jsapi.cpp | 26 +- js/src/jsapi.h | 31 +- js/src/jscntxt.cpp | 20 +- js/src/jscntxt.h | 71 ++- js/src/jscntxtinlines.h | 146 ++++-- js/src/jscompartment.cpp | 13 +- js/src/jsinterp.cpp | 27 -- js/src/jsinterp.h | 3 - js/src/jsobj.cpp | 36 +- js/src/jsobjinlines.h | 12 +- js/src/jsparse.cpp | 5 +- js/src/jsregexp.cpp | 21 +- js/src/jsstr.cpp | 16 +- js/src/jsxml.cpp | 30 +- js/src/methodjit/MonoIC.cpp | 14 +- js/src/shell/js.cpp | 147 ++++-- .../e4x/extensions/cross-global-settings.js | 41 ++ js/src/tests/e4x/extensions/jstests.list | 1 + .../ecma_5/extensions/cross-global-call.js | 70 +++ js/src/tests/ecma_5/extensions/jstests.list | 1 + js/src/xpconnect/src/xpccomponents.cpp | 12 +- js/src/xpconnect/src/xpcthrower.cpp | 3 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 27 +- .../ctypes/tests/unit/test_jsctypes.js.in | 419 +++++++++--------- toolkit/components/perf/PerfMeasurement.cpp | 6 +- 34 files changed, 904 insertions(+), 480 deletions(-) create mode 100644 js/src/jsapi-tests/testCrossGlobal.cpp create mode 100644 js/src/tests/e4x/extensions/cross-global-settings.js create mode 100644 js/src/tests/ecma_5/extensions/cross-global-call.js diff --git a/caps/tests/mochitest/test_bug246699.html b/caps/tests/mochitest/test_bug246699.html index d21cc4066a20..8849d21f20e2 100644 --- a/caps/tests/mochitest/test_bug246699.html +++ b/caps/tests/mochitest/test_bug246699.html @@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=246699 Mozilla Bug 246699

 
 
diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index e953340d4b38..2fec90552bb8 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5799,9 +5799,13 @@ CloneSimpleValues(JSContext* cx, // RegExp objects. if (js_ObjectIsRegExp(obj)) { + JSObject* global = JS_GetGlobalForScopeChain(cx); + if (!global) { + return NS_ERROR_FAILURE; + } + JSObject* proto; - if (!js_GetClassPrototype(cx, JS_GetScopeChain(cx), JSProto_RegExp, - &proto)) { + if (!js_GetClassPrototype(cx, global, JSProto_RegExp, &proto)) { return NS_ERROR_FAILURE; } JSObject* newRegExp = js_CloneRegExpObject(cx, obj, proto); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index bc90c1938756..fea12e49f3d7 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -5804,30 +5804,17 @@ nsGlobalWindow::CallerInnerWindow() return nsnull; } - JSObject *scope = nsnull; - JSStackFrame *fp = nsnull; - JS_FrameIterator(cx, &fp); - if (fp) { - while (fp->isDummyFrame()) { - if (!JS_FrameIterator(cx, &fp)) - break; - } - - if (fp) - scope = &fp->scopeChain(); - } - - if (!scope) - scope = JS_GetScopeChain(cx); + JSObject *callerGlobal; + if (!::JS_GetGlobalForCallingScript(cx, &callerGlobal) || !callerGlobal) + return nsnull; JSAutoEnterCompartment ac; - if (!ac.enter(cx, scope)) + if (!ac.enter(cx, callerGlobal)) return nsnull; nsCOMPtr wrapper; nsContentUtils::XPConnect()-> - GetWrappedNativeOfJSObject(cx, ::JS_GetGlobalForObject(cx, scope), - getter_AddRefs(wrapper)); + GetWrappedNativeOfJSObject(cx, callerGlobal, getter_AddRefs(wrapper)); if (!wrapper) return nsnull; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 2f026e56c986..b4a88fb4fa7d 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -670,9 +670,25 @@ nsJSContext::DOMOperationCallback(JSContext *cx) // Check the amount of time this script has been running, or if the // dialog is disabled. - JSObject* global = ::JS_GetGlobalForScopeChain(cx); + JSObject* global; + if (!::JS_GetGlobalForCallingScript(cx, &global)) { + return JS_FALSE; + } + if (!global) { + global = JS_GetGlobalObject(cx); + if (!global) { + return NS_ERROR_FAILURE; + } + OBJ_TO_INNER_OBJECT(cx, global); + if (!global) { + return NS_ERROR_FAILURE; + } + } + + NS_ABORT_IF_FALSE(global != NULL, "operation callback without script?"); + PRBool isTrackingChromeCodeTime = - global && xpc::AccessCheck::isChrome(global->getCompartment()); + xpc::AccessCheck::isChrome(global->getCompartment()); if (duration < (isTrackingChromeCodeTime ? sMaxChromeScriptRunTime : sMaxScriptRunTime)) { return JS_TRUE; diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index f5642cb3f65e..323af29b7e91 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -190,19 +190,18 @@ nsJSUtils::GetCurrentlyRunningCodeWindowID(JSContext *aContext) if (!aContext) return 0; - PRUint64 windowID = 0; + JSObject *jsGlobal; + if (!JS_GetGlobalForCallingScript(aContext, &jsGlobal) || !jsGlobal) + return 0; - JSObject *jsGlobal = JS_GetGlobalForScopeChain(aContext); - if (jsGlobal) { - nsIScriptGlobalObject *scriptGlobal = GetStaticScriptGlobal(aContext, - jsGlobal); - if (scriptGlobal) { - nsCOMPtr win = do_QueryInterface(scriptGlobal); - if (win) - windowID = win->GetOuterWindow()->WindowID(); - } - } + JSAutoEnterCompartment ac; + if (!ac.enter(aContext, jsGlobal)) + return 0; + nsIScriptGlobalObject *sg = GetStaticScriptGlobal(aContext, jsGlobal); + if (!sg) + return 0; - return windowID; + nsCOMPtr win = do_QueryInterface(sg); + return win ? win->GetOuterWindow()->WindowID() : 0; } diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index 1ab108653208..c238c7e325a9 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -114,7 +114,7 @@ ReturnKeyRange(JSContext* aCx, nsIXPConnect* xpc = nsContentUtils::XPConnect(); NS_ASSERTION(xpc, "This should never be null!"); - JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); + JSObject* global = JS_GetGlobalForScopeChain(aCx); NS_ENSURE_TRUE(global, JS_FALSE); nsCOMPtr holder; diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp index d2ebee238026..29b9f16d60d5 100644 --- a/dom/src/threads/nsDOMWorker.cpp +++ b/dom/src/threads/nsDOMWorker.cpp @@ -634,7 +634,7 @@ nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx, } } - JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); + JSObject* global = JS_GetGlobalForScopeChain(aCx); if (!global) { NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!"); return JS_FALSE; @@ -2533,8 +2533,7 @@ nsDOMWorker::ReadStructuredClone(JSContext* aCx, if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) { NS_ASSERTION(wrappedNative, "Null pointer?!"); - JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); - if (global) { + if (JSObject* global = JS_GetGlobalForScopeChain(aCx)) { jsval val; nsCOMPtr wrapper; if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative, diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index f79f55792487..b256c7a8c658 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -54,6 +54,7 @@ CPPSRCS = \ testCloneScript.cpp \ testConservativeGC.cpp \ testContexts.cpp \ + testCrossGlobal.cpp \ testDebugger.cpp \ testDeepFreeze.cpp \ testDefineGetterSetterNonEnumerable.cpp \ diff --git a/js/src/jsapi-tests/testCrossGlobal.cpp b/js/src/jsapi-tests/testCrossGlobal.cpp new file mode 100644 index 000000000000..09d7ff8681d1 --- /dev/null +++ b/js/src/jsapi-tests/testCrossGlobal.cpp @@ -0,0 +1,90 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ +#include "tests.h" + +static const char CODE[] = + "function f() " + "{ " + " return new otherGlobal.Array() instanceof otherGlobal.Array; " + "} " + "f() && f() && f(); "; + +BEGIN_TEST(testCrossGlobal_call) +{ + JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass()); + CHECK(otherGlobal); + + JSBool res = JS_InitStandardClasses(cx, otherGlobal); + CHECK(res); + + res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); + CHECK(res); + + uintN oldopts = JS_GetOptions(cx); + uintN newopts = oldopts & ~(JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING); + newopts |= JSOPTION_METHODJIT; + JS_SetOptions(cx, newopts); + + jsval rv; + EVAL(CODE, &rv); + + CHECK_SAME(rv, JSVAL_TRUE); + + JS_SetOptions(cx, oldopts); + + return true; +} +END_TEST(testCrossGlobal_call) + +BEGIN_TEST(testCrossGlobal_compileAndGo) +{ + + JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass()); + CHECK(otherGlobal); + + JSBool res = JS_InitStandardClasses(cx, otherGlobal); + CHECK(res); + + res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); + CHECK(res); + + res = JS_DefineProperty(cx, otherGlobal, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), + JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); + CHECK(res); + + uintN oldopts = JS_GetOptions(cx); + uintN newopts = + oldopts + & ~(JSOPTION_COMPILE_N_GO | JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING); + newopts |= JSOPTION_METHODJIT; + JS_SetOptions(cx, newopts); + + jsval rv; + + // Compile script + + JSScript *script = JS_CompileScript(cx, global, CODE, strlen(CODE), __FILE__, __LINE__); + CHECK(script); + JSObject *scriptObj = JS_NewScriptObject(cx, script); + CHECK(scriptObj); + JS::Anchor anch(scriptObj); + + // Run script against new other global + res = JS_ExecuteScript(cx, otherGlobal, script, &rv); + CHECK(res); + CHECK_SAME(rv, JSVAL_TRUE); + + // Run script against original global + res = JS_ExecuteScript(cx, global, script, &rv); + CHECK(res); + CHECK_SAME(rv, JSVAL_TRUE); + + JS_SetOptions(cx, oldopts); + + return true; +} +END_TEST(testCrossGlobal_compileAndGo) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ca3acefdaf18..872eb8a1caa3 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1930,13 +1930,6 @@ JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) return js_GetClassObject(cx, obj, key, objp); } -JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx) -{ - CHECK_REQUEST(cx); - return GetScopeChain(cx); -} - JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj) { @@ -1948,7 +1941,24 @@ JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx) { CHECK_REQUEST(cx); - return GetGlobalForScopeChain(cx); + return cx->getGlobalFromScopeChain(); +} + +JS_PUBLIC_API(JSBool) +JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp) +{ + JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL); + if (!fp) { + *objp = NULL; + return true; + } + + JSObject *scope = JS_GetFrameScopeChain(cx, fp); + if (!scope) + return false; + + *objp = scope->getGlobal(); + return true; } JS_PUBLIC_API(jsval) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index efa1e556f9c1..f4013eaa93d4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1098,15 +1098,40 @@ extern JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); -extern JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx); - extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj); +/* + * Returns the global for the currently executing method or of the object + * currently being accessed if a property access (get or set) is in progress. + * If no action is currently in progress, the context's global object is + * returned. If the context has no global, this method will report an error and + * return null. + * + * To illustrate: suppose a global G with a method defined by a JSNative + * corresponding to the property G.method. When JS_GetGlobalForScopeChain is + * invoked during execution of G.method, the object returned will be G. This is + * so if a script in G calls method, and it is also so if script in a different + * global G2 calls |G1.method()|, or extracts |G.method| and calls it, and so + * on. This behavior also applies for property accesses to an |obj.prop| if + * that property is represented by a |JSPropertyOp| and |JSStrictPropertyOp| + * pair: the global object will be that corresponding to |obj|. + */ extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx); +/* + * Returns the global of the most recent interpreted code if there is any. + * + * NB: This method ignores the current scope chain, any sense of privilege + * separation encapsulated in intervening stack frames, and so on. If you + * are attempting to determine the global object to expose it to script, + * this is almost certainly not the method you want! You *probably* want + * JS_GetGlobalForScopeChain instead. + */ +extern JS_PUBLIC_API(JSBool) +JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp); + #ifdef JS_HAS_CTYPES /* * Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes' diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index e5309444673b..a4bbbe9cf24f 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -231,6 +231,8 @@ StackSpace::mark(JSTracer *trc) /* This may be the only pointer to the initialVarObj. */ if (seg->hasInitialVarObj()) MarkObject(trc, seg->getInitialVarObj(), "varobj"); + if (seg->hasLastNativeCalleeScope()) + MarkObject(trc, *seg->lastNativeCalleeScope(), "lastNativeCalleeScope"); /* Mark slots/args trailing off of the last stack frame. */ JSStackFrame *fp = seg->getCurrentFrame(); @@ -1991,6 +1993,12 @@ JSContext::JSContext(JSRuntime *rt) busyArrays() {} +void +JSContext::reportInactive() +{ + JS_ReportErrorNumber(this, js_GetErrorMessage, NULL, JSMSG_INACTIVE); +} + void JSContext::resetCompartment() { @@ -2084,7 +2092,9 @@ void JSContext::saveActiveSegment() { JS_ASSERT(hasActiveSegment()); - currentSegment->save(regs); + currentSegment->save(regs, lastNativeCalleeScope, lastNativeCalleeScopePtr); + lastNativeCalleeScope = NULL; + lastNativeCalleeScopePtr = NULL; setCurrentRegs(NULL); resetCompartment(); } @@ -2092,9 +2102,11 @@ JSContext::saveActiveSegment() void JSContext::restoreSegment() { - js::StackSegment *ccs = currentSegment; - setCurrentRegs(ccs->getSuspendedRegs()); - ccs->restore(); + js::StackSegment *cs = currentSegment; + setCurrentRegs(cs->getSuspendedRegs()); + lastNativeCalleeScopePtr = cs->lastNativeCalleeScopePtr(); + lastNativeCalleeScope = cs->lastNativeCalleeScope(); + cs->restore(); resetCompartment(); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 17a1567dc6ff..ddaea772f847 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -245,6 +245,10 @@ class StackSegment /* Whether this segment was suspended by JS_SaveFrameChain. */ bool saved; + /* Maintained to implement getGlobalFromScopeChain. */ + JSObject *savedLastNativeCalleeScope_; + void *savedLastNativeCalleeScopePtr_; + /* Align at 8 bytes on all platforms. */ #if JS_BITS_PER_WORD == 32 void *padding; @@ -260,7 +264,8 @@ class StackSegment StackSegment() : cx(NULL), previousInContext(NULL), previousInMemory(NULL), initialFrame(NULL), suspendedRegs(NON_NULL_SUSPENDED_REGS), - initialVarObj(NULL), saved(false) + initialVarObj(NULL), saved(false), savedLastNativeCalleeScope_(NULL), + savedLastNativeCalleeScopePtr_(NULL) { JS_ASSERT(!inContext()); } @@ -348,19 +353,9 @@ class StackSegment /* When isSuspended, transitioning isSaved <--> !isSaved */ - void save(JSFrameRegs *regs) { - JS_ASSERT(!isSuspended()); - suspend(regs); - saved = true; - JS_ASSERT(isSaved()); - } + void save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope, void *lastNativeCalleeScopePtr); - void restore() { - JS_ASSERT(isSaved()); - saved = false; - resume(); - JS_ASSERT(!isSuspended()); - } + void restore(); /* Data available when inContext */ @@ -416,6 +411,21 @@ class StackSegment return *initialVarObj; } + bool hasLastNativeCalleeScope() const { + JS_ASSERT(inContext()); + return savedLastNativeCalleeScope_ != NULL; + } + + JSObject *lastNativeCalleeScope() const { + JS_ASSERT(inContext()); + return savedLastNativeCalleeScope_; + } + + void *lastNativeCalleeScopePtr() const { + JS_ASSERT(inContext()); + return savedLastNativeCalleeScopePtr_; + } + #ifdef DEBUG JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const; #endif @@ -623,8 +633,6 @@ class StackSpace inline void popInvokeArgs(const InvokeArgsGuard &args); inline void popInvokeFrame(const InvokeFrameGuard &ag); - inline Value *firstUnused() const; - inline bool isCurrentAndActive(JSContext *cx) const; friend class AllFramesIter; StackSegment *getCurrentSegment() const { return currentSegment; } @@ -634,6 +642,11 @@ class StackSpace JS_FRIEND_API(bool) bumpCommit(Value *from, ptrdiff_t nvals) const; #endif + /* The first stack location available to push new values and frames. */ + inline js::Value *firstUnused() const; + public: + inline void *constFirstUnused() const; + public: static const size_t CAPACITY_VALS = 512 * 1024; static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value); @@ -1684,6 +1697,32 @@ struct JSContext return !!regs; } + /* + * An object created for the global of the currently executing native + * method, native getter, or native setter (or null if none is executing), + * used for scope chain computation. The exact identity of this value is + * unspecified: function calls and property accesses fill this with + * different values, and the trace JIT and method JIT don't update the + * value during intra-global-object method calls and property accesses. + */ + JSObject *lastNativeCalleeScope; + + /* + * A JavaScript stack location associated with a native call/property + * access. + */ + void *lastNativeCalleeScopePtr; + + /* + * Computes the global object corresponding to the current scope chain, as + * determined by the current callee if one exists, by the object being + * accessed if a native property operation is occurring, or by the context + * global object if one does not. + */ + inline JSObject *getGlobalFromScopeChain(); + + void reportInactive(); + public: friend class js::StackSpace; friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode); @@ -1768,7 +1807,7 @@ struct JSContext return currentSegment; } - inline js::RegExpStatics *regExpStatics(); + inline js::RegExpStatics *getRegExpStatics(); /* Add the given segment to the list as the new active segment. */ void pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs ®s); diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index bf3b055d48bb..a2d2aa5386a7 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -49,35 +49,6 @@ #include "jsregexp.h" #include "jsgc.h" -namespace js { - -static inline JSObject * -GetGlobalForScopeChain(JSContext *cx) -{ - /* - * This is essentially GetScopeChain(cx)->getGlobal(), but without - * falling off trace. - * - * This use of cx->fp, possibly on trace, is deliberate: - * cx->fp->scopeChain->getGlobal() returns the same object whether we're on - * trace or not, since we do not trace calls across global objects. - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - if (cx->hasfp()) - return cx->fp()->scopeChain().getGlobal(); - - JSObject *scope = cx->globalObject; - if (!scope) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - OBJ_TO_INNER_OBJECT(cx, scope); - return scope; -} - -} - #ifdef JS_METHODJIT inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() { @@ -85,6 +56,45 @@ inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() } #endif +inline JSObject * +JSContext::getGlobalFromScopeChain() +{ + if (regs) { + /* + * It's fine if the frame is stale on-trace, because we don't trace + * calls crossing globals. + */ + VOUCH_DOES_NOT_REQUIRE_STACK(); + + /* Consult the frame if it's newer than any last native call. */ + if (regs->fp > lastNativeCalleeScopePtr) + return regs->fp->scopeChain().getGlobal(); + + /* Otherwise the callee gives us our global object. */ + JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused()); + JS_ASSERT(currentSegment < lastNativeCalleeScopePtr); + return lastNativeCalleeScope->getGlobal(); + } + + /* If we have only native calls, consult the last one. */ + if (lastNativeCalleeScope) { + JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused()); + return lastNativeCalleeScope->getGlobal(); + } + + /* + * If no native call has occurred, this is a free-floating request for + * a global: return the context globalObject if possible. + */ + JSObject *globalObj = globalObject; + if (!globalObj) { + reportInactive(); + return NULL; + } + OBJ_TO_INNER_OBJECT(this, globalObj); + return globalObj; +} + inline bool JSContext::ensureGeneratorStackSpace() { @@ -110,13 +120,37 @@ JSContext::computeNextFrame(JSStackFrame *fp) } inline js::RegExpStatics * -JSContext::regExpStatics() +JSContext::getRegExpStatics() { - return js::RegExpStatics::extractFrom(js::GetGlobalForScopeChain(this)); + JSObject *global = getGlobalFromScopeChain(); + if (!global) + return NULL; + return js::RegExpStatics::extractFrom(global); } namespace js { +JS_ALWAYS_INLINE void +StackSegment::save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope, + void *lastNativeCalleeScopePtr) +{ + JS_ASSERT(!isSuspended()); + suspend(regs); + saved = true; + savedLastNativeCalleeScope_ = lastNativeCalleeScope; + savedLastNativeCalleeScopePtr_ = lastNativeCalleeScopePtr; + JS_ASSERT(isSaved()); +} + +JS_ALWAYS_INLINE void +StackSegment::restore() +{ + JS_ASSERT(isSaved()); + saved = false; + resume(); + JS_ASSERT(!isSuspended()); +} + JS_REQUIRES_STACK JS_ALWAYS_INLINE JSFrameRegs * StackSegment::getCurrentRegs() const { @@ -153,6 +187,12 @@ StackSpace::firstUnused() const return invokeArgEnd; } +JS_REQUIRES_STACK inline void * +StackSpace::constFirstUnused() const +{ + return firstUnused(); +} + /* Inline so we don't need the friend API. */ JS_ALWAYS_INLINE bool @@ -532,7 +572,7 @@ class CompartmentChecker public: explicit CompartmentChecker(JSContext *cx) : context(cx), compartment(cx->compartment) { - check(cx->hasfp() ? JS_GetGlobalForScopeChain(cx) : cx->globalObject); + check(cx->getGlobalFromScopeChain()); VOUCH_DOES_NOT_REQUIRE_STACK(); } @@ -690,6 +730,35 @@ assertSameCompartment(JSContext *cx, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) #undef START_ASSERT_SAME_COMPARTMENT +namespace detail { + +class AutoScopeChainSetter { + JSContext * const cx; + JSObject * const oldLastNativeCalleeScope; + void * const oldLastNativeCalleeScopePtr; + JSObject * const scope; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + + public: + AutoScopeChainSetter(JSContext *cx, JSObject *scope, void *scopePtr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : cx(cx), + oldLastNativeCalleeScope(cx->lastNativeCalleeScope), + oldLastNativeCalleeScopePtr(cx->lastNativeCalleeScopePtr), + scope(scope) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->lastNativeCalleeScope = scope; + cx->lastNativeCalleeScopePtr = scopePtr; + } + ~AutoScopeChainSetter() { + cx->lastNativeCalleeScope = oldLastNativeCalleeScope; + cx->lastNativeCalleeScopePtr = oldLastNativeCalleeScopePtr; + } +}; + +} + STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2) JS_ALWAYS_INLINE bool CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) @@ -697,6 +766,7 @@ CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) #ifdef DEBUG JSBool alreadyThrowing = cx->isExceptionPending(); #endif + detail::AutoScopeChainSetter scs(cx, &vp[0].toObject(), vp); assertSameCompartment(cx, ValueArray(vp, argc + 2)); JSBool ok = native(cx, argc, vp); if (ok) { @@ -747,6 +817,15 @@ JS_ALWAYS_INLINE bool CallJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *obj, jsid id, js::Value *vp) { assertSameCompartment(cx, obj, id, *vp); + + /* + * This property access may occur not in relation to a stack location, so + * we can't use |vp| here. However, since we use the location only in + * comparisons, to determine when to refer to the last interpreted frame's + * scope chain versus the last native callee's global object, the first + * unused location at the end of the stack will suffice. + */ + detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused()); JSBool ok = op(cx, obj, id, vp); if (ok) assertSameCompartment(cx, obj, *vp); @@ -758,6 +837,9 @@ CallJSPropertyOpSetter(JSContext *cx, js::StrictPropertyOp op, JSObject *obj, js JSBool strict, js::Value *vp) { assertSameCompartment(cx, obj, id, *vp); + + /* See the comment in CallJSPropertyOp for an explanation. */ + detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused()); return op(cx, obj, id, strict, vp); } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index aeeb0e2c9664..f60e356b2852 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -219,19 +219,12 @@ JSCompartment::wrap(JSContext *cx, Value *vp) * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead -, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ - JSObject *global; - if (cx->hasfp()) { - global = cx->fp()->scopeChain().getGlobal(); - } else { - global = cx->globalObject; - OBJ_TO_INNER_OBJECT(cx, global); - if (!global) - return false; - } + JSObject *global = cx->getGlobalFromScopeChain(); + if (!global) + return false; /* Unwrap incoming objects. */ if (vp->isObject()) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index fde50aec9f10..3bc4c73f89da 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -142,33 +142,6 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #endif } -JSObject * -js::GetScopeChain(JSContext *cx) -{ - JSStackFrame *fp = js_GetTopStackFrame(cx); - if (!fp) { - /* - * There is no code active on this context. In place of an actual - * scope chain, use the context's global object, which is set in - * js_InitFunctionAndObjectClasses, and which represents the default - * scope chain for the embedding. See also js_FindClassObject. - * - * For embeddings that use the inner and outer object hooks, the inner - * object represents the ultimate global object, with the outer object - * acting as a stand-in. - */ - JSObject *obj = cx->globalObject; - if (!obj) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - - OBJ_TO_INNER_OBJECT(cx, obj); - return obj; - } - return GetScopeChain(cx, fp); -} - /* * This computes the blockChain by iterating through the bytecode * of the current script until it reaches the PC. Each time it sees diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 6e6761c65cf5..57df714a5ef8 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -803,9 +803,6 @@ GetBlockChain(JSContext *cx, JSStackFrame *fp); extern JSObject * GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen); -extern JSObject * -GetScopeChain(JSContext *cx); - /* * Refresh and return fp->scopeChain. It may be stale if block scopes are * active but not yet reflected by objects in the scope chain. If a block diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b2925942c7cc..34519e27f2d9 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4242,33 +4242,17 @@ JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey, Value *vp, Class *clasp) { - JSStackFrame *fp; JSObject *obj, *cobj, *pobj; jsid id; JSProperty *prop; const Shape *shape; - /* - * Find the global object. Use cx->fp() directly to avoid falling off - * trace; all JIT-elided stack frames have the same global object as - * cx->fp(). - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - if (!start && (fp = cx->maybefp()) != NULL) - start = &fp->scopeChain(); - if (start) { - /* Find the topmost object in the scope chain. */ - do { - obj = start; - start = obj->getParent(); - } while (start); + obj = start->getGlobal(); } else { - obj = cx->globalObject; - if (!obj) { - vp->setUndefined(); - return JS_TRUE; - } + obj = cx->getGlobalFromScopeChain(); + if (!obj) + return JS_FALSE; } OBJ_TO_INNER_OBJECT(cx, obj); @@ -6189,15 +6173,9 @@ js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey, if (protoKey != JSProto_Null) { if (!scopeobj) { - if (cx->hasfp()) - scopeobj = &cx->fp()->scopeChain(); - if (!scopeobj) { - scopeobj = cx->globalObject; - if (!scopeobj) { - *protop = NULL; - return true; - } - } + scopeobj = cx->getGlobalFromScopeChain(); + if (!scopeobj) + return false; } scopeobj = scopeobj->getGlobal(); if (scopeobj->isGlobal()) { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index a536724cbf29..d08804012453 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -969,15 +969,9 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) JS_ASSERT(protoKey != JSProto_Null); /* NB: inline-expanded and specialized version of js_GetClassPrototype. */ - JSObject *global; - if (!cx->hasfp()) { - global = cx->globalObject; - OBJ_TO_INNER_OBJECT(cx, global); - if (!global) - return NULL; - } else { - global = cx->fp()->scopeChain().getGlobal(); - } + JSObject *global = cx->getGlobalFromScopeChain(); + if (!global) + return NULL; JS_ASSERT(global->isGlobal()); const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey); diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index c7359707c985..4b7710c1c94f 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -8952,7 +8952,10 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) JSObject *obj; if (context->hasfp()) { - obj = RegExp::createObject(context, context->regExpStatics(), + RegExpStatics *res = context->getRegExpStatics(); + if (!res) + return NULL; + obj = RegExp::createObject(context, res, tokenStream.getTokenbuf().begin(), tokenStream.getTokenbuf().length(), tokenStream.currentToken().t_reflags); diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index bf8f80328453..d5c0b97a6923 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -150,7 +150,9 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) */ assertSameCompartment(cx, obj, clone); - RegExpStatics *res = cx->regExpStatics(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return NULL; RegExp *re = RegExp::extractFrom(obj); { uint32 origFlags = re->getFlags(); @@ -401,7 +403,9 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uint32 flags, JSObject **o static JSBool \ name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ { \ - RegExpStatics *res = cx->regExpStatics(); \ + RegExpStatics *res = cx->getRegExpStatics(); \ + if (!res) \ + return false; \ code; \ } @@ -427,7 +431,9 @@ DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, static JSBool \ name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \ { \ - RegExpStatics *res = cx->regExpStatics(); \ + RegExpStatics *res = cx->getRegExpStatics(); \ + if (!res) \ + return false; \ code; \ return true; \ } @@ -672,7 +678,10 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) static bool SwapRegExpInternals(JSContext *cx, JSObject *obj, Value *rval, JSString *str, uint32 flags = 0) { - flags |= cx->regExpStatics()->getFlags(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return false; + flags |= res->getFlags(); AlreadyIncRefed re = RegExp::create(cx, str, flags); if (!re) return false; @@ -713,7 +722,9 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool te lastIndex = 0; } - RegExpStatics *res = cx->regExpStatics(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return false; JSString *input; if (argc) { diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 8d85a93c9319..f05508c35b0d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1876,8 +1876,8 @@ str_match(JSContext *cx, uintN argc, Value *vp) AutoObjectRooter array(cx); MatchArgType arg = array.addr(); - RegExpStatics *res = cx->regExpStatics(); - if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS)) + RegExpStatics *res = cx->getRegExpStatics(); + if (!res || !DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS)) return false; /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */ @@ -1906,7 +1906,9 @@ str_search(JSContext *cx, uintN argc, Value *vp) if (!rep) return false; - RegExpStatics *res = cx->regExpStatics(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return false; size_t i = 0; if (!rep->re().execute(cx, res, str, &i, true, vp)) return false; @@ -2357,7 +2359,9 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) rdata.leftIndex = 0; rdata.calledBack = false; - RegExpStatics *res = cx->regExpStatics(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return false; if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS)) return false; @@ -2697,7 +2701,9 @@ str_split(JSContext *cx, uintN argc, Value *vp) AutoValueVector splits(cx); - RegExpStatics *res = cx->regExpStatics(); + RegExpStatics *res = cx->getRegExpStatics(); + if (!res) + return false; jsint i, j; uint32 len = i = 0; while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) { diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index b432efec3c24..8922439f4959 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1677,6 +1677,32 @@ GetXMLSettingFlags(JSContext *cx, uintN *flagsp) return true; } +static JSObject * +GetXMLScopeChain(JSContext *cx) +{ + if (JSStackFrame *fp = js_GetTopStackFrame(cx)) + return GetScopeChain(cx, fp); + + /* + * There is no code active on this context. In place of an actual scope + * chain, use the context's global object, which is set in + * s_InitFunctionAndObjectClasses, and which represents the default scope + * chain for the embedding. See also js_FindClassObject. + * + * For embeddings that use the inner and outer object hooks, the inner + * object represents the ultimate global object, with the outer object + * acting as a stand-in. + */ + JSObject *obj = cx->globalObject; + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } + + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; +} + static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { @@ -1756,7 +1782,7 @@ ParseXMLSource(JSContext *cx, JSString *src) { Parser parser(cx); if (parser.init(chars, length, filename, lineno, cx->findVersion())) { - JSObject *scopeChain = GetScopeChain(cx); + JSObject *scopeChain = GetXMLScopeChain(cx); if (!scopeChain) { cx->free(chars); return NULL; @@ -7231,7 +7257,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) JSObject *ns, *obj, *tmp; jsval v; - JSObject *scopeChain = GetScopeChain(cx); + JSObject *scopeChain = GetXMLScopeChain(cx); obj = NULL; for (tmp = scopeChain; tmp; tmp = tmp->getParent()) { diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 089cfa4a3c1c..1dc5caad493c 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -808,9 +808,19 @@ class CallCompiler : public BaseCompiler if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp)) THROWV(true); - /* Right now, take slow-path for IC misses or multiple stubs. */ - if (ic.fastGuardedNative || ic.hasJsFunCheck) + /* + * Right now, take slow-path for IC misses or multiple stubs. Also take + * it if the call crosses, or might cross, globals: in that case + * JSContext::getGlobalFromScopeChain requires we note the change of + * global. + */ + if (ic.fastGuardedNative || + ic.hasJsFunCheck || + !f.regs.fp->script()->compileAndGo || + obj->getGlobal() != f.regs.fp->scopeChain().getGlobal()) + { return true; + } /* Native MIC needs to warm up first. */ if (!ic.hit) { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index eeb0f892fff1..bdacdbe862a6 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4356,6 +4356,40 @@ StringStats(JSContext *cx, uintN argc, jsval *vp) return true; } +enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT }; + +static JSObject * +NewGlobalObject(JSContext *cx, CompartmentKind compartment); + +JSBool +NewGlobal(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal"); + return false; + } + + JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]); + + JSBool equalSame = JS_FALSE, equalNew = JS_FALSE; + if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) || + !JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) { + return false; + } + + if (!equalSame && !equalNew) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal"); + return false; + } + + JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT); + if (!global) + return false; + + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global)); + return true; +} + static JSFunctionSpec shell_functions[] = { JS_FN("version", Version, 0,0), JS_FN("revertVersion", RevertVersion, 0,0), @@ -4452,6 +4486,7 @@ static JSFunctionSpec shell_functions[] = { JS_FN("mjitstats", MJitStats, 0,0), #endif JS_FN("stringstats", StringStats, 0,0), + JS_FN("newGlobal", NewGlobal, 1,0), JS_FS_END }; @@ -4477,7 +4512,7 @@ static const char *const shell_help_messages[] = { "assertEq(actual, expected[, msg])\n" " Throw if the first two arguments are not the same (both +0 or both -0,\n" " both NaN, or non-zero and ===)", -"assertJit() Throw if the calling function failed to JIT\n", +"assertJit() Throw if the calling function failed to JIT", "gc() Run the garbage collector", #ifdef JS_GCMETER "gcstats() Print garbage collector statistics", @@ -4522,7 +4557,7 @@ static const char *const shell_help_messages[] = { "dumpObject() Dump an internal representation of an object", "notes([fun]) Show source notes for functions", "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n" -" With filename, send to file.\n", +" With filename, send to file.", "stats([string ...]) Dump 'arena', 'atom', 'global' stats", #endif #ifdef TEST_CVTARGS @@ -4573,24 +4608,37 @@ static const char *const shell_help_messages[] = { " Get/Set the limit in seconds for the execution time for the current context.\n" " A negative value (default) means that the execution time is unlimited.", "elapsed() Execution time elapsed for the current context.", -"parent(obj) Returns the parent of obj.\n", -"wrap(obj) Wrap an object into a noop wrapper.\n", -"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n", -"deserialize(a) Deserialize data generated by serialize.\n", +"parent(obj) Returns the parent of obj.", +"wrap(obj) Wrap an object into a noop wrapper.", +"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.", +"deserialize(a) Deserialize data generated by serialize.", #ifdef JS_METHODJIT -"mjitstats() Return stats on mjit memory usage.\n", +"mjitstats() Return stats on mjit memory usage.", #endif -"stringstats() Return stats on string memory usage.\n" +"stringstats() Return stats on string memory usage.", +"newGlobal(kind) Return a new global object, in the current\n" +" compartment if kind === 'same-compartment' or in a\n" +" new compartment if kind === 'new-compartment'", + +/* Keep these last: see the static assertion below. */ #ifdef MOZ_PROFILING "startProfiling() Start a profiling session.\n" -" Profiler must be running with programatic sampling\n" -"stopProfiling() Stop a running profiling session" +" Profiler must be running with programatic sampling", +"stopProfiling() Stop a running profiling session\n" #endif }; +#ifdef MOZ_PROFILING +#define PROFILING_FUNCTION_COUNT 2 +#else +#define PROFILING_FUNCTION_COUNT 0 +#endif + /* Help messages must match shell functions. */ -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 == - JS_ARRAY_LENGTH(shell_functions)); +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT == + JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); + +#undef PROFILING_FUNCTION_COUNT #ifdef DEBUG static void @@ -4599,12 +4647,13 @@ CheckHelpMessages() const char *const *m; const char *lp; - /* Each message must begin with "function_name(" prefix. */ + /* Messages begin with "function_name(" prefix and don't end with \n. */ for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) { lp = strchr(*m, '('); JS_ASSERT(lp); JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, *m, lp - *m) == 0); + JS_ASSERT((*m)[strlen(*m) - 1] != '\n'); } } #else @@ -5424,46 +5473,52 @@ DestroyContext(JSContext *cx, bool withGC) } static JSObject * -NewGlobalObject(JSContext *cx) +NewGlobalObject(JSContext *cx, CompartmentKind compartment) { - JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL); + JSObject *glob = (compartment == NEW_COMPARTMENT) + ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL) + : JS_NewGlobalObject(cx, &global_class); if (!glob) return NULL; - JSAutoEnterCompartment ac; - if (!ac.enter(cx, glob)) - return NULL; + { + JSAutoEnterCompartment ac; + if (!ac.enter(cx, glob)) + return NULL; -#ifdef LAZY_STANDARD_CLASSES - JS_SetGlobalObject(cx, glob); -#else - if (!JS_InitStandardClasses(cx, glob)) - return NULL; +#ifndef LAZY_STANDARD_CLASSES + if (!JS_InitStandardClasses(cx, glob)) + return NULL; #endif + #ifdef JS_HAS_CTYPES - if (!JS_InitCTypesClass(cx, glob)) - return NULL; + if (!JS_InitCTypesClass(cx, glob)) + return NULL; #endif - if (!JS::RegisterPerfMeasurement(cx, glob)) - return NULL; - if (!JS_DefineFunctions(cx, glob, shell_functions) || - !JS_DefineProfilingFunctions(cx, glob)) { - return NULL; + if (!JS::RegisterPerfMeasurement(cx, glob)) + return NULL; + if (!JS_DefineFunctions(cx, glob, shell_functions) || + !JS_DefineProfilingFunctions(cx, glob)) { + return NULL; + } + + JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); + if (!it) + return NULL; + if (!JS_DefineProperties(cx, it, its_props)) + return NULL; + if (!JS_DefineFunctions(cx, it, its_methods)) + return NULL; + + if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter, + its_setter, 0)) + return NULL; + if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter, + its_setter, JSPROP_READONLY)) + return NULL; } - JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); - if (!it) - return NULL; - if (!JS_DefineProperties(cx, it, its_props)) - return NULL; - if (!JS_DefineFunctions(cx, it, its_methods)) - return NULL; - - if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter, - its_setter, 0)) - return NULL; - if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter, - its_setter, JSPROP_READONLY)) + if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, &glob)) return NULL; return glob; @@ -5474,7 +5529,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) { JSAutoRequest ar(cx); - JSObject *glob = NewGlobalObject(cx); + JSObject *glob = NewGlobalObject(cx, NEW_COMPARTMENT); if (!glob) return 1; @@ -5482,6 +5537,8 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) if (!ac.enter(cx, glob)) return 1; + JS_SetGlobalObject(cx, glob); + JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); if (!envobj || !JS_SetPrivate(cx, envobj, envp)) return 1; @@ -5518,7 +5575,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) class ShellWorkerHooks : public js::workers::WorkerHooks { public: JSObject *newGlobalObject(JSContext *cx) { - return NewGlobalObject(cx); + return NewGlobalObject(cx, NEW_COMPARTMENT); } }; ShellWorkerHooks hooks; diff --git a/js/src/tests/e4x/extensions/cross-global-settings.js b/js/src/tests/e4x/extensions/cross-global-settings.js new file mode 100644 index 000000000000..967fff1a83f5 --- /dev/null +++ b/js/src/tests/e4x/extensions/cross-global-settings.js @@ -0,0 +1,41 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = 'cross-global-settings.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 631135; +var summary = + "Look at the callee's global.XML.* setting values, not the caller's"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var otherGlobal = newGlobal("same-compartment"); +otherGlobal.XML.prettyPrinting = true; +otherGlobal.XML.prettyIndent = 2; +otherGlobal.indent = " "; + +XML.prettyPrinting = true; +XML.prettyIndent = 16; + +var indent = " " + + " " + + " " + + " "; + +var str = otherGlobal.XML("").toXMLString(); + +assertEq(str.indexOf(indent), -1); +assertEq(str.indexOf(otherGlobal.indent) > 0, true); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/e4x/extensions/jstests.list b/js/src/tests/e4x/extensions/jstests.list index 75d657c5b000..56602bb13435 100644 --- a/js/src/tests/e4x/extensions/jstests.list +++ b/js/src/tests/e4x/extensions/jstests.list @@ -22,3 +22,4 @@ script regress-450871-02.js script regress-462734-01.js script extensibility.js script regress-595207.js +skip-if(!xulRuntime.shell) script cross-global-settings.js diff --git a/js/src/tests/ecma_5/extensions/cross-global-call.js b/js/src/tests/ecma_5/extensions/cross-global-call.js new file mode 100644 index 000000000000..44f370004be3 --- /dev/null +++ b/js/src/tests/ecma_5/extensions/cross-global-call.js @@ -0,0 +1,70 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 631135; +var summary = + "Objects created by/for natives are created for the caller's scope, not " + + "for the native's scope"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var otherGlobal = newGlobal("same-compartment"); + +function makeArray() +{ + return new otherGlobal.Array(); +} + +var a; + +a = makeArray(); +assertEq(a instanceof otherGlobal.Array, true); +assertEq(a instanceof Array, false); +a = makeArray(); +assertEq(a instanceof otherGlobal.Array, true); +assertEq(a instanceof Array, false); +a = makeArray(); +assertEq(a instanceof otherGlobal.Array, true); +assertEq(a instanceof Array, false); + +for (var i = 0; i < 3; i++) +{ + a = new otherGlobal.Array(); + assertEq(a instanceof otherGlobal.Array, true); + assertEq(a instanceof Array, false); +} + + +var res = otherGlobal.Array.prototype.slice.call([1, 2, 3], 0, 1); +assertEq(res instanceof otherGlobal.Array, true); +assertEq(res instanceof Array, false); + +var obj = { p: 1 }; +var desc = otherGlobal.Object.getOwnPropertyDescriptor(obj, "p"); +assertEq(desc instanceof otherGlobal.Object, true); +assertEq(desc instanceof Object, false); + +try +{ + otherGlobal.Function.prototype.call.call(null); + var err = "no error"; +} +catch (e) +{ + err = e; +} +assertEq(err instanceof otherGlobal.TypeError, true, + "bad error: " + err); +assertEq(err instanceof TypeError, false, "bad error: " + err); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/ecma_5/extensions/jstests.list b/js/src/tests/ecma_5/extensions/jstests.list index 962f4ee99078..74f3b4b8ec47 100644 --- a/js/src/tests/ecma_5/extensions/jstests.list +++ b/js/src/tests/ecma_5/extensions/jstests.list @@ -9,6 +9,7 @@ script bug352085.js script bug472534.js script bug496985.js script bug566661.js +skip-if(!xulRuntime.shell) script cross-global-call.js # needs newGlobal() script eval-native-callback-is-indirect.js script extension-methods-reject-null-undefined-this.js skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate() diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 1c141f9d95b4..17837aa5b1af 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3630,11 +3630,17 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, { JSAutoRequest req(cx); - callingScope = JS_GetScopeChain(cx); - if (!callingScope) { + if (!JS_GetGlobalForCallingScript(cx, &callingScope)) { return NS_ERROR_FAILURE; } - callingScope = JS_GetGlobalForObject(cx, callingScope); + if (!callingScope) { + callingScope = JS_GetGlobalObject(cx); + if (!callingScope) + return NS_ERROR_FAILURE; + OBJ_TO_INNER_OBJECT(cx, callingScope); + if (!callingScope) + return NS_ERROR_FAILURE; + } } nsRefPtr sandcx = new ContextHolder(cx, sandbox); diff --git a/js/src/xpconnect/src/xpcthrower.cpp b/js/src/xpconnect/src/xpcthrower.cpp index e694d811ca94..cc1306f54ab3 100644 --- a/js/src/xpconnect/src/xpcthrower.cpp +++ b/js/src/xpconnect/src/xpcthrower.cpp @@ -317,10 +317,9 @@ XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e) } else if((xpc = nsXPConnect::GetXPConnect())) { - JSObject* glob = JS_GetScopeChain(cx); + JSObject* glob = JS_GetGlobalForScopeChain(cx); if(!glob) return JS_FALSE; - glob = JS_GetGlobalForObject(cx, glob); nsCOMPtr holder; nsresult rv = xpc->WrapNative(cx, glob, e, diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 492097a4b462..661f365afdb6 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -234,37 +234,28 @@ GetPrincipal(JSObject *obj) bool AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj) { - JSObject *scope = nsnull; - JSStackFrame *fp = nsnull; - JS_FrameIterator(cx, &fp); - if (fp) { - while (fp->isDummyFrame()) { - if (!JS_FrameIterator(cx, &fp)) - break; - } - - if (fp) - scope = &fp->scopeChain(); + JSObject *scope; + if (!JS_GetGlobalForCallingScript(cx, &scope)) + return false; + if (!scope) { + scope = JS_GetGlobalForScopeChain(cx); + if (!scope) + return false; } - if (!scope) - scope = JS_GetScopeChain(cx); - nsIPrincipal *subject; - nsIPrincipal *object; - { JSAutoEnterCompartment ac; if (!ac.enter(cx, scope)) return false; - subject = GetPrincipal(JS_GetGlobalForObject(cx, scope)); + subject = GetPrincipal(scope); } - if (!subject) return false; + nsIPrincipal *object; { JSAutoEnterCompartment ac; diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in index 2be35d8003d8..a766be2c635d 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in @@ -38,10 +38,15 @@ * * ***** END LICENSE BLOCK ***** */ +var ctypesGlobal = this; try { // We might be running without privileges, in which case it's up to the // harness to give us the 'ctypes' object. Components.utils.import("resource://gre/modules/ctypes.jsm"); + + // Hack: Determine the global object with which errors thrown by ctypes + // methods will be associated for more precise testing purposes. + ctypesGlobal = ctypes.CType.constructor("return this")(); } catch(e) { } @@ -217,18 +222,18 @@ function run_test() // test library.close let test_void_t = library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t); library.close(); - do_check_throws(function() { test_void_t(); }, Error); + do_check_throws(function() { test_void_t(); }, ctypesGlobal.Error); do_check_throws(function() { library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t); - }, Error); + }, ctypesGlobal.Error); // test that library functions throw when bound to other objects library = ctypes.open(libfile.path); let obj = {}; obj.declare = library.declare; - do_check_throws(function () { run_void_tests(obj); }, Error); + do_check_throws(function () { run_void_tests(obj); }, ctypesGlobal.Error); obj.close = library.close; - do_check_throws(function () { obj.close(); }, Error); + do_check_throws(function () { obj.close(); }, ctypesGlobal.Error); // test that functions work as properties of other objects let getter = library.declare("get_int8_t_cdecl", ctypes.default_abi, ctypes.int8_t); @@ -237,7 +242,7 @@ function run_test() do_check_eq(obj.t(), 109); // bug 521937 - do_check_throws(function () { let nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, Error); + do_check_throws(function () { let nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, ctypesGlobal.Error); // bug 522360 do_check_eq(run_load_system_library(), true); @@ -259,8 +264,8 @@ function run_test() function run_abstract_class_tests() { // Test that ctypes.CType is an abstract constructor that throws. - do_check_throws(function() { ctypes.CType(); }, Error); - do_check_throws(function() { new ctypes.CType() }, Error); + do_check_throws(function() { ctypes.CType(); }, ctypesGlobal.Error); + do_check_throws(function() { new ctypes.CType() }, ctypesGlobal.Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.CType, "Function"); @@ -273,8 +278,8 @@ function run_abstract_class_tests() }); do_check_true(ctypes.CType.hasOwnProperty("prototype")); - do_check_throws(function() { ctypes.CType.prototype(); }, Error); - do_check_throws(function() { new ctypes.CType.prototype() }, Error); + do_check_throws(function() { ctypes.CType.prototype(); }, ctypesGlobal.Error); + do_check_throws(function() { new ctypes.CType.prototype() }, ctypesGlobal.Error); checkParentIsCTypes(ctypes.CType.prototype); getParent(ctypes, function(p) { do_check_true(Object.getPrototypeOf(ctypes.CType.prototype) === p.Function.prototype); @@ -292,16 +297,16 @@ function run_abstract_class_tests() do_check_true(ctypes.CType.prototype.hasOwnProperty("toSource")); // Check that the shared properties and functions on ctypes.CType.prototype throw. - do_check_throws(function() { ctypes.CType.prototype.name; }, Error); - do_check_throws(function() { ctypes.CType.prototype.size; }, Error); - do_check_throws(function() { ctypes.CType.prototype.ptr; }, Error); - do_check_throws(function() { ctypes.CType.prototype.array(); }, Error); - do_check_throws(function() { ctypes.CType.prototype.toString(); }, Error); - do_check_throws(function() { ctypes.CType.prototype.toSource(); }, Error); + do_check_throws(function() { ctypes.CType.prototype.name; }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.size; }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.ptr; }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.array(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.toString(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.toSource(); }, ctypesGlobal.Error); // Test that ctypes.CData is an abstract constructor that throws. - do_check_throws(function() { ctypes.CData(); }, Error); - do_check_throws(function() { new ctypes.CData() }, Error); + do_check_throws(function() { ctypes.CData(); }, ctypesGlobal.Error); + do_check_throws(function() { new ctypes.CData() }, ctypesGlobal.Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.CData, "Function"); @@ -327,16 +332,16 @@ function run_abstract_class_tests() do_check_true(ctypes.CData.prototype.hasOwnProperty("toSource")); // Check that the shared properties and functions on ctypes.CData.prototype throw. - do_check_throws(function() { ctypes.CData.prototype.value; }, Error); - do_check_throws(function() { ctypes.CData.prototype.value = null; }, Error); - do_check_throws(function() { ctypes.CData.prototype.address(); }, Error); - do_check_throws(function() { ctypes.CData.prototype.readString(); }, Error); - do_check_throws(function() { ctypes.CData.prototype.toString(); }, Error); - do_check_throws(function() { ctypes.CData.prototype.toSource(); }, Error); + do_check_throws(function() { ctypes.CData.prototype.value; }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.value = null; }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.address(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.readString(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.toString(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.toSource(); }, ctypesGlobal.Error); } function run_Int64_tests() { - do_check_throws(function() { ctypes.Int64(); }, Error); + do_check_throws(function() { ctypes.Int64(); }, ctypesGlobal.Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.Int64, "Function"); @@ -363,8 +368,8 @@ function run_Int64_tests() { do_check_true(ctypes.Int64.prototype.hasOwnProperty("toSource")); // Check that the shared functions on ctypes.Int64.prototype throw. - do_check_throws(function() { ctypes.Int64.prototype.toString(); }, Error); - do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, Error); + do_check_throws(function() { ctypes.Int64.prototype.toString(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, ctypesGlobal.Error); let i = ctypes.Int64(0); checkParentIsCTypes(i); @@ -375,14 +380,14 @@ function run_Int64_tests() { do_check_eq(i.toString(), "0"); for (let radix = 2; radix <= 36; ++radix) do_check_eq(i.toString(radix), "0"); - do_check_throws(function() { i.toString(0); }, Error); - do_check_throws(function() { i.toString(1); }, Error); - do_check_throws(function() { i.toString(37); }, Error); - do_check_throws(function() { i.toString(10, 2); }, Error); + do_check_throws(function() { i.toString(0); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(1); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(37); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(10, 2); }, ctypesGlobal.Error); // Test Int64.toSource(). do_check_eq(i.toSource(), "ctypes.Int64(\"0\")"); - do_check_throws(function() { i.toSource(10); }, Error); + do_check_throws(function() { i.toSource(10); }, ctypesGlobal.Error); i = ctypes.Int64("0x28590a1c921def71"); do_check_eq(i.toString(), i.toString(10)); @@ -480,7 +485,7 @@ function run_Int64_tests() { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { ctypes.Int64(vals[i]); }, TypeError); + do_check_throws(function () { ctypes.Int64(vals[i]); }, ctypesGlobal.TypeError); // Test ctypes.Int64.compare. do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(5)), 0); @@ -489,18 +494,18 @@ function run_Int64_tests() { do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-5)), 0); do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-4)), -1); do_check_eq(ctypes.Int64.compare(ctypes.Int64(-4), ctypes.Int64(-5)), 1); - do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, Error); - do_check_throws(function() { ctypes.Int64.compare(4, 5); }, Error); + do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.compare(4, 5); }, ctypesGlobal.Error); // Test ctypes.Int64.{lo,hi}. do_check_eq(ctypes.Int64.lo(ctypes.Int64(0x28590a1c921de000)), 0x921de000); do_check_eq(ctypes.Int64.hi(ctypes.Int64(0x28590a1c921de000)), 0x28590a1c); do_check_eq(ctypes.Int64.lo(ctypes.Int64(-0x28590a1c921de000)), 0x6de22000); do_check_eq(ctypes.Int64.hi(ctypes.Int64(-0x28590a1c921de000)), -0x28590a1d); - do_check_throws(function() { ctypes.Int64.lo(ctypes.UInt64(0)); }, Error); - do_check_throws(function() { ctypes.Int64.hi(ctypes.UInt64(0)); }, Error); - do_check_throws(function() { ctypes.Int64.lo(0); }, Error); - do_check_throws(function() { ctypes.Int64.hi(0); }, Error); + do_check_throws(function() { ctypes.Int64.lo(ctypes.UInt64(0)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.hi(ctypes.UInt64(0)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.lo(0); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.hi(0); }, ctypesGlobal.Error); // Test ctypes.Int64.join. do_check_eq(ctypes.Int64.join(0, 0).toString(), "0"); @@ -508,14 +513,14 @@ function run_Int64_tests() { do_check_eq(ctypes.Int64.join(-0x28590a1d, 0x6de22000).toString(16), "-28590a1c921de000"); do_check_eq(ctypes.Int64.join(0x7fffffff, 0xffffffff).toString(16), "7fffffffffffffff"); do_check_eq(ctypes.Int64.join(-0x80000000, 0x00000000).toString(16), "-8000000000000000"); - do_check_throws(function() { ctypes.Int64.join(-0x80000001, 0); }, TypeError); - do_check_throws(function() { ctypes.Int64.join(0x80000000, 0); }, TypeError); - do_check_throws(function() { ctypes.Int64.join(0, -0x1); }, TypeError); - do_check_throws(function() { ctypes.Int64.join(0, 0x800000000); }, TypeError); + do_check_throws(function() { ctypes.Int64.join(-0x80000001, 0); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.Int64.join(0x80000000, 0); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.Int64.join(0, -0x1); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.Int64.join(0, 0x800000000); }, ctypesGlobal.TypeError); } function run_UInt64_tests() { - do_check_throws(function() { ctypes.UInt64(); }, Error); + do_check_throws(function() { ctypes.UInt64(); }, ctypesGlobal.Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.UInt64, "Function"); @@ -544,8 +549,8 @@ function run_UInt64_tests() { do_check_true(ctypes.UInt64.prototype.hasOwnProperty("toSource")); // Check that the shared functions on ctypes.UInt64.prototype throw. - do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, Error); - do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, Error); + do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, ctypesGlobal.Error); let i = ctypes.UInt64(0); checkParentIsCTypes(i); @@ -556,14 +561,14 @@ function run_UInt64_tests() { do_check_eq(i.toString(), "0"); for (let radix = 2; radix <= 36; ++radix) do_check_eq(i.toString(radix), "0"); - do_check_throws(function() { i.toString(0); }, Error); - do_check_throws(function() { i.toString(1); }, Error); - do_check_throws(function() { i.toString(37); }, Error); - do_check_throws(function() { i.toString(10, 2); }, Error); + do_check_throws(function() { i.toString(0); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(1); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(37); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(10, 2); }, ctypesGlobal.Error); // Test UInt64.toSource(). do_check_eq(i.toSource(), "ctypes.UInt64(\"0\")"); - do_check_throws(function() { i.toSource(10); }, Error); + do_check_throws(function() { i.toSource(10); }, ctypesGlobal.Error); i = ctypes.UInt64("0x28590a1c921def71"); do_check_eq(i.toString(), i.toString(10)); @@ -640,24 +645,24 @@ function run_UInt64_tests() { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { ctypes.UInt64(vals[i]); }, TypeError); + do_check_throws(function () { ctypes.UInt64(vals[i]); }, ctypesGlobal.TypeError); // Test ctypes.UInt64.compare. do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(5)), 0); do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(4)), 1); do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.UInt64(5)), -1); - do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, Error); - do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, Error); + do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, ctypesGlobal.Error); // Test ctypes.UInt64.{lo,hi}. do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0x28590a1c921de000)), 0x921de000); do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0x28590a1c921de000)), 0x28590a1c); do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0xa8590a1c921de000)), 0x921de000); do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0xa8590a1c921de000)), 0xa8590a1c); - do_check_throws(function() { ctypes.UInt64.lo(ctypes.Int64(0)); }, Error); - do_check_throws(function() { ctypes.UInt64.hi(ctypes.Int64(0)); }, Error); - do_check_throws(function() { ctypes.UInt64.lo(0); }, Error); - do_check_throws(function() { ctypes.UInt64.hi(0); }, Error); + do_check_throws(function() { ctypes.UInt64.lo(ctypes.Int64(0)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.hi(ctypes.Int64(0)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.lo(0); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.hi(0); }, ctypesGlobal.Error); // Test ctypes.UInt64.join. do_check_eq(ctypes.UInt64.join(0, 0).toString(), "0"); @@ -665,10 +670,10 @@ function run_UInt64_tests() { do_check_eq(ctypes.UInt64.join(0xa8590a1c, 0x921de000).toString(16), "a8590a1c921de000"); do_check_eq(ctypes.UInt64.join(0xffffffff, 0xffffffff).toString(16), "ffffffffffffffff"); do_check_eq(ctypes.UInt64.join(0, 0).toString(16), "0"); - do_check_throws(function() { ctypes.UInt64.join(-0x1, 0); }, TypeError); - do_check_throws(function() { ctypes.UInt64.join(0x100000000, 0); }, TypeError); - do_check_throws(function() { ctypes.UInt64.join(0, -0x1); }, TypeError); - do_check_throws(function() { ctypes.UInt64.join(0, 0x1000000000); }, TypeError); + do_check_throws(function() { ctypes.UInt64.join(-0x1, 0); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.UInt64.join(0x100000000, 0); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.UInt64.join(0, -0x1); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.UInt64.join(0, 0x1000000000); }, ctypesGlobal.TypeError); } function run_basic_abi_tests(library, t, name, toprimitive, @@ -797,12 +802,12 @@ function run_basic_class_tests(t) do_check_true(t.prototype.constructor === t); // Check that the shared properties and functions on 't.prototype' throw. - do_check_throws(function() { t.prototype.value; }, Error); - do_check_throws(function() { t.prototype.value = null; }, Error); - do_check_throws(function() { t.prototype.address(); }, Error); - do_check_throws(function() { t.prototype.readString(); }, Error); - do_check_throws(function() { t.prototype.toString(); }, Error); - do_check_throws(function() { t.prototype.toSource(); }, Error); + do_check_throws(function() { t.prototype.value; }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.value = null; }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.address(); }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.readString(); }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.toString(); }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.toSource(); }, ctypesGlobal.Error); // Test that an instance 'd' of 't' is a CData. let d = t(); @@ -847,7 +852,7 @@ function run_bool_tests(library) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, d.value); @@ -927,7 +932,7 @@ function run_integer_tests(library, t, name, size, signed, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, d.value); @@ -976,9 +981,9 @@ function run_float_tests(library, t, name, size) { d = new t(20); do_check_eq(d.value, 20); - do_check_throws(function() { d.value = ctypes.Int64(5); }, TypeError); - do_check_throws(function() { d.value = ctypes.Int64(-5); }, TypeError); - do_check_throws(function() { d.value = ctypes.UInt64(5); }, TypeError); + do_check_throws(function() { d.value = ctypes.Int64(5); }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = ctypes.Int64(-5); }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = ctypes.UInt64(5); }, ctypesGlobal.TypeError); if (size == 4) { d.value = 0x7fffff; @@ -995,7 +1000,7 @@ function run_float_tests(library, t, name, size) { d.value = 0xfffffffffffff000; do_check_eq(d.value, 0xfffffffffffff000); - do_check_throws(function() { d.value = ctypes.Int64("0x7fffffffffffffff"); }, TypeError); + do_check_throws(function() { d.value = ctypes.Int64("0x7fffffffffffffff"); }, ctypesGlobal.TypeError); } d.value = Infinity; @@ -1014,7 +1019,7 @@ function run_float_tests(library, t, name, size) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); // Check that values roundtrip through toSource() correctly. function test_roundtrip(t, val) @@ -1114,7 +1119,7 @@ function run_wrapped_integer_tests(library, t, name, size, signed, w, wname, lim {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents.toString(), d.value.toString()); @@ -1160,7 +1165,7 @@ function run_char_tests(library, t, name, size, signed, limits) { d.value = -10; do_check_eq(d.value, -10); } else { - do_check_throws(function() { d.value = -10; }, TypeError); + do_check_throws(function() { d.value = -10; }, ctypesGlobal.TypeError); } d = new t(20); do_check_eq(d.value, 20); @@ -1189,7 +1194,7 @@ function run_char_tests(library, t, name, size, signed, limits) { d.value = true; do_check_eq(d.value, 1); - do_check_throws(function() { d.value = "5"; }, TypeError); + do_check_throws(function() { d.value = "5"; }, ctypesGlobal.TypeError); // don't convert anything else let vals = [limits[0] - 1, limits[1] + 1, Infinity, -Infinity, NaN, 0.1, @@ -1197,7 +1202,7 @@ function run_char_tests(library, t, name, size, signed, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, 1); @@ -1211,9 +1216,9 @@ function run_char_tests(library, t, name, size, signed, limits) { do_check_eq(s.constructor.length, literal.length + 1); s = t.array(50)(literal); do_check_eq(s.readString(), literal); - do_check_throws(function() { t.array(3)(literal); }, Error); + do_check_throws(function() { t.array(3)(literal); }, ctypesGlobal.Error); - do_check_throws(function() { t.ptr(literal); }, TypeError); + do_check_throws(function() { t.ptr(literal); }, ctypesGlobal.TypeError); let p = t.ptr(s); do_check_eq(p.readString(), literal); @@ -1255,13 +1260,13 @@ function run_jschar_tests(library, t, name, limits) { do_check_eq(toprimitive(d.value), 5); d = t(10); do_check_eq(toprimitive(d.value), 10); - do_check_throws(function() { d.value = -10; }, TypeError); + do_check_throws(function() { d.value = -10; }, ctypesGlobal.TypeError); d = new t(20); do_check_eq(toprimitive(d.value), 20); d.value = ctypes.Int64(5); do_check_eq(d.value.charCodeAt(0), 5); - do_check_throws(function() { d.value = ctypes.Int64(-10); }, TypeError); + do_check_throws(function() { d.value = ctypes.Int64(-10); }, ctypesGlobal.TypeError); d.value = ctypes.UInt64(5); do_check_eq(d.value.charCodeAt(0), 5); @@ -1289,7 +1294,7 @@ function run_jschar_tests(library, t, name, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, TypeError); + do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, "a"); @@ -1303,9 +1308,9 @@ function run_jschar_tests(library, t, name, limits) { do_check_eq(s.constructor.length, literal.length + 1); s = t.array(50)(literal); do_check_eq(s.readString(), literal); - do_check_throws(function() { t.array(3)(literal); }, Error); + do_check_throws(function() { t.array(3)(literal); }, ctypesGlobal.Error); - do_check_throws(function() { t.ptr(literal); }, TypeError); + do_check_throws(function() { t.ptr(literal); }, ctypesGlobal.TypeError); let p = t.ptr(s); do_check_eq(p.readString(), literal); @@ -1351,9 +1356,9 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared properties and functions on 'c.prototype' throw. for each (let p in props) - do_check_throws(function() { c.prototype[p]; }, Error); + do_check_throws(function() { c.prototype[p]; }, ctypesGlobal.Error); for each (let f in fns) - do_check_throws(function() { c.prototype[f](); }, Error); + do_check_throws(function() { c.prototype[f](); }, ctypesGlobal.Error); // Test that classes and prototypes are set up correctly on a constructed // type 't'. @@ -1386,12 +1391,12 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared properties and functions on 't.prototype.__proto__' // (and thus also 't.prototype') throw. for each (let p in instanceProps) { - do_check_throws(function() { t.prototype.__proto__[p]; }, Error); - do_check_throws(function() { t.prototype[p]; }, Error); + do_check_throws(function() { t.prototype.__proto__[p]; }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype[p]; }, ctypesGlobal.Error); } for each (let f in instanceFns) { - do_check_throws(function() { t.prototype.__proto__[f]() }, Error); - do_check_throws(function() { t.prototype[f]() }, Error); + do_check_throws(function() { t.prototype.__proto__[f]() }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype[f]() }, ctypesGlobal.Error); } // Check that 't.prototype' has the correct special properties. @@ -1400,7 +1405,7 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared special properties on 't.prototype' throw. for each (let p in specialProps) - do_check_throws(function() { t.prototype[p]; }, Error); + do_check_throws(function() { t.prototype[p]; }, ctypesGlobal.Error); // Test that an instance 'd' of 't' is a CData. if (t.__proto__ != ctypes.FunctionType.prototype) { @@ -1419,33 +1424,33 @@ function run_StructType_tests() { ctypes.StructType("t", [{"c": ctypes.int32_t}, {"d": ctypes.int64_t}]), [ "fields" ], [ "define" ], [], [ "addressOfField" ], [ "a", "b" ]); - do_check_throws(function() { ctypes.StructType(); }, Error); - do_check_throws(function() { ctypes.StructType("a", [], 5); }, Error); - do_check_throws(function() { ctypes.StructType(null, []); }, Error); - do_check_throws(function() { ctypes.StructType("a", null); }, Error); + do_check_throws(function() { ctypes.StructType(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.StructType("a", [], 5); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.StructType(null, []); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.StructType("a", null); }, ctypesGlobal.Error); // Check that malformed descriptors are an error. do_check_throws(function() { ctypes.StructType("a", [{"x":ctypes.int32_t}, {"x":ctypes.int8_t}]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [5]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [{}]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [{5:ctypes.int32_t}]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [{"5":ctypes.int32_t}]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [{"x":5}]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("a", [{"x":ctypes.int32_t()}]); - }, Error); + }, ctypesGlobal.Error); // Check that opaque structs work. let opaque_t = ctypes.StructType("a"); @@ -1455,7 +1460,7 @@ function run_StructType_tests() { do_check_true(opaque_t.prototype === undefined); do_check_true(opaque_t.fields === undefined); do_check_true(opaque_t.size === undefined); - do_check_throws(function() { opaque_t(); }, Error); + do_check_throws(function() { opaque_t(); }, ctypesGlobal.Error); let opaqueptr_t = opaque_t.ptr; do_check_true(opaqueptr_t.targetType === opaque_t); do_check_eq(opaqueptr_t.name, "a*"); @@ -1468,24 +1473,24 @@ function run_StructType_tests() { do_check_eq(ptrValue(opaqueptr), 1); do_check_throws(function() { opaqueptr.value = ctypes.StructType("a").ptr(); - }, TypeError); + }, ctypesGlobal.TypeError); // Check that 'define' works. - do_check_throws(function() { opaque_t.define(); }, Error); - do_check_throws(function() { opaque_t.define([], 0); }, Error); - do_check_throws(function() { opaque_t.define([{}]); }, Error); - do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, Error); + do_check_throws(function() { opaque_t.define(); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t.define([], 0); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t.define([{}]); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, ctypesGlobal.Error); do_check_throws(function() { opaque_t.define([{ a: ctypes.int32_t, b: ctypes.int64_t }]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { opaque_t.define([{ a: ctypes.int32_t }, { b: 0 }]); - }, Error); + }, ctypesGlobal.Error); do_check_false(opaque_t.hasOwnProperty("prototype")); // Check that circular references work with opaque structs... // but not crazy ones. - do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, Error); + do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, ctypesGlobal.Error); let circular_t = ctypes.StructType("circular", [{ a: opaqueptr_t }]); opaque_t.define([{ b: circular_t }]); let opaque = opaque_t(); @@ -1498,7 +1503,7 @@ function run_StructType_tests() { // original definition is preserved. do_check_throws(function() { opaque_t.define([{ c: ctypes.int32_t.array(8) }]); - }, Error); + }, ctypesGlobal.Error); do_check_eq(opaque_t.size, circular_t.size); do_check_true(opaque_t.prototype.hasOwnProperty("b")); do_check_false(opaque_t.prototype.hasOwnProperty("c")); @@ -1527,10 +1532,10 @@ function run_StructType_tests() { do_check_true(t_t.fields[2].c === g_t); do_check_true(t_t.fields[3].d === ctypes.int8_t); /* disabled temporarily per bug 598225. - do_check_throws(function() { t_t.fields.z = 0; }, Error); - do_check_throws(function() { t_t.fields[4] = 0; }, Error); - do_check_throws(function() { t_t.fields[4].a = 0; }, Error); - do_check_throws(function() { t_t.fields[4].e = 0; }, Error); + do_check_throws(function() { t_t.fields.z = 0; }, ctypesGlobal.Error); + do_check_throws(function() { t_t.fields[4] = 0; }, ctypesGlobal.Error); + do_check_throws(function() { t_t.fields[4].a = 0; }, ctypesGlobal.Error); + do_check_throws(function() { t_t.fields[4].e = 0; }, ctypesGlobal.Error); */ // Check that struct size bounds work, and that large, but not illegal, sizes @@ -1542,7 +1547,7 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xffffffff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, Error); + }, ctypesGlobal.Error); // Test 2: overflow struct size + struct tail padding. // To do this, we use a struct with maximum size and alignment 2. @@ -1551,7 +1556,7 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xfffffffe); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, Error); + }, ctypesGlobal.Error); } else { // Test 1: overflow struct size when converting from size_t to jsdouble. @@ -1560,25 +1565,25 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xfffffffffffff800); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, Error); + }, ctypesGlobal.Error); let small_t = ctypes.int8_t.array(0x400); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, Error); + }, ctypesGlobal.Error); large_t = ctypes.StructType("large_t", [{"a": ctypes.int8_t.array(0x1fffffffffffff)}]); do_check_eq(large_t.size, 0x1fffffffffffff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t.array(2)}, {"b": ctypes.int8_t}]); - }, Error); + }, ctypesGlobal.Error); // Test 2: overflow struct size + field padding + field size. large_t = ctypes.int8_t.array(0xfffffffffffff800); small_t = ctypes.int8_t.array(0x800); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, Error); + }, ctypesGlobal.Error); // Test 3: overflow struct size + struct tail padding. // To do this, we use a struct with maximum size and alignment 2. @@ -1588,7 +1593,7 @@ function run_StructType_tests() { small_t = ctypes.int8_t.array(0xfff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, Error); + }, ctypesGlobal.Error); } let g = g_t(); @@ -1597,8 +1602,8 @@ function run_StructType_tests() { g = new g_t(1, 2); do_check_eq(g.a, 1); do_check_eq(g.b, 2); - do_check_throws(function() { g_t(1); }, Error); - do_check_throws(function() { g_t(1, 2, 3); }, Error); + do_check_throws(function() { g_t(1); }, ctypesGlobal.Error); + do_check_throws(function() { g_t(1, 2, 3); }, ctypesGlobal.Error); for (let field in g) do_check_true(field == "a" || field == "b"); @@ -1623,9 +1628,9 @@ function run_StructType_tests() { g_a = s.addressOfField("b"); do_check_true(g_a.constructor === g_t.ptr); do_check_eq(g_a.contents.a, s.b.a); - do_check_throws(function() { s.addressOfField(); }, Error); - do_check_throws(function() { s.addressOfField("d"); }, Error); - do_check_throws(function() { s.addressOfField("a", 2); }, Error); + do_check_throws(function() { s.addressOfField(); }, ctypesGlobal.Error); + do_check_throws(function() { s.addressOfField("d"); }, ctypesGlobal.Error); + do_check_throws(function() { s.addressOfField("a", 2); }, ctypesGlobal.Error); do_check_eq(s.toSource(), "s_t(4, {\"a\": 7, \"b\": 2}, 10)"); do_check_eq(s.toSource(), s.toString()); @@ -1634,25 +1639,25 @@ function run_StructType_tests() { do_check_eq(s.b.b, s2.b.b); // Test that structs can be set from an object using 'value'. - do_check_throws(function() { s.value; }, Error); + do_check_throws(function() { s.value; }, ctypesGlobal.Error); let s_init = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13 }; s.value = s_init; do_check_eq(s.b.a, 9); do_check_eq(s.c, 13); - do_check_throws(function() { s.value = 5; }, Error); - do_check_throws(function() { s.value = ctypes.int32_t(); }, Error); - do_check_throws(function() { s.value = {}; }, Error); - do_check_throws(function() { s.value = { "a": 2 }; }, Error); - do_check_throws(function() { s.value = { "a": 2, "b": 5, "c": 10 }; }, Error); + do_check_throws(function() { s.value = 5; }, ctypesGlobal.Error); + do_check_throws(function() { s.value = ctypes.int32_t(); }, ctypesGlobal.Error); + do_check_throws(function() { s.value = {}; }, ctypesGlobal.Error); + do_check_throws(function() { s.value = { "a": 2 }; }, ctypesGlobal.Error); + do_check_throws(function() { s.value = { "a": 2, "b": 5, "c": 10 }; }, ctypesGlobal.Error); do_check_throws(function() { s.value = { "5": 2, "b": { "a": 9, "b": 5 }, "c": 13 }; - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { s.value = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13, "d": 17 }; - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { s.value = { "a": 2, "b": { "a": 9, "b": 5, "e": 9 }, "c": 13 }; - }, Error); + }, ctypesGlobal.Error); // Test that structs can be constructed similarly through ExplicitConvert, // and that the single-field case is disambiguated correctly. @@ -1665,7 +1670,7 @@ function run_StructType_tests() { v = v_t(8); do_check_eq(v.x, 8); let w_t = ctypes.StructType("w_t", [{ "y": v_t }]); - do_check_throws(function() { w_t(9); }, Error); + do_check_throws(function() { w_t(9); }, ctypesGlobal.Error); let w = w_t({ "x": 3 }); do_check_eq(w.y.x, 3); w = w_t({ "y": { "x": 19 } }); @@ -1685,10 +1690,10 @@ function run_StructType_tests() { // are illegal, but arrays of defined length work. do_check_throws(function() { ctypes.StructType("z_t", [{ a: ctypes.int32_t.array() }]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(0) }]); - }, Error); + }, ctypesGlobal.Error); z_t = ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(6) }]); do_check_eq(z_t.size, ctypes.int32_t.size * 6); let z = z_t([1, 2, 3, 4, 5, 6]); @@ -1704,11 +1709,11 @@ function run_PointerType_tests() { ctypes.PointerType(ctypes.int32_t), ctypes.PointerType(ctypes.int64_t), [ "targetType" ], [], [ "contents" ], [ "isNull" ], []); - do_check_throws(function() { ctypes.PointerType(); }, Error); - do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, Error); - do_check_throws(function() { ctypes.PointerType(null); }, Error); - do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, Error); - do_check_throws(function() { ctypes.PointerType("void"); }, Error); + do_check_throws(function() { ctypes.PointerType(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.PointerType(null); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.PointerType("void"); }, ctypesGlobal.Error); let name = "g_t"; let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -1729,10 +1734,10 @@ function run_PointerType_tests() { // Test ExplicitConvert. let p = p_t(); - do_check_throws(function() { p.value; }, Error); + do_check_throws(function() { p.value; }, ctypesGlobal.Error); do_check_eq(ptrValue(p), 0); - do_check_throws(function() { p.contents; }, Error); - do_check_throws(function() { p.contents = g; }, Error); + do_check_throws(function() { p.contents; }, ctypesGlobal.Error); + do_check_throws(function() { p.contents = g; }, ctypesGlobal.Error); p = p_t(5); do_check_eq(ptrValue(p), 5); p = p_t(ctypes.UInt64(10)); @@ -1741,22 +1746,22 @@ function run_PointerType_tests() { // Test ImplicitConvert. p.value = null; do_check_eq(ptrValue(p), 0); - do_check_throws(function() { p.value = 5; }, Error); + do_check_throws(function() { p.value = 5; }, ctypesGlobal.Error); // Test opaque pointers. let f_t = ctypes.StructType("FILE").ptr; do_check_eq(f_t.name, "FILE*"); do_check_eq(f_t.toSource(), 'ctypes.StructType("FILE").ptr'); let f = new f_t(); - do_check_throws(function() { f.contents; }, Error); - do_check_throws(function() { f.contents = 0; }, Error); + do_check_throws(function() { f.contents; }, ctypesGlobal.Error); + do_check_throws(function() { f.contents = 0; }, ctypesGlobal.Error); let f = f_t(5); - do_check_throws(function() { f.contents = 0; }, Error); + do_check_throws(function() { f.contents = 0; }, ctypesGlobal.Error); do_check_eq(f.toSource(), 'FILE.ptr(ctypes.UInt64("0x5"))'); - do_check_throws(function() { f_t(p); }, Error); - do_check_throws(function() { f.value = p; }, Error); - do_check_throws(function() { p.value = f; }, Error); + do_check_throws(function() { f_t(p); }, ctypesGlobal.Error); + do_check_throws(function() { f.value = p; }, ctypesGlobal.Error); + do_check_throws(function() { p.value = f; }, ctypesGlobal.Error); // Test void pointers. let v_t = ctypes.PointerType(ctypes.void_t); @@ -1781,7 +1786,7 @@ function run_PointerType_tests() { // but that the former cannot be dereferenced. let z_t = ctypes.int32_t.array().ptr; do_check_eq(ptrValue(z_t()), 0); - do_check_throws(function() { z_t().contents }, Error); + do_check_throws(function() { z_t().contents }, ctypesGlobal.Error); z_t = ctypes.int32_t.array(0).ptr; do_check_eq(ptrValue(z_t()), 0); let z = ctypes.int32_t.array(0)().address(); @@ -1795,25 +1800,25 @@ function run_FunctionType_tests() { [ "abi", "returnType", "argTypes", "isVariadic" ], undefined, undefined, undefined, undefined); - do_check_throws(function() { ctypes.FunctionType(); }, Error); + do_check_throws(function() { ctypes.FunctionType(); }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.void_t ]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.void_t ], 5); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, ctypes.void_t); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, null); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t()); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.void_t, ctypes.void_t); - }, Error); + }, ctypesGlobal.Error); let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]); let g = g_t(1, 2); @@ -1840,11 +1845,11 @@ function run_FunctionType_tests() { "ctypes.FunctionType(ctypes.default_abi, g_t).ptr"); // Check that constructing a FunctionType CData directly throws. - do_check_throws(function() { f_t(); }, Error); + do_check_throws(function() { f_t(); }, ctypesGlobal.Error); // Test ExplicitConvert. let f = fp_t(); - do_check_throws(function() { f.value; }, Error); + do_check_throws(function() { f.value; }, ctypesGlobal.Error); do_check_eq(ptrValue(f), 0); f = fp_t(5); do_check_eq(ptrValue(f), 5); @@ -1854,15 +1859,15 @@ function run_FunctionType_tests() { // Test ImplicitConvert. f.value = null; do_check_eq(ptrValue(f), 0); - do_check_throws(function() { f.value = 5; }, Error); + do_check_throws(function() { f.value = 5; }, ctypesGlobal.Error); do_check_eq(f.toSource(), 'ctypes.FunctionType(ctypes.default_abi, g_t).ptr(ctypes.UInt64("0x0"))'); // Test ImplicitConvert from a function pointer of different type. let f2_t = ctypes.FunctionType(ctypes.default_abi, g_t, [ ctypes.int32_t ]); let f2 = f2_t.ptr(); - do_check_throws(function() { f.value = f2; }, Error); - do_check_throws(function() { f2.value = f; }, Error); + do_check_throws(function() { f.value = f2; }, ctypesGlobal.Error); + do_check_throws(function() { f2.value = f; }, ctypesGlobal.Error); // Test that converting to a voidptr_t works. let v = ctypes.voidptr_t(f2); @@ -1893,8 +1898,8 @@ function run_FunctionType_tests() { do_check_true(f4_t.argTypes[0] === ctypes.int32_t); do_check_true(f4_t.argTypes[1] === fp_t); /* disabled temporarily per bug 598225. - do_check_throws(function() { f4_t.argTypes.z = 0; }, Error); - do_check_throws(function() { f4_t.argTypes[0] = 0; }, Error); + do_check_throws(function() { f4_t.argTypes.z = 0; }, ctypesGlobal.Error); + do_check_throws(function() { f4_t.argTypes[0] = 0; }, ctypesGlobal.Error); */ let t4_t = f4_t.ptr.ptr.array(8).array(); @@ -1922,10 +1927,10 @@ function run_ArrayType_tests() { ctypes.ArrayType(ctypes.int32_t, 10), ctypes.ArrayType(ctypes.int64_t), [ "elementType", "length" ], [], [ "length" ], [ "addressOfElement" ]); - do_check_throws(function() { ctypes.ArrayType(); }, Error); - do_check_throws(function() { ctypes.ArrayType(null); }, Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, 1, 5); }, Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, -1); }, Error); + do_check_throws(function() { ctypes.ArrayType(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(null); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, 1, 5); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, -1); }, ctypesGlobal.Error); let name = "g_t"; let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -1942,7 +1947,7 @@ function run_ArrayType_tests() { "ctypes.StructType(\"g_t\", [{ \"a\": ctypes.int32_t }, { \"b\": ctypes.double }]).array(10)"); do_check_eq(a_t.array().name, name + "[][10]"); do_check_eq(a_t.array(5).name, name + "[5][10]"); - do_check_throws(function() { ctypes.int32_t.array().array(); }, Error); + do_check_throws(function() { ctypes.int32_t.array().array(); }, ctypesGlobal.Error); let a = new a_t(); do_check_eq(a[0].a, 0); @@ -1950,9 +1955,9 @@ function run_ArrayType_tests() { a[0] = g; do_check_eq(a[0].a, 1); do_check_eq(a[0].b, 2); - do_check_throws(function() { a[-1]; }, Error); + do_check_throws(function() { a[-1]; }, ctypesGlobal.Error); do_check_eq(a[9].a, 0); - do_check_throws(function() { a[10]; }, Error); + do_check_throws(function() { a[10]; }, ctypesGlobal.Error); do_check_eq(a[ctypes.Int64(0)].a, 1); do_check_eq(a[ctypes.UInt64(0)].b, 2); @@ -1973,9 +1978,9 @@ function run_ArrayType_tests() { do_check_eq(a2.constructor.length, 5); do_check_eq(a2.length, 5); do_check_eq(a2.constructor.size, g_t.size * 5); - do_check_throws(function() { new a2_t(); }, Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t)); }, Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t), 5); }, Error); + do_check_throws(function() { new a2_t(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t)); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t), 5); }, ctypesGlobal.Error); let b_t = ctypes.int8_t.array(ctypes.UInt64(0xffff)); do_check_eq(b_t.length, 0xffff); @@ -1987,24 +1992,24 @@ function run_ArrayType_tests() { if (ctypes.size_t.size == 4) { do_check_throws(function() { ctypes.ArrayType(ctypes.int8_t, 0x100000000); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.ArrayType(ctypes.int16_t, 0x80000000); - }, Error); + }, ctypesGlobal.Error); let large_t = ctypes.int8_t.array(0x80000000); - do_check_throws(function() { large_t.array(2); }, Error); + do_check_throws(function() { large_t.array(2); }, ctypesGlobal.Error); } else { do_check_throws(function() { ctypes.ArrayType(ctypes.int8_t, ctypes.UInt64("0xffffffffffffffff")); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.ArrayType(ctypes.int16_t, ctypes.UInt64("0x8000000000000000")); - }, Error); + }, ctypesGlobal.Error); let large_t = ctypes.int8_t.array(0x8000000000000000); - do_check_throws(function() { large_t.array(2); }, Error); + do_check_throws(function() { large_t.array(2); }, ctypesGlobal.Error); } // Test that arrays ImplicitConvert to pointers. @@ -2029,11 +2034,11 @@ function run_ArrayType_tests() { c.value = c; do_check_eq(c[3], 4); - do_check_throws(function() { c.value; }, Error); - do_check_throws(function() { c.value = [1, 2, 3, 4, 5]; }, Error); - do_check_throws(function() { c.value = [1, 2, 3, 4, 5, 6, 7]; }, Error); - do_check_throws(function() { c.value = [1, 2, 7.4, 4, 5, 6]; }, Error); - do_check_throws(function() { c.value = []; }, Error); + do_check_throws(function() { c.value; }, ctypesGlobal.Error); + do_check_throws(function() { c.value = [1, 2, 3, 4, 5]; }, ctypesGlobal.Error); + do_check_throws(function() { c.value = [1, 2, 3, 4, 5, 6, 7]; }, ctypesGlobal.Error); + do_check_throws(function() { c.value = [1, 2, 7.4, 4, 5, 6]; }, ctypesGlobal.Error); + do_check_throws(function() { c.value = []; }, ctypesGlobal.Error); } function run_cast_tests() { @@ -2047,9 +2052,9 @@ function run_cast_tests() { do_check_eq(i.value, k.value); // Test casting to a type of undefined or larger size. - do_check_throws(function() { ctypes.cast(i, ctypes.void_t); }, Error); - do_check_throws(function() { ctypes.cast(i, ctypes.int32_t.array()); }, Error); - do_check_throws(function() { ctypes.cast(i, ctypes.int64_t); }, Error); + do_check_throws(function() { ctypes.cast(i, ctypes.void_t); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.cast(i, ctypes.int32_t.array()); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.cast(i, ctypes.int64_t); }, ctypesGlobal.Error); // Test casting between special types. let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -2087,7 +2092,7 @@ function run_void_tests(library) { // Test that library.declare throws with void function args. do_check_throws(function() { library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t, ctypes.void_t); - }, Error); + }, ctypesGlobal.Error); #ifdef WIN32 #ifndef HAVE_64BIT_OS @@ -2097,7 +2102,7 @@ function run_void_tests(library) { // Check that WINAPI symbol lookup for a regular stdcall function fails. do_check_throws(function() { let test_winapi_t = library.declare("test_void_t_stdcall", ctypes.winapi_abi, ctypes.void_t); - }, Error); + }, ctypesGlobal.Error); #endif #endif } @@ -2110,7 +2115,7 @@ function run_string_tests(library) { // don't convert anything else to a string let vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []]; for (let i = 0; i < vals.length; i++) - do_check_throws(function() { test_ansi_len(vals[i]); }, TypeError); + do_check_throws(function() { test_ansi_len(vals[i]); }, ctypesGlobal.TypeError); let test_wide_len = library.declare("test_wide_len", ctypes.default_abi, ctypes.int32_t, ctypes.jschar.ptr); do_check_eq(test_wide_len("hello world"), 11); @@ -2235,7 +2240,7 @@ function run_function_tests(library) do_check_eq(test_ansi_len.constructor.toSource(), "ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.char.ptr]).ptr"); /* disabled temporarily per bug 598225. - do_check_throws(function() { test_ansi_len.value = null; }, Error); + do_check_throws(function() { test_ansi_len.value = null; }, ctypesGlobal.Error); do_check_eq(ptrValue(test_ansi_len), ptrValue(ptr)); */ @@ -2244,7 +2249,7 @@ function run_function_tests(library) do_check_true(test_ansi_len_2.constructor === fn_t); do_check_eq(ptrValue(test_ansi_len), ptrValue(test_ansi_len_2)); /* disabled temporarily per bug 598225. - do_check_throws(function() { test_ansi_len_2.value = null; }, Error); + do_check_throws(function() { test_ansi_len_2.value = null; }, ctypesGlobal.Error); do_check_eq(ptrValue(test_ansi_len_2), ptrValue(ptr)); */ } @@ -2262,7 +2267,7 @@ function run_closure_tests(library) return 1; } let fn_t = ctypes.FunctionType(ctypes.winapi_abi, ctypes.int32_t, []).ptr; - do_check_throws(function() { fn_t(closure_fn) }, Error); + do_check_throws(function() { fn_t(closure_fn) }, ctypesGlobal.Error); #endif #endif } @@ -2317,29 +2322,29 @@ function run_variadic_tests(library) { do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.bool, [ctypes.bool, "...", ctypes.bool]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.bool, ["..."]); - }, Error); + }, ctypesGlobal.Error); #ifdef WIN32 #ifndef HAVE_64BIT_OS do_check_throws(function() { ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool, [ctypes.bool, "..."]); - }, Error); + }, ctypesGlobal.Error); do_check_throws(function() { ctypes.FunctionType(ctypes.winapi_abi, ctypes.bool, [ctypes.bool, "..."]); - }, Error); + }, ctypesGlobal.Error); #endif #endif do_check_throws(function() { // No variadic closure callbacks allowed. sum_va_type(function(){}); - }, Error); + }, ctypesGlobal.Error); let count_true_va = library.declare("test_sum_va_cdecl", ctypes.default_abi, ctypes.uint8_t, ctypes.uint8_t, "..."); @@ -2385,7 +2390,7 @@ function run_variadic_tests(library) { [ctypes.uint8_t]).ptr; do_check_throws(function() { sum_va_type().value = sum_notva_type(); - }, Error); + }, ctypesGlobal.Error); } function run_static_data_tests(library) diff --git a/toolkit/components/perf/PerfMeasurement.cpp b/toolkit/components/perf/PerfMeasurement.cpp index acac0358b76a..f166328fd7e9 100644 --- a/toolkit/components/perf/PerfMeasurement.cpp +++ b/toolkit/components/perf/PerfMeasurement.cpp @@ -112,11 +112,7 @@ Module::Call(nsIXPConnectWrappedNative* wrapper, jsval* vp, PRBool* _retval) { - JSObject* scope = JS_GetScopeChain(cx); - if (!scope) - return NS_ERROR_NOT_AVAILABLE; - - JSObject* global = JS_GetGlobalForObject(cx, scope); + JSObject* global = JS_GetGlobalForScopeChain(cx); if (!global) return NS_ERROR_NOT_AVAILABLE; From 294b9979c3a89437bf1e907ef26d2fc141184a9d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 18 Feb 2011 18:29:20 -0800 Subject: [PATCH 17/42] Fix g-mail JIT crash (bug 635295, r=luke). --- js/src/methodjit/MonoIC.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 1dc5caad493c..e639d23d162a 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1268,6 +1268,20 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) bool nativeDead = ic.fastGuardedNative && (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative)); + /* + * There are three conditions where we need to relink: + * (1) purgeAll is true. + * (2) The native is dead, since it always has a stub. + * (3) The fastFun is dead *and* there is a closure stub. + * + * Note although both objects can be non-NULL, there can only be one + * of [closure, native] stub per call IC. + */ + if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) { + repatcher.relink(ic.funJump, ic.slowPathStart); + ic.hit = false; + } + if (fastFunDead) { repatcher.repatch(ic.funGuard, NULL); ic.releasePool(CallICInfo::Pool_ClosureStub); @@ -1286,16 +1300,6 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); repatcher.relink(oolJump, icCall); } - - /* - * Only relink the fast-path if there are no connected stubs, or we're - * trying to disconnect all stubs. Otherwise, we're just disabling an - * optimization that must take up space anyway (see bug 632729). - */ - if (purgeAll || !(ic.fastGuardedObject || ic.fastGuardedNative)) { - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.hit = false; - } } if (purgeAll) { From f445d3820e4508f1b7358761ff7f41860800e595 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 18 Feb 2011 19:30:18 -0800 Subject: [PATCH 18/42] Bug 634444 - Back out for regressions (see the bug for their details). Either r=njn or r=me depending how you want to consider whose idea/plan it was --- js/src/jsscan.cpp | 120 ++++++++++++++-------------------------------- js/src/jsscan.h | 2 +- 2 files changed, 38 insertions(+), 84 deletions(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 54174b7b6b7d..028f4f675b42 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -371,9 +371,8 @@ TokenStream::peekChars(intN n, jschar *cp) return i == n; } -/* Finds the next EOL, but stops once 'max' chars past the start of the token have been scanned. */ jschar * -TokenStream::findWlineLimit(jschar *tokptr, int max) +TokenStream::findEOL() { TokenBuf tmpUserbuf = userbuf; jschar *tmpLinebase = linebase; @@ -381,24 +380,12 @@ TokenStream::findWlineLimit(jschar *tokptr, int max) uintN tmpFlags = flags; uintN tmpLineno = lineno; - /* FIXME: this assertion is commented out due to bug 635144. */ - /*JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit);*/ - userbuf.ptr = tokptr; /* Start scanning at tokptr. */ - - jschar *wlinelimit; - jschar *wlinelimitmax = tokptr + max + 1; while (true) { - if (userbuf.ptr > wlinelimitmax) { - wlinelimit = wlinelimitmax; - break; - } - jschar* next = userbuf.ptr; int32 c = getChar(); - if (c == '\n' || c == EOF) { - wlinelimit = next; + if (c == '\n' || c == EOF) break; - } } + jschar *linelimit = userbuf.ptr; /* Need to restore everything changed by getChar(). */ userbuf = tmpUserbuf; @@ -407,7 +394,7 @@ TokenStream::findWlineLimit(jschar *tokptr, int max) flags = tmpFlags; lineno = tmpLineno; - return wlinelimit; + return linelimit; } bool @@ -416,15 +403,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro { JSErrorReport report; char *message; - - /* "wline" is short for "window into line", because we might not show it all. */ - size_t wlinelength; - jschar *wlinechars; - jschar *wlinelimit; - jschar *wlinebase; - jschar *tokptr; - char *wlinebytes; - + size_t linelength; + jschar *linechars; + jschar *linelimit; + char *linebytes; bool warning; JSBool ok; TokenPos *tp; @@ -444,8 +426,8 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro report.flags = flags; report.errorNumber = errorNumber; message = NULL; - wlinechars = NULL; - wlinebytes = NULL; + linechars = NULL; + linebytes = NULL; MUST_FLOW_THROUGH("out"); ok = js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, @@ -469,49 +451,29 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro } report.lineno = lineno; + linelimit = findEOL(); + linelength = linelimit - linebase; + + linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar)); + if (!linechars) { + warning = false; + goto out; + } + memcpy(linechars, linebase, linelength * sizeof(jschar)); + linechars[linelength] = 0; + linebytes = js_DeflateString(cx, linechars, linelength); + if (!linebytes) { + warning = false; + goto out; + } + report.linebuf = linebytes; /* the offending source line, without final \n */ + index = (tp->begin.lineno == tp->end.lineno) - ? tp->begin.index /* the column number of the start of the bad token */ - : 0; /* the bad token didn't start on this line; don't give a column */ - tokptr = linebase + index; - - /* - * We show only a portion of the line around the erroneous token -- WINDOW - * chars before and after the first char in the token. This is because - * lines can be very long and printing the whole line is (a) not that - * helpful, and (b) can waste a lot of memory. See bug 634444. - */ - static const size_t WINDOW = 100; - - /* Truncate at the front if necessary. */ - if (linebase + WINDOW < tokptr) { - wlinebase = tokptr - WINDOW; - size_t nTrunc = wlinebase - linebase; - index -= nTrunc; - } else { - wlinebase = linebase; - } - - /* Find EOL, or truncate at the back if necessary. */ - wlinelimit = findWlineLimit(tokptr, WINDOW); - - wlinelength = wlinelimit - wlinebase; - JS_ASSERT(wlinelength <= WINDOW * 2 + 1); - wlinechars = (jschar *)cx->malloc((wlinelength + 1) * sizeof(jschar)); - if (!wlinechars) { - warning = false; - goto out; - } - memcpy(wlinechars, wlinebase, wlinelength * sizeof(jschar)); - wlinechars[wlinelength] = 0; - wlinebytes = js_DeflateString(cx, wlinechars, wlinelength); - if (!wlinebytes) { - warning = false; - goto out; - } - report.linebuf = wlinebytes; /* some or all of the offending source line, without final \n */ + ? tp->begin.index /* the column number of the start of the bad token */ + : 0; report.tokenptr = report.linebuf + index; - report.uclinebuf = wlinechars; + report.uclinebuf = linechars; report.uctokenptr = report.uclinebuf + index; /* @@ -568,10 +530,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro (*onError)(cx, message, &report); out: - if (wlinebytes) - cx->free(wlinebytes); - if (wlinechars) - cx->free(wlinechars); + if (linebytes) + cx->free(linebytes); + if (linechars) + cx->free(linechars); if (message) cx->free(message); if (report.ucmessage) @@ -810,7 +772,6 @@ TokenStream::newToken(ptrdiff_t adjust) cursor = (cursor + 1) & ntokensMask; Token *tp = &tokens[cursor]; tp->ptr = userbuf.ptr + adjust; - JS_ASSERT(tp->ptr >= linebase); tp->pos.begin.index = tp->ptr - linebase; tp->pos.begin.lineno = tp->pos.end.lineno = lineno; return tp; @@ -839,7 +800,6 @@ TokenStream::getTokenInternal() Token *tp; JSAtom *atom; bool hadUnicodeEscape; - int adjust; #if JS_HAS_XML_SUPPORT JSBool inTarget; size_t targetLength; @@ -1015,28 +975,22 @@ TokenStream::getTokenInternal() retry: /* - * This gets the next non-space char and starts the token. adjust is set - * to -1 because we'll probably scan the first char of the upcoming token - * while chewing up the whitespace. + * This gets the next non-space char and starts the token. */ - adjust = -1; do { c = getChar(); if (c == '\n') { flags &= ~TSF_DIRTYLINE; - if (flags & TSF_NEWLINES) { - adjust = 0; /* early break means we didn't scan an extra char */ + if (flags & TSF_NEWLINES) break; - } } } while (ScanAsSpace((jschar)c)); + tp = newToken(-1); if (c == EOF) { - tp = newToken(0); /* no -1 here because userbuf.ptr isn't incremented for EOF */ tt = TOK_EOF; goto out; } - tp = newToken(adjust); /* * Look for an identifier. diff --git a/js/src/jsscan.h b/js/src/jsscan.h index 7157124e52cc..b65219e7d947 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -476,7 +476,7 @@ class TokenStream bool matchUnicodeEscapeIdent(int32 *c); JSBool peekChars(intN n, jschar *cp); JSBool getXMLEntity(); - jschar *findWlineLimit(jschar *tokptr, int max); + jschar *findEOL(); JSBool matchChar(int32 expect) { int32 c = getChar(); From e6c71263318379ee65183e2e68ed3262828bbc2a Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 18 Feb 2011 20:54:47 -0800 Subject: [PATCH 19/42] Followup fix for bug 631135 to use the right method to compute calling scope when calling evalInSandbox. r=mrbkap --- js/src/xpconnect/src/xpccomponents.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 17837aa5b1af..16004d5d229a 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3630,16 +3630,9 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, { JSAutoRequest req(cx); - if (!JS_GetGlobalForCallingScript(cx, &callingScope)) { - return NS_ERROR_FAILURE; - } + callingScope = JS_GetGlobalForScopeChain(cx); if (!callingScope) { - callingScope = JS_GetGlobalObject(cx); - if (!callingScope) - return NS_ERROR_FAILURE; - OBJ_TO_INNER_OBJECT(cx, callingScope); - if (!callingScope) - return NS_ERROR_FAILURE; + return NS_ERROR_FAILURE; } } From f12065a1c0c10a18fcd9395c07e2b4e8f14d5769 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Feb 2011 17:52:55 -0800 Subject: [PATCH 20/42] Unqualified function invocation doesn't use the global object the property was gotten from as |this| (bug 634590, r=brendan). --- js/src/jit-test/tests/basic/testBug634590.js | 7 ++ js/src/jsfuninlines.h | 26 ++++++ js/src/jsinterp.cpp | 84 +++++++++++--------- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 11 ++- js/src/jsobj.h | 13 ++- js/src/jsobjinlines.h | 2 + js/src/jstracer.cpp | 4 +- js/src/methodjit/PolyIC.cpp | 4 +- 9 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug634590.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js new file mode 100644 index 000000000000..da47ed0090df --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -0,0 +1,7 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), + "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 4cbb744b236e..d951e93c4625 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -49,4 +49,30 @@ JSFunction::inStrictMode() const return script()->strictModeCode; } +namespace js { + +static inline bool +IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) +{ + /* + * Look past any wrappers. If the callee is a strict function it is always + * safe because we won't do 'this' coercion in strict mode. Otherwise the + * callee is only safe to transform into the lazy 'this' token (undefined) + * if it is in the current scope. Without this restriction, lazy 'this' + * coercion would pick up the wrong global in the other scope. + */ + if (callee->isProxy()) { + callee = callee->unwrap(); + if (!callee->isFunction()) + return true; // treat any non-wrapped-function proxy as strict + + JSFunction *fun = callee->getFunctionPrivate(); + if (fun->isInterpreted() && fun->inStrictMode()) + return true; + } + return callee->getGlobal() == cx->fp()->scopeChain().getGlobal(); +} + +} + #endif /* jsfuninlines_h___ */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 3bc4c73f89da..9dbc9e5bac6c 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2151,6 +2151,30 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } +static inline bool +SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +{ + if (!funval.isObject() || + ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && + IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { + /* + * We can avoid computing 'this' eagerly and push the implicit 'this' + * value (undefined), as long the scope is cachable and we are not + * crossing into another scope (in which case lazy calculation of 'this' + * would pick up the new and incorrect scope). 'strict' functions are an + * exception. We don't want to eagerly calculate 'this' for them even if + * the callee is in a different scope. + */ + *vp = UndefinedValue(); + return true; + } + + if (!(obj = obj->thisObject(cx))) + return false; + *vp = ObjectValue(*obj); + return true; +} + namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2176,8 +2200,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4765,26 +4789,13 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj) \ - JS_BEGIN_MACRO \ - Class *clasp; \ - JSObject *thisp = obj; \ - if (!thisp->getParent() || \ - (clasp = thisp->getClass()) == &js_CallClass || \ - clasp == &js_BlockClass || \ - clasp == &js_DeclEnvClass) { \ - /* Push the ImplicitThisValue for the Environment Record */ \ - /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ - /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ - /* (Function Calls). */ \ - PUSH_UNDEFINED(); \ - } else { \ - thisp = thisp->thisObject(cx); \ - if (!thisp) \ - goto error; \ - PUSH_OBJECT(*thisp); \ - } \ - JS_END_MACRO +#define SLOW_PUSH_THISV(cx, obj, funval) \ + JS_BEGIN_MACRO \ + Value v; \ + if (!SlowThis(cx, obj, funval, &v)) \ + goto error; \ + PUSH_COPY(v); \ + JS_END_MACRO \ BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4814,20 +4825,17 @@ BEGIN_CASE(JSOP_CALLNAME) PUSH_COPY(rval); } - /* - * Push results, the same as below, but with a prop$ hit there - * is no need to test for the unusual and uncacheable case where - * the caller determines |this|. - */ -#if DEBUG - Class *clasp; - JS_ASSERT(!obj->getParent() || - (clasp = obj->getClass()) == &js_CallClass || - clasp == &js_BlockClass || - clasp == &js_DeclEnvClass); -#endif - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_UNDEFINED(); + JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { + if (regs.sp[-1].isObject() && + !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { + if (!(obj = obj->thisObject(cx))) + return false; + PUSH_OBJECT(*obj); + } else { + PUSH_UNDEFINED(); + } + } len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4865,7 +4873,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6368,7 +6376,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c55c868b3feb..c976cdea8da1 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 34519e27f2d9..dff4eff44829 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4429,6 +4429,11 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } +namespace js { + + +} + /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5036,7 +5041,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? js_IsCacheableNonGlobalScope(obj) + ? IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5146,11 +5151,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because js_IsCacheableNonGlobalScope + * The test order here matters because IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || js_IsCacheableNonGlobalScope(obj); + !obj->getParent() || IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 27d59fa02121..176a91182fbf 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,16 +1661,19 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); +extern JS_FRIEND_DATA(js::Class) js_CallClass; +extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; + +namespace js { + /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ -inline bool -js_IsCacheableNonGlobalScope(JSObject *obj) +static inline bool +IsCacheableNonGlobalScope(JSObject *obj) { - extern JS_FRIEND_DATA(js::Class) js_CallClass; - extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1682,6 +1685,8 @@ js_IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } +} + /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d08804012453..697e796b2a4b 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -49,6 +49,7 @@ #include "jsobj.h" #include "jsprobes.h" #include "jspropertytree.h" +#include "jsproxy.h" #include "jsscope.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -60,6 +61,7 @@ #include "jsscopeinlines.h" #include "jsstr.h" +#include "jsfuninlines.h" #include "jsgcinlines.h" #include "jsprobes.h" diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 79fddf099902..6d540c5e461a 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!js_IsCacheableNonGlobalScope(child)) { + if (!IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15183,7 +15183,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!js_IsCacheableNonGlobalScope(obj)) + if (!IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index a954a7536819..aa82dabf6dbf 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From 551d670628519935497da49ac0ec713465d64c9d Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sat, 19 Feb 2011 02:25:39 -0800 Subject: [PATCH 21/42] Diagnostic patch to debug intermittent assertion following bug 631135. r=debugging --HG-- extra : rebase_source : e8fb8988765236ce92ca4a7477a54d9eb8070794 --- js/src/jsinterp.h | 2 +- js/src/jsinterpinlines.h | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 57df714a5ef8..c344e2a965a7 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -196,7 +196,7 @@ struct JSStackFrame uint32 nactual, uint32 flags); /* Used for SessionInvoke. */ - inline void resetInvokeCallFrame(); + inline void resetInvokeCallFrame(JSContext *cx); /* Called by method-jit stubs and serve as a specification for jit-code. */ inline void initCallFrameCallerHalf(JSContext *cx, uint32 flags, void *ncode); diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c976cdea8da1..c00c4af2c6b9 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -98,8 +98,21 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, } inline void -JSStackFrame::resetInvokeCallFrame() +JSStackFrame::resetInvokeCallFrame(JSContext *cx) { +#ifdef DEBUG + if (!hasCallObj() && scopeChain_ != calleeValue().toObject().getParent()) { + js_DumpValue(calleeValue()); + if (JSObject *sc = scopeChain_) + js_DumpObject(sc); + else + fprintf(stderr, "scopeChain_ == NULL\n"); + if (JSObject *par = calleeValue().toObject().getParent()) + js_DumpObject(par); + else + fprintf(stderr, "parent == NULL\n"); + } +#endif /* Undo changes to frame made during execution; see initCallFrame */ if (hasArgsObj()) @@ -598,7 +611,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const /* Clear any garbage left from the last Invoke. */ JSStackFrame *fp = frame_.fp(); fp->clearMissingArgs(); - fp->resetInvokeCallFrame(); + fp->resetInvokeCallFrame(cx); SetValueRangeToUndefined(fp->slots(), script_->nfixed); JSBool ok; From 024e26d08bf6d58c6b309a50b17bef82ffaccb0c Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sat, 19 Feb 2011 16:43:36 -0800 Subject: [PATCH 22/42] Fix spurious "attempt to run compile-and-go script on a cleared scope" errors (635548, r/a=shaver). --- js/src/jsinterp.cpp | 5 +++-- js/src/jsscript.h | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 9dbc9e5bac6c..d7bd63581be0 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -593,8 +593,9 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) JS_ASSERT(script); /* FIXME: Once bug 470510 is fixed, make this an assert. */ - if (script->compileAndGo) { - int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32(); + if (script->compileAndGo && script->hasGlobals()) { + int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS) + .toInt32(); if (flags & JSGLOBAL_FLAGS_CLEARED) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE); return false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 7394abe95f3c..b9995ac7890b 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -514,6 +514,10 @@ struct JSScript { return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset); } + bool hasGlobals() const { + return isValidOffset(globalsOffset); + } + js::GlobalSlotArray *globals() { JS_ASSERT(isValidOffset(globalsOffset)); return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset); From 313285871c2e540eeeca96d6951e5b46479a7f32 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sat, 19 Feb 2011 19:50:19 -0800 Subject: [PATCH 23/42] Backed out changeset 4d86e63ff60d, diagnostic patch; back out 3da12edf735e, followup fix; back out bug 631135 completely, unexplained intermittent orange. --HG-- extra : rebase_source : e2e56a78bc0eea0d6e005bc976c74b33d8c22281 --- caps/tests/mochitest/test_bug246699.html | 14 +- content/base/src/nsContentUtils.cpp | 8 +- dom/base/nsGlobalWindow.cpp | 23 +- dom/base/nsJSEnvironment.cpp | 20 +- dom/base/nsJSUtils.cpp | 23 +- dom/indexedDB/IDBKeyRange.cpp | 2 +- dom/src/threads/nsDOMWorker.cpp | 5 +- js/src/jsapi-tests/Makefile.in | 1 - js/src/jsapi-tests/testCrossGlobal.cpp | 90 ---- js/src/jsapi.cpp | 26 +- js/src/jsapi.h | 31 +- js/src/jscntxt.cpp | 20 +- js/src/jscntxt.h | 71 +-- js/src/jscntxtinlines.h | 146 ++---- js/src/jscompartment.cpp | 13 +- js/src/jsinterp.cpp | 27 ++ js/src/jsinterp.h | 5 +- js/src/jsinterpinlines.h | 17 +- js/src/jsobj.cpp | 36 +- js/src/jsobjinlines.h | 12 +- js/src/jsparse.cpp | 5 +- js/src/jsregexp.cpp | 21 +- js/src/jsstr.cpp | 16 +- js/src/jsxml.cpp | 30 +- js/src/methodjit/MonoIC.cpp | 14 +- js/src/shell/js.cpp | 147 ++---- .../e4x/extensions/cross-global-settings.js | 41 -- js/src/tests/e4x/extensions/jstests.list | 1 - .../ecma_5/extensions/cross-global-call.js | 70 --- js/src/tests/ecma_5/extensions/jstests.list | 1 - js/src/xpconnect/src/xpccomponents.cpp | 3 +- js/src/xpconnect/src/xpcthrower.cpp | 3 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 27 +- .../ctypes/tests/unit/test_jsctypes.js.in | 419 +++++++++--------- toolkit/components/perf/PerfMeasurement.cpp | 6 +- 35 files changed, 482 insertions(+), 912 deletions(-) delete mode 100644 js/src/jsapi-tests/testCrossGlobal.cpp delete mode 100644 js/src/tests/e4x/extensions/cross-global-settings.js delete mode 100644 js/src/tests/ecma_5/extensions/cross-global-call.js diff --git a/caps/tests/mochitest/test_bug246699.html b/caps/tests/mochitest/test_bug246699.html index 8849d21f20e2..d21cc4066a20 100644 --- a/caps/tests/mochitest/test_bug246699.html +++ b/caps/tests/mochitest/test_bug246699.html @@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=246699 Mozilla Bug 246699

 
 
diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 2fec90552bb8..e953340d4b38 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5799,13 +5799,9 @@ CloneSimpleValues(JSContext* cx, // RegExp objects. if (js_ObjectIsRegExp(obj)) { - JSObject* global = JS_GetGlobalForScopeChain(cx); - if (!global) { - return NS_ERROR_FAILURE; - } - JSObject* proto; - if (!js_GetClassPrototype(cx, global, JSProto_RegExp, &proto)) { + if (!js_GetClassPrototype(cx, JS_GetScopeChain(cx), JSProto_RegExp, + &proto)) { return NS_ERROR_FAILURE; } JSObject* newRegExp = js_CloneRegExpObject(cx, obj, proto); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index fea12e49f3d7..bc90c1938756 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -5804,17 +5804,30 @@ nsGlobalWindow::CallerInnerWindow() return nsnull; } - JSObject *callerGlobal; - if (!::JS_GetGlobalForCallingScript(cx, &callerGlobal) || !callerGlobal) - return nsnull; + JSObject *scope = nsnull; + JSStackFrame *fp = nsnull; + JS_FrameIterator(cx, &fp); + if (fp) { + while (fp->isDummyFrame()) { + if (!JS_FrameIterator(cx, &fp)) + break; + } + + if (fp) + scope = &fp->scopeChain(); + } + + if (!scope) + scope = JS_GetScopeChain(cx); JSAutoEnterCompartment ac; - if (!ac.enter(cx, callerGlobal)) + if (!ac.enter(cx, scope)) return nsnull; nsCOMPtr wrapper; nsContentUtils::XPConnect()-> - GetWrappedNativeOfJSObject(cx, callerGlobal, getter_AddRefs(wrapper)); + GetWrappedNativeOfJSObject(cx, ::JS_GetGlobalForObject(cx, scope), + getter_AddRefs(wrapper)); if (!wrapper) return nsnull; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index b4a88fb4fa7d..2f026e56c986 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -670,25 +670,9 @@ nsJSContext::DOMOperationCallback(JSContext *cx) // Check the amount of time this script has been running, or if the // dialog is disabled. - JSObject* global; - if (!::JS_GetGlobalForCallingScript(cx, &global)) { - return JS_FALSE; - } - if (!global) { - global = JS_GetGlobalObject(cx); - if (!global) { - return NS_ERROR_FAILURE; - } - OBJ_TO_INNER_OBJECT(cx, global); - if (!global) { - return NS_ERROR_FAILURE; - } - } - - NS_ABORT_IF_FALSE(global != NULL, "operation callback without script?"); - + JSObject* global = ::JS_GetGlobalForScopeChain(cx); PRBool isTrackingChromeCodeTime = - xpc::AccessCheck::isChrome(global->getCompartment()); + global && xpc::AccessCheck::isChrome(global->getCompartment()); if (duration < (isTrackingChromeCodeTime ? sMaxChromeScriptRunTime : sMaxScriptRunTime)) { return JS_TRUE; diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 323af29b7e91..f5642cb3f65e 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -190,18 +190,19 @@ nsJSUtils::GetCurrentlyRunningCodeWindowID(JSContext *aContext) if (!aContext) return 0; - JSObject *jsGlobal; - if (!JS_GetGlobalForCallingScript(aContext, &jsGlobal) || !jsGlobal) - return 0; + PRUint64 windowID = 0; - JSAutoEnterCompartment ac; - if (!ac.enter(aContext, jsGlobal)) - return 0; - nsIScriptGlobalObject *sg = GetStaticScriptGlobal(aContext, jsGlobal); - if (!sg) - return 0; + JSObject *jsGlobal = JS_GetGlobalForScopeChain(aContext); + if (jsGlobal) { + nsIScriptGlobalObject *scriptGlobal = GetStaticScriptGlobal(aContext, + jsGlobal); + if (scriptGlobal) { + nsCOMPtr win = do_QueryInterface(scriptGlobal); + if (win) + windowID = win->GetOuterWindow()->WindowID(); + } + } - nsCOMPtr win = do_QueryInterface(sg); - return win ? win->GetOuterWindow()->WindowID() : 0; + return windowID; } diff --git a/dom/indexedDB/IDBKeyRange.cpp b/dom/indexedDB/IDBKeyRange.cpp index c238c7e325a9..1ab108653208 100644 --- a/dom/indexedDB/IDBKeyRange.cpp +++ b/dom/indexedDB/IDBKeyRange.cpp @@ -114,7 +114,7 @@ ReturnKeyRange(JSContext* aCx, nsIXPConnect* xpc = nsContentUtils::XPConnect(); NS_ASSERTION(xpc, "This should never be null!"); - JSObject* global = JS_GetGlobalForScopeChain(aCx); + JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); NS_ENSURE_TRUE(global, JS_FALSE); nsCOMPtr holder; diff --git a/dom/src/threads/nsDOMWorker.cpp b/dom/src/threads/nsDOMWorker.cpp index 29b9f16d60d5..d2ebee238026 100644 --- a/dom/src/threads/nsDOMWorker.cpp +++ b/dom/src/threads/nsDOMWorker.cpp @@ -634,7 +634,7 @@ nsDOMWorkerFunctions::GetInstanceCommon(JSContext* aCx, } } - JSObject* global = JS_GetGlobalForScopeChain(aCx); + JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); if (!global) { NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!"); return JS_FALSE; @@ -2533,7 +2533,8 @@ nsDOMWorker::ReadStructuredClone(JSContext* aCx, if (JS_ReadBytes(aReader, &wrappedNative, sizeof(wrappedNative))) { NS_ASSERTION(wrappedNative, "Null pointer?!"); - if (JSObject* global = JS_GetGlobalForScopeChain(aCx)) { + JSObject* global = JS_GetGlobalForObject(aCx, JS_GetScopeChain(aCx)); + if (global) { jsval val; nsCOMPtr wrapper; if (NS_SUCCEEDED(nsContentUtils::WrapNative(aCx, global, wrappedNative, diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index b256c7a8c658..f79f55792487 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -54,7 +54,6 @@ CPPSRCS = \ testCloneScript.cpp \ testConservativeGC.cpp \ testContexts.cpp \ - testCrossGlobal.cpp \ testDebugger.cpp \ testDeepFreeze.cpp \ testDefineGetterSetterNonEnumerable.cpp \ diff --git a/js/src/jsapi-tests/testCrossGlobal.cpp b/js/src/jsapi-tests/testCrossGlobal.cpp deleted file mode 100644 index 09d7ff8681d1..000000000000 --- a/js/src/jsapi-tests/testCrossGlobal.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ -#include "tests.h" - -static const char CODE[] = - "function f() " - "{ " - " return new otherGlobal.Array() instanceof otherGlobal.Array; " - "} " - "f() && f() && f(); "; - -BEGIN_TEST(testCrossGlobal_call) -{ - JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass()); - CHECK(otherGlobal); - - JSBool res = JS_InitStandardClasses(cx, otherGlobal); - CHECK(res); - - res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), - JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); - CHECK(res); - - uintN oldopts = JS_GetOptions(cx); - uintN newopts = oldopts & ~(JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING); - newopts |= JSOPTION_METHODJIT; - JS_SetOptions(cx, newopts); - - jsval rv; - EVAL(CODE, &rv); - - CHECK_SAME(rv, JSVAL_TRUE); - - JS_SetOptions(cx, oldopts); - - return true; -} -END_TEST(testCrossGlobal_call) - -BEGIN_TEST(testCrossGlobal_compileAndGo) -{ - - JSObject *otherGlobal = JS_NewGlobalObject(cx, basicGlobalClass()); - CHECK(otherGlobal); - - JSBool res = JS_InitStandardClasses(cx, otherGlobal); - CHECK(res); - - res = JS_DefineProperty(cx, global, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), - JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); - CHECK(res); - - res = JS_DefineProperty(cx, otherGlobal, "otherGlobal", OBJECT_TO_JSVAL(otherGlobal), - JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); - CHECK(res); - - uintN oldopts = JS_GetOptions(cx); - uintN newopts = - oldopts - & ~(JSOPTION_COMPILE_N_GO | JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_PROFILING); - newopts |= JSOPTION_METHODJIT; - JS_SetOptions(cx, newopts); - - jsval rv; - - // Compile script - - JSScript *script = JS_CompileScript(cx, global, CODE, strlen(CODE), __FILE__, __LINE__); - CHECK(script); - JSObject *scriptObj = JS_NewScriptObject(cx, script); - CHECK(scriptObj); - JS::Anchor anch(scriptObj); - - // Run script against new other global - res = JS_ExecuteScript(cx, otherGlobal, script, &rv); - CHECK(res); - CHECK_SAME(rv, JSVAL_TRUE); - - // Run script against original global - res = JS_ExecuteScript(cx, global, script, &rv); - CHECK(res); - CHECK_SAME(rv, JSVAL_TRUE); - - JS_SetOptions(cx, oldopts); - - return true; -} -END_TEST(testCrossGlobal_compileAndGo) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 872eb8a1caa3..ca3acefdaf18 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1930,6 +1930,13 @@ JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) return js_GetClassObject(cx, obj, key, objp); } +JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx) +{ + CHECK_REQUEST(cx); + return GetScopeChain(cx); +} + JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj) { @@ -1941,24 +1948,7 @@ JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx) { CHECK_REQUEST(cx); - return cx->getGlobalFromScopeChain(); -} - -JS_PUBLIC_API(JSBool) -JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp) -{ - JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL); - if (!fp) { - *objp = NULL; - return true; - } - - JSObject *scope = JS_GetFrameScopeChain(cx, fp); - if (!scope) - return false; - - *objp = scope->getGlobal(); - return true; + return GetGlobalForScopeChain(cx); } JS_PUBLIC_API(jsval) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index f4013eaa93d4..efa1e556f9c1 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1098,40 +1098,15 @@ extern JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); +extern JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx); + extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj); -/* - * Returns the global for the currently executing method or of the object - * currently being accessed if a property access (get or set) is in progress. - * If no action is currently in progress, the context's global object is - * returned. If the context has no global, this method will report an error and - * return null. - * - * To illustrate: suppose a global G with a method defined by a JSNative - * corresponding to the property G.method. When JS_GetGlobalForScopeChain is - * invoked during execution of G.method, the object returned will be G. This is - * so if a script in G calls method, and it is also so if script in a different - * global G2 calls |G1.method()|, or extracts |G.method| and calls it, and so - * on. This behavior also applies for property accesses to an |obj.prop| if - * that property is represented by a |JSPropertyOp| and |JSStrictPropertyOp| - * pair: the global object will be that corresponding to |obj|. - */ extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForScopeChain(JSContext *cx); -/* - * Returns the global of the most recent interpreted code if there is any. - * - * NB: This method ignores the current scope chain, any sense of privilege - * separation encapsulated in intervening stack frames, and so on. If you - * are attempting to determine the global object to expose it to script, - * this is almost certainly not the method you want! You *probably* want - * JS_GetGlobalForScopeChain instead. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetGlobalForCallingScript(JSContext *cx, JSObject **objp); - #ifdef JS_HAS_CTYPES /* * Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes' diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index a4bbbe9cf24f..e5309444673b 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -231,8 +231,6 @@ StackSpace::mark(JSTracer *trc) /* This may be the only pointer to the initialVarObj. */ if (seg->hasInitialVarObj()) MarkObject(trc, seg->getInitialVarObj(), "varobj"); - if (seg->hasLastNativeCalleeScope()) - MarkObject(trc, *seg->lastNativeCalleeScope(), "lastNativeCalleeScope"); /* Mark slots/args trailing off of the last stack frame. */ JSStackFrame *fp = seg->getCurrentFrame(); @@ -1993,12 +1991,6 @@ JSContext::JSContext(JSRuntime *rt) busyArrays() {} -void -JSContext::reportInactive() -{ - JS_ReportErrorNumber(this, js_GetErrorMessage, NULL, JSMSG_INACTIVE); -} - void JSContext::resetCompartment() { @@ -2092,9 +2084,7 @@ void JSContext::saveActiveSegment() { JS_ASSERT(hasActiveSegment()); - currentSegment->save(regs, lastNativeCalleeScope, lastNativeCalleeScopePtr); - lastNativeCalleeScope = NULL; - lastNativeCalleeScopePtr = NULL; + currentSegment->save(regs); setCurrentRegs(NULL); resetCompartment(); } @@ -2102,11 +2092,9 @@ JSContext::saveActiveSegment() void JSContext::restoreSegment() { - js::StackSegment *cs = currentSegment; - setCurrentRegs(cs->getSuspendedRegs()); - lastNativeCalleeScopePtr = cs->lastNativeCalleeScopePtr(); - lastNativeCalleeScope = cs->lastNativeCalleeScope(); - cs->restore(); + js::StackSegment *ccs = currentSegment; + setCurrentRegs(ccs->getSuspendedRegs()); + ccs->restore(); resetCompartment(); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ddaea772f847..17a1567dc6ff 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -245,10 +245,6 @@ class StackSegment /* Whether this segment was suspended by JS_SaveFrameChain. */ bool saved; - /* Maintained to implement getGlobalFromScopeChain. */ - JSObject *savedLastNativeCalleeScope_; - void *savedLastNativeCalleeScopePtr_; - /* Align at 8 bytes on all platforms. */ #if JS_BITS_PER_WORD == 32 void *padding; @@ -264,8 +260,7 @@ class StackSegment StackSegment() : cx(NULL), previousInContext(NULL), previousInMemory(NULL), initialFrame(NULL), suspendedRegs(NON_NULL_SUSPENDED_REGS), - initialVarObj(NULL), saved(false), savedLastNativeCalleeScope_(NULL), - savedLastNativeCalleeScopePtr_(NULL) + initialVarObj(NULL), saved(false) { JS_ASSERT(!inContext()); } @@ -353,9 +348,19 @@ class StackSegment /* When isSuspended, transitioning isSaved <--> !isSaved */ - void save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope, void *lastNativeCalleeScopePtr); + void save(JSFrameRegs *regs) { + JS_ASSERT(!isSuspended()); + suspend(regs); + saved = true; + JS_ASSERT(isSaved()); + } - void restore(); + void restore() { + JS_ASSERT(isSaved()); + saved = false; + resume(); + JS_ASSERT(!isSuspended()); + } /* Data available when inContext */ @@ -411,21 +416,6 @@ class StackSegment return *initialVarObj; } - bool hasLastNativeCalleeScope() const { - JS_ASSERT(inContext()); - return savedLastNativeCalleeScope_ != NULL; - } - - JSObject *lastNativeCalleeScope() const { - JS_ASSERT(inContext()); - return savedLastNativeCalleeScope_; - } - - void *lastNativeCalleeScopePtr() const { - JS_ASSERT(inContext()); - return savedLastNativeCalleeScopePtr_; - } - #ifdef DEBUG JS_REQUIRES_STACK bool contains(const JSStackFrame *fp) const; #endif @@ -633,6 +623,8 @@ class StackSpace inline void popInvokeArgs(const InvokeArgsGuard &args); inline void popInvokeFrame(const InvokeFrameGuard &ag); + inline Value *firstUnused() const; + inline bool isCurrentAndActive(JSContext *cx) const; friend class AllFramesIter; StackSegment *getCurrentSegment() const { return currentSegment; } @@ -642,11 +634,6 @@ class StackSpace JS_FRIEND_API(bool) bumpCommit(Value *from, ptrdiff_t nvals) const; #endif - /* The first stack location available to push new values and frames. */ - inline js::Value *firstUnused() const; - public: - inline void *constFirstUnused() const; - public: static const size_t CAPACITY_VALS = 512 * 1024; static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value); @@ -1697,32 +1684,6 @@ struct JSContext return !!regs; } - /* - * An object created for the global of the currently executing native - * method, native getter, or native setter (or null if none is executing), - * used for scope chain computation. The exact identity of this value is - * unspecified: function calls and property accesses fill this with - * different values, and the trace JIT and method JIT don't update the - * value during intra-global-object method calls and property accesses. - */ - JSObject *lastNativeCalleeScope; - - /* - * A JavaScript stack location associated with a native call/property - * access. - */ - void *lastNativeCalleeScopePtr; - - /* - * Computes the global object corresponding to the current scope chain, as - * determined by the current callee if one exists, by the object being - * accessed if a native property operation is occurring, or by the context - * global object if one does not. - */ - inline JSObject *getGlobalFromScopeChain(); - - void reportInactive(); - public: friend class js::StackSpace; friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode); @@ -1807,7 +1768,7 @@ struct JSContext return currentSegment; } - inline js::RegExpStatics *getRegExpStatics(); + inline js::RegExpStatics *regExpStatics(); /* Add the given segment to the list as the new active segment. */ void pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs ®s); diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index a2d2aa5386a7..bf3b055d48bb 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -49,6 +49,35 @@ #include "jsregexp.h" #include "jsgc.h" +namespace js { + +static inline JSObject * +GetGlobalForScopeChain(JSContext *cx) +{ + /* + * This is essentially GetScopeChain(cx)->getGlobal(), but without + * falling off trace. + * + * This use of cx->fp, possibly on trace, is deliberate: + * cx->fp->scopeChain->getGlobal() returns the same object whether we're on + * trace or not, since we do not trace calls across global objects. + */ + VOUCH_DOES_NOT_REQUIRE_STACK(); + + if (cx->hasfp()) + return cx->fp()->scopeChain().getGlobal(); + + JSObject *scope = cx->globalObject; + if (!scope) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } + OBJ_TO_INNER_OBJECT(cx, scope); + return scope; +} + +} + #ifdef JS_METHODJIT inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() { @@ -56,45 +85,6 @@ inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() } #endif -inline JSObject * -JSContext::getGlobalFromScopeChain() -{ - if (regs) { - /* - * It's fine if the frame is stale on-trace, because we don't trace - * calls crossing globals. - */ - VOUCH_DOES_NOT_REQUIRE_STACK(); - - /* Consult the frame if it's newer than any last native call. */ - if (regs->fp > lastNativeCalleeScopePtr) - return regs->fp->scopeChain().getGlobal(); - - /* Otherwise the callee gives us our global object. */ - JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused()); - JS_ASSERT(currentSegment < lastNativeCalleeScopePtr); - return lastNativeCalleeScope->getGlobal(); - } - - /* If we have only native calls, consult the last one. */ - if (lastNativeCalleeScope) { - JS_ASSERT(lastNativeCalleeScopePtr <= stack().constFirstUnused()); - return lastNativeCalleeScope->getGlobal(); - } - - /* - * If no native call has occurred, this is a free-floating request for - * a global: return the context globalObject if possible. - */ - JSObject *globalObj = globalObject; - if (!globalObj) { - reportInactive(); - return NULL; - } - OBJ_TO_INNER_OBJECT(this, globalObj); - return globalObj; -} - inline bool JSContext::ensureGeneratorStackSpace() { @@ -120,37 +110,13 @@ JSContext::computeNextFrame(JSStackFrame *fp) } inline js::RegExpStatics * -JSContext::getRegExpStatics() +JSContext::regExpStatics() { - JSObject *global = getGlobalFromScopeChain(); - if (!global) - return NULL; - return js::RegExpStatics::extractFrom(global); + return js::RegExpStatics::extractFrom(js::GetGlobalForScopeChain(this)); } namespace js { -JS_ALWAYS_INLINE void -StackSegment::save(JSFrameRegs *regs, JSObject *lastNativeCalleeScope, - void *lastNativeCalleeScopePtr) -{ - JS_ASSERT(!isSuspended()); - suspend(regs); - saved = true; - savedLastNativeCalleeScope_ = lastNativeCalleeScope; - savedLastNativeCalleeScopePtr_ = lastNativeCalleeScopePtr; - JS_ASSERT(isSaved()); -} - -JS_ALWAYS_INLINE void -StackSegment::restore() -{ - JS_ASSERT(isSaved()); - saved = false; - resume(); - JS_ASSERT(!isSuspended()); -} - JS_REQUIRES_STACK JS_ALWAYS_INLINE JSFrameRegs * StackSegment::getCurrentRegs() const { @@ -187,12 +153,6 @@ StackSpace::firstUnused() const return invokeArgEnd; } -JS_REQUIRES_STACK inline void * -StackSpace::constFirstUnused() const -{ - return firstUnused(); -} - /* Inline so we don't need the friend API. */ JS_ALWAYS_INLINE bool @@ -572,7 +532,7 @@ class CompartmentChecker public: explicit CompartmentChecker(JSContext *cx) : context(cx), compartment(cx->compartment) { - check(cx->getGlobalFromScopeChain()); + check(cx->hasfp() ? JS_GetGlobalForScopeChain(cx) : cx->globalObject); VOUCH_DOES_NOT_REQUIRE_STACK(); } @@ -730,35 +690,6 @@ assertSameCompartment(JSContext *cx, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) #undef START_ASSERT_SAME_COMPARTMENT -namespace detail { - -class AutoScopeChainSetter { - JSContext * const cx; - JSObject * const oldLastNativeCalleeScope; - void * const oldLastNativeCalleeScopePtr; - JSObject * const scope; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - AutoScopeChainSetter(JSContext *cx, JSObject *scope, void *scopePtr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : cx(cx), - oldLastNativeCalleeScope(cx->lastNativeCalleeScope), - oldLastNativeCalleeScopePtr(cx->lastNativeCalleeScopePtr), - scope(scope) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - cx->lastNativeCalleeScope = scope; - cx->lastNativeCalleeScopePtr = scopePtr; - } - ~AutoScopeChainSetter() { - cx->lastNativeCalleeScope = oldLastNativeCalleeScope; - cx->lastNativeCalleeScopePtr = oldLastNativeCalleeScopePtr; - } -}; - -} - STATIC_PRECONDITION_ASSUME(ubound(vp) >= argc + 2) JS_ALWAYS_INLINE bool CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) @@ -766,7 +697,6 @@ CallJSNative(JSContext *cx, js::Native native, uintN argc, js::Value *vp) #ifdef DEBUG JSBool alreadyThrowing = cx->isExceptionPending(); #endif - detail::AutoScopeChainSetter scs(cx, &vp[0].toObject(), vp); assertSameCompartment(cx, ValueArray(vp, argc + 2)); JSBool ok = native(cx, argc, vp); if (ok) { @@ -817,15 +747,6 @@ JS_ALWAYS_INLINE bool CallJSPropertyOp(JSContext *cx, js::PropertyOp op, JSObject *obj, jsid id, js::Value *vp) { assertSameCompartment(cx, obj, id, *vp); - - /* - * This property access may occur not in relation to a stack location, so - * we can't use |vp| here. However, since we use the location only in - * comparisons, to determine when to refer to the last interpreted frame's - * scope chain versus the last native callee's global object, the first - * unused location at the end of the stack will suffice. - */ - detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused()); JSBool ok = op(cx, obj, id, vp); if (ok) assertSameCompartment(cx, obj, *vp); @@ -837,9 +758,6 @@ CallJSPropertyOpSetter(JSContext *cx, js::StrictPropertyOp op, JSObject *obj, js JSBool strict, js::Value *vp) { assertSameCompartment(cx, obj, id, *vp); - - /* See the comment in CallJSPropertyOp for an explanation. */ - detail::AutoScopeChainSetter scs(cx, obj, cx->stack().constFirstUnused()); return op(cx, obj, id, strict, vp); } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index f60e356b2852..aeeb0e2c9664 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -219,12 +219,19 @@ JSCompartment::wrap(JSContext *cx, Value *vp) * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead +, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ - JSObject *global = cx->getGlobalFromScopeChain(); - if (!global) - return false; + JSObject *global; + if (cx->hasfp()) { + global = cx->fp()->scopeChain().getGlobal(); + } else { + global = cx->globalObject; + OBJ_TO_INNER_OBJECT(cx, global); + if (!global) + return false; + } /* Unwrap incoming objects. */ if (vp->isObject()) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index d7bd63581be0..f19b02ef0e84 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -142,6 +142,33 @@ JSStackFrame::pc(JSContext *cx, JSStackFrame *next) #endif } +JSObject * +js::GetScopeChain(JSContext *cx) +{ + JSStackFrame *fp = js_GetTopStackFrame(cx); + if (!fp) { + /* + * There is no code active on this context. In place of an actual + * scope chain, use the context's global object, which is set in + * js_InitFunctionAndObjectClasses, and which represents the default + * scope chain for the embedding. See also js_FindClassObject. + * + * For embeddings that use the inner and outer object hooks, the inner + * object represents the ultimate global object, with the outer object + * acting as a stand-in. + */ + JSObject *obj = cx->globalObject; + if (!obj) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); + return NULL; + } + + OBJ_TO_INNER_OBJECT(cx, obj); + return obj; + } + return GetScopeChain(cx, fp); +} + /* * This computes the blockChain by iterating through the bytecode * of the current script until it reaches the PC. Each time it sees diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index c344e2a965a7..6e6761c65cf5 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -196,7 +196,7 @@ struct JSStackFrame uint32 nactual, uint32 flags); /* Used for SessionInvoke. */ - inline void resetInvokeCallFrame(JSContext *cx); + inline void resetInvokeCallFrame(); /* Called by method-jit stubs and serve as a specification for jit-code. */ inline void initCallFrameCallerHalf(JSContext *cx, uint32 flags, void *ncode); @@ -803,6 +803,9 @@ GetBlockChain(JSContext *cx, JSStackFrame *fp); extern JSObject * GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen); +extern JSObject * +GetScopeChain(JSContext *cx); + /* * Refresh and return fp->scopeChain. It may be stale if block scopes are * active but not yet reflected by objects in the scope chain. If a block diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c00c4af2c6b9..c976cdea8da1 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -98,21 +98,8 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun, } inline void -JSStackFrame::resetInvokeCallFrame(JSContext *cx) +JSStackFrame::resetInvokeCallFrame() { -#ifdef DEBUG - if (!hasCallObj() && scopeChain_ != calleeValue().toObject().getParent()) { - js_DumpValue(calleeValue()); - if (JSObject *sc = scopeChain_) - js_DumpObject(sc); - else - fprintf(stderr, "scopeChain_ == NULL\n"); - if (JSObject *par = calleeValue().toObject().getParent()) - js_DumpObject(par); - else - fprintf(stderr, "parent == NULL\n"); - } -#endif /* Undo changes to frame made during execution; see initCallFrame */ if (hasArgsObj()) @@ -611,7 +598,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const /* Clear any garbage left from the last Invoke. */ JSStackFrame *fp = frame_.fp(); fp->clearMissingArgs(); - fp->resetInvokeCallFrame(cx); + fp->resetInvokeCallFrame(); SetValueRangeToUndefined(fp->slots(), script_->nfixed); JSBool ok; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index dff4eff44829..173921e21ba4 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4242,17 +4242,33 @@ JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey, Value *vp, Class *clasp) { + JSStackFrame *fp; JSObject *obj, *cobj, *pobj; jsid id; JSProperty *prop; const Shape *shape; + /* + * Find the global object. Use cx->fp() directly to avoid falling off + * trace; all JIT-elided stack frames have the same global object as + * cx->fp(). + */ + VOUCH_DOES_NOT_REQUIRE_STACK(); + if (!start && (fp = cx->maybefp()) != NULL) + start = &fp->scopeChain(); + if (start) { - obj = start->getGlobal(); + /* Find the topmost object in the scope chain. */ + do { + obj = start; + start = obj->getParent(); + } while (start); } else { - obj = cx->getGlobalFromScopeChain(); - if (!obj) - return JS_FALSE; + obj = cx->globalObject; + if (!obj) { + vp->setUndefined(); + return JS_TRUE; + } } OBJ_TO_INNER_OBJECT(cx, obj); @@ -6178,9 +6194,15 @@ js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey, if (protoKey != JSProto_Null) { if (!scopeobj) { - scopeobj = cx->getGlobalFromScopeChain(); - if (!scopeobj) - return false; + if (cx->hasfp()) + scopeobj = &cx->fp()->scopeChain(); + if (!scopeobj) { + scopeobj = cx->globalObject; + if (!scopeobj) { + *protop = NULL; + return true; + } + } } scopeobj = scopeobj->getGlobal(); if (scopeobj->isGlobal()) { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 697e796b2a4b..d60557c24e51 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -971,9 +971,15 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) JS_ASSERT(protoKey != JSProto_Null); /* NB: inline-expanded and specialized version of js_GetClassPrototype. */ - JSObject *global = cx->getGlobalFromScopeChain(); - if (!global) - return NULL; + JSObject *global; + if (!cx->hasfp()) { + global = cx->globalObject; + OBJ_TO_INNER_OBJECT(cx, global); + if (!global) + return NULL; + } else { + global = cx->fp()->scopeChain().getGlobal(); + } JS_ASSERT(global->isGlobal()); const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey); diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 4b7710c1c94f..c7359707c985 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -8952,10 +8952,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) JSObject *obj; if (context->hasfp()) { - RegExpStatics *res = context->getRegExpStatics(); - if (!res) - return NULL; - obj = RegExp::createObject(context, res, + obj = RegExp::createObject(context, context->regExpStatics(), tokenStream.getTokenbuf().begin(), tokenStream.getTokenbuf().length(), tokenStream.currentToken().t_reflags); diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index d5c0b97a6923..bf8f80328453 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -150,9 +150,7 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) */ assertSameCompartment(cx, obj, clone); - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return NULL; + RegExpStatics *res = cx->regExpStatics(); RegExp *re = RegExp::extractFrom(obj); { uint32 origFlags = re->getFlags(); @@ -403,9 +401,7 @@ regexp_resolve(JSContext *cx, JSObject *obj, jsid id, uint32 flags, JSObject **o static JSBool \ name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \ { \ - RegExpStatics *res = cx->getRegExpStatics(); \ - if (!res) \ - return false; \ + RegExpStatics *res = cx->regExpStatics(); \ code; \ } @@ -431,9 +427,7 @@ DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, static JSBool \ name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \ { \ - RegExpStatics *res = cx->getRegExpStatics(); \ - if (!res) \ - return false; \ + RegExpStatics *res = cx->regExpStatics(); \ code; \ return true; \ } @@ -678,10 +672,7 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) static bool SwapRegExpInternals(JSContext *cx, JSObject *obj, Value *rval, JSString *str, uint32 flags = 0) { - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return false; - flags |= res->getFlags(); + flags |= cx->regExpStatics()->getFlags(); AlreadyIncRefed re = RegExp::create(cx, str, flags); if (!re) return false; @@ -722,9 +713,7 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, Value *argv, JSBool te lastIndex = 0; } - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return false; + RegExpStatics *res = cx->regExpStatics(); JSString *input; if (argc) { diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index f05508c35b0d..8d85a93c9319 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1876,8 +1876,8 @@ str_match(JSContext *cx, uintN argc, Value *vp) AutoObjectRooter array(cx); MatchArgType arg = array.addr(); - RegExpStatics *res = cx->getRegExpStatics(); - if (!res || !DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS)) + RegExpStatics *res = cx->regExpStatics(); + if (!DoMatch(cx, res, vp, str, *rep, MatchCallback, arg, MATCH_ARGS)) return false; /* When not global, DoMatch will leave |RegExp.exec()| in *vp. */ @@ -1906,9 +1906,7 @@ str_search(JSContext *cx, uintN argc, Value *vp) if (!rep) return false; - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return false; + RegExpStatics *res = cx->regExpStatics(); size_t i = 0; if (!rep->re().execute(cx, res, str, &i, true, vp)) return false; @@ -2359,9 +2357,7 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) rdata.leftIndex = 0; rdata.calledBack = false; - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return false; + RegExpStatics *res = cx->regExpStatics(); if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS)) return false; @@ -2701,9 +2697,7 @@ str_split(JSContext *cx, uintN argc, Value *vp) AutoValueVector splits(cx); - RegExpStatics *res = cx->getRegExpStatics(); - if (!res) - return false; + RegExpStatics *res = cx->regExpStatics(); jsint i, j; uint32 len = i = 0; while ((j = find_split(cx, res, str, re, &i, sep)) >= 0) { diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 8922439f4959..b432efec3c24 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1677,32 +1677,6 @@ GetXMLSettingFlags(JSContext *cx, uintN *flagsp) return true; } -static JSObject * -GetXMLScopeChain(JSContext *cx) -{ - if (JSStackFrame *fp = js_GetTopStackFrame(cx)) - return GetScopeChain(cx, fp); - - /* - * There is no code active on this context. In place of an actual scope - * chain, use the context's global object, which is set in - * s_InitFunctionAndObjectClasses, and which represents the default scope - * chain for the embedding. See also js_FindClassObject. - * - * For embeddings that use the inner and outer object hooks, the inner - * object represents the ultimate global object, with the outer object - * acting as a stand-in. - */ - JSObject *obj = cx->globalObject; - if (!obj) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - - OBJ_TO_INNER_OBJECT(cx, obj); - return obj; -} - static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { @@ -1782,7 +1756,7 @@ ParseXMLSource(JSContext *cx, JSString *src) { Parser parser(cx); if (parser.init(chars, length, filename, lineno, cx->findVersion())) { - JSObject *scopeChain = GetXMLScopeChain(cx); + JSObject *scopeChain = GetScopeChain(cx); if (!scopeChain) { cx->free(chars); return NULL; @@ -7257,7 +7231,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) JSObject *ns, *obj, *tmp; jsval v; - JSObject *scopeChain = GetXMLScopeChain(cx); + JSObject *scopeChain = GetScopeChain(cx); obj = NULL; for (tmp = scopeChain; tmp; tmp = tmp->getParent()) { diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index e639d23d162a..2cbbd259fe3a 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -808,19 +808,9 @@ class CallCompiler : public BaseCompiler if (!CallJSNative(cx, fun->u.n.native, ic.frameSize.getArgc(f), vp)) THROWV(true); - /* - * Right now, take slow-path for IC misses or multiple stubs. Also take - * it if the call crosses, or might cross, globals: in that case - * JSContext::getGlobalFromScopeChain requires we note the change of - * global. - */ - if (ic.fastGuardedNative || - ic.hasJsFunCheck || - !f.regs.fp->script()->compileAndGo || - obj->getGlobal() != f.regs.fp->scopeChain().getGlobal()) - { + /* Right now, take slow-path for IC misses or multiple stubs. */ + if (ic.fastGuardedNative || ic.hasJsFunCheck) return true; - } /* Native MIC needs to warm up first. */ if (!ic.hit) { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index bdacdbe862a6..eeb0f892fff1 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4356,40 +4356,6 @@ StringStats(JSContext *cx, uintN argc, jsval *vp) return true; } -enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT }; - -static JSObject * -NewGlobalObject(JSContext *cx, CompartmentKind compartment); - -JSBool -NewGlobal(JSContext *cx, uintN argc, jsval *vp) -{ - if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal"); - return false; - } - - JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]); - - JSBool equalSame = JS_FALSE, equalNew = JS_FALSE; - if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) || - !JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) { - return false; - } - - if (!equalSame && !equalNew) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal"); - return false; - } - - JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT); - if (!global) - return false; - - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global)); - return true; -} - static JSFunctionSpec shell_functions[] = { JS_FN("version", Version, 0,0), JS_FN("revertVersion", RevertVersion, 0,0), @@ -4486,7 +4452,6 @@ static JSFunctionSpec shell_functions[] = { JS_FN("mjitstats", MJitStats, 0,0), #endif JS_FN("stringstats", StringStats, 0,0), - JS_FN("newGlobal", NewGlobal, 1,0), JS_FS_END }; @@ -4512,7 +4477,7 @@ static const char *const shell_help_messages[] = { "assertEq(actual, expected[, msg])\n" " Throw if the first two arguments are not the same (both +0 or both -0,\n" " both NaN, or non-zero and ===)", -"assertJit() Throw if the calling function failed to JIT", +"assertJit() Throw if the calling function failed to JIT\n", "gc() Run the garbage collector", #ifdef JS_GCMETER "gcstats() Print garbage collector statistics", @@ -4557,7 +4522,7 @@ static const char *const shell_help_messages[] = { "dumpObject() Dump an internal representation of an object", "notes([fun]) Show source notes for functions", "tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n" -" With filename, send to file.", +" With filename, send to file.\n", "stats([string ...]) Dump 'arena', 'atom', 'global' stats", #endif #ifdef TEST_CVTARGS @@ -4608,37 +4573,24 @@ static const char *const shell_help_messages[] = { " Get/Set the limit in seconds for the execution time for the current context.\n" " A negative value (default) means that the execution time is unlimited.", "elapsed() Execution time elapsed for the current context.", -"parent(obj) Returns the parent of obj.", -"wrap(obj) Wrap an object into a noop wrapper.", -"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.", -"deserialize(a) Deserialize data generated by serialize.", +"parent(obj) Returns the parent of obj.\n", +"wrap(obj) Wrap an object into a noop wrapper.\n", +"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n", +"deserialize(a) Deserialize data generated by serialize.\n", #ifdef JS_METHODJIT -"mjitstats() Return stats on mjit memory usage.", +"mjitstats() Return stats on mjit memory usage.\n", #endif -"stringstats() Return stats on string memory usage.", -"newGlobal(kind) Return a new global object, in the current\n" -" compartment if kind === 'same-compartment' or in a\n" -" new compartment if kind === 'new-compartment'", - -/* Keep these last: see the static assertion below. */ +"stringstats() Return stats on string memory usage.\n" #ifdef MOZ_PROFILING "startProfiling() Start a profiling session.\n" -" Profiler must be running with programatic sampling", -"stopProfiling() Stop a running profiling session\n" +" Profiler must be running with programatic sampling\n" +"stopProfiling() Stop a running profiling session" #endif }; -#ifdef MOZ_PROFILING -#define PROFILING_FUNCTION_COUNT 2 -#else -#define PROFILING_FUNCTION_COUNT 0 -#endif - /* Help messages must match shell functions. */ -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT == - JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); - -#undef PROFILING_FUNCTION_COUNT +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 == + JS_ARRAY_LENGTH(shell_functions)); #ifdef DEBUG static void @@ -4647,13 +4599,12 @@ CheckHelpMessages() const char *const *m; const char *lp; - /* Messages begin with "function_name(" prefix and don't end with \n. */ + /* Each message must begin with "function_name(" prefix. */ for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) { lp = strchr(*m, '('); JS_ASSERT(lp); JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, *m, lp - *m) == 0); - JS_ASSERT((*m)[strlen(*m) - 1] != '\n'); } } #else @@ -5473,52 +5424,46 @@ DestroyContext(JSContext *cx, bool withGC) } static JSObject * -NewGlobalObject(JSContext *cx, CompartmentKind compartment) +NewGlobalObject(JSContext *cx) { - JSObject *glob = (compartment == NEW_COMPARTMENT) - ? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL) - : JS_NewGlobalObject(cx, &global_class); + JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL); if (!glob) return NULL; - { - JSAutoEnterCompartment ac; - if (!ac.enter(cx, glob)) - return NULL; + JSAutoEnterCompartment ac; + if (!ac.enter(cx, glob)) + return NULL; -#ifndef LAZY_STANDARD_CLASSES - if (!JS_InitStandardClasses(cx, glob)) - return NULL; +#ifdef LAZY_STANDARD_CLASSES + JS_SetGlobalObject(cx, glob); +#else + if (!JS_InitStandardClasses(cx, glob)) + return NULL; #endif - #ifdef JS_HAS_CTYPES - if (!JS_InitCTypesClass(cx, glob)) - return NULL; + if (!JS_InitCTypesClass(cx, glob)) + return NULL; #endif - if (!JS::RegisterPerfMeasurement(cx, glob)) - return NULL; - if (!JS_DefineFunctions(cx, glob, shell_functions) || - !JS_DefineProfilingFunctions(cx, glob)) { - return NULL; - } - - JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); - if (!it) - return NULL; - if (!JS_DefineProperties(cx, it, its_props)) - return NULL; - if (!JS_DefineFunctions(cx, it, its_methods)) - return NULL; - - if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter, - its_setter, 0)) - return NULL; - if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter, - its_setter, JSPROP_READONLY)) - return NULL; + if (!JS::RegisterPerfMeasurement(cx, glob)) + return NULL; + if (!JS_DefineFunctions(cx, glob, shell_functions) || + !JS_DefineProfilingFunctions(cx, glob)) { + return NULL; } - if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, &glob)) + JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); + if (!it) + return NULL; + if (!JS_DefineProperties(cx, it, its_props)) + return NULL; + if (!JS_DefineFunctions(cx, it, its_methods)) + return NULL; + + if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter, + its_setter, 0)) + return NULL; + if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter, + its_setter, JSPROP_READONLY)) return NULL; return glob; @@ -5529,7 +5474,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) { JSAutoRequest ar(cx); - JSObject *glob = NewGlobalObject(cx, NEW_COMPARTMENT); + JSObject *glob = NewGlobalObject(cx); if (!glob) return 1; @@ -5537,8 +5482,6 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) if (!ac.enter(cx, glob)) return 1; - JS_SetGlobalObject(cx, glob); - JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0); if (!envobj || !JS_SetPrivate(cx, envobj, envp)) return 1; @@ -5575,7 +5518,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp) class ShellWorkerHooks : public js::workers::WorkerHooks { public: JSObject *newGlobalObject(JSContext *cx) { - return NewGlobalObject(cx, NEW_COMPARTMENT); + return NewGlobalObject(cx); } }; ShellWorkerHooks hooks; diff --git a/js/src/tests/e4x/extensions/cross-global-settings.js b/js/src/tests/e4x/extensions/cross-global-settings.js deleted file mode 100644 index 967fff1a83f5..000000000000 --- a/js/src/tests/e4x/extensions/cross-global-settings.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -var gTestfile = 'cross-global-settings.js'; -//----------------------------------------------------------------------------- -var BUGNUMBER = 631135; -var summary = - "Look at the callee's global.XML.* setting values, not the caller's"; - -print(BUGNUMBER + ": " + summary); - -/************** - * BEGIN TEST * - **************/ - -var otherGlobal = newGlobal("same-compartment"); -otherGlobal.XML.prettyPrinting = true; -otherGlobal.XML.prettyIndent = 2; -otherGlobal.indent = " "; - -XML.prettyPrinting = true; -XML.prettyIndent = 16; - -var indent = " " + - " " + - " " + - " "; - -var str = otherGlobal.XML("").toXMLString(); - -assertEq(str.indexOf(indent), -1); -assertEq(str.indexOf(otherGlobal.indent) > 0, true); - -/******************************************************************************/ - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("All tests passed!"); diff --git a/js/src/tests/e4x/extensions/jstests.list b/js/src/tests/e4x/extensions/jstests.list index 56602bb13435..75d657c5b000 100644 --- a/js/src/tests/e4x/extensions/jstests.list +++ b/js/src/tests/e4x/extensions/jstests.list @@ -22,4 +22,3 @@ script regress-450871-02.js script regress-462734-01.js script extensibility.js script regress-595207.js -skip-if(!xulRuntime.shell) script cross-global-settings.js diff --git a/js/src/tests/ecma_5/extensions/cross-global-call.js b/js/src/tests/ecma_5/extensions/cross-global-call.js deleted file mode 100644 index 44f370004be3..000000000000 --- a/js/src/tests/ecma_5/extensions/cross-global-call.js +++ /dev/null @@ -1,70 +0,0 @@ -// Any copyright is dedicated to the Public Domain. -// http://creativecommons.org/licenses/publicdomain/ - -//----------------------------------------------------------------------------- -var BUGNUMBER = 631135; -var summary = - "Objects created by/for natives are created for the caller's scope, not " + - "for the native's scope"; - -print(BUGNUMBER + ": " + summary); - -/************** - * BEGIN TEST * - **************/ - -var otherGlobal = newGlobal("same-compartment"); - -function makeArray() -{ - return new otherGlobal.Array(); -} - -var a; - -a = makeArray(); -assertEq(a instanceof otherGlobal.Array, true); -assertEq(a instanceof Array, false); -a = makeArray(); -assertEq(a instanceof otherGlobal.Array, true); -assertEq(a instanceof Array, false); -a = makeArray(); -assertEq(a instanceof otherGlobal.Array, true); -assertEq(a instanceof Array, false); - -for (var i = 0; i < 3; i++) -{ - a = new otherGlobal.Array(); - assertEq(a instanceof otherGlobal.Array, true); - assertEq(a instanceof Array, false); -} - - -var res = otherGlobal.Array.prototype.slice.call([1, 2, 3], 0, 1); -assertEq(res instanceof otherGlobal.Array, true); -assertEq(res instanceof Array, false); - -var obj = { p: 1 }; -var desc = otherGlobal.Object.getOwnPropertyDescriptor(obj, "p"); -assertEq(desc instanceof otherGlobal.Object, true); -assertEq(desc instanceof Object, false); - -try -{ - otherGlobal.Function.prototype.call.call(null); - var err = "no error"; -} -catch (e) -{ - err = e; -} -assertEq(err instanceof otherGlobal.TypeError, true, - "bad error: " + err); -assertEq(err instanceof TypeError, false, "bad error: " + err); - -/******************************************************************************/ - -if (typeof reportCompare === "function") - reportCompare(true, true); - -print("All tests passed!"); diff --git a/js/src/tests/ecma_5/extensions/jstests.list b/js/src/tests/ecma_5/extensions/jstests.list index 74f3b4b8ec47..962f4ee99078 100644 --- a/js/src/tests/ecma_5/extensions/jstests.list +++ b/js/src/tests/ecma_5/extensions/jstests.list @@ -9,7 +9,6 @@ script bug352085.js script bug472534.js script bug496985.js script bug566661.js -skip-if(!xulRuntime.shell) script cross-global-call.js # needs newGlobal() script eval-native-callback-is-indirect.js script extension-methods-reject-null-undefined-this.js skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate() diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp index 16004d5d229a..1c141f9d95b4 100644 --- a/js/src/xpconnect/src/xpccomponents.cpp +++ b/js/src/xpconnect/src/xpccomponents.cpp @@ -3630,10 +3630,11 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source, { JSAutoRequest req(cx); - callingScope = JS_GetGlobalForScopeChain(cx); + callingScope = JS_GetScopeChain(cx); if (!callingScope) { return NS_ERROR_FAILURE; } + callingScope = JS_GetGlobalForObject(cx, callingScope); } nsRefPtr sandcx = new ContextHolder(cx, sandbox); diff --git a/js/src/xpconnect/src/xpcthrower.cpp b/js/src/xpconnect/src/xpcthrower.cpp index cc1306f54ab3..e694d811ca94 100644 --- a/js/src/xpconnect/src/xpcthrower.cpp +++ b/js/src/xpconnect/src/xpcthrower.cpp @@ -317,9 +317,10 @@ XPCThrower::ThrowExceptionObject(JSContext* cx, nsIException* e) } else if((xpc = nsXPConnect::GetXPConnect())) { - JSObject* glob = JS_GetGlobalForScopeChain(cx); + JSObject* glob = JS_GetScopeChain(cx); if(!glob) return JS_FALSE; + glob = JS_GetGlobalForObject(cx, glob); nsCOMPtr holder; nsresult rv = xpc->WrapNative(cx, glob, e, diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 661f365afdb6..492097a4b462 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -234,28 +234,37 @@ GetPrincipal(JSObject *obj) bool AccessCheck::documentDomainMakesSameOrigin(JSContext *cx, JSObject *obj) { - JSObject *scope; - if (!JS_GetGlobalForCallingScript(cx, &scope)) - return false; - if (!scope) { - scope = JS_GetGlobalForScopeChain(cx); - if (!scope) - return false; + JSObject *scope = nsnull; + JSStackFrame *fp = nsnull; + JS_FrameIterator(cx, &fp); + if (fp) { + while (fp->isDummyFrame()) { + if (!JS_FrameIterator(cx, &fp)) + break; + } + + if (fp) + scope = &fp->scopeChain(); } + if (!scope) + scope = JS_GetScopeChain(cx); + nsIPrincipal *subject; + nsIPrincipal *object; + { JSAutoEnterCompartment ac; if (!ac.enter(cx, scope)) return false; - subject = GetPrincipal(scope); + subject = GetPrincipal(JS_GetGlobalForObject(cx, scope)); } + if (!subject) return false; - nsIPrincipal *object; { JSAutoEnterCompartment ac; diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in index a766be2c635d..2be35d8003d8 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in @@ -38,15 +38,10 @@ * * ***** END LICENSE BLOCK ***** */ -var ctypesGlobal = this; try { // We might be running without privileges, in which case it's up to the // harness to give us the 'ctypes' object. Components.utils.import("resource://gre/modules/ctypes.jsm"); - - // Hack: Determine the global object with which errors thrown by ctypes - // methods will be associated for more precise testing purposes. - ctypesGlobal = ctypes.CType.constructor("return this")(); } catch(e) { } @@ -222,18 +217,18 @@ function run_test() // test library.close let test_void_t = library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t); library.close(); - do_check_throws(function() { test_void_t(); }, ctypesGlobal.Error); + do_check_throws(function() { test_void_t(); }, Error); do_check_throws(function() { library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t); - }, ctypesGlobal.Error); + }, Error); // test that library functions throw when bound to other objects library = ctypes.open(libfile.path); let obj = {}; obj.declare = library.declare; - do_check_throws(function () { run_void_tests(obj); }, ctypesGlobal.Error); + do_check_throws(function () { run_void_tests(obj); }, Error); obj.close = library.close; - do_check_throws(function () { obj.close(); }, ctypesGlobal.Error); + do_check_throws(function () { obj.close(); }, Error); // test that functions work as properties of other objects let getter = library.declare("get_int8_t_cdecl", ctypes.default_abi, ctypes.int8_t); @@ -242,7 +237,7 @@ function run_test() do_check_eq(obj.t(), 109); // bug 521937 - do_check_throws(function () { let nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, ctypesGlobal.Error); + do_check_throws(function () { let nolib = ctypes.open("notfoundlibrary.dll"); nolib.close(); }, Error); // bug 522360 do_check_eq(run_load_system_library(), true); @@ -264,8 +259,8 @@ function run_test() function run_abstract_class_tests() { // Test that ctypes.CType is an abstract constructor that throws. - do_check_throws(function() { ctypes.CType(); }, ctypesGlobal.Error); - do_check_throws(function() { new ctypes.CType() }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType(); }, Error); + do_check_throws(function() { new ctypes.CType() }, Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.CType, "Function"); @@ -278,8 +273,8 @@ function run_abstract_class_tests() }); do_check_true(ctypes.CType.hasOwnProperty("prototype")); - do_check_throws(function() { ctypes.CType.prototype(); }, ctypesGlobal.Error); - do_check_throws(function() { new ctypes.CType.prototype() }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype(); }, Error); + do_check_throws(function() { new ctypes.CType.prototype() }, Error); checkParentIsCTypes(ctypes.CType.prototype); getParent(ctypes, function(p) { do_check_true(Object.getPrototypeOf(ctypes.CType.prototype) === p.Function.prototype); @@ -297,16 +292,16 @@ function run_abstract_class_tests() do_check_true(ctypes.CType.prototype.hasOwnProperty("toSource")); // Check that the shared properties and functions on ctypes.CType.prototype throw. - do_check_throws(function() { ctypes.CType.prototype.name; }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CType.prototype.size; }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CType.prototype.ptr; }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CType.prototype.array(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CType.prototype.toString(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CType.prototype.toSource(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CType.prototype.name; }, Error); + do_check_throws(function() { ctypes.CType.prototype.size; }, Error); + do_check_throws(function() { ctypes.CType.prototype.ptr; }, Error); + do_check_throws(function() { ctypes.CType.prototype.array(); }, Error); + do_check_throws(function() { ctypes.CType.prototype.toString(); }, Error); + do_check_throws(function() { ctypes.CType.prototype.toSource(); }, Error); // Test that ctypes.CData is an abstract constructor that throws. - do_check_throws(function() { ctypes.CData(); }, ctypesGlobal.Error); - do_check_throws(function() { new ctypes.CData() }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData(); }, Error); + do_check_throws(function() { new ctypes.CData() }, Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.CData, "Function"); @@ -332,16 +327,16 @@ function run_abstract_class_tests() do_check_true(ctypes.CData.prototype.hasOwnProperty("toSource")); // Check that the shared properties and functions on ctypes.CData.prototype throw. - do_check_throws(function() { ctypes.CData.prototype.value; }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CData.prototype.value = null; }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CData.prototype.address(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CData.prototype.readString(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CData.prototype.toString(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.CData.prototype.toSource(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.CData.prototype.value; }, Error); + do_check_throws(function() { ctypes.CData.prototype.value = null; }, Error); + do_check_throws(function() { ctypes.CData.prototype.address(); }, Error); + do_check_throws(function() { ctypes.CData.prototype.readString(); }, Error); + do_check_throws(function() { ctypes.CData.prototype.toString(); }, Error); + do_check_throws(function() { ctypes.CData.prototype.toSource(); }, Error); } function run_Int64_tests() { - do_check_throws(function() { ctypes.Int64(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64(); }, Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.Int64, "Function"); @@ -368,8 +363,8 @@ function run_Int64_tests() { do_check_true(ctypes.Int64.prototype.hasOwnProperty("toSource")); // Check that the shared functions on ctypes.Int64.prototype throw. - do_check_throws(function() { ctypes.Int64.prototype.toString(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.prototype.toString(); }, Error); + do_check_throws(function() { ctypes.Int64.prototype.toSource(); }, Error); let i = ctypes.Int64(0); checkParentIsCTypes(i); @@ -380,14 +375,14 @@ function run_Int64_tests() { do_check_eq(i.toString(), "0"); for (let radix = 2; radix <= 36; ++radix) do_check_eq(i.toString(radix), "0"); - do_check_throws(function() { i.toString(0); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(1); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(37); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(10, 2); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(0); }, Error); + do_check_throws(function() { i.toString(1); }, Error); + do_check_throws(function() { i.toString(37); }, Error); + do_check_throws(function() { i.toString(10, 2); }, Error); // Test Int64.toSource(). do_check_eq(i.toSource(), "ctypes.Int64(\"0\")"); - do_check_throws(function() { i.toSource(10); }, ctypesGlobal.Error); + do_check_throws(function() { i.toSource(10); }, Error); i = ctypes.Int64("0x28590a1c921def71"); do_check_eq(i.toString(), i.toString(10)); @@ -485,7 +480,7 @@ function run_Int64_tests() { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { ctypes.Int64(vals[i]); }, ctypesGlobal.TypeError); + do_check_throws(function () { ctypes.Int64(vals[i]); }, TypeError); // Test ctypes.Int64.compare. do_check_eq(ctypes.Int64.compare(ctypes.Int64(5), ctypes.Int64(5)), 0); @@ -494,18 +489,18 @@ function run_Int64_tests() { do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-5)), 0); do_check_eq(ctypes.Int64.compare(ctypes.Int64(-5), ctypes.Int64(-4)), -1); do_check_eq(ctypes.Int64.compare(ctypes.Int64(-4), ctypes.Int64(-5)), 1); - do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.Int64.compare(4, 5); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.compare(ctypes.Int64(4), ctypes.UInt64(4)); }, Error); + do_check_throws(function() { ctypes.Int64.compare(4, 5); }, Error); // Test ctypes.Int64.{lo,hi}. do_check_eq(ctypes.Int64.lo(ctypes.Int64(0x28590a1c921de000)), 0x921de000); do_check_eq(ctypes.Int64.hi(ctypes.Int64(0x28590a1c921de000)), 0x28590a1c); do_check_eq(ctypes.Int64.lo(ctypes.Int64(-0x28590a1c921de000)), 0x6de22000); do_check_eq(ctypes.Int64.hi(ctypes.Int64(-0x28590a1c921de000)), -0x28590a1d); - do_check_throws(function() { ctypes.Int64.lo(ctypes.UInt64(0)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.Int64.hi(ctypes.UInt64(0)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.Int64.lo(0); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.Int64.hi(0); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.Int64.lo(ctypes.UInt64(0)); }, Error); + do_check_throws(function() { ctypes.Int64.hi(ctypes.UInt64(0)); }, Error); + do_check_throws(function() { ctypes.Int64.lo(0); }, Error); + do_check_throws(function() { ctypes.Int64.hi(0); }, Error); // Test ctypes.Int64.join. do_check_eq(ctypes.Int64.join(0, 0).toString(), "0"); @@ -513,14 +508,14 @@ function run_Int64_tests() { do_check_eq(ctypes.Int64.join(-0x28590a1d, 0x6de22000).toString(16), "-28590a1c921de000"); do_check_eq(ctypes.Int64.join(0x7fffffff, 0xffffffff).toString(16), "7fffffffffffffff"); do_check_eq(ctypes.Int64.join(-0x80000000, 0x00000000).toString(16), "-8000000000000000"); - do_check_throws(function() { ctypes.Int64.join(-0x80000001, 0); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.Int64.join(0x80000000, 0); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.Int64.join(0, -0x1); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.Int64.join(0, 0x800000000); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.Int64.join(-0x80000001, 0); }, TypeError); + do_check_throws(function() { ctypes.Int64.join(0x80000000, 0); }, TypeError); + do_check_throws(function() { ctypes.Int64.join(0, -0x1); }, TypeError); + do_check_throws(function() { ctypes.Int64.join(0, 0x800000000); }, TypeError); } function run_UInt64_tests() { - do_check_throws(function() { ctypes.UInt64(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64(); }, Error); // Test that classes and prototypes are set up correctly. do_check_class(ctypes.UInt64, "Function"); @@ -549,8 +544,8 @@ function run_UInt64_tests() { do_check_true(ctypes.UInt64.prototype.hasOwnProperty("toSource")); // Check that the shared functions on ctypes.UInt64.prototype throw. - do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.prototype.toString(); }, Error); + do_check_throws(function() { ctypes.UInt64.prototype.toSource(); }, Error); let i = ctypes.UInt64(0); checkParentIsCTypes(i); @@ -561,14 +556,14 @@ function run_UInt64_tests() { do_check_eq(i.toString(), "0"); for (let radix = 2; radix <= 36; ++radix) do_check_eq(i.toString(radix), "0"); - do_check_throws(function() { i.toString(0); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(1); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(37); }, ctypesGlobal.Error); - do_check_throws(function() { i.toString(10, 2); }, ctypesGlobal.Error); + do_check_throws(function() { i.toString(0); }, Error); + do_check_throws(function() { i.toString(1); }, Error); + do_check_throws(function() { i.toString(37); }, Error); + do_check_throws(function() { i.toString(10, 2); }, Error); // Test UInt64.toSource(). do_check_eq(i.toSource(), "ctypes.UInt64(\"0\")"); - do_check_throws(function() { i.toSource(10); }, ctypesGlobal.Error); + do_check_throws(function() { i.toSource(10); }, Error); i = ctypes.UInt64("0x28590a1c921def71"); do_check_eq(i.toString(), i.toString(10)); @@ -645,24 +640,24 @@ function run_UInt64_tests() { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { ctypes.UInt64(vals[i]); }, ctypesGlobal.TypeError); + do_check_throws(function () { ctypes.UInt64(vals[i]); }, TypeError); // Test ctypes.UInt64.compare. do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(5)), 0); do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(5), ctypes.UInt64(4)), 1); do_check_eq(ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.UInt64(5)), -1); - do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.compare(ctypes.UInt64(4), ctypes.Int64(4)); }, Error); + do_check_throws(function() { ctypes.UInt64.compare(4, 5); }, Error); // Test ctypes.UInt64.{lo,hi}. do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0x28590a1c921de000)), 0x921de000); do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0x28590a1c921de000)), 0x28590a1c); do_check_eq(ctypes.UInt64.lo(ctypes.UInt64(0xa8590a1c921de000)), 0x921de000); do_check_eq(ctypes.UInt64.hi(ctypes.UInt64(0xa8590a1c921de000)), 0xa8590a1c); - do_check_throws(function() { ctypes.UInt64.lo(ctypes.Int64(0)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.UInt64.hi(ctypes.Int64(0)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.UInt64.lo(0); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.UInt64.hi(0); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.UInt64.lo(ctypes.Int64(0)); }, Error); + do_check_throws(function() { ctypes.UInt64.hi(ctypes.Int64(0)); }, Error); + do_check_throws(function() { ctypes.UInt64.lo(0); }, Error); + do_check_throws(function() { ctypes.UInt64.hi(0); }, Error); // Test ctypes.UInt64.join. do_check_eq(ctypes.UInt64.join(0, 0).toString(), "0"); @@ -670,10 +665,10 @@ function run_UInt64_tests() { do_check_eq(ctypes.UInt64.join(0xa8590a1c, 0x921de000).toString(16), "a8590a1c921de000"); do_check_eq(ctypes.UInt64.join(0xffffffff, 0xffffffff).toString(16), "ffffffffffffffff"); do_check_eq(ctypes.UInt64.join(0, 0).toString(16), "0"); - do_check_throws(function() { ctypes.UInt64.join(-0x1, 0); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.UInt64.join(0x100000000, 0); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.UInt64.join(0, -0x1); }, ctypesGlobal.TypeError); - do_check_throws(function() { ctypes.UInt64.join(0, 0x1000000000); }, ctypesGlobal.TypeError); + do_check_throws(function() { ctypes.UInt64.join(-0x1, 0); }, TypeError); + do_check_throws(function() { ctypes.UInt64.join(0x100000000, 0); }, TypeError); + do_check_throws(function() { ctypes.UInt64.join(0, -0x1); }, TypeError); + do_check_throws(function() { ctypes.UInt64.join(0, 0x1000000000); }, TypeError); } function run_basic_abi_tests(library, t, name, toprimitive, @@ -802,12 +797,12 @@ function run_basic_class_tests(t) do_check_true(t.prototype.constructor === t); // Check that the shared properties and functions on 't.prototype' throw. - do_check_throws(function() { t.prototype.value; }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype.value = null; }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype.address(); }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype.readString(); }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype.toString(); }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype.toSource(); }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.value; }, Error); + do_check_throws(function() { t.prototype.value = null; }, Error); + do_check_throws(function() { t.prototype.address(); }, Error); + do_check_throws(function() { t.prototype.readString(); }, Error); + do_check_throws(function() { t.prototype.toString(); }, Error); + do_check_throws(function() { t.prototype.toSource(); }, Error); // Test that an instance 'd' of 't' is a CData. let d = t(); @@ -852,7 +847,7 @@ function run_bool_tests(library) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, d.value); @@ -932,7 +927,7 @@ function run_integer_tests(library, t, name, size, signed, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, d.value); @@ -981,9 +976,9 @@ function run_float_tests(library, t, name, size) { d = new t(20); do_check_eq(d.value, 20); - do_check_throws(function() { d.value = ctypes.Int64(5); }, ctypesGlobal.TypeError); - do_check_throws(function() { d.value = ctypes.Int64(-5); }, ctypesGlobal.TypeError); - do_check_throws(function() { d.value = ctypes.UInt64(5); }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = ctypes.Int64(5); }, TypeError); + do_check_throws(function() { d.value = ctypes.Int64(-5); }, TypeError); + do_check_throws(function() { d.value = ctypes.UInt64(5); }, TypeError); if (size == 4) { d.value = 0x7fffff; @@ -1000,7 +995,7 @@ function run_float_tests(library, t, name, size) { d.value = 0xfffffffffffff000; do_check_eq(d.value, 0xfffffffffffff000); - do_check_throws(function() { d.value = ctypes.Int64("0x7fffffffffffffff"); }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = ctypes.Int64("0x7fffffffffffffff"); }, TypeError); } d.value = Infinity; @@ -1019,7 +1014,7 @@ function run_float_tests(library, t, name, size) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); // Check that values roundtrip through toSource() correctly. function test_roundtrip(t, val) @@ -1119,7 +1114,7 @@ function run_wrapped_integer_tests(library, t, name, size, signed, w, wname, lim {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents.toString(), d.value.toString()); @@ -1165,7 +1160,7 @@ function run_char_tests(library, t, name, size, signed, limits) { d.value = -10; do_check_eq(d.value, -10); } else { - do_check_throws(function() { d.value = -10; }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = -10; }, TypeError); } d = new t(20); do_check_eq(d.value, 20); @@ -1194,7 +1189,7 @@ function run_char_tests(library, t, name, size, signed, limits) { d.value = true; do_check_eq(d.value, 1); - do_check_throws(function() { d.value = "5"; }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = "5"; }, TypeError); // don't convert anything else let vals = [limits[0] - 1, limits[1] + 1, Infinity, -Infinity, NaN, 0.1, @@ -1202,7 +1197,7 @@ function run_char_tests(library, t, name, size, signed, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, 1); @@ -1216,9 +1211,9 @@ function run_char_tests(library, t, name, size, signed, limits) { do_check_eq(s.constructor.length, literal.length + 1); s = t.array(50)(literal); do_check_eq(s.readString(), literal); - do_check_throws(function() { t.array(3)(literal); }, ctypesGlobal.Error); + do_check_throws(function() { t.array(3)(literal); }, Error); - do_check_throws(function() { t.ptr(literal); }, ctypesGlobal.TypeError); + do_check_throws(function() { t.ptr(literal); }, TypeError); let p = t.ptr(s); do_check_eq(p.readString(), literal); @@ -1260,13 +1255,13 @@ function run_jschar_tests(library, t, name, limits) { do_check_eq(toprimitive(d.value), 5); d = t(10); do_check_eq(toprimitive(d.value), 10); - do_check_throws(function() { d.value = -10; }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = -10; }, TypeError); d = new t(20); do_check_eq(toprimitive(d.value), 20); d.value = ctypes.Int64(5); do_check_eq(d.value.charCodeAt(0), 5); - do_check_throws(function() { d.value = ctypes.Int64(-10); }, ctypesGlobal.TypeError); + do_check_throws(function() { d.value = ctypes.Int64(-10); }, TypeError); d.value = ctypes.UInt64(5); do_check_eq(d.value.charCodeAt(0), 5); @@ -1294,7 +1289,7 @@ function run_jschar_tests(library, t, name, limits) { {toString: function () { return 7; }}, {valueOf: function () { return 7; }}]; for (let i = 0; i < vals.length; i++) - do_check_throws(function () { d.value = vals[i]; }, ctypesGlobal.TypeError); + do_check_throws(function () { d.value = vals[i]; }, TypeError); do_check_true(d.address().constructor === t.ptr); do_check_eq(d.address().contents, "a"); @@ -1308,9 +1303,9 @@ function run_jschar_tests(library, t, name, limits) { do_check_eq(s.constructor.length, literal.length + 1); s = t.array(50)(literal); do_check_eq(s.readString(), literal); - do_check_throws(function() { t.array(3)(literal); }, ctypesGlobal.Error); + do_check_throws(function() { t.array(3)(literal); }, Error); - do_check_throws(function() { t.ptr(literal); }, ctypesGlobal.TypeError); + do_check_throws(function() { t.ptr(literal); }, TypeError); let p = t.ptr(s); do_check_eq(p.readString(), literal); @@ -1356,9 +1351,9 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared properties and functions on 'c.prototype' throw. for each (let p in props) - do_check_throws(function() { c.prototype[p]; }, ctypesGlobal.Error); + do_check_throws(function() { c.prototype[p]; }, Error); for each (let f in fns) - do_check_throws(function() { c.prototype[f](); }, ctypesGlobal.Error); + do_check_throws(function() { c.prototype[f](); }, Error); // Test that classes and prototypes are set up correctly on a constructed // type 't'. @@ -1391,12 +1386,12 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared properties and functions on 't.prototype.__proto__' // (and thus also 't.prototype') throw. for each (let p in instanceProps) { - do_check_throws(function() { t.prototype.__proto__[p]; }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype[p]; }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.__proto__[p]; }, Error); + do_check_throws(function() { t.prototype[p]; }, Error); } for each (let f in instanceFns) { - do_check_throws(function() { t.prototype.__proto__[f]() }, ctypesGlobal.Error); - do_check_throws(function() { t.prototype[f]() }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype.__proto__[f]() }, Error); + do_check_throws(function() { t.prototype[f]() }, Error); } // Check that 't.prototype' has the correct special properties. @@ -1405,7 +1400,7 @@ function run_type_ctor_class_tests(c, t, t2, props, fns, instanceProps, instance // Check that the shared special properties on 't.prototype' throw. for each (let p in specialProps) - do_check_throws(function() { t.prototype[p]; }, ctypesGlobal.Error); + do_check_throws(function() { t.prototype[p]; }, Error); // Test that an instance 'd' of 't' is a CData. if (t.__proto__ != ctypes.FunctionType.prototype) { @@ -1424,33 +1419,33 @@ function run_StructType_tests() { ctypes.StructType("t", [{"c": ctypes.int32_t}, {"d": ctypes.int64_t}]), [ "fields" ], [ "define" ], [], [ "addressOfField" ], [ "a", "b" ]); - do_check_throws(function() { ctypes.StructType(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.StructType("a", [], 5); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.StructType(null, []); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.StructType("a", null); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.StructType(); }, Error); + do_check_throws(function() { ctypes.StructType("a", [], 5); }, Error); + do_check_throws(function() { ctypes.StructType(null, []); }, Error); + do_check_throws(function() { ctypes.StructType("a", null); }, Error); // Check that malformed descriptors are an error. do_check_throws(function() { ctypes.StructType("a", [{"x":ctypes.int32_t}, {"x":ctypes.int8_t}]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [5]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [{}]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [{5:ctypes.int32_t}]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [{"5":ctypes.int32_t}]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [{"x":5}]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("a", [{"x":ctypes.int32_t()}]); - }, ctypesGlobal.Error); + }, Error); // Check that opaque structs work. let opaque_t = ctypes.StructType("a"); @@ -1460,7 +1455,7 @@ function run_StructType_tests() { do_check_true(opaque_t.prototype === undefined); do_check_true(opaque_t.fields === undefined); do_check_true(opaque_t.size === undefined); - do_check_throws(function() { opaque_t(); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t(); }, Error); let opaqueptr_t = opaque_t.ptr; do_check_true(opaqueptr_t.targetType === opaque_t); do_check_eq(opaqueptr_t.name, "a*"); @@ -1473,24 +1468,24 @@ function run_StructType_tests() { do_check_eq(ptrValue(opaqueptr), 1); do_check_throws(function() { opaqueptr.value = ctypes.StructType("a").ptr(); - }, ctypesGlobal.TypeError); + }, TypeError); // Check that 'define' works. - do_check_throws(function() { opaque_t.define(); }, ctypesGlobal.Error); - do_check_throws(function() { opaque_t.define([], 0); }, ctypesGlobal.Error); - do_check_throws(function() { opaque_t.define([{}]); }, ctypesGlobal.Error); - do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t.define(); }, Error); + do_check_throws(function() { opaque_t.define([], 0); }, Error); + do_check_throws(function() { opaque_t.define([{}]); }, Error); + do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, Error); do_check_throws(function() { opaque_t.define([{ a: ctypes.int32_t, b: ctypes.int64_t }]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { opaque_t.define([{ a: ctypes.int32_t }, { b: 0 }]); - }, ctypesGlobal.Error); + }, Error); do_check_false(opaque_t.hasOwnProperty("prototype")); // Check that circular references work with opaque structs... // but not crazy ones. - do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, ctypesGlobal.Error); + do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, Error); let circular_t = ctypes.StructType("circular", [{ a: opaqueptr_t }]); opaque_t.define([{ b: circular_t }]); let opaque = opaque_t(); @@ -1503,7 +1498,7 @@ function run_StructType_tests() { // original definition is preserved. do_check_throws(function() { opaque_t.define([{ c: ctypes.int32_t.array(8) }]); - }, ctypesGlobal.Error); + }, Error); do_check_eq(opaque_t.size, circular_t.size); do_check_true(opaque_t.prototype.hasOwnProperty("b")); do_check_false(opaque_t.prototype.hasOwnProperty("c")); @@ -1532,10 +1527,10 @@ function run_StructType_tests() { do_check_true(t_t.fields[2].c === g_t); do_check_true(t_t.fields[3].d === ctypes.int8_t); /* disabled temporarily per bug 598225. - do_check_throws(function() { t_t.fields.z = 0; }, ctypesGlobal.Error); - do_check_throws(function() { t_t.fields[4] = 0; }, ctypesGlobal.Error); - do_check_throws(function() { t_t.fields[4].a = 0; }, ctypesGlobal.Error); - do_check_throws(function() { t_t.fields[4].e = 0; }, ctypesGlobal.Error); + do_check_throws(function() { t_t.fields.z = 0; }, Error); + do_check_throws(function() { t_t.fields[4] = 0; }, Error); + do_check_throws(function() { t_t.fields[4].a = 0; }, Error); + do_check_throws(function() { t_t.fields[4].e = 0; }, Error); */ // Check that struct size bounds work, and that large, but not illegal, sizes @@ -1547,7 +1542,7 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xffffffff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, ctypesGlobal.Error); + }, Error); // Test 2: overflow struct size + struct tail padding. // To do this, we use a struct with maximum size and alignment 2. @@ -1556,7 +1551,7 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xfffffffe); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, ctypesGlobal.Error); + }, Error); } else { // Test 1: overflow struct size when converting from size_t to jsdouble. @@ -1565,25 +1560,25 @@ function run_StructType_tests() { do_check_eq(large_t.size, 0xfffffffffffff800); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": ctypes.int8_t}]); - }, ctypesGlobal.Error); + }, Error); let small_t = ctypes.int8_t.array(0x400); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, ctypesGlobal.Error); + }, Error); large_t = ctypes.StructType("large_t", [{"a": ctypes.int8_t.array(0x1fffffffffffff)}]); do_check_eq(large_t.size, 0x1fffffffffffff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t.array(2)}, {"b": ctypes.int8_t}]); - }, ctypesGlobal.Error); + }, Error); // Test 2: overflow struct size + field padding + field size. large_t = ctypes.int8_t.array(0xfffffffffffff800); small_t = ctypes.int8_t.array(0x800); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, ctypesGlobal.Error); + }, Error); // Test 3: overflow struct size + struct tail padding. // To do this, we use a struct with maximum size and alignment 2. @@ -1593,7 +1588,7 @@ function run_StructType_tests() { small_t = ctypes.int8_t.array(0xfff); do_check_throws(function() { ctypes.StructType("large_t", [{"a": large_t}, {"b": small_t}]); - }, ctypesGlobal.Error); + }, Error); } let g = g_t(); @@ -1602,8 +1597,8 @@ function run_StructType_tests() { g = new g_t(1, 2); do_check_eq(g.a, 1); do_check_eq(g.b, 2); - do_check_throws(function() { g_t(1); }, ctypesGlobal.Error); - do_check_throws(function() { g_t(1, 2, 3); }, ctypesGlobal.Error); + do_check_throws(function() { g_t(1); }, Error); + do_check_throws(function() { g_t(1, 2, 3); }, Error); for (let field in g) do_check_true(field == "a" || field == "b"); @@ -1628,9 +1623,9 @@ function run_StructType_tests() { g_a = s.addressOfField("b"); do_check_true(g_a.constructor === g_t.ptr); do_check_eq(g_a.contents.a, s.b.a); - do_check_throws(function() { s.addressOfField(); }, ctypesGlobal.Error); - do_check_throws(function() { s.addressOfField("d"); }, ctypesGlobal.Error); - do_check_throws(function() { s.addressOfField("a", 2); }, ctypesGlobal.Error); + do_check_throws(function() { s.addressOfField(); }, Error); + do_check_throws(function() { s.addressOfField("d"); }, Error); + do_check_throws(function() { s.addressOfField("a", 2); }, Error); do_check_eq(s.toSource(), "s_t(4, {\"a\": 7, \"b\": 2}, 10)"); do_check_eq(s.toSource(), s.toString()); @@ -1639,25 +1634,25 @@ function run_StructType_tests() { do_check_eq(s.b.b, s2.b.b); // Test that structs can be set from an object using 'value'. - do_check_throws(function() { s.value; }, ctypesGlobal.Error); + do_check_throws(function() { s.value; }, Error); let s_init = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13 }; s.value = s_init; do_check_eq(s.b.a, 9); do_check_eq(s.c, 13); - do_check_throws(function() { s.value = 5; }, ctypesGlobal.Error); - do_check_throws(function() { s.value = ctypes.int32_t(); }, ctypesGlobal.Error); - do_check_throws(function() { s.value = {}; }, ctypesGlobal.Error); - do_check_throws(function() { s.value = { "a": 2 }; }, ctypesGlobal.Error); - do_check_throws(function() { s.value = { "a": 2, "b": 5, "c": 10 }; }, ctypesGlobal.Error); + do_check_throws(function() { s.value = 5; }, Error); + do_check_throws(function() { s.value = ctypes.int32_t(); }, Error); + do_check_throws(function() { s.value = {}; }, Error); + do_check_throws(function() { s.value = { "a": 2 }; }, Error); + do_check_throws(function() { s.value = { "a": 2, "b": 5, "c": 10 }; }, Error); do_check_throws(function() { s.value = { "5": 2, "b": { "a": 9, "b": 5 }, "c": 13 }; - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { s.value = { "a": 2, "b": { "a": 9, "b": 5 }, "c": 13, "d": 17 }; - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { s.value = { "a": 2, "b": { "a": 9, "b": 5, "e": 9 }, "c": 13 }; - }, ctypesGlobal.Error); + }, Error); // Test that structs can be constructed similarly through ExplicitConvert, // and that the single-field case is disambiguated correctly. @@ -1670,7 +1665,7 @@ function run_StructType_tests() { v = v_t(8); do_check_eq(v.x, 8); let w_t = ctypes.StructType("w_t", [{ "y": v_t }]); - do_check_throws(function() { w_t(9); }, ctypesGlobal.Error); + do_check_throws(function() { w_t(9); }, Error); let w = w_t({ "x": 3 }); do_check_eq(w.y.x, 3); w = w_t({ "y": { "x": 19 } }); @@ -1690,10 +1685,10 @@ function run_StructType_tests() { // are illegal, but arrays of defined length work. do_check_throws(function() { ctypes.StructType("z_t", [{ a: ctypes.int32_t.array() }]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(0) }]); - }, ctypesGlobal.Error); + }, Error); z_t = ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(6) }]); do_check_eq(z_t.size, ctypes.int32_t.size * 6); let z = z_t([1, 2, 3, 4, 5, 6]); @@ -1709,11 +1704,11 @@ function run_PointerType_tests() { ctypes.PointerType(ctypes.int32_t), ctypes.PointerType(ctypes.int64_t), [ "targetType" ], [], [ "contents" ], [ "isNull" ], []); - do_check_throws(function() { ctypes.PointerType(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.PointerType(null); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.PointerType("void"); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.PointerType(); }, Error); + do_check_throws(function() { ctypes.PointerType(ctypes.int32_t, 5); }, Error); + do_check_throws(function() { ctypes.PointerType(null); }, Error); + do_check_throws(function() { ctypes.PointerType(ctypes.int32_t()); }, Error); + do_check_throws(function() { ctypes.PointerType("void"); }, Error); let name = "g_t"; let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -1734,10 +1729,10 @@ function run_PointerType_tests() { // Test ExplicitConvert. let p = p_t(); - do_check_throws(function() { p.value; }, ctypesGlobal.Error); + do_check_throws(function() { p.value; }, Error); do_check_eq(ptrValue(p), 0); - do_check_throws(function() { p.contents; }, ctypesGlobal.Error); - do_check_throws(function() { p.contents = g; }, ctypesGlobal.Error); + do_check_throws(function() { p.contents; }, Error); + do_check_throws(function() { p.contents = g; }, Error); p = p_t(5); do_check_eq(ptrValue(p), 5); p = p_t(ctypes.UInt64(10)); @@ -1746,22 +1741,22 @@ function run_PointerType_tests() { // Test ImplicitConvert. p.value = null; do_check_eq(ptrValue(p), 0); - do_check_throws(function() { p.value = 5; }, ctypesGlobal.Error); + do_check_throws(function() { p.value = 5; }, Error); // Test opaque pointers. let f_t = ctypes.StructType("FILE").ptr; do_check_eq(f_t.name, "FILE*"); do_check_eq(f_t.toSource(), 'ctypes.StructType("FILE").ptr'); let f = new f_t(); - do_check_throws(function() { f.contents; }, ctypesGlobal.Error); - do_check_throws(function() { f.contents = 0; }, ctypesGlobal.Error); + do_check_throws(function() { f.contents; }, Error); + do_check_throws(function() { f.contents = 0; }, Error); let f = f_t(5); - do_check_throws(function() { f.contents = 0; }, ctypesGlobal.Error); + do_check_throws(function() { f.contents = 0; }, Error); do_check_eq(f.toSource(), 'FILE.ptr(ctypes.UInt64("0x5"))'); - do_check_throws(function() { f_t(p); }, ctypesGlobal.Error); - do_check_throws(function() { f.value = p; }, ctypesGlobal.Error); - do_check_throws(function() { p.value = f; }, ctypesGlobal.Error); + do_check_throws(function() { f_t(p); }, Error); + do_check_throws(function() { f.value = p; }, Error); + do_check_throws(function() { p.value = f; }, Error); // Test void pointers. let v_t = ctypes.PointerType(ctypes.void_t); @@ -1786,7 +1781,7 @@ function run_PointerType_tests() { // but that the former cannot be dereferenced. let z_t = ctypes.int32_t.array().ptr; do_check_eq(ptrValue(z_t()), 0); - do_check_throws(function() { z_t().contents }, ctypesGlobal.Error); + do_check_throws(function() { z_t().contents }, Error); z_t = ctypes.int32_t.array(0).ptr; do_check_eq(ptrValue(z_t()), 0); let z = ctypes.int32_t.array(0)().address(); @@ -1800,25 +1795,25 @@ function run_FunctionType_tests() { [ "abi", "returnType", "argTypes", "isVariadic" ], undefined, undefined, undefined, undefined); - do_check_throws(function() { ctypes.FunctionType(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.FunctionType(); }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.void_t ]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, [ ctypes.void_t ], 5); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, ctypes.void_t); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, null); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t()); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.void_t, ctypes.void_t); - }, ctypesGlobal.Error); + }, Error); let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]); let g = g_t(1, 2); @@ -1845,11 +1840,11 @@ function run_FunctionType_tests() { "ctypes.FunctionType(ctypes.default_abi, g_t).ptr"); // Check that constructing a FunctionType CData directly throws. - do_check_throws(function() { f_t(); }, ctypesGlobal.Error); + do_check_throws(function() { f_t(); }, Error); // Test ExplicitConvert. let f = fp_t(); - do_check_throws(function() { f.value; }, ctypesGlobal.Error); + do_check_throws(function() { f.value; }, Error); do_check_eq(ptrValue(f), 0); f = fp_t(5); do_check_eq(ptrValue(f), 5); @@ -1859,15 +1854,15 @@ function run_FunctionType_tests() { // Test ImplicitConvert. f.value = null; do_check_eq(ptrValue(f), 0); - do_check_throws(function() { f.value = 5; }, ctypesGlobal.Error); + do_check_throws(function() { f.value = 5; }, Error); do_check_eq(f.toSource(), 'ctypes.FunctionType(ctypes.default_abi, g_t).ptr(ctypes.UInt64("0x0"))'); // Test ImplicitConvert from a function pointer of different type. let f2_t = ctypes.FunctionType(ctypes.default_abi, g_t, [ ctypes.int32_t ]); let f2 = f2_t.ptr(); - do_check_throws(function() { f.value = f2; }, ctypesGlobal.Error); - do_check_throws(function() { f2.value = f; }, ctypesGlobal.Error); + do_check_throws(function() { f.value = f2; }, Error); + do_check_throws(function() { f2.value = f; }, Error); // Test that converting to a voidptr_t works. let v = ctypes.voidptr_t(f2); @@ -1898,8 +1893,8 @@ function run_FunctionType_tests() { do_check_true(f4_t.argTypes[0] === ctypes.int32_t); do_check_true(f4_t.argTypes[1] === fp_t); /* disabled temporarily per bug 598225. - do_check_throws(function() { f4_t.argTypes.z = 0; }, ctypesGlobal.Error); - do_check_throws(function() { f4_t.argTypes[0] = 0; }, ctypesGlobal.Error); + do_check_throws(function() { f4_t.argTypes.z = 0; }, Error); + do_check_throws(function() { f4_t.argTypes[0] = 0; }, Error); */ let t4_t = f4_t.ptr.ptr.array(8).array(); @@ -1927,10 +1922,10 @@ function run_ArrayType_tests() { ctypes.ArrayType(ctypes.int32_t, 10), ctypes.ArrayType(ctypes.int64_t), [ "elementType", "length" ], [], [ "length" ], [ "addressOfElement" ]); - do_check_throws(function() { ctypes.ArrayType(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.ArrayType(null); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, 1, 5); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, -1); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.ArrayType(); }, Error); + do_check_throws(function() { ctypes.ArrayType(null); }, Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, 1, 5); }, Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.int32_t, -1); }, Error); let name = "g_t"; let g_t = ctypes.StructType(name, [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -1947,7 +1942,7 @@ function run_ArrayType_tests() { "ctypes.StructType(\"g_t\", [{ \"a\": ctypes.int32_t }, { \"b\": ctypes.double }]).array(10)"); do_check_eq(a_t.array().name, name + "[][10]"); do_check_eq(a_t.array(5).name, name + "[5][10]"); - do_check_throws(function() { ctypes.int32_t.array().array(); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.int32_t.array().array(); }, Error); let a = new a_t(); do_check_eq(a[0].a, 0); @@ -1955,9 +1950,9 @@ function run_ArrayType_tests() { a[0] = g; do_check_eq(a[0].a, 1); do_check_eq(a[0].b, 2); - do_check_throws(function() { a[-1]; }, ctypesGlobal.Error); + do_check_throws(function() { a[-1]; }, Error); do_check_eq(a[9].a, 0); - do_check_throws(function() { a[10]; }, ctypesGlobal.Error); + do_check_throws(function() { a[10]; }, Error); do_check_eq(a[ctypes.Int64(0)].a, 1); do_check_eq(a[ctypes.UInt64(0)].b, 2); @@ -1978,9 +1973,9 @@ function run_ArrayType_tests() { do_check_eq(a2.constructor.length, 5); do_check_eq(a2.length, 5); do_check_eq(a2.constructor.size, g_t.size * 5); - do_check_throws(function() { new a2_t(); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t)); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t), 5); }, ctypesGlobal.Error); + do_check_throws(function() { new a2_t(); }, Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t)); }, Error); + do_check_throws(function() { ctypes.ArrayType(ctypes.ArrayType(g_t), 5); }, Error); let b_t = ctypes.int8_t.array(ctypes.UInt64(0xffff)); do_check_eq(b_t.length, 0xffff); @@ -1992,24 +1987,24 @@ function run_ArrayType_tests() { if (ctypes.size_t.size == 4) { do_check_throws(function() { ctypes.ArrayType(ctypes.int8_t, 0x100000000); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.ArrayType(ctypes.int16_t, 0x80000000); - }, ctypesGlobal.Error); + }, Error); let large_t = ctypes.int8_t.array(0x80000000); - do_check_throws(function() { large_t.array(2); }, ctypesGlobal.Error); + do_check_throws(function() { large_t.array(2); }, Error); } else { do_check_throws(function() { ctypes.ArrayType(ctypes.int8_t, ctypes.UInt64("0xffffffffffffffff")); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.ArrayType(ctypes.int16_t, ctypes.UInt64("0x8000000000000000")); - }, ctypesGlobal.Error); + }, Error); let large_t = ctypes.int8_t.array(0x8000000000000000); - do_check_throws(function() { large_t.array(2); }, ctypesGlobal.Error); + do_check_throws(function() { large_t.array(2); }, Error); } // Test that arrays ImplicitConvert to pointers. @@ -2034,11 +2029,11 @@ function run_ArrayType_tests() { c.value = c; do_check_eq(c[3], 4); - do_check_throws(function() { c.value; }, ctypesGlobal.Error); - do_check_throws(function() { c.value = [1, 2, 3, 4, 5]; }, ctypesGlobal.Error); - do_check_throws(function() { c.value = [1, 2, 3, 4, 5, 6, 7]; }, ctypesGlobal.Error); - do_check_throws(function() { c.value = [1, 2, 7.4, 4, 5, 6]; }, ctypesGlobal.Error); - do_check_throws(function() { c.value = []; }, ctypesGlobal.Error); + do_check_throws(function() { c.value; }, Error); + do_check_throws(function() { c.value = [1, 2, 3, 4, 5]; }, Error); + do_check_throws(function() { c.value = [1, 2, 3, 4, 5, 6, 7]; }, Error); + do_check_throws(function() { c.value = [1, 2, 7.4, 4, 5, 6]; }, Error); + do_check_throws(function() { c.value = []; }, Error); } function run_cast_tests() { @@ -2052,9 +2047,9 @@ function run_cast_tests() { do_check_eq(i.value, k.value); // Test casting to a type of undefined or larger size. - do_check_throws(function() { ctypes.cast(i, ctypes.void_t); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.cast(i, ctypes.int32_t.array()); }, ctypesGlobal.Error); - do_check_throws(function() { ctypes.cast(i, ctypes.int64_t); }, ctypesGlobal.Error); + do_check_throws(function() { ctypes.cast(i, ctypes.void_t); }, Error); + do_check_throws(function() { ctypes.cast(i, ctypes.int32_t.array()); }, Error); + do_check_throws(function() { ctypes.cast(i, ctypes.int64_t); }, Error); // Test casting between special types. let g_t = ctypes.StructType("g_t", [{ a: ctypes.int32_t }, { b: ctypes.double }]); @@ -2092,7 +2087,7 @@ function run_void_tests(library) { // Test that library.declare throws with void function args. do_check_throws(function() { library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t, ctypes.void_t); - }, ctypesGlobal.Error); + }, Error); #ifdef WIN32 #ifndef HAVE_64BIT_OS @@ -2102,7 +2097,7 @@ function run_void_tests(library) { // Check that WINAPI symbol lookup for a regular stdcall function fails. do_check_throws(function() { let test_winapi_t = library.declare("test_void_t_stdcall", ctypes.winapi_abi, ctypes.void_t); - }, ctypesGlobal.Error); + }, Error); #endif #endif } @@ -2115,7 +2110,7 @@ function run_string_tests(library) { // don't convert anything else to a string let vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []]; for (let i = 0; i < vals.length; i++) - do_check_throws(function() { test_ansi_len(vals[i]); }, ctypesGlobal.TypeError); + do_check_throws(function() { test_ansi_len(vals[i]); }, TypeError); let test_wide_len = library.declare("test_wide_len", ctypes.default_abi, ctypes.int32_t, ctypes.jschar.ptr); do_check_eq(test_wide_len("hello world"), 11); @@ -2240,7 +2235,7 @@ function run_function_tests(library) do_check_eq(test_ansi_len.constructor.toSource(), "ctypes.FunctionType(ctypes.default_abi, ctypes.int32_t, [ctypes.char.ptr]).ptr"); /* disabled temporarily per bug 598225. - do_check_throws(function() { test_ansi_len.value = null; }, ctypesGlobal.Error); + do_check_throws(function() { test_ansi_len.value = null; }, Error); do_check_eq(ptrValue(test_ansi_len), ptrValue(ptr)); */ @@ -2249,7 +2244,7 @@ function run_function_tests(library) do_check_true(test_ansi_len_2.constructor === fn_t); do_check_eq(ptrValue(test_ansi_len), ptrValue(test_ansi_len_2)); /* disabled temporarily per bug 598225. - do_check_throws(function() { test_ansi_len_2.value = null; }, ctypesGlobal.Error); + do_check_throws(function() { test_ansi_len_2.value = null; }, Error); do_check_eq(ptrValue(test_ansi_len_2), ptrValue(ptr)); */ } @@ -2267,7 +2262,7 @@ function run_closure_tests(library) return 1; } let fn_t = ctypes.FunctionType(ctypes.winapi_abi, ctypes.int32_t, []).ptr; - do_check_throws(function() { fn_t(closure_fn) }, ctypesGlobal.Error); + do_check_throws(function() { fn_t(closure_fn) }, Error); #endif #endif } @@ -2322,29 +2317,29 @@ function run_variadic_tests(library) { do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.bool, [ctypes.bool, "...", ctypes.bool]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.default_abi, ctypes.bool, ["..."]); - }, ctypesGlobal.Error); + }, Error); #ifdef WIN32 #ifndef HAVE_64BIT_OS do_check_throws(function() { ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool, [ctypes.bool, "..."]); - }, ctypesGlobal.Error); + }, Error); do_check_throws(function() { ctypes.FunctionType(ctypes.winapi_abi, ctypes.bool, [ctypes.bool, "..."]); - }, ctypesGlobal.Error); + }, Error); #endif #endif do_check_throws(function() { // No variadic closure callbacks allowed. sum_va_type(function(){}); - }, ctypesGlobal.Error); + }, Error); let count_true_va = library.declare("test_sum_va_cdecl", ctypes.default_abi, ctypes.uint8_t, ctypes.uint8_t, "..."); @@ -2390,7 +2385,7 @@ function run_variadic_tests(library) { [ctypes.uint8_t]).ptr; do_check_throws(function() { sum_va_type().value = sum_notva_type(); - }, ctypesGlobal.Error); + }, Error); } function run_static_data_tests(library) diff --git a/toolkit/components/perf/PerfMeasurement.cpp b/toolkit/components/perf/PerfMeasurement.cpp index f166328fd7e9..acac0358b76a 100644 --- a/toolkit/components/perf/PerfMeasurement.cpp +++ b/toolkit/components/perf/PerfMeasurement.cpp @@ -112,7 +112,11 @@ Module::Call(nsIXPConnectWrappedNative* wrapper, jsval* vp, PRBool* _retval) { - JSObject* global = JS_GetGlobalForScopeChain(cx); + JSObject* scope = JS_GetScopeChain(cx); + if (!scope) + return NS_ERROR_NOT_AVAILABLE; + + JSObject* global = JS_GetGlobalForObject(cx, scope); if (!global) return NS_ERROR_NOT_AVAILABLE; From a002d71353edb8612b9e653fb5d9bf2ab8eb43c9 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sat, 19 Feb 2011 22:35:54 -0800 Subject: [PATCH 24/42] Back out on orange (635548, r/a=shaver). --- js/src/jsinterp.cpp | 5 ++--- js/src/jsscript.h | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index f19b02ef0e84..eab8a4ef6bbf 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -620,9 +620,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp) JS_ASSERT(script); /* FIXME: Once bug 470510 is fixed, make this an assert. */ - if (script->compileAndGo && script->hasGlobals()) { - int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS) - .toInt32(); + if (script->compileAndGo) { + int32 flags = fp->scopeChain().getGlobal()->getReservedSlot(JSRESERVED_GLOBAL_FLAGS).toInt32(); if (flags & JSGLOBAL_FLAGS_CLEARED) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE); return false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index b9995ac7890b..7394abe95f3c 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -514,10 +514,6 @@ struct JSScript { return (JSTryNoteArray *) ((uint8 *) (this + 1) + trynotesOffset); } - bool hasGlobals() const { - return isValidOffset(globalsOffset); - } - js::GlobalSlotArray *globals() { JS_ASSERT(isValidOffset(globalsOffset)); return (js::GlobalSlotArray *) ((uint8 *) (this + 1) + globalsOffset); From b04b02701079cfe6d5a365402d5c4943e5dda634 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sat, 19 Feb 2011 22:59:49 -0800 Subject: [PATCH 25/42] When idle the GC holds on to unused chunks indefinitely (bug 631733, r=brendan, a=blocker). --- dom/base/nsJSEnvironment.cpp | 6 ++++++ js/src/jsapi.cpp | 2 ++ js/src/jsapi.h | 5 ++++- js/src/jscntxt.h | 1 + js/src/jsgc.cpp | 22 ++++++++++------------ js/src/jsgc.h | 2 -- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 2f026e56c986..f9dc93bb675e 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3484,6 +3484,12 @@ DOMGCCallback(JSContext *cx, JSGCStatus status) nsJSContext::PokeCC(); } } + + // If we didn't end up scheduling a GC, and there are unused + // chunks waiting to expire, make sure we will GC again soon. + if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) { + nsJSContext::PokeGC(); + } } JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ca3acefdaf18..629ef34a7d58 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2644,6 +2644,8 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) return rt->gcBytes; case JSGC_MODE: return uint32(rt->gcMode); + case JSGC_UNUSED_CHUNKS: + return uint32(rt->gcChunksWaitingToExpire); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index efa1e556f9c1..a598b04ad233 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1785,7 +1785,10 @@ typedef enum JSGCParamKey { JSGC_MAX_CODE_CACHE_BYTES = 6, /* Select GC mode. */ - JSGC_MODE = 7 + JSGC_MODE = 7, + + /* Number of GC chunks waiting to expire. */ + JSGC_UNUSED_CHUNKS = 8 } JSGCParamKey; typedef enum JSGCMode { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 17a1567dc6ff..4ff77fa42f81 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1047,6 +1047,7 @@ struct JSRuntime { size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; + size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9ef631349d79..18c962cc36c8 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -360,14 +360,6 @@ Chunk::releaseArena(Arena *arena) info.age = 0; } -bool -Chunk::expire() -{ - if (!unused()) - return false; - return info.age++ > MaxAge; -} - JSRuntime * Chunk::getRuntime() { @@ -456,16 +448,22 @@ PickChunk(JSRuntime *rt) static void ExpireGCChunks(JSRuntime *rt) { + static const size_t MaxAge = 3; + /* Remove unused chunks. */ AutoLockGC lock(rt); + rt->gcChunksWaitingToExpire = 0; for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) { Chunk *chunk = e.front(); JS_ASSERT(chunk->info.runtime == rt); - if (chunk->expire()) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; + if (chunk->unused()) { + if (chunk->info.age++ > MaxAge) { + e.removeFront(); + ReleaseGCChunk(rt, chunk); + continue; + } + rt->gcChunksWaitingToExpire++; } } } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6379023ac6f6..bb666e617e55 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -340,7 +340,6 @@ struct Chunk { sizeof(MarkingDelay); static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena; - static const size_t MaxAge = 3; Arena arenas[ArenasPerChunk]; ArenaBitmap bitmaps[ArenasPerChunk]; @@ -362,7 +361,6 @@ struct Chunk { void releaseArena(Arena *a); JSRuntime *getRuntime(); - bool expire(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE); From ab845aac657ff39a96ddcc012e47d4fc4f37df7e Mon Sep 17 00:00:00 2001 From: "Olli.Pettay@gmail.com" Date: Sun, 20 Feb 2011 00:20:14 -0800 Subject: [PATCH 26/42] Don't call GC or CC unexpectedly during shutdown (bug 635251, r/a=gal). --- dom/base/nsJSEnvironment.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index f9dc93bb675e..f19e7c9eb780 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3365,8 +3365,6 @@ nsJSContext::PokeGC() if (!sGCTimer) { NS_WARNING("Failed to create timer"); - - GarbageCollectNow(); return; } @@ -3403,8 +3401,6 @@ nsJSContext::PokeCC() if (!sCCTimer) { NS_WARNING("Failed to create timer"); - - CycleCollectNow(); return; } From fa538e1fe862b304dbe3520cdeff420d5a8b3d28 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sat, 19 Feb 2011 23:13:56 -0800 Subject: [PATCH 27/42] Fix unqualified function invocation etc., part deux (635582, r=gal). --- js/src/jit-test/tests/basic/testBug634590.js | 7 ++++- js/src/jit-test/tests/basic/testBug634590b.js | 19 +++++++++++++ js/src/jit-test/tests/basic/testBug634590c.js | 20 ++++++++++++++ js/src/jit-test/tests/basic/testBug634590d.js | 15 +++++++++++ js/src/jsfuninlines.h | 15 +++++++---- js/src/jsinterp.cpp | 27 +++++++------------ 6 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug634590b.js create mode 100644 js/src/jit-test/tests/basic/testBug634590c.js create mode 100644 js/src/jit-test/tests/basic/testBug634590d.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js index da47ed0090df..5787c61c9d76 100644 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -3,5 +3,10 @@ var sb = evalcx(''); sb.name = "inner"; sb.parent = this; function f() { return this.name; } -assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), +assertEq(evalcx('this.f = parent.f;\n' + + 'var s = "";\n' + + 'for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + 's', + sb), "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jit-test/tests/basic/testBug634590b.js b/js/src/jit-test/tests/basic/testBug634590b.js new file mode 100644 index 000000000000..42531a82958d --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590b.js @@ -0,0 +1,19 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +f.notMuchTodo = '42'; +assertEq(evalcx('{\n' + + ' let f = parent.f;\n' + + ' let name = "block";\n' + + ' (function () {\n' + + ' eval(f.notMuchTodo);\n' + // reify Block + ' var s = "";\n' + + ' for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + ' return s;\n' + + ' })();\n' + + '}', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590c.js b/js/src/jit-test/tests/basic/testBug634590c.js new file mode 100644 index 000000000000..1ae2542def1e --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590c.js @@ -0,0 +1,20 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +f.notMuchTodo = '42'; +assertEq(evalcx('(function () {\n' + + ' arguments = null;\n' + // force heavyweight + ' var f = parent.f;\n' + + ' var name = "call";\n' + + ' return (function () {\n' + + ' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME + ' var s = "";\n' + + ' for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + ' return s;\n' + + ' })();\n' + + '})()', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590d.js b/js/src/jit-test/tests/basic/testBug634590d.js new file mode 100644 index 000000000000..63612e2664f3 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590d.js @@ -0,0 +1,15 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +this.f = function name(outer) { + if (outer) return name(false); + return this.name; +} +assertEq(evalcx('this.f = parent.f;\n' + + 'var s = "";\n' + + 'for (i = 0; i < 10; ++i)\n' + + ' s += f(true);\n' + + 's', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index d951e93c4625..331e45724650 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -55,11 +55,16 @@ static inline bool IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) { /* - * Look past any wrappers. If the callee is a strict function it is always - * safe because we won't do 'this' coercion in strict mode. Otherwise the - * callee is only safe to transform into the lazy 'this' token (undefined) - * if it is in the current scope. Without this restriction, lazy 'this' - * coercion would pick up the wrong global in the other scope. + * Look past any function wrappers. If the callee is a wrapped strict-mode + * function, lazy 'this' coercion is vacuously safe because strict-mode + * functions don't coerce 'this' at all. Otherwise, the callee is safe to + * transform into the lazy 'this' cookie (the undefined value) only if it + * is in the current scope. + * + * Without this restriction, lazy 'this' coercion would pick up the "wrong" + * global at the end of the callee function object's scope, rather than the + * "right" (backward compatible since 1995) global that was the "Reference + * base object" in the callee expression. */ if (callee->isProxy()) { callee = callee->unwrap(); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index eab8a4ef6bbf..bd00499fb77d 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2179,11 +2179,12 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) } static inline bool -SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) { if (!funval.isObject() || - ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && - IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { + (obj->isGlobal() + ? IsSafeForLazyThisCoercion(cx, &funval.toObject()) + : IsCacheableNonGlobalScope(obj))) { /* * We can avoid computing 'this' eagerly and push the implicit 'this' * value (undefined), as long the scope is cachable and we are not @@ -4816,10 +4817,10 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj, funval) \ +#define PUSH_THISV(cx, obj, funval) \ JS_BEGIN_MACRO \ Value v; \ - if (!SlowThis(cx, obj, funval, &v)) \ + if (!ComputeThis(cx, obj, funval, &v)) \ goto error; \ PUSH_COPY(v); \ JS_END_MACRO \ @@ -4853,16 +4854,8 @@ BEGIN_CASE(JSOP_CALLNAME) } JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { - if (regs.sp[-1].isObject() && - !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { - if (!(obj = obj->thisObject(cx))) - return false; - PUSH_OBJECT(*obj); - } else { - PUSH_UNDEFINED(); - } - } + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) + PUSH_THISV(cx, obj, regs.sp[-1]); len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4900,7 +4893,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj, rval); + PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6403,7 +6396,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj, rval); + PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) From b6bc44114783e610a3d42d21a463560a76f3fd30 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sun, 20 Feb 2011 15:07:25 -0800 Subject: [PATCH 28/42] Improve E4X syntax error reporting. r=nnethercote, a=shaver. --- js/src/jsscan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 028f4f675b42..7994f06e3550 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -857,6 +857,7 @@ TokenStream::getTokenInternal() c = getChar(); } while (JS_ISXMLSPACE(c)); ungetChar(c); + tp->pos.end.lineno = lineno; tt = TOK_XMLSPACE; goto out; } From 355d67127fac98b844e5cff492b3b5fa97a9433f Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sun, 20 Feb 2011 17:46:35 -0800 Subject: [PATCH 29/42] =?UTF-8?q?#jsapi=20observed=20the=20extent=20of=20o?= =?UTF-8?q?range=20in=20the=20TM=20tree=20and=20saw=20that=20OS=20X=20x86?= =?UTF-8?q?=20opt=20was=20consistently=20and=20totally=20evil.=20So=20jwal?= =?UTF-8?q?den=20was=20sorry=20he=20had=20ever=20pushed=20changes=20for=20?= =?UTF-8?q?bug=20631135.=20It=20broke=20his=20heart.=20And=20he=20said,=20?= =?UTF-8?q?=E2=80=9CI=20will=20wipe=20this=20tree=20of=20every=20push=20ba?= =?UTF-8?q?ck=20to=209105cd721d46.=20Yes,=20and=20I=20will=20revert=20the?= =?UTF-8?q?=20entire=20TM=20CLOSED=20TREE.=20I=20am=20sorry=20I=20ever=20p?= =?UTF-8?q?ushed=20to=20it.=E2=80=9D=20But=209105cd721d46=20found=20favor?= =?UTF-8?q?=20with=20#jsapi.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dom/base/nsJSEnvironment.cpp | 10 +- js/src/jit-test/tests/basic/testBug634590.js | 12 -- js/src/jit-test/tests/basic/testBug634590b.js | 19 --- js/src/jit-test/tests/basic/testBug634590c.js | 20 --- js/src/jit-test/tests/basic/testBug634590d.js | 15 --- js/src/jsapi.cpp | 2 - js/src/jsapi.h | 5 +- js/src/jscntxt.h | 1 - js/src/jsfuninlines.h | 31 ----- js/src/jsgc.cpp | 22 ++-- js/src/jsgc.h | 2 + js/src/jsinterp.cpp | 75 ++++++----- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 11 +- js/src/jsobj.h | 13 +- js/src/jsobjinlines.h | 2 - js/src/jsscan.cpp | 123 ++++++++++++------ js/src/jsscan.h | 2 +- js/src/jstracer.cpp | 4 +- js/src/methodjit/MonoIC.cpp | 24 ++-- js/src/methodjit/PolyIC.cpp | 4 +- 21 files changed, 163 insertions(+), 236 deletions(-) delete mode 100644 js/src/jit-test/tests/basic/testBug634590.js delete mode 100644 js/src/jit-test/tests/basic/testBug634590b.js delete mode 100644 js/src/jit-test/tests/basic/testBug634590c.js delete mode 100644 js/src/jit-test/tests/basic/testBug634590d.js diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index f19e7c9eb780..2f026e56c986 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3365,6 +3365,8 @@ nsJSContext::PokeGC() if (!sGCTimer) { NS_WARNING("Failed to create timer"); + + GarbageCollectNow(); return; } @@ -3401,6 +3403,8 @@ nsJSContext::PokeCC() if (!sCCTimer) { NS_WARNING("Failed to create timer"); + + CycleCollectNow(); return; } @@ -3480,12 +3484,6 @@ DOMGCCallback(JSContext *cx, JSGCStatus status) nsJSContext::PokeCC(); } } - - // If we didn't end up scheduling a GC, and there are unused - // chunks waiting to expire, make sure we will GC again soon. - if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) { - nsJSContext::PokeGC(); - } } JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js deleted file mode 100644 index 5787c61c9d76..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ /dev/null @@ -1,12 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -assertEq(evalcx('this.f = parent.f;\n' + - 'var s = "";\n' + - 'for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - 's', - sb), - "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jit-test/tests/basic/testBug634590b.js b/js/src/jit-test/tests/basic/testBug634590b.js deleted file mode 100644 index 42531a82958d..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590b.js +++ /dev/null @@ -1,19 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -f.notMuchTodo = '42'; -assertEq(evalcx('{\n' + - ' let f = parent.f;\n' + - ' let name = "block";\n' + - ' (function () {\n' + - ' eval(f.notMuchTodo);\n' + // reify Block - ' var s = "";\n' + - ' for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - ' return s;\n' + - ' })();\n' + - '}', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590c.js b/js/src/jit-test/tests/basic/testBug634590c.js deleted file mode 100644 index 1ae2542def1e..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590c.js +++ /dev/null @@ -1,20 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -f.notMuchTodo = '42'; -assertEq(evalcx('(function () {\n' + - ' arguments = null;\n' + // force heavyweight - ' var f = parent.f;\n' + - ' var name = "call";\n' + - ' return (function () {\n' + - ' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME - ' var s = "";\n' + - ' for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - ' return s;\n' + - ' })();\n' + - '})()', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590d.js b/js/src/jit-test/tests/basic/testBug634590d.js deleted file mode 100644 index 63612e2664f3..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590d.js +++ /dev/null @@ -1,15 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -this.f = function name(outer) { - if (outer) return name(false); - return this.name; -} -assertEq(evalcx('this.f = parent.f;\n' + - 'var s = "";\n' + - 'for (i = 0; i < 10; ++i)\n' + - ' s += f(true);\n' + - 's', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 629ef34a7d58..ca3acefdaf18 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2644,8 +2644,6 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) return rt->gcBytes; case JSGC_MODE: return uint32(rt->gcMode); - case JSGC_UNUSED_CHUNKS: - return uint32(rt->gcChunksWaitingToExpire); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a598b04ad233..efa1e556f9c1 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1785,10 +1785,7 @@ typedef enum JSGCParamKey { JSGC_MAX_CODE_CACHE_BYTES = 6, /* Select GC mode. */ - JSGC_MODE = 7, - - /* Number of GC chunks waiting to expire. */ - JSGC_UNUSED_CHUNKS = 8 + JSGC_MODE = 7 } JSGCParamKey; typedef enum JSGCMode { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4ff77fa42f81..17a1567dc6ff 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1047,7 +1047,6 @@ struct JSRuntime { size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; - size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 331e45724650..4cbb744b236e 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -49,35 +49,4 @@ JSFunction::inStrictMode() const return script()->strictModeCode; } -namespace js { - -static inline bool -IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) -{ - /* - * Look past any function wrappers. If the callee is a wrapped strict-mode - * function, lazy 'this' coercion is vacuously safe because strict-mode - * functions don't coerce 'this' at all. Otherwise, the callee is safe to - * transform into the lazy 'this' cookie (the undefined value) only if it - * is in the current scope. - * - * Without this restriction, lazy 'this' coercion would pick up the "wrong" - * global at the end of the callee function object's scope, rather than the - * "right" (backward compatible since 1995) global that was the "Reference - * base object" in the callee expression. - */ - if (callee->isProxy()) { - callee = callee->unwrap(); - if (!callee->isFunction()) - return true; // treat any non-wrapped-function proxy as strict - - JSFunction *fun = callee->getFunctionPrivate(); - if (fun->isInterpreted() && fun->inStrictMode()) - return true; - } - return callee->getGlobal() == cx->fp()->scopeChain().getGlobal(); -} - -} - #endif /* jsfuninlines_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 18c962cc36c8..9ef631349d79 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -360,6 +360,14 @@ Chunk::releaseArena(Arena *arena) info.age = 0; } +bool +Chunk::expire() +{ + if (!unused()) + return false; + return info.age++ > MaxAge; +} + JSRuntime * Chunk::getRuntime() { @@ -448,22 +456,16 @@ PickChunk(JSRuntime *rt) static void ExpireGCChunks(JSRuntime *rt) { - static const size_t MaxAge = 3; - /* Remove unused chunks. */ AutoLockGC lock(rt); - rt->gcChunksWaitingToExpire = 0; for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) { Chunk *chunk = e.front(); JS_ASSERT(chunk->info.runtime == rt); - if (chunk->unused()) { - if (chunk->info.age++ > MaxAge) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; - } - rt->gcChunksWaitingToExpire++; + if (chunk->expire()) { + e.removeFront(); + ReleaseGCChunk(rt, chunk); + continue; } } } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index bb666e617e55..6379023ac6f6 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -340,6 +340,7 @@ struct Chunk { sizeof(MarkingDelay); static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena; + static const size_t MaxAge = 3; Arena arenas[ArenasPerChunk]; ArenaBitmap bitmaps[ArenasPerChunk]; @@ -361,6 +362,7 @@ struct Chunk { void releaseArena(Arena *a); JSRuntime *getRuntime(); + bool expire(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index bd00499fb77d..fde50aec9f10 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2178,31 +2178,6 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } -static inline bool -ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) -{ - if (!funval.isObject() || - (obj->isGlobal() - ? IsSafeForLazyThisCoercion(cx, &funval.toObject()) - : IsCacheableNonGlobalScope(obj))) { - /* - * We can avoid computing 'this' eagerly and push the implicit 'this' - * value (undefined), as long the scope is cachable and we are not - * crossing into another scope (in which case lazy calculation of 'this' - * would pick up the new and incorrect scope). 'strict' functions are an - * exception. We don't want to eagerly calculate 'this' for them even if - * the callee is in a different scope. - */ - *vp = UndefinedValue(); - return true; - } - - if (!(obj = obj->thisObject(cx))) - return false; - *vp = ObjectValue(*obj); - return true; -} - namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2228,8 +2203,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4817,13 +4792,26 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define PUSH_THISV(cx, obj, funval) \ - JS_BEGIN_MACRO \ - Value v; \ - if (!ComputeThis(cx, obj, funval, &v)) \ - goto error; \ - PUSH_COPY(v); \ - JS_END_MACRO \ +#define SLOW_PUSH_THISV(cx, obj) \ + JS_BEGIN_MACRO \ + Class *clasp; \ + JSObject *thisp = obj; \ + if (!thisp->getParent() || \ + (clasp = thisp->getClass()) == &js_CallClass || \ + clasp == &js_BlockClass || \ + clasp == &js_DeclEnvClass) { \ + /* Push the ImplicitThisValue for the Environment Record */ \ + /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ + /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ + /* (Function Calls). */ \ + PUSH_UNDEFINED(); \ + } else { \ + thisp = thisp->thisObject(cx); \ + if (!thisp) \ + goto error; \ + PUSH_OBJECT(*thisp); \ + } \ + JS_END_MACRO BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4853,9 +4841,20 @@ BEGIN_CASE(JSOP_CALLNAME) PUSH_COPY(rval); } - JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); + /* + * Push results, the same as below, but with a prop$ hit there + * is no need to test for the unusual and uncacheable case where + * the caller determines |this|. + */ +#if DEBUG + Class *clasp; + JS_ASSERT(!obj->getParent() || + (clasp = obj->getClass()) == &js_CallClass || + clasp == &js_BlockClass || + clasp == &js_DeclEnvClass); +#endif if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_THISV(cx, obj, regs.sp[-1]); + PUSH_UNDEFINED(); len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4893,7 +4892,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_NAME) @@ -6396,7 +6395,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c976cdea8da1..c55c868b3feb 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 173921e21ba4..b2925942c7cc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4445,11 +4445,6 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } -namespace js { - - -} - /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5057,7 +5052,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? IsCacheableNonGlobalScope(obj) + ? js_IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5167,11 +5162,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because IsCacheableNonGlobalScope + * The test order here matters because js_IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || IsCacheableNonGlobalScope(obj); + !obj->getParent() || js_IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 176a91182fbf..27d59fa02121 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,19 +1661,16 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); -extern JS_FRIEND_DATA(js::Class) js_CallClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - -namespace js { - /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ -static inline bool -IsCacheableNonGlobalScope(JSObject *obj) +inline bool +js_IsCacheableNonGlobalScope(JSObject *obj) { + extern JS_FRIEND_DATA(js::Class) js_CallClass; + extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1685,8 +1682,6 @@ IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } -} - /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d60557c24e51..a536724cbf29 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -49,7 +49,6 @@ #include "jsobj.h" #include "jsprobes.h" #include "jspropertytree.h" -#include "jsproxy.h" #include "jsscope.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -61,7 +60,6 @@ #include "jsscopeinlines.h" #include "jsstr.h" -#include "jsfuninlines.h" #include "jsgcinlines.h" #include "jsprobes.h" diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 7994f06e3550..54174b7b6b7d 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -371,8 +371,9 @@ TokenStream::peekChars(intN n, jschar *cp) return i == n; } +/* Finds the next EOL, but stops once 'max' chars past the start of the token have been scanned. */ jschar * -TokenStream::findEOL() +TokenStream::findWlineLimit(jschar *tokptr, int max) { TokenBuf tmpUserbuf = userbuf; jschar *tmpLinebase = linebase; @@ -380,12 +381,24 @@ TokenStream::findEOL() uintN tmpFlags = flags; uintN tmpLineno = lineno; + /* FIXME: this assertion is commented out due to bug 635144. */ + /*JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit);*/ + userbuf.ptr = tokptr; /* Start scanning at tokptr. */ + + jschar *wlinelimit; + jschar *wlinelimitmax = tokptr + max + 1; while (true) { - int32 c = getChar(); - if (c == '\n' || c == EOF) + if (userbuf.ptr > wlinelimitmax) { + wlinelimit = wlinelimitmax; break; + } + jschar* next = userbuf.ptr; + int32 c = getChar(); + if (c == '\n' || c == EOF) { + wlinelimit = next; + break; + } } - jschar *linelimit = userbuf.ptr; /* Need to restore everything changed by getChar(). */ userbuf = tmpUserbuf; @@ -394,7 +407,7 @@ TokenStream::findEOL() flags = tmpFlags; lineno = tmpLineno; - return linelimit; + return wlinelimit; } bool @@ -403,10 +416,15 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro { JSErrorReport report; char *message; - size_t linelength; - jschar *linechars; - jschar *linelimit; - char *linebytes; + + /* "wline" is short for "window into line", because we might not show it all. */ + size_t wlinelength; + jschar *wlinechars; + jschar *wlinelimit; + jschar *wlinebase; + jschar *tokptr; + char *wlinebytes; + bool warning; JSBool ok; TokenPos *tp; @@ -426,8 +444,8 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro report.flags = flags; report.errorNumber = errorNumber; message = NULL; - linechars = NULL; - linebytes = NULL; + wlinechars = NULL; + wlinebytes = NULL; MUST_FLOW_THROUGH("out"); ok = js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, @@ -451,29 +469,49 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro } report.lineno = lineno; - linelimit = findEOL(); - linelength = linelimit - linebase; - - linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar)); - if (!linechars) { - warning = false; - goto out; - } - memcpy(linechars, linebase, linelength * sizeof(jschar)); - linechars[linelength] = 0; - linebytes = js_DeflateString(cx, linechars, linelength); - if (!linebytes) { - warning = false; - goto out; - } - report.linebuf = linebytes; /* the offending source line, without final \n */ - index = (tp->begin.lineno == tp->end.lineno) - ? tp->begin.index /* the column number of the start of the bad token */ - : 0; + ? tp->begin.index /* the column number of the start of the bad token */ + : 0; /* the bad token didn't start on this line; don't give a column */ + tokptr = linebase + index; + + /* + * We show only a portion of the line around the erroneous token -- WINDOW + * chars before and after the first char in the token. This is because + * lines can be very long and printing the whole line is (a) not that + * helpful, and (b) can waste a lot of memory. See bug 634444. + */ + static const size_t WINDOW = 100; + + /* Truncate at the front if necessary. */ + if (linebase + WINDOW < tokptr) { + wlinebase = tokptr - WINDOW; + size_t nTrunc = wlinebase - linebase; + index -= nTrunc; + } else { + wlinebase = linebase; + } + + /* Find EOL, or truncate at the back if necessary. */ + wlinelimit = findWlineLimit(tokptr, WINDOW); + + wlinelength = wlinelimit - wlinebase; + JS_ASSERT(wlinelength <= WINDOW * 2 + 1); + wlinechars = (jschar *)cx->malloc((wlinelength + 1) * sizeof(jschar)); + if (!wlinechars) { + warning = false; + goto out; + } + memcpy(wlinechars, wlinebase, wlinelength * sizeof(jschar)); + wlinechars[wlinelength] = 0; + wlinebytes = js_DeflateString(cx, wlinechars, wlinelength); + if (!wlinebytes) { + warning = false; + goto out; + } + report.linebuf = wlinebytes; /* some or all of the offending source line, without final \n */ report.tokenptr = report.linebuf + index; - report.uclinebuf = linechars; + report.uclinebuf = wlinechars; report.uctokenptr = report.uclinebuf + index; /* @@ -530,10 +568,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro (*onError)(cx, message, &report); out: - if (linebytes) - cx->free(linebytes); - if (linechars) - cx->free(linechars); + if (wlinebytes) + cx->free(wlinebytes); + if (wlinechars) + cx->free(wlinechars); if (message) cx->free(message); if (report.ucmessage) @@ -772,6 +810,7 @@ TokenStream::newToken(ptrdiff_t adjust) cursor = (cursor + 1) & ntokensMask; Token *tp = &tokens[cursor]; tp->ptr = userbuf.ptr + adjust; + JS_ASSERT(tp->ptr >= linebase); tp->pos.begin.index = tp->ptr - linebase; tp->pos.begin.lineno = tp->pos.end.lineno = lineno; return tp; @@ -800,6 +839,7 @@ TokenStream::getTokenInternal() Token *tp; JSAtom *atom; bool hadUnicodeEscape; + int adjust; #if JS_HAS_XML_SUPPORT JSBool inTarget; size_t targetLength; @@ -857,7 +897,6 @@ TokenStream::getTokenInternal() c = getChar(); } while (JS_ISXMLSPACE(c)); ungetChar(c); - tp->pos.end.lineno = lineno; tt = TOK_XMLSPACE; goto out; } @@ -976,22 +1015,28 @@ TokenStream::getTokenInternal() retry: /* - * This gets the next non-space char and starts the token. + * This gets the next non-space char and starts the token. adjust is set + * to -1 because we'll probably scan the first char of the upcoming token + * while chewing up the whitespace. */ + adjust = -1; do { c = getChar(); if (c == '\n') { flags &= ~TSF_DIRTYLINE; - if (flags & TSF_NEWLINES) + if (flags & TSF_NEWLINES) { + adjust = 0; /* early break means we didn't scan an extra char */ break; + } } } while (ScanAsSpace((jschar)c)); - tp = newToken(-1); if (c == EOF) { + tp = newToken(0); /* no -1 here because userbuf.ptr isn't incremented for EOF */ tt = TOK_EOF; goto out; } + tp = newToken(adjust); /* * Look for an identifier. diff --git a/js/src/jsscan.h b/js/src/jsscan.h index b65219e7d947..7157124e52cc 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -476,7 +476,7 @@ class TokenStream bool matchUnicodeEscapeIdent(int32 *c); JSBool peekChars(intN n, jschar *cp); JSBool getXMLEntity(); - jschar *findEOL(); + jschar *findWlineLimit(jschar *tokptr, int max); JSBool matchChar(int32 expect) { int32 c = getChar(); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6d540c5e461a..79fddf099902 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!IsCacheableNonGlobalScope(child)) { + if (!js_IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15183,7 +15183,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!IsCacheableNonGlobalScope(obj)) + if (!js_IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 2cbbd259fe3a..089cfa4a3c1c 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1258,20 +1258,6 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) bool nativeDead = ic.fastGuardedNative && (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative)); - /* - * There are three conditions where we need to relink: - * (1) purgeAll is true. - * (2) The native is dead, since it always has a stub. - * (3) The fastFun is dead *and* there is a closure stub. - * - * Note although both objects can be non-NULL, there can only be one - * of [closure, native] stub per call IC. - */ - if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) { - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.hit = false; - } - if (fastFunDead) { repatcher.repatch(ic.funGuard, NULL); ic.releasePool(CallICInfo::Pool_ClosureStub); @@ -1290,6 +1276,16 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); repatcher.relink(oolJump, icCall); } + + /* + * Only relink the fast-path if there are no connected stubs, or we're + * trying to disconnect all stubs. Otherwise, we're just disabling an + * optimization that must take up space anyway (see bug 632729). + */ + if (purgeAll || !(ic.fastGuardedObject || ic.fastGuardedNative)) { + repatcher.relink(ic.funJump, ic.slowPathStart); + ic.hit = false; + } } if (purgeAll) { diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index aa82dabf6dbf..a954a7536819 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From ee8057dfaaa06f3470df2e8dba6154105227525d Mon Sep 17 00:00:00 2001 From: "Olli.Pettay@gmail.com" Date: Sun, 20 Feb 2011 00:20:14 -0800 Subject: [PATCH 30/42] Don't call GC or CC unexpectedly during shutdown (bug 635251, r/a=gal). (relanding in a CLOSED TREE) --- dom/base/nsJSEnvironment.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 2f026e56c986..e89f9e39f417 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3365,8 +3365,6 @@ nsJSContext::PokeGC() if (!sGCTimer) { NS_WARNING("Failed to create timer"); - - GarbageCollectNow(); return; } @@ -3403,8 +3401,6 @@ nsJSContext::PokeCC() if (!sCCTimer) { NS_WARNING("Failed to create timer"); - - CycleCollectNow(); return; } From 9ce341f3f0534b57babdfc4ea6bb47cb7d2259bb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 18 Feb 2011 18:29:20 -0800 Subject: [PATCH 31/42] Fix g-mail JIT crash (bug 635295, r=luke). (relanding in a CLOSED TREE) --- js/src/methodjit/MonoIC.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 089cfa4a3c1c..2cbbd259fe3a 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1258,6 +1258,20 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) bool nativeDead = ic.fastGuardedNative && (purgeAll || IsAboutToBeFinalized(cx, ic.fastGuardedNative)); + /* + * There are three conditions where we need to relink: + * (1) purgeAll is true. + * (2) The native is dead, since it always has a stub. + * (3) The fastFun is dead *and* there is a closure stub. + * + * Note although both objects can be non-NULL, there can only be one + * of [closure, native] stub per call IC. + */ + if (purgeAll || nativeDead || (fastFunDead && ic.hasJsFunCheck)) { + repatcher.relink(ic.funJump, ic.slowPathStart); + ic.hit = false; + } + if (fastFunDead) { repatcher.repatch(ic.funGuard, NULL); ic.releasePool(CallICInfo::Pool_ClosureStub); @@ -1276,16 +1290,6 @@ JITScript::sweepCallICs(JSContext *cx, bool purgeAll) JSC::CodeLocationLabel icCall = ic.slowPathStart.labelAtOffset(ic.icCallOffset); repatcher.relink(oolJump, icCall); } - - /* - * Only relink the fast-path if there are no connected stubs, or we're - * trying to disconnect all stubs. Otherwise, we're just disabling an - * optimization that must take up space anyway (see bug 632729). - */ - if (purgeAll || !(ic.fastGuardedObject || ic.fastGuardedNative)) { - repatcher.relink(ic.funJump, ic.slowPathStart); - ic.hit = false; - } } if (purgeAll) { From 8c0fc66bbc6a6c03bda37f07a5fb3637fe7d9fda Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 18 Feb 2011 19:30:18 -0800 Subject: [PATCH 32/42] Bug 634444 - Back out for regressions (see the bug for their details). Either r=njn or r=me depending how you want to consider whose idea/plan it was (relanding in a CLOSED TREE) --- js/src/jsscan.cpp | 120 ++++++++++++++-------------------------------- js/src/jsscan.h | 2 +- 2 files changed, 38 insertions(+), 84 deletions(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 54174b7b6b7d..028f4f675b42 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -371,9 +371,8 @@ TokenStream::peekChars(intN n, jschar *cp) return i == n; } -/* Finds the next EOL, but stops once 'max' chars past the start of the token have been scanned. */ jschar * -TokenStream::findWlineLimit(jschar *tokptr, int max) +TokenStream::findEOL() { TokenBuf tmpUserbuf = userbuf; jschar *tmpLinebase = linebase; @@ -381,24 +380,12 @@ TokenStream::findWlineLimit(jschar *tokptr, int max) uintN tmpFlags = flags; uintN tmpLineno = lineno; - /* FIXME: this assertion is commented out due to bug 635144. */ - /*JS_ASSERT(userbuf.base <= tokptr && tokptr <= userbuf.limit);*/ - userbuf.ptr = tokptr; /* Start scanning at tokptr. */ - - jschar *wlinelimit; - jschar *wlinelimitmax = tokptr + max + 1; while (true) { - if (userbuf.ptr > wlinelimitmax) { - wlinelimit = wlinelimitmax; - break; - } - jschar* next = userbuf.ptr; int32 c = getChar(); - if (c == '\n' || c == EOF) { - wlinelimit = next; + if (c == '\n' || c == EOF) break; - } } + jschar *linelimit = userbuf.ptr; /* Need to restore everything changed by getChar(). */ userbuf = tmpUserbuf; @@ -407,7 +394,7 @@ TokenStream::findWlineLimit(jschar *tokptr, int max) flags = tmpFlags; lineno = tmpLineno; - return wlinelimit; + return linelimit; } bool @@ -416,15 +403,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro { JSErrorReport report; char *message; - - /* "wline" is short for "window into line", because we might not show it all. */ - size_t wlinelength; - jschar *wlinechars; - jschar *wlinelimit; - jschar *wlinebase; - jschar *tokptr; - char *wlinebytes; - + size_t linelength; + jschar *linechars; + jschar *linelimit; + char *linebytes; bool warning; JSBool ok; TokenPos *tp; @@ -444,8 +426,8 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro report.flags = flags; report.errorNumber = errorNumber; message = NULL; - wlinechars = NULL; - wlinebytes = NULL; + linechars = NULL; + linebytes = NULL; MUST_FLOW_THROUGH("out"); ok = js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, @@ -469,49 +451,29 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro } report.lineno = lineno; + linelimit = findEOL(); + linelength = linelimit - linebase; + + linechars = (jschar *)cx->malloc((linelength + 1) * sizeof(jschar)); + if (!linechars) { + warning = false; + goto out; + } + memcpy(linechars, linebase, linelength * sizeof(jschar)); + linechars[linelength] = 0; + linebytes = js_DeflateString(cx, linechars, linelength); + if (!linebytes) { + warning = false; + goto out; + } + report.linebuf = linebytes; /* the offending source line, without final \n */ + index = (tp->begin.lineno == tp->end.lineno) - ? tp->begin.index /* the column number of the start of the bad token */ - : 0; /* the bad token didn't start on this line; don't give a column */ - tokptr = linebase + index; - - /* - * We show only a portion of the line around the erroneous token -- WINDOW - * chars before and after the first char in the token. This is because - * lines can be very long and printing the whole line is (a) not that - * helpful, and (b) can waste a lot of memory. See bug 634444. - */ - static const size_t WINDOW = 100; - - /* Truncate at the front if necessary. */ - if (linebase + WINDOW < tokptr) { - wlinebase = tokptr - WINDOW; - size_t nTrunc = wlinebase - linebase; - index -= nTrunc; - } else { - wlinebase = linebase; - } - - /* Find EOL, or truncate at the back if necessary. */ - wlinelimit = findWlineLimit(tokptr, WINDOW); - - wlinelength = wlinelimit - wlinebase; - JS_ASSERT(wlinelength <= WINDOW * 2 + 1); - wlinechars = (jschar *)cx->malloc((wlinelength + 1) * sizeof(jschar)); - if (!wlinechars) { - warning = false; - goto out; - } - memcpy(wlinechars, wlinebase, wlinelength * sizeof(jschar)); - wlinechars[wlinelength] = 0; - wlinebytes = js_DeflateString(cx, wlinechars, wlinelength); - if (!wlinebytes) { - warning = false; - goto out; - } - report.linebuf = wlinebytes; /* some or all of the offending source line, without final \n */ + ? tp->begin.index /* the column number of the start of the bad token */ + : 0; report.tokenptr = report.linebuf + index; - report.uclinebuf = wlinechars; + report.uclinebuf = linechars; report.uctokenptr = report.uclinebuf + index; /* @@ -568,10 +530,10 @@ TokenStream::reportCompileErrorNumberVA(JSParseNode *pn, uintN flags, uintN erro (*onError)(cx, message, &report); out: - if (wlinebytes) - cx->free(wlinebytes); - if (wlinechars) - cx->free(wlinechars); + if (linebytes) + cx->free(linebytes); + if (linechars) + cx->free(linechars); if (message) cx->free(message); if (report.ucmessage) @@ -810,7 +772,6 @@ TokenStream::newToken(ptrdiff_t adjust) cursor = (cursor + 1) & ntokensMask; Token *tp = &tokens[cursor]; tp->ptr = userbuf.ptr + adjust; - JS_ASSERT(tp->ptr >= linebase); tp->pos.begin.index = tp->ptr - linebase; tp->pos.begin.lineno = tp->pos.end.lineno = lineno; return tp; @@ -839,7 +800,6 @@ TokenStream::getTokenInternal() Token *tp; JSAtom *atom; bool hadUnicodeEscape; - int adjust; #if JS_HAS_XML_SUPPORT JSBool inTarget; size_t targetLength; @@ -1015,28 +975,22 @@ TokenStream::getTokenInternal() retry: /* - * This gets the next non-space char and starts the token. adjust is set - * to -1 because we'll probably scan the first char of the upcoming token - * while chewing up the whitespace. + * This gets the next non-space char and starts the token. */ - adjust = -1; do { c = getChar(); if (c == '\n') { flags &= ~TSF_DIRTYLINE; - if (flags & TSF_NEWLINES) { - adjust = 0; /* early break means we didn't scan an extra char */ + if (flags & TSF_NEWLINES) break; - } } } while (ScanAsSpace((jschar)c)); + tp = newToken(-1); if (c == EOF) { - tp = newToken(0); /* no -1 here because userbuf.ptr isn't incremented for EOF */ tt = TOK_EOF; goto out; } - tp = newToken(adjust); /* * Look for an identifier. diff --git a/js/src/jsscan.h b/js/src/jsscan.h index 7157124e52cc..b65219e7d947 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -476,7 +476,7 @@ class TokenStream bool matchUnicodeEscapeIdent(int32 *c); JSBool peekChars(intN n, jschar *cp); JSBool getXMLEntity(); - jschar *findWlineLimit(jschar *tokptr, int max); + jschar *findEOL(); JSBool matchChar(int32 expect) { int32 c = getChar(); From 04bbcc7b36786b50f995dd6cf802bec65a4d71c5 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Feb 2011 17:52:55 -0800 Subject: [PATCH 33/42] Unqualified function invocation doesn't use the global object the property was gotten from as |this| (bug 634590, r=brendan). (relanding in a CLOSED TREE) --- js/src/jit-test/tests/basic/testBug634590.js | 7 ++ js/src/jsfuninlines.h | 26 ++++++ js/src/jsinterp.cpp | 84 +++++++++++--------- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 11 ++- js/src/jsobj.h | 13 ++- js/src/jsobjinlines.h | 2 + js/src/jstracer.cpp | 4 +- js/src/methodjit/PolyIC.cpp | 4 +- 9 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug634590.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js new file mode 100644 index 000000000000..da47ed0090df --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -0,0 +1,7 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), + "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 4cbb744b236e..d951e93c4625 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -49,4 +49,30 @@ JSFunction::inStrictMode() const return script()->strictModeCode; } +namespace js { + +static inline bool +IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) +{ + /* + * Look past any wrappers. If the callee is a strict function it is always + * safe because we won't do 'this' coercion in strict mode. Otherwise the + * callee is only safe to transform into the lazy 'this' token (undefined) + * if it is in the current scope. Without this restriction, lazy 'this' + * coercion would pick up the wrong global in the other scope. + */ + if (callee->isProxy()) { + callee = callee->unwrap(); + if (!callee->isFunction()) + return true; // treat any non-wrapped-function proxy as strict + + JSFunction *fun = callee->getFunctionPrivate(); + if (fun->isInterpreted() && fun->inStrictMode()) + return true; + } + return callee->getGlobal() == cx->fp()->scopeChain().getGlobal(); +} + +} + #endif /* jsfuninlines_h___ */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index fde50aec9f10..eab8a4ef6bbf 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2178,6 +2178,30 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } +static inline bool +SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +{ + if (!funval.isObject() || + ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && + IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { + /* + * We can avoid computing 'this' eagerly and push the implicit 'this' + * value (undefined), as long the scope is cachable and we are not + * crossing into another scope (in which case lazy calculation of 'this' + * would pick up the new and incorrect scope). 'strict' functions are an + * exception. We don't want to eagerly calculate 'this' for them even if + * the callee is in a different scope. + */ + *vp = UndefinedValue(); + return true; + } + + if (!(obj = obj->thisObject(cx))) + return false; + *vp = ObjectValue(*obj); + return true; +} + namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2203,8 +2227,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4792,26 +4816,13 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj) \ - JS_BEGIN_MACRO \ - Class *clasp; \ - JSObject *thisp = obj; \ - if (!thisp->getParent() || \ - (clasp = thisp->getClass()) == &js_CallClass || \ - clasp == &js_BlockClass || \ - clasp == &js_DeclEnvClass) { \ - /* Push the ImplicitThisValue for the Environment Record */ \ - /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ - /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ - /* (Function Calls). */ \ - PUSH_UNDEFINED(); \ - } else { \ - thisp = thisp->thisObject(cx); \ - if (!thisp) \ - goto error; \ - PUSH_OBJECT(*thisp); \ - } \ - JS_END_MACRO +#define SLOW_PUSH_THISV(cx, obj, funval) \ + JS_BEGIN_MACRO \ + Value v; \ + if (!SlowThis(cx, obj, funval, &v)) \ + goto error; \ + PUSH_COPY(v); \ + JS_END_MACRO \ BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4841,20 +4852,17 @@ BEGIN_CASE(JSOP_CALLNAME) PUSH_COPY(rval); } - /* - * Push results, the same as below, but with a prop$ hit there - * is no need to test for the unusual and uncacheable case where - * the caller determines |this|. - */ -#if DEBUG - Class *clasp; - JS_ASSERT(!obj->getParent() || - (clasp = obj->getClass()) == &js_CallClass || - clasp == &js_BlockClass || - clasp == &js_DeclEnvClass); -#endif - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_UNDEFINED(); + JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { + if (regs.sp[-1].isObject() && + !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { + if (!(obj = obj->thisObject(cx))) + return false; + PUSH_OBJECT(*obj); + } else { + PUSH_UNDEFINED(); + } + } len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4892,7 +4900,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6395,7 +6403,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c55c868b3feb..c976cdea8da1 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b2925942c7cc..173921e21ba4 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4445,6 +4445,11 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } +namespace js { + + +} + /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5052,7 +5057,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? js_IsCacheableNonGlobalScope(obj) + ? IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5162,11 +5167,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because js_IsCacheableNonGlobalScope + * The test order here matters because IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || js_IsCacheableNonGlobalScope(obj); + !obj->getParent() || IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 27d59fa02121..176a91182fbf 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,16 +1661,19 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); +extern JS_FRIEND_DATA(js::Class) js_CallClass; +extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; + +namespace js { + /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ -inline bool -js_IsCacheableNonGlobalScope(JSObject *obj) +static inline bool +IsCacheableNonGlobalScope(JSObject *obj) { - extern JS_FRIEND_DATA(js::Class) js_CallClass; - extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1682,6 +1685,8 @@ js_IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } +} + /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index a536724cbf29..d60557c24e51 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -49,6 +49,7 @@ #include "jsobj.h" #include "jsprobes.h" #include "jspropertytree.h" +#include "jsproxy.h" #include "jsscope.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -60,6 +61,7 @@ #include "jsscopeinlines.h" #include "jsstr.h" +#include "jsfuninlines.h" #include "jsgcinlines.h" #include "jsprobes.h" diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 79fddf099902..6d540c5e461a 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!js_IsCacheableNonGlobalScope(child)) { + if (!IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15183,7 +15183,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!js_IsCacheableNonGlobalScope(obj)) + if (!IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index a954a7536819..aa82dabf6dbf 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!js_IsCacheableNonGlobalScope(tobj)) + if (!IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From a2f58bb615a6185f10c1da617eff47fbe0cc3cc9 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sat, 19 Feb 2011 22:59:49 -0800 Subject: [PATCH 34/42] When idle the GC holds on to unused chunks indefinitely (bug 631733, r=brendan, a=blocker). (relanding in a CLOSED TREE) --- dom/base/nsJSEnvironment.cpp | 6 ++++++ js/src/jsapi.cpp | 2 ++ js/src/jsapi.h | 5 ++++- js/src/jscntxt.h | 1 + js/src/jsgc.cpp | 22 ++++++++++------------ js/src/jsgc.h | 2 -- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index e89f9e39f417..f19e7c9eb780 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -3480,6 +3480,12 @@ DOMGCCallback(JSContext *cx, JSGCStatus status) nsJSContext::PokeCC(); } } + + // If we didn't end up scheduling a GC, and there are unused + // chunks waiting to expire, make sure we will GC again soon. + if (!sGCTimer && JS_GetGCParameter(cx->runtime, JSGC_UNUSED_CHUNKS) > 0) { + nsJSContext::PokeGC(); + } } JSBool result = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index ca3acefdaf18..629ef34a7d58 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2644,6 +2644,8 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) return rt->gcBytes; case JSGC_MODE: return uint32(rt->gcMode); + case JSGC_UNUSED_CHUNKS: + return uint32(rt->gcChunksWaitingToExpire); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index efa1e556f9c1..a598b04ad233 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1785,7 +1785,10 @@ typedef enum JSGCParamKey { JSGC_MAX_CODE_CACHE_BYTES = 6, /* Select GC mode. */ - JSGC_MODE = 7 + JSGC_MODE = 7, + + /* Number of GC chunks waiting to expire. */ + JSGC_UNUSED_CHUNKS = 8 } JSGCParamKey; typedef enum JSGCMode { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 17a1567dc6ff..4ff77fa42f81 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1047,6 +1047,7 @@ struct JSRuntime { size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; + size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 9ef631349d79..18c962cc36c8 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -360,14 +360,6 @@ Chunk::releaseArena(Arena *arena) info.age = 0; } -bool -Chunk::expire() -{ - if (!unused()) - return false; - return info.age++ > MaxAge; -} - JSRuntime * Chunk::getRuntime() { @@ -456,16 +448,22 @@ PickChunk(JSRuntime *rt) static void ExpireGCChunks(JSRuntime *rt) { + static const size_t MaxAge = 3; + /* Remove unused chunks. */ AutoLockGC lock(rt); + rt->gcChunksWaitingToExpire = 0; for (GCChunkSet::Enum e(rt->gcChunkSet); !e.empty(); e.popFront()) { Chunk *chunk = e.front(); JS_ASSERT(chunk->info.runtime == rt); - if (chunk->expire()) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; + if (chunk->unused()) { + if (chunk->info.age++ > MaxAge) { + e.removeFront(); + ReleaseGCChunk(rt, chunk); + continue; + } + rt->gcChunksWaitingToExpire++; } } } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6379023ac6f6..bb666e617e55 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -340,7 +340,6 @@ struct Chunk { sizeof(MarkingDelay); static const size_t ArenasPerChunk = (GC_CHUNK_SIZE - sizeof(ChunkInfo)) / BytesPerArena; - static const size_t MaxAge = 3; Arena arenas[ArenasPerChunk]; ArenaBitmap bitmaps[ArenasPerChunk]; @@ -362,7 +361,6 @@ struct Chunk { void releaseArena(Arena *a); JSRuntime *getRuntime(); - bool expire(); }; JS_STATIC_ASSERT(sizeof(Chunk) <= GC_CHUNK_SIZE); JS_STATIC_ASSERT(sizeof(Chunk) + Chunk::BytesPerArena > GC_CHUNK_SIZE); From 79b4ee784723fa06ed755e7207e9bf7119865591 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sat, 19 Feb 2011 23:13:56 -0800 Subject: [PATCH 35/42] Fix unqualified function invocation etc., part deux (635582, r=gal). (relanding in a CLOSED TREE) --- js/src/jit-test/tests/basic/testBug634590.js | 7 ++++- js/src/jit-test/tests/basic/testBug634590b.js | 19 +++++++++++++ js/src/jit-test/tests/basic/testBug634590c.js | 20 ++++++++++++++ js/src/jit-test/tests/basic/testBug634590d.js | 15 +++++++++++ js/src/jsfuninlines.h | 15 +++++++---- js/src/jsinterp.cpp | 27 +++++++------------ 6 files changed, 80 insertions(+), 23 deletions(-) create mode 100644 js/src/jit-test/tests/basic/testBug634590b.js create mode 100644 js/src/jit-test/tests/basic/testBug634590c.js create mode 100644 js/src/jit-test/tests/basic/testBug634590d.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js index da47ed0090df..5787c61c9d76 100644 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -3,5 +3,10 @@ var sb = evalcx(''); sb.name = "inner"; sb.parent = this; function f() { return this.name; } -assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), +assertEq(evalcx('this.f = parent.f;\n' + + 'var s = "";\n' + + 'for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + 's', + sb), "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jit-test/tests/basic/testBug634590b.js b/js/src/jit-test/tests/basic/testBug634590b.js new file mode 100644 index 000000000000..42531a82958d --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590b.js @@ -0,0 +1,19 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +f.notMuchTodo = '42'; +assertEq(evalcx('{\n' + + ' let f = parent.f;\n' + + ' let name = "block";\n' + + ' (function () {\n' + + ' eval(f.notMuchTodo);\n' + // reify Block + ' var s = "";\n' + + ' for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + ' return s;\n' + + ' })();\n' + + '}', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590c.js b/js/src/jit-test/tests/basic/testBug634590c.js new file mode 100644 index 000000000000..1ae2542def1e --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590c.js @@ -0,0 +1,20 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +function f() { return this.name; } +f.notMuchTodo = '42'; +assertEq(evalcx('(function () {\n' + + ' arguments = null;\n' + // force heavyweight + ' var f = parent.f;\n' + + ' var name = "call";\n' + + ' return (function () {\n' + + ' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME + ' var s = "";\n' + + ' for (i = 0; i < 10; ++i)\n' + + ' s += f();\n' + + ' return s;\n' + + ' })();\n' + + '})()', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590d.js b/js/src/jit-test/tests/basic/testBug634590d.js new file mode 100644 index 000000000000..63612e2664f3 --- /dev/null +++ b/js/src/jit-test/tests/basic/testBug634590d.js @@ -0,0 +1,15 @@ +this.name = "outer"; +var sb = evalcx(''); +sb.name = "inner"; +sb.parent = this; +this.f = function name(outer) { + if (outer) return name(false); + return this.name; +} +assertEq(evalcx('this.f = parent.f;\n' + + 'var s = "";\n' + + 'for (i = 0; i < 10; ++i)\n' + + ' s += f(true);\n' + + 's', + sb), + "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index d951e93c4625..331e45724650 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -55,11 +55,16 @@ static inline bool IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) { /* - * Look past any wrappers. If the callee is a strict function it is always - * safe because we won't do 'this' coercion in strict mode. Otherwise the - * callee is only safe to transform into the lazy 'this' token (undefined) - * if it is in the current scope. Without this restriction, lazy 'this' - * coercion would pick up the wrong global in the other scope. + * Look past any function wrappers. If the callee is a wrapped strict-mode + * function, lazy 'this' coercion is vacuously safe because strict-mode + * functions don't coerce 'this' at all. Otherwise, the callee is safe to + * transform into the lazy 'this' cookie (the undefined value) only if it + * is in the current scope. + * + * Without this restriction, lazy 'this' coercion would pick up the "wrong" + * global at the end of the callee function object's scope, rather than the + * "right" (backward compatible since 1995) global that was the "Reference + * base object" in the callee expression. */ if (callee->isProxy()) { callee = callee->unwrap(); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index eab8a4ef6bbf..bd00499fb77d 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2179,11 +2179,12 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) } static inline bool -SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) { if (!funval.isObject() || - ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && - IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { + (obj->isGlobal() + ? IsSafeForLazyThisCoercion(cx, &funval.toObject()) + : IsCacheableNonGlobalScope(obj))) { /* * We can avoid computing 'this' eagerly and push the implicit 'this' * value (undefined), as long the scope is cachable and we are not @@ -4816,10 +4817,10 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj, funval) \ +#define PUSH_THISV(cx, obj, funval) \ JS_BEGIN_MACRO \ Value v; \ - if (!SlowThis(cx, obj, funval, &v)) \ + if (!ComputeThis(cx, obj, funval, &v)) \ goto error; \ PUSH_COPY(v); \ JS_END_MACRO \ @@ -4853,16 +4854,8 @@ BEGIN_CASE(JSOP_CALLNAME) } JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { - if (regs.sp[-1].isObject() && - !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { - if (!(obj = obj->thisObject(cx))) - return false; - PUSH_OBJECT(*obj); - } else { - PUSH_UNDEFINED(); - } - } + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) + PUSH_THISV(cx, obj, regs.sp[-1]); len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4900,7 +4893,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj, rval); + PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6403,7 +6396,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj, rval); + PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) From 4acdd015cec8fafc11893dfbedb7a3cbf93fc101 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sun, 20 Feb 2011 15:07:25 -0800 Subject: [PATCH 36/42] Improve E4X syntax error reporting. r=nnethercote, a=shaver. (relanding in a CLOSED TREE) --- js/src/jsscan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 028f4f675b42..7994f06e3550 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -857,6 +857,7 @@ TokenStream::getTokenInternal() c = getChar(); } while (JS_ISXMLSPACE(c)); ungetChar(c); + tp->pos.end.lineno = lineno; tt = TOK_XMLSPACE; goto out; } From 097210b32e9a633b13dc7dfb7cb03b2b5dec784a Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sun, 20 Feb 2011 21:38:29 -0800 Subject: [PATCH 37/42] Back out a8574322a3ba from a CLOSED TREE, for its predecessor is orangetastic on one platform. --- js/src/jit-test/tests/basic/testBug634590.js | 7 +---- js/src/jit-test/tests/basic/testBug634590b.js | 19 ------------- js/src/jit-test/tests/basic/testBug634590c.js | 20 -------------- js/src/jit-test/tests/basic/testBug634590d.js | 15 ----------- js/src/jsfuninlines.h | 15 ++++------- js/src/jsinterp.cpp | 27 ++++++++++++------- 6 files changed, 23 insertions(+), 80 deletions(-) delete mode 100644 js/src/jit-test/tests/basic/testBug634590b.js delete mode 100644 js/src/jit-test/tests/basic/testBug634590c.js delete mode 100644 js/src/jit-test/tests/basic/testBug634590d.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js index 5787c61c9d76..da47ed0090df 100644 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ b/js/src/jit-test/tests/basic/testBug634590.js @@ -3,10 +3,5 @@ var sb = evalcx(''); sb.name = "inner"; sb.parent = this; function f() { return this.name; } -assertEq(evalcx('this.f = parent.f;\n' + - 'var s = "";\n' + - 'for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - 's', - sb), +assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jit-test/tests/basic/testBug634590b.js b/js/src/jit-test/tests/basic/testBug634590b.js deleted file mode 100644 index 42531a82958d..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590b.js +++ /dev/null @@ -1,19 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -f.notMuchTodo = '42'; -assertEq(evalcx('{\n' + - ' let f = parent.f;\n' + - ' let name = "block";\n' + - ' (function () {\n' + - ' eval(f.notMuchTodo);\n' + // reify Block - ' var s = "";\n' + - ' for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - ' return s;\n' + - ' })();\n' + - '}', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590c.js b/js/src/jit-test/tests/basic/testBug634590c.js deleted file mode 100644 index 1ae2542def1e..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590c.js +++ /dev/null @@ -1,20 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -f.notMuchTodo = '42'; -assertEq(evalcx('(function () {\n' + - ' arguments = null;\n' + // force heavyweight - ' var f = parent.f;\n' + - ' var name = "call";\n' + - ' return (function () {\n' + - ' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME - ' var s = "";\n' + - ' for (i = 0; i < 10; ++i)\n' + - ' s += f();\n' + - ' return s;\n' + - ' })();\n' + - '})()', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jit-test/tests/basic/testBug634590d.js b/js/src/jit-test/tests/basic/testBug634590d.js deleted file mode 100644 index 63612e2664f3..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590d.js +++ /dev/null @@ -1,15 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -this.f = function name(outer) { - if (outer) return name(false); - return this.name; -} -assertEq(evalcx('this.f = parent.f;\n' + - 'var s = "";\n' + - 'for (i = 0; i < 10; ++i)\n' + - ' s += f(true);\n' + - 's', - sb), - "outerouterouterouterouterouterouterouterouterouter"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 331e45724650..d951e93c4625 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -55,16 +55,11 @@ static inline bool IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) { /* - * Look past any function wrappers. If the callee is a wrapped strict-mode - * function, lazy 'this' coercion is vacuously safe because strict-mode - * functions don't coerce 'this' at all. Otherwise, the callee is safe to - * transform into the lazy 'this' cookie (the undefined value) only if it - * is in the current scope. - * - * Without this restriction, lazy 'this' coercion would pick up the "wrong" - * global at the end of the callee function object's scope, rather than the - * "right" (backward compatible since 1995) global that was the "Reference - * base object" in the callee expression. + * Look past any wrappers. If the callee is a strict function it is always + * safe because we won't do 'this' coercion in strict mode. Otherwise the + * callee is only safe to transform into the lazy 'this' token (undefined) + * if it is in the current scope. Without this restriction, lazy 'this' + * coercion would pick up the wrong global in the other scope. */ if (callee->isProxy()) { callee = callee->unwrap(); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index bd00499fb77d..eab8a4ef6bbf 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2179,12 +2179,11 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) } static inline bool -ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) +SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) { if (!funval.isObject() || - (obj->isGlobal() - ? IsSafeForLazyThisCoercion(cx, &funval.toObject()) - : IsCacheableNonGlobalScope(obj))) { + ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && + IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { /* * We can avoid computing 'this' eagerly and push the implicit 'this' * value (undefined), as long the scope is cachable and we are not @@ -4817,10 +4816,10 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define PUSH_THISV(cx, obj, funval) \ +#define SLOW_PUSH_THISV(cx, obj, funval) \ JS_BEGIN_MACRO \ Value v; \ - if (!ComputeThis(cx, obj, funval, &v)) \ + if (!SlowThis(cx, obj, funval, &v)) \ goto error; \ PUSH_COPY(v); \ JS_END_MACRO \ @@ -4854,8 +4853,16 @@ BEGIN_CASE(JSOP_CALLNAME) } JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_THISV(cx, obj, regs.sp[-1]); + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { + if (regs.sp[-1].isObject() && + !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { + if (!(obj = obj->thisObject(cx))) + return false; + PUSH_OBJECT(*obj); + } else { + PUSH_UNDEFINED(); + } + } len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4893,7 +4900,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_NAME) @@ -6396,7 +6403,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj, rval); } END_CASE(JSOP_XMLNAME) From 70ed6d9fcd8d55c3bb0b1826691c7715c260cabb Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sun, 20 Feb 2011 21:40:06 -0800 Subject: [PATCH 38/42] Back out af1e10bea0f4 from a CLOSED TREE for being orangetastic on one platform. --- js/src/jit-test/tests/basic/testBug634590.js | 7 -- js/src/jsfuninlines.h | 26 ------ js/src/jsinterp.cpp | 84 +++++++++----------- js/src/jsinterpinlines.h | 2 +- js/src/jsobj.cpp | 11 +-- js/src/jsobj.h | 13 +-- js/src/jsobjinlines.h | 2 - js/src/jstracer.cpp | 4 +- js/src/methodjit/PolyIC.cpp | 4 +- 9 files changed, 50 insertions(+), 103 deletions(-) delete mode 100644 js/src/jit-test/tests/basic/testBug634590.js diff --git a/js/src/jit-test/tests/basic/testBug634590.js b/js/src/jit-test/tests/basic/testBug634590.js deleted file mode 100644 index da47ed0090df..000000000000 --- a/js/src/jit-test/tests/basic/testBug634590.js +++ /dev/null @@ -1,7 +0,0 @@ -this.name = "outer"; -var sb = evalcx(''); -sb.name = "inner"; -sb.parent = this; -function f() { return this.name; } -assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb), - "innerinnerinnerinnerinnerinnerinnerinnerinnerinner"); diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index d951e93c4625..4cbb744b236e 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -49,30 +49,4 @@ JSFunction::inStrictMode() const return script()->strictModeCode; } -namespace js { - -static inline bool -IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee) -{ - /* - * Look past any wrappers. If the callee is a strict function it is always - * safe because we won't do 'this' coercion in strict mode. Otherwise the - * callee is only safe to transform into the lazy 'this' token (undefined) - * if it is in the current scope. Without this restriction, lazy 'this' - * coercion would pick up the wrong global in the other scope. - */ - if (callee->isProxy()) { - callee = callee->unwrap(); - if (!callee->isFunction()) - return true; // treat any non-wrapped-function proxy as strict - - JSFunction *fun = callee->getFunctionPrivate(); - if (fun->isInterpreted() && fun->inStrictMode()) - return true; - } - return callee->getGlobal() == cx->fp()->scopeChain().getGlobal(); -} - -} - #endif /* jsfuninlines_h___ */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index eab8a4ef6bbf..fde50aec9f10 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2178,30 +2178,6 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp) return true; } -static inline bool -SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp) -{ - if (!funval.isObject() || - ((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) && - IsSafeForLazyThisCoercion(cx, &funval.toObject()))) { - /* - * We can avoid computing 'this' eagerly and push the implicit 'this' - * value (undefined), as long the scope is cachable and we are not - * crossing into another scope (in which case lazy calculation of 'this' - * would pick up the new and incorrect scope). 'strict' functions are an - * exception. We don't want to eagerly calculate 'this' for them even if - * the callee is in a different scope. - */ - *vp = UndefinedValue(); - return true; - } - - if (!(obj = obj->thisObject(cx))) - return false; - *vp = ObjectValue(*obj); - return true; -} - namespace js { JS_REQUIRES_STACK JS_NEVER_INLINE bool @@ -2227,8 +2203,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte * expect from looking at the code. (We do omit POPs after SETs; * unfortunate, but not worth fixing.) */ -# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ - if (JS_UNLIKELY(cx->logfp != NULL) && \ +# define LOG_OPCODE(OP) JS_BEGIN_MACRO \ + if (JS_UNLIKELY(cx->logfp != NULL) && \ (OP) == *regs.pc) \ js_LogOpcode(cx); \ JS_END_MACRO @@ -4816,13 +4792,26 @@ BEGIN_CASE(JSOP_SETCALL) } END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj, funval) \ - JS_BEGIN_MACRO \ - Value v; \ - if (!SlowThis(cx, obj, funval, &v)) \ - goto error; \ - PUSH_COPY(v); \ - JS_END_MACRO \ +#define SLOW_PUSH_THISV(cx, obj) \ + JS_BEGIN_MACRO \ + Class *clasp; \ + JSObject *thisp = obj; \ + if (!thisp->getParent() || \ + (clasp = thisp->getClass()) == &js_CallClass || \ + clasp == &js_BlockClass || \ + clasp == &js_DeclEnvClass) { \ + /* Push the ImplicitThisValue for the Environment Record */ \ + /* associated with obj. See ES5 sections 10.2.1.1.6 and */ \ + /* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \ + /* (Function Calls). */ \ + PUSH_UNDEFINED(); \ + } else { \ + thisp = thisp->thisObject(cx); \ + if (!thisp) \ + goto error; \ + PUSH_OBJECT(*thisp); \ + } \ + JS_END_MACRO BEGIN_CASE(JSOP_GETGNAME) BEGIN_CASE(JSOP_CALLGNAME) @@ -4852,17 +4841,20 @@ BEGIN_CASE(JSOP_CALLNAME) PUSH_COPY(rval); } - JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj)); - if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) { - if (regs.sp[-1].isObject() && - !IsSafeForLazyThisCoercion(cx, ®s.sp[-1].toObject())) { - if (!(obj = obj->thisObject(cx))) - return false; - PUSH_OBJECT(*obj); - } else { - PUSH_UNDEFINED(); - } - } + /* + * Push results, the same as below, but with a prop$ hit there + * is no need to test for the unusual and uncacheable case where + * the caller determines |this|. + */ +#if DEBUG + Class *clasp; + JS_ASSERT(!obj->getParent() || + (clasp = obj->getClass()) == &js_CallClass || + clasp == &js_BlockClass || + clasp == &js_DeclEnvClass); +#endif + if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) + PUSH_UNDEFINED(); len = JSOP_NAME_LENGTH; DO_NEXT_OP(len); } @@ -4900,7 +4892,7 @@ BEGIN_CASE(JSOP_CALLNAME) /* obj must be on the scope chain, thus not a function. */ if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) - SLOW_PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_NAME) @@ -6403,7 +6395,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; regs.sp[-1] = rval; if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj, rval); + SLOW_PUSH_THISV(cx, obj); } END_CASE(JSOP_XMLNAME) diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index c976cdea8da1..c55c868b3feb 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -479,7 +479,7 @@ JSStackFrame::callObj() const JS_ASSERT(hasCallObj()); JSObject *pobj = &scopeChain(); while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) { - JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith()); + JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } return *pobj; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 173921e21ba4..b2925942c7cc 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4445,11 +4445,6 @@ JSObject::freeSlot(JSContext *cx, uint32 slot) return false; } -namespace js { - - -} - /* JSBOXEDWORD_INT_MAX as a string */ #define JSBOXEDWORD_INT_MAX_STRING "1073741823" @@ -5057,7 +5052,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, parent = obj->getParent(); for (scopeIndex = 0; parent - ? IsCacheableNonGlobalScope(obj) + ? js_IsCacheableNonGlobalScope(obj) : !obj->getOps()->lookupProperty; ++scopeIndex) { protoIndex = @@ -5167,11 +5162,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) * farther checks or lookups. For details see the JSOP_BINDNAME case of * js_Interpret. * - * The test order here matters because IsCacheableNonGlobalScope + * The test order here matters because js_IsCacheableNonGlobalScope * must not be passed a global object (i.e. one with null parent). */ for (int scopeIndex = 0; - !obj->getParent() || IsCacheableNonGlobalScope(obj); + !obj->getParent() || js_IsCacheableNonGlobalScope(obj); scopeIndex++) { JSObject *pobj; JSProperty *prop; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 176a91182fbf..27d59fa02121 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1661,19 +1661,16 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp, JSProperty **propp); -extern JS_FRIEND_DATA(js::Class) js_CallClass; -extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; - -namespace js { - /* * We cache name lookup results only for the global object or for native * non-global objects without prototype or with prototype that never mutates, * see bug 462734 and bug 487039. */ -static inline bool -IsCacheableNonGlobalScope(JSObject *obj) +inline bool +js_IsCacheableNonGlobalScope(JSObject *obj) { + extern JS_FRIEND_DATA(js::Class) js_CallClass; + extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; JS_ASSERT(obj->getParent()); js::Class *clasp = obj->getClass(); @@ -1685,8 +1682,6 @@ IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } -} - /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d60557c24e51..a536724cbf29 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -49,7 +49,6 @@ #include "jsobj.h" #include "jsprobes.h" #include "jspropertytree.h" -#include "jsproxy.h" #include "jsscope.h" #include "jsstaticcheck.h" #include "jsxml.h" @@ -61,7 +60,6 @@ #include "jsscopeinlines.h" #include "jsstr.h" -#include "jsfuninlines.h" #include "jsgcinlines.h" #include "jsprobes.h" diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6d540c5e461a..79fddf099902 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f) */ JSObject* child = &cx->fp()->scopeChain(); while (JSObject* parent = child->getParent()) { - if (!IsCacheableNonGlobalScope(child)) { + if (!js_IsCacheableNonGlobalScope(child)) { debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n"); Blacklist((jsbytecode*) f->root->ip); return false; @@ -15183,7 +15183,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { - if (!IsCacheableNonGlobalScope(obj)) + if (!js_IsCacheableNonGlobalScope(obj)) RETURN_STOP("scope chain lookup crosses non-cacheable object"); // We must guard on the shape of all call objects for heavyweight functions diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index aa82dabf6dbf..a954a7536819 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj); while (tobj && tobj != getprop.holder) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable scope chain object"); JS_ASSERT(tobj->isNative()); @@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler JSObject *tobj = scopeChain; Address parent(pic.objReg, offsetof(JSObject, parent)); while (tobj && tobj != obj) { - if (!IsCacheableNonGlobalScope(tobj)) + if (!js_IsCacheableNonGlobalScope(tobj)) return disable("non-cacheable obj in scope chain"); masm.loadPtr(parent, pic.objReg); Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg); From 0889a9533c982b264a1958ffdec40c0d6880ec08 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 18 Feb 2011 10:57:28 -0600 Subject: [PATCH 39/42] Silence MSVC warnings. no_r=me. --HG-- extra : rebase_source : edff65eb68b63b90603d552855023a63f2073626 --- js/src/jsdbgapi.cpp | 4 ++-- js/src/jstracer.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index dc8dc803eda9..25a359c5bea4 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -145,7 +145,7 @@ CompartmentHasLiveScripts(JSCompartment *comp) JS_FRIEND_API(JSBool) JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug) { - if (comp->debugMode == debug) + if (comp->debugMode == !!debug) return JS_TRUE; // This should only be called when no scripts are live. It would even be @@ -154,7 +154,7 @@ JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug) JS_ASSERT(!CompartmentHasLiveScripts(comp)); // All scripts compiled from this point on should be in the requested debugMode. - comp->debugMode = debug; + comp->debugMode = !!debug; // Discard JIT code for any scripts that change debugMode. This function // assumes that 'comp' is in the same thread as 'cx'. diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 79fddf099902..6ee512e81c1e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -13802,7 +13802,7 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc, if (constructing) { LIns* thisobj_ins; CHECK_STATUS(createThis(fval.toObject(), get(&fval), &thisobj_ins)); - stack(-argc - 1, thisobj_ins); + stack(-int(argc) - 1, thisobj_ins); } // Generate a type map for the outgoing frame and stash it in the LIR From 6d847feb4698e1792af9f8ade7047bc061966edc Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 18 Feb 2011 10:57:33 -0600 Subject: [PATCH 40/42] Add resolver built-in function to the JS shell. This is for testing. r=brendan in bug 458271, a=npotb. --HG-- extra : rebase_source : 585c1fd53672763341a3f476c46c0704c3e1bf12 --- js/src/shell/js.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index eeb0f892fff1..42ab19207793 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3564,6 +3564,127 @@ ShapeOf(JSContext *cx, uintN argc, jsval *vp) return JS_NewNumberValue(cx, obj->shape(), vp); } +/* + * If referent has an own property named id, copy that property to obj[id]. + * Since obj is native, this isn't totally transparent; properties of a + * non-native referent may be simplified to data properties. + */ +static JSBool +CopyProperty(JSContext *cx, JSObject *obj, JSObject *referent, jsid id, + uintN lookupFlags, JSObject **objp) +{ + JSProperty *prop; + PropertyDescriptor desc; + uintN propFlags = 0; + JSObject *obj2; + + *objp = NULL; + if (referent->isNative()) { + if (js_LookupPropertyWithFlags(cx, referent, id, lookupFlags, &obj2, &prop) < 0) + return false; + if (obj2 != referent) + return true; + + const Shape *shape = (Shape *) prop; + if (shape->isMethod()) { + shape = referent->methodReadBarrier(cx, *shape, &desc.value); + if (!shape) + return false; + } else if (shape->hasSlot()) { + desc.value = referent->nativeGetSlot(shape->slot); + } else { + desc.value.setUndefined(); + } + + desc.attrs = shape->attributes(); + desc.getter = shape->getter(); + if (!desc.getter && !(desc.attrs & JSPROP_GETTER)) + desc.getter = PropertyStub; + desc.setter = shape->setter(); + if (!desc.setter && !(desc.attrs & JSPROP_SETTER)) + desc.setter = StrictPropertyStub; + desc.shortid = shape->shortid; + propFlags = shape->getFlags(); + } else if (referent->isProxy()) { + PropertyDescriptor desc; + if (!JSProxy::getOwnPropertyDescriptor(cx, referent, id, false, &desc)) + return false; + if (!desc.obj) + return true; + } else { + if (!referent->lookupProperty(cx, id, objp, &prop)) + return false; + if (*objp != referent) + return true; + if (!referent->getProperty(cx, id, &desc.value) || + !referent->getAttributes(cx, id, &desc.attrs)) { + return false; + } + desc.attrs &= JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT; + desc.getter = PropertyStub; + desc.setter = StrictPropertyStub; + desc.shortid = 0; + } + + *objp = obj; + return js_DefineNativeProperty(cx, obj, id, desc.value, + desc.getter, desc.setter, desc.attrs, propFlags, + desc.shortid, &prop); +} + +static JSBool +resolver_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) +{ + jsval v; + JS_ALWAYS_TRUE(JS_GetReservedSlot(cx, obj, 0, &v)); + return CopyProperty(cx, obj, JSVAL_TO_OBJECT(v), id, flags, objp); +} +static JSNewResolveOp resolver_resolve_check = resolver_resolve; + +static JSBool +resolver_enumerate(JSContext *cx, JSObject *obj) +{ + jsval v; + JS_ALWAYS_TRUE(JS_GetReservedSlot(cx, obj, 0, &v)); + JSObject *referent = JSVAL_TO_OBJECT(v); + + AutoIdArray ida(cx, JS_Enumerate(cx, referent)); + bool ok = !!ida; + JSObject *ignore; + for (size_t i = 0; ok && i < ida.length(); i++) + ok = CopyProperty(cx, obj, referent, ida[i], JSRESOLVE_QUALIFIED, &ignore); + return ok; +} + +static JSClass resolver_class = { + "resolver", + JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_StrictPropertyStub, + resolver_enumerate, (JSResolveOp)resolver_resolve, + JS_ConvertStub, NULL, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + + +static JSBool +Resolver(JSContext *cx, uintN argc, jsval *vp) +{ + JSObject *referent, *proto = NULL; + if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o/o", &referent, &proto)) + return false; + + JSObject *result = (argc > 1 + ? JS_NewObjectWithGivenProto + : JS_NewObject)(cx, &resolver_class, proto, JS_GetParent(cx, referent)); + if (!result) + return false; + + JS_ALWAYS_TRUE(JS_SetReservedSlot(cx, result, 0, OBJECT_TO_JSVAL(referent))); + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); + return true; +} + #ifdef JS_THREADSAFE /* @@ -4416,6 +4537,7 @@ static JSFunctionSpec shell_functions[] = { JS_FN("evalcx", EvalInContext, 1,0), JS_FN("evalInFrame", EvalInFrame, 2,0), JS_FN("shapeOf", ShapeOf, 1,0), + JS_FN("resolver", Resolver, 1,0), #ifdef MOZ_CALLGRIND JS_FN("startCallgrind", js_StartCallgrind, 0,0), JS_FN("stopCallgrind", js_StopCallgrind, 0,0), @@ -4543,6 +4665,8 @@ static const char *const shell_help_messages[] = { "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n" " If 'save' (default false), save the frame chain", "shapeOf(obj) Get the shape of obj (an implementation detail)", +"resolver(src[, proto]) Create object with resolve hook that copies properties\n" +" from src. If proto is omitted, use Object.prototype.", #ifdef MOZ_CALLGRIND "startCallgrind() Start callgrind instrumentation", "stopCallgrind() Stop callgrind instrumentation", From c0d06ac9f27f2fb2dcd811c27fe4ae55b4e8d8b0 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 18 Feb 2011 10:57:42 -0600 Subject: [PATCH 41/42] Bug 633890 - Assertion failure: prop == (JSProperty*) shape. r=brendan, a=jst. --HG-- extra : rebase_source : 8b5f7ad67b3119c56700f3a2d50f84d320fbda11 --- js/src/jit-test/tests/basic/bug633890.js | 9 +++++++++ js/src/jstracer.cpp | 5 ++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug633890.js diff --git a/js/src/jit-test/tests/basic/bug633890.js b/js/src/jit-test/tests/basic/bug633890.js new file mode 100644 index 000000000000..11b60a875d2a --- /dev/null +++ b/js/src/jit-test/tests/basic/bug633890.js @@ -0,0 +1,9 @@ +// |jit-test| error:Error +var p = /./, x = resolver({}, p), y = resolver({lastIndex: 2}, p), v; +var a = []; +for (var i = 0; i < HOTLOOP; i++) + a[i] = x; +a[HOTLOOP] = y; +for (i = 0; i < a.length; i++) + v = a[i].lastIndex; +assertEq(v, 2); // fails due to bug 458271 diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6ee512e81c1e..776f7d0573d9 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12852,10 +12852,9 @@ GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, Shape* shape, Value* v LeaveTraceIfGlobalObject(cx, obj); #ifdef DEBUG - JSProperty* prop; JSObject* pobj; - JS_ASSERT(obj->lookupProperty(cx, shape->id, &pobj, &prop)); - JS_ASSERT(prop == (JSProperty*) shape); + const Shape* shape2; + JS_ASSERT_IF(SafeLookup(cx, obj, shape->id, &pobj, &shape2), shape == shape2); #endif // Shape::get contains a special case for With objects. We can elide it From 96379e62ab2c102c525e81c661c0ac59b27866b4 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 18 Feb 2011 14:27:00 -0800 Subject: [PATCH 42/42] Bug 635298 - Check return value of JS_ValueToString() (r=luke, a=gal) --HG-- extra : rebase_source : 578154e99a3192c7c6610f62b181bf222373b834 --- js/jsd/jsd_val.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/js/jsd/jsd_val.c b/js/jsd/jsd_val.c index 4cda7764b2a1..51ddafcccd5d 100644 --- a/js/jsd/jsd_val.c +++ b/js/jsd/jsd_val.c @@ -241,9 +241,13 @@ jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) JS_RestoreExceptionState(cx, exceptionState); JS_LeaveCrossCompartmentCall(call); - stringval = STRING_TO_JSVAL(string); - call = JS_EnterCrossCompartmentCall(cx, jsdc->glob); - if(!call || !JS_WrapValue(cx, &stringval)) { + if(string) { + stringval = STRING_TO_JSVAL(string); + call = JS_EnterCrossCompartmentCall(cx, jsdc->glob); + } + if(!string || !call || !JS_WrapValue(cx, &stringval)) { + if(call) + JS_LeaveCrossCompartmentCall(call); JS_EndRequest(cx); return NULL; }