Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS debugging API.
9 */
11 #include "js/OldDebugAPI.h"
13 #include <string.h>
15 #include "jscntxt.h"
16 #include "jsfun.h"
17 #include "jsgc.h"
18 #include "jsobj.h"
19 #include "jsopcode.h"
20 #include "jsprf.h"
21 #include "jsscript.h"
22 #include "jsstr.h"
23 #include "jstypes.h"
24 #include "jswatchpoint.h"
26 #include "frontend/SourceNotes.h"
27 #include "jit/AsmJSModule.h"
28 #include "vm/Debugger.h"
29 #include "vm/Shape.h"
31 #include "jsatominlines.h"
32 #include "jsinferinlines.h"
33 #include "jsscriptinlines.h"
35 #include "vm/Debugger-inl.h"
36 #include "vm/Interpreter-inl.h"
37 #include "vm/Stack-inl.h"
39 using namespace js;
40 using namespace js::gc;
42 using mozilla::PodZero;
44 JS_PUBLIC_API(bool)
45 JS_GetDebugMode(JSContext *cx)
46 {
47 return cx->compartment()->debugMode();
48 }
50 JS_PUBLIC_API(bool)
51 JS_SetDebugMode(JSContext *cx, bool debug)
52 {
53 return JS_SetDebugModeForCompartment(cx, cx->compartment(), debug);
54 }
56 JS_PUBLIC_API(void)
57 JS_SetRuntimeDebugMode(JSRuntime *rt, bool debug)
58 {
59 rt->debugMode = !!debug;
60 }
62 static bool
63 IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
64 {
65 ScriptFrameIter iter(cx);
66 JS_ASSERT(iter.abstractFramePtr() == frame);
67 return iter.isConstructing();
68 }
70 JSTrapStatus
71 js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
72 {
73 JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
75 if (!frame.script()->selfHosted()) {
76 JSAbstractFramePtr jsframe(frame.raw(), pc);
77 if (frame.isFramePushedByExecute()) {
78 if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
79 frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
80 true, 0, cx->runtime()->debugHooks.executeHookData));
81 } else {
82 if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
83 frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
84 true, 0, cx->runtime()->debugHooks.callHookData));
85 }
86 }
88 RootedValue rval(cx);
89 JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval);
90 switch (status) {
91 case JSTRAP_CONTINUE:
92 break;
93 case JSTRAP_THROW:
94 cx->setPendingException(rval);
95 break;
96 case JSTRAP_ERROR:
97 cx->clearPendingException();
98 break;
99 case JSTRAP_RETURN:
100 frame.setReturnValue(rval);
101 break;
102 default:
103 MOZ_ASSUME_UNREACHABLE("bad Debugger::onEnterFrame JSTrapStatus value");
104 }
105 return status;
106 }
108 bool
109 js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
110 {
111 JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
113 bool ok = okArg;
115 // We don't add hook data for self-hosted scripts, so we don't need to check for them, here.
116 if (void *hookData = frame.maybeHookData()) {
117 JSAbstractFramePtr jsframe(frame.raw(), pc);
118 if (frame.isFramePushedByExecute()) {
119 if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
120 hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
121 } else {
122 if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
123 hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
124 }
125 }
127 return Debugger::onLeaveFrame(cx, frame, ok);
128 }
130 JSTrapStatus
131 js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
132 {
133 JS_ASSERT(cx->compartment()->debugMode());
135 if (!cx->runtime()->debugHooks.throwHook && cx->compartment()->getDebuggees().empty())
136 return JSTRAP_CONTINUE;
138 /* Call debugger throw hook if set. */
139 RootedValue rval(cx);
140 JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval);
141 if (status == JSTRAP_CONTINUE) {
142 if (JSThrowHook handler = cx->runtime()->debugHooks.throwHook) {
143 RootedScript script(cx, frame.script());
144 status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.throwHookData);
145 }
146 }
148 switch (status) {
149 case JSTRAP_ERROR:
150 cx->clearPendingException();
151 break;
153 case JSTRAP_RETURN:
154 cx->clearPendingException();
155 frame.setReturnValue(rval);
156 break;
158 case JSTRAP_THROW:
159 cx->setPendingException(rval);
160 break;
162 case JSTRAP_CONTINUE:
163 break;
165 default:
166 MOZ_ASSUME_UNREACHABLE("Invalid trap status");
167 }
169 return status;
170 }
172 JS_FRIEND_API(bool)
173 JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
174 {
175 for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
176 // Invalidate a zone at a time to avoid doing a zone-wide CellIter
177 // per compartment.
178 AutoDebugModeInvalidation invalidate(zone);
179 for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
180 // Ignore special compartments (atoms, JSD compartments)
181 if (c->principals) {
182 if (!c->setDebugModeFromC(cx, !!debug, invalidate))
183 return false;
184 }
185 }
186 }
187 return true;
188 }
190 JS_FRIEND_API(bool)
191 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
192 {
193 AutoDebugModeInvalidation invalidate(comp);
194 return comp->setDebugModeFromC(cx, !!debug, invalidate);
195 }
197 static bool
198 CheckDebugMode(JSContext *cx)
199 {
200 bool debugMode = JS_GetDebugMode(cx);
201 /*
202 * :TODO:
203 * This probably should be an assertion, since it's indicative of a severe
204 * API misuse.
205 */
206 if (!debugMode) {
207 JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
208 nullptr, JSMSG_NEED_DEBUG_MODE);
209 }
210 return debugMode;
211 }
213 JS_PUBLIC_API(bool)
214 JS_SetSingleStepMode(JSContext *cx, HandleScript script, bool singleStep)
215 {
216 assertSameCompartment(cx, script);
218 if (!CheckDebugMode(cx))
219 return false;
221 return script->setStepModeFlag(cx, singleStep);
222 }
224 JS_PUBLIC_API(bool)
225 JS_SetTrap(JSContext *cx, HandleScript script, jsbytecode *pc, JSTrapHandler handler,
226 HandleValue closure)
227 {
228 assertSameCompartment(cx, script, closure);
230 if (!CheckDebugMode(cx))
231 return false;
233 BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
234 if (!site)
235 return false;
236 site->setTrap(cx->runtime()->defaultFreeOp(), handler, closure);
237 return true;
238 }
240 JS_PUBLIC_API(void)
241 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
242 JSTrapHandler *handlerp, jsval *closurep)
243 {
244 if (BreakpointSite *site = script->getBreakpointSite(pc)) {
245 site->clearTrap(cx->runtime()->defaultFreeOp(), handlerp, closurep);
246 } else {
247 if (handlerp)
248 *handlerp = nullptr;
249 if (closurep)
250 *closurep = JSVAL_VOID;
251 }
252 }
254 JS_PUBLIC_API(void)
255 JS_ClearScriptTraps(JSRuntime *rt, JSScript *script)
256 {
257 script->clearTraps(rt->defaultFreeOp());
258 }
260 JS_PUBLIC_API(void)
261 JS_ClearAllTrapsForCompartment(JSContext *cx)
262 {
263 cx->compartment()->clearTraps(cx->runtime()->defaultFreeOp());
264 }
266 JS_PUBLIC_API(bool)
267 JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
268 {
269 rt->debugHooks.interruptHook = hook;
270 rt->debugHooks.interruptHookData = closure;
272 for (ActivationIterator iter(rt); !iter.done(); ++iter) {
273 if (iter->isInterpreter())
274 iter->asInterpreter()->enableInterruptsUnconditionally();
275 }
277 return true;
278 }
280 JS_PUBLIC_API(bool)
281 JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
282 {
283 if (hoop)
284 *hoop = rt->debugHooks.interruptHook;
285 if (closurep)
286 *closurep = rt->debugHooks.interruptHookData;
287 rt->debugHooks.interruptHook = 0;
288 rt->debugHooks.interruptHookData = 0;
289 return true;
290 }
292 /************************************************************************/
294 JS_PUBLIC_API(bool)
295 JS_SetWatchPoint(JSContext *cx, HandleObject origobj, HandleId id,
296 JSWatchPointHandler handler, HandleObject closure)
297 {
298 assertSameCompartment(cx, origobj);
300 RootedObject obj(cx, GetInnerObject(cx, origobj));
301 if (!obj)
302 return false;
304 RootedId propid(cx);
306 if (JSID_IS_INT(id)) {
307 propid = id;
308 } else if (JSID_IS_OBJECT(id)) {
309 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP);
310 return false;
311 } else {
312 RootedValue val(cx, IdToValue(id));
313 if (!ValueToId<CanGC>(cx, val, &propid))
314 return false;
315 }
317 if (!obj->isNative() || obj->is<TypedArrayObject>()) {
318 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
319 obj->getClass()->name);
320 return false;
321 }
323 /*
324 * Use sparse indexes for watched objects, as dense elements can be written
325 * to without checking the watchpoint map.
326 */
327 if (!JSObject::sparsifyDenseElements(cx, obj))
328 return false;
330 types::MarkTypePropertyNonData(cx, obj, propid);
332 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
333 if (!wpmap) {
334 wpmap = cx->runtime()->new_<WatchpointMap>();
335 if (!wpmap || !wpmap->init()) {
336 js_ReportOutOfMemory(cx);
337 return false;
338 }
339 cx->compartment()->watchpointMap = wpmap;
340 }
341 return wpmap->watch(cx, obj, propid, handler, closure);
342 }
344 JS_PUBLIC_API(bool)
345 JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
346 JSWatchPointHandler *handlerp, JSObject **closurep)
347 {
348 assertSameCompartment(cx, obj, id);
350 if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
351 wpmap->unwatch(obj, id, handlerp, closurep);
352 return true;
353 }
355 JS_PUBLIC_API(bool)
356 JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
357 {
358 assertSameCompartment(cx, obj);
360 if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
361 wpmap->unwatchObject(obj);
362 return true;
363 }
365 /************************************************************************/
367 JS_PUBLIC_API(unsigned)
368 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
369 {
370 return js::PCToLineNumber(script, pc);
371 }
373 JS_PUBLIC_API(jsbytecode *)
374 JS_LineNumberToPC(JSContext *cx, JSScript *script, unsigned lineno)
375 {
376 return js_LineNumberToPC(script, lineno);
377 }
379 JS_PUBLIC_API(jsbytecode *)
380 JS_EndPC(JSContext *cx, JSScript *script)
381 {
382 return script->codeEnd();
383 }
385 JS_PUBLIC_API(bool)
386 JS_GetLinePCs(JSContext *cx, JSScript *script,
387 unsigned startLine, unsigned maxLines,
388 unsigned* count, unsigned** retLines, jsbytecode*** retPCs)
389 {
390 size_t len = (script->length() > maxLines ? maxLines : script->length());
391 unsigned *lines = cx->pod_malloc<unsigned>(len);
392 if (!lines)
393 return false;
395 jsbytecode **pcs = cx->pod_malloc<jsbytecode*>(len);
396 if (!pcs) {
397 js_free(lines);
398 return false;
399 }
401 unsigned lineno = script->lineno();
402 unsigned offset = 0;
403 unsigned i = 0;
404 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
405 offset += SN_DELTA(sn);
406 SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
407 if (type == SRC_SETLINE || type == SRC_NEWLINE) {
408 if (type == SRC_SETLINE)
409 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
410 else
411 lineno++;
413 if (lineno >= startLine) {
414 lines[i] = lineno;
415 pcs[i] = script->offsetToPC(offset);
416 if (++i >= maxLines)
417 break;
418 }
419 }
420 }
422 *count = i;
423 if (retLines)
424 *retLines = lines;
425 else
426 js_free(lines);
428 if (retPCs)
429 *retPCs = pcs;
430 else
431 js_free(pcs);
433 return true;
434 }
436 JS_PUBLIC_API(unsigned)
437 JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
438 {
439 return fun->nargs();
440 }
442 JS_PUBLIC_API(bool)
443 JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
444 {
445 return fun->nonLazyScript()->bindings.count() > 0;
446 }
448 extern JS_PUBLIC_API(uintptr_t *)
449 JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **memp)
450 {
451 RootedScript script(cx, fun->nonLazyScript());
452 BindingVector bindings(cx);
453 if (!FillBindingVector(script, &bindings))
454 return nullptr;
456 LifoAlloc &lifo = cx->tempLifoAlloc();
458 // Store the LifoAlloc::Mark right before the allocation.
459 LifoAlloc::Mark mark = lifo.mark();
460 void *mem = lifo.alloc(sizeof(LifoAlloc::Mark) + bindings.length() * sizeof(uintptr_t));
461 if (!mem) {
462 js_ReportOutOfMemory(cx);
463 return nullptr;
464 }
465 *memp = mem;
466 *reinterpret_cast<LifoAlloc::Mark*>(mem) = mark;
468 // Munge data into the API this method implements. Avert your eyes!
469 uintptr_t *names = reinterpret_cast<uintptr_t*>((char*)mem + sizeof(LifoAlloc::Mark));
470 for (size_t i = 0; i < bindings.length(); i++)
471 names[i] = reinterpret_cast<uintptr_t>(bindings[i].name());
473 return names;
474 }
476 extern JS_PUBLIC_API(JSAtom *)
477 JS_LocalNameToAtom(uintptr_t w)
478 {
479 return reinterpret_cast<JSAtom *>(w);
480 }
482 extern JS_PUBLIC_API(JSString *)
483 JS_AtomKey(JSAtom *atom)
484 {
485 return atom;
486 }
488 extern JS_PUBLIC_API(void)
489 JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mem)
490 {
491 cx->tempLifoAlloc().release(*reinterpret_cast<LifoAlloc::Mark*>(mem));
492 }
494 JS_PUBLIC_API(JSScript *)
495 JS_GetFunctionScript(JSContext *cx, HandleFunction fun)
496 {
497 if (fun->isNative())
498 return nullptr;
499 if (fun->isInterpretedLazy()) {
500 AutoCompartment funCompartment(cx, fun);
501 JSScript *script = fun->getOrCreateScript(cx);
502 if (!script)
503 MOZ_CRASH();
504 return script;
505 }
506 return fun->nonLazyScript();
507 }
509 JS_PUBLIC_API(JSNative)
510 JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
511 {
512 return fun->maybeNative();
513 }
515 JS_PUBLIC_API(JSPrincipals *)
516 JS_GetScriptPrincipals(JSScript *script)
517 {
518 return script->principals();
519 }
521 JS_PUBLIC_API(JSPrincipals *)
522 JS_GetScriptOriginPrincipals(JSScript *script)
523 {
524 return script->originPrincipals();
525 }
527 /************************************************************************/
529 JS_PUBLIC_API(JSFunction *)
530 JS_GetScriptFunction(JSContext *cx, JSScript *script)
531 {
532 script->ensureNonLazyCanonicalFunction(cx);
533 return script->functionNonDelazifying();
534 }
536 JS_PUBLIC_API(JSObject *)
537 JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
538 {
539 return obj->enclosingScope();
540 }
542 JS_PUBLIC_API(const char *)
543 JS_GetDebugClassName(JSObject *obj)
544 {
545 if (obj->is<DebugScopeObject>())
546 return obj->as<DebugScopeObject>().scope().getClass()->name;
547 return obj->getClass()->name;
548 }
550 /************************************************************************/
552 JS_PUBLIC_API(const char *)
553 JS_GetScriptFilename(JSScript *script)
554 {
555 return script->filename();
556 }
558 JS_PUBLIC_API(const jschar *)
559 JS_GetScriptSourceMap(JSContext *cx, JSScript *script)
560 {
561 ScriptSource *source = script->scriptSource();
562 JS_ASSERT(source);
563 return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr;
564 }
566 JS_PUBLIC_API(unsigned)
567 JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
568 {
569 return script->lineno();
570 }
572 JS_PUBLIC_API(unsigned)
573 JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
574 {
575 return js_GetScriptLineExtent(script);
576 }
578 JS_PUBLIC_API(JSVersion)
579 JS_GetScriptVersion(JSContext *cx, JSScript *script)
580 {
581 return VersionNumber(script->getVersion());
582 }
584 JS_PUBLIC_API(bool)
585 JS_GetScriptIsSelfHosted(JSScript *script)
586 {
587 return script->selfHosted();
588 }
590 /***************************************************************************/
592 JS_PUBLIC_API(void)
593 JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
594 {
595 rt->debugHooks.newScriptHook = hook;
596 rt->debugHooks.newScriptHookData = callerdata;
597 }
599 JS_PUBLIC_API(void)
600 JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
601 void *callerdata)
602 {
603 rt->debugHooks.destroyScriptHook = hook;
604 rt->debugHooks.destroyScriptHookData = callerdata;
605 }
607 /***************************************************************************/
609 /* This all should be reworked to avoid requiring JSScopeProperty types. */
611 static bool
612 GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd)
613 {
614 assertSameCompartment(cx, obj_);
615 pd->id = IdToValue(shape->propid());
617 RootedObject obj(cx, obj_);
619 bool wasThrowing = cx->isExceptionPending();
620 RootedValue lastException(cx, UndefinedValue());
621 if (wasThrowing) {
622 if (!cx->getPendingException(&lastException))
623 return false;
624 }
625 cx->clearPendingException();
627 Rooted<jsid> id(cx, shape->propid());
628 RootedValue value(cx);
629 if (!baseops::GetProperty(cx, obj, id, &value)) {
630 if (!cx->isExceptionPending()) {
631 pd->flags = JSPD_ERROR;
632 pd->value = JSVAL_VOID;
633 } else {
634 pd->flags = JSPD_EXCEPTION;
635 if (!cx->getPendingException(&value))
636 return false;
637 pd->value = value;
638 }
639 } else {
640 pd->flags = 0;
641 pd->value = value;
642 }
644 if (wasThrowing)
645 cx->setPendingException(lastException);
647 pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
648 | (!shape->writable() ? JSPD_READONLY : 0)
649 | (!shape->configurable() ? JSPD_PERMANENT : 0);
650 pd->spare = 0;
651 pd->alias = JSVAL_VOID;
653 return true;
654 }
656 JS_PUBLIC_API(bool)
657 JS_GetPropertyDescArray(JSContext *cx, JS::HandleObject obj, JSPropertyDescArray *pda)
658 {
659 assertSameCompartment(cx, obj);
660 uint32_t i = 0;
661 JSPropertyDesc *pd = nullptr;
663 if (obj->is<DebugScopeObject>()) {
664 AutoIdVector props(cx);
665 if (!Proxy::enumerate(cx, obj, props))
666 return false;
668 pd = cx->pod_calloc<JSPropertyDesc>(props.length());
669 if (!pd)
670 return false;
672 for (i = 0; i < props.length(); ++i) {
673 pd[i].id = JSVAL_NULL;
674 pd[i].value = JSVAL_NULL;
675 if (!AddValueRoot(cx, &pd[i].id, nullptr))
676 goto bad;
677 pd[i].id = IdToValue(props[i]);
678 if (!AddValueRoot(cx, &pd[i].value, nullptr))
679 goto bad;
680 if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value)))
681 goto bad;
682 }
684 pda->length = props.length();
685 pda->array = pd;
686 return true;
687 }
689 const Class *clasp;
690 clasp = obj->getClass();
691 if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
692 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
693 JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
694 return false;
695 }
696 if (!clasp->enumerate(cx, obj))
697 return false;
699 /* Return an empty pda early if obj has no own properties. */
700 if (obj->nativeEmpty()) {
701 pda->length = 0;
702 pda->array = nullptr;
703 return true;
704 }
706 pd = cx->pod_malloc<JSPropertyDesc>(obj->propertyCount());
707 if (!pd)
708 return false;
710 {
711 Shape::Range<CanGC> r(cx, obj->lastProperty());
712 RootedShape shape(cx);
713 for (; !r.empty(); r.popFront()) {
714 pd[i].id = JSVAL_NULL;
715 pd[i].value = JSVAL_NULL;
716 pd[i].alias = JSVAL_NULL;
717 if (!AddValueRoot(cx, &pd[i].id, nullptr))
718 goto bad;
719 if (!AddValueRoot(cx, &pd[i].value, nullptr))
720 goto bad;
721 shape = const_cast<Shape *>(&r.front());
722 if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
723 goto bad;
724 if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, nullptr))
725 goto bad;
726 if (++i == obj->propertyCount())
727 break;
728 }
729 }
731 pda->length = i;
732 pda->array = pd;
733 return true;
735 bad:
736 pda->length = i + 1;
737 pda->array = pd;
738 JS_PutPropertyDescArray(cx, pda);
739 return false;
740 }
742 JS_PUBLIC_API(void)
743 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
744 {
745 JSPropertyDesc *pd;
746 uint32_t i;
748 pd = pda->array;
749 for (i = 0; i < pda->length; i++) {
750 RemoveRoot(cx->runtime(), &pd[i].id);
751 RemoveRoot(cx->runtime(), &pd[i].value);
752 if (pd[i].flags & JSPD_ALIAS)
753 RemoveRoot(cx->runtime(), &pd[i].alias);
754 }
755 js_free(pd);
756 pda->array = nullptr;
757 pda->length = 0;
758 }
760 /************************************************************************/
762 JS_PUBLIC_API(bool)
763 JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
764 {
765 rt->debugHooks.debuggerHandler = handler;
766 rt->debugHooks.debuggerHandlerData = closure;
767 return true;
768 }
770 JS_PUBLIC_API(bool)
771 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
772 {
773 rt->debugHooks.sourceHandler = handler;
774 rt->debugHooks.sourceHandlerData = closure;
775 return true;
776 }
778 JS_PUBLIC_API(bool)
779 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
780 {
781 rt->debugHooks.executeHook = hook;
782 rt->debugHooks.executeHookData = closure;
783 return true;
784 }
786 JS_PUBLIC_API(bool)
787 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
788 {
789 rt->debugHooks.callHook = hook;
790 rt->debugHooks.callHookData = closure;
791 return true;
792 }
794 JS_PUBLIC_API(bool)
795 JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
796 {
797 rt->debugHooks.throwHook = hook;
798 rt->debugHooks.throwHookData = closure;
799 return true;
800 }
802 JS_PUBLIC_API(bool)
803 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
804 {
805 rt->debugHooks.debugErrorHook = hook;
806 rt->debugHooks.debugErrorHookData = closure;
807 return true;
808 }
810 /************************************************************************/
812 JS_PUBLIC_API(const JSDebugHooks *)
813 JS_GetGlobalDebugHooks(JSRuntime *rt)
814 {
815 return &rt->debugHooks;
816 }
818 /************************************************************************/
820 extern JS_PUBLIC_API(void)
821 JS_DumpPCCounts(JSContext *cx, HandleScript script)
822 {
823 JS_ASSERT(script->hasScriptCounts());
825 Sprinter sprinter(cx);
826 if (!sprinter.init())
827 return;
829 fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
830 js_DumpPCCounts(cx, script, &sprinter);
831 fputs(sprinter.string(), stdout);
832 fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
833 }
835 JS_PUBLIC_API(void)
836 JS_DumpCompartmentPCCounts(JSContext *cx)
837 {
838 for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
839 RootedScript script(cx, i.get<JSScript>());
840 if (script->compartment() != cx->compartment())
841 continue;
843 if (script->hasScriptCounts())
844 JS_DumpPCCounts(cx, script);
845 }
846 }
848 JS_FRIEND_API(bool)
849 js::CanCallContextDebugHandler(JSContext *cx)
850 {
851 return !!cx->runtime()->debugHooks.debuggerHandler;
852 }
854 static JSTrapStatus
855 CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval)
856 {
857 if (!cx->runtime()->debugHooks.debuggerHandler)
858 return JSTRAP_RETURN;
860 return cx->runtime()->debugHooks.debuggerHandler(cx, script, bc, rval,
861 cx->runtime()->debugHooks.debuggerHandlerData);
862 }
864 JS_FRIEND_API(bool)
865 js_CallContextDebugHandler(JSContext *cx)
866 {
867 NonBuiltinFrameIter iter(cx);
869 // If there is no script to debug, then abort execution even if the user
870 // clicks 'Debug' in the slow-script dialog.
871 if (!iter.hasScript())
872 return false;
874 // Even if script was running during the operation callback, it's possible
875 // it was a builtin which 'iter' will have skipped over.
876 if (iter.done())
877 return false;
879 RootedValue rval(cx);
880 RootedScript script(cx, iter.script());
881 switch (CallContextDebugHandler(cx, script, iter.pc(), rval.address())) {
882 case JSTRAP_ERROR:
883 JS_ClearPendingException(cx);
884 return false;
885 case JSTRAP_THROW:
886 JS_SetPendingException(cx, rval);
887 return false;
888 case JSTRAP_RETURN:
889 case JSTRAP_CONTINUE:
890 default:
891 return true;
892 }
893 }
895 /*
896 * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid
897 * constructing a FrameDescription on the stack just to append it to a vector.
898 * FrameDescription contains Heap<T> fields that should not live on the stack.
899 */
900 JS::FrameDescription::FrameDescription(const ScriptFrameIter& iter)
901 : script_(iter.script()),
902 funDisplayName_(nullptr),
903 pc_(iter.pc()),
904 linenoComputed(false)
905 {
906 if (JSFunction *fun = iter.maybeCallee())
907 funDisplayName_ = fun->displayAtom();
908 }
910 JS_PUBLIC_API(JS::StackDescription *)
911 JS::DescribeStack(JSContext *cx, unsigned maxFrames)
912 {
913 Vector<FrameDescription> frames(cx);
915 NonBuiltinScriptFrameIter i(cx, ScriptFrameIter::ALL_CONTEXTS,
916 ScriptFrameIter::GO_THROUGH_SAVED,
917 cx->compartment()->principals);
918 for ( ; !i.done(); ++i) {
919 if (!frames.append(i))
920 return nullptr;
921 if (frames.length() == maxFrames)
922 break;
923 }
925 JS::StackDescription *desc = js_new<JS::StackDescription>();
926 if (!desc)
927 return nullptr;
929 desc->nframes = frames.length();
930 desc->frames = frames.extractRawBuffer();
931 return desc;
932 }
934 JS_PUBLIC_API(void)
935 JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc)
936 {
937 for (size_t i = 0; i < desc->nframes; ++i)
938 desc->frames[i].~FrameDescription();
939 js_free(desc->frames);
940 js_delete(desc);
941 }
943 namespace {
945 class AutoPropertyDescArray
946 {
947 JSContext *cx_;
948 JSPropertyDescArray descArray_;
950 public:
951 AutoPropertyDescArray(JSContext *cx)
952 : cx_(cx)
953 {
954 PodZero(&descArray_);
955 }
956 ~AutoPropertyDescArray()
957 {
958 if (descArray_.array)
959 JS_PutPropertyDescArray(cx_, &descArray_);
960 }
962 void fetch(JS::HandleObject obj) {
963 JS_ASSERT(!descArray_.array);
964 if (!JS_GetPropertyDescArray(cx_, obj, &descArray_))
965 descArray_.array = nullptr;
966 }
968 JSPropertyDescArray * operator ->() {
969 return &descArray_;
970 }
971 };
973 } /* anonymous namespace */
975 static const char *
976 FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
977 {
978 RootedValue v(cx, vArg);
980 /*
981 * We could use Maybe<AutoCompartment> here, but G++ can't quite follow
982 * that, and warns about uninitialized members being used in the
983 * destructor.
984 */
985 RootedString str(cx);
986 if (v.isObject()) {
987 AutoCompartment ac(cx, &v.toObject());
988 str = ToString<CanGC>(cx, v);
989 } else {
990 str = ToString<CanGC>(cx, v);
991 }
993 if (!str)
994 return nullptr;
995 const char *buf = bytes.encodeLatin1(cx, str);
996 if (!buf)
997 return nullptr;
998 const char *found = strstr(buf, "function ");
999 if (found && (found - buf <= 2))
1000 return "[function]";
1001 return buf;
1002 }
1004 static char *
1005 FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num,
1006 bool showArgs, bool showLocals, bool showThisProps)
1007 {
1008 JS_ASSERT(!cx->isExceptionPending());
1009 RootedScript script(cx, iter.script());
1010 jsbytecode* pc = iter.pc();
1012 RootedObject scopeChain(cx, iter.scopeChain());
1013 JSAutoCompartment ac(cx, scopeChain);
1015 const char *filename = script->filename();
1016 unsigned lineno = PCToLineNumber(script, pc);
1017 RootedFunction fun(cx, iter.maybeCallee());
1018 RootedString funname(cx);
1019 if (fun)
1020 funname = fun->atom();
1022 RootedValue thisVal(cx);
1023 AutoPropertyDescArray thisProps(cx);
1024 if (iter.computeThis(cx)) {
1025 thisVal = iter.computedThisValue();
1026 if (showThisProps && !thisVal.isPrimitive()) {
1027 RootedObject thisObj(cx, &thisVal.toObject());
1028 thisProps.fetch(thisObj);
1029 }
1030 }
1032 // print the frame number and function name
1033 if (funname) {
1034 JSAutoByteString funbytes;
1035 buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname));
1036 } else if (fun) {
1037 buf = JS_sprintf_append(buf, "%d anonymous(", num);
1038 } else {
1039 buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
1040 }
1041 if (!buf)
1042 return buf;
1044 if (showArgs && iter.hasArgs()) {
1045 BindingVector bindings(cx);
1046 if (fun && fun->isInterpreted()) {
1047 if (!FillBindingVector(script, &bindings))
1048 return buf;
1049 }
1052 bool first = true;
1053 for (unsigned i = 0; i < iter.numActualArgs(); i++) {
1054 RootedValue arg(cx);
1055 if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
1056 for (AliasedFormalIter fi(script); ; fi++) {
1057 if (fi.frameIndex() == i) {
1058 arg = iter.callObj().aliasedVar(fi);
1059 break;
1060 }
1061 }
1062 } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
1063 arg = iter.argsObj().arg(i);
1064 } else {
1065 arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
1066 }
1068 JSAutoByteString valueBytes;
1069 const char *value = FormatValue(cx, arg, valueBytes);
1071 JSAutoByteString nameBytes;
1072 const char *name = nullptr;
1074 if (i < bindings.length()) {
1075 name = nameBytes.encodeLatin1(cx, bindings[i].name());
1076 if (!buf)
1077 return nullptr;
1078 }
1080 if (value) {
1081 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
1082 !first ? ", " : "",
1083 name ? name :"",
1084 name ? " = " : "",
1085 arg.isString() ? "\"" : "",
1086 value ? value : "?unknown?",
1087 arg.isString() ? "\"" : "");
1088 if (!buf)
1089 return buf;
1091 first = false;
1092 } else {
1093 buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n");
1094 if (!buf)
1095 return buf;
1096 cx->clearPendingException();
1098 }
1099 }
1100 }
1102 // print filename and line number
1103 buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
1104 fun ? ")" : "",
1105 filename ? filename : "<unknown>",
1106 lineno);
1107 if (!buf)
1108 return buf;
1111 // Note: Right now we don't dump the local variables anymore, because
1112 // that is hard to support across all the JITs etc.
1114 // print the value of 'this'
1115 if (showLocals) {
1116 if (!thisVal.isUndefined()) {
1117 JSAutoByteString thisValBytes;
1118 RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
1119 const char *str = nullptr;
1120 if (thisValStr &&
1121 (str = thisValBytes.encodeLatin1(cx, thisValStr)))
1122 {
1123 buf = JS_sprintf_append(buf, " this = %s\n", str);
1124 if (!buf)
1125 return buf;
1126 } else {
1127 buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n");
1128 cx->clearPendingException();
1129 }
1130 }
1131 }
1133 // print the properties of 'this', if it is an object
1134 if (showThisProps && thisProps->array) {
1135 for (uint32_t i = 0; i < thisProps->length; i++) {
1136 JSPropertyDesc* desc = &thisProps->array[i];
1137 if (desc->flags & JSPD_ENUMERATE) {
1138 JSAutoByteString nameBytes;
1139 JSAutoByteString valueBytes;
1140 const char *name = FormatValue(cx, desc->id, nameBytes);
1141 const char *value = FormatValue(cx, desc->value, valueBytes);
1142 if (name && value) {
1143 buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n",
1144 name,
1145 desc->value.isString() ? "\"" : "",
1146 value,
1147 desc->value.isString() ? "\"" : "");
1148 if (!buf)
1149 return buf;
1150 } else {
1151 buf = JS_sprintf_append(buf, " <Failed to format values while inspecting stack frame>\n");
1152 cx->clearPendingException();
1153 }
1154 }
1155 }
1156 }
1158 JS_ASSERT(!cx->isExceptionPending());
1159 return buf;
1160 }
1162 JS_PUBLIC_API(char *)
1163 JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps)
1164 {
1165 int num = 0;
1167 for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
1168 buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps);
1169 num++;
1170 }
1172 if (!num)
1173 buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
1175 return buf;
1176 }
1178 JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc)
1179 : ptr_(uintptr_t(raw)), pc_(pc)
1180 { }
1182 JSObject *
1183 JSAbstractFramePtr::scopeChain(JSContext *cx)
1184 {
1185 AbstractFramePtr frame(*this);
1186 RootedObject scopeChain(cx, frame.scopeChain());
1187 AutoCompartment ac(cx, scopeChain);
1188 return GetDebugScopeForFrame(cx, frame, pc());
1189 }
1191 JSObject *
1192 JSAbstractFramePtr::callObject(JSContext *cx)
1193 {
1194 AbstractFramePtr frame(*this);
1195 if (!frame.isFunctionFrame())
1196 return nullptr;
1198 JSObject *o = GetDebugScopeForFrame(cx, frame, pc());
1200 /*
1201 * Given that fp is a function frame and GetDebugScopeForFrame always fills
1202 * in missing scopes, we can expect to find fp's CallObject on 'o'. Note:
1203 * - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with
1204 * a DebugScopeObject proxy.
1205 * - If fp is an eval-in-function, then fp has no callobj of its own and
1206 * JS_GetFrameCallObject will return the innermost function's callobj.
1207 */
1208 while (o) {
1209 ScopeObject &scope = o->as<DebugScopeObject>().scope();
1210 if (scope.is<CallObject>())
1211 return o;
1212 o = o->enclosingScope();
1213 }
1214 return nullptr;
1215 }
1217 JSFunction *
1218 JSAbstractFramePtr::maybeFun()
1219 {
1220 AbstractFramePtr frame(*this);
1221 return frame.maybeFun();
1222 }
1224 JSScript *
1225 JSAbstractFramePtr::script()
1226 {
1227 AbstractFramePtr frame(*this);
1228 return frame.script();
1229 }
1231 bool
1232 JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
1233 {
1234 AbstractFramePtr frame(*this);
1236 RootedObject scopeChain(cx, frame.scopeChain());
1237 js::AutoCompartment ac(cx, scopeChain);
1238 if (!ComputeThis(cx, frame))
1239 return false;
1241 thisv.set(frame.thisValue());
1242 return true;
1243 }
1245 bool
1246 JSAbstractFramePtr::isDebuggerFrame()
1247 {
1248 AbstractFramePtr frame(*this);
1249 return frame.isDebuggerFrame();
1250 }
1252 bool
1253 JSAbstractFramePtr::evaluateInStackFrame(JSContext *cx,
1254 const char *bytes, unsigned length,
1255 const char *filename, unsigned lineno,
1256 MutableHandleValue rval)
1257 {
1258 if (!CheckDebugMode(cx))
1259 return false;
1261 size_t len = length;
1262 jschar *chars = InflateString(cx, bytes, &len);
1263 if (!chars)
1264 return false;
1265 length = (unsigned) len;
1267 bool ok = evaluateUCInStackFrame(cx, chars, length, filename, lineno, rval);
1268 js_free(chars);
1270 return ok;
1271 }
1273 bool
1274 JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
1275 const jschar *chars, unsigned length,
1276 const char *filename, unsigned lineno,
1277 MutableHandleValue rval)
1278 {
1279 if (!CheckDebugMode(cx))
1280 return false;
1282 RootedObject scope(cx, scopeChain(cx));
1283 Rooted<Env*> env(cx, scope);
1284 if (!env)
1285 return false;
1287 AbstractFramePtr frame(*this);
1288 if (!ComputeThis(cx, frame))
1289 return false;
1290 RootedValue thisv(cx, frame.thisValue());
1292 js::AutoCompartment ac(cx, env);
1293 return EvaluateInEnv(cx, env, thisv, frame, ConstTwoByteChars(chars, length), length,
1294 filename, lineno, rval);
1295 }
1297 JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx)
1298 {
1299 // Show all frames on the stack whose principal is subsumed by the current principal.
1300 NonBuiltinScriptFrameIter iter(cx,
1301 ScriptFrameIter::ALL_CONTEXTS,
1302 ScriptFrameIter::GO_THROUGH_SAVED,
1303 cx->compartment()->principals);
1304 data_ = iter.copyData();
1305 }
1307 JSBrokenFrameIterator::~JSBrokenFrameIterator()
1308 {
1309 js_free((ScriptFrameIter::Data *)data_);
1310 }
1312 bool
1313 JSBrokenFrameIterator::done() const
1314 {
1315 NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
1316 return iter.done();
1317 }
1319 JSBrokenFrameIterator &
1320 JSBrokenFrameIterator::operator++()
1321 {
1322 ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_;
1323 NonBuiltinScriptFrameIter iter(*data);
1324 ++iter;
1325 *data = iter.data_;
1326 return *this;
1327 }
1329 JSAbstractFramePtr
1330 JSBrokenFrameIterator::abstractFramePtr() const
1331 {
1332 NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
1333 return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc());
1334 }
1336 jsbytecode *
1337 JSBrokenFrameIterator::pc() const
1338 {
1339 NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
1340 return iter.pc();
1341 }
1343 bool
1344 JSBrokenFrameIterator::isConstructing() const
1345 {
1346 NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
1347 return iter.isConstructing();
1348 }