Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jsdbgapi.cpp
blobdc8dc803eda96f9eb7b7af82d79fa82167dea191
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 "jsprvtd.h"
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsutil.h"
49 #include "jsclist.h"
50 #include "jshashtable.h"
51 #include "jsapi.h"
52 #include "jscntxt.h"
53 #include "jsversion.h"
54 #include "jsdbgapi.h"
55 #include "jsemit.h"
56 #include "jsfun.h"
57 #include "jsgc.h"
58 #include "jsinterp.h"
59 #include "jslock.h"
60 #include "jsobj.h"
61 #include "jsopcode.h"
62 #include "jsparse.h"
63 #include "jsscope.h"
64 #include "jsscript.h"
65 #include "jsstaticcheck.h"
66 #include "jsstr.h"
67 #include "jswrapper.h"
69 #include "jsatominlines.h"
70 #include "jsdbgapiinlines.h"
71 #include "jsinterpinlines.h"
72 #include "jsobjinlines.h"
73 #include "jsscopeinlines.h"
74 #include "jsscriptinlines.h"
76 #include "jsautooplen.h"
78 #include "methodjit/MethodJIT.h"
79 #include "methodjit/Retcon.h"
81 using namespace js;
82 using namespace js::gc;
84 typedef struct JSTrap {
85 JSCList links;
86 JSScript *script;
87 jsbytecode *pc;
88 JSOp op;
89 JSTrapHandler handler;
90 jsval closure;
91 } JSTrap;
93 #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
94 #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
95 #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
97 JS_PUBLIC_API(JSBool)
98 JS_GetDebugMode(JSContext *cx)
100 return cx->compartment->debugMode;
103 JS_PUBLIC_API(JSBool)
104 JS_SetDebugMode(JSContext *cx, JSBool debug)
106 return JS_SetDebugModeForCompartment(cx, cx->compartment, debug);
109 JS_PUBLIC_API(void)
110 JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
112 rt->debugMode = debug;
115 #ifdef DEBUG
116 static bool
117 CompartmentHasLiveScripts(JSCompartment *comp)
119 #ifdef JS_METHODJIT
120 # ifdef JS_THREADSAFE
121 jsword currentThreadId = reinterpret_cast<jsword>(js_CurrentThreadId());
122 # endif
123 #endif
125 // Unsynchronized context iteration is technically a race; but this is only
126 // for debug asserts where such a race would be rare
127 JSContext *iter = NULL;
128 JSContext *icx;
129 while ((icx = JS_ContextIterator(comp->rt, &iter))) {
130 #ifdef JS_THREADSAFE
131 if (JS_GetContextThread(icx) != currentThreadId)
132 continue;
133 #endif
134 for (AllFramesIter i(icx); !i.done(); ++i) {
135 JSScript *script = i.fp()->maybeScript();
136 if (script && script->compartment == comp)
137 return JS_TRUE;
141 return JS_FALSE;
143 #endif
145 JS_FRIEND_API(JSBool)
146 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
148 if (comp->debugMode == debug)
149 return JS_TRUE;
151 // This should only be called when no scripts are live. It would even be
152 // incorrect to discard just the non-live scripts' JITScripts because they
153 // might share ICs with live scripts (bug 632343).
154 JS_ASSERT(!CompartmentHasLiveScripts(comp));
156 // All scripts compiled from this point on should be in the requested debugMode.
157 comp->debugMode = debug;
159 // Discard JIT code for any scripts that change debugMode. This function
160 // assumes that 'comp' is in the same thread as 'cx'.
162 #ifdef JS_METHODJIT
163 JSAutoEnterCompartment ac;
165 for (JSScript *script = (JSScript *)comp->scripts.next;
166 &script->links != &comp->scripts;
167 script = (JSScript *)script->links.next)
169 if (!script->debugMode == !debug)
170 continue;
173 * If compartment entry fails, debug mode is left partially on, leading
174 * to a small performance overhead but no loss of correctness. We set
175 * the debug flags to false so that the caller will not later attempt
176 * to use debugging features.
178 if (!ac.entered() && !ac.enter(cx, script)) {
179 comp->debugMode = JS_FALSE;
180 return JS_FALSE;
183 mjit::ReleaseScriptCode(cx, script);
184 script->debugMode = !!debug;
186 #endif
188 return JS_TRUE;
191 JS_FRIEND_API(JSBool)
192 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
194 #ifdef JS_METHODJIT
195 if (!script->singleStepMode == !singleStep)
196 return JS_TRUE;
197 #endif
199 JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
201 #ifdef JS_METHODJIT
202 /* request the next recompile to inject single step interrupts */
203 script->singleStepMode = !!singleStep;
205 js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
206 if (jit && script->singleStepMode != jit->singleStepMode) {
207 js::mjit::Recompiler recompiler(cx, script);
208 if (!recompiler.recompile()) {
209 script->singleStepMode = !singleStep;
210 return JS_FALSE;
213 #endif
214 return JS_TRUE;
217 static JSBool
218 CheckDebugMode(JSContext *cx)
220 JSBool debugMode = JS_GetDebugMode(cx);
222 * :TODO:
223 * This probably should be an assertion, since it's indicative of a severe
224 * API misuse.
226 if (!debugMode) {
227 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
228 NULL, JSMSG_NEED_DEBUG_MODE);
230 return debugMode;
233 JS_PUBLIC_API(JSBool)
234 JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
236 if (!CheckDebugMode(cx))
237 return JS_FALSE;
239 return js_SetSingleStepMode(cx, script, singleStep);
243 * NB: FindTrap must be called with rt->debuggerLock acquired.
245 static JSTrap *
246 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
248 JSTrap *trap;
250 for (trap = (JSTrap *)rt->trapList.next;
251 &trap->links != &rt->trapList;
252 trap = (JSTrap *)trap->links.next) {
253 if (trap->script == script && trap->pc == pc)
254 return trap;
256 return NULL;
259 jsbytecode *
260 js_UntrapScriptCode(JSContext *cx, JSScript *script)
262 jsbytecode *code;
263 JSRuntime *rt;
264 JSTrap *trap;
266 code = script->code;
267 rt = cx->runtime;
268 DBG_LOCK(rt);
269 for (trap = (JSTrap *)rt->trapList.next;
270 &trap->links !=
271 &rt->trapList;
272 trap = (JSTrap *)trap->links.next) {
273 if (trap->script == script &&
274 (size_t)(trap->pc - script->code) < script->length) {
275 if (code == script->code) {
276 jssrcnote *sn, *notes;
277 size_t nbytes;
279 nbytes = script->length * sizeof(jsbytecode);
280 notes = script->notes();
281 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
282 continue;
283 nbytes += (sn - notes + 1) * sizeof *sn;
285 code = (jsbytecode *) cx->malloc(nbytes);
286 if (!code)
287 break;
288 memcpy(code, script->code, nbytes);
289 JS_PURGE_GSN_CACHE(cx);
291 code[trap->pc - script->code] = trap->op;
294 DBG_UNLOCK(rt);
295 return code;
298 JS_PUBLIC_API(JSBool)
299 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
300 JSTrapHandler handler, jsval closure)
302 JSTrap *junk, *trap, *twin;
303 JSRuntime *rt;
304 uint32 sample;
306 if (!CheckDebugMode(cx))
307 return JS_FALSE;
309 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
310 junk = NULL;
311 rt = cx->runtime;
312 DBG_LOCK(rt);
313 trap = FindTrap(rt, script, pc);
314 if (trap) {
315 JS_ASSERT(trap->script == script && trap->pc == pc);
316 JS_ASSERT(*pc == JSOP_TRAP);
317 } else {
318 sample = rt->debuggerMutations;
319 DBG_UNLOCK(rt);
320 trap = (JSTrap *) cx->malloc(sizeof *trap);
321 if (!trap)
322 return JS_FALSE;
323 trap->closure = JSVAL_NULL;
324 DBG_LOCK(rt);
325 twin = (rt->debuggerMutations != sample)
326 ? FindTrap(rt, script, pc)
327 : NULL;
328 if (twin) {
329 junk = trap;
330 trap = twin;
331 } else {
332 JS_APPEND_LINK(&trap->links, &rt->trapList);
333 ++rt->debuggerMutations;
334 trap->script = script;
335 trap->pc = pc;
336 trap->op = (JSOp)*pc;
337 *pc = JSOP_TRAP;
340 trap->handler = handler;
341 trap->closure = closure;
342 DBG_UNLOCK(rt);
343 if (junk)
344 cx->free(junk);
346 #ifdef JS_METHODJIT
347 if (script->hasJITCode()) {
348 js::mjit::Recompiler recompiler(cx, script);
349 if (!recompiler.recompile())
350 return JS_FALSE;
352 #endif
354 return JS_TRUE;
357 JS_PUBLIC_API(JSOp)
358 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
360 JSRuntime *rt;
361 JSTrap *trap;
362 JSOp op;
364 rt = cx->runtime;
365 DBG_LOCK(rt);
366 trap = FindTrap(rt, script, pc);
367 op = trap ? trap->op : (JSOp) *pc;
368 DBG_UNLOCK(rt);
369 return op;
372 static void
373 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
375 ++cx->runtime->debuggerMutations;
376 JS_REMOVE_LINK(&trap->links);
377 *trap->pc = (jsbytecode)trap->op;
378 DBG_UNLOCK(cx->runtime);
379 cx->free(trap);
382 JS_PUBLIC_API(void)
383 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
384 JSTrapHandler *handlerp, jsval *closurep)
386 JSTrap *trap;
388 DBG_LOCK(cx->runtime);
389 trap = FindTrap(cx->runtime, script, pc);
390 if (handlerp)
391 *handlerp = trap ? trap->handler : NULL;
392 if (closurep)
393 *closurep = trap ? trap->closure : JSVAL_NULL;
394 if (trap)
395 DestroyTrapAndUnlock(cx, trap);
396 else
397 DBG_UNLOCK(cx->runtime);
399 #ifdef JS_METHODJIT
400 if (script->hasJITCode()) {
401 mjit::Recompiler recompiler(cx, script);
402 recompiler.recompile();
404 #endif
407 JS_PUBLIC_API(void)
408 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
410 JSRuntime *rt;
411 JSTrap *trap, *next;
412 uint32 sample;
414 rt = cx->runtime;
415 DBG_LOCK(rt);
416 for (trap = (JSTrap *)rt->trapList.next;
417 &trap->links != &rt->trapList;
418 trap = next) {
419 next = (JSTrap *)trap->links.next;
420 if (trap->script == script) {
421 sample = rt->debuggerMutations;
422 DestroyTrapAndUnlock(cx, trap);
423 DBG_LOCK(rt);
424 if (rt->debuggerMutations != sample + 1)
425 next = (JSTrap *)rt->trapList.next;
428 DBG_UNLOCK(rt);
431 JS_PUBLIC_API(void)
432 JS_ClearAllTraps(JSContext *cx)
434 JSRuntime *rt;
435 JSTrap *trap, *next;
436 uint32 sample;
438 rt = cx->runtime;
439 DBG_LOCK(rt);
440 for (trap = (JSTrap *)rt->trapList.next;
441 &trap->links != &rt->trapList;
442 trap = next) {
443 next = (JSTrap *)trap->links.next;
444 sample = rt->debuggerMutations;
445 DestroyTrapAndUnlock(cx, trap);
446 DBG_LOCK(rt);
447 if (rt->debuggerMutations != sample + 1)
448 next = (JSTrap *)rt->trapList.next;
450 DBG_UNLOCK(rt);
454 * NB: js_MarkTraps does not acquire cx->runtime->debuggerLock, since the
455 * debugger should never be racing with the GC (i.e., the debugger must
456 * respect the request model).
458 void
459 js_MarkTraps(JSTracer *trc)
461 JSRuntime *rt = trc->context->runtime;
463 for (JSTrap *trap = (JSTrap *) rt->trapList.next;
464 &trap->links != &rt->trapList;
465 trap = (JSTrap *) trap->links.next) {
466 MarkValue(trc, Valueify(trap->closure), "trap->closure");
470 JS_PUBLIC_API(JSTrapStatus)
471 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
473 JSTrap *trap;
474 jsint op;
475 JSTrapStatus status;
477 DBG_LOCK(cx->runtime);
478 trap = FindTrap(cx->runtime, script, pc);
479 JS_ASSERT(!trap || trap->handler);
480 if (!trap) {
481 op = (JSOp) *pc;
482 DBG_UNLOCK(cx->runtime);
484 /* Defend against "pc for wrong script" API usage error. */
485 JS_ASSERT(op != JSOP_TRAP);
487 #ifdef JS_THREADSAFE
488 /* If the API was abused, we must fail for want of the real op. */
489 if (op == JSOP_TRAP)
490 return JSTRAP_ERROR;
492 /* Assume a race with a debugger thread and try to carry on. */
493 *rval = INT_TO_JSVAL(op);
494 return JSTRAP_CONTINUE;
495 #else
496 /* Always fail if single-threaded (must be an API usage error). */
497 return JSTRAP_ERROR;
498 #endif
500 DBG_UNLOCK(cx->runtime);
503 * It's important that we not use 'trap->' after calling the callback --
504 * the callback might remove the trap!
506 op = (jsint)trap->op;
507 status = trap->handler(cx, script, pc, rval, trap->closure);
508 if (status == JSTRAP_CONTINUE) {
509 /* By convention, return the true op to the interpreter in rval. */
510 *rval = INT_TO_JSVAL(op);
512 return status;
515 #ifdef JS_TRACER
516 static void
517 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
519 if (wasInhibited) {
520 if (!rt->debuggerInhibitsJIT()) {
521 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
522 js_ContextFromLinkField(cl)->updateJITEnabled();
524 } else if (rt->debuggerInhibitsJIT()) {
525 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
526 js_ContextFromLinkField(cl)->traceJitEnabled = false;
529 #endif
531 JS_PUBLIC_API(JSBool)
532 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
534 #ifdef JS_TRACER
536 AutoLockGC lock(rt);
537 bool wasInhibited = rt->debuggerInhibitsJIT();
538 #endif
539 rt->globalDebugHooks.interruptHook = hook;
540 rt->globalDebugHooks.interruptHookData = closure;
541 #ifdef JS_TRACER
542 JITInhibitingHookChange(rt, wasInhibited);
544 #endif
545 return JS_TRUE;
548 JS_PUBLIC_API(JSBool)
549 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
551 #ifdef JS_TRACER
552 AutoLockGC lock(rt);
553 bool wasInhibited = rt->debuggerInhibitsJIT();
554 #endif
555 if (hoop)
556 *hoop = rt->globalDebugHooks.interruptHook;
557 if (closurep)
558 *closurep = rt->globalDebugHooks.interruptHookData;
559 rt->globalDebugHooks.interruptHook = 0;
560 rt->globalDebugHooks.interruptHookData = 0;
561 #ifdef JS_TRACER
562 JITInhibitingHookChange(rt, wasInhibited);
563 #endif
564 return JS_TRUE;
567 /************************************************************************/
569 struct JSWatchPoint {
570 JSCList links;
571 JSObject *object; /* weak link, see js_SweepWatchPoints */
572 const Shape *shape;
573 StrictPropertyOp setter;
574 JSWatchPointHandler handler;
575 JSObject *closure;
576 uintN flags;
579 #define JSWP_LIVE 0x1 /* live because set and not cleared */
580 #define JSWP_HELD 0x2 /* held while running handler/setter */
583 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
585 static JSBool
586 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
588 bool ok = true;
589 JSRuntime *rt = cx->runtime;
591 wp->flags &= ~flag;
592 if (wp->flags != 0) {
593 DBG_UNLOCK(rt);
594 return ok;
598 * Switch to the same compartment as the watch point, since changeProperty, below,
599 * needs to have a compartment.
601 SwitchToCompartment sc(cx, wp->object);
603 /* Remove wp from the list, then restore wp->shape->setter from wp. */
604 ++rt->debuggerMutations;
605 JS_REMOVE_LINK(&wp->links);
606 DBG_UNLOCK(rt);
609 * If the property isn't found on wp->object, then someone else must have deleted it,
610 * and we don't need to change the property attributes.
612 const Shape *shape = wp->shape;
613 const Shape *wprop = wp->object->nativeLookup(shape->id);
614 if (wprop &&
615 wprop->hasSetterValue() == shape->hasSetterValue() &&
616 IsWatchedProperty(cx, wprop)) {
617 shape = wp->object->changeProperty(cx, wprop, 0, wprop->attributes(),
618 wprop->getter(), wp->setter);
619 if (!shape)
620 ok = false;
623 cx->free(wp);
624 return ok;
628 * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
629 * the debugger should never be racing with the GC (i.e., the debugger must
630 * respect the request model).
632 void
633 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
635 JSRuntime *rt;
636 JSWatchPoint *wp;
638 rt = trc->context->runtime;
640 for (wp = (JSWatchPoint *)rt->watchPointList.next;
641 &wp->links != &rt->watchPointList;
642 wp = (JSWatchPoint *)wp->links.next) {
643 if (wp->object == obj) {
644 wp->shape->trace(trc);
645 if (wp->shape->hasSetterValue() && wp->setter)
646 MarkObject(trc, *CastAsObject(wp->setter), "wp->setter");
647 MarkObject(trc, *wp->closure, "wp->closure");
652 void
653 js_SweepWatchPoints(JSContext *cx)
655 JSRuntime *rt;
656 JSWatchPoint *wp, *next;
657 uint32 sample;
659 rt = cx->runtime;
660 DBG_LOCK(rt);
661 for (wp = (JSWatchPoint *)rt->watchPointList.next;
662 &wp->links != &rt->watchPointList;
663 wp = next) {
664 next = (JSWatchPoint *)wp->links.next;
665 if (IsAboutToBeFinalized(cx, wp->object)) {
666 sample = rt->debuggerMutations;
668 /* Ignore failures. */
669 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
670 DBG_LOCK(rt);
671 if (rt->debuggerMutations != sample + 1)
672 next = (JSWatchPoint *)rt->watchPointList.next;
675 DBG_UNLOCK(rt);
681 * NB: LockedFindWatchPoint must be called with rt->debuggerLock acquired.
683 static JSWatchPoint *
684 LockedFindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
686 JSWatchPoint *wp;
688 for (wp = (JSWatchPoint *)rt->watchPointList.next;
689 &wp->links != &rt->watchPointList;
690 wp = (JSWatchPoint *)wp->links.next) {
691 if (wp->object == obj && wp->shape->id == id)
692 return wp;
694 return NULL;
697 static JSWatchPoint *
698 FindWatchPoint(JSRuntime *rt, JSObject *obj, jsid id)
700 JSWatchPoint *wp;
702 DBG_LOCK(rt);
703 wp = LockedFindWatchPoint(rt, obj, id);
704 DBG_UNLOCK(rt);
705 return wp;
708 JSBool
709 js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
711 JSRuntime *rt = cx->runtime;
712 DBG_LOCK(rt);
713 for (JSWatchPoint *wp = (JSWatchPoint *)rt->watchPointList.next;
714 &wp->links != &rt->watchPointList;
715 wp = (JSWatchPoint *)wp->links.next) {
716 const Shape *shape = wp->shape;
717 if (wp->object == obj && SHAPE_USERID(shape) == id && !(wp->flags & JSWP_HELD)) {
718 wp->flags |= JSWP_HELD;
719 DBG_UNLOCK(rt);
721 jsid propid = shape->id;
722 shape = obj->nativeLookup(propid);
723 JS_ASSERT(IsWatchedProperty(cx, shape));
724 jsid userid = SHAPE_USERID(shape);
726 /* Determine the property's old value. */
727 bool ok;
728 uint32 slot = shape->slot;
729 Value old = obj->containsSlot(slot) ? obj->nativeGetSlot(slot) : UndefinedValue();
730 const Shape *needMethodSlotWrite = NULL;
731 if (shape->isMethod()) {
733 * We get here in two cases: (1) the existing watched property
734 * is a method; or (2) the watched property was deleted and is
735 * now in the middle of being re-added via JSOP_SETMETHOD. In
736 * both cases we must trip the method read barrier in order to
737 * avoid passing an uncloned function object to the handler.
739 * Case 2 is especially hairy. js_watch_set, uniquely, gets
740 * called in the middle of creating a method property, after
741 * shape is in obj but before the slot has been set. So in this
742 * case we must finish initializing the half-finished method
743 * property before triggering the method read barrier.
745 * Bonus weirdness: because this changes obj's shape,
746 * js_NativeSet (which is our caller) will not write to the
747 * slot, as it will appear the property was deleted and a new
748 * property added. We must write the slot ourselves -- however
749 * we must do it after calling the watchpoint handler. So set
750 * needMethodSlotWrite here and use it to write to the slot
751 * below, if the handler does not tinker with the property
752 * further.
754 JS_ASSERT(!wp->setter);
755 Value method = ObjectValue(shape->methodObject());
756 if (old.isUndefined())
757 obj->nativeSetSlot(slot, method);
758 ok = obj->methodReadBarrier(cx, *shape, &method);
759 if (!ok)
760 goto out;
761 wp->shape = shape = needMethodSlotWrite = obj->nativeLookup(propid);
762 JS_ASSERT(shape->isDataDescriptor());
763 JS_ASSERT(!shape->isMethod());
764 if (old.isUndefined())
765 obj->nativeSetSlot(shape->slot, old);
766 else
767 old = method;
771 Conditionally<AutoShapeRooter> tvr(needMethodSlotWrite, cx, needMethodSlotWrite);
774 * Call the handler. This invalidates shape, so re-lookup the shape.
775 * NB: wp is held, so we can safely dereference it still.
777 ok = wp->handler(cx, obj, propid, Jsvalify(old), Jsvalify(vp), wp->closure);
778 if (!ok)
779 goto out;
780 shape = obj->nativeLookup(propid);
781 JS_ASSERT_IF(!shape, !wp->setter);
783 if (!shape) {
784 ok = true;
785 } else if (wp->setter) {
787 * Pass the output of the handler to the setter. Security wrappers
788 * prevent any funny business between watchpoints and setters.
790 ok = shape->hasSetterValue()
791 ? ExternalInvoke(cx, ObjectValue(*obj),
792 ObjectValue(*CastAsObject(wp->setter)),
793 1, vp, vp)
794 : CallJSPropertyOpSetter(cx, wp->setter, obj, userid, strict, vp);
795 } else if (shape == needMethodSlotWrite) {
796 /* See comment above about needMethodSlotWrite. */
797 obj->nativeSetSlot(shape->slot, *vp);
798 ok = true;
799 } else {
801 * A property with the default setter might be either a method
802 * or an ordinary function-valued data property subject to the
803 * method write barrier.
805 * It is not the setter's job to call methodWriteBarrier,
806 * but js_watch_set must do so, because the caller will be
807 * fooled into not doing it: shape does *not* have the
808 * default setter and therefore seems not to be a method.
810 ok = obj->methodWriteBarrier(cx, *shape, *vp) != NULL;
814 out:
815 DBG_LOCK(rt);
816 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
819 DBG_UNLOCK(rt);
820 return true;
823 static JSBool
824 js_watch_set_wrapper(JSContext *cx, uintN argc, Value *vp)
826 JSObject *obj = ToObject(cx, &vp[1]);
827 if (!obj)
828 return false;
830 JSObject &funobj = JS_CALLEE(cx, vp).toObject();
831 JSFunction *wrapper = funobj.getFunctionPrivate();
832 jsid userid = ATOM_TO_JSID(wrapper->atom);
834 JS_SET_RVAL(cx, vp, argc ? JS_ARGV(cx, vp)[0] : UndefinedValue());
836 * The strictness we pass here doesn't matter, since we know that it's
837 * a JS setter, which can't depend on the assigning code's strictness.
839 return js_watch_set(cx, obj, userid, false, vp);
842 namespace js {
844 bool
845 IsWatchedProperty(JSContext *cx, const Shape *shape)
847 if (shape->hasSetterValue()) {
848 JSObject *funobj = shape->setterObject();
849 if (!funobj || !funobj->isFunction())
850 return false;
852 JSFunction *fun = funobj->getFunctionPrivate();
853 return fun->maybeNative() == js_watch_set_wrapper;
855 return shape->setterOp() == js_watch_set;
861 * Return an appropriate setter to substitute for |setter| on a property
862 * with attributes |attrs|, to implement a watchpoint on the property named
863 * |id|.
865 static StrictPropertyOp
866 WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, StrictPropertyOp setter)
868 JSAtom *atom;
869 JSFunction *wrapper;
871 /* Wrap a C++ setter simply by returning our own C++ setter. */
872 if (!(attrs & JSPROP_SETTER))
873 return &js_watch_set; /* & to silence schoolmarmish MSVC */
876 * Wrap a JSObject * setter by constructing our own JSFunction * that saves the
877 * property id as the function name, and calls js_watch_set.
879 if (JSID_IS_ATOM(id)) {
880 atom = JSID_TO_ATOM(id);
881 } else if (JSID_IS_INT(id)) {
882 if (!js_ValueToStringId(cx, IdToValue(id), &id))
883 return NULL;
884 atom = JSID_TO_ATOM(id);
885 } else {
886 atom = NULL;
889 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
890 setter ? CastAsObject(setter)->getParent() : NULL, atom);
891 if (!wrapper)
892 return NULL;
893 return CastAsStrictPropertyOp(FUN_OBJECT(wrapper));
896 static const Shape *
897 UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
899 JS_ASSERT_IF(wp->shape, wp->shape->id == newShape->id);
900 JS_ASSERT(!IsWatchedProperty(cx, newShape));
902 /* Create a watching setter we can substitute for the new shape's setter. */
903 StrictPropertyOp watchingSetter =
904 WrapWatchedSetter(cx, newShape->id, newShape->attributes(), newShape->setter());
905 if (!watchingSetter)
906 return NULL;
909 * Save the shape's setter; we don't know whether js_ChangeNativePropertyAttrs will
910 * return a new shape, or mutate this one.
912 StrictPropertyOp originalSetter = newShape->setter();
915 * Drop the watching setter into the object, in place of newShape. Note that a single
916 * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape: we
917 * wrap all (JSPropertyOp, not JSObject *) setters with js_watch_set, so shapes that
918 * differ only in their setter may all get wrapped to the same shape.
920 const Shape *watchingShape =
921 js_ChangeNativePropertyAttrs(cx, wp->object, newShape, 0, newShape->attributes(),
922 newShape->getter(), watchingSetter);
923 if (!watchingShape)
924 return NULL;
926 /* Update the watchpoint with the new shape and its original setter. */
927 wp->setter = originalSetter;
928 wp->shape = watchingShape;
930 return watchingShape;
933 const Shape *
934 js_SlowPathUpdateWatchpointsForShape(JSContext *cx, JSObject *obj, const Shape *newShape)
937 * The watchpoint code uses the normal property-modification functions to install its
938 * own watchpoint-aware shapes. Those functions report those changes back to the
939 * watchpoint code, just as they do user-level changes. So if this change is
940 * installing a watchpoint-aware shape, it's something we asked for ourselves, and can
941 * proceed without interference.
943 if (IsWatchedProperty(cx, newShape))
944 return newShape;
946 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, newShape->id);
947 if (!wp)
948 return newShape;
950 return UpdateWatchpointShape(cx, wp, newShape);
954 * Return the underlying setter for |shape| on |obj|, seeing through any
955 * watchpoint-wrapping. Note that we need |obj| to disambiguate, since a single
956 * watchpoint-wrapped shape may correspond to more than one non-watchpoint shape; see the
957 * comments in UpdateWatchpointShape.
959 static StrictPropertyOp
960 UnwrapSetter(JSContext *cx, JSObject *obj, const Shape *shape)
962 /* If it's not a watched property, its setter is not wrapped. */
963 if (!IsWatchedProperty(cx, shape))
964 return shape->setter();
966 /* Look up the watchpoint, from which we can retrieve the underlying setter. */
967 JSWatchPoint *wp = FindWatchPoint(cx->runtime, obj, shape->id);
970 * Since we know |shape| is watched, we *must* find a watchpoint: we should never
971 * leave wrapped setters lying around in shapes after removing a watchpoint.
973 JS_ASSERT(wp);
975 return wp->setter;
978 JS_PUBLIC_API(JSBool)
979 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsid id,
980 JSWatchPointHandler handler, JSObject *closure)
982 JSObject *origobj;
983 Value v;
984 uintN attrs;
985 jsid propid;
987 origobj = obj;
988 OBJ_TO_INNER_OBJECT(cx, obj);
989 if (!obj)
990 return JS_FALSE;
992 AutoValueRooter idroot(cx);
993 if (JSID_IS_INT(id)) {
994 propid = id;
995 } else {
996 if (!js_ValueToStringId(cx, IdToValue(id), &propid))
997 return JS_FALSE;
998 propid = js_CheckForStringIndex(propid);
999 idroot.set(IdToValue(propid));
1003 * If, by unwrapping and innerizing, we changed the object, check
1004 * again to make sure that we're allowed to set a watch point.
1006 if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
1007 return JS_FALSE;
1009 if (!obj->isNative()) {
1010 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
1011 obj->getClass()->name);
1012 return JS_FALSE;
1015 JSObject *pobj;
1016 JSProperty *prop;
1017 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
1018 return JS_FALSE;
1019 const Shape *shape = (Shape *) prop;
1020 JSRuntime *rt = cx->runtime;
1021 if (!shape) {
1022 /* Check for a deleted symbol watchpoint, which holds its property. */
1023 JSWatchPoint *wp = FindWatchPoint(rt, obj, propid);
1024 if (!wp) {
1025 /* Make a new property in obj so we can watch for the first set. */
1026 if (!js_DefineNativeProperty(cx, obj, propid, UndefinedValue(), NULL, NULL,
1027 JSPROP_ENUMERATE, 0, 0, &prop)) {
1028 return JS_FALSE;
1030 shape = (Shape *) prop;
1032 } else if (pobj != obj) {
1033 /* Clone the prototype property so we can watch the right object. */
1034 AutoValueRooter valroot(cx);
1035 PropertyOp getter;
1036 StrictPropertyOp setter;
1037 uintN attrs, flags;
1038 intN shortid;
1040 if (pobj->isNative()) {
1041 valroot.set(pobj->containsSlot(shape->slot)
1042 ? pobj->nativeGetSlot(shape->slot)
1043 : UndefinedValue());
1044 getter = shape->getter();
1045 setter = UnwrapSetter(cx, pobj, shape);
1046 attrs = shape->attributes();
1047 flags = shape->getFlags();
1048 shortid = shape->shortid;
1049 } else {
1050 if (!pobj->getProperty(cx, propid, valroot.addr()) ||
1051 !pobj->getAttributes(cx, propid, &attrs)) {
1052 return JS_FALSE;
1054 getter = NULL;
1055 setter = NULL;
1056 flags = 0;
1057 shortid = 0;
1060 /* Recall that obj is native, whether or not pobj is native. */
1061 if (!js_DefineNativeProperty(cx, obj, propid, valroot.value(),
1062 getter, setter, attrs, flags,
1063 shortid, &prop)) {
1064 return JS_FALSE;
1066 shape = (Shape *) prop;
1070 * At this point, prop/shape exists in obj, obj is locked, and we must
1071 * unlock the object before returning.
1073 DBG_LOCK(rt);
1074 JSWatchPoint *wp = LockedFindWatchPoint(rt, obj, propid);
1075 if (!wp) {
1076 DBG_UNLOCK(rt);
1077 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
1078 if (!wp)
1079 return JS_FALSE;
1080 wp->handler = NULL;
1081 wp->closure = NULL;
1082 wp->object = obj;
1083 wp->shape = NULL;
1084 wp->flags = JSWP_LIVE;
1086 /* XXXbe nest in obj lock here */
1087 if (!UpdateWatchpointShape(cx, wp, shape)) {
1088 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
1089 JS_INIT_CLIST(&wp->links);
1090 DBG_LOCK(rt);
1091 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1092 return JS_FALSE;
1096 * Now that wp is fully initialized, append it to rt's wp list.
1097 * Because obj is locked we know that no other thread could have added
1098 * a watchpoint for (obj, propid).
1100 DBG_LOCK(rt);
1101 JS_ASSERT(!LockedFindWatchPoint(rt, obj, propid));
1102 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
1103 ++rt->debuggerMutations;
1107 * Ensure that an object with watchpoints never has the same shape as an
1108 * object without them, even if the watched properties are deleted.
1110 obj->watchpointOwnShapeChange(cx);
1112 wp->handler = handler;
1113 wp->closure = reinterpret_cast<JSObject*>(closure);
1114 DBG_UNLOCK(rt);
1115 return JS_TRUE;
1118 JS_PUBLIC_API(JSBool)
1119 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
1120 JSWatchPointHandler *handlerp, JSObject **closurep)
1122 JSRuntime *rt;
1123 JSWatchPoint *wp;
1125 rt = cx->runtime;
1126 DBG_LOCK(rt);
1127 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1128 &wp->links != &rt->watchPointList;
1129 wp = (JSWatchPoint *)wp->links.next) {
1130 if (wp->object == obj && SHAPE_USERID(wp->shape) == id) {
1131 if (handlerp)
1132 *handlerp = wp->handler;
1133 if (closurep)
1134 *closurep = wp->closure;
1135 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
1138 DBG_UNLOCK(rt);
1139 if (handlerp)
1140 *handlerp = NULL;
1141 if (closurep)
1142 *closurep = NULL;
1143 return JS_TRUE;
1146 JS_PUBLIC_API(JSBool)
1147 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
1149 JSRuntime *rt;
1150 JSWatchPoint *wp, *next;
1151 uint32 sample;
1153 rt = cx->runtime;
1154 DBG_LOCK(rt);
1155 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1156 &wp->links != &rt->watchPointList;
1157 wp = next) {
1158 next = (JSWatchPoint *)wp->links.next;
1159 if (wp->object == obj) {
1160 sample = rt->debuggerMutations;
1161 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1162 return JS_FALSE;
1163 DBG_LOCK(rt);
1164 if (rt->debuggerMutations != sample + 1)
1165 next = (JSWatchPoint *)rt->watchPointList.next;
1168 DBG_UNLOCK(rt);
1169 return JS_TRUE;
1172 JS_PUBLIC_API(JSBool)
1173 JS_ClearAllWatchPoints(JSContext *cx)
1175 JSRuntime *rt;
1176 JSWatchPoint *wp, *next;
1177 uint32 sample;
1179 rt = cx->runtime;
1180 DBG_LOCK(rt);
1181 for (wp = (JSWatchPoint *)rt->watchPointList.next;
1182 &wp->links != &rt->watchPointList;
1183 wp = next) {
1184 next = (JSWatchPoint *)wp->links.next;
1185 sample = rt->debuggerMutations;
1186 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
1187 return JS_FALSE;
1188 DBG_LOCK(rt);
1189 if (rt->debuggerMutations != sample + 1)
1190 next = (JSWatchPoint *)rt->watchPointList.next;
1192 DBG_UNLOCK(rt);
1193 return JS_TRUE;
1196 /************************************************************************/
1198 JS_PUBLIC_API(uintN)
1199 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1201 return js_PCToLineNumber(cx, script, pc);
1204 JS_PUBLIC_API(jsbytecode *)
1205 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1207 return js_LineNumberToPC(script, lineno);
1210 JS_PUBLIC_API(jsbytecode *)
1211 JS_EndPC(JSContext *cx, JSScript *script)
1213 return script->code + script->length;
1216 JS_PUBLIC_API(uintN)
1217 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
1219 return fun->nargs;
1222 JS_PUBLIC_API(JSBool)
1223 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
1225 return fun->script()->bindings.hasLocalNames();
1228 extern JS_PUBLIC_API(jsuword *)
1229 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
1231 *markp = JS_ARENA_MARK(&cx->tempPool);
1232 return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
1235 extern JS_PUBLIC_API(JSAtom *)
1236 JS_LocalNameToAtom(jsuword w)
1238 return JS_LOCAL_NAME_TO_ATOM(w);
1241 extern JS_PUBLIC_API(JSString *)
1242 JS_AtomKey(JSAtom *atom)
1244 return ATOM_TO_STRING(atom);
1247 extern JS_PUBLIC_API(void)
1248 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark)
1250 JS_ARENA_RELEASE(&cx->tempPool, mark);
1253 JS_PUBLIC_API(JSScript *)
1254 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1256 return FUN_SCRIPT(fun);
1259 JS_PUBLIC_API(JSNative)
1260 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1262 return Jsvalify(fun->maybeNative());
1265 JS_PUBLIC_API(JSPrincipals *)
1266 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1268 return script->principals;
1271 /************************************************************************/
1274 * Stack Frame Iterator
1276 JS_PUBLIC_API(JSStackFrame *)
1277 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1279 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->prev();
1280 return *iteratorp;
1283 JS_PUBLIC_API(JSScript *)
1284 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1286 return fp->maybeScript();
1289 JS_PUBLIC_API(jsbytecode *)
1290 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1292 return fp->pc(cx);
1295 JS_PUBLIC_API(JSStackFrame *)
1296 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1298 return js_GetScriptedCaller(cx, fp);
1301 JSPrincipals *
1302 js_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1304 JSSecurityCallbacks *callbacks;
1306 if (fp->isFunctionFrame()) {
1307 callbacks = JS_GetSecurityCallbacks(cx);
1308 if (callbacks && callbacks->findObjectPrincipals) {
1309 if (&fp->fun()->compiledFunObj() != &fp->callee())
1310 return callbacks->findObjectPrincipals(cx, &fp->callee());
1311 /* FALL THROUGH */
1314 if (fp->isScriptFrame())
1315 return fp->script()->principals;
1316 return NULL;
1319 JSPrincipals *
1320 js_EvalFramePrincipals(JSContext *cx, JSObject *callee, JSStackFrame *caller)
1322 JSPrincipals *principals, *callerPrincipals;
1323 JSSecurityCallbacks *callbacks;
1325 callbacks = JS_GetSecurityCallbacks(cx);
1326 if (callbacks && callbacks->findObjectPrincipals)
1327 principals = callbacks->findObjectPrincipals(cx, callee);
1328 else
1329 principals = NULL;
1330 if (!caller)
1331 return principals;
1332 callerPrincipals = js_StackFramePrincipals(cx, caller);
1333 return (callerPrincipals && principals &&
1334 callerPrincipals->subsume(callerPrincipals, principals))
1335 ? principals
1336 : callerPrincipals;
1339 JS_PUBLIC_API(void *)
1340 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1342 if (fp->annotation() && fp->isScriptFrame()) {
1343 JSPrincipals *principals = js_StackFramePrincipals(cx, fp);
1345 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1347 * Give out an annotation only if privileges have not been revoked
1348 * or disabled globally.
1350 return fp->annotation();
1354 return NULL;
1357 JS_PUBLIC_API(void)
1358 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1360 fp->setAnnotation(annotation);
1363 JS_PUBLIC_API(void *)
1364 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1366 JSPrincipals *principals;
1368 principals = js_StackFramePrincipals(cx, fp);
1369 if (!principals)
1370 return NULL;
1371 return principals->getPrincipalArray(cx, principals);
1374 JS_PUBLIC_API(JSBool)
1375 JS_IsScriptFrame(JSContext *cx, JSStackFrame *fp)
1377 return !fp->isDummyFrame();
1380 /* this is deprecated, use JS_GetFrameScopeChain instead */
1381 JS_PUBLIC_API(JSObject *)
1382 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1384 return &fp->scopeChain();
1387 JS_PUBLIC_API(JSObject *)
1388 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1390 JS_ASSERT(cx->stack().contains(fp));
1392 js::AutoCompartment ac(cx, &fp->scopeChain());
1393 if (!ac.enter())
1394 return NULL;
1396 /* Force creation of argument and call objects if not yet created */
1397 (void) JS_GetFrameCallObject(cx, fp);
1398 return GetScopeChain(cx, fp);
1401 JS_PUBLIC_API(JSObject *)
1402 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1404 JS_ASSERT(cx->stack().contains(fp));
1406 if (!fp->isFunctionFrame())
1407 return NULL;
1409 js::AutoCompartment ac(cx, &fp->scopeChain());
1410 if (!ac.enter())
1411 return NULL;
1413 /* Force creation of argument object if not yet created */
1414 (void) js_GetArgsObject(cx, fp);
1417 * XXX ill-defined: null return here means error was reported, unlike a
1418 * null returned above or in the #else
1420 return js_GetCallObject(cx, fp);
1423 JS_PUBLIC_API(JSBool)
1424 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp, jsval *thisv)
1426 if (fp->isDummyFrame())
1427 return false;
1429 js::AutoCompartment ac(cx, &fp->scopeChain());
1430 if (!ac.enter())
1431 return false;
1433 if (!fp->computeThis(cx))
1434 return false;
1435 *thisv = Jsvalify(fp->thisValue());
1436 return true;
1439 JS_PUBLIC_API(JSFunction *)
1440 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1442 return fp->maybeFun();
1445 JS_PUBLIC_API(JSObject *)
1446 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1448 if (!fp->isFunctionFrame())
1449 return NULL;
1451 JS_ASSERT(fp->callee().isFunction());
1452 JS_ASSERT(fp->callee().getPrivate() == fp->fun());
1453 return &fp->callee();
1456 JS_PUBLIC_API(JSBool)
1457 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1459 return fp->isConstructing();
1462 JS_PUBLIC_API(JSObject *)
1463 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1465 return fp->maybeCallee();
1468 JS_PUBLIC_API(JSBool)
1469 JS_GetValidFrameCalleeObject(JSContext *cx, JSStackFrame *fp, jsval *vp)
1471 Value v;
1473 if (!fp->getValidCalleeObject(cx, &v))
1474 return false;
1475 *vp = Jsvalify(v);
1476 return true;
1479 JS_PUBLIC_API(JSBool)
1480 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1482 return fp->isDebuggerFrame();
1485 JS_PUBLIC_API(jsval)
1486 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1488 return Jsvalify(fp->returnValue());
1491 JS_PUBLIC_API(void)
1492 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1494 #ifdef JS_METHODJIT
1495 JS_ASSERT_IF(fp->isScriptFrame(), fp->script()->debugMode);
1496 #endif
1497 assertSameCompartment(cx, fp, rval);
1498 fp->setReturnValue(Valueify(rval));
1501 /************************************************************************/
1503 JS_PUBLIC_API(const char *)
1504 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1506 return script->filename;
1509 JS_PUBLIC_API(uintN)
1510 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1512 return script->lineno;
1515 JS_PUBLIC_API(uintN)
1516 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1518 return js_GetScriptLineExtent(script);
1521 JS_PUBLIC_API(JSVersion)
1522 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1524 return VersionNumber(script->getVersion());
1527 /***************************************************************************/
1529 JS_PUBLIC_API(void)
1530 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1532 rt->globalDebugHooks.newScriptHook = hook;
1533 rt->globalDebugHooks.newScriptHookData = callerdata;
1536 JS_PUBLIC_API(void)
1537 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1538 void *callerdata)
1540 rt->globalDebugHooks.destroyScriptHook = hook;
1541 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1544 /***************************************************************************/
1546 JS_PUBLIC_API(JSBool)
1547 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1548 const jschar *chars, uintN length,
1549 const char *filename, uintN lineno,
1550 jsval *rval)
1552 JS_ASSERT_NOT_ON_TRACE(cx);
1554 if (!CheckDebugMode(cx))
1555 return false;
1557 JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
1558 if (!scobj)
1559 return false;
1561 js::AutoCompartment ac(cx, scobj);
1562 if (!ac.enter())
1563 return false;
1566 * NB: This function breaks the assumption that the compiler can see all
1567 * calls and properly compute a static level. In order to get around this,
1568 * we use a static level that will cause us not to attempt to optimize
1569 * variable references made by this frame.
1571 JSScript *script = Compiler::compileScript(cx, scobj, fp, js_StackFramePrincipals(cx, fp),
1572 TCF_COMPILE_N_GO, chars, length,
1573 filename, lineno, cx->findVersion(),
1574 NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
1576 if (!script)
1577 return false;
1579 bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
1581 js_DestroyScript(cx, script);
1582 return ok;
1585 JS_PUBLIC_API(JSBool)
1586 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1587 const char *bytes, uintN length,
1588 const char *filename, uintN lineno,
1589 jsval *rval)
1591 jschar *chars;
1592 JSBool ok;
1593 size_t len = length;
1595 if (!CheckDebugMode(cx))
1596 return JS_FALSE;
1598 chars = js_InflateString(cx, bytes, &len);
1599 if (!chars)
1600 return JS_FALSE;
1601 length = (uintN) len;
1602 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1603 rval);
1604 cx->free(chars);
1606 return ok;
1609 /************************************************************************/
1611 /* This all should be reworked to avoid requiring JSScopeProperty types. */
1613 JS_PUBLIC_API(JSScopeProperty *)
1614 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1616 const Shape *shape;
1618 /* The caller passes null in *iteratorp to get things started. */
1619 shape = (Shape *) *iteratorp;
1620 if (!shape) {
1621 shape = obj->lastProperty();
1622 } else {
1623 shape = shape->previous();
1624 if (!shape->previous()) {
1625 JS_ASSERT(JSID_IS_EMPTY(shape->id));
1626 shape = NULL;
1630 return *iteratorp = reinterpret_cast<JSScopeProperty *>(const_cast<Shape *>(shape));
1633 JS_PUBLIC_API(JSBool)
1634 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1635 JSPropertyDesc *pd)
1637 assertSameCompartment(cx, obj);
1638 Shape *shape = (Shape *) sprop;
1639 pd->id = IdToJsval(shape->id);
1641 JSBool wasThrowing = cx->isExceptionPending();
1642 Value lastException = UndefinedValue();
1643 if (wasThrowing)
1644 lastException = cx->getPendingException();
1645 cx->clearPendingException();
1647 if (!js_GetProperty(cx, obj, shape->id, Valueify(&pd->value))) {
1648 if (!cx->isExceptionPending()) {
1649 pd->flags = JSPD_ERROR;
1650 pd->value = JSVAL_VOID;
1651 } else {
1652 pd->flags = JSPD_EXCEPTION;
1653 pd->value = Jsvalify(cx->getPendingException());
1655 } else {
1656 pd->flags = 0;
1659 if (wasThrowing)
1660 cx->setPendingException(lastException);
1662 pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
1663 | (!shape->writable() ? JSPD_READONLY : 0)
1664 | (!shape->configurable() ? JSPD_PERMANENT : 0);
1665 pd->spare = 0;
1666 if (shape->getter() == GetCallArg) {
1667 pd->slot = shape->shortid;
1668 pd->flags |= JSPD_ARGUMENT;
1669 } else if (shape->getter() == GetCallVar) {
1670 pd->slot = shape->shortid;
1671 pd->flags |= JSPD_VARIABLE;
1672 } else {
1673 pd->slot = 0;
1675 pd->alias = JSVAL_VOID;
1677 if (obj->containsSlot(shape->slot)) {
1678 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1679 const Shape &aprop = r.front();
1680 if (&aprop != shape && aprop.slot == shape->slot) {
1681 pd->alias = IdToJsval(aprop.id);
1682 break;
1686 return JS_TRUE;
1689 JS_PUBLIC_API(JSBool)
1690 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1692 assertSameCompartment(cx, obj);
1693 Class *clasp = obj->getClass();
1694 if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1695 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1696 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1697 return JS_FALSE;
1699 if (!clasp->enumerate(cx, obj))
1700 return JS_FALSE;
1702 /* Return an empty pda early if obj has no own properties. */
1703 if (obj->nativeEmpty()) {
1704 pda->length = 0;
1705 pda->array = NULL;
1706 return JS_TRUE;
1709 uint32 n = obj->propertyCount();
1710 JSPropertyDesc *pd = (JSPropertyDesc *) cx->malloc(size_t(n) * sizeof(JSPropertyDesc));
1711 if (!pd)
1712 return JS_FALSE;
1713 uint32 i = 0;
1714 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
1715 if (!js_AddRoot(cx, Valueify(&pd[i].id), NULL))
1716 goto bad;
1717 if (!js_AddRoot(cx, Valueify(&pd[i].value), NULL))
1718 goto bad;
1719 Shape *shape = const_cast<Shape *>(&r.front());
1720 if (!JS_GetPropertyDesc(cx, obj, reinterpret_cast<JSScopeProperty *>(shape), &pd[i]))
1721 goto bad;
1722 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, Valueify(&pd[i].alias), NULL))
1723 goto bad;
1724 if (++i == n)
1725 break;
1727 pda->length = i;
1728 pda->array = pd;
1729 return JS_TRUE;
1731 bad:
1732 pda->length = i + 1;
1733 pda->array = pd;
1734 JS_PutPropertyDescArray(cx, pda);
1735 return JS_FALSE;
1738 JS_PUBLIC_API(void)
1739 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1741 JSPropertyDesc *pd;
1742 uint32 i;
1744 pd = pda->array;
1745 for (i = 0; i < pda->length; i++) {
1746 js_RemoveRoot(cx->runtime, &pd[i].id);
1747 js_RemoveRoot(cx->runtime, &pd[i].value);
1748 if (pd[i].flags & JSPD_ALIAS)
1749 js_RemoveRoot(cx->runtime, &pd[i].alias);
1751 cx->free(pd);
1754 /************************************************************************/
1756 JS_PUBLIC_API(JSBool)
1757 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
1759 rt->globalDebugHooks.debuggerHandler = handler;
1760 rt->globalDebugHooks.debuggerHandlerData = closure;
1761 return JS_TRUE;
1764 JS_PUBLIC_API(JSBool)
1765 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1767 rt->globalDebugHooks.sourceHandler = handler;
1768 rt->globalDebugHooks.sourceHandlerData = closure;
1769 return JS_TRUE;
1772 JS_PUBLIC_API(JSBool)
1773 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1775 rt->globalDebugHooks.executeHook = hook;
1776 rt->globalDebugHooks.executeHookData = closure;
1777 return JS_TRUE;
1780 JS_PUBLIC_API(JSBool)
1781 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1783 #ifdef JS_TRACER
1785 AutoLockGC lock(rt);
1786 bool wasInhibited = rt->debuggerInhibitsJIT();
1787 #endif
1788 rt->globalDebugHooks.callHook = hook;
1789 rt->globalDebugHooks.callHookData = closure;
1790 #ifdef JS_TRACER
1791 JITInhibitingHookChange(rt, wasInhibited);
1793 #endif
1794 return JS_TRUE;
1797 JS_PUBLIC_API(JSBool)
1798 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
1800 rt->globalDebugHooks.throwHook = hook;
1801 rt->globalDebugHooks.throwHookData = closure;
1802 return JS_TRUE;
1805 JS_PUBLIC_API(JSBool)
1806 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1808 rt->globalDebugHooks.debugErrorHook = hook;
1809 rt->globalDebugHooks.debugErrorHookData = closure;
1810 return JS_TRUE;
1813 /************************************************************************/
1815 JS_PUBLIC_API(size_t)
1816 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1818 return obj->slotsAndStructSize();
1821 static size_t
1822 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1824 size_t nbytes;
1826 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1827 nbytes += sizeof(JSString);
1828 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1829 return nbytes;
1832 JS_PUBLIC_API(size_t)
1833 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1835 size_t nbytes;
1837 nbytes = sizeof *fun;
1838 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1839 if (FUN_INTERPRETED(fun))
1840 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1841 if (fun->atom)
1842 nbytes += GetAtomTotalSize(cx, fun->atom);
1843 return nbytes;
1846 #include "jsemit.h"
1848 JS_PUBLIC_API(size_t)
1849 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1851 size_t nbytes, pbytes;
1852 jsatomid i;
1853 jssrcnote *sn, *notes;
1854 JSObjectArray *objarray;
1855 JSPrincipals *principals;
1857 nbytes = sizeof *script;
1858 if (script->u.object)
1859 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1861 nbytes += script->length * sizeof script->code[0];
1862 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1863 for (i = 0; i < script->atomMap.length; i++)
1864 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1866 if (script->filename)
1867 nbytes += strlen(script->filename) + 1;
1869 notes = script->notes();
1870 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1871 continue;
1872 nbytes += (sn - notes + 1) * sizeof *sn;
1874 if (JSScript::isValidOffset(script->objectsOffset)) {
1875 objarray = script->objects();
1876 i = objarray->length;
1877 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1878 do {
1879 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1880 } while (i != 0);
1883 if (JSScript::isValidOffset(script->regexpsOffset)) {
1884 objarray = script->regexps();
1885 i = objarray->length;
1886 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1887 do {
1888 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1889 } while (i != 0);
1892 if (JSScript::isValidOffset(script->trynotesOffset)) {
1893 nbytes += sizeof(JSTryNoteArray) +
1894 script->trynotes()->length * sizeof(JSTryNote);
1897 principals = script->principals;
1898 if (principals) {
1899 JS_ASSERT(principals->refcount);
1900 pbytes = sizeof *principals;
1901 if (principals->refcount > 1)
1902 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1903 nbytes += pbytes;
1906 return nbytes;
1909 JS_PUBLIC_API(uint32)
1910 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1912 if (!fp)
1913 fp = js_GetTopStackFrame(cx);
1914 while (fp) {
1915 if (fp->isScriptFrame())
1916 return JS_GetScriptFilenameFlags(fp->script());
1917 fp = fp->prev();
1919 return 0;
1922 JS_PUBLIC_API(uint32)
1923 JS_GetScriptFilenameFlags(JSScript *script)
1925 JS_ASSERT(script);
1926 if (!script->filename)
1927 return JSFILENAME_NULL;
1928 return js_GetScriptFilenameFlags(script->filename);
1931 JS_PUBLIC_API(JSBool)
1932 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1934 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1935 return JS_FALSE;
1936 return JS_TRUE;
1939 JS_PUBLIC_API(JSBool)
1940 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1942 return obj->isSystem();
1945 JS_PUBLIC_API(JSBool)
1946 JS_MakeSystemObject(JSContext *cx, JSObject *obj)
1948 obj->setSystem();
1949 return true;
1952 /************************************************************************/
1954 JS_PUBLIC_API(JSObject *)
1955 JS_UnwrapObject(JSContext *cx, JSObject *obj)
1957 return obj->unwrap();
1960 /************************************************************************/
1962 JS_FRIEND_API(void)
1963 js_RevertVersion(JSContext *cx)
1965 cx->clearVersionOverride();
1968 JS_PUBLIC_API(const JSDebugHooks *)
1969 JS_GetGlobalDebugHooks(JSRuntime *rt)
1971 return &rt->globalDebugHooks;
1974 const JSDebugHooks js_NullDebugHooks = {};
1976 JS_PUBLIC_API(JSDebugHooks *)
1977 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1979 JS_ASSERT(hooks);
1980 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1981 LeaveTrace(cx);
1983 #ifdef JS_TRACER
1984 AutoLockGC lock(cx->runtime);
1985 #endif
1986 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1987 cx->debugHooks = hooks;
1988 #ifdef JS_TRACER
1989 cx->updateJITEnabled();
1990 #endif
1991 return old;
1994 JS_PUBLIC_API(JSDebugHooks *)
1995 JS_ClearContextDebugHooks(JSContext *cx)
1997 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
2000 JS_PUBLIC_API(JSBool)
2001 JS_StartProfiling()
2003 return Probes::startProfiling();
2006 JS_PUBLIC_API(void)
2007 JS_StopProfiling()
2009 Probes::stopProfiling();
2012 #ifdef MOZ_PROFILING
2014 static JSBool
2015 StartProfiling(JSContext *cx, uintN argc, jsval *vp)
2017 JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling()));
2018 return true;
2021 static JSBool
2022 StopProfiling(JSContext *cx, uintN argc, jsval *vp)
2024 JS_StopProfiling();
2025 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2026 return true;
2029 #ifdef MOZ_SHARK
2031 static JSBool
2032 IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp)
2034 JS_SET_RVAL(cx, vp, JSVAL_TRUE);
2035 return true;
2038 #endif
2040 static JSFunctionSpec profiling_functions[] = {
2041 JS_FN("startProfiling", StartProfiling, 0,0),
2042 JS_FN("stopProfiling", StopProfiling, 0,0),
2043 #ifdef MOZ_SHARK
2044 /* Keep users of the old shark API happy. */
2045 JS_FN("connectShark", IgnoreAndReturnTrue, 0,0),
2046 JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
2047 JS_FN("startShark", StartProfiling, 0,0),
2048 JS_FN("stopShark", StopProfiling, 0,0),
2049 #endif
2050 JS_FS_END
2053 #endif
2055 JS_PUBLIC_API(JSBool)
2056 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj)
2058 #ifdef MOZ_PROFILING
2059 return JS_DefineFunctions(cx, obj, profiling_functions);
2060 #else
2061 return true;
2062 #endif
2065 #ifdef MOZ_CALLGRIND
2067 #include <valgrind/callgrind.h>
2069 JS_FRIEND_API(JSBool)
2070 js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp)
2072 CALLGRIND_START_INSTRUMENTATION;
2073 CALLGRIND_ZERO_STATS;
2074 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2075 return JS_TRUE;
2078 JS_FRIEND_API(JSBool)
2079 js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp)
2081 CALLGRIND_STOP_INSTRUMENTATION;
2082 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2083 return JS_TRUE;
2086 JS_FRIEND_API(JSBool)
2087 js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp)
2089 JSString *str;
2091 jsval *argv = JS_ARGV(cx, vp);
2092 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2093 str = JSVAL_TO_STRING(argv[0]);
2094 JSAutoByteString bytes(cx, str);
2095 if (!!bytes) {
2096 CALLGRIND_DUMP_STATS_AT(bytes.ptr());
2097 return JS_TRUE;
2100 CALLGRIND_DUMP_STATS;
2102 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2103 return JS_TRUE;
2106 #endif /* MOZ_CALLGRIND */
2108 #ifdef MOZ_VTUNE
2109 #include <VTuneApi.h>
2111 static const char *vtuneErrorMessages[] = {
2112 "unknown, error #0",
2113 "invalid 'max samples' field",
2114 "invalid 'samples per buffer' field",
2115 "invalid 'sample interval' field",
2116 "invalid path",
2117 "sample file in use",
2118 "invalid 'number of events' field",
2119 "unknown, error #7",
2120 "internal error",
2121 "bad event name",
2122 "VTStopSampling called without calling VTStartSampling",
2123 "no events selected for event-based sampling",
2124 "events selected cannot be run together",
2125 "no sampling parameters",
2126 "sample database already exists",
2127 "sampling already started",
2128 "time-based sampling not supported",
2129 "invalid 'sampling parameters size' field",
2130 "invalid 'event size' field",
2131 "sampling file already bound",
2132 "invalid event path",
2133 "invalid license",
2134 "invalid 'global options' field",
2138 JS_FRIEND_API(JSBool)
2139 js_StartVtune(JSContext *cx, uintN argc, jsval *vp)
2141 VTUNE_EVENT events[] = {
2142 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2143 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2146 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2147 char *default_filename = "mozilla-vtune.tb5";
2148 JSString *str;
2149 U32 status;
2151 VTUNE_SAMPLING_PARAMS params = {
2152 sizeof(VTUNE_SAMPLING_PARAMS),
2153 sizeof(VTUNE_EVENT),
2154 0, 0, /* Reserved fields */
2155 1, /* Initialize in "paused" state */
2156 0, /* Max samples, or 0 for "continuous" */
2157 4096, /* Samples per buffer */
2158 0.1, /* Sampling interval in ms */
2159 1, /* 1 for event-based sampling, 0 for time-based */
2161 n_events,
2162 events,
2163 default_filename,
2166 jsval *argv = JS_ARGV(cx, vp);
2167 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2168 str = JSVAL_TO_STRING(argv[0]);
2169 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2172 status = VTStartSampling(&params);
2174 if (params.tb5Filename != default_filename)
2175 cx->free(params.tb5Filename);
2177 if (status != 0) {
2178 if (status == VTAPI_MULTIPLE_RUNS)
2179 VTStopSampling(0);
2180 if (status < sizeof(vtuneErrorMessages))
2181 JS_ReportError(cx, "Vtune setup error: %s",
2182 vtuneErrorMessages[status]);
2183 else
2184 JS_ReportError(cx, "Vtune setup error: %d",
2185 status);
2186 return false;
2188 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2189 return true;
2192 JS_FRIEND_API(JSBool)
2193 js_StopVtune(JSContext *cx, uintN argc, jsval *vp)
2195 U32 status = VTStopSampling(1);
2196 if (status) {
2197 if (status < sizeof(vtuneErrorMessages))
2198 JS_ReportError(cx, "Vtune shutdown error: %s",
2199 vtuneErrorMessages[status]);
2200 else
2201 JS_ReportError(cx, "Vtune shutdown error: %d",
2202 status);
2203 return false;
2205 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2206 return true;
2209 JS_FRIEND_API(JSBool)
2210 js_PauseVtune(JSContext *cx, uintN argc, jsval *vp)
2212 VTPause();
2213 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2214 return true;
2217 JS_FRIEND_API(JSBool)
2218 js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp)
2220 VTResume();
2221 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2222 return true;
2225 #endif /* MOZ_VTUNE */
2227 #ifdef MOZ_TRACEVIS
2229 * Ethogram - Javascript wrapper for TraceVis state
2231 * ethology: The scientific study of animal behavior,
2232 * especially as it occurs in a natural environment.
2233 * ethogram: A pictorial catalog of the behavioral patterns of
2234 * an organism or a species.
2237 #if defined(XP_WIN)
2238 #include "jswin.h"
2239 #else
2240 #include <sys/time.h>
2241 #endif
2242 #include "jstracer.h"
2244 #define ETHOGRAM_BUF_SIZE 65536
2246 static JSBool
2247 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2248 static void
2249 ethogram_finalize(JSContext *cx, JSObject *obj);
2251 static JSClass ethogram_class = {
2252 "Ethogram",
2253 JSCLASS_HAS_PRIVATE,
2254 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
2255 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2256 JSCLASS_NO_OPTIONAL_MEMBERS
2259 struct EthogramEvent {
2260 TraceVisState s;
2261 TraceVisExitReason r;
2262 int ts;
2263 int tus;
2264 JSString *filename;
2265 int lineno;
2268 static int
2269 compare_strings(const void *k1, const void *k2)
2271 return strcmp((const char *) k1, (const char *) k2) == 0;
2274 class EthogramEventBuffer {
2275 private:
2276 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2277 int mReadPos;
2278 int mWritePos;
2279 JSObject *mFilenames;
2280 int mStartSecond;
2282 struct EthogramScriptEntry {
2283 char *filename;
2284 JSString *jsfilename;
2286 EthogramScriptEntry *next;
2288 EthogramScriptEntry *mScripts;
2290 public:
2291 friend JSBool
2292 ethogram_construct(JSContext *cx, uintN argc, jsval *vp);
2294 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2295 mBuf[mWritePos].s = s;
2296 mBuf[mWritePos].r = r;
2297 #if defined(XP_WIN)
2298 FILETIME now;
2299 GetSystemTimeAsFileTime(&now);
2300 unsigned long long raw_us = 0.1 *
2301 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2302 (unsigned long long) now.dwLowDateTime);
2303 unsigned int sec = raw_us / 1000000L;
2304 unsigned int usec = raw_us % 1000000L;
2305 mBuf[mWritePos].ts = sec - mStartSecond;
2306 mBuf[mWritePos].tus = usec;
2307 #else
2308 struct timeval tv;
2309 gettimeofday(&tv, NULL);
2310 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2311 mBuf[mWritePos].tus = tv.tv_usec;
2312 #endif
2314 JSString *jsfilename = findScript(filename);
2315 mBuf[mWritePos].filename = jsfilename;
2316 mBuf[mWritePos].lineno = lineno;
2318 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2319 if (mWritePos == mReadPos) {
2320 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2324 inline EthogramEvent *pop() {
2325 EthogramEvent *e = &mBuf[mReadPos];
2326 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2327 return e;
2330 bool isEmpty() {
2331 return (mReadPos == mWritePos);
2334 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2335 JSHashNumber hash = JS_HashString(filename);
2336 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2337 if (*hep != NULL)
2338 return NULL;
2340 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2342 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2343 if (entry == NULL)
2344 return NULL;
2346 entry->next = mScripts;
2347 mScripts = entry;
2348 entry->filename = filename;
2349 entry->jsfilename = jsfilename;
2351 return mScripts;
2354 void removeScripts(JSContext *cx) {
2355 EthogramScriptEntry *se = mScripts;
2356 while (se != NULL) {
2357 char *filename = se->filename;
2359 JSHashNumber hash = JS_HashString(filename);
2360 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2361 JSHashEntry *he = *hep;
2362 if (he) {
2363 /* we hardly knew he */
2364 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2367 EthogramScriptEntry *se_head = se;
2368 se = se->next;
2369 JS_free(cx, se_head);
2373 JSString *findScript(char *filename) {
2374 EthogramScriptEntry *se = mScripts;
2375 while (se != NULL) {
2376 if (compare_strings(se->filename, filename))
2377 return (se->jsfilename);
2378 se = se->next;
2380 return NULL;
2383 JSObject *filenames() {
2384 return mFilenames;
2387 int length() {
2388 if (mWritePos < mReadPos)
2389 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2390 else
2391 return mWritePos - mReadPos;
2395 static char jstv_empty[] = "<null>";
2397 inline char *
2398 jstv_Filename(JSStackFrame *fp)
2400 while (fp && !fp->isScriptFrame())
2401 fp = fp->prev();
2402 return (fp && fp->maybeScript() && fp->script()->filename)
2403 ? (char *)fp->script()->filename
2404 : jstv_empty;
2406 inline uintN
2407 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2409 while (fp && fp->pc(cx) == NULL)
2410 fp = fp->prev();
2411 return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
2414 /* Collect states here and distribute to a matching buffer, if any */
2415 JS_FRIEND_API(void)
2416 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2418 JSStackFrame *fp = cx->fp();
2420 char *script_file = jstv_Filename(fp);
2421 JSHashNumber hash = JS_HashString(script_file);
2423 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2424 /* update event buffer, flag if overflowed */
2425 JSHashEntry *he = *hep;
2426 if (he) {
2427 EthogramEventBuffer *p;
2428 p = (EthogramEventBuffer *) he->value;
2430 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2434 static JSBool
2435 ethogram_construct(JSContext *cx, uintN argc, jsval *vp)
2437 EthogramEventBuffer *p;
2439 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2440 if (!p)
2441 return JS_FALSE;
2443 p->mReadPos = p->mWritePos = 0;
2444 p->mScripts = NULL;
2445 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2447 #if defined(XP_WIN)
2448 FILETIME now;
2449 GetSystemTimeAsFileTime(&now);
2450 unsigned long long raw_us = 0.1 *
2451 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2452 (unsigned long long) now.dwLowDateTime);
2453 unsigned int s = raw_us / 1000000L;
2454 p->mStartSecond = s;
2455 #else
2456 struct timeval tv;
2457 gettimeofday(&tv, NULL);
2458 p->mStartSecond = tv.tv_sec;
2459 #endif
2460 JSObject *obj;
2461 if (JS_IsConstructing(cx, vp)) {
2462 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2463 if (!obj)
2464 return JS_FALSE;
2465 } else {
2466 obj = JS_THIS_OBJECT(cx, vp);
2469 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2470 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2471 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2472 return JS_FALSE;
2474 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
2475 JS_SetPrivate(cx, obj, p);
2476 return JS_TRUE;
2479 static void
2480 ethogram_finalize(JSContext *cx, JSObject *obj)
2482 EthogramEventBuffer *p;
2483 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2484 if (!p)
2485 return;
2487 p->removeScripts(cx);
2489 JS_free(cx, p);
2492 static JSBool
2493 ethogram_addScript(JSContext *cx, uintN argc, jsval *vp)
2495 JSString *str;
2496 char *filename = NULL;
2497 jsval *argv = JS_ARGV(cx, vp);
2498 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2499 if (!obj)
2500 return false;
2501 if (argc < 1) {
2502 /* silently ignore no args */
2503 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2504 return true;
2506 if (JSVAL_IS_STRING(argv[0])) {
2507 str = JSVAL_TO_STRING(argv[0]);
2508 filename = js_DeflateString(cx, str->chars(), str->length());
2509 if (!filename)
2510 return false;
2513 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2515 p->addScript(cx, obj, filename, str);
2516 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2517 jsval dummy;
2518 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, &dummy);
2519 return true;
2522 static JSBool
2523 ethogram_getAllEvents(JSContext *cx, uintN argc, jsval *vp)
2525 EthogramEventBuffer *p;
2526 jsval *argv = JS_ARGV(cx, vp);
2528 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2529 if (!obj)
2530 return JS_FALSE;
2532 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2533 if (!p)
2534 return JS_FALSE;
2536 if (p->isEmpty()) {
2537 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2538 return JS_TRUE;
2541 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2542 if (rarray == NULL) {
2543 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2544 return JS_TRUE;
2547 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(rarray));
2549 for (int i = 0; !p->isEmpty(); i++) {
2551 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2552 if (x == NULL)
2553 return JS_FALSE;
2555 EthogramEvent *e = p->pop();
2557 jsval state = INT_TO_JSVAL(e->s);
2558 jsval reason = INT_TO_JSVAL(e->r);
2559 jsval ts = INT_TO_JSVAL(e->ts);
2560 jsval tus = INT_TO_JSVAL(e->tus);
2562 jsval filename = STRING_TO_JSVAL(e->filename);
2563 jsval lineno = INT_TO_JSVAL(e->lineno);
2565 if (!JS_SetProperty(cx, x, "state", &state))
2566 return JS_FALSE;
2567 if (!JS_SetProperty(cx, x, "reason", &reason))
2568 return JS_FALSE;
2569 if (!JS_SetProperty(cx, x, "ts", &ts))
2570 return JS_FALSE;
2571 if (!JS_SetProperty(cx, x, "tus", &tus))
2572 return JS_FALSE;
2574 if (!JS_SetProperty(cx, x, "filename", &filename))
2575 return JS_FALSE;
2576 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2577 return JS_FALSE;
2579 jsval element = OBJECT_TO_JSVAL(x);
2580 JS_SetElement(cx, rarray, i, &element);
2583 return JS_TRUE;
2586 static JSBool
2587 ethogram_getNextEvent(JSContext *cx, uintN argc, jsval *vp)
2589 EthogramEventBuffer *p;
2590 jsval *argv = JS_ARGV(cx, vp);
2592 JSObject *obj = JS_THIS_OBJECT(cx, vp);
2593 if (!obj)
2594 return JS_FALSE;
2596 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2597 if (!p)
2598 return JS_FALSE;
2600 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2601 if (x == NULL)
2602 return JS_FALSE;
2604 if (p->isEmpty()) {
2605 JS_SET_RVAL(cx, vp, JSVAL_NULL);
2606 return JS_TRUE;
2609 EthogramEvent *e = p->pop();
2610 jsval state = INT_TO_JSVAL(e->s);
2611 jsval reason = INT_TO_JSVAL(e->r);
2612 jsval ts = INT_TO_JSVAL(e->ts);
2613 jsval tus = INT_TO_JSVAL(e->tus);
2615 jsval filename = STRING_TO_JSVAL(e->filename);
2616 jsval lineno = INT_TO_JSVAL(e->lineno);
2618 if (!JS_SetProperty(cx, x, "state", &state))
2619 return JS_FALSE;
2620 if (!JS_SetProperty(cx, x, "reason", &reason))
2621 return JS_FALSE;
2622 if (!JS_SetProperty(cx, x, "ts", &ts))
2623 return JS_FALSE;
2624 if (!JS_SetProperty(cx, x, "tus", &tus))
2625 return JS_FALSE;
2626 if (!JS_SetProperty(cx, x, "filename", &filename))
2627 return JS_FALSE;
2629 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2630 return JS_FALSE;
2632 JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(x));
2634 return JS_TRUE;
2637 static JSFunctionSpec ethogram_methods[] = {
2638 JS_FN("addScript", ethogram_addScript, 1,0),
2639 JS_FN("getAllEvents", ethogram_getAllEvents, 0,0),
2640 JS_FN("getNextEvent", ethogram_getNextEvent, 0,0),
2641 JS_FS_END
2645 * An |Ethogram| organizes the output of a collection of files that should be
2646 * monitored together. A single object gets events for the group.
2648 JS_FRIEND_API(JSBool)
2649 js_InitEthogram(JSContext *cx, uintN argc, jsval *vp)
2651 if (!traceVisScriptTable) {
2652 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2653 NULL, NULL, NULL);
2656 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2657 ethogram_construct, 0, NULL, ethogram_methods,
2658 NULL, NULL);
2660 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2661 return true;
2664 JS_FRIEND_API(JSBool)
2665 js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp)
2667 if (traceVisScriptTable)
2668 JS_HashTableDestroy(traceVisScriptTable);
2670 JS_SET_RVAL(cx, vp, JSVAL_VOID);
2671 return true;
2674 #endif /* MOZ_TRACEVIS */