js/src/vm/OldDebugAPI.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * JS debugging API.
     9  */
    11 #include "js/OldDebugAPI.h"
    13 #include <string.h>
    15 #include "jscntxt.h"
    16 #include "jsfun.h"
    17 #include "jsgc.h"
    18 #include "jsobj.h"
    19 #include "jsopcode.h"
    20 #include "jsprf.h"
    21 #include "jsscript.h"
    22 #include "jsstr.h"
    23 #include "jstypes.h"
    24 #include "jswatchpoint.h"
    26 #include "frontend/SourceNotes.h"
    27 #include "jit/AsmJSModule.h"
    28 #include "vm/Debugger.h"
    29 #include "vm/Shape.h"
    31 #include "jsatominlines.h"
    32 #include "jsinferinlines.h"
    33 #include "jsscriptinlines.h"
    35 #include "vm/Debugger-inl.h"
    36 #include "vm/Interpreter-inl.h"
    37 #include "vm/Stack-inl.h"
    39 using namespace js;
    40 using namespace js::gc;
    42 using mozilla::PodZero;
    44 JS_PUBLIC_API(bool)
    45 JS_GetDebugMode(JSContext *cx)
    46 {
    47     return cx->compartment()->debugMode();
    48 }
    50 JS_PUBLIC_API(bool)
    51 JS_SetDebugMode(JSContext *cx, bool debug)
    52 {
    53     return JS_SetDebugModeForCompartment(cx, cx->compartment(), debug);
    54 }
    56 JS_PUBLIC_API(void)
    57 JS_SetRuntimeDebugMode(JSRuntime *rt, bool debug)
    58 {
    59     rt->debugMode = !!debug;
    60 }
    62 static bool
    63 IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
    64 {
    65     ScriptFrameIter iter(cx);
    66     JS_ASSERT(iter.abstractFramePtr() == frame);
    67     return iter.isConstructing();
    68 }
    70 JSTrapStatus
    71 js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
    72 {
    73     JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
    75     if (!frame.script()->selfHosted()) {
    76         JSAbstractFramePtr jsframe(frame.raw(), pc);
    77         if (frame.isFramePushedByExecute()) {
    78             if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
    79                 frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
    80                                        true, 0, cx->runtime()->debugHooks.executeHookData));
    81         } else {
    82             if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
    83                 frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
    84                                        true, 0, cx->runtime()->debugHooks.callHookData));
    85         }
    86     }
    88     RootedValue rval(cx);
    89     JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval);
    90     switch (status) {
    91       case JSTRAP_CONTINUE:
    92         break;
    93       case JSTRAP_THROW:
    94         cx->setPendingException(rval);
    95         break;
    96       case JSTRAP_ERROR:
    97         cx->clearPendingException();
    98         break;
    99       case JSTRAP_RETURN:
   100         frame.setReturnValue(rval);
   101         break;
   102       default:
   103         MOZ_ASSUME_UNREACHABLE("bad Debugger::onEnterFrame JSTrapStatus value");
   104     }
   105     return status;
   106 }
   108 bool
   109 js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
   110 {
   111     JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
   113     bool ok = okArg;
   115     // We don't add hook data for self-hosted scripts, so we don't need to check for them, here.
   116     if (void *hookData = frame.maybeHookData()) {
   117         JSAbstractFramePtr jsframe(frame.raw(), pc);
   118         if (frame.isFramePushedByExecute()) {
   119             if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
   120                 hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
   121         } else {
   122             if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
   123                 hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
   124         }
   125     }
   127     return Debugger::onLeaveFrame(cx, frame, ok);
   128 }
   130 JSTrapStatus
   131 js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
   132 {
   133     JS_ASSERT(cx->compartment()->debugMode());
   135     if (!cx->runtime()->debugHooks.throwHook && cx->compartment()->getDebuggees().empty())
   136         return JSTRAP_CONTINUE;
   138     /* Call debugger throw hook if set. */
   139     RootedValue rval(cx);
   140     JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval);
   141     if (status == JSTRAP_CONTINUE) {
   142         if (JSThrowHook handler = cx->runtime()->debugHooks.throwHook) {
   143             RootedScript script(cx, frame.script());
   144             status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.throwHookData);
   145         }
   146     }
   148     switch (status) {
   149       case JSTRAP_ERROR:
   150         cx->clearPendingException();
   151         break;
   153       case JSTRAP_RETURN:
   154         cx->clearPendingException();
   155         frame.setReturnValue(rval);
   156         break;
   158       case JSTRAP_THROW:
   159         cx->setPendingException(rval);
   160         break;
   162       case JSTRAP_CONTINUE:
   163         break;
   165       default:
   166         MOZ_ASSUME_UNREACHABLE("Invalid trap status");
   167     }
   169     return status;
   170 }
   172 JS_FRIEND_API(bool)
   173 JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
   174 {
   175     for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
   176         // Invalidate a zone at a time to avoid doing a zone-wide CellIter
   177         // per compartment.
   178         AutoDebugModeInvalidation invalidate(zone);
   179         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
   180             // Ignore special compartments (atoms, JSD compartments)
   181             if (c->principals) {
   182                 if (!c->setDebugModeFromC(cx, !!debug, invalidate))
   183                     return false;
   184             }
   185         }
   186     }
   187     return true;
   188 }
   190 JS_FRIEND_API(bool)
   191 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
   192 {
   193     AutoDebugModeInvalidation invalidate(comp);
   194     return comp->setDebugModeFromC(cx, !!debug, invalidate);
   195 }
   197 static bool
   198 CheckDebugMode(JSContext *cx)
   199 {
   200     bool debugMode = JS_GetDebugMode(cx);
   201     /*
   202      * :TODO:
   203      * This probably should be an assertion, since it's indicative of a severe
   204      * API misuse.
   205      */
   206     if (!debugMode) {
   207         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
   208                                      nullptr, JSMSG_NEED_DEBUG_MODE);
   209     }
   210     return debugMode;
   211 }
   213 JS_PUBLIC_API(bool)
   214 JS_SetSingleStepMode(JSContext *cx, HandleScript script, bool singleStep)
   215 {
   216     assertSameCompartment(cx, script);
   218     if (!CheckDebugMode(cx))
   219         return false;
   221     return script->setStepModeFlag(cx, singleStep);
   222 }
   224 JS_PUBLIC_API(bool)
   225 JS_SetTrap(JSContext *cx, HandleScript script, jsbytecode *pc, JSTrapHandler handler,
   226            HandleValue closure)
   227 {
   228     assertSameCompartment(cx, script, closure);
   230     if (!CheckDebugMode(cx))
   231         return false;
   233     BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
   234     if (!site)
   235         return false;
   236     site->setTrap(cx->runtime()->defaultFreeOp(), handler, closure);
   237     return true;
   238 }
   240 JS_PUBLIC_API(void)
   241 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
   242              JSTrapHandler *handlerp, jsval *closurep)
   243 {
   244     if (BreakpointSite *site = script->getBreakpointSite(pc)) {
   245         site->clearTrap(cx->runtime()->defaultFreeOp(), handlerp, closurep);
   246     } else {
   247         if (handlerp)
   248             *handlerp = nullptr;
   249         if (closurep)
   250             *closurep = JSVAL_VOID;
   251     }
   252 }
   254 JS_PUBLIC_API(void)
   255 JS_ClearScriptTraps(JSRuntime *rt, JSScript *script)
   256 {
   257     script->clearTraps(rt->defaultFreeOp());
   258 }
   260 JS_PUBLIC_API(void)
   261 JS_ClearAllTrapsForCompartment(JSContext *cx)
   262 {
   263     cx->compartment()->clearTraps(cx->runtime()->defaultFreeOp());
   264 }
   266 JS_PUBLIC_API(bool)
   267 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
   268 {
   269     rt->debugHooks.interruptHook = hook;
   270     rt->debugHooks.interruptHookData = closure;
   272     for (ActivationIterator iter(rt); !iter.done(); ++iter) {
   273         if (iter->isInterpreter())
   274             iter->asInterpreter()->enableInterruptsUnconditionally();
   275     }
   277     return true;
   278 }
   280 JS_PUBLIC_API(bool)
   281 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
   282 {
   283     if (hoop)
   284         *hoop = rt->debugHooks.interruptHook;
   285     if (closurep)
   286         *closurep = rt->debugHooks.interruptHookData;
   287     rt->debugHooks.interruptHook = 0;
   288     rt->debugHooks.interruptHookData = 0;
   289     return true;
   290 }
   292 /************************************************************************/
   294 JS_PUBLIC_API(bool)
   295 JS_SetWatchPoint(JSContext *cx, HandleObject origobj, HandleId id,
   296                  JSWatchPointHandler handler, HandleObject closure)
   297 {
   298     assertSameCompartment(cx, origobj);
   300     RootedObject obj(cx, GetInnerObject(cx, origobj));
   301     if (!obj)
   302         return false;
   304     RootedId propid(cx);
   306     if (JSID_IS_INT(id)) {
   307         propid = id;
   308     } else if (JSID_IS_OBJECT(id)) {
   309         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP);
   310         return false;
   311     } else {
   312         RootedValue val(cx, IdToValue(id));
   313         if (!ValueToId<CanGC>(cx, val, &propid))
   314             return false;
   315     }
   317     if (!obj->isNative() || obj->is<TypedArrayObject>()) {
   318         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
   319                              obj->getClass()->name);
   320         return false;
   321     }
   323     /*
   324      * Use sparse indexes for watched objects, as dense elements can be written
   325      * to without checking the watchpoint map.
   326      */
   327     if (!JSObject::sparsifyDenseElements(cx, obj))
   328         return false;
   330     types::MarkTypePropertyNonData(cx, obj, propid);
   332     WatchpointMap *wpmap = cx->compartment()->watchpointMap;
   333     if (!wpmap) {
   334         wpmap = cx->runtime()->new_<WatchpointMap>();
   335         if (!wpmap || !wpmap->init()) {
   336             js_ReportOutOfMemory(cx);
   337             return false;
   338         }
   339         cx->compartment()->watchpointMap = wpmap;
   340     }
   341     return wpmap->watch(cx, obj, propid, handler, closure);
   342 }
   344 JS_PUBLIC_API(bool)
   345 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
   346                    JSWatchPointHandler *handlerp, JSObject **closurep)
   347 {
   348     assertSameCompartment(cx, obj, id);
   350     if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
   351         wpmap->unwatch(obj, id, handlerp, closurep);
   352     return true;
   353 }
   355 JS_PUBLIC_API(bool)
   356 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
   357 {
   358     assertSameCompartment(cx, obj);
   360     if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
   361         wpmap->unwatchObject(obj);
   362     return true;
   363 }
   365 /************************************************************************/
   367 JS_PUBLIC_API(unsigned)
   368 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
   369 {
   370     return js::PCToLineNumber(script, pc);
   371 }
   373 JS_PUBLIC_API(jsbytecode *)
   374 JS_LineNumberToPC(JSContext *cx, JSScript *script, unsigned lineno)
   375 {
   376     return js_LineNumberToPC(script, lineno);
   377 }
   379 JS_PUBLIC_API(jsbytecode *)
   380 JS_EndPC(JSContext *cx, JSScript *script)
   381 {
   382     return script->codeEnd();
   383 }
   385 JS_PUBLIC_API(bool)
   386 JS_GetLinePCs(JSContext *cx, JSScript *script,
   387               unsigned startLine, unsigned maxLines,
   388               unsigned* count, unsigned** retLines, jsbytecode*** retPCs)
   389 {
   390     size_t len = (script->length() > maxLines ? maxLines : script->length());
   391     unsigned *lines = cx->pod_malloc<unsigned>(len);
   392     if (!lines)
   393         return false;
   395     jsbytecode **pcs = cx->pod_malloc<jsbytecode*>(len);
   396     if (!pcs) {
   397         js_free(lines);
   398         return false;
   399     }
   401     unsigned lineno = script->lineno();
   402     unsigned offset = 0;
   403     unsigned i = 0;
   404     for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
   405         offset += SN_DELTA(sn);
   406         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
   407         if (type == SRC_SETLINE || type == SRC_NEWLINE) {
   408             if (type == SRC_SETLINE)
   409                 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
   410             else
   411                 lineno++;
   413             if (lineno >= startLine) {
   414                 lines[i] = lineno;
   415                 pcs[i] = script->offsetToPC(offset);
   416                 if (++i >= maxLines)
   417                     break;
   418             }
   419         }
   420     }
   422     *count = i;
   423     if (retLines)
   424         *retLines = lines;
   425     else
   426         js_free(lines);
   428     if (retPCs)
   429         *retPCs = pcs;
   430     else
   431         js_free(pcs);
   433     return true;
   434 }
   436 JS_PUBLIC_API(unsigned)
   437 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
   438 {
   439     return fun->nargs();
   440 }
   442 JS_PUBLIC_API(bool)
   443 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
   444 {
   445     return fun->nonLazyScript()->bindings.count() > 0;
   446 }
   448 extern JS_PUBLIC_API(uintptr_t *)
   449 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **memp)
   450 {
   451     RootedScript script(cx, fun->nonLazyScript());
   452     BindingVector bindings(cx);
   453     if (!FillBindingVector(script, &bindings))
   454         return nullptr;
   456     LifoAlloc &lifo = cx->tempLifoAlloc();
   458     // Store the LifoAlloc::Mark right before the allocation.
   459     LifoAlloc::Mark mark = lifo.mark();
   460     void *mem = lifo.alloc(sizeof(LifoAlloc::Mark) + bindings.length() * sizeof(uintptr_t));
   461     if (!mem) {
   462         js_ReportOutOfMemory(cx);
   463         return nullptr;
   464     }
   465     *memp = mem;
   466     *reinterpret_cast<LifoAlloc::Mark*>(mem) = mark;
   468     // Munge data into the API this method implements.  Avert your eyes!
   469     uintptr_t *names = reinterpret_cast<uintptr_t*>((char*)mem + sizeof(LifoAlloc::Mark));
   470     for (size_t i = 0; i < bindings.length(); i++)
   471         names[i] = reinterpret_cast<uintptr_t>(bindings[i].name());
   473     return names;
   474 }
   476 extern JS_PUBLIC_API(JSAtom *)
   477 JS_LocalNameToAtom(uintptr_t w)
   478 {
   479     return reinterpret_cast<JSAtom *>(w);
   480 }
   482 extern JS_PUBLIC_API(JSString *)
   483 JS_AtomKey(JSAtom *atom)
   484 {
   485     return atom;
   486 }
   488 extern JS_PUBLIC_API(void)
   489 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mem)
   490 {
   491     cx->tempLifoAlloc().release(*reinterpret_cast<LifoAlloc::Mark*>(mem));
   492 }
   494 JS_PUBLIC_API(JSScript *)
   495 JS_GetFunctionScript(JSContext *cx, HandleFunction fun)
   496 {
   497     if (fun->isNative())
   498         return nullptr;
   499     if (fun->isInterpretedLazy()) {
   500         AutoCompartment funCompartment(cx, fun);
   501         JSScript *script = fun->getOrCreateScript(cx);
   502         if (!script)
   503             MOZ_CRASH();
   504         return script;
   505     }
   506     return fun->nonLazyScript();
   507 }
   509 JS_PUBLIC_API(JSNative)
   510 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
   511 {
   512     return fun->maybeNative();
   513 }
   515 JS_PUBLIC_API(JSPrincipals *)
   516 JS_GetScriptPrincipals(JSScript *script)
   517 {
   518     return script->principals();
   519 }
   521 JS_PUBLIC_API(JSPrincipals *)
   522 JS_GetScriptOriginPrincipals(JSScript *script)
   523 {
   524     return script->originPrincipals();
   525 }
   527 /************************************************************************/
   529 JS_PUBLIC_API(JSFunction *)
   530 JS_GetScriptFunction(JSContext *cx, JSScript *script)
   531 {
   532     script->ensureNonLazyCanonicalFunction(cx);
   533     return script->functionNonDelazifying();
   534 }
   536 JS_PUBLIC_API(JSObject *)
   537 JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
   538 {
   539     return obj->enclosingScope();
   540 }
   542 JS_PUBLIC_API(const char *)
   543 JS_GetDebugClassName(JSObject *obj)
   544 {
   545     if (obj->is<DebugScopeObject>())
   546         return obj->as<DebugScopeObject>().scope().getClass()->name;
   547     return obj->getClass()->name;
   548 }
   550 /************************************************************************/
   552 JS_PUBLIC_API(const char *)
   553 JS_GetScriptFilename(JSScript *script)
   554 {
   555     return script->filename();
   556 }
   558 JS_PUBLIC_API(const jschar *)
   559 JS_GetScriptSourceMap(JSContext *cx, JSScript *script)
   560 {
   561     ScriptSource *source = script->scriptSource();
   562     JS_ASSERT(source);
   563     return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr;
   564 }
   566 JS_PUBLIC_API(unsigned)
   567 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
   568 {
   569     return script->lineno();
   570 }
   572 JS_PUBLIC_API(unsigned)
   573 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
   574 {
   575     return js_GetScriptLineExtent(script);
   576 }
   578 JS_PUBLIC_API(JSVersion)
   579 JS_GetScriptVersion(JSContext *cx, JSScript *script)
   580 {
   581     return VersionNumber(script->getVersion());
   582 }
   584 JS_PUBLIC_API(bool)
   585 JS_GetScriptIsSelfHosted(JSScript *script)
   586 {
   587     return script->selfHosted();
   588 }
   590 /***************************************************************************/
   592 JS_PUBLIC_API(void)
   593 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
   594 {
   595     rt->debugHooks.newScriptHook = hook;
   596     rt->debugHooks.newScriptHookData = callerdata;
   597 }
   599 JS_PUBLIC_API(void)
   600 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
   601                         void *callerdata)
   602 {
   603     rt->debugHooks.destroyScriptHook = hook;
   604     rt->debugHooks.destroyScriptHookData = callerdata;
   605 }
   607 /***************************************************************************/
   609 /* This all should be reworked to avoid requiring JSScopeProperty types. */
   611 static bool
   612 GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd)
   613 {
   614     assertSameCompartment(cx, obj_);
   615     pd->id = IdToValue(shape->propid());
   617     RootedObject obj(cx, obj_);
   619     bool wasThrowing = cx->isExceptionPending();
   620     RootedValue lastException(cx, UndefinedValue());
   621     if (wasThrowing) {
   622         if (!cx->getPendingException(&lastException))
   623             return false;
   624     }
   625     cx->clearPendingException();
   627     Rooted<jsid> id(cx, shape->propid());
   628     RootedValue value(cx);
   629     if (!baseops::GetProperty(cx, obj, id, &value)) {
   630         if (!cx->isExceptionPending()) {
   631             pd->flags = JSPD_ERROR;
   632             pd->value = JSVAL_VOID;
   633         } else {
   634             pd->flags = JSPD_EXCEPTION;
   635             if (!cx->getPendingException(&value))
   636                 return false;
   637             pd->value = value;
   638         }
   639     } else {
   640         pd->flags = 0;
   641         pd->value = value;
   642     }
   644     if (wasThrowing)
   645         cx->setPendingException(lastException);
   647     pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
   648               |  (!shape->writable()  ? JSPD_READONLY  : 0)
   649               |  (!shape->configurable() ? JSPD_PERMANENT : 0);
   650     pd->spare = 0;
   651     pd->alias = JSVAL_VOID;
   653     return true;
   654 }
   656 JS_PUBLIC_API(bool)
   657 JS_GetPropertyDescArray(JSContext *cx, JS::HandleObject obj, JSPropertyDescArray *pda)
   658 {
   659     assertSameCompartment(cx, obj);
   660     uint32_t i = 0;
   661     JSPropertyDesc *pd = nullptr;
   663     if (obj->is<DebugScopeObject>()) {
   664         AutoIdVector props(cx);
   665         if (!Proxy::enumerate(cx, obj, props))
   666             return false;
   668         pd = cx->pod_calloc<JSPropertyDesc>(props.length());
   669         if (!pd)
   670             return false;
   672         for (i = 0; i < props.length(); ++i) {
   673             pd[i].id = JSVAL_NULL;
   674             pd[i].value = JSVAL_NULL;
   675             if (!AddValueRoot(cx, &pd[i].id, nullptr))
   676                 goto bad;
   677             pd[i].id = IdToValue(props[i]);
   678             if (!AddValueRoot(cx, &pd[i].value, nullptr))
   679                 goto bad;
   680             if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value)))
   681                 goto bad;
   682         }
   684         pda->length = props.length();
   685         pda->array = pd;
   686         return true;
   687     }
   689     const Class *clasp;
   690     clasp = obj->getClass();
   691     if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
   692         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   693                              JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
   694         return false;
   695     }
   696     if (!clasp->enumerate(cx, obj))
   697         return false;
   699     /* Return an empty pda early if obj has no own properties. */
   700     if (obj->nativeEmpty()) {
   701         pda->length = 0;
   702         pda->array = nullptr;
   703         return true;
   704     }
   706     pd = cx->pod_malloc<JSPropertyDesc>(obj->propertyCount());
   707     if (!pd)
   708         return false;
   710     {
   711         Shape::Range<CanGC> r(cx, obj->lastProperty());
   712         RootedShape shape(cx);
   713         for (; !r.empty(); r.popFront()) {
   714             pd[i].id = JSVAL_NULL;
   715             pd[i].value = JSVAL_NULL;
   716             pd[i].alias = JSVAL_NULL;
   717             if (!AddValueRoot(cx, &pd[i].id, nullptr))
   718                 goto bad;
   719             if (!AddValueRoot(cx, &pd[i].value, nullptr))
   720                 goto bad;
   721             shape = const_cast<Shape *>(&r.front());
   722             if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
   723                 goto bad;
   724             if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, nullptr))
   725                 goto bad;
   726             if (++i == obj->propertyCount())
   727                 break;
   728         }
   729     }
   731     pda->length = i;
   732     pda->array = pd;
   733     return true;
   735 bad:
   736     pda->length = i + 1;
   737     pda->array = pd;
   738     JS_PutPropertyDescArray(cx, pda);
   739     return false;
   740 }
   742 JS_PUBLIC_API(void)
   743 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
   744 {
   745     JSPropertyDesc *pd;
   746     uint32_t i;
   748     pd = pda->array;
   749     for (i = 0; i < pda->length; i++) {
   750         RemoveRoot(cx->runtime(), &pd[i].id);
   751         RemoveRoot(cx->runtime(), &pd[i].value);
   752         if (pd[i].flags & JSPD_ALIAS)
   753             RemoveRoot(cx->runtime(), &pd[i].alias);
   754     }
   755     js_free(pd);
   756     pda->array = nullptr;
   757     pda->length = 0;
   758 }
   760 /************************************************************************/
   762 JS_PUBLIC_API(bool)
   763 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
   764 {
   765     rt->debugHooks.debuggerHandler = handler;
   766     rt->debugHooks.debuggerHandlerData = closure;
   767     return true;
   768 }
   770 JS_PUBLIC_API(bool)
   771 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
   772 {
   773     rt->debugHooks.sourceHandler = handler;
   774     rt->debugHooks.sourceHandlerData = closure;
   775     return true;
   776 }
   778 JS_PUBLIC_API(bool)
   779 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
   780 {
   781     rt->debugHooks.executeHook = hook;
   782     rt->debugHooks.executeHookData = closure;
   783     return true;
   784 }
   786 JS_PUBLIC_API(bool)
   787 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
   788 {
   789     rt->debugHooks.callHook = hook;
   790     rt->debugHooks.callHookData = closure;
   791     return true;
   792 }
   794 JS_PUBLIC_API(bool)
   795 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
   796 {
   797     rt->debugHooks.throwHook = hook;
   798     rt->debugHooks.throwHookData = closure;
   799     return true;
   800 }
   802 JS_PUBLIC_API(bool)
   803 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
   804 {
   805     rt->debugHooks.debugErrorHook = hook;
   806     rt->debugHooks.debugErrorHookData = closure;
   807     return true;
   808 }
   810 /************************************************************************/
   812 JS_PUBLIC_API(const JSDebugHooks *)
   813 JS_GetGlobalDebugHooks(JSRuntime *rt)
   814 {
   815     return &rt->debugHooks;
   816 }
   818 /************************************************************************/
   820 extern JS_PUBLIC_API(void)
   821 JS_DumpPCCounts(JSContext *cx, HandleScript script)
   822 {
   823     JS_ASSERT(script->hasScriptCounts());
   825     Sprinter sprinter(cx);
   826     if (!sprinter.init())
   827         return;
   829     fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
   830     js_DumpPCCounts(cx, script, &sprinter);
   831     fputs(sprinter.string(), stdout);
   832     fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
   833 }
   835 JS_PUBLIC_API(void)
   836 JS_DumpCompartmentPCCounts(JSContext *cx)
   837 {
   838     for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
   839         RootedScript script(cx, i.get<JSScript>());
   840         if (script->compartment() != cx->compartment())
   841             continue;
   843         if (script->hasScriptCounts())
   844             JS_DumpPCCounts(cx, script);
   845     }
   846 }
   848 JS_FRIEND_API(bool)
   849 js::CanCallContextDebugHandler(JSContext *cx)
   850 {
   851     return !!cx->runtime()->debugHooks.debuggerHandler;
   852 }
   854 static JSTrapStatus
   855 CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval)
   856 {
   857     if (!cx->runtime()->debugHooks.debuggerHandler)
   858         return JSTRAP_RETURN;
   860     return cx->runtime()->debugHooks.debuggerHandler(cx, script, bc, rval,
   861                                                      cx->runtime()->debugHooks.debuggerHandlerData);
   862 }
   864 JS_FRIEND_API(bool)
   865 js_CallContextDebugHandler(JSContext *cx)
   866 {
   867     NonBuiltinFrameIter iter(cx);
   869     // If there is no script to debug, then abort execution even if the user
   870     // clicks 'Debug' in the slow-script dialog.
   871     if (!iter.hasScript())
   872         return false;
   874     // Even if script was running during the operation callback, it's possible
   875     // it was a builtin which 'iter' will have skipped over.
   876     if (iter.done())
   877         return false;
   879     RootedValue rval(cx);
   880     RootedScript script(cx, iter.script());
   881     switch (CallContextDebugHandler(cx, script, iter.pc(), rval.address())) {
   882       case JSTRAP_ERROR:
   883         JS_ClearPendingException(cx);
   884         return false;
   885       case JSTRAP_THROW:
   886         JS_SetPendingException(cx, rval);
   887         return false;
   888       case JSTRAP_RETURN:
   889       case JSTRAP_CONTINUE:
   890       default:
   891         return true;
   892     }
   893 }
   895 /*
   896  * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid
   897  * constructing a FrameDescription on the stack just to append it to a vector.
   898  * FrameDescription contains Heap<T> fields that should not live on the stack.
   899  */
   900 JS::FrameDescription::FrameDescription(const ScriptFrameIter& iter)
   901   : script_(iter.script()),
   902     funDisplayName_(nullptr),
   903     pc_(iter.pc()),
   904     linenoComputed(false)
   905 {
   906     if (JSFunction *fun = iter.maybeCallee())
   907         funDisplayName_ = fun->displayAtom();
   908 }
   910 JS_PUBLIC_API(JS::StackDescription *)
   911 JS::DescribeStack(JSContext *cx, unsigned maxFrames)
   912 {
   913     Vector<FrameDescription> frames(cx);
   915     NonBuiltinScriptFrameIter i(cx, ScriptFrameIter::ALL_CONTEXTS,
   916                                 ScriptFrameIter::GO_THROUGH_SAVED,
   917                                 cx->compartment()->principals);
   918     for ( ; !i.done(); ++i) {
   919         if (!frames.append(i))
   920             return nullptr;
   921         if (frames.length() == maxFrames)
   922             break;
   923     }
   925     JS::StackDescription *desc = js_new<JS::StackDescription>();
   926     if (!desc)
   927         return nullptr;
   929     desc->nframes = frames.length();
   930     desc->frames = frames.extractRawBuffer();
   931     return desc;
   932 }
   934 JS_PUBLIC_API(void)
   935 JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc)
   936 {
   937     for (size_t i = 0; i < desc->nframes; ++i)
   938         desc->frames[i].~FrameDescription();
   939     js_free(desc->frames);
   940     js_delete(desc);
   941 }
   943 namespace {
   945 class AutoPropertyDescArray
   946 {
   947     JSContext *cx_;
   948     JSPropertyDescArray descArray_;
   950   public:
   951     AutoPropertyDescArray(JSContext *cx)
   952       : cx_(cx)
   953     {
   954         PodZero(&descArray_);
   955     }
   956     ~AutoPropertyDescArray()
   957     {
   958         if (descArray_.array)
   959             JS_PutPropertyDescArray(cx_, &descArray_);
   960     }
   962     void fetch(JS::HandleObject obj) {
   963         JS_ASSERT(!descArray_.array);
   964         if (!JS_GetPropertyDescArray(cx_, obj, &descArray_))
   965             descArray_.array = nullptr;
   966     }
   968     JSPropertyDescArray * operator ->() {
   969         return &descArray_;
   970     }
   971 };
   973 } /* anonymous namespace */
   975 static const char *
   976 FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
   977 {
   978     RootedValue v(cx, vArg);
   980     /*
   981      * We could use Maybe<AutoCompartment> here, but G++ can't quite follow
   982      * that, and warns about uninitialized members being used in the
   983      * destructor.
   984      */
   985     RootedString str(cx);
   986     if (v.isObject()) {
   987         AutoCompartment ac(cx, &v.toObject());
   988         str = ToString<CanGC>(cx, v);
   989     } else {
   990         str = ToString<CanGC>(cx, v);
   991     }
   993     if (!str)
   994         return nullptr;
   995     const char *buf = bytes.encodeLatin1(cx, str);
   996     if (!buf)
   997         return nullptr;
   998     const char *found = strstr(buf, "function ");
   999     if (found && (found - buf <= 2))
  1000         return "[function]";
  1001     return buf;
  1004 static char *
  1005 FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num,
  1006             bool showArgs, bool showLocals, bool showThisProps)
  1008     JS_ASSERT(!cx->isExceptionPending());
  1009     RootedScript script(cx, iter.script());
  1010     jsbytecode* pc = iter.pc();
  1012     RootedObject scopeChain(cx, iter.scopeChain());
  1013     JSAutoCompartment ac(cx, scopeChain);
  1015     const char *filename = script->filename();
  1016     unsigned lineno = PCToLineNumber(script, pc);
  1017     RootedFunction fun(cx, iter.maybeCallee());
  1018     RootedString funname(cx);
  1019     if (fun)
  1020         funname = fun->atom();
  1022     RootedValue thisVal(cx);
  1023     AutoPropertyDescArray thisProps(cx);
  1024     if (iter.computeThis(cx)) {
  1025         thisVal = iter.computedThisValue();
  1026         if (showThisProps && !thisVal.isPrimitive()) {
  1027             RootedObject thisObj(cx, &thisVal.toObject());
  1028             thisProps.fetch(thisObj);
  1032     // print the frame number and function name
  1033     if (funname) {
  1034         JSAutoByteString funbytes;
  1035         buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname));
  1036     } else if (fun) {
  1037         buf = JS_sprintf_append(buf, "%d anonymous(", num);
  1038     } else {
  1039         buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
  1041     if (!buf)
  1042         return buf;
  1044     if (showArgs && iter.hasArgs()) {
  1045         BindingVector bindings(cx);
  1046         if (fun && fun->isInterpreted()) {
  1047             if (!FillBindingVector(script, &bindings))
  1048                 return buf;
  1052         bool first = true;
  1053         for (unsigned i = 0; i < iter.numActualArgs(); i++) {
  1054             RootedValue arg(cx);
  1055             if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
  1056                 for (AliasedFormalIter fi(script); ; fi++) {
  1057                     if (fi.frameIndex() == i) {
  1058                         arg = iter.callObj().aliasedVar(fi);
  1059                         break;
  1062             } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
  1063                 arg = iter.argsObj().arg(i);
  1064             } else {
  1065                 arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
  1068             JSAutoByteString valueBytes;
  1069             const char *value = FormatValue(cx, arg, valueBytes);
  1071             JSAutoByteString nameBytes;
  1072             const char *name = nullptr;
  1074             if (i < bindings.length()) {
  1075                 name = nameBytes.encodeLatin1(cx, bindings[i].name());
  1076                 if (!buf)
  1077                     return nullptr;
  1080             if (value) {
  1081                 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
  1082                                         !first ? ", " : "",
  1083                                         name ? name :"",
  1084                                         name ? " = " : "",
  1085                                         arg.isString() ? "\"" : "",
  1086                                         value ? value : "?unknown?",
  1087                                         arg.isString() ? "\"" : "");
  1088                 if (!buf)
  1089                     return buf;
  1091                 first = false;
  1092             } else {
  1093                 buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
  1094                 if (!buf)
  1095                     return buf;
  1096                 cx->clearPendingException();
  1102     // print filename and line number
  1103     buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
  1104                             fun ? ")" : "",
  1105                             filename ? filename : "<unknown>",
  1106                             lineno);
  1107     if (!buf)
  1108         return buf;
  1111     // Note: Right now we don't dump the local variables anymore, because
  1112     // that is hard to support across all the JITs etc.
  1114     // print the value of 'this'
  1115     if (showLocals) {
  1116         if (!thisVal.isUndefined()) {
  1117             JSAutoByteString thisValBytes;
  1118             RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
  1119             const char *str = nullptr;
  1120             if (thisValStr &&
  1121                 (str = thisValBytes.encodeLatin1(cx, thisValStr)))
  1123                 buf = JS_sprintf_append(buf, "    this = %s\n", str);
  1124                 if (!buf)
  1125                     return buf;
  1126             } else {
  1127                 buf = JS_sprintf_append(buf, "    <failed to get 'this' value>\n");
  1128                 cx->clearPendingException();
  1133     // print the properties of 'this', if it is an object
  1134     if (showThisProps && thisProps->array) {
  1135         for (uint32_t i = 0; i < thisProps->length; i++) {
  1136             JSPropertyDesc* desc = &thisProps->array[i];
  1137             if (desc->flags & JSPD_ENUMERATE) {
  1138                 JSAutoByteString nameBytes;
  1139                 JSAutoByteString valueBytes;
  1140                 const char *name = FormatValue(cx, desc->id, nameBytes);
  1141                 const char *value = FormatValue(cx, desc->value, valueBytes);
  1142                 if (name && value) {
  1143                     buf = JS_sprintf_append(buf, "    this.%s = %s%s%s\n",
  1144                                             name,
  1145                                             desc->value.isString() ? "\"" : "",
  1146                                             value,
  1147                                             desc->value.isString() ? "\"" : "");
  1148                     if (!buf)
  1149                         return buf;
  1150                 } else {
  1151                     buf = JS_sprintf_append(buf, "    <Failed to format values while inspecting stack frame>\n");
  1152                     cx->clearPendingException();
  1158     JS_ASSERT(!cx->isExceptionPending());
  1159     return buf;
  1162 JS_PUBLIC_API(char *)
  1163 JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps)
  1165     int num = 0;
  1167     for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
  1168         buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps);
  1169         num++;
  1172     if (!num)
  1173         buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
  1175     return buf;
  1178 JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc)
  1179   : ptr_(uintptr_t(raw)), pc_(pc)
  1180 { }
  1182 JSObject *
  1183 JSAbstractFramePtr::scopeChain(JSContext *cx)
  1185     AbstractFramePtr frame(*this);
  1186     RootedObject scopeChain(cx, frame.scopeChain());
  1187     AutoCompartment ac(cx, scopeChain);
  1188     return GetDebugScopeForFrame(cx, frame, pc());
  1191 JSObject *
  1192 JSAbstractFramePtr::callObject(JSContext *cx)
  1194     AbstractFramePtr frame(*this);
  1195     if (!frame.isFunctionFrame())
  1196         return nullptr;
  1198     JSObject *o = GetDebugScopeForFrame(cx, frame, pc());
  1200     /*
  1201      * Given that fp is a function frame and GetDebugScopeForFrame always fills
  1202      * in missing scopes, we can expect to find fp's CallObject on 'o'. Note:
  1203      *  - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with
  1204      *    a DebugScopeObject proxy.
  1205      *  - If fp is an eval-in-function, then fp has no callobj of its own and
  1206      *    JS_GetFrameCallObject will return the innermost function's callobj.
  1207      */
  1208     while (o) {
  1209         ScopeObject &scope = o->as<DebugScopeObject>().scope();
  1210         if (scope.is<CallObject>())
  1211             return o;
  1212         o = o->enclosingScope();
  1214     return nullptr;
  1217 JSFunction *
  1218 JSAbstractFramePtr::maybeFun()
  1220     AbstractFramePtr frame(*this);
  1221     return frame.maybeFun();
  1224 JSScript *
  1225 JSAbstractFramePtr::script()
  1227     AbstractFramePtr frame(*this);
  1228     return frame.script();
  1231 bool
  1232 JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
  1234     AbstractFramePtr frame(*this);
  1236     RootedObject scopeChain(cx, frame.scopeChain());
  1237     js::AutoCompartment ac(cx, scopeChain);
  1238     if (!ComputeThis(cx, frame))
  1239         return false;
  1241     thisv.set(frame.thisValue());
  1242     return true;
  1245 bool
  1246 JSAbstractFramePtr::isDebuggerFrame()
  1248     AbstractFramePtr frame(*this);
  1249     return frame.isDebuggerFrame();
  1252 bool
  1253 JSAbstractFramePtr::evaluateInStackFrame(JSContext *cx,
  1254                                          const char *bytes, unsigned length,
  1255                                          const char *filename, unsigned lineno,
  1256                                          MutableHandleValue rval)
  1258     if (!CheckDebugMode(cx))
  1259         return false;
  1261     size_t len = length;
  1262     jschar *chars = InflateString(cx, bytes, &len);
  1263     if (!chars)
  1264         return false;
  1265     length = (unsigned) len;
  1267     bool ok = evaluateUCInStackFrame(cx, chars, length, filename, lineno, rval);
  1268     js_free(chars);
  1270     return ok;
  1273 bool
  1274 JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
  1275                                            const jschar *chars, unsigned length,
  1276                                            const char *filename, unsigned lineno,
  1277                                            MutableHandleValue rval)
  1279     if (!CheckDebugMode(cx))
  1280         return false;
  1282     RootedObject scope(cx, scopeChain(cx));
  1283     Rooted<Env*> env(cx, scope);
  1284     if (!env)
  1285         return false;
  1287     AbstractFramePtr frame(*this);
  1288     if (!ComputeThis(cx, frame))
  1289         return false;
  1290     RootedValue thisv(cx, frame.thisValue());
  1292     js::AutoCompartment ac(cx, env);
  1293     return EvaluateInEnv(cx, env, thisv, frame, ConstTwoByteChars(chars, length), length,
  1294                          filename, lineno, rval);
  1297 JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx)
  1299     // Show all frames on the stack whose principal is subsumed by the current principal.
  1300     NonBuiltinScriptFrameIter iter(cx,
  1301                                    ScriptFrameIter::ALL_CONTEXTS,
  1302                                    ScriptFrameIter::GO_THROUGH_SAVED,
  1303                                    cx->compartment()->principals);
  1304     data_ = iter.copyData();
  1307 JSBrokenFrameIterator::~JSBrokenFrameIterator()
  1309     js_free((ScriptFrameIter::Data *)data_);
  1312 bool
  1313 JSBrokenFrameIterator::done() const
  1315     NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1316     return iter.done();
  1319 JSBrokenFrameIterator &
  1320 JSBrokenFrameIterator::operator++()
  1322     ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_;
  1323     NonBuiltinScriptFrameIter iter(*data);
  1324     ++iter;
  1325     *data = iter.data_;
  1326     return *this;
  1329 JSAbstractFramePtr
  1330 JSBrokenFrameIterator::abstractFramePtr() const
  1332     NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1333     return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc());
  1336 jsbytecode *
  1337 JSBrokenFrameIterator::pc() const
  1339     NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1340     return iter.pc();
  1343 bool
  1344 JSBrokenFrameIterator::isConstructing() const
  1346     NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1347     return iter.isConstructing();

mercurial