☠☠ backed out by fd729f2e9387 ☠ ☠ | |
author | Andrew Drake <drakedevel@gmail.com> |
Thu, 05 Aug 2010 17:15:49 -0700 | |
changeset 53375 | a8aeff259925a393619bac12e6745afb0a5beff1 |
parent 53374 | 225495aeb3d502f07781d498a9b7fabc440b583a |
child 53376 | c22ea4aa11653d967539aa427c00c8b8c9598c7e |
child 53377 | fd729f2e9387c6c9aad05c7ca8b83c2dc68be139 |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dvander |
bugs | 578154 |
milestone | 2.0b4pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -325,8 +325,9 @@ MSG_DEF(JSMSG_DEFINE_ARRAY_LENGTH_UNSUPP MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property") MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index") MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0") MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments") MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP") MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function") MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler") MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument") +MSG_DEF(JSMSG_NEED_DEBUG_MODE, 251, 0, JSEXN_ERR, "function can be called only in debug mode")
--- a/js/src/jsapi-tests/testTrap.cpp +++ b/js/src/jsapi-tests/testTrap.cpp @@ -40,16 +40,19 @@ BEGIN_TEST(testTrap_gc) jsvalRoot v2(cx); CHECK(JS_ExecuteScript(cx, global, script, v2.addr())); CHECK(JSVAL_IS_OBJECT(v2)); CHECK(emptyTrapCallCount == 0); // Disable JIT for debugging JS_SetOptions(cx, JS_GetOptions(cx) & ~JSOPTION_JIT); + // Enable debug mode + CHECK(JS_SetDebugMode(cx, JS_TRUE)); + jsbytecode *line2 = JS_LineNumberToPC(cx, script, 1); CHECK(line2); jsbytecode *line6 = JS_LineNumberToPC(cx, script, 5); CHECK(line2); static const char trapClosureText[] = "some trap closure"; JSString *trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -508,16 +508,35 @@ FrameRegsIter::operator++() } else { cursp = InlineDownFrameSP(f); f = f->down; } } return *this; } +AllFramesIter::AllFramesIter(JSContext *cx) + : curcs(cx->stack().getCurrentSegment()), + curfp(curcs ? curcs->getCurrentFrame() : NULL) +{ +} + +AllFramesIter& +AllFramesIter::operator++() +{ + JS_ASSERT(!done()); + if (curfp == curcs->getInitialFrame()) { + curcs = curcs->getPreviousInMemory(); + curfp = curcs ? curcs->getCurrentFrame() : NULL; + } else { + curfp = curfp->down; + } + return *this; +} + bool JSThreadData::init() { #ifdef DEBUG /* The data must be already zeroed. */ for (size_t i = 0; i != sizeof(*this); ++i) JS_ASSERT(reinterpret_cast<uint8*>(this)[i] == 0); #endif @@ -585,19 +604,16 @@ JSThreadData::purge(JSContext *cx) #ifdef JS_TRACER /* * If we are about to regenerate shapes, we have to flush the JIT cache, * which will eventually abort any current recording. */ if (cx->runtime->gcRegenShapes) traceMonitor.needFlush = JS_TRUE; #endif -#ifdef JS_METHODJIT - jmData.purge(cx); -#endif /* Destroy eval'ed scripts. */ js_DestroyScriptsToGC(cx, this); /* Purge cached native iterators. */ memset(cachedNativeIterators, 0, sizeof(cachedNativeIterators)); dtoaCache.s = NULL; @@ -2263,19 +2279,20 @@ FreeOldArenas(JSRuntime *rt, JSArenaPool JS_FreeArenaPool(pool); } } void JSContext::purge() { FreeOldArenas(runtime, ®expPool); + /* FIXME: bug 586161 */ + compartment->purge(this); } - namespace js { void SetPendingException(JSContext *cx, const Value &v) { cx->throwing = JS_TRUE; cx->exception = v; }
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -232,31 +232,23 @@ namespace mjit { void (* forceReturn)(); JSC::ExecutablePool *forceReturnPool; }; struct ThreadData { JSC::ExecutableAllocator *execPool; - // Scripts that have had PICs patched or PIC stubs generated. - typedef js::HashSet<JSScript*, DefaultHasher<JSScript*>, js::SystemAllocPolicy> ScriptSet; - ScriptSet picScripts; - // Trampolines for JIT code. Trampolines trampolines; VMFrame *activeFrame; bool Initialize(); void Finish(); - - bool addScript(JSScript *script); - void removeScript(JSScript *script); - void purge(JSContext *cx); }; } #endif /* JS_METHODJIT */ /* * Storage for the execution state and store during trace execution. Generated * code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS| * doubles after the stack begins. Thus, on trace, |TracerState::eos| holds a @@ -818,16 +810,34 @@ class FrameRegsIter bool done() const { return curfp == NULL; } FrameRegsIter &operator++(); JSStackFrame *fp() const { return curfp; } Value *sp() const { return cursp; } jsbytecode *pc() const { return curpc; } }; +/* + * Utility class for iteration over all active stack frames. + */ +class AllFramesIter +{ +public: + AllFramesIter(JSContext *cx); + + bool done() const { return curfp == NULL; } + AllFramesIter& operator++(); + + JSStackFrame *fp() const { return curfp; } + +private: + CallStackSegment *curcs; + JSStackFrame *curfp; +}; + /* Holds the number of recording attemps for an address. */ typedef HashMap<jsbytecode*, size_t, DefaultHasher<jsbytecode*>, SystemAllocPolicy> RecordAttemptMap; class Oracle; @@ -1238,32 +1248,47 @@ class AutoIdVector; } /* namespace js */ struct JSCompartment { JSRuntime *rt; JSPrincipals *principals; void *data; bool marked; js::WrapperMap crossCompartmentWrappers; + bool debugMode; + +#ifdef JS_METHODJIT + /* Executable allocator for PIC buffers. */ + JSC::ExecutableAllocator *execPool; + + /* Needed to re-JIT scripts for debug mode and so we can flush PICs. */ + JSCList scripts; +#endif JSCompartment(JSRuntime *cx); ~JSCompartment(); bool init(); bool wrap(JSContext *cx, js::Value *vp); bool wrap(JSContext *cx, JSString **strp); bool wrap(JSContext *cx, JSObject **objp); bool wrapId(JSContext *cx, jsid *idp); bool wrap(JSContext *cx, js::PropertyOp *op); bool wrap(JSContext *cx, js::PropertyDescriptor *desc); bool wrap(JSContext *cx, js::AutoIdVector &props); bool wrapException(JSContext *cx); void sweep(JSContext *cx); + +#ifdef JS_METHODJIT + bool addScript(JSContext *cx, JSScript *script); + void removeScript(JSScript *script); +#endif + void purge(JSContext *cx); }; struct JSRuntime { /* Default compartment. */ JSCompartment *defaultCompartment; /* List of compartments (protected by the GC lock). */ js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> compartments;
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -64,36 +64,106 @@ #include "jsstr.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "jsscopeinlines.h" #include "jsautooplen.h" -#ifdef JS_METHODJIT -# include "methodjit/MethodJIT.h" -# include "methodjit/Retcon.h" -#endif +#include "methodjit/MethodJIT.h" +#include "methodjit/Retcon.h" using namespace js; typedef struct JSTrap { JSCList links; JSScript *script; jsbytecode *pc; JSOp op; JSTrapHandler handler; jsval closure; } JSTrap; #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock) #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock) #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt)) +JS_PUBLIC_API(JSBool) +JS_GetDebugMode(JSContext *cx) +{ + return cx->compartment->debugMode; +} + +static bool +IsScriptLive(JSContext *cx, JSScript *script) +{ + for (AllFramesIter i(cx); !i.done(); ++i) { + if (i.fp()->script == script) + return true; + } + return false; +} + +JS_FRIEND_API(JSBool) +js_SetDebugMode(JSContext *cx, JSBool debug) +{ + cx->compartment->debugMode = debug; +#ifdef JS_METHODJIT + for (JSScript *script = (JSScript *)cx->compartment->scripts.next; + &script->links != &cx->compartment->scripts; + script = (JSScript *)script->links.next) { + if (script->debugMode != debug && + script->ncode && + script->ncode != JS_UNJITTABLE_METHOD && + !IsScriptLive(cx, script)) { + /* + * In the event that this fails, debug mode is left partially on, + * leading to a small performance overhead but no loss of + * correctness. We set the debug flag to false so that the caller + * will not later attempt to use debugging features. + */ + mjit::Recompiler recompiler(cx, script); + if (!recompiler.recompile()) { + cx->compartment->debugMode = JS_FALSE; + return JS_FALSE; + } + } + } +#endif + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetDebugMode(JSContext *cx, JSBool debug) +{ +#ifdef DEBUG + for (AllFramesIter i(cx); !i.done(); ++i) + JS_ASSERT(JS_IsNativeFrame(cx, i.fp())); +#endif + + return js_SetDebugMode(cx, debug); +} + +static JSBool +CheckDebugMode(JSContext *cx) +{ + JSBool debugMode = JS_GetDebugMode(cx); + /* + * :TODO: + * This probably should be an assertion, since it's indicative of a severe + * API misuse. + */ + if (!debugMode) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, + NULL, JSMSG_NEED_DEBUG_MODE); + } + return debugMode; +} + /* * NB: FindTrap must be called with rt->debuggerLock acquired. */ static JSTrap * FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) { JSTrap *trap; @@ -148,16 +218,19 @@ js_UntrapScriptCode(JSContext *cx, JSScr JS_PUBLIC_API(JSBool) JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, jsval closure) { JSTrap *junk, *trap, *twin; JSRuntime *rt; uint32 sample; + if (!CheckDebugMode(cx)) + return JS_FALSE; + if (script == JSScript::emptyScript()) { JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, "empty script"); return JS_FALSE; } JS_ASSERT((JSOp) *pc != JSOP_TRAP); junk = NULL; @@ -232,17 +305,17 @@ DestroyTrapAndUnlock(JSContext *cx, JSTr cx->free(trap); } JS_PUBLIC_API(void) JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler *handlerp, jsval *closurep) { JSTrap *trap; - + DBG_LOCK(cx->runtime); trap = FindTrap(cx->runtime, script, pc); if (handlerp) *handlerp = trap ? trap->handler : NULL; if (closurep) *closurep = trap ? trap->closure : JSVAL_NULL; if (trap) DestroyTrapAndUnlock(cx, trap); @@ -1368,16 +1441,19 @@ JS_SetDestroyScriptHook(JSRuntime *rt, J JS_PUBLIC_API(JSBool) JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, const jschar *chars, uintN length, const char *filename, uintN lineno, jsval *rval) { JS_ASSERT_NOT_ON_TRACE(cx); + if (!CheckDebugMode(cx)) + return JS_FALSE; + JSObject *scobj = JS_GetFrameScopeChain(cx, fp); if (!scobj) return false; /* * NB: This function breaks the assumption that the compiler can see all * calls and properly compute a static level. In order to get around this, * we use a static level that will cause us not to attempt to optimize @@ -1401,16 +1477,19 @@ JS_PUBLIC_API(JSBool) JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, const char *bytes, uintN length, const char *filename, uintN lineno, jsval *rval) { jschar *chars; JSBool ok; size_t len = length; + + if (!CheckDebugMode(cx)) + return JS_FALSE; chars = js_InflateString(cx, bytes, &len); if (!chars) return JS_FALSE; length = (uintN) len; ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, rval); cx->free(chars);
--- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -45,16 +45,38 @@ */ #include "jsapi.h" #include "jsopcode.h" #include "jsprvtd.h" JS_BEGIN_EXTERN_C /* + * Debug mode is a compartment-wide mode that enables a debugger to attach + * to and interact with running methodjit-ed frames. In particular, it causes + * every function to be compiled as if an eval was present (so eval-in-frame) + * can work, and it ensures that functions can be re-JITed for other debug + * features. In general, it is not safe to interact with frames that were live + * before debug mode was enabled. For this reason, it is also not safe to + * enable debug mode while frames are live. + */ + +/* Get current state of debugging mode. */ +extern JS_PUBLIC_API(JSBool) +JS_GetDebugMode(JSContext *cx); + +/* Turn on debugging mode, ignoring the presence of live frames. */ +extern JS_FRIEND_API(JSBool) +js_SetDebugMode(JSContext *cx, JSBool debug); + +/* Turn on debugging mode. */ +extern JS_PUBLIC_API(JSBool) +JS_SetDebugMode(JSContext *cx, JSBool debug); + +/* * Unexported library-private helper used to unpatch all traps in a script. * Returns script->code if script has no traps, else a JS_malloc'ed copy of * script->code which the caller must JS_free, or null on JS_malloc OOM. */ extern jsbytecode * js_UntrapScriptCode(JSContext *cx, JSScript *script); /* The closure argument will be marked. */
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -69,19 +69,25 @@ #include "jsobjinlines.h" #include "jsscriptinlines.h" using namespace js; static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL}; /* static */ const JSScript JSScript::emptyScriptConst = { + JS_INIT_STATIC_CLIST(NULL), const_cast<jsbytecode*>(emptyScriptCode), 1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, 0, true, false, false, false, false, - false, true, const_cast<jsbytecode*>(emptyScriptCode), + false, true, +#ifdef JS_METHODJIT + /* debugMode */ + false, +#endif + const_cast<jsbytecode*>(emptyScriptCode), {0, NULL}, NULL, 0, 0, 0, NULL, {NULL}, #ifdef CHECK_SCRIPT_OWNER reinterpret_cast<JSThread*>(1) #endif }; #if JS_HAS_XDR @@ -866,16 +872,17 @@ js_NewScript(JSContext *cx, uint32 lengt } size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote); script = (JSScript *) cx->malloc(size); if (!script) return NULL; + PodZero(script); script->length = length; script->version = cx->version; cursor = (uint8 *)script + sizeof(JSScript); if (nobjects != 0) { script->objectsOffset = (uint8)(cursor - (uint8 *)script); cursor += sizeof(JSObjectArray); @@ -975,16 +982,18 @@ js_NewScript(JSContext *cx, uint32 lengt JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == (uint8 *)script + size); #ifdef CHECK_SCRIPT_OWNER script->owner = cx->thread; #endif + + JS_APPEND_LINK(&script->links, &cx->compartment->scripts); return script; } JSScript * js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) { uint32 mainLength, prologLength, nsrcnotes, nfixed; JSScript *script; @@ -1245,16 +1254,17 @@ js_DestroyScript(JSContext *cx, JSScript #ifdef JS_TRACER PurgeScriptFragments(cx, script); #endif #if defined(JS_METHODJIT) mjit::ReleaseScriptCode(cx, script); #endif + JS_REMOVE_LINK(&script->links); cx->free(script); JS_RUNTIME_UNMETER(cx->runtime, liveScripts); } void js_TraceScript(JSTracer *trc, JSScript *script)
--- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -41,16 +41,17 @@ #ifndef jsscript_h___ #define jsscript_h___ /* * JS script descriptor. */ #include "jsatom.h" #include "jsprvtd.h" #include "jsdbgapi.h" +#include "jsclist.h" /* * Type of try note associated with each catch or finally block, and also with * for-in loops. */ typedef enum JSTryNoteKind { JSTRY_CATCH, JSTRY_FINALLY, @@ -179,16 +180,18 @@ namespace ic { # endif } union CallSite; } } #endif struct JSScript { + /* FIXME: bug 586181 */ + JSCList links; /* Links for compartment script list */ jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ uint16 version; /* JS version under which script was compiled */ uint16 nfixed; /* number of slots besides stack operands in slot array */ uint8 objectsOffset; /* offset to the array of nested function, block, scope, xml and one-time regexps objects or 0 if none */ @@ -207,16 +210,19 @@ struct JSScript { bool savedCallerFun:1; /* object 0 is caller function */ bool hasSharps:1; /* script uses sharp variables */ bool strictModeCode:1; /* code is in strict mode */ bool compileAndGo:1; /* script was compiled with TCF_COMPILE_N_GO */ bool usesEval:1; /* script uses eval() */ bool warnedAboutTwoArgumentEval:1; /* have warned about use of obsolete eval(s, o) in this script */ +#ifdef JS_METHODJIT + bool debugMode:1; /* script was compiled in debug mode */ +#endif jsbytecode *main; /* main entry point, after predef'ing prolog */ JSAtomMap atomMap; /* maps immediate index to literal struct */ const char *filename; /* source filename or null */ uint32 lineno; /* base line number of script */ uint16 nslots; /* vars plus maximum stack depth */ uint16 staticLevel;/* static level for display maintenance */ JSPrincipals *principals;/* principals for this script */
--- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -40,16 +40,18 @@ * ***** END LICENSE BLOCK ***** */ #include "jsapi.h" #include "jscntxt.h" #include "jsiter.h" #include "jsnum.h" #include "jsregexp.h" #include "jswrapper.h" +#include "methodjit/PolyIC.h" +#include "assembler/jit/ExecutableAllocator.h" #include "jsobjinlines.h" using namespace js; static int sWrapperFamily = 0; bool @@ -284,27 +286,34 @@ TransparentObjectWrapper(JSContext *cx, { JS_ASSERT(!obj->isWrapper()); return JSWrapper::New(cx, obj, wrappedProto, NULL, &JSCrossCompartmentWrapper::singleton); } } JSCompartment::JSCompartment(JSRuntime *rt) - : rt(rt), principals(NULL), data(NULL), marked(false) + : rt(rt), principals(NULL), data(NULL), marked(false), debugMode(false) { + JS_INIT_CLIST(&scripts); } JSCompartment::~JSCompartment() { + delete execPool; } bool JSCompartment::init() { +#ifdef JS_METHODJIT + execPool = new JSC::ExecutableAllocator(); + if (!execPool) + return false; +#endif return crossCompartmentWrappers.init(); } bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); @@ -469,16 +478,36 @@ JSCompartment::sweep(JSContext *cx) { /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { if (js_IsAboutToBeFinalized(e.front().value.asGCThing())) e.removeFront(); } } +void +JSCompartment::purge(JSContext *cx) +{ +#ifdef JS_METHODJIT + if (!cx->runtime->gcRegenShapes) + return; + + for (JSScript *script = (JSScript *)scripts.next; + &script->links != &scripts; + script = (JSScript *)script->links.next) { +# if defined JS_POLYIC + mjit::ic::PurgePICs(cx, script); +# endif +# if defined JS_MONOIC + //mjit::ic::PurgeMICs(cx, script); +# endif + } +#endif +} + static bool SetupFakeFrame(JSContext *cx, ExecuteFrameGuard &frame, JSFrameRegs ®s, JSObject *obj) { const uintN vplen = 2; const uintN nfixed = 0; if (!cx->stack().getExecuteFrame(cx, js_GetTopStackFrame(cx), vplen, nfixed, frame)) return false;
--- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -332,16 +332,18 @@ mjit::Compiler::finishThisUp() uint8 *cursor = (uint8 *)cx->calloc(sizeof(ic::PICInfo) * pics.length() + sizeof(uint32)); if (!cursor) { execPool->release(); return Compile_Error; } *(uint32*)cursor = pics.length(); cursor += sizeof(uint32); script->pics = (ic::PICInfo *)cursor; + } else { + script->pics = NULL; } for (size_t i = 0; i < pics.length(); i++) { pics[i].copySimpleMembersTo(script->pics[i]); script->pics[i].fastPathStart = fullCode.locationOf(pics[i].fastPathStart); script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack); script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart); script->pics[i].callReturn = uint16((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() - @@ -408,16 +410,20 @@ mjit::Compiler::finishThisUp() callSiteList[i].c.codeOffset = masm.size() + stubcc.masm.distanceOf(callSites[i].location); else callSiteList[i].c.codeOffset = masm.distanceOf(callSites[i].location); callSiteList[i].c.pcOffset = callSites[i].pc - script->code; callSiteList[i].c.id = callSites[i].id; } script->callSites = callSiteList; +#ifdef JS_METHODJIT + script->debugMode = cx->compartment->debugMode; +#endif + return Compile_Okay; } #ifdef DEBUG #define SPEW_OPCODE() \ JS_BEGIN_MACRO \ if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \ JaegerSpew(JSpew_JSOps, " %2d ", frame.stackDepth()); \
--- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -64,17 +64,17 @@ FrameState::init(uint32 nargs) uint32 nslots = script->nslots + nargs; if (!nslots) { sp = spBase = locals = args = base = NULL; return true; } uint32 nlocals = script->nslots; - if ((eval = script->usesEval)) + if ((eval = script->usesEval || cx->compartment->debugMode)) nlocals = 0; uint8 *cursor = (uint8 *)cx->malloc(sizeof(FrameEntry) * nslots + // entries[] sizeof(FrameEntry *) * nslots + // base[] sizeof(FrameEntry *) * nslots + // tracker.entries[] sizeof(uint32) * nlocals // escaping[] ); if (!cursor)
--- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -575,21 +575,16 @@ ThreadData::Initialize() return false; TrampolineCompiler tc(execPool, &trampolines); if (!tc.compile()) { delete execPool; return false; } - if (!picScripts.init()) { - delete execPool; - return false; - } - #ifdef JS_METHODJIT_PROFILE_STUBS for (size_t i = 0; i < STUB_CALLS_FOR_OP_COUNT; ++i) StubCallsForOp[i] = 0; #endif activeFrame = NULL; return true; @@ -605,53 +600,16 @@ ThreadData::Finish() # define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) \ fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]); # include "jsopcode.tbl" # undef OPDEF fclose(fp); #endif } -bool -ThreadData::addScript(JSScript *script) -{ - ScriptSet::AddPtr p = picScripts.lookupForAdd(script); - if (p) - return true; - return picScripts.add(p, script); -} - -void -ThreadData::removeScript(JSScript *script) -{ - ScriptSet::Ptr p = picScripts.lookup(script); - if (p) - picScripts.remove(p); -} - -void -ThreadData::purge(JSContext *cx) -{ - if (!cx->runtime->gcRegenShapes) - return; - - for (ThreadData::ScriptSet::Enum e(picScripts); !e.empty(); e.popFront()) { -#if defined JS_POLYIC - JSScript *script = e.front(); - ic::PurgePICs(cx, script); -#endif -#if defined JS_MONOIC - //PurgeMICs(cs, script); -#endif - } - - picScripts.clear(); -} - - extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, uintptr_t inlineCallCount); JSBool mjit::JaegerShot(JSContext *cx) { JS_ASSERT(cx->regs); @@ -721,25 +679,24 @@ mjit::ReleaseScriptCode(JSContext *cx, J memset(script->nmap[-1], 0xcc, script->inlineLength + script->outOfLineLength); #endif script->execPool->release(); script->execPool = NULL; // Releasing the execPool takes care of releasing the code. script->ncode = NULL; script->inlineLength = 0; script->outOfLineLength = 0; - + #if defined JS_POLYIC if (script->pics) { uint32 npics = script->numPICs(); for (uint32 i = 0; i < npics; i++) { script->pics[i].releasePools(); Destroy(script->pics[i].execPools); } - JS_METHODJIT_DATA(cx).removeScript(script); cx->free((uint8*)script->pics - sizeof(uint32)); } #endif } if (script->nmap) { cx->free(script->nmap - 1); script->nmap = NULL;
--- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -123,18 +123,17 @@ class PICStubCompiler ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress()); MacroAssemblerCodePtr target(stub); repatcher.relinkCallerToTrampoline(retPtr, target); return true; } JSC::ExecutablePool *getExecPool(size_t size) { - mjit::ThreadData *jd = &JS_METHODJIT_DATA(f.cx); - return jd->execPool->poolForSize(size); + return f.cx->compartment->execPool->poolForSize(size); } protected: void spew(const char *event, const char *op) { JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n", type, event, op, script->filename, js_FramePCToLineNumber(f.cx, f.fp)); @@ -679,22 +678,16 @@ class GetPropCompiler : public PICStubCo bool generateStringCallStub() { JS_ASSERT(pic.hasTypeCheck()); JS_ASSERT(pic.kind == ic::PICInfo::CALL); if (!f.fp->script->compileAndGo) return disable("String.prototype without compile-and-go"); - mjit::ThreadData &jm = JS_METHODJIT_DATA(f.cx); - if (!jm.addScript(script)) { - js_ReportOutOfMemory(f.cx); - return false; - } - JSObject *holder; JSProperty *prop; if (!obj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop)) return false; if (!prop) return disable("property not found"); AutoPropertyDropper dropper(f.cx, holder, prop); @@ -810,22 +803,16 @@ class GetPropCompiler : public PICStubCo return true; } bool patchInline(JSObject *holder, JSScopeProperty *sprop) { spew("patch", "inline"); PICRepatchBuffer repatcher(pic, pic.fastPathStart); - mjit::ThreadData &jm = JS_METHODJIT_DATA(f.cx); - if (!jm.addScript(script)) { - js_ReportOutOfMemory(f.cx); - return false; - } - int32 offset; if (sprop->slot < JS_INITIAL_NSLOTS) { JSC::CodeLocationInstruction istr; istr = pic.storeBack.instructionAtOffset(dslotsLoad()); repatcher.repatchLoadPtrToLEA(istr); // // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
--- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -91,17 +91,18 @@ Recompiler::findPatch(void **location) void Recompiler::applyPatch(Compiler& c, PatchableAddress& toPatch) { void *result = c.findCallSite(toPatch.callSite); JS_ASSERT(result); *toPatch.location = result; } -Recompiler::Recompiler(JSContext *cx, JSScript *script) : cx(cx), script(script) +Recompiler::Recompiler(JSContext *cx, JSScript *script) + : cx(cx), script(script) { } /* * The strategy for this goes as follows: * * 1) Scan the stack, looking at all return addresses that could go into JIT * code. @@ -116,94 +117,59 @@ bool Recompiler::recompile() { JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD); Vector<PatchableAddress> toPatch(cx); /* Scan the stack, saving the ncode elements of the frames. */ JSStackFrame *firstFrame = NULL; - for (CallStackIterator cs(cx); !cs.done(); ++cs) { - FrameIterator fp = cs.top(); - for (FrameIterator fp = cs.top(); fp != cs.bottom(); ++fp) { - if (!firstFrame && fp.fp()->script == script) - firstFrame = fp.fp(); - if (script->isValidJitCode(fp.fp()->ncode)) { - if (!toPatch.append(findPatch(&fp.fp()->ncode))) - return false; - } + for (AllFramesIter i(cx); !i.done(); ++i) { + if (!firstFrame && i.fp()->script == script) + firstFrame = i.fp(); + if (script->isValidJitCode(i.fp()->ncode)) { + if (!toPatch.append(findPatch(&i.fp()->ncode))) + return false; } } /* Iterate over VMFrames saving the machine and scripted return. */ for (VMFrame *f = JS_METHODJIT_DATA(cx).activeFrame; f != NULL; f = f->previous) { if (script->isValidJitCode(f->scriptedReturn)) { if (!toPatch.append(findPatch(&f->scriptedReturn))) return false; } void **machineReturn = f->returnAddressLocation(); - if (script->isValidJitCode(*machineReturn)) + if (script->isValidJitCode(*machineReturn)) { if (!toPatch.append(findPatch(machineReturn))) return false; + } } ReleaseScriptCode(cx, script); /* No need to actually compile or fixup if no frames on the stack */ if (!firstFrame) return true; + /* If we get this far, the script is live, and we better be safe to re-jit. */ + JS_ASSERT(cx->compartment->debugMode); + Compiler c(cx, script, firstFrame->fun, firstFrame->scopeChain); if (c.Compile() != Compile_Okay) return false; /* Perform the earlier scanned patches */ for (uint32 i = 0; i < toPatch.length(); i++) applyPatch(c, toPatch[i]); return true; } -FrameIterator& -FrameIterator::operator++() { - JS_ASSERT(curfp); - curfp = curfp->down; - return *this; -} - -bool -FrameIterator::operator==(const FrameIterator& other) const { - return curfp == other.curfp; -} - -bool -FrameIterator::operator!=(const FrameIterator& other) const { - return curfp != other.curfp; -} - -CallStackIterator& -CallStackIterator::operator++() { - JS_ASSERT(curcs); - curcs = curcs->getPreviousInMemory(); - return *this; -} - -FrameIterator -CallStackIterator::top() const { - JS_ASSERT(curcs); - return FrameIterator(curcs->getCurrentFrame()); -} - -FrameIterator -CallStackIterator::bottom() const { - JS_ASSERT(curcs); - return FrameIterator(curcs->getInitialFrame()->down); -} - } /* namespace mjit */ } /* namespace js */ #endif /* JS_METHODJIT */
--- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -96,48 +96,13 @@ public: private: JSContext *cx; JSScript *script; PatchableAddress findPatch(void **location); void applyPatch(Compiler& c, PatchableAddress& toPatch); }; -/* - * Utility classes to make iteration over the stack clean. - */ -class FrameIterator -{ -public: - FrameIterator(JSStackFrame *frame) : curfp(frame) { }; - - bool done() const { return curfp == NULL; } - FrameIterator& operator++(); - bool operator==(const FrameIterator& other) const; - bool operator!=(const FrameIterator& other) const; - - JSStackFrame *fp() const { return curfp; } - -private: - JSStackFrame *curfp; -}; - -class CallStackIterator -{ -public: - CallStackIterator(JSContext *cx) : curcs(cx->stack().getCurrentSegment()) { }; - - bool done() const { return curcs == NULL; } - CallStackIterator& operator++(); - FrameIterator top() const; - FrameIterator bottom() const; - - CallStackSegment *cs() const { return curcs; } - -private: - CallStackSegment *curcs; -}; - } /* namespace mjit */ } /* namespace js */ #endif
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1466,16 +1466,30 @@ ValueToScript(JSContext *cx, jsval v) JSSMSG_SCRIPTS_ONLY); } } return script; } static JSBool +SetDebug(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (argc == 0 || !JSVAL_IS_BOOLEAN(argv[0])) { + JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, + JSSMSG_NOT_ENOUGH_ARGS, "setDebug"); + return JS_FALSE; + } + + js_SetDebugMode(cx, JSVAL_TO_BOOLEAN(argv[0])); + *rval = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp, int32 *ip) { jsval v; uintN intarg; JSScript *script; *scriptp = JS_GetScriptedCaller(cx, NULL)->script; @@ -3978,16 +3992,17 @@ static JSFunctionSpec shell_functions[] #ifdef JS_GCMETER JS_FN("gcstats", GCStats, 0,0), #endif JS_FN("gcparam", GCParameter, 2,0), JS_FN("countHeap", CountHeap, 0,0), #ifdef JS_GC_ZEAL JS_FN("gczeal", GCZeal, 1,0), #endif + JS_FS("setDebug", SetDebug, 1,0,0), JS_FS("trap", Trap, 3,0,0), JS_FS("untrap", Untrap, 2,0,0), JS_FS("line2pc", LineToPC, 0,0,0), JS_FS("pc2line", PCToLine, 0,0,0), JS_FN("stackQuota", StackQuota, 0,0), JS_FS("stringsAreUTF8", StringsAreUTF8, 0,0,0), JS_FS("testUTF8", TestUTF8, 1,0,0), JS_FS("throwError", ThrowError, 0,0,0), @@ -4083,16 +4098,17 @@ static const char *const shell_help_mess "countHeap([start[, kind]])\n" " Count the number of live GC things in the heap or things reachable from\n" " start when it is given and is not null. kind is either 'all' (default) to\n" " count all things or one of 'object', 'double', 'string', 'function',\n" " 'qname', 'namespace', 'xml' to count only things of that kind", #ifdef JS_GC_ZEAL "gczeal(level) How zealous the garbage collector should be", #endif +"setDebug(debug) Set debug mode", "trap([fun, [pc,]] exp) Trap bytecode execution", "untrap(fun[, pc]) Remove a trap", "line2pc([fun,] line) Map line number to PC", "pc2line(fun[, pc]) Map PC to line number", "stackQuota([number]) Query/set script stack quota", "stringsAreUTF8() Check if strings are UTF-8 encoded", "testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", "throwError() Throw an error from JS_ReportError",
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/basic/testBug552248.js @@ -0,0 +1,36 @@ +setDebug(true); +var a = new Array(); + +function i(save) { + var x = 9; + evalInFrame(0, "a.push(x)", save); + evalInFrame(1, "a.push(z)", save); + evalInFrame(2, "a.push(z)", save); + evalInFrame(3, "a.push(y)", save); + evalInFrame(4, "a.push(x)", save); +} + +function h() { + var z = 5; + evalInFrame(0, "a.push(z)"); + evalInFrame(1, "a.push(y)"); + evalInFrame(2, "a.push(x)"); + evalInFrame(0, "i(false)"); + evalInFrame(0, "a.push(z)", true); + evalInFrame(1, "a.push(y)", true); + evalInFrame(2, "a.push(x)", true); + evalInFrame(0, "i(true)", true); +} + +function g() { + var y = 4; + h(); +} + +function f() { + var x = 3; + g(); +} + +f(); +assertEq(a+'', [5, 4, 3, 9, 5, 5, 4, 3, 5, 4, 3, 9, 5, 5, 4, 3]+'');
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-call-newvar.js @@ -0,0 +1,13 @@ +setDebug(true); + +function callee() { + assertJit(); + evalInFrame(1, "var x = 'success'"); +} +function caller() { + assertJit(); + callee(); + return x; +} +assertEq(caller(), "success"); +assertEq(typeof x, "undefined");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-call-typechange.js @@ -0,0 +1,13 @@ +setDebug(true); + +function callee() { + assertJit(); + evalInFrame(1, "x = 'success'"); +} +function caller() { + assertJit(); + var x = ({ dana : "zuul" }); + callee(); + return x; +} +assertEq(caller(), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-call.js @@ -0,0 +1,13 @@ +setDebug(true); + +function callee() { + assertJit(); + evalInFrame(1, "x = 'success'"); +} +function caller() { + assertJit(); + var x = "failure"; + callee(); + return x; +} +assertEq(caller(), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-getter-newvar.js @@ -0,0 +1,9 @@ +setDebug(true); + +__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); }); +function caller(obj) { + assertJit(); + obj.someProperty; + return x; +} +assertEq(caller(this), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-getter-typechange.js @@ -0,0 +1,10 @@ +setDebug(true); + +__defineGetter__("someProperty", function () { evalInFrame(1, "var x = 'success'"); }); +function caller(obj) { + assertJit(); + var x = ({ dana : 'zuul' }); + obj.someProperty; + return x; +} +assertEq(caller(this), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-getter.js @@ -0,0 +1,10 @@ +setDebug(true); + +__defineGetter__("someProperty", function () { evalInFrame(1, "x = 'success'"); }); +function caller(obj) { + assertJit(); + var x = "failure"; + obj.someProperty; + return x; +} +assertEq(caller(this), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-newvar.js @@ -0,0 +1,9 @@ +setDebug(true); + +function nop(){} +function caller(obj) { + assertJit(); + return x; +} +trap(caller, 9, "var x = 'success'; nop()"); +assertEq(caller(this), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap-typechange.js @@ -0,0 +1,10 @@ +setDebug(true); + +function nop(){} +function caller(obj) { + assertJit(); + var x = ({ dana : "zuul" }); + return x; +} +trap(caller, 22, "x = 'success'; nop()"); +assertEq(caller(this), "success");
new file mode 100644 --- /dev/null +++ b/js/src/trace-test/tests/jaeger/bug563000/eif-trap.js @@ -0,0 +1,10 @@ +setDebug(true); + +function nop(){} +function caller(obj) { + assertJit(); + var x = "failure"; + return x; +} +trap(caller, 16, "x = 'success'; nop()"); +assertEq(caller(this), "success");
--- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-1.js @@ -1,8 +1,9 @@ +setDebug(true); var x = "failure"; function main() { x = "success"; } /* The JSOP_STOP in a. */ trap(main, 8, ""); main(); assertEq(x, "success");
--- a/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-trap-2.js @@ -1,8 +1,9 @@ +setDebug(true); var x = "notset"; function main() { x = "failure"; } function success() { x = "success"; } /* The JSOP_STOP in a. */ trap(main, 8, "success()"); main();
--- a/js/src/trace-test/tests/jaeger/bug563000/simple-untrap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/simple-untrap.js @@ -1,8 +1,9 @@ +setDebug(true); var x = "notset"; function main() { x = "success"; } function failure() { x = "failure"; } /* The JSOP_STOP in a. */ trap(main, 8, "failure()"); untrap(main, 8); main();
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js @@ -1,6 +1,7 @@ +setDebug(true); function main() { return "failure"; } /* JSOP_RETURN in main. */ trap(main, 4, "'success'"); assertEq(main(), "success");
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-2.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-2.js @@ -1,6 +1,7 @@ +setDebug(true); function main() { return 1; } /* JSOP_RETURN in main. */ trap(main, 2, "0"); assertEq(main(), 0);
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-own-callsite.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function myparent(nested) { if (nested) { /* myparent call in myparent. */ trap(myparent, 40, "failure()"); } else { x = "success"; myparent(true);
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function child() { x = "failure1"; /* JSOP_STOP in parent. */ trap(parent, 11, "success()"); }
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-parent.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function child() { /* JSOP_STOP in parent. */ trap(parent, 19, "success()"); } function parent() { child(); x = "failure";
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-as-parent.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function myparent(nested) { if (nested) { /* noop call in myparent */ trap(myparent, 48, "success()"); } else { myparent(true);
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function doNothing() { } function myparent(nested) { if (nested) { /* JSOP_CALL to doNothing in myparent with nested = true. */ trap(myparent, 26, "success()");
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-self.js +++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function main() { /* The JSOP_STOP in a. */ trap(main, 27, "success()"); x = "failure"; } function success() { x = "success"; }
--- a/js/src/trace-test/tests/jaeger/bug563000/untrap-own-trapsite.js +++ b/js/src/trace-test/tests/jaeger/bug563000/untrap-own-trapsite.js @@ -1,8 +1,9 @@ +setDebug(true); x = "notset"; function child() { /* JSOP_STOP in parent */ untrap(parent, 11); x = "success"; } function parent() { x = "failure";