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

Contents of /trunk/js/jsdbgapi.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (10 years, 1 month ago) by siliconforks
File size: 57424 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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 "jsstddef.h"
45 #include <string.h>
46 #include "jstypes.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 "jsautooplen.h"
67
68 typedef struct JSTrap {
69 JSCList links;
70 JSScript *script;
71 jsbytecode *pc;
72 JSOp op;
73 JSTrapHandler handler;
74 void *closure;
75 } JSTrap;
76
77 #define DBG_LOCK(rt) JS_ACQUIRE_LOCK((rt)->debuggerLock)
78 #define DBG_UNLOCK(rt) JS_RELEASE_LOCK((rt)->debuggerLock)
79 #define DBG_LOCK_EVAL(rt,expr) (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
80
81 /*
82 * NB: FindTrap must be called with rt->debuggerLock acquired.
83 */
84 static JSTrap *
85 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
86 {
87 JSTrap *trap;
88
89 for (trap = (JSTrap *)rt->trapList.next;
90 &trap->links != &rt->trapList;
91 trap = (JSTrap *)trap->links.next) {
92 if (trap->script == script && trap->pc == pc)
93 return trap;
94 }
95 return NULL;
96 }
97
98 jsbytecode *
99 js_UntrapScriptCode(JSContext *cx, JSScript *script)
100 {
101 jsbytecode *code;
102 JSRuntime *rt;
103 JSTrap *trap;
104
105 code = script->code;
106 rt = cx->runtime;
107 DBG_LOCK(rt);
108 for (trap = (JSTrap *)rt->trapList.next;
109 &trap->links !=
110 &rt->trapList;
111 trap = (JSTrap *)trap->links.next) {
112 if (trap->script == script &&
113 (size_t)(trap->pc - script->code) < script->length) {
114 if (code == script->code) {
115 jssrcnote *sn, *notes;
116 size_t nbytes;
117
118 nbytes = script->length * sizeof(jsbytecode);
119 notes = SCRIPT_NOTES(script);
120 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
121 continue;
122 nbytes += (sn - notes + 1) * sizeof *sn;
123
124 code = (jsbytecode *) JS_malloc(cx, nbytes);
125 if (!code)
126 break;
127 memcpy(code, script->code, nbytes);
128 JS_PURGE_GSN_CACHE(cx);
129 }
130 code[trap->pc - script->code] = trap->op;
131 }
132 }
133 DBG_UNLOCK(rt);
134 return code;
135 }
136
137 JS_PUBLIC_API(JSBool)
138 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
139 JSTrapHandler handler, void *closure)
140 {
141 JSTrap *junk, *trap, *twin;
142 JSRuntime *rt;
143 uint32 sample;
144
145 JS_ASSERT((JSOp) *pc != JSOP_TRAP);
146 junk = NULL;
147 rt = cx->runtime;
148 DBG_LOCK(rt);
149 trap = FindTrap(rt, script, pc);
150 if (trap) {
151 JS_ASSERT(trap->script == script && trap->pc == pc);
152 JS_ASSERT(*pc == JSOP_TRAP);
153 } else {
154 sample = rt->debuggerMutations;
155 DBG_UNLOCK(rt);
156 trap = (JSTrap *) JS_malloc(cx, sizeof *trap);
157 if (!trap)
158 return JS_FALSE;
159 trap->closure = NULL;
160 if(!js_AddRoot(cx, &trap->closure, "trap->closure")) {
161 JS_free(cx, trap);
162 return JS_FALSE;
163 }
164 DBG_LOCK(rt);
165 twin = (rt->debuggerMutations != sample)
166 ? FindTrap(rt, script, pc)
167 : NULL;
168 if (twin) {
169 junk = trap;
170 trap = twin;
171 } else {
172 JS_APPEND_LINK(&trap->links, &rt->trapList);
173 ++rt->debuggerMutations;
174 trap->script = script;
175 trap->pc = pc;
176 trap->op = (JSOp)*pc;
177 *pc = JSOP_TRAP;
178 }
179 }
180 trap->handler = handler;
181 trap->closure = closure;
182 DBG_UNLOCK(rt);
183 if (junk) {
184 js_RemoveRoot(rt, &junk->closure);
185 JS_free(cx, junk);
186 }
187 return JS_TRUE;
188 }
189
190 JS_PUBLIC_API(JSOp)
191 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
192 {
193 JSRuntime *rt;
194 JSTrap *trap;
195 JSOp op;
196
197 rt = cx->runtime;
198 DBG_LOCK(rt);
199 trap = FindTrap(rt, script, pc);
200 op = trap ? trap->op : (JSOp) *pc;
201 DBG_UNLOCK(rt);
202 return op;
203 }
204
205 static void
206 DestroyTrapAndUnlock(JSContext *cx, JSTrap *trap)
207 {
208 ++cx->runtime->debuggerMutations;
209 JS_REMOVE_LINK(&trap->links);
210 *trap->pc = (jsbytecode)trap->op;
211 DBG_UNLOCK(cx->runtime);
212
213 js_RemoveRoot(cx->runtime, &trap->closure);
214 JS_free(cx, trap);
215 }
216
217 JS_PUBLIC_API(void)
218 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
219 JSTrapHandler *handlerp, void **closurep)
220 {
221 JSTrap *trap;
222
223 DBG_LOCK(cx->runtime);
224 trap = FindTrap(cx->runtime, script, pc);
225 if (handlerp)
226 *handlerp = trap ? trap->handler : NULL;
227 if (closurep)
228 *closurep = trap ? trap->closure : NULL;
229 if (trap)
230 DestroyTrapAndUnlock(cx, trap);
231 else
232 DBG_UNLOCK(cx->runtime);
233 }
234
235 JS_PUBLIC_API(void)
236 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
237 {
238 JSRuntime *rt;
239 JSTrap *trap, *next;
240 uint32 sample;
241
242 rt = cx->runtime;
243 DBG_LOCK(rt);
244 for (trap = (JSTrap *)rt->trapList.next;
245 &trap->links != &rt->trapList;
246 trap = next) {
247 next = (JSTrap *)trap->links.next;
248 if (trap->script == script) {
249 sample = rt->debuggerMutations;
250 DestroyTrapAndUnlock(cx, trap);
251 DBG_LOCK(rt);
252 if (rt->debuggerMutations != sample + 1)
253 next = (JSTrap *)rt->trapList.next;
254 }
255 }
256 DBG_UNLOCK(rt);
257 }
258
259 JS_PUBLIC_API(void)
260 JS_ClearAllTraps(JSContext *cx)
261 {
262 JSRuntime *rt;
263 JSTrap *trap, *next;
264 uint32 sample;
265
266 rt = cx->runtime;
267 DBG_LOCK(rt);
268 for (trap = (JSTrap *)rt->trapList.next;
269 &trap->links != &rt->trapList;
270 trap = next) {
271 next = (JSTrap *)trap->links.next;
272 sample = rt->debuggerMutations;
273 DestroyTrapAndUnlock(cx, trap);
274 DBG_LOCK(rt);
275 if (rt->debuggerMutations != sample + 1)
276 next = (JSTrap *)rt->trapList.next;
277 }
278 DBG_UNLOCK(rt);
279 }
280
281 JS_PUBLIC_API(JSTrapStatus)
282 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
283 {
284 JSTrap *trap;
285 jsint op;
286 JSTrapStatus status;
287
288 DBG_LOCK(cx->runtime);
289 trap = FindTrap(cx->runtime, script, pc);
290 JS_ASSERT(!trap || trap->handler);
291 if (!trap) {
292 op = (JSOp) *pc;
293 DBG_UNLOCK(cx->runtime);
294
295 /* Defend against "pc for wrong script" API usage error. */
296 JS_ASSERT(op != JSOP_TRAP);
297
298 #ifdef JS_THREADSAFE
299 /* If the API was abused, we must fail for want of the real op. */
300 if (op == JSOP_TRAP)
301 return JSTRAP_ERROR;
302
303 /* Assume a race with a debugger thread and try to carry on. */
304 *rval = INT_TO_JSVAL(op);
305 return JSTRAP_CONTINUE;
306 #else
307 /* Always fail if single-threaded (must be an API usage error). */
308 return JSTRAP_ERROR;
309 #endif
310 }
311 DBG_UNLOCK(cx->runtime);
312
313 /*
314 * It's important that we not use 'trap->' after calling the callback --
315 * the callback might remove the trap!
316 */
317 op = (jsint)trap->op;
318 status = trap->handler(cx, script, pc, rval, trap->closure);
319 if (status == JSTRAP_CONTINUE) {
320 /* By convention, return the true op to the interpreter in rval. */
321 *rval = INT_TO_JSVAL(op);
322 }
323 return status;
324 }
325
326 JS_PUBLIC_API(JSBool)
327 JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
328 {
329 rt->globalDebugHooks.interruptHandler = handler;
330 rt->globalDebugHooks.interruptHandlerData = closure;
331 return JS_TRUE;
332 }
333
334 JS_PUBLIC_API(JSBool)
335 JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
336 {
337 if (handlerp)
338 *handlerp = (JSTrapHandler)rt->globalDebugHooks.interruptHandler;
339 if (closurep)
340 *closurep = rt->globalDebugHooks.interruptHandlerData;
341 rt->globalDebugHooks.interruptHandler = 0;
342 rt->globalDebugHooks.interruptHandlerData = 0;
343 return JS_TRUE;
344 }
345
346 /************************************************************************/
347
348 typedef struct JSWatchPoint {
349 JSCList links;
350 JSObject *object; /* weak link, see js_FinalizeObject */
351 JSScopeProperty *sprop;
352 JSPropertyOp setter;
353 JSWatchPointHandler handler;
354 void *closure;
355 uintN flags;
356 } JSWatchPoint;
357
358 #define JSWP_LIVE 0x1 /* live because set and not cleared */
359 #define JSWP_HELD 0x2 /* held while running handler/setter */
360
361 /*
362 * NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
363 */
364 static JSBool
365 DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
366 {
367 JSBool ok, found;
368 JSScopeProperty *sprop;
369 JSScope *scope;
370 JSPropertyOp setter;
371
372 ok = JS_TRUE;
373 wp->flags &= ~flag;
374 if (wp->flags != 0) {
375 DBG_UNLOCK(cx->runtime);
376 return ok;
377 }
378
379 /*
380 * Remove wp from the list, then if there are no other watchpoints for
381 * wp->sprop in any scope, restore wp->sprop->setter from wp.
382 */
383 ++cx->runtime->debuggerMutations;
384 JS_REMOVE_LINK(&wp->links);
385 sprop = wp->sprop;
386
387 /*
388 * Passing null for the scope parameter tells js_GetWatchedSetter to find
389 * any watch point for sprop, and not to lock or unlock rt->debuggerLock.
390 * If js_ChangeNativePropertyAttrs fails, propagate failure after removing
391 * wp->closure's root and freeing wp.
392 */
393 setter = js_GetWatchedSetter(cx->runtime, NULL, sprop);
394 DBG_UNLOCK(cx->runtime);
395 if (!setter) {
396 JS_LOCK_OBJ(cx, wp->object);
397 scope = OBJ_SCOPE(wp->object);
398 found = (scope->object == wp->object &&
399 SCOPE_GET_PROPERTY(scope, sprop->id));
400 JS_UNLOCK_SCOPE(cx, scope);
401
402 /*
403 * If the property wasn't found on wp->object or didn't exist, then
404 * someone else has dealt with this sprop, and we don't need to change
405 * the property attributes.
406 */
407 if (found) {
408 sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop,
409 0, sprop->attrs,
410 sprop->getter,
411 wp->setter);
412 if (!sprop)
413 ok = JS_FALSE;
414 }
415 }
416
417 JS_free(cx, wp);
418 return ok;
419 }
420
421 /*
422 * NB: js_TraceWatchPoints does not acquire cx->runtime->debuggerLock, since
423 * the debugger should never be racing with the GC (i.e., the debugger must
424 * respect the request model).
425 */
426 void
427 js_TraceWatchPoints(JSTracer *trc, JSObject *obj)
428 {
429 JSRuntime *rt;
430 JSWatchPoint *wp;
431
432 rt = trc->context->runtime;
433
434 for (wp = (JSWatchPoint *)rt->watchPointList.next;
435 &wp->links != &rt->watchPointList;
436 wp = (JSWatchPoint *)wp->links.next) {
437 if (wp->object == obj) {
438 TRACE_SCOPE_PROPERTY(trc, wp->sprop);
439 if ((wp->sprop->attrs & JSPROP_SETTER) && wp->setter) {
440 JS_CALL_OBJECT_TRACER(trc, js_CastAsObject(wp->setter),
441 "wp->setter");
442 }
443 JS_SET_TRACING_NAME(trc, "wp->closure");
444 js_CallValueTracerIfGCThing(trc, (jsval) wp->closure);
445 }
446 }
447 }
448
449 void
450 js_SweepWatchPoints(JSContext *cx)
451 {
452 JSRuntime *rt;
453 JSWatchPoint *wp, *next;
454 uint32 sample;
455
456 rt = cx->runtime;
457 DBG_LOCK(rt);
458 for (wp = (JSWatchPoint *)rt->watchPointList.next;
459 &wp->links != &rt->watchPointList;
460 wp = next) {
461 next = (JSWatchPoint *)wp->links.next;
462 if (js_IsAboutToBeFinalized(cx, wp->object)) {
463 sample = rt->debuggerMutations;
464
465 /* Ignore failures. */
466 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
467 DBG_LOCK(rt);
468 if (rt->debuggerMutations != sample + 1)
469 next = (JSWatchPoint *)rt->watchPointList.next;
470 }
471 }
472 DBG_UNLOCK(rt);
473 }
474
475
476
477 /*
478 * NB: FindWatchPoint must be called with rt->debuggerLock acquired.
479 */
480 static JSWatchPoint *
481 FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
482 {
483 JSWatchPoint *wp;
484
485 for (wp = (JSWatchPoint *)rt->watchPointList.next;
486 &wp->links != &rt->watchPointList;
487 wp = (JSWatchPoint *)wp->links.next) {
488 if (wp->object == scope->object && wp->sprop->id == id)
489 return wp;
490 }
491 return NULL;
492 }
493
494 JSScopeProperty *
495 js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
496 {
497 JSWatchPoint *wp;
498 JSScopeProperty *sprop;
499
500 DBG_LOCK(rt);
501 wp = FindWatchPoint(rt, scope, id);
502 sprop = wp ? wp->sprop : NULL;
503 DBG_UNLOCK(rt);
504 return sprop;
505 }
506
507 /*
508 * Secret handshake with DropWatchPointAndUnlock: if (!scope), we know our
509 * caller has acquired rt->debuggerLock, so we don't have to.
510 */
511 JSPropertyOp
512 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
513 const JSScopeProperty *sprop)
514 {
515 JSPropertyOp setter;
516 JSWatchPoint *wp;
517
518 setter = NULL;
519 if (scope)
520 DBG_LOCK(rt);
521 for (wp = (JSWatchPoint *)rt->watchPointList.next;
522 &wp->links != &rt->watchPointList;
523 wp = (JSWatchPoint *)wp->links.next) {
524 if ((!scope || wp->object == scope->object) && wp->sprop == sprop) {
525 setter = wp->setter;
526 break;
527 }
528 }
529 if (scope)
530 DBG_UNLOCK(rt);
531 return setter;
532 }
533
534 JSBool
535 js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
536 {
537 JSRuntime *rt;
538 JSWatchPoint *wp;
539 JSScopeProperty *sprop;
540 jsval propid, userid;
541 JSScope *scope;
542 JSBool ok;
543
544 rt = cx->runtime;
545 DBG_LOCK(rt);
546 for (wp = (JSWatchPoint *)rt->watchPointList.next;
547 &wp->links != &rt->watchPointList;
548 wp = (JSWatchPoint *)wp->links.next) {
549 sprop = wp->sprop;
550 if (wp->object == obj && SPROP_USERID(sprop) == id &&
551 !(wp->flags & JSWP_HELD)) {
552 wp->flags |= JSWP_HELD;
553 DBG_UNLOCK(rt);
554
555 JS_LOCK_OBJ(cx, obj);
556 propid = ID_TO_VALUE(sprop->id);
557 userid = (sprop->flags & SPROP_HAS_SHORTID)
558 ? INT_TO_JSVAL(sprop->shortid)
559 : propid;
560 scope = OBJ_SCOPE(obj);
561 JS_UNLOCK_OBJ(cx, obj);
562
563 /* NB: wp is held, so we can safely dereference it still. */
564 ok = wp->handler(cx, obj, propid,
565 SPROP_HAS_VALID_SLOT(sprop, scope)
566 ? OBJ_GET_SLOT(cx, obj, sprop->slot)
567 : JSVAL_VOID,
568 vp, wp->closure);
569 if (ok) {
570 /*
571 * Create a pseudo-frame for the setter invocation so that any
572 * stack-walking security code under the setter will correctly
573 * identify the guilty party. So that the watcher appears to
574 * be active to obj_eval and other such code, point frame.pc
575 * at the JSOP_STOP at the end of the script.
576 *
577 * The pseudo-frame is not created for fast natives as they
578 * are treated as interpreter frame extensions and always
579 * trusted.
580 */
581 JSObject *closure;
582 JSClass *clasp;
583 JSFunction *fun;
584 JSScript *script;
585 JSBool injectFrame;
586 uintN nslots, slotsStart;
587 jsval smallv[5];
588 jsval *argv;
589 JSStackFrame frame;
590 JSFrameRegs regs;
591
592 closure = (JSObject *) wp->closure;
593 clasp = OBJ_GET_CLASS(cx, closure);
594 if (clasp == &js_FunctionClass) {
595 fun = GET_FUNCTION_PRIVATE(cx, closure);
596 script = FUN_SCRIPT(fun);
597 } else if (clasp == &js_ScriptClass) {
598 fun = NULL;
599 script = (JSScript *) JS_GetPrivate(cx, closure);
600 } else {
601 fun = NULL;
602 script = NULL;
603 }
604
605 slotsStart = nslots = 2;
606 injectFrame = JS_TRUE;
607 if (fun) {
608 nslots += FUN_MINARGS(fun);
609 if (!FUN_INTERPRETED(fun)) {
610 nslots += fun->u.n.extra;
611 injectFrame = !(fun->flags & JSFUN_FAST_NATIVE);
612 }
613
614 slotsStart = nslots;
615 }
616 if (script)
617 nslots += script->nslots;
618
619 if (injectFrame) {
620 if (nslots <= JS_ARRAY_LENGTH(smallv)) {
621 argv = smallv;
622 } else {
623 argv = (jsval *) JS_malloc(cx, nslots * sizeof(jsval));
624 if (!argv) {
625 DBG_LOCK(rt);
626 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
627 return JS_FALSE;
628 }
629 }
630
631 argv[0] = OBJECT_TO_JSVAL(closure);
632 argv[1] = JSVAL_NULL;
633 memset(argv + 2, 0, (nslots - 2) * sizeof(jsval));
634
635 memset(&frame, 0, sizeof(frame));
636 frame.script = script;
637 frame.regs = NULL;
638 frame.callee = closure;
639 frame.fun = fun;
640 frame.argv = argv + 2;
641 frame.down = js_GetTopStackFrame(cx);
642 frame.scopeChain = OBJ_GET_PARENT(cx, closure);
643 if (script && script->nslots)
644 frame.slots = argv + slotsStart;
645 if (script) {
646 JS_ASSERT(script->length >= JSOP_STOP_LENGTH);
647 regs.pc = script->code + script->length
648 - JSOP_STOP_LENGTH;
649 regs.sp = NULL;
650 frame.regs = &regs;
651 if (fun &&
652 JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
653 !js_GetCallObject(cx, &frame)) {
654 if (argv != smallv)
655 JS_free(cx, argv);
656 DBG_LOCK(rt);
657 DropWatchPointAndUnlock(cx, wp, JSWP_HELD);
658 return JS_FALSE;
659 }
660 }
661
662 cx->fp = &frame;
663 }
664 #ifdef __GNUC__
665 else
666 argv = NULL; /* suppress bogus gcc warnings */
667 #endif
668 ok = !wp->setter ||
669 ((sprop->attrs & JSPROP_SETTER)
670 ? js_InternalCall(cx, obj,
671 js_CastAsObjectJSVal(wp->setter),
672 1, vp, vp)
673 : wp->setter(cx, obj, userid, vp));
674 if (injectFrame) {
675 /* Evil code can cause us to have an arguments object. */
676 if (frame.callobj)
677 ok &= js_PutCallObject(cx, &frame);
678 if (frame.argsobj)
679 ok &= js_PutArgsObject(cx, &frame);
680
681 cx->fp = frame.down;
682 if (argv != smallv)
683 JS_free(cx, argv);
684 }
685 }
686 DBG_LOCK(rt);
687 return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
688 }
689 }
690 DBG_UNLOCK(rt);
691 return JS_TRUE;
692 }
693
694 JS_REQUIRES_STACK JSBool
695 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
696 jsval *rval)
697 {
698 JSObject *funobj;
699 JSFunction *wrapper;
700 jsval userid;
701
702 funobj = JSVAL_TO_OBJECT(argv[-2]);
703 wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
704 userid = ATOM_KEY(wrapper->atom);
705 *rval = argv[0];
706 return js_watch_set(cx, obj, userid, rval);
707 }
708
709 JSPropertyOp
710 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
711 {
712 JSAtom *atom;
713 JSFunction *wrapper;
714
715 if (!(attrs & JSPROP_SETTER))
716 return &js_watch_set; /* & to silence schoolmarmish MSVC */
717
718 if (JSID_IS_ATOM(id)) {
719 atom = JSID_TO_ATOM(id);
720 } else if (JSID_IS_INT(id)) {
721 if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
722 return NULL;
723 atom = JSID_TO_ATOM(id);
724 } else {
725 atom = NULL;
726 }
727 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
728 OBJ_GET_PARENT(cx, js_CastAsObject(setter)),
729 atom);
730 if (!wrapper)
731 return NULL;
732 return js_CastAsPropertyOp(FUN_OBJECT(wrapper));
733 }
734
735 JS_PUBLIC_API(JSBool)
736 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
737 JSWatchPointHandler handler, void *closure)
738 {
739 jsid propid;
740 JSObject *pobj;
741 JSProperty *prop;
742 JSScopeProperty *sprop;
743 JSRuntime *rt;
744 JSBool ok;
745 JSWatchPoint *wp;
746 JSPropertyOp watcher;
747
748 if (!OBJ_IS_NATIVE(obj)) {
749 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
750 OBJ_GET_CLASS(cx, obj)->name);
751 return JS_FALSE;
752 }
753
754 if (JSVAL_IS_INT(idval)) {
755 propid = INT_JSVAL_TO_JSID(idval);
756 } else {
757 if (!js_ValueToStringId(cx, idval, &propid))
758 return JS_FALSE;
759 CHECK_FOR_STRING_INDEX(propid);
760 }
761
762 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
763 return JS_FALSE;
764 sprop = (JSScopeProperty *) prop;
765 rt = cx->runtime;
766 if (!sprop) {
767 /* Check for a deleted symbol watchpoint, which holds its property. */
768 sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
769 if (!sprop) {
770 /* Make a new property in obj so we can watch for the first set. */
771 if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID,
772 NULL, NULL, JSPROP_ENUMERATE,
773 &prop)) {
774 return JS_FALSE;
775 }
776 sprop = (JSScopeProperty *) prop;
777 }
778 } else if (pobj != obj) {
779 /* Clone the prototype property so we can watch the right object. */
780 jsval value;
781 JSPropertyOp getter, setter;
782 uintN attrs, flags;
783 intN shortid;
784
785 if (OBJ_IS_NATIVE(pobj)) {
786 value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
787 ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
788 : JSVAL_VOID;
789 getter = sprop->getter;
790 setter = sprop->setter;
791 attrs = sprop->attrs;
792 flags = sprop->flags;
793 shortid = sprop->shortid;
794 } else {
795 if (!OBJ_GET_PROPERTY(cx, pobj, propid, &value) ||
796 !OBJ_GET_ATTRIBUTES(cx, pobj, propid, prop, &attrs)) {
797 OBJ_DROP_PROPERTY(cx, pobj, prop);
798 return JS_FALSE;
799 }
800 getter = setter = NULL;
801 flags = 0;
802 shortid = 0;
803 }
804 OBJ_DROP_PROPERTY(cx, pobj, prop);
805
806 /* Recall that obj is native, whether or not pobj is native. */
807 if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter,
808 attrs, flags, shortid, &prop)) {
809 return JS_FALSE;
810 }
811 sprop = (JSScopeProperty *) prop;
812 }
813
814 /*
815 * At this point, prop/sprop exists in obj, obj is locked, and we must
816 * OBJ_DROP_PROPERTY(cx, obj, prop) before returning.
817 */
818 ok = JS_TRUE;
819 DBG_LOCK(rt);
820 wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
821 if (!wp) {
822 DBG_UNLOCK(rt);
823 watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
824 if (!watcher) {
825 ok = JS_FALSE;
826 goto out;
827 }
828
829 wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);
830 if (!wp) {
831 ok = JS_FALSE;
832 goto out;
833 }
834 wp->handler = NULL;
835 wp->closure = NULL;
836 wp->object = obj;
837 JS_ASSERT(sprop->setter != js_watch_set || pobj != obj);
838 wp->setter = sprop->setter;
839 wp->flags = JSWP_LIVE;
840
841 /* XXXbe nest in obj lock here */
842 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
843 sprop->getter, watcher);
844 if (!sprop) {
845 /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
846 JS_INIT_CLIST(&wp->links);
847 DBG_LOCK(rt);
848 DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
849 ok = JS_FALSE;
850 goto out;
851 }
852 wp->sprop = sprop;
853
854 /*
855 * Now that wp is fully initialized, append it to rt's wp list.
856 * Because obj is locked we know that no other thread could have added
857 * a watchpoint for (obj, propid).
858 */
859 DBG_LOCK(rt);
860 JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid));
861 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
862 ++rt->debuggerMutations;
863 }
864 wp->handler = handler;
865 wp->closure = closure;
866 DBG_UNLOCK(rt);
867
868 out:
869 OBJ_DROP_PROPERTY(cx, obj, prop);
870 return ok;
871 }
872
873 JS_PUBLIC_API(JSBool)
874 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
875 JSWatchPointHandler *handlerp, void **closurep)
876 {
877 JSRuntime *rt;
878 JSWatchPoint *wp;
879
880 rt = cx->runtime;
881 DBG_LOCK(rt);
882 for (wp = (JSWatchPoint *)rt->watchPointList.next;
883 &wp->links != &rt->watchPointList;
884 wp = (JSWatchPoint *)wp->links.next) {
885 if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
886 if (handlerp)
887 *handlerp = wp->handler;
888 if (closurep)
889 *closurep = wp->closure;
890 return DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
891 }
892 }
893 DBG_UNLOCK(rt);
894 if (handlerp)
895 *handlerp = NULL;
896 if (closurep)
897 *closurep = NULL;
898 return JS_TRUE;
899 }
900
901 JS_PUBLIC_API(JSBool)
902 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
903 {
904 JSRuntime *rt;
905 JSWatchPoint *wp, *next;
906 uint32 sample;
907
908 rt = cx->runtime;
909 DBG_LOCK(rt);
910 for (wp = (JSWatchPoint *)rt->watchPointList.next;
911 &wp->links != &rt->watchPointList;
912 wp = next) {
913 next = (JSWatchPoint *)wp->links.next;
914 if (wp->object == obj) {
915 sample = rt->debuggerMutations;
916 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
917 return JS_FALSE;
918 DBG_LOCK(rt);
919 if (rt->debuggerMutations != sample + 1)
920 next = (JSWatchPoint *)rt->watchPointList.next;
921 }
922 }
923 DBG_UNLOCK(rt);
924 return JS_TRUE;
925 }
926
927 JS_PUBLIC_API(JSBool)
928 JS_ClearAllWatchPoints(JSContext *cx)
929 {
930 JSRuntime *rt;
931 JSWatchPoint *wp, *next;
932 uint32 sample;
933
934 rt = cx->runtime;
935 DBG_LOCK(rt);
936 for (wp = (JSWatchPoint *)rt->watchPointList.next;
937 &wp->links != &rt->watchPointList;
938 wp = next) {
939 next = (JSWatchPoint *)wp->links.next;
940 sample = rt->debuggerMutations;
941 if (!DropWatchPointAndUnlock(cx, wp, JSWP_LIVE))
942 return JS_FALSE;
943 DBG_LOCK(rt);
944 if (rt->debuggerMutations != sample + 1)
945 next = (JSWatchPoint *)rt->watchPointList.next;
946 }
947 DBG_UNLOCK(rt);
948 return JS_TRUE;
949 }
950
951 /************************************************************************/
952
953 JS_PUBLIC_API(uintN)
954 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
955 {
956 return js_PCToLineNumber(cx, script, pc);
957 }
958
959 JS_PUBLIC_API(jsbytecode *)
960 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
961 {
962 return js_LineNumberToPC(script, lineno);
963 }
964
965 JS_PUBLIC_API(JSScript *)
966 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
967 {
968 return FUN_SCRIPT(fun);
969 }
970
971 JS_PUBLIC_API(JSNative)
972 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
973 {
974 return FUN_NATIVE(fun);
975 }
976
977 JS_PUBLIC_API(JSFastNative)
978 JS_GetFunctionFastNative(JSContext *cx, JSFunction *fun)
979 {
980 return FUN_FAST_NATIVE(fun);
981 }
982
983 JS_PUBLIC_API(JSPrincipals *)
984 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
985 {
986 return script->principals;
987 }
988
989 /************************************************************************/
990
991 /*
992 * Stack Frame Iterator
993 */
994 JS_PUBLIC_API(JSStackFrame *)
995 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
996 {
997 *iteratorp = (*iteratorp == NULL) ? js_GetTopStackFrame(cx) : (*iteratorp)->down;
998 return *iteratorp;
999 }
1000
1001 JS_PUBLIC_API(JSScript *)
1002 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
1003 {
1004 return fp->script;
1005 }
1006
1007 JS_PUBLIC_API(jsbytecode *)
1008 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
1009 {
1010 return fp->regs ? fp->regs->pc : NULL;
1011 }
1012
1013 JS_PUBLIC_API(JSStackFrame *)
1014 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
1015 {
1016 return js_GetScriptedCaller(cx, fp);
1017 }
1018
1019 JS_PUBLIC_API(JSPrincipals *)
1020 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
1021 {
1022 JSSecurityCallbacks *callbacks;
1023
1024 if (fp->fun) {
1025 callbacks = JS_GetSecurityCallbacks(cx);
1026 if (callbacks && callbacks->findObjectPrincipals) {
1027 if (FUN_OBJECT(fp->fun) != fp->callee)
1028 return callbacks->findObjectPrincipals(cx, fp->callee);
1029 /* FALL THROUGH */
1030 }
1031 }
1032 if (fp->script)
1033 return fp->script->principals;
1034 return NULL;
1035 }
1036
1037 JS_PUBLIC_API(JSPrincipals *)
1038 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
1039 {
1040 JSPrincipals *principals, *callerPrincipals;
1041 JSSecurityCallbacks *callbacks;
1042
1043 callbacks = JS_GetSecurityCallbacks(cx);
1044 if (callbacks && callbacks->findObjectPrincipals) {
1045 principals = callbacks->findObjectPrincipals(cx, fp->callee);
1046 } else {
1047 principals = NULL;
1048 }
1049 if (!caller)
1050 return principals;
1051 callerPrincipals = JS_StackFramePrincipals(cx, caller);
1052 return (callerPrincipals && principals &&
1053 callerPrincipals->subsume(callerPrincipals, principals))
1054 ? principals
1055 : callerPrincipals;
1056 }
1057
1058 JS_PUBLIC_API(void *)
1059 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
1060 {
1061 if (fp->annotation && fp->script) {
1062 JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
1063
1064 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
1065 /*
1066 * Give out an annotation only if privileges have not been revoked
1067 * or disabled globally.
1068 */
1069 return fp->annotation;
1070 }
1071 }
1072
1073 return NULL;
1074 }
1075
1076 JS_PUBLIC_API(void)
1077 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
1078 {
1079 fp->annotation = annotation;
1080 }
1081
1082 JS_PUBLIC_API(void *)
1083 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
1084 {
1085 JSPrincipals *principals;
1086
1087 principals = JS_StackFramePrincipals(cx, fp);
1088 if (!principals)
1089 return NULL;
1090 return principals->getPrincipalArray(cx, principals);
1091 }
1092
1093 JS_PUBLIC_API(JSBool)
1094 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
1095 {
1096 return !fp->script;
1097 }
1098
1099 /* this is deprecated, use JS_GetFrameScopeChain instead */
1100 JS_PUBLIC_API(JSObject *)
1101 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
1102 {
1103 return fp->scopeChain;
1104 }
1105
1106 JS_PUBLIC_API(JSObject *)
1107 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
1108 {
1109 /* Force creation of argument and call objects if not yet created */
1110 (void) JS_GetFrameCallObject(cx, fp);
1111 return js_GetScopeChain(cx, fp);
1112 }
1113
1114 JS_PUBLIC_API(JSObject *)
1115 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
1116 {
1117 if (! fp->fun)
1118 return NULL;
1119
1120 /* Force creation of argument object if not yet created */
1121 (void) js_GetArgsObject(cx, fp);
1122
1123 /*
1124 * XXX ill-defined: null return here means error was reported, unlike a
1125 * null returned above or in the #else
1126 */
1127 return js_GetCallObject(cx, fp);
1128 }
1129
1130 JS_PUBLIC_API(JSObject *)
1131 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
1132 {
1133 JSStackFrame *afp;
1134
1135 if (fp->flags & JSFRAME_COMPUTED_THIS)
1136 return fp->thisp;
1137
1138 /* js_ComputeThis gets confused if fp != cx->fp, so set it aside. */
1139 if (js_GetTopStackFrame(cx) != fp) {
1140 afp = cx->fp;
1141 if (afp) {
1142 afp->dormantNext = cx->dormantFrameChain;
1143 cx->dormantFrameChain = afp;
1144 cx->fp = fp;
1145 }
1146 } else {
1147 afp = NULL;
1148 }
1149
1150 if (!fp->thisp && fp->argv)
1151 fp->thisp = js_ComputeThis(cx, JS_TRUE, fp->argv);
1152
1153 if (afp) {
1154 cx->fp = afp;
1155 cx->dormantFrameChain = afp->dormantNext;
1156 afp->dormantNext = NULL;
1157 }
1158
1159 return fp->thisp;
1160 }
1161
1162 JS_PUBLIC_API(JSFunction *)
1163 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
1164 {
1165 return fp->fun;
1166 }
1167
1168 JS_PUBLIC_API(JSObject *)
1169 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
1170 {
1171 if (!fp->fun)
1172 return NULL;
1173
1174 JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee));
1175 JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->callee) == fp->fun);
1176 return fp->callee;
1177 }
1178
1179 JS_PUBLIC_API(JSBool)
1180 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
1181 {
1182 return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
1183 }
1184
1185 JS_PUBLIC_API(JSObject *)
1186 JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp)
1187 {
1188 return fp->callee;
1189 }
1190
1191 JS_PUBLIC_API(JSBool)
1192 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
1193 {
1194 return (fp->flags & JSFRAME_DEBUGGER) != 0;
1195 }
1196
1197 JS_PUBLIC_API(jsval)
1198 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
1199 {
1200 return fp->rval;
1201 }
1202
1203 JS_PUBLIC_API(void)
1204 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
1205 {
1206 fp->rval = rval;
1207 }
1208
1209 /************************************************************************/
1210
1211 JS_PUBLIC_API(const char *)
1212 JS_GetScriptFilename(JSContext *cx, JSScript *script)
1213 {
1214 return script->filename;
1215 }
1216
1217 JS_PUBLIC_API(uintN)
1218 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
1219 {
1220 return script->lineno;
1221 }
1222
1223 JS_PUBLIC_API(uintN)
1224 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
1225 {
1226 return js_GetScriptLineExtent(script);
1227 }
1228
1229 JS_PUBLIC_API(JSVersion)
1230 JS_GetScriptVersion(JSContext *cx, JSScript *script)
1231 {
1232 return (JSVersion) (script->version & JSVERSION_MASK);
1233 }
1234
1235 /***************************************************************************/
1236
1237 JS_PUBLIC_API(void)
1238 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
1239 {
1240 rt->globalDebugHooks.newScriptHook = hook;
1241 rt->globalDebugHooks.newScriptHookData = callerdata;
1242 }
1243
1244 JS_PUBLIC_API(void)
1245 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
1246 void *callerdata)
1247 {
1248 rt->globalDebugHooks.destroyScriptHook = hook;
1249 rt->globalDebugHooks.destroyScriptHookData = callerdata;
1250 }
1251
1252 /***************************************************************************/
1253
1254 JS_PUBLIC_API(JSBool)
1255 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
1256 const jschar *chars, uintN length,
1257 const char *filename, uintN lineno,
1258 jsval *rval)
1259 {
1260 JS_ASSERT_NOT_ON_TRACE(cx);
1261
1262 JSObject *scobj;
1263 JSScript *script;
1264 JSBool ok;
1265
1266 scobj = JS_GetFrameScopeChain(cx, fp);
1267 if (!scobj)
1268 return JS_FALSE;
1269
1270 /*
1271 * NB: This function breaks the assumption that the compiler can see all
1272 * calls and properly compute a static level. In order to get around this,
1273 * we use a static level that will cause us not to attempt to optimize
1274 * variable references made by this frame.
1275 */
1276 script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
1277 TCF_COMPILE_N_GO |
1278 TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE),
1279 chars, length, NULL,
1280 filename, lineno);
1281
1282 if (!script)
1283 return JS_FALSE;
1284
1285 JSStackFrame *displayCopy[JS_DISPLAY_SIZE];
1286 if (cx->fp != fp) {
1287 memcpy(displayCopy, cx->display, sizeof displayCopy);
1288
1289 /*
1290 * Set up cx->display as it would have been when fp was active.
1291 *
1292 * NB: To reconstruct cx->display for fp, we have to follow the frame
1293 * chain from oldest to youngest, in the opposite direction to its
1294 * single linkge. To avoid the obvious recursive reversal algorithm,
1295 * which might use too much stack, we reverse in place and reverse
1296 * again as we reconstruct the display. This is safe because cx is
1297 * thread-local and we can't cause GC until the call to js_Execute
1298 * below.
1299 */
1300 JSStackFrame *fp2 = fp, *last = NULL;
1301 while (fp2) {
1302 JSStackFrame *next = fp2->down;
1303 fp2->down = last;
1304 last = fp2;
1305 fp2 = next;
1306 }
1307
1308 fp2 = last;
1309 last = NULL;
1310 while (fp2) {
1311 JSStackFrame *next = fp2->down;
1312 fp2->down = last;
1313 last = fp2;
1314
1315 JSScript *script = fp2->script;
1316 if (script && script->staticLevel < JS_DISPLAY_SIZE)
1317 cx->display[script->staticLevel] = fp2;
1318 fp2 = next;
1319 }
1320 }
1321
1322 ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
1323 rval);
1324
1325 if (cx->fp != fp)
1326 memcpy(cx->display, displayCopy, sizeof cx->display);
1327 js_DestroyScript(cx, script);
1328 return ok;
1329 }
1330
1331 JS_PUBLIC_API(JSBool)
1332 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
1333 const char *bytes, uintN length,
1334 const char *filename, uintN lineno,
1335 jsval *rval)
1336 {
1337 jschar *chars;
1338 JSBool ok;
1339 size_t len = length;
1340
1341 chars = js_InflateString(cx, bytes, &len);
1342 if (!chars)
1343 return JS_FALSE;
1344 length = (uintN) len;
1345 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
1346 rval);
1347 JS_free(cx, chars);
1348
1349 return ok;
1350 }
1351
1352 /************************************************************************/
1353
1354 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
1355
1356 JS_PUBLIC_API(JSScopeProperty *)
1357 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
1358 {
1359 JSScopeProperty *sprop;
1360 JSScope *scope;
1361
1362 sprop = *iteratorp;
1363 scope = OBJ_SCOPE(obj);
1364
1365 /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
1366 if (!sprop) {
1367 sprop = SCOPE_LAST_PROP(scope);
1368 } else {
1369 while ((sprop = sprop->parent) != NULL) {
1370 if (!SCOPE_HAD_MIDDLE_DELETE(scope))
1371 break;
1372 if (SCOPE_HAS_PROPERTY(scope, sprop))
1373 break;
1374 }
1375 }
1376 *iteratorp = sprop;
1377 return sprop;
1378 }
1379
1380 JS_PUBLIC_API(JSBool)
1381 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
1382 JSPropertyDesc *pd)
1383 {
1384 JSScope *scope;
1385 JSScopeProperty *aprop;
1386 jsval lastException;
1387 JSBool wasThrowing;
1388
1389 pd->id = ID_TO_VALUE(sprop->id);
1390
1391 wasThrowing = cx->throwing;
1392 if (wasThrowing) {
1393 lastException = cx->exception;
1394 if (JSVAL_IS_GCTHING(lastException) &&
1395 !js_AddRoot(cx, &lastException, "lastException")) {
1396 return JS_FALSE;
1397 }
1398 cx->throwing = JS_FALSE;
1399 }
1400
1401 if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) {
1402 if (!cx->throwing) {
1403 pd->flags = JSPD_ERROR;
1404 pd->value = JSVAL_VOID;
1405 } else {
1406 pd->flags = JSPD_EXCEPTION;
1407 pd->value = cx->exception;
1408 }
1409 } else {
1410 pd->flags = 0;
1411 }
1412
1413 cx->throwing = wasThrowing;
1414 if (wasThrowing) {
1415 cx->exception = lastException;
1416 if (JSVAL_IS_GCTHING(lastException))
1417 js_RemoveRoot(cx->runtime, &lastException);
1418 }
1419
1420 pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
1421 | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0)
1422 | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0);
1423 pd->spare = 0;
1424 if (sprop->getter == js_GetCallArg) {
1425 pd->slot = sprop->shortid;
1426 pd->flags |= JSPD_ARGUMENT;
1427 } else if (sprop->getter == js_GetCallVar) {
1428 pd->slot = sprop->shortid;
1429 pd->flags |= JSPD_VARIABLE;
1430 } else {
1431 pd->slot = 0;
1432 }
1433 pd->alias = JSVAL_VOID;
1434 scope = OBJ_SCOPE(obj);
1435 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
1436 for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
1437 if (aprop != sprop && aprop->slot == sprop->slot) {
1438 pd->alias = ID_TO_VALUE(aprop->id);
1439 break;
1440 }
1441 }
1442 }
1443 return JS_TRUE;
1444 }
1445
1446 JS_PUBLIC_API(JSBool)
1447 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1448 {
1449 JSClass *clasp;
1450 JSScope *scope;
1451 uint32 i, n;
1452 JSPropertyDesc *pd;
1453 JSScopeProperty *sprop;
1454
1455 clasp = OBJ_GET_CLASS(cx, obj);
1456 if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1457 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1458 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1459 return JS_FALSE;
1460 }
1461 if (!clasp->enumerate(cx, obj))
1462 return JS_FALSE;
1463
1464 /* have no props, or object's scope has not mutated from that of proto */
1465 scope = OBJ_SCOPE(obj);
1466 if (scope->object != obj || scope->entryCount == 0) {
1467 pda->length = 0;
1468 pda->array = NULL;
1469 return JS_TRUE;
1470 }
1471
1472 n = scope->entryCount;
1473 pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
1474 if (!pd)
1475 return JS_FALSE;
1476 i = 0;
1477 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1478 if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1479 continue;
1480 if (!js_AddRoot(cx, &pd[i].id, NULL))
1481 goto bad;
1482 if (!js_AddRoot(cx, &pd[i].value, NULL))
1483 goto bad;
1484 if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1485 goto bad;
1486 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
1487 goto bad;
1488 if (++i == n)
1489 break;
1490 }
1491 pda->length = i;
1492 pda->array = pd;
1493 return JS_TRUE;
1494
1495 bad:
1496 pda->length = i + 1;
1497 pda->array = pd;
1498 JS_PutPropertyDescArray(cx, pda);
1499 return JS_FALSE;
1500 }
1501
1502 JS_PUBLIC_API(void)
1503 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1504 {
1505 JSPropertyDesc *pd;
1506 uint32 i;
1507
1508 pd = pda->array;
1509 for (i = 0; i < pda->length; i++) {
1510 js_RemoveRoot(cx->runtime, &pd[i].id);
1511 js_RemoveRoot(cx->runtime, &pd[i].value);
1512 if (pd[i].flags & JSPD_ALIAS)
1513 js_RemoveRoot(cx->runtime, &pd[i].alias);
1514 }
1515 JS_free(cx, pd);
1516 }
1517
1518 /************************************************************************/
1519
1520 JS_PUBLIC_API(JSBool)
1521 JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
1522 {
1523 rt->globalDebugHooks.debuggerHandler = handler;
1524 rt->globalDebugHooks.debuggerHandlerData = closure;
1525 return JS_TRUE;
1526 }
1527
1528 JS_PUBLIC_API(JSBool)
1529 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1530 {
1531 rt->globalDebugHooks.sourceHandler = handler;
1532 rt->globalDebugHooks.sourceHandlerData = closure;
1533 return JS_TRUE;
1534 }
1535
1536 JS_PUBLIC_API(JSBool)
1537 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1538 {
1539 rt->globalDebugHooks.executeHook = hook;
1540 rt->globalDebugHooks.executeHookData = closure;
1541 return JS_TRUE;
1542 }
1543
1544 JS_PUBLIC_API(JSBool)
1545 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1546 {
1547 rt->globalDebugHooks.callHook = hook;
1548 rt->globalDebugHooks.callHookData = closure;
1549 return JS_TRUE;
1550 }
1551
1552 JS_PUBLIC_API(JSBool)
1553 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
1554 {
1555 rt->globalDebugHooks.objectHook = hook;
1556 rt->globalDebugHooks.objectHookData = closure;
1557 return JS_TRUE;
1558 }
1559
1560 JS_PUBLIC_API(JSBool)
1561 JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
1562 {
1563 rt->globalDebugHooks.throwHook = hook;
1564 rt->globalDebugHooks.throwHookData = closure;
1565 return JS_TRUE;
1566 }
1567
1568 JS_PUBLIC_API(JSBool)
1569 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1570 {
1571 rt->globalDebugHooks.debugErrorHook = hook;
1572 rt->globalDebugHooks.debugErrorHookData = closure;
1573 return JS_TRUE;
1574 }
1575
1576 /************************************************************************/
1577
1578 JS_PUBLIC_API(size_t)
1579 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1580 {
1581 size_t nbytes;
1582 JSScope *scope;
1583
1584 nbytes = sizeof *obj;
1585 if (obj->dslots) {
1586 nbytes += ((uint32)obj->dslots[-1] - JS_INITIAL_NSLOTS + 1)
1587 * sizeof obj->dslots[0];
1588 }
1589 if (OBJ_IS_NATIVE(obj)) {
1590 scope = OBJ_SCOPE(obj);
1591 if (scope->object == obj) {
1592 nbytes += sizeof *scope;
1593 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1594 }
1595 }
1596 return nbytes;
1597 }
1598
1599 static size_t
1600 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1601 {
1602 size_t nbytes;
1603
1604 nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
1605 if (ATOM_IS_STRING(atom)) {
1606 nbytes += sizeof(JSString);
1607 nbytes += (JSFLATSTR_LENGTH(ATOM_TO_STRING(atom)) + 1) * sizeof(jschar);
1608 } else if (ATOM_IS_DOUBLE(atom)) {
1609 nbytes += sizeof(jsdouble);
1610 }
1611 return nbytes;
1612 }
1613
1614 JS_PUBLIC_API(size_t)
1615 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1616 {
1617 size_t nbytes;
1618
1619 nbytes = sizeof *fun;
1620 nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun));
1621 if (FUN_INTERPRETED(fun))
1622 nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script);
1623 if (fun->atom)
1624 nbytes += GetAtomTotalSize(cx, fun->atom);
1625 return nbytes;
1626 }
1627
1628 #include "jsemit.h"
1629
1630 JS_PUBLIC_API(size_t)
1631 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1632 {
1633 size_t nbytes, pbytes;
1634 jsatomid i;
1635 jssrcnote *sn, *notes;
1636 JSObjectArray *objarray;
1637 JSPrincipals *principals;
1638
1639 nbytes = sizeof *script;
1640 if (script->u.object)
1641 nbytes += JS_GetObjectTotalSize(cx, script->u.object);
1642
1643 nbytes += script->length * sizeof script->code[0];
1644 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1645 for (i = 0; i < script->atomMap.length; i++)
1646 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1647
1648 if (script->filename)
1649 nbytes += strlen(script->filename) + 1;
1650
1651 notes = SCRIPT_NOTES(script);
1652 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1653 continue;
1654 nbytes += (sn - notes + 1) * sizeof *sn;
1655
1656 if (script->objectsOffset != 0) {
1657 objarray = JS_SCRIPT_OBJECTS(script);
1658 i = objarray->length;
1659 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1660 do {
1661 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1662 } while (i != 0);
1663 }
1664
1665 if (script->regexpsOffset != 0) {
1666 objarray = JS_SCRIPT_REGEXPS(script);
1667 i = objarray->length;
1668 nbytes += sizeof *objarray + i * sizeof objarray->vector[0];
1669 do {
1670 nbytes += JS_GetObjectTotalSize(cx, objarray->vector[--i]);
1671 } while (i != 0);
1672 }
1673
1674 if (script->trynotesOffset != 0) {
1675 nbytes += sizeof(JSTryNoteArray) +
1676 JS_SCRIPT_TRYNOTES(script)->length * sizeof(JSTryNote);
1677 }
1678
1679 principals = script->principals;
1680 if (principals) {
1681 JS_ASSERT(principals->refcount);
1682 pbytes = sizeof *principals;
1683 if (principals->refcount > 1)
1684 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1685 nbytes += pbytes;
1686 }
1687
1688 return nbytes;
1689 }
1690
1691 JS_PUBLIC_API(uint32)
1692 JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp)
1693 {
1694 if (!fp)
1695 fp = js_GetTopStackFrame(cx);
1696 while (fp) {
1697 if (fp->script)
1698 return JS_GetScriptFilenameFlags(fp->script);
1699 fp = fp->down;
1700 }
1701 return 0;
1702 }
1703
1704 JS_PUBLIC_API(uint32)
1705 JS_GetScriptFilenameFlags(JSScript *script)
1706 {
1707 JS_ASSERT(script);
1708 if (!script->filename)
1709 return JSFILENAME_NULL;
1710 return js_GetScriptFilenameFlags(script->filename);
1711 }
1712
1713 JS_PUBLIC_API(JSBool)
1714 JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags)
1715 {
1716 if (!js_SaveScriptFilenameRT(rt, prefix, flags))
1717 return JS_FALSE;
1718 return JS_TRUE;
1719 }
1720
1721 JS_PUBLIC_API(JSBool)
1722 JS_IsSystemObject(JSContext *cx, JSObject *obj)
1723 {
1724 return STOBJ_IS_SYSTEM(obj);
1725 }
1726
1727 JS_PUBLIC_API(JSObject *)
1728 JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1729 JSObject *parent, JSBool system)
1730 {
1731 JSObject *obj;
1732
1733 obj = js_NewObject(cx, clasp, proto, parent, 0);
1734 if (obj && system)
1735 STOBJ_SET_SYSTEM(obj);
1736 return obj;
1737 }
1738
1739 /************************************************************************/
1740
1741 JS_PUBLIC_API(JSDebugHooks *)
1742 JS_GetGlobalDebugHooks(JSRuntime *rt)
1743 {
1744 return &rt->globalDebugHooks;
1745 }
1746
1747 JS_PUBLIC_API(JSDebugHooks *)
1748 JS_SetContextDebugHooks(JSContext *cx, JSDebugHooks *hooks)
1749 {
1750 JSDebugHooks *old;
1751
1752 JS_ASSERT(hooks);
1753 old = cx->debugHooks;
1754 cx->debugHooks = hooks;
1755 return old;
1756 }
1757
1758 #ifdef MOZ_SHARK
1759
1760 #include <CHUD/CHUD.h>
1761
1762 JS_PUBLIC_API(JSBool)
1763 JS_StartChudRemote()
1764 {
1765 if (chudIsRemoteAccessAcquired() &&
1766 (chudStartRemotePerfMonitor("Mozilla") == chudSuccess)) {
1767 return JS_TRUE;
1768 }
1769
1770 return JS_FALSE;
1771 }
1772
1773 JS_PUBLIC_API(JSBool)
1774 JS_StopChudRemote()
1775 {
1776 if (chudIsRemoteAccessAcquired() &&
1777 (chudStopRemotePerfMonitor() == chudSuccess)) {
1778 return JS_TRUE;
1779 }
1780
1781 return JS_FALSE;
1782 }
1783
1784 JS_PUBLIC_API(JSBool)
1785 JS_ConnectShark()
1786 {
1787 if (!chudIsInitialized() && (chudInitialize() != chudSuccess))
1788 return JS_FALSE;
1789
1790 if (chudAcquireRemoteAccess() != chudSuccess)
1791 return JS_FALSE;
1792
1793 return JS_TRUE;
1794 }
1795
1796 JS_PUBLIC_API(JSBool)
1797 JS_DisconnectShark()
1798 {
1799 if (chudIsRemoteAccessAcquired() && (chudReleaseRemoteAccess() != chudSuccess))
1800 return JS_FALSE;
1801
1802 return JS_TRUE;
1803 }
1804
1805 JS_FRIEND_API(JSBool)
1806 js_StartShark(JSContext *cx, JSObject *obj,
1807 uintN argc, jsval *argv, jsval *rval)
1808 {
1809 if (!JS_StartChudRemote()) {
1810 JS_ReportError(cx, "Error starting CHUD.");
1811 return JS_FALSE;
1812 }
1813
1814 return JS_TRUE;
1815 }
1816
1817 JS_FRIEND_API(JSBool)
1818 js_StopShark(JSContext *cx, JSObject *obj,
1819 uintN argc, jsval *argv, jsval *rval)
1820 {
1821 if (!JS_StopChudRemote()) {
1822 JS_ReportError(cx, "Error stopping CHUD.");
1823 return JS_FALSE;
1824 }
1825
1826 return JS_TRUE;
1827 }
1828
1829 JS_FRIEND_API(JSBool)
1830 js_ConnectShark(JSContext *cx, JSObject *obj,
1831 uintN argc, jsval *argv, jsval *rval)
1832 {
1833 if (!JS_ConnectShark()) {
1834 JS_ReportError(cx, "Error connecting to Shark.");
1835 return JS_FALSE;
1836 }
1837
1838 return JS_TRUE;
1839 }
1840
1841 JS_FRIEND_API(JSBool)
1842 js_DisconnectShark(JSContext *cx, JSObject *obj,
1843 uintN argc, jsval *argv, jsval *rval)
1844 {
1845 if (!JS_DisconnectShark()) {
1846 JS_ReportError(cx, "Error disconnecting from Shark.");
1847 return JS_FALSE;
1848 }
1849
1850 return JS_TRUE;
1851 }
1852
1853 #endif /* MOZ_SHARK */
1854
1855 #ifdef MOZ_CALLGRIND
1856
1857 #include <valgrind/callgrind.h>
1858
1859 JS_FRIEND_API(JSBool)
1860 js_StartCallgrind(JSContext *cx, JSObject *obj,
1861 uintN argc, jsval *argv, jsval *rval)
1862 {
1863 CALLGRIND_START_INSTRUMENTATION;
1864 CALLGRIND_ZERO_STATS;
1865 return JS_TRUE;
1866 }
1867
1868 JS_FRIEND_API(JSBool)
1869 js_StopCallgrind(JSContext *cx, JSObject *obj,
1870 uintN argc, jsval *argv, jsval *rval)
1871 {
1872 CALLGRIND_STOP_INSTRUMENTATION;
1873 return JS_TRUE;
1874 }
1875
1876 JS_FRIEND_API(JSBool)
1877 js_DumpCallgrind(JSContext *cx, JSObject *obj,
1878 uintN argc, jsval *argv, jsval *rval)
1879 {
1880 JSString *str;
1881 char *cstr;
1882
1883 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1884 str = JSVAL_TO_STRING(argv[0]);
1885 cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
1886 if (cstr) {
1887 CALLGRIND_DUMP_STATS_AT(cstr);
1888 JS_free(cx, cstr);
1889 return JS_TRUE;
1890 }
1891 }
1892 CALLGRIND_DUMP_STATS;
1893
1894 return JS_TRUE;
1895 }
1896
1897 #endif /* MOZ_CALLGRIND */
1898
1899 #ifdef MOZ_VTUNE
1900 #include <VTuneApi.h>
1901
1902 static const char *vtuneErrorMessages[] = {
1903 "unknown, error #0",
1904 "invalid 'max samples' field",
1905 "invalid 'samples per buffer' field",
1906 "invalid 'sample interval' field",
1907 "invalid path",
1908 "sample file in use",
1909 "invalid 'number of events' field",
1910 "unknown, error #7",
1911 "internal error",
1912 "bad event name",
1913 "VTStopSampling called without calling VTStartSampling",
1914 "no events selected for event-based sampling",
1915 "events selected cannot be run together",
1916 "no sampling parameters",
1917 "sample database already exists",
1918 "sampling already started",
1919 "time-based sampling not supported",
1920 "invalid 'sampling parameters size' field",
1921 "invalid 'event size' field",
1922 "sampling file already bound",
1923 "invalid event path",
1924 "invalid license",
1925 "invalid 'global options' field",
1926
1927 };
1928
1929 JS_FRIEND_API(JSBool)
1930 js_StartVtune(JSContext *cx, JSObject *obj,
1931 uintN argc, jsval *argv, jsval *rval)
1932 {
1933 VTUNE_EVENT events[] = {
1934 { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
1935 { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
1936 };
1937
1938 U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
1939 char *default_filename = "mozilla-vtune.tb5";
1940 JSString *str;
1941 U32 status;
1942
1943 VTUNE_SAMPLING_PARAMS params =
1944 sizeof(VTUNE_SAMPLING_PARAMS),
1945 sizeof(VTUNE_EVENT),
1946 0, 0, /* Reserved fields */
1947 1, /* Initialize in "paused" state */
1948 0, /* Max samples, or 0 for "continuous" */
1949 4096, /* Samples per buffer */
1950 0.1, /* Sampling interval in ms */
1951 1, /* 1 for event-based sampling, 0 for time-based */
1952
1953 n_events,
1954 events,
1955 default_filename,
1956 };
1957
1958 if (argc > 0 && JSVAL_IS_STRING(argv[0])) {
1959 str = JSVAL_TO_STRING(argv[0]);
1960 params.tb5Filename = js_DeflateString(cx,
1961 JSSTRING_CHARS(str),
1962 JSSTRING_LENGTH(str));
1963 }
1964
1965 status = VTStartSampling(&params);
1966
1967 if (params.tb5Filename != default_filename)
1968 JS_free(cx, params.tb5Filename);
1969
1970 if (status != 0) {
1971 if (status == VTAPI_MULTIPLE_RUNS)
1972 VTStopSampling(0);
1973 if (status < sizeof(vtuneErrorMessages))
1974 JS_ReportError(cx, "Vtune setup error: %s",
1975 vtuneErrorMessages[status]);
1976 else
1977 JS_ReportError(cx, "Vtune setup error: %d",
1978 status);
1979 return JS_FALSE;
1980 }
1981 return JS_TRUE;
1982 }
1983
1984 JS_FRIEND_API(JSBool)
1985 js_StopVtune(JSContext *cx, JSObject *obj,
1986 uintN argc, jsval *argv, jsval *rval)
1987 {
1988 U32 status = VTStopSampling(1);
1989 if (status) {
1990 if (status < sizeof(vtuneErrorMessages))
1991 JS_ReportError(cx, "Vtune shutdown error: %s",
1992 vtuneErrorMessages[status]);
1993 else
1994 JS_ReportError(cx, "Vtune shutdown error: %d",
1995 status);
1996 return JS_FALSE;
1997 }
1998 return JS_TRUE;
1999 }
2000
2001 JS_FRIEND_API(JSBool)
2002 js_PauseVtune(JSContext *cx, JSObject *obj,
2003 uintN argc, jsval *argv, jsval *rval)
2004 {
2005 VTPause();
2006 return JS_TRUE;
2007 }
2008
2009 JS_FRIEND_API(JSBool)
2010 js_ResumeVtune(JSContext *cx, JSObject *obj,
2011 uintN argc, jsval *argv, jsval *rval)
2012 {
2013 VTResume();
2014 return JS_TRUE;
2015 }
2016
2017 #endif /* MOZ_VTUNE */

  ViewVC Help
Powered by ViewVC 1.1.24