Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / js / src / jsdbgapi.cpp
blob4f27d972739adbc4798688c2f3085352d009ea51
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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 ***** */
42 * JS debugging API.
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsutil.h"
48 #include "jsclist.h"
49 #include "jsapi.h"
50 #include "jscntxt.h"
51 #include "jsversion.h"
52 #include "jsdbgapi.h"
53 #include "jsemit.h"
54 #include "jsfun.h"
55 #include "jsgc.h"
56 #include "jsinterp.h"
57 #include "jslock.h"
58 #include "jsobj.h"
59 #include "jsopcode.h"
60 #include "jsparse.h"
61 #include "jsscope.h"
62 #include "jsscript.h"
63 #include "jsstaticcheck.h"
64 #include "jsstr.h"
65 #include "jswrapper.h"
67 #include "jsatominlines.h"
68 #include "jsdbgapiinlines.h"
69 #include "jsinterpinlines.h"
70 #include "jsobjinlines.h"
71 #include "jsscopeinlines.h"
72 #include "jsscriptinlines.h"
74 #include "jsautooplen.h"
76 #include "methodjit/MethodJIT.h"
77 #include "methodjit/Retcon.h"
79 using namespace js;
80 using namespace js::gc;
82 typedef struct JSTrap {
83 JSCList links;
84 JSScript *script;
85 jsbytecode *pc;
86 JSOp op;
87 JSTrapHandler handler;
88 jsval closure;
89 } JSTrap;
91 #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
92 #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
93 #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
95 JS_PUBLIC_API(JSBool)
96 JS_GetDebugMode(JSContext *cx)
98 return cx->compartment->debugMode;
101 #ifdef JS_METHODJIT
102 static bool
103 IsScriptLive(JSContext *cx, JSScript *script)
105 for (AllFramesIter i(cx); !i.done(); ++i) {
106 if (i.fp()->maybeScript() == script)
107 return true;
109 return false;
111 #endif
113 JS_PUBLIC_API(void)
114 JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
116 rt->debugMode = debug;
119 #ifdef JS_METHODJIT
120 static void
121 PurgeCallICs(JSContext *cx, JSScript *start)
123 for (JSScript *script = start;
124 &script->links != &cx->compartment->scripts;
125 script = (JSScript *)script->links.next)
127 // Debug mode does not use call ICs.
128 if (script->debugMode)
129 continue;
131 JS_ASSERT(!IsScriptLive(cx, script));
133 if (script->jitNormal)
134 script->jitNormal->nukeScriptDependentICs();
135 if (script->jitCtor)
136 script->jitCtor->nukeScriptDependentICs();
139 #endif
141 JS_FRIEND_API(JSBool)
142 js_SetDebugMode(JSContext *cx, JSBool debug)
144 if (!cx->compartment)
145 return JS_TRUE;
147 cx->compartment->debugMode = debug;
148 #ifdef JS_METHODJIT
149 for (JSScript *script = (JSScript *)cx->compartment->scripts.next;
150 &script->links != &cx->compartment->scripts;
151 script = (JSScript *)script->links.next) {
152 if (script->debugMode != !!debug &&
153 script->hasJITCode() &&
154 !IsScriptLive(cx, script)) {
156 * In the event that this fails, debug mode is left partially on,
157 * leading to a small performance overhead but no loss of
158 * correctness. We set the debug flag to false so that the caller
159 * will not later attempt to use debugging features.
161 js::mjit::Recompiler recompiler(cx, script);
162 if (!recompiler.recompile()) {
164 * If recompilation failed, we could be in a state where
165 * remaining compiled scripts hold call IC references that
166 * have been destroyed by recompilation. Clear those ICs now.
168 PurgeCallICs(cx, script);
169 cx->compartment->debugMode = JS_FALSE;
170 return JS_FALSE;
174 #endif
175 return JS_TRUE;
178 JS_PUBLIC_API(JSBool)
179 JS_SetDebugMode(JSContext *cx, JSBool debug)
181 #ifdef DEBUG
182 for (AllFramesIter i(cx); !i.done(); ++i)
183 JS_ASSERT(!JS_IsScriptFrame(cx, i.fp()));
184 #endif
186 return js_SetDebugMode(cx, debug);
189 JS_FRIEND_API(JSBool)
190 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
192 #ifdef JS_METHODJIT
193 if (!script->singleStepMode == !singleStep)
194 return JS_TRUE;
195 #endif
197 JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
199 #ifdef JS_METHODJIT
200 /* request the next recompile to inject single step interrupts */
201 script->singleStepMode = !!singleStep;
203 js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
204 if (jit && script->singleStepMode != jit->singleStepMode) {
205 js::mjit::Recompiler recompiler(cx, script);
206 if (!recompiler.recompile()) {
207 script->singleStepMode = !singleStep;
208 return JS_FALSE;
211 #endif
212 return JS_TRUE;
215 static JSBool
216 CheckDebugMode(JSContext *cx)
218 JSBool debugMode = JS_GetDebugMode(cx);
220 * :TODO:
221 * This probably should be an assertion, since it's indicative of a severe
222 * API misuse.
224 if (!debugMode) {
225 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
226 NULL, JSMSG_NEED_DEBUG_MODE);
228 return debugMode;
231 JS_PUBLIC_API(JSBool)
232 JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
234 if (!CheckDebugMode(cx))
235 return JS_FALSE;
237 return js_SetSingleStepMode(cx, script, singleStep);
241 * NB: FindTrap must be called with rt->debuggerLock acquired.
243 static JSTrap *
244 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
246 JSTrap *trap;
248 for (trap = (JSTrap *)rt->trapList.next;
249 &trap->links != &rt->trapList;
250 trap = (JSTrap *)trap->links.next) {
251 if (trap->script == script && trap->pc == pc)
252 return trap;
254 return NULL;
257 jsbytecode *
258 js_UntrapScriptCode(JSContext *cx, JSScript *script)
260 jsbytecode *code;
261 JSRuntime *rt;
262 JSTrap *trap;
264 code = script->code;
265 rt = cx->runtime;
266 DBG_LOCK(rt);
267 for (trap = (JSTrap *)rt->trapList.next;
268 &trap->links !=
269 &rt->trapList;
270 trap = (JSTrap *)trap->links.next) {
271 if (trap->script == script &&
272 (size_t)(trap->pc - script->code) < script->length) {
273 if (code == script->code) {
274 jssrcnote *sn, *notes;
275 size_t nbytes;
277 nbytes = script->length * sizeof(jsbytecode);
278 notes = script->notes();
279 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
280 continue;
281 nbytes += (sn - notes + 1) * sizeof *sn;
283 code = (jsbytecode *) cx->malloc(nbytes);
284 if (!code)
285 break;
286 memcpy(code, script->code, nbytes);
287 JS_PURGE_GSN_CACHE(cx);
289 code[trap->pc - script->code] = trap->op;
292 DBG_UNLOCK(rt);
293 return code;
296 JS_PUBLIC_API(JSBool)
297 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
298 JSTrapHandler handler, jsval closure)
300 JSTrap *junk, *trap, *twin;
301 JSRuntime *rt;
302 uint32 sample;
304 if (!CheckDebugMode(cx))
305 return JS_FALSE;
307 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
308 junk = NULL;
309 rt = cx->runtime;
310 DBG_LOCK(rt);
311 trap = FindTrap(rt, script, pc);
312 if (trap) {
313 JS_ASSERT(trap->script == script && trap->pc == pc);
314 JS_ASSERT(*pc == JSOP_TRAP);
315 } else {
316 sample = rt->debuggerMutations;
317 DBG_UNLOCK(rt);
318 trap = (JSTrap *) cx->malloc(sizeof *trap);
319 if (!trap)
320 return JS_FALSE;
321 trap->closure = JSVAL_NULL;
322 DBG_LOCK(rt);
323 twin = (rt->debuggerMutations != sample)
324 ? FindTrap(rt, script, pc)
325 : NULL;
326 if (twin) {
327 junk = trap;
328 trap = twin;
329 } else {
330 JS_APPEND_LINK(&trap->links, &rt->trapList);
331 ++rt->debuggerMutations;
332 trap->script = script;
333 trap->pc = pc;
334 trap->op = (JSOp)*pc;
335 *pc = JSOP_TRAP;
338 trap->handler = handler;
339 trap->closure = closure;
340 DBG_UNLOCK(rt);
341 if (junk)
342 cx->free(junk);
344 #ifdef JS_METHODJIT
345 if (script->hasJITCode()) {
346 js::mjit::Recompiler recompiler(cx, script);
347 if (!recompiler.recompile())
348 return JS_FALSE;
350 #endif
352 return JS_TRUE;
355 JS_PUBLIC_API(JSOp)
356 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
358 JSRuntime *rt;
359 JSTrap *trap;
360 JSOp op;
362 rt = cx->runtime;
363 DBG_LOCK(rt);
364 trap = FindTrap(rt, script, pc);
365 op = trap ? trap->op : (JSOp) *pc;
366 DBG_UNLOCK(rt);
367 return op;
370 static void
371 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
373 ++cx->runtime->debuggerMutations;
374 JS_REMOVE_LINK(&trap->links);
375 *trap->pc = (jsbytecode)trap->op;
376 DBG_UNLOCK(cx->runtime);
377 cx->free(trap);
380 JS_PUBLIC_API(void)
381 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
382 JSTrapHandler *handlerp, jsval *closurep)
384 JSTrap *trap;
386 DBG_LOCK(cx->runtime);
387 trap = FindTrap(cx->runtime, script, pc);
388 if (handlerp)
389 *handlerp = trap ? trap->handler : NULL;
390 if (closurep)
391 *closurep = trap ? trap->closure : JSVAL_NULL;
392 if (trap)
393 DestroyTrapAndUnlock(cx, trap);
394 else
395 DBG_UNLOCK(cx->runtime);
397 #ifdef JS_METHODJIT
398 if (script->hasJITCode()) {
399 mjit::Recompiler recompiler(cx, script);
400 recompiler.recompile();
402 #endif
405 JS_PUBLIC_API(void)
406 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
408 JSRuntime *rt;
409 JSTrap *trap, *next;
410 uint32 sample;
412 rt = cx->runtime;
413 DBG_LOCK(rt);
414 for (trap = (JSTrap *)rt->trapList.next;
415 &trap->links != &rt->trapList;
416 trap = next) {
417 next = (JSTrap *)trap->links.next;
418 if (trap->script == script) {
419 sample = rt->debuggerMutations;
420 DestroyTrapAndUnlock(cx, trap);
421 DBG_LOCK(rt);
422 if (rt->debuggerMutations != sample + 1)
423 next = (JSTrap *)rt->trapList.next;
426 DBG_UNLOCK(rt);
429 JS_PUBLIC_API(void)
430 JS_ClearAllTraps(JSContext *cx)
432 JSRuntime *rt;
433 JSTrap *trap, *next;
434 uint32 sample;
436 rt = cx->runtime;
437 DBG_LOCK(rt);
438 for (trap = (JSTrap *)rt->trapList.next;
439 &trap->links != &rt->trapList;
440 trap = next) {
441 next = (JSTrap *)trap->links.next;
442 sample = rt->debuggerMutations;
443 DestroyTrapAndUnlock(cx, trap);
444 DBG_LOCK(rt);
445 if (rt->debuggerMutations != sample + 1)
446 next = (JSTrap *)rt->trapList.next;
448 DBG_UNLOCK(rt);
452 * NB: js_MarkTraps does not acquire cx->runtime->debuggerLock, since the
453 * debugger should never be racing with the GC (i.e., the debugger must
454 * respect the request model).
456 void
457 js_MarkTraps(JSTracer *trc)
459 JSRuntime *rt = trc->context->runtime;
461 for (JSTrap *trap = (JSTrap *) rt->trapList.next;
462 &trap->links != &rt->trapList;
463 trap = (JSTrap *) trap->links.next) {
464 MarkValue(trc, Valueify(trap->closure), "trap->closure");
468 JS_PUBLIC_API(JSTrapStatus)
469 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
471 JSTrap *trap;
472 jsint op;
473 JSTrapStatus status;
475 DBG_LOCK(cx->runtime);
476 trap = FindTrap(cx->runtime, script, pc);
477 JS_ASSERT(!trap || trap->handler);
478 if (!trap) {
479 op = (JSOp) *pc;
480 DBG_UNLOCK(cx->runtime);
482 /* Defend against "pc for wrong script" API usage error. */
483 JS_ASSERT(op != JSOP_TRAP);
485 #ifdef JS_THREADSAFE
486 /* If the API was abused, we must fail for want of the real op. */
487 if (op == JSOP_TRAP)
488 return JSTRAP_ERROR;
490 /* Assume a race with a debugger thread and try to carry on. */
491 *rval = INT_TO_JSVAL(op);
492 return JSTRAP_CONTINUE;
493 #else
494 /* Always fail if single-threaded (must be an API usage error). */
495 return JSTRAP_ERROR;
496 #endif
498 DBG_UNLOCK(cx->runtime);
501 * It's important that we not use 'trap->' after calling the callback --
502 * the callback might remove the trap!
504 op = (jsint)trap->op;
505 status = trap->handler(cx, script, pc, rval, trap->closure);
506 if (status == JSTRAP_CONTINUE) {
507 /* By convention, return the true op to the interpreter in rval. */
508 *rval = INT_TO_JSVAL(op);
510 return status;
513 #ifdef JS_TRACER
514 static void
515 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
517 if (wasInhibited) {
518 if (!rt->debuggerInhibitsJIT()) {
519 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
520 js_ContextFromLinkField(cl)->updateJITEnabled();
522 } else if (rt->debuggerInhibitsJIT()) {
523 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
524 js_ContextFromLinkField(cl)->traceJitEnabled = false;
527 #endif
529 JS_PUBLIC_API(JSBool)
530 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
532 #ifdef JS_TRACER
534 AutoLockGC lock(rt);
535 bool wasInhibited = rt->debuggerInhibitsJIT();
536 #endif
537 rt->globalDebugHooks.interruptHook = hook;
538 rt->globalDebugHooks.interruptHookData = closure;
539 #ifdef JS_TRACER
540 JITInhibitingHookChange(rt, wasInhibited);
542 #endif
543 return JS_TRUE;
546 JS_PUBLIC_API(JSBool)
547 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
549 #ifdef JS_TRACER
550 AutoLockGC lock(rt);
551 bool wasInhibited = rt->debuggerInhibitsJIT();
552 #endif
553 if (hoop)
554 *hoop = rt->globalDebugHooks.interruptHook;
555 if (closurep)
556 *closurep = rt->globalDebugHooks.interruptHookData;
557 rt->globalDebugHooks.interruptHook = 0;
558 rt->globalDebugHooks.interruptHookData = 0;
559 #ifdef JS_TRACER
560 JITInhibitingHookChange(rt, wasInhibited);
561 #endif
562 return JS_TRUE;
565 /************************************************************************/
567 struct JSWatchPoint {
568 JSCList links;
569 JSObject *object; /* weak link, see js_FinalizeObject */
570 const Shape *shape;
571 PropertyOp setter;
572 JSWatchPointHandler handler;
573 JSObject *closure;
574 uintN flags;
577 #define JSWP_LIVE 0x1 /* live because set and not cleared */
578 #define JSWP_HELD 0x2 /* held while running handler/setter */
580 static bool
581 IsWatchedProperty(JSContext *cx, const Shape *shape);
584 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
586 static JSBool
587 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
589 bool ok = true;
590 JSRuntime *rt = cx->runtime;
592 wp->flags &= ~flag;
593 if (wp->flags != 0) {
594 DBG_UNLOCK(rt);
595 return ok;
598 /* Remove wp from the list, then restore wp->shape->setter from wp. */
599 ++rt->debuggerMutations;
600 JS_REMOVE_LINK(&wp->links);
601 DBG_UNLOCK(rt);
604 * If the property isn't found on wp->object, then someone else must have deleted it,
605 * and we don't need to change the property attributes.
607 const Shape *shape = wp->shape;
608 const Shape *wprop = wp->object->nativeLookup(shape->id);
609 if (wprop &&
610 wprop->hasSetterValue() == shape->hasSetterValue() &&
611 IsWatchedProperty(cx, wprop)) {
612 shape = wp->object->changeProperty(cx, wprop, 0, wprop->attributes(),
613 wprop->getter(), wp->setter);
614 if (!shape)
615 ok = false;
618 cx->free(wp);
619 return ok;
623 * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
624 * the debugger should never be racing with the GC (i.e., the debugger must
625 * respect the request model).
627 void
628 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
630 JSRuntime *rt;
631 JSWatchPoint *wp;
633 rt = trc->context->runtime;
635 for (wp = (JSWatchPoint *)rt->watchPointList.next;
636 &wp->links != &rt->watchPointList;
637 wp = (JSWatchPoint *)wp->links.next) {
638 if (wp->object == obj) {
639 wp->shape->trace(trc);
640 if (wp->shape->hasSetterValue() && wp->setter)
641 MarkObject(trc, *CastAsObject(wp->setter), "wp->setter");
642 MarkObject(trc, *wp->closure, "wp->closure");
647 void
648 js_SweepWatchPoints(JSContext *cx)
650 JSRuntime *rt;
651 JSWatchPoint *wp, *next;
652 uint32 sample;
654 rt = cx->runtime;
655 DBG_LOCK(rt);
656 for (wp = (JSWatchPoint *)rt->watchPointList.next;
657 &wp->links != &rt->watchPointList;
658 wp = next) {
659 next = (JSWatchPoint *)wp->links.next;
660 if (IsAboutToBeFinalized(cx, wp->object)) {
661 sample = rt->debuggerMutations;
663 /* Ignore failures. */
664 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
665 DBG_LOCK(rt);
666 if (rt->debuggerMutations != sample + 1)
667 next = (JSWatchPoint *)rt->watchPointList.next;
670 DBG_UNLOCK(rt);
676 * NB: LockedFindWatchPoint must be called with rt->debuggerLock acquired.
678 static JSWatchPoint *
679 LockedFindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
681 JSWatchPoint *wp;
683 for (wp = (JSWatchPoint *)rt->watchPointList.next;
684 &wp->links != &rt->watchPointList;
685 wp = (JSWatchPoint *)wp->links.next) {
686 if (wp->object == obj && wp->shape->id == id)
687 return wp;
689 return NULL;
692 static JSWatchPoint *
693 FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
695 JSWatchPoint *wp;
697 DBG_LOCK(rt);
698 wp = LockedFindWatchPoint(rt, obj, id);
699 DBG_UNLOCK(rt);
700 return wp;
703 JSBool
704 js_watch_set(JSContext *cx, JSObject *obj, jsid id, Value *vp)
706 JSRuntime *rt = cx->runtime;
707 DBG_LOCK(rt);
708 for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
709 &wp->links != &rt->watchPointList;
710 wp = (JSWatchPoint *)wp->links.next) {
711 const Shape *shape = wp->shape;
712 if (wp->object == obj && SHAPE_USERID(shape) == id &&
713 !(wp->flags & JSWP_HELD)) {
714 wp->flags |= JSWP_HELD;
715 DBG_UNLOCK(rt);
717 jsid propid = shape->id;
718 jsid userid = SHAPE_USERID(shape);
720 /* NB: wp is held, so we can safely dereference it still. */
721 if (!wp->handler(cx, obj, propid,
722 obj->containsSlot(shape->slot)
723 ? Jsvalify(obj->nativeGetSlot(shape->slot))
724 : JSVAL_VOID,
725 Jsvalify(vp), wp->closure)) {
726 DBG_LOCK(rt);
727 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
728 return JS_FALSE;
731 /* Handler could have redefined the shape; see bug 624050. */
732 shape = wp->shape;
735 * Pass the output of the handler to the setter. Security wrappers
736 * prevent any funny business between watchpoints and setters.
738 JSBool ok = !wp->setter ||
739 (shape->hasSetterValue()
740 ? ExternalInvoke(cx, obj,
741 ObjectValue(*CastAsObject(wp->setter)),
742 1, vp, vp)
743 : CallJSPropertyOpSetter(cx, wp->setter, obj, userid, vp));
745 DBG_LOCK(rt);
746 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
749 DBG_UNLOCK(rt);
750 return JS_TRUE;
753 static JSBool
754 js_watch_set_wrapper(JSContext *cx, uintN argc, Value *vp)
756 JSObject *obj = ComputeThisFromVp(cx, vp);
757 if (!obj)
758 return false;
760 JSObject &funobj = JS_CALLEE(cx, vp).toObject();
761 JSFunction *wrapper = funobj.getFunctionPrivate();
762 jsid userid = ATOM_TO_JSID(wrapper->atom);
764 JS_SET_RVAL(cx, vp, argc ? JS_ARGV(cx, vp)[0] : UndefinedValue());
765 return js_watch_set(cx, obj, userid, vp);
768 static bool
769 IsWatchedProperty(JSContext *cx, const Shape *shape)
771 if (shape->hasSetterValue()) {
772 JSObject *funobj = shape->setterObject();
773 if (!funobj || !funobj->isFunction())
774 return false;
776 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
777 return fun->maybeNative() == js_watch_set_wrapper;
779 return shape->setterOp() == js_watch_set;
783 * Return an appropriate setter to substitute for |setter| on a property
784 * with attributes |attrs|, to implement a watchpoint on the property named
785 * |id|.
787 static PropertyOp
788 WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, PropertyOp setter)
790 JSAtom *atom;
791 JSFunction *wrapper;
793 /* Wrap a JSPropertyOp setter simply by returning our own JSPropertyOp. */
794 if (!(attrs & JSPROP_SETTER))
795 return &js_watch_set; /* & to silence schoolmarmish MSVC */
798 * Wrap a JSObject * setter by constructing our own JSFunction * that saves the
799 * property id as the function name, and calls js_watch_set.
801 if (JSID_IS_ATOM(id)) {
802 atom = JSID_TO_ATOM(id);
803 } else if (JSID_IS_INT(id)) {
804 if (!js_ValueToStringId(cx, IdToValue(id), &id))
805 return NULL;
806 atom = JSID_TO_ATOM(id);
807 } else {
808 atom = NULL;
811 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
812 setter ? CastAsObject(setter)->getParent() : NULL, atom);
813 if (!wrapper)
814 return NULL;
815 return CastAsPropertyOp(FUN_OBJECT(wrapper));
818 static bool
819 UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const js::Shape *newShape)
821 JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id);
822 JS_ASSERT(!IsWatchedProperty(cx, newShape));
824 /* Create a watching setter we can substitute for the new shape's setter. */
825 js::PropertyOp watchingSetter = WrapWatchedSetter(cx, newShape->id, newShape->attributes(),
826 newShape->setter());
827 if (!watchingSetter)
828 return false;
831 * Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will
832 * return a new shape, or mutate this one.
834 js::PropertyOp originalSetter = newShape->setter();
837 * Drop the watching setter into the object, in place of newShape. Note that a single
838 * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape: we
839 * wrap all (JSPropertyOp, not JSObject *) setters with js_watch_set, so shapes that
840 * differ only in their setter may all get wrapped to the same shape.
842 const js::Shape *watchingShape =
843 js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(),
844 newShape->getter(), watchingSetter);
845 if (!watchingShape)
846 return false;
848 /* Update the watchpoint with the new shape and its original setter. */
849 wp->setter = originalSetter;
850 wp->shape = watchingShape;
852 return true;
855 bool
856 js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const js::Shape *newShape)
859 * The watchpoint code uses the normal property-modification functions to install its
860 * own watchpoint-aware shapes. Those functions report those changes back to the
861 * watchpoint code, just as they do user-level changes. So if this change is
862 * installing a watchpoint-aware shape, it's something we asked for ourselves, and can
863 * proceed without interference.
865 if (IsWatchedProperty(cx, newShape))
866 return true;
868 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id);
869 if (!wp)
870 return true;
872 return UpdateWatchpointShape(cx, wp, newShape);
876 * Return the underlying setter for |shape| on |obj|, seeing through any
877 * watchpoint-wrapping. Note that we need |obj| to disambiguate, since a single
878 * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape; see the
879 * comments in UpdateWatchpointShape.
881 static PropertyOp
882 UnwrapSetter(JSContext *cx, JSObject *obj, const Shape *shape)
884 /* If it's not a watched property, its setter is not wrapped. */
885 if (!IsWatchedProperty(cx, shape))
886 return shape->setter();
888 /* Look up the watchpoint, from which we can retrieve the underlying setter. */
889 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, shape->id);
892 * Since we know |shape| is watched, we *must* find a watchpoint: we should never
893 * leave wrapped setters lying around in shapes after removing a watchpoint.
895 JS_ASSERT(wp);
897 return wp->setter;
900 JS_PUBLIC_API(JSBool)
901 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
902 JSWatchPointHandler handler, JSObject *closure)
904 JSObject *origobj;
905 Value v;
906 uintN attrs;
907 jsid propid;
909 origobj = obj;
910 OBJ_TO_INNER_OBJECT(cx, obj);
911 if (!obj)
912 return JS_FALSE;
914 AutoValueRooter idroot(cx);
915 if (JSID_IS_INT(id)) {
916 propid = id;
917 } else {
918 if (!js_ValueToStringId(cx, IdToValue(id), &propid))
919 return JS_FALSE;
920 propid = js_CheckForStringIndex(propid);
921 idroot.set(IdToValue(propid));
925 * If, by unwrapping and innerizing, we changed the object, check
926 * again to make sure that we're allowed to set a watch point.
928 if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
929 return JS_FALSE;
931 if (!obj->isNative()) {
932 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
933 obj->getClass()->name);
934 return JS_FALSE;
937 JSObject *pobj;
938 JSProperty *prop;
939 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
940 return JS_FALSE;
941 const Shape *shape = (Shape *) prop;
942 JSRuntime *rt = cx->runtime;
943 if (!shape) {
944 /* Check for a deleted symbol watchpoint, which holds its property. */
945 JSWatchPoint *wp = FindWatchPoint(rt, obj, propid);
946 if (!wp) {
947 /* Make a new property in obj so we can watch for the first set. */
948 if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
949 JSPROP_ENUMERATE, 0, 0, &prop)) {
950 return JS_FALSE;
952 shape = (Shape *) prop;
954 } else if (pobj != obj) {
955 /* Clone the prototype property so we can watch the right object. */
956 AutoValueRooter valroot(cx);
957 PropertyOp getter, setter;
958 uintN attrs, flags;
959 intN shortid;
961 if (pobj->isNative()) {
962 valroot.set(pobj->containsSlot(shape->slot)
963 ? pobj->nativeGetSlot(shape->slot)
964 : UndefinedValue());
965 getter = shape->getter();
966 setter = UnwrapSetter(cx, pobj, shape);
967 attrs = shape->attributes();
968 flags = shape->getFlags();
969 shortid = shape->shortid;
970 } else {
971 if (!pobj->getProperty(cx, propid, valroot.addr()) ||
972 !pobj->getAttributes(cx, propid, &attrs)) {
973 return JS_FALSE;
975 getter = setter = NULL;
976 flags = 0;
977 shortid = 0;
980 /* Recall that obj is native, whether or not pobj is native. */
981 if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
982 getter, setter, attrs, flags,
983 shortid, &prop)) {
984 return JS_FALSE;
986 shape = (Shape *) prop;
990 * At this point, prop/shape exists in obj, obj is locked, and we must
991 * unlock the object before returning.
993 DBG_LOCK(rt);
994 JSWatchPoint *wp = LockedFindWatchPoint(rt, obj, propid);
995 if (!wp) {
996 DBG_UNLOCK(rt);
997 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
998 if (!wp)
999 return JS_FALSE;
1000 wp->handler = NULL;
1001 wp->closure = NULL;
1002 wp->object = obj;
1003 wp->shape = NULL;
1004 wp->flags = JSWP_LIVE;
1006 /* XXXbe nest in obj lock here */
1007 if (!UpdateWatchpointShape(cx, wp, shape)) {
1008 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
1009 JS_INIT_CLIST(&wp->links);
1010 DBG_LOCK(rt);
1011 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1012 return JS_FALSE;
1016 * Now that wp is fully initialized, append it to rt's wp list.
1017 * Because obj is locked we know that no other thread could have added
1018 * a watchpoint for (obj, propid).
1020 DBG_LOCK(rt);
1021 JS_ASSERT(!LockedFindWatchPoint(rt, obj, propid));
1022 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
1023 ++rt->debuggerMutations;
1025 wp->handler = handler;
1026 wp->closure = reinterpret_cast<JSObject*>(closure);
1027 DBG_UNLOCK(rt);
1028 return JS_TRUE;
1031 JS_PUBLIC_API(JSBool)
1032 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
1033 JSWatchPointHandler *handlerp, JSObject **closurep)
1035 JSRuntime *rt;
1036 JSWatchPoint *wp;
1038 rt = cx->runtime;
1039 DBG_LOCK(rt);
1040 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1041 &wp->links != &rt->watchPointList;
1042 wp = (JSWatchPoint *)wp->links.next) {
1043 if (wp->object == obj && SHAPE_USERID(wp->shape) == id) {
1044 if (handlerp)
1045 *handlerp = wp->handler;
1046 if (closurep)
1047 *closurep = wp->closure;
1048 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1051 DBG_UNLOCK(rt);
1052 if (handlerp)
1053 *handlerp = NULL;
1054 if (closurep)
1055 *closurep = NULL;
1056 return JS_TRUE;
1059 JS_PUBLIC_API(JSBool)
1060 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
1062 JSRuntime *rt;
1063 JSWatchPoint *wp, *next;
1064 uint32 sample;
1066 rt = cx->runtime;
1067 DBG_LOCK(rt);
1068 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1069 &wp->links != &rt->watchPointList;
1070 wp = next) {
1071 next = (JSWatchPoint *)wp->links.next;
1072 if (wp->object == obj) {
1073 sample = rt->debuggerMutations;
1074 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1075 return JS_FALSE;
1076 DBG_LOCK(rt);
1077 if (rt->debuggerMutations != sample + 1)
1078 next = (JSWatchPoint *)rt->watchPointList.next;
1081 DBG_UNLOCK(rt);
1082 return JS_TRUE;
1085 JS_PUBLIC_API(JSBool)
1086 JS_ClearAllWatchPoints(JSContext *cx)
1088 JSRuntime *rt;
1089 JSWatchPoint *wp, *next;
1090 uint32 sample;
1092 rt = cx->runtime;
1093 DBG_LOCK(rt);
1094 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1095 &wp->links != &rt->watchPointList;
1096 wp = next) {
1097 next = (JSWatchPoint *)wp->links.next;
1098 sample = rt->debuggerMutations;
1099 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1100 return JS_FALSE;
1101 DBG_LOCK(rt);
1102 if (rt->debuggerMutations != sample + 1)
1103 next = (JSWatchPoint *)rt->watchPointList.next;
1105 DBG_UNLOCK(rt);
1106 return JS_TRUE;
1109 /************************************************************************/
1111 JS_PUBLIC_API(uintN)
1112 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1114 return js_PCToLineNumber(cx, script, pc);
1117 JS_PUBLIC_API(jsbytecode *)
1118 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1120 return js_LineNumberToPC(script, lineno);
1123 JS_PUBLIC_API(jsbytecode *)
1124 JS_EndPC(JSContext *cx, JSScript *script)
1126 return script->code + script->length;
1129 JS_PUBLIC_API(uintN)
1130 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
1132 return fun->nargs;
1135 JS_PUBLIC_API(JSBool)
1136 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
1138 return fun->script()->bindings.hasLocalNames();
1141 extern JS_PUBLIC_API(jsuword *)
1142 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
1144 *markp = JS_ARENA_MARK(&cx->tempPool);
1145 return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
1148 extern JS_PUBLIC_API(JSAtom *)
1149 JS_LocalNameToAtom(jsuword w)
1151 return JS_LOCAL_NAME_TO_ATOM(w);
1154 extern JS_PUBLIC_API(JSString *)
1155 JS_AtomKey(JSAtom *atom)
1157 return ATOM_TO_STRING(atom);
1160 extern JS_PUBLIC_API(void)
1161 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
1163 JS_ARENA_RELEASE(&cx->tempPool, mark);
1166 JS_PUBLIC_API(JSScript *)
1167 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1169 return FUN_SCRIPT(fun);
1172 JS_PUBLIC_API(JSNative)
1173 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1175 return Jsvalify(fun->maybeNative());
1178 JS_PUBLIC_API(JSPrincipals *)
1179 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1181 return script->principals;
1184 /************************************************************************/
1187 * Stack Frame Iterator
1189 JS_PUBLIC_API(JSStackFrame *)
1190 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1192 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->prev();
1193 return *iteratorp;
1196 JS_PUBLIC_API(JSScript *)
1197 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1199 return fp->maybeScript();
1202 JS_PUBLIC_API(jsbytecode *)
1203 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1205 return fp->pc(cx);
1208 JS_PUBLIC_API(JSStackFrame *)
1209 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1211 return js_GetScriptedCaller(cx, fp);
1214 JSPrincipals *
1215 js_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1217 JSSecurityCallbacks *callbacks;
1219 if (fp->isFunctionFrame()) {
1220 callbacks = JS_GetSecurityCallbacks(cx);
1221 if (callbacks && callbacks->findObjectPrincipals) {
1222 if (&fp->fun()->compiledFunObj() != &fp->callee())
1223 return callbacks->findObjectPrincipals(cx, &fp->callee());
1224 /* FALL THROUGH */
1227 if (fp->isScriptFrame())
1228 return fp->script()->principals;
1229 return NULL;
1232 JSPrincipals *
1233 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1235 JSPrincipals *principals, *callerPrincipals;
1236 JSSecurityCallbacks *callbacks;
1238 callbacks = JS_GetSecurityCallbacks(cx);
1239 if (callbacks && callbacks->findObjectPrincipals)
1240 principals = callbacks->findObjectPrincipals(cx, callee);
1241 else
1242 principals = NULL;
1243 if (!caller)
1244 return principals;
1245 callerPrincipals = js_StackFramePrincipals(cx, caller);
1246 return (callerPrincipals && principals &&
1247 callerPrincipals->subsume(callerPrincipals, principals))
1248 ? principals
1249 : callerPrincipals;
1252 JS_PUBLIC_API(void *)
1253 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1255 if (fp->annotation() && fp->isScriptFrame()) {
1256 JSPrincipals *principals = js_StackFramePrincipals(cx, fp);
1258 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1260 * Give out an annotation only if privileges have not been revoked
1261 * or disabled globally.
1263 return fp->annotation();
1267 return NULL;
1270 JS_PUBLIC_API(void)
1271 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1273 fp->setAnnotation(annotation);
1276 JS_PUBLIC_API(void *)
1277 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1279 JSPrincipals *principals;
1281 principals = js_StackFramePrincipals(cx, fp);
1282 if (!principals)
1283 return NULL;
1284 return principals->getPrincipalArray(cx, principals);
1287 JS_PUBLIC_API(JSBool)
1288 JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp)
1290 return !fp->isDummyFrame();
1293 /* this is deprecated, use JS_GetFrameScopeChain instead */
1294 JS_PUBLIC_API(JSObject *)
1295 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1297 return &fp->scopeChain();
1300 JS_PUBLIC_API(JSObject *)
1301 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1303 JS_ASSERT(cx->stack().contains(fp));
1305 js::AutoCompartment ac(cx, &fp->scopeChain());
1306 if (!ac.enter())
1307 return NULL;
1309 /* Force creation of argument and call objects if not yet created */
1310 (void) JS_GetFrameCallObject(cx, fp);
1311 return GetScopeChain(cx, fp);
1314 JS_PUBLIC_API(JSObject *)
1315 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1317 JS_ASSERT(cx->stack().contains(fp));
1319 if (!fp->isFunctionFrame())
1320 return NULL;
1322 js::AutoCompartment ac(cx, &fp->scopeChain());
1323 if (!ac.enter())
1324 return NULL;
1326 /* Force creation of argument object if not yet created */
1327 (void) js_GetArgsObject(cx, fp);
1330 * XXX ill-defined: null return here means error was reported, unlike a
1331 * null returned above or in the #else
1333 return js_GetCallObject(cx, fp);
1336 JS_PUBLIC_API(JSBool)
1337 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv)
1339 if (fp->isDummyFrame())
1340 return false;
1342 js::AutoCompartment ac(cx, &fp->scopeChain());
1343 if (!ac.enter())
1344 return false;
1346 if (!fp->computeThis(cx))
1347 return false;
1348 *thisv = Jsvalify(fp->thisValue());
1349 return true;
1352 JS_PUBLIC_API(JSFunction *)
1353 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1355 return fp->maybeFun();
1358 JS_PUBLIC_API(JSObject *)
1359 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1361 if (!fp->isFunctionFrame())
1362 return NULL;
1364 JS_ASSERT(fp->callee().isFunction());
1365 JS_ASSERT(fp->callee().getPrivate() == fp->fun());
1366 return &fp->callee();
1369 JS_PUBLIC_API(JSBool)
1370 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1372 return fp->isConstructing();
1375 JS_PUBLIC_API(JSObject *)
1376 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1378 return fp->maybeCallee();
1381 JS_PUBLIC_API(JSBool)
1382 JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
1384 Value v;
1386 if (!fp->getValidCalleeObject(cx, &v))
1387 return false;
1388 *vp = Jsvalify(v);
1389 return true;
1392 JS_PUBLIC_API(JSBool)
1393 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1395 return fp->isDebuggerFrame();
1398 JS_PUBLIC_API(jsval)
1399 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1401 return Jsvalify(fp->returnValue());
1404 JS_PUBLIC_API(void)
1405 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1407 #ifdef JS_METHODJIT
1408 JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode);
1409 #endif
1410 assertSameCompartment(cx, fp, rval);
1411 fp->setReturnValue(Valueify(rval));
1414 /************************************************************************/
1416 JS_PUBLIC_API(const char *)
1417 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1419 return script->filename;
1422 JS_PUBLIC_API(uintN)
1423 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1425 return script->lineno;
1428 JS_PUBLIC_API(uintN)
1429 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1431 return js_GetScriptLineExtent(script);
1434 JS_PUBLIC_API(JSVersion)
1435 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1437 return VersionNumber(script->getVersion());
1440 /***************************************************************************/
1442 JS_PUBLIC_API(void)
1443 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1445 rt->globalDebugHooks.newScriptHook = hook;
1446 rt->globalDebugHooks.newScriptHookData = callerdata;
1449 JS_PUBLIC_API(void)
1450 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1451 void *callerdata)
1453 rt->globalDebugHooks.destroyScriptHook = hook;
1454 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1457 /***************************************************************************/
1459 JS_PUBLIC_API(JSBool)
1460 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1461 const jschar *chars, uintN length,
1462 const char *filename, uintN lineno,
1463 jsval *rval)
1465 JS_ASSERT_NOT_ON_TRACE(cx);
1467 if (!CheckDebugMode(cx))
1468 return false;
1470 JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1471 if (!scobj)
1472 return false;
1474 js::AutoCompartment ac(cx, scobj);
1475 if (!ac.enter())
1476 return false;
1479 * NB: This function breaks the assumption that the compiler can see all
1480 * calls and properly compute a static level. In order to get around this,
1481 * we use a static level that will cause us not to attempt to optimize
1482 * variable references made by this frame.
1484 JSScript *script = Compiler::compileScript(cx, scobj, fp, js_StackFramePrincipals(cx, fp),
1485 TCF_COMPILE_N_GO, chars, length,
1486 filename, lineno, NULL,
1487 UpvarCookie::UPVAR_LEVEL_LIMIT);
1489 if (!script)
1490 return false;
1492 bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1494 js_DestroyScript(cx, script);
1495 return ok;
1498 JS_PUBLIC_API(JSBool)
1499 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1500 const char *bytes, uintN length,
1501 const char *filename, uintN lineno,
1502 jsval *rval)
1504 jschar *chars;
1505 JSBool ok;
1506 size_t len = length;
1508 if (!CheckDebugMode(cx))
1509 return JS_FALSE;
1511 chars = js_InflateString(cx, bytes, &len);
1512 if (!chars)
1513 return JS_FALSE;
1514 length = (uintN) len;
1515 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1516 rval);
1517 cx->free(chars);
1519 return ok;
1522 /************************************************************************/
1524 /* This all should be reworked to avoid requiring JSScopeProperty types. */
1526 JS_PUBLIC_API(JSScopeProperty *)
1527 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1529 const Shape *shape;
1531 /* The caller passes null in *iteratorp to get things started. */
1532 shape = (Shape *) *iteratorp;
1533 if (!shape) {
1534 shape = obj->lastProperty();
1535 } else {
1536 shape = shape->previous();
1537 if (!shape->previous()) {
1538 JS_ASSERT(JSID_IS_EMPTY(shape->id));
1539 shape = NULL;
1543 return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape));
1546 JS_PUBLIC_API(JSBool)
1547 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1548 JSPropertyDesc *pd)
1550 assertSameCompartment(cx, obj);
1551 Shape *shape = (Shape *) sprop;
1552 pd->id = IdToJsval(shape->id);
1554 JSBool wasThrowing = cx->isExceptionPending();
1555 Value lastException = UndefinedValue();
1556 if (wasThrowing)
1557 lastException = cx->getPendingException();
1558 cx->clearPendingException();
1560 if (!js_GetProperty(cx, obj, shape->id, Valueify(&pd->value))) {
1561 if (!cx->isExceptionPending()) {
1562 pd->flags = JSPD_ERROR;
1563 pd->value = JSVAL_VOID;
1564 } else {
1565 pd->flags = JSPD_EXCEPTION;
1566 pd->value = Jsvalify(cx->getPendingException());
1568 } else {
1569 pd->flags = 0;
1572 if (wasThrowing)
1573 cx->setPendingException(lastException);
1575 pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
1576 | (!shape->writable() ? JSPD_READONLY : 0)
1577 | (!shape->configurable() ? JSPD_PERMANENT : 0);
1578 pd->spare = 0;
1579 if (shape->getter() == GetCallArg) {
1580 pd->slot = shape->shortid;
1581 pd->flags |= JSPD_ARGUMENT;
1582 } else if (shape->getter() == GetCallVar) {
1583 pd->slot = shape->shortid;
1584 pd->flags |= JSPD_VARIABLE;
1585 } else {
1586 pd->slot = 0;
1588 pd->alias = JSVAL_VOID;
1590 if (obj->containsSlot(shape->slot)) {
1591 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1592 const Shape &aprop = r.front();
1593 if (&aprop != shape && aprop.slot == shape->slot) {
1594 pd->alias = IdToJsval(aprop.id);
1595 break;
1599 return JS_TRUE;
1602 JS_PUBLIC_API(JSBool)
1603 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1605 assertSameCompartment(cx, obj);
1606 Class *clasp = obj->getClass();
1607 if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1608 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1609 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1610 return JS_FALSE;
1612 if (!clasp->enumerate(cx, obj))
1613 return JS_FALSE;
1615 /* Return an empty pda early if obj has no own properties. */
1616 if (obj->nativeEmpty()) {
1617 pda->length = 0;
1618 pda->array = NULL;
1619 return JS_TRUE;
1622 uint32 n = obj->propertyCount();
1623 JSPropertyDesc *pd = (JSPropertyDesc *) cx->malloc(size_t(n) * sizeof(JSPropertyDesc));
1624 if (!pd)
1625 return JS_FALSE;
1626 uint32 i = 0;
1627 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1628 if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1629 goto bad;
1630 if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1631 goto bad;
1632 Shape *shape = const_cast<Shape *>(&r.front());
1633 if (!JS_GetPropertyDesc(cx, obj, reinterpret_cast<JSScopeProperty *>(shape), &pd[i]))
1634 goto bad;
1635 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1636 goto bad;
1637 if (++i == n)
1638 break;
1640 pda->length = i;
1641 pda->array = pd;
1642 return JS_TRUE;
1644 bad:
1645 pda->length = i + 1;
1646 pda->array = pd;
1647 JS_PutPropertyDescArray(cx, pda);
1648 return JS_FALSE;
1651 JS_PUBLIC_API(void)
1652 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1654 JSPropertyDesc *pd;
1655 uint32 i;
1657 pd = pda->array;
1658 for (i = 0; i < pda->length; i++) {
1659 js_RemoveRoot(cx->runtime, &pd[i].id);
1660 js_RemoveRoot(cx->runtime, &pd[i].value);
1661 if (pd[i].flags & JSPD_ALIAS)
1662 js_RemoveRoot(cx->runtime, &pd[i].alias);
1664 cx->free(pd);
1667 /************************************************************************/
1669 JS_PUBLIC_API(JSBool)
1670 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1672 rt->globalDebugHooks.debuggerHandler = handler;
1673 rt->globalDebugHooks.debuggerHandlerData = closure;
1674 return JS_TRUE;
1677 JS_PUBLIC_API(JSBool)
1678 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1680 rt->globalDebugHooks.sourceHandler = handler;
1681 rt->globalDebugHooks.sourceHandlerData = closure;
1682 return JS_TRUE;
1685 JS_PUBLIC_API(JSBool)
1686 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1688 rt->globalDebugHooks.executeHook = hook;
1689 rt->globalDebugHooks.executeHookData = closure;
1690 return JS_TRUE;
1693 JS_PUBLIC_API(JSBool)
1694 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1696 #ifdef JS_TRACER
1698 AutoLockGC lock(rt);
1699 bool wasInhibited = rt->debuggerInhibitsJIT();
1700 #endif
1701 rt->globalDebugHooks.callHook = hook;
1702 rt->globalDebugHooks.callHookData = closure;
1703 #ifdef JS_TRACER
1704 JITInhibitingHookChange(rt, wasInhibited);
1706 #endif
1707 return JS_TRUE;
1710 JS_PUBLIC_API(JSBool)
1711 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1713 rt->globalDebugHooks.throwHook = hook;
1714 rt->globalDebugHooks.throwHookData = closure;
1715 return JS_TRUE;
1718 JS_PUBLIC_API(JSBool)
1719 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1721 rt->globalDebugHooks.debugErrorHook = hook;
1722 rt->globalDebugHooks.debugErrorHookData = closure;
1723 return JS_TRUE;
1726 /************************************************************************/
1728 JS_PUBLIC_API(size_t)
1729 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1731 return obj->slotsAndStructSize();
1734 static size_t
1735 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1737 size_t nbytes;
1739 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1740 nbytes += sizeof(JSString);
1741 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1742 return nbytes;
1745 JS_PUBLIC_API(size_t)
1746 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1748 size_t nbytes;
1750 nbytes = sizeof *fun;
1751 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1752 if (FUN_INTERPRETED(fun))
1753 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1754 if (fun->atom)
1755 nbytes += GetAtomTotalSize(cx, fun->atom);
1756 return nbytes;
1759 #include "jsemit.h"
1761 JS_PUBLIC_API(size_t)
1762 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1764 size_t nbytes, pbytes;
1765 jsatomid i;
1766 jssrcnote *sn, *notes;
1767 JSObjectArray *objarray;
1768 JSPrincipals *principals;
1770 nbytes = sizeof *script;
1771 if (script->u.object)
1772 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1774 nbytes += script->length * sizeof script->code[0];
1775 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1776 for (i = 0; i < script->atomMap.length; i++)
1777 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1779 if (script->filename)
1780 nbytes += strlen(script->filename) + 1;
1782 notes = script->notes();
1783 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1784 continue;
1785 nbytes += (sn - notes + 1) * sizeof *sn;
1787 if (JSScript::isValidOffset(script->objectsOffset)) {
1788 objarray = script->objects();
1789 i = objarray->length;
1790 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1791 do {
1792 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1793 } while (i != 0);
1796 if (JSScript::isValidOffset(script->regexpsOffset)) {
1797 objarray = script->regexps();
1798 i = objarray->length;
1799 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1800 do {
1801 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1802 } while (i != 0);
1805 if (JSScript::isValidOffset(script->trynotesOffset)) {
1806 nbytes += sizeof(JSTryNoteArray) +
1807 script->trynotes()->length * sizeof(JSTryNote);
1810 principals = script->principals;
1811 if (principals) {
1812 JS_ASSERT(principals->refcount);
1813 pbytes = sizeof *principals;
1814 if (principals->refcount > 1)
1815 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1816 nbytes += pbytes;
1819 return nbytes;
1822 JS_PUBLIC_API(uint32)
1823 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1825 if (!fp)
1826 fp = js_GetTopStackFrame(cx);
1827 while (fp) {
1828 if (fp->isScriptFrame())
1829 return JS_GetScriptFilenameFlags(fp->script());
1830 fp = fp->prev();
1832 return 0;
1835 JS_PUBLIC_API(uint32)
1836 JS_GetScriptFilenameFlags(JSScript *script)
1838 JS_ASSERT(script);
1839 if (!script->filename)
1840 return JSFILENAME_NULL;
1841 return js_GetScriptFilenameFlags(script->filename);
1844 JS_PUBLIC_API(JSBool)
1845 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1847 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1848 return JS_FALSE;
1849 return JS_TRUE;
1852 JS_PUBLIC_API(JSBool)
1853 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1855 return obj->isSystem();
1858 JS_PUBLIC_API(JSBool)
1859 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1861 obj->setSystem();
1862 return true;
1865 /************************************************************************/
1867 JS_PUBLIC_API(JSObject *)
1868 JS_UnwrapObject(JSContext *cx, JSObject *obj)
1870 return obj->unwrap();
1873 /************************************************************************/
1875 JS_FRIEND_API(void)
1876 js_RevertVersion(JSContext *cx)
1878 cx->clearVersionOverride();
1881 JS_PUBLIC_API(const JSDebugHooks *)
1882 JS_GetGlobalDebugHooks(JSRuntime *rt)
1884 return &rt->globalDebugHooks;
1887 const JSDebugHooks js_NullDebugHooks = {};
1889 JS_PUBLIC_API(JSDebugHooks *)
1890 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1892 JS_ASSERT(hooks);
1893 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1894 LeaveTrace(cx);
1896 #ifdef JS_TRACER
1897 AutoLockGC lock(cx->runtime);
1898 #endif
1899 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1900 cx->debugHooks = hooks;
1901 #ifdef JS_TRACER
1902 cx->updateJITEnabled();
1903 #endif
1904 return old;
1907 JS_PUBLIC_API(JSDebugHooks *)
1908 JS_ClearContextDebugHooks(JSContext *cx)
1910 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1913 JS_PUBLIC_API(JSBool)
1914 JS_StartProfiling()
1916 return Probes::startProfiling();
1919 JS_PUBLIC_API(void)
1920 JS_StopProfiling()
1922 Probes::stopProfiling();
1925 #ifdef MOZ_PROFILING
1927 static JSBool
1928 StartProfiling(JSContext *cx, uintN argc, jsval *vp)
1930 JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling()));
1931 return true;
1934 static JSBool
1935 StopProfiling(JSContext *cx, uintN argc, jsval *vp)
1937 JS_StopProfiling();
1938 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1939 return true;
1942 #ifdef MOZ_SHARK
1944 static JSBool
1945 IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
1947 JS_SET_RVAL(cx, vp, JSVAL_TRUE);
1948 return true;
1951 #endif
1953 static JSFunctionSpec profiling_functions[] = {
1954 JS_FN("startProfiling", StartProfiling, 0,0),
1955 JS_FN("stopProfiling", StopProfiling, 0,0),
1956 #ifdef MOZ_SHARK
1957 /* Keep users of the old shark API happy. */
1958 JS_FN("connectShark", IgnoreAndReturnTrue, 0,0),
1959 JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
1960 JS_FN("startShark", StartProfiling, 0,0),
1961 JS_FN("stopShark", StopProfiling, 0,0),
1962 #endif
1963 JS_FS_END
1966 #endif
1968 JS_PUBLIC_API(JSBool)
1969 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
1971 #ifdef MOZ_PROFILING
1972 return JS_DefineFunctions(cx, obj, profiling_functions);
1973 #else
1974 return true;
1975 #endif
1978 #ifdef MOZ_CALLGRIND
1980 #include <valgrind/callgrind.h>
1982 JS_FRIEND_API(JSBool)
1983 js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
1985 CALLGRIND_START_INSTRUMENTATION;
1986 CALLGRIND_ZERO_STATS;
1987 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1988 return JS_TRUE;
1991 JS_FRIEND_API(JSBool)
1992 js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
1994 CALLGRIND_STOP_INSTRUMENTATION;
1995 JS_SET_RVAL(cx, vp, JSVAL_VOID);
1996 return JS_TRUE;
1999 JS_FRIEND_API(JSBool)
2000 js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
2002 JSString *str;
2004 jsval *argv = JS_ARGV(cx, vp);
2005 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2006 str = JSVAL_TO_STRING(argv[0]);
2007 JSAutoByteString bytes(cx, str);
2008 if (!!bytes) {
2009 CALLGRIND_DUMP_STATS_AT(bytes.ptr());
2010 return JS_TRUE;
2013 CALLGRIND_DUMP_STATS;
2015 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2016 return JS_TRUE;
2019 #endif /* MOZ_CALLGRIND */
2021 #ifdef MOZ_VTUNE
2022 #include <VTuneApi.h>
2024 static const char *vtuneErrorMessages[] = {
2025 "unknown, error #0",
2026 "invalid 'max samples' field",
2027 "invalid 'samples per buffer' field",
2028 "invalid 'sample interval' field",
2029 "invalid path",
2030 "sample file in use",
2031 "invalid 'number of events' field",
2032 "unknown, error #7",
2033 "internal error",
2034 "bad event name",
2035 "VTStopSampling called without calling VTStartSampling",
2036 "no events selected for event-based sampling",
2037 "events selected cannot be run together",
2038 "no sampling parameters",
2039 "sample database already exists",
2040 "sampling already started",
2041 "time-based sampling not supported",
2042 "invalid 'sampling parameters size' field",
2043 "invalid 'event size' field",
2044 "sampling file already bound",
2045 "invalid event path",
2046 "invalid license",
2047 "invalid 'global options' field",
2051 JS_FRIEND_API(JSBool)
2052 js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
2054 VTUNE_EVENT events[] = {
2055 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2056 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2059 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2060 char *default_filename = "mozilla-vtune.tb5";
2061 JSString *str;
2062 U32 status;
2064 VTUNE_SAMPLING_PARAMS params = {
2065 sizeof(VTUNE_SAMPLING_PARAMS),
2066 sizeof(VTUNE_EVENT),
2067 0, 0, /* Reserved fields */
2068 1, /* Initialize in "paused" state */
2069 0, /* Max samples, or 0 for "continuous" */
2070 4096, /* Samples per buffer */
2071 0.1, /* Sampling interval in ms */
2072 1, /* 1 for event-based sampling, 0 for time-based */
2074 n_events,
2075 events,
2076 default_filename,
2079 jsval *argv = JS_ARGV(cx, vp);
2080 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2081 str = JSVAL_TO_STRING(argv[0]);
2082 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2085 status = VTStartSampling(&params);
2087 if (params.tb5Filename != default_filename)
2088 cx->free(params.tb5Filename);
2090 if (status != 0) {
2091 if (status == VTAPI_MULTIPLE_RUNS)
2092 VTStopSampling(0);
2093 if (status < sizeof(vtuneErrorMessages))
2094 JS_ReportError(cx, "Vtune setup error: %s",
2095 vtuneErrorMessages[status]);
2096 else
2097 JS_ReportError(cx, "Vtune setup error: %d",
2098 status);
2099 return false;
2101 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2102 return true;
2105 JS_FRIEND_API(JSBool)
2106 js_StopVtune(JSContext *cx, uintN argc, jsval *vp)
2108 U32 status = VTStopSampling(1);
2109 if (status) {
2110 if (status < sizeof(vtuneErrorMessages))
2111 JS_ReportError(cx, "Vtune shutdown error: %s",
2112 vtuneErrorMessages[status]);
2113 else
2114 JS_ReportError(cx, "Vtune shutdown error: %d",
2115 status);
2116 return false;
2118 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2119 return true;
2122 JS_FRIEND_API(JSBool)
2123 js_PauseVtune(JSContext *cx, uintN argc, jsval *vp)
2125 VTPause();
2126 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2127 return true;
2130 JS_FRIEND_API(JSBool)
2131 js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
2133 VTResume();
2134 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2135 return true;
2138 #endif /* MOZ_VTUNE */
2140 #ifdef MOZ_TRACEVIS
2142 * Ethogram - Javascript wrapper for TraceVis state
2144 * ethology: The scientific study of animal behavior,
2145 * especially as it occurs in a natural environment.
2146 * ethogram: A pictorial catalog of the behavioral patterns of
2147 * an organism or a species.
2150 #if defined(XP_WIN)
2151 #include "jswin.h"
2152 #else
2153 #include <sys/time.h>
2154 #endif
2155 #include "jstracer.h"
2157 #define ETHOGRAM_BUF_SIZE 65536
2159 static JSBool
2160 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2161 static void
2162 ethogram_finalize(JSContext *cx, JSObject *obj);
2164 static JSClass ethogram_class = {
2165 "Ethogram",
2166 JSCLASS_HAS_PRIVATE,
2167 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2168 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2169 JSCLASS_NO_OPTIONAL_MEMBERS
2172 struct EthogramEvent {
2173 TraceVisState s;
2174 TraceVisExitReason r;
2175 int ts;
2176 int tus;
2177 JSString *filename;
2178 int lineno;
2181 static int
2182 compare_strings(const void *k1, const void *k2)
2184 return strcmp((const char *) k1, (const char *) k2) == 0;
2187 class EthogramEventBuffer {
2188 private:
2189 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2190 int mReadPos;
2191 int mWritePos;
2192 JSObject *mFilenames;
2193 int mStartSecond;
2195 struct EthogramScriptEntry {
2196 char *filename;
2197 JSString *jsfilename;
2199 EthogramScriptEntry *next;
2201 EthogramScriptEntry *mScripts;
2203 public:
2204 friend JSBool
2205 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2207 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2208 mBuf[mWritePos].s = s;
2209 mBuf[mWritePos].r = r;
2210 #if defined(XP_WIN)
2211 FILETIME now;
2212 GetSystemTimeAsFileTime(&now);
2213 unsigned long long raw_us = 0.1 *
2214 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2215 (unsigned long long) now.dwLowDateTime);
2216 unsigned int sec = raw_us / 1000000L;
2217 unsigned int usec = raw_us % 1000000L;
2218 mBuf[mWritePos].ts = sec - mStartSecond;
2219 mBuf[mWritePos].tus = usec;
2220 #else
2221 struct timeval tv;
2222 gettimeofday(&tv, NULL);
2223 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2224 mBuf[mWritePos].tus = tv.tv_usec;
2225 #endif
2227 JSString *jsfilename = findScript(filename);
2228 mBuf[mWritePos].filename = jsfilename;
2229 mBuf[mWritePos].lineno = lineno;
2231 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2232 if (mWritePos == mReadPos) {
2233 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2237 inline EthogramEvent *pop() {
2238 EthogramEvent *e = &mBuf[mReadPos];
2239 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2240 return e;
2243 bool isEmpty() {
2244 return (mReadPos == mWritePos);
2247 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2248 JSHashNumber hash = JS_HashString(filename);
2249 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2250 if (*hep != NULL)
2251 return NULL;
2253 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2255 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2256 if (entry == NULL)
2257 return NULL;
2259 entry->next = mScripts;
2260 mScripts = entry;
2261 entry->filename = filename;
2262 entry->jsfilename = jsfilename;
2264 return mScripts;
2267 void removeScripts(JSContext *cx) {
2268 EthogramScriptEntry *se = mScripts;
2269 while (se != NULL) {
2270 char *filename = se->filename;
2272 JSHashNumber hash = JS_HashString(filename);
2273 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2274 JSHashEntry *he = *hep;
2275 if (he) {
2276 /* we hardly knew he */
2277 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2280 EthogramScriptEntry *se_head = se;
2281 se = se->next;
2282 JS_free(cx, se_head);
2286 JSString *findScript(char *filename) {
2287 EthogramScriptEntry *se = mScripts;
2288 while (se != NULL) {
2289 if (compare_strings(se->filename, filename))
2290 return (se->jsfilename);
2291 se = se->next;
2293 return NULL;
2296 JSObject *filenames() {
2297 return mFilenames;
2300 int length() {
2301 if (mWritePos < mReadPos)
2302 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2303 else
2304 return mWritePos - mReadPos;
2308 static char jstv_empty[] = "<null>";
2310 inline char *
2311 jstv_Filename(JSStackFrame *fp)
2313 while (fp && !fp->isScriptFrame())
2314 fp = fp->prev();
2315 return (fp && fp->maybeScript() && fp->script()->filename)
2316 ? (char *)fp->script()->filename
2317 : jstv_empty;
2319 inline uintN
2320 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2322 while (fp && fp->pc(cx) == NULL)
2323 fp = fp->prev();
2324 return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2327 /* Collect states here and distribute to a matching buffer, if any */
2328 JS_FRIEND_API(void)
2329 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2331 JSStackFrame *fp = cx->fp();
2333 char *script_file = jstv_Filename(fp);
2334 JSHashNumber hash = JS_HashString(script_file);
2336 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2337 /* update event buffer, flag if overflowed */
2338 JSHashEntry *he = *hep;
2339 if (he) {
2340 EthogramEventBuffer *p;
2341 p = (EthogramEventBuffer *) he->value;
2343 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2347 static JSBool
2348 ethogram_construct(JSContext *cx, uintN argc, jsval *vp)
2350 EthogramEventBuffer *p;
2352 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2353 if (!p)
2354 return JS_FALSE;
2356 p->mReadPos = p->mWritePos = 0;
2357 p->mScripts = NULL;
2358 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2360 #if defined(XP_WIN)
2361 FILETIME now;
2362 GetSystemTimeAsFileTime(&now);
2363 unsigned long long raw_us = 0.1 *
2364 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2365 (unsigned long long) now.dwLowDateTime);
2366 unsigned int s = raw_us / 1000000L;
2367 p->mStartSecond = s;
2368 #else
2369 struct timeval tv;
2370 gettimeofday(&tv, NULL);
2371 p->mStartSecond = tv.tv_sec;
2372 #endif
2373 JSObject *obj;
2374 if (JS_IsConstructing(cx, vp)) {
2375 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2376 if (!obj)
2377 return JS_FALSE;
2378 } else {
2379 obj = JS_THIS_OBJECT(cx, vp);
2382 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2383 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2384 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2385 return JS_FALSE;
2387 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
2388 JS_SetPrivate(cx, obj, p);
2389 return JS_TRUE;
2392 static void
2393 ethogram_finalize(JSContext *cx, JSObject *obj)
2395 EthogramEventBuffer *p;
2396 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2397 if (!p)
2398 return;
2400 p->removeScripts(cx);
2402 JS_free(cx, p);
2405 static JSBool
2406 ethogram_addScript(JSContext *cx, uintN argc, jsval *vp)
2408 JSString *str;
2409 char *filename = NULL;
2410 jsval *argv = JS_ARGV(cx, vp);
2411 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2412 if (!obj)
2413 return false;
2414 if (argc < 1) {
2415 /* silently ignore no args */
2416 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2417 return true;
2419 if (JSVAL_IS_STRING(argv[0])) {
2420 str = JSVAL_TO_STRING(argv[0]);
2421 filename = js_DeflateString(cx, str->chars(), str->length());
2422 if (!filename)
2423 return false;
2426 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2428 p->addScript(cx, obj, filename, str);
2429 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2430 jsval dummy;
2431 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, &dummy);
2432 return true;
2435 static JSBool
2436 ethogram_getAllEvents(JSContext *cx, uintN argc, jsval *vp)
2438 EthogramEventBuffer *p;
2439 jsval *argv = JS_ARGV(cx, vp);
2441 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2442 if (!obj)
2443 return JS_FALSE;
2445 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2446 if (!p)
2447 return JS_FALSE;
2449 if (p->isEmpty()) {
2450 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2451 return JS_TRUE;
2454 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2455 if (rarray == NULL) {
2456 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2457 return JS_TRUE;
2460 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rarray));
2462 for (int i = 0; !p->isEmpty(); i++) {
2464 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2465 if (x == NULL)
2466 return JS_FALSE;
2468 EthogramEvent *e = p->pop();
2470 jsval state = INT_TO_JSVAL(e->s);
2471 jsval reason = INT_TO_JSVAL(e->r);
2472 jsval ts = INT_TO_JSVAL(e->ts);
2473 jsval tus = INT_TO_JSVAL(e->tus);
2475 jsval filename = STRING_TO_JSVAL(e->filename);
2476 jsval lineno = INT_TO_JSVAL(e->lineno);
2478 if (!JS_SetProperty(cx, x, "state", &state))
2479 return JS_FALSE;
2480 if (!JS_SetProperty(cx, x, "reason", &reason))
2481 return JS_FALSE;
2482 if (!JS_SetProperty(cx, x, "ts", &ts))
2483 return JS_FALSE;
2484 if (!JS_SetProperty(cx, x, "tus", &tus))
2485 return JS_FALSE;
2487 if (!JS_SetProperty(cx, x, "filename", &filename))
2488 return JS_FALSE;
2489 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2490 return JS_FALSE;
2492 jsval element = OBJECT_TO_JSVAL(x);
2493 JS_SetElement(cx, rarray, i, &element);
2496 return JS_TRUE;
2499 static JSBool
2500 ethogram_getNextEvent(JSContext *cx, uintN argc, jsval *vp)
2502 EthogramEventBuffer *p;
2503 jsval *argv = JS_ARGV(cx, vp);
2505 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2506 if (!obj)
2507 return JS_FALSE;
2509 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2510 if (!p)
2511 return JS_FALSE;
2513 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2514 if (x == NULL)
2515 return JS_FALSE;
2517 if (p->isEmpty()) {
2518 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2519 return JS_TRUE;
2522 EthogramEvent *e = p->pop();
2523 jsval state = INT_TO_JSVAL(e->s);
2524 jsval reason = INT_TO_JSVAL(e->r);
2525 jsval ts = INT_TO_JSVAL(e->ts);
2526 jsval tus = INT_TO_JSVAL(e->tus);
2528 jsval filename = STRING_TO_JSVAL(e->filename);
2529 jsval lineno = INT_TO_JSVAL(e->lineno);
2531 if (!JS_SetProperty(cx, x, "state", &state))
2532 return JS_FALSE;
2533 if (!JS_SetProperty(cx, x, "reason", &reason))
2534 return JS_FALSE;
2535 if (!JS_SetProperty(cx, x, "ts", &ts))
2536 return JS_FALSE;
2537 if (!JS_SetProperty(cx, x, "tus", &tus))
2538 return JS_FALSE;
2539 if (!JS_SetProperty(cx, x, "filename", &filename))
2540 return JS_FALSE;
2542 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2543 return JS_FALSE;
2545 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(x));
2547 return JS_TRUE;
2550 static JSFunctionSpec ethogram_methods[] = {
2551 JS_FN("addScript", ethogram_addScript, 1,0),
2552 JS_FN("getAllEvents", ethogram_getAllEvents, 0,0),
2553 JS_FN("getNextEvent", ethogram_getNextEvent, 0,0),
2554 JS_FS_END
2558 * An |Ethogram| organizes the output of a collection of files that should be
2559 * monitored together. A single object gets events for the group.
2561 JS_FRIEND_API(JSBool)
2562 js_InitEthogram(JSContext *cx, uintN argc, jsval *vp)
2564 if (!traceVisScriptTable) {
2565 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2566 NULL, NULL, NULL);
2569 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2570 ethogram_construct, 0, NULL, ethogram_methods,
2571 NULL, NULL);
2573 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2574 return true;
2577 JS_FRIEND_API(JSBool)
2578 js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp)
2580 if (traceVisScriptTable)
2581 JS_HashTableDestroy(traceVisScriptTable);
2583 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2584 return true;
2587 #endif /* MOZ_TRACEVIS */