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

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

Generated by: LCOV version 1.7