1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 /*
41 * JS debugging API.
42 */
43 #include "jsstddef.h"
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsutil.h" /* Added by JSIFY */
47 #include "jsclist.h"
48 #include "jsapi.h"
49 #include "jscntxt.h"
50 #include "jsconfig.h"
51 #include "jsdbgapi.h"
52 #include "jsfun.h"
53 #include "jsgc.h"
54 #include "jsinterp.h"
55 #include "jslock.h"
56 #include "jsobj.h"
57 #include "jsopcode.h"
58 #include "jsscope.h"
59 #include "jsscript.h"
60 #include "jsstr.h"
62 typedef struct JSTrap {
63 JSCList links;
64 JSScript *script;
65 jsbytecode *pc;
66 JSOp op;
67 JSTrapHandler handler;
68 void *closure;
69 } JSTrap;
71 static JSTrap *
72 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
73 {
74 JSTrap *trap;
76 for (trap = (JSTrap *)rt->trapList.next;
77 trap != (JSTrap *)&rt->trapList;
78 trap = (JSTrap *)trap->links.next) {
79 if (trap->script == script && trap->pc == pc)
80 return trap;
81 }
82 return NULL;
83 }
85 void
86 js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
87 {
88 JSTrap *trap;
90 trap = FindTrap(cx->runtime, script, pc);
91 if (trap)
92 trap->op = op;
93 else
94 *pc = (jsbytecode)op;
95 }
97 JS_PUBLIC_API(JSBool)
98 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
99 JSTrapHandler handler, void *closure)
100 {
101 JSRuntime *rt;
102 JSTrap *trap;
104 rt = cx->runtime;
105 trap = FindTrap(rt, script, pc);
106 if (trap) {
107 JS_ASSERT(trap->script == script && trap->pc == pc);
108 JS_ASSERT(*pc == JSOP_TRAP);
109 } else {
110 trap = (JSTrap *) JS_malloc(cx, sizeof *trap);
111 if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) {
112 if (trap)
113 JS_free(cx, trap);
114 return JS_FALSE;
115 }
116 JS_APPEND_LINK(&trap->links, &rt->trapList);
117 trap->script = script;
118 trap->pc = pc;
119 trap->op = (JSOp)*pc;
120 *pc = JSOP_TRAP;
121 }
122 trap->handler = handler;
123 trap->closure = closure;
124 return JS_TRUE;
125 }
127 JS_PUBLIC_API(JSOp)
128 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
129 {
130 JSTrap *trap;
132 trap = FindTrap(cx->runtime, script, pc);
133 if (!trap) {
134 JS_ASSERT(0); /* XXX can't happen */
135 return JSOP_LIMIT;
136 }
137 return trap->op;
138 }
140 static void
141 DestroyTrap(JSContext *cx, JSTrap *trap)
142 {
143 JS_REMOVE_LINK(&trap->links);
144 *trap->pc = (jsbytecode)trap->op;
145 js_RemoveRoot(cx->runtime, &trap->closure);
146 JS_free(cx, trap);
147 }
149 JS_PUBLIC_API(void)
150 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
151 JSTrapHandler *handlerp, void **closurep)
152 {
153 JSTrap *trap;
155 trap = FindTrap(cx->runtime, script, pc);
156 if (handlerp)
157 *handlerp = trap ? trap->handler : NULL;
158 if (closurep)
159 *closurep = trap ? trap->closure : NULL;
160 if (trap)
161 DestroyTrap(cx, trap);
162 }
164 JS_PUBLIC_API(void)
165 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
166 {
167 JSRuntime *rt;
168 JSTrap *trap, *next;
170 rt = cx->runtime;
171 for (trap = (JSTrap *)rt->trapList.next;
172 trap != (JSTrap *)&rt->trapList;
173 trap = next) {
174 next = (JSTrap *)trap->links.next;
175 if (trap->script == script)
176 DestroyTrap(cx, trap);
177 }
178 }
180 JS_PUBLIC_API(void)
181 JS_ClearAllTraps(JSContext *cx)
182 {
183 JSRuntime *rt;
184 JSTrap *trap, *next;
186 rt = cx->runtime;
187 for (trap = (JSTrap *)rt->trapList.next;
188 trap != (JSTrap *)&rt->trapList;
189 trap = next) {
190 next = (JSTrap *)trap->links.next;
191 DestroyTrap(cx, trap);
192 }
193 }
195 JS_PUBLIC_API(JSTrapStatus)
196 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
197 {
198 JSTrap *trap;
199 JSTrapStatus status;
200 jsint op;
202 trap = FindTrap(cx->runtime, script, pc);
203 if (!trap) {
204 JS_ASSERT(0); /* XXX can't happen */
205 return JSTRAP_ERROR;
206 }
207 /*
208 * It's important that we not use 'trap->' after calling the callback --
209 * the callback might remove the trap!
210 */
211 op = (jsint)trap->op;
212 status = trap->handler(cx, script, pc, rval, trap->closure);
213 if (status == JSTRAP_CONTINUE) {
214 /* By convention, return the true op to the interpreter in rval. */
215 *rval = INT_TO_JSVAL(op);
216 }
217 return status;
218 }
220 JS_PUBLIC_API(JSBool)
221 JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
222 {
223 rt->interruptHandler = handler;
224 rt->interruptHandlerData = closure;
225 return JS_TRUE;
226 }
228 JS_PUBLIC_API(JSBool)
229 JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
230 {
231 if (handlerp)
232 *handlerp = (JSTrapHandler)rt->interruptHandler;
233 if (closurep)
234 *closurep = rt->interruptHandlerData;
235 rt->interruptHandler = 0;
236 rt->interruptHandlerData = 0;
237 return JS_TRUE;
238 }
240 /************************************************************************/
242 typedef struct JSWatchPoint {
243 JSCList links;
244 JSObject *object; /* weak link, see js_FinalizeObject */
245 JSScopeProperty *sprop;
246 JSPropertyOp setter;
247 JSWatchPointHandler handler;
248 void *closure;
249 jsrefcount nrefs;
250 } JSWatchPoint;
252 #define HoldWatchPoint(wp) ((wp)->nrefs++)
254 static JSBool
255 DropWatchPoint(JSContext *cx, JSWatchPoint *wp)
256 {
257 JSScopeProperty *sprop;
259 if (--wp->nrefs != 0)
260 return JS_TRUE;
262 /*
263 * Remove wp from the list, then if there are no other watchpoints for
264 * wp->sprop in any scope, restore wp->sprop->setter from wp.
265 */
266 JS_REMOVE_LINK(&wp->links);
267 sprop = wp->sprop;
268 if (!js_GetWatchedSetter(cx->runtime, NULL, sprop)) {
269 sprop = js_ChangeNativePropertyAttrs(cx, wp->object, sprop,
270 0, sprop->attrs,
271 sprop->getter, wp->setter);
272 if (!sprop)
273 return JS_FALSE;
274 }
275 js_RemoveRoot(cx->runtime, &wp->closure);
276 JS_free(cx, wp);
277 return JS_TRUE;
278 }
280 void
281 js_MarkWatchPoints(JSRuntime *rt)
282 {
283 JSWatchPoint *wp;
285 for (wp = (JSWatchPoint *)rt->watchPointList.next;
286 wp != (JSWatchPoint *)&rt->watchPointList;
287 wp = (JSWatchPoint *)wp->links.next) {
288 MARK_SCOPE_PROPERTY(wp->sprop);
289 }
290 }
292 static JSWatchPoint *
293 FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
294 {
295 JSWatchPoint *wp;
297 for (wp = (JSWatchPoint *)rt->watchPointList.next;
298 wp != (JSWatchPoint *)&rt->watchPointList;
299 wp = (JSWatchPoint *)wp->links.next) {
300 if (wp->object == scope->object && wp->sprop->id == id)
301 return wp;
302 }
303 return NULL;
304 }
306 JSScopeProperty *
307 js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
308 {
309 JSWatchPoint *wp;
311 wp = FindWatchPoint(rt, scope, id);
312 if (!wp)
313 return NULL;
314 return wp->sprop;
315 }
317 JSPropertyOp
318 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
319 const JSScopeProperty *sprop)
320 {
321 JSWatchPoint *wp;
323 for (wp = (JSWatchPoint *)rt->watchPointList.next;
324 wp != (JSWatchPoint *)&rt->watchPointList;
325 wp = (JSWatchPoint *)wp->links.next) {
326 if ((!scope || wp->object == scope->object) && wp->sprop == sprop)
327 return wp->setter;
328 }
329 return NULL;
330 }
332 JSBool JS_DLL_CALLBACK
333 js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
334 {
335 JSRuntime *rt;
336 JSWatchPoint *wp;
337 JSScopeProperty *sprop;
338 jsval userid;
339 JSScope *scope;
340 JSBool ok;
342 rt = cx->runtime;
343 for (wp = (JSWatchPoint *)rt->watchPointList.next;
344 wp != (JSWatchPoint *)&rt->watchPointList;
345 wp = (JSWatchPoint *)wp->links.next) {
346 sprop = wp->sprop;
347 if (wp->object == obj && SPROP_USERID(sprop) == id) {
348 JS_LOCK_OBJ(cx, obj);
349 userid = SPROP_USERID(sprop);
350 scope = OBJ_SCOPE(obj);
351 JS_UNLOCK_OBJ(cx, obj);
352 HoldWatchPoint(wp);
353 ok = wp->handler(cx, obj, userid,
354 SPROP_HAS_VALID_SLOT(sprop, scope)
355 ? OBJ_GET_SLOT(cx, obj, wp->sprop->slot)
356 : JSVAL_VOID,
357 vp, wp->closure);
358 if (ok) {
359 /*
360 * Create pseudo-frame for call to setter so that any
361 * stack-walking security code in the setter will correctly
362 * identify the guilty party.
363 */
364 JSObject *funobj = (JSObject *) wp->closure;
365 JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, funobj);
366 JSStackFrame frame;
368 memset(&frame, 0, sizeof(frame));
369 frame.script = FUN_SCRIPT(fun);
370 frame.fun = fun;
371 frame.down = cx->fp;
372 cx->fp = &frame;
373 ok = !wp->setter ||
374 ((sprop->attrs & JSPROP_SETTER)
375 ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter),
376 1, vp, vp)
377 : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp));
378 cx->fp = frame.down;
379 }
380 return DropWatchPoint(cx, wp);
381 }
382 }
383 JS_ASSERT(0); /* XXX can't happen */
384 return JS_FALSE;
385 }
387 JSBool JS_DLL_CALLBACK
388 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
389 jsval *rval)
390 {
391 JSObject *funobj;
392 JSFunction *wrapper;
393 jsval userid;
395 funobj = JSVAL_TO_OBJECT(argv[-2]);
396 wrapper = (JSFunction *) JS_GetPrivate(cx, funobj);
397 userid = ATOM_KEY(wrapper->atom);
398 *rval = argv[0];
399 return js_watch_set(cx, obj, userid, rval);
400 }
402 JSPropertyOp
403 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
404 {
405 JSAtom *atom;
406 JSFunction *wrapper;
408 if (!(attrs & JSPROP_SETTER))
409 return &js_watch_set; /* & to silence schoolmarmish MSVC */
411 if (!JSVAL_IS_INT(id)) {
412 atom = (JSAtom *)id;
413 } else {
414 atom = js_AtomizeInt(cx, JSVAL_TO_INT(id), 0);
415 if (!atom)
416 return NULL;
417 }
418 wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
419 OBJ_GET_PARENT(cx, (JSObject *)setter),
420 atom);
421 if (!wrapper)
422 return NULL;
423 return (JSPropertyOp) wrapper->object;
424 }
426 JS_PUBLIC_API(JSBool)
427 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
428 JSWatchPointHandler handler, void *closure)
429 {
430 JSAtom *atom;
431 jsid propid;
432 JSObject *pobj;
433 JSProperty *prop;
434 JSScopeProperty *sprop;
435 JSRuntime *rt;
436 JSBool ok;
437 JSWatchPoint *wp;
438 JSPropertyOp watcher;
440 if (!OBJ_IS_NATIVE(obj)) {
441 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
442 OBJ_GET_CLASS(cx, obj)->name);
443 return JS_FALSE;
444 }
446 if (JSVAL_IS_INT(id)) {
447 propid = (jsid)id;
448 atom = NULL;
449 } else {
450 atom = js_ValueToStringAtom(cx, id);
451 if (!atom)
452 return JS_FALSE;
453 propid = (jsid)atom;
454 }
456 if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
457 return JS_FALSE;
458 sprop = (JSScopeProperty *) prop;
459 rt = cx->runtime;
460 if (!sprop) {
461 /* Check for a deleted symbol watchpoint, which holds its property. */
462 sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
463 if (!sprop) {
464 /* Make a new property in obj so we can watch for the first set. */
465 if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID,
466 NULL, NULL, JSPROP_ENUMERATE,
467 &prop)) {
468 return JS_FALSE;
469 }
470 sprop = (JSScopeProperty *) prop;
471 }
472 } else if (pobj != obj) {
473 /* Clone the prototype property so we can watch the right object. */
474 jsval value;
475 JSPropertyOp getter, setter;
476 uintN attrs;
478 if (OBJ_IS_NATIVE(pobj)) {
479 value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
480 ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
481 : JSVAL_VOID;
482 getter = sprop->getter;
483 setter = sprop->setter;
484 attrs = sprop->attrs;
485 } else {
486 if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) {
487 OBJ_DROP_PROPERTY(cx, pobj, prop);
488 return JS_FALSE;
489 }
490 getter = setter = JS_PropertyStub;
491 attrs = JSPROP_ENUMERATE;
492 }
493 OBJ_DROP_PROPERTY(cx, pobj, prop);
495 if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs,
496 &prop)) {
497 return JS_FALSE;
498 }
499 sprop = (JSScopeProperty *) prop;
500 }
502 /*
503 * At this point, prop/sprop exists in obj, obj is locked, and we must
504 * OBJ_DROP_PROPERTY(cx, obj, prop) before returning.
505 */
506 ok = JS_TRUE;
507 wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
508 if (!wp) {
509 watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
510 if (!watcher) {
511 ok = JS_FALSE;
512 goto out;
513 }
515 wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);
516 if (!wp) {
517 ok = JS_FALSE;
518 goto out;
519 }
520 wp->handler = NULL;
521 wp->closure = NULL;
522 ok = js_AddRoot(cx, &wp->closure, "wp->closure");
523 if (!ok) {
524 JS_free(cx, wp);
525 goto out;
526 }
527 JS_APPEND_LINK(&wp->links, &rt->watchPointList);
528 wp->object = obj;
529 wp->sprop = sprop;
530 JS_ASSERT(sprop->setter != js_watch_set);
531 wp->setter = sprop->setter;
532 wp->nrefs = 1;
534 /* XXXbe nest in obj lock here */
535 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
536 sprop->getter, watcher);
537 if (!sprop) {
538 DropWatchPoint(cx, wp);
539 ok = JS_FALSE;
540 goto out;
541 }
542 }
543 wp->handler = handler;
544 wp->closure = closure;
546 out:
547 OBJ_DROP_PROPERTY(cx, obj, prop);
548 return ok;
549 }
551 JS_PUBLIC_API(JSBool)
552 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
553 JSWatchPointHandler *handlerp, void **closurep)
554 {
555 JSRuntime *rt;
556 JSWatchPoint *wp;
558 rt = cx->runtime;
559 for (wp = (JSWatchPoint *)rt->watchPointList.next;
560 wp != (JSWatchPoint *)&rt->watchPointList;
561 wp = (JSWatchPoint *)wp->links.next) {
562 if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
563 if (handlerp)
564 *handlerp = wp->handler;
565 if (closurep)
566 *closurep = wp->closure;
567 return DropWatchPoint(cx, wp);
568 }
569 }
570 if (handlerp)
571 *handlerp = NULL;
572 if (closurep)
573 *closurep = NULL;
574 return JS_TRUE;
575 }
577 JS_PUBLIC_API(JSBool)
578 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
579 {
580 JSRuntime *rt;
581 JSWatchPoint *wp, *next;
583 rt = cx->runtime;
584 for (wp = (JSWatchPoint *)rt->watchPointList.next;
585 wp != (JSWatchPoint *)&rt->watchPointList;
586 wp = next) {
587 next = (JSWatchPoint *)wp->links.next;
588 if (wp->object == obj && !DropWatchPoint(cx, wp))
589 return JS_FALSE;
590 }
591 return JS_TRUE;
592 }
594 JS_PUBLIC_API(JSBool)
595 JS_ClearAllWatchPoints(JSContext *cx)
596 {
597 JSRuntime *rt;
598 JSWatchPoint *wp, *next;
600 rt = cx->runtime;
601 for (wp = (JSWatchPoint *)rt->watchPointList.next;
602 wp != (JSWatchPoint *)&rt->watchPointList;
603 wp = next) {
604 next = (JSWatchPoint *)wp->links.next;
605 if (!DropWatchPoint(cx, wp))
606 return JS_FALSE;
607 }
608 return JS_TRUE;
609 }
611 /************************************************************************/
613 JS_PUBLIC_API(uintN)
614 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
615 {
616 return js_PCToLineNumber(cx, script, pc);
617 }
619 JS_PUBLIC_API(jsbytecode *)
620 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
621 {
622 return js_LineNumberToPC(script, lineno);
623 }
625 JS_PUBLIC_API(JSScript *)
626 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
627 {
628 return FUN_SCRIPT(fun);
629 }
631 JS_PUBLIC_API(JSPrincipals *)
632 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
633 {
634 return script->principals;
635 }
637 /************************************************************************/
639 /*
640 * Stack Frame Iterator
641 */
642 JS_PUBLIC_API(JSStackFrame *)
643 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
644 {
645 *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down;
646 return *iteratorp;
647 }
649 JS_PUBLIC_API(JSScript *)
650 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
651 {
652 return fp->script;
653 }
655 JS_PUBLIC_API(jsbytecode *)
656 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
657 {
658 return fp->pc;
659 }
661 JS_PUBLIC_API(JSStackFrame *)
662 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
663 {
664 if (!fp)
665 fp = cx->fp;
666 while ((fp = fp->down) != NULL) {
667 if (fp->script)
668 return fp;
669 }
670 return NULL;
671 }
673 JS_PUBLIC_API(JSPrincipals *)
674 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
675 {
676 if (fp->fun && cx->findObjectPrincipals) {
677 JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]);
679 if (fp->fun->object != callee)
680 return cx->findObjectPrincipals(cx, callee);
681 /* FALL THROUGH */
682 }
683 if (fp->script)
684 return fp->script->principals;
685 return NULL;
686 }
688 JS_PUBLIC_API(JSPrincipals *)
689 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
690 {
691 if (cx->findObjectPrincipals)
692 return cx->findObjectPrincipals(cx, JSVAL_TO_OBJECT(fp->argv[-2]));
693 if (!caller)
694 return NULL;
695 return JS_StackFramePrincipals(cx, caller);
696 }
698 JS_PUBLIC_API(void *)
699 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
700 {
701 if (fp->annotation && fp->script) {
702 JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
704 if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
705 /*
706 * Give out an annotation only if privileges have not been revoked
707 * or disabled globally.
708 */
709 return fp->annotation;
710 }
711 }
713 return NULL;
714 }
716 JS_PUBLIC_API(void)
717 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
718 {
719 fp->annotation = annotation;
720 }
722 JS_PUBLIC_API(void *)
723 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
724 {
725 JSPrincipals *principals;
727 principals = JS_StackFramePrincipals(cx, fp);
728 if (!principals)
729 return NULL;
730 return principals->getPrincipalArray(cx, principals);
731 }
733 JS_PUBLIC_API(JSBool)
734 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
735 {
736 return !fp->script;
737 }
739 /* this is deprecated, use JS_GetFrameScopeChain instead */
740 JS_PUBLIC_API(JSObject *)
741 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
742 {
743 return fp->scopeChain;
744 }
746 JS_PUBLIC_API(JSObject *)
747 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
748 {
749 /* Force creation of argument and call objects if not yet created */
750 (void) JS_GetFrameCallObject(cx, fp);
751 return fp->scopeChain;
752 }
754 JS_PUBLIC_API(JSObject *)
755 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
756 {
757 if (! fp->fun)
758 return NULL;
759 #if JS_HAS_ARGS_OBJECT
760 /* Force creation of argument object if not yet created */
761 (void) js_GetArgsObject(cx, fp);
762 #endif
763 #if JS_HAS_CALL_OBJECT
764 /*
765 * XXX ill-defined: null return here means error was reported, unlike a
766 * null returned above or in the #else
767 */
768 return js_GetCallObject(cx, fp, NULL);
769 #else
770 return NULL;
771 #endif /* JS_HAS_CALL_OBJECT */
772 }
775 JS_PUBLIC_API(JSObject *)
776 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
777 {
778 return fp->thisp;
779 }
781 JS_PUBLIC_API(JSFunction *)
782 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
783 {
784 return fp->fun;
785 }
787 JS_PUBLIC_API(JSObject *)
788 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
789 {
790 return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL;
791 }
793 JS_PUBLIC_API(JSBool)
794 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
795 {
796 return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
797 }
799 JS_PUBLIC_API(JSBool)
800 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
801 {
802 return (fp->flags & JSFRAME_DEBUGGER) != 0;
803 }
805 JS_PUBLIC_API(jsval)
806 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
807 {
808 return fp->rval;
809 }
811 JS_PUBLIC_API(void)
812 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
813 {
814 fp->rval = rval;
815 }
817 /************************************************************************/
819 JS_PUBLIC_API(const char *)
820 JS_GetScriptFilename(JSContext *cx, JSScript *script)
821 {
822 return script->filename;
823 }
825 JS_PUBLIC_API(uintN)
826 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
827 {
828 return script->lineno;
829 }
831 JS_PUBLIC_API(uintN)
832 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
833 {
834 return js_GetScriptLineExtent(script);
835 }
837 JS_PUBLIC_API(JSVersion)
838 JS_GetScriptVersion(JSContext *cx, JSScript *script)
839 {
840 return script->version;
841 }
843 /***************************************************************************/
845 JS_PUBLIC_API(void)
846 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
847 {
848 rt->newScriptHook = hook;
849 rt->newScriptHookData = callerdata;
850 }
852 JS_PUBLIC_API(void)
853 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
854 void *callerdata)
855 {
856 rt->destroyScriptHook = hook;
857 rt->destroyScriptHookData = callerdata;
858 }
860 /***************************************************************************/
862 JS_PUBLIC_API(JSBool)
863 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
864 const jschar *bytes, uintN length,
865 const char *filename, uintN lineno,
866 jsval *rval)
867 {
868 uint32 flags;
869 JSScript *script;
870 JSBool ok;
872 /*
873 * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL
874 * flags to the code generator (see js_EmitTree's TOK_SEMI case).
875 */
876 flags = fp->flags;
877 fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL;
878 script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain,
879 JS_StackFramePrincipals(cx, fp),
880 bytes, length, filename, lineno);
881 fp->flags = flags;
882 if (!script)
883 return JS_FALSE;
885 ok = js_Execute(cx, fp->scopeChain, script, fp,
886 JSFRAME_DEBUGGER | JSFRAME_EVAL, rval);
887 js_DestroyScript(cx, script);
888 return ok;
889 }
891 JS_PUBLIC_API(JSBool)
892 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
893 const char *bytes, uintN length,
894 const char *filename, uintN lineno,
895 jsval *rval)
896 {
897 jschar *chars;
898 JSBool ok;
900 chars = js_InflateString(cx, bytes, length);
901 if (!chars)
902 return JS_FALSE;
903 ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
904 rval);
905 JS_free(cx, chars);
907 return ok;
908 }
910 /************************************************************************/
912 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
914 JS_PUBLIC_API(JSScopeProperty *)
915 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
916 {
917 JSScopeProperty *sprop;
918 JSScope *scope;
920 sprop = *iteratorp;
921 scope = OBJ_SCOPE(obj);
923 /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
924 if (!sprop) {
925 sprop = SCOPE_LAST_PROP(scope);
926 } else {
927 while ((sprop = sprop->parent) != NULL) {
928 if (!SCOPE_HAD_MIDDLE_DELETE(scope))
929 break;
930 if (SCOPE_HAS_PROPERTY(scope, sprop))
931 break;
932 }
933 }
934 *iteratorp = sprop;
935 return sprop;
936 }
938 JS_PUBLIC_API(JSBool)
939 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
940 JSPropertyDesc *pd)
941 {
942 JSPropertyOp getter;
943 JSScope *scope;
944 JSScopeProperty *aprop;
945 jsval lastException;
946 JSBool wasThrowing;
948 pd->id = ID_TO_VALUE(sprop->id);
950 wasThrowing = cx->throwing;
951 if (wasThrowing) {
952 lastException = cx->exception;
953 if (JSVAL_IS_GCTHING(lastException) &&
954 !js_AddRoot(cx, &lastException, "lastException")) {
955 return JS_FALSE;
956 }
957 cx->throwing = JS_FALSE;
958 }
960 if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) {
961 if (!cx->throwing) {
962 pd->flags = JSPD_ERROR;
963 pd->value = JSVAL_VOID;
964 } else {
965 pd->flags = JSPD_EXCEPTION;
966 pd->value = cx->exception;
967 }
968 } else {
969 pd->flags = 0;
970 }
972 cx->throwing = wasThrowing;
973 if (wasThrowing) {
974 cx->exception = lastException;
975 if (JSVAL_IS_GCTHING(lastException))
976 js_RemoveRoot(cx->runtime, &lastException);
977 }
979 getter = sprop->getter;
980 pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
981 | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0)
982 | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0)
983 #if JS_HAS_CALL_OBJECT
984 | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0)
985 #endif /* JS_HAS_CALL_OBJECT */
986 | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0)
987 | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0);
988 #if JS_HAS_CALL_OBJECT
989 /* for Call Object 'real' getter isn't passed in to us */
990 if (OBJ_GET_CLASS(cx, obj) == &js_CallClass &&
991 getter == js_CallClass.getProperty) {
992 /*
993 * Property of a heavyweight function's variable object having the
994 * class-default getter. It's either an argument if permanent, or a
995 * nested function if impermanent. Local variables have a special
996 * getter (js_GetCallVariable, tested above) and setter, and not the
997 * class default.
998 */
999 pd->flags |= (sprop->attrs & JSPROP_PERMANENT)
1000 ? JSPD_ARGUMENT
1001 : JSPD_VARIABLE;
1002 }
1003 #endif /* JS_HAS_CALL_OBJECT */
1004 pd->spare = 0;
1005 pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))
1006 ? sprop->shortid
1007 : 0;
1008 pd->alias = JSVAL_VOID;
1009 scope = OBJ_SCOPE(obj);
1010 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
1011 for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
1012 if (aprop != sprop && aprop->slot == sprop->slot) {
1013 pd->alias = ID_TO_VALUE(aprop->id);
1014 break;
1015 }
1016 }
1017 }
1018 return JS_TRUE;
1019 }
1021 JS_PUBLIC_API(JSBool)
1022 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1023 {
1024 JSClass *clasp;
1025 JSScope *scope;
1026 uint32 i, n;
1027 JSPropertyDesc *pd;
1028 JSScopeProperty *sprop;
1030 clasp = OBJ_GET_CLASS(cx, obj);
1031 if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1032 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1033 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1034 return JS_FALSE;
1035 }
1036 if (!clasp->enumerate(cx, obj))
1037 return JS_FALSE;
1039 /* have no props, or object's scope has not mutated from that of proto */
1040 scope = OBJ_SCOPE(obj);
1041 if (scope->object != obj || scope->entryCount == 0) {
1042 pda->length = 0;
1043 pda->array = NULL;
1044 return JS_TRUE;
1045 }
1047 n = scope->entryCount;
1048 if (n > scope->map.nslots)
1049 n = scope->map.nslots;
1050 pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
1051 if (!pd)
1052 return JS_FALSE;
1053 i = 0;
1054 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1055 if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1056 continue;
1057 if (!js_AddRoot(cx, &pd[i].id, NULL))
1058 goto bad;
1059 if (!js_AddRoot(cx, &pd[i].value, NULL))
1060 goto bad;
1061 if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1062 goto bad;
1063 if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
1064 goto bad;
1065 if (++i == n)
1066 break;
1067 }
1068 pda->length = i;
1069 pda->array = pd;
1070 return JS_TRUE;
1072 bad:
1073 pda->length = i + 1;
1074 pda->array = pd;
1075 JS_PutPropertyDescArray(cx, pda);
1076 return JS_FALSE;
1077 }
1079 JS_PUBLIC_API(void)
1080 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1081 {
1082 JSPropertyDesc *pd;
1083 uint32 i;
1085 pd = pda->array;
1086 for (i = 0; i < pda->length; i++) {
1087 js_RemoveRoot(cx->runtime, &pd[i].id);
1088 js_RemoveRoot(cx->runtime, &pd[i].value);
1089 if (pd[i].flags & JSPD_ALIAS)
1090 js_RemoveRoot(cx->runtime, &pd[i].alias);
1091 }
1092 JS_free(cx, pd);
1093 }
1095 /************************************************************************/
1097 JS_PUBLIC_API(JSBool)
1098 JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
1099 {
1100 rt->debuggerHandler = handler;
1101 rt->debuggerHandlerData = closure;
1102 return JS_TRUE;
1103 }
1105 JS_PUBLIC_API(JSBool)
1106 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1107 {
1108 rt->sourceHandler = handler;
1109 rt->sourceHandlerData = closure;
1110 return JS_TRUE;
1111 }
1113 JS_PUBLIC_API(JSBool)
1114 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1115 {
1116 rt->executeHook = hook;
1117 rt->executeHookData = closure;
1118 return JS_TRUE;
1119 }
1121 JS_PUBLIC_API(JSBool)
1122 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1123 {
1124 rt->callHook = hook;
1125 rt->callHookData = closure;
1126 return JS_TRUE;
1127 }
1129 JS_PUBLIC_API(JSBool)
1130 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
1131 {
1132 rt->objectHook = hook;
1133 rt->objectHookData = closure;
1134 return JS_TRUE;
1135 }
1137 JS_PUBLIC_API(JSBool)
1138 JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
1139 {
1140 rt->throwHook = hook;
1141 rt->throwHookData = closure;
1142 return JS_TRUE;
1143 }
1145 JS_PUBLIC_API(JSBool)
1146 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1147 {
1148 rt->debugErrorHook = hook;
1149 rt->debugErrorHookData = closure;
1150 return JS_TRUE;
1151 }
1153 /************************************************************************/
1155 JS_PUBLIC_API(size_t)
1156 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1157 {
1158 size_t nbytes;
1159 JSScope *scope;
1161 nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0];
1162 if (OBJ_IS_NATIVE(obj)) {
1163 scope = OBJ_SCOPE(obj);
1164 if (scope->object == obj) {
1165 nbytes += sizeof *scope;
1166 nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1167 }
1168 }
1169 return nbytes;
1170 }
1172 static size_t
1173 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1174 {
1175 size_t nbytes;
1177 nbytes = sizeof *atom;
1178 if (ATOM_IS_STRING(atom)) {
1179 nbytes += sizeof(JSString);
1180 nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar);
1181 } else if (ATOM_IS_DOUBLE(atom)) {
1182 nbytes += sizeof(jsdouble);
1183 } else if (ATOM_IS_OBJECT(atom)) {
1184 nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom));
1185 }
1186 return nbytes;
1187 }
1189 JS_PUBLIC_API(size_t)
1190 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1191 {
1192 size_t nbytes, obytes;
1193 JSObject *obj;
1194 JSAtom *atom;
1196 nbytes = sizeof *fun;
1197 JS_ASSERT(fun->nrefs);
1198 obj = fun->object;
1199 if (obj) {
1200 obytes = JS_GetObjectTotalSize(cx, obj);
1201 if (fun->nrefs > 1)
1202 obytes = JS_HOWMANY(obytes, fun->nrefs);
1203 nbytes += obytes;
1204 }
1205 if (fun->interpreted)
1206 nbytes += JS_GetScriptTotalSize(cx, fun->u.script);
1207 atom = fun->atom;
1208 if (atom)
1209 nbytes += GetAtomTotalSize(cx, atom);
1210 return nbytes;
1211 }
1213 #include "jsemit.h"
1215 JS_PUBLIC_API(size_t)
1216 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1217 {
1218 size_t nbytes, pbytes;
1219 JSObject *obj;
1220 jsatomid i;
1221 jssrcnote *sn, *notes;
1222 JSTryNote *tn, *tnotes;
1223 JSPrincipals *principals;
1225 nbytes = sizeof *script;
1226 obj = script->object;
1227 if (obj)
1228 nbytes += JS_GetObjectTotalSize(cx, obj);
1230 nbytes += script->length * sizeof script->code[0];
1231 nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1232 for (i = 0; i < script->atomMap.length; i++)
1233 nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1235 if (script->filename)
1236 nbytes += strlen(script->filename) + 1;
1238 notes = SCRIPT_NOTES(script);
1239 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1240 continue;
1241 nbytes += (sn - notes + 1) * sizeof *sn;
1243 tnotes = script->trynotes;
1244 if (tnotes) {
1245 for (tn = tnotes; tn->catchStart; tn++)
1246 continue;
1247 nbytes += (tn - tnotes + 1) * sizeof *tn;
1248 }
1250 principals = script->principals;
1251 if (principals) {
1252 JS_ASSERT(principals->refcount);
1253 pbytes = sizeof *principals;
1254 if (principals->refcount > 1)
1255 pbytes = JS_HOWMANY(pbytes, principals->refcount);
1256 nbytes += pbytes;
1257 }
1259 return nbytes;
1260 }