LCOV - code coverage report
Current view: directory - js/src/vm - Debugger.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1955 1709 87.4 %
Date: 2012-06-02 Functions: 177 171 96.6 %

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

Generated by: LCOV version 1.7