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 = ®s; |
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(¶ms); |
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 */ |