js/jsd/jsd_scpt.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 * JavaScript Debugging support - Script support
michael@0 9 */
michael@0 10
michael@0 11 #include "jsd.h"
michael@0 12 #include "jsfriendapi.h"
michael@0 13 #include "nsCxPusher.h"
michael@0 14
michael@0 15 using mozilla::AutoSafeJSContext;
michael@0 16
michael@0 17 /* Comment this out to disable (NT specific) dumping as we go */
michael@0 18 /*
michael@0 19 ** #ifdef DEBUG
michael@0 20 ** #define JSD_DUMP 1
michael@0 21 ** #endif
michael@0 22 */
michael@0 23
michael@0 24 #define NOT_SET_YET -1
michael@0 25
michael@0 26 /***************************************************************************/
michael@0 27
michael@0 28 #ifdef DEBUG
michael@0 29 void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript)
michael@0 30 {
michael@0 31 MOZ_ASSERT(jsdscript);
michael@0 32 MOZ_ASSERT(jsdscript->script);
michael@0 33 }
michael@0 34 void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook)
michael@0 35 {
michael@0 36 MOZ_ASSERT(jsdhook);
michael@0 37 MOZ_ASSERT(jsdhook->hook);
michael@0 38 }
michael@0 39 #endif
michael@0 40
michael@0 41 static JSDScript*
michael@0 42 _newJSDScript(JSDContext* jsdc,
michael@0 43 JSContext *cx,
michael@0 44 JSScript *script_)
michael@0 45 {
michael@0 46 JS::RootedScript script(cx, script_);
michael@0 47 if ( JS_GetScriptIsSelfHosted(script) )
michael@0 48 return nullptr;
michael@0 49
michael@0 50 JSDScript* jsdscript;
michael@0 51 unsigned lineno;
michael@0 52 const char* raw_filename;
michael@0 53
michael@0 54 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 55
michael@0 56 /* these are inlined javascript: urls and we can't handle them now */
michael@0 57 lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script);
michael@0 58 if( lineno == 0 )
michael@0 59 return nullptr;
michael@0 60
michael@0 61 jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript));
michael@0 62 if( ! jsdscript )
michael@0 63 return nullptr;
michael@0 64
michael@0 65 raw_filename = JS_GetScriptFilename(script);
michael@0 66
michael@0 67 JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript);
michael@0 68 JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts);
michael@0 69 jsdscript->jsdc = jsdc;
michael@0 70 jsdscript->script = script;
michael@0 71 jsdscript->lineBase = lineno;
michael@0 72 jsdscript->lineExtent = (unsigned)NOT_SET_YET;
michael@0 73 jsdscript->data = nullptr;
michael@0 74 jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename);
michael@0 75
michael@0 76 JS_INIT_CLIST(&jsdscript->hooks);
michael@0 77
michael@0 78 return jsdscript;
michael@0 79 }
michael@0 80
michael@0 81 static void
michael@0 82 _destroyJSDScript(JSDContext* jsdc,
michael@0 83 JSDScript* jsdscript)
michael@0 84 {
michael@0 85 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 86
michael@0 87 /* destroy all hooks */
michael@0 88 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
michael@0 89
michael@0 90 JS_REMOVE_LINK(&jsdscript->links);
michael@0 91 if(jsdscript->url)
michael@0 92 free(jsdscript->url);
michael@0 93
michael@0 94 if (jsdscript->profileData)
michael@0 95 free(jsdscript->profileData);
michael@0 96
michael@0 97 free(jsdscript);
michael@0 98 }
michael@0 99
michael@0 100 /***************************************************************************/
michael@0 101
michael@0 102 #ifdef JSD_DUMP
michael@0 103 #ifndef XP_WIN
michael@0 104 void
michael@0 105 OutputDebugString (char *buf)
michael@0 106 {
michael@0 107 fprintf (stderr, "%s", buf);
michael@0 108 }
michael@0 109 #endif
michael@0 110
michael@0 111 static void
michael@0 112 _dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
michael@0 113 {
michael@0 114 const char* name;
michael@0 115 JSString* fun;
michael@0 116 unsigned base;
michael@0 117 unsigned extent;
michael@0 118 char Buf[256];
michael@0 119 size_t n;
michael@0 120
michael@0 121 name = jsd_GetScriptFilename(jsdc, jsdscript);
michael@0 122 fun = jsd_GetScriptFunctionId(jsdc, jsdscript);
michael@0 123 base = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
michael@0 124 extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
michael@0 125 n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
michael@0 126 leadingtext, (unsigned) jsdscript->script,
michael@0 127 name ? name : "no URL"));
michael@0 128 if (n + 1 < sizeof(Buf)) {
michael@0 129 if (fun) {
michael@0 130 n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
michael@0 131 } else {
michael@0 132 n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
michael@0 133 MOZ_ASSERT_STRING_IS_FLAT(fun), 0);
michael@0 134 Buf[sizeof(Buf) - 1] = '\0';
michael@0 135 }
michael@0 136 if (n + 1 < sizeof(Buf))
michael@0 137 snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
michael@0 138 }
michael@0 139 OutputDebugString( Buf );
michael@0 140 }
michael@0 141
michael@0 142 static void
michael@0 143 _dumpJSDScriptList( JSDContext* jsdc )
michael@0 144 {
michael@0 145 JSDScript* iterp = nullptr;
michael@0 146 JSDScript* jsdscript = nullptr;
michael@0 147
michael@0 148 OutputDebugString( "*** JSDScriptDump\n" );
michael@0 149 while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
michael@0 150 _dumpJSDScript( jsdc, jsdscript, " script: " );
michael@0 151 }
michael@0 152 #endif /* JSD_DUMP */
michael@0 153
michael@0 154 /***************************************************************************/
michael@0 155 static JSHashNumber
michael@0 156 jsd_hash_script(const void *key)
michael@0 157 {
michael@0 158 return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
michael@0 159 }
michael@0 160
michael@0 161 static void *
michael@0 162 jsd_alloc_script_table(void *priv, size_t size)
michael@0 163 {
michael@0 164 return malloc(size);
michael@0 165 }
michael@0 166
michael@0 167 static void
michael@0 168 jsd_free_script_table(void *priv, void *item, size_t size)
michael@0 169 {
michael@0 170 free(item);
michael@0 171 }
michael@0 172
michael@0 173 static JSHashEntry *
michael@0 174 jsd_alloc_script_entry(void *priv, const void *item)
michael@0 175 {
michael@0 176 return (JSHashEntry*) malloc(sizeof(JSHashEntry));
michael@0 177 }
michael@0 178
michael@0 179 static void
michael@0 180 jsd_free_script_entry(void *priv, JSHashEntry *he, unsigned flag)
michael@0 181 {
michael@0 182 if (flag == HT_FREE_ENTRY)
michael@0 183 {
michael@0 184 _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value);
michael@0 185 free(he);
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 static const JSHashAllocOps script_alloc_ops = {
michael@0 190 jsd_alloc_script_table, jsd_free_script_table,
michael@0 191 jsd_alloc_script_entry, jsd_free_script_entry
michael@0 192 };
michael@0 193
michael@0 194 #ifndef JSD_SCRIPT_HASH_SIZE
michael@0 195 #define JSD_SCRIPT_HASH_SIZE 1024
michael@0 196 #endif
michael@0 197
michael@0 198 bool
michael@0 199 jsd_InitScriptManager(JSDContext* jsdc)
michael@0 200 {
michael@0 201 JS_INIT_CLIST(&jsdc->scripts);
michael@0 202 jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script,
michael@0 203 JS_CompareValues, JS_CompareValues,
michael@0 204 &script_alloc_ops, (void*) jsdc);
michael@0 205 return !!jsdc->scriptsTable;
michael@0 206 }
michael@0 207
michael@0 208 void
michael@0 209 jsd_DestroyScriptManager(JSDContext* jsdc)
michael@0 210 {
michael@0 211 JSD_LOCK_SCRIPTS(jsdc);
michael@0 212 if (jsdc->scriptsTable)
michael@0 213 JS_HashTableDestroy(jsdc->scriptsTable);
michael@0 214 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 215 }
michael@0 216
michael@0 217 JSDScript*
michael@0 218 jsd_FindJSDScript( JSDContext* jsdc,
michael@0 219 JSScript *script )
michael@0 220 {
michael@0 221 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 222 return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
michael@0 223 }
michael@0 224
michael@0 225 JSDScript *
michael@0 226 jsd_FindOrCreateJSDScript(JSDContext *jsdc,
michael@0 227 JSContext *cx,
michael@0 228 JSScript *script_,
michael@0 229 JSAbstractFramePtr frame)
michael@0 230 {
michael@0 231 JS::RootedScript script(cx, script_);
michael@0 232 JSDScript *jsdscript;
michael@0 233 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 234
michael@0 235 jsdscript = jsd_FindJSDScript(jsdc, script);
michael@0 236 if (jsdscript)
michael@0 237 return jsdscript;
michael@0 238
michael@0 239 /* Fallback for unknown scripts: create a new script. */
michael@0 240 if (!frame) {
michael@0 241 JSBrokenFrameIterator iter(cx);
michael@0 242 if (!iter.done())
michael@0 243 frame = iter.abstractFramePtr();
michael@0 244 }
michael@0 245 if (frame)
michael@0 246 jsdscript = _newJSDScript(jsdc, cx, script);
michael@0 247
michael@0 248 return jsdscript;
michael@0 249 }
michael@0 250
michael@0 251 JSDProfileData*
michael@0 252 jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
michael@0 253 {
michael@0 254 if (!script->profileData)
michael@0 255 script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData));
michael@0 256
michael@0 257 return script->profileData;
michael@0 258 }
michael@0 259
michael@0 260 uint32_t
michael@0 261 jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script)
michael@0 262 {
michael@0 263 return script->flags;
michael@0 264 }
michael@0 265
michael@0 266 void
michael@0 267 jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags)
michael@0 268 {
michael@0 269 script->flags = flags;
michael@0 270 }
michael@0 271
michael@0 272 unsigned
michael@0 273 jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script)
michael@0 274 {
michael@0 275 if (script->profileData)
michael@0 276 return script->profileData->callCount;
michael@0 277
michael@0 278 return 0;
michael@0 279 }
michael@0 280
michael@0 281 unsigned
michael@0 282 jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script)
michael@0 283 {
michael@0 284 if (script->profileData)
michael@0 285 return script->profileData->maxRecurseDepth;
michael@0 286
michael@0 287 return 0;
michael@0 288 }
michael@0 289
michael@0 290 double
michael@0 291 jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 292 {
michael@0 293 if (script->profileData)
michael@0 294 return script->profileData->minExecutionTime;
michael@0 295
michael@0 296 return 0.0;
michael@0 297 }
michael@0 298
michael@0 299 double
michael@0 300 jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 301 {
michael@0 302 if (script->profileData)
michael@0 303 return script->profileData->maxExecutionTime;
michael@0 304
michael@0 305 return 0.0;
michael@0 306 }
michael@0 307
michael@0 308 double
michael@0 309 jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 310 {
michael@0 311 if (script->profileData)
michael@0 312 return script->profileData->totalExecutionTime;
michael@0 313
michael@0 314 return 0.0;
michael@0 315 }
michael@0 316
michael@0 317 double
michael@0 318 jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 319 {
michael@0 320 if (script->profileData)
michael@0 321 return script->profileData->minOwnExecutionTime;
michael@0 322
michael@0 323 return 0.0;
michael@0 324 }
michael@0 325
michael@0 326 double
michael@0 327 jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 328 {
michael@0 329 if (script->profileData)
michael@0 330 return script->profileData->maxOwnExecutionTime;
michael@0 331
michael@0 332 return 0.0;
michael@0 333 }
michael@0 334
michael@0 335 double
michael@0 336 jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
michael@0 337 {
michael@0 338 if (script->profileData)
michael@0 339 return script->profileData->totalOwnExecutionTime;
michael@0 340
michael@0 341 return 0.0;
michael@0 342 }
michael@0 343
michael@0 344 void
michael@0 345 jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script)
michael@0 346 {
michael@0 347 if (script->profileData)
michael@0 348 {
michael@0 349 free(script->profileData);
michael@0 350 script->profileData = nullptr;
michael@0 351 }
michael@0 352 }
michael@0 353
michael@0 354 JSScript *
michael@0 355 jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
michael@0 356 {
michael@0 357 return script->script;
michael@0 358 }
michael@0 359
michael@0 360 JSFunction *
michael@0 361 jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
michael@0 362 {
michael@0 363 AutoSafeJSContext cx;
michael@0 364 return JS_GetScriptFunction(cx, script->script);
michael@0 365 }
michael@0 366
michael@0 367 JSDScript*
michael@0 368 jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
michael@0 369 {
michael@0 370 JSDScript *jsdscript = *iterp;
michael@0 371
michael@0 372 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 373
michael@0 374 if( !jsdscript )
michael@0 375 jsdscript = (JSDScript *)jsdc->scripts.next;
michael@0 376 if( jsdscript == (JSDScript *)&jsdc->scripts )
michael@0 377 return nullptr;
michael@0 378 *iterp = (JSDScript*) jsdscript->links.next;
michael@0 379 return jsdscript;
michael@0 380 }
michael@0 381
michael@0 382 void *
michael@0 383 jsd_SetScriptPrivate(JSDScript *jsdscript, void *data)
michael@0 384 {
michael@0 385 void *rval = jsdscript->data;
michael@0 386 jsdscript->data = data;
michael@0 387 return rval;
michael@0 388 }
michael@0 389
michael@0 390 void *
michael@0 391 jsd_GetScriptPrivate(JSDScript *jsdscript)
michael@0 392 {
michael@0 393 return jsdscript->data;
michael@0 394 }
michael@0 395
michael@0 396 bool
michael@0 397 jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript)
michael@0 398 {
michael@0 399 JSDScript *current;
michael@0 400
michael@0 401 MOZ_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
michael@0 402
michael@0 403 for( current = (JSDScript *)jsdc->scripts.next;
michael@0 404 current != (JSDScript *)&jsdc->scripts;
michael@0 405 current = (JSDScript *)current->links.next )
michael@0 406 {
michael@0 407 if(jsdscript == current)
michael@0 408 return true;
michael@0 409 }
michael@0 410 return false;
michael@0 411 }
michael@0 412
michael@0 413 const char*
michael@0 414 jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
michael@0 415 {
michael@0 416 return jsdscript->url;
michael@0 417 }
michael@0 418
michael@0 419 JSString*
michael@0 420 jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript)
michael@0 421 {
michael@0 422 JSString* str;
michael@0 423 JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript);
michael@0 424
michael@0 425 if( ! fun )
michael@0 426 return nullptr;
michael@0 427 str = JS_GetFunctionId(fun);
michael@0 428
michael@0 429 /* For compatibility we return "anonymous", not an empty string here. */
michael@0 430 return str ? str : JS_GetAnonymousString(jsdc->jsrt);
michael@0 431 }
michael@0 432
michael@0 433 unsigned
michael@0 434 jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
michael@0 435 {
michael@0 436 return jsdscript->lineBase;
michael@0 437 }
michael@0 438
michael@0 439 unsigned
michael@0 440 jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
michael@0 441 {
michael@0 442 AutoSafeJSContext cx;
michael@0 443 JSAutoCompartment ac(cx, jsdc->glob); // Just in case.
michael@0 444 if( NOT_SET_YET == (int)jsdscript->lineExtent )
michael@0 445 jsdscript->lineExtent = JS_GetScriptLineExtent(cx, jsdscript->script);
michael@0 446 return jsdscript->lineExtent;
michael@0 447 }
michael@0 448
michael@0 449 uintptr_t
michael@0 450 jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, unsigned line)
michael@0 451 {
michael@0 452 uintptr_t pc;
michael@0 453
michael@0 454 if( !jsdscript )
michael@0 455 return 0;
michael@0 456
michael@0 457 AutoSafeJSContext cx;
michael@0 458 JSAutoCompartment ac(cx, jsdscript->script);
michael@0 459 pc = (uintptr_t) JS_LineNumberToPC(cx, jsdscript->script, line );
michael@0 460 return pc;
michael@0 461 }
michael@0 462
michael@0 463 unsigned
michael@0 464 jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
michael@0 465 {
michael@0 466 unsigned first = jsdscript->lineBase;
michael@0 467 unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
michael@0 468 unsigned line = 0;
michael@0 469
michael@0 470 if (pc) {
michael@0 471 AutoSafeJSContext cx;
michael@0 472 JSAutoCompartment ac(cx, jsdscript->script);
michael@0 473 line = JS_PCToLineNumber(cx, jsdscript->script, (jsbytecode*)pc);
michael@0 474 }
michael@0 475
michael@0 476 if( line < first )
michael@0 477 return first;
michael@0 478 if( line > last )
michael@0 479 return last;
michael@0 480
michael@0 481 return line;
michael@0 482 }
michael@0 483
michael@0 484 bool
michael@0 485 jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
michael@0 486 unsigned startLine, unsigned maxLines,
michael@0 487 unsigned* count, unsigned** retLines, uintptr_t** retPCs)
michael@0 488 {
michael@0 489 unsigned first = jsdscript->lineBase;
michael@0 490 unsigned last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
michael@0 491 bool ok;
michael@0 492 jsbytecode **pcs;
michael@0 493 unsigned i;
michael@0 494
michael@0 495 if (last < startLine)
michael@0 496 return true;
michael@0 497
michael@0 498 AutoSafeJSContext cx;
michael@0 499 JSAutoCompartment ac(cx, jsdscript->script);
michael@0 500
michael@0 501 ok = JS_GetLinePCs(cx, jsdscript->script,
michael@0 502 startLine, maxLines,
michael@0 503 count, retLines, &pcs);
michael@0 504
michael@0 505 if (ok) {
michael@0 506 if (retPCs) {
michael@0 507 for (i = 0; i < *count; ++i) {
michael@0 508 (*retPCs)[i] = (*pcs)[i];
michael@0 509 }
michael@0 510 }
michael@0 511
michael@0 512 JS_free(cx, pcs);
michael@0 513 }
michael@0 514
michael@0 515 return ok;
michael@0 516 }
michael@0 517
michael@0 518 bool
michael@0 519 jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
michael@0 520 {
michael@0 521 JSD_LOCK();
michael@0 522 jsdc->scriptHook = hook;
michael@0 523 jsdc->scriptHookData = callerdata;
michael@0 524 JSD_UNLOCK();
michael@0 525 return true;
michael@0 526 }
michael@0 527
michael@0 528 bool
michael@0 529 jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
michael@0 530 {
michael@0 531 JSD_LOCK();
michael@0 532 if( hook )
michael@0 533 *hook = jsdc->scriptHook;
michael@0 534 if( callerdata )
michael@0 535 *callerdata = jsdc->scriptHookData;
michael@0 536 JSD_UNLOCK();
michael@0 537 return true;
michael@0 538 }
michael@0 539
michael@0 540 bool
michael@0 541 jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, bool enable)
michael@0 542 {
michael@0 543 bool rv;
michael@0 544 AutoSafeJSContext cx;
michael@0 545 JS::RootedScript script(cx, jsdscript->script);
michael@0 546 JSAutoCompartment ac(cx, script);
michael@0 547 JSD_LOCK();
michael@0 548 rv = JS_SetSingleStepMode(cx, script, enable);
michael@0 549 JSD_UNLOCK();
michael@0 550 return rv;
michael@0 551 }
michael@0 552
michael@0 553
michael@0 554 /***************************************************************************/
michael@0 555
michael@0 556 void
michael@0 557 jsd_NewScriptHookProc(
michael@0 558 JSContext *cx,
michael@0 559 const char *filename, /* URL this script loads from */
michael@0 560 unsigned lineno, /* line where this script starts */
michael@0 561 JSScript *script,
michael@0 562 JSFunction *fun,
michael@0 563 void* callerdata )
michael@0 564 {
michael@0 565 JSDScript* jsdscript = nullptr;
michael@0 566 JSDContext* jsdc = (JSDContext*) callerdata;
michael@0 567 JSD_ScriptHookProc hook;
michael@0 568 void* hookData;
michael@0 569
michael@0 570 JSD_ASSERT_VALID_CONTEXT(jsdc);
michael@0 571
michael@0 572 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
michael@0 573 return;
michael@0 574
michael@0 575 JSD_LOCK_SCRIPTS(jsdc);
michael@0 576 jsdscript = _newJSDScript(jsdc, cx, script);
michael@0 577 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 578 if( ! jsdscript )
michael@0 579 return;
michael@0 580
michael@0 581 #ifdef JSD_DUMP
michael@0 582 JSD_LOCK_SCRIPTS(jsdc);
michael@0 583 _dumpJSDScript(jsdc, jsdscript, "***NEW Script: ");
michael@0 584 _dumpJSDScriptList( jsdc );
michael@0 585 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 586 #endif /* JSD_DUMP */
michael@0 587
michael@0 588 /* local in case jsdc->scriptHook gets cleared on another thread */
michael@0 589 JSD_LOCK();
michael@0 590 hook = jsdc->scriptHook;
michael@0 591 if( hook )
michael@0 592 jsdscript->flags = jsdscript->flags | JSD_SCRIPT_CALL_DESTROY_HOOK_BIT;
michael@0 593 hookData = jsdc->scriptHookData;
michael@0 594 JSD_UNLOCK();
michael@0 595
michael@0 596 if( hook )
michael@0 597 hook(jsdc, jsdscript, true, hookData);
michael@0 598 }
michael@0 599
michael@0 600 void
michael@0 601 jsd_DestroyScriptHookProc(
michael@0 602 JSFreeOp *fop,
michael@0 603 JSScript *script_,
michael@0 604 void* callerdata )
michael@0 605 {
michael@0 606 JSDScript* jsdscript = nullptr;
michael@0 607 JSDContext* jsdc = (JSDContext*) callerdata;
michael@0 608 // NB: We're called during GC, so we can't push a cx. Root directly with
michael@0 609 // the runtime.
michael@0 610 JS::RootedScript script(jsdc->jsrt, script_);
michael@0 611 JSD_ScriptHookProc hook;
michael@0 612 void* hookData;
michael@0 613
michael@0 614 JSD_ASSERT_VALID_CONTEXT(jsdc);
michael@0 615
michael@0 616 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
michael@0 617 return;
michael@0 618
michael@0 619 JSD_LOCK_SCRIPTS(jsdc);
michael@0 620 jsdscript = jsd_FindJSDScript(jsdc, script);
michael@0 621 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 622
michael@0 623 if( ! jsdscript )
michael@0 624 return;
michael@0 625
michael@0 626 #ifdef JSD_DUMP
michael@0 627 JSD_LOCK_SCRIPTS(jsdc);
michael@0 628 _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: ");
michael@0 629 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 630 #endif /* JSD_DUMP */
michael@0 631
michael@0 632 /* local in case hook gets cleared on another thread */
michael@0 633 JSD_LOCK();
michael@0 634 hook = (jsdscript->flags & JSD_SCRIPT_CALL_DESTROY_HOOK_BIT) ? jsdc->scriptHook
michael@0 635 : nullptr;
michael@0 636 hookData = jsdc->scriptHookData;
michael@0 637 JSD_UNLOCK();
michael@0 638
michael@0 639 if( hook )
michael@0 640 hook(jsdc, jsdscript, false, hookData);
michael@0 641
michael@0 642 JSD_LOCK_SCRIPTS(jsdc);
michael@0 643 JS_HashTableRemove(jsdc->scriptsTable, (void *)script);
michael@0 644 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 645
michael@0 646 #ifdef JSD_DUMP
michael@0 647 JSD_LOCK_SCRIPTS(jsdc);
michael@0 648 _dumpJSDScriptList(jsdc);
michael@0 649 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 650 #endif /* JSD_DUMP */
michael@0 651 }
michael@0 652
michael@0 653
michael@0 654 /***************************************************************************/
michael@0 655
michael@0 656 static JSDExecHook*
michael@0 657 _findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
michael@0 658 {
michael@0 659 JSDExecHook* jsdhook;
michael@0 660 JSCList* list = &jsdscript->hooks;
michael@0 661
michael@0 662 for( jsdhook = (JSDExecHook*)list->next;
michael@0 663 jsdhook != (JSDExecHook*)list;
michael@0 664 jsdhook = (JSDExecHook*)jsdhook->links.next )
michael@0 665 {
michael@0 666 if (jsdhook->pc == pc)
michael@0 667 return jsdhook;
michael@0 668 }
michael@0 669 return nullptr;
michael@0 670 }
michael@0 671
michael@0 672 static bool
michael@0 673 _isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook)
michael@0 674 {
michael@0 675 JSDExecHook* current;
michael@0 676 JSCList* list;
michael@0 677 JSDScript* jsdscript;
michael@0 678
michael@0 679 JSD_LOCK_SCRIPTS(jsdc);
michael@0 680 jsdscript = jsd_FindJSDScript(jsdc, script);
michael@0 681 if( ! jsdscript)
michael@0 682 {
michael@0 683 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 684 return false;
michael@0 685 }
michael@0 686
michael@0 687 list = &jsdscript->hooks;
michael@0 688
michael@0 689 for( current = (JSDExecHook*)list->next;
michael@0 690 current != (JSDExecHook*)list;
michael@0 691 current = (JSDExecHook*)current->links.next )
michael@0 692 {
michael@0 693 if(current == jsdhook)
michael@0 694 {
michael@0 695 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 696 return true;
michael@0 697 }
michael@0 698 }
michael@0 699 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 700 return false;
michael@0 701 }
michael@0 702
michael@0 703
michael@0 704 JSTrapStatus
michael@0 705 jsd_TrapHandler(JSContext *cx, JSScript *script_, jsbytecode *pc, jsval *rval,
michael@0 706 jsval closure)
michael@0 707 {
michael@0 708 JS::RootedScript script(cx, script_);
michael@0 709 JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure);
michael@0 710 JSD_ExecutionHookProc hook;
michael@0 711 void* hookData;
michael@0 712 JSDContext* jsdc;
michael@0 713
michael@0 714 JSD_LOCK();
michael@0 715
michael@0 716 if( nullptr == (jsdc = jsd_JSDContextForJSContext(cx)) ||
michael@0 717 ! _isActiveHook(jsdc, script, jsdhook) )
michael@0 718 {
michael@0 719 JSD_UNLOCK();
michael@0 720 return JSTRAP_CONTINUE;
michael@0 721 }
michael@0 722
michael@0 723 JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
michael@0 724 MOZ_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc);
michael@0 725 MOZ_ASSERT(jsdhook->jsdscript->script == script);
michael@0 726 MOZ_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
michael@0 727
michael@0 728 hook = jsdhook->hook;
michael@0 729 hookData = jsdhook->callerdata;
michael@0 730
michael@0 731 /* do not use jsdhook-> after this point */
michael@0 732 JSD_UNLOCK();
michael@0 733
michael@0 734 if( ! jsdc || ! jsdc->inited )
michael@0 735 return JSTRAP_CONTINUE;
michael@0 736
michael@0 737 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
michael@0 738 return JSTRAP_CONTINUE;
michael@0 739
michael@0 740 return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT,
michael@0 741 hook, hookData, rval);
michael@0 742 }
michael@0 743
michael@0 744
michael@0 745
michael@0 746 bool
michael@0 747 jsd_SetExecutionHook(JSDContext* jsdc,
michael@0 748 JSDScript* jsdscript,
michael@0 749 uintptr_t pc,
michael@0 750 JSD_ExecutionHookProc hook,
michael@0 751 void* callerdata)
michael@0 752 {
michael@0 753 JSDExecHook* jsdhook;
michael@0 754 bool rv;
michael@0 755
michael@0 756 JSD_LOCK();
michael@0 757 if( ! hook )
michael@0 758 {
michael@0 759 jsd_ClearExecutionHook(jsdc, jsdscript, pc);
michael@0 760 JSD_UNLOCK();
michael@0 761 return true;
michael@0 762 }
michael@0 763
michael@0 764 jsdhook = _findHook(jsdc, jsdscript, pc);
michael@0 765 if( jsdhook )
michael@0 766 {
michael@0 767 jsdhook->hook = hook;
michael@0 768 jsdhook->callerdata = callerdata;
michael@0 769 JSD_UNLOCK();
michael@0 770 return true;
michael@0 771 }
michael@0 772 /* else... */
michael@0 773
michael@0 774 jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook));
michael@0 775 if( ! jsdhook ) {
michael@0 776 JSD_UNLOCK();
michael@0 777 return false;
michael@0 778 }
michael@0 779 jsdhook->jsdscript = jsdscript;
michael@0 780 jsdhook->pc = pc;
michael@0 781 jsdhook->hook = hook;
michael@0 782 jsdhook->callerdata = callerdata;
michael@0 783
michael@0 784 {
michael@0 785 AutoSafeJSContext cx;
michael@0 786 JSAutoCompartment ac(cx, jsdscript->script);
michael@0 787 JS::RootedScript script(cx, jsdscript->script);
michael@0 788 JS::RootedValue hookValue(cx, PRIVATE_TO_JSVAL(jsdhook));
michael@0 789 rv = JS_SetTrap(cx, script, (jsbytecode*)pc, jsd_TrapHandler, hookValue);
michael@0 790 }
michael@0 791
michael@0 792 if ( ! rv ) {
michael@0 793 free(jsdhook);
michael@0 794 JSD_UNLOCK();
michael@0 795 return false;
michael@0 796 }
michael@0 797
michael@0 798 JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
michael@0 799 JSD_UNLOCK();
michael@0 800
michael@0 801 return true;
michael@0 802 }
michael@0 803
michael@0 804 bool
michael@0 805 jsd_ClearExecutionHook(JSDContext* jsdc,
michael@0 806 JSDScript* jsdscript,
michael@0 807 uintptr_t pc)
michael@0 808 {
michael@0 809 JSDExecHook* jsdhook;
michael@0 810
michael@0 811 JSD_LOCK();
michael@0 812
michael@0 813 jsdhook = _findHook(jsdc, jsdscript, pc);
michael@0 814 if( ! jsdhook )
michael@0 815 {
michael@0 816 JSD_UNLOCK();
michael@0 817 return false;
michael@0 818 }
michael@0 819
michael@0 820 {
michael@0 821 AutoSafeJSContext cx;
michael@0 822 JSAutoCompartment ac(cx, jsdscript->script);
michael@0 823 JS_ClearTrap(cx, jsdscript->script,
michael@0 824 (jsbytecode*)pc, nullptr, nullptr);
michael@0 825 }
michael@0 826
michael@0 827 JS_REMOVE_LINK(&jsdhook->links);
michael@0 828 free(jsdhook);
michael@0 829
michael@0 830 JSD_UNLOCK();
michael@0 831 return true;
michael@0 832 }
michael@0 833
michael@0 834 bool
michael@0 835 jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
michael@0 836 {
michael@0 837 JSDExecHook* jsdhook;
michael@0 838 JSCList* list = &jsdscript->hooks;
michael@0 839 JSD_LOCK();
michael@0 840
michael@0 841 while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
michael@0 842 {
michael@0 843 JS_REMOVE_LINK(&jsdhook->links);
michael@0 844 free(jsdhook);
michael@0 845 }
michael@0 846
michael@0 847 JS_ClearScriptTraps(jsdc->jsrt, jsdscript->script);
michael@0 848 JSD_UNLOCK();
michael@0 849
michael@0 850 return true;
michael@0 851 }
michael@0 852
michael@0 853 bool
michael@0 854 jsd_ClearAllExecutionHooks(JSDContext* jsdc)
michael@0 855 {
michael@0 856 JSDScript* jsdscript;
michael@0 857 JSDScript* iterp = nullptr;
michael@0 858
michael@0 859 JSD_LOCK();
michael@0 860 while( nullptr != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
michael@0 861 jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
michael@0 862 JSD_UNLOCK();
michael@0 863 return true;
michael@0 864 }
michael@0 865
michael@0 866 void
michael@0 867 jsd_ScriptCreated(JSDContext* jsdc,
michael@0 868 JSContext *cx,
michael@0 869 const char *filename, /* URL this script loads from */
michael@0 870 unsigned lineno, /* line where this script starts */
michael@0 871 JSScript *script,
michael@0 872 JSFunction *fun)
michael@0 873 {
michael@0 874 jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc);
michael@0 875 }
michael@0 876
michael@0 877 void
michael@0 878 jsd_ScriptDestroyed(JSDContext* jsdc,
michael@0 879 JSFreeOp *fop,
michael@0 880 JSScript *script)
michael@0 881 {
michael@0 882 jsd_DestroyScriptHookProc(fop, script, jsdc);
michael@0 883 }

mercurial