/[jscoverage]/trunk/js/jsdbgapi.cpp
ViewVC logotype

Contents of /trunk/js/jsdbgapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (12 years, 4 months ago) by siliconforks
File size: 70969 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS debugging API.
43 */
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"
65
66 #include "jsatominlines.h"
67
68 #include "jsautooplen.h"
69
70 typedef struct JSTrap {
71 JSCList links;
72 JSScript *script;
73 jsbytecode *pc;
74 JSOp op;
75 JSTrapHandler handler;
76 void *closure;
77 } JSTrap;
78
79 #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
80 #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
81 #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
82
83 /*
84 * NB: FindTrap must be called with rt->debuggerLock acquired.
85 */
86 static JSTrap *
87 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
88 {
89 JSTrap *trap;
90
91 for (trap = (JSTrap *)rt->trapList.next;
92 &trap->links != &rt->trapList;
93 trap = (JSTrap *)trap->links.next) {
94 if (trap->script == script && trap->pc == pc)
95 return trap;
96 }
97 return NULL;
98 }
99
100 jsbytecode *
101 js_UntrapScriptCode(JSContext *cx, JSScript *script)
102 {
103 jsbytecode *code;
104 JSRuntime *rt;
105 JSTrap *trap;
106
107 code = script->code;
108 rt = cx->runtime;
109 DBG_LOCK(rt);
110 for (trap = (JSTrap *)rt->trapList.next;
111 &trap->links !=
112 &rt->trapList;
113 trap = (JSTrap *)trap->links.next) {
114 if (trap->script == script &&
115 (size_t)(trap->pc - script->code) < script->length) {
116 if (code == script->code) {
117 jssrcnote *sn, *notes;
118 size_t nbytes;
119
120 nbytes = script->length * sizeof(jsbytecode);
121 notes = script->notes();
122 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
123 continue;
124 nbytes += (sn - notes + 1) * sizeof *sn;
125
126 code = (jsbytecode *) cx->malloc(nbytes);
127 if (!code)
128 break;
129 memcpy(code, script->code, nbytes);
130 JS_PURGE_GSN_CACHE(cx);
131 }
132 code[trap->pc - script->code] = trap->op;
133 }
134 }
135 DBG_UNLOCK(rt);
136 return code;
137 }
138
139 JS_PUBLIC_API(JSBool)
140 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
141 JSTrapHandler handler, void *closure)
142 {
143 JSTrap *junk, *trap, *twin;
144 JSRuntime *rt;
145 uint32 sample;
146
147 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
148 junk = NULL;
149 rt = cx->runtime;
150 DBG_LOCK(rt);
151 trap = FindTrap(rt, script, pc);
152 if (trap) {
153 JS_ASSERT(trap->script == script && trap->pc == pc);
154 JS_ASSERT(*pc == JSOP_TRAP);
155 } else {
156 sample = rt->debuggerMutations;
157 DBG_UNLOCK(rt);
158 trap = (JSTrap *) cx->malloc(sizeof *trap);
159 if (!trap)
160 return JS_FALSE;
161 trap->closure = NULL;
162 if(!js_AddRoot(cx, &trap->closure, "trap->closure")) {
163 cx->free(trap);
164 return JS_FALSE;
165 }
166 DBG_LOCK(rt);
167 twin = (rt->debuggerMutations != sample)
168 ? FindTrap(rt, script, pc)
169 : NULL;
170 if (twin) {
171 junk = trap;
172 trap = twin;
173 } else {
174 JS_APPEND_LINK(&trap->links, &rt->trapList);
175 ++rt->debuggerMutations;
176 trap->script = script;
177 trap->pc = pc;
178 trap->op = (JSOp)*pc;
179 *pc = JSOP_TRAP;
180 }
181 }
182 trap->handler = handler;
183 trap->closure = closure;
184 DBG_UNLOCK(rt);
185 if (junk) {
186 js_RemoveRoot(rt, &junk->closure);
187 cx->free(junk);
188 }
189 return JS_TRUE;
190 }
191
192 JS_PUBLIC_API(JSOp)
193 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
194 {
195 JSRuntime *rt;
196 JSTrap *trap;
197 JSOp op;
198
199 rt = cx->runtime;
200 DBG_LOCK(rt);
201 trap = FindTrap(rt, script, pc);
202 op = trap ? trap->op : (JSOp) *pc;
203 DBG_UNLOCK(rt);
204 return op;
205 }
206
207 static void
208 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
209 {
210 ++cx->runtime->debuggerMutations;
211 JS_REMOVE_LINK(&trap->links);
212 *trap->pc = (jsbytecode)trap->op;
213 DBG_UNLOCK(cx->runtime);
214
215 js_RemoveRoot(cx->runtime, &trap->closure);
216 cx->free(trap);
217 }
218
219 JS_PUBLIC_API(void)
220 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
221 JSTrapHandler *handlerp, void **closurep)
222 {
223 JSTrap *trap;
224
225 DBG_LOCK(cx->runtime);
226 trap = FindTrap(cx->runtime, script, pc);
227 if (handlerp)
228 *handlerp = trap ? trap->handler : NULL;
229 if (closurep)
230 *closurep = trap ? trap->closure : NULL;
231 if (trap)
232 DestroyTrapAndUnlock(cx, trap);
233 else
234 DBG_UNLOCK(cx->runtime);
235 }
236
237 JS_PUBLIC_API(void)
238 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
239 {
240 JSRuntime *rt;
241 JSTrap *trap, *next;
242 uint32 sample;
243
244 rt = cx->runtime;
245 DBG_LOCK(rt);
246 for (trap = (JSTrap *)rt->trapList.next;
247 &trap->links != &rt->trapList;
248 trap = next) {
249 next = (JSTrap *)trap->links.next;
250 if (trap->script == script) {
251 sample = rt->debuggerMutations;
252 DestroyTrapAndUnlock(cx, trap);
253 DBG_LOCK(rt);
254 if (rt->debuggerMutations != sample + 1)
255 next = (JSTrap *)rt->trapList.next;
256 }
257 }
258 DBG_UNLOCK(rt);
259 }
260
261 JS_PUBLIC_API(void)
262 JS_ClearAllTraps(JSContext *cx)
263 {
264 JSRuntime *rt;
265 JSTrap *trap, *next;
266 uint32 sample;
267
268 rt = cx->runtime;
269 DBG_LOCK(rt);
270 for (trap = (JSTrap *)rt->trapList.next;
271 &trap->links != &rt->trapList;
272 trap = next) {
273 next = (JSTrap *)trap->links.next;
274 sample = rt->debuggerMutations;
275 DestroyTrapAndUnlock(cx, trap);
276 DBG_LOCK(rt);
277 if (rt->debuggerMutations != sample + 1)
278 next = (JSTrap *)rt->trapList.next;
279 }
280 DBG_UNLOCK(rt);
281 }
282
283 JS_PUBLIC_API(JSTrapStatus)
284 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
285 {
286 JSTrap *trap;
287 jsint op;
288 JSTrapStatus status;
289
290 DBG_LOCK(cx->runtime);
291 trap = FindTrap(cx->runtime, script, pc);
292 JS_ASSERT(!trap || trap->handler);
293 if (!trap) {
294 op = (JSOp) *pc;
295 DBG_UNLOCK(cx->runtime);
296
297 /* Defend against "pc for wrong script" API usage error. */
298 JS_ASSERT(op != JSOP_TRAP);
299
300 #ifdef JS_THREADSAFE
301 /* If the API was abused, we must fail for want of the real op. */
302 if (op == JSOP_TRAP)
303 return JSTRAP_ERROR;
304
305 /* Assume a race with a debugger thread and try to carry on. */
306 *rval = INT_TO_JSVAL(op);
307 return JSTRAP_CONTINUE;
308 #else
309 /* Always fail if single-threaded (must be an API usage error). */
310 return JSTRAP_ERROR;
311 #endif
312 }
313 DBG_UNLOCK(cx->runtime);
314
315 /*
316 * It's important that we not use 'trap->' after calling the callback --
317 * the callback might remove the trap!
318 */
319 op = (jsint)trap->op;
320 status = trap->handler(cx, script, pc, rval, trap->closure);
321 if (status == JSTRAP_CONTINUE) {
322 /* By convention, return the true op to the interpreter in rval. */
323 *rval = INT_TO_JSVAL(op);
324 }
325 return status;
326 }
327
328 #ifdef JS_TRACER
329 static void
330 JITInhibitingHookChange(JSRuntime *rt, bool wasInhibited)
331 {
332 if (wasInhibited) {
333 if (!rt->debuggerInhibitsJIT()) {
334 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
335 js_ContextFromLinkField(cl)->updateJITEnabled();
336 }
337 } else if (rt->debuggerInhibitsJIT()) {
338 for (JSCList *cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next)
339 js_ContextFromLinkField(cl)->jitEnabled = false;
340 }
341 }
342
343 static void
344 LeaveTraceRT(JSRuntime *rt)
345 {
346 JSThreadData *data = js_CurrentThreadData(rt);
347 JSContext *cx = data ? data->traceMonitor.tracecx : NULL;
348 JS_UNLOCK_GC(rt);
349
350 if (cx)
351 js_LeaveTrace(cx);
352 }
353 #endif
354
355 JS_PUBLIC_API(JSBool)
356 JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
357 {
358 #ifdef JS_TRACER
359 JS_LOCK_GC(rt);
360 bool wasInhibited = rt->debuggerInhibitsJIT();
361 #endif
362 rt->globalDebugHooks.interruptHandler = handler;
363 rt->globalDebugHooks.interruptHandlerData = closure;
364 #ifdef JS_TRACER
365 JITInhibitingHookChange(rt, wasInhibited);
366 JS_UNLOCK_GC(rt);
367 LeaveTraceRT(rt);
368 #endif
369 return JS_TRUE;
370 }
371
372 JS_PUBLIC_API(JSBool)
373 JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
374 {
375 #ifdef JS_TRACER
376 JS_LOCK_GC(rt);
377 bool wasInhibited = rt->debuggerInhibitsJIT();
378 #endif
379 if (handlerp)
380 *handlerp = rt->globalDebugHooks.interruptHandler;
381 if (closurep)
382 *closurep = rt->globalDebugHooks.interruptHandlerData;
383 rt->globalDebugHooks.interruptHandler = 0;
384 rt->globalDebugHooks.interruptHandlerData = 0;
385 #ifdef JS_TRACER
386 JITInhibitingHookChange(rt, wasInhibited);
387 JS_UNLOCK_GC(rt);
388 #endif
389 return JS_TRUE;
390 }
391
392 /************************************************************************/
393
394 typedef struct JSWatchPoint {
395 JSCList links;
396 JSObject *object; /* weak link, see js_FinalizeObject */
397 JSScopeProperty *sprop;
398 JSPropertyOp setter;
399 JSWatchPointHandler handler;
400 JSObject *closure;
401 uintN flags;
402 } JSWatchPoint;
403
404 #define JSWP_LIVE 0x1 /* live because set and not cleared */
405 #define JSWP_HELD 0x2 /* held while running handler/setter */
406
407 /*
408 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
409 */
410 static JSBool
411 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
412 {
413 JSBool ok, found;
414 JSScopeProperty *sprop;
415 JSScope *scope;
416 JSPropertyOp setter;
417
418 ok = JS_TRUE;
419 wp->flags &= ~flag;
420 if (wp->flags != 0) {
421 DBG_UNLOCK(cx->runtime);
422 return ok;
423 }
424
425 /*
426 * Remove wp from the list, then if there are no other watchpoints for
427 * wp->sprop in any scope, restore wp->sprop->setter from wp.
428 */
429 ++cx->runtime->debuggerMutations;
430 JS_REMOVE_LINK(&wp->links);
431 sprop = wp->sprop;
432
433 /*
434 * Passing null for the scope parameter tells js_GetWatchedSetter to find
435 * any watch point for sprop, and not to lock or unlock rt->debuggerLock.
436 * If js_ChangeNativePropertyAttrs fails, propagate failure after removing
437 * wp->closure's root and freeing wp.
438 */
439 setter = js_GetWatchedSetter(cx->runtime, NULL, sprop);
440 DBG_UNLOCK(cx->runtime);
441 if (!setter) {
442 JS_LOCK_OBJ(cx, wp->object);
443 scope = OBJ_SCOPE(wp->object);
444 found = (scope->lookup(sprop->id) != NULL);
445 JS_UNLOCK_SCOPE(cx, scope);
446
447 /*
448 * If the property wasn't found on wp->object or didn't exist, then
449 * someone else has dealt with this sprop, and we don't need to change
450 * the property attributes.
451 */
452 if (found) {
453 sprop = scope->change(cx, sprop, 0, sprop->attrs,
454 sprop->getter, wp->setter);
455 if (!sprop)
456 ok = JS_FALSE;
457 }
458 }
459
460 cx->free(wp);
461 return ok;
462 }
463
464 /*
465 * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
466 * the debugger should never be racing with the GC (i.e., the debugger must
467 * respect the request model).
468 */
469 void
470 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
471 {
472 JSRuntime *rt;
473 JSWatchPoint *wp;
474
475 rt = trc->context->runtime;
476
477 for (wp = (JSWatchPoint *)rt->watchPointList.next;
478 &wp->links != &rt->watchPointList;
479 wp = (JSWatchPoint *)wp->links.next) {
480 if (wp->object == obj) {
481 wp->sprop->trace(trc);
482 if ((wp->sprop->attrs & JSPROP_SETTER) && wp->setter) {
483 JS_CALL_OBJECT_TRACER(trc, js_CastAsObject(wp->setter),
484 "wp->setter");
485 }
486 JS_SET_TRACING_NAME(trc, "wp->closure");
487 js_CallValueTracerIfGCThing(trc, OBJECT_TO_JSVAL(wp->closure));
488 }
489 }
490 }
491
492 void
493 js_SweepWatchPoints(JSContext *cx)
494 {
495 JSRuntime *rt;
496 JSWatchPoint *wp, *next;
497 uint32 sample;
498
499 rt = cx->runtime;
500 DBG_LOCK(rt);
501 for (wp = (JSWatchPoint *)rt->watchPointList.next;
502 &wp->links != &rt->watchPointList;
503 wp = next) {
504 next = (JSWatchPoint *)wp->links.next;
505 if (js_IsAboutToBeFinalized(cx, wp->object)) {
506 sample = rt->debuggerMutations;
507
508 /* Ignore failures. */
509 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
510 DBG_LOCK(rt);
511 if (rt->debuggerMutations != sample + 1)
512 next = (JSWatchPoint *)rt->watchPointList.next;
513 }
514 }
515 DBG_UNLOCK(rt);
516 }
517
518
519
520 /*
521 * NB: FindWatchPoint must be called with rt->debuggerLock acquired.
522 */
523 static JSWatchPoint *
524 FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
525 {
526 JSWatchPoint *wp;
527
528 for (wp = (JSWatchPoint *)rt->watchPointList.next;
529 &wp->links != &rt->watchPointList;
530 wp = (JSWatchPoint *)wp->links.next) {
531 if (OBJ_SCOPE(wp->object) == scope && wp->sprop->id == id)
532 return wp;
533 }
534 return NULL;
535 }
536
537 JSScopeProperty *
538 js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
539 {
540 JSWatchPoint *wp;
541 JSScopeProperty *sprop;
542
543 DBG_LOCK(rt);
544 wp = FindWatchPoint(rt, scope, id);
545 sprop = wp ? wp->sprop : NULL;
546 DBG_UNLOCK(rt);
547 return sprop;
548 }
549
550 /*
551 * Secret handshake with DropWatchPointAndUnlock: if (!scope), we know our
552 * caller has acquired rt->debuggerLock, so we don't have to.
553 */
554 JSPropertyOp
555 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
556 const JSScopeProperty *sprop)
557 {
558 JSPropertyOp setter;
559 JSWatchPoint *wp;
560
561 setter = NULL;
562 if (scope)
563 DBG_LOCK(rt);
564 for (wp = (JSWatchPoint *)rt->watchPointList.next;
565 &wp->links != &rt->watchPointList;
566 wp = (JSWatchPoint *)wp->links.next) {
567 if ((!scope || OBJ_SCOPE(wp->object) == scope) && wp->sprop == sprop) {
568 setter = wp->setter;
569 break;
570 }
571 }
572 if (scope)
573 DBG_UNLOCK(rt);
574 return setter;
575 }
576
577 JSBool
578 js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
579 {
580 JSRuntime *rt;
581 JSWatchPoint *wp;
582 JSScopeProperty *sprop;
583 jsval propid, userid;
584 JSScope *scope;
585 JSBool ok;
586
587 rt = cx->runtime;
588 DBG_LOCK(rt);
589 for (wp = (JSWatchPoint *)rt->watchPointList.next;
590 &wp->links != &rt->watchPointList;
591 wp = (JSWatchPoint *)wp->links.next) {
592 sprop = wp->sprop;
593 if (wp->object == obj && SPROP_USERID(sprop) == id &&
594 !(wp->flags & JSWP_HELD)) {
595 wp->flags |= JSWP_HELD;
596 DBG_UNLOCK(rt);
597
598 JS_LOCK_OBJ(cx, obj);
599 propid = ID_TO_VALUE(sprop->id);
600 userid = (sprop->flags & SPROP_HAS_SHORTID)
601 ? INT_TO_JSVAL(sprop->shortid)
602 : propid;
603 scope = OBJ_SCOPE(obj);
604 JS_UNLOCK_OBJ(cx, obj);
605
606 /* NB: wp is held, so we can safely dereference it still. */
607 ok = wp->handler(cx, obj, propid,
608 SPROP_HAS_VALID_SLOT(sprop, scope)
609 ? OBJ_GET_SLOT(cx, obj, sprop->slot)
610 : JSVAL_VOID,
611 vp, wp->closure);
612 if (ok) {
613 /*
614 * Create a pseudo-frame for the setter invocation so that any
615 * stack-walking security code under the setter will correctly
616 * identify the guilty party. So that the watcher appears to
617 * be active to obj_eval and other such code, point frame.pc
618 * at the JSOP_STOP at the end of the script.
619 *
620 * The pseudo-frame is not created for fast natives as they
621 * are treated as interpreter frame extensions and always
622 * trusted.
623 */
624 JSObject *closure;
625 JSClass *clasp;
626 JSFunction *fun;
627 JSScript *script;
628 JSBool injectFrame;
629 uintN nslots, slotsStart;
630 jsval smallv[5];
631 jsval *argv;
632 JSStackFrame frame;
633 JSFrameRegs regs;
634
635 closure = wp->closure;
636 clasp = OBJ_GET_CLASS(cx, closure);
637 if (clasp == &js_FunctionClass) {
638 fun = GET_FUNCTION_PRIVATE(cx, closure);
639 script = FUN_SCRIPT(fun);
640 } else if (clasp == &js_ScriptClass) {
641 fun = NULL;
642 script = (JSScript *) closure->getPrivate();
643 } else {
644 fun = NULL;
645 script = NULL;
646 }
647
648 slotsStart = nslots = 2;
649 injectFrame = JS_TRUE;
650 if (fun) {
651 nslots += FUN_MINARGS(fun);
652 if (!FUN_INTERPRETED(fun)) {
653 nslots += fun->u.n.extra;
654 injectFrame = !(fun->flags & JSFUN_FAST_NATIVE);
655 }
656
657 slotsStart = nslots;
658 }
659 if (script)
660 nslots += script->nslots;
661
662 if (injectFrame) {
663 if (nslots <= JS_ARRAY_LENGTH(smallv)) {
664 argv = smallv;
665 } else {
666 argv = (jsval *) cx->malloc(nslots * sizeof(jsval));
667 if (!argv) {
668 DBG_LOCK(rt);
669 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
670 return JS_FALSE;
671 }
672 }
673
674 argv[0] = OBJECT_TO_JSVAL(closure);
675 argv[1] = JSVAL_NULL;
676 memset(argv + 2, 0, (nslots - 2) * sizeof(jsval));
677
678 memset(&frame, 0, sizeof(frame));
679 frame.script = script;
680 frame.regs = NULL;
681 frame.fun = fun;
682 frame.argv = argv + 2;
683 frame.down = js_GetTopStackFrame(cx);
684 frame.scopeChain = OBJ_GET_PARENT(cx, closure);
685 if (script && script->nslots)
686 frame.slots = argv + slotsStart;
687 if (script) {
688 JS_ASSERT(script->length >= JSOP_STOP_LENGTH);
689 regs.pc = script->code + script->length
690 - JSOP_STOP_LENGTH;
691 regs.sp = NULL;
692 frame.regs = &regs;
693 if (fun &&
694 JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
695 !js_GetCallObject(cx, &frame)) {
696 if (argv != smallv)
697 cx->free(argv);
698 DBG_LOCK(rt);
699 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
700 return JS_FALSE;
701 }
702 }
703
704 cx->fp = &frame;
705 }
706 #ifdef __GNUC__
707 else
708 argv = NULL; /* suppress bogus gcc warnings */
709 #endif
710 ok = !wp->setter ||
711 ((sprop->attrs & JSPROP_SETTER)
712 ? js_InternalCall(cx, obj,
713 js_CastAsObjectJSVal(wp->setter),
714 1, vp, vp)
715 : wp->setter(cx, obj, userid, vp));
716 if (injectFrame) {
717 /* Evil code can cause us to have an arguments object. */
718 frame.putActivationObjects(cx);
719 cx->fp = frame.down;
720 if (argv != smallv)
721 cx->free(argv);
722 }
723 }
724 DBG_LOCK(rt);
725 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
726 }
727 }
728 DBG_UNLOCK(rt);
729 return JS_TRUE;
730 }
731
732 JSBool
733 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
734 jsval *rval)
735 {
736 JSObject *funobj;
737 JSFunction *wrapper;
738 jsval userid;
739
740 funobj = JSVAL_TO_OBJECT(argv[-2]);
741 wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
742 userid = ATOM_KEY(wrapper->atom);
743 *rval = argv[0];
744 return js_watch_set(cx, obj, userid, rval);
745 }
746
747 JSPropertyOp
748 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
749 {
750 JSAtom *atom;
751 JSFunction *wrapper;
752
753 if (!(attrs & JSPROP_SETTER))
754 return &js_watch_set; /* & to silence schoolmarmish MSVC */
755
756 if (JSID_IS_ATOM(id)) {
757 atom = JSID_TO_ATOM(id);
758 } else if (JSID_IS_INT(id)) {
759 if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
760 return NULL;
761 atom = JSID_TO_ATOM(id);
762 } else {
763 atom = NULL;
764 }
765 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
766 OBJ_GET_PARENT(cx, js_CastAsObject(setter)),
767 atom);
768 if (!wrapper)
769 return NULL;
770 return js_CastAsPropertyOp(FUN_OBJECT(wrapper));
771 }
772
773 JS_PUBLIC_API(JSBool)
774 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
775 JSWatchPointHandler handler, void *closure)
776 {
777 JSObject *origobj;
778 jsval v;
779 uintN attrs;
780 jsid propid;
781 JSObject *pobj;
782 JSProperty *prop;
783 JSScopeProperty *sprop;
784 JSRuntime *rt;
785 JSBool ok;
786 JSWatchPoint *wp;
787 JSPropertyOp watcher;
788
789 origobj = obj;
790 obj = js_GetWrappedObject(cx, obj);
791 OBJ_TO_INNER_OBJECT(cx, obj);
792 if (!obj)
793 return JS_FALSE;
794
795 if (JSVAL_IS_INT(idval)) {
796 propid = INT_JSVAL_TO_JSID(idval);
797 } else {
798 if (!js_ValueToStringId(cx, idval, &propid))
799 return JS_FALSE;
800 propid = js_CheckForStringIndex(propid);
801 }
802
803 /*
804 * If, by unwrapping and innerizing, we changed the object, check
805 * again to make sure that we're allowed to set a watch point.
806 */
807 if (origobj != obj && !obj->checkAccess(cx, propid, JSACC_WATCH, &v, &attrs))
808 return JS_FALSE;
809
810 if (!OBJ_IS_NATIVE(obj)) {
811 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
812 OBJ_GET_CLASS(cx, obj)->name);
813 return JS_FALSE;
814 }
815
816 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
817 return JS_FALSE;
818 sprop = (JSScopeProperty *) prop;
819 rt = cx->runtime;
820 if (!sprop) {
821 /* Check for a deleted symbol watchpoint, which holds its property. */
822 sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
823 if (!sprop) {
824 /* Make a new property in obj so we can watch for the first set. */
825 if (!js_DefineNativeProperty(cx, obj, propid, JSVAL_VOID, NULL, NULL,
826 JSPROP_ENUMERATE, 0, 0, &prop)) {
827 return JS_FALSE;
828 }
829 sprop = (JSScopeProperty *) prop;
830 }
831 } else if (pobj != obj) {
832 /* Clone the prototype property so we can watch the right object. */
833 jsval value;
834 JSPropertyOp getter, setter;
835 uintN attrs, flags;
836 intN shortid;
837
838 if (OBJ_IS_NATIVE(pobj)) {
839 value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
840 ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
841 : JSVAL_VOID;
842 getter = sprop->getter;
843 setter = sprop->setter;
844 attrs = sprop->attrs;
845 flags = sprop->flags;
846 shortid = sprop->shortid;
847 } else {
848 if (!pobj->getProperty(cx, propid, &value) ||
849 !pobj->getAttributes(cx, propid, prop, &attrs)) {
850 pobj->dropProperty(cx, prop);
851 return JS_FALSE;
852 }
853 getter = setter = NULL;
854 flags = 0;
855 shortid = 0;
856 }
857 pobj->dropProperty(cx, prop);
858
859 /* Recall that obj is native, whether or not pobj is native. */
860 if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter,
861 attrs, flags, shortid, &prop)) {
862 return JS_FALSE;
863 }
864 sprop = (JSScopeProperty *) prop;
865 }
866
867 /*
868 * At this point, prop/sprop exists in obj, obj is locked, and we must
869 * obj->dropProperty(cx, prop) before returning.
870 */
871 ok = JS_TRUE;
872 DBG_LOCK(rt);
873 wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
874 if (!wp) {
875 DBG_UNLOCK(rt);
876 watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
877 if (!watcher) {
878 ok = JS_FALSE;
879 goto out;
880 }
881
882 wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
883 if (!wp) {
884 ok = JS_FALSE;
885 goto out;
886 }
887 wp->handler = NULL;
888 wp->closure = NULL;
889 wp->object = obj;
890 JS_ASSERT(sprop->setter != js_watch_set || pobj != obj);
891 wp->setter = sprop->setter;
892 wp->flags = JSWP_LIVE;
893
894 /* XXXbe nest in obj lock here */
895 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
896 sprop->getter, watcher);
897 if (!sprop) {
898 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
899 JS_INIT_CLIST(&wp->links);
900 DBG_LOCK(rt);
901 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
902 ok = JS_FALSE;
903 goto out;
904 }
905 wp->sprop = sprop;
906
907 /*
908 * Now that wp is fully initialized, append it to rt's wp list.
909 * Because obj is locked we know that no other thread could have added
910 * a watchpoint for (obj, propid).
911 */
912 DBG_LOCK(rt);
913 JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid));
914 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
915 ++rt->debuggerMutations;
916 }
917 wp->handler = handler;
918 wp->closure = reinterpret_cast<JSObject*>(closure);
919 DBG_UNLOCK(rt);
920
921 out:
922 obj->dropProperty(cx, prop);
923 return ok;
924 }
925
926 JS_PUBLIC_API(JSBool)
927 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
928 JSWatchPointHandler *handlerp, void **closurep)
929 {
930 JSRuntime *rt;
931 JSWatchPoint *wp;
932
933 rt = cx->runtime;
934 DBG_LOCK(rt);
935 for (wp = (JSWatchPoint *)rt->watchPointList.next;
936 &wp->links != &rt->watchPointList;
937 wp = (JSWatchPoint *)wp->links.next) {
938 if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
939 if (handlerp)
940 *handlerp = wp->handler;
941 if (closurep)
942 *closurep = wp->closure;
943 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
944 }
945 }
946 DBG_UNLOCK(rt);
947 if (handlerp)
948 *handlerp = NULL;
949 if (closurep)
950 *closurep = NULL;
951 return JS_TRUE;
952 }
953
954 JS_PUBLIC_API(JSBool)
955 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
956 {
957 JSRuntime *rt;
958 JSWatchPoint *wp, *next;
959 uint32 sample;
960
961 rt = cx->runtime;
962 DBG_LOCK(rt);
963 for (wp = (JSWatchPoint *)rt->watchPointList.next;
964 &wp->links != &rt->watchPointList;
965 wp = next) {
966 next = (JSWatchPoint *)wp->links.next;
967 if (wp->object == obj) {
968 sample = rt->debuggerMutations;
969 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
970 return JS_FALSE;
971 DBG_LOCK(rt);
972 if (rt->debuggerMutations != sample + 1)
973 next = (JSWatchPoint *)rt->watchPointList.next;
974 }
975 }
976 DBG_UNLOCK(rt);
977 return JS_TRUE;
978 }
979
980 JS_PUBLIC_API(JSBool)
981 JS_ClearAllWatchPoints(JSContext *cx)
982 {
983 JSRuntime *rt;
984 JSWatchPoint *wp, *next;
985 uint32 sample;
986
987 rt = cx->runtime;
988 DBG_LOCK(rt);
989 for (wp = (JSWatchPoint *)rt->watchPointList.next;
990 &wp->links != &rt->watchPointList;
991 wp = next) {
992 next = (JSWatchPoint *)wp->links.next;
993 sample = rt->debuggerMutations;
994 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
995 return JS_FALSE;
996 DBG_LOCK(rt);
997 if (rt->debuggerMutations != sample + 1)
998 next = (JSWatchPoint *)rt->watchPointList.next;
999 }
1000 DBG_UNLOCK(rt);
1001 return JS_TRUE;
1002 }
1003
1004 /************************************************************************/
1005
1006 JS_PUBLIC_API(uintN)
1007 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1008 {
1009 return js_PCToLineNumber(cx, script, pc);
1010 }
1011
1012 JS_PUBLIC_API(jsbytecode *)
1013 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
1014 {
1015 return js_LineNumberToPC(script, lineno);
1016 }
1017
1018 JS_PUBLIC_API(JSScript *)
1019 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
1020 {
1021 return FUN_SCRIPT(fun);
1022 }
1023
1024 JS_PUBLIC_API(JSNative)
1025 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
1026 {
1027 return FUN_NATIVE(fun);
1028 }
1029
1030 JS_PUBLIC_API(JSFastNative)
1031 JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun)
1032 {
1033 return FUN_FAST_NATIVE(fun);
1034 }
1035
1036 JS_PUBLIC_API(JSPrincipals *)
1037 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
1038 {
1039 return script->principals;
1040 }
1041
1042 /************************************************************************/
1043
1044 /*
1045 * Stack Frame Iterator
1046 */
1047 JS_PUBLIC_API(JSStackFrame *)
1048 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
1049 {
1050 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down;
1051 return *iteratorp;
1052 }
1053
1054 JS_PUBLIC_API(JSScript *)
1055 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1056 {
1057 return fp->script;
1058 }
1059
1060 JS_PUBLIC_API(jsbytecode *)
1061 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1062 {
1063 return fp->regs ? fp->regs->pc : NULL;
1064 }
1065
1066 JS_PUBLIC_API(JSStackFrame *)
1067 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1068 {
1069 return js_GetScriptedCaller(cx, fp);
1070 }
1071
1072 JS_PUBLIC_API(JSPrincipals *)
1073 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1074 {
1075 JSSecurityCallbacks *callbacks;
1076
1077 if (fp->fun) {
1078 callbacks = JS_GetSecurityCallbacks(cx);
1079 if (callbacks && callbacks->findObjectPrincipals) {
1080 if (FUN_OBJECT(fp->fun) != fp->callee())
1081 return callbacks->findObjectPrincipals(cx, fp->callee());
1082 /* FALL THROUGH */
1083 }
1084 }
1085 if (fp->script)
1086 return fp->script->principals;
1087 return NULL;
1088 }
1089
1090 JS_PUBLIC_API(JSPrincipals *)
1091 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
1092 {
1093 JSPrincipals *principals, *callerPrincipals;
1094 JSSecurityCallbacks *callbacks;
1095
1096 callbacks = JS_GetSecurityCallbacks(cx);
1097 if (callbacks && callbacks->findObjectPrincipals) {
1098 principals = callbacks->findObjectPrincipals(cx, fp->callee());
1099 } else {
1100 principals = NULL;
1101 }
1102 if (!caller)
1103 return principals;
1104 callerPrincipals = JS_StackFramePrincipals(cx, caller);
1105 return (callerPrincipals && principals &&
1106 callerPrincipals->subsume(callerPrincipals, principals))
1107 ? principals
1108 : callerPrincipals;
1109 }
1110
1111 JS_PUBLIC_API(void *)
1112 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1113 {
1114 if (fp->annotation && fp->script) {
1115 JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
1116
1117 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1118 /*
1119 * Give out an annotation only if privileges have not been revoked
1120 * or disabled globally.
1121 */
1122 return fp->annotation;
1123 }
1124 }
1125
1126 return NULL;
1127 }
1128
1129 JS_PUBLIC_API(void)
1130 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1131 {
1132 fp->annotation = annotation;
1133 }
1134
1135 JS_PUBLIC_API(void *)
1136 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1137 {
1138 JSPrincipals *principals;
1139
1140 principals = JS_StackFramePrincipals(cx, fp);
1141 if (!principals)
1142 return NULL;
1143 return principals->getPrincipalArray(cx, principals);
1144 }
1145
1146 JS_PUBLIC_API(JSBool)
1147 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
1148 {
1149 return !fp->script;
1150 }
1151
1152 /* this is deprecated, use JS_GetFrameScopeChain instead */
1153 JS_PUBLIC_API(JSObject *)
1154 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1155 {
1156 return fp->scopeChain;
1157 }
1158
1159 JS_PUBLIC_API(JSObject *)
1160 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1161 {
1162 /* Force creation of argument and call objects if not yet created */
1163 (void) JS_GetFrameCallObject(cx, fp);
1164 return js_GetScopeChain(cx, fp);
1165 }
1166
1167 JS_PUBLIC_API(JSObject *)
1168 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1169 {
1170 if (! fp->fun)
1171 return NULL;
1172
1173 /* Force creation of argument object if not yet created */
1174 (void) js_GetArgsObject(cx, fp);
1175
1176 /*
1177 * XXX ill-defined: null return here means error was reported, unlike a
1178 * null returned above or in the #else
1179 */
1180 return js_GetCallObject(cx, fp);
1181 }
1182
1183 JS_PUBLIC_API(JSObject *)
1184 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
1185 {
1186 JSStackFrame *afp;
1187
1188 if (fp->flags & JSFRAME_COMPUTED_THIS)
1189 return fp->thisp;
1190
1191 /* js_ComputeThis gets confused if fp != cx->fp, so set it aside. */
1192 if (js_GetTopStackFrame(cx) != fp) {
1193 afp = cx->fp;
1194 if (afp) {
1195 afp->dormantNext = cx->dormantFrameChain;
1196 cx->dormantFrameChain = afp;
1197 cx->fp = fp;
1198 }
1199 } else {
1200 afp = NULL;
1201 }
1202
1203 if (fp->argv)
1204 fp->thisp = js_ComputeThis(cx, JS_TRUE, fp->argv);
1205
1206 if (afp) {
1207 cx->fp = afp;
1208 cx->dormantFrameChain = afp->dormantNext;
1209 afp->dormantNext = NULL;
1210 }
1211
1212 return fp->thisp;
1213 }
1214
1215 JS_PUBLIC_API(JSFunction *)
1216 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1217 {
1218 return fp->fun;
1219 }
1220
1221 JS_PUBLIC_API(JSObject *)
1222 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1223 {
1224 if (!fp->fun)
1225 return NULL;
1226
1227 JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee()));
1228 JS_ASSERT(fp->callee()->getPrivate() == fp->fun);
1229 return fp->callee();
1230 }
1231
1232 JS_PUBLIC_API(JSBool)
1233 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1234 {
1235 return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
1236 }
1237
1238 JS_PUBLIC_API(JSObject *)
1239 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1240 {
1241 return fp->callee();
1242 }
1243
1244 JS_PUBLIC_API(JSBool)
1245 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1246 {
1247 return (fp->flags & JSFRAME_DEBUGGER) != 0;
1248 }
1249
1250 JS_PUBLIC_API(jsval)
1251 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1252 {
1253 return fp->rval;
1254 }
1255
1256 JS_PUBLIC_API(void)
1257 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1258 {
1259 fp->rval = rval;
1260 }
1261
1262 /************************************************************************/
1263
1264 JS_PUBLIC_API(const char *)
1265 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1266 {
1267 return script->filename;
1268 }
1269
1270 JS_PUBLIC_API(uintN)
1271 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1272 {
1273 return script->lineno;
1274 }
1275
1276 JS_PUBLIC_API(uintN)
1277 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1278 {
1279 return js_GetScriptLineExtent(script);
1280 }
1281
1282 JS_PUBLIC_API(JSVersion)
1283 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1284 {
1285 return (JSVersion) (script->version & JSVERSION_MASK);
1286 }
1287
1288 /***************************************************************************/
1289
1290 JS_PUBLIC_API(void)
1291 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1292 {
1293 rt->globalDebugHooks.newScriptHook = hook;
1294 rt->globalDebugHooks.newScriptHookData = callerdata;
1295 }
1296
1297 JS_PUBLIC_API(void)
1298 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1299 void *callerdata)
1300 {
1301 rt->globalDebugHooks.destroyScriptHook = hook;
1302 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1303 }
1304
1305 /***************************************************************************/
1306
1307 JS_PUBLIC_API(JSBool)
1308 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1309 const jschar *chars, uintN length,
1310 const char *filename, uintN lineno,
1311 jsval *rval)
1312 {
1313 JS_ASSERT_NOT_ON_TRACE(cx);
1314
1315 JSObject *scobj;
1316 JSScript *script;
1317 JSBool ok;
1318
1319 scobj = JS_GetFrameScopeChain(cx, fp);
1320 if (!scobj)
1321 return JS_FALSE;
1322
1323 /*
1324 * NB: This function breaks the assumption that the compiler can see all
1325 * calls and properly compute a static level. In order to get around this,
1326 * we use a static level that will cause us not to attempt to optimize
1327 * variable references made by this frame.
1328 */
1329 script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
1330 TCF_COMPILE_N_GO |
1331 TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE),
1332 chars, length, NULL,
1333 filename, lineno);
1334
1335 if (!script)
1336 return JS_FALSE;
1337
1338 JSStackFrame *displayCopy[JS_DISPLAY_SIZE];
1339 if (cx->fp != fp) {
1340 memcpy(displayCopy, cx->display, sizeof displayCopy);
1341
1342 /*
1343 * Set up cx->display as it would have been when fp was active.
1344 *
1345 * NB: To reconstruct cx->display for fp, we have to follow the frame
1346 * chain from oldest to youngest, in the opposite direction to its
1347 * single linkge. To avoid the obvious recursive reversal algorithm,
1348 * which might use too much stack, we reverse in place and reverse
1349 * again as we reconstruct the display. This is safe because cx is
1350 * thread-local and we can't cause GC until the call to js_Execute
1351 * below.
1352 */
1353 JSStackFrame *fp2 = fp, *last = NULL;
1354 while (fp2) {
1355 JSStackFrame *next = fp2->down;
1356 fp2->down = last;
1357 last = fp2;
1358 fp2 = next;
1359 }
1360
1361 fp2 = last;
1362 last = NULL;
1363 while (fp2) {
1364 JSStackFrame *next = fp2->down;
1365 fp2->down = last;
1366 last = fp2;
1367
1368 JSScript *script = fp2->script;
1369 if (script && script->staticLevel < JS_DISPLAY_SIZE)
1370 cx->display[script->staticLevel] = fp2;
1371 fp2 = next;
1372 }
1373 }
1374
1375 ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
1376 rval);
1377
1378 if (cx->fp != fp)
1379 memcpy(cx->display, displayCopy, sizeof cx->display);
1380 js_DestroyScript(cx, script);
1381 return ok;
1382 }
1383
1384 JS_PUBLIC_API(JSBool)
1385 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1386 const char *bytes, uintN length,
1387 const char *filename, uintN lineno,
1388 jsval *rval)
1389 {
1390 jschar *chars;
1391 JSBool ok;
1392 size_t len = length;
1393
1394 chars = js_InflateString(cx, bytes, &len);
1395 if (!chars)
1396 return JS_FALSE;
1397 length = (uintN) len;
1398 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1399 rval);
1400 cx->free(chars);
1401
1402 return ok;
1403 }
1404
1405 /************************************************************************/
1406
1407 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
1408
1409 JS_PUBLIC_API(JSScopeProperty *)
1410 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1411 {
1412 JSScopeProperty *sprop;
1413 JSScope *scope;
1414
1415 sprop = *iteratorp;
1416 scope = OBJ_SCOPE(obj);
1417
1418 /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
1419 if (!sprop) {
1420 sprop = SCOPE_LAST_PROP(scope);
1421 } else {
1422 while ((sprop = sprop->parent) != NULL) {
1423 if (!scope->hadMiddleDelete())
1424 break;
1425 if (scope->has(sprop))
1426 break;
1427 }
1428 }
1429 *iteratorp = sprop;
1430 return sprop;
1431 }
1432
1433 JS_PUBLIC_API(JSBool)
1434 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1435 JSPropertyDesc *pd)
1436 {
1437 JSScope *scope;
1438 JSScopeProperty *aprop;
1439 jsval lastException;
1440 JSBool wasThrowing;
1441
1442 pd->id = ID_TO_VALUE(sprop->id);
1443
1444 wasThrowing = cx->throwing;
1445 if (wasThrowing) {
1446 lastException = cx->exception;
1447 if (JSVAL_IS_GCTHING(lastException) &&
1448 !js_AddRoot(cx, &lastException, "lastException")) {
1449 return JS_FALSE;
1450 }
1451 cx->throwing = JS_FALSE;
1452 }
1453
1454 if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) {
1455 if (!cx->throwing) {
1456 pd->flags = JSPD_ERROR;
1457 pd->value = JSVAL_VOID;
1458 } else {
1459 pd->flags = JSPD_EXCEPTION;
1460 pd->value = cx->exception;
1461 }
1462 } else {
1463 pd->flags = 0;
1464 }
1465
1466 cx->throwing = wasThrowing;
1467 if (wasThrowing) {
1468 cx->exception = lastException;
1469 if (JSVAL_IS_GCTHING(lastException))
1470 js_RemoveRoot(cx->runtime, &lastException);
1471 }
1472
1473 pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
1474 | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0)
1475 | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0);
1476 pd->spare = 0;
1477 if (sprop->getter == js_GetCallArg) {
1478 pd->slot = sprop->shortid;
1479 pd->flags |= JSPD_ARGUMENT;
1480 } else if (sprop->getter == js_GetCallVar) {
1481 pd->slot = sprop->shortid;
1482 pd->flags |= JSPD_VARIABLE;
1483 } else {
1484 pd->slot = 0;
1485 }
1486 pd->alias = JSVAL_VOID;
1487 scope = OBJ_SCOPE(obj);
1488 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
1489 for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
1490 if (aprop != sprop && aprop->slot == sprop->slot) {
1491 pd->alias = ID_TO_VALUE(aprop->id);
1492 break;
1493 }
1494 }
1495 }
1496 return JS_TRUE;
1497 }
1498
1499 JS_PUBLIC_API(JSBool)
1500 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1501 {
1502 JSClass *clasp;
1503 JSScope *scope;
1504 uint32 i, n;
1505 JSPropertyDesc *pd;
1506 JSScopeProperty *sprop;
1507
1508 clasp = OBJ_GET_CLASS(cx, obj);
1509 if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1510 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1511 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1512 return JS_FALSE;
1513 }
1514 if (!clasp->enumerate(cx, obj))
1515 return JS_FALSE;
1516
1517 /* have no props, or object's scope has not mutated from that of proto */
1518 scope = OBJ_SCOPE(obj);
1519 if (scope->entryCount == 0) {
1520 pda->length = 0;
1521 pda->array = NULL;
1522 return JS_TRUE;
1523 }
1524
1525 n = scope->entryCount;
1526 pd = (JSPropertyDesc *) cx->malloc((size_t)n * sizeof(JSPropertyDesc));
1527 if (!pd)
1528 return JS_FALSE;
1529 i = 0;
1530 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1531 if (scope->hadMiddleDelete() && !scope->has(sprop))
1532 continue;
1533 if (!js_AddRoot(cx, &pd[i].id, NULL))
1534 goto bad;
1535 if (!js_AddRoot(cx, &pd[i].value, NULL))
1536 goto bad;
1537 if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1538 goto bad;
1539 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
1540 goto bad;
1541 if (++i == n)
1542 break;
1543 }
1544 pda->length = i;
1545 pda->array = pd;
1546 return JS_TRUE;
1547
1548 bad:
1549 pda->length = i + 1;
1550 pda->array = pd;
1551 JS_PutPropertyDescArray(cx, pda);
1552 return JS_FALSE;
1553 }
1554
1555 JS_PUBLIC_API(void)
1556 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1557 {
1558 JSPropertyDesc *pd;
1559 uint32 i;
1560
1561 pd = pda->array;
1562 for (i = 0; i < pda->length; i++) {
1563 js_RemoveRoot(cx->runtime, &pd[i].id);
1564 js_RemoveRoot(cx->runtime, &pd[i].value);
1565 if (pd[i].flags & JSPD_ALIAS)
1566 js_RemoveRoot(cx->runtime, &pd[i].alias);
1567 }
1568 cx->free(pd);
1569 }
1570
1571 /************************************************************************/
1572
1573 JS_PUBLIC_API(JSBool)
1574 JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
1575 {
1576 rt->globalDebugHooks.debuggerHandler = handler;
1577 rt->globalDebugHooks.debuggerHandlerData = closure;
1578 return JS_TRUE;
1579 }
1580
1581 JS_PUBLIC_API(JSBool)
1582 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1583 {
1584 rt->globalDebugHooks.sourceHandler = handler;
1585 rt->globalDebugHooks.sourceHandlerData = closure;
1586 return JS_TRUE;
1587 }
1588
1589 JS_PUBLIC_API(JSBool)
1590 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1591 {
1592 rt->globalDebugHooks.executeHook = hook;
1593 rt->globalDebugHooks.executeHookData = closure;
1594 return JS_TRUE;
1595 }
1596
1597 JS_PUBLIC_API(JSBool)
1598 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1599 {
1600 #ifdef JS_TRACER
1601 JS_LOCK_GC(rt);
1602 bool wasInhibited = rt->debuggerInhibitsJIT();
1603 #endif
1604 rt->globalDebugHooks.callHook = hook;
1605 rt->globalDebugHooks.callHookData = closure;
1606 #ifdef JS_TRACER
1607 JITInhibitingHookChange(rt, wasInhibited);
1608 JS_UNLOCK_GC(rt);
1609 if (hook)
1610 LeaveTraceRT(rt);
1611 #endif
1612 return JS_TRUE;
1613 }
1614
1615 JS_PUBLIC_API(JSBool)
1616 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
1617 {
1618 #ifdef JS_TRACER
1619 JS_LOCK_GC(rt);
1620 bool wasInhibited = rt->debuggerInhibitsJIT();
1621 #endif
1622 rt->globalDebugHooks.objectHook = hook;
1623 rt->globalDebugHooks.objectHookData = closure;
1624 #ifdef JS_TRACER
1625 JITInhibitingHookChange(rt, wasInhibited);
1626 JS_UNLOCK_GC(rt);
1627 if (hook)
1628 LeaveTraceRT(rt);
1629 #endif
1630 return JS_TRUE;
1631 }
1632
1633 JS_PUBLIC_API(JSBool)
1634 JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
1635 {
1636 rt->globalDebugHooks.throwHook = hook;
1637 rt->globalDebugHooks.throwHookData = closure;
1638 return JS_TRUE;
1639 }
1640
1641 JS_PUBLIC_API(JSBool)
1642 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1643 {
1644 rt->globalDebugHooks.debugErrorHook = hook;
1645 rt->globalDebugHooks.debugErrorHookData = closure;
1646 return JS_TRUE;
1647 }
1648
1649 /************************************************************************/
1650
1651 JS_PUBLIC_API(size_t)
1652 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1653 {
1654 size_t nbytes;
1655 JSScope *scope;
1656
1657 nbytes = sizeof *obj;
1658 if (obj->dslots) {
1659 nbytes += ((uint32)obj->dslots[-1] - JS_INITIAL_NSLOTS + 1)
1660 * sizeof obj->dslots[0];
1661 }
1662 if (OBJ_IS_NATIVE(obj)) {
1663 scope = OBJ_SCOPE(obj);
1664 if (scope->owned()) {
1665 nbytes += sizeof *scope;
1666 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1667 }
1668 }
1669 return nbytes;
1670 }
1671
1672 static size_t
1673 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1674 {
1675 size_t nbytes;
1676
1677 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1678 if (ATOM_IS_STRING(atom)) {
1679 nbytes += sizeof(JSString);
1680 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1681 } else if (ATOM_IS_DOUBLE(atom)) {
1682 nbytes += sizeof(jsdouble);
1683 }
1684 return nbytes;
1685 }
1686
1687 JS_PUBLIC_API(size_t)
1688 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1689 {
1690 size_t nbytes;
1691
1692 nbytes = sizeof *fun;
1693 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1694 if (FUN_INTERPRETED(fun))
1695 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1696 if (fun->atom)
1697 nbytes += GetAtomTotalSize(cx, fun->atom);
1698 return nbytes;
1699 }
1700
1701 #include "jsemit.h"
1702
1703 JS_PUBLIC_API(size_t)
1704 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1705 {
1706 size_t nbytes, pbytes;
1707 jsatomid i;
1708 jssrcnote *sn, *notes;
1709 JSObjectArray *objarray;
1710 JSPrincipals *principals;
1711
1712 nbytes = sizeof *script;
1713 if (script->u.object)
1714 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1715
1716 nbytes += script->length * sizeof script->code[0];
1717 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1718 for (i = 0; i < script->atomMap.length; i++)
1719 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1720
1721 if (script->filename)
1722 nbytes += strlen(script->filename) + 1;
1723
1724 notes = script->notes();
1725 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1726 continue;
1727 nbytes += (sn - notes + 1) * sizeof *sn;
1728
1729 if (script->objectsOffset != 0) {
1730 objarray = script->objects();
1731 i = objarray->length;
1732 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1733 do {
1734 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1735 } while (i != 0);
1736 }
1737
1738 if (script->regexpsOffset != 0) {
1739 objarray = script->regexps();
1740 i = objarray->length;
1741 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1742 do {
1743 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1744 } while (i != 0);
1745 }
1746
1747 if (script->trynotesOffset != 0) {
1748 nbytes += sizeof(JSTryNoteArray) +
1749 script->trynotes()->length * sizeof(JSTryNote);
1750 }
1751
1752 principals = script->principals;
1753 if (principals) {
1754 JS_ASSERT(principals->refcount);
1755 pbytes = sizeof *principals;
1756 if (principals->refcount > 1)
1757 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1758 nbytes += pbytes;
1759 }
1760
1761 return nbytes;
1762 }
1763
1764 JS_PUBLIC_API(uint32)
1765 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1766 {
1767 if (!fp)
1768 fp = js_GetTopStackFrame(cx);
1769 while (fp) {
1770 if (fp->script)
1771 return JS_GetScriptFilenameFlags(fp->script);
1772 fp = fp->down;
1773 }
1774 return 0;
1775 }
1776
1777 JS_PUBLIC_API(uint32)
1778 JS_GetScriptFilenameFlags(JSScript *script)
1779 {
1780 JS_ASSERT(script);
1781 if (!script->filename)
1782 return JSFILENAME_NULL;
1783 return js_GetScriptFilenameFlags(script->filename);
1784 }
1785
1786 JS_PUBLIC_API(JSBool)
1787 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1788 {
1789 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1790 return JS_FALSE;
1791 return JS_TRUE;
1792 }
1793
1794 JS_PUBLIC_API(JSBool)
1795 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1796 {
1797 return obj->isSystem();
1798 }
1799
1800 JS_PUBLIC_API(JSObject *)
1801 JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1802 JSObject *parent, JSBool system)
1803 {
1804 JSObject *obj;
1805
1806 obj = js_NewObject(cx, clasp, proto, parent);
1807 if (obj && system)
1808 obj->setSystem();
1809 return obj;
1810 }
1811
1812 /************************************************************************/
1813
1814 JS_PUBLIC_API(const JSDebugHooks *)
1815 JS_GetGlobalDebugHooks(JSRuntime *rt)
1816 {
1817 return &rt->globalDebugHooks;
1818 }
1819
1820 JS_PUBLIC_API(JSDebugHooks *)
1821 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1822 {
1823 JS_ASSERT(hooks);
1824 if (hooks != &cx->runtime->globalDebugHooks)
1825 js_LeaveTrace(cx);
1826
1827 #ifdef JS_TRACER
1828 JS_LOCK_GC(cx->runtime);
1829 #endif
1830 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1831 cx->debugHooks = hooks;
1832 #ifdef JS_TRACER
1833 cx->updateJITEnabled();
1834 JS_UNLOCK_GC(cx->runtime);
1835 #endif
1836 return old;
1837 }
1838
1839 #ifdef MOZ_SHARK
1840
1841 #include <CHUD/CHUD.h>
1842
1843 JS_PUBLIC_API(JSBool)
1844 JS_StartChudRemote()
1845 {
1846 if (chudIsRemoteAccessAcquired() &&
1847 (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
1848 return JS_TRUE;
1849 }
1850
1851 return JS_FALSE;
1852 }
1853
1854 JS_PUBLIC_API(JSBool)
1855 JS_StopChudRemote()
1856 {
1857 if (chudIsRemoteAccessAcquired() &&
1858 (chudStopRemotePerfMonitor() == chudSuccess)) {
1859 return JS_TRUE;
1860 }
1861
1862 return JS_FALSE;
1863 }
1864
1865 JS_PUBLIC_API(JSBool)
1866 JS_ConnectShark()
1867 {
1868 if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
1869 return JS_FALSE;
1870
1871 if (chudAcquireRemoteAccess() != chudSuccess)
1872 return JS_FALSE;
1873
1874 return JS_TRUE;
1875 }
1876
1877 JS_PUBLIC_API(JSBool)
1878 JS_DisconnectShark()
1879 {
1880 if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
1881 return JS_FALSE;
1882
1883 return JS_TRUE;
1884 }
1885
1886 JS_FRIEND_API(JSBool)
1887 js_StartShark(JSContext *cx, JSObject *obj,
1888 uintN argc, jsval *argv, jsval *rval)
1889 {
1890 if (!JS_StartChudRemote()) {
1891 JS_ReportError(cx, "Error starting CHUD.");
1892 return JS_FALSE;
1893 }
1894
1895 return JS_TRUE;
1896 }
1897
1898 JS_FRIEND_API(JSBool)
1899 js_StopShark(JSContext *cx, JSObject *obj,
1900 uintN argc, jsval *argv, jsval *rval)
1901 {
1902 if (!JS_StopChudRemote()) {
1903 JS_ReportError(cx, "Error stopping CHUD.");
1904 return JS_FALSE;
1905 }
1906
1907 return JS_TRUE;
1908 }
1909
1910 JS_FRIEND_API(JSBool)
1911 js_ConnectShark(JSContext *cx, JSObject *obj,
1912 uintN argc, jsval *argv, jsval *rval)
1913 {
1914 if (!JS_ConnectShark()) {
1915 JS_ReportError(cx, "Error connecting to Shark.");
1916 return JS_FALSE;
1917 }
1918
1919 return JS_TRUE;
1920 }
1921
1922 JS_FRIEND_API(JSBool)
1923 js_DisconnectShark(JSContext *cx, JSObject *obj,
1924 uintN argc, jsval *argv, jsval *rval)
1925 {
1926 if (!JS_DisconnectShark()) {
1927 JS_ReportError(cx, "Error disconnecting from Shark.");
1928 return JS_FALSE;
1929 }
1930
1931 return JS_TRUE;
1932 }
1933
1934 #endif /* MOZ_SHARK */
1935
1936 #ifdef MOZ_CALLGRIND
1937
1938 #include <valgrind/callgrind.h>
1939
1940 JS_FRIEND_API(JSBool)
1941 js_StartCallgrind(JSContext *cx, JSObject *obj,
1942 uintN argc, jsval *argv, jsval *rval)
1943 {
1944 CALLGRIND_START_INSTRUMENTATION;
1945 CALLGRIND_ZERO_STATS;
1946 return JS_TRUE;
1947 }
1948
1949 JS_FRIEND_API(JSBool)
1950 js_StopCallgrind(JSContext *cx, JSObject *obj,
1951 uintN argc, jsval *argv, jsval *rval)
1952 {
1953 CALLGRIND_STOP_INSTRUMENTATION;
1954 return JS_TRUE;
1955 }
1956
1957 JS_FRIEND_API(JSBool)
1958 js_DumpCallgrind(JSContext *cx, JSObject *obj,
1959 uintN argc, jsval *argv, jsval *rval)
1960 {
1961 JSString *str;
1962 char *cstr;
1963
1964 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1965 str = JSVAL_TO_STRING(argv[0]);
1966 cstr = js_DeflateString(cx, str->chars(), str->length());
1967 if (cstr) {
1968 CALLGRIND_DUMP_STATS_AT(cstr);
1969 cx->free(cstr);
1970 return JS_TRUE;
1971 }
1972 }
1973 CALLGRIND_DUMP_STATS;
1974
1975 return JS_TRUE;
1976 }
1977
1978 #endif /* MOZ_CALLGRIND */
1979
1980 #ifdef MOZ_VTUNE
1981 #include <VTuneApi.h>
1982
1983 static const char *vtuneErrorMessages[] = {
1984 "unknown, error #0",
1985 "invalid 'max samples' field",
1986 "invalid 'samples per buffer' field",
1987 "invalid 'sample interval' field",
1988 "invalid path",
1989 "sample file in use",
1990 "invalid 'number of events' field",
1991 "unknown, error #7",
1992 "internal error",
1993 "bad event name",
1994 "VTStopSampling called without calling VTStartSampling",
1995 "no events selected for event-based sampling",
1996 "events selected cannot be run together",
1997 "no sampling parameters",
1998 "sample database already exists",
1999 "sampling already started",
2000 "time-based sampling not supported",
2001 "invalid 'sampling parameters size' field",
2002 "invalid 'event size' field",
2003 "sampling file already bound",
2004 "invalid event path",
2005 "invalid license",
2006 "invalid 'global options' field",
2007
2008 };
2009
2010 JS_FRIEND_API(JSBool)
2011 js_StartVtune(JSContext *cx, JSObject *obj,
2012 uintN argc, jsval *argv, jsval *rval)
2013 {
2014 VTUNE_EVENT events[] = {
2015 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2016 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2017 };
2018
2019 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2020 char *default_filename = "mozilla-vtune.tb5";
2021 JSString *str;
2022 U32 status;
2023
2024 VTUNE_SAMPLING_PARAMS params =
2025 sizeof(VTUNE_SAMPLING_PARAMS),
2026 sizeof(VTUNE_EVENT),
2027 0, 0, /* Reserved fields */
2028 1, /* Initialize in "paused" state */
2029 0, /* Max samples, or 0 for "continuous" */
2030 4096, /* Samples per buffer */
2031 0.1, /* Sampling interval in ms */
2032 1, /* 1 for event-based sampling, 0 for time-based */
2033
2034 n_events,
2035 events,
2036 default_filename,
2037 };
2038
2039 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2040 str = JSVAL_TO_STRING(argv[0]);
2041 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2042 }
2043
2044 status = VTStartSampling(&params);
2045
2046 if (params.tb5Filename != default_filename)
2047 cx->free(params.tb5Filename);
2048
2049 if (status != 0) {
2050 if (status == VTAPI_MULTIPLE_RUNS)
2051 VTStopSampling(0);
2052 if (status < sizeof(vtuneErrorMessages))
2053 JS_ReportError(cx, "Vtune setup error: %s",
2054 vtuneErrorMessages[status]);
2055 else
2056 JS_ReportError(cx, "Vtune setup error: %d",
2057 status);
2058 return JS_FALSE;
2059 }
2060 return JS_TRUE;
2061 }
2062
2063 JS_FRIEND_API(JSBool)
2064 js_StopVtune(JSContext *cx, JSObject *obj,
2065 uintN argc, jsval *argv, jsval *rval)
2066 {
2067 U32 status = VTStopSampling(1);
2068 if (status) {
2069 if (status < sizeof(vtuneErrorMessages))
2070 JS_ReportError(cx, "Vtune shutdown error: %s",
2071 vtuneErrorMessages[status]);
2072 else
2073 JS_ReportError(cx, "Vtune shutdown error: %d",
2074 status);
2075 return JS_FALSE;
2076 }
2077 return JS_TRUE;
2078 }
2079
2080 JS_FRIEND_API(JSBool)
2081 js_PauseVtune(JSContext *cx, JSObject *obj,
2082 uintN argc, jsval *argv, jsval *rval)
2083 {
2084 VTPause();
2085 return JS_TRUE;
2086 }
2087
2088 JS_FRIEND_API(JSBool)
2089 js_ResumeVtune(JSContext *cx, JSObject *obj,
2090 uintN argc, jsval *argv, jsval *rval)
2091 {
2092 VTResume();
2093 return JS_TRUE;
2094 }
2095
2096 #endif /* MOZ_VTUNE */
2097
2098 #ifdef MOZ_TRACEVIS
2099 /*
2100 * Ethogram - Javascript wrapper for TraceVis state
2101 *
2102 * ethology: The scientific study of animal behavior,
2103 * especially as it occurs in a natural environment.
2104 * ethogram: A pictorial catalog of the behavioral patterns of
2105 * an organism or a species.
2106 *
2107 */
2108 #if defined(XP_WIN)
2109 #include <windows.h>
2110 #else
2111 #include <sys/time.h>
2112 #endif
2113 #include "jstracer.h"
2114
2115 #define ETHOGRAM_BUF_SIZE 65536
2116
2117 static JSBool
2118 ethogram_construct(JSContext *cx, JSObject *obj,
2119 uintN argc, jsval *argv, jsval *rval);
2120 static void
2121 ethogram_finalize(JSContext *cx, JSObject *obj);
2122
2123 static JSClass ethogram_class = {
2124 "Ethogram",
2125 JSCLASS_HAS_PRIVATE,
2126 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2127 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2128 JSCLASS_NO_OPTIONAL_MEMBERS
2129 };
2130
2131 struct EthogramEvent {
2132 TraceVisState s;
2133 TraceVisExitReason r;
2134 int ts;
2135 int tus;
2136 JSString *filename;
2137 int lineno;
2138 };
2139
2140 static int
2141 compare_strings(const void *k1, const void *k2)
2142 {
2143 return strcmp((const char *) k1, (const char *) k2) == 0;
2144 }
2145
2146 class EthogramEventBuffer {
2147 private:
2148 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2149 int mReadPos;
2150 int mWritePos;
2151 JSObject *mFilenames;
2152 int mStartSecond;
2153
2154 struct EthogramScriptEntry {
2155 char *filename;
2156 JSString *jsfilename;
2157
2158 EthogramScriptEntry *next;
2159 };
2160 EthogramScriptEntry *mScripts;
2161
2162 public:
2163 friend JSBool
2164 ethogram_construct(JSContext *cx, JSObject *obj,
2165 uintN argc, jsval *argv, jsval *rval);
2166
2167 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2168 mBuf[mWritePos].s = s;
2169 mBuf[mWritePos].r = r;
2170 #if defined(XP_WIN)
2171 FILETIME now;
2172 GetSystemTimeAsFileTime(&now);
2173 unsigned long long raw_us = 0.1 *
2174 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2175 (unsigned long long) now.dwLowDateTime);
2176 unsigned int sec = raw_us / 1000000L;
2177 unsigned int usec = raw_us % 1000000L;
2178 mBuf[mWritePos].ts = sec - mStartSecond;
2179 mBuf[mWritePos].tus = usec;
2180 #else
2181 struct timeval tv;
2182 gettimeofday(&tv, NULL);
2183 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2184 mBuf[mWritePos].tus = tv.tv_usec;
2185 #endif
2186
2187 JSString *jsfilename = findScript(filename);
2188 mBuf[mWritePos].filename = jsfilename;
2189 mBuf[mWritePos].lineno = lineno;
2190
2191 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2192 if (mWritePos == mReadPos) {
2193 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2194 }
2195 }
2196
2197 inline EthogramEvent *pop() {
2198 EthogramEvent *e = &mBuf[mReadPos];
2199 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2200 return e;
2201 }
2202
2203 bool isEmpty() {
2204 return (mReadPos == mWritePos);
2205 }
2206
2207 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2208 JSHashNumber hash = JS_HashString(filename);
2209 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2210 if (*hep != NULL)
2211 return JS_FALSE;
2212
2213 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2214
2215 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2216 if (entry == NULL)
2217 return NULL;
2218
2219 entry->next = mScripts;
2220 mScripts = entry;
2221 entry->filename = filename;
2222 entry->jsfilename = jsfilename;
2223
2224 return mScripts;
2225 }
2226
2227 void removeScripts(JSContext *cx) {
2228 EthogramScriptEntry *se = mScripts;
2229 while (se != NULL) {
2230 char *filename = se->filename;
2231
2232 JSHashNumber hash = JS_HashString(filename);
2233 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2234 JSHashEntry *he = *hep;
2235 if (he) {
2236 /* we hardly knew he */
2237 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2238 }
2239
2240 EthogramScriptEntry *se_head = se;
2241 se = se->next;
2242 JS_free(cx, se_head);
2243 }
2244 }
2245
2246 JSString *findScript(char *filename) {
2247 EthogramScriptEntry *se = mScripts;
2248 while (se != NULL) {
2249 if (compare_strings(se->filename, filename))
2250 return (se->jsfilename);
2251 se = se->next;
2252 }
2253 return NULL;
2254 }
2255
2256 JSObject *filenames() {
2257 return mFilenames;
2258 }
2259
2260 int length() {
2261 if (mWritePos < mReadPos)
2262 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2263 else
2264 return mWritePos - mReadPos;
2265 }
2266 };
2267
2268 static char jstv_empty[] = "<null>";
2269
2270 inline char *
2271 jstv_Filename(JSStackFrame *fp)
2272 {
2273 while (fp && fp->script == NULL)
2274 fp = fp->down;
2275 return (fp && fp->script && fp->script->filename)
2276 ? (char *)fp->script->filename
2277 : jstv_empty;
2278 }
2279 inline uintN
2280 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2281 {
2282 while (fp && fp->regs == NULL)
2283 fp = fp->down;
2284 return (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
2285 }
2286
2287 /* Collect states here and distribute to a matching buffer, if any */
2288 JS_FRIEND_API(void)
2289 js_StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2290 {
2291 JSStackFrame *fp = cx->fp;
2292
2293 char *script_file = jstv_Filename(fp);
2294 JSHashNumber hash = JS_HashString(script_file);
2295
2296 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2297 /* update event buffer, flag if overflowed */
2298 JSHashEntry *he = *hep;
2299 if (he) {
2300 EthogramEventBuffer *p;
2301 p = (EthogramEventBuffer *) he->value;
2302
2303 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2304 }
2305 }
2306
2307 static JSBool
2308 ethogram_construct(JSContext *cx, JSObject *obj,
2309 uintN argc, jsval *argv, jsval *rval)
2310 {
2311 EthogramEventBuffer *p;
2312
2313 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2314
2315 p->mReadPos = p->mWritePos = 0;
2316 p->mScripts = NULL;
2317 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2318
2319 #if defined(XP_WIN)
2320 FILETIME now;
2321 GetSystemTimeAsFileTime(&now);
2322 unsigned long long raw_us = 0.1 *
2323 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2324 (unsigned long long) now.dwLowDateTime);
2325 unsigned int s = raw_us / 1000000L;
2326 p->mStartSecond = s;
2327 #else
2328 struct timeval tv;
2329 gettimeofday(&tv, NULL);
2330 p->mStartSecond = tv.tv_sec;
2331 #endif
2332 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2333 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2334 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2335 return JS_FALSE;
2336
2337 if (!JS_IsConstructing(cx)) {
2338 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2339 if (!obj)
2340 return JS_FALSE;
2341 *rval = OBJECT_TO_JSVAL(obj);
2342 }
2343 JS_SetPrivate(cx, obj, p);
2344 return JS_TRUE;
2345 }
2346
2347 static void
2348 ethogram_finalize(JSContext *cx, JSObject *obj)
2349 {
2350 EthogramEventBuffer *p;
2351 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2352 if (!p)
2353 return;
2354
2355 p->removeScripts(cx);
2356
2357 JS_free(cx, p);
2358 }
2359
2360 static JSBool
2361 ethogram_addScript(JSContext *cx, JSObject *obj,
2362 uintN argc, jsval *argv, jsval *rval)
2363 {
2364 JSString *str;
2365 char *filename = NULL;
2366 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2367 str = JSVAL_TO_STRING(argv[0]);
2368 filename = js_DeflateString(cx,
2369 str->chars(),
2370 str->length());
2371 }
2372
2373 /* silently ignore no args */
2374 if (!filename)
2375 return JS_TRUE;
2376
2377 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2378
2379 p->addScript(cx, obj, filename, str);
2380 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, rval);
2381 return JS_TRUE;
2382 }
2383
2384 static JSBool
2385 ethogram_getAllEvents(JSContext *cx, JSObject *obj,
2386 uintN argc, jsval *argv, jsval *rval)
2387 {
2388 EthogramEventBuffer *p;
2389
2390 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2391 if (!p)
2392 return JS_FALSE;
2393
2394 if (p->isEmpty()) {
2395 *rval = JSVAL_NULL;
2396 return JS_TRUE;
2397 }
2398
2399 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2400 if (rarray == NULL) {
2401 *rval = JSVAL_NULL;
2402 return JS_TRUE;
2403 }
2404
2405 *rval = OBJECT_TO_JSVAL(rarray);
2406
2407 for (int i = 0; !p->isEmpty(); i++) {
2408
2409 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2410 if (x == NULL)
2411 return JS_FALSE;
2412
2413 EthogramEvent *e = p->pop();
2414
2415 jsval state = INT_TO_JSVAL(e->s);
2416 jsval reason = INT_TO_JSVAL(e->r);
2417 jsval ts = INT_TO_JSVAL(e->ts);
2418 jsval tus = INT_TO_JSVAL(e->tus);
2419
2420 jsval filename = STRING_TO_JSVAL(e->filename);
2421 jsval lineno = INT_TO_JSVAL(e->lineno);
2422
2423 if (!JS_SetProperty(cx, x, "state", &state))
2424 return JS_FALSE;
2425 if (!JS_SetProperty(cx, x, "reason", &reason))
2426 return JS_FALSE;
2427 if (!JS_SetProperty(cx, x, "ts", &ts))
2428 return JS_FALSE;
2429 if (!JS_SetProperty(cx, x, "tus", &tus))
2430 return JS_FALSE;
2431
2432 if (!JS_SetProperty(cx, x, "filename", &filename))
2433 return JS_FALSE;
2434 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2435 return JS_FALSE;
2436
2437 jsval element = OBJECT_TO_JSVAL(x);
2438 JS_SetElement(cx, rarray, i, &element);
2439 }
2440
2441 return JS_TRUE;
2442 }
2443
2444 static JSBool
2445 ethogram_getNextEvent(JSContext *cx, JSObject *obj,
2446 uintN argc, jsval *argv, jsval *rval)
2447 {
2448 EthogramEventBuffer *p;
2449
2450 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2451 if (!p)
2452 return JS_FALSE;
2453
2454 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2455 if (x == NULL)
2456 return JS_FALSE;
2457
2458 if (p->isEmpty()) {
2459 *rval = JSVAL_NULL;
2460 return JS_TRUE;
2461 }
2462
2463 EthogramEvent *e = p->pop();
2464 jsval state = INT_TO_JSVAL(e->s);
2465 jsval reason = INT_TO_JSVAL(e->r);
2466 jsval ts = INT_TO_JSVAL(e->ts);
2467 jsval tus = INT_TO_JSVAL(e->tus);
2468
2469 jsval filename = STRING_TO_JSVAL(e->filename);
2470 jsval lineno = INT_TO_JSVAL(e->lineno);
2471
2472 if (!JS_SetProperty(cx, x, "state", &state))
2473 return JS_FALSE;
2474 if (!JS_SetProperty(cx, x, "reason", &reason))
2475 return JS_FALSE;
2476 if (!JS_SetProperty(cx, x, "ts", &ts))
2477 return JS_FALSE;
2478 if (!JS_SetProperty(cx, x, "tus", &tus))
2479 return JS_FALSE;
2480 if (!JS_SetProperty(cx, x, "filename", &filename))
2481 return JS_FALSE;
2482
2483 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2484 return JS_FALSE;
2485
2486 *rval = OBJECT_TO_JSVAL(x);
2487
2488 return JS_TRUE;
2489 }
2490
2491 static JSFunctionSpec ethogram_methods[] = {
2492 {"addScript", ethogram_addScript, 1},
2493 {"getAllEvents", ethogram_getAllEvents, 0},
2494 {"getNextEvent", ethogram_getNextEvent, 0},
2495 {0}
2496 };
2497
2498 /*
2499 * An |Ethogram| organizes the output of a collection of files that should be
2500 * monitored together. A single object gets events for the group.
2501 */
2502 JS_FRIEND_API(JSBool)
2503 js_InitEthogram(JSContext *cx, JSObject *obj,
2504 uintN argc, jsval *argv, jsval *rval)
2505 {
2506 if (!traceVisScriptTable) {
2507 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2508 NULL, NULL, NULL);
2509 }
2510
2511 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2512 ethogram_construct, 0, NULL, ethogram_methods,
2513 NULL, NULL);
2514
2515 return JS_TRUE;
2516 }
2517
2518 JS_FRIEND_API(JSBool)
2519 js_ShutdownEthogram(JSContext *cx, JSObject *obj,
2520 uintN argc, jsval *argv, jsval *rval)
2521 {
2522 if (traceVisScriptTable)
2523 JS_HashTableDestroy(traceVisScriptTable);
2524
2525 return JS_TRUE;
2526 }
2527
2528 #endif /* MOZ_TRACEVIS */

  ViewVC Help
Powered by ViewVC 1.1.24