1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
39 * ***** END LICENSE BLOCK ***** */
50 #include "jshashtable.h"
53 #include "jsversion.h"
65 #include "jsstaticcheck.h"
67 #include "jswrapper.h"
69 #include "jsatominlines.h"
70 #include "jsdbgapiinlines.h"
71 #include "jsinterpinlines.h"
72 #include "jsobjinlines.h"
73 #include "jsscopeinlines.h"
74 #include "jsscriptinlines.h"
76 #include "jsautooplen.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/Retcon.h"
82 using namespace js::gc;
84 typedef struct JSTrap {
89 JSTrapHandler handler;
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))
98 JS_GetDebugMode(JSContext *cx)
100 return cx->compartment->debugMode;
103 JS_PUBLIC_API(JSBool)
104 JS_SetDebugMode(JSContext *cx, JSBool debug)
106 return JS_SetDebugModeForCompartment(cx, cx->compartment, debug);
110 JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
112 rt->debugMode = debug;
117 CompartmentHasLiveScripts(JSCompartment *comp)
120 # ifdef JS_THREADSAFE
121 jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
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;
129 while ((icx = JS_ContextIterator(comp->rt, &iter))) {
131 if (JS_GetContextThread(icx) != currentThreadId)
134 for (AllFramesIter i(icx); !i.done(); ++i) {
135 JSScript *script = i.fp()->maybeScript();
136 if (script && script->compartment == comp)
145 JS_FRIEND_API(JSBool)
146 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
148 if (comp->debugMode == !!debug)
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));
156 // All scripts compiled from this point on should be in the requested debugMode.
157 comp->debugMode = !!debug;
159 // Discard JIT code for any scripts that change debugMode. This function
160 // assumes that 'comp' is in the same thread as 'cx'.
163 JS::AutoEnterScriptCompartment ac;
165 for (JSScript *script = (JSScript *)comp->scripts.next;
166 &script->links != &comp->scripts;
167 script = (JSScript *)script->links.next)
169 if (!script->debugMode == !debug)
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.
178 if (!ac.entered() && !ac.enter(cx, script)) {
179 comp->debugMode = JS_FALSE;
183 mjit::ReleaseScriptCode(cx, script);
184 script->debugMode = !!debug;
191 JS_FRIEND_API(JSBool)
192 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
195 if (!script->singleStepMode == !singleStep)
199 JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
202 /* request the next recompile to inject single step interrupts */
203 script->singleStepMode = !!singleStep;
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;
218 CheckDebugMode(JSContext *cx)
220 JSBool debugMode = JS_GetDebugMode(cx);
223 * This probably should be an assertion, since it's indicative of a severe
227 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
228 NULL, JSMSG_NEED_DEBUG_MODE);
233 JS_PUBLIC_API(JSBool)
234 JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
236 if (!CheckDebugMode(cx))
239 return js_SetSingleStepMode(cx, script, singleStep);
243 * NB: FindTrap must be called with rt->debuggerLock acquired.
246 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
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)
260 js_UntrapScriptCode(JSContext *cx, JSScript *script)
269 for (trap = (JSTrap *)rt->trapList.next;
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;
279 nbytes = script->length * sizeof(jsbytecode);
280 notes = script->notes();
281 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
283 nbytes += (sn - notes + 1) * sizeof *sn;
285 code = (jsbytecode *) cx->malloc(nbytes);
288 memcpy(code, script->code, nbytes);
289 JS_PURGE_GSN_CACHE(cx);
291 code[trap->pc - script->code] = trap->op;
298 JS_PUBLIC_API(JSBool)
299 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
300 JSTrapHandler handler, jsval closure)
302 JSTrap *junk, *trap, *twin;
306 if (!CheckDebugMode(cx))
309 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
313 trap = FindTrap(rt, script, pc);
315 JS_ASSERT(trap->script == script && trap->pc == pc);
316 JS_ASSERT(*pc == JSOP_TRAP);
318 sample = rt->debuggerMutations;
320 trap = (JSTrap *) cx->malloc(sizeof *trap);
323 trap->closure = JSVAL_NULL;
325 twin = (rt->debuggerMutations != sample)
326 ? FindTrap(rt, script, pc)
332 JS_APPEND_LINK(&trap->links, &rt->trapList);
333 ++rt->debuggerMutations;
334 trap->script = script;
336 trap->op = (JSOp)*pc;
340 trap->handler = handler;
341 trap->closure = closure;
347 if (script->hasJITCode()) {
348 js::mjit::Recompiler recompiler(cx, script);
349 if (!recompiler.recompile())
358 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
366 trap = FindTrap(rt, script, pc);
367 op = trap ? trap->op : (JSOp) *pc;
373 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
375 ++cx->runtime->debuggerMutations;
376 JS_REMOVE_LINK(&trap->links);
377 *trap->pc = (jsbytecode)trap->op;
378 DBG_UNLOCK(cx->runtime);
383 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
384 JSTrapHandler *handlerp, jsval *closurep)
388 DBG_LOCK(cx->runtime);
389 trap = FindTrap(cx->runtime, script, pc);
391 *handlerp = trap ? trap->handler : NULL;
393 *closurep = trap ? trap->closure : JSVAL_NULL;
395 DestroyTrapAndUnlock(cx, trap);
397 DBG_UNLOCK(cx->runtime);
400 if (script->hasJITCode()) {
401 mjit::Recompiler recompiler(cx, script);
402 recompiler.recompile();
408 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
416 for (trap = (JSTrap *)rt->trapList.next;
417 &trap->links != &rt->trapList;
419 next = (JSTrap *)trap->links.next;
420 if (trap->script == script) {
421 sample = rt->debuggerMutations;
422 DestroyTrapAndUnlock(cx, trap);
424 if (rt->debuggerMutations != sample + 1)
425 next = (JSTrap *)rt->trapList.next;
432 JS_ClearAllTraps(JSContext *cx)
440 for (trap = (JSTrap *)rt->trapList.next;
441 &trap->links != &rt->trapList;
443 next = (JSTrap *)trap->links.next;
444 sample = rt->debuggerMutations;
445 DestroyTrapAndUnlock(cx, trap);
447 if (rt->debuggerMutations != sample + 1)
448 next = (JSTrap *)rt->trapList.next;
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).
459 js_MarkTraps(JSTracer *trc)
461 JSRuntime *rt = trc->context->runtime;
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");
470 JS_PUBLIC_API(JSTrapStatus)
471 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
477 DBG_LOCK(cx->runtime);
478 trap = FindTrap(cx->runtime, script, pc);
479 JS_ASSERT(!trap || trap->handler);
482 DBG_UNLOCK(cx->runtime);
484 /* Defend against "pc for wrong script" API usage error. */
485 JS_ASSERT(op != JSOP_TRAP);
488 /* If the API was abused, we must fail for want of the real op. */
492 /* Assume a race with a debugger thread and try to carry on. */
493 *rval = INT_TO_JSVAL(op);
494 return JSTRAP_CONTINUE;
496 /* Always fail if single-threaded (must be an API usage error). */
500 DBG_UNLOCK(cx->runtime);
503 * It's important that we not use 'trap->' after calling the callback --
504 * the callback might remove the trap!
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);
517 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
520 if (!rt->debuggerInhibitsJIT()) {
521 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
522 js_ContextFromLinkField(cl)->updateJITEnabled();
524 } else if (rt->debuggerInhibitsJIT()) {
525 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
526 js_ContextFromLinkField(cl)->traceJitEnabled = false;
531 JS_PUBLIC_API(JSBool)
532 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
537 bool wasInhibited = rt->debuggerInhibitsJIT();
539 rt->globalDebugHooks.interruptHook = hook;
540 rt->globalDebugHooks.interruptHookData = closure;
542 JITInhibitingHookChange(rt, wasInhibited);
548 JS_PUBLIC_API(JSBool)
549 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
553 bool wasInhibited = rt->debuggerInhibitsJIT();
556 *hoop = rt->globalDebugHooks.interruptHook;
558 *closurep = rt->globalDebugHooks.interruptHookData;
559 rt->globalDebugHooks.interruptHook = 0;
560 rt->globalDebugHooks.interruptHookData = 0;
562 JITInhibitingHookChange(rt, wasInhibited);
567 /************************************************************************/
569 struct JSWatchPoint {
571 JSObject *object; /* weak link, see js_SweepWatchPoints */
573 StrictPropertyOp setter;
574 JSWatchPointHandler handler;
579 #define JSWP_LIVE 0x1 /* live because set and not cleared */
580 #define JSWP_HELD 0x2 /* held while running handler/setter */
583 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
586 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
589 JSRuntime *rt = cx->runtime;
592 if (wp->flags != 0) {
598 * Switch to the same compartment as the watch point, since changeProperty, below,
599 * needs to have a compartment.
601 SwitchToCompartment sc(cx, wp->object);
603 /* Remove wp from the list, then restore wp->shape->setter from wp. */
604 ++rt->debuggerMutations;
605 JS_REMOVE_LINK(&wp->links);
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.
612 const Shape *shape = wp->shape;
613 const Shape *wprop = wp->object->nativeLookup(shape->id);
615 wprop->hasSetterValue() == shape->hasSetterValue() &&
616 IsWatchedProperty(cx, wprop)) {
617 shape = wp->object->changeProperty(cx, wprop, 0, wprop->attributes(),
618 wprop->getter(), wp->setter);
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).
633 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
638 rt = trc->context->runtime;
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");
653 js_SweepWatchPoints(JSContext *cx)
656 JSWatchPoint *wp, *next;
661 for (wp = (JSWatchPoint *)rt->watchPointList.next;
662 &wp->links != &rt->watchPointList;
664 next = (JSWatchPoint *)wp->links.next;
665 if (IsAboutToBeFinalized(cx, wp->object)) {
666 sample = rt->debuggerMutations;
668 /* Ignore failures. */
669 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
671 if (rt->debuggerMutations != sample + 1)
672 next = (JSWatchPoint *)rt->watchPointList.next;
681 * NB: LockedFindWatchPoint must be called with rt->debuggerLock acquired.
683 static JSWatchPoint *
684 LockedFindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
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)
697 static JSWatchPoint *
698 FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
703 wp = LockedFindWatchPoint(rt, obj, id);
709 js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
711 JSRuntime *rt = cx->runtime;
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;
721 jsid propid = shape->id;
722 shape = obj->nativeLookup(propid);
723 JS_ASSERT(IsWatchedProperty(cx, shape));
724 jsid userid = SHAPE_USERID(shape);
726 /* Determine the property's old value. */
728 uint32 slot = shape->slot;
729 Value old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
730 const Shape *needMethodSlotWrite = NULL;
731 if (shape->isMethod()) {
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.
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.
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
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);
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);
771 Conditionally<AutoShapeRooter> tvr(needMethodSlotWrite, cx, needMethodSlotWrite);
774 * Call the handler. This invalidates shape, so re-lookup the shape.
775 * NB: wp is held, so we can safely dereference it still.
777 ok = wp->handler(cx, obj, propid, Jsvalify(old), Jsvalify(vp), wp->closure);
780 shape = obj->nativeLookup(propid);
784 } else if (wp->setter) {
786 * Pass the output of the handler to the setter. Security wrappers
787 * prevent any funny business between watchpoints and setters.
789 ok = shape->hasSetterValue()
790 ? ExternalInvoke(cx, ObjectValue(*obj),
791 ObjectValue(*CastAsObject(wp->setter)),
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);
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.
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.
809 ok = obj->methodWriteBarrier(cx, *shape, *vp) != NULL;
815 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
823 js_watch_set_wrapper(JSContext *cx, uintN argc, Value *vp)
825 JSObject *obj = ToObject(cx, &vp[1]);
829 JSObject &funobj = JS_CALLEE(cx, vp).toObject();
830 JSFunction *wrapper = funobj.getFunctionPrivate();
831 jsid userid = ATOM_TO_JSID(wrapper->atom);
833 JS_SET_RVAL(cx, vp, argc ? JS_ARGV(cx, vp)[0] : UndefinedValue());
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.
838 return js_watch_set(cx, obj, userid, false, vp);
844 IsWatchedProperty(JSContext *cx, const Shape *shape)
846 if (shape->hasSetterValue()) {
847 JSObject *funobj = shape->setterObject();
848 if (!funobj || !funobj->isFunction())
851 JSFunction *fun = funobj->getFunctionPrivate();
852 return fun->maybeNative() == js_watch_set_wrapper;
854 return shape->setterOp() == js_watch_set;
860 * Return an appropriate setter to substitute for |setter| on a property
861 * with attributes |attrs|, to implement a watchpoint on the property named
864 static StrictPropertyOp
865 WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, StrictPropertyOp setter)
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 */
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.
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))
883 atom = JSID_TO_ATOM(id);
888 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
889 setter ? CastAsObject(setter)->getParent() : NULL, atom);
892 return CastAsStrictPropertyOp(FUN_OBJECT(wrapper));
896 UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
898 JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id);
899 JS_ASSERT(!IsWatchedProperty(cx, newShape));
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());
908 * Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will
909 * return a new shape, or mutate this one.
911 StrictPropertyOp originalSetter = newShape->setter();
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.
919 const Shape *watchingShape =
920 js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(),
921 newShape->getter(), watchingSetter);
925 /* Update the watchpoint with the new shape and its original setter. */
926 wp->setter = originalSetter;
927 wp->shape = watchingShape;
929 return watchingShape;
933 js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const Shape *newShape)
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.
942 if (IsWatchedProperty(cx, newShape))
945 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id);
949 return UpdateWatchpointShape(cx, wp, newShape);
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.
958 static StrictPropertyOp
959 UnwrapSetter(JSContext *cx, JSObject *obj, const Shape *shape)
961 /* If it's not a watched property, its setter is not wrapped. */
962 if (!IsWatchedProperty(cx, shape))
963 return shape->setter();
965 /* Look up the watchpoint, from which we can retrieve the underlying setter. */
966 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, shape->id);
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.
977 JS_PUBLIC_API(JSBool)
978 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
979 JSWatchPointHandler handler, JSObject *closure)
987 OBJ_TO_INNER_OBJECT(cx, obj);
991 AutoValueRooter idroot(cx);
992 if (JSID_IS_INT(id)) {
995 if (!js_ValueToStringId(cx, IdToValue(id), &propid))
997 propid = js_CheckForStringIndex(propid);
998 idroot.set(IdToValue(propid));
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.
1005 if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
1008 if (!obj->isNative()) {
1009 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
1010 obj->getClass()->name);
1016 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
1018 const Shape *shape = (Shape *) prop;
1019 JSRuntime *rt = cx->runtime;
1021 /* Check for a deleted symbol watchpoint, which holds its property. */
1022 JSWatchPoint *wp = FindWatchPoint(rt, obj, propid);
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)) {
1029 shape = (Shape *) prop;
1031 } else if (pobj != obj) {
1032 /* Clone the prototype property so we can watch the right object. */
1033 AutoValueRooter valroot(cx);
1035 StrictPropertyOp setter;
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;
1049 if (!pobj->getProperty(cx, propid, valroot.addr()) ||
1050 !pobj->getAttributes(cx, propid, &attrs)) {
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,
1065 shape = (Shape *) prop;
1069 * At this point, prop/shape exists in obj, obj is locked, and we must
1070 * unlock the object before returning.
1073 JSWatchPoint *wp = LockedFindWatchPoint(rt, obj, propid);
1076 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
1083 wp->flags = JSWP_LIVE;
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);
1090 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
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).
1100 JS_ASSERT(!LockedFindWatchPoint(rt, obj, propid));
1101 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
1102 ++rt->debuggerMutations;
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.
1109 obj->watchpointOwnShapeChange(cx);
1111 wp->handler = handler;
1112 wp->closure = reinterpret_cast<JSObject*>(closure);
1117 JS_PUBLIC_API(JSBool)
1118 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
1119 JSWatchPointHandler *handlerp, JSObject **closurep)
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) {
1131 *handlerp = wp->handler;
1133 *closurep = wp->closure;
1134 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1145 JS_PUBLIC_API(JSBool)
1146 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
1149 JSWatchPoint *wp, *next;
1154 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1155 &wp->links != &rt->watchPointList;
1157 next = (JSWatchPoint *)wp->links.next;
1158 if (wp->object == obj) {
1159 sample = rt->debuggerMutations;
1160 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1163 if (rt->debuggerMutations != sample + 1)
1164 next = (JSWatchPoint *)rt->watchPointList.next;
1171 JS_PUBLIC_API(JSBool)
1172 JS_ClearAllWatchPoints(JSContext *cx)
1175 JSWatchPoint *wp, *next;
1180 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1181 &wp->links != &rt->watchPointList;
1183 next = (JSWatchPoint *)wp->links.next;
1184 sample = rt->debuggerMutations;
1185 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1188 if (rt->debuggerMutations != sample + 1)
1189 next = (JSWatchPoint *)rt->watchPointList.next;
1195 /************************************************************************/
1197 JS_PUBLIC_API(uintN)
1198 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1200 return js_PCToLineNumber(cx, script, pc);
1203 JS_PUBLIC_API(jsbytecode *)
1204 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1206 return js_LineNumberToPC(script, lineno);
1209 JS_PUBLIC_API(jsbytecode *)
1210 JS_EndPC(JSContext *cx, JSScript *script)
1212 return script->code + script->length;
1215 JS_PUBLIC_API(uintN)
1216 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
1221 JS_PUBLIC_API(JSBool)
1222 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
1224 return fun->script()->bindings.hasLocalNames();
1227 extern JS_PUBLIC_API(jsuword *)
1228 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
1230 *markp = JS_ARENA_MARK(&cx->tempPool);
1231 return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
1234 extern JS_PUBLIC_API(JSAtom *)
1235 JS_LocalNameToAtom(jsuword w)
1237 return JS_LOCAL_NAME_TO_ATOM(w);
1240 extern JS_PUBLIC_API(JSString *)
1241 JS_AtomKey(JSAtom *atom)
1243 return ATOM_TO_STRING(atom);
1246 extern JS_PUBLIC_API(void)
1247 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
1249 JS_ARENA_RELEASE(&cx->tempPool, mark);
1252 JS_PUBLIC_API(JSScript *)
1253 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1255 return FUN_SCRIPT(fun);
1258 JS_PUBLIC_API(JSNative)
1259 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1261 return Jsvalify(fun->maybeNative());
1264 JS_PUBLIC_API(JSPrincipals *)
1265 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1267 return script->principals;
1270 /************************************************************************/
1273 * Stack Frame Iterator
1275 JS_PUBLIC_API(JSStackFrame *)
1276 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1278 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->prev();
1282 JS_PUBLIC_API(JSScript *)
1283 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1285 return fp->maybeScript();
1288 JS_PUBLIC_API(jsbytecode *)
1289 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1294 JS_PUBLIC_API(JSStackFrame *)
1295 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1297 return js_GetScriptedCaller(cx, fp);
1301 js_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1303 JSSecurityCallbacks *callbacks;
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());
1313 if (fp->isScriptFrame())
1314 return fp->script()->principals;
1319 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1321 JSPrincipals *principals, *callerPrincipals;
1322 JSSecurityCallbacks *callbacks;
1324 callbacks = JS_GetSecurityCallbacks(cx);
1325 if (callbacks && callbacks->findObjectPrincipals)
1326 principals = callbacks->findObjectPrincipals(cx, callee);
1331 callerPrincipals = js_StackFramePrincipals(cx, caller);
1332 return (callerPrincipals && principals &&
1333 callerPrincipals->subsume(callerPrincipals, principals))
1338 JS_PUBLIC_API(void *)
1339 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1341 if (fp->annotation() && fp->isScriptFrame()) {
1342 JSPrincipals *principals = js_StackFramePrincipals(cx, fp);
1344 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1346 * Give out an annotation only if privileges have not been revoked
1347 * or disabled globally.
1349 return fp->annotation();
1357 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1359 fp->setAnnotation(annotation);
1362 JS_PUBLIC_API(void *)
1363 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1365 JSPrincipals *principals;
1367 principals = js_StackFramePrincipals(cx, fp);
1370 return principals->getPrincipalArray(cx, principals);
1373 JS_PUBLIC_API(JSBool)
1374 JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp)
1376 return !fp->isDummyFrame();
1379 /* this is deprecated, use JS_GetFrameScopeChain instead */
1380 JS_PUBLIC_API(JSObject *)
1381 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1383 return &fp->scopeChain();
1386 JS_PUBLIC_API(JSObject *)
1387 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1389 JS_ASSERT(cx->stack().contains(fp));
1391 js::AutoCompartment ac(cx, &fp->scopeChain());
1395 /* Force creation of argument and call objects if not yet created */
1396 (void) JS_GetFrameCallObject(cx, fp);
1397 return GetScopeChain(cx, fp);
1400 JS_PUBLIC_API(JSObject *)
1401 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1403 JS_ASSERT(cx->stack().contains(fp));
1405 if (!fp->isFunctionFrame())
1408 js::AutoCompartment ac(cx, &fp->scopeChain());
1412 /* Force creation of argument object if not yet created */
1413 (void) js_GetArgsObject(cx, fp);
1416 * XXX ill-defined: null return here means error was reported, unlike a
1417 * null returned above or in the #else
1419 return js_GetCallObject(cx, fp);
1422 JS_PUBLIC_API(JSBool)
1423 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv)
1425 if (fp->isDummyFrame())
1428 js::AutoCompartment ac(cx, &fp->scopeChain());
1432 if (!fp->computeThis(cx))
1434 *thisv = Jsvalify(fp->thisValue());
1438 JS_PUBLIC_API(JSFunction *)
1439 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1441 return fp->maybeFun();
1444 JS_PUBLIC_API(JSObject *)
1445 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1447 if (!fp->isFunctionFrame())
1450 JS_ASSERT(fp->callee().isFunction());
1451 JS_ASSERT(fp->callee().getPrivate() == fp->fun());
1452 return &fp->callee();
1455 JS_PUBLIC_API(JSBool)
1456 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1458 return fp->isConstructing();
1461 JS_PUBLIC_API(JSObject *)
1462 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1464 return fp->maybeCallee();
1467 JS_PUBLIC_API(JSBool)
1468 JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
1472 if (!fp->getValidCalleeObject(cx, &v))
1478 JS_PUBLIC_API(JSBool)
1479 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1481 return fp->isDebuggerFrame();
1484 JS_PUBLIC_API(jsval)
1485 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1487 return Jsvalify(fp->returnValue());
1491 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1494 JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode);
1496 assertSameCompartment(cx, fp, rval);
1497 fp->setReturnValue(Valueify(rval));
1500 /************************************************************************/
1502 JS_PUBLIC_API(const char *)
1503 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1505 return script->filename;
1508 JS_PUBLIC_API(uintN)
1509 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1511 return script->lineno;
1514 JS_PUBLIC_API(uintN)
1515 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1517 return js_GetScriptLineExtent(script);
1520 JS_PUBLIC_API(JSVersion)
1521 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1523 return VersionNumber(script->getVersion());
1526 /***************************************************************************/
1529 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1531 rt->globalDebugHooks.newScriptHook = hook;
1532 rt->globalDebugHooks.newScriptHookData = callerdata;
1536 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1539 rt->globalDebugHooks.destroyScriptHook = hook;
1540 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1543 /***************************************************************************/
1545 JS_PUBLIC_API(JSBool)
1546 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1547 const jschar *chars, uintN length,
1548 const char *filename, uintN lineno,
1551 JS_ASSERT_NOT_ON_TRACE(cx);
1553 if (!CheckDebugMode(cx))
1556 JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1560 js::AutoCompartment ac(cx, scobj);
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.
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);
1578 bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1580 js_DestroyScript(cx, script);
1584 JS_PUBLIC_API(JSBool)
1585 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1586 const char *bytes, uintN length,
1587 const char *filename, uintN lineno,
1592 size_t len = length;
1594 if (!CheckDebugMode(cx))
1597 chars = js_InflateString(cx, bytes, &len);
1600 length = (uintN) len;
1601 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1608 /************************************************************************/
1610 /* This all should be reworked to avoid requiring JSScopeProperty types. */
1612 JS_PUBLIC_API(JSScopeProperty *)
1613 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1617 /* The caller passes null in *iteratorp to get things started. */
1618 shape = (Shape *) *iteratorp;
1620 shape = obj->lastProperty();
1622 shape = shape->previous();
1623 if (!shape->previous()) {
1624 JS_ASSERT(JSID_IS_EMPTY(shape->id));
1629 return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape));
1632 JS_PUBLIC_API(JSBool)
1633 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1636 assertSameCompartment(cx, obj);
1637 Shape *shape = (Shape *) sprop;
1638 pd->id = IdToJsval(shape->id);
1640 JSBool wasThrowing = cx->isExceptionPending();
1641 Value lastException = UndefinedValue();
1643 lastException = cx->getPendingException();
1644 cx->clearPendingException();
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;
1651 pd->flags = JSPD_EXCEPTION;
1652 pd->value = Jsvalify(cx->getPendingException());
1659 cx->setPendingException(lastException);
1661 pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
1662 | (!shape->writable() ? JSPD_READONLY : 0)
1663 | (!shape->configurable() ? JSPD_PERMANENT : 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;
1674 pd->alias = JSVAL_VOID;
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);
1688 JS_PUBLIC_API(JSBool)
1689 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
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);
1698 if (!clasp->enumerate(cx, obj))
1701 /* Return an empty pda early if obj has no own properties. */
1702 if (obj->nativeEmpty()) {
1708 uint32 n = obj->propertyCount();
1709 JSPropertyDesc *pd = (JSPropertyDesc *) cx->malloc(size_t(n) * sizeof(JSPropertyDesc));
1713 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1714 if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1716 if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1718 Shape *shape = const_cast<Shape *>(&r.front());
1719 if (!JS_GetPropertyDesc(cx, obj, reinterpret_cast<JSScopeProperty *>(shape), &pd[i]))
1721 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1731 pda->length = i + 1;
1733 JS_PutPropertyDescArray(cx, pda);
1738 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
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);
1753 /************************************************************************/
1755 JS_PUBLIC_API(JSBool)
1756 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1758 rt->globalDebugHooks.debuggerHandler = handler;
1759 rt->globalDebugHooks.debuggerHandlerData = closure;
1763 JS_PUBLIC_API(JSBool)
1764 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1766 rt->globalDebugHooks.sourceHandler = handler;
1767 rt->globalDebugHooks.sourceHandlerData = closure;
1771 JS_PUBLIC_API(JSBool)
1772 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1774 rt->globalDebugHooks.executeHook = hook;
1775 rt->globalDebugHooks.executeHookData = closure;
1779 JS_PUBLIC_API(JSBool)
1780 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1784 AutoLockGC lock(rt);
1785 bool wasInhibited = rt->debuggerInhibitsJIT();
1787 rt->globalDebugHooks.callHook = hook;
1788 rt->globalDebugHooks.callHookData = closure;
1790 JITInhibitingHookChange(rt, wasInhibited);
1796 JS_PUBLIC_API(JSBool)
1797 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1799 rt->globalDebugHooks.throwHook = hook;
1800 rt->globalDebugHooks.throwHookData = closure;
1804 JS_PUBLIC_API(JSBool)
1805 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1807 rt->globalDebugHooks.debugErrorHook = hook;
1808 rt->globalDebugHooks.debugErrorHookData = closure;
1812 /************************************************************************/
1814 JS_PUBLIC_API(size_t)
1815 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1817 return obj->slotsAndStructSize();
1821 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1825 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1826 nbytes += sizeof(JSString);
1827 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1831 JS_PUBLIC_API(size_t)
1832 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
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);
1841 nbytes += GetAtomTotalSize(cx, fun->atom);
1847 JS_PUBLIC_API(size_t)
1848 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1850 size_t nbytes, pbytes;
1852 jssrcnote *sn, *notes;
1853 JSObjectArray *objarray;
1854 JSPrincipals *principals;
1856 nbytes = sizeof *script;
1857 if (script->u.object)
1858 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
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]);
1865 if (script->filename)
1866 nbytes += strlen(script->filename) + 1;
1868 notes = script->notes();
1869 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1871 nbytes += (sn - notes + 1) * sizeof *sn;
1873 if (JSScript::isValidOffset(script->objectsOffset)) {
1874 objarray = script->objects();
1875 i = objarray->length;
1876 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1878 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1882 if (JSScript::isValidOffset(script->regexpsOffset)) {
1883 objarray = script->regexps();
1884 i = objarray->length;
1885 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1887 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1891 if (JSScript::isValidOffset(script->trynotesOffset)) {
1892 nbytes += sizeof(JSTryNoteArray) +
1893 script->trynotes()->length * sizeof(JSTryNote);
1896 principals = script->principals;
1898 JS_ASSERT(principals->refcount);
1899 pbytes = sizeof *principals;
1900 if (principals->refcount > 1)
1901 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1908 JS_PUBLIC_API(uint32)
1909 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1912 fp = js_GetTopStackFrame(cx);
1914 if (fp->isScriptFrame())
1915 return JS_GetScriptFilenameFlags(fp->script());
1921 JS_PUBLIC_API(uint32)
1922 JS_GetScriptFilenameFlags(JSScript *script)
1925 if (!script->filename)
1926 return JSFILENAME_NULL;
1927 return js_GetScriptFilenameFlags(script->filename);
1930 JS_PUBLIC_API(JSBool)
1931 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1933 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1938 JS_PUBLIC_API(JSBool)
1939 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1941 return obj->isSystem();
1944 JS_PUBLIC_API(JSBool)
1945 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1951 /************************************************************************/
1953 JS_PUBLIC_API(JSObject *)
1954 JS_UnwrapObject(JSContext *cx, JSObject *obj)
1956 return obj->unwrap();
1959 /************************************************************************/
1962 js_RevertVersion(JSContext *cx)
1964 cx->clearVersionOverride();
1967 JS_PUBLIC_API(const JSDebugHooks *)
1968 JS_GetGlobalDebugHooks(JSRuntime *rt)
1970 return &rt->globalDebugHooks;
1973 const JSDebugHooks js_NullDebugHooks = {};
1975 JS_PUBLIC_API(JSDebugHooks *)
1976 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1979 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1983 AutoLockGC lock(cx->runtime);
1985 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1986 cx->debugHooks = hooks;
1988 cx->updateJITEnabled();
1993 JS_PUBLIC_API(JSDebugHooks *)
1994 JS_ClearContextDebugHooks(JSContext *cx)
1996 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1999 JS_PUBLIC_API(JSBool)
2002 return Probes::startProfiling();
2008 Probes::stopProfiling();
2011 #ifdef MOZ_PROFILING
2014 StartProfiling(JSContext *cx, uintN argc, jsval *vp)
2016 JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling()));
2021 StopProfiling(JSContext *cx, uintN argc, jsval *vp)
2024 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2031 IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
2033 JS_SET_RVAL(cx, vp, JSVAL_TRUE);
2039 static JSFunctionSpec profiling_functions[] = {
2040 JS_FN("startProfiling", StartProfiling, 0,0),
2041 JS_FN("stopProfiling", StopProfiling, 0,0),
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),
2054 JS_PUBLIC_API(JSBool)
2055 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
2057 #ifdef MOZ_PROFILING
2058 return JS_DefineFunctions(cx, obj, profiling_functions);
2064 #ifdef MOZ_CALLGRIND
2066 #include <valgrind/callgrind.h>
2068 JS_FRIEND_API(JSBool)
2069 js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
2071 CALLGRIND_START_INSTRUMENTATION;
2072 CALLGRIND_ZERO_STATS;
2073 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2077 JS_FRIEND_API(JSBool)
2078 js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
2080 CALLGRIND_STOP_INSTRUMENTATION;
2081 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2085 JS_FRIEND_API(JSBool)
2086 js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
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);
2095 CALLGRIND_DUMP_STATS_AT(bytes.ptr());
2099 CALLGRIND_DUMP_STATS;
2101 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2105 #endif /* MOZ_CALLGRIND */
2108 #include <VTuneApi.h>
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",
2116 "sample file in use",
2117 "invalid 'number of events' field",
2118 "unknown, error #7",
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",
2133 "invalid 'global options' field",
2137 JS_FRIEND_API(JSBool)
2138 js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
2140 VTUNE_EVENT events[] = {
2141 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2142 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2145 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2146 char *default_filename = "mozilla-vtune.tb5";
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 */
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());
2171 status = VTStartSampling(¶ms);
2173 if (params.tb5Filename != default_filename)
2174 cx->free(params.tb5Filename);
2177 if (status == VTAPI_MULTIPLE_RUNS)
2179 if (status < sizeof(vtuneErrorMessages))
2180 JS_ReportError(cx, "Vtune setup error: %s",
2181 vtuneErrorMessages[status]);
2183 JS_ReportError(cx, "Vtune setup error: %d",
2187 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2191 JS_FRIEND_API(JSBool)
2192 js_StopVtune(JSContext *cx, uintN argc, jsval *vp)
2194 U32 status = VTStopSampling(1);
2196 if (status < sizeof(vtuneErrorMessages))
2197 JS_ReportError(cx, "Vtune shutdown error: %s",
2198 vtuneErrorMessages[status]);
2200 JS_ReportError(cx, "Vtune shutdown error: %d",
2204 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2208 JS_FRIEND_API(JSBool)
2209 js_PauseVtune(JSContext *cx, uintN argc, jsval *vp)
2212 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2216 JS_FRIEND_API(JSBool)
2217 js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
2220 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2224 #endif /* MOZ_VTUNE */
2228 * Ethogram - Javascript wrapper for TraceVis state
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.
2239 #include <sys/time.h>
2241 #include "jstracer.h"
2243 #define ETHOGRAM_BUF_SIZE 65536
2246 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2248 ethogram_finalize(JSContext *cx, JSObject *obj);
2250 static JSClass ethogram_class = {
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
2258 struct EthogramEvent {
2260 TraceVisExitReason r;
2268 compare_strings(const void *k1, const void *k2)
2270 return strcmp((const char *) k1, (const char *) k2) == 0;
2273 class EthogramEventBuffer {
2275 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2278 JSObject *mFilenames;
2281 struct EthogramScriptEntry {
2283 JSString *jsfilename;
2285 EthogramScriptEntry *next;
2287 EthogramScriptEntry *mScripts;
2291 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2293 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2294 mBuf[mWritePos].s = s;
2295 mBuf[mWritePos].r = r;
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;
2308 gettimeofday(&tv, NULL);
2309 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2310 mBuf[mWritePos].tus = tv.tv_usec;
2313 JSString *jsfilename = findScript(filename);
2314 mBuf[mWritePos].filename = jsfilename;
2315 mBuf[mWritePos].lineno = lineno;
2317 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2318 if (mWritePos == mReadPos) {
2319 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2323 inline EthogramEvent *pop() {
2324 EthogramEvent *e = &mBuf[mReadPos];
2325 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2330 return (mReadPos == mWritePos);
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);
2339 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2341 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2345 entry->next = mScripts;
2347 entry->filename = filename;
2348 entry->jsfilename = jsfilename;
2353 void removeScripts(JSContext *cx) {
2354 EthogramScriptEntry *se = mScripts;
2355 while (se != NULL) {
2356 char *filename = se->filename;
2358 JSHashNumber hash = JS_HashString(filename);
2359 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2360 JSHashEntry *he = *hep;
2362 /* we hardly knew he */
2363 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2366 EthogramScriptEntry *se_head = se;
2368 JS_free(cx, se_head);
2372 JSString *findScript(char *filename) {
2373 EthogramScriptEntry *se = mScripts;
2374 while (se != NULL) {
2375 if (compare_strings(se->filename, filename))
2376 return (se->jsfilename);
2382 JSObject *filenames() {
2387 if (mWritePos < mReadPos)
2388 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2390 return mWritePos - mReadPos;
2394 static char jstv_empty[] = "<null>";
2397 jstv_Filename(JSStackFrame *fp)
2399 while (fp && !fp->isScriptFrame())
2401 return (fp && fp->maybeScript() && fp->script()->filename)
2402 ? (char *)fp->script()->filename
2406 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2408 while (fp && fp->pc(cx) == NULL)
2410 return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2413 /* Collect states here and distribute to a matching buffer, if any */
2415 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2417 JSStackFrame *fp = cx->fp();
2419 char *script_file = jstv_Filename(fp);
2420 JSHashNumber hash = JS_HashString(script_file);
2422 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2423 /* update event buffer, flag if overflowed */
2424 JSHashEntry *he = *hep;
2426 EthogramEventBuffer *p;
2427 p = (EthogramEventBuffer *) he->value;
2429 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2434 ethogram_construct(JSContext *cx, uintN argc, jsval *vp)
2436 EthogramEventBuffer *p;
2438 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2442 p->mReadPos = p->mWritePos = 0;
2444 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
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;
2456 gettimeofday(&tv, NULL);
2457 p->mStartSecond = tv.tv_sec;
2460 if (JS_IsConstructing(cx, vp)) {
2461 obj = JS_NewObject(cx, ðogram_class, NULL, NULL);
2465 obj = JS_THIS_OBJECT(cx, vp);
2468 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2469 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2470 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2473 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
2474 JS_SetPrivate(cx, obj, p);
2479 ethogram_finalize(JSContext *cx, JSObject *obj)
2481 EthogramEventBuffer *p;
2482 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, NULL);
2486 p->removeScripts(cx);
2492 ethogram_addScript(JSContext *cx, uintN argc, jsval *vp)
2495 char *filename = NULL;
2496 jsval *argv = JS_ARGV(cx, vp);
2497 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2501 /* silently ignore no args */
2502 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2505 if (JSVAL_IS_STRING(argv[0])) {
2506 str = JSVAL_TO_STRING(argv[0]);
2507 filename = js_DeflateString(cx, str->chars(), str->length());
2512 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv);
2514 p->addScript(cx, obj, filename, str);
2515 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2517 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, &dummy);
2522 ethogram_getAllEvents(JSContext *cx, uintN argc, jsval *vp)
2524 EthogramEventBuffer *p;
2525 jsval *argv = JS_ARGV(cx, vp);
2527 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2531 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv);
2536 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2540 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2541 if (rarray == NULL) {
2542 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2546 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rarray));
2548 for (int i = 0; !p->isEmpty(); i++) {
2550 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2554 EthogramEvent *e = p->pop();
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);
2561 jsval filename = STRING_TO_JSVAL(e->filename);
2562 jsval lineno = INT_TO_JSVAL(e->lineno);
2564 if (!JS_SetProperty(cx, x, "state", &state))
2566 if (!JS_SetProperty(cx, x, "reason", &reason))
2568 if (!JS_SetProperty(cx, x, "ts", &ts))
2570 if (!JS_SetProperty(cx, x, "tus", &tus))
2573 if (!JS_SetProperty(cx, x, "filename", &filename))
2575 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2578 jsval element = OBJECT_TO_JSVAL(x);
2579 JS_SetElement(cx, rarray, i, &element);
2586 ethogram_getNextEvent(JSContext *cx, uintN argc, jsval *vp)
2588 EthogramEventBuffer *p;
2589 jsval *argv = JS_ARGV(cx, vp);
2591 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2595 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, ðogram_class, argv);
2599 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2604 JS_SET_RVAL(cx, vp, JSVAL_NULL);
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);
2614 jsval filename = STRING_TO_JSVAL(e->filename);
2615 jsval lineno = INT_TO_JSVAL(e->lineno);
2617 if (!JS_SetProperty(cx, x, "state", &state))
2619 if (!JS_SetProperty(cx, x, "reason", &reason))
2621 if (!JS_SetProperty(cx, x, "ts", &ts))
2623 if (!JS_SetProperty(cx, x, "tus", &tus))
2625 if (!JS_SetProperty(cx, x, "filename", &filename))
2628 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2631 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(x));
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),
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.
2647 JS_FRIEND_API(JSBool)
2648 js_InitEthogram(JSContext *cx, uintN argc, jsval *vp)
2650 if (!traceVisScriptTable) {
2651 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2655 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, ðogram_class,
2656 ethogram_construct, 0, NULL, ethogram_methods,
2659 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2663 JS_FRIEND_API(JSBool)
2664 js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp)
2666 if (traceVisScriptTable)
2667 JS_HashTableDestroy(traceVisScriptTable);
2669 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2673 #endif /* MOZ_TRACEVIS */
2675 #ifdef MOZ_TRACE_JSCALLS
2678 JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
2680 cx->functionCallback = fcb;
2683 JS_PUBLIC_API(JSFunctionCallback)
2684 JS_GetFunctionCallback(JSContext *cx)
2686 return cx->functionCallback;
2689 #endif /* MOZ_TRACE_JSCALLS */