Bug 578272: Remove Algol-like display optimization. (r=brendan)
[mozilla-central.git] / js / src / jsdbgapi.cpp
blob7cf8c3bdaded15690c52a14a9223cdb472aeb393
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" /* Added by JSIFY */
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"
66 #include "jsatominlines.h"
67 #include "jsobjinlines.h"
68 #include "jsscopeinlines.h"
70 #include "jsautooplen.h"
72 using namespace js;
74 typedef struct JSTrap {
75 JSCList links;
76 JSScript *script;
77 jsbytecode *pc;
78 JSOp op;
79 JSTrapHandler handler;
80 jsval closure;
81 } JSTrap;
83 #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
84 #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
85 #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
88 * NB: FindTrap must be called with rt->debuggerLock acquired.
90 static JSTrap *
91 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
93 JSTrap *trap;
95 for (trap = (JSTrap *)rt->trapList.next;
96 &trap->links != &rt->trapList;
97 trap = (JSTrap *)trap->links.next) {
98 if (trap->script == script && trap->pc == pc)
99 return trap;
101 return NULL;
104 jsbytecode *
105 js_UntrapScriptCode(JSContext *cx, JSScript *script)
107 jsbytecode *code;
108 JSRuntime *rt;
109 JSTrap *trap;
111 code = script->code;
112 rt = cx->runtime;
113 DBG_LOCK(rt);
114 for (trap = (JSTrap *)rt->trapList.next;
115 &trap->links !=
116 &rt->trapList;
117 trap = (JSTrap *)trap->links.next) {
118 if (trap->script == script &&
119 (size_t)(trap->pc - script->code) < script->length) {
120 if (code == script->code) {
121 jssrcnote *sn, *notes;
122 size_t nbytes;
124 nbytes = script->length * sizeof(jsbytecode);
125 notes = script->notes();
126 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
127 continue;
128 nbytes += (sn - notes + 1) * sizeof *sn;
130 code = (jsbytecode *) cx->malloc(nbytes);
131 if (!code)
132 break;
133 memcpy(code, script->code, nbytes);
134 JS_PURGE_GSN_CACHE(cx);
136 code[trap->pc - script->code] = trap->op;
139 DBG_UNLOCK(rt);
140 return code;
143 JS_PUBLIC_API(JSBool)
144 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
145 JSTrapHandler handler, jsval closure)
147 JSTrap *junk, *trap, *twin;
148 JSRuntime *rt;
149 uint32 sample;
151 if (script == JSScript::emptyScript()) {
152 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
153 NULL, JSMSG_READ_ONLY, "empty script");
154 return JS_FALSE;
157 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
158 junk = NULL;
159 rt = cx->runtime;
160 DBG_LOCK(rt);
161 trap = FindTrap(rt, script, pc);
162 if (trap) {
163 JS_ASSERT(trap->script == script && trap->pc == pc);
164 JS_ASSERT(*pc == JSOP_TRAP);
165 } else {
166 sample = rt->debuggerMutations;
167 DBG_UNLOCK(rt);
168 trap = (JSTrap *) cx->malloc(sizeof *trap);
169 if (!trap)
170 return JS_FALSE;
171 trap->closure = JSVAL_NULL;
172 DBG_LOCK(rt);
173 twin = (rt->debuggerMutations != sample)
174 ? FindTrap(rt, script, pc)
175 : NULL;
176 if (twin) {
177 junk = trap;
178 trap = twin;
179 } else {
180 JS_APPEND_LINK(&trap->links, &rt->trapList);
181 ++rt->debuggerMutations;
182 trap->script = script;
183 trap->pc = pc;
184 trap->op = (JSOp)*pc;
185 *pc = JSOP_TRAP;
188 trap->handler = handler;
189 trap->closure = closure;
190 DBG_UNLOCK(rt);
191 if (junk)
192 cx->free(junk);
193 return JS_TRUE;
196 JS_PUBLIC_API(JSOp)
197 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
199 JSRuntime *rt;
200 JSTrap *trap;
201 JSOp op;
203 rt = cx->runtime;
204 DBG_LOCK(rt);
205 trap = FindTrap(rt, script, pc);
206 op = trap ? trap->op : (JSOp) *pc;
207 DBG_UNLOCK(rt);
208 return op;
211 static void
212 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
214 ++cx->runtime->debuggerMutations;
215 JS_REMOVE_LINK(&trap->links);
216 *trap->pc = (jsbytecode)trap->op;
217 DBG_UNLOCK(cx->runtime);
218 cx->free(trap);
221 JS_PUBLIC_API(void)
222 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
223 JSTrapHandler *handlerp, jsval *closurep)
225 JSTrap *trap;
227 DBG_LOCK(cx->runtime);
228 trap = FindTrap(cx->runtime, script, pc);
229 if (handlerp)
230 *handlerp = trap ? trap->handler : NULL;
231 if (closurep)
232 *closurep = trap ? trap->closure : JSVAL_NULL;
233 if (trap)
234 DestroyTrapAndUnlock(cx, trap);
235 else
236 DBG_UNLOCK(cx->runtime);
239 JS_PUBLIC_API(void)
240 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
242 JSRuntime *rt;
243 JSTrap *trap, *next;
244 uint32 sample;
246 rt = cx->runtime;
247 DBG_LOCK(rt);
248 for (trap = (JSTrap *)rt->trapList.next;
249 &trap->links != &rt->trapList;
250 trap = next) {
251 next = (JSTrap *)trap->links.next;
252 if (trap->script == script) {
253 sample = rt->debuggerMutations;
254 DestroyTrapAndUnlock(cx, trap);
255 DBG_LOCK(rt);
256 if (rt->debuggerMutations != sample + 1)
257 next = (JSTrap *)rt->trapList.next;
260 DBG_UNLOCK(rt);
263 JS_PUBLIC_API(void)
264 JS_ClearAllTraps(JSContext *cx)
266 JSRuntime *rt;
267 JSTrap *trap, *next;
268 uint32 sample;
270 rt = cx->runtime;
271 DBG_LOCK(rt);
272 for (trap = (JSTrap *)rt->trapList.next;
273 &trap->links != &rt->trapList;
274 trap = next) {
275 next = (JSTrap *)trap->links.next;
276 sample = rt->debuggerMutations;
277 DestroyTrapAndUnlock(cx, trap);
278 DBG_LOCK(rt);
279 if (rt->debuggerMutations != sample + 1)
280 next = (JSTrap *)rt->trapList.next;
282 DBG_UNLOCK(rt);
286 * NB: js_MarkTraps does not acquire cx->runtime->debuggerLock, since the
287 * debugger should never be racing with the GC (i.e., the debugger must
288 * respect the request model).
290 void
291 js_MarkTraps(JSTracer *trc)
293 JSRuntime *rt = trc->context->runtime;
295 for (JSTrap *trap = (JSTrap *) rt->trapList.next;
296 &trap->links != &rt->trapList;
297 trap = (JSTrap *) trap->links.next) {
298 MarkValue(trc, Valueify(trap->closure), "trap->closure");
302 JS_PUBLIC_API(JSTrapStatus)
303 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
305 JSTrap *trap;
306 jsint op;
307 JSTrapStatus status;
309 DBG_LOCK(cx->runtime);
310 trap = FindTrap(cx->runtime, script, pc);
311 JS_ASSERT(!trap || trap->handler);
312 if (!trap) {
313 op = (JSOp) *pc;
314 DBG_UNLOCK(cx->runtime);
316 /* Defend against "pc for wrong script" API usage error. */
317 JS_ASSERT(op != JSOP_TRAP);
319 #ifdef JS_THREADSAFE
320 /* If the API was abused, we must fail for want of the real op. */
321 if (op == JSOP_TRAP)
322 return JSTRAP_ERROR;
324 /* Assume a race with a debugger thread and try to carry on. */
325 *rval = INT_TO_JSVAL(op);
326 return JSTRAP_CONTINUE;
327 #else
328 /* Always fail if single-threaded (must be an API usage error). */
329 return JSTRAP_ERROR;
330 #endif
332 DBG_UNLOCK(cx->runtime);
335 * It's important that we not use 'trap->' after calling the callback --
336 * the callback might remove the trap!
338 op = (jsint)trap->op;
339 status = trap->handler(cx, script, pc, rval, trap->closure);
340 if (status == JSTRAP_CONTINUE) {
341 /* By convention, return the true op to the interpreter in rval. */
342 *rval = INT_TO_JSVAL(op);
344 return status;
347 #ifdef JS_TRACER
348 static void
349 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
351 if (wasInhibited) {
352 if (!rt->debuggerInhibitsJIT()) {
353 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
354 js_ContextFromLinkField(cl)->updateJITEnabled();
356 } else if (rt->debuggerInhibitsJIT()) {
357 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
358 js_ContextFromLinkField(cl)->jitEnabled = false;
362 static void
363 LeaveTraceRT(JSRuntime *rt)
365 JSThreadData *data = js_CurrentThreadData(rt);
366 JSContext *cx = data ? data->traceMonitor.tracecx : NULL;
367 JS_UNLOCK_GC(rt);
369 if (cx)
370 LeaveTrace(cx);
372 #endif
374 JS_PUBLIC_API(JSBool)
375 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
377 #ifdef JS_TRACER
379 AutoLockGC lock(rt);
380 bool wasInhibited = rt->debuggerInhibitsJIT();
381 #endif
382 rt->globalDebugHooks.interruptHook = hook;
383 rt->globalDebugHooks.interruptHookData = closure;
384 #ifdef JS_TRACER
385 JITInhibitingHookChange(rt, wasInhibited);
387 LeaveTraceRT(rt);
388 #endif
389 return JS_TRUE;
392 JS_PUBLIC_API(JSBool)
393 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
395 #ifdef JS_TRACER
396 AutoLockGC lock(rt);
397 bool wasInhibited = rt->debuggerInhibitsJIT();
398 #endif
399 if (hoop)
400 *hoop = rt->globalDebugHooks.interruptHook;
401 if (closurep)
402 *closurep = rt->globalDebugHooks.interruptHookData;
403 rt->globalDebugHooks.interruptHook = 0;
404 rt->globalDebugHooks.interruptHookData = 0;
405 #ifdef JS_TRACER
406 JITInhibitingHookChange(rt, wasInhibited);
407 #endif
408 return JS_TRUE;
411 /************************************************************************/
413 typedef struct JSWatchPoint {
414 JSCList links;
415 JSObject *object; /* weak link, see js_FinalizeObject */
416 JSScopeProperty *sprop;
417 PropertyOp setter;
418 JSWatchPointHandler handler;
419 JSObject *closure;
420 uintN flags;
421 } JSWatchPoint;
423 #define JSWP_LIVE 0x1 /* live because set and not cleared */
424 #define JSWP_HELD 0x2 /* held while running handler/setter */
426 static bool
427 IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop);
430 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
432 static JSBool
433 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
435 JSBool ok;
436 JSScopeProperty *sprop;
437 JSScope *scope;
438 PropertyOp setter;
440 ok = JS_TRUE;
441 wp->flags &= ~flag;
442 if (wp->flags != 0) {
443 DBG_UNLOCK(cx->runtime);
444 return ok;
448 * Remove wp from the list, then if there are no other watchpoints for
449 * wp->sprop in any scope, restore wp->sprop->setter from wp.
451 ++cx->runtime->debuggerMutations;
452 JS_REMOVE_LINK(&wp->links);
453 sprop = wp->sprop;
456 * Passing null for the scope parameter tells js_GetWatchedSetter to find
457 * any watch point for sprop, and not to lock or unlock rt->debuggerLock.
458 * If js_ChangeNativePropertyAttrs fails, propagate failure after removing
459 * wp->closure's root and freeing wp.
461 setter = js_GetWatchedSetter(cx->runtime, NULL, sprop);
462 DBG_UNLOCK(cx->runtime);
463 if (!setter) {
464 JS_LOCK_OBJ(cx, wp->object);
465 scope = wp->object->scope();
468 * If the property wasn't found on wp->object, or it isn't still being
469 * watched, then someone else must have deleted or unwatched it, and we
470 * don't need to change the property attributes.
472 JSScopeProperty *wprop = scope->lookup(sprop->id);
473 if (wprop &&
474 wprop->hasSetterValue() == sprop->hasSetterValue() &&
475 IsWatchedProperty(cx, wprop)) {
476 sprop = scope->changeProperty(cx, wprop, 0, wprop->attributes(),
477 wprop->getter(), wp->setter);
478 if (!sprop)
479 ok = JS_FALSE;
481 JS_UNLOCK_SCOPE(cx, scope);
484 cx->free(wp);
485 return ok;
489 * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
490 * the debugger should never be racing with the GC (i.e., the debugger must
491 * respect the request model).
493 void
494 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
496 JSRuntime *rt;
497 JSWatchPoint *wp;
499 rt = trc->context->runtime;
501 for (wp = (JSWatchPoint *)rt->watchPointList.next;
502 &wp->links != &rt->watchPointList;
503 wp = (JSWatchPoint *)wp->links.next) {
504 if (wp->object == obj) {
505 wp->sprop->trace(trc);
506 if (wp->sprop->hasSetterValue() && wp->setter)
507 JS_CALL_OBJECT_TRACER(trc, CastAsObject(wp->setter), "wp->setter");
508 JS_CALL_OBJECT_TRACER(trc, wp->closure, "wp->closure");
513 void
514 js_SweepWatchPoints(JSContext *cx)
516 JSRuntime *rt;
517 JSWatchPoint *wp, *next;
518 uint32 sample;
520 rt = cx->runtime;
521 DBG_LOCK(rt);
522 for (wp = (JSWatchPoint *)rt->watchPointList.next;
523 &wp->links != &rt->watchPointList;
524 wp = next) {
525 next = (JSWatchPoint *)wp->links.next;
526 if (js_IsAboutToBeFinalized(wp->object)) {
527 sample = rt->debuggerMutations;
529 /* Ignore failures. */
530 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
531 DBG_LOCK(rt);
532 if (rt->debuggerMutations != sample + 1)
533 next = (JSWatchPoint *)rt->watchPointList.next;
536 DBG_UNLOCK(rt);
542 * NB: FindWatchPoint must be called with rt->debuggerLock acquired.
544 static JSWatchPoint *
545 FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
547 JSWatchPoint *wp;
549 for (wp = (JSWatchPoint *)rt->watchPointList.next;
550 &wp->links != &rt->watchPointList;
551 wp = (JSWatchPoint *)wp->links.next) {
552 if (wp->object->scope() == scope && wp->sprop->id == id)
553 return wp;
555 return NULL;
558 JSScopeProperty *
559 js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
561 JSWatchPoint *wp;
562 JSScopeProperty *sprop;
564 DBG_LOCK(rt);
565 wp = FindWatchPoint(rt, scope, id);
566 sprop = wp ? wp->sprop : NULL;
567 DBG_UNLOCK(rt);
568 return sprop;
572 * Secret handshake with DropWatchPointAndUnlock: if (!scope), we know our
573 * caller has acquired rt->debuggerLock, so we don't have to.
575 PropertyOp
576 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
577 const JSScopeProperty *sprop)
579 PropertyOp setter;
580 JSWatchPoint *wp;
582 setter = NULL;
583 if (scope)
584 DBG_LOCK(rt);
585 for (wp = (JSWatchPoint *)rt->watchPointList.next;
586 &wp->links != &rt->watchPointList;
587 wp = (JSWatchPoint *)wp->links.next) {
588 if ((!scope || wp->object->scope() == scope) && wp->sprop == sprop) {
589 setter = wp->setter;
590 break;
593 if (scope)
594 DBG_UNLOCK(rt);
595 return setter;
598 JSBool
599 js_watch_set(JSContext *cx, JSObject *obj, jsid id, Value *vp)
601 JSRuntime *rt = cx->runtime;
602 DBG_LOCK(rt);
603 for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
604 &wp->links != &rt->watchPointList;
605 wp = (JSWatchPoint *)wp->links.next) {
606 JSScopeProperty *sprop = wp->sprop;
607 if (wp->object == obj && SPROP_USERID(sprop) == id &&
608 !(wp->flags & JSWP_HELD)) {
609 wp->flags |= JSWP_HELD;
610 DBG_UNLOCK(rt);
612 JS_LOCK_OBJ(cx, obj);
613 jsid propid = sprop->id;
614 jsid userid = SPROP_USERID(sprop);
615 JSScope *scope = obj->scope();
616 JS_UNLOCK_OBJ(cx, obj);
618 /* NB: wp is held, so we can safely dereference it still. */
619 if (!wp->handler(cx, obj, propid,
620 SPROP_HAS_VALID_SLOT(sprop, scope)
621 ? Jsvalify(obj->getSlotMT(cx, sprop->slot))
622 : JSVAL_VOID,
623 Jsvalify(vp), wp->closure)) {
624 DBG_LOCK(rt);
625 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
626 return JS_FALSE;
630 * Create a pseudo-frame for the setter invocation so that any
631 * stack-walking security code under the setter will correctly
632 * identify the guilty party. So that the watcher appears to
633 * be active to obj_eval and other such code, point frame.pc
634 * at the JSOP_STOP at the end of the script.
636 * The pseudo-frame is not created for fast natives as they
637 * are treated as interpreter frame extensions and always
638 * trusted.
640 JSObject *closure = wp->closure;
641 Class *clasp = closure->getClass();
642 JSFunction *fun;
643 JSScript *script;
644 if (clasp == &js_FunctionClass) {
645 fun = GET_FUNCTION_PRIVATE(cx, closure);
646 script = FUN_SCRIPT(fun);
647 } else if (clasp == &js_ScriptClass) {
648 fun = NULL;
649 script = (JSScript *) closure->getPrivate();
650 } else {
651 fun = NULL;
652 script = NULL;
655 uintN vplen = 2;
656 if (fun)
657 vplen += fun->minArgs() + (fun->isInterpreted() ? 0 : fun->u.n.extra);
658 uintN nfixed = script ? script->nfixed : 0;
660 /* Destructor pops frame. */
661 JSFrameRegs regs;
662 ExecuteFrameGuard frame;
664 if (fun && !fun->isFastNative()) {
666 * Get a pointer to new frame/slots. This memory is not
667 * "claimed", so the code before pushExecuteFrame must not
668 * reenter the interpreter.
670 JSStackFrame *down = js_GetTopStackFrame(cx);
671 if (!cx->stack().getExecuteFrame(cx, down, vplen, nfixed, frame)) {
672 DBG_LOCK(rt);
673 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
674 return JS_FALSE;
677 /* Initialize slots/frame. */
678 Value *vp = frame.getvp();
679 MakeValueRangeGCSafe(vp, vplen);
680 vp[0].setObject(*closure);
681 vp[1].setNull(); // satisfy LeaveTree assert
682 JSStackFrame *fp = frame.getFrame();
683 PodZero(fp);
684 MakeValueRangeGCSafe(fp->slots(), nfixed);
685 fp->script = script;
686 fp->fun = fun;
687 fp->argv = vp + 2;
688 fp->scopeChain = closure->getParent();
689 fp->argsobj = NULL;
691 /* Initialize regs. */
692 regs.pc = script ? script->code : NULL;
693 regs.sp = fp->slots() + nfixed;
695 /* Officially push |fp|. |frame|'s destructor pops. */
696 cx->stack().pushExecuteFrame(cx, frame, regs, NULL);
698 /* Now that fp has been pushed, get the call object. */
699 if (script && fun && fun->isHeavyweight() &&
700 !js_GetCallObject(cx, fp)) {
701 DBG_LOCK(rt);
702 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
703 return JS_FALSE;
707 JSBool ok = !wp->setter ||
708 (sprop->hasSetterValue()
709 ? InternalCall(cx, obj,
710 ObjectValue(*CastAsObject(wp->setter)),
711 1, vp, vp)
712 : callJSPropertyOpSetter(cx, wp->setter, obj, userid, vp));
714 /* Evil code can cause us to have an arguments object. */
715 if (frame.getFrame())
716 frame.getFrame()->putActivationObjects(cx);
718 DBG_LOCK(rt);
719 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
722 DBG_UNLOCK(rt);
723 return JS_TRUE;
726 JSBool
727 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
728 Value *rval)
730 JSObject *funobj;
731 JSFunction *wrapper;
732 jsid userid;
734 funobj = &argv[-2].toObject();
735 wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
736 userid = ATOM_TO_JSID(wrapper->atom);
737 *rval = argv[0];
738 return js_watch_set(cx, obj, userid, rval);
741 static bool
742 IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop)
744 if (sprop->hasSetterValue()) {
745 JSObject *funobj = sprop->setterObject();
746 if (!funobj || !funobj->isFunction())
747 return false;
749 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
750 return FUN_NATIVE(fun) == js_watch_set_wrapper;
752 return sprop->setterOp() == js_watch_set;
755 PropertyOp
756 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, PropertyOp setter)
758 JSAtom *atom;
759 JSFunction *wrapper;
761 if (!(attrs & JSPROP_SETTER))
762 return &js_watch_set; /* & to silence schoolmarmish MSVC */
764 if (JSID_IS_ATOM(id)) {
765 atom = JSID_TO_ATOM(id);
766 } else if (JSID_IS_INT(id)) {
767 if (!js_ValueToStringId(cx, IdToValue(id), &id))
768 return NULL;
769 atom = JSID_TO_ATOM(id);
770 } else {
771 atom = NULL;
774 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
775 setter ? CastAsObject(setter)->getParent() : NULL, atom);
776 if (!wrapper)
777 return NULL;
778 return CastAsPropertyOp(FUN_OBJECT(wrapper));
781 JS_PUBLIC_API(JSBool)
782 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
783 JSWatchPointHandler handler, void *closure)
785 JSObject *origobj;
786 Value v;
787 uintN attrs;
788 jsid propid;
789 JSObject *pobj;
790 JSProperty *prop;
791 JSScopeProperty *sprop;
792 JSRuntime *rt;
793 JSBool ok;
794 JSWatchPoint *wp;
795 PropertyOp watcher;
797 origobj = obj;
798 obj = obj->wrappedObject(cx);
799 OBJ_TO_INNER_OBJECT(cx, obj);
800 if (!obj)
801 return JS_FALSE;
803 AutoValueRooter idroot(cx);
804 if (JSID_IS_INT(id)) {
805 propid = id;
806 } else {
807 if (!js_ValueToStringId(cx, IdToValue(id), &propid))
808 return JS_FALSE;
809 propid = js_CheckForStringIndex(propid);
810 idroot.set(IdToValue(propid));
814 * If, by unwrapping and innerizing, we changed the object, check
815 * again to make sure that we're allowed to set a watch point.
817 if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
818 return JS_FALSE;
820 if (!obj->isNative()) {
821 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
822 obj->getClass()->name);
823 return JS_FALSE;
826 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
827 return JS_FALSE;
828 sprop = (JSScopeProperty *) prop;
829 rt = cx->runtime;
830 if (!sprop) {
831 /* Check for a deleted symbol watchpoint, which holds its property. */
832 sprop = js_FindWatchPoint(rt, obj->scope(), propid);
833 if (!sprop) {
834 /* Make a new property in obj so we can watch for the first set. */
835 if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
836 JSPROP_ENUMERATE, 0, 0, &prop)) {
837 return JS_FALSE;
839 sprop = (JSScopeProperty *) prop;
841 } else if (pobj != obj) {
842 /* Clone the prototype property so we can watch the right object. */
843 AutoValueRooter valroot(cx);
844 PropertyOp getter, setter;
845 uintN attrs, flags;
846 intN shortid;
848 if (pobj->isNative()) {
849 valroot.set(SPROP_HAS_VALID_SLOT(sprop, pobj->scope())
850 ? pobj->lockedGetSlot(sprop->slot)
851 : UndefinedValue());
852 getter = sprop->getter();
853 setter = sprop->setter();
854 attrs = sprop->attributes();
855 flags = sprop->getFlags();
856 shortid = sprop->shortid;
857 JS_UNLOCK_OBJ(cx, pobj);
858 } else {
859 if (!pobj->getProperty(cx, propid, valroot.addr()) ||
860 !pobj->getAttributes(cx, propid, &attrs)) {
861 return JS_FALSE;
863 getter = setter = NULL;
864 flags = 0;
865 shortid = 0;
868 /* Recall that obj is native, whether or not pobj is native. */
869 if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
870 getter, setter, attrs, flags,
871 shortid, &prop)) {
872 return JS_FALSE;
874 sprop = (JSScopeProperty *) prop;
878 * At this point, prop/sprop exists in obj, obj is locked, and we must
879 * unlock the object before returning.
881 ok = JS_TRUE;
882 DBG_LOCK(rt);
883 wp = FindWatchPoint(rt, obj->scope(), propid);
884 if (!wp) {
885 DBG_UNLOCK(rt);
886 watcher = js_WrapWatchedSetter(cx, propid, sprop->attributes(), sprop->setter());
887 if (!watcher) {
888 ok = JS_FALSE;
889 goto out;
892 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
893 if (!wp) {
894 ok = JS_FALSE;
895 goto out;
897 wp->handler = NULL;
898 wp->closure = NULL;
899 wp->object = obj;
900 wp->setter = sprop->setter();
901 wp->flags = JSWP_LIVE;
903 /* XXXbe nest in obj lock here */
904 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attributes(),
905 sprop->getter(), watcher);
906 if (!sprop) {
907 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
908 JS_INIT_CLIST(&wp->links);
909 DBG_LOCK(rt);
910 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
911 ok = JS_FALSE;
912 goto out;
914 wp->sprop = sprop;
917 * Now that wp is fully initialized, append it to rt's wp list.
918 * Because obj is locked we know that no other thread could have added
919 * a watchpoint for (obj, propid).
921 DBG_LOCK(rt);
922 JS_ASSERT(!FindWatchPoint(rt, obj->scope(), propid));
923 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
924 ++rt->debuggerMutations;
926 wp->handler = handler;
927 wp->closure = reinterpret_cast<JSObject*>(closure);
928 DBG_UNLOCK(rt);
930 out:
931 JS_UNLOCK_OBJ(cx, obj);
932 return ok;
935 JS_PUBLIC_API(JSBool)
936 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
937 JSWatchPointHandler *handlerp, void **closurep)
939 JSRuntime *rt;
940 JSWatchPoint *wp;
942 rt = cx->runtime;
943 DBG_LOCK(rt);
944 for (wp = (JSWatchPoint *)rt->watchPointList.next;
945 &wp->links != &rt->watchPointList;
946 wp = (JSWatchPoint *)wp->links.next) {
947 if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
948 if (handlerp)
949 *handlerp = wp->handler;
950 if (closurep)
951 *closurep = wp->closure;
952 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
955 DBG_UNLOCK(rt);
956 if (handlerp)
957 *handlerp = NULL;
958 if (closurep)
959 *closurep = NULL;
960 return JS_TRUE;
963 JS_PUBLIC_API(JSBool)
964 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
966 JSRuntime *rt;
967 JSWatchPoint *wp, *next;
968 uint32 sample;
970 rt = cx->runtime;
971 DBG_LOCK(rt);
972 for (wp = (JSWatchPoint *)rt->watchPointList.next;
973 &wp->links != &rt->watchPointList;
974 wp = next) {
975 next = (JSWatchPoint *)wp->links.next;
976 if (wp->object == obj) {
977 sample = rt->debuggerMutations;
978 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
979 return JS_FALSE;
980 DBG_LOCK(rt);
981 if (rt->debuggerMutations != sample + 1)
982 next = (JSWatchPoint *)rt->watchPointList.next;
985 DBG_UNLOCK(rt);
986 return JS_TRUE;
989 JS_PUBLIC_API(JSBool)
990 JS_ClearAllWatchPoints(JSContext *cx)
992 JSRuntime *rt;
993 JSWatchPoint *wp, *next;
994 uint32 sample;
996 rt = cx->runtime;
997 DBG_LOCK(rt);
998 for (wp = (JSWatchPoint *)rt->watchPointList.next;
999 &wp->links != &rt->watchPointList;
1000 wp = next) {
1001 next = (JSWatchPoint *)wp->links.next;
1002 sample = rt->debuggerMutations;
1003 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1004 return JS_FALSE;
1005 DBG_LOCK(rt);
1006 if (rt->debuggerMutations != sample + 1)
1007 next = (JSWatchPoint *)rt->watchPointList.next;
1009 DBG_UNLOCK(rt);
1010 return JS_TRUE;
1013 /************************************************************************/
1015 JS_PUBLIC_API(uintN)
1016 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1018 return js_PCToLineNumber(cx, script, pc);
1021 JS_PUBLIC_API(jsbytecode *)
1022 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1024 return js_LineNumberToPC(script, lineno);
1027 JS_PUBLIC_API(uintN)
1028 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
1030 return fun->nargs;
1033 JS_PUBLIC_API(JSBool)
1034 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
1036 return fun->hasLocalNames();
1039 extern JS_PUBLIC_API(jsuword *)
1040 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
1042 *markp = JS_ARENA_MARK(&cx->tempPool);
1043 return js_GetLocalNameArray(cx, fun, &cx->tempPool);
1046 extern JS_PUBLIC_API(JSAtom *)
1047 JS_LocalNameToAtom(jsuword w)
1049 return JS_LOCAL_NAME_TO_ATOM(w);
1052 extern JS_PUBLIC_API(JSString *)
1053 JS_AtomKey(JSAtom *atom)
1055 return ATOM_TO_STRING(atom);
1058 extern JS_PUBLIC_API(void)
1059 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
1061 JS_ARENA_RELEASE(&cx->tempPool, mark);
1064 JS_PUBLIC_API(JSScript *)
1065 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1067 return FUN_SCRIPT(fun);
1070 JS_PUBLIC_API(JSNative)
1071 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1073 return Jsvalify(FUN_NATIVE(fun));
1076 JS_PUBLIC_API(JSFastNative)
1077 JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun)
1079 return Jsvalify(FUN_FAST_NATIVE(fun));
1082 JS_PUBLIC_API(JSPrincipals *)
1083 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1085 return script->principals;
1088 /************************************************************************/
1091 * Stack Frame Iterator
1093 JS_PUBLIC_API(JSStackFrame *)
1094 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1096 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down;
1097 return *iteratorp;
1100 JS_PUBLIC_API(JSScript *)
1101 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1103 return fp->script;
1106 JS_PUBLIC_API(jsbytecode *)
1107 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1109 return fp->pc(cx);
1112 JS_PUBLIC_API(JSStackFrame *)
1113 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1115 return js_GetScriptedCaller(cx, fp);
1118 JS_PUBLIC_API(JSPrincipals *)
1119 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1121 JSSecurityCallbacks *callbacks;
1123 if (fp->fun) {
1124 callbacks = JS_GetSecurityCallbacks(cx);
1125 if (callbacks && callbacks->findObjectPrincipals) {
1126 if (FUN_OBJECT(fp->fun) != fp->callee())
1127 return callbacks->findObjectPrincipals(cx, fp->callee());
1128 /* FALL THROUGH */
1131 if (fp->script)
1132 return fp->script->principals;
1133 return NULL;
1136 JSPrincipals *
1137 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1139 JSPrincipals *principals, *callerPrincipals;
1140 JSSecurityCallbacks *callbacks;
1142 callbacks = JS_GetSecurityCallbacks(cx);
1143 if (callbacks && callbacks->findObjectPrincipals)
1144 principals = callbacks->findObjectPrincipals(cx, callee);
1145 else
1146 principals = NULL;
1147 if (!caller)
1148 return principals;
1149 callerPrincipals = JS_StackFramePrincipals(cx, caller);
1150 return (callerPrincipals && principals &&
1151 callerPrincipals->subsume(callerPrincipals, principals))
1152 ? principals
1153 : callerPrincipals;
1156 JS_PUBLIC_API(JSPrincipals *)
1157 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
1159 return js_EvalFramePrincipals(cx, fp->callee(), caller);
1162 JS_PUBLIC_API(void *)
1163 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1165 if (fp->annotation && fp->script) {
1166 JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
1168 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1170 * Give out an annotation only if privileges have not been revoked
1171 * or disabled globally.
1173 return fp->annotation;
1177 return NULL;
1180 JS_PUBLIC_API(void)
1181 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1183 fp->annotation = annotation;
1186 JS_PUBLIC_API(void *)
1187 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1189 JSPrincipals *principals;
1191 principals = JS_StackFramePrincipals(cx, fp);
1192 if (!principals)
1193 return NULL;
1194 return principals->getPrincipalArray(cx, principals);
1197 JS_PUBLIC_API(JSBool)
1198 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
1200 return !fp->script;
1203 /* this is deprecated, use JS_GetFrameScopeChain instead */
1204 JS_PUBLIC_API(JSObject *)
1205 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1207 return fp->scopeChain;
1210 JS_PUBLIC_API(JSObject *)
1211 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1213 JS_ASSERT(cx->stack().contains(fp));
1215 /* Force creation of argument and call objects if not yet created */
1216 (void) JS_GetFrameCallObject(cx, fp);
1217 return js_GetScopeChain(cx, fp);
1220 JS_PUBLIC_API(JSObject *)
1221 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1223 JS_ASSERT(cx->stack().contains(fp));
1225 if (! fp->fun)
1226 return NULL;
1228 /* Force creation of argument object if not yet created */
1229 (void) js_GetArgsObject(cx, fp);
1232 * XXX ill-defined: null return here means error was reported, unlike a
1233 * null returned above or in the #else
1235 return js_GetCallObject(cx, fp);
1238 JS_PUBLIC_API(JSObject *)
1239 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
1241 return fp->getThisObject(cx);
1244 JS_PUBLIC_API(JSFunction *)
1245 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1247 return fp->fun;
1250 JS_PUBLIC_API(JSObject *)
1251 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1253 if (!fp->fun)
1254 return NULL;
1256 JS_ASSERT(fp->callee()->isFunction());
1257 JS_ASSERT(fp->callee()->getPrivate() == fp->fun);
1258 return fp->callee();
1261 JS_PUBLIC_API(JSBool)
1262 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1264 return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
1267 JS_PUBLIC_API(JSObject *)
1268 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1270 return fp->callee();
1273 JS_PUBLIC_API(JSBool)
1274 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1276 return (fp->flags & JSFRAME_DEBUGGER) != 0;
1279 JS_PUBLIC_API(jsval)
1280 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1282 return Jsvalify(fp->rval);
1285 JS_PUBLIC_API(void)
1286 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1288 fp->rval = Valueify(rval);
1291 /************************************************************************/
1293 JS_PUBLIC_API(const char *)
1294 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1296 return script->filename;
1299 JS_PUBLIC_API(uintN)
1300 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1302 return script->lineno;
1305 JS_PUBLIC_API(uintN)
1306 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1308 return js_GetScriptLineExtent(script);
1311 JS_PUBLIC_API(JSVersion)
1312 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1314 return (JSVersion) (script->version & JSVERSION_MASK);
1317 /***************************************************************************/
1319 JS_PUBLIC_API(void)
1320 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1322 rt->globalDebugHooks.newScriptHook = hook;
1323 rt->globalDebugHooks.newScriptHookData = callerdata;
1326 JS_PUBLIC_API(void)
1327 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1328 void *callerdata)
1330 rt->globalDebugHooks.destroyScriptHook = hook;
1331 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1334 /***************************************************************************/
1336 JS_PUBLIC_API(JSBool)
1337 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1338 const jschar *chars, uintN length,
1339 const char *filename, uintN lineno,
1340 jsval *rval)
1342 JS_ASSERT_NOT_ON_TRACE(cx);
1344 JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1345 if (!scobj)
1346 return false;
1349 * NB: This function breaks the assumption that the compiler can see all
1350 * calls and properly compute a static level. In order to get around this,
1351 * we use a static level that will cause us not to attempt to optimize
1352 * variable references made by this frame.
1354 JSScript *script = Compiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
1355 TCF_COMPILE_N_GO, chars, length, NULL,
1356 filename, lineno, NULL,
1357 UpvarCookie::UPVAR_LEVEL_LIMIT);
1359 if (!script)
1360 return false;
1362 bool ok = !!Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1364 js_DestroyScript(cx, script);
1365 return ok;
1368 JS_PUBLIC_API(JSBool)
1369 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1370 const char *bytes, uintN length,
1371 const char *filename, uintN lineno,
1372 jsval *rval)
1374 jschar *chars;
1375 JSBool ok;
1376 size_t len = length;
1378 chars = js_InflateString(cx, bytes, &len);
1379 if (!chars)
1380 return JS_FALSE;
1381 length = (uintN) len;
1382 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1383 rval);
1384 cx->free(chars);
1386 return ok;
1389 /************************************************************************/
1391 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
1393 JS_PUBLIC_API(JSScopeProperty *)
1394 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1396 JSScopeProperty *sprop;
1397 JSScope *scope;
1399 sprop = *iteratorp;
1400 scope = obj->scope();
1402 /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
1403 sprop = sprop ? sprop->parent : scope->lastProperty();
1404 *iteratorp = sprop;
1405 return sprop;
1408 JS_PUBLIC_API(JSBool)
1409 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1410 JSPropertyDesc *pd)
1412 pd->id = IdToJsval(sprop->id);
1414 JSBool wasThrowing = cx->throwing;
1415 AutoValueRooter lastException(cx, cx->exception);
1416 cx->throwing = JS_FALSE;
1418 if (!js_GetProperty(cx, obj, sprop->id, Valueify(&pd->value))) {
1419 if (!cx->throwing) {
1420 pd->flags = JSPD_ERROR;
1421 pd->value = JSVAL_VOID;
1422 } else {
1423 pd->flags = JSPD_EXCEPTION;
1424 pd->value = Jsvalify(cx->exception);
1426 } else {
1427 pd->flags = 0;
1430 cx->throwing = wasThrowing;
1431 if (wasThrowing)
1432 cx->exception = lastException.value();
1434 pd->flags |= (sprop->enumerable() ? JSPD_ENUMERATE : 0)
1435 | (!sprop->writable() ? JSPD_READONLY : 0)
1436 | (!sprop->configurable() ? JSPD_PERMANENT : 0);
1437 pd->spare = 0;
1438 if (sprop->getter() == js_GetCallArg) {
1439 pd->slot = sprop->shortid;
1440 pd->flags |= JSPD_ARGUMENT;
1441 } else if (sprop->getter() == js_GetCallVar) {
1442 pd->slot = sprop->shortid;
1443 pd->flags |= JSPD_VARIABLE;
1444 } else {
1445 pd->slot = 0;
1447 pd->alias = JSVAL_VOID;
1449 JSScope *scope = obj->scope();
1450 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
1451 JSScopeProperty *aprop;
1452 for (aprop = scope->lastProperty(); aprop; aprop = aprop->parent) {
1453 if (aprop != sprop && aprop->slot == sprop->slot) {
1454 pd->alias = IdToJsval(aprop->id);
1455 break;
1459 return JS_TRUE;
1462 JS_PUBLIC_API(JSBool)
1463 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1465 JSScope *scope;
1466 uint32 i, n;
1467 JSPropertyDesc *pd;
1468 JSScopeProperty *sprop;
1470 Class *clasp = obj->getClass();
1471 if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1472 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1473 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1474 return JS_FALSE;
1476 if (!clasp->enumerate(cx, obj))
1477 return JS_FALSE;
1479 /* have no props, or object's scope has not mutated from that of proto */
1480 scope = obj->scope();
1481 if (scope->entryCount == 0) {
1482 pda->length = 0;
1483 pda->array = NULL;
1484 return JS_TRUE;
1487 n = scope->entryCount;
1488 pd = (JSPropertyDesc *) cx->malloc((size_t)n * sizeof(JSPropertyDesc));
1489 if (!pd)
1490 return JS_FALSE;
1491 i = 0;
1492 for (sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
1493 if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1494 goto bad;
1495 if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1496 goto bad;
1497 if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1498 goto bad;
1499 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1500 goto bad;
1501 if (++i == n)
1502 break;
1504 pda->length = i;
1505 pda->array = pd;
1506 return JS_TRUE;
1508 bad:
1509 pda->length = i + 1;
1510 pda->array = pd;
1511 JS_PutPropertyDescArray(cx, pda);
1512 return JS_FALSE;
1515 JS_PUBLIC_API(void)
1516 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1518 JSPropertyDesc *pd;
1519 uint32 i;
1521 pd = pda->array;
1522 for (i = 0; i < pda->length; i++) {
1523 js_RemoveRoot(cx->runtime, &pd[i].id);
1524 js_RemoveRoot(cx->runtime, &pd[i].value);
1525 if (pd[i].flags & JSPD_ALIAS)
1526 js_RemoveRoot(cx->runtime, &pd[i].alias);
1528 cx->free(pd);
1531 /************************************************************************/
1533 static bool
1534 SetupFakeFrame(JSContext *cx, ExecuteFrameGuard &frame, JSFrameRegs &regs, JSObject *scopeobj)
1536 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, scopeobj);
1537 JS_ASSERT(fun->minArgs() == 0 && !fun->isInterpreted() && fun->u.n.extra == 0);
1539 const uintN vplen = 2;
1540 const uintN nfixed = 0;
1541 if (!cx->stack().getExecuteFrame(cx, js_GetTopStackFrame(cx), vplen, nfixed, frame))
1542 return false;
1544 Value *vp = frame.getvp();
1545 PodZero(vp, vplen);
1546 vp[0].setObject(*scopeobj);
1547 vp[1].setNull(); // satisfy LeaveTree assert
1549 JSStackFrame *fp = frame.getFrame();
1550 PodZero(fp);
1551 fp->fun = fun;
1552 fp->argv = vp + 2;
1553 fp->scopeChain = scopeobj->getGlobal();
1555 regs.pc = NULL;
1556 regs.sp = fp->slots();
1558 cx->stack().pushExecuteFrame(cx, frame, regs, NULL);
1559 return true;
1562 JS_FRIEND_API(JSBool)
1563 js_GetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1564 jsval *vp)
1566 ExecuteFrameGuard frame;
1567 JSFrameRegs regs;
1569 if (!SetupFakeFrame(cx, frame, regs, scopeobj))
1570 return false;
1572 bool ok = JS_GetPropertyById(cx, obj, id, vp);
1573 frame.getFrame()->putActivationObjects(cx);
1574 return ok;
1577 JS_FRIEND_API(JSBool)
1578 js_SetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1579 jsval *vp)
1581 ExecuteFrameGuard frame;
1582 JSFrameRegs regs;
1584 if (!SetupFakeFrame(cx, frame, regs, scopeobj))
1585 return false;
1587 bool ok = JS_SetPropertyById(cx, obj, id, vp);
1588 frame.getFrame()->putActivationObjects(cx);
1589 return ok;
1592 JS_FRIEND_API(JSBool)
1593 js_CallFunctionValueWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsval funval,
1594 uintN argc, jsval *argv, jsval *rval)
1596 ExecuteFrameGuard frame;
1597 JSFrameRegs regs;
1599 if (!SetupFakeFrame(cx, frame, regs, scopeobj))
1600 return false;
1602 bool ok = JS_CallFunctionValue(cx, obj, funval, argc, argv, rval);
1603 frame.getFrame()->putActivationObjects(cx);
1604 return ok;
1607 /************************************************************************/
1609 JS_PUBLIC_API(JSBool)
1610 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1612 rt->globalDebugHooks.debuggerHandler = handler;
1613 rt->globalDebugHooks.debuggerHandlerData = closure;
1614 return JS_TRUE;
1617 JS_PUBLIC_API(JSBool)
1618 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1620 rt->globalDebugHooks.sourceHandler = handler;
1621 rt->globalDebugHooks.sourceHandlerData = closure;
1622 return JS_TRUE;
1625 JS_PUBLIC_API(JSBool)
1626 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1628 rt->globalDebugHooks.executeHook = hook;
1629 rt->globalDebugHooks.executeHookData = closure;
1630 return JS_TRUE;
1633 JS_PUBLIC_API(JSBool)
1634 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1636 #ifdef JS_TRACER
1638 AutoLockGC lock(rt);
1639 bool wasInhibited = rt->debuggerInhibitsJIT();
1640 #endif
1641 rt->globalDebugHooks.callHook = hook;
1642 rt->globalDebugHooks.callHookData = closure;
1643 #ifdef JS_TRACER
1644 JITInhibitingHookChange(rt, wasInhibited);
1646 if (hook)
1647 LeaveTraceRT(rt);
1648 #endif
1649 return JS_TRUE;
1652 JS_PUBLIC_API(JSBool)
1653 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1655 rt->globalDebugHooks.throwHook = hook;
1656 rt->globalDebugHooks.throwHookData = closure;
1657 return JS_TRUE;
1660 JS_PUBLIC_API(JSBool)
1661 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1663 rt->globalDebugHooks.debugErrorHook = hook;
1664 rt->globalDebugHooks.debugErrorHookData = closure;
1665 return JS_TRUE;
1668 /************************************************************************/
1670 JS_PUBLIC_API(size_t)
1671 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1673 size_t nbytes;
1674 JSScope *scope;
1676 nbytes = sizeof *obj;
1677 if (obj->dslots) {
1678 nbytes += (obj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS + 1)
1679 * sizeof obj->dslots[0];
1681 if (obj->isNative()) {
1682 scope = obj->scope();
1683 if (!scope->isSharedEmpty()) {
1684 nbytes += sizeof *scope;
1685 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1688 return nbytes;
1691 static size_t
1692 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1694 size_t nbytes;
1696 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1697 nbytes += sizeof(JSString);
1698 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1699 return nbytes;
1702 JS_PUBLIC_API(size_t)
1703 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1705 size_t nbytes;
1707 nbytes = sizeof *fun;
1708 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1709 if (FUN_INTERPRETED(fun))
1710 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1711 if (fun->atom)
1712 nbytes += GetAtomTotalSize(cx, fun->atom);
1713 return nbytes;
1716 #include "jsemit.h"
1718 JS_PUBLIC_API(size_t)
1719 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1721 size_t nbytes, pbytes;
1722 jsatomid i;
1723 jssrcnote *sn, *notes;
1724 JSObjectArray *objarray;
1725 JSPrincipals *principals;
1727 nbytes = sizeof *script;
1728 if (script->u.object)
1729 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1731 nbytes += script->length * sizeof script->code[0];
1732 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1733 for (i = 0; i < script->atomMap.length; i++)
1734 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1736 if (script->filename)
1737 nbytes += strlen(script->filename) + 1;
1739 notes = script->notes();
1740 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1741 continue;
1742 nbytes += (sn - notes + 1) * sizeof *sn;
1744 if (script->objectsOffset != 0) {
1745 objarray = script->objects();
1746 i = objarray->length;
1747 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1748 do {
1749 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1750 } while (i != 0);
1753 if (script->regexpsOffset != 0) {
1754 objarray = script->regexps();
1755 i = objarray->length;
1756 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1757 do {
1758 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1759 } while (i != 0);
1762 if (script->trynotesOffset != 0) {
1763 nbytes += sizeof(JSTryNoteArray) +
1764 script->trynotes()->length * sizeof(JSTryNote);
1767 principals = script->principals;
1768 if (principals) {
1769 JS_ASSERT(principals->refcount);
1770 pbytes = sizeof *principals;
1771 if (principals->refcount > 1)
1772 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1773 nbytes += pbytes;
1776 return nbytes;
1779 JS_PUBLIC_API(uint32)
1780 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1782 if (!fp)
1783 fp = js_GetTopStackFrame(cx);
1784 while (fp) {
1785 if (fp->script)
1786 return JS_GetScriptFilenameFlags(fp->script);
1787 fp = fp->down;
1789 return 0;
1792 JS_PUBLIC_API(uint32)
1793 JS_GetScriptFilenameFlags(JSScript *script)
1795 JS_ASSERT(script);
1796 if (!script->filename)
1797 return JSFILENAME_NULL;
1798 return js_GetScriptFilenameFlags(script->filename);
1801 JS_PUBLIC_API(JSBool)
1802 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1804 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1805 return JS_FALSE;
1806 return JS_TRUE;
1809 JS_PUBLIC_API(JSBool)
1810 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1812 return obj->isSystem();
1815 JS_PUBLIC_API(JSBool)
1816 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1818 obj->setSystem();
1819 return true;
1822 /************************************************************************/
1824 JS_PUBLIC_API(const JSDebugHooks *)
1825 JS_GetGlobalDebugHooks(JSRuntime *rt)
1827 return &rt->globalDebugHooks;
1830 const JSDebugHooks js_NullDebugHooks = {};
1832 JS_PUBLIC_API(JSDebugHooks *)
1833 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1835 JS_ASSERT(hooks);
1836 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1837 LeaveTrace(cx);
1839 #ifdef JS_TRACER
1840 AutoLockGC lock(cx->runtime);
1841 #endif
1842 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1843 cx->debugHooks = hooks;
1844 #ifdef JS_TRACER
1845 cx->updateJITEnabled();
1846 #endif
1847 return old;
1850 JS_PUBLIC_API(JSDebugHooks *)
1851 JS_ClearContextDebugHooks(JSContext *cx)
1853 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1856 #ifdef MOZ_SHARK
1858 #include <CHUD/CHUD.h>
1860 JS_PUBLIC_API(JSBool)
1861 JS_StartChudRemote()
1863 if (chudIsRemoteAccessAcquired() &&
1864 (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
1865 return JS_TRUE;
1868 return JS_FALSE;
1871 JS_PUBLIC_API(JSBool)
1872 JS_StopChudRemote()
1874 if (chudIsRemoteAccessAcquired() &&
1875 (chudStopRemotePerfMonitor() == chudSuccess)) {
1876 return JS_TRUE;
1879 return JS_FALSE;
1882 JS_PUBLIC_API(JSBool)
1883 JS_ConnectShark()
1885 if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
1886 return JS_FALSE;
1888 if (chudAcquireRemoteAccess() != chudSuccess)
1889 return JS_FALSE;
1891 return JS_TRUE;
1894 JS_PUBLIC_API(JSBool)
1895 JS_DisconnectShark()
1897 if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
1898 return JS_FALSE;
1900 return JS_TRUE;
1903 JS_FRIEND_API(JSBool)
1904 js_StartShark(JSContext *cx, JSObject *obj,
1905 uintN argc, jsval *argv, jsval *rval)
1907 if (!JS_StartChudRemote()) {
1908 JS_ReportError(cx, "Error starting CHUD.");
1909 return JS_FALSE;
1912 return JS_TRUE;
1915 JS_FRIEND_API(JSBool)
1916 js_StopShark(JSContext *cx, JSObject *obj,
1917 uintN argc, jsval *argv, jsval *rval)
1919 if (!JS_StopChudRemote()) {
1920 JS_ReportError(cx, "Error stopping CHUD.");
1921 return JS_FALSE;
1924 return JS_TRUE;
1927 JS_FRIEND_API(JSBool)
1928 js_ConnectShark(JSContext *cx, JSObject *obj,
1929 uintN argc, jsval *argv, jsval *rval)
1931 if (!JS_ConnectShark()) {
1932 JS_ReportError(cx, "Error connecting to Shark.");
1933 return JS_FALSE;
1936 return JS_TRUE;
1939 JS_FRIEND_API(JSBool)
1940 js_DisconnectShark(JSContext *cx, JSObject *obj,
1941 uintN argc, jsval *argv, jsval *rval)
1943 if (!JS_DisconnectShark()) {
1944 JS_ReportError(cx, "Error disconnecting from Shark.");
1945 return JS_FALSE;
1948 return JS_TRUE;
1951 #endif /* MOZ_SHARK */
1953 #ifdef MOZ_CALLGRIND
1955 #include <valgrind/callgrind.h>
1957 JS_FRIEND_API(JSBool)
1958 js_StartCallgrind(JSContext *cx, JSObject *obj,
1959 uintN argc, jsval *argv, jsval *rval)
1961 CALLGRIND_START_INSTRUMENTATION;
1962 CALLGRIND_ZERO_STATS;
1963 return JS_TRUE;
1966 JS_FRIEND_API(JSBool)
1967 js_StopCallgrind(JSContext *cx, JSObject *obj,
1968 uintN argc, jsval *argv, jsval *rval)
1970 CALLGRIND_STOP_INSTRUMENTATION;
1971 return JS_TRUE;
1974 JS_FRIEND_API(JSBool)
1975 js_DumpCallgrind(JSContext *cx, JSObject *obj,
1976 uintN argc, jsval *argv, jsval *rval)
1978 JSString *str;
1979 char *cstr;
1981 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1982 str = JSVAL_TO_STRING(argv[0]);
1983 cstr = js_DeflateString(cx, str->chars(), str->length());
1984 if (cstr) {
1985 CALLGRIND_DUMP_STATS_AT(cstr);
1986 cx->free(cstr);
1987 return JS_TRUE;
1990 CALLGRIND_DUMP_STATS;
1992 return JS_TRUE;
1995 #endif /* MOZ_CALLGRIND */
1997 #ifdef MOZ_VTUNE
1998 #include <VTuneApi.h>
2000 static const char *vtuneErrorMessages[] = {
2001 "unknown, error #0",
2002 "invalid 'max samples' field",
2003 "invalid 'samples per buffer' field",
2004 "invalid 'sample interval' field",
2005 "invalid path",
2006 "sample file in use",
2007 "invalid 'number of events' field",
2008 "unknown, error #7",
2009 "internal error",
2010 "bad event name",
2011 "VTStopSampling called without calling VTStartSampling",
2012 "no events selected for event-based sampling",
2013 "events selected cannot be run together",
2014 "no sampling parameters",
2015 "sample database already exists",
2016 "sampling already started",
2017 "time-based sampling not supported",
2018 "invalid 'sampling parameters size' field",
2019 "invalid 'event size' field",
2020 "sampling file already bound",
2021 "invalid event path",
2022 "invalid license",
2023 "invalid 'global options' field",
2027 JS_FRIEND_API(JSBool)
2028 js_StartVtune(JSContext *cx, JSObject *obj,
2029 uintN argc, jsval *argv, jsval *rval)
2031 VTUNE_EVENT events[] = {
2032 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2033 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2036 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2037 char *default_filename = "mozilla-vtune.tb5";
2038 JSString *str;
2039 U32 status;
2041 VTUNE_SAMPLING_PARAMS params =
2042 sizeof(VTUNE_SAMPLING_PARAMS),
2043 sizeof(VTUNE_EVENT),
2044 0, 0, /* Reserved fields */
2045 1, /* Initialize in "paused" state */
2046 0, /* Max samples, or 0 for "continuous" */
2047 4096, /* Samples per buffer */
2048 0.1, /* Sampling interval in ms */
2049 1, /* 1 for event-based sampling, 0 for time-based */
2051 n_events,
2052 events,
2053 default_filename,
2056 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2057 str = JSVAL_TO_STRING(argv[0]);
2058 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2061 status = VTStartSampling(&params);
2063 if (params.tb5Filename != default_filename)
2064 cx->free(params.tb5Filename);
2066 if (status != 0) {
2067 if (status == VTAPI_MULTIPLE_RUNS)
2068 VTStopSampling(0);
2069 if (status < sizeof(vtuneErrorMessages))
2070 JS_ReportError(cx, "Vtune setup error: %s",
2071 vtuneErrorMessages[status]);
2072 else
2073 JS_ReportError(cx, "Vtune setup error: %d",
2074 status);
2075 return JS_FALSE;
2077 return JS_TRUE;
2080 JS_FRIEND_API(JSBool)
2081 js_StopVtune(JSContext *cx, JSObject *obj,
2082 uintN argc, jsval *argv, jsval *rval)
2084 U32 status = VTStopSampling(1);
2085 if (status) {
2086 if (status < sizeof(vtuneErrorMessages))
2087 JS_ReportError(cx, "Vtune shutdown error: %s",
2088 vtuneErrorMessages[status]);
2089 else
2090 JS_ReportError(cx, "Vtune shutdown error: %d",
2091 status);
2092 return JS_FALSE;
2094 return JS_TRUE;
2097 JS_FRIEND_API(JSBool)
2098 js_PauseVtune(JSContext *cx, JSObject *obj,
2099 uintN argc, jsval *argv, jsval *rval)
2101 VTPause();
2102 return JS_TRUE;
2105 JS_FRIEND_API(JSBool)
2106 js_ResumeVtune(JSContext *cx, JSObject *obj,
2107 uintN argc, jsval *argv, jsval *rval)
2109 VTResume();
2110 return JS_TRUE;
2113 #endif /* MOZ_VTUNE */
2115 #ifdef MOZ_TRACEVIS
2117 * Ethogram - Javascript wrapper for TraceVis state
2119 * ethology: The scientific study of animal behavior,
2120 * especially as it occurs in a natural environment.
2121 * ethogram: A pictorial catalog of the behavioral patterns of
2122 * an organism or a species.
2125 #if defined(XP_WIN)
2126 #include <windows.h>
2127 #else
2128 #include <sys/time.h>
2129 #endif
2130 #include "jstracer.h"
2132 #define ETHOGRAM_BUF_SIZE 65536
2134 static JSBool
2135 ethogram_construct(JSContext *cx, JSObject *obj,
2136 uintN argc, jsval *argv, jsval *rval);
2137 static void
2138 ethogram_finalize(JSContext *cx, JSObject *obj);
2140 static JSClass ethogram_class = {
2141 "Ethogram",
2142 JSCLASS_HAS_PRIVATE,
2143 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2144 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2145 JSCLASS_NO_OPTIONAL_MEMBERS
2148 struct EthogramEvent {
2149 TraceVisState s;
2150 TraceVisExitReason r;
2151 int ts;
2152 int tus;
2153 JSString *filename;
2154 int lineno;
2157 static int
2158 compare_strings(const void *k1, const void *k2)
2160 return strcmp((const char *) k1, (const char *) k2) == 0;
2163 class EthogramEventBuffer {
2164 private:
2165 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2166 int mReadPos;
2167 int mWritePos;
2168 JSObject *mFilenames;
2169 int mStartSecond;
2171 struct EthogramScriptEntry {
2172 char *filename;
2173 JSString *jsfilename;
2175 EthogramScriptEntry *next;
2177 EthogramScriptEntry *mScripts;
2179 public:
2180 friend JSBool
2181 ethogram_construct(JSContext *cx, JSObject *obj,
2182 uintN argc, jsval *argv, jsval *rval);
2184 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2185 mBuf[mWritePos].s = s;
2186 mBuf[mWritePos].r = r;
2187 #if defined(XP_WIN)
2188 FILETIME now;
2189 GetSystemTimeAsFileTime(&now);
2190 unsigned long long raw_us = 0.1 *
2191 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2192 (unsigned long long) now.dwLowDateTime);
2193 unsigned int sec = raw_us / 1000000L;
2194 unsigned int usec = raw_us % 1000000L;
2195 mBuf[mWritePos].ts = sec - mStartSecond;
2196 mBuf[mWritePos].tus = usec;
2197 #else
2198 struct timeval tv;
2199 gettimeofday(&tv, NULL);
2200 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2201 mBuf[mWritePos].tus = tv.tv_usec;
2202 #endif
2204 JSString *jsfilename = findScript(filename);
2205 mBuf[mWritePos].filename = jsfilename;
2206 mBuf[mWritePos].lineno = lineno;
2208 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2209 if (mWritePos == mReadPos) {
2210 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2214 inline EthogramEvent *pop() {
2215 EthogramEvent *e = &mBuf[mReadPos];
2216 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2217 return e;
2220 bool isEmpty() {
2221 return (mReadPos == mWritePos);
2224 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2225 JSHashNumber hash = JS_HashString(filename);
2226 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2227 if (*hep != NULL)
2228 return JS_FALSE;
2230 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2232 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2233 if (entry == NULL)
2234 return NULL;
2236 entry->next = mScripts;
2237 mScripts = entry;
2238 entry->filename = filename;
2239 entry->jsfilename = jsfilename;
2241 return mScripts;
2244 void removeScripts(JSContext *cx) {
2245 EthogramScriptEntry *se = mScripts;
2246 while (se != NULL) {
2247 char *filename = se->filename;
2249 JSHashNumber hash = JS_HashString(filename);
2250 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2251 JSHashEntry *he = *hep;
2252 if (he) {
2253 /* we hardly knew he */
2254 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2257 EthogramScriptEntry *se_head = se;
2258 se = se->next;
2259 JS_free(cx, se_head);
2263 JSString *findScript(char *filename) {
2264 EthogramScriptEntry *se = mScripts;
2265 while (se != NULL) {
2266 if (compare_strings(se->filename, filename))
2267 return (se->jsfilename);
2268 se = se->next;
2270 return NULL;
2273 JSObject *filenames() {
2274 return mFilenames;
2277 int length() {
2278 if (mWritePos < mReadPos)
2279 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2280 else
2281 return mWritePos - mReadPos;
2285 static char jstv_empty[] = "<null>";
2287 inline char *
2288 jstv_Filename(JSStackFrame *fp)
2290 while (fp && fp->script == NULL)
2291 fp = fp->down;
2292 return (fp && fp->script && fp->script->filename)
2293 ? (char *)fp->script->filename
2294 : jstv_empty;
2296 inline uintN
2297 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2299 while (fp && fp->pc(cx) == NULL)
2300 fp = fp->down;
2301 return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2304 /* Collect states here and distribute to a matching buffer, if any */
2305 JS_FRIEND_API(void)
2306 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2308 JSStackFrame *fp = cx->fp;
2310 char *script_file = jstv_Filename(fp);
2311 JSHashNumber hash = JS_HashString(script_file);
2313 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2314 /* update event buffer, flag if overflowed */
2315 JSHashEntry *he = *hep;
2316 if (he) {
2317 EthogramEventBuffer *p;
2318 p = (EthogramEventBuffer *) he->value;
2320 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2324 static JSBool
2325 ethogram_construct(JSContext *cx, JSObject *obj,
2326 uintN argc, jsval *argv, jsval *rval)
2328 EthogramEventBuffer *p;
2330 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2332 p->mReadPos = p->mWritePos = 0;
2333 p->mScripts = NULL;
2334 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2336 #if defined(XP_WIN)
2337 FILETIME now;
2338 GetSystemTimeAsFileTime(&now);
2339 unsigned long long raw_us = 0.1 *
2340 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2341 (unsigned long long) now.dwLowDateTime);
2342 unsigned int s = raw_us / 1000000L;
2343 p->mStartSecond = s;
2344 #else
2345 struct timeval tv;
2346 gettimeofday(&tv, NULL);
2347 p->mStartSecond = tv.tv_sec;
2348 #endif
2349 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2350 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2351 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2352 return JS_FALSE;
2354 if (!JS_IsConstructing(cx)) {
2355 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2356 if (!obj)
2357 return JS_FALSE;
2358 *rval = OBJECT_TO_JSVAL(obj);
2360 JS_SetPrivate(cx, obj, p);
2361 return JS_TRUE;
2364 static void
2365 ethogram_finalize(JSContext *cx, JSObject *obj)
2367 EthogramEventBuffer *p;
2368 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2369 if (!p)
2370 return;
2372 p->removeScripts(cx);
2374 JS_free(cx, p);
2377 static JSBool
2378 ethogram_addScript(JSContext *cx, JSObject *obj,
2379 uintN argc, jsval *argv, jsval *rval)
2381 JSString *str;
2382 char *filename = NULL;
2383 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2384 str = JSVAL_TO_STRING(argv[0]);
2385 filename = js_DeflateString(cx,
2386 str->chars(),
2387 str->length());
2390 /* silently ignore no args */
2391 if (!filename)
2392 return JS_TRUE;
2394 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2396 p->addScript(cx, obj, filename, str);
2397 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, rval);
2398 return JS_TRUE;
2401 static JSBool
2402 ethogram_getAllEvents(JSContext *cx, JSObject *obj,
2403 uintN argc, jsval *argv, jsval *rval)
2405 EthogramEventBuffer *p;
2407 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2408 if (!p)
2409 return JS_FALSE;
2411 if (p->isEmpty()) {
2412 *rval = JSVAL_NULL;
2413 return JS_TRUE;
2416 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2417 if (rarray == NULL) {
2418 *rval = JSVAL_NULL;
2419 return JS_TRUE;
2422 *rval = OBJECT_TO_JSVAL(rarray);
2424 for (int i = 0; !p->isEmpty(); i++) {
2426 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2427 if (x == NULL)
2428 return JS_FALSE;
2430 EthogramEvent *e = p->pop();
2432 jsval state = INT_TO_JSVAL(e->s);
2433 jsval reason = INT_TO_JSVAL(e->r);
2434 jsval ts = INT_TO_JSVAL(e->ts);
2435 jsval tus = INT_TO_JSVAL(e->tus);
2437 jsval filename = STRING_TO_JSVAL(e->filename);
2438 jsval lineno = INT_TO_JSVAL(e->lineno);
2440 if (!JS_SetProperty(cx, x, "state", &state))
2441 return JS_FALSE;
2442 if (!JS_SetProperty(cx, x, "reason", &reason))
2443 return JS_FALSE;
2444 if (!JS_SetProperty(cx, x, "ts", &ts))
2445 return JS_FALSE;
2446 if (!JS_SetProperty(cx, x, "tus", &tus))
2447 return JS_FALSE;
2449 if (!JS_SetProperty(cx, x, "filename", &filename))
2450 return JS_FALSE;
2451 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2452 return JS_FALSE;
2454 jsval element = OBJECT_TO_JSVAL(x);
2455 JS_SetElement(cx, rarray, i, &element);
2458 return JS_TRUE;
2461 static JSBool
2462 ethogram_getNextEvent(JSContext *cx, JSObject *obj,
2463 uintN argc, jsval *argv, jsval *rval)
2465 EthogramEventBuffer *p;
2467 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2468 if (!p)
2469 return JS_FALSE;
2471 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2472 if (x == NULL)
2473 return JS_FALSE;
2475 if (p->isEmpty()) {
2476 *rval = JSVAL_NULL;
2477 return JS_TRUE;
2480 EthogramEvent *e = p->pop();
2481 jsval state = INT_TO_JSVAL(e->s);
2482 jsval reason = INT_TO_JSVAL(e->r);
2483 jsval ts = INT_TO_JSVAL(e->ts);
2484 jsval tus = INT_TO_JSVAL(e->tus);
2486 jsval filename = STRING_TO_JSVAL(e->filename);
2487 jsval lineno = INT_TO_JSVAL(e->lineno);
2489 if (!JS_SetProperty(cx, x, "state", &state))
2490 return JS_FALSE;
2491 if (!JS_SetProperty(cx, x, "reason", &reason))
2492 return JS_FALSE;
2493 if (!JS_SetProperty(cx, x, "ts", &ts))
2494 return JS_FALSE;
2495 if (!JS_SetProperty(cx, x, "tus", &tus))
2496 return JS_FALSE;
2497 if (!JS_SetProperty(cx, x, "filename", &filename))
2498 return JS_FALSE;
2500 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2501 return JS_FALSE;
2503 *rval = OBJECT_TO_JSVAL(x);
2505 return JS_TRUE;
2508 static JSFunctionSpec ethogram_methods[] = {
2509 {"addScript", ethogram_addScript, 1},
2510 {"getAllEvents", ethogram_getAllEvents, 0},
2511 {"getNextEvent", ethogram_getNextEvent, 0},
2516 * An |Ethogram| organizes the output of a collection of files that should be
2517 * monitored together. A single object gets events for the group.
2519 JS_FRIEND_API(JSBool)
2520 js_InitEthogram(JSContext *cx, JSObject *obj,
2521 uintN argc, jsval *argv, jsval *rval)
2523 if (!traceVisScriptTable) {
2524 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2525 NULL, NULL, NULL);
2528 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2529 ethogram_construct, 0, NULL, ethogram_methods,
2530 NULL, NULL);
2532 return JS_TRUE;
2535 JS_FRIEND_API(JSBool)
2536 js_ShutdownEthogram(JSContext *cx, JSObject *obj,
2537 uintN argc, jsval *argv, jsval *rval)
2539 if (traceVisScriptTable)
2540 JS_HashTableDestroy(traceVisScriptTable);
2542 return JS_TRUE;
2545 #endif /* MOZ_TRACEVIS */