LCOV - app.info - js/src/vm/Debugger.cpp

LCOV - code coverage report
| Current view: | directory - js/src/vm - Debugger.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 2045 | 1786 | 87.3 %
| Date: | 2012-04-07 | Functions: | 187 | 181 | 96.8 %

           1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=8 sw=4 et tw=99 ft=cpp:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is SpiderMonkey Debugger object.
          18                 :  *
          19                 :  * The Initial Developer of the Original Code is
          20                 :  * Mozilla Foundation.
          21                 :  * Portions created by the Initial Developer are Copyright (C) 1998-1999
          22                 :  * the Initial Developer. All Rights Reserved.
          23                 :  *
          24                 :  * Contributors:
          25                 :  *   Jim Blandy <jimb@mozilla.com>
          26                 :  *   Jason Orendorff <jorendorff@mozilla.com>
          27                 :  *
          28                 :  * Alternatively, the contents of this file may be used under the terms of
          29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
          30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          32                 :  * of those above. If you wish to allow use of your version of this file only
          33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          34                 :  * use your version of this file under the terms of the MPL, indicate your
          35                 :  * decision by deleting the provisions above and replace them with the notice
          36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          37                 :  * the provisions above, a recipient may use your version of this file under
          38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          39                 :  *
          40                 :  * ***** END LICENSE BLOCK ***** */
          41                 :
          42                 : #include "vm/Debugger.h"
          43                 : #include "jsapi.h"
          44                 : #include "jscntxt.h"
          45                 : #include "jsgcmark.h"
          46                 : #include "jsnum.h"
          47                 : #include "jsobj.h"
          48                 : #include "jswrapper.h"
          49                 : #include "jsarrayinlines.h"
          50                 : #include "jsgcinlines.h"
          51                 : #include "jsinterpinlines.h"
          52                 : #include "jsobjinlines.h"
          53                 : #include "jsopcodeinlines.h"
          54                 :
          55                 : #include "frontend/BytecodeCompiler.h"
          56                 : #include "frontend/BytecodeEmitter.h"
          57                 : #include "methodjit/Retcon.h"
          58                 : #include "js/Vector.h"
          59                 :
          60                 : #include "vm/Stack-inl.h"
          61                 :
          62                 : using namespace js;
          63                 :
          64                 :
          65                 : /*** Forward declarations ************************************************************************/
          66                 :
          67                 : extern Class DebuggerFrame_class;
          68                 :
          69                 : enum {
          70                 :     JSSLOT_DEBUGFRAME_OWNER,
          71                 :     JSSLOT_DEBUGFRAME_ARGUMENTS,
          72                 :     JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
          73                 :     JSSLOT_DEBUGFRAME_ONPOP_HANDLER,
          74                 :     JSSLOT_DEBUGFRAME_COUNT
          75                 : };
          76                 :
          77                 : extern Class DebuggerArguments_class;
          78                 :
          79                 : enum {
          80                 :     JSSLOT_DEBUGARGUMENTS_FRAME,
          81                 :     JSSLOT_DEBUGARGUMENTS_COUNT
          82                 : };
          83                 :
          84                 : extern Class DebuggerEnv_class;
          85                 :
          86                 : enum {
          87                 :     JSSLOT_DEBUGENV_OWNER,
          88                 :     JSSLOT_DEBUGENV_COUNT
          89                 : };
          90                 :
          91                 : extern Class DebuggerObject_class;
          92                 :
          93                 : enum {
          94                 :     JSSLOT_DEBUGOBJECT_OWNER,
          95                 :     JSSLOT_DEBUGOBJECT_COUNT
          96                 : };
          97                 :
          98                 : extern Class DebuggerScript_class;
          99                 :
         100                 : enum {
         101                 :     JSSLOT_DEBUGSCRIPT_OWNER,
         102                 :     JSSLOT_DEBUGSCRIPT_COUNT
         103                 : };
         104                 :
         105                 :
         106                 : /*** Utils ***************************************************************************************/
         107                 :
         108                 : bool
         109              54 : ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required)
         110                 : {
         111              54 :     JS_ASSERT(required > 0);
         112              54 :     JS_ASSERT(required <= 10);
         113                 :     char s[2];
         114              54 :     s[0] = '0' + (required - 1);
         115              54 :     s[1] = '\0';
         116                 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
         117              54 :                          name, s, required == 1 ? "" : "s");
         118              54 :     return false;
         119                 : }
         120                 :
         121                 : #define REQUIRE_ARGC(name, n)                                                 \
         122                 :     JS_BEGIN_MACRO                                                            \
         123                 :         if (argc < (n))                                                       \
         124                 :             return ReportMoreArgsNeeded(cx, name, n);                         \
         125                 :     JS_END_MACRO
         126                 :
         127                 : bool
         128              81 : ReportObjectRequired(JSContext *cx)
         129                 : {
         130              81 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
         131              81 :     return false;
         132                 : }
         133                 :
         134                 : bool
         135             819 : ValueToIdentifier(JSContext *cx, const Value &v, jsid *idp)
         136                 : {
         137                 :     jsid id;
         138             819 :     if (!ValueToId(cx, v, &id))
         139               0 :         return false;
         140             819 :     if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) {
         141                 :         js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
         142             108 :                                  JSDVG_SEARCH_STACK, v, NULL, "not an identifier", NULL);
         143             108 :         return false;
         144                 :     }
         145             711 :     *idp = id;
         146             711 :     return true;
         147                 : }
         148                 :
         149                 : /*
         150                 :  * A range of all the Debugger.Frame objects for a particular StackFrame.
         151                 :  *
         152                 :  * FIXME This checks only current debuggers, so it relies on a hack in
         153                 :  * Debugger::removeDebuggeeGlobal to make sure only current debuggers have Frame
         154                 :  * objects with .live === true.
         155                 :  */
         156                 : class Debugger::FrameRange {
         157                 :     JSContext *cx;
         158                 :     StackFrame *fp;
         159                 :
         160                 :     /* The debuggers in |fp|'s compartment, or NULL if there are none. */
         161                 :     GlobalObject::DebuggerVector *debuggers;
         162                 :
         163                 :     /*
         164                 :      * The index of the front Debugger.Frame's debugger in debuggers.
         165                 :      * nextDebugger < debuggerCount if and only if the range is not empty.
         166                 :      */
         167                 :     size_t debuggerCount, nextDebugger;
         168                 :
         169                 :     /*
         170                 :      * If the range is not empty, this is front Debugger.Frame's entry in its
         171                 :      * debugger's frame table.
         172                 :      */
         173                 :     FrameMap::Ptr entry;
         174                 :
         175                 :   public:
         176                 :     /*
         177                 :      * Return a range containing all Debugger.Frame instances referring to |fp|.
         178                 :      * |global| is |fp|'s global object; if NULL or omitted, we compute it
         179                 :      * ourselves from |fp|.
         180                 :      *
         181                 :      * We keep an index into the compartment's debugger list, and a
         182                 :      * FrameMap::Ptr into the current debugger's frame map. Thus, if the set of
         183                 :      * debuggers in |fp|'s compartment changes, this range becomes invalid.
         184                 :      * Similarly, if stack frames are added to or removed from frontDebugger(),
         185                 :      * then the range's front is invalid until popFront is called.
         186                 :      */
         187           62549 :     FrameRange(JSContext *cx, StackFrame *fp, GlobalObject *global = NULL)
         188           62549 :       : cx(cx), fp(fp) {
         189           62549 :         nextDebugger = 0;
         190                 :
         191                 :         /* Find our global, if we were not given one. */
         192           62549 :         if (!global)
         193            5813 :             global = &fp->scopeChain().global();
         194                 :
         195                 :         /* The frame and global must match. */
         196           62549 :         JS_ASSERT(&fp->scopeChain().global() == global);
         197                 :
         198                 :         /* Find the list of debuggers we'll iterate over. There may be none. */
         199           62549 :         debuggers = global->getDebuggers();
         200           62549 :         if (debuggers) {
         201           61916 :             debuggerCount = debuggers->length();
         202           61916 :             findNext();
         203                 :         } else {
         204             633 :             debuggerCount = 0;
         205                 :         }
         206           62549 :     }
         207                 :
         208          307502 :     bool empty() const {
         209          307502 :         return nextDebugger >= debuggerCount;
         210                 :     }
         211                 :
         212           34299 :     JSObject *frontFrame() const {
         213           34299 :         JS_ASSERT(!empty());
         214           34299 :         return entry->value;
         215                 :     }
         216                 :
         217           14256 :     Debugger *frontDebugger() const {
         218           14256 :         JS_ASSERT(!empty());
         219           14256 :         return (*debuggers)[nextDebugger];
         220                 :     }
         221                 :
         222                 :     /*
         223                 :      * Delete the front frame from its Debugger's frame map. After this call,
         224                 :      * the range's front is invalid until popFront is called.
         225                 :      */
         226                 :     void removeFrontFrame() const {
         227                 :         JS_ASSERT(!empty());
         228                 :         frontDebugger()->frames.remove(entry);
         229                 :     }
         230                 :
         231           34299 :     void popFront() {
         232           34299 :         JS_ASSERT(!empty());
         233           34299 :         nextDebugger++;
         234           34299 :         findNext();
         235           34299 :     }
         236                 :
         237                 :   private:
         238                 :     /*
         239                 :      * Either make this range refer to the first appropriate Debugger.Frame at
         240                 :      * or after nextDebugger, or make it empty.
         241                 :      */
         242           96215 :     void findNext() {
         243          224015 :         while (!empty()) {
         244           65884 :             Debugger *dbg = (*debuggers)[nextDebugger];
         245           65884 :             entry = dbg->frames.lookup(fp);
         246           65884 :             if (entry)
         247           34299 :                 break;
         248           31585 :             nextDebugger++;
         249                 :         }
         250           96215 :     }
         251                 : };
         252                 :
         253                 :
         254                 : /*** Breakpoints *********************************************************************************/
         255                 :
         256            1172 : BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
         257                 :   : script(script), pc(pc), scriptGlobal(NULL), enabledCount(0),
         258            1172 :     trapHandler(NULL), trapClosure(UndefinedValue())
         259                 : {
         260            1172 :     JS_ASSERT(!script->hasBreakpointsAt(pc));
         261            1172 :     JS_INIT_CLIST(&breakpoints);
         262            1172 : }
         263                 :
         264                 : /*
         265                 :  * Precondition: script is live, meaning either it is a non-held script that is
         266                 :  * on the stack or a held script that hasn't been GC'd.
         267                 :  */
         268                 : static GlobalObject *
         269            1296 : ScriptGlobal(JSContext *cx, JSScript *script, GlobalObject *scriptGlobal)
         270                 : {
         271            1296 :     if (scriptGlobal)
         272             603 :         return scriptGlobal;
         273                 :
         274                 :     /*
         275                 :      * The referent is a non-held script. There is no direct reference from
         276                 :      * script to the scope, so find it on the stack.
         277                 :      */
         278            2115 :     for (AllFramesIter i(cx->stack.space()); ; ++i) {
         279            2115 :         JS_ASSERT(!i.done());
         280            2115 :         if (i.fp()->maybeScript() == script)
         281             693 :             return &i.fp()->scopeChain().global();
         282                 :     }
         283                 :     JS_NOT_REACHED("ScriptGlobal: live non-held script not on stack");
         284                 : }
         285                 :
         286                 : void
         287            2344 : BreakpointSite::recompile(FreeOp *fop)
         288                 : {
         289                 : #ifdef JS_METHODJIT
         290            2344 :     if (script->hasJITCode()) {
         291             331 :         mjit::Recompiler::clearStackReferences(fop, script);
         292             331 :         mjit::ReleaseScriptCode(fop, script);
         293                 :     }
         294                 : #endif
         295            2344 : }
         296                 :
         297                 : void
         298            1278 : BreakpointSite::inc(FreeOp *fop)
         299                 : {
         300            1278 :     if (enabledCount == 0 && !trapHandler)
         301             801 :         recompile(fop);
         302            1278 :     enabledCount++;
         303            1278 : }
         304                 :
         305                 : void
         306            1278 : BreakpointSite::dec(FreeOp *fop)
         307                 : {
         308            1278 :     JS_ASSERT(enabledCount > 0);
         309            1278 :     enabledCount--;
         310            1278 :     if (enabledCount == 0 && !trapHandler)
         311             810 :         recompile(fop);
         312            1278 : }
         313                 :
         314                 : void
         315             371 : BreakpointSite::setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure)
         316                 : {
         317             371 :     if (enabledCount == 0)
         318             371 :         recompile(fop);
         319             371 :     trapHandler = handler;
         320             371 :     trapClosure = closure;
         321             371 : }
         322                 :
         323                 : void
         324             659 : BreakpointSite::clearTrap(FreeOp *fop, JSTrapHandler *handlerp, Value *closurep)
         325                 : {
         326             659 :     if (handlerp)
         327               0 :         *handlerp = trapHandler;
         328             659 :     if (closurep)
         329               0 :         *closurep = trapClosure;
         330                 :
         331             659 :     trapHandler = NULL;
         332             659 :     trapClosure = UndefinedValue();
         333             659 :     if (enabledCount == 0) {
         334             362 :         if (!fop->runtime()->gcRunning) {
         335                 :             /* If the GC is running then the script is being destroyed. */
         336             362 :             recompile(fop);
         337                 :         }
         338             362 :         destroyIfEmpty(fop);
         339                 :     }
         340             659 : }
         341                 :
         342                 : void
         343            1631 : BreakpointSite::destroyIfEmpty(FreeOp *fop)
         344                 : {
         345            1631 :     if (JS_CLIST_IS_EMPTY(&breakpoints) && !trapHandler)
         346            1172 :         script->destroyBreakpointSite(fop, pc);
         347            1631 : }
         348                 :
         349                 : Breakpoint *
         350            3755 : BreakpointSite::firstBreakpoint() const
         351                 : {
         352            3755 :     if (JS_CLIST_IS_EMPTY(&breakpoints))
         353             344 :         return NULL;
         354            3411 :     return Breakpoint::fromSiteLinks(JS_NEXT_LINK(&breakpoints));
         355                 : }
         356                 :
         357                 : bool
         358            1359 : BreakpointSite::hasBreakpoint(Breakpoint *bp)
         359                 : {
         360            2349 :     for (Breakpoint *p = firstBreakpoint(); p; p = p->nextInSite())
         361            2340 :         if (p == bp)
         362            1350 :             return true;
         363               9 :     return false;
         364                 : }
         365                 :
         366            1269 : Breakpoint::Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler)
         367            1269 :     : debugger(debugger), site(site), handler(handler)
         368                 : {
         369            1269 :     JS_APPEND_LINK(&debuggerLinks, &debugger->breakpoints);
         370            1269 :     JS_APPEND_LINK(&siteLinks, &site->breakpoints);
         371            1269 : }
         372                 :
         373                 : Breakpoint *
         374             583 : Breakpoint::fromDebuggerLinks(JSCList *links)
         375                 : {
         376             583 :     return (Breakpoint *) ((unsigned char *) links - offsetof(Breakpoint, debuggerLinks));
         377                 : }
         378                 :
         379                 : Breakpoint *
         380            5760 : Breakpoint::fromSiteLinks(JSCList *links)
         381                 : {
         382            5760 :     return (Breakpoint *) ((unsigned char *) links - offsetof(Breakpoint, siteLinks));
         383                 : }
         384                 :
         385                 : void
         386            1269 : Breakpoint::destroy(FreeOp *fop)
         387                 : {
         388            1269 :     if (debugger->enabled)
         389            1269 :         site->dec(fop);
         390            1269 :     JS_REMOVE_LINK(&debuggerLinks);
         391            1269 :     JS_REMOVE_LINK(&siteLinks);
         392            1269 :     site->destroyIfEmpty(fop);
         393            1269 :     fop->delete_(this);
         394            1269 : }
         395                 :
         396                 : Breakpoint *
         397             549 : Breakpoint::nextInDebugger()
         398                 : {
         399             549 :     JSCList *link = JS_NEXT_LINK(&debuggerLinks);
         400             549 :     return (link == &debugger->breakpoints) ? NULL : fromDebuggerLinks(link);
         401                 : }
         402                 :
         403                 : Breakpoint *
         404            4410 : Breakpoint::nextInSite()
         405                 : {
         406            4410 :     JSCList *link = JS_NEXT_LINK(&siteLinks);
         407            4410 :     return (link == &site->breakpoints) ? NULL : fromSiteLinks(link);
         408                 : }
         409                 :
         410                 :
         411                 : /*** Debugger hook dispatch **********************************************************************/
         412                 :
         413            4259 : Debugger::Debugger(JSContext *cx, JSObject *dbg)
         414                 :   : object(dbg), uncaughtExceptionHook(NULL), enabled(true),
         415            4259 :     frames(cx), scripts(cx), objects(cx), environments(cx)
         416                 : {
         417            4259 :     assertSameCompartment(cx, dbg);
         418                 :
         419            4259 :     JSRuntime *rt = cx->runtime;
         420            4259 :     JS_APPEND_LINK(&link, &rt->debuggerList);
         421            4259 :     JS_INIT_CLIST(&breakpoints);
         422            4259 : }
         423                 :
         424            8518 : Debugger::~Debugger()
         425                 : {
         426            4259 :     JS_ASSERT(debuggees.empty());
         427                 :
         428                 :     /* This always happens in the GC thread, so no locking is required. */
         429            4259 :     JS_ASSERT(object->compartment()->rt->gcRunning);
         430            4259 :     JS_REMOVE_LINK(&link);
         431            4259 : }
         432                 :
         433                 : bool
         434            4259 : Debugger::init(JSContext *cx)
         435                 : {
         436            4259 :     bool ok = debuggees.init() &&
         437            4259 :               frames.init() &&
         438            4259 :               scripts.init() &&
         439            4259 :               objects.init() &&
         440           17036 :               environments.init();
         441            4259 :     if (!ok)
         442               0 :         js_ReportOutOfMemory(cx);
         443            4259 :     return ok;
         444                 : }
         445                 :
         446                 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
         447                 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER));
         448                 : JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGENV_OWNER));
         449                 :
         450                 : Debugger *
         451           73027 : Debugger::fromChildJSObject(JSObject *obj)
         452                 : {
         453          132868 :     JS_ASSERT(obj->getClass() == &DebuggerFrame_class ||
         454                 :               obj->getClass() == &DebuggerScript_class ||
         455                 :               obj->getClass() == &DebuggerObject_class ||
         456          132868 :               obj->getClass() == &DebuggerEnv_class);
         457           73027 :     JSObject *dbgobj = &obj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject();
         458           73027 :     return fromJSObject(dbgobj);
         459                 : }
         460                 :
         461                 : bool
         462           19460 : Debugger::getScriptFrame(JSContext *cx, StackFrame *fp, Value *vp)
         463                 : {
         464           19460 :     JS_ASSERT(fp->isScriptFrame());
         465           38920 :     FrameMap::AddPtr p = frames.lookupForAdd(fp);
         466           19460 :     if (!p) {
         467                 :         /* Create and populate the Debugger.Frame object. */
         468           14292 :         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject();
         469                 :         JSObject *frameobj =
         470           14292 :             NewObjectWithGivenProto(cx, &DebuggerFrame_class, proto, NULL);
         471           14292 :         if (!frameobj)
         472               0 :             return false;
         473           14292 :         frameobj->setPrivate(fp);
         474           14292 :         frameobj->setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*object));
         475                 :
         476           14292 :         if (!frames.add(p, fp, frameobj)) {
         477               0 :             js_ReportOutOfMemory(cx);
         478               0 :             return false;
         479                 :         }
         480                 :     }
         481           19460 :     vp->setObject(*p->value);
         482           19460 :     return true;
         483                 : }
         484                 :
         485                 : JSObject *
         486           78913 : Debugger::getHook(Hook hook) const
         487                 : {
         488           78913 :     JS_ASSERT(hook >= 0 && hook < HookCount);
         489           78913 :     const Value &v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
         490           78913 :     return v.isUndefined() ? NULL : &v.toObject();
         491                 : }
         492                 :
         493                 : bool
         494             924 : Debugger::hasAnyLiveHooks() const
         495                 : {
         496             924 :     if (!enabled)
         497             122 :         return false;
         498                 :
         499            1083 :     if (getHook(OnDebuggerStatement) ||
         500             213 :         getHook(OnExceptionUnwind) ||
         501              34 :         getHook(OnNewScript) ||
         502              34 :         getHook(OnEnterFrame))
         503                 :     {
         504             768 :         return true;
         505                 :     }
         506                 :
         507                 :     /* If any breakpoints are in live scripts, return true. */
         508              34 :     for (Breakpoint *bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
         509              34 :         if (!IsAboutToBeFinalized(bp->site->script))
         510              34 :             return true;
         511                 :     }
         512                 :
         513               0 :     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         514               0 :         JSObject *frameObj = r.front().value;
         515               0 :         if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() ||
         516               0 :             !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
         517               0 :             return true;
         518                 :     }
         519                 :
         520               0 :     return false;
         521                 : }
         522                 :
         523                 : JSTrapStatus
         524           28174 : Debugger::slowPathOnEnterFrame(JSContext *cx, Value *vp)
         525                 : {
         526                 :     /* Build the list of recipients. */
         527           56348 :     AutoValueVector triggered(cx);
         528           28174 :     GlobalObject *global = &cx->fp()->scopeChain().global();
         529           28174 :     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
         530           57514 :         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
         531           29648 :             Debugger *dbg = *p;
         532           29648 :             JS_ASSERT(dbg->observesFrame(cx->fp()));
         533           29648 :             if (dbg->observesEnterFrame() && !triggered.append(ObjectValue(*dbg->toJSObject())))
         534               0 :                 return JSTRAP_ERROR;
         535                 :         }
         536                 :     }
         537                 :
         538                 :     /* Deliver the event, checking again as in dispatchHook. */
         539           30316 :     for (Value *p = triggered.begin(); p != triggered.end(); p++) {
         540            4255 :         Debugger *dbg = Debugger::fromJSObject(&p->toObject());
         541            4255 :         if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) {
         542            4255 :             JSTrapStatus status = dbg->fireEnterFrame(cx, vp);
         543            4255 :             if (status != JSTRAP_CONTINUE)
         544            2113 :                 return status;
         545                 :         }
         546                 :     }
         547                 :
         548           26061 :     return JSTRAP_CONTINUE;
         549                 : }
         550                 :
         551                 : /*
         552                 :  * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
         553                 :  * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
         554                 :  * |cx->fp()|'s return value, and return a new success value.
         555                 :  */
         556                 : bool
         557           28368 : Debugger::slowPathOnLeaveFrame(JSContext *cx, bool frameOk)
         558                 : {
         559           28368 :     StackFrame *fp = cx->fp();
         560           28368 :     GlobalObject *global = &fp->scopeChain().global();
         561                 :
         562                 :     /* Save the frame's completion value. */
         563                 :     JSTrapStatus status;
         564                 :     Value value;
         565           28368 :     Debugger::resultToCompletion(cx, frameOk, fp->returnValue(), &status, &value);
         566                 :
         567                 :     /* Build a list of the recipients. */
         568           56736 :     AutoObjectVector frames(cx);
         569           42615 :     for (FrameRange r(cx, fp, global); !r.empty(); r.popFront()) {
         570           14247 :         if (!frames.append(r.frontFrame())) {
         571               0 :             cx->clearPendingException();
         572               0 :             return false;
         573                 :         }
         574                 :     }
         575                 :
         576                 :     /* For each Debugger.Frame, fire its onPop handler, if any. */
         577           42615 :     for (JSObject **p = frames.begin(); p != frames.end(); p++) {
         578           14247 :         JSObject *frameobj = *p;
         579           14247 :         Debugger *dbg = Debugger::fromChildJSObject(frameobj);
         580                 :
         581           28350 :         if (dbg->enabled &&
         582           14103 :             !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined()) {
         583             981 :             const Value &handler = frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);
         584                 :
         585            1962 :             AutoCompartment ac(cx, dbg->object);
         586                 :
         587             981 :             if (!ac.enter()) {
         588               0 :                 status = JSTRAP_ERROR;
         589                 :                 break;
         590                 :             }
         591                 :
         592                 :             Value completion;
         593             981 :             if (!dbg->newCompletionValue(cx, status, value, &completion)) {
         594               0 :                 status = dbg->handleUncaughtException(ac, NULL, false);
         595                 :                 break;
         596                 :             }
         597                 :
         598                 :             /* Call the onPop handler. */
         599                 :             Value rval;
         600             981 :             bool hookOk = Invoke(cx, ObjectValue(*frameobj), handler, 1, &completion, &rval);
         601                 :             Value nextValue;
         602             981 :             JSTrapStatus nextStatus = dbg->parseResumptionValue(ac, hookOk, rval, &nextValue);
         603                 :
         604                 :             /*
         605                 :              * At this point, we are back in the debuggee compartment, and any error has
         606                 :              * been wrapped up as a completion value.
         607                 :              */
         608             981 :             JS_ASSERT(cx->compartment == global->compartment());
         609             981 :             JS_ASSERT(!cx->isExceptionPending());
         610                 :
         611                 :             /* JSTRAP_CONTINUE means "make no change". */
         612             981 :             if (nextStatus != JSTRAP_CONTINUE) {
         613             414 :                 status = nextStatus;
         614             414 :                 value = nextValue;
         615                 :             }
         616                 :         }
         617                 :     }
         618                 :
         619                 :     /*
         620                 :      * Clean up all Debugger.Frame instances. Use a fresh FrameRange, as one
         621                 :      * debugger's onPop handler could have caused another debugger to create its
         622                 :      * own Debugger.Frame instance.
         623                 :      */
         624           42624 :     for (FrameRange r(cx, fp, global); !r.empty(); r.popFront()) {
         625           14256 :         JSObject *frameobj = r.frontFrame();
         626           14256 :         Debugger *dbg = r.frontDebugger();
         627           14256 :         JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
         628                 :
         629           14256 :         frameobj->setPrivate(NULL);
         630                 :
         631                 :         /* If this frame had an onStep handler, adjust the script's count. */
         632           15138 :         if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
         633             441 :             fp->isScriptFrame() &&
         634             441 :             !fp->script()->changeStepModeCount(cx, -1))
         635                 :         {
         636               0 :             status = JSTRAP_ERROR;
         637                 :             /* Don't exit the loop; we must mark all frames as dead. */
         638                 :         }
         639                 :
         640           14256 :         dbg->frames.remove(fp);
         641                 :     }
         642                 :
         643                 :     /*
         644                 :      * If this is an eval frame, then from the debugger's perspective the
         645                 :      * script is about to be destroyed. Remove any breakpoints in it.
         646                 :      */
         647           28368 :     if (fp->isEvalFrame()) {
         648            8675 :         JSScript *script = fp->script();
         649            8675 :         script->clearBreakpointsIn(cx, NULL, NULL);
         650                 :     }
         651                 :
         652                 :     /* Establish (status, value) as our resumption value. */
         653           28368 :     switch (status) {
         654                 :       case JSTRAP_RETURN:
         655           25080 :         fp->setReturnValue(value);
         656           25080 :         return true;
         657                 :
         658                 :       case JSTRAP_THROW:
         659             745 :         cx->setPendingException(value);
         660             745 :         return false;
         661                 :
         662                 :       case JSTRAP_ERROR:
         663            2543 :         JS_ASSERT(!cx->isExceptionPending());
         664            2543 :         return false;
         665                 :
         666                 :       default:
         667               0 :         JS_NOT_REACHED("bad final trap status");
         668                 :     }
         669                 : }
         670                 :
         671                 : bool
         672            8973 : Debugger::wrapEnvironment(JSContext *cx, Env *env, Value *rval)
         673                 : {
         674            8973 :     if (!env) {
         675             135 :         rval->setNull();
         676             135 :         return true;
         677                 :     }
         678                 :
         679                 :     JSObject *envobj;
         680           17676 :     ObjectWeakMap::AddPtr p = environments.lookupForAdd(env);
         681            8838 :     if (p) {
         682            1233 :         envobj = p->value;
         683                 :     } else {
         684                 :         /* Create a new Debugger.Environment for env. */
         685            7605 :         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject();
         686            7605 :         envobj = NewObjectWithGivenProto(cx, &DebuggerEnv_class, proto, NULL);
         687            7605 :         if (!envobj)
         688               0 :             return false;
         689            7605 :         envobj->setPrivate(env);
         690            7605 :         envobj->setReservedSlot(JSSLOT_DEBUGENV_OWNER, ObjectValue(*object));
         691            7605 :         if (!environments.relookupOrAdd(p, env, envobj)) {
         692               0 :             js_ReportOutOfMemory(cx);
         693               0 :             return false;
         694                 :         }
         695                 :     }
         696            8838 :     rval->setObject(*envobj);
         697            8838 :     return true;
         698                 : }
         699                 :
         700                 : bool
         701           12614 : Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
         702                 : {
         703           12614 :     assertSameCompartment(cx, object.get());
         704                 :
         705           12614 :     if (vp->isObject()) {
         706            7039 :         JSObject *obj = &vp->toObject();
         707                 :
         708           14078 :         ObjectWeakMap::AddPtr p = objects.lookupForAdd(obj);
         709            7039 :         if (p) {
         710            2782 :             vp->setObject(*p->value);
         711                 :         } else {
         712                 :             /* Create a new Debugger.Object for obj. */
         713            4257 :             JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject();
         714                 :             JSObject *dobj =
         715            4257 :                 NewObjectWithGivenProto(cx, &DebuggerObject_class, proto, NULL);
         716            4257 :             if (!dobj)
         717               0 :                 return false;
         718            4257 :             dobj->setPrivate(obj);
         719            4257 :             dobj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*object));
         720            4257 :             if (!objects.relookupOrAdd(p, obj, dobj)) {
         721               0 :                 js_ReportOutOfMemory(cx);
         722               0 :                 return false;
         723                 :             }
         724            4257 :             vp->setObject(*dobj);
         725                 :         }
         726            5575 :     } else if (!cx->compartment->wrap(cx, vp)) {
         727               0 :         vp->setUndefined();
         728               0 :         return false;
         729                 :     }
         730                 :
         731           12614 :     return true;
         732                 : }
         733                 :
         734                 : bool
         735            2889 : Debugger::unwrapDebuggeeValue(JSContext *cx, Value *vp)
         736                 : {
         737            2889 :     assertSameCompartment(cx, object.get(), *vp);
         738            2889 :     if (vp->isObject()) {
         739             540 :         JSObject *dobj = &vp->toObject();
         740             540 :         if (dobj->getClass() != &DebuggerObject_class) {
         741                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE,
         742              54 :                                  "Debugger", "Debugger.Object", dobj->getClass()->name);
         743              54 :             return false;
         744                 :         }
         745                 :
         746             486 :         Value owner = dobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
         747             486 :         if (owner.toObjectOrNull() != object) {
         748                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
         749              27 :                                  owner.isNull()
         750                 :                                  ? JSMSG_DEBUG_OBJECT_PROTO
         751              27 :                                  : JSMSG_DEBUG_OBJECT_WRONG_OWNER);
         752              27 :             return false;
         753                 :         }
         754                 :
         755             459 :         vp->setObject(*(JSObject *) dobj->getPrivate());
         756                 :     }
         757            2808 :     return true;
         758                 : }
         759                 :
         760                 : JSTrapStatus
         761            2140 : Debugger::handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook)
         762                 : {
         763            2140 :     JSContext *cx = ac.context;
         764            2140 :     if (cx->isExceptionPending()) {
         765             108 :         if (callHook && uncaughtExceptionHook) {
         766              81 :             Value fval = ObjectValue(*uncaughtExceptionHook);
         767              81 :             Value exc = cx->getPendingException();
         768                 :             Value rv;
         769              81 :             cx->clearPendingException();
         770              81 :             if (Invoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
         771              81 :                 return vp ? parseResumptionValue(ac, true, rv, vp, false) : JSTRAP_CONTINUE;
         772                 :         }
         773                 :
         774              27 :         if (cx->isExceptionPending()) {
         775              27 :             JS_ReportPendingException(cx);
         776              27 :             cx->clearPendingException();
         777                 :         }
         778                 :     }
         779            2059 :     ac.leave();
         780            2059 :     return JSTRAP_ERROR;
         781                 : }
         782                 :
         783                 : void
         784           30870 : Debugger::resultToCompletion(JSContext *cx, bool ok, const Value &rv,
         785                 :                              JSTrapStatus *status, Value *value)
         786                 : {
         787           30870 :     JS_ASSERT_IF(ok, !cx->isExceptionPending());
         788                 :
         789           30870 :     if (ok) {
         790           27222 :         *status = JSTRAP_RETURN;
         791           27222 :         *value = rv;
         792            3648 :     } else if (cx->isExceptionPending()) {
         793             817 :         *status = JSTRAP_THROW;
         794             817 :         *value = cx->getPendingException();
         795             817 :         cx->clearPendingException();
         796                 :     } else {
         797            2831 :         *status = JSTRAP_ERROR;
         798            2831 :         value->setUndefined();
         799                 :     }
         800           30870 : }
         801                 :
         802                 : bool
         803            3483 : Debugger::newCompletionValue(JSContext *cx, JSTrapStatus status, Value value, Value *result)
         804                 : {
         805                 :     /*
         806                 :      * We must be in the debugger's compartment, since that's where we want
         807                 :      * to construct the completion value.
         808                 :      */
         809            3483 :     assertSameCompartment(cx, object.get());
         810                 :
         811                 :     jsid key;
         812                 :
         813            3483 :     switch (status) {
         814                 :       case JSTRAP_RETURN:
         815            2700 :         key = ATOM_TO_JSID(cx->runtime->atomState.returnAtom);
         816            2700 :         break;
         817                 :
         818                 :       case JSTRAP_THROW:
         819             297 :         key = ATOM_TO_JSID(cx->runtime->atomState.throwAtom);
         820             297 :         break;
         821                 :
         822                 :       case JSTRAP_ERROR:
         823             486 :         result->setNull();
         824             486 :         return true;
         825                 :
         826                 :       default:
         827               0 :         JS_NOT_REACHED("bad status passed to Debugger::newCompletionValue");
         828                 :     }
         829                 :
         830                 :     /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */
         831            2997 :     JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
         832            8991 :     if (!obj ||
         833            2997 :         !wrapDebuggeeValue(cx, &value) ||
         834                 :         !DefineNativeProperty(cx, obj, key, value, JS_PropertyStub, JS_StrictPropertyStub,
         835            2997 :                               JSPROP_ENUMERATE, 0, 0))
         836                 :     {
         837               0 :         return false;
         838                 :     }
         839                 :
         840            2997 :     result->setObject(*obj);
         841            2997 :     return true;
         842                 : }
         843                 :
         844                 : bool
         845            2502 : Debugger::receiveCompletionValue(AutoCompartment &ac, bool ok, Value val, Value *vp)
         846                 : {
         847            2502 :     JSContext *cx = ac.context;
         848                 :
         849                 :     JSTrapStatus status;
         850                 :     Value value;
         851            2502 :     resultToCompletion(cx, ok, val, &status, &value);
         852            2502 :     ac.leave();
         853            2502 :     return newCompletionValue(cx, status, value, vp);
         854                 : }
         855                 :
         856                 : JSTrapStatus
         857           21320 : Debugger::parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
         858                 :                                bool callHook)
         859                 : {
         860           21320 :     vp->setUndefined();
         861           21320 :     if (!ok)
         862            2131 :         return handleUncaughtException(ac, vp, callHook);
         863           19189 :     if (rv.isUndefined()) {
         864           18001 :         ac.leave();
         865           18001 :         return JSTRAP_CONTINUE;
         866                 :     }
         867            1188 :     if (rv.isNull()) {
         868             378 :         ac.leave();
         869             378 :         return JSTRAP_ERROR;
         870                 :     }
         871                 :
         872                 :     /* Check that rv is {return: val} or {throw: val}. */
         873             810 :     JSContext *cx = ac.context;
         874                 :     JSObject *obj;
         875                 :     const Shape *shape;
         876             810 :     jsid returnId = ATOM_TO_JSID(cx->runtime->atomState.returnAtom);
         877             810 :     jsid throwId = ATOM_TO_JSID(cx->runtime->atomState.throwAtom);
         878             810 :     bool okResumption = rv.isObject();
         879             810 :     if (okResumption) {
         880             810 :         obj = &rv.toObject();
         881             810 :         okResumption = obj->isObject();
         882                 :     }
         883             810 :     if (okResumption) {
         884             810 :         shape = obj->lastProperty();
         885             810 :         okResumption = shape->previous() &&
         886            1620 :              !shape->previous()->previous() &&
         887            1143 :              (shape->propid() == returnId || shape->propid() == throwId) &&
         888            2763 :              shape->isDataDescriptor();
         889                 :     }
         890             810 :     if (!okResumption) {
         891               9 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_RESUMPTION);
         892               9 :         return handleUncaughtException(ac, vp, callHook);
         893                 :     }
         894                 :
         895             801 :     if (!js_NativeGet(cx, obj, obj, shape, 0, vp) || !unwrapDebuggeeValue(cx, vp))
         896               0 :         return handleUncaughtException(ac, vp, callHook);
         897                 :
         898             801 :     ac.leave();
         899             801 :     if (!cx->compartment->wrap(cx, vp)) {
         900               0 :         vp->setUndefined();
         901               0 :         return JSTRAP_ERROR;
         902                 :     }
         903             801 :     return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW;
         904                 : }
         905                 :
         906                 : bool
         907            1350 : CallMethodIfPresent(JSContext *cx, JSObject *obj, const char *name, int argc, Value *argv,
         908                 :                     Value *rval)
         909                 : {
         910            1350 :     rval->setUndefined();
         911            1350 :     JSAtom *atom = js_Atomize(cx, name, strlen(name));
         912                 :     Value fval;
         913                 :     return atom &&
         914            1350 :            js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, &fval) &&
         915            1350 :            (!js_IsCallable(fval) ||
         916            4050 :             Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
         917                 : }
         918                 :
         919                 : JSTrapStatus
         920            8662 : Debugger::fireDebuggerStatement(JSContext *cx, Value *vp)
         921                 : {
         922            8662 :     JSObject *hook = getHook(OnDebuggerStatement);
         923            8662 :     JS_ASSERT(hook);
         924            8662 :     JS_ASSERT(hook->isCallable());
         925                 :
         926                 :     /* Grab cx->fp() before pushing a dummy frame. */
         927            8662 :     StackFrame *fp = cx->fp();
         928           17324 :     AutoCompartment ac(cx, object);
         929            8662 :     if (!ac.enter())
         930               0 :         return JSTRAP_ERROR;
         931                 :
         932                 :     Value argv[1];
         933            8662 :     if (!getScriptFrame(cx, fp, argv))
         934               0 :         return handleUncaughtException(ac, vp, false);
         935                 :
         936                 :     Value rv;
         937            8662 :     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
         938            8662 :     return parseResumptionValue(ac, ok, rv, vp);
         939                 : }
         940                 :
         941                 : JSTrapStatus
         942             450 : Debugger::fireExceptionUnwind(JSContext *cx, Value *vp)
         943                 : {
         944             450 :     JSObject *hook = getHook(OnExceptionUnwind);
         945             450 :     JS_ASSERT(hook);
         946             450 :     JS_ASSERT(hook->isCallable());
         947                 :
         948             450 :     StackFrame *fp = cx->fp();
         949             450 :     Value exc = cx->getPendingException();
         950             450 :     cx->clearPendingException();
         951                 :
         952             900 :     AutoCompartment ac(cx, object);
         953             450 :     if (!ac.enter())
         954               0 :         return JSTRAP_ERROR;
         955                 :
         956                 :     Value argv[2];
         957             450 :     argv[1] = exc;
         958             450 :     if (!getScriptFrame(cx, fp, &argv[0]) || !wrapDebuggeeValue(cx, &argv[1]))
         959               0 :         return handleUncaughtException(ac, vp, false);
         960                 :
         961                 :     Value rv;
         962             450 :     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv, &rv);
         963             450 :     JSTrapStatus st = parseResumptionValue(ac, ok, rv, vp);
         964             450 :     if (st == JSTRAP_CONTINUE)
         965             387 :         cx->setPendingException(exc);
         966             450 :     return st;
         967                 : }
         968                 :
         969                 : JSTrapStatus
         970            4255 : Debugger::fireEnterFrame(JSContext *cx, Value *vp)
         971                 : {
         972            4255 :     JSObject *hook = getHook(OnEnterFrame);
         973            4255 :     JS_ASSERT(hook);
         974            4255 :     JS_ASSERT(hook->isCallable());
         975                 :
         976            4255 :     StackFrame *fp = cx->fp();
         977            8510 :     AutoCompartment ac(cx, object);
         978            4255 :     if (!ac.enter())
         979               0 :         return JSTRAP_ERROR;
         980                 :
         981                 :     Value argv[1];
         982            4255 :     if (!getScriptFrame(cx, fp, &argv[0]))
         983               0 :         return handleUncaughtException(ac, vp, false);
         984                 :
         985                 :     Value rv;
         986            4255 :     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
         987            4255 :     return parseResumptionValue(ac, ok, rv, vp);
         988                 : }
         989                 :
         990                 : void
         991             200 : Debugger::fireNewScript(JSContext *cx, JSScript *script)
         992                 : {
         993             200 :     JSObject *hook = getHook(OnNewScript);
         994             200 :     JS_ASSERT(hook);
         995             200 :     JS_ASSERT(hook->isCallable());
         996                 :
         997             400 :     AutoCompartment ac(cx, object);
         998             200 :     if (!ac.enter())
         999                 :         return;
        1000                 :
        1001             200 :     JSObject *dsobj = wrapScript(cx, script);
        1002             200 :     if (!dsobj) {
        1003               0 :         handleUncaughtException(ac, NULL, false);
        1004                 :         return;
        1005                 :     }
        1006                 :
        1007                 :     Value argv[1];
        1008             200 :     argv[0].setObject(*dsobj);
        1009                 :     Value rv;
        1010             200 :     if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
        1011               0 :         handleUncaughtException(ac, NULL, true);
        1012                 : }
        1013                 :
        1014                 : JSTrapStatus
        1015            9191 : Debugger::dispatchHook(JSContext *cx, Value *vp, Hook which)
        1016                 : {
        1017            9191 :     JS_ASSERT(which == OnDebuggerStatement || which == OnExceptionUnwind);
        1018                 :
        1019                 :     /*
        1020                 :      * Determine which debuggers will receive this event, and in what order.
        1021                 :      * Make a copy of the list, since the original is mutable and we will be
        1022                 :      * calling into arbitrary JS.
        1023                 :      *
        1024                 :      * Note: In the general case, 'triggered' contains references to objects in
        1025                 :      * different compartments--every compartment *except* this one.
        1026                 :      */
        1027           18382 :     AutoValueVector triggered(cx);
        1028            9191 :     GlobalObject *global = &cx->fp()->scopeChain().global();
        1029            9191 :     if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
        1030           19354 :         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
        1031           10181 :             Debugger *dbg = *p;
        1032           10181 :             if (dbg->enabled && dbg->getHook(which)) {
        1033            9184 :                 if (!triggered.append(ObjectValue(*dbg->toJSObject())))
        1034               0 :                     return JSTRAP_ERROR;
        1035                 :             }
        1036                 :         }
        1037                 :     }
        1038                 :
        1039                 :     /*
        1040                 :      * Deliver the event to each debugger, checking again to make sure it
        1041                 :      * should still be delivered.
        1042                 :      */
        1043           17682 :     for (Value *p = triggered.begin(); p != triggered.end(); p++) {
        1044            9139 :         Debugger *dbg = Debugger::fromJSObject(&p->toObject());
        1045            9139 :         if (dbg->debuggees.has(global) && dbg->enabled && dbg->getHook(which)) {
        1046                 :             JSTrapStatus st = (which == OnDebuggerStatement)
        1047                 :                               ? dbg->fireDebuggerStatement(cx, vp)
        1048            9112 :                               : dbg->fireExceptionUnwind(cx, vp);
        1049            9112 :             if (st != JSTRAP_CONTINUE)
        1050             648 :                 return st;
        1051                 :         }
        1052                 :     }
        1053            8543 :     return JSTRAP_CONTINUE;
        1054                 : }
        1055                 :
        1056                 : static bool
        1057            9607 : AddNewScriptRecipients(GlobalObject::DebuggerVector *src, AutoValueVector *dest)
        1058                 : {
        1059            9607 :     bool wasEmpty = dest->length() == 0;
        1060           20717 :     for (Debugger **p = src->begin(); p != src->end(); p++) {
        1061           11110 :         Debugger *dbg = *p;
        1062           11110 :         Value v = ObjectValue(*dbg->toJSObject());
        1063           11310 :         if (dbg->observesNewScript() &&
        1064               0 :             (wasEmpty || Find(dest->begin(), dest->end(), v) == dest->end()) &&
        1065             200 :             !dest->append(v))
        1066                 :         {
        1067               0 :             return false;
        1068                 :         }
        1069                 :     }
        1070            9607 :     return true;
        1071                 : }
        1072                 :
        1073                 : void
        1074            9834 : Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, GlobalObject *compileAndGoGlobal)
        1075                 : {
        1076            9834 :     JS_ASSERT(script->compileAndGo == !!compileAndGoGlobal);
        1077                 :
        1078                 :     /*
        1079                 :      * Build the list of recipients. For compile-and-go scripts, this is the
        1080                 :      * same as the generic Debugger::dispatchHook code, but non-compile-and-go
        1081                 :      * scripts are not tied to particular globals. We deliver them to every
        1082                 :      * debugger observing any global in the script's compartment.
        1083                 :      */
        1084           19668 :     AutoValueVector triggered(cx);
        1085            9834 :     if (script->compileAndGo) {
        1086            8790 :         if (GlobalObject::DebuggerVector *debuggers = compileAndGoGlobal->getDebuggers()) {
        1087            8563 :             if (!AddNewScriptRecipients(debuggers, &triggered))
        1088                 :                 return;
        1089                 :         }
        1090                 :     } else {
        1091            1044 :         GlobalObjectSet &debuggees = script->compartment()->getDebuggees();
        1092            2088 :         for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
        1093            1044 :             if (!AddNewScriptRecipients(r.front()->getDebuggers(), &triggered))
        1094                 :                 return;
        1095                 :         }
        1096                 :     }
        1097                 :
        1098                 :     /*
        1099                 :      * Deliver the event to each debugger, checking again as in
        1100                 :      * Debugger::dispatchHook.
        1101                 :      */
        1102           10034 :     for (Value *p = triggered.begin(); p != triggered.end(); p++) {
        1103             200 :         Debugger *dbg = Debugger::fromJSObject(&p->toObject());
        1104             400 :         if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) &&
        1105             200 :             dbg->enabled && dbg->getHook(OnNewScript)) {
        1106             200 :             dbg->fireNewScript(cx, script);
        1107                 :         }
        1108                 :     }
        1109                 : }
        1110                 :
        1111                 : JSTrapStatus
        1112            1262 : Debugger::onTrap(JSContext *cx, Value *vp)
        1113                 : {
        1114            1262 :     StackFrame *fp = cx->fp();
        1115            1262 :     JSScript *script = fp->script();
        1116            1262 :     GlobalObject *scriptGlobal = &fp->scopeChain().global();
        1117            1262 :     jsbytecode *pc = cx->regs().pc;
        1118            1262 :     BreakpointSite *site = script->getBreakpointSite(pc);
        1119            1262 :     JSOp op = JSOp(*pc);
        1120                 :
        1121                 :     /* Build list of breakpoint handlers. */
        1122            2524 :     Vector<Breakpoint *> triggered(cx);
        1123            2648 :     for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
        1124            1386 :         if (!triggered.append(bp))
        1125               0 :             return JSTRAP_ERROR;
        1126                 :     }
        1127                 :
        1128            2621 :     for (Breakpoint **p = triggered.begin(); p != triggered.end(); p++) {
        1129            1386 :         Breakpoint *bp = *p;
        1130                 :
        1131                 :         /* Handlers can clear breakpoints. Check that bp still exists. */
        1132            1386 :         if (!site || !site->hasBreakpoint(bp))
        1133              36 :             continue;
        1134                 :
        1135            1350 :         Debugger *dbg = bp->debugger;
        1136            1350 :         if (dbg->enabled && dbg->debuggees.lookup(scriptGlobal)) {
        1137            2700 :             AutoCompartment ac(cx, dbg->object);
        1138            1350 :             if (!ac.enter())
        1139               0 :                 return JSTRAP_ERROR;
        1140                 :
        1141                 :             Value argv[1];
        1142            1350 :             if (!dbg->getScriptFrame(cx, fp, &argv[0]))
        1143               0 :                 return dbg->handleUncaughtException(ac, vp, false);
        1144                 :             Value rv;
        1145            1350 :             bool ok = CallMethodIfPresent(cx, bp->handler, "hit", 1, argv, &rv);
        1146            1350 :             JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv, vp, true);
        1147            1350 :             if (st != JSTRAP_CONTINUE)
        1148              27 :                 return st;
        1149                 :
        1150                 :             /* Calling JS code invalidates site. Reload it. */
        1151            2673 :             site = script->getBreakpointSite(pc);
        1152                 :         }
        1153                 :     }
        1154                 :
        1155            1235 :     if (site && site->trapHandler) {
        1156             353 :         JSTrapStatus st = site->trapHandler(cx, fp->script(), pc, vp, site->trapClosure);
        1157             353 :         if (st != JSTRAP_CONTINUE)
        1158              45 :             return st;
        1159                 :     }
        1160                 :
        1161                 :     /* By convention, return the true op to the interpreter in vp. */
        1162            1190 :     vp->setInt32(op);
        1163            1190 :     return JSTRAP_CONTINUE;
        1164                 : }
        1165                 :
        1166                 : JSTrapStatus
        1167            5813 : Debugger::onSingleStep(JSContext *cx, Value *vp)
        1168                 : {
        1169            5813 :     StackFrame *fp = cx->fp();
        1170                 :
        1171                 :     /*
        1172                 :      * We may be stepping over a JSOP_EXCEPTION, that pushes the context's
        1173                 :      * pending exception for a 'catch' clause to handle. Don't let the
        1174                 :      * onStep handlers mess with that (other than by returning a resumption
        1175                 :      * value).
        1176                 :      */
        1177            5813 :     Value exception = UndefinedValue();
        1178            5813 :     bool exceptionPending = cx->isExceptionPending();
        1179            5813 :     if (exceptionPending) {
        1180              44 :         exception = cx->getPendingException();
        1181              44 :         cx->clearPendingException();
        1182                 :     }
        1183                 :
        1184                 :     /* We should only receive single-step traps for scripted frames. */
        1185            5813 :     JS_ASSERT(fp->isScriptFrame());
        1186                 :
        1187                 :     /*
        1188                 :      * Build list of Debugger.Frame instances referring to this frame with
        1189                 :      * onStep handlers.
        1190                 :      */
        1191           11626 :     AutoObjectVector frames(cx);
        1192           11609 :     for (FrameRange r(cx, fp); !r.empty(); r.popFront()) {
        1193            5796 :         JSObject *frame = r.frontFrame();
        1194           11337 :         if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
        1195            5541 :             !frames.append(frame))
        1196                 :         {
        1197               0 :             return JSTRAP_ERROR;
        1198                 :         }
        1199                 :     }
        1200                 :
        1201                 : #ifdef DEBUG
        1202                 :     /*
        1203                 :      * Validate the single-step count on this frame's script, to ensure that
        1204                 :      * we're not receiving traps we didn't ask for. Even when frames is
        1205                 :      * non-empty (and thus we know this trap was requested), do the check
        1206                 :      * anyway, to make sure the count has the correct non-zero value.
        1207                 :      *
        1208                 :      * The converse --- ensuring that we do receive traps when we should --- can
        1209                 :      * be done with unit tests.
        1210                 :      */
        1211                 :     {
        1212            5813 :         uint32_t stepperCount = 0;
        1213            5813 :         JSScript *trappingScript = fp->script();
        1214            5813 :         GlobalObject *global = &fp->scopeChain().global();
        1215            5813 :         if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
        1216           11592 :             for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) {
        1217            5796 :                 Debugger *dbg = *p;
        1218           13777 :                 for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) {
        1219            7981 :                     StackFrame *frame = r.front().key;
        1220            7981 :                     JSObject *frameobj = r.front().value;
        1221           23925 :                     if (frame->isScriptFrame() &&
        1222            7981 :                         frame->script() == trappingScript &&
        1223            7963 :                         !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
        1224                 :                     {
        1225            6456 :                         stepperCount++;
        1226                 :                     }
        1227                 :                 }
        1228                 :             }
        1229                 :         }
        1230            5813 :         if (trappingScript->compileAndGo)
        1231            5813 :             JS_ASSERT(stepperCount == trappingScript->stepModeCount());
        1232                 :         else
        1233               0 :             JS_ASSERT(stepperCount <= trappingScript->stepModeCount());
        1234                 :     }
        1235                 : #endif
        1236                 :
        1237                 :     /* Call all the onStep handlers we found. */
        1238           11318 :     for (JSObject **p = frames.begin(); p != frames.end(); p++) {
        1239            5541 :         JSObject *frame = *p;
        1240            5541 :         Debugger *dbg = Debugger::fromChildJSObject(frame);
        1241           11082 :         AutoCompartment ac(cx, dbg->object);
        1242            5541 :         if (!ac.enter())
        1243               0 :             return JSTRAP_ERROR;
        1244            5541 :         const Value &handler = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
        1245                 :         Value rval;
        1246            5541 :         bool ok = Invoke(cx, ObjectValue(*frame), handler, 0, NULL, &rval);
        1247            5541 :         JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rval, vp);
        1248            5541 :         if (st != JSTRAP_CONTINUE)
        1249              36 :             return st;
        1250                 :     }
        1251                 :
        1252            5777 :     vp->setUndefined();
        1253            5777 :     if (exceptionPending)
        1254              44 :         cx->setPendingException(exception);
        1255            5777 :     return JSTRAP_CONTINUE;
        1256                 : }
        1257                 :
        1258                 :
        1259                 : /*** Debugger JSObjects **************************************************************************/
        1260                 :
        1261                 : void
        1262              54 : Debugger::markKeysInCompartment(JSTracer *tracer)
        1263                 : {
        1264                 :     /*
        1265                 :      * WeakMap::Range is deliberately private, to discourage C++ code from
        1266                 :      * enumerating WeakMap keys. However in this case we need access, so we
        1267                 :      * make a base-class reference. Range is public in HashMap.
        1268                 :      */
        1269                 :     typedef HashMap<HeapPtrObject, HeapPtrObject, DefaultHasher<HeapPtrObject>, RuntimeAllocPolicy>
        1270                 :         ObjectMap;
        1271              54 :     const ObjectMap &objStorage = objects;
        1272             162 :     for (ObjectMap::Range r = objStorage.all(); !r.empty(); r.popFront()) {
        1273             108 :         const HeapPtrObject &key = r.front().key;
        1274             216 :         HeapPtrObject tmp(key);
        1275             108 :         gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key");
        1276             108 :         JS_ASSERT(tmp == key);
        1277                 :     }
        1278                 :
        1279              54 :     const ObjectMap &envStorage = environments;
        1280              99 :     for (ObjectMap::Range r = envStorage.all(); !r.empty(); r.popFront()) {
        1281              45 :         const HeapPtrObject &key = r.front().key;
        1282              90 :         HeapPtrObject tmp(key);
        1283              45 :         js::gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key");
        1284              45 :         JS_ASSERT(tmp == key);
        1285                 :     }
        1286                 :
        1287                 :     typedef HashMap<HeapPtrScript, HeapPtrObject, DefaultHasher<HeapPtrScript>, RuntimeAllocPolicy>
        1288                 :         ScriptMap;
        1289              54 :     const ScriptMap &scriptStorage = scripts;
        1290             954 :     for (ScriptMap::Range r = scriptStorage.all(); !r.empty(); r.popFront()) {
        1291             900 :         const HeapPtrScript &key = r.front().key;
        1292            1800 :         HeapPtrScript tmp(key);
        1293             900 :         gc::MarkScript(tracer, &tmp, "cross-compartment WeakMap key");
        1294             900 :         JS_ASSERT(tmp == key);
        1295                 :     }
        1296              54 : }
        1297                 :
        1298                 : /*
        1299                 :  * Ordinarily, WeakMap keys and values are marked because at some point it was
        1300                 :  * discovered that the WeakMap was live; that is, some object containing the
        1301                 :  * WeakMap was marked during mark phase.
        1302                 :  *
        1303                 :  * However, during compartment GC, we have to do something about
        1304                 :  * cross-compartment WeakMaps in non-GC'd compartments. If their keys and values
        1305                 :  * might need to be marked, we have to do it manually.
        1306                 :  *
        1307                 :  * Each Debugger object keeps three cross-compartment WeakMaps: objects, script,
        1308                 :  * and environments. They have the nice property that all their values are in
        1309                 :  * the same compartment as the Debugger object, so we only need to mark the
        1310                 :  * keys. We must simply mark all keys that are in a compartment being GC'd.
        1311                 :  *
        1312                 :  * We must scan all Debugger objects regardless of whether they *currently*
        1313                 :  * have any debuggees in a compartment being GC'd, because the WeakMap
        1314                 :  * entries persist even when debuggees are removed.
        1315                 :  *
        1316                 :  * This happens during the initial mark phase, not iterative marking, because
        1317                 :  * all the edges being reported here are strong references.
        1318                 :  */
        1319                 : void
        1320           38463 : Debugger::markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer)
        1321                 : {
        1322           38463 :     JSRuntime *rt = tracer->runtime;
        1323                 :
        1324                 :     /*
        1325                 :      * Mark all objects in comp that are referents of Debugger.Objects in other
        1326                 :      * compartments.
        1327                 :      */
        1328           84143 :     for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
        1329            7217 :         Debugger *dbg = Debugger::fromLinks(p);
        1330            7217 :         if (!dbg->object->compartment()->isCollecting())
        1331              54 :             dbg->markKeysInCompartment(tracer);
        1332                 :     }
        1333           38463 : }
        1334                 :
        1335                 : /*
        1336                 :  * This method has two tasks:
        1337                 :  *   1. Mark Debugger objects that are unreachable except for debugger hooks that
        1338                 :  *      may yet be called.
        1339                 :  *   2. Mark breakpoint handlers.
        1340                 :  *
        1341                 :  * This happens during the iterative part of the GC mark phase. This method
        1342                 :  * returns true if it has to mark anything; GC calls it repeatedly until it
        1343                 :  * returns false.
        1344                 :  */
        1345                 : bool
        1346           76944 : Debugger::markAllIteratively(GCMarker *trc)
        1347                 : {
        1348           76944 :     bool markedAny = false;
        1349                 :
        1350                 :     /*
        1351                 :      * Find all Debugger objects in danger of GC. This code is a little
        1352                 :      * convoluted since the easiest way to find them is via their debuggees.
        1353                 :      */
        1354           76944 :     JSRuntime *rt = trc->runtime;
        1355          245112 :     for (CompartmentsIter c(rt); !c.done(); c.next()) {
        1356          168168 :         const GlobalObjectSet &debuggees = c->getDebuggees();
        1357          177484 :         for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
        1358            9316 :             GlobalObject *global = r.front();
        1359            9316 :             if (IsAboutToBeFinalized(global))
        1360            6470 :                 continue;
        1361                 :
        1362                 :             /*
        1363                 :              * Every debuggee has at least one debugger, so in this case
        1364                 :              * getDebuggers can't return NULL.
        1365                 :              */
        1366            2846 :             const GlobalObject::DebuggerVector *debuggers = global->getDebuggers();
        1367            2846 :             JS_ASSERT(debuggers);
        1368            9703 :             for (Debugger * const *p = debuggers->begin(); p != debuggers->end(); p++) {
        1369            6857 :                 Debugger *dbg = *p;
        1370                 :
        1371                 :                 /*
        1372                 :                  * dbg is a Debugger with at least one debuggee. Check three things:
        1373                 :                  *   - dbg is actually in a compartment being GC'd
        1374                 :                  *   - it isn't already marked
        1375                 :                  *   - it actually has hooks that might be called
        1376                 :                  */
        1377            6857 :                 HeapPtrObject &dbgobj = dbg->toJSObjectRef();
        1378            6857 :                 if (!dbgobj->compartment()->isCollecting())
        1379             108 :                     continue;
        1380                 :
        1381            6749 :                 bool dbgMarked = !IsAboutToBeFinalized(dbgobj);
        1382            6749 :                 if (!dbgMarked && dbg->hasAnyLiveHooks()) {
        1383                 :                     /*
        1384                 :                      * obj could be reachable only via its live, enabled
        1385                 :                      * debugger hooks, which may yet be called.
        1386                 :                      */
        1387             802 :                     MarkObject(trc, &dbgobj, "enabled Debugger");
        1388             802 :                     markedAny = true;
        1389             802 :                     dbgMarked = true;
        1390                 :                 }
        1391                 :
        1392            6749 :                 if (dbgMarked) {
        1393                 :                     /* Search for breakpoints to mark. */
        1394            7158 :                     for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
        1395             531 :                         if (!IsAboutToBeFinalized(bp->site->script)) {
        1396                 :                             /*
        1397                 :                              * The debugger and the script are both live.
        1398                 :                              * Therefore the breakpoint handler is live.
        1399                 :                              */
        1400             531 :                             if (IsAboutToBeFinalized(bp->getHandler())) {
        1401             163 :                                 MarkObject(trc, &bp->getHandlerRef(), "breakpoint handler");
        1402             163 :                                 markedAny = true;
        1403                 :                             }
        1404                 :                         }
        1405                 :                     }
        1406                 :                 }
        1407                 :             }
        1408                 :         }
        1409                 :     }
        1410           76944 :     return markedAny;
        1411                 : }
        1412                 :
        1413                 : void
        1414           53253 : Debugger::traceObject(JSTracer *trc, JSObject *obj)
        1415                 : {
        1416           53253 :     if (Debugger *dbg = Debugger::fromJSObject(obj))
        1417            6824 :         dbg->trace(trc);
        1418           53253 : }
        1419                 :
        1420                 : void
        1421            6824 : Debugger::trace(JSTracer *trc)
        1422                 : {
        1423            6824 :     if (uncaughtExceptionHook)
        1424               0 :         MarkObject(trc, &uncaughtExceptionHook, "hooks");
        1425                 :
        1426                 :     /*
        1427                 :      * Mark Debugger.Frame objects. These are all reachable from JS, because the
        1428                 :      * corresponding StackFrames are still on the stack.
        1429                 :      *
        1430                 :      * (Once we support generator frames properly, we will need
        1431                 :      * weakly-referenced Debugger.Frame objects as well, for suspended generator
        1432                 :      * frames.)
        1433                 :      */
        1434           10928 :     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
        1435            4104 :         HeapPtrObject &frameobj = r.front().value;
        1436            4104 :         JS_ASSERT(frameobj->getPrivate());
        1437            4104 :         MarkObject(trc, &frameobj, "live Debugger.Frame");
        1438                 :     }
        1439                 :
        1440                 :     /* Trace the weak map from JSScript instances to Debugger.Script objects. */
        1441            6824 :     scripts.trace(trc);
        1442                 :
        1443                 :     /* Trace the referent -> Debugger.Object weak map. */
        1444            6824 :     objects.trace(trc);
        1445                 :
        1446                 :     /* Trace the referent -> Debugger.Environment weak map. */
        1447            6824 :     environments.trace(trc);
        1448            6824 : }
        1449                 :
        1450                 : void
        1451           38427 : Debugger::sweepAll(FreeOp *fop)
        1452                 : {
        1453           38427 :     JSRuntime *rt = fop->runtime();
        1454                 :
        1455           84071 :     for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
        1456            7217 :         Debugger *dbg = Debugger::fromLinks(p);
        1457                 :
        1458            7217 :         if (IsAboutToBeFinalized(dbg->object)) {
        1459                 :             /*
        1460                 :              * dbg is being GC'd. Detach it from its debuggees. The debuggee
        1461                 :              * might be GC'd too. Since detaching requires access to both
        1462                 :              * objects, this must be done before finalize time.
        1463                 :              */
        1464            8500 :             for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
        1465            4241 :                 dbg->removeDebuggeeGlobal(fop, e.front(), NULL, &e);
        1466                 :         }
        1467                 :
        1468                 :     }
        1469                 :
        1470          122385 :     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
        1471                 :         /* For each debuggee being GC'd, detach it from all its debuggers. */
        1472           83958 :         GlobalObjectSet &debuggees = (*c)->getDebuggees();
        1473           85407 :         for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
        1474            1449 :             GlobalObject *global = e.front();
        1475            1449 :             if (IsAboutToBeFinalized(global))
        1476              90 :                 detachAllDebuggersFromGlobal(fop, global, &e);
        1477                 :         }
        1478                 :     }
        1479           38427 : }
        1480                 :
        1481                 : void
        1482              90 : Debugger::detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
        1483                 :                                        GlobalObjectSet::Enum *compartmentEnum)
        1484                 : {
        1485              90 :     const GlobalObject::DebuggerVector *debuggers = global->getDebuggers();
        1486              90 :     JS_ASSERT(!debuggers->empty());
        1487             270 :     while (!debuggers->empty())
        1488              90 :         debuggers->back()->removeDebuggeeGlobal(fop, global, compartmentEnum, NULL);
        1489              90 : }
        1490                 :
        1491                 : void
        1492           27589 : Debugger::finalize(FreeOp *fop, JSObject *obj)
        1493                 : {
        1494           27589 :     Debugger *dbg = fromJSObject(obj);
        1495           27589 :     if (!dbg)
        1496           23330 :         return;
        1497            4259 :     JS_ASSERT(dbg->debuggees.empty());
        1498            4259 :     fop->delete_(dbg);
        1499                 : }
        1500                 :
        1501                 : Class Debugger::jsclass = {
        1502                 :     "Debugger",
        1503                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
        1504                 :     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
        1505                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        1506                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize,
        1507                 :     NULL,                 /* checkAccess */
        1508                 :     NULL,                 /* call        */
        1509                 :     NULL,                 /* construct   */
        1510                 :     NULL,                 /* hasInstance */
        1511                 :     Debugger::traceObject
        1512                 : };
        1513                 :
        1514                 : Debugger *
        1515           10784 : Debugger::fromThisValue(JSContext *cx, const CallArgs &args, const char *fnname)
        1516                 : {
        1517           10784 :     if (!args.thisv().isObject()) {
        1518               9 :         ReportObjectRequired(cx);
        1519               9 :         return NULL;
        1520                 :     }
        1521           10775 :     JSObject *thisobj = &args.thisv().toObject();
        1522           10775 :     if (thisobj->getClass() != &Debugger::jsclass) {
        1523                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        1524              18 :                              "Debugger", fnname, thisobj->getClass()->name);
        1525              18 :         return NULL;
        1526                 :     }
        1527                 :
        1528                 :     /*
        1529                 :      * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't
        1530                 :      * really a Debugger object. The prototype object is distinguished by
        1531                 :      * having a NULL private value.
        1532                 :      */
        1533           10757 :     Debugger *dbg = fromJSObject(thisobj);
        1534           10757 :     if (!dbg) {
        1535                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        1536              27 :                              "Debugger", fnname, "prototype object");
        1537                 :     }
        1538           10757 :     return dbg;
        1539                 : }
        1540                 :
        1541                 : #define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg)                       \
        1542                 :     CallArgs args = CallArgsFromVp(argc, vp);                                \
        1543                 :     Debugger *dbg = Debugger::fromThisValue(cx, args, fnname);               \
        1544                 :     if (!dbg)                                                                \
        1545                 :         return false
        1546                 :
        1547                 : JSBool
        1548              90 : Debugger::getEnabled(JSContext *cx, unsigned argc, Value *vp)
        1549                 : {
        1550              90 :     THIS_DEBUGGER(cx, argc, vp, "get enabled", args, dbg);
        1551              90 :     args.rval().setBoolean(dbg->enabled);
        1552              90 :     return true;
        1553                 : }
        1554                 :
        1555                 : JSBool
        1556             351 : Debugger::setEnabled(JSContext *cx, unsigned argc, Value *vp)
        1557                 : {
        1558             351 :     REQUIRE_ARGC("Debugger.set enabled", 1);
        1559             351 :     THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg);
        1560             351 :     bool enabled = js_ValueToBoolean(args[0]);
        1561                 :
        1562             351 :     if (enabled != dbg->enabled) {
        1563             297 :         for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
        1564              18 :             if (enabled)
        1565               9 :                 bp->site->inc(cx->runtime->defaultFreeOp());
        1566                 :             else
        1567               9 :                 bp->site->dec(cx->runtime->defaultFreeOp());
        1568                 :         }
        1569                 :     }
        1570                 :
        1571             351 :     dbg->enabled = enabled;
        1572             351 :     args.rval().setUndefined();
        1573             351 :     return true;
        1574                 : }
        1575                 :
        1576                 : JSBool
        1577              54 : Debugger::getHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which)
        1578                 : {
        1579              54 :     JS_ASSERT(which >= 0 && which < HookCount);
        1580              54 :     THIS_DEBUGGER(cx, argc, vp, "getHook", args, dbg);
        1581              27 :     args.rval() = dbg->object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + which);
        1582              27 :     return true;
        1583                 : }
        1584                 :
        1585                 : JSBool
        1586            5807 : Debugger::setHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which)
        1587                 : {
        1588            5807 :     JS_ASSERT(which >= 0 && which < HookCount);
        1589            5807 :     REQUIRE_ARGC("Debugger.setHook", 1);
        1590            5789 :     THIS_DEBUGGER(cx, argc, vp, "setHook", args, dbg);
        1591            5771 :     const Value &v = args[0];
        1592            5771 :     if (v.isObject()) {
        1593            5555 :         if (!v.toObject().isCallable()) {
        1594               9 :             js_ReportIsNotFunction(cx, vp, JSV2F_SEARCH_STACK);
        1595               9 :             return false;
        1596                 :         }
        1597             216 :     } else if (!v.isUndefined()) {
        1598              18 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
        1599              18 :         return false;
        1600                 :     }
        1601            5744 :     dbg->object->setReservedSlot(JSSLOT_DEBUG_HOOK_START + which, v);
        1602            5744 :     args.rval().setUndefined();
        1603            5744 :     return true;
        1604                 : }
        1605                 :
        1606                 : JSBool
        1607              54 : Debugger::getOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp)
        1608                 : {
        1609              54 :     return getHookImpl(cx, argc, vp, OnDebuggerStatement);
        1610                 : }
        1611                 :
        1612                 : JSBool
        1613            3898 : Debugger::setOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp)
        1614                 : {
        1615            3898 :     return setHookImpl(cx, argc, vp, OnDebuggerStatement);
        1616                 : }
        1617                 :
        1618                 : JSBool
        1619               0 : Debugger::getOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp)
        1620                 : {
        1621               0 :     return getHookImpl(cx, argc, vp, OnExceptionUnwind);
        1622                 : }
        1623                 :
        1624                 : JSBool
        1625             360 : Debugger::setOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp)
        1626                 : {
        1627             360 :     return setHookImpl(cx, argc, vp, OnExceptionUnwind);
        1628                 : }
        1629                 :
        1630                 : JSBool
        1631               0 : Debugger::getOnNewScript(JSContext *cx, unsigned argc, Value *vp)
        1632                 : {
        1633               0 :     return getHookImpl(cx, argc, vp, OnNewScript);
        1634                 : }
        1635                 :
        1636                 : JSBool
        1637              28 : Debugger::setOnNewScript(JSContext *cx, unsigned argc, Value *vp)
        1638                 : {
        1639              28 :     return setHookImpl(cx, argc, vp, OnNewScript);
        1640                 : }
        1641                 :
        1642                 : JSBool
        1643               0 : Debugger::getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp)
        1644                 : {
        1645               0 :     return getHookImpl(cx, argc, vp, OnEnterFrame);
        1646                 : }
        1647                 :
        1648                 : JSBool
        1649            1521 : Debugger::setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp)
        1650                 : {
        1651            1521 :     return setHookImpl(cx, argc, vp, OnEnterFrame);
        1652                 : }
        1653                 :
        1654                 : JSBool
        1655              27 : Debugger::getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp)
        1656                 : {
        1657              27 :     THIS_DEBUGGER(cx, argc, vp, "get uncaughtExceptionHook", args, dbg);
        1658              27 :     args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
        1659              27 :     return true;
        1660                 : }
        1661                 :
        1662                 : JSBool
        1663             153 : Debugger::setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp)
        1664                 : {
        1665             153 :     REQUIRE_ARGC("Debugger.set uncaughtExceptionHook", 1);
        1666             153 :     THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg);
        1667             144 :     if (!args[0].isNull() && (!args[0].isObject() || !args[0].toObject().isCallable())) {
        1668                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ASSIGN_FUNCTION_OR_NULL,
        1669              18 :                              "uncaughtExceptionHook");
        1670              18 :         return false;
        1671                 :     }
        1672                 :
        1673             126 :     dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
        1674             126 :     args.rval().setUndefined();
        1675             126 :     return true;
        1676                 : }
        1677                 :
        1678                 : JSObject *
        1679            3105 : Debugger::unwrapDebuggeeArgument(JSContext *cx, const Value &v)
        1680                 : {
        1681                 :     /*
        1682                 :      * The argument to {add,remove,has}Debuggee may be
        1683                 :      *   - a Debugger.Object belonging to this Debugger: return its referent
        1684                 :      *   - a cross-compartment wrapper: return the wrapped object
        1685                 :      *   - any other non-Debugger.Object object: return it
        1686                 :      * If it is a primitive, or a Debugger.Object that belongs to some other
        1687                 :      * Debugger, throw a TypeError.
        1688                 :      */
        1689            3105 :     JSObject *obj = NonNullObject(cx, v);
        1690            3105 :     if (obj) {
        1691            2907 :         if (obj->getClass() == &DebuggerObject_class) {
        1692             180 :             Value rv = v;
        1693             180 :             if (!unwrapDebuggeeValue(cx, &rv))
        1694              27 :                 return NULL;
        1695             153 :             return &rv.toObject();
        1696                 :         }
        1697            2727 :         if (IsCrossCompartmentWrapper(obj))
        1698            2619 :             return &GetProxyPrivate(obj).toObject();
        1699                 :     }
        1700             306 :     return obj;
        1701                 : }
        1702                 :
        1703                 : JSBool
        1704            2034 : Debugger::addDebuggee(JSContext *cx, unsigned argc, Value *vp)
        1705                 : {
        1706            2034 :     REQUIRE_ARGC("Debugger.addDebuggee", 1);
        1707            2034 :     THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
        1708            2034 :     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
        1709            2034 :     if (!referent)
        1710              63 :         return false;
        1711            1971 :     GlobalObject *global = &referent->global();
        1712            1971 :     if (!dbg->addDebuggeeGlobal(cx, global))
        1713              36 :         return false;
        1714                 :
        1715            1935 :     Value v = ObjectValue(*referent);
        1716            1935 :     if (!dbg->wrapDebuggeeValue(cx, &v))
        1717               0 :         return false;
        1718            1935 :     args.rval() = v;
        1719            1935 :     return true;
        1720                 : }
        1721                 :
        1722                 : JSBool
        1723             225 : Debugger::removeDebuggee(JSContext *cx, unsigned argc, Value *vp)
        1724                 : {
        1725             225 :     REQUIRE_ARGC("Debugger.removeDebuggee", 1);
        1726             225 :     THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
        1727             225 :     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
        1728             225 :     if (!referent)
        1729              63 :         return false;
        1730             162 :     GlobalObject *global = &referent->global();
        1731             162 :     if (dbg->debuggees.has(global))
        1732             108 :         dbg->removeDebuggeeGlobal(cx->runtime->defaultFreeOp(), global, NULL, NULL);
        1733             162 :     args.rval().setUndefined();
        1734             162 :     return true;
        1735                 : }
        1736                 :
        1737                 : JSBool
        1738             432 : Debugger::hasDebuggee(JSContext *cx, unsigned argc, Value *vp)
        1739                 : {
        1740             432 :     REQUIRE_ARGC("Debugger.hasDebuggee", 1);
        1741             432 :     THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
        1742             432 :     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, args[0]);
        1743             432 :     if (!referent)
        1744              63 :         return false;
        1745             369 :     args.rval().setBoolean(!!dbg->debuggees.lookup(&referent->global()));
        1746             369 :     return true;
        1747                 : }
        1748                 :
        1749                 : JSBool
        1750             126 : Debugger::getDebuggees(JSContext *cx, unsigned argc, Value *vp)
        1751                 : {
        1752             126 :     THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg);
        1753             126 :     JSObject *arrobj = NewDenseAllocatedArray(cx, dbg->debuggees.count(), NULL);
        1754             126 :     if (!arrobj)
        1755               0 :         return false;
        1756             126 :     arrobj->ensureDenseArrayInitializedLength(cx, 0, dbg->debuggees.count());
        1757             126 :     unsigned i = 0;
        1758             217 :     for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
        1759              91 :         Value v = ObjectValue(*e.front());
        1760              91 :         if (!dbg->wrapDebuggeeValue(cx, &v))
        1761               0 :             return false;
        1762              91 :         arrobj->setDenseArrayElement(i++, v);
        1763                 :     }
        1764             126 :     args.rval().setObject(*arrobj);
        1765             126 :     return true;
        1766                 : }
        1767                 :
        1768                 : JSBool
        1769             414 : Debugger::getNewestFrame(JSContext *cx, unsigned argc, Value *vp)
        1770                 : {
        1771             414 :     THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg);
        1772                 :
        1773                 :     /*
        1774                 :      * cx->fp() would return the topmost frame in the current context.
        1775                 :      * Since there may be multiple contexts, use AllFramesIter instead.
        1776                 :      */
        1777            1251 :     for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) {
        1778            1233 :         if (dbg->observesFrame(i.fp()))
        1779             396 :             return dbg->getScriptFrame(cx, i.fp(), vp);
        1780                 :     }
        1781              18 :     args.rval().setNull();
        1782              18 :     return true;
        1783                 : }
        1784                 :
        1785                 : JSBool
        1786              36 : Debugger::clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp)
        1787                 : {
        1788              36 :     THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
        1789              72 :     for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
        1790              36 :         r.front()->compartment()->clearBreakpointsIn(cx, dbg, NULL);
        1791              36 :     return true;
        1792                 : }
        1793                 :
        1794                 : JSBool
        1795            4394 : Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
        1796                 : {
        1797            4394 :     CallArgs args = CallArgsFromVp(argc, vp);
        1798                 :
        1799                 :     /* Check that the arguments, if any, are cross-compartment wrappers. */
        1800            8239 :     for (unsigned i = 0; i < argc; i++) {
        1801            3980 :         const Value &arg = args[i];
        1802            3980 :         if (!arg.isObject())
        1803              72 :             return ReportObjectRequired(cx);
        1804            3908 :         JSObject *argobj = &arg.toObject();
        1805            3908 :         if (!IsCrossCompartmentWrapper(argobj)) {
        1806              63 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CCW_REQUIRED, "Debugger");
        1807              63 :             return false;
        1808                 :         }
        1809                 :     }
        1810                 :
        1811                 :     /* Get Debugger.prototype. */
        1812                 :     Value v;
        1813            4259 :     if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &v))
        1814               0 :         return false;
        1815            4259 :     JSObject *proto = &v.toObject();
        1816            4259 :     JS_ASSERT(proto->getClass() == &Debugger::jsclass);
        1817                 :
        1818                 :     /*
        1819                 :      * Make the new Debugger object. Each one has a reference to
        1820                 :      * Debugger.{Frame,Object,Script}.prototype in reserved slots. The rest of
        1821                 :      * the reserved slots are for hooks; they default to undefined.
        1822                 :      */
        1823            4259 :     JSObject *obj = NewObjectWithGivenProto(cx, &Debugger::jsclass, proto, NULL);
        1824            4259 :     if (!obj)
        1825               0 :         return false;
        1826           21295 :     for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
        1827           17036 :         obj->setReservedSlot(slot, proto->getReservedSlot(slot));
        1828                 :
        1829            4259 :     Debugger *dbg = cx->new_<Debugger>(cx, obj);
        1830            4259 :     if (!dbg)
        1831               0 :         return false;
        1832            4259 :     obj->setPrivate(dbg);
        1833            4259 :     if (!dbg->init(cx)) {
        1834               0 :         cx->delete_(dbg);
        1835               0 :         return false;
        1836                 :     }
        1837                 :
        1838                 :     /* Add the initial debuggees, if any. */
        1839            8086 :     for (unsigned i = 0; i < argc; i++) {
        1840            3845 :         GlobalObject *debuggee = &GetProxyPrivate(&args[i].toObject()).toObject().global();
        1841            3845 :         if (!dbg->addDebuggeeGlobal(cx, debuggee))
        1842              18 :             return false;
        1843                 :     }
        1844                 :
        1845            4241 :     args.rval().setObject(*obj);
        1846            4241 :     return true;
        1847                 : }
        1848                 :
        1849                 : bool
        1850            5816 : Debugger::addDebuggeeGlobal(JSContext *cx, GlobalObject *global)
        1851                 : {
        1852            5816 :     if (debuggees.has(global))
        1853            1323 :         return true;
        1854                 :
        1855            4493 :     JSCompartment *debuggeeCompartment = global->compartment();
        1856                 :
        1857                 :     /*
        1858                 :      * Check for cycles. If global's compartment is reachable from this
        1859                 :      * Debugger object's compartment by following debuggee-to-debugger links,
        1860                 :      * then adding global would create a cycle. (Typically nobody is debugging
        1861                 :      * the debugger, in which case we zip through this code without looping.)
        1862                 :      */
        1863            8986 :     Vector<JSCompartment *> visited(cx);
        1864            4493 :     if (!visited.append(object->compartment()))
        1865               0 :         return false;
        1866            9031 :     for (size_t i = 0; i < visited.length(); i++) {
        1867            4592 :         JSCompartment *c = visited[i];
        1868            4592 :         if (c == debuggeeCompartment) {
        1869              54 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_LOOP);
        1870              54 :             return false;
        1871                 :         }
        1872                 :
        1873                 :         /*
        1874                 :          * Find all compartments containing debuggers debugging global objects
        1875                 :          * in c. Add those compartments to visited.
        1876                 :          */
        1877            4637 :         for (GlobalObjectSet::Range r = c->getDebuggees().all(); !r.empty(); r.popFront()) {
        1878              99 :             GlobalObject::DebuggerVector *v = r.front()->getDebuggers();
        1879             198 :             for (Debugger **p = v->begin(); p != v->end(); p++) {
        1880              99 :                 JSCompartment *next = (*p)->object->compartment();
        1881              99 :                 if (Find(visited, next) == visited.end() && !visited.append(next))
        1882               0 :                     return false;
        1883                 :             }
        1884                 :         }
        1885                 :     }
        1886                 :
        1887                 :     /* Refuse to enable debug mode for a compartment that has running scripts. */
        1888            4439 :     if (!debuggeeCompartment->debugMode() && debuggeeCompartment->hasScriptsOnStack()) {
        1889               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
        1890               0 :         return false;
        1891                 :     }
        1892                 :
        1893                 :     /*
        1894                 :      * Each debugger-debuggee relation must be stored in up to three places.
        1895                 :      * JSCompartment::addDebuggee enables debug mode if needed.
        1896                 :      */
        1897            8878 :     AutoCompartment ac(cx, global);
        1898            4439 :     if (!ac.enter())
        1899               0 :         return false;
        1900            4439 :     GlobalObject::DebuggerVector *v = global->getOrCreateDebuggers(cx);
        1901            4439 :     if (!v || !v->append(this)) {
        1902               0 :         js_ReportOutOfMemory(cx);
        1903                 :     } else {
        1904            4439 :         if (!debuggees.put(global)) {
        1905               0 :             js_ReportOutOfMemory(cx);
        1906                 :         } else {
        1907            4439 :             if (global->getDebuggers()->length() > 1)
        1908            1086 :                 return true;
        1909            3353 :             if (debuggeeCompartment->addDebuggee(cx, global))
        1910            3353 :                 return true;
        1911                 :
        1912                 :             /* Maintain consistency on error. */
        1913               0 :             debuggees.remove(global);
        1914                 :         }
        1915               0 :         JS_ASSERT(v->back() == this);
        1916               0 :         v->popBack();
        1917                 :     }
        1918               0 :     return false;
        1919                 : }
        1920                 :
        1921                 : void
        1922            4439 : Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
        1923                 :                                GlobalObjectSet::Enum *compartmentEnum,
        1924                 :                                GlobalObjectSet::Enum *debugEnum)
        1925                 : {
        1926                 :     /*
        1927                 :      * Each debuggee is in two HashSets: one for its compartment and one for
        1928                 :      * its debugger (this). The caller might be enumerating either set; if so,
        1929                 :      * use HashSet::Enum::removeFront rather than HashSet::remove below, to
        1930                 :      * avoid invalidating the live enumerator.
        1931                 :      */
        1932            4439 :     JS_ASSERT(global->compartment()->getDebuggees().has(global));
        1933            4439 :     JS_ASSERT_IF(compartmentEnum, compartmentEnum->front() == global);
        1934            4439 :     JS_ASSERT(debuggees.has(global));
        1935            4439 :     JS_ASSERT_IF(debugEnum, debugEnum->front() == global);
        1936                 :
        1937                 :     /*
        1938                 :      * FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame
        1939                 :      * objects referring to a particular js::StackFrame. This is hard if
        1940                 :      * Debugger objects that are no longer debugging the relevant global might
        1941                 :      * have live Frame objects. So we take the easy way out and kill them here.
        1942                 :      * This is a bug, since it's observable and contrary to the spec. One
        1943                 :      * possible fix would be to put such objects into a compartment-wide bag
        1944                 :      * which slowPathOnLeaveFrame would have to examine.
        1945                 :      */
        1946            4484 :     for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
        1947              45 :         StackFrame *fp = e.front().key;
        1948              45 :         if (&fp->scopeChain().global() == global) {
        1949              36 :             e.front().value->setPrivate(NULL);
        1950              36 :             e.removeFront();
        1951                 :         }
        1952                 :     }
        1953                 :
        1954            4439 :     GlobalObject::DebuggerVector *v = global->getDebuggers();
        1955                 :     Debugger **p;
        1956            4508 :     for (p = v->begin(); p != v->end(); p++) {
        1957            4508 :         if (*p == this)
        1958            4439 :             break;
        1959                 :     }
        1960            4439 :     JS_ASSERT(p != v->end());
        1961                 :
        1962                 :     /*
        1963                 :      * The relation must be removed from up to three places: *v and debuggees
        1964                 :      * for sure, and possibly the compartment's debuggee set.
        1965                 :      */
        1966            4439 :     v->erase(p);
        1967            4439 :     if (v->empty())
        1968            3353 :         global->compartment()->removeDebuggee(fop, global, compartmentEnum);
        1969            4439 :     if (debugEnum)
        1970            4241 :         debugEnum->removeFront();
        1971                 :     else
        1972             198 :         debuggees.remove(global);
        1973            4439 : }
        1974                 :
        1975                 : /*
        1976                 :  * A class for parsing 'findScripts' query arguments and searching for
        1977                 :  * scripts that match the criteria they represent.
        1978                 :  */
        1979            1053 : class Debugger::ScriptQuery {
        1980                 :   public:
        1981                 :     /* Construct a ScriptQuery to use matching scripts for |dbg|. */
        1982            1053 :     ScriptQuery(JSContext *cx, Debugger *dbg):
        1983            1053 :         cx(cx), debugger(dbg), compartments(cx), innermostForGlobal(cx) {}
        1984                 :
        1985                 :     /*
        1986                 :      * Initialize this ScriptQuery. Raise an error and return false if we
        1987                 :      * haven't enough memory.
        1988                 :      */
        1989            1053 :     bool init() {
        1990            3159 :         if (!globals.init() ||
        1991            1053 :             !compartments.init() ||
        1992            1053 :             !innermostForGlobal.init())
        1993                 :         {
        1994               0 :             js_ReportOutOfMemory(cx);
        1995               0 :             return false;
        1996                 :         }
        1997                 :
        1998            1053 :         return true;
        1999                 :     }
        2000                 :
        2001                 :     /*
        2002                 :      * Parse the query object |query|, and prepare to match only the scripts
        2003                 :      * it specifies.
        2004                 :      */
        2005             972 :     bool parseQuery(JSObject *query) {
        2006                 :         /*
        2007                 :          * Check for a 'global' property, which limits the results to those
        2008                 :          * scripts scoped to a particular global object.
        2009                 :          */
        2010                 :         Value global;
        2011             972 :         if (!query->getProperty(cx, cx->runtime->atomState.globalAtom, &global))
        2012               0 :             return false;
        2013             972 :         if (global.isUndefined()) {
        2014             558 :             matchAllDebuggeeGlobals();
        2015                 :         } else {
        2016             414 :             JSObject *referent = debugger->unwrapDebuggeeArgument(cx, global);
        2017             414 :             if (!referent)
        2018              36 :                 return false;
        2019             378 :             GlobalObject *globalObject = &referent->global();
        2020                 :
        2021                 :             /*
        2022                 :              * If the given global isn't a debuggee, just leave the set of
        2023                 :              * acceptable globals empty; we'll return no scripts.
        2024                 :              */
        2025             378 :             if (debugger->debuggees.has(globalObject)) {
        2026             342 :                 if (!matchSingleGlobal(globalObject))
        2027               0 :                     return false;
        2028                 :             }
        2029                 :         }
        2030                 :
        2031                 :         /* Check for a 'url' property. */
        2032             936 :         if (!query->getProperty(cx, cx->runtime->atomState.urlAtom, &url))
        2033               0 :             return false;
        2034             936 :         if (!url.isUndefined() && !url.isString()) {
        2035                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
        2036              36 :                                  "query object's 'url' property", "neither undefined nor a string");
        2037              36 :             return false;
        2038                 :         }
        2039                 :
        2040                 :         /* Check for a 'line' property. */
        2041                 :         Value lineProperty;
        2042             900 :         if (!query->getProperty(cx, cx->runtime->atomState.lineAtom, &lineProperty))
        2043               0 :             return false;
        2044             900 :         if (lineProperty.isUndefined()) {
        2045             270 :             hasLine = false;
        2046             630 :         } else if (lineProperty.isNumber()) {
        2047             594 :             if (url.isUndefined()) {
        2048              18 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_QUERY_LINE_WITHOUT_URL);
        2049              18 :                 return false;
        2050                 :             }
        2051             576 :             double doubleLine = lineProperty.toNumber();
        2052             576 :             if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) {
        2053              27 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_LINE);
        2054              27 :                 return false;
        2055                 :             }
        2056             549 :             hasLine = true;
        2057             549 :             line = doubleLine;
        2058                 :         } else {
        2059                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
        2060                 :                                  "query object's 'line' property",
        2061              36 :                                  "neither undefined nor an integer");
        2062              36 :             return false;
        2063                 :         }
        2064                 :
        2065                 :         /* Check for an 'innermost' property. */
        2066                 :         Value innermostProperty;
        2067             819 :         if (!query->getProperty(cx, cx->runtime->atomState.innermostAtom, &innermostProperty))
        2068               0 :             return false;
        2069             819 :         innermost = js_ValueToBoolean(innermostProperty);
        2070             819 :         if (innermost) {
        2071                 :             /* Technically, we need only check hasLine, but this is clearer. */
        2072             252 :             if (url.isUndefined() || !hasLine) {
        2073                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
        2074              18 :                                      JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
        2075              18 :                 return false;
        2076                 :             }
        2077                 :         }
        2078                 :
        2079             801 :         return true;
        2080                 :     }
        2081                 :
        2082                 :     /* Set up this ScriptQuery appropriately for a missing query argument. */
        2083              81 :     bool omittedQuery() {
        2084              81 :         url.setUndefined();
        2085              81 :         hasLine = false;
        2086              81 :         innermost = false;
        2087              81 :         return matchAllDebuggeeGlobals();
        2088                 :     }
        2089                 :
        2090                 :     /*
        2091                 :      * Search all relevant compartments and the stack for scripts matching
        2092                 :      * this query, and append the matching scripts to |vector|.
        2093                 :      */
        2094             882 :     bool findScripts(AutoScriptVector *vector) {
        2095             882 :         if (!prepareQuery())
        2096               0 :             return false;
        2097                 :
        2098                 :         /* Search each compartment for debuggee scripts. */
        2099            1989 :         for (CompartmentSet::Range r = compartments.all(); !r.empty(); r.popFront()) {
        2100            8019 :             for (gc::CellIter i(r.front(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
        2101            6912 :                 JSScript *script = i.get<JSScript>();
        2102            6912 :                 GlobalObject *global = script->getGlobalObjectOrNull();
        2103            6912 :                 if (global && !consider(script, global, vector))
        2104               0 :                     return false;
        2105                 :             }
        2106                 :         }
        2107                 :
        2108                 :         /*
        2109                 :          * Since eval scripts have no global, we need to find them via the call
        2110                 :          * stack, where frame's scope tells us the global in use.
        2111                 :          */
        2112            2223 :         for (FrameRegsIter fri(cx); !fri.done(); ++fri) {
        2113            1341 :             if (fri.fp()->isEvalFrame()) {
        2114              27 :                 JSScript *script = fri.fp()->script();
        2115                 :
        2116                 :                 /*
        2117                 :                  * If eval scripts never have global objects set, then we don't need
        2118                 :                  * to check the existing script vector for duplicates, since we only
        2119                 :                  * include scripts with globals above.
        2120                 :                  */
        2121              27 :                 JS_ASSERT(!script->getGlobalObjectOrNull());
        2122                 :
        2123              27 :                 GlobalObject *global = &fri.fp()->scopeChain().global();
        2124              27 :                 if (!consider(script, global, vector))
        2125               0 :                     return false;
        2126                 :             }
        2127                 :         }
        2128                 :
        2129                 :         /*
        2130                 :          * For most queries, we just accumulate results in 'vector' as we find
        2131                 :          * them. But if this is an 'innermost' query, then we've accumulated the
        2132                 :          * results in the 'innermostForGlobal' map. In that case, we now need to
        2133                 :          * walk that map and populate 'vector'.
        2134                 :          */
        2135             882 :         if (innermost) {
        2136             477 :             for (GlobalToScriptMap::Range r = innermostForGlobal.all(); !r.empty(); r.popFront()) {
        2137             243 :                 if (!vector->append(r.front().value)) {
        2138               0 :                     js_ReportOutOfMemory(cx);
        2139               0 :                     return false;
        2140                 :                 }
        2141                 :             }
        2142                 :         }
        2143                 :
        2144             882 :         return true;
        2145                 :     }
        2146                 :
        2147                 :   private:
        2148                 :     /* The context in which we should do our work. */
        2149                 :     JSContext *cx;
        2150                 :
        2151                 :     /* The debugger for which we conduct queries. */
        2152                 :     Debugger *debugger;
        2153                 :
        2154                 :     /* A script must run in one of these globals to match the query. */
        2155                 :     GlobalObjectSet globals;
        2156                 :
        2157                 :     typedef HashSet<JSCompartment *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy>
        2158                 :         CompartmentSet;
        2159                 :
        2160                 :     /* The smallest set of compartments that contains all globals in globals. */
        2161                 :     CompartmentSet compartments;
        2162                 :
        2163                 :     /* If this is a string, matching scripts have urls equal to it. */
        2164                 :     Value url;
        2165                 :
        2166                 :     /* url as a C string. */
        2167                 :     JSAutoByteString urlCString;
        2168                 :
        2169                 :     /* True if the query contained a 'line' property. */
        2170                 :     bool hasLine;
        2171                 :
        2172                 :     /* The line matching scripts must cover. */
        2173                 :     unsigned int line;
        2174                 :
        2175                 :     /* True if the query has an 'innermost' property whose value is true. */
        2176                 :     bool innermost;
        2177                 :
        2178                 :     typedef HashMap<GlobalObject *, JSScript *, DefaultHasher<GlobalObject *>, RuntimeAllocPolicy>
        2179                 :         GlobalToScriptMap;
        2180                 :
        2181                 :     /*
        2182                 :      * For 'innermost' queries, a map from global objects to the innermost
        2183                 :      * script we've seen so far in that global. (Instantiation code size
        2184                 :      * explosion ho!)
        2185                 :      */
        2186                 :     GlobalToScriptMap innermostForGlobal;
        2187                 :
        2188                 :     /* Arrange for this ScriptQuery to match only scripts that run in |global|. */
        2189             342 :     bool matchSingleGlobal(GlobalObject *global) {
        2190             342 :         JS_ASSERT(globals.count() == 0);
        2191             342 :         if (!globals.put(global)) {
        2192               0 :             js_ReportOutOfMemory(cx);
        2193               0 :             return false;
        2194                 :         }
        2195             342 :         return true;
        2196                 :     }
        2197                 :
        2198                 :     /*
        2199                 :      * Arrange for this ScriptQuery to match all scripts running in debuggee
        2200                 :      * globals.
        2201                 :      */
        2202             639 :     bool matchAllDebuggeeGlobals() {
        2203             639 :         JS_ASSERT(globals.count() == 0);
        2204                 :         /* Copy the debugger's set of debuggee globals to our global set. */
        2205            1404 :         for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
        2206             765 :             if (!globals.put(r.front())) {
        2207               0 :                 js_ReportOutOfMemory(cx);
        2208               0 :                 return false;
        2209                 :             }
        2210                 :         }
        2211             639 :         return true;
        2212                 :     }
        2213                 :
        2214                 :     /*
        2215                 :      * Given that parseQuery or omittedQuery has been called, prepare to
        2216                 :      * match scripts. Set urlCString as appropriate.
        2217                 :      */
        2218             882 :     bool prepareQuery() {
        2219                 :         /*
        2220                 :          * Compute the proper value for |compartments|, given the present
        2221                 :          * value of |globals|.
        2222                 :          */
        2223            1989 :         for (GlobalObjectSet::Range r = globals.all(); !r.empty(); r.popFront()) {
        2224            1107 :             if (!compartments.put(r.front()->compartment())) {
        2225               0 :                 js_ReportOutOfMemory(cx);
        2226               0 :                 return false;
        2227                 :             }
        2228                 :         }
        2229                 :
        2230                 :         /* Compute urlCString, if a url was given. */
        2231             882 :         if (url.isString()) {
        2232             684 :             if (!urlCString.encode(cx, url.toString()))
        2233               0 :                 return false;
        2234                 :         }
        2235                 :
        2236             882 :         return true;
        2237                 :     }
        2238                 :
        2239                 :     /*
        2240                 :      * If |script|, a script in |global|, matches this query, append it to
        2241                 :      * |vector| or place it in |innermostForGlobal|, as appropriate. Return true
        2242                 :      * if no error occurs, false if an error occurs.
        2243                 :      */
        2244            5409 :     bool consider(JSScript *script, GlobalObject *global, AutoScriptVector *vector) {
        2245            5409 :         if (!globals.has(global))
        2246               0 :             return true;
        2247            5409 :         if (urlCString.ptr()) {
        2248            4860 :             if (!script->filename || strcmp(script->filename, urlCString.ptr()) != 0)
        2249            2232 :                 return true;
        2250                 :         }
        2251            3177 :         if (hasLine) {
        2252            2268 :             if (line < script->lineno || script->lineno + js_GetScriptLineExtent(script) < line)
        2253             963 :                 return true;
        2254                 :         }
        2255                 :
        2256            2214 :         if (innermost) {
        2257                 :             /*
        2258                 :              * For 'innermost' queries, we don't place scripts in |vector| right
        2259                 :              * away; we may later find another script that is nested inside this
        2260                 :              * one. Instead, we record the innermost script we've found so far
        2261                 :              * for each global in innermostForGlobal, and only populate |vector|
        2262                 :              * at the bottom of findScripts, when we've traversed all the
        2263                 :              * scripts.
        2264                 :              *
        2265                 :              * So: check this script against the innermost one we've found so
        2266                 :              * far (if any), as recorded in innermostForGlobal, and replace that
        2267                 :              * if it's better.
        2268                 :              */
        2269            1170 :             GlobalToScriptMap::AddPtr p = innermostForGlobal.lookupForAdd(global);
        2270             585 :             if (p) {
        2271                 :                 /* Is our newly found script deeper than the last one we found? */
        2272             342 :                 JSScript *incumbent = p->value;
        2273             342 :                 if (script->staticLevel > incumbent->staticLevel)
        2274               0 :                     p->value = script;
        2275                 :             } else {
        2276                 :                 /*
        2277                 :                  * This is the first matching script we've encountered for this
        2278                 :                  * global, so it is thus the innermost such script.
        2279                 :                  */
        2280             243 :                 if (!innermostForGlobal.add(p, global, script)) {
        2281               0 :                     js_ReportOutOfMemory(cx);
        2282               0 :                     return false;
        2283                 :                 }
        2284                 :             }
        2285                 :         } else {
        2286                 :             /* Record this matching script in the results vector. */
        2287            1629 :             if (!vector->append(script)) {
        2288               0 :                 js_ReportOutOfMemory(cx);
        2289               0 :                 return false;
        2290                 :             }
        2291                 :         }
        2292                 :
        2293            2214 :         return true;
        2294                 :     }
        2295                 : };
        2296                 :
        2297                 : JSBool
        2298            1053 : Debugger::findScripts(JSContext *cx, unsigned argc, Value *vp)
        2299                 : {
        2300            1053 :     THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg);
        2301                 :
        2302            2106 :     ScriptQuery query(cx, dbg);
        2303            1053 :     if (!query.init())
        2304               0 :         return false;
        2305                 :
        2306            1053 :     if (argc >= 1) {
        2307             972 :         JSObject *queryObject = NonNullObject(cx, args[0]);
        2308             972 :         if (!queryObject || !query.parseQuery(queryObject))
        2309             171 :             return false;
        2310                 :     } else {
        2311              81 :         if (!query.omittedQuery())
        2312               0 :             return false;
        2313                 :     }
        2314                 :
        2315                 :     /*
        2316                 :      * Accumulate the scripts in an AutoScriptVector, instead of creating
        2317                 :      * the JS array as we go, because we mustn't allocate JS objects or GC
        2318                 :      * while we use the CellIter.
        2319                 :      */
        2320            1764 :     AutoScriptVector scripts(cx);
        2321                 :
        2322             882 :     if (!query.findScripts(&scripts))
        2323               0 :         return false;
        2324                 :
        2325             882 :     JSObject *result = NewDenseAllocatedArray(cx, scripts.length(), NULL);
        2326             882 :     if (!result)
        2327               0 :         return false;
        2328                 :
        2329             882 :     result->ensureDenseArrayInitializedLength(cx, 0, scripts.length());
        2330                 :
        2331            2754 :     for (size_t i = 0; i < scripts.length(); i++) {
        2332            1872 :         JSObject *scriptObject = dbg->wrapScript(cx, scripts[i]);
        2333            1872 :         if (!scriptObject)
        2334               0 :             return false;
        2335            1872 :         result->setDenseArrayElement(i, ObjectValue(*scriptObject));
        2336                 :     }
        2337                 :
        2338             882 :     args.rval().setObject(*result);
        2339             882 :     return true;
        2340                 : }
        2341                 :
        2342                 : JSPropertySpec Debugger::properties[] = {
        2343                 :     JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
        2344                 :     JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
        2345                 :             Debugger::setOnDebuggerStatement, 0),
        2346                 :     JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
        2347                 :             Debugger::setOnExceptionUnwind, 0),
        2348                 :     JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
        2349                 :     JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame, Debugger::setOnEnterFrame, 0),
        2350                 :     JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook,
        2351                 :             Debugger::setUncaughtExceptionHook, 0),
        2352                 :     JS_PS_END
        2353                 : };
        2354                 :
        2355                 : JSFunctionSpec Debugger::methods[] = {
        2356                 :     JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0),
        2357                 :     JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0),
        2358                 :     JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0),
        2359                 :     JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0),
        2360                 :     JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
        2361                 :     JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 1, 0),
        2362                 :     JS_FN("findScripts", Debugger::findScripts, 1, 0),
        2363                 :     JS_FS_END
        2364                 : };
        2365                 :
        2366                 :
        2367                 : /*** Debugger.Script *****************************************************************************/
        2368                 :
        2369                 : static inline JSScript *
        2370           64815 : GetScriptReferent(JSObject *obj)
        2371                 : {
        2372           64815 :     JS_ASSERT(obj->getClass() == &DebuggerScript_class);
        2373           64815 :     return static_cast<JSScript *>(obj->getPrivate());
        2374                 : }
        2375                 :
        2376                 : static inline void
        2377                 : SetScriptReferent(JSObject *obj, JSScript *script)
        2378                 : {
        2379                 :     JS_ASSERT(obj->getClass() == &DebuggerScript_class);
        2380                 :     obj->setPrivate(script);
        2381                 : }
        2382                 :
        2383                 : static void
        2384           46876 : DebuggerScript_trace(JSTracer *trc, JSObject *obj)
        2385                 : {
        2386                 :     /* This comes from a private pointer, so no barrier needed. */
        2387           46876 :     if (JSScript *script = GetScriptReferent(obj)) {
        2388            1152 :         MarkCrossCompartmentScriptUnbarriered(trc, &script, "Debugger.Script referent");
        2389            1152 :         obj->setPrivateUnbarriered(script);
        2390                 :     }
        2391           46876 : }
        2392                 :
        2393                 : Class DebuggerScript_class = {
        2394                 :     "Script",
        2395                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
        2396                 :     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
        2397                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        2398                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
        2399                 :     NULL,                 /* checkAccess */
        2400                 :     NULL,                 /* call        */
        2401                 :     NULL,                 /* construct   */
        2402                 :     NULL,                 /* hasInstance */
        2403                 :     DebuggerScript_trace
        2404                 : };
        2405                 :
        2406                 : JSObject *
        2407            2909 : Debugger::newDebuggerScript(JSContext *cx, JSScript *script)
        2408                 : {
        2409            2909 :     assertSameCompartment(cx, object.get());
        2410                 :
        2411            2909 :     JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject();
        2412            2909 :     JS_ASSERT(proto);
        2413            2909 :     JSObject *scriptobj = NewObjectWithGivenProto(cx, &DebuggerScript_class, proto, NULL);
        2414            2909 :     if (!scriptobj)
        2415               0 :         return NULL;
        2416            2909 :     scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object));
        2417            2909 :     scriptobj->setPrivate(script);
        2418                 :
        2419            2909 :     return scriptobj;
        2420                 : }
        2421                 :
        2422                 : JSObject *
        2423            7085 : Debugger::wrapScript(JSContext *cx, JSScript *script)
        2424                 : {
        2425            7085 :     assertSameCompartment(cx, object.get());
        2426            7085 :     JS_ASSERT(cx->compartment != script->compartment());
        2427           14170 :     ScriptWeakMap::AddPtr p = scripts.lookupForAdd(script);
        2428            7085 :     if (!p) {
        2429            2909 :         JSObject *scriptobj = newDebuggerScript(cx, script);
        2430                 :
        2431                 :         /* The allocation may have caused a GC, which can remove table entries. */
        2432            2909 :         if (!scriptobj || !scripts.relookupOrAdd(p, script, scriptobj))
        2433               0 :             return NULL;
        2434                 :     }
        2435                 :
        2436            7085 :     JS_ASSERT(GetScriptReferent(p->value) == script);
        2437            7085 :     return p->value;
        2438                 : }
        2439                 :
        2440                 : static JSObject *
        2441            5427 : DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const char *fnname)
        2442                 : {
        2443            5427 :     if (!v.isObject()) {
        2444               0 :         ReportObjectRequired(cx);
        2445               0 :         return NULL;
        2446                 :     }
        2447            5427 :     JSObject *thisobj = &v.toObject();
        2448            5427 :     if (thisobj->getClass() != &DebuggerScript_class) {
        2449                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        2450               0 :                              clsname, fnname, thisobj->getClass()->name);
        2451               0 :         return NULL;
        2452                 :     }
        2453                 :
        2454                 :     /*
        2455                 :      * Check for Debugger.Script.prototype, which is of class DebuggerScript_class
        2456                 :      * but whose script is null.
        2457                 :      */
        2458            5427 :     if (!GetScriptReferent(thisobj)) {
        2459               0 :         JS_ASSERT(!GetScriptReferent(thisobj));
        2460                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        2461               0 :                              clsname, fnname, "prototype object");
        2462               0 :         return NULL;
        2463                 :     }
        2464                 :
        2465            5427 :     return thisobj;
        2466                 : }
        2467                 :
        2468                 : static JSObject *
        2469            5427 : DebuggerScript_checkThis(JSContext *cx, const CallArgs &args, const char *fnname)
        2470                 : {
        2471            5427 :     return DebuggerScript_check(cx, args.thisv(), "Debugger.Script", fnname);
        2472                 : }
        2473                 :
        2474                 : #define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script)            \
        2475                 :     CallArgs args = CallArgsFromVp(argc, vp);                                       \
        2476                 :     JSObject *obj = DebuggerScript_checkThis(cx, args, fnname);                     \
        2477                 :     if (!obj)                                                                       \
        2478                 :         return false;                                                               \
        2479                 :     JSScript *script = GetScriptReferent(obj)
        2480                 :
        2481                 : static JSBool
        2482              54 : DebuggerScript_getUrl(JSContext *cx, unsigned argc, Value *vp)
        2483                 : {
        2484              54 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getUrl", args, obj, script);
        2485                 :
        2486              54 :     JSString *str = js_NewStringCopyZ(cx, script->filename);
        2487              54 :     if (!str)
        2488               0 :         return false;
        2489              54 :     args.rval().setString(str);
        2490              54 :     return true;
        2491                 : }
        2492                 :
        2493                 : static JSBool
        2494              54 : DebuggerScript_getStartLine(JSContext *cx, unsigned argc, Value *vp)
        2495                 : {
        2496              54 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getStartLine", args, obj, script);
        2497              54 :     args.rval().setNumber(script->lineno);
        2498              54 :     return true;
        2499                 : }
        2500                 :
        2501                 : static JSBool
        2502            1035 : DebuggerScript_getLineCount(JSContext *cx, unsigned argc, Value *vp)
        2503                 : {
        2504            1035 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineCount", args, obj, script);
        2505                 :
        2506            1035 :     unsigned maxLine = js_GetScriptLineExtent(script);
        2507            1035 :     args.rval().setNumber(double(maxLine));
        2508            1035 :     return true;
        2509                 : }
        2510                 :
        2511                 : static JSBool
        2512             207 : DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
        2513                 : {
        2514             207 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script);
        2515             207 :     Debugger *dbg = Debugger::fromChildJSObject(obj);
        2516                 :
        2517             207 :     JSObject *result = NewDenseEmptyArray(cx);
        2518             207 :     if (!result)
        2519               0 :         return false;
        2520             207 :     if (JSScript::isValidOffset(script->objectsOffset)) {
        2521                 :         /*
        2522                 :          * script->savedCallerFun indicates that this is a direct eval script
        2523                 :          * and the calling function is stored as script->objects()->vector[0].
        2524                 :          * It is not really a child script of this script, so skip it.
        2525                 :          */
        2526             117 :         JSObjectArray *objects = script->objects();
        2527             270 :         for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
        2528             153 :             JSObject *obj = objects->vector[i];
        2529             153 :             if (obj->isFunction()) {
        2530             135 :                 JSFunction *fun = static_cast<JSFunction *>(obj);
        2531             135 :                 JSObject *s = dbg->wrapScript(cx, fun->script());
        2532             135 :                 if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
        2533               0 :                     return false;
        2534                 :             }
        2535                 :         }
        2536                 :     }
        2537             207 :     args.rval().setObject(*result);
        2538             207 :     return true;
        2539                 : }
        2540                 :
        2541                 : static bool
        2542            2817 : ScriptOffset(JSContext *cx, JSScript *script, const Value &v, size_t *offsetp)
        2543                 : {
        2544                 :     double d;
        2545                 :     size_t off;
        2546                 :
        2547            2817 :     bool ok = v.isNumber();
        2548            2817 :     if (ok) {
        2549            2772 :         d = v.toNumber();
        2550            2772 :         off = size_t(d);
        2551                 :     }
        2552            2817 :     if (!ok || off != d || !IsValidBytecodeOffset(cx, script, off)) {
        2553             117 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_OFFSET);
        2554             117 :         return false;
        2555                 :     }
        2556            2700 :     *offsetp = off;
        2557            2700 :     return true;
        2558                 : }
        2559                 :
        2560                 : static JSBool
        2561            1521 : DebuggerScript_getOffsetLine(JSContext *cx, unsigned argc, Value *vp)
        2562                 : {
        2563            1521 :     REQUIRE_ARGC("Debugger.Script.getOffsetLine", 1);
        2564            1512 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetLine", args, obj, script);
        2565                 :     size_t offset;
        2566            1512 :     if (!ScriptOffset(cx, script, args[0], &offset))
        2567             108 :         return false;
        2568            1404 :     unsigned lineno = JS_PCToLineNumber(cx, script, script->code + offset);
        2569            1404 :     args.rval().setNumber(lineno);
        2570            1404 :     return true;
        2571                 : }
        2572                 :
        2573                 : class BytecodeRangeWithLineNumbers : private BytecodeRange
        2574                 : {
        2575                 :   public:
        2576                 :     using BytecodeRange::empty;
        2577                 :     using BytecodeRange::frontPC;
        2578                 :     using BytecodeRange::frontOpcode;
        2579                 :     using BytecodeRange::frontOffset;
        2580                 :
        2581            1962 :     BytecodeRangeWithLineNumbers(JSScript *script)
        2582            1962 :       : BytecodeRange(script), lineno(script->lineno), sn(script->notes()), snpc(script->code)
        2583                 :     {
        2584            1962 :         if (!SN_IS_TERMINATOR(sn))
        2585            1944 :             snpc += SN_DELTA(sn);
        2586            1962 :         updateLine();
        2587            1962 :     }
        2588                 :
        2589         5939568 :     void popFront() {
        2590         5939568 :         BytecodeRange::popFront();
        2591         5939568 :         if (!empty())
        2592         5937606 :             updateLine();
        2593         5939568 :     }
        2594                 :
        2595         5939568 :     size_t frontLineNumber() const { return lineno; }
        2596                 :
        2597                 :   private:
        2598         5939568 :     void updateLine() {
        2599                 :         /*
        2600                 :          * Determine the current line number by reading all source notes up to
        2601                 :          * and including the current offset.
        2602                 :          */
        2603        12179376 :         while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
        2604          300240 :             SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
        2605          300240 :             if (type == SRC_SETLINE)
        2606            1476 :                 lineno = size_t(js_GetSrcNoteOffset(sn, 0));
        2607          298764 :             else if (type == SRC_NEWLINE)
        2608            6066 :                 lineno++;
        2609                 :
        2610          300240 :             sn = SN_NEXT(sn);
        2611          300240 :             snpc += SN_DELTA(sn);
        2612                 :         }
        2613         5939568 :     }
        2614                 :
        2615                 :     size_t lineno;
        2616                 :     jssrcnote *sn;
        2617                 :     jsbytecode *snpc;
        2618                 : };
        2619                 :
        2620                 : static const size_t NoEdges = -1;
        2621                 : static const size_t MultipleEdges = -2;
        2622                 :
        2623                 : /*
        2624                 :  * FlowGraphSummary::populate(cx, script) computes a summary of script's
        2625                 :  * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}.
        2626                 :  *
        2627                 :  * jumpData[offset] is:
        2628                 :  *   - NoEdges if offset isn't the offset of an instruction, or if the
        2629                 :  *     instruction is apparently unreachable;
        2630                 :  *   - MultipleEdges if you can arrive at that instruction from
        2631                 :  *     instructions on multiple different lines OR it's the first
        2632                 :  *     instruction of the script;
        2633                 :  *   - otherwise, the (unique) line number of all instructions that can
        2634                 :  *     precede the instruction at offset.
        2635                 :  *
        2636                 :  * The generated graph does not contain edges for JSOP_RETSUB, which appears at
        2637                 :  * the end of finally blocks. The algorithm that uses this information works
        2638                 :  * anyway, because in non-exception cases, JSOP_RETSUB always returns to a
        2639                 :  * !FlowsIntoNext instruction (JSOP_GOTO/GOTOX or JSOP_RETRVAL) which generates
        2640                 :  * an edge if needed.
        2641                 :  */
        2642             981 : class FlowGraphSummary : public Vector<size_t> {
        2643                 :   public:
        2644                 :     typedef Vector<size_t> Base;
        2645             981 :     FlowGraphSummary(JSContext *cx) : Base(cx) {}
        2646                 :
        2647         2970153 :     void addEdge(size_t sourceLine, size_t targetOffset) {
        2648         2970153 :         FlowGraphSummary &self = *this;
        2649         2970153 :         if (self[targetOffset] == NoEdges)
        2650         2968398 :             self[targetOffset] = sourceLine;
        2651            1755 :         else if (self[targetOffset] != sourceLine)
        2652            1305 :             self[targetOffset] = MultipleEdges;
        2653         2970153 :     }
        2654                 :
        2655                 :     void addEdgeFromAnywhere(size_t targetOffset) {
        2656                 :         (*this)[targetOffset] = MultipleEdges;
        2657                 :     }
        2658                 :
        2659             981 :     bool populate(JSContext *cx, JSScript *script) {
        2660             981 :         if (!growBy(script->length))
        2661               0 :             return false;
        2662             981 :         FlowGraphSummary &self = *this;
        2663             981 :         self[0] = MultipleEdges;
        2664         8910576 :         for (size_t i = 1; i < script->length; i++)
        2665         8909595 :             self[i] = NoEdges;
        2666                 :
        2667             981 :         size_t prevLine = script->lineno;
        2668             981 :         JSOp prevOp = JSOP_NOP;
        2669         2970765 :         for (BytecodeRangeWithLineNumbers r(script); !r.empty(); r.popFront()) {
        2670         2969784 :             size_t lineno = r.frontLineNumber();
        2671         2969784 :             JSOp op = r.frontOpcode();
        2672                 :
        2673         2969784 :             if (FlowsIntoNext(prevOp))
        2674         2968749 :                 addEdge(prevLine, r.frontOffset());
        2675                 :
        2676         2969784 :             if (js_CodeSpec[op].type() == JOF_JUMP) {
        2677            1062 :                 addEdge(lineno, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
        2678         2968722 :             } else if (op == JSOP_TABLESWITCH || op == JSOP_LOOKUPSWITCH) {
        2679              99 :                 jsbytecode *pc = r.frontPC();
        2680              99 :                 size_t offset = r.frontOffset();
        2681              99 :                 ptrdiff_t step = JUMP_OFFSET_LEN;
        2682              99 :                 size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
        2683              99 :                 pc += step;
        2684              99 :                 addEdge(lineno, defaultOffset);
        2685                 :
        2686                 :                 int ncases;
        2687              99 :                 if (op == JSOP_TABLESWITCH) {
        2688              54 :                     int32_t low = GET_JUMP_OFFSET(pc);
        2689              54 :                     pc += JUMP_OFFSET_LEN;
        2690              54 :                     ncases = GET_JUMP_OFFSET(pc) - low + 1;
        2691              54 :                     pc += JUMP_OFFSET_LEN;
        2692                 :                 } else {
        2693              45 :                     ncases = GET_UINT16(pc);
        2694              45 :                     pc += UINT16_LEN;
        2695              45 :                     JS_ASSERT(ncases > 0);
        2696                 :                 }
        2697                 :
        2698             342 :                 for (int i = 0; i < ncases; i++) {
        2699             243 :                     if (op == JSOP_LOOKUPSWITCH)
        2700             108 :                         pc += UINT32_INDEX_LEN;
        2701             243 :                     size_t target = offset + GET_JUMP_OFFSET(pc);
        2702             243 :                     addEdge(lineno, target);
        2703             243 :                     pc += step;
        2704                 :                 }
        2705                 :             }
        2706                 :
        2707         2969784 :             prevOp = op;
        2708         2969784 :             prevLine = lineno;
        2709                 :         }
        2710                 :
        2711             981 :         return true;
        2712                 :     }
        2713                 : };
        2714                 :
        2715                 : static JSBool
        2716              54 : DebuggerScript_getAllOffsets(JSContext *cx, unsigned argc, Value *vp)
        2717                 : {
        2718              54 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script);
        2719                 :
        2720                 :     /*
        2721                 :      * First pass: determine which offsets in this script are jump targets and
        2722                 :      * which line numbers jump to them.
        2723                 :      */
        2724             108 :     FlowGraphSummary flowData(cx);
        2725              54 :     if (!flowData.populate(cx, script))
        2726               0 :         return false;
        2727                 :
        2728                 :     /* Second pass: build the result array. */
        2729              54 :     JSObject *result = NewDenseEmptyArray(cx);
        2730              54 :     if (!result)
        2731               0 :         return false;
        2732            1458 :     for (BytecodeRangeWithLineNumbers r(script); !r.empty(); r.popFront()) {
        2733            1404 :         size_t offset = r.frontOffset();
        2734            1404 :         size_t lineno = r.frontLineNumber();
        2735                 :
        2736                 :         /* Make a note, if the current instruction is an entry point for the current line. */
        2737            1404 :         if (flowData[offset] != NoEdges && flowData[offset] != lineno) {
        2738                 :             /* Get the offsets array for this line. */
        2739                 :             JSObject *offsets;
        2740                 :             Value offsetsv;
        2741             360 :             if (!result->arrayGetOwnDataElement(cx, lineno, &offsetsv))
        2742               0 :                 return false;
        2743                 :
        2744                 :             jsid id;
        2745             360 :             if (offsetsv.isObject()) {
        2746              54 :                 offsets = &offsetsv.toObject();
        2747                 :             } else {
        2748             306 :                 JS_ASSERT(offsetsv.isMagic(JS_ARRAY_HOLE));
        2749                 :
        2750                 :                 /*
        2751                 :                  * Create an empty offsets array for this line.
        2752                 :                  * Store it in the result array.
        2753                 :                  */
        2754             306 :                 offsets = NewDenseEmptyArray(cx);
        2755             918 :                 if (!offsets ||
        2756             306 :                     !ValueToId(cx, NumberValue(lineno), &id) ||
        2757             306 :                     !result->defineGeneric(cx, id, ObjectValue(*offsets)))
        2758                 :                 {
        2759               0 :                     return false;
        2760                 :                 }
        2761                 :             }
        2762                 :
        2763                 :             /* Append the current offset to the offsets array. */
        2764             360 :             if (!js_NewbornArrayPush(cx, offsets, NumberValue(offset)))
        2765               0 :                 return false;
        2766                 :         }
        2767                 :     }
        2768                 :
        2769              54 :     args.rval().setObject(*result);
        2770              54 :     return true;
        2771                 : }
        2772                 :
        2773                 : static JSBool
        2774             927 : DebuggerScript_getLineOffsets(JSContext *cx, unsigned argc, Value *vp)
        2775                 : {
        2776             927 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineOffsets", args, obj, script);
        2777             927 :     REQUIRE_ARGC("Debugger.Script.getLineOffsets", 1);
        2778                 :
        2779                 :     /* Parse lineno argument. */
        2780                 :     size_t lineno;
        2781             927 :     bool ok = false;
        2782             927 :     if (args[0].isNumber()) {
        2783             927 :         double d = args[0].toNumber();
        2784             927 :         lineno = size_t(d);
        2785             927 :         ok = (lineno == d);
        2786                 :     }
        2787             927 :     if (!ok) {
        2788               0 :         JS_ReportErrorNumber(cx,  js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_LINE);
        2789               0 :         return false;
        2790                 :     }
        2791                 :
        2792                 :     /*
        2793                 :      * First pass: determine which offsets in this script are jump targets and
        2794                 :      * which line numbers jump to them.
        2795                 :      */
        2796            1854 :     FlowGraphSummary flowData(cx);
        2797             927 :     if (!flowData.populate(cx, script))
        2798               0 :         return false;
        2799                 :
        2800                 :     /* Second pass: build the result array. */
        2801             927 :     JSObject *result = NewDenseEmptyArray(cx);
        2802             927 :     if (!result)
        2803               0 :         return false;
        2804         2969307 :     for (BytecodeRangeWithLineNumbers r(script); !r.empty(); r.popFront()) {
        2805         2968380 :         size_t offset = r.frontOffset();
        2806                 :
        2807                 :         /* If the op at offset is an entry point, append offset to result. */
        2808         2983428 :         if (r.frontLineNumber() == lineno &&
        2809            7641 :             flowData[offset] != NoEdges &&
        2810            7407 :             flowData[offset] != lineno)
        2811                 :         {
        2812             999 :             if (!js_NewbornArrayPush(cx, result, NumberValue(offset)))
        2813               0 :                 return false;
        2814                 :         }
        2815                 :     }
        2816                 :
        2817             927 :     args.rval().setObject(*result);
        2818             927 :     return true;
        2819                 : }
        2820                 :
        2821                 : static JSBool
        2822            1296 : DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp)
        2823                 : {
        2824            1296 :     REQUIRE_ARGC("Debugger.Script.setBreakpoint", 2);
        2825            1296 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script);
        2826            1296 :     Debugger *dbg = Debugger::fromChildJSObject(obj);
        2827                 :
        2828            1296 :     GlobalObject *scriptGlobal = script->getGlobalObjectOrNull();
        2829            1296 :     if (!dbg->observesGlobal(ScriptGlobal(cx, script, scriptGlobal))) {
        2830              18 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_DEBUGGING);
        2831              18 :         return false;
        2832                 :     }
        2833                 :
        2834                 :     size_t offset;
        2835            1278 :     if (!ScriptOffset(cx, script, args[0], &offset))
        2836               9 :         return false;
        2837                 :
        2838            1269 :     JSObject *handler = NonNullObject(cx, args[1]);
        2839            1269 :     if (!handler)
        2840               0 :         return false;
        2841                 :
        2842            1269 :     jsbytecode *pc = script->code + offset;
        2843            1269 :     BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, scriptGlobal);
        2844            1269 :     if (!site)
        2845               0 :         return false;
        2846            1269 :     site->inc(cx->runtime->defaultFreeOp());
        2847            1269 :     if (cx->runtime->new_<Breakpoint>(dbg, site, handler)) {
        2848            1269 :         args.rval().setUndefined();
        2849            1269 :         return true;
        2850                 :     }
        2851               0 :     site->dec(cx->runtime->defaultFreeOp());
        2852               0 :     site->destroyIfEmpty(cx->runtime->defaultFreeOp());
        2853               0 :     return false;
        2854                 : }
        2855                 :
        2856                 : static JSBool
        2857             180 : DebuggerScript_getBreakpoints(JSContext *cx, unsigned argc, Value *vp)
        2858                 : {
        2859             180 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script);
        2860             180 :     Debugger *dbg = Debugger::fromChildJSObject(obj);
        2861                 :
        2862                 :     jsbytecode *pc;
        2863             180 :     if (argc > 0) {
        2864                 :         size_t offset;
        2865              27 :         if (!ScriptOffset(cx, script, args[0], &offset))
        2866               0 :             return false;
        2867              27 :         pc = script->code + offset;
        2868                 :     } else {
        2869             153 :         pc = NULL;
        2870                 :     }
        2871                 :
        2872             180 :     JSObject *arr = NewDenseEmptyArray(cx);
        2873             180 :     if (!arr)
        2874               0 :         return false;
        2875                 :
        2876            3600 :     for (unsigned i = 0; i < script->length; i++) {
        2877            3420 :         BreakpointSite *site = script->getBreakpointSite(script->code + i);
        2878            3420 :         if (site && (!pc || site->pc == pc)) {
        2879             540 :             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
        2880             540 :                 if (bp->debugger == dbg &&
        2881             162 :                     !js_NewbornArrayPush(cx, arr, ObjectValue(*bp->getHandler())))
        2882                 :                 {
        2883               0 :                     return false;
        2884                 :                 }
        2885                 :             }
        2886                 :         }
        2887                 :     }
        2888             180 :     args.rval().setObject(*arr);
        2889             180 :     return true;
        2890                 : }
        2891                 :
        2892                 : static JSBool
        2893              81 : DebuggerScript_clearBreakpoint(JSContext *cx, unsigned argc, Value *vp)
        2894                 : {
        2895              81 :     REQUIRE_ARGC("Debugger.Script.clearBreakpoint", 1);
        2896              81 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script);
        2897              81 :     Debugger *dbg = Debugger::fromChildJSObject(obj);
        2898                 :
        2899              81 :     JSObject *handler = NonNullObject(cx, args[0]);
        2900              81 :     if (!handler)
        2901               0 :         return false;
        2902                 :
        2903              81 :     script->clearBreakpointsIn(cx, dbg, handler);
        2904              81 :     args.rval().setUndefined();
        2905              81 :     return true;
        2906                 : }
        2907                 :
        2908                 : static JSBool
        2909              27 : DebuggerScript_clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp)
        2910                 : {
        2911              27 :     THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearAllBreakpoints", args, obj, script);
        2912              27 :     Debugger *dbg = Debugger::fromChildJSObject(obj);
        2913              27 :     script->clearBreakpointsIn(cx, dbg, NULL);
        2914              27 :     args.rval().setUndefined();
        2915              27 :     return true;
        2916                 : }
        2917                 :
        2918                 : static JSBool
        2919              18 : DebuggerScript_construct(JSContext *cx, unsigned argc, Value *vp)
        2920                 : {
        2921              18 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Script");
        2922              18 :     return false;
        2923                 : }
        2924                 :
        2925                 : static JSPropertySpec DebuggerScript_properties[] = {
        2926                 :     JS_PSG("url", DebuggerScript_getUrl, 0),
        2927                 :     JS_PSG("startLine", DebuggerScript_getStartLine, 0),
        2928                 :     JS_PSG("lineCount", DebuggerScript_getLineCount, 0),
        2929                 :     JS_PS_END
        2930                 : };
        2931                 :
        2932                 : static JSFunctionSpec DebuggerScript_methods[] = {
        2933                 :     JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0),
        2934                 :     JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0),
        2935                 :     JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0),
        2936                 :     JS_FN("getOffsetLine", DebuggerScript_getOffsetLine, 0, 0),
        2937                 :     JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0),
        2938                 :     JS_FN("getBreakpoints", DebuggerScript_getBreakpoints, 1, 0),
        2939                 :     JS_FN("clearBreakpoint", DebuggerScript_clearBreakpoint, 1, 0),
        2940                 :     JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0),
        2941                 :     JS_FS_END
        2942                 : };
        2943                 :
        2944                 :
        2945                 : /*** Debugger.Frame ******************************************************************************/
        2946                 :
        2947                 : Class DebuggerFrame_class = {
        2948                 :     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
        2949                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        2950                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
        2951                 : };
        2952                 :
        2953                 : static JSObject *
        2954           28259 : CheckThisFrame(JSContext *cx, const CallArgs &args, const char *fnname, bool checkLive)
        2955                 : {
        2956           28259 :     if (!args.thisv().isObject()) {
        2957               0 :         ReportObjectRequired(cx);
        2958               0 :         return NULL;
        2959                 :     }
        2960           28259 :     JSObject *thisobj = &args.thisv().toObject();
        2961           28259 :     if (thisobj->getClass() != &DebuggerFrame_class) {
        2962                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        2963              18 :                              "Debugger.Frame", fnname, thisobj->getClass()->name);
        2964              18 :         return NULL;
        2965                 :     }
        2966                 :
        2967                 :     /*
        2968                 :      * Forbid Debugger.Frame.prototype, which is of class DebuggerFrame_class
        2969                 :      * but isn't really a working Debugger.Frame object. The prototype object
        2970                 :      * is distinguished by having a NULL private value. Also, forbid popped
        2971                 :      * frames.
        2972                 :      */
        2973           28241 :     if (!thisobj->getPrivate()) {
        2974            1647 :         if (thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_OWNER).isUndefined()) {
        2975                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        2976              18 :                                  "Debugger.Frame", fnname, "prototype object");
        2977              18 :             return NULL;
        2978                 :         }
        2979            1629 :         if (checkLive) {
        2980                 :             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_LIVE,
        2981             153 :                                  "Debugger.Frame", fnname, "stack frame");
        2982             153 :             return NULL;
        2983                 :         }
        2984                 :     }
        2985           28070 :     return thisobj;
        2986                 : }
        2987                 :
        2988                 : #if DEBUG
        2989                 : static bool
        2990           23561 : StackContains(JSContext *cx, StackFrame *fp)
        2991                 : {
        2992           95955 :     for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) {
        2993           95955 :         if (fp == i.fp())
        2994           23561 :             return true;
        2995                 :     }
        2996               0 :     return false;
        2997                 : }
        2998                 : #endif
        2999                 :
        3000                 : #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, fp)                  \
        3001                 :     CallArgs args = CallArgsFromVp(argc, vp);                                \
        3002                 :     JSObject *thisobj = CheckThisFrame(cx, args, fnname, true);              \
        3003                 :     if (!thisobj)                                                            \
        3004                 :         return false;                                                        \
        3005                 :     StackFrame *fp = (StackFrame *) thisobj->getPrivate();                   \
        3006                 :     JS_ASSERT(StackContains(cx, fp))
        3007                 :
        3008                 : #define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, fp, dbg)       \
        3009                 :     THIS_FRAME(cx, argc, vp, fnname, args, thisobj, fp);                     \
        3010                 :     Debugger *dbg = Debugger::fromChildJSObject(thisobj)
        3011                 :
        3012                 : static JSBool
        3013            2151 : DebuggerFrame_getType(JSContext *cx, unsigned argc, Value *vp)
        3014                 : {
        3015            2151 :     THIS_FRAME(cx, argc, vp, "get type", args, thisobj, fp);
        3016                 :
        3017                 :     /*
        3018                 :      * Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the
        3019                 :      * order of checks here is significant.
        3020                 :      */
        3021            4284 :     args.rval().setString(fp->isEvalFrame()
        3022                 :                           ? cx->runtime->atomState.evalAtom
        3023            1458 :                           : fp->isGlobalFrame()
        3024                 :                           ? cx->runtime->atomState.globalAtom
        3025            5742 :                           : cx->runtime->atomState.callAtom);
        3026            2142 :     return true;
        3027                 : }
        3028                 :
        3029                 : static Env *
        3030            1287 : Frame_GetEnv(JSContext *cx, StackFrame *fp)
        3031                 : {
        3032            1287 :     assertSameCompartment(cx, fp);
        3033            1287 :     if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj() && !CallObject::createForFunction(cx, fp))
        3034               0 :         return NULL;
        3035            1287 :     return GetScopeChain(cx, fp);
        3036                 : }
        3037                 :
        3038                 : static JSBool
        3039            1296 : DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
        3040                 : {
        3041            1296 :     THIS_FRAME_OWNER(cx, argc, vp, "get environment", args, thisobj, fp, dbg);
        3042                 :
        3043                 :     Env *env;
        3044                 :     {
        3045            2574 :         AutoCompartment ac(cx, &fp->scopeChain());
        3046            1287 :         if (!ac.enter())
        3047               0 :             return false;
        3048            1287 :         env = Frame_GetEnv(cx, fp);
        3049            1287 :         if (!env)
        3050               0 :             return false;
        3051                 :     }
        3052                 :
        3053            1287 :     return dbg->wrapEnvironment(cx, env, &args.rval());
        3054                 : }
        3055                 :
        3056                 : static JSBool
        3057            1404 : DebuggerFrame_getCallee(JSContext *cx, unsigned argc, Value *vp)
        3058                 : {
        3059            1404 :     THIS_FRAME(cx, argc, vp, "get callee", args, thisobj, fp);
        3060            1395 :     Value calleev = (fp->isFunctionFrame() && !fp->isEvalFrame()) ? fp->calleev() : NullValue();
        3061            1395 :     if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &calleev))
        3062               0 :         return false;
        3063            1395 :     args.rval() = calleev;
        3064            1395 :     return true;
        3065                 : }
        3066                 :
        3067                 : static JSBool
        3068             108 : DebuggerFrame_getGenerator(JSContext *cx, unsigned argc, Value *vp)
        3069                 : {
        3070             108 :     THIS_FRAME(cx, argc, vp, "get generator", args, thisobj, fp);
        3071              99 :     args.rval().setBoolean(fp->isGeneratorFrame());
        3072              99 :     return true;
        3073                 : }
        3074                 :
        3075                 : static JSBool
        3076             342 : DebuggerFrame_getConstructing(JSContext *cx, unsigned argc, Value *vp)
        3077                 : {
        3078             342 :     THIS_FRAME(cx, argc, vp, "get constructing", args, thisobj, fp);
        3079             333 :     args.rval().setBoolean(fp->isFunctionFrame() && fp->isConstructing());
        3080             333 :     return true;
        3081                 : }
        3082                 :
        3083                 : static JSBool
        3084             387 : DebuggerFrame_getThis(JSContext *cx, unsigned argc, Value *vp)
        3085                 : {
        3086             387 :     THIS_FRAME(cx, argc, vp, "get this", args, thisobj, fp);
        3087                 :     Value thisv;
        3088                 :     {
        3089             756 :         AutoCompartment ac(cx, &fp->scopeChain());
        3090             378 :         if (!ac.enter())
        3091               0 :             return false;
        3092             378 :         if (!ComputeThis(cx, fp))
        3093               0 :             return false;
        3094             756 :         thisv = fp->thisValue();
        3095                 :     }
        3096             378 :     if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &thisv))
        3097               0 :         return false;
        3098             378 :     args.rval() = thisv;
        3099             378 :     return true;
        3100                 : }
        3101                 :
        3102                 : static JSBool
        3103            4860 : DebuggerFrame_getOlder(JSContext *cx, unsigned argc, Value *vp)
        3104                 : {
        3105            4860 :     THIS_FRAME(cx, argc, vp, "get this", args, thisobj, thisfp);
        3106            4842 :     Debugger *dbg = Debugger::fromChildJSObject(thisobj);
        3107            5517 :     for (StackFrame *fp = thisfp->prev(); fp; fp = fp->prev()) {
        3108            5022 :         if (dbg->observesFrame(fp))
        3109            4347 :             return dbg->getScriptFrame(cx, fp, vp);
        3110                 :     }
        3111             495 :     args.rval().setNull();
        3112             495 :     return true;
        3113                 : }
        3114                 :
        3115                 : Class DebuggerArguments_class = {
        3116                 :     "Arguments", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT),
        3117                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        3118                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
        3119                 : };
        3120                 :
        3121                 : /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
        3122                 : static JSBool
        3123            3127 : DebuggerArguments_getArg(JSContext *cx, unsigned argc, Value *vp)
        3124                 : {
        3125            3127 :     CallArgs args = CallArgsFromVp(argc, vp);
        3126            3127 :     int32_t i = args.callee().toFunction()->getExtendedSlot(0).toInt32();
        3127                 :
        3128                 :     /* Check that the this value is an Arguments object. */
        3129            3127 :     if (!args.thisv().isObject()) {
        3130               0 :         ReportObjectRequired(cx);
        3131               0 :         return false;
        3132                 :     }
        3133            3127 :     JSObject *argsobj = &args.thisv().toObject();
        3134            3127 :     if (argsobj->getClass() != &DebuggerArguments_class) {
        3135                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        3136               9 :                              "Arguments", "getArgument", argsobj->getClass()->name);
        3137               9 :         return false;
        3138                 :     }
        3139                 :
        3140                 :     /*
        3141                 :      * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
        3142                 :      * to check that it is still live and get the fp.
        3143                 :      */
        3144            3118 :     args.thisv() = argsobj->getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME);
        3145            3118 :     THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, fp);
        3146                 :
        3147                 :     /*
        3148                 :      * Since getters can be extracted and applied to other objects,
        3149                 :      * there is no guarantee this object has an ith argument.
        3150                 :      */
        3151            3109 :     JS_ASSERT(i >= 0);
        3152                 :     Value arg;
        3153            3109 :     if (unsigned(i) < fp->numActualArgs())
        3154            3100 :         arg = fp->canonicalActualArg(i);
        3155                 :     else
        3156               9 :         arg.setUndefined();
        3157                 :
        3158            3109 :     if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg))
        3159               0 :         return false;
        3160            3109 :     args.rval() = arg;
        3161            3109 :     return true;
        3162                 : }
        3163                 :
        3164                 : static JSBool
        3165            2677 : DebuggerFrame_getArguments(JSContext *cx, unsigned argc, Value *vp)
        3166                 : {
        3167            2677 :     THIS_FRAME(cx, argc, vp, "get arguments", args, thisobj, fp);
        3168            2668 :     Value argumentsv = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);
        3169            2668 :     if (!argumentsv.isUndefined()) {
        3170             940 :         JS_ASSERT(argumentsv.isObjectOrNull());
        3171             940 :         args.rval() = argumentsv;
        3172             940 :         return true;
        3173                 :     }
        3174                 :
        3175                 :     JSObject *argsobj;
        3176            1728 :     if (fp->hasArgs()) {
        3177                 :         /* Create an arguments object. */
        3178            3456 :         RootedVar<GlobalObject*> global(cx);
        3179            1728 :         global = &args.callee().global();
        3180            1728 :         JSObject *proto = global->getOrCreateArrayPrototype(cx);
        3181            1728 :         if (!proto)
        3182               0 :             return false;
        3183            1728 :         argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global);
        3184            1728 :         if (!argsobj)
        3185               0 :             return false;
        3186            1728 :         SetReservedSlot(argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj));
        3187                 :
        3188            1728 :         JS_ASSERT(fp->numActualArgs() <= 0x7fffffff);
        3189            1728 :         int32_t fargc = int32_t(fp->numActualArgs());
        3190            3456 :         if (!DefineNativeProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
        3191                 :                                   Int32Value(fargc), NULL, NULL,
        3192            3456 :                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
        3193                 :         {
        3194               0 :             return false;
        3195                 :         }
        3196                 :
        3197            4140 :         for (int32_t i = 0; i < fargc; i++) {
        3198                 :             JSFunction *getobj =
        3199                 :                 js_NewFunction(cx, NULL, DebuggerArguments_getArg, 0, 0, global, NULL,
        3200            2412 :                                JSFunction::ExtendedFinalizeKind);
        3201            4824 :             if (!getobj ||
        3202                 :                 !DefineNativeProperty(cx, argsobj, INT_TO_JSID(i), UndefinedValue(),
        3203                 :                                       JS_DATA_TO_FUNC_PTR(PropertyOp, getobj), NULL,
        3204            2412 :                                       JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER, 0, 0))
        3205                 :             {
        3206               0 :                 return false;
        3207                 :             }
        3208            2412 :             getobj->setExtendedSlot(0, Int32Value(i));
        3209                 :         }
        3210                 :     } else {
        3211               0 :         argsobj = NULL;
        3212                 :     }
        3213            1728 :     args.rval() = ObjectOrNullValue(argsobj);
        3214            1728 :     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, args.rval());
        3215            1728 :     return true;
        3216                 : }
        3217                 :
        3218                 : static JSBool
        3219            2592 : DebuggerFrame_getScript(JSContext *cx, unsigned argc, Value *vp)
        3220                 : {
        3221            2592 :     THIS_FRAME(cx, argc, vp, "get script", args, thisobj, fp);
        3222            2592 :     Debugger *debug = Debugger::fromChildJSObject(thisobj);
        3223                 :
        3224            2592 :     JSObject *scriptObject = NULL;
        3225            2592 :     if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
        3226            1476 :         JSFunction *callee = fp->callee().toFunction();
        3227            1476 :         if (callee->isInterpreted()) {
        3228            1476 :             scriptObject = debug->wrapScript(cx, callee->script());
        3229            1476 :             if (!scriptObject)
        3230               0 :                 return false;
        3231                 :         }
        3232            1116 :     } else if (fp->isScriptFrame()) {
        3233                 :         /*
        3234                 :          * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script
        3235                 :          * frames.
        3236                 :          */
        3237            1116 :         JSScript *script = fp->script();
        3238            1116 :         scriptObject = debug->wrapScript(cx, script);
        3239            1116 :         if (!scriptObject)
        3240               0 :             return false;
        3241                 :     }
        3242            2592 :     args.rval().setObjectOrNull(scriptObject);
        3243            2592 :     return true;
        3244                 : }
        3245                 :
        3246                 : static JSBool
        3247            1125 : DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp)
        3248                 : {
        3249            1125 :     THIS_FRAME(cx, argc, vp, "get offset", args, thisobj, fp);
        3250            1116 :     if (fp->isScriptFrame()) {
        3251            1116 :         JSScript *script = fp->script();
        3252            1116 :         jsbytecode *pc = fp->pcQuadratic(cx);
        3253            1116 :         JS_ASSERT(script->code <= pc);
        3254            1116 :         JS_ASSERT(pc < script->code + script->length);
        3255            1116 :         size_t offset = pc - script->code;
        3256            1116 :         args.rval().setNumber(double(offset));
        3257                 :     } else {
        3258               0 :         args.rval().setUndefined();
        3259                 :     }
        3260            1116 :     return true;
        3261                 : }
        3262                 :
        3263                 : static JSBool
        3264            4509 : DebuggerFrame_getLive(JSContext *cx, unsigned argc, Value *vp)
        3265                 : {
        3266            4509 :     CallArgs args = CallArgsFromVp(argc, vp);
        3267            4509 :     JSObject *thisobj = CheckThisFrame(cx, args, "get live", false);
        3268            4509 :     if (!thisobj)
        3269               0 :         return false;
        3270            4509 :     StackFrame *fp = (StackFrame *) thisobj->getPrivate();
        3271            4509 :     args.rval().setBoolean(!!fp);
        3272            4509 :     return true;
        3273                 : }
        3274                 :
        3275                 : static bool
        3276            1620 : IsValidHook(const Value &v)
        3277                 : {
        3278            1620 :     return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
        3279                 : }
        3280                 :
        3281                 : static JSBool
        3282               9 : DebuggerFrame_getOnStep(JSContext *cx, unsigned argc, Value *vp)
        3283                 : {
        3284               9 :     THIS_FRAME(cx, argc, vp, "get onStep", args, thisobj, fp);
        3285                 :     (void) fp;  // Silence GCC warning
        3286               9 :     Value handler = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
        3287               9 :     JS_ASSERT(IsValidHook(handler));
        3288               9 :     args.rval() = handler;
        3289               9 :     return true;
        3290                 : }
        3291                 :
        3292                 : static JSBool
        3293             495 : DebuggerFrame_setOnStep(JSContext *cx, unsigned argc, Value *vp)
        3294                 : {
        3295             495 :     REQUIRE_ARGC("Debugger.Frame.set onStep", 1);
        3296             495 :     THIS_FRAME(cx, argc, vp, "set onStep", args, thisobj, fp);
        3297             495 :     if (!fp->isScriptFrame()) {
        3298               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_SCRIPT_FRAME);
        3299               0 :         return false;
        3300                 :     }
        3301             495 :     if (!IsValidHook(args[0])) {
        3302               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
        3303               0 :         return false;
        3304                 :     }
        3305                 :
        3306             495 :     Value prior = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
        3307             495 :     int delta = !args[0].isUndefined() - !prior.isUndefined();
        3308             495 :     if (delta != 0) {
        3309                 :         /* Try to adjust this frame's script single-step mode count. */
        3310             990 :         AutoCompartment ac(cx, &fp->scopeChain());
        3311             495 :         if (!ac.enter())
        3312               0 :             return false;
        3313             495 :         if (!fp->script()->changeStepModeCount(cx, delta))
        3314               0 :             return false;
        3315                 :     }
        3316                 :
        3317                 :     /* Now that the step mode switch has succeeded, we can install the handler. */
        3318             495 :     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER, args[0]);
        3319             495 :     args.rval().setUndefined();
        3320             495 :     return true;
        3321                 : }
        3322                 :
        3323                 : static JSBool
        3324              27 : DebuggerFrame_getOnPop(JSContext *cx, unsigned argc, Value *vp)
        3325                 : {
        3326              27 :     THIS_FRAME(cx, argc, vp, "get onPop", args, thisobj, fp);
        3327                 :     (void) fp;  // Silence GCC warning
        3328               9 :     Value handler = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER);
        3329               9 :     JS_ASSERT(IsValidHook(handler));
        3330               9 :     args.rval() = handler;
        3331               9 :     return true;
        3332                 : }
        3333                 :
        3334                 : static JSBool
        3335            1161 : DebuggerFrame_setOnPop(JSContext *cx, unsigned argc, Value *vp)
        3336                 : {
        3337            1161 :     REQUIRE_ARGC("Debugger.Frame.set onPop", 1);
        3338            1161 :     THIS_FRAME(cx, argc, vp, "set onPop", args, thisobj, fp);
        3339            1107 :     if (!fp->isScriptFrame()) {
        3340               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_SCRIPT_FRAME);
        3341               0 :         return false;
        3342                 :     }
        3343            1107 :     if (!IsValidHook(args[0])) {
        3344              54 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
        3345              54 :         return false;
        3346                 :     }
        3347                 :
        3348            1053 :     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER, args[0]);
        3349            1053 :     args.rval().setUndefined();
        3350            1053 :     return true;
        3351                 : }
        3352                 :
        3353                 : namespace js {
        3354                 :
        3355                 : JSBool
        3356            2952 : EvaluateInEnv(JSContext *cx, Env *env, StackFrame *fp, const jschar *chars,
        3357                 :               unsigned length, const char *filename, unsigned lineno, Value *rval)
        3358                 : {
        3359            2952 :     assertSameCompartment(cx, env, fp);
        3360                 :
        3361            2952 :     if (fp) {
        3362                 :         /* Execute assumes an already-computed 'this" value. */
        3363            2952 :         if (!ComputeThis(cx, fp))
        3364               0 :             return false;
        3365                 :     }
        3366                 :
        3367                 :     /*
        3368                 :      * NB: This function breaks the assumption that the compiler can see all
        3369                 :      * calls and properly compute a static level. In order to get around this,
        3370                 :      * we use a static level that will cause us not to attempt to optimize
        3371                 :      * variable references made by this frame.
        3372                 :      */
        3373            2952 :     JSPrincipals *prin = fp->scopeChain().principals(cx);
        3374                 :     JSScript *script = frontend::CompileScript(cx, env, fp, prin, prin,
        3375                 :                                                TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_GLOBAL,
        3376                 :                                                chars, length, filename, lineno,
        3377                 :                                                cx->findVersion(), NULL,
        3378            2952 :                                                UpvarCookie::UPVAR_LEVEL_LIMIT);
        3379                 :
        3380            2952 :     if (!script)
        3381               9 :         return false;
        3382                 :
        3383            2943 :     return ExecuteKernel(cx, script, *env, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
        3384                 : }
        3385                 :
        3386                 : }
        3387                 :
        3388                 : enum EvalBindingsMode { WithoutBindings, WithBindings };
        3389                 :
        3390                 : static JSBool
        3391            1998 : DebuggerFrameEval(JSContext *cx, unsigned argc, Value *vp, EvalBindingsMode mode)
        3392                 : {
        3393            1998 :     if (mode == WithBindings)
        3394             333 :         REQUIRE_ARGC("Debugger.Frame.evalWithBindings", 2);
        3395                 :     else
        3396            1665 :         REQUIRE_ARGC("Debugger.Frame.eval", 1);
        3397            1998 :     THIS_FRAME(cx, argc, vp, mode == WithBindings ? "evalWithBindings" : "eval",
        3398            1980 :                args, thisobj, fp);
        3399            1980 :     Debugger *dbg = Debugger::fromChildJSObject(thisobj);
        3400                 :
        3401                 :     /* Check the first argument, the eval code string. */
        3402            1980 :     if (!args[0].isString()) {
        3403                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_EXPECTED_TYPE,
        3404               0 :                              "Debugger.Frame.eval", "string", InformalValueTypeName(args[0]));
        3405               0 :         return false;
        3406                 :     }
        3407            1980 :     JSLinearString *linearStr = args[0].toString()->ensureLinear(cx);
        3408            1980 :     if (!linearStr)
        3409               0 :         return false;
        3410                 :
        3411                 :     /*
        3412                 :      * Gather keys and values of bindings, if any. This must be done in the
        3413                 :      * debugger compartment, since that is where any exceptions must be
        3414                 :      * thrown.
        3415                 :      */
        3416            3960 :     AutoIdVector keys(cx);
        3417            3960 :     AutoValueVector values(cx);
        3418            1980 :     if (mode == WithBindings) {
        3419             333 :         JSObject *bindingsobj = NonNullObject(cx, args[1]);
        3420             999 :         if (!bindingsobj ||
        3421             333 :             !GetPropertyNames(cx, bindingsobj, JSITER_OWNONLY, &keys) ||
        3422             333 :             !values.growBy(keys.length()))
        3423                 :         {
        3424               0 :             return false;
        3425                 :         }
        3426             693 :         for (size_t i = 0; i < keys.length(); i++) {
        3427             360 :             Value *valp = &values[i];
        3428             720 :             if (!bindingsobj->getGeneric(cx, bindingsobj, keys[i], valp) ||
        3429             360 :                 !dbg->unwrapDebuggeeValue(cx, valp))
        3430                 :             {
        3431               0 :                 return false;
        3432                 :             }
        3433                 :         }
        3434                 :     }
        3435                 :
        3436            3960 :     AutoCompartment ac(cx, &fp->scopeChain());
        3437            1980 :     if (!ac.enter())
        3438               0 :         return false;
        3439                 :
        3440            1980 :     Env *env = JS_GetFrameScopeChain(cx, Jsvalify(fp));
        3441            1980 :     if (!env)
        3442               0 :         return false;
        3443                 :
        3444                 :     /* If evalWithBindings, create the inner environment. */
        3445            1980 :     if (mode == WithBindings) {
        3446                 :         /* TODO - This should probably be a Call object, like ES5 strict eval. */
        3447             333 :         env = NewObjectWithGivenProto(cx, &ObjectClass, NULL, env);
        3448             333 :         if (!env)
        3449               0 :             return false;
        3450             693 :         for (size_t i = 0; i < keys.length(); i++) {
        3451             720 :             if (!cx->compartment->wrap(cx, &values[i]) ||
        3452             360 :                 !DefineNativeProperty(cx, env, keys[i], values[i], NULL, NULL, 0, 0, 0))
        3453                 :             {
        3454               0 :                 return false;
        3455                 :             }
        3456                 :         }
        3457                 :     }
        3458                 :
        3459                 :     /* Run the code and produce the completion value. */
        3460                 :     Value rval;
        3461            3960 :     JS::Anchor<JSString *> anchor(linearStr);
        3462                 :     bool ok = EvaluateInEnv(cx, env, fp, linearStr->chars(), linearStr->length(),
        3463            1980 :                             "debugger eval code", 1, &rval);
        3464            1980 :     return dbg->receiveCompletionValue(ac, ok, rval, vp);
        3465                 : }
        3466                 :
        3467                 : static JSBool
        3468            1665 : DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp)
        3469                 : {
        3470            1665 :     return DebuggerFrameEval(cx, argc, vp, WithoutBindings);
        3471                 : }
        3472                 :
        3473                 : static JSBool
        3474             333 : DebuggerFrame_evalWithBindings(JSContext *cx, unsigned argc, Value *vp)
        3475                 : {
        3476             333 :     return DebuggerFrameEval(cx, argc, vp, WithBindings);
        3477                 : }
        3478                 :
        3479                 : static JSBool
        3480               0 : DebuggerFrame_construct(JSContext *cx, unsigned argc, Value *vp)
        3481                 : {
        3482               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Frame");
        3483               0 :     return false;
        3484                 : }
        3485                 :
        3486                 : static JSPropertySpec DebuggerFrame_properties[] = {
        3487                 :     JS_PSG("arguments", DebuggerFrame_getArguments, 0),
        3488                 :     JS_PSG("callee", DebuggerFrame_getCallee, 0),
        3489                 :     JS_PSG("constructing", DebuggerFrame_getConstructing, 0),
        3490                 :     JS_PSG("environment", DebuggerFrame_getEnvironment, 0),
        3491                 :     JS_PSG("generator", DebuggerFrame_getGenerator, 0),
        3492                 :     JS_PSG("live", DebuggerFrame_getLive, 0),
        3493                 :     JS_PSG("offset", DebuggerFrame_getOffset, 0),
        3494                 :     JS_PSG("older", DebuggerFrame_getOlder, 0),
        3495                 :     JS_PSG("script", DebuggerFrame_getScript, 0),
        3496                 :     JS_PSG("this", DebuggerFrame_getThis, 0),
        3497                 :     JS_PSG("type", DebuggerFrame_getType, 0),
        3498                 :     JS_PSGS("onStep", DebuggerFrame_getOnStep, DebuggerFrame_setOnStep, 0),
        3499                 :     JS_PSGS("onPop", DebuggerFrame_getOnPop, DebuggerFrame_setOnPop, 0),
        3500                 :     JS_PS_END
        3501                 : };
        3502                 :
        3503                 : static JSFunctionSpec DebuggerFrame_methods[] = {
        3504                 :     JS_FN("eval", DebuggerFrame_eval, 1, 0),
        3505                 :     JS_FN("evalWithBindings", DebuggerFrame_evalWithBindings, 1, 0),
        3506                 :     JS_FS_END
        3507                 : };
        3508                 :
        3509                 :
        3510                 : /*** Debugger.Object *****************************************************************************/
        3511                 :
        3512                 : static void
        3513           47340 : DebuggerObject_trace(JSTracer *trc, JSObject *obj)
        3514                 : {
        3515                 :     /*
        3516                 :      * There is a barrier on private pointers, so the Unbarriered marking
        3517                 :      * is okay.
        3518                 :      */
        3519           47340 :     if (JSObject *referent = (JSObject *) obj->getPrivate()) {
        3520            2507 :         MarkCrossCompartmentObjectUnbarriered(trc, &referent, "Debugger.Object referent");
        3521            2507 :         obj->setPrivateUnbarriered(referent);
        3522                 :     }
        3523           47340 : }
        3524                 :
        3525                 : Class DebuggerObject_class = {
        3526                 :     "Object",
        3527                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
        3528                 :     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
        3529                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        3530                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
        3531                 :     NULL,                 /* checkAccess */
        3532                 :     NULL,                 /* call        */
        3533                 :     NULL,                 /* construct   */
        3534                 :     NULL,                 /* hasInstance */
        3535                 :     DebuggerObject_trace
        3536                 : };
        3537                 :
        3538                 : static JSObject *
        3539            7317 : DebuggerObject_checkThis(JSContext *cx, const CallArgs &args, const char *fnname)
        3540                 : {
        3541            7317 :     if (!args.thisv().isObject()) {
        3542               0 :         ReportObjectRequired(cx);
        3543               0 :         return NULL;
        3544                 :     }
        3545            7317 :     JSObject *thisobj = &args.thisv().toObject();
        3546            7317 :     if (thisobj->getClass() != &DebuggerObject_class) {
        3547                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        3548               0 :                              "Debugger.Object", fnname, thisobj->getClass()->name);
        3549               0 :         return NULL;
        3550                 :     }
        3551                 :
        3552                 :     /*
        3553                 :      * Forbid Debugger.Object.prototype, which is of class DebuggerObject_class
        3554                 :      * but isn't a real working Debugger.Object. The prototype object is
        3555                 :      * distinguished by having no referent.
        3556                 :      */
        3557            7317 :     if (!thisobj->getPrivate()) {
        3558                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        3559               0 :                              "Debugger.Object", fnname, "prototype object");
        3560               0 :         return NULL;
        3561                 :     }
        3562            7317 :     return thisobj;
        3563                 : }
        3564                 :
        3565                 : #define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj)            \
        3566                 :     CallArgs args = CallArgsFromVp(argc, vp);                                 \
        3567                 :     JSObject *obj = DebuggerObject_checkThis(cx, args, fnname);               \
        3568                 :     if (!obj)                                                                 \
        3569                 :         return false;                                                         \
        3570                 :     obj = (JSObject *) obj->getPrivate();                                     \
        3571                 :     JS_ASSERT(obj)
        3572                 :
        3573                 : #define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \
        3574                 :     CallArgs args = CallArgsFromVp(argc, vp);                                 \
        3575                 :     JSObject *obj = DebuggerObject_checkThis(cx, args, fnname);               \
        3576                 :     if (!obj)                                                                 \
        3577                 :         return false;                                                         \
        3578                 :     Debugger *dbg = Debugger::fromChildJSObject(obj);                         \
        3579                 :     obj = (JSObject *) obj->getPrivate();                                     \
        3580                 :     JS_ASSERT(obj)
        3581                 :
        3582                 : static JSBool
        3583               0 : DebuggerObject_construct(JSContext *cx, unsigned argc, Value *vp)
        3584                 : {
        3585               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Object");
        3586               0 :     return false;
        3587                 : }
        3588                 :
        3589                 : static JSBool
        3590             225 : DebuggerObject_getProto(JSContext *cx, unsigned argc, Value *vp)
        3591                 : {
        3592             225 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get proto", args, dbg, refobj);
        3593             225 :     Value protov = ObjectOrNullValue(refobj->getProto());
        3594             225 :     if (!dbg->wrapDebuggeeValue(cx, &protov))
        3595               0 :         return false;
        3596             225 :     args.rval() = protov;
        3597             225 :     return true;
        3598                 : }
        3599                 :
        3600                 : static JSBool
        3601             441 : DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
        3602                 : {
        3603             441 :     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
        3604             441 :     const char *s = refobj->getClass()->name;
        3605             441 :     JSAtom *str = js_Atomize(cx, s, strlen(s));
        3606             441 :     if (!str)
        3607               0 :         return false;
        3608             441 :     args.rval().setString(str);
        3609             441 :     return true;
        3610                 : }
        3611                 :
        3612                 : static JSBool
        3613              45 : DebuggerObject_getCallable(JSContext *cx, unsigned argc, Value *vp)
        3614                 : {
        3615              45 :     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get callable", args, refobj);
        3616              45 :     args.rval().setBoolean(refobj->isCallable());
        3617              45 :     return true;
        3618                 : }
        3619                 :
        3620                 : static JSBool
        3621            1188 : DebuggerObject_getName(JSContext *cx, unsigned argc, Value *vp)
        3622                 : {
        3623            1188 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get name", args, dbg, obj);
        3624            1188 :     if (!obj->isFunction()) {
        3625              45 :         args.rval().setUndefined();
        3626              45 :         return true;
        3627                 :     }
        3628                 :
        3629            1143 :     JSString *name = obj->toFunction()->atom;
        3630            1143 :     if (!name) {
        3631              18 :         args.rval().setUndefined();
        3632              18 :         return true;
        3633                 :     }
        3634                 :
        3635            1125 :     Value namev = StringValue(name);
        3636            1125 :     if (!dbg->wrapDebuggeeValue(cx, &namev))
        3637               0 :         return false;
        3638            1125 :     args.rval() = namev;
        3639            1125 :     return true;
        3640                 : }
        3641                 :
        3642                 : static JSBool
        3643              54 : DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
        3644                 : {
        3645              54 :     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get parameterNames", args, obj);
        3646              54 :     if (!obj->isFunction()) {
        3647               9 :         args.rval().setUndefined();
        3648               9 :         return true;
        3649                 :     }
        3650                 :
        3651              45 :     const JSFunction *fun = obj->toFunction();
        3652              45 :     JSObject *result = NewDenseAllocatedArray(cx, fun->nargs, NULL);
        3653              45 :     if (!result)
        3654               0 :         return false;
        3655              45 :     result->ensureDenseArrayInitializedLength(cx, 0, fun->nargs);
        3656                 :
        3657              45 :     if (fun->isInterpreted()) {
        3658              36 :         JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
        3659                 :
        3660              36 :         if (fun->nargs > 0) {
        3661              54 :             Vector<JSAtom *> names(cx);
        3662              27 :             if (!fun->script()->bindings.getLocalNameArray(cx, &names))
        3663               0 :                 return false;
        3664                 :
        3665             297 :             for (size_t i = 0; i < fun->nargs; i++) {
        3666             270 :                 JSAtom *name = names[i];
        3667             270 :                 result->setDenseArrayElement(i, name ? StringValue(name) : UndefinedValue());
        3668                 :             }
        3669                 :         }
        3670                 :     } else {
        3671              27 :         for (size_t i = 0; i < fun->nargs; i++)
        3672              18 :             result->setDenseArrayElement(i, UndefinedValue());
        3673                 :     }
        3674                 :
        3675              45 :     args.rval().setObject(*result);
        3676              45 :     return true;
        3677                 : }
        3678                 :
        3679                 : static JSBool
        3680            2304 : DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
        3681                 : {
        3682            2304 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
        3683                 :
        3684            2304 :     if (!obj->isFunction()) {
        3685               9 :         args.rval().setUndefined();
        3686               9 :         return true;
        3687                 :     }
        3688                 :
        3689            2295 :     JSFunction *fun = obj->toFunction();
        3690            2295 :     if (!fun->isInterpreted()) {
        3691               9 :         args.rval().setUndefined();
        3692               9 :         return true;
        3693                 :     }
        3694                 :
        3695            2286 :     JSObject *scriptObject = dbg->wrapScript(cx, fun->script());
        3696            2286 :     if (!scriptObject)
        3697               0 :         return false;
        3698                 :
        3699            2286 :     args.rval().setObject(*scriptObject);
        3700            2286 :     return true;
        3701                 : }
        3702                 :
        3703                 : static JSBool
        3704              99 : DebuggerObject_getEnvironment(JSContext *cx, unsigned argc, Value *vp)
        3705                 : {
        3706              99 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj);
        3707                 :
        3708                 :     /* Don't bother switching compartments just to check obj's type and get its env. */
        3709              99 :     if (!obj->isFunction() || !obj->toFunction()->isInterpreted()) {
        3710              27 :         args.rval().setUndefined();
        3711              27 :         return true;
        3712                 :     }
        3713                 :
        3714              72 :     Env *env = obj->toFunction()->environment();
        3715              72 :     return dbg->wrapEnvironment(cx, env, &args.rval());
        3716                 : }
        3717                 :
        3718                 : static JSBool
        3719             837 : DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
        3720                 : {
        3721             837 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj);
        3722                 :
        3723                 :     jsid id;
        3724             837 :     if (!ValueToId(cx, argc >= 1 ? args[0] : UndefinedValue(), &id))
        3725               0 :         return false;
        3726                 :
        3727                 :     /* Bug: This can cause the debuggee to run! */
        3728            1674 :     AutoPropertyDescriptorRooter desc(cx);
        3729                 :     {
        3730            1674 :         AutoCompartment ac(cx, obj);
        3731             837 :         if (!ac.enter() || !cx->compartment->wrapId(cx, &id))
        3732               0 :             return false;
        3733                 :
        3734            1674 :         ErrorCopier ec(ac, dbg->toJSObject());
        3735             837 :         if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
        3736               0 :             return false;
        3737                 :     }
        3738                 :
        3739             837 :     if (desc.obj) {
        3740                 :         /* Rewrap the debuggee values in desc for the debugger. */
        3741             765 :         if (!dbg->wrapDebuggeeValue(cx, &desc.value))
        3742               0 :             return false;
        3743             765 :         if (desc.attrs & JSPROP_GETTER) {
        3744              27 :             Value get = ObjectOrNullValue(CastAsObject(desc.getter));
        3745              27 :             if (!dbg->wrapDebuggeeValue(cx, &get))
        3746               0 :                 return false;
        3747              27 :             desc.getter = CastAsPropertyOp(get.toObjectOrNull());
        3748                 :         }
        3749             765 :         if (desc.attrs & JSPROP_SETTER) {
        3750               9 :             Value set = ObjectOrNullValue(CastAsObject(desc.setter));
        3751               9 :             if (!dbg->wrapDebuggeeValue(cx, &set))
        3752               0 :                 return false;
        3753               9 :             desc.setter = CastAsStrictPropertyOp(set.toObjectOrNull());
        3754                 :         }
        3755                 :     }
        3756                 :
        3757             837 :     return NewPropertyDescriptorObject(cx, &desc, &args.rval());
        3758                 : }
        3759                 :
        3760                 : static JSBool
        3761             135 : DebuggerObject_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
        3762                 : {
        3763             135 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyNames", args, dbg, obj);
        3764                 :
        3765             270 :     AutoIdVector keys(cx);
        3766                 :     {
        3767             270 :         AutoCompartment ac(cx, obj);
        3768             135 :         if (!ac.enter())
        3769               0 :             return false;
        3770                 :
        3771             270 :         ErrorCopier ec(ac, dbg->toJSObject());
        3772             135 :         if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
        3773               0 :             return false;
        3774                 :     }
        3775                 :
        3776             270 :     AutoValueVector vals(cx);
        3777             135 :     if (!vals.resize(keys.length()))
        3778               0 :         return false;
        3779                 :
        3780             378 :     for (size_t i = 0, len = keys.length(); i < len; i++) {
        3781             243 :          jsid id = keys[i];
        3782             243 :          if (JSID_IS_INT(id)) {
        3783              27 :              JSString *str = js_IntToString(cx, JSID_TO_INT(id));
        3784              27 :              if (!str)
        3785               0 :                  return false;
        3786              27 :              vals[i].setString(str);
        3787             216 :          } else if (JSID_IS_ATOM(id)) {
        3788             216 :              vals[i].setString(JSID_TO_STRING(id));
        3789             216 :              if (!cx->compartment->wrap(cx, &vals[i]))
        3790               0 :                  return false;
        3791                 :          } else {
        3792               0 :              vals[i].setObject(*JSID_TO_OBJECT(id));
        3793               0 :              if (!dbg->wrapDebuggeeValue(cx, &vals[i]))
        3794               0 :                  return false;
        3795                 :          }
        3796                 :     }
        3797                 :
        3798             135 :     JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
        3799             135 :     if (!aobj)
        3800               0 :         return false;
        3801             135 :     args.rval().setObject(*aobj);
        3802             135 :     return true;
        3803                 : }
        3804                 :
        3805                 : static bool
        3806             270 : CheckArgCompartment(JSContext *cx, JSObject *obj, const Value &v,
        3807                 :                     const char *methodname, const char *propname)
        3808                 : {
        3809             270 :     if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
        3810                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
        3811               9 :                              methodname, propname);
        3812               9 :         return false;
        3813                 :     }
        3814             261 :     return true;
        3815                 : }
        3816                 :
        3817                 : /*
        3818                 :  * Convert Debugger.Objects in desc to debuggee values.
        3819                 :  * Reject non-callable getters and setters.
        3820                 :  */
        3821                 : static bool
        3822             297 : UnwrapPropDesc(JSContext *cx, Debugger *dbg, JSObject *obj, PropDesc *desc)
        3823                 : {
        3824             522 :     return (!desc->hasValue || (dbg->unwrapDebuggeeValue(cx, &desc->value) &&
        3825                 :                                 CheckArgCompartment(cx, obj, desc->value, "defineProperty",
        3826             216 :                                                     "value"))) &&
        3827             324 :            (!desc->hasGet || (dbg->unwrapDebuggeeValue(cx, &desc->get) &&
        3828              36 :                               CheckArgCompartment(cx, obj, desc->get, "defineProperty", "get") &&
        3829              36 :                               desc->checkGetter(cx))) &&
        3830             270 :            (!desc->hasSet || (dbg->unwrapDebuggeeValue(cx, &desc->set) &&
        3831              18 :                               CheckArgCompartment(cx, obj, desc->set, "defineProperty", "set") &&
        3832            1422 :                               desc->checkSetter(cx)));
        3833                 : }
        3834                 :
        3835                 : /*
        3836                 :  * Rewrap *idp and the fields of *desc for the current compartment.  Also:
        3837                 :  * defining a property on a proxy requires the pd field to contain a descriptor
        3838                 :  * object, so reconstitute desc->pd if needed.
        3839                 :  */
        3840                 : static bool
        3841             252 : WrapIdAndPropDesc(JSContext *cx, JSObject *obj, jsid *idp, PropDesc *desc)
        3842                 : {
        3843             252 :     JSCompartment *comp = cx->compartment;
        3844             252 :     return comp->wrapId(cx, idp) &&
        3845             252 :            comp->wrap(cx, &desc->value) &&
        3846             252 :            comp->wrap(cx, &desc->get) &&
        3847             252 :            comp->wrap(cx, &desc->set) &&
        3848            1008 :            (!IsProxy(obj) || desc->makeObject(cx));
        3849                 : }
        3850                 :
        3851                 : static JSBool
        3852             189 : DebuggerObject_defineProperty(JSContext *cx, unsigned argc, Value *vp)
        3853                 : {
        3854             189 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj);
        3855             189 :     REQUIRE_ARGC("Debugger.Object.defineProperty", 2);
        3856                 :
        3857                 :     jsid id;
        3858             180 :     if (!ValueToId(cx, args[0], &id))
        3859               0 :         return JS_FALSE;
        3860                 :
        3861             180 :     const Value &descval = args[1];
        3862             360 :     AutoPropDescArrayRooter descs(cx);
        3863             180 :     PropDesc *desc = descs.append();
        3864             180 :     if (!desc || !desc->initialize(cx, descval, false))
        3865               0 :         return false;
        3866                 :
        3867             180 :     desc->pd.setUndefined();
        3868             180 :     if (!UnwrapPropDesc(cx, dbg, obj, desc))
        3869              45 :         return false;
        3870                 :
        3871                 :     {
        3872             270 :         AutoCompartment ac(cx, obj);
        3873             135 :         if (!ac.enter() || !WrapIdAndPropDesc(cx, obj, &id, desc))
        3874               0 :             return false;
        3875                 :
        3876             270 :         ErrorCopier ec(ac, dbg->toJSObject());
        3877                 :         bool dummy;
        3878             135 :         if (!DefineProperty(cx, obj, id, *desc, true, &dummy))
        3879               9 :             return false;
        3880                 :     }
        3881                 :
        3882             126 :     args.rval().setUndefined();
        3883             126 :     return true;
        3884                 : }
        3885                 :
        3886                 : static JSBool
        3887              90 : DebuggerObject_defineProperties(JSContext *cx, unsigned argc, Value *vp)
        3888                 : {
        3889              90 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperties", args, dbg, obj);
        3890              90 :     REQUIRE_ARGC("Debugger.Object.defineProperties", 1);
        3891              90 :     JSObject *props = ToObject(cx, &args[0]);
        3892              90 :     if (!props)
        3893               0 :         return false;
        3894                 :
        3895             180 :     AutoIdVector ids(cx);
        3896             180 :     AutoPropDescArrayRooter descs(cx);
        3897              90 :     if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
        3898               0 :         return false;
        3899              90 :     size_t n = ids.length();
        3900                 :
        3901             207 :     for (size_t i = 0; i < n; i++) {
        3902             117 :         if (!UnwrapPropDesc(cx, dbg, obj, &descs[i]))
        3903               0 :             return false;
        3904                 :     }
        3905                 :
        3906                 :     {
        3907             180 :         AutoCompartment ac(cx, obj);
        3908              90 :         if (!ac.enter())
        3909               0 :             return false;
        3910             207 :         for (size_t i = 0; i < n; i++) {
        3911             117 :             if (!WrapIdAndPropDesc(cx, obj, &ids[i], &descs[i]))
        3912               0 :                 return false;
        3913                 :         }
        3914                 :
        3915             180 :         ErrorCopier ec(ac, dbg->toJSObject());
        3916             198 :         for (size_t i = 0; i < n; i++) {
        3917                 :             bool dummy;
        3918             117 :             if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
        3919               9 :                 return false;
        3920                 :         }
        3921                 :     }
        3922                 :
        3923              81 :     args.rval().setUndefined();
        3924              81 :     return true;
        3925                 : }
        3926                 :
        3927                 : /*
        3928                 :  * This does a non-strict delete, as a matter of API design. The case where the
        3929                 :  * property is non-configurable isn't necessarily exceptional here.
        3930                 :  */
        3931                 : static JSBool
        3932              45 : DebuggerObject_deleteProperty(JSContext *cx, unsigned argc, Value *vp)
        3933                 : {
        3934              45 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "deleteProperty", args, dbg, obj);
        3935              45 :     Value nameArg = argc > 0 ? args[0] : UndefinedValue();
        3936                 :
        3937              90 :     AutoCompartment ac(cx, obj);
        3938              45 :     if (!ac.enter() || !cx->compartment->wrap(cx, &nameArg))
        3939               0 :         return false;
        3940                 :
        3941              90 :     ErrorCopier ec(ac, dbg->toJSObject());
        3942              45 :     return obj->deleteByValue(cx, nameArg, &args.rval(), false);
        3943                 : }
        3944                 :
        3945                 : enum SealHelperOp { Seal, Freeze, PreventExtensions };
        3946                 :
        3947                 : static JSBool
        3948             207 : DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op, const char *name)
        3949                 : {
        3950             207 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, name, args, dbg, obj);
        3951                 :
        3952             414 :     AutoCompartment ac(cx, obj);
        3953             207 :     if (!ac.enter())
        3954               0 :         return false;
        3955                 :
        3956             414 :     ErrorCopier ec(ac, dbg->toJSObject());
        3957                 :     bool ok;
        3958             207 :     if (op == Seal) {
        3959              63 :         ok = obj->seal(cx);
        3960             144 :     } else if (op == Freeze) {
        3961              63 :         ok = obj->freeze(cx);
        3962                 :     } else {
        3963              81 :         JS_ASSERT(op == PreventExtensions);
        3964              81 :         if (!obj->isExtensible()) {
        3965               9 :             args.rval().setUndefined();
        3966               9 :             return true;
        3967                 :         }
        3968             144 :         AutoIdVector props(cx);
        3969              72 :         ok = obj->preventExtensions(cx, &props);
        3970                 :     }
        3971             198 :     if (!ok)
        3972               0 :         return false;
        3973             198 :     args.rval().setUndefined();
        3974             198 :     return true;
        3975                 : }
        3976                 :
        3977                 : static JSBool
        3978              63 : DebuggerObject_seal(JSContext *cx, unsigned argc, Value *vp)
        3979                 : {
        3980              63 :     return DebuggerObject_sealHelper(cx, argc, vp, Seal, "seal");
        3981                 : }
        3982                 :
        3983                 : static JSBool
        3984              63 : DebuggerObject_freeze(JSContext *cx, unsigned argc, Value *vp)
        3985                 : {
        3986              63 :     return DebuggerObject_sealHelper(cx, argc, vp, Freeze, "freeze");
        3987                 : }
        3988                 :
        3989                 : static JSBool
        3990              81 : DebuggerObject_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
        3991                 : {
        3992              81 :     return DebuggerObject_sealHelper(cx, argc, vp, PreventExtensions, "preventExtensions");
        3993                 : }
        3994                 :
        3995                 : static JSBool
        3996             774 : DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op,
        3997                 :                               const char *name)
        3998                 : {
        3999             774 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, name, args, dbg, obj);
        4000                 :
        4001            1548 :     AutoCompartment ac(cx, obj);
        4002             774 :     if (!ac.enter())
        4003               0 :         return false;
        4004                 :
        4005            1548 :     ErrorCopier ec(ac, dbg->toJSObject());
        4006                 :     bool r;
        4007             774 :     if (op == Seal) {
        4008             252 :         if (!obj->isSealed(cx, &r))
        4009               0 :             return false;
        4010             522 :     } else if (op == Freeze) {
        4011             252 :         if (!obj->isFrozen(cx, &r))
        4012               0 :             return false;
        4013                 :     } else {
        4014             270 :         r = obj->isExtensible();
        4015                 :     }
        4016             774 :     args.rval().setBoolean(r);
        4017             774 :     return true;
        4018                 : }
        4019                 :
        4020                 : static JSBool
        4021             252 : DebuggerObject_isSealed(JSContext *cx, unsigned argc, Value *vp)
        4022                 : {
        4023             252 :     return DebuggerObject_isSealedHelper(cx, argc, vp, Seal, "isSealed");
        4024                 : }
        4025                 :
        4026                 : static JSBool
        4027             252 : DebuggerObject_isFrozen(JSContext *cx, unsigned argc, Value *vp)
        4028                 : {
        4029             252 :     return DebuggerObject_isSealedHelper(cx, argc, vp, Freeze, "isFrozen");
        4030                 : }
        4031                 :
        4032                 : static JSBool
        4033             270 : DebuggerObject_isExtensible(JSContext *cx, unsigned argc, Value *vp)
        4034                 : {
        4035             270 :     return DebuggerObject_isSealedHelper(cx, argc, vp, PreventExtensions, "isExtensible");
        4036                 : }
        4037                 :
        4038                 : enum ApplyOrCallMode { ApplyMode, CallMode };
        4039                 :
        4040                 : static JSBool
        4041             576 : ApplyOrCall(JSContext *cx, unsigned argc, Value *vp, ApplyOrCallMode mode)
        4042                 : {
        4043             576 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "apply", args, dbg, obj);
        4044                 :
        4045                 :     /*
        4046                 :      * Any JS exceptions thrown must be in the debugger compartment, so do
        4047                 :      * sanity checks and fallible conversions before entering the debuggee.
        4048                 :      */
        4049             576 :     Value calleev = ObjectValue(*obj);
        4050             576 :     if (!obj->isCallable()) {
        4051                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        4052               0 :                              "Debugger.Object", "apply", obj->getClass()->name);
        4053               0 :         return false;
        4054                 :     }
        4055                 :
        4056                 :     /*
        4057                 :      * Unwrap Debugger.Objects. This happens in the debugger's compartment since
        4058                 :      * that is where any exceptions must be reported.
        4059                 :      */
        4060             576 :     Value thisv = argc > 0 ? args[0] : UndefinedValue();
        4061             576 :     if (!dbg->unwrapDebuggeeValue(cx, &thisv))
        4062              18 :         return false;
        4063             558 :     unsigned callArgc = 0;
        4064             558 :     Value *callArgv = NULL;
        4065            1116 :     AutoValueVector argv(cx);
        4066             558 :     if (mode == ApplyMode) {
        4067             270 :         if (argc >= 2 && !args[1].isNullOrUndefined()) {
        4068             243 :             if (!args[1].isObject()) {
        4069                 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS,
        4070              18 :                                      js_apply_str);
        4071              18 :                 return false;
        4072                 :             }
        4073             225 :             JSObject *argsobj = &args[1].toObject();
        4074             225 :             if (!js_GetLengthProperty(cx, argsobj, &callArgc))
        4075               0 :                 return false;
        4076             225 :             callArgc = unsigned(JS_MIN(callArgc, StackSpace::ARGS_LENGTH_MAX));
        4077             225 :             if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin()))
        4078               0 :                 return false;
        4079             225 :             callArgv = argv.begin();
        4080                 :         }
        4081                 :     } else {
        4082             288 :         callArgc = argc > 0 ? unsigned(JS_MIN(argc - 1, StackSpace::ARGS_LENGTH_MAX)) : 0;
        4083             288 :         callArgv = args.array() + 1;
        4084                 :     }
        4085            1206 :     for (unsigned i = 0; i < callArgc; i++) {
        4086             684 :         if (!dbg->unwrapDebuggeeValue(cx, &callArgv[i]))
        4087              18 :             return false;
        4088                 :     }
        4089                 :
        4090                 :     /*
        4091                 :      * Enter the debuggee compartment and rewrap all input value for that compartment.
        4092                 :      * (Rewrapping always takes place in the destination compartment.)
        4093                 :      */
        4094            1044 :     AutoCompartment ac(cx, obj);
        4095             522 :     if (!ac.enter() || !cx->compartment->wrap(cx, &calleev) || !cx->compartment->wrap(cx, &thisv))
        4096               0 :         return false;
        4097            1188 :     for (unsigned i = 0; i < callArgc; i++) {
        4098             666 :         if (!cx->compartment->wrap(cx, &callArgv[i]))
        4099               0 :             return false;
        4100                 :     }
        4101                 :
        4102                 :     /*
        4103                 :      * Call the function. Use receiveCompletionValue to return to the debugger
        4104                 :      * compartment and populate args.rval().
        4105                 :      */
        4106                 :     Value rval;
        4107             522 :     bool ok = Invoke(cx, thisv, calleev, callArgc, callArgv, &rval);
        4108             522 :     return dbg->receiveCompletionValue(ac, ok, rval, &args.rval());
        4109                 : }
        4110                 :
        4111                 : static JSBool
        4112             279 : DebuggerObject_apply(JSContext *cx, unsigned argc, Value *vp)
        4113                 : {
        4114             279 :     return ApplyOrCall(cx, argc, vp, ApplyMode);
        4115                 : }
        4116                 :
        4117                 : static JSBool
        4118             297 : DebuggerObject_call(JSContext *cx, unsigned argc, Value *vp)
        4119                 : {
        4120             297 :     return ApplyOrCall(cx, argc, vp, CallMode);
        4121                 : }
        4122                 :
        4123                 : static JSBool
        4124             108 : DebuggerObject_makeDebuggeeValue(JSContext *cx, unsigned argc, Value *vp)
        4125                 : {
        4126             108 :     REQUIRE_ARGC("Debugger.Object.prototype.makeDebuggeeValue", 1);
        4127             108 :     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "makeDebuggeeValue", args, dbg, referent);
        4128                 :
        4129                 :     /* Non-objects are already debuggee values. */
        4130             108 :     if (args[0].isObject()) {
        4131                 :         // Enter this Debugger.Object's referent's compartment, and wrap the
        4132                 :         // argument as appropriate for references from there.
        4133                 :         {
        4134              90 :             AutoCompartment ac(cx, referent);
        4135              90 :             if (!ac.enter() ||
        4136              45 :                 !cx->compartment->wrap(cx, &args[0]))
        4137               0 :                 return false;
        4138                 :         }
        4139                 :
        4140                 :         // Back in the debugger's compartment, produce a new Debugger.Object
        4141                 :         // instance referring to the wrapped argument.
        4142              45 :         if (!dbg->wrapDebuggeeValue(cx, &args[0]))
        4143               0 :             return false;
        4144                 :     }
        4145                 :
        4146             108 :     args.rval() = args[0];
        4147             108 :     return true;
        4148                 : }
        4149                 :
        4150                 : static JSPropertySpec DebuggerObject_properties[] = {
        4151                 :     JS_PSG("proto", DebuggerObject_getProto, 0),
        4152                 :     JS_PSG("class", DebuggerObject_getClass, 0),
        4153                 :     JS_PSG("callable", DebuggerObject_getCallable, 0),
        4154                 :     JS_PSG("name", DebuggerObject_getName, 0),
        4155                 :     JS_PSG("parameterNames", DebuggerObject_getParameterNames, 0),
        4156                 :     JS_PSG("script", DebuggerObject_getScript, 0),
        4157                 :     JS_PSG("environment", DebuggerObject_getEnvironment, 0),
        4158                 :     JS_PS_END
        4159                 : };
        4160                 :
        4161                 : static JSFunctionSpec DebuggerObject_methods[] = {
        4162                 :     JS_FN("getOwnPropertyDescriptor", DebuggerObject_getOwnPropertyDescriptor, 1, 0),
        4163                 :     JS_FN("getOwnPropertyNames", DebuggerObject_getOwnPropertyNames, 0, 0),
        4164                 :     JS_FN("defineProperty", DebuggerObject_defineProperty, 2, 0),
        4165                 :     JS_FN("defineProperties", DebuggerObject_defineProperties, 1, 0),
        4166                 :     JS_FN("deleteProperty", DebuggerObject_deleteProperty, 1, 0),
        4167                 :     JS_FN("seal", DebuggerObject_seal, 0, 0),
        4168                 :     JS_FN("freeze", DebuggerObject_freeze, 0, 0),
        4169                 :     JS_FN("preventExtensions", DebuggerObject_preventExtensions, 0, 0),
        4170                 :     JS_FN("isSealed", DebuggerObject_isSealed, 0, 0),
        4171                 :     JS_FN("isFrozen", DebuggerObject_isFrozen, 0, 0),
        4172                 :     JS_FN("isExtensible", DebuggerObject_isExtensible, 0, 0),
        4173                 :     JS_FN("apply", DebuggerObject_apply, 0, 0),
        4174                 :     JS_FN("call", DebuggerObject_call, 0, 0),
        4175                 :     JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0),
        4176                 :     JS_FS_END
        4177                 : };
        4178                 :
        4179                 :
        4180                 : /*** Debugger.Environment ************************************************************************/
        4181                 :
        4182                 : static void
        4183           44923 : DebuggerEnv_trace(JSTracer *trc, JSObject *obj)
        4184                 : {
        4185                 :     /*
        4186                 :      * There is a barrier on private pointers, so the Unbarriered marking
        4187                 :      * is okay.
        4188                 :      */
        4189           44923 :     if (Env *referent = (JSObject *) obj->getPrivate()) {
        4190              90 :         MarkCrossCompartmentObjectUnbarriered(trc, &referent, "Debugger.Environment referent");
        4191              90 :         obj->setPrivateUnbarriered(referent);
        4192                 :     }
        4193           44923 : }
        4194                 :
        4195                 : Class DebuggerEnv_class = {
        4196                 :     "Environment",
        4197                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
        4198                 :     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
        4199                 :     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
        4200                 :     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
        4201                 :     NULL,                 /* checkAccess */
        4202                 :     NULL,                 /* call        */
        4203                 :     NULL,                 /* construct   */
        4204                 :     NULL,                 /* hasInstance */
        4205                 :     DebuggerEnv_trace
        4206                 : };
        4207                 :
        4208                 : static JSObject *
        4209           15264 : DebuggerEnv_checkThis(JSContext *cx, const CallArgs &args, const char *fnname)
        4210                 : {
        4211           15264 :     if (!args.thisv().isObject()) {
        4212               0 :         ReportObjectRequired(cx);
        4213               0 :         return NULL;
        4214                 :     }
        4215           15264 :     JSObject *thisobj = &args.thisv().toObject();
        4216           15264 :     if (thisobj->getClass() != &DebuggerEnv_class) {
        4217                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        4218               0 :                              "Debugger.Environment", fnname, thisobj->getClass()->name);
        4219               0 :         return NULL;
        4220                 :     }
        4221                 :
        4222                 :     /*
        4223                 :      * Forbid Debugger.Environment.prototype, which is of class DebuggerEnv_class
        4224                 :      * but isn't a real working Debugger.Environment. The prototype object is
        4225                 :      * distinguished by having no referent.
        4226                 :      */
        4227           15264 :     if (!thisobj->getPrivate()) {
        4228                 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
        4229               0 :                              "Debugger.Environment", fnname, "prototype object");
        4230               0 :         return NULL;
        4231                 :     }
        4232           15264 :     return thisobj;
        4233                 : }
        4234                 :
        4235                 : #define THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env)                \
        4236                 :     CallArgs args = CallArgsFromVp(argc, vp);                                 \
        4237                 :     JSObject *envobj = DebuggerEnv_checkThis(cx, args, fnname);               \
        4238                 :     if (!envobj)                                                              \
        4239                 :         return false;                                                         \
        4240                 :     Env *env = static_cast<Env *>(envobj->getPrivate());                      \
        4241                 :     JS_ASSERT(env)
        4242                 :
        4243                 : #define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg)     \
        4244                 :     THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env);                   \
        4245                 :     Debugger *dbg = Debugger::fromChildJSObject(envobj)
        4246                 :
        4247                 : static JSBool
        4248               0 : DebuggerEnv_construct(JSContext *cx, unsigned argc, Value *vp)
        4249                 : {
        4250               0 :     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Environment");
        4251               0 :     return false;
        4252                 : }
        4253                 :
        4254                 : static JSBool
        4255             432 : DebuggerEnv_getType(JSContext *cx, unsigned argc, Value *vp)
        4256                 : {
        4257             432 :     THIS_DEBUGENV(cx, argc, vp, "get type", args, envobj, env);
        4258                 :
        4259                 :     /* Don't bother switching compartments just to check env's class. */
        4260                 :     const char *s;
        4261             432 :     if (env->isCall() || env->isBlock() || env->isDeclEnv())
        4262             351 :         s = "declarative";
        4263                 :     else
        4264              81 :         s = "object";
        4265                 :
        4266             432 :     JSAtom *str = js_Atomize(cx, s, strlen(s), InternAtom, NormalEncoding);
        4267             432 :     if (!str)
        4268               0 :         return false;
        4269             432 :     args.rval().setString(str);
        4270             432 :     return true;
        4271                 : }
        4272                 :
        4273                 : static JSBool
        4274            6903 : DebuggerEnv_getParent(JSContext *cx, unsigned argc, Value *vp)
        4275                 : {
        4276            6903 :     THIS_DEBUGENV_OWNER(cx, argc, vp, "get parent", args, envobj, env, dbg);
        4277                 :
        4278                 :     /* Don't bother switching compartments just to get env's parent. */
        4279            6903 :     Env *parent = env->enclosingScope();
        4280            6903 :     return dbg->wrapEnvironment(cx, parent, &args.rval());
        4281                 : }
        4282                 :
        4283                 : static JSBool
        4284              63 : DebuggerEnv_getObject(JSContext *cx, unsigned argc, Value *vp)
        4285                 : {
        4286              63 :     THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg);
        4287                 :
        4288                 :     /*
        4289                 :      * Don't bother switching compartments just to check env's class and
        4290                 :      * possibly get its proto.
        4291                 :      */
        4292              63 :     if (env->isCall() || env->isBlock() || env->isDeclEnv()) {
        4293               0 :         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NO_SCOPE_OBJECT);
        4294               0 :         return false;
        4295                 :     }
        4296              63 :     JSObject *obj = env->isWith() ? env->getProto() : env;
        4297                 :
        4298              63 :     Value rval = ObjectValue(*obj);
        4299              63 :     if (!dbg->wrapDebuggeeValue(cx, &rval))
        4300               0 :         return false;
        4301              63 :     args.rval() = rval;
        4302              63 :     return true;
        4303                 : }
        4304                 :
        4305                 : static JSBool
        4306            7047 : DebuggerEnv_names(JSContext *cx, unsigned argc, Value *vp)
        4307                 : {
        4308            7047 :     THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg);
        4309                 :
        4310           14094 :     AutoIdVector keys(cx);
        4311                 :     {
        4312           14094 :         AutoCompartment ac(cx, env);
        4313            7047 :         if (!ac.enter())
        4314               0 :             return false;
        4315                 :
        4316           14094 :         ErrorCopier ec(ac, dbg->toJSObject());
        4317            7047 :         if (!GetPropertyNames(cx, env, JSITER_HIDDEN, &keys))
        4318               0 :             return false;
        4319                 :     }
        4320                 :
        4321            7047 :     JSObject *arr = NewDenseEmptyArray(cx);
        4322            7047 :     if (!arr)
        4323               0 :         return false;
        4324          462213 :     for (size_t i = 0, len = keys.length(); i < len; i++) {
        4325          455166 :          jsid id = keys[i];
        4326          455166 :          if (JSID_IS_ATOM(id) && IsIdentifier(JSID_TO_ATOM(id))) {
        4327          455130 :              if (!cx->compartment->wrapId(cx, &id))
        4328               0 :                  return false;
        4329          455130 :              if (!js_NewbornArrayPush(cx, arr, StringValue(JSID_TO_STRING(id))))
        4330               0 :                  return false;
        4331                 :          }
        4332                 :     }
        4333            7047 :     args.rval().setObject(*arr);
        4334            7047 :     return true;
        4335                 : }
        4336                 :
        4337                 : static JSBool
        4338             837 : DebuggerEnv_find(JSContext *cx, unsigned argc, Value *vp)
        4339                 : {
        4340             837 :     REQUIRE_ARGC("Debugger.Environment.find", 1);
        4341             819 :     THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg);
        4342                 :
        4343                 :     jsid id;
        4344             819 :     if (!ValueToIdentifier(cx, args[0], &id))
        4345             108 :         return false;
        4346                 :
        4347                 :     {
        4348            1422 :         AutoCompartment ac(cx, env);
        4349             711 :         if (!ac.enter() || !cx->compartment->wrapId(cx, &id))
        4350               0 :             return false;
        4351                 :
        4352                 :         /* This can trigger resolve hooks. */
        4353            1422 :         ErrorCopier ec(ac, dbg->toJSObject());
        4354             711 :         JSProperty *prop = NULL;
        4355                 :         JSObject *pobj;
        4356            7560 :         for (; env && !prop; env = env->enclosingScope()) {
        4357            7488 :             if (!env->lookupGeneric(cx, id, &pobj, &prop))
        4358               0 :                 return false;
        4359            7488 :             if (prop)
        4360             639 :                 break;
        4361                 :         }
        4362                 :     }
        4363                 :
        4364             711 :     return dbg->wrapEnvironment(cx, env, &args.rval());
        4365                 : }
        4366                 :
        4367                 : static JSPropertySpec DebuggerEnv_properties[] = {
        4368                 :     JS_PSG("type", DebuggerEnv_getType, 0),
        4369                 :     JS_PSG("object", DebuggerEnv_getObject, 0),
        4370                 :     JS_PSG("parent", DebuggerEnv_getParent, 0),
        4371                 :     JS_PS_END
        4372                 : };
        4373                 :
        4374                 : static JSFunctionSpec DebuggerEnv_methods[] = {
        4375                 :     JS_FN("names", DebuggerEnv_names, 0, 0),
        4376                 :     JS_FN("find", DebuggerEnv_find, 1, 0),
        4377                 :     JS_FS_END
        4378                 : };
        4379                 :
        4380                 :
        4381                 :
        4382                 : /*** Glue ****************************************************************************************/
        4383                 :
        4384                 : extern JS_PUBLIC_API(JSBool)
        4385           23330 : JS_DefineDebuggerObject(JSContext *cx, JSObject *obj)
        4386                 : {
        4387           46660 :     RootObject objRoot(cx, &obj);
        4388                 :
        4389                 :     RootedVarObject
        4390           46660 :         objProto(cx),
        4391           46660 :         debugCtor(cx),
        4392           46660 :         debugProto(cx),
        4393           46660 :         frameProto(cx),
        4394           46660 :         scriptProto(cx),
        4395           46660 :         objectProto(cx);
        4396                 :
        4397           23330 :     objProto = obj->asGlobal().getOrCreateObjectPrototype(cx);
        4398           23330 :     if (!objProto)
        4399               0 :         return false;
        4400                 :
        4401                 :
        4402                 :     debugProto = js_InitClass(cx, objRoot,
        4403                 :                               objProto, &Debugger::jsclass, Debugger::construct,
        4404                 :                               1, Debugger::properties, Debugger::methods, NULL, NULL,
        4405           23330 :                               debugCtor.address());
        4406           23330 :     if (!debugProto)
        4407               0 :         return false;
        4408                 :
        4409                 :     frameProto = js_InitClass(cx, debugCtor, objProto, &DebuggerFrame_class,
        4410                 :                               DebuggerFrame_construct, 0,
        4411                 :                               DebuggerFrame_properties, DebuggerFrame_methods,
        4412           23330 :                               NULL, NULL);
        4413           23330 :     if (!frameProto)
        4414               0 :         return false;
        4415                 :
        4416                 :     scriptProto = js_InitClass(cx, debugCtor, objProto, &DebuggerScript_class,
        4417                 :                                DebuggerScript_construct, 0,
        4418                 :                                DebuggerScript_properties, DebuggerScript_methods,
        4419           23330 :                                NULL, NULL);
        4420           23330 :     if (!scriptProto)
        4421               0 :         return false;
        4422                 :
        4423                 :     objectProto = js_InitClass(cx, debugCtor, objProto, &DebuggerObject_class,
        4424                 :                                DebuggerObject_construct, 0,
        4425                 :                                DebuggerObject_properties, DebuggerObject_methods,
        4426           23330 :                                NULL, NULL);
        4427           23330 :     if (!objectProto)
        4428               0 :         return false;
        4429                 :
        4430                 :     JSObject *envProto = js_InitClass(cx, debugCtor, objProto, &DebuggerEnv_class,
        4431                 :                                       DebuggerEnv_construct, 0,
        4432                 :                                       DebuggerEnv_properties, DebuggerEnv_methods,
        4433           23330 :                                       NULL, NULL);
        4434           23330 :     if (!envProto)
        4435               0 :         return false;
        4436                 :
        4437           23330 :     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto));
        4438           23330 :     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO, ObjectValue(*objectProto));
        4439           23330 :     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO, ObjectValue(*scriptProto));
        4440           23330 :     debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO, ObjectValue(*envProto));
        4441           23330 :     return true;
        4442                 : }

Generated by: LCOV version 1.7