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 ***** */
39
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"
61
62 typedef struct JSTrap {
63     JSCList         links;
64     JSScript        *script;
65     jsbytecode      *pc;
66     JSOp            op;
67     JSTrapHandler   handler;
68     void            *closure;
69 } JSTrap;
70
71 static JSTrap *
72 FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)
73 0 {
74 0     JSTrap *trap;
75
76 0     for (trap = (JSTrap *)rt->trapList.next;
77          trap != (JSTrap *)&rt->trapList;
78          trap = (JSTrap *)trap->links.next) {
79 0         if (trap->script == script && trap->pc == pc)
80 0             return trap;
81     }
82 0     return NULL;
83 }
84
85 void
86 js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
87 0 {
88 0     JSTrap *trap;
89
90 0     trap = FindTrap(cx->runtime, script, pc);
91 0     if (trap)
92 0         trap->op = op;
93     else
94 0         *pc = (jsbytecode)op;
95 }
96
97 JS_PUBLIC_API(JSBool)
98 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
99            JSTrapHandler handler, void *closure)
100 0 {
101 0     JSRuntime *rt;
102 0     JSTrap *trap;
103
104 0     rt = cx->runtime;
105 0     trap = FindTrap(rt, script, pc);
106 0     if (trap) {
107 0         JS_ASSERT(trap->script == script && trap->pc == pc);
108 0         JS_ASSERT(*pc == JSOP_TRAP);
109     } else {
110 0         trap = (JSTrap *) JS_malloc(cx, sizeof *trap);
111 0         if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) {
112 0             if (trap)
113 0                 JS_free(cx, trap);
114 0             return JS_FALSE;
115         }
116 0         JS_APPEND_LINK(&trap->links, &rt->trapList);
117 0         trap->script = script;
118 0         trap->pc = pc;
119 0         trap->op = (JSOp)*pc;
120 0         *pc = JSOP_TRAP;
121     }
122 0     trap->handler = handler;
123 0     trap->closure = closure;
124 0     return JS_TRUE;
125 }
126
127 JS_PUBLIC_API(JSOp)
128 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
129 0 {
130 0     JSTrap *trap;
131
132 0     trap = FindTrap(cx->runtime, script, pc);
133 0     if (!trap) {
134 0         JS_ASSERT(0);   /* XXX can't happen */
135 0         return JSOP_LIMIT;
136     }
137 0     return trap->op;
138 }
139
140 static void
141 DestroyTrap(JSContext *cx, JSTrap *trap)
142 0 {
143 0     JS_REMOVE_LINK(&trap->links);
144 0     *trap->pc = (jsbytecode)trap->op;
145 0     js_RemoveRoot(cx->runtime, &trap->closure);
146 0     JS_free(cx, trap);
147 }
148
149 JS_PUBLIC_API(void)
150 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
151              JSTrapHandler *handlerp, void **closurep)
152 0 {
153 0     JSTrap *trap;
154
155 0     trap = FindTrap(cx->runtime, script, pc);
156 0     if (handlerp)
157 0         *handlerp = trap ? trap->handler : NULL;
158 0     if (closurep)
159 0         *closurep = trap ? trap->closure : NULL;
160 0     if (trap)
161 0         DestroyTrap(cx, trap);
162 }
163
164 JS_PUBLIC_API(void)
165 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
166 0 {
167 0     JSRuntime *rt;
168 0     JSTrap *trap, *next;
169
170 0     rt = cx->runtime;
171 0     for (trap = (JSTrap *)rt->trapList.next;
172          trap != (JSTrap *)&rt->trapList;
173          trap = next) {
174 0         next = (JSTrap *)trap->links.next;
175 0         if (trap->script == script)
176 0             DestroyTrap(cx, trap);
177     }
178 }
179
180 JS_PUBLIC_API(void)
181 JS_ClearAllTraps(JSContext *cx)
182 0 {
183 0     JSRuntime *rt;
184 0     JSTrap *trap, *next;
185
186 0     rt = cx->runtime;
187 0     for (trap = (JSTrap *)rt->trapList.next;
188          trap != (JSTrap *)&rt->trapList;
189          trap = next) {
190 0         next = (JSTrap *)trap->links.next;
191 0         DestroyTrap(cx, trap);
192     }
193 }
194
195 JS_PUBLIC_API(JSTrapStatus)
196 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
197 0 {
198 0     JSTrap *trap;
199 0     JSTrapStatus status;
200 0     jsint op;
201
202 0     trap = FindTrap(cx->runtime, script, pc);
203 0     if (!trap) {
204 0         JS_ASSERT(0);   /* XXX can't happen */
205 0         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 0     op = (jsint)trap->op;
212 0     status = trap->handler(cx, script, pc, rval, trap->closure);
213 0     if (status == JSTRAP_CONTINUE) {
214         /* By convention, return the true op to the interpreter in rval. */
215 0         *rval = INT_TO_JSVAL(op);
216     }
217 0     return status;
218 }
219
220 JS_PUBLIC_API(JSBool)
221 JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
222 0 {
223 0     rt->interruptHandler = handler;
224 0     rt->interruptHandlerData = closure;
225 0     return JS_TRUE;
226 }
227
228 JS_PUBLIC_API(JSBool)
229 JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
230 0 {
231 0     if (handlerp)
232 0         *handlerp = (JSTrapHandler)rt->interruptHandler;
233 0     if (closurep)
234 0         *closurep = rt->interruptHandlerData;
235 0     rt->interruptHandler = 0;
236 0     rt->interruptHandlerData = 0;
237 0     return JS_TRUE;
238 }
239
240 /************************************************************************/
241
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;
251
252 #define HoldWatchPoint(wp) ((wp)->nrefs++)
253
254 static JSBool
255 DropWatchPoint(JSContext *cx, JSWatchPoint *wp)
256 0 {
257 0     JSScopeProperty *sprop;
258
259 0     if (--wp->nrefs != 0)
260 0         return JS_TRUE;
261
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 0     JS_REMOVE_LINK(&wp->links);
267 0     sprop = wp->sprop;
268 0     if (!js_GetWatchedSetter(cx->runtime, NULL, sprop)) {
269 0         sprop = js_ChangeNativePropertyAttrs(cx, wp->object, sprop,
270                                              0, sprop->attrs,
271                                              sprop->getter, wp->setter);
272 0         if (!sprop)
273 0             return JS_FALSE;
274     }
275 0     js_RemoveRoot(cx->runtime, &wp->closure);
276 0     JS_free(cx, wp);
277 0     return JS_TRUE;
278 }
279
280 void
281 js_MarkWatchPoints(JSRuntime *rt)
282 0 {
283 0     JSWatchPoint *wp;
284
285 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
286          wp != (JSWatchPoint *)&rt->watchPointList;
287          wp = (JSWatchPoint *)wp->links.next) {
288 0         MARK_SCOPE_PROPERTY(wp->sprop);
289     }
290 }
291
292 static JSWatchPoint *
293 FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
294 0 {
295 0     JSWatchPoint *wp;
296
297 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
298          wp != (JSWatchPoint *)&rt->watchPointList;
299          wp = (JSWatchPoint *)wp->links.next) {
300 0         if (wp->object == scope->object && wp->sprop->id == id)
301 0             return wp;
302     }
303 0     return NULL;
304 }
305
306 JSScopeProperty *
307 js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
308 0 {
309 0     JSWatchPoint *wp;
310
311 0     wp = FindWatchPoint(rt, scope, id);
312 0     if (!wp)
313 0         return NULL;
314 0     return wp->sprop;
315 }
316
317 JSPropertyOp
318 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
319                     const JSScopeProperty *sprop)
320 0 {
321 0     JSWatchPoint *wp;
322
323 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
324          wp != (JSWatchPoint *)&rt->watchPointList;
325          wp = (JSWatchPoint *)wp->links.next) {
326 0         if ((!scope || wp->object == scope->object) && wp->sprop == sprop)
327 0             return wp->setter;
328     }
329 0     return NULL;
330 }
331
332 JSBool JS_DLL_CALLBACK
333 js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
334 0 {
335 0     JSRuntime *rt;
336 0     JSWatchPoint *wp;
337 0     JSScopeProperty *sprop;
338 0     jsval userid;
339 0     JSScope *scope;
340 0     JSBool ok;
341
342 0     rt = cx->runtime;
343 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
344          wp != (JSWatchPoint *)&rt->watchPointList;
345          wp = (JSWatchPoint *)wp->links.next) {
346 0         sprop = wp->sprop;
347 0         if (wp->object == obj && SPROP_USERID(sprop) == id) {
348 0             JS_LOCK_OBJ(cx, obj);
349 0             userid = SPROP_USERID(sprop);
350 0             scope = OBJ_SCOPE(obj);
351 0             JS_UNLOCK_OBJ(cx, obj);
352 0             HoldWatchPoint(wp);
353 0             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 0             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 0                 JSObject *funobj = (JSObject *) wp->closure;
365 0                 JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, funobj);
366 0                 JSStackFrame frame;
367
368 0                 memset(&frame, 0, sizeof(frame));
369 0                 frame.script = fun->script;
370 0                 frame.fun = fun;
371 0                 frame.down = cx->fp;
372 0                 cx->fp = &frame;
373 0                 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 0                 cx->fp = frame.down;
379             }
380 0             return DropWatchPoint(cx, wp);
381         }
382     }
383 0     JS_ASSERT(0);       /* XXX can't happen */
384 0     return JS_FALSE;
385 }
386
387 JSBool JS_DLL_CALLBACK
388 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
389                      jsval *rval)
390 0 {
391 0     JSObject *funobj;
392 0     JSFunction *wrapper;
393 0     jsval userid;
394
395 0     funobj = JSVAL_TO_OBJECT(argv[-2]);
396 0     wrapper = (JSFunction *) JS_GetPrivate(cx, funobj);
397 0     userid = ATOM_KEY(wrapper->atom);
398 0     *rval = argv[0];
399 0     return js_watch_set(cx, obj, userid, rval);
400 }
401
402 JSPropertyOp
403 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
404 0 {
405 0     JSAtom *atom;
406 0     JSFunction *wrapper;
407
408 0     if (!(attrs & JSPROP_SETTER))
409 0         return &js_watch_set;   /* & to silence schoolmarmish MSVC */
410
411 0     if (!JSVAL_IS_INT(id)) {
412 0         atom = (JSAtom *)id;
413     } else {
414 0         atom = js_AtomizeInt(cx, JSVAL_TO_INT(id), 0);
415 0         if (!atom)
416 0             return NULL;
417     }
418 0     wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
419                              OBJ_GET_PARENT(cx, (JSObject *)setter),
420                              atom);
421 0     if (!wrapper)
422 0         return NULL;
423 0     return (JSPropertyOp) wrapper->object;
424 }
425
426 JS_PUBLIC_API(JSBool)
427 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
428                  JSWatchPointHandler handler, void *closure)
429 0 {
430 0     JSAtom *atom;
431 0     jsid propid;
432 0     JSObject *pobj;
433 0     JSScopeProperty *sprop;
434 0     JSRuntime *rt;
435 0     JSWatchPoint *wp;
436 0     JSPropertyOp watcher;
437
438 0     if (!OBJ_IS_NATIVE(obj)) {
439 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
440                              OBJ_GET_CLASS(cx, obj)->name);
441 0         return JS_FALSE;
442     }
443
444 0     if (JSVAL_IS_INT(id)) {
445 0         propid = (jsid)id;
446 0         atom = NULL;
447     } else {
448 0         atom = js_ValueToStringAtom(cx, id);
449 0         if (!atom)
450 0             return JS_FALSE;
451 0         propid = (jsid)atom;
452     }
453
454 0     if (!js_LookupProperty(cx, obj, propid, &pobj, (JSProperty **)&sprop))
455 0         return JS_FALSE;
456 0     rt = cx->runtime;
457 0     if (!sprop) {
458         /* Check for a deleted symbol watchpoint, which holds its property. */
459 0         sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
460 0         if (!sprop) {
461             /* Make a new property in obj so we can watch for the first set. */
462 0             if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID,
463                                    NULL, NULL, JSPROP_ENUMERATE,
464                                    (JSProperty **)&sprop)) {
465 0                 sprop = NULL;
466             }
467         }
468 0     } else if (pobj != obj) {
469         /* Clone the prototype property so we can watch the right object. */
470 0         jsval value;
471 0         JSPropertyOp getter, setter;
472 0         uintN attrs;
473
474 0         if (OBJ_IS_NATIVE(pobj)) {
475 0             value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
476                     ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
477                     : JSVAL_VOID;
478 0             getter = sprop->getter;
479 0             setter = sprop->setter;
480 0             attrs = sprop->attrs;
481         } else {
482 0             if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) {
483 0                 OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
484 0                 return JS_FALSE;
485             }
486 0             getter = setter = JS_PropertyStub;
487 0             attrs = JSPROP_ENUMERATE;
488         }
489 0         OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
490
491 0         if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs,
492                                (JSProperty **)&sprop)) {
493 0             sprop = NULL;
494         }
495     }
496 0     if (!sprop)
497 0         return JS_FALSE;
498
499 0     wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
500 0     if (!wp) {
501 0         watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter);
502 0         if (!watcher)
503 0             return JS_FALSE;
504
505 0         wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);
506 0         if (!wp)
507 0             return JS_FALSE;
508 0         wp->handler = NULL;
509 0         wp->closure = NULL;
510 0         if (!js_AddRoot(cx, &wp->closure, "wp->closure")) {
511 0             JS_free(cx, wp);
512 0             return JS_FALSE;
513         }
514 0         JS_APPEND_LINK(&wp->links, &rt->watchPointList);
515 0         wp->object = obj;
516 0         wp->sprop = sprop;
517 0         JS_ASSERT(sprop->setter != js_watch_set);
518 0         wp->setter = sprop->setter;
519 0         wp->nrefs = 1;
520 0         sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
521                                              sprop->getter, watcher);
522 0         if (!sprop)
523 0             return DropWatchPoint(cx, wp);
524     }
525 0     wp->handler = handler;
526 0     wp->closure = closure;
527 0     OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);
528 0     return JS_TRUE;
529 }
530
531 JS_PUBLIC_API(JSBool)
532 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,
533                    JSWatchPointHandler *handlerp, void **closurep)
534 0 {
535 0     JSRuntime *rt;
536 0     JSWatchPoint *wp;
537
538 0     rt = cx->runtime;
539 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
540          wp != (JSWatchPoint *)&rt->watchPointList;
541          wp = (JSWatchPoint *)wp->links.next) {
542 0         if (wp->object == obj && SPROP_USERID(wp->sprop) == id) {
543 0             if (handlerp)
544 0                 *handlerp = wp->handler;
545 0             if (closurep)
546 0                 *closurep = wp->closure;
547 0             return DropWatchPoint(cx, wp);
548         }
549     }
550 0     if (handlerp)
551 0         *handlerp = NULL;
552 0     if (closurep)
553 0         *closurep = NULL;
554 0     return JS_TRUE;
555 }
556
557 JS_PUBLIC_API(JSBool)
558 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
559 0 {
560 0     JSRuntime *rt;
561 0     JSWatchPoint *wp, *next;
562
563 0     rt = cx->runtime;
564 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
565          wp != (JSWatchPoint *)&rt->watchPointList;
566          wp = next) {
567 0         next = (JSWatchPoint *)wp->links.next;
568 0         if (wp->object == obj && !DropWatchPoint(cx, wp))
569 0             return JS_FALSE;
570     }
571 0     return JS_TRUE;
572 }
573
574 JS_PUBLIC_API(JSBool)
575 JS_ClearAllWatchPoints(JSContext *cx)
576 0 {
577 0     JSRuntime *rt;
578 0     JSWatchPoint *wp, *next;
579
580 0     rt = cx->runtime;
581 0     for (wp = (JSWatchPoint *)rt->watchPointList.next;
582          wp != (JSWatchPoint *)&rt->watchPointList;
583          wp = next) {
584 0         next = (JSWatchPoint *)wp->links.next;
585 0         if (!DropWatchPoint(cx, wp))
586 0             return JS_FALSE;
587     }
588 0     return JS_TRUE;
589 }
590
591 /************************************************************************/
592
593 JS_PUBLIC_API(uintN)
594 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
595 0 {
596 0     return js_PCToLineNumber(cx, script, pc);
597 }
598
599 JS_PUBLIC_API(jsbytecode *)
600 JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
601 0 {
602 0     return js_LineNumberToPC(script, lineno);
603 }
604
605 JS_PUBLIC_API(JSScript *)
606 JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
607 0 {
608 0     return fun->script;
609 }
610
611 JS_PUBLIC_API(JSPrincipals *)
612 JS_GetScriptPrincipals(JSContext *cx, JSScript *script)
613 0 {
614 0     return script->principals;
615 }
616
617 /************************************************************************/
618
619 /*
620  *  Stack Frame Iterator
621  */
622 JS_PUBLIC_API(JSStackFrame *)
623 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
624 0 {
625 0     *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down;
626 0     return *iteratorp;
627 }
628
629 JS_PUBLIC_API(JSScript *)
630 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
631 0 {
632 0     return fp->script;
633 }
634
635 JS_PUBLIC_API(jsbytecode *)
636 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
637 0 {
638 0     return fp->pc;
639 }
640
641 JS_PUBLIC_API(JSStackFrame *)
642 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
643 0 {
644 0     if (!fp)
645 0         fp = cx->fp;
646 0     while ((fp = fp->down) != NULL) {
647 0         if (fp->script)
648 0             return fp;
649     }
650 0     return NULL;
651 }
652
653 JS_PUBLIC_API(JSPrincipals *)
654 JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp)
655 0 {
656 0     if (fp->fun && cx->findObjectPrincipals) {
657 0         JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]);
658
659 0         if (fp->fun->object != callee)
660 0             return cx->findObjectPrincipals(cx, callee);
661         /* FALL THROUGH */
662     }
663 0     if (fp->script)
664 0         return fp->script->principals;
665 0     return NULL;
666 }
667
668 JS_PUBLIC_API(JSPrincipals *)
669 JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller)
670 0 {
671 0     if (cx->findObjectPrincipals)
672 0         return cx->findObjectPrincipals(cx, JSVAL_TO_OBJECT(fp->argv[-2]));
673 0     if (!caller)
674 0         return NULL;
675 0     return JS_StackFramePrincipals(cx, caller);
676 }
677
678 JS_PUBLIC_API(void *)
679 JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)
680 0 {
681 0     if (fp->annotation && fp->script) {
682 0         JSPrincipals *principals = JS_StackFramePrincipals(cx, fp);
683
684 0         if (principals && principals->globalPrivilegesEnabled(cx, principals)) {
685             /*
686              * Give out an annotation only if privileges have not been revoked
687              * or disabled globally.
688              */
689 0             return fp->annotation;
690         }
691     }
692
693 0     return NULL;
694 }
695
696 JS_PUBLIC_API(void)
697 JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)
698 0 {
699 0     fp->annotation = annotation;
700 }
701
702 JS_PUBLIC_API(void *)
703 JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)
704 0 {
705 0     JSPrincipals *principals;
706
707 0     principals = JS_StackFramePrincipals(cx, fp);
708 0     if (!principals)
709 0         return NULL;
710 0     return principals->getPrincipalArray(cx, principals);
711 }
712
713 JS_PUBLIC_API(JSBool)
714 JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)
715 0 {
716 0     return !fp->script;
717 }
718
719 /* this is deprecated, use JS_GetFrameScopeChain instead */
720 JS_PUBLIC_API(JSObject *)
721 JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)
722 0 {
723 0     return fp->scopeChain;
724 }
725
726 JS_PUBLIC_API(JSObject *)
727 JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
728 0 {
729     /* Force creation of argument and call objects if not yet created */
730 0     (void) JS_GetFrameCallObject(cx, fp);
731 0     return fp->scopeChain;
732 }
733
734 JS_PUBLIC_API(JSObject *)
735 JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
736 0 {
737 0     if (! fp->fun)
738 0         return NULL;
739 #if JS_HAS_ARGS_OBJECT
740     /* Force creation of argument object if not yet created */
741 0     (void) js_GetArgsObject(cx, fp);
742 #endif
743 #if JS_HAS_CALL_OBJECT
744     /*
745      * XXX ill-defined: null return here means error was reported, unlike a
746      *     null returned above or in the #else
747      */
748 0     return js_GetCallObject(cx, fp, NULL);
749 #else
750     return NULL;
751 #endif /* JS_HAS_CALL_OBJECT */
752 }
753
754
755 JS_PUBLIC_API(JSObject *)
756 JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
757 0 {
758 0     return fp->thisp;
759 }
760
761 JS_PUBLIC_API(JSFunction *)
762 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
763 0 {
764 0     return fp->fun;
765 }
766
767 JS_PUBLIC_API(JSObject *)
768 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
769 0 {
770 0     return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL;
771 }
772
773 JS_PUBLIC_API(JSBool)
774 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
775 0 {
776 0     return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
777 }
778
779 JS_PUBLIC_API(JSBool)
780 JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)
781 0 {
782 0     return (fp->flags & JSFRAME_DEBUGGER) != 0;
783 }
784
785 JS_PUBLIC_API(jsval)
786 JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)
787 0 {
788 0     return fp->rval;
789 }
790
791 JS_PUBLIC_API(void)
792 JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)
793 0 {
794 0     fp->rval = rval;
795 }
796
797 /************************************************************************/
798
799 JS_PUBLIC_API(const char *)
800 JS_GetScriptFilename(JSContext *cx, JSScript *script)
801 0 {
802 0     return script->filename;
803 }
804
805 JS_PUBLIC_API(uintN)
806 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
807 0 {
808 0     return script->lineno;
809 }
810
811 JS_PUBLIC_API(uintN)
812 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
813 0 {
814 0     return js_GetScriptLineExtent(script);
815 }
816
817 JS_PUBLIC_API(JSVersion)
818 JS_GetScriptVersion(JSContext *cx, JSScript *script)
819 0 {
820 0     return script->version;
821 }
822
823 /***************************************************************************/
824
825 JS_PUBLIC_API(void)
826 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
827 0 {
828 0     rt->newScriptHook = hook;
829 0     rt->newScriptHookData = callerdata;
830 }
831
832 JS_PUBLIC_API(void)
833 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
834                         void *callerdata)
835 0 {
836 0     rt->destroyScriptHook = hook;
837 0     rt->destroyScriptHookData = callerdata;
838 }
839
840 /***************************************************************************/
841
842 JS_PUBLIC_API(JSBool)
843 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
844                           const jschar *bytes, uintN length,
845                           const char *filename, uintN lineno,
846                           jsval *rval)
847 0 {
848 0     uint32 flags;
849 0     JSScript *script;
850 0     JSBool ok;
851
852     /*
853      * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL
854      * flags to the code generator (see js_EmitTree's TOK_SEMI case).
855      */
856 0     flags = fp->flags;
857 0     fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL;
858 0     script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain,
859                                              JS_StackFramePrincipals(cx, fp),
860                                              bytes, length, filename, lineno);
861 0     fp->flags = flags;
862 0     if (!script)
863 0         return JS_FALSE;
864
865 0     ok = js_Execute(cx, fp->scopeChain, script, fp,
866                     JSFRAME_DEBUGGER | JSFRAME_EVAL, rval);
867 0     js_DestroyScript(cx, script);
868 0     return ok;
869 }
870
871 JS_PUBLIC_API(JSBool)
872 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
873                         const char *bytes, uintN length,
874                         const char *filename, uintN lineno,
875                         jsval *rval)
876 0 {
877 0     jschar *chars;
878 0     JSBool ok;
879
880 0     chars = js_InflateString(cx, bytes, length);
881 0     if (!chars)
882 0         return JS_FALSE;
883 0     ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno,
884                                    rval);
885 0     JS_free(cx, chars);
886
887 0     return ok;
888 }
889
890 /************************************************************************/
891
892 /* XXXbe this all needs to be reworked to avoid requiring JSScope types. */
893
894 JS_PUBLIC_API(JSScopeProperty *)
895 JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
896 0 {
897 0     JSScopeProperty *sprop;
898 0     JSScope *scope;
899
900 0     sprop = *iteratorp;
901 0     scope = OBJ_SCOPE(obj);
902
903     /* XXXbe minor(?) incompatibility: iterate in reverse definition order */
904 0     if (!sprop) {
905 0         sprop = SCOPE_LAST_PROP(scope);
906     } else {
907 0         while ((sprop = sprop->parent) != NULL) {
908 0             if (!SCOPE_HAD_MIDDLE_DELETE(scope))
909 0                 break;
910 0             if (SCOPE_HAS_PROPERTY(scope, sprop))
911 0                 break;
912         }
913     }
914 0     *iteratorp = sprop;
915 0     return sprop;
916 }
917
918 JS_PUBLIC_API(JSBool)
919 JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
920                    JSPropertyDesc *pd)
921 0 {
922 0     JSPropertyOp getter;
923 0     JSScope *scope;
924 0     JSScopeProperty *aprop;
925 0     jsval lastException;
926 0     JSBool wasThrowing;
927
928 0     pd->id = ID_TO_VALUE(sprop->id);
929
930 0     wasThrowing = cx->throwing;
931 0     if (wasThrowing) {
932 0         lastException = cx->exception;
933 0         if (JSVAL_IS_GCTHING(lastException) &&
934             !js_AddRoot(cx, &lastException, "lastException")) {
935 0                 return JS_FALSE;
936         }
937 0         cx->throwing = JS_FALSE;
938     }
939
940 0     if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) {
941 0         if (!cx->throwing) {
942 0             pd->flags = JSPD_ERROR;
943 0             pd->value = JSVAL_VOID;
944         } else {
945 0             pd->flags = JSPD_EXCEPTION;
946 0             pd->value = cx->exception;
947         }
948     } else {
949 0         pd->flags = 0;
950     }
951
952 0     cx->throwing = wasThrowing;
953 0     if (wasThrowing) {
954 0         cx->exception = lastException;
955 0         if (JSVAL_IS_GCTHING(lastException))
956 0             js_RemoveRoot(cx->runtime, &lastException);
957     }
958
959 0     getter = sprop->getter;
960 0     pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
961               | ((sprop->attrs & JSPROP_READONLY)  ? JSPD_READONLY  : 0)
962               | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0)
963 #if JS_HAS_CALL_OBJECT
964               | ((getter == js_GetCallVariable)    ? JSPD_VARIABLE  : 0)
965 #endif /* JS_HAS_CALL_OBJECT */
966               | ((getter == js_GetArgument)        ? JSPD_ARGUMENT  : 0)
967               | ((getter == js_GetLocalVariable)   ? JSPD_VARIABLE  : 0);
968 #if JS_HAS_CALL_OBJECT
969     /* for Call Object 'real' getter isn't passed in to us */
970 0     if (OBJ_GET_CLASS(cx, obj) == &js_CallClass &&
971         getter == js_CallClass.getProperty) {
972         /*
973          * Property of a heavyweight function's variable object having the
974          * class-default getter.  It's either an argument if permanent, or a
975          * nested function if impermanent.  Local variables have a special
976          * getter (js_GetCallVariable, tested above) and setter, and not the
977          * class default.
978          */
979 0         pd->flags |= (sprop->attrs & JSPROP_PERMANENT)
980                      ? JSPD_ARGUMENT
981                      : JSPD_VARIABLE;
982     }
983 #endif /* JS_HAS_CALL_OBJECT */
984 0     pd->spare = 0;
985 0     pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))
986                ? sprop->shortid
987                : 0;
988 0     pd->alias = JSVAL_VOID;
989 0     scope = OBJ_SCOPE(obj);
990 0     if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
991 0         for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
992 0             if (aprop != sprop && aprop->slot == sprop->slot) {
993 0                 pd->alias = ID_TO_VALUE(aprop->id);
994 0                 break;
995             }
996         }
997     }
998 0     return JS_TRUE;
999 }
1000
1001 JS_PUBLIC_API(JSBool)
1002 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
1003 0 {
1004 0     JSClass *clasp;
1005 0     JSScope *scope;
1006 0     uint32 i, n;
1007 0     JSPropertyDesc *pd;
1008 0     JSScopeProperty *sprop;
1009
1010 0     clasp = OBJ_GET_CLASS(cx, obj);
1011 0     if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
1012 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1013                              JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
1014 0         return JS_FALSE;
1015     }
1016 0     if (!clasp->enumerate(cx, obj))
1017 0         return JS_FALSE;
1018
1019     /* have no props, or object's scope has not mutated from that of proto */
1020 0     scope = OBJ_SCOPE(obj);
1021 0     if (scope->object != obj || scope->entryCount == 0) {
1022 0         pda->length = 0;
1023 0         pda->array = NULL;
1024 0         return JS_TRUE;
1025     }
1026
1027 0     n = scope->entryCount;
1028 0     if (n > scope->map.nslots)
1029 0         n = scope->map.nslots;
1030 0     pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));
1031 0     if (!pd)
1032 0         return JS_FALSE;
1033 0     i = 0;
1034 0     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1035 0         if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
1036 0             continue;
1037 0         if (!js_AddRoot(cx, &pd[i].id, NULL))
1038 0             goto bad;
1039 0         if (!js_AddRoot(cx, &pd[i].value, NULL))
1040 0             goto bad;
1041 0         if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i]))
1042 0             goto bad;
1043 0         if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
1044 0             goto bad;
1045 0         if (++i == n)
1046 0             break;
1047     }
1048 0     pda->length = i;
1049 0     pda->array = pd;
1050 0     return JS_TRUE;
1051
1052 bad:
1053 0     pda->length = i + 1;
1054 0     pda->array = pd;
1055 0     JS_PutPropertyDescArray(cx, pda);
1056 0     return JS_FALSE;
1057 }
1058
1059 JS_PUBLIC_API(void)
1060 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
1061 0 {
1062 0     JSPropertyDesc *pd;
1063 0     uint32 i;
1064
1065 0     pd = pda->array;
1066 0     for (i = 0; i < pda->length; i++) {
1067 0         js_RemoveRoot(cx->runtime, &pd[i].id);
1068 0         js_RemoveRoot(cx->runtime, &pd[i].value);
1069 0         if (pd[i].flags & JSPD_ALIAS)
1070 0             js_RemoveRoot(cx->runtime, &pd[i].alias);
1071     }
1072 0     JS_free(cx, pd);
1073 }
1074
1075 /************************************************************************/
1076
1077 JS_PUBLIC_API(JSBool)
1078 JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
1079 0 {
1080 0     rt->debuggerHandler = handler;
1081 0     rt->debuggerHandlerData = closure;
1082 0     return JS_TRUE;
1083 }
1084
1085 JS_PUBLIC_API(JSBool)
1086 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
1087 0 {
1088 0     rt->sourceHandler = handler;
1089 0     rt->sourceHandlerData = closure;
1090 0     return JS_TRUE;
1091 }
1092
1093 JS_PUBLIC_API(JSBool)
1094 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1095 0 {
1096 0     rt->executeHook = hook;
1097 0     rt->executeHookData = closure;
1098 0     return JS_TRUE;
1099 }
1100
1101 JS_PUBLIC_API(JSBool)
1102 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
1103 0 {
1104 0     rt->callHook = hook;
1105 0     rt->callHookData = closure;
1106 0     return JS_TRUE;
1107 }
1108
1109 JS_PUBLIC_API(JSBool)
1110 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)
1111 0 {
1112 0     rt->objectHook = hook;
1113 0     rt->objectHookData = closure;
1114 0     return JS_TRUE;
1115 }
1116
1117 JS_PUBLIC_API(JSBool)
1118 JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
1119 0 {
1120 0     rt->throwHook = hook;
1121 0     rt->throwHookData = closure;
1122 0     return JS_TRUE;
1123 }
1124
1125 JS_PUBLIC_API(JSBool)
1126 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
1127 0 {
1128 0     rt->debugErrorHook = hook;
1129 0     rt->debugErrorHookData = closure;
1130 0     return JS_TRUE;
1131 }
1132
1133 /************************************************************************/
1134
1135 JS_PUBLIC_API(size_t)
1136 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
1137 0 {
1138 0     size_t nbytes;
1139 0     JSScope *scope;
1140
1141 0     nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0];
1142 0     if (OBJ_IS_NATIVE(obj)) {
1143 0         scope = OBJ_SCOPE(obj);
1144 0         if (scope->object == obj) {
1145 0             nbytes += sizeof *scope;
1146 0             nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
1147         }
1148     }
1149 0     return nbytes;
1150 }
1151
1152 static size_t
1153 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
1154 0 {
1155 0     size_t nbytes;
1156
1157 0     nbytes = sizeof *atom;
1158 0     if (ATOM_IS_STRING(atom)) {
1159 0         nbytes += sizeof(JSString);
1160 0         nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar);
1161 0     } else if (ATOM_IS_DOUBLE(atom)) {
1162 0         nbytes += sizeof(jsdouble);
1163 0     } else if (ATOM_IS_OBJECT(atom)) {
1164 0         nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom));
1165     }
1166 0     return nbytes;
1167 }
1168
1169 JS_PUBLIC_API(size_t)
1170 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
1171 0 {
1172 0     size_t nbytes, obytes;
1173 0     JSObject *obj;
1174 0     JSAtom *atom;
1175
1176 0     nbytes = sizeof *fun;
1177 0     JS_ASSERT(fun->nrefs);
1178 0     obj = fun->object;
1179 0     if (obj) {
1180 0         obytes = JS_GetObjectTotalSize(cx, obj);
1181 0         if (fun->nrefs > 1)
1182 0             obytes = JS_HOWMANY(obytes, fun->nrefs);
1183 0         nbytes += obytes;
1184     }
1185 0     if (fun->script)
1186 0         nbytes += JS_GetScriptTotalSize(cx, fun->script);
1187 0     atom = fun->atom;
1188 0     if (atom)
1189 0         nbytes += GetAtomTotalSize(cx, atom);
1190 0     return nbytes;
1191 }
1192
1193 #include "jsemit.h"
1194
1195 JS_PUBLIC_API(size_t)
1196 JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
1197 0 {
1198 0     size_t nbytes, pbytes;
1199 0     JSObject *obj;
1200 0     jsatomid i;
1201 0     jssrcnote *sn, *notes;
1202 0     JSTryNote *tn, *tnotes;
1203 0     JSPrincipals *principals;
1204
1205 0     nbytes = sizeof *script;
1206 0     obj = script->object;
1207 0     if (obj)
1208 0         nbytes += JS_GetObjectTotalSize(cx, obj);
1209
1210 0     nbytes += script->length * sizeof script->code[0];
1211 0     nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
1212 0     for (i = 0; i < script->atomMap.length; i++)
1213 0         nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
1214
1215 0     if (script->filename)
1216 0         nbytes += strlen(script->filename) + 1;
1217
1218 0     notes = SCRIPT_NOTES(script);
1219 0     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1220 0         continue;
1221 0     nbytes += (sn - notes + 1) * sizeof *sn;
1222
1223 0     tnotes = script->trynotes;
1224 0     if (tnotes) {
1225 0         for (tn = tnotes; tn->catchStart; tn++)
1226 0             continue;
1227 0         nbytes += (tn - tnotes + 1) * sizeof *tn;
1228     }
1229
1230 0     principals = script->principals;
1231 0     if (principals) {
1232 0         JS_ASSERT(principals->refcount);
1233 0         pbytes = sizeof *principals;
1234 0         if (principals->refcount > 1)
1235 0             pbytes = JS_HOWMANY(pbytes, principals->refcount);
1236 0         nbytes += pbytes;
1237     }
1238
1239 0     return nbytes;