Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsdbgapi.cpp
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=99:
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 Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is
21  * Netscape Communications Corporation.
22  * Portions created by the Initial Developer are Copyright (C) 1998
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40
41 /*
42  * JS debugging API.
43  */
44 #include <string.h>
45 #include "jsprvtd.h"
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsutil.h"
49 #include "jsclist.h"
50 #include "jshashtable.h"
51 #include "jsapi.h"
52 #include "jscntxt.h"
53 #include "jsversion.h"
54 #include "jsdbgapi.h"
55 #include "jsemit.h"
56 #include "jsfun.h"
57 #include "jsgc.h"
58 #include "jsinterp.h"
59 #include "jslock.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsparse.h"
63 #include "jsscope.h"
64 #include "jsscript.h"
65 #include "jsstaticcheck.h"
66 #include "jsstr.h"
67 #include "jswrapper.h"
68
69 #include "jsatominlines.h"
70 #include "jsdbgapiinlines.h"
71 #include "jsinterpinlines.h"
72 #include "jsobjinlines.h"
73 #include "jsscopeinlines.h"
74 #include "jsscriptinlines.h"
75
76 #include "jsautooplen.h"
77
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/Retcon.h"
80
81 using namespace js;
82 using namespace js::gc;
83
84 typedef struct JSTrap {
85     JSCList         links;
86     JSScript        *script;
87     jsbytecode      *pc;
88     JSOp            op;
89     JSTrapHandler   handler;
90     jsval           closure;
91 } JSTrap;
92
93 #define DBG_LOCK(rt)            JS_ACQUIRE_LOCK((rt)->debuggerLock)
94 #define DBG_UNLOCK(rt)          JS_RELEASE_LOCK((rt)->debuggerLock)
95 #define DBG_LOCK_EVAL(rt,expr)  (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
96
97 JS_PUBLIC_API(JSBool)
98 JS_GetDebugMode(JSContext *cx)
99 {
100     return cx->compartment->debugMode;
101 }
102
103 JS_PUBLIC_API(JSBool)
104 JS_SetDebugMode(JSContext *cx, JSBool debug)
105 {
106     return JS_SetDebugModeForCompartment(cx, cx->compartment, debug);
107 }
108
109 JS_PUBLIC_API(void)
110 JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
111 {
112     rt->debugMode = debug;
113 }
114
115 #ifdef DEBUG
116 static bool
117 CompartmentHasLiveScripts(JSCompartment *comp)
118 {
119 #ifdef JS_METHODJIT
120 # ifdef JS_THREADSAFE
121     jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
122 # endif
123 #endif
124
125     // Unsynchronized context iteration is technically a race; but this is only
126     // for debug asserts where such a race would be rare
127     JSContext *iter = NULL;
128     JSContext *icx;
129     while ((icx = JS_ContextIterator(comp->rt, &iter))) {
130 #ifdef JS_THREADSAFE
131         if (JS_GetContextThread(icx) != currentThreadId)
132             continue;
133 #endif
134         for (AllFramesIter i(icx); !i.done(); ++i) {
135             JSScript *script = i.fp()->maybeScript();
136             if (script && script->compartment == comp)
137                 return JS_TRUE;
138         }
139     }
140
141     return JS_FALSE;
142 }
143 #endif
144
145 JS_FRIEND_API(JSBool)
146 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
147 {
148     if (comp->debugMode == !!debug)
149         return JS_TRUE;
150
151     // This should only be called when no scripts are live. It would even be
152     // incorrect to discard just the non-live scripts' JITScripts because they
153     // might share ICs with live scripts (bug 632343).
154     JS_ASSERT(!CompartmentHasLiveScripts(comp));
155
156     // All scripts compiled from this point on should be in the requested debugMode.
157     comp->debugMode = !!debug;
158
159     // Discard JIT code for any scripts that change debugMode. This function
160     // assumes that 'comp' is in the same thread as 'cx'.
161
162 #ifdef JS_METHODJIT
163     JS::AutoEnterScriptCompartment ac;
164
165     for (JSScript *script = (JSScript *)comp->scripts.next;
166          &script->links != &comp->scripts;
167          script = (JSScript *)script->links.next)
168     {
169         if (!script->debugMode == !debug)
170             continue;
171
172         /*
173          * If compartment entry fails, debug mode is left partially on, leading
174          * to a small performance overhead but no loss of correctness. We set
175          * the debug flags to false so that the caller will not later attempt
176          * to use debugging features.
177          */
178         if (!ac.entered() && !ac.enter(cx, script)) {
179             comp->debugMode = JS_FALSE;
180             return JS_FALSE;
181         }
182
183         mjit::ReleaseScriptCode(cx, script);
184         script->debugMode = !!debug;
185     }
186 #endif
187
188     return JS_TRUE;
189 }
190
191 JS_FRIEND_API(JSBool)
192 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
193 {
194 #ifdef JS_METHODJIT
195     if (!script->singleStepMode == !singleStep)
196         return JS_TRUE;
197 #endif
198
199     JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
200
201 #ifdef JS_METHODJIT
202     /* request the next recompile to inject single step interrupts */
203     script->singleStepMode = !!singleStep;
204
205     js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
206     if (jit && script->singleStepMode != jit->singleStepMode) {
207         js::mjit::Recompiler recompiler(cx, script);
208         if (!recompiler.recompile()) {
209             script->singleStepMode = !singleStep;
210             return JS_FALSE;
211         }
212     }
213 #endif
214     return JS_TRUE;
215 }
216
217 static JSBool
218 CheckDebugMode(JSContext *cx)
219 {
220     JSBool debugMode = JS_GetDebugMode(cx);
221     /*
222      * :TODO:
223      * This probably should be an assertion, since it's indicative of a severe
224      * API misuse.
225      */
226     if (!debugMode) {
227         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
228                                      NULL, JSMSG_NEED_DEBUG_MODE);
229     }
230     return debugMode;
231 }
232
233 JS_PUBLIC_API(JSBool)
234 JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
235 {
236     if (!CheckDebugMode(cx))
237         return JS_FALSE;
238
239     return js_SetSingleStepMode(cx, script, singleStep);
240 }
241
242 /*
243  * NB: FindTrap must be called with rt->debuggerLock acquired.
244  */
245 static JSTrap *
246 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
247 {
248     JSTrap *trap;
249
250     for (trap = (JSTrap *)rt->trapList.next;
251          &trap->links != &rt->trapList;
252          trap = (JSTrap *)trap->links.next) {
253         if (trap->script == script && trap->pc == pc)
254             return trap;
255     }
256     return NULL;
257 }
258
259 jsbytecode *
260 js_UntrapScriptCode(JSContext *cx, JSScript *script)
261 {
262     jsbytecode *code;
263     JSRuntime *rt;
264     JSTrap *trap;
265
266     code = script->code;
267     rt = cx->runtime;
268     DBG_LOCK(rt);
269     for (trap = (JSTrap *)rt->trapList.next;
270          &trap->links !=
271                 &rt->trapList;
272          trap = (JSTrap *)trap->links.next) {
273         if (trap->script == script &&
274             (size_t)(trap->pc - script->code) < script->length) {
275             if (code == script->code) {
276                 jssrcnote *sn, *notes;
277                 size_t nbytes;
278
279                 nbytes = script->length * sizeof(jsbytecode);
280                 notes = script->notes();
281                 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
282                     continue;
283                 nbytes += (sn - notes + 1) * sizeof *sn;
284
285                 code = (jsbytecode *) cx->malloc(nbytes);
286                 if (!code)
287                     break;
288                 memcpy(code, script->code, nbytes);
289                 JS_PURGE_GSN_CACHE(cx);
290             }
291             code[trap->pc - script->code] = trap->op;
292         }
293     }
294     DBG_UNLOCK(rt);
295     return code;
296 }
297
298 JS_PUBLIC_API(JSBool)
299 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
300            JSTrapHandler handler, jsval closure)
301 {
302     JSTrap *junk, *trap, *twin;
303     JSRuntime *rt;
304     uint32 sample;
305
306     if (!CheckDebugMode(cx))
307         return JS_FALSE;
308
309     JS_ASSERT((JSOp) *pc != JSOP_TRAP);
310     junk = NULL;
311     rt = cx->runtime;
312     DBG_LOCK(rt);
313     trap = FindTrap(rt, script, pc);
314     if (trap) {
315         JS_ASSERT(trap->script == script && trap->pc == pc);
316         JS_ASSERT(*pc == JSOP_TRAP);
317     } else {
318         sample = rt->debuggerMutations;
319         DBG_UNLOCK(rt);
320         trap = (JSTrap *) cx->malloc(sizeof *trap);
321         if (!trap)
322             return JS_FALSE;
323         trap->closure = JSVAL_NULL;
324         DBG_LOCK(rt);
325         twin = (rt->debuggerMutations != sample)
326                ? FindTrap(rt, script, pc)
327                : NULL;
328         if (twin) {
329             junk = trap;
330             trap = twin;
331         } else {
332             JS_APPEND_LINK(&trap->links, &rt->trapList);
333             ++rt->debuggerMutations;
334             trap->script = script;
335             trap->pc = pc;
336             trap->op = (JSOp)*pc;
337             *pc = JSOP_TRAP;
338         }
339     }
340     trap->handler = handler;
341     trap->closure = closure;
342     DBG_UNLOCK(rt);
343     if (junk)
344         cx->free(junk);
345
346 #ifdef JS_METHODJIT
347     if (script->hasJITCode()) {
348         js::mjit::Recompiler recompiler(cx, script);
349         if (!recompiler.recompile())
350             return JS_FALSE;
351     }
352 #endif
353
354     return JS_TRUE;
355 }
356
357 JS_PUBLIC_API(JSOp)
358 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
359 {
360     JSRuntime *rt;
361     JSTrap *trap;
362     JSOp op;
363
364     rt = cx->runtime;
365     DBG_LOCK(rt);
366     trap = FindTrap(rt, script, pc);
367     op = trap ? trap->op : (JSOp) *pc;
368     DBG_UNLOCK(rt);
369     return op;
370 }
371
372 static void
373 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
374 {
375     ++cx->runtime->debuggerMutations;
376     JS_REMOVE_LINK(&trap->links);
377     *trap->pc = (jsbytecode)trap->op;
378     DBG_UNLOCK(cx->runtime);
379     cx->free(trap);
380 }
381
382 JS_PUBLIC_API(void)
383 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
384              JSTrapHandler *handlerp, jsval *closurep)
385 {
386     JSTrap *trap;
387     
388     DBG_LOCK(cx->runtime);
389     trap = FindTrap(cx->runtime, script, pc);
390     if (handlerp)
391         *handlerp = trap ? trap->handler : NULL;
392     if (closurep)
393         *closurep = trap ? trap->closure : JSVAL_NULL;
394     if (trap)
395         DestroyTrapAndUnlock(cx, trap);
396     else
397         DBG_UNLOCK(cx->runtime);
398
399 #ifdef JS_METHODJIT
400     if (script->hasJITCode()) {
401         mjit::Recompiler recompiler(cx, script);
402         recompiler.recompile();
403     }
404 #endif
405 }
406
407 JS_PUBLIC_API(void)
408 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
409 {
410     JSRuntime *rt;
411     JSTrap *trap, *next;
412     uint32 sample;
413
414     rt = cx->runtime;
415     DBG_LOCK(rt);
416     for (trap = (JSTrap *)rt->trapList.next;
417          &trap->links != &rt->trapList;
418          trap = next) {
419         next = (JSTrap *)trap->links.next;
420         if (trap->script == script) {
421             sample = rt->debuggerMutations;
422             DestroyTrapAndUnlock(cx, trap);
423             DBG_LOCK(rt);
424             if (rt->debuggerMutations != sample + 1)
425                 next = (JSTrap *)rt->trapList.next;
426         }
427     }
428     DBG_UNLOCK(rt);
429 }
430
431 JS_PUBLIC_API(void)
432 JS_ClearAllTraps(JSContext *cx)
433 {
434     JSRuntime *rt;
435     JSTrap *trap, *next;
436     uint32 sample;
437
438     rt = cx->runtime;
439     DBG_LOCK(rt);
440     for (trap = (JSTrap *)rt->trapList.next;
441          &trap->links != &rt->trapList;
442          trap = next) {
443         next = (JSTrap *)trap->links.next;
444         sample = rt->debuggerMutations;
445         DestroyTrapAndUnlock(cx, trap);
446         DBG_LOCK(rt);
447         if (rt->debuggerMutations != sample + 1)
448             next = (JSTrap *)rt->trapList.next;
449     }
450     DBG_UNLOCK(rt);
451 }
452
453 /*
454  * NB: js_MarkTraps does not acquire cx->runtime->debuggerLock, since the
455  * debugger should never be racing with the GC (i.e., the debugger must
456  * respect the request model).
457  */
458 void
459 js_MarkTraps(JSTracer *trc)
460 {
461     JSRuntime *rt = trc->context->runtime;
462
463     for (JSTrap *trap = (JSTrap *) rt->trapList.next;
464          &trap->links != &rt->trapList;
465          trap = (JSTrap *) trap->links.next) {
466         MarkValue(trc, Valueify(trap->closure), "trap->closure");
467     }
468 }
469
470 JS_PUBLIC_API(JSTrapStatus)
471 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
472 {
473     JSTrap *trap;
474     jsint op;
475     JSTrapStatus status;
476
477     DBG_LOCK(cx->runtime);
478     trap = FindTrap(cx->runtime, script, pc);
479     JS_ASSERT(!trap || trap->handler);
480     if (!trap) {
481         op = (JSOp) *pc;
482         DBG_UNLOCK(cx->runtime);
483
484         /* Defend against "pc for wrong script" API usage error. */
485         JS_ASSERT(op != JSOP_TRAP);
486
487 #ifdef JS_THREADSAFE
488         /* If the API was abused, we must fail for want of the real op. */
489         if (op == JSOP_TRAP)
490             return JSTRAP_ERROR;
491
492         /* Assume a race with a debugger thread and try to carry on. */
493         *rval = INT_TO_JSVAL(op);
494         return JSTRAP_CONTINUE;
495 #else
496         /* Always fail if single-threaded (must be an API usage error). */
497         return JSTRAP_ERROR;
498 #endif
499     }
500     DBG_UNLOCK(cx->runtime);
501
502     /*
503      * It's important that we not use 'trap->' after calling the callback --
504      * the callback might remove the trap!
505      */
506     op = (jsint)trap->op;
507     status = trap->handler(cx, script, pc, rval, trap->closure);
508     if (status == JSTRAP_CONTINUE) {
509         /* By convention, return the true op to the interpreter in rval. */
510         *rval = INT_TO_JSVAL(op);
511     }
512     return status;
513 }
514
515 #ifdef JS_TRACER
516 static void
517 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
518 {
519     if (wasInhibited) {
520         if (!rt->debuggerInhibitsJIT()) {
521             for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
522                 js_ContextFromLinkField(cl)->updateJITEnabled();
523         }
524     } else if (rt->debuggerInhibitsJIT()) {
525         for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
526             js_ContextFromLinkField(cl)->traceJitEnabled = false;
527     }
528 }
529 #endif
530
531 JS_PUBLIC_API(JSBool)
532 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
533 {
534 #ifdef JS_TRACER
535     {
536         AutoLockGC lock(rt);
537         bool wasInhibited = rt->debuggerInhibitsJIT();
538 #endif
539         rt->globalDebugHooks.interruptHook = hook;
540         rt->globalDebugHooks.interruptHookData = closure;
541 #ifdef JS_TRACER
542         JITInhibitingHookChange(rt, wasInhibited);
543     }
544 #endif
545     return JS_TRUE;
546 }
547
548 JS_PUBLIC_API(JSBool)
549 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
550 {
551 #ifdef JS_TRACER
552     AutoLockGC lock(rt);
553     bool wasInhibited = rt->debuggerInhibitsJIT();
554 #endif
555     if (hoop)
556         *hoop = rt->globalDebugHooks.interruptHook;
557     if (closurep)
558         *closurep = rt->globalDebugHooks.interruptHookData;
559     rt->globalDebugHooks.interruptHook = 0;
560     rt->globalDebugHooks.interruptHookData = 0;
561 #ifdef JS_TRACER
562     JITInhibitingHookChange(rt, wasInhibited);
563 #endif
564     return JS_TRUE;
565 }
566
567 /************************************************************************/
568
569 struct JSWatchPoint {
570     JSCList             links;
571     JSObject            *object;        /* weak link, see js_SweepWatchPoints */
572     const Shape         *shape;
573     StrictPropertyOp    setter;
574     JSWatchPointHandler handler;
575     JSObject            *closure;
576     uintN               flags;
577 };
578
579 #define JSWP_LIVE       0x1             /* live because set and not cleared */
580 #define JSWP_HELD       0x2             /* held while running handler/setter */
581
582 /*
583  * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
584  */
585 static JSBool
586 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
587 {
588     bool ok = true;
589     JSRuntime *rt = cx->runtime;
590
591     wp->flags &= ~flag;
592     if (wp->flags != 0) {
593         DBG_UNLOCK(rt);
594         return ok;
595     }
596
597     /*
598      * Switch to the same compartment as the watch point, since changeProperty, below,
599      * needs to have a compartment.
600      */
601     SwitchToCompartment sc(cx, wp->object);
602
603     /* Remove wp from the list, then restore wp->shape->setter from wp. */
604     ++rt->debuggerMutations;
605     JS_REMOVE_LINK(&wp->links);
606     DBG_UNLOCK(rt);
607
608     /*
609      * If the property isn't found on wp->object, then someone else must have deleted it,
610      * and we don't need to change the property attributes.
611      */
612     const Shape *shape = wp->shape;
613     const Shape *wprop = wp->object->nativeLookup(shape->id);
614     if (wprop &&
615         wprop->hasSetterValue() == shape->hasSetterValue() &&
616         IsWatchedProperty(cx, wprop)) {
617         shape = wp->object->changeProperty(cx, wprop, 0, wprop->attributes(),
618                                            wprop->getter(), wp->setter);
619         if (!shape)
620             ok = false;
621     }
622
623     cx->free(wp);
624     return ok;
625 }
626
627 /*
628  * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
629  * the debugger should never be racing with the GC (i.e., the debugger must
630  * respect the request model).
631  */
632 void
633 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
634 {
635     JSRuntime *rt;
636     JSWatchPoint *wp;
637
638     rt = trc->context->runtime;
639
640     for (wp = (JSWatchPoint *)rt->watchPointList.next;
641          &wp->links != &rt->watchPointList;
642          wp = (JSWatchPoint *)wp->links.next) {
643         if (wp->object == obj) {
644             wp->shape->trace(trc);
645             if (wp->shape->hasSetterValue() && wp->setter)
646                 MarkObject(trc, *CastAsObject(wp->setter), "wp->setter");
647             MarkObject(trc, *wp->closure, "wp->closure");
648         }
649     }
650 }
651
652 void
653 js_SweepWatchPoints(JSContext *cx)
654 {
655     JSRuntime *rt;
656     JSWatchPoint *wp, *next;
657     uint32 sample;
658
659     rt = cx->runtime;
660     DBG_LOCK(rt);
661     for (wp = (JSWatchPoint *)rt->watchPointList.next;
662          &wp->links != &rt->watchPointList;
663          wp = next) {
664         next = (JSWatchPoint *)wp->links.next;
665         if (IsAboutToBeFinalized(cx, wp->object)) {
666             sample = rt->debuggerMutations;
667
668             /* Ignore failures. */
669             DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
670             DBG_LOCK(rt);
671             if (rt->debuggerMutations != sample + 1)
672                 next = (JSWatchPoint *)rt->watchPointList.next;
673         }
674     }
675     DBG_UNLOCK(rt);
676 }
677
678
679
680 /*
681  * NB: LockedFindWatchPoint must be called with rt->debuggerLock acquired.
682  */
683 static JSWatchPoint *
684 LockedFindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
685 {
686     JSWatchPoint *wp;
687
688     for (wp = (JSWatchPoint *)rt->watchPointList.next;
689          &wp->links != &rt->watchPointList;
690          wp = (JSWatchPoint *)wp->links.next) {
691         if (wp->object == obj && wp->shape->id == id)
692             return wp;
693     }
694     return NULL;
695 }
696
697 static JSWatchPoint *
698 FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
699 {
700     JSWatchPoint *wp;
701
702     DBG_LOCK(rt);
703     wp = LockedFindWatchPoint(rt, obj, id);
704     DBG_UNLOCK(rt);
705     return wp;
706 }
707
708 JSBool
709 js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
710 {
711     JSRuntime *rt = cx->runtime;
712     DBG_LOCK(rt);
713     for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
714          &wp->links != &rt->watchPointList;
715          wp = (JSWatchPoint *)wp->links.next) {
716         const Shape *shape = wp->shape;
717         if (wp->object == obj && SHAPE_USERID(shape) == id && !(wp->flags & JSWP_HELD)) {
718             wp->flags |= JSWP_HELD;
719             DBG_UNLOCK(rt);
720
721             jsid propid = shape->id;
722             shape = obj->nativeLookup(propid);
723             JS_ASSERT(IsWatchedProperty(cx, shape));
724             jsid userid = SHAPE_USERID(shape);
725
726             /* Determine the property's old value. */
727             bool ok;
728             uint32 slot = shape->slot;
729             Value old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
730             const Shape *needMethodSlotWrite = NULL;
731             if (shape->isMethod()) {
732                 /*
733                  * We get here in two cases: (1) the existing watched property
734                  * is a method; or (2) the watched property was deleted and is
735                  * now in the middle of being re-added via JSOP_SETMETHOD. In
736                  * both cases we must trip the method read barrier in order to
737                  * avoid passing an uncloned function object to the handler.
738                  *
739                  * Case 2 is especially hairy. js_watch_set, uniquely, gets
740                  * called in the middle of creating a method property, after
741                  * shape is in obj but before the slot has been set. So in this
742                  * case we must finish initializing the half-finished method
743                  * property before triggering the method read barrier.
744                  *
745                  * Bonus weirdness: because this changes obj's shape,
746                  * js_NativeSet (which is our caller) will not write to the
747                  * slot, as it will appear the property was deleted and a new
748                  * property added. We must write the slot ourselves -- however
749                  * we must do it after calling the watchpoint handler. So set
750                  * needMethodSlotWrite here and use it to write to the slot
751                  * below, if the handler does not tinker with the property
752                  * further.
753                  */
754                 JS_ASSERT(!wp->setter);
755                 Value method = ObjectValue(shape->methodObject());
756                 if (old.isUndefined())
757                     obj->nativeSetSlot(slot, method);
758                 ok = obj->methodReadBarrier(cx, *shape, &method);
759                 if (!ok)
760                     goto out;
761                 wp->shape = shape = needMethodSlotWrite = obj->nativeLookup(propid);
762                 JS_ASSERT(shape->isDataDescriptor());
763                 JS_ASSERT(!shape->isMethod());
764                 if (old.isUndefined())
765                     obj->nativeSetSlot(shape->slot, old);
766                 else
767                     old = method;
768             }
769
770             {
771                 Conditionally<AutoShapeRooter> tvr(needMethodSlotWrite, cx, needMethodSlotWrite);
772
773                 /*
774                  * Call the handler. This invalidates shape, so re-lookup the shape.
775                  * NB: wp is held, so we can safely dereference it still.
776                  */
777                 ok = wp->handler(cx, obj, propid, Jsvalify(old), Jsvalify(vp), wp->closure);
778                 if (!ok)
779                     goto out;
780                 shape = obj->nativeLookup(propid);
781
782                 if (!shape) {
783                     ok = true;
784                 } else if (wp->setter) {
785                     /*
786                      * Pass the output of the handler to the setter. Security wrappers
787                      * prevent any funny business between watchpoints and setters.
788                      */
789                     ok = shape->hasSetterValue()
790                          ? ExternalInvoke(cx, ObjectValue(*obj),
791                                           ObjectValue(*CastAsObject(wp->setter)),
792                                           1, vp, vp)
793                          : CallJSPropertyOpSetter(cx, wp->setter, obj, userid, strict, vp);
794                 } else if (shape == needMethodSlotWrite) {
795                     /* See comment above about needMethodSlotWrite. */
796                     obj->nativeSetSlot(shape->slot, *vp);
797                     ok = true;
798                 } else {
799                     /*
800                      * A property with the default setter might be either a method
801                      * or an ordinary function-valued data property subject to the
802                      * method write barrier.
803                      *
804                      * It is not the setter's job to call methodWriteBarrier,
805                      * but js_watch_set must do so, because the caller will be
806                      * fooled into not doing it: shape does *not* have the
807                      * default setter and therefore seems not to be a method.
808                      */
809                     ok = obj->methodWriteBarrier(cx, *shape, *vp) != NULL;
810                 }
811             }
812
813         out:
814             DBG_LOCK(rt);
815             return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
816         }
817     }
818     DBG_UNLOCK(rt);
819     return true;
820 }
821
822 static JSBool
823 js_watch_set_wrapper(JSContext *cx, uintN argc, Value *vp)
824 {
825     JSObject *obj = ToObject(cx, &vp[1]);
826     if (!obj)
827         return false;
828
829     JSObject &funobj = JS_CALLEE(cx, vp).toObject();
830     JSFunction *wrapper = funobj.getFunctionPrivate();
831     jsid userid = ATOM_TO_JSID(wrapper->atom);
832
833     JS_SET_RVAL(cx, vp, argc ? JS_ARGV(cx, vp)[0] : UndefinedValue());
834     /*
835      * The strictness we pass here doesn't matter, since we know that it's
836      * a JS setter, which can't depend on the assigning code's strictness.
837      */
838     return js_watch_set(cx, obj, userid, false, vp);
839 }
840
841 namespace js {
842
843 bool
844 IsWatchedProperty(JSContext *cx, const Shape *shape)
845 {
846     if (shape->hasSetterValue()) {
847         JSObject *funobj = shape->setterObject();
848         if (!funobj || !funobj->isFunction())
849             return false;
850
851         JSFunction *fun = funobj->getFunctionPrivate();
852         return fun->maybeNative() == js_watch_set_wrapper;
853     }
854     return shape->setterOp() == js_watch_set;
855 }
856
857 }
858
859 /*
860  * Return an appropriate setter to substitute for |setter| on a property
861  * with attributes |attrs|, to implement a watchpoint on the property named
862  * |id|.
863  */
864 static StrictPropertyOp
865 WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, StrictPropertyOp setter)
866 {
867     JSAtom *atom;
868     JSFunction *wrapper;
869
870     /* Wrap a C++ setter simply by returning our own C++ setter. */
871     if (!(attrs & JSPROP_SETTER))
872         return &js_watch_set;   /* & to silence schoolmarmish MSVC */
873
874     /*
875      * Wrap a JSObject * setter by constructing our own JSFunction * that saves the
876      * property id as the function name, and calls js_watch_set.
877      */
878     if (JSID_IS_ATOM(id)) {
879         atom = JSID_TO_ATOM(id);
880     } else if (JSID_IS_INT(id)) {
881         if (!js_ValueToStringId(cx, IdToValue(id), &id))
882             return NULL;
883         atom = JSID_TO_ATOM(id);
884     } else {
885         atom = NULL;
886     }
887
888     wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
889                              setter ? CastAsObject(setter)->getParent() : NULL, atom);
890     if (!wrapper)
891         return NULL;
892     return CastAsStrictPropertyOp(FUN_OBJECT(wrapper));
893 }
894
895 static const Shape *
896 UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
897 {
898     JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id);
899     JS_ASSERT(!IsWatchedProperty(cx, newShape));
900
901     /* Create a watching setter we can substitute for the new shape's setter. */
902     StrictPropertyOp watchingSetter =
903         WrapWatchedSetter(cx, newShape->id, newShape->attributes(), newShape->setter());
904     if (!watchingSetter)
905         return NULL;
906
907     /*
908      * Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will
909      * return a new shape, or mutate this one.
910      */
911     StrictPropertyOp originalSetter = newShape->setter();
912
913     /*
914      * Drop the watching setter into the object, in place of newShape. Note that a single
915      * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape: we
916      * wrap all (JSPropertyOp, not JSObject *) setters with js_watch_set, so shapes that
917      * differ only in their setter may all get wrapped to the same shape.
918      */
919     const Shape *watchingShape = 
920         js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(),
921                                      newShape->getter(), watchingSetter);
922     if (!watchingShape)
923         return NULL;
924
925     /* Update the watchpoint with the new shape and its original setter. */
926     wp->setter = originalSetter;
927     wp->shape = watchingShape;
928
929     return watchingShape;
930 }
931
932 const Shape *
933 js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const Shape *newShape)
934 {
935     /*
936      * The watchpoint code uses the normal property-modification functions to install its
937      * own watchpoint-aware shapes. Those functions report those changes back to the
938      * watchpoint code, just as they do user-level changes. So if this change is
939      * installing a watchpoint-aware shape, it's something we asked for ourselves, and can
940      * proceed without interference.
941      */
942     if (IsWatchedProperty(cx, newShape))
943         return newShape;
944
945     JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id);
946     if (!wp)
947         return newShape;
948
949     return UpdateWatchpointShape(cx, wp, newShape);
950 }
951
952 /*
953  * Return the underlying setter for |shape| on |obj|, seeing through any
954  * watchpoint-wrapping. Note that we need |obj| to disambiguate, since a single
955  * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape; see the
956  * comments in UpdateWatchpointShape.
957  */
958 static StrictPropertyOp
959 UnwrapSetter(JSContext *cx, JSObject *obj, const Shape *shape)
960 {
961     /* If it's not a watched property, its setter is not wrapped. */
962     if (!IsWatchedProperty(cx, shape))
963         return shape->setter();
964
965     /* Look up the watchpoint, from which we can retrieve the underlying setter. */
966     JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, shape->id);
967
968     /* 
969      * Since we know |shape| is watched, we *must* find a watchpoint: we should never
970      * leave wrapped setters lying around in shapes after removing a watchpoint.
971      */
972     JS_ASSERT(wp);
973
974     return wp->setter;
975 }
976
977 JS_PUBLIC_API(JSBool)
978 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
979                  JSWatchPointHandler handler, JSObject *closure)
980 {
981     JSObject *origobj;
982     Value v;
983     uintN attrs;
984     jsid propid;
985
986     origobj = obj;
987     OBJ_TO_INNER_OBJECT(cx, obj);
988     if (!obj)
989         return JS_FALSE;
990
991     AutoValueRooter idroot(cx);
992     if (JSID_IS_INT(id)) {
993         propid = id;
994     } else {
995         if (!js_ValueToStringId(cx, IdToValue(id), &propid))
996             return JS_FALSE;
997         propid = js_CheckForStringIndex(propid);
998         idroot.set(IdToValue(propid));
999     }
1000
1001     /*
1002      * If, by unwrapping and innerizing, we changed the object, check
1003      * again to make sure that we're allowed to set a watch point.
1004      */
1005     if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
1006         return JS_FALSE;
1007
1008     if (!obj->isNative()) {
1009         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
1010                              obj->getClass()->name);
1011         return JS_FALSE;
1012     }
1013
1014     JSObject *pobj;
1015     JSProperty *prop;
1016     if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
1017         return JS_FALSE;
1018     const Shape *shape = (Shape *) prop;
1019     JSRuntime *rt = cx->runtime;
1020     if (!shape) {
1021         /* Check for a deleted symbol watchpoint, which holds its property. */
1022         JSWatchPoint *wp = FindWatchPoint(rt, obj, propid);
1023         if (!wp) {
1024             /* Make a new property in obj so we can watch for the first set. */
1025             if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
1026                                          JSPROP_ENUMERATE, 0, 0, &prop)) {
1027                 return JS_FALSE;
1028             }
1029             shape = (Shape *) prop;
1030         }
1031     } else if (pobj != obj) {
1032         /* Clone the prototype property so we can watch the right object. */
1033         AutoValueRooter valroot(cx);
1034         PropertyOp getter;
1035         StrictPropertyOp setter;
1036         uintN attrs, flags;
1037         intN shortid;
1038
1039         if (pobj->isNative()) {
1040             valroot.set(pobj->containsSlot(shape->slot)
1041                         ? pobj->nativeGetSlot(shape->slot)
1042                         : UndefinedValue());
1043             getter = shape->getter();
1044             setter = UnwrapSetter(cx, pobj, shape);
1045             attrs = shape->attributes();
1046             flags = shape->getFlags();
1047             shortid = shape->shortid;
1048         } else {
1049             if (!pobj->getProperty(cx, propid, valroot.addr()) ||
1050                 !pobj->getAttributes(cx, propid, &attrs)) {
1051                 return JS_FALSE;
1052             }
1053             getter = NULL;
1054             setter = NULL;
1055             flags = 0;
1056             shortid = 0;
1057         }
1058
1059         /* Recall that obj is native, whether or not pobj is native. */
1060         if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
1061                                      getter, setter, attrs, flags,
1062                                      shortid, &prop)) {
1063             return JS_FALSE;
1064         }
1065         shape = (Shape *) prop;
1066     }
1067
1068     /*
1069      * At this point, prop/shape exists in obj, obj is locked, and we must
1070      * unlock the object before returning.
1071      */
1072     DBG_LOCK(rt);
1073     JSWatchPoint *wp = LockedFindWatchPoint(rt, obj, propid);
1074     if (!wp) {
1075         DBG_UNLOCK(rt);
1076         wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
1077         if (!wp)
1078             return JS_FALSE;
1079         wp->handler = NULL;
1080         wp->closure = NULL;
1081         wp->object = obj;
1082         wp->shape = NULL;
1083         wp->flags = JSWP_LIVE;
1084
1085         /* XXXbe nest in obj lock here */
1086         if (!UpdateWatchpointShape(cx, wp, shape)) {
1087             /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
1088             JS_INIT_CLIST(&wp->links);
1089             DBG_LOCK(rt);
1090             DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1091             return JS_FALSE;
1092         }
1093
1094         /*
1095          * Now that wp is fully initialized, append it to rt's wp list.
1096          * Because obj is locked we know that no other thread could have added
1097          * a watchpoint for (obj, propid).
1098          */
1099         DBG_LOCK(rt);
1100         JS_ASSERT(!LockedFindWatchPoint(rt, obj, propid));
1101         JS_APPEND_LINK(&wp->links, &rt->watchPointList);
1102         ++rt->debuggerMutations;
1103     }
1104
1105     /*
1106      * Ensure that an object with watchpoints never has the same shape as an
1107      * object without them, even if the watched properties are deleted.
1108      */
1109     obj->watchpointOwnShapeChange(cx);
1110
1111     wp->handler = handler;
1112     wp->closure = reinterpret_cast<JSObject*>(closure);
1113     DBG_UNLOCK(rt);
1114     return JS_TRUE;
1115 }
1116
1117 JS_PUBLIC_API(JSBool)
1118 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
1119                    JSWatchPointHandler *handlerp, JSObject **closurep)
1120 {
1121     JSRuntime *rt;
1122     JSWatchPoint *wp;
1123
1124     rt = cx->runtime;
1125     DBG_LOCK(rt);
1126     for (wp = (JSWatchPoint *)rt->watchPointList.next;
1127          &wp->links != &rt->watchPointList;
1128          wp = (JSWatchPoint *)wp->links.next) {
1129         if (wp->object == obj && SHAPE_USERID(wp->shape) == id) {
1130             if (handlerp)
1131                 *handlerp = wp->handler;
1132             if (closurep)
1133                 *closurep = wp->closure;
1134             return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1135         }
1136     }
1137     DBG_UNLOCK(rt);
1138     if (handlerp)
1139         *handlerp = NULL;
1140     if (closurep)
1141         *closurep = NULL;
1142     return JS_TRUE;
1143 }
1144
1145 JS_PUBLIC_API(JSBool)
1146 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
1147 {
1148     JSRuntime *rt;
1149     JSWatchPoint *wp, *next;
1150     uint32 sample;
1151
1152     rt = cx->runtime;
1153     DBG_LOCK(rt);
1154     for (wp = (JSWatchPoint *)rt->watchPointList.next;
1155          &wp->links != &rt->watchPointList;
1156          wp = next) {
1157         next = (JSWatchPoint *)wp->links.next;
1158         if (wp->object == obj) {
1159             sample = rt->debuggerMutations;
1160             if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1161                 return JS_FALSE;
1162             DBG_LOCK(rt);
1163             if (rt->debuggerMutations != sample + 1)
1164                 next = (JSWatchPoint *)rt->watchPointList.next;
1165         }
1166     }
1167     DBG_UNLOCK(rt);
1168     return JS_TRUE;
1169 }
1170
1171 JS_PUBLIC_API(JSBool)
1172 JS_ClearAllWatchPoints(JSContext *cx)
1173 {
1174     JSRuntime *rt;
1175     JSWatchPoint *wp, *next;
1176     uint32 sample;
1177
1178     rt = cx->runtime;
1179     DBG_LOCK(rt);
1180     for (wp = (JSWatchPoint *)rt->watchPointList.next;
1181          &wp->links != &rt->watchPointList;
1182          wp = next) {
1183         next = (JSWatchPoint *)wp->links.next;
1184         sample = rt->debuggerMutations;
1185         if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1186             return JS_FALSE;
1187         DBG_LOCK(rt);
1188         if (rt->debuggerMutations != sample + 1)
1189             next = (JSWatchPoint *)rt->watchPointList.next;
1190     }
1191     DBG_UNLOCK(rt);
1192     return JS_TRUE;
1193 }
1194
1195 /************************************************************************/
1196
1197 JS_PUBLIC_API(uintN)
1198 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1199 {
1200     return js_PCToLineNumber(cx, script, pc);
1201 }
1202
1203 JS_PUBLIC_API(jsbytecode *)
1204 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1205 {
1206     return js_LineNumberToPC(script, lineno);
1207 }
1208
1209 JS_PUBLIC_API(jsbytecode *)
1210 JS_EndPC(JSContext *cx, JSScript *script)
1211 {
1212     return script->code + script->length;
1213 }
1214
1215 JS_PUBLIC_API(uintN)
1216 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
1217 {
1218     return fun->nargs;
1219 }
1220
1221 JS_PUBLIC_API(JSBool)
1222 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
1223 {
1224     return fun->script()->bindings.hasLocalNames();
1225 }
1226
1227 extern JS_PUBLIC_API(jsuword *)
1228 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
1229 {
1230     *markp = JS_ARENA_MARK(&cx->tempPool);
1231     return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
1232 }
1233
1234 extern JS_PUBLIC_API(JSAtom *)
1235 JS_LocalNameToAtom(jsuword w)
1236 {
1237     return JS_LOCAL_NAME_TO_ATOM(w);
1238 }
1239
1240 extern JS_PUBLIC_API(JSString *)
1241 JS_AtomKey(JSAtom *atom)
1242 {
1243     return ATOM_TO_STRING(atom);
1244 }
1245
1246 extern JS_PUBLIC_API(void)
1247 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
1248 {
1249     JS_ARENA_RELEASE(&cx->tempPool, mark);
1250 }
1251
1252 JS_PUBLIC_API(JSScript *)
1253 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1254 {
1255     return FUN_SCRIPT(fun);
1256 }
1257
1258 JS_PUBLIC_API(JSNative)
1259 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1260 {
1261     return Jsvalify(fun->maybeNative());
1262 }
1263
1264 JS_PUBLIC_API(JSPrincipals *)
1265 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1266 {
1267     return script->principals;
1268 }
1269
1270 /************************************************************************/
1271
1272 /*
1273  *  Stack Frame Iterator
1274  */
1275 JS_PUBLIC_API(JSStackFrame *)
1276 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1277 {
1278     *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->prev();
1279     return *iteratorp;
1280 }
1281
1282 JS_PUBLIC_API(JSScript *)
1283 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1284 {
1285     return fp->maybeScript();
1286 }
1287
1288 JS_PUBLIC_API(jsbytecode *)
1289 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1290 {
1291     return fp->pc(cx);
1292 }
1293
1294 JS_PUBLIC_API(JSStackFrame *)
1295 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1296 {
1297     return js_GetScriptedCaller(cx, fp);
1298 }
1299
1300 JSPrincipals *
1301 js_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1302 {
1303     JSSecurityCallbacks *callbacks;
1304
1305     if (fp->isFunctionFrame()) {
1306         callbacks = JS_GetSecurityCallbacks(cx);
1307         if (callbacks && callbacks->findObjectPrincipals) {
1308             if (&fp->fun()->compiledFunObj() != &fp->callee())
1309                 return callbacks->findObjectPrincipals(cx, &fp->callee());
1310             /* FALL THROUGH */
1311         }
1312     }
1313     if (fp->isScriptFrame())
1314         return fp->script()->principals;
1315     return NULL;
1316 }
1317
1318 JSPrincipals *
1319 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1320 {
1321     JSPrincipals *principals, *callerPrincipals;
1322     JSSecurityCallbacks *callbacks;
1323
1324     callbacks = JS_GetSecurityCallbacks(cx);
1325     if (callbacks && callbacks->findObjectPrincipals)
1326         principals = callbacks->findObjectPrincipals(cx, callee);
1327     else
1328         principals = NULL;
1329     if (!caller)
1330         return principals;
1331     callerPrincipals = js_StackFramePrincipals(cx, caller);
1332     return (callerPrincipals && principals &&
1333             callerPrincipals->subsume(callerPrincipals, principals))
1334            ? principals
1335            : callerPrincipals;
1336 }
1337
1338 JS_PUBLIC_API(void *)
1339 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1340 {
1341     if (fp->annotation() && fp->isScriptFrame()) {
1342         JSPrincipals *principals = js_StackFramePrincipals(cx, fp);
1343
1344         if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1345             /*
1346              * Give out an annotation only if privileges have not been revoked
1347              * or disabled globally.
1348              */
1349             return fp->annotation();
1350         }
1351     }
1352
1353     return NULL;
1354 }
1355
1356 JS_PUBLIC_API(void)
1357 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1358 {
1359     fp->setAnnotation(annotation);
1360 }
1361
1362 JS_PUBLIC_API(void *)
1363 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1364 {
1365     JSPrincipals *principals;
1366
1367     principals = js_StackFramePrincipals(cx, fp);
1368     if (!principals)
1369         return NULL;
1370     return principals->getPrincipalArray(cx, principals);
1371 }
1372
1373 JS_PUBLIC_API(JSBool)
1374 JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp)
1375 {
1376     return !fp->isDummyFrame();
1377 }
1378
1379 /* this is deprecated, use JS_GetFrameScopeChain instead */
1380 JS_PUBLIC_API(JSObject *)
1381 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1382 {
1383     return &fp->scopeChain();
1384 }
1385
1386 JS_PUBLIC_API(JSObject *)
1387 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1388 {
1389     JS_ASSERT(cx->stack().contains(fp));
1390
1391     js::AutoCompartment ac(cx, &fp->scopeChain());
1392     if (!ac.enter())
1393         return NULL;
1394
1395     /* Force creation of argument and call objects if not yet created */
1396     (void) JS_GetFrameCallObject(cx, fp);
1397     return GetScopeChain(cx, fp);
1398 }
1399
1400 JS_PUBLIC_API(JSObject *)
1401 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1402 {
1403     JS_ASSERT(cx->stack().contains(fp));
1404
1405     if (!fp->isFunctionFrame())
1406         return NULL;
1407
1408     js::AutoCompartment ac(cx, &fp->scopeChain());
1409     if (!ac.enter())
1410         return NULL;
1411
1412     /* Force creation of argument object if not yet created */
1413     (void) js_GetArgsObject(cx, fp);
1414
1415     /*
1416      * XXX ill-defined: null return here means error was reported, unlike a
1417      *     null returned above or in the #else
1418      */
1419     return js_GetCallObject(cx, fp);
1420 }
1421
1422 JS_PUBLIC_API(JSBool)
1423 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv)
1424 {
1425     if (fp->isDummyFrame())
1426         return false;
1427
1428     js::AutoCompartment ac(cx, &fp->scopeChain());
1429     if (!ac.enter())
1430         return false;
1431
1432     if (!fp->computeThis(cx))
1433         return false;
1434     *thisv = Jsvalify(fp->thisValue());
1435     return true;
1436 }
1437
1438 JS_PUBLIC_API(JSFunction *)
1439 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1440 {
1441     return fp->maybeFun();
1442 }
1443
1444 JS_PUBLIC_API(JSObject *)
1445 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1446 {
1447     if (!fp->isFunctionFrame())
1448         return NULL;
1449
1450     JS_ASSERT(fp->callee().isFunction());
1451     JS_ASSERT(fp->callee().getPrivate() == fp->fun());
1452     return &fp->callee();
1453 }
1454
1455 JS_PUBLIC_API(JSBool)
1456 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1457 {
1458     return fp->isConstructing();
1459 }
1460
1461 JS_PUBLIC_API(JSObject *)
1462 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1463 {
1464     return fp->maybeCallee();
1465 }
1466
1467 JS_PUBLIC_API(JSBool)
1468 JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
1469 {
1470     Value v;
1471
1472     if (!fp->getValidCalleeObject(cx, &v))
1473         return false;
1474     *vp = Jsvalify(v);
1475     return true;
1476 }
1477
1478 JS_PUBLIC_API(JSBool)
1479 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1480 {
1481     return fp->isDebuggerFrame();
1482 }
1483
1484 JS_PUBLIC_API(jsval)
1485 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1486 {
1487     return Jsvalify(fp->returnValue());
1488 }
1489
1490 JS_PUBLIC_API(void)
1491 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1492 {
1493 #ifdef JS_METHODJIT
1494     JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode);
1495 #endif
1496     assertSameCompartment(cx, fp, rval);
1497     fp->setReturnValue(Valueify(rval));
1498 }
1499
1500 /************************************************************************/
1501
1502 JS_PUBLIC_API(const char *)
1503 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1504 {
1505     return script->filename;
1506 }
1507
1508 JS_PUBLIC_API(uintN)
1509 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1510 {
1511     return script->lineno;
1512 }
1513
1514 JS_PUBLIC_API(uintN)
1515 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1516 {
1517     return js_GetScriptLineExtent(script);
1518 }
1519
1520 JS_PUBLIC_API(JSVersion)
1521 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1522 {
1523     return VersionNumber(script->getVersion());
1524 }
1525
1526 /***************************************************************************/
1527
1528 JS_PUBLIC_API(void)
1529 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1530 {
1531     rt->globalDebugHooks.newScriptHook = hook;
1532     rt->globalDebugHooks.newScriptHookData = callerdata;
1533 }
1534
1535 JS_PUBLIC_API(void)
1536 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1537                         void *callerdata)
1538 {
1539     rt->globalDebugHooks.destroyScriptHook = hook;
1540     rt->globalDebugHooks.destroyScriptHookData = callerdata;
1541 }
1542
1543 /***************************************************************************/
1544
1545 JS_PUBLIC_API(JSBool)
1546 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1547                           const jschar *chars, uintN length,
1548                           const char *filename, uintN lineno,
1549                           jsval *rval)
1550 {
1551     JS_ASSERT_NOT_ON_TRACE(cx);
1552
1553     if (!CheckDebugMode(cx))
1554         return false;
1555
1556     JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1557     if (!scobj)
1558         return false;
1559
1560     js::AutoCompartment ac(cx, scobj);
1561     if (!ac.enter())
1562         return false;
1563
1564     /*
1565      * NB: This function breaks the assumption that the compiler can see all
1566      * calls and properly compute a static level. In order to get around this,
1567      * we use a static level that will cause us not to attempt to optimize
1568      * variable references made by this frame.
1569      */
1570     JSScript *script = Compiler::compileScript(cx, scobj, fp, js_StackFramePrincipals(cx, fp),
1571                                                TCF_COMPILE_N_GO, chars, length,
1572                                                filename, lineno, cx->findVersion(),
1573                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
1574
1575     if (!script)
1576         return false;
1577
1578     bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1579
1580     js_DestroyScript(cx, script);
1581     return ok;
1582 }
1583
1584 JS_PUBLIC_API(JSBool)
1585 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1586                         const char *bytes, uintN length,
1587                         const char *filename, uintN lineno,
1588                         jsval *rval)
1589 {
1590     jschar *chars;
1591     JSBool ok;
1592     size_t len = length;
1593     
1594     if (!CheckDebugMode(cx))
1595         return JS_FALSE;
1596
1597     chars = js_InflateString(cx, bytes, &len);
1598     if (!chars)
1599         return JS_FALSE;
1600     length = (uintN) len;
1601     ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1602                                    rval);
1603     cx->free(chars);
1604
1605     return ok;
1606 }
1607
1608 /************************************************************************/
1609
1610 /* This all should be reworked to avoid requiring JSScopeProperty types. */
1611
1612 JS_PUBLIC_API(JSScopeProperty *)
1613 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1614 {
1615     const Shape *shape;
1616
1617     /* The caller passes null in *iteratorp to get things started. */
1618     shape = (Shape *) *iteratorp;
1619     if (!shape) {
1620         shape = obj->lastProperty();
1621     } else {
1622         shape = shape->previous();
1623         if (!shape->previous()) {
1624             JS_ASSERT(JSID_IS_EMPTY(shape->id));
1625             shape = NULL;
1626         }
1627     }
1628
1629     return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape));
1630 }
1631
1632 JS_PUBLIC_API(JSBool)
1633 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1634                    JSPropertyDesc *pd)
1635 {
1636     assertSameCompartment(cx, obj);
1637     Shape *shape = (Shape *) sprop;
1638     pd->id = IdToJsval(shape->id);
1639
1640     JSBool wasThrowing = cx->isExceptionPending();
1641     Value lastException = UndefinedValue();
1642     if (wasThrowing)
1643         lastException = cx->getPendingException();
1644     cx->clearPendingException();
1645
1646     if (!js_GetProperty(cx, obj, shape->id, Valueify(&pd->value))) {
1647         if (!cx->isExceptionPending()) {
1648             pd->flags = JSPD_ERROR;
1649             pd->value = JSVAL_VOID;
1650         } else {
1651             pd->flags = JSPD_EXCEPTION;
1652             pd->value = Jsvalify(cx->getPendingException());
1653         }
1654     } else {
1655         pd->flags = 0;
1656     }
1657
1658     if (wasThrowing)
1659         cx->setPendingException(lastException);
1660
1661     pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
1662               |  (!shape->writable()  ? JSPD_READONLY  : 0)
1663               |  (!shape->configurable() ? JSPD_PERMANENT : 0);
1664     pd->spare = 0;
1665     if (shape->getter() == GetCallArg) {
1666         pd->slot = shape->shortid;
1667         pd->flags |= JSPD_ARGUMENT;
1668     } else if (shape->getter() == GetCallVar) {
1669         pd->slot = shape->shortid;
1670         pd->flags |= JSPD_VARIABLE;
1671     } else {
1672         pd->slot = 0;
1673     }
1674     pd->alias = JSVAL_VOID;
1675
1676     if (obj->containsSlot(shape->slot)) {
1677         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1678             const Shape &aprop = r.front();
1679             if (&aprop != shape && aprop.slot == shape->slot) {
1680                 pd->alias = IdToJsval(aprop.id);
1681                 break;
1682             }
1683         }
1684     }
1685     return JS_TRUE;
1686 }
1687
1688 JS_PUBLIC_API(JSBool)
1689 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1690 {
1691     assertSameCompartment(cx, obj);
1692     Class *clasp = obj->getClass();
1693     if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1694         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1695                              JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1696         return JS_FALSE;
1697     }
1698     if (!clasp->enumerate(cx, obj))
1699         return JS_FALSE;
1700
1701     /* Return an empty pda early if obj has no own properties. */
1702     if (obj->nativeEmpty()) {
1703         pda->length = 0;
1704         pda->array = NULL;
1705         return JS_TRUE;
1706     }
1707
1708     uint32 n = obj->propertyCount();
1709     JSPropertyDesc *pd = (JSPropertyDesc *) cx->malloc(size_t(n) * sizeof(JSPropertyDesc));
1710     if (!pd)
1711         return JS_FALSE;
1712     uint32 i = 0;
1713     for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1714         if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1715             goto bad;
1716         if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1717             goto bad;
1718         Shape *shape = const_cast<Shape *>(&r.front());
1719         if (!JS_GetPropertyDesc(cx, obj, reinterpret_cast<JSScopeProperty *>(shape), &pd[i]))
1720             goto bad;
1721         if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1722             goto bad;
1723         if (++i == n)
1724             break;
1725     }
1726     pda->length = i;
1727     pda->array = pd;
1728     return JS_TRUE;
1729
1730 bad:
1731     pda->length = i + 1;
1732     pda->array = pd;
1733     JS_PutPropertyDescArray(cx, pda);
1734     return JS_FALSE;
1735 }
1736
1737 JS_PUBLIC_API(void)
1738 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1739 {
1740     JSPropertyDesc *pd;
1741     uint32 i;
1742
1743     pd = pda->array;
1744     for (i = 0; i < pda->length; i++) {
1745         js_RemoveRoot(cx->runtime, &pd[i].id);
1746         js_RemoveRoot(cx->runtime, &pd[i].value);
1747         if (pd[i].flags & JSPD_ALIAS)
1748             js_RemoveRoot(cx->runtime, &pd[i].alias);
1749     }
1750     cx->free(pd);
1751 }
1752
1753 /************************************************************************/
1754
1755 JS_PUBLIC_API(JSBool)
1756 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1757 {
1758     rt->globalDebugHooks.debuggerHandler = handler;
1759     rt->globalDebugHooks.debuggerHandlerData = closure;
1760     return JS_TRUE;
1761 }
1762
1763 JS_PUBLIC_API(JSBool)
1764 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1765 {
1766     rt->globalDebugHooks.sourceHandler = handler;
1767     rt->globalDebugHooks.sourceHandlerData = closure;
1768     return JS_TRUE;
1769 }
1770
1771 JS_PUBLIC_API(JSBool)
1772 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1773 {
1774     rt->globalDebugHooks.executeHook = hook;
1775     rt->globalDebugHooks.executeHookData = closure;
1776     return JS_TRUE;
1777 }
1778
1779 JS_PUBLIC_API(JSBool)
1780 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1781 {
1782 #ifdef JS_TRACER
1783     {
1784         AutoLockGC lock(rt);
1785         bool wasInhibited = rt->debuggerInhibitsJIT();
1786 #endif
1787         rt->globalDebugHooks.callHook = hook;
1788         rt->globalDebugHooks.callHookData = closure;
1789 #ifdef JS_TRACER
1790         JITInhibitingHookChange(rt, wasInhibited);
1791     }
1792 #endif
1793     return JS_TRUE;
1794 }
1795
1796 JS_PUBLIC_API(JSBool)
1797 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1798 {
1799     rt->globalDebugHooks.throwHook = hook;
1800     rt->globalDebugHooks.throwHookData = closure;
1801     return JS_TRUE;
1802 }
1803
1804 JS_PUBLIC_API(JSBool)
1805 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1806 {
1807     rt->globalDebugHooks.debugErrorHook = hook;
1808     rt->globalDebugHooks.debugErrorHookData = closure;
1809     return JS_TRUE;
1810 }
1811
1812 /************************************************************************/
1813
1814 JS_PUBLIC_API(size_t)
1815 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1816 {
1817     return obj->slotsAndStructSize();
1818 }
1819
1820 static size_t
1821 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1822 {
1823     size_t nbytes;
1824
1825     nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1826     nbytes += sizeof(JSString);
1827     nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1828     return nbytes;
1829 }
1830
1831 JS_PUBLIC_API(size_t)
1832 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1833 {
1834     size_t nbytes;
1835
1836     nbytes = sizeof *fun;
1837     nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1838     if (FUN_INTERPRETED(fun))
1839         nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1840     if (fun->atom)
1841         nbytes += GetAtomTotalSize(cx, fun->atom);
1842     return nbytes;
1843 }
1844
1845 #include "jsemit.h"
1846
1847 JS_PUBLIC_API(size_t)
1848 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1849 {
1850     size_t nbytes, pbytes;
1851     jsatomid i;
1852     jssrcnote *sn, *notes;
1853     JSObjectArray *objarray;
1854     JSPrincipals *principals;
1855
1856     nbytes = sizeof *script;
1857     if (script->u.object)
1858         nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1859
1860     nbytes += script->length * sizeof script->code[0];
1861     nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1862     for (i = 0; i < script->atomMap.length; i++)
1863         nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1864
1865     if (script->filename)
1866         nbytes += strlen(script->filename) + 1;
1867
1868     notes = script->notes();
1869     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1870         continue;
1871     nbytes += (sn - notes + 1) * sizeof *sn;
1872
1873     if (JSScript::isValidOffset(script->objectsOffset)) {
1874         objarray = script->objects();
1875         i = objarray->length;
1876         nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1877         do {
1878             nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1879         } while (i != 0);
1880     }
1881
1882     if (JSScript::isValidOffset(script->regexpsOffset)) {
1883         objarray = script->regexps();
1884         i = objarray->length;
1885         nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1886         do {
1887             nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1888         } while (i != 0);
1889     }
1890
1891     if (JSScript::isValidOffset(script->trynotesOffset)) {
1892         nbytes += sizeof(JSTryNoteArray) +
1893             script->trynotes()->length * sizeof(JSTryNote);
1894     }
1895
1896     principals = script->principals;
1897     if (principals) {
1898         JS_ASSERT(principals->refcount);
1899         pbytes = sizeof *principals;
1900         if (principals->refcount > 1)
1901             pbytes = JS_HOWMANY(pbytes, principals->refcount);
1902         nbytes += pbytes;
1903     }
1904
1905     return nbytes;
1906 }
1907
1908 JS_PUBLIC_API(uint32)
1909 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1910 {
1911     if (!fp)
1912         fp = js_GetTopStackFrame(cx);
1913     while (fp) {
1914         if (fp->isScriptFrame())
1915             return JS_GetScriptFilenameFlags(fp->script());
1916         fp = fp->prev();
1917     }
1918     return 0;
1919  }
1920
1921 JS_PUBLIC_API(uint32)
1922 JS_GetScriptFilenameFlags(JSScript *script)
1923 {
1924     JS_ASSERT(script);
1925     if (!script->filename)
1926         return JSFILENAME_NULL;
1927     return js_GetScriptFilenameFlags(script->filename);
1928 }
1929
1930 JS_PUBLIC_API(JSBool)
1931 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1932 {
1933     if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1934         return JS_FALSE;
1935     return JS_TRUE;
1936 }
1937
1938 JS_PUBLIC_API(JSBool)
1939 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1940 {
1941     return obj->isSystem();
1942 }
1943
1944 JS_PUBLIC_API(JSBool)
1945 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1946 {
1947     obj->setSystem();
1948     return true;
1949 }
1950
1951 /************************************************************************/
1952
1953 JS_PUBLIC_API(JSObject *)
1954 JS_UnwrapObject(JSContext *cx, JSObject *obj)
1955 {
1956     return obj->unwrap();
1957 }
1958
1959 /************************************************************************/
1960
1961 JS_FRIEND_API(void)
1962 js_RevertVersion(JSContext *cx)
1963 {
1964     cx->clearVersionOverride();
1965 }
1966
1967 JS_PUBLIC_API(const JSDebugHooks *)
1968 JS_GetGlobalDebugHooks(JSRuntime *rt)
1969 {
1970     return &rt->globalDebugHooks;
1971 }
1972
1973 const JSDebugHooks js_NullDebugHooks = {};
1974
1975 JS_PUBLIC_API(JSDebugHooks *)
1976 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1977 {
1978     JS_ASSERT(hooks);
1979     if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1980         LeaveTrace(cx);
1981
1982 #ifdef JS_TRACER
1983     AutoLockGC lock(cx->runtime);
1984 #endif
1985     JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1986     cx->debugHooks = hooks;
1987 #ifdef JS_TRACER
1988     cx->updateJITEnabled();
1989 #endif
1990     return old;
1991 }
1992
1993 JS_PUBLIC_API(JSDebugHooks *)
1994 JS_ClearContextDebugHooks(JSContext *cx)
1995 {
1996     return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1997 }
1998
1999 JS_PUBLIC_API(JSBool)
2000 JS_StartProfiling()
2001 {
2002     return Probes::startProfiling();
2003 }
2004
2005 JS_PUBLIC_API(void)
2006 JS_StopProfiling()
2007 {
2008     Probes::stopProfiling();
2009 }
2010
2011 #ifdef MOZ_PROFILING
2012
2013 static JSBool
2014 StartProfiling(JSContext *cx, uintN argc, jsval *vp)
2015 {
2016     JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling()));
2017     return true;
2018 }
2019
2020 static JSBool
2021 StopProfiling(JSContext *cx, uintN argc, jsval *vp)
2022 {
2023     JS_StopProfiling();
2024     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2025     return true;
2026 }
2027
2028 #ifdef MOZ_SHARK
2029
2030 static JSBool
2031 IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
2032 {
2033     JS_SET_RVAL(cx, vp, JSVAL_TRUE);
2034     return true;
2035 }
2036
2037 #endif
2038
2039 static JSFunctionSpec profiling_functions[] = {
2040     JS_FN("startProfiling",  StartProfiling,      0,0),
2041     JS_FN("stopProfiling",   StopProfiling,       0,0),
2042 #ifdef MOZ_SHARK
2043     /* Keep users of the old shark API happy. */
2044     JS_FN("connectShark",    IgnoreAndReturnTrue, 0,0),
2045     JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
2046     JS_FN("startShark",      StartProfiling,      0,0),
2047     JS_FN("stopShark",       StopProfiling,       0,0),
2048 #endif
2049     JS_FS_END
2050 };
2051
2052 #endif
2053
2054 JS_PUBLIC_API(JSBool)
2055 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
2056 {
2057 #ifdef MOZ_PROFILING
2058     return JS_DefineFunctions(cx, obj, profiling_functions);
2059 #else
2060     return true;
2061 #endif
2062 }
2063
2064 #ifdef MOZ_CALLGRIND
2065
2066 #include <valgrind/callgrind.h>
2067
2068 JS_FRIEND_API(JSBool)
2069 js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
2070 {
2071     CALLGRIND_START_INSTRUMENTATION;
2072     CALLGRIND_ZERO_STATS;
2073     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2074     return JS_TRUE;
2075 }
2076
2077 JS_FRIEND_API(JSBool)
2078 js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
2079 {
2080     CALLGRIND_STOP_INSTRUMENTATION;
2081     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2082     return JS_TRUE;
2083 }
2084
2085 JS_FRIEND_API(JSBool)
2086 js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
2087 {
2088     JSString *str;
2089
2090     jsval *argv = JS_ARGV(cx, vp);
2091     if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2092         str = JSVAL_TO_STRING(argv[0]);
2093         JSAutoByteString bytes(cx, str);
2094         if (!!bytes) {
2095             CALLGRIND_DUMP_STATS_AT(bytes.ptr());
2096             return JS_TRUE;
2097         }
2098     }
2099     CALLGRIND_DUMP_STATS;
2100
2101     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2102     return JS_TRUE;
2103 }
2104
2105 #endif /* MOZ_CALLGRIND */
2106
2107 #ifdef MOZ_VTUNE
2108 #include <VTuneApi.h>
2109
2110 static const char *vtuneErrorMessages[] = {
2111   "unknown, error #0",
2112   "invalid 'max samples' field",
2113   "invalid 'samples per buffer' field",
2114   "invalid 'sample interval' field",
2115   "invalid path",
2116   "sample file in use",
2117   "invalid 'number of events' field",
2118   "unknown, error #7",
2119   "internal error",
2120   "bad event name",
2121   "VTStopSampling called without calling VTStartSampling",
2122   "no events selected for event-based sampling",
2123   "events selected cannot be run together",
2124   "no sampling parameters",
2125   "sample database already exists",
2126   "sampling already started",
2127   "time-based sampling not supported",
2128   "invalid 'sampling parameters size' field",
2129   "invalid 'event size' field",
2130   "sampling file already bound",
2131   "invalid event path",
2132   "invalid license",
2133   "invalid 'global options' field",
2134
2135 };
2136
2137 JS_FRIEND_API(JSBool)
2138 js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
2139 {
2140     VTUNE_EVENT events[] = {
2141         { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2142         { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2143     };
2144
2145     U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2146     char *default_filename = "mozilla-vtune.tb5";
2147     JSString *str;
2148     U32 status;
2149
2150     VTUNE_SAMPLING_PARAMS params = {
2151         sizeof(VTUNE_SAMPLING_PARAMS),
2152         sizeof(VTUNE_EVENT),
2153         0, 0, /* Reserved fields */
2154         1,    /* Initialize in "paused" state */
2155         0,    /* Max samples, or 0 for "continuous" */
2156         4096, /* Samples per buffer */
2157         0.1,  /* Sampling interval in ms */
2158         1,    /* 1 for event-based sampling, 0 for time-based */
2159
2160         n_events,
2161         events,
2162         default_filename,
2163     };
2164
2165     jsval *argv = JS_ARGV(cx, vp);
2166     if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2167         str = JSVAL_TO_STRING(argv[0]);
2168         params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2169     }
2170
2171     status = VTStartSampling(&params);
2172
2173     if (params.tb5Filename != default_filename)
2174         cx->free(params.tb5Filename);
2175
2176     if (status != 0) {
2177         if (status == VTAPI_MULTIPLE_RUNS)
2178             VTStopSampling(0);
2179         if (status < sizeof(vtuneErrorMessages))
2180             JS_ReportError(cx, "Vtune setup error: %s",
2181                            vtuneErrorMessages[status]);
2182         else
2183             JS_ReportError(cx, "Vtune setup error: %d",
2184                            status);
2185         return false;
2186     }
2187     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2188     return true;
2189 }
2190
2191 JS_FRIEND_API(JSBool)
2192 js_StopVtune(JSContext *cx, uintN argc, jsval *vp)
2193 {
2194     U32 status = VTStopSampling(1);
2195     if (status) {
2196         if (status < sizeof(vtuneErrorMessages))
2197             JS_ReportError(cx, "Vtune shutdown error: %s",
2198                            vtuneErrorMessages[status]);
2199         else
2200             JS_ReportError(cx, "Vtune shutdown error: %d",
2201                            status);
2202         return false;
2203     }
2204     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2205     return true;
2206 }
2207
2208 JS_FRIEND_API(JSBool)
2209 js_PauseVtune(JSContext *cx, uintN argc, jsval *vp)
2210 {
2211     VTPause();
2212     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2213     return true;
2214 }
2215
2216 JS_FRIEND_API(JSBool)
2217 js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
2218 {
2219     VTResume();
2220     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2221     return true;
2222 }
2223
2224 #endif /* MOZ_VTUNE */
2225
2226 #ifdef MOZ_TRACEVIS
2227 /*
2228  * Ethogram - Javascript wrapper for TraceVis state
2229  *
2230  * ethology: The scientific study of animal behavior,
2231  *           especially as it occurs in a natural environment.
2232  * ethogram: A pictorial catalog of the behavioral patterns of
2233  *           an organism or a species.
2234  *
2235  */
2236 #if defined(XP_WIN)
2237 #include "jswin.h"
2238 #else
2239 #include <sys/time.h>
2240 #endif
2241 #include "jstracer.h"
2242
2243 #define ETHOGRAM_BUF_SIZE 65536
2244
2245 static JSBool
2246 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2247 static void
2248 ethogram_finalize(JSContext *cx, JSObject *obj);
2249
2250 static JSClass ethogram_class = {
2251     "Ethogram",
2252     JSCLASS_HAS_PRIVATE,
2253     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
2254     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2255     JSCLASS_NO_OPTIONAL_MEMBERS
2256 };
2257
2258 struct EthogramEvent {
2259     TraceVisState s;
2260     TraceVisExitReason r;
2261     int ts;
2262     int tus;
2263     JSString *filename;
2264     int lineno;
2265 };
2266
2267 static int
2268 compare_strings(const void *k1, const void *k2)
2269 {
2270     return strcmp((const char *) k1, (const char *) k2) == 0;
2271 }
2272
2273 class EthogramEventBuffer {
2274 private:
2275     EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2276     int mReadPos;
2277     int mWritePos;
2278     JSObject *mFilenames;
2279     int mStartSecond;
2280
2281     struct EthogramScriptEntry {
2282         char *filename;
2283         JSString *jsfilename;
2284
2285         EthogramScriptEntry *next;
2286     };
2287     EthogramScriptEntry *mScripts;
2288
2289 public:
2290     friend JSBool
2291     ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2292
2293     inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2294         mBuf[mWritePos].s = s;
2295         mBuf[mWritePos].r = r;
2296 #if defined(XP_WIN)
2297         FILETIME now;
2298         GetSystemTimeAsFileTime(&now);
2299         unsigned long long raw_us = 0.1 *
2300             (((unsigned long long) now.dwHighDateTime << 32ULL) |
2301              (unsigned long long) now.dwLowDateTime);
2302         unsigned int sec = raw_us / 1000000L;
2303         unsigned int usec = raw_us % 1000000L;
2304         mBuf[mWritePos].ts = sec - mStartSecond;
2305         mBuf[mWritePos].tus = usec;
2306 #else
2307         struct timeval tv;
2308         gettimeofday(&tv, NULL);
2309         mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2310         mBuf[mWritePos].tus = tv.tv_usec;
2311 #endif
2312
2313         JSString *jsfilename = findScript(filename);
2314         mBuf[mWritePos].filename = jsfilename;
2315         mBuf[mWritePos].lineno = lineno;
2316
2317         mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2318         if (mWritePos == mReadPos) {
2319             mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2320         }
2321     }
2322
2323     inline EthogramEvent *pop() {
2324         EthogramEvent *e = &mBuf[mReadPos];
2325         mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2326         return e;
2327     }
2328
2329     bool isEmpty() {
2330         return (mReadPos == mWritePos);
2331     }
2332
2333     EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2334         JSHashNumber hash = JS_HashString(filename);
2335         JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2336         if (*hep != NULL)
2337             return NULL;
2338
2339         JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2340
2341         EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2342         if (entry == NULL)
2343             return NULL;
2344
2345         entry->next = mScripts;
2346         mScripts = entry;
2347         entry->filename = filename;
2348         entry->jsfilename = jsfilename;
2349
2350         return mScripts;
2351     }
2352
2353     void removeScripts(JSContext *cx) {
2354         EthogramScriptEntry *se = mScripts;
2355         while (se != NULL) {
2356             char *filename = se->filename;
2357
2358             JSHashNumber hash = JS_HashString(filename);
2359             JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2360             JSHashEntry *he = *hep;
2361             if (he) {
2362                 /* we hardly knew he */
2363                 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2364             }
2365
2366             EthogramScriptEntry *se_head = se;
2367             se = se->next;
2368             JS_free(cx, se_head);
2369         }
2370     }
2371
2372     JSString *findScript(char *filename) {
2373         EthogramScriptEntry *se = mScripts;
2374         while (se != NULL) {
2375             if (compare_strings(se->filename, filename))
2376                 return (se->jsfilename);
2377             se = se->next;
2378         }
2379         return NULL;
2380     }
2381
2382     JSObject *filenames() {
2383         return mFilenames;
2384     }
2385
2386     int length() {
2387         if (mWritePos < mReadPos)
2388             return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2389         else
2390             return mWritePos - mReadPos;
2391     }
2392 };
2393
2394 static char jstv_empty[] = "<null>";
2395
2396 inline char *
2397 jstv_Filename(JSStackFrame *fp)
2398 {
2399     while (fp && !fp->isScriptFrame())
2400         fp = fp->prev();
2401     return (fp && fp->maybeScript() && fp->script()->filename)
2402            ? (char *)fp->script()->filename
2403            : jstv_empty;
2404 }
2405 inline uintN
2406 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2407 {
2408     while (fp && fp->pc(cx) == NULL)
2409         fp = fp->prev();
2410     return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2411 }
2412
2413 /* Collect states here and distribute to a matching buffer, if any */
2414 JS_FRIEND_API(void)
2415 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2416 {
2417     JSStackFrame *fp = cx->fp();
2418
2419     char *script_file = jstv_Filename(fp);
2420     JSHashNumber hash = JS_HashString(script_file);
2421
2422     JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2423     /* update event buffer, flag if overflowed */
2424     JSHashEntry *he = *hep;
2425     if (he) {
2426         EthogramEventBuffer *p;
2427         p = (EthogramEventBuffer *) he->value;
2428
2429         p->push(s, r, script_file, jstv_Lineno(cx, fp));
2430     }
2431 }
2432
2433 static JSBool
2434 ethogram_construct(JSContext *cx, uintN argc, jsval *vp)
2435 {
2436     EthogramEventBuffer *p;
2437
2438     p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2439     if (!p)
2440         return JS_FALSE;
2441
2442     p->mReadPos = p->mWritePos = 0;
2443     p->mScripts = NULL;
2444     p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2445
2446 #if defined(XP_WIN)
2447     FILETIME now;
2448     GetSystemTimeAsFileTime(&now);
2449     unsigned long long raw_us = 0.1 *
2450         (((unsigned long long) now.dwHighDateTime << 32ULL) |
2451          (unsigned long long) now.dwLowDateTime);
2452     unsigned int s = raw_us / 1000000L;
2453     p->mStartSecond = s;
2454 #else
2455     struct timeval tv;
2456     gettimeofday(&tv, NULL);
2457     p->mStartSecond = tv.tv_sec;
2458 #endif
2459     JSObject *obj;
2460     if (JS_IsConstructing(cx, vp)) {
2461         obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2462         if (!obj)
2463             return JS_FALSE;
2464     } else {
2465         obj = JS_THIS_OBJECT(cx, vp);
2466     }
2467
2468     jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2469     if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2470                            NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2471         return JS_FALSE;
2472
2473     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
2474     JS_SetPrivate(cx, obj, p);
2475     return JS_TRUE;
2476 }
2477
2478 static void
2479 ethogram_finalize(JSContext *cx, JSObject *obj)
2480 {
2481     EthogramEventBuffer *p;
2482     p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2483     if (!p)
2484         return;
2485
2486     p->removeScripts(cx);
2487
2488     JS_free(cx, p);
2489 }
2490
2491 static JSBool
2492 ethogram_addScript(JSContext *cx, uintN argc, jsval *vp)
2493 {
2494     JSString *str;
2495     char *filename = NULL;
2496     jsval *argv = JS_ARGV(cx, vp);
2497     JSObject *obj = JS_THIS_OBJECT(cx, vp);
2498     if (!obj)
2499         return false;
2500     if (argc < 1) {
2501         /* silently ignore no args */
2502         JS_SET_RVAL(cx, vp, JSVAL_VOID);
2503         return true;
2504     }
2505     if (JSVAL_IS_STRING(argv[0])) {
2506         str = JSVAL_TO_STRING(argv[0]);
2507         filename = js_DeflateString(cx, str->chars(), str->length());
2508         if (!filename)
2509             return false;
2510     }
2511
2512     EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2513
2514     p->addScript(cx, obj, filename, str);
2515     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2516     jsval dummy;
2517     JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, &dummy);
2518     return true;
2519 }
2520
2521 static JSBool
2522 ethogram_getAllEvents(JSContext *cx, uintN argc, jsval *vp)
2523 {
2524     EthogramEventBuffer *p;
2525     jsval *argv = JS_ARGV(cx, vp);
2526
2527     JSObject *obj = JS_THIS_OBJECT(cx, vp);
2528     if (!obj)
2529         return JS_FALSE;
2530
2531     p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2532     if (!p)
2533         return JS_FALSE;
2534
2535     if (p->isEmpty()) {
2536         JS_SET_RVAL(cx, vp, JSVAL_NULL);
2537         return JS_TRUE;
2538     }
2539
2540     JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2541     if (rarray == NULL) {
2542         JS_SET_RVAL(cx, vp, JSVAL_NULL);
2543         return JS_TRUE;
2544     }
2545
2546     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rarray));
2547
2548     for (int i = 0; !p->isEmpty(); i++) {
2549
2550         JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2551         if (x == NULL)
2552             return JS_FALSE;
2553
2554         EthogramEvent *e = p->pop();
2555
2556         jsval state = INT_TO_JSVAL(e->s);
2557         jsval reason = INT_TO_JSVAL(e->r);
2558         jsval ts = INT_TO_JSVAL(e->ts);
2559         jsval tus = INT_TO_JSVAL(e->tus);
2560
2561         jsval filename = STRING_TO_JSVAL(e->filename);
2562         jsval lineno = INT_TO_JSVAL(e->lineno);
2563
2564         if (!JS_SetProperty(cx, x, "state", &state))
2565             return JS_FALSE;
2566         if (!JS_SetProperty(cx, x, "reason", &reason))
2567             return JS_FALSE;
2568         if (!JS_SetProperty(cx, x, "ts", &ts))
2569             return JS_FALSE;
2570         if (!JS_SetProperty(cx, x, "tus", &tus))
2571             return JS_FALSE;
2572
2573         if (!JS_SetProperty(cx, x, "filename", &filename))
2574             return JS_FALSE;
2575         if (!JS_SetProperty(cx, x, "lineno", &lineno))
2576             return JS_FALSE;
2577
2578         jsval element = OBJECT_TO_JSVAL(x);
2579         JS_SetElement(cx, rarray, i, &element);
2580     }
2581
2582     return JS_TRUE;
2583 }
2584
2585 static JSBool
2586 ethogram_getNextEvent(JSContext *cx, uintN argc, jsval *vp)
2587 {
2588     EthogramEventBuffer *p;
2589     jsval *argv = JS_ARGV(cx, vp);
2590
2591     JSObject *obj = JS_THIS_OBJECT(cx, vp);
2592     if (!obj)
2593         return JS_FALSE;
2594
2595     p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2596     if (!p)
2597         return JS_FALSE;
2598
2599     JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2600     if (x == NULL)
2601         return JS_FALSE;
2602
2603     if (p->isEmpty()) {
2604         JS_SET_RVAL(cx, vp, JSVAL_NULL);
2605         return JS_TRUE;
2606     }
2607
2608     EthogramEvent *e = p->pop();
2609     jsval state = INT_TO_JSVAL(e->s);
2610     jsval reason = INT_TO_JSVAL(e->r);
2611     jsval ts = INT_TO_JSVAL(e->ts);
2612     jsval tus = INT_TO_JSVAL(e->tus);
2613
2614     jsval filename = STRING_TO_JSVAL(e->filename);
2615     jsval lineno = INT_TO_JSVAL(e->lineno);
2616
2617     if (!JS_SetProperty(cx, x, "state", &state))
2618         return JS_FALSE;
2619     if (!JS_SetProperty(cx, x, "reason", &reason))
2620         return JS_FALSE;
2621     if (!JS_SetProperty(cx, x, "ts", &ts))
2622         return JS_FALSE;
2623     if (!JS_SetProperty(cx, x, "tus", &tus))
2624         return JS_FALSE;
2625     if (!JS_SetProperty(cx, x, "filename", &filename))
2626         return JS_FALSE;
2627
2628     if (!JS_SetProperty(cx, x, "lineno", &lineno))
2629         return JS_FALSE;
2630
2631     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(x));
2632
2633     return JS_TRUE;
2634 }
2635
2636 static JSFunctionSpec ethogram_methods[] = {
2637     JS_FN("addScript",    ethogram_addScript,    1,0),
2638     JS_FN("getAllEvents", ethogram_getAllEvents, 0,0),
2639     JS_FN("getNextEvent", ethogram_getNextEvent, 0,0),
2640     JS_FS_END
2641 };
2642
2643 /*
2644  * An |Ethogram| organizes the output of a collection of files that should be
2645  * monitored together. A single object gets events for the group.
2646  */
2647 JS_FRIEND_API(JSBool)
2648 js_InitEthogram(JSContext *cx, uintN argc, jsval *vp)
2649 {
2650     if (!traceVisScriptTable) {
2651         traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2652                                          NULL, NULL, NULL);
2653     }
2654
2655     JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2656                  ethogram_construct, 0, NULL, ethogram_methods,
2657                  NULL, NULL);
2658
2659     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2660     return true;
2661 }
2662
2663 JS_FRIEND_API(JSBool)
2664 js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp)
2665 {
2666     if (traceVisScriptTable)
2667         JS_HashTableDestroy(traceVisScriptTable);
2668
2669     JS_SET_RVAL(cx, vp, JSVAL_VOID);
2670     return true;
2671 }
2672
2673 #endif /* MOZ_TRACEVIS */
2674
2675 #ifdef MOZ_TRACE_JSCALLS
2676
2677 JS_PUBLIC_API(void)
2678 JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
2679 {
2680     cx->functionCallback = fcb;
2681 }
2682
2683 JS_PUBLIC_API(JSFunctionCallback)
2684 JS_GetFunctionCallback(JSContext *cx)
2685 {
2686     return cx->functionCallback;
2687 }
2688
2689 #endif /* MOZ_TRACE_JSCALLS */
2690