From 44cd5a6f2c3d8a5638eefda69f00c8a8a750565f Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Tue, 23 Aug 2011 14:42:17 -0500 Subject: [PATCH] Bug 680428 - onEnterFrame does not work with tracejit. r=dvander. --HG-- rename : js/src/jit-test/tests/debug/onEnterFrame-05.js => js/src/jit-test/tests/debug/onEnterFrame-04.js --- .../jit-test/tests/debug/onEnterFrame-04.js | 17 ++++++ .../jit-test/tests/debug/onEnterFrame-05.js | 26 +++++----- .../jit-test/tests/debug/onEnterFrame-06.js | 19 +++++++ js/src/jscntxt.cpp | 6 ++- js/src/jscntxt.h | 52 +++++++++++++++---- js/src/jscompartment.cpp | 8 ++- js/src/jsdbgapi.cpp | 4 +- 7 files changed, 103 insertions(+), 29 deletions(-) create mode 100644 js/src/jit-test/tests/debug/onEnterFrame-04.js create mode 100644 js/src/jit-test/tests/debug/onEnterFrame-06.js diff --git a/js/src/jit-test/tests/debug/onEnterFrame-04.js b/js/src/jit-test/tests/debug/onEnterFrame-04.js new file mode 100644 index 000000000000..cda6ee2794d4 --- /dev/null +++ b/js/src/jit-test/tests/debug/onEnterFrame-04.js @@ -0,0 +1,17 @@ +// We detect and stop the runaway recursion caused by making onEnterFrame a wrapper of a debuggee function. + +var g = newGlobal('new-compartment'); +g.n = 0; +g.eval("function f(frame) { n++; return 42; }"); +print('ok'); +var dbg = Debugger(g); +dbg.onEnterFrame = g.f; + +// Since enterFrame cannot throw, the InternalError is reported and execution proceeds. +var x = g.f(); +assertEq(x, 42); +assertEq(g.n > 20, true); + +// When an error is reported, the shell usually exits with a nonzero exit +// code. If we get here, the test passed, so override that behavior. +quit(0); diff --git a/js/src/jit-test/tests/debug/onEnterFrame-05.js b/js/src/jit-test/tests/debug/onEnterFrame-05.js index cda6ee2794d4..ac4851080609 100644 --- a/js/src/jit-test/tests/debug/onEnterFrame-05.js +++ b/js/src/jit-test/tests/debug/onEnterFrame-05.js @@ -1,17 +1,15 @@ -// We detect and stop the runaway recursion caused by making onEnterFrame a wrapper of a debuggee function. +// The tracejit does not prevent onEnterFrame from being called. var g = newGlobal('new-compartment'); -g.n = 0; -g.eval("function f(frame) { n++; return 42; }"); -print('ok'); +g.eval("function f() { return 1; }\n"); +var N = g.N = RUNLOOP + 2; +g.eval("function h() {\n" + + " for (var i = 0; i < N; i += f()) {}\n" + + "}"); +g.h(); // record loop + var dbg = Debugger(g); -dbg.onEnterFrame = g.f; - -// Since enterFrame cannot throw, the InternalError is reported and execution proceeds. -var x = g.f(); -assertEq(x, 42); -assertEq(g.n > 20, true); - -// When an error is reported, the shell usually exits with a nonzero exit -// code. If we get here, the test passed, so override that behavior. -quit(0); +var log = ''; +dbg.onEnterFrame = function (frame) { log += frame.callee.name; }; +g.h(); +assertEq(log, 'h' + Array(N + 1).join('f')); diff --git a/js/src/jit-test/tests/debug/onEnterFrame-06.js b/js/src/jit-test/tests/debug/onEnterFrame-06.js new file mode 100644 index 000000000000..3cb1dcdd7a0e --- /dev/null +++ b/js/src/jit-test/tests/debug/onEnterFrame-06.js @@ -0,0 +1,19 @@ +// The tracejit does not prevent onEnterFrame from being called after entering +// a debuggee compartment from a non-debuggee compartment. + +var g1 = newGlobal('new-compartment'); +var g2 = newGlobal('new-compartment'); +var dbg = Debugger(g1, g2); +dbg.removeDebuggee(g2); // turn off debug mode in g2 + +g1.eval("function f() { return 1; }\n"); +var N = g1.N = RUNLOOP + 2; +g1.eval("function h() {\n" + + " for (var i = 0; i < N; i += f()) {}\n" + + "}"); +g1.h(); // record loop + +var log = ''; +dbg.onEnterFrame = function (frame) { log += frame.callee.name; }; +g1.h(); +assertEq(log, 'h' + Array(N + 1).join('f')); diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index ee7369ea95d3..9cda55d6c382 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -570,7 +570,7 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) Maybe lockIf; if (unlocked) lockIf.construct(rt); - cx = js_ContextFromLinkField(cx ? cx->link.next : rt->contextList.next); + cx = JSContext::fromLinkField(cx ? cx->link.next : rt->contextList.next); if (&cx->link == &rt->contextList) cx = NULL; *iterp = cx; @@ -1387,9 +1387,9 @@ JSContext::resetCompartment() } compartment = scopeobj->compartment(); - if (isExceptionPending()) wrapPendingException(); + updateJITEnabled(); return; error: @@ -1580,6 +1580,8 @@ JSContext::updateJITEnabled() #ifdef JS_TRACER traceJitEnabled = ((runOptions & JSOPTION_JIT) && !IsJITBrokenHere() && + compartment && + !compartment->debugMode() && (debugHooks == &js_NullDebugHooks || (debugHooks == &runtime->globalDebugHooks && !runtime->debuggerInhibitsJIT()))); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ef937c2f22e3..ba6344ac61f7 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1144,9 +1144,6 @@ struct JSContext without the corresponding JS_EndRequest. */ JSCList threadLinks; /* JSThread contextList linkage */ - -#define CX_FROM_THREAD_LINKS(tl) \ - ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) #endif /* Stack of thread-stack-allocated GC roots. */ @@ -1325,6 +1322,18 @@ struct JSContext */ bool runningWithTrustedPrincipals() const; + static inline JSContext *fromLinkField(JSCList *link) { + JS_ASSERT(link); + return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, link)); + } + +#ifdef JS_THREADSAFE + static inline JSContext *fromThreadLinks(JSCList *link) { + JS_ASSERT(link); + return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, threadLinks)); + } +#endif + private: /* * The allocation code calls the function to indicate either OOM failure @@ -2137,6 +2146,36 @@ class ThreadDataIter #endif /* !JS_THREADSAFE */ +/* + * Enumerate all contexts in a runtime that are in the same thread as a given + * context. + */ +class ThreadContextRange { + JSCList *begin; + JSCList *end; + +public: + explicit ThreadContextRange(JSContext *cx) { +#ifdef JS_THREADSAFE + end = &cx->thread()->contextList; +#else + end = &cx->runtime->contextList; +#endif + begin = end->next; + } + + bool empty() const { return begin == end; } + void popFront() { JS_ASSERT(!empty()); begin = begin->next; } + + JSContext *front() const { +#ifdef JS_THREADSAFE + return JSContext::fromThreadLinks(begin); +#else + return JSContext::fromLinkField(begin); +#endif + } +}; + } /* namespace js */ /* @@ -2149,13 +2188,6 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize); extern void js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); -static JS_INLINE JSContext * -js_ContextFromLinkField(JSCList *link) -{ - JS_ASSERT(link); - return reinterpret_cast(uintptr_t(link) - offsetof(JSContext, link)); -} - /* * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise * the caller must be holding rt->gcLock. diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 2d528964d33d..7f6366907841 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -667,7 +667,7 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b) debugModeBits = (debugModeBits & ~uintN(DebugFromC)) | (b ? DebugFromC : 0); JS_ASSERT(debugMode() == enabledAfter); - if (enabledBefore != enabledAfter && !onStack) + if (enabledBefore != enabledAfter) updateForDebugMode(cx); return true; } @@ -675,6 +675,12 @@ JSCompartment::setDebugModeFromC(JSContext *cx, bool b) void JSCompartment::updateForDebugMode(JSContext *cx) { + for (ThreadContextRange r(cx); !r.empty(); r.popFront()) { + JSContext *cx = r.front(); + if (cx->compartment == this) + cx->updateJITEnabled(); + } + #ifdef JS_METHODJIT bool enabled = debugMode(); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index b439e6801086..89be6ff7ef57 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -289,11 +289,11 @@ JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited) if (wasInhibited) { if (!rt->debuggerInhibitsJIT()) { for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) - js_ContextFromLinkField(cl)->updateJITEnabled(); + JSContext::fromLinkField(cl)->updateJITEnabled(); } } else if (rt->debuggerInhibitsJIT()) { for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) - js_ContextFromLinkField(cl)->traceJitEnabled = false; + JSContext::fromLinkField(cl)->traceJitEnabled = false; } } #endif