Bug 588735 - Mirror glass caption buttons for rtl windows. r=roc, a=blocking-betaN.
[mozilla-central.git] / js / src / jsdbgapi.cpp
blobb3528e666bd29d94b200a2429d27ad6718a79c7f
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 * Pass the output of the handler to the setter. Security wrappers
631 * prevent any funny business between watchpoints and setters.
633 JSBool ok = !wp->setter ||
634 (sprop->hasSetterValue()
635 ? InternalCall(cx, obj,
636 ObjectValue(*CastAsObject(wp->setter)),
637 1, vp, vp)
638 : callJSPropertyOpSetter(cx, wp->setter, obj, userid, vp));
640 DBG_LOCK(rt);
641 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
644 DBG_UNLOCK(rt);
645 return JS_TRUE;
648 JSBool
649 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
650 Value *rval)
652 JSObject *funobj;
653 JSFunction *wrapper;
654 jsid userid;
656 funobj = &argv[-2].toObject();
657 wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
658 userid = ATOM_TO_JSID(wrapper->atom);
659 *rval = argv[0];
660 return js_watch_set(cx, obj, userid, rval);
663 static bool
664 IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop)
666 if (sprop->hasSetterValue()) {
667 JSObject *funobj = sprop->setterObject();
668 if (!funobj || !funobj->isFunction())
669 return false;
671 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
672 return FUN_NATIVE(fun) == js_watch_set_wrapper;
674 return sprop->setterOp() == js_watch_set;
677 PropertyOp
678 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, PropertyOp setter)
680 JSAtom *atom;
681 JSFunction *wrapper;
683 if (!(attrs & JSPROP_SETTER))
684 return &js_watch_set; /* & to silence schoolmarmish MSVC */
686 if (JSID_IS_ATOM(id)) {
687 atom = JSID_TO_ATOM(id);
688 } else if (JSID_IS_INT(id)) {
689 if (!js_ValueToStringId(cx, IdToValue(id), &id))
690 return NULL;
691 atom = JSID_TO_ATOM(id);
692 } else {
693 atom = NULL;
696 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
697 setter ? CastAsObject(setter)->getParent() : NULL, atom);
698 if (!wrapper)
699 return NULL;
700 return CastAsPropertyOp(FUN_OBJECT(wrapper));
703 JS_PUBLIC_API(JSBool)
704 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
705 JSWatchPointHandler handler, JSObject *closure)
707 JSObject *origobj;
708 Value v;
709 uintN attrs;
710 jsid propid;
711 JSObject *pobj;
712 JSProperty *prop;
713 JSScopeProperty *sprop;
714 JSRuntime *rt;
715 JSBool ok;
716 JSWatchPoint *wp;
717 PropertyOp watcher;
719 origobj = obj;
720 obj = obj->wrappedObject(cx);
721 OBJ_TO_INNER_OBJECT(cx, obj);
722 if (!obj)
723 return JS_FALSE;
725 AutoValueRooter idroot(cx);
726 if (JSID_IS_INT(id)) {
727 propid = id;
728 } else {
729 if (!js_ValueToStringId(cx, IdToValue(id), &propid))
730 return JS_FALSE;
731 propid = js_CheckForStringIndex(propid);
732 idroot.set(IdToValue(propid));
736 * If, by unwrapping and innerizing, we changed the object, check
737 * again to make sure that we're allowed to set a watch point.
739 if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
740 return JS_FALSE;
742 if (!obj->isNative()) {
743 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
744 obj->getClass()->name);
745 return JS_FALSE;
748 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
749 return JS_FALSE;
750 sprop = (JSScopeProperty *) prop;
751 rt = cx->runtime;
752 if (!sprop) {
753 /* Check for a deleted symbol watchpoint, which holds its property. */
754 sprop = js_FindWatchPoint(rt, obj->scope(), propid);
755 if (!sprop) {
756 /* Make a new property in obj so we can watch for the first set. */
757 if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
758 JSPROP_ENUMERATE, 0, 0, &prop)) {
759 return JS_FALSE;
761 sprop = (JSScopeProperty *) prop;
763 } else if (pobj != obj) {
764 /* Clone the prototype property so we can watch the right object. */
765 AutoValueRooter valroot(cx);
766 PropertyOp getter, setter;
767 uintN attrs, flags;
768 intN shortid;
770 if (pobj->isNative()) {
771 valroot.set(SPROP_HAS_VALID_SLOT(sprop, pobj->scope())
772 ? pobj->lockedGetSlot(sprop->slot)
773 : UndefinedValue());
774 getter = sprop->getter();
775 setter = sprop->setter();
776 attrs = sprop->attributes();
777 flags = sprop->getFlags();
778 shortid = sprop->shortid;
779 JS_UNLOCK_OBJ(cx, pobj);
780 } else {
781 if (!pobj->getProperty(cx, propid, valroot.addr()) ||
782 !pobj->getAttributes(cx, propid, &attrs)) {
783 return JS_FALSE;
785 getter = setter = NULL;
786 flags = 0;
787 shortid = 0;
790 /* Recall that obj is native, whether or not pobj is native. */
791 if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
792 getter, setter, attrs, flags,
793 shortid, &prop)) {
794 return JS_FALSE;
796 sprop = (JSScopeProperty *) prop;
800 * At this point, prop/sprop exists in obj, obj is locked, and we must
801 * unlock the object before returning.
803 ok = JS_TRUE;
804 DBG_LOCK(rt);
805 wp = FindWatchPoint(rt, obj->scope(), propid);
806 if (!wp) {
807 DBG_UNLOCK(rt);
808 watcher = js_WrapWatchedSetter(cx, propid, sprop->attributes(), sprop->setter());
809 if (!watcher) {
810 ok = JS_FALSE;
811 goto out;
814 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
815 if (!wp) {
816 ok = JS_FALSE;
817 goto out;
819 wp->handler = NULL;
820 wp->closure = NULL;
821 wp->object = obj;
822 wp->setter = sprop->setter();
823 wp->flags = JSWP_LIVE;
825 /* XXXbe nest in obj lock here */
826 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attributes(),
827 sprop->getter(), watcher);
828 if (!sprop) {
829 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
830 JS_INIT_CLIST(&wp->links);
831 DBG_LOCK(rt);
832 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
833 ok = JS_FALSE;
834 goto out;
836 wp->sprop = sprop;
839 * Now that wp is fully initialized, append it to rt's wp list.
840 * Because obj is locked we know that no other thread could have added
841 * a watchpoint for (obj, propid).
843 DBG_LOCK(rt);
844 JS_ASSERT(!FindWatchPoint(rt, obj->scope(), propid));
845 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
846 ++rt->debuggerMutations;
848 wp->handler = handler;
849 wp->closure = reinterpret_cast<JSObject*>(closure);
850 DBG_UNLOCK(rt);
852 out:
853 JS_UNLOCK_OBJ(cx, obj);
854 return ok;
857 JS_PUBLIC_API(JSBool)
858 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
859 JSWatchPointHandler *handlerp, JSObject **closurep)
861 JSRuntime *rt;
862 JSWatchPoint *wp;
864 rt = cx->runtime;
865 DBG_LOCK(rt);
866 for (wp = (JSWatchPoint *)rt->watchPointList.next;
867 &wp->links != &rt->watchPointList;
868 wp = (JSWatchPoint *)wp->links.next) {
869 if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
870 if (handlerp)
871 *handlerp = wp->handler;
872 if (closurep)
873 *closurep = wp->closure;
874 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
877 DBG_UNLOCK(rt);
878 if (handlerp)
879 *handlerp = NULL;
880 if (closurep)
881 *closurep = NULL;
882 return JS_TRUE;
885 JS_PUBLIC_API(JSBool)
886 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
888 JSRuntime *rt;
889 JSWatchPoint *wp, *next;
890 uint32 sample;
892 rt = cx->runtime;
893 DBG_LOCK(rt);
894 for (wp = (JSWatchPoint *)rt->watchPointList.next;
895 &wp->links != &rt->watchPointList;
896 wp = next) {
897 next = (JSWatchPoint *)wp->links.next;
898 if (wp->object == obj) {
899 sample = rt->debuggerMutations;
900 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
901 return JS_FALSE;
902 DBG_LOCK(rt);
903 if (rt->debuggerMutations != sample + 1)
904 next = (JSWatchPoint *)rt->watchPointList.next;
907 DBG_UNLOCK(rt);
908 return JS_TRUE;
911 JS_PUBLIC_API(JSBool)
912 JS_ClearAllWatchPoints(JSContext *cx)
914 JSRuntime *rt;
915 JSWatchPoint *wp, *next;
916 uint32 sample;
918 rt = cx->runtime;
919 DBG_LOCK(rt);
920 for (wp = (JSWatchPoint *)rt->watchPointList.next;
921 &wp->links != &rt->watchPointList;
922 wp = next) {
923 next = (JSWatchPoint *)wp->links.next;
924 sample = rt->debuggerMutations;
925 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
926 return JS_FALSE;
927 DBG_LOCK(rt);
928 if (rt->debuggerMutations != sample + 1)
929 next = (JSWatchPoint *)rt->watchPointList.next;
931 DBG_UNLOCK(rt);
932 return JS_TRUE;
935 /************************************************************************/
937 JS_PUBLIC_API(uintN)
938 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
940 return js_PCToLineNumber(cx, script, pc);
943 JS_PUBLIC_API(jsbytecode *)
944 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
946 return js_LineNumberToPC(script, lineno);
949 JS_PUBLIC_API(uintN)
950 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
952 return fun->nargs;
955 JS_PUBLIC_API(JSBool)
956 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
958 return fun->hasLocalNames();
961 extern JS_PUBLIC_API(jsuword *)
962 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
964 *markp = JS_ARENA_MARK(&cx->tempPool);
965 return js_GetLocalNameArray(cx, fun, &cx->tempPool);
968 extern JS_PUBLIC_API(JSAtom *)
969 JS_LocalNameToAtom(jsuword w)
971 return JS_LOCAL_NAME_TO_ATOM(w);
974 extern JS_PUBLIC_API(JSString *)
975 JS_AtomKey(JSAtom *atom)
977 return ATOM_TO_STRING(atom);
980 extern JS_PUBLIC_API(void)
981 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
983 JS_ARENA_RELEASE(&cx->tempPool, mark);
986 JS_PUBLIC_API(JSScript *)
987 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
989 return FUN_SCRIPT(fun);
992 JS_PUBLIC_API(JSNative)
993 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
995 return Jsvalify(FUN_NATIVE(fun));
998 JS_PUBLIC_API(JSFastNative)
999 JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun)
1001 return Jsvalify(FUN_FAST_NATIVE(fun));
1004 JS_PUBLIC_API(JSPrincipals *)
1005 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1007 return script->principals;
1010 /************************************************************************/
1013 * Stack Frame Iterator
1015 JS_PUBLIC_API(JSStackFrame *)
1016 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1018 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down;
1019 return *iteratorp;
1022 JS_PUBLIC_API(JSScript *)
1023 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1025 return fp->maybeScript();
1028 JS_PUBLIC_API(jsbytecode *)
1029 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1031 return fp->pc(cx);
1034 JS_PUBLIC_API(JSStackFrame *)
1035 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1037 return js_GetScriptedCaller(cx, fp);
1040 JS_PUBLIC_API(JSPrincipals *)
1041 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1043 JSSecurityCallbacks *callbacks;
1045 if (fp->hasFunction()) {
1046 callbacks = JS_GetSecurityCallbacks(cx);
1047 if (callbacks && callbacks->findObjectPrincipals) {
1048 if (FUN_OBJECT(fp->getFunction()) != fp->callee())
1049 return callbacks->findObjectPrincipals(cx, fp->callee());
1050 /* FALL THROUGH */
1053 if (fp->hasScript())
1054 return fp->getScript()->principals;
1055 return NULL;
1058 JSPrincipals *
1059 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1061 JSPrincipals *principals, *callerPrincipals;
1062 JSSecurityCallbacks *callbacks;
1064 callbacks = JS_GetSecurityCallbacks(cx);
1065 if (callbacks && callbacks->findObjectPrincipals)
1066 principals = callbacks->findObjectPrincipals(cx, callee);
1067 else
1068 principals = NULL;
1069 if (!caller)
1070 return principals;
1071 callerPrincipals = JS_StackFramePrincipals(cx, caller);
1072 return (callerPrincipals && principals &&
1073 callerPrincipals->subsume(callerPrincipals, principals))
1074 ? principals
1075 : callerPrincipals;
1078 JS_PUBLIC_API(JSPrincipals *)
1079 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
1081 return js_EvalFramePrincipals(cx, fp->callee(), caller);
1084 JS_PUBLIC_API(void *)
1085 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1087 if (fp->hasAnnotation() && fp->hasScript()) {
1088 JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
1090 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1092 * Give out an annotation only if privileges have not been revoked
1093 * or disabled globally.
1095 return fp->getAnnotation();
1099 return NULL;
1102 JS_PUBLIC_API(void)
1103 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1105 fp->setAnnotation(annotation);
1108 JS_PUBLIC_API(void *)
1109 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1111 JSPrincipals *principals;
1113 principals = JS_StackFramePrincipals(cx, fp);
1114 if (!principals)
1115 return NULL;
1116 return principals->getPrincipalArray(cx, principals);
1119 JS_PUBLIC_API(JSBool)
1120 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
1122 return !fp->hasScript();
1125 /* this is deprecated, use JS_GetFrameScopeChain instead */
1126 JS_PUBLIC_API(JSObject *)
1127 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1129 return fp->maybeScopeChain();
1132 JS_PUBLIC_API(JSObject *)
1133 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1135 JS_ASSERT(cx->stack().contains(fp));
1137 /* Force creation of argument and call objects if not yet created */
1138 (void) JS_GetFrameCallObject(cx, fp);
1139 return js_GetScopeChain(cx, fp);
1142 JS_PUBLIC_API(JSObject *)
1143 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1145 JS_ASSERT(cx->stack().contains(fp));
1147 if (!fp->hasFunction())
1148 return NULL;
1150 /* Force creation of argument object if not yet created */
1151 (void) js_GetArgsObject(cx, fp);
1154 * XXX ill-defined: null return here means error was reported, unlike a
1155 * null returned above or in the #else
1157 return js_GetCallObject(cx, fp);
1160 JS_PUBLIC_API(JSObject *)
1161 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
1163 if (fp->isDummyFrame())
1164 return NULL;
1165 else
1166 return fp->getThisObject(cx);
1169 JS_PUBLIC_API(JSFunction *)
1170 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1172 return fp->maybeFunction();
1175 JS_PUBLIC_API(JSObject *)
1176 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1178 if (!fp->hasFunction())
1179 return NULL;
1181 JS_ASSERT(fp->callee()->isFunction());
1182 JS_ASSERT(fp->callee()->getPrivate() == fp->getFunction());
1183 return fp->callee();
1186 JS_PUBLIC_API(JSBool)
1187 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1189 return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
1192 JS_PUBLIC_API(JSObject *)
1193 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1195 return fp->callee();
1198 JS_PUBLIC_API(JSBool)
1199 JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
1201 Value v;
1203 if (!fp->getValidCalleeObject(cx, &v))
1204 return false;
1205 *vp = Jsvalify(v);
1206 return true;
1209 JS_PUBLIC_API(JSBool)
1210 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1212 return (fp->flags & JSFRAME_DEBUGGER) != 0;
1215 JS_PUBLIC_API(jsval)
1216 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1218 return Jsvalify(fp->getReturnValue());
1221 JS_PUBLIC_API(void)
1222 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1224 fp->setReturnValue(Valueify(rval));
1227 /************************************************************************/
1229 JS_PUBLIC_API(const char *)
1230 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1232 return script->filename;
1235 JS_PUBLIC_API(uintN)
1236 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1238 return script->lineno;
1241 JS_PUBLIC_API(uintN)
1242 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1244 return js_GetScriptLineExtent(script);
1247 JS_PUBLIC_API(JSVersion)
1248 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1250 return (JSVersion) (script->version & JSVERSION_MASK);
1253 /***************************************************************************/
1255 JS_PUBLIC_API(void)
1256 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1258 rt->globalDebugHooks.newScriptHook = hook;
1259 rt->globalDebugHooks.newScriptHookData = callerdata;
1262 JS_PUBLIC_API(void)
1263 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1264 void *callerdata)
1266 rt->globalDebugHooks.destroyScriptHook = hook;
1267 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1270 /***************************************************************************/
1272 JS_PUBLIC_API(JSBool)
1273 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1274 const jschar *chars, uintN length,
1275 const char *filename, uintN lineno,
1276 jsval *rval)
1278 JS_ASSERT_NOT_ON_TRACE(cx);
1280 JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1281 if (!scobj)
1282 return false;
1285 * NB: This function breaks the assumption that the compiler can see all
1286 * calls and properly compute a static level. In order to get around this,
1287 * we use a static level that will cause us not to attempt to optimize
1288 * variable references made by this frame.
1290 JSScript *script = Compiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
1291 TCF_COMPILE_N_GO, chars, length, NULL,
1292 filename, lineno, NULL,
1293 UpvarCookie::UPVAR_LEVEL_LIMIT);
1295 if (!script)
1296 return false;
1298 bool ok = !!Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1300 js_DestroyScript(cx, script);
1301 return ok;
1304 JS_PUBLIC_API(JSBool)
1305 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1306 const char *bytes, uintN length,
1307 const char *filename, uintN lineno,
1308 jsval *rval)
1310 jschar *chars;
1311 JSBool ok;
1312 size_t len = length;
1314 chars = js_InflateString(cx, bytes, &len);
1315 if (!chars)
1316 return JS_FALSE;
1317 length = (uintN) len;
1318 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1319 rval);
1320 cx->free(chars);
1322 return ok;
1325 /************************************************************************/
1327 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
1329 JS_PUBLIC_API(JSScopeProperty *)
1330 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1332 JSScopeProperty *sprop;
1333 JSScope *scope;
1335 sprop = *iteratorp;
1336 scope = obj->scope();
1338 /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
1339 sprop = sprop ? sprop->parent : scope->lastProperty();
1340 *iteratorp = sprop;
1341 return sprop;
1344 JS_PUBLIC_API(JSBool)
1345 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1346 JSPropertyDesc *pd)
1348 pd->id = IdToJsval(sprop->id);
1350 JSBool wasThrowing = cx->throwing;
1351 AutoValueRooter lastException(cx, cx->exception);
1352 cx->throwing = JS_FALSE;
1354 if (!js_GetProperty(cx, obj, sprop->id, Valueify(&pd->value))) {
1355 if (!cx->throwing) {
1356 pd->flags = JSPD_ERROR;
1357 pd->value = JSVAL_VOID;
1358 } else {
1359 pd->flags = JSPD_EXCEPTION;
1360 pd->value = Jsvalify(cx->exception);
1362 } else {
1363 pd->flags = 0;
1366 cx->throwing = wasThrowing;
1367 if (wasThrowing)
1368 cx->exception = lastException.value();
1370 pd->flags |= (sprop->enumerable() ? JSPD_ENUMERATE : 0)
1371 | (!sprop->writable() ? JSPD_READONLY : 0)
1372 | (!sprop->configurable() ? JSPD_PERMANENT : 0);
1373 pd->spare = 0;
1374 if (sprop->getter() == js_GetCallArg) {
1375 pd->slot = sprop->shortid;
1376 pd->flags |= JSPD_ARGUMENT;
1377 } else if (sprop->getter() == js_GetCallVar) {
1378 pd->slot = sprop->shortid;
1379 pd->flags |= JSPD_VARIABLE;
1380 } else {
1381 pd->slot = 0;
1383 pd->alias = JSVAL_VOID;
1385 JSScope *scope = obj->scope();
1386 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
1387 JSScopeProperty *aprop;
1388 for (aprop = scope->lastProperty(); aprop; aprop = aprop->parent) {
1389 if (aprop != sprop && aprop->slot == sprop->slot) {
1390 pd->alias = IdToJsval(aprop->id);
1391 break;
1395 return JS_TRUE;
1398 JS_PUBLIC_API(JSBool)
1399 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1401 JSScope *scope;
1402 uint32 i, n;
1403 JSPropertyDesc *pd;
1404 JSScopeProperty *sprop;
1406 Class *clasp = obj->getClass();
1407 if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1408 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1409 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1410 return JS_FALSE;
1412 if (!clasp->enumerate(cx, obj))
1413 return JS_FALSE;
1415 /* have no props, or object's scope has not mutated from that of proto */
1416 scope = obj->scope();
1417 if (scope->entryCount == 0) {
1418 pda->length = 0;
1419 pda->array = NULL;
1420 return JS_TRUE;
1423 n = scope->entryCount;
1424 pd = (JSPropertyDesc *) cx->malloc((size_t)n * sizeof(JSPropertyDesc));
1425 if (!pd)
1426 return JS_FALSE;
1427 i = 0;
1428 for (sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
1429 if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1430 goto bad;
1431 if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1432 goto bad;
1433 if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1434 goto bad;
1435 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1436 goto bad;
1437 if (++i == n)
1438 break;
1440 pda->length = i;
1441 pda->array = pd;
1442 return JS_TRUE;
1444 bad:
1445 pda->length = i + 1;
1446 pda->array = pd;
1447 JS_PutPropertyDescArray(cx, pda);
1448 return JS_FALSE;
1451 JS_PUBLIC_API(void)
1452 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1454 JSPropertyDesc *pd;
1455 uint32 i;
1457 pd = pda->array;
1458 for (i = 0; i < pda->length; i++) {
1459 js_RemoveRoot(cx->runtime, &pd[i].id);
1460 js_RemoveRoot(cx->runtime, &pd[i].value);
1461 if (pd[i].flags & JSPD_ALIAS)
1462 js_RemoveRoot(cx->runtime, &pd[i].alias);
1464 cx->free(pd);
1467 /************************************************************************/
1469 JS_FRIEND_API(JSBool)
1470 js_GetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1471 jsval *vp)
1473 JS_ASSERT(scopeobj->isGlobal());
1475 JSFrameRegs regs;
1476 FrameGuard frame;
1477 if (!cx->stack().pushDummyFrame(cx, frame, regs, scopeobj))
1478 return false;
1480 bool ok = JS_GetPropertyById(cx, obj, id, vp);
1481 frame.getFrame()->putActivationObjects(cx);
1482 return ok;
1485 JS_FRIEND_API(JSBool)
1486 js_SetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1487 jsval *vp)
1489 JS_ASSERT(scopeobj->isGlobal());
1491 JSFrameRegs regs;
1492 FrameGuard frame;
1493 if (!cx->stack().pushDummyFrame(cx, frame, regs, scopeobj))
1494 return false;
1496 bool ok = JS_SetPropertyById(cx, obj, id, vp);
1497 frame.getFrame()->putActivationObjects(cx);
1498 return ok;
1501 JS_FRIEND_API(JSBool)
1502 js_CallFunctionValueWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsval funval,
1503 uintN argc, jsval *argv, jsval *rval)
1505 JS_ASSERT(scopeobj->isGlobal());
1507 JSFrameRegs regs;
1508 FrameGuard frame;
1509 if (!cx->stack().pushDummyFrame(cx, frame, regs, scopeobj))
1510 return false;
1512 bool ok = JS_CallFunctionValue(cx, obj, funval, argc, argv, rval);
1513 frame.getFrame()->putActivationObjects(cx);
1514 return ok;
1517 /************************************************************************/
1519 JS_PUBLIC_API(JSBool)
1520 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1522 rt->globalDebugHooks.debuggerHandler = handler;
1523 rt->globalDebugHooks.debuggerHandlerData = closure;
1524 return JS_TRUE;
1527 JS_PUBLIC_API(JSBool)
1528 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1530 rt->globalDebugHooks.sourceHandler = handler;
1531 rt->globalDebugHooks.sourceHandlerData = closure;
1532 return JS_TRUE;
1535 JS_PUBLIC_API(JSBool)
1536 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1538 rt->globalDebugHooks.executeHook = hook;
1539 rt->globalDebugHooks.executeHookData = closure;
1540 return JS_TRUE;
1543 JS_PUBLIC_API(JSBool)
1544 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1546 #ifdef JS_TRACER
1548 AutoLockGC lock(rt);
1549 bool wasInhibited = rt->debuggerInhibitsJIT();
1550 #endif
1551 rt->globalDebugHooks.callHook = hook;
1552 rt->globalDebugHooks.callHookData = closure;
1553 #ifdef JS_TRACER
1554 JITInhibitingHookChange(rt, wasInhibited);
1556 if (hook)
1557 LeaveTraceRT(rt);
1558 #endif
1559 return JS_TRUE;
1562 JS_PUBLIC_API(JSBool)
1563 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1565 rt->globalDebugHooks.throwHook = hook;
1566 rt->globalDebugHooks.throwHookData = closure;
1567 return JS_TRUE;
1570 JS_PUBLIC_API(JSBool)
1571 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1573 rt->globalDebugHooks.debugErrorHook = hook;
1574 rt->globalDebugHooks.debugErrorHookData = closure;
1575 return JS_TRUE;
1578 /************************************************************************/
1580 JS_PUBLIC_API(size_t)
1581 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1583 size_t nbytes;
1584 JSScope *scope;
1586 nbytes = sizeof *obj;
1587 if (obj->dslots) {
1588 nbytes += (obj->dslots[-1].toPrivateUint32() - JS_INITIAL_NSLOTS + 1)
1589 * sizeof obj->dslots[0];
1591 if (obj->isNative()) {
1592 scope = obj->scope();
1593 if (!scope->isSharedEmpty()) {
1594 nbytes += sizeof *scope;
1595 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1598 return nbytes;
1601 static size_t
1602 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1604 size_t nbytes;
1606 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1607 nbytes += sizeof(JSString);
1608 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1609 return nbytes;
1612 JS_PUBLIC_API(size_t)
1613 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1615 size_t nbytes;
1617 nbytes = sizeof *fun;
1618 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1619 if (FUN_INTERPRETED(fun))
1620 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1621 if (fun->atom)
1622 nbytes += GetAtomTotalSize(cx, fun->atom);
1623 return nbytes;
1626 #include "jsemit.h"
1628 JS_PUBLIC_API(size_t)
1629 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1631 size_t nbytes, pbytes;
1632 jsatomid i;
1633 jssrcnote *sn, *notes;
1634 JSObjectArray *objarray;
1635 JSPrincipals *principals;
1637 nbytes = sizeof *script;
1638 if (script->u.object)
1639 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1641 nbytes += script->length * sizeof script->code[0];
1642 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1643 for (i = 0; i < script->atomMap.length; i++)
1644 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1646 if (script->filename)
1647 nbytes += strlen(script->filename) + 1;
1649 notes = script->notes();
1650 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1651 continue;
1652 nbytes += (sn - notes + 1) * sizeof *sn;
1654 if (script->objectsOffset != 0) {
1655 objarray = script->objects();
1656 i = objarray->length;
1657 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1658 do {
1659 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1660 } while (i != 0);
1663 if (script->regexpsOffset != 0) {
1664 objarray = script->regexps();
1665 i = objarray->length;
1666 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1667 do {
1668 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1669 } while (i != 0);
1672 if (script->trynotesOffset != 0) {
1673 nbytes += sizeof(JSTryNoteArray) +
1674 script->trynotes()->length * sizeof(JSTryNote);
1677 principals = script->principals;
1678 if (principals) {
1679 JS_ASSERT(principals->refcount);
1680 pbytes = sizeof *principals;
1681 if (principals->refcount > 1)
1682 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1683 nbytes += pbytes;
1686 return nbytes;
1689 JS_PUBLIC_API(uint32)
1690 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1692 if (!fp)
1693 fp = js_GetTopStackFrame(cx);
1694 while (fp) {
1695 if (fp->hasScript())
1696 return JS_GetScriptFilenameFlags(fp->getScript());
1697 fp = fp->down;
1699 return 0;
1702 JS_PUBLIC_API(uint32)
1703 JS_GetScriptFilenameFlags(JSScript *script)
1705 JS_ASSERT(script);
1706 if (!script->filename)
1707 return JSFILENAME_NULL;
1708 return js_GetScriptFilenameFlags(script->filename);
1711 JS_PUBLIC_API(JSBool)
1712 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1714 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1715 return JS_FALSE;
1716 return JS_TRUE;
1719 JS_PUBLIC_API(JSBool)
1720 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1722 return obj->isSystem();
1725 JS_PUBLIC_API(JSBool)
1726 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1728 obj->setSystem();
1729 return true;
1732 /************************************************************************/
1734 JS_PUBLIC_API(const JSDebugHooks *)
1735 JS_GetGlobalDebugHooks(JSRuntime *rt)
1737 return &rt->globalDebugHooks;
1740 const JSDebugHooks js_NullDebugHooks = {};
1742 JS_PUBLIC_API(JSDebugHooks *)
1743 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1745 JS_ASSERT(hooks);
1746 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1747 LeaveTrace(cx);
1749 #ifdef JS_TRACER
1750 AutoLockGC lock(cx->runtime);
1751 #endif
1752 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1753 cx->debugHooks = hooks;
1754 #ifdef JS_TRACER
1755 cx->updateJITEnabled();
1756 #endif
1757 return old;
1760 JS_PUBLIC_API(JSDebugHooks *)
1761 JS_ClearContextDebugHooks(JSContext *cx)
1763 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1766 #ifdef MOZ_SHARK
1768 #include <CHUD/CHUD.h>
1770 JS_PUBLIC_API(JSBool)
1771 JS_StartChudRemote()
1773 if (chudIsRemoteAccessAcquired() &&
1774 (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
1775 return JS_TRUE;
1778 return JS_FALSE;
1781 JS_PUBLIC_API(JSBool)
1782 JS_StopChudRemote()
1784 if (chudIsRemoteAccessAcquired() &&
1785 (chudStopRemotePerfMonitor() == chudSuccess)) {
1786 return JS_TRUE;
1789 return JS_FALSE;
1792 JS_PUBLIC_API(JSBool)
1793 JS_ConnectShark()
1795 if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
1796 return JS_FALSE;
1798 if (chudAcquireRemoteAccess() != chudSuccess)
1799 return JS_FALSE;
1801 return JS_TRUE;
1804 JS_PUBLIC_API(JSBool)
1805 JS_DisconnectShark()
1807 if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
1808 return JS_FALSE;
1810 return JS_TRUE;
1813 JS_FRIEND_API(JSBool)
1814 js_StartShark(JSContext *cx, JSObject *obj,
1815 uintN argc, jsval *argv, jsval *rval)
1817 if (!JS_StartChudRemote()) {
1818 JS_ReportError(cx, "Error starting CHUD.");
1819 return JS_FALSE;
1822 return JS_TRUE;
1825 JS_FRIEND_API(JSBool)
1826 js_StopShark(JSContext *cx, JSObject *obj,
1827 uintN argc, jsval *argv, jsval *rval)
1829 if (!JS_StopChudRemote()) {
1830 JS_ReportError(cx, "Error stopping CHUD.");
1831 return JS_FALSE;
1834 return JS_TRUE;
1837 JS_FRIEND_API(JSBool)
1838 js_ConnectShark(JSContext *cx, JSObject *obj,
1839 uintN argc, jsval *argv, jsval *rval)
1841 if (!JS_ConnectShark()) {
1842 JS_ReportError(cx, "Error connecting to Shark.");
1843 return JS_FALSE;
1846 return JS_TRUE;
1849 JS_FRIEND_API(JSBool)
1850 js_DisconnectShark(JSContext *cx, JSObject *obj,
1851 uintN argc, jsval *argv, jsval *rval)
1853 if (!JS_DisconnectShark()) {
1854 JS_ReportError(cx, "Error disconnecting from Shark.");
1855 return JS_FALSE;
1858 return JS_TRUE;
1861 #endif /* MOZ_SHARK */
1863 #ifdef MOZ_CALLGRIND
1865 #include <valgrind/callgrind.h>
1867 JS_FRIEND_API(JSBool)
1868 js_StartCallgrind(JSContext *cx, JSObject *obj,
1869 uintN argc, jsval *argv, jsval *rval)
1871 CALLGRIND_START_INSTRUMENTATION;
1872 CALLGRIND_ZERO_STATS;
1873 return JS_TRUE;
1876 JS_FRIEND_API(JSBool)
1877 js_StopCallgrind(JSContext *cx, JSObject *obj,
1878 uintN argc, jsval *argv, jsval *rval)
1880 CALLGRIND_STOP_INSTRUMENTATION;
1881 return JS_TRUE;
1884 JS_FRIEND_API(JSBool)
1885 js_DumpCallgrind(JSContext *cx, JSObject *obj,
1886 uintN argc, jsval *argv, jsval *rval)
1888 JSString *str;
1889 char *cstr;
1891 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1892 str = JSVAL_TO_STRING(argv[0]);
1893 cstr = js_DeflateString(cx, str->chars(), str->length());
1894 if (cstr) {
1895 CALLGRIND_DUMP_STATS_AT(cstr);
1896 cx->free(cstr);
1897 return JS_TRUE;
1900 CALLGRIND_DUMP_STATS;
1902 return JS_TRUE;
1905 #endif /* MOZ_CALLGRIND */
1907 #ifdef MOZ_VTUNE
1908 #include <VTuneApi.h>
1910 static const char *vtuneErrorMessages[] = {
1911 "unknown, error #0",
1912 "invalid 'max samples' field",
1913 "invalid 'samples per buffer' field",
1914 "invalid 'sample interval' field",
1915 "invalid path",
1916 "sample file in use",
1917 "invalid 'number of events' field",
1918 "unknown, error #7",
1919 "internal error",
1920 "bad event name",
1921 "VTStopSampling called without calling VTStartSampling",
1922 "no events selected for event-based sampling",
1923 "events selected cannot be run together",
1924 "no sampling parameters",
1925 "sample database already exists",
1926 "sampling already started",
1927 "time-based sampling not supported",
1928 "invalid 'sampling parameters size' field",
1929 "invalid 'event size' field",
1930 "sampling file already bound",
1931 "invalid event path",
1932 "invalid license",
1933 "invalid 'global options' field",
1937 JS_FRIEND_API(JSBool)
1938 js_StartVtune(JSContext *cx, JSObject *obj,
1939 uintN argc, jsval *argv, jsval *rval)
1941 VTUNE_EVENT events[] = {
1942 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
1943 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
1946 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
1947 char *default_filename = "mozilla-vtune.tb5";
1948 JSString *str;
1949 U32 status;
1951 VTUNE_SAMPLING_PARAMS params =
1952 sizeof(VTUNE_SAMPLING_PARAMS),
1953 sizeof(VTUNE_EVENT),
1954 0, 0, /* Reserved fields */
1955 1, /* Initialize in "paused" state */
1956 0, /* Max samples, or 0 for "continuous" */
1957 4096, /* Samples per buffer */
1958 0.1, /* Sampling interval in ms */
1959 1, /* 1 for event-based sampling, 0 for time-based */
1961 n_events,
1962 events,
1963 default_filename,
1966 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1967 str = JSVAL_TO_STRING(argv[0]);
1968 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
1971 status = VTStartSampling(&params);
1973 if (params.tb5Filename != default_filename)
1974 cx->free(params.tb5Filename);
1976 if (status != 0) {
1977 if (status == VTAPI_MULTIPLE_RUNS)
1978 VTStopSampling(0);
1979 if (status < sizeof(vtuneErrorMessages))
1980 JS_ReportError(cx, "Vtune setup error: %s",
1981 vtuneErrorMessages[status]);
1982 else
1983 JS_ReportError(cx, "Vtune setup error: %d",
1984 status);
1985 return JS_FALSE;
1987 return JS_TRUE;
1990 JS_FRIEND_API(JSBool)
1991 js_StopVtune(JSContext *cx, JSObject *obj,
1992 uintN argc, jsval *argv, jsval *rval)
1994 U32 status = VTStopSampling(1);
1995 if (status) {
1996 if (status < sizeof(vtuneErrorMessages))
1997 JS_ReportError(cx, "Vtune shutdown error: %s",
1998 vtuneErrorMessages[status]);
1999 else
2000 JS_ReportError(cx, "Vtune shutdown error: %d",
2001 status);
2002 return JS_FALSE;
2004 return JS_TRUE;
2007 JS_FRIEND_API(JSBool)
2008 js_PauseVtune(JSContext *cx, JSObject *obj,
2009 uintN argc, jsval *argv, jsval *rval)
2011 VTPause();
2012 return JS_TRUE;
2015 JS_FRIEND_API(JSBool)
2016 js_ResumeVtune(JSContext *cx, JSObject *obj,
2017 uintN argc, jsval *argv, jsval *rval)
2019 VTResume();
2020 return JS_TRUE;
2023 #endif /* MOZ_VTUNE */
2025 #ifdef MOZ_TRACEVIS
2027 * Ethogram - Javascript wrapper for TraceVis state
2029 * ethology: The scientific study of animal behavior,
2030 * especially as it occurs in a natural environment.
2031 * ethogram: A pictorial catalog of the behavioral patterns of
2032 * an organism or a species.
2035 #if defined(XP_WIN)
2036 #include <windows.h>
2037 #else
2038 #include <sys/time.h>
2039 #endif
2040 #include "jstracer.h"
2042 #define ETHOGRAM_BUF_SIZE 65536
2044 static JSBool
2045 ethogram_construct(JSContext *cx, JSObject *obj,
2046 uintN argc, jsval *argv, jsval *rval);
2047 static void
2048 ethogram_finalize(JSContext *cx, JSObject *obj);
2050 static JSClass ethogram_class = {
2051 "Ethogram",
2052 JSCLASS_HAS_PRIVATE,
2053 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2054 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2055 JSCLASS_NO_OPTIONAL_MEMBERS
2058 struct EthogramEvent {
2059 TraceVisState s;
2060 TraceVisExitReason r;
2061 int ts;
2062 int tus;
2063 JSString *filename;
2064 int lineno;
2067 static int
2068 compare_strings(const void *k1, const void *k2)
2070 return strcmp((const char *) k1, (const char *) k2) == 0;
2073 class EthogramEventBuffer {
2074 private:
2075 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2076 int mReadPos;
2077 int mWritePos;
2078 JSObject *mFilenames;
2079 int mStartSecond;
2081 struct EthogramScriptEntry {
2082 char *filename;
2083 JSString *jsfilename;
2085 EthogramScriptEntry *next;
2087 EthogramScriptEntry *mScripts;
2089 public:
2090 friend JSBool
2091 ethogram_construct(JSContext *cx, JSObject *obj,
2092 uintN argc, jsval *argv, jsval *rval);
2094 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2095 mBuf[mWritePos].s = s;
2096 mBuf[mWritePos].r = r;
2097 #if defined(XP_WIN)
2098 FILETIME now;
2099 GetSystemTimeAsFileTime(&now);
2100 unsigned long long raw_us = 0.1 *
2101 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2102 (unsigned long long) now.dwLowDateTime);
2103 unsigned int sec = raw_us / 1000000L;
2104 unsigned int usec = raw_us % 1000000L;
2105 mBuf[mWritePos].ts = sec - mStartSecond;
2106 mBuf[mWritePos].tus = usec;
2107 #else
2108 struct timeval tv;
2109 gettimeofday(&tv, NULL);
2110 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2111 mBuf[mWritePos].tus = tv.tv_usec;
2112 #endif
2114 JSString *jsfilename = findScript(filename);
2115 mBuf[mWritePos].filename = jsfilename;
2116 mBuf[mWritePos].lineno = lineno;
2118 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2119 if (mWritePos == mReadPos) {
2120 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2124 inline EthogramEvent *pop() {
2125 EthogramEvent *e = &mBuf[mReadPos];
2126 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2127 return e;
2130 bool isEmpty() {
2131 return (mReadPos == mWritePos);
2134 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2135 JSHashNumber hash = JS_HashString(filename);
2136 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2137 if (*hep != NULL)
2138 return JS_FALSE;
2140 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2142 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2143 if (entry == NULL)
2144 return NULL;
2146 entry->next = mScripts;
2147 mScripts = entry;
2148 entry->filename = filename;
2149 entry->jsfilename = jsfilename;
2151 return mScripts;
2154 void removeScripts(JSContext *cx) {
2155 EthogramScriptEntry *se = mScripts;
2156 while (se != NULL) {
2157 char *filename = se->filename;
2159 JSHashNumber hash = JS_HashString(filename);
2160 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2161 JSHashEntry *he = *hep;
2162 if (he) {
2163 /* we hardly knew he */
2164 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2167 EthogramScriptEntry *se_head = se;
2168 se = se->next;
2169 JS_free(cx, se_head);
2173 JSString *findScript(char *filename) {
2174 EthogramScriptEntry *se = mScripts;
2175 while (se != NULL) {
2176 if (compare_strings(se->filename, filename))
2177 return (se->jsfilename);
2178 se = se->next;
2180 return NULL;
2183 JSObject *filenames() {
2184 return mFilenames;
2187 int length() {
2188 if (mWritePos < mReadPos)
2189 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2190 else
2191 return mWritePos - mReadPos;
2195 static char jstv_empty[] = "<null>";
2197 inline char *
2198 jstv_Filename(JSStackFrame *fp)
2200 while (fp && fp->script == NULL)
2201 fp = fp->down;
2202 return (fp && fp->script && fp->script->filename)
2203 ? (char *)fp->script->filename
2204 : jstv_empty;
2206 inline uintN
2207 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2209 while (fp && fp->pc(cx) == NULL)
2210 fp = fp->down;
2211 return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2214 /* Collect states here and distribute to a matching buffer, if any */
2215 JS_FRIEND_API(void)
2216 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2218 JSStackFrame *fp = cx->fp();
2220 char *script_file = jstv_Filename(fp);
2221 JSHashNumber hash = JS_HashString(script_file);
2223 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2224 /* update event buffer, flag if overflowed */
2225 JSHashEntry *he = *hep;
2226 if (he) {
2227 EthogramEventBuffer *p;
2228 p = (EthogramEventBuffer *) he->value;
2230 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2234 static JSBool
2235 ethogram_construct(JSContext *cx, JSObject *obj,
2236 uintN argc, jsval *argv, jsval *rval)
2238 EthogramEventBuffer *p;
2240 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2242 p->mReadPos = p->mWritePos = 0;
2243 p->mScripts = NULL;
2244 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2246 #if defined(XP_WIN)
2247 FILETIME now;
2248 GetSystemTimeAsFileTime(&now);
2249 unsigned long long raw_us = 0.1 *
2250 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2251 (unsigned long long) now.dwLowDateTime);
2252 unsigned int s = raw_us / 1000000L;
2253 p->mStartSecond = s;
2254 #else
2255 struct timeval tv;
2256 gettimeofday(&tv, NULL);
2257 p->mStartSecond = tv.tv_sec;
2258 #endif
2259 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2260 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2261 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2262 return JS_FALSE;
2264 if (!JS_IsConstructing(cx)) {
2265 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2266 if (!obj)
2267 return JS_FALSE;
2268 *rval = OBJECT_TO_JSVAL(obj);
2270 JS_SetPrivate(cx, obj, p);
2271 return JS_TRUE;
2274 static void
2275 ethogram_finalize(JSContext *cx, JSObject *obj)
2277 EthogramEventBuffer *p;
2278 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2279 if (!p)
2280 return;
2282 p->removeScripts(cx);
2284 JS_free(cx, p);
2287 static JSBool
2288 ethogram_addScript(JSContext *cx, JSObject *obj,
2289 uintN argc, jsval *argv, jsval *rval)
2291 JSString *str;
2292 char *filename = NULL;
2293 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2294 str = JSVAL_TO_STRING(argv[0]);
2295 filename = js_DeflateString(cx,
2296 str->chars(),
2297 str->length());
2300 /* silently ignore no args */
2301 if (!filename)
2302 return JS_TRUE;
2304 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2306 p->addScript(cx, obj, filename, str);
2307 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, rval);
2308 return JS_TRUE;
2311 static JSBool
2312 ethogram_getAllEvents(JSContext *cx, JSObject *obj,
2313 uintN argc, jsval *argv, jsval *rval)
2315 EthogramEventBuffer *p;
2317 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2318 if (!p)
2319 return JS_FALSE;
2321 if (p->isEmpty()) {
2322 *rval = JSVAL_NULL;
2323 return JS_TRUE;
2326 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2327 if (rarray == NULL) {
2328 *rval = JSVAL_NULL;
2329 return JS_TRUE;
2332 *rval = OBJECT_TO_JSVAL(rarray);
2334 for (int i = 0; !p->isEmpty(); i++) {
2336 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2337 if (x == NULL)
2338 return JS_FALSE;
2340 EthogramEvent *e = p->pop();
2342 jsval state = INT_TO_JSVAL(e->s);
2343 jsval reason = INT_TO_JSVAL(e->r);
2344 jsval ts = INT_TO_JSVAL(e->ts);
2345 jsval tus = INT_TO_JSVAL(e->tus);
2347 jsval filename = STRING_TO_JSVAL(e->filename);
2348 jsval lineno = INT_TO_JSVAL(e->lineno);
2350 if (!JS_SetProperty(cx, x, "state", &state))
2351 return JS_FALSE;
2352 if (!JS_SetProperty(cx, x, "reason", &reason))
2353 return JS_FALSE;
2354 if (!JS_SetProperty(cx, x, "ts", &ts))
2355 return JS_FALSE;
2356 if (!JS_SetProperty(cx, x, "tus", &tus))
2357 return JS_FALSE;
2359 if (!JS_SetProperty(cx, x, "filename", &filename))
2360 return JS_FALSE;
2361 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2362 return JS_FALSE;
2364 jsval element = OBJECT_TO_JSVAL(x);
2365 JS_SetElement(cx, rarray, i, &element);
2368 return JS_TRUE;
2371 static JSBool
2372 ethogram_getNextEvent(JSContext *cx, JSObject *obj,
2373 uintN argc, jsval *argv, jsval *rval)
2375 EthogramEventBuffer *p;
2377 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2378 if (!p)
2379 return JS_FALSE;
2381 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2382 if (x == NULL)
2383 return JS_FALSE;
2385 if (p->isEmpty()) {
2386 *rval = JSVAL_NULL;
2387 return JS_TRUE;
2390 EthogramEvent *e = p->pop();
2391 jsval state = INT_TO_JSVAL(e->s);
2392 jsval reason = INT_TO_JSVAL(e->r);
2393 jsval ts = INT_TO_JSVAL(e->ts);
2394 jsval tus = INT_TO_JSVAL(e->tus);
2396 jsval filename = STRING_TO_JSVAL(e->filename);
2397 jsval lineno = INT_TO_JSVAL(e->lineno);
2399 if (!JS_SetProperty(cx, x, "state", &state))
2400 return JS_FALSE;
2401 if (!JS_SetProperty(cx, x, "reason", &reason))
2402 return JS_FALSE;
2403 if (!JS_SetProperty(cx, x, "ts", &ts))
2404 return JS_FALSE;
2405 if (!JS_SetProperty(cx, x, "tus", &tus))
2406 return JS_FALSE;
2407 if (!JS_SetProperty(cx, x, "filename", &filename))
2408 return JS_FALSE;
2410 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2411 return JS_FALSE;
2413 *rval = OBJECT_TO_JSVAL(x);
2415 return JS_TRUE;
2418 static JSFunctionSpec ethogram_methods[] = {
2419 {"addScript", ethogram_addScript, 1},
2420 {"getAllEvents", ethogram_getAllEvents, 0},
2421 {"getNextEvent", ethogram_getNextEvent, 0},
2426 * An |Ethogram| organizes the output of a collection of files that should be
2427 * monitored together. A single object gets events for the group.
2429 JS_FRIEND_API(JSBool)
2430 js_InitEthogram(JSContext *cx, JSObject *obj,
2431 uintN argc, jsval *argv, jsval *rval)
2433 if (!traceVisScriptTable) {
2434 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2435 NULL, NULL, NULL);
2438 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2439 ethogram_construct, 0, NULL, ethogram_methods,
2440 NULL, NULL);
2442 return JS_TRUE;
2445 JS_FRIEND_API(JSBool)
2446 js_ShutdownEthogram(JSContext *cx, JSObject *obj,
2447 uintN argc, jsval *argv, jsval *rval)
2449 if (traceVisScriptTable)
2450 JS_HashTableDestroy(traceVisScriptTable);
2452 return JS_TRUE;
2455 #endif /* MOZ_TRACEVIS */