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

Contents of /trunk/js/jsdbgapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (8 years, 9 months ago) by siliconforks
File size: 72872 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
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 class FakeFrame {
1574 public:
1575 FakeFrame(JSContext *cx, JSObject *scopeobj)
1576 : cx(cx) {
1577 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, scopeobj);
1578 JS_ASSERT(FUN_MINARGS(fun) == 0 && !FUN_INTERPRETED(fun) && fun->u.n.extra == 0);
1579
1580 vp[0] = OBJECT_TO_JSVAL(scopeobj);
1581 vp[1] = JSVAL_NULL;
1582
1583 memset(&frame, 0, sizeof (frame));
1584 frame.fun = fun;
1585 frame.down = js_GetTopStackFrame(cx);
1586 frame.scopeChain = JS_GetGlobalForObject(cx, scopeobj);
1587 frame.argv = vp + 2;
1588
1589 cx->fp = &frame;
1590 }
1591
1592 ~FakeFrame() {
1593 if (frame.callobj)
1594 js_PutCallObject(cx, &frame);
1595 else if (frame.argsobj)
1596 js_PutArgsObject(cx, &frame);
1597 cx->fp = frame.down;
1598 }
1599
1600 private:
1601 JSContext *cx;
1602 JSStackFrame frame;
1603 jsval vp[2];
1604 };
1605
1606 JS_FRIEND_API(JSBool)
1607 js_GetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1608 jsval *vp)
1609 {
1610 FakeFrame frame(cx, scopeobj);
1611 return JS_GetPropertyById(cx, obj, id, vp);
1612 }
1613
1614 JS_FRIEND_API(JSBool)
1615 js_SetPropertyByIdWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsid id,
1616 jsval *vp)
1617 {
1618 FakeFrame frame(cx, scopeobj);
1619 return JS_SetPropertyById(cx, obj, id, vp);
1620 }
1621
1622 JS_FRIEND_API(JSBool)
1623 js_CallFunctionValueWithFakeFrame(JSContext *cx, JSObject *obj, JSObject *scopeobj, jsval funval,
1624 uintN argc, jsval *argv, jsval *rval)
1625 {
1626 FakeFrame frame(cx, scopeobj);
1627 return JS_CallFunctionValue(cx, obj, funval, argc, argv, rval);
1628 }
1629
1630 /************************************************************************/
1631
1632 JS_PUBLIC_API(JSBool)
1633 JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
1634 {
1635 rt->globalDebugHooks.debuggerHandler = handler;
1636 rt->globalDebugHooks.debuggerHandlerData = closure;
1637 return JS_TRUE;
1638 }
1639
1640 JS_PUBLIC_API(JSBool)
1641 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1642 {
1643 rt->globalDebugHooks.sourceHandler = handler;
1644 rt->globalDebugHooks.sourceHandlerData = closure;
1645 return JS_TRUE;
1646 }
1647
1648 JS_PUBLIC_API(JSBool)
1649 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1650 {
1651 rt->globalDebugHooks.executeHook = hook;
1652 rt->globalDebugHooks.executeHookData = closure;
1653 return JS_TRUE;
1654 }
1655
1656 JS_PUBLIC_API(JSBool)
1657 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1658 {
1659 #ifdef JS_TRACER
1660 JS_LOCK_GC(rt);
1661 bool wasInhibited = rt->debuggerInhibitsJIT();
1662 #endif
1663 rt->globalDebugHooks.callHook = hook;
1664 rt->globalDebugHooks.callHookData = closure;
1665 #ifdef JS_TRACER
1666 JITInhibitingHookChange(rt, wasInhibited);
1667 JS_UNLOCK_GC(rt);
1668 if (hook)
1669 LeaveTraceRT(rt);
1670 #endif
1671 return JS_TRUE;
1672 }
1673
1674 JS_PUBLIC_API(JSBool)
1675 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
1676 {
1677 #ifdef JS_TRACER
1678 JS_LOCK_GC(rt);
1679 bool wasInhibited = rt->debuggerInhibitsJIT();
1680 #endif
1681 rt->globalDebugHooks.objectHook = hook;
1682 rt->globalDebugHooks.objectHookData = closure;
1683 #ifdef JS_TRACER
1684 JITInhibitingHookChange(rt, wasInhibited);
1685 JS_UNLOCK_GC(rt);
1686 if (hook)
1687 LeaveTraceRT(rt);
1688 #endif
1689 return JS_TRUE;
1690 }
1691
1692 JS_PUBLIC_API(JSBool)
1693 JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
1694 {
1695 rt->globalDebugHooks.throwHook = hook;
1696 rt->globalDebugHooks.throwHookData = closure;
1697 return JS_TRUE;
1698 }
1699
1700 JS_PUBLIC_API(JSBool)
1701 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1702 {
1703 rt->globalDebugHooks.debugErrorHook = hook;
1704 rt->globalDebugHooks.debugErrorHookData = closure;
1705 return JS_TRUE;
1706 }
1707
1708 /************************************************************************/
1709
1710 JS_PUBLIC_API(size_t)
1711 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1712 {
1713 size_t nbytes;
1714 JSScope *scope;
1715
1716 nbytes = sizeof *obj;
1717 if (obj->dslots) {
1718 nbytes += ((uint32)obj->dslots[-1] - JS_INITIAL_NSLOTS + 1)
1719 * sizeof obj->dslots[0];
1720 }
1721 if (OBJ_IS_NATIVE(obj)) {
1722 scope = OBJ_SCOPE(obj);
1723 if (scope->owned()) {
1724 nbytes += sizeof *scope;
1725 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1726 }
1727 }
1728 return nbytes;
1729 }
1730
1731 static size_t
1732 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1733 {
1734 size_t nbytes;
1735
1736 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1737 if (ATOM_IS_STRING(atom)) {
1738 nbytes += sizeof(JSString);
1739 nbytes += (ATOM_TO_STRING(atom)->flatLength() + 1) * sizeof(jschar);
1740 } else if (ATOM_IS_DOUBLE(atom)) {
1741 nbytes += sizeof(jsdouble);
1742 }
1743 return nbytes;
1744 }
1745
1746 JS_PUBLIC_API(size_t)
1747 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1748 {
1749 size_t nbytes;
1750
1751 nbytes = sizeof *fun;
1752 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1753 if (FUN_INTERPRETED(fun))
1754 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1755 if (fun->atom)
1756 nbytes += GetAtomTotalSize(cx, fun->atom);
1757 return nbytes;
1758 }
1759
1760 #include "jsemit.h"
1761
1762 JS_PUBLIC_API(size_t)
1763 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1764 {
1765 size_t nbytes, pbytes;
1766 jsatomid i;
1767 jssrcnote *sn, *notes;
1768 JSObjectArray *objarray;
1769 JSPrincipals *principals;
1770
1771 nbytes = sizeof *script;
1772 if (script->u.object)
1773 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1774
1775 nbytes += script->length * sizeof script->code[0];
1776 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1777 for (i = 0; i < script->atomMap.length; i++)
1778 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1779
1780 if (script->filename)
1781 nbytes += strlen(script->filename) + 1;
1782
1783 notes = script->notes();
1784 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1785 continue;
1786 nbytes += (sn - notes + 1) * sizeof *sn;
1787
1788 if (script->objectsOffset != 0) {
1789 objarray = script->objects();
1790 i = objarray->length;
1791 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1792 do {
1793 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1794 } while (i != 0);
1795 }
1796
1797 if (script->regexpsOffset != 0) {
1798 objarray = script->regexps();
1799 i = objarray->length;
1800 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1801 do {
1802 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1803 } while (i != 0);
1804 }
1805
1806 if (script->trynotesOffset != 0) {
1807 nbytes += sizeof(JSTryNoteArray) +
1808 script->trynotes()->length * sizeof(JSTryNote);
1809 }
1810
1811 principals = script->principals;
1812 if (principals) {
1813 JS_ASSERT(principals->refcount);
1814 pbytes = sizeof *principals;
1815 if (principals->refcount > 1)
1816 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1817 nbytes += pbytes;
1818 }
1819
1820 return nbytes;
1821 }
1822
1823 JS_PUBLIC_API(uint32)
1824 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1825 {
1826 if (!fp)
1827 fp = js_GetTopStackFrame(cx);
1828 while (fp) {
1829 if (fp->script)
1830 return JS_GetScriptFilenameFlags(fp->script);
1831 fp = fp->down;
1832 }
1833 return 0;
1834 }
1835
1836 JS_PUBLIC_API(uint32)
1837 JS_GetScriptFilenameFlags(JSScript *script)
1838 {
1839 JS_ASSERT(script);
1840 if (!script->filename)
1841 return JSFILENAME_NULL;
1842 return js_GetScriptFilenameFlags(script->filename);
1843 }
1844
1845 JS_PUBLIC_API(JSBool)
1846 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1847 {
1848 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1849 return JS_FALSE;
1850 return JS_TRUE;
1851 }
1852
1853 JS_PUBLIC_API(JSBool)
1854 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1855 {
1856 return obj->isSystem();
1857 }
1858
1859 JS_PUBLIC_API(JSObject *)
1860 JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1861 JSObject *parent, JSBool system)
1862 {
1863 JSObject *obj;
1864
1865 obj = js_NewObject(cx, clasp, proto, parent);
1866 if (obj && system)
1867 obj->setSystem();
1868 return obj;
1869 }
1870
1871 /************************************************************************/
1872
1873 JS_PUBLIC_API(const JSDebugHooks *)
1874 JS_GetGlobalDebugHooks(JSRuntime *rt)
1875 {
1876 return &rt->globalDebugHooks;
1877 }
1878
1879 const JSDebugHooks js_NullDebugHooks = {};
1880
1881 JS_PUBLIC_API(JSDebugHooks *)
1882 JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks)
1883 {
1884 JS_ASSERT(hooks);
1885 if (hooks != &cx->runtime->globalDebugHooks && hooks != &js_NullDebugHooks)
1886 js_LeaveTrace(cx);
1887
1888 #ifdef JS_TRACER
1889 JS_LOCK_GC(cx->runtime);
1890 #endif
1891 JSDebugHooks *old = const_cast<JSDebugHooks *>(cx->debugHooks);
1892 cx->debugHooks = hooks;
1893 #ifdef JS_TRACER
1894 cx->updateJITEnabled();
1895 JS_UNLOCK_GC(cx->runtime);
1896 #endif
1897 return old;
1898 }
1899
1900 JS_PUBLIC_API(JSDebugHooks *)
1901 JS_ClearContextDebugHooks(JSContext *cx)
1902 {
1903 return JS_SetContextDebugHooks(cx, &js_NullDebugHooks);
1904 }
1905
1906 #ifdef MOZ_SHARK
1907
1908 #include <CHUD/CHUD.h>
1909
1910 JS_PUBLIC_API(JSBool)
1911 JS_StartChudRemote()
1912 {
1913 if (chudIsRemoteAccessAcquired() &&
1914 (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
1915 return JS_TRUE;
1916 }
1917
1918 return JS_FALSE;
1919 }
1920
1921 JS_PUBLIC_API(JSBool)
1922 JS_StopChudRemote()
1923 {
1924 if (chudIsRemoteAccessAcquired() &&
1925 (chudStopRemotePerfMonitor() == chudSuccess)) {
1926 return JS_TRUE;
1927 }
1928
1929 return JS_FALSE;
1930 }
1931
1932 JS_PUBLIC_API(JSBool)
1933 JS_ConnectShark()
1934 {
1935 if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
1936 return JS_FALSE;
1937
1938 if (chudAcquireRemoteAccess() != chudSuccess)
1939 return JS_FALSE;
1940
1941 return JS_TRUE;
1942 }
1943
1944 JS_PUBLIC_API(JSBool)
1945 JS_DisconnectShark()
1946 {
1947 if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
1948 return JS_FALSE;
1949
1950 return JS_TRUE;
1951 }
1952
1953 JS_FRIEND_API(JSBool)
1954 js_StartShark(JSContext *cx, JSObject *obj,
1955 uintN argc, jsval *argv, jsval *rval)
1956 {
1957 if (!JS_StartChudRemote()) {
1958 JS_ReportError(cx, "Error starting CHUD.");
1959 return JS_FALSE;
1960 }
1961
1962 return JS_TRUE;
1963 }
1964
1965 JS_FRIEND_API(JSBool)
1966 js_StopShark(JSContext *cx, JSObject *obj,
1967 uintN argc, jsval *argv, jsval *rval)
1968 {
1969 if (!JS_StopChudRemote()) {
1970 JS_ReportError(cx, "Error stopping CHUD.");
1971 return JS_FALSE;
1972 }
1973
1974 return JS_TRUE;
1975 }
1976
1977 JS_FRIEND_API(JSBool)
1978 js_ConnectShark(JSContext *cx, JSObject *obj,
1979 uintN argc, jsval *argv, jsval *rval)
1980 {
1981 if (!JS_ConnectShark()) {
1982 JS_ReportError(cx, "Error connecting to Shark.");
1983 return JS_FALSE;
1984 }
1985
1986 return JS_TRUE;
1987 }
1988
1989 JS_FRIEND_API(JSBool)
1990 js_DisconnectShark(JSContext *cx, JSObject *obj,
1991 uintN argc, jsval *argv, jsval *rval)
1992 {
1993 if (!JS_DisconnectShark()) {
1994 JS_ReportError(cx, "Error disconnecting from Shark.");
1995 return JS_FALSE;
1996 }
1997
1998 return JS_TRUE;
1999 }
2000
2001 #endif /* MOZ_SHARK */
2002
2003 #ifdef MOZ_CALLGRIND
2004
2005 #include <valgrind/callgrind.h>
2006
2007 JS_FRIEND_API(JSBool)
2008 js_StartCallgrind(JSContext *cx, JSObject *obj,
2009 uintN argc, jsval *argv, jsval *rval)
2010 {
2011 CALLGRIND_START_INSTRUMENTATION;
2012 CALLGRIND_ZERO_STATS;
2013 return JS_TRUE;
2014 }
2015
2016 JS_FRIEND_API(JSBool)
2017 js_StopCallgrind(JSContext *cx, JSObject *obj,
2018 uintN argc, jsval *argv, jsval *rval)
2019 {
2020 CALLGRIND_STOP_INSTRUMENTATION;
2021 return JS_TRUE;
2022 }
2023
2024 JS_FRIEND_API(JSBool)
2025 js_DumpCallgrind(JSContext *cx, JSObject *obj,
2026 uintN argc, jsval *argv, jsval *rval)
2027 {
2028 JSString *str;
2029 char *cstr;
2030
2031 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2032 str = JSVAL_TO_STRING(argv[0]);
2033 cstr = js_DeflateString(cx, str->chars(), str->length());
2034 if (cstr) {
2035 CALLGRIND_DUMP_STATS_AT(cstr);
2036 cx->free(cstr);
2037 return JS_TRUE;
2038 }
2039 }
2040 CALLGRIND_DUMP_STATS;
2041
2042 return JS_TRUE;
2043 }
2044
2045 #endif /* MOZ_CALLGRIND */
2046
2047 #ifdef MOZ_VTUNE
2048 #include <VTuneApi.h>
2049
2050 static const char *vtuneErrorMessages[] = {
2051 "unknown, error #0",
2052 "invalid 'max samples' field",
2053 "invalid 'samples per buffer' field",
2054 "invalid 'sample interval' field",
2055 "invalid path",
2056 "sample file in use",
2057 "invalid 'number of events' field",
2058 "unknown, error #7",
2059 "internal error",
2060 "bad event name",
2061 "VTStopSampling called without calling VTStartSampling",
2062 "no events selected for event-based sampling",
2063 "events selected cannot be run together",
2064 "no sampling parameters",
2065 "sample database already exists",
2066 "sampling already started",
2067 "time-based sampling not supported",
2068 "invalid 'sampling parameters size' field",
2069 "invalid 'event size' field",
2070 "sampling file already bound",
2071 "invalid event path",
2072 "invalid license",
2073 "invalid 'global options' field",
2074
2075 };
2076
2077 JS_FRIEND_API(JSBool)
2078 js_StartVtune(JSContext *cx, JSObject *obj,
2079 uintN argc, jsval *argv, jsval *rval)
2080 {
2081 VTUNE_EVENT events[] = {
2082 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
2083 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
2084 };
2085
2086 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
2087 char *default_filename = "mozilla-vtune.tb5";
2088 JSString *str;
2089 U32 status;
2090
2091 VTUNE_SAMPLING_PARAMS params =
2092 sizeof(VTUNE_SAMPLING_PARAMS),
2093 sizeof(VTUNE_EVENT),
2094 0, 0, /* Reserved fields */
2095 1, /* Initialize in "paused" state */
2096 0, /* Max samples, or 0 for "continuous" */
2097 4096, /* Samples per buffer */
2098 0.1, /* Sampling interval in ms */
2099 1, /* 1 for event-based sampling, 0 for time-based */
2100
2101 n_events,
2102 events,
2103 default_filename,
2104 };
2105
2106 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2107 str = JSVAL_TO_STRING(argv[0]);
2108 params.tb5Filename = js_DeflateString(cx, str->chars(), str->length());
2109 }
2110
2111 status = VTStartSampling(&params);
2112
2113 if (params.tb5Filename != default_filename)
2114 cx->free(params.tb5Filename);
2115
2116 if (status != 0) {
2117 if (status == VTAPI_MULTIPLE_RUNS)
2118 VTStopSampling(0);
2119 if (status < sizeof(vtuneErrorMessages))
2120 JS_ReportError(cx, "Vtune setup error: %s",
2121 vtuneErrorMessages[status]);
2122 else
2123 JS_ReportError(cx, "Vtune setup error: %d",
2124 status);
2125 return JS_FALSE;
2126 }
2127 return JS_TRUE;
2128 }
2129
2130 JS_FRIEND_API(JSBool)
2131 js_StopVtune(JSContext *cx, JSObject *obj,
2132 uintN argc, jsval *argv, jsval *rval)
2133 {
2134 U32 status = VTStopSampling(1);
2135 if (status) {
2136 if (status < sizeof(vtuneErrorMessages))
2137 JS_ReportError(cx, "Vtune shutdown error: %s",
2138 vtuneErrorMessages[status]);
2139 else
2140 JS_ReportError(cx, "Vtune shutdown error: %d",
2141 status);
2142 return JS_FALSE;
2143 }
2144 return JS_TRUE;
2145 }
2146
2147 JS_FRIEND_API(JSBool)
2148 js_PauseVtune(JSContext *cx, JSObject *obj,
2149 uintN argc, jsval *argv, jsval *rval)
2150 {
2151 VTPause();
2152 return JS_TRUE;
2153 }
2154
2155 JS_FRIEND_API(JSBool)
2156 js_ResumeVtune(JSContext *cx, JSObject *obj,
2157 uintN argc, jsval *argv, jsval *rval)
2158 {
2159 VTResume();
2160 return JS_TRUE;
2161 }
2162
2163 #endif /* MOZ_VTUNE */
2164
2165 #ifdef MOZ_TRACEVIS
2166 /*
2167 * Ethogram - Javascript wrapper for TraceVis state
2168 *
2169 * ethology: The scientific study of animal behavior,
2170 * especially as it occurs in a natural environment.
2171 * ethogram: A pictorial catalog of the behavioral patterns of
2172 * an organism or a species.
2173 *
2174 */
2175 #if defined(XP_WIN)
2176 #include <windows.h>
2177 #else
2178 #include <sys/time.h>
2179 #endif
2180 #include "jstracer.h"
2181
2182 #define ETHOGRAM_BUF_SIZE 65536
2183
2184 static JSBool
2185 ethogram_construct(JSContext *cx, JSObject *obj,
2186 uintN argc, jsval *argv, jsval *rval);
2187 static void
2188 ethogram_finalize(JSContext *cx, JSObject *obj);
2189
2190 static JSClass ethogram_class = {
2191 "Ethogram",
2192 JSCLASS_HAS_PRIVATE,
2193 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2194 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ethogram_finalize,
2195 JSCLASS_NO_OPTIONAL_MEMBERS
2196 };
2197
2198 struct EthogramEvent {
2199 TraceVisState s;
2200 TraceVisExitReason r;
2201 int ts;
2202 int tus;
2203 JSString *filename;
2204 int lineno;
2205 };
2206
2207 static int
2208 compare_strings(const void *k1, const void *k2)
2209 {
2210 return strcmp((const char *) k1, (const char *) k2) == 0;
2211 }
2212
2213 class EthogramEventBuffer {
2214 private:
2215 EthogramEvent mBuf[ETHOGRAM_BUF_SIZE];
2216 int mReadPos;
2217 int mWritePos;
2218 JSObject *mFilenames;
2219 int mStartSecond;
2220
2221 struct EthogramScriptEntry {
2222 char *filename;
2223 JSString *jsfilename;
2224
2225 EthogramScriptEntry *next;
2226 };
2227 EthogramScriptEntry *mScripts;
2228
2229 public:
2230 friend JSBool
2231 ethogram_construct(JSContext *cx, JSObject *obj,
2232 uintN argc, jsval *argv, jsval *rval);
2233
2234 inline void push(TraceVisState s, TraceVisExitReason r, char *filename, int lineno) {
2235 mBuf[mWritePos].s = s;
2236 mBuf[mWritePos].r = r;
2237 #if defined(XP_WIN)
2238 FILETIME now;
2239 GetSystemTimeAsFileTime(&now);
2240 unsigned long long raw_us = 0.1 *
2241 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2242 (unsigned long long) now.dwLowDateTime);
2243 unsigned int sec = raw_us / 1000000L;
2244 unsigned int usec = raw_us % 1000000L;
2245 mBuf[mWritePos].ts = sec - mStartSecond;
2246 mBuf[mWritePos].tus = usec;
2247 #else
2248 struct timeval tv;
2249 gettimeofday(&tv, NULL);
2250 mBuf[mWritePos].ts = tv.tv_sec - mStartSecond;
2251 mBuf[mWritePos].tus = tv.tv_usec;
2252 #endif
2253
2254 JSString *jsfilename = findScript(filename);
2255 mBuf[mWritePos].filename = jsfilename;
2256 mBuf[mWritePos].lineno = lineno;
2257
2258 mWritePos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2259 if (mWritePos == mReadPos) {
2260 mReadPos = (mWritePos + 1) % ETHOGRAM_BUF_SIZE;
2261 }
2262 }
2263
2264 inline EthogramEvent *pop() {
2265 EthogramEvent *e = &mBuf[mReadPos];
2266 mReadPos = (mReadPos + 1) % ETHOGRAM_BUF_SIZE;
2267 return e;
2268 }
2269
2270 bool isEmpty() {
2271 return (mReadPos == mWritePos);
2272 }
2273
2274 EthogramScriptEntry *addScript(JSContext *cx, JSObject *obj, char *filename, JSString *jsfilename) {
2275 JSHashNumber hash = JS_HashString(filename);
2276 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2277 if (*hep != NULL)
2278 return JS_FALSE;
2279
2280 JS_HashTableRawAdd(traceVisScriptTable, hep, hash, filename, this);
2281
2282 EthogramScriptEntry * entry = (EthogramScriptEntry *) JS_malloc(cx, sizeof(EthogramScriptEntry));
2283 if (entry == NULL)
2284 return NULL;
2285
2286 entry->next = mScripts;
2287 mScripts = entry;
2288 entry->filename = filename;
2289 entry->jsfilename = jsfilename;
2290
2291 return mScripts;
2292 }
2293
2294 void removeScripts(JSContext *cx) {
2295 EthogramScriptEntry *se = mScripts;
2296 while (se != NULL) {
2297 char *filename = se->filename;
2298
2299 JSHashNumber hash = JS_HashString(filename);
2300 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, filename);
2301 JSHashEntry *he = *hep;
2302 if (he) {
2303 /* we hardly knew he */
2304 JS_HashTableRawRemove(traceVisScriptTable, hep, he);
2305 }
2306
2307 EthogramScriptEntry *se_head = se;
2308 se = se->next;
2309 JS_free(cx, se_head);
2310 }
2311 }
2312
2313 JSString *findScript(char *filename) {
2314 EthogramScriptEntry *se = mScripts;
2315 while (se != NULL) {
2316 if (compare_strings(se->filename, filename))
2317 return (se->jsfilename);
2318 se = se->next;
2319 }
2320 return NULL;
2321 }
2322
2323 JSObject *filenames() {
2324 return mFilenames;
2325 }
2326
2327 int length() {
2328 if (mWritePos < mReadPos)
2329 return (mWritePos + ETHOGRAM_BUF_SIZE) - mReadPos;
2330 else
2331 return mWritePos - mReadPos;
2332 }
2333 };
2334
2335 static char jstv_empty[] = "<null>";
2336
2337 inline char *
2338 jstv_Filename(JSStackFrame *fp)
2339 {
2340 while (fp && fp->script == NULL)
2341 fp = fp->down;
2342 return (fp && fp->script && fp->script->filename)
2343 ? (char *)fp->script->filename
2344 : jstv_empty;
2345 }
2346 inline uintN
2347 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
2348 {
2349 while (fp && fp->regs == NULL)
2350 fp = fp->down;
2351 return (fp && fp->regs) ? js_FramePCToLineNumber(cx, fp) : 0;
2352 }
2353
2354 /* Collect states here and distribute to a matching buffer, if any */
2355 JS_FRIEND_API(void)
2356 js_StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
2357 {
2358 JSStackFrame *fp = cx->fp;
2359
2360 char *script_file = jstv_Filename(fp);
2361 JSHashNumber hash = JS_HashString(script_file);
2362
2363 JSHashEntry **hep = JS_HashTableRawLookup(traceVisScriptTable, hash, script_file);
2364 /* update event buffer, flag if overflowed */
2365 JSHashEntry *he = *hep;
2366 if (he) {
2367 EthogramEventBuffer *p;
2368 p = (EthogramEventBuffer *) he->value;
2369
2370 p->push(s, r, script_file, jstv_Lineno(cx, fp));
2371 }
2372 }
2373
2374 static JSBool
2375 ethogram_construct(JSContext *cx, JSObject *obj,
2376 uintN argc, jsval *argv, jsval *rval)
2377 {
2378 EthogramEventBuffer *p;
2379
2380 p = (EthogramEventBuffer *) JS_malloc(cx, sizeof(EthogramEventBuffer));
2381
2382 p->mReadPos = p->mWritePos = 0;
2383 p->mScripts = NULL;
2384 p->mFilenames = JS_NewArrayObject(cx, 0, NULL);
2385
2386 #if defined(XP_WIN)
2387 FILETIME now;
2388 GetSystemTimeAsFileTime(&now);
2389 unsigned long long raw_us = 0.1 *
2390 (((unsigned long long) now.dwHighDateTime << 32ULL) |
2391 (unsigned long long) now.dwLowDateTime);
2392 unsigned int s = raw_us / 1000000L;
2393 p->mStartSecond = s;
2394 #else
2395 struct timeval tv;
2396 gettimeofday(&tv, NULL);
2397 p->mStartSecond = tv.tv_sec;
2398 #endif
2399 jsval filenames = OBJECT_TO_JSVAL(p->filenames());
2400 if (!JS_DefineProperty(cx, obj, "filenames", filenames,
2401 NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT))
2402 return JS_FALSE;
2403
2404 if (!JS_IsConstructing(cx)) {
2405 obj = JS_NewObject(cx, &ethogram_class, NULL, NULL);
2406 if (!obj)
2407 return JS_FALSE;
2408 *rval = OBJECT_TO_JSVAL(obj);
2409 }
2410 JS_SetPrivate(cx, obj, p);
2411 return JS_TRUE;
2412 }
2413
2414 static void
2415 ethogram_finalize(JSContext *cx, JSObject *obj)
2416 {
2417 EthogramEventBuffer *p;
2418 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, NULL);
2419 if (!p)
2420 return;
2421
2422 p->removeScripts(cx);
2423
2424 JS_free(cx, p);
2425 }
2426
2427 static JSBool
2428 ethogram_addScript(JSContext *cx, JSObject *obj,
2429 uintN argc, jsval *argv, jsval *rval)
2430 {
2431 JSString *str;
2432 char *filename = NULL;
2433 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
2434 str = JSVAL_TO_STRING(argv[0]);
2435 filename = js_DeflateString(cx,
2436 str->chars(),
2437 str->length());
2438 }
2439
2440 /* silently ignore no args */
2441 if (!filename)
2442 return JS_TRUE;
2443
2444 EthogramEventBuffer *p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2445
2446 p->addScript(cx, obj, filename, str);
2447 JS_CallFunctionName(cx, p->filenames(), "push", 1, argv, rval);
2448 return JS_TRUE;
2449 }
2450
2451 static JSBool
2452 ethogram_getAllEvents(JSContext *cx, JSObject *obj,
2453 uintN argc, jsval *argv, jsval *rval)
2454 {
2455 EthogramEventBuffer *p;
2456
2457 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2458 if (!p)
2459 return JS_FALSE;
2460
2461 if (p->isEmpty()) {
2462 *rval = JSVAL_NULL;
2463 return JS_TRUE;
2464 }
2465
2466 JSObject *rarray = JS_NewArrayObject(cx, 0, NULL);
2467 if (rarray == NULL) {
2468 *rval = JSVAL_NULL;
2469 return JS_TRUE;
2470 }
2471
2472 *rval = OBJECT_TO_JSVAL(rarray);
2473
2474 for (int i = 0; !p->isEmpty(); i++) {
2475
2476 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2477 if (x == NULL)
2478 return JS_FALSE;
2479
2480 EthogramEvent *e = p->pop();
2481
2482 jsval state = INT_TO_JSVAL(e->s);
2483 jsval reason = INT_TO_JSVAL(e->r);
2484 jsval ts = INT_TO_JSVAL(e->ts);
2485 jsval tus = INT_TO_JSVAL(e->tus);
2486
2487 jsval filename = STRING_TO_JSVAL(e->filename);
2488 jsval lineno = INT_TO_JSVAL(e->lineno);
2489
2490 if (!JS_SetProperty(cx, x, "state", &state))
2491 return JS_FALSE;
2492 if (!JS_SetProperty(cx, x, "reason", &reason))
2493 return JS_FALSE;
2494 if (!JS_SetProperty(cx, x, "ts", &ts))
2495 return JS_FALSE;
2496 if (!JS_SetProperty(cx, x, "tus", &tus))
2497 return JS_FALSE;
2498
2499 if (!JS_SetProperty(cx, x, "filename", &filename))
2500 return JS_FALSE;
2501 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2502 return JS_FALSE;
2503
2504 jsval element = OBJECT_TO_JSVAL(x);
2505 JS_SetElement(cx, rarray, i, &element);
2506 }
2507
2508 return JS_TRUE;
2509 }
2510
2511 static JSBool
2512 ethogram_getNextEvent(JSContext *cx, JSObject *obj,
2513 uintN argc, jsval *argv, jsval *rval)
2514 {
2515 EthogramEventBuffer *p;
2516
2517 p = (EthogramEventBuffer *) JS_GetInstancePrivate(cx, obj, &ethogram_class, argv);
2518 if (!p)
2519 return JS_FALSE;
2520
2521 JSObject *x = JS_NewObject(cx, NULL, NULL, NULL);
2522 if (x == NULL)
2523 return JS_FALSE;
2524
2525 if (p->isEmpty()) {
2526 *rval = JSVAL_NULL;
2527 return JS_TRUE;
2528 }
2529
2530 EthogramEvent *e = p->pop();
2531 jsval state = INT_TO_JSVAL(e->s);
2532 jsval reason = INT_TO_JSVAL(e->r);
2533 jsval ts = INT_TO_JSVAL(e->ts);
2534 jsval tus = INT_TO_JSVAL(e->tus);
2535
2536 jsval filename = STRING_TO_JSVAL(e->filename);
2537 jsval lineno = INT_TO_JSVAL(e->lineno);
2538
2539 if (!JS_SetProperty(cx, x, "state", &state))
2540 return JS_FALSE;
2541 if (!JS_SetProperty(cx, x, "reason", &reason))
2542 return JS_FALSE;
2543 if (!JS_SetProperty(cx, x, "ts", &ts))
2544 return JS_FALSE;
2545 if (!JS_SetProperty(cx, x, "tus", &tus))
2546 return JS_FALSE;
2547 if (!JS_SetProperty(cx, x, "filename", &filename))
2548 return JS_FALSE;
2549
2550 if (!JS_SetProperty(cx, x, "lineno", &lineno))
2551 return JS_FALSE;
2552
2553 *rval = OBJECT_TO_JSVAL(x);
2554
2555 return JS_TRUE;
2556 }
2557
2558 static JSFunctionSpec ethogram_methods[] = {
2559 {"addScript", ethogram_addScript, 1},
2560 {"getAllEvents", ethogram_getAllEvents, 0},
2561 {"getNextEvent", ethogram_getNextEvent, 0},
2562 {0}
2563 };
2564
2565 /*
2566 * An |Ethogram| organizes the output of a collection of files that should be
2567 * monitored together. A single object gets events for the group.
2568 */
2569 JS_FRIEND_API(JSBool)
2570 js_InitEthogram(JSContext *cx, JSObject *obj,
2571 uintN argc, jsval *argv, jsval *rval)
2572 {
2573 if (!traceVisScriptTable) {
2574 traceVisScriptTable = JS_NewHashTable(8, JS_HashString, compare_strings,
2575 NULL, NULL, NULL);
2576 }
2577
2578 JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, &ethogram_class,
2579 ethogram_construct, 0, NULL, ethogram_methods,
2580 NULL, NULL);
2581
2582 return JS_TRUE;
2583 }
2584
2585 JS_FRIEND_API(JSBool)
2586 js_ShutdownEthogram(JSContext *cx, JSObject *obj,
2587 uintN argc, jsval *argv, jsval *rval)
2588 {
2589 if (traceVisScriptTable)
2590 JS_HashTableDestroy(traceVisScriptTable);
2591
2592 return JS_TRUE;
2593 }
2594
2595 #endif /* MOZ_TRACEVIS */

  ViewVC Help
Powered by ViewVC 1.1.24