js/src/vm/OldDebugAPI.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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.

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

mercurial