PageRenderTime 53ms CodeModel.GetById 8ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/js/jsd/jsd_scpt.c

http://github.com/zpao/v8monkey
C | 1002 lines | 779 code | 163 blank | 60 comment | 103 complexity | 83f14ea7f9ef847a101ad703cc421983 MD5 | raw file
   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
   2/* ***** BEGIN LICENSE BLOCK *****
   3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   4 *
   5 * The contents of this file are subject to the Mozilla Public License Version
   6 * 1.1 (the "License"); you may not use this file except in compliance with
   7 * the License. You may obtain a copy of the License at
   8 * http://www.mozilla.org/MPL/
   9 *
  10 * Software distributed under the License is distributed on an "AS IS" basis,
  11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12 * for the specific language governing rights and limitations under the
  13 * License.
  14 *
  15 * The Original Code is mozilla.org code.
  16 *
  17 * The Initial Developer of the Original Code is
  18 * Netscape Communications Corporation.
  19 * Portions created by the Initial Developer are Copyright (C) 1998
  20 * the Initial Developer. All Rights Reserved.
  21 *
  22 * Contributor(s):
  23 *
  24 * Alternatively, the contents of this file may be used under the terms of
  25 * either the GNU General Public License Version 2 or later (the "GPL"), or
  26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27 * in which case the provisions of the GPL or the LGPL are applicable instead
  28 * of those above. If you wish to allow use of your version of this file only
  29 * under the terms of either the GPL or the LGPL, and not to allow others to
  30 * use your version of this file under the terms of the MPL, indicate your
  31 * decision by deleting the provisions above and replace them with the notice
  32 * and other provisions required by the GPL or the LGPL. If you do not delete
  33 * the provisions above, a recipient may use your version of this file under
  34 * the terms of any one of the MPL, the GPL or the LGPL.
  35 *
  36 * ***** END LICENSE BLOCK ***** */
  37
  38/*
  39 * JavaScript Debugging support - Script support
  40 */
  41
  42#include "jsd.h"
  43#include "jsfriendapi.h"
  44
  45/* Comment this out to disable (NT specific) dumping as we go */
  46/*
  47** #ifdef DEBUG      
  48** #define JSD_DUMP 1
  49** #endif            
  50*/
  51
  52#define NOT_SET_YET -1
  53
  54/***************************************************************************/
  55
  56#ifdef DEBUG
  57void JSD_ASSERT_VALID_SCRIPT(JSDScript* jsdscript)
  58{
  59    JS_ASSERT(jsdscript);
  60    JS_ASSERT(jsdscript->script);
  61}
  62void JSD_ASSERT_VALID_EXEC_HOOK(JSDExecHook* jsdhook)
  63{
  64    JS_ASSERT(jsdhook);
  65    JS_ASSERT(jsdhook->hook);
  66}
  67#endif
  68
  69#ifdef LIVEWIRE
  70static JSBool
  71HasFileExtention(const char* name, const char* ext)
  72{
  73    int i;
  74    int len = strlen(ext);
  75    const char* p = strrchr(name,'.');
  76    if( !p )
  77        return JS_FALSE;
  78    p++;
  79    for(i = 0; i < len; i++ )
  80    {
  81        JS_ASSERT(islower(ext[i]));
  82        if( 0 == p[i] || tolower(p[i]) != ext[i] )
  83            return JS_FALSE;
  84    }
  85    if( 0 != p[i] )
  86        return JS_FALSE;
  87    return JS_TRUE;
  88}    
  89#endif /* LIVEWIRE */
  90
  91static JSDScript*
  92_newJSDScript(JSDContext*  jsdc,
  93              JSContext    *cx,
  94              JSScript     *script)
  95{
  96    JSDScript*  jsdscript;
  97    uintN     lineno;
  98    const char* raw_filename;
  99
 100    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 101
 102    /* these are inlined javascript: urls and we can't handle them now */
 103    lineno = (uintN) JS_GetScriptBaseLineNumber(cx, script);
 104    if( lineno == 0 )
 105        return NULL;
 106
 107    jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript));
 108    if( ! jsdscript )
 109        return NULL;
 110
 111    raw_filename = JS_GetScriptFilename(cx,script);
 112
 113    JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript);
 114    JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts);
 115    jsdscript->jsdc         = jsdc;
 116    jsdscript->script       = script;  
 117    jsdscript->lineBase     = lineno;
 118    jsdscript->lineExtent   = (uintN)NOT_SET_YET;
 119    jsdscript->data         = NULL;
 120#ifndef LIVEWIRE
 121    jsdscript->url          = (char*) jsd_BuildNormalizedURL(raw_filename);
 122#else
 123    jsdscript->app = LWDBG_GetCurrentApp();    
 124    if( jsdscript->app && raw_filename )
 125    {
 126        jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename);
 127        if( function )
 128        {
 129            JSString* funid = JS_GetFunctionId(function);
 130            char* funbytes;
 131            const char* funnanme;
 132            if( fuinid )
 133            {
 134                funbytes = JS_EncodeString(cx, funid);
 135                funname = funbytes ? funbytes : "";
 136            }
 137            else
 138            {
 139                funbytes = NULL;
 140                funname = "anonymous";
 141            }
 142            jsdscript->lwscript = 
 143                LWDBG_GetScriptOfFunction(jsdscript->app,funname);
 144            JS_Free(cx, funbytes);
 145    
 146            /* also, make sure this file is added to filelist if is .js file */
 147            if( HasFileExtention(raw_filename,"js") || 
 148                HasFileExtention(raw_filename,"sjs") )
 149            {
 150                jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE);
 151            }
 152        }
 153        else
 154        {
 155            jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript();
 156        }
 157    }
 158#endif
 159
 160    JS_INIT_CLIST(&jsdscript->hooks);
 161    
 162    return jsdscript;
 163}           
 164
 165static void 
 166_destroyJSDScript(JSDContext*  jsdc,
 167                  JSDScript*   jsdscript)
 168{
 169    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 170
 171    /* destroy all hooks */
 172    jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
 173
 174    JS_REMOVE_LINK(&jsdscript->links);
 175    if(jsdscript->url)
 176        free(jsdscript->url);
 177
 178    if (jsdscript->profileData)
 179        free(jsdscript->profileData);
 180    
 181    free(jsdscript);
 182}
 183
 184/***************************************************************************/
 185
 186#ifdef JSD_DUMP
 187#ifndef XP_WIN
 188void
 189OutputDebugString (char *buf)
 190{
 191    fprintf (stderr, "%s", buf);
 192}
 193#endif
 194
 195static void
 196_dumpJSDScript(JSDContext* jsdc, JSDScript* jsdscript, const char* leadingtext)
 197{
 198    const char* name;
 199    JSString* fun;
 200    uintN base;
 201    uintN extent;
 202    char Buf[256];
 203    size_t n;
 204
 205    name   = jsd_GetScriptFilename(jsdc, jsdscript);
 206    fun    = jsd_GetScriptFunctionId(jsdc, jsdscript);
 207    base   = jsd_GetScriptBaseLineNumber(jsdc, jsdscript);
 208    extent = jsd_GetScriptLineExtent(jsdc, jsdscript);
 209    n = size_t(snprintf(Buf, sizeof(Buf), "%sscript=%08X, %s, ",
 210                        leadingtext, (unsigned) jsdscript->script,
 211                        name ? name : "no URL"));
 212    if (n + 1 < sizeof(Buf)) {
 213        if (fun) {
 214            n += size_t(snprintf(Buf + n, sizeof(Buf) - n, "%s", "no fun"));
 215        } else {
 216            n += JS_PutEscapedFlatString(Buf + n, sizeof(Buf) - n,
 217                                         JS_ASSERT_STRING_IS_FLAT(fun), 0);
 218            Buf[sizeof(Buf) - 1] = '\0';
 219        }
 220        if (n + 1 < sizeof(Buf))
 221            snprintf(Buf + n, sizeof(Buf) - n, ", %d-%d\n", base, base + extent - 1);
 222    }
 223    OutputDebugString( Buf );
 224}
 225
 226static void
 227_dumpJSDScriptList( JSDContext* jsdc )
 228{
 229    JSDScript* iterp = NULL;
 230    JSDScript* jsdscript = NULL;
 231    
 232    OutputDebugString( "*** JSDScriptDump\n" );
 233    while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
 234        _dumpJSDScript( jsdc, jsdscript, "  script: " );
 235}
 236#endif /* JSD_DUMP */
 237
 238/***************************************************************************/
 239static JSHashNumber
 240jsd_hash_script(const void *key)
 241{
 242    return ((JSHashNumber)(ptrdiff_t) key) >> 2; /* help lame MSVC1.5 on Win16 */
 243}
 244
 245static void *
 246jsd_alloc_script_table(void *priv, size_t size)
 247{
 248    return malloc(size);
 249}
 250
 251static void
 252jsd_free_script_table(void *priv, void *item, size_t size)
 253{
 254    free(item);
 255}
 256
 257static JSHashEntry *
 258jsd_alloc_script_entry(void *priv, const void *item)
 259{
 260    return (JSHashEntry*) malloc(sizeof(JSHashEntry));
 261}
 262
 263static void
 264jsd_free_script_entry(void *priv, JSHashEntry *he, uintN flag)
 265{
 266    if (flag == HT_FREE_ENTRY)
 267    {
 268        _destroyJSDScript((JSDContext*) priv, (JSDScript*) he->value);
 269        free(he);
 270    }
 271}
 272
 273static JSHashAllocOps script_alloc_ops = {
 274    jsd_alloc_script_table, jsd_free_script_table,
 275    jsd_alloc_script_entry, jsd_free_script_entry
 276};
 277
 278#ifndef JSD_SCRIPT_HASH_SIZE
 279#define JSD_SCRIPT_HASH_SIZE 1024
 280#endif
 281
 282JSBool
 283jsd_InitScriptManager(JSDContext* jsdc)
 284{
 285    JS_INIT_CLIST(&jsdc->scripts);
 286    jsdc->scriptsTable = JS_NewHashTable(JSD_SCRIPT_HASH_SIZE, jsd_hash_script,
 287                                         JS_CompareValues, JS_CompareValues,
 288                                         &script_alloc_ops, (void*) jsdc);
 289    return !!jsdc->scriptsTable;
 290}
 291
 292void
 293jsd_DestroyScriptManager(JSDContext* jsdc)
 294{
 295    JSD_LOCK_SCRIPTS(jsdc);
 296    if (jsdc->scriptsTable)
 297        JS_HashTableDestroy(jsdc->scriptsTable);
 298    JSD_UNLOCK_SCRIPTS(jsdc);
 299}
 300
 301JSDScript*
 302jsd_FindJSDScript( JSDContext*  jsdc,
 303                   JSScript     *script )
 304{
 305    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 306    return (JSDScript*) JS_HashTableLookup(jsdc->scriptsTable, (void *)script);
 307}
 308
 309JSDScript *
 310jsd_FindOrCreateJSDScript(JSDContext    *jsdc,
 311                          JSContext     *cx,
 312                          JSScript      *script,
 313                          JSStackFrame  *fp)
 314{
 315    JSDScript *jsdscript;
 316    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 317
 318    jsdscript = jsd_FindJSDScript(jsdc, script);
 319    if (jsdscript)
 320        return jsdscript;
 321
 322    /* Fallback for unknown scripts: create a new script. */
 323    if (!fp)
 324        JS_FrameIterator(cx, &fp);
 325    if (fp)
 326        jsdscript = _newJSDScript(jsdc, cx, script);
 327
 328    return jsdscript;
 329}
 330
 331JSDProfileData*
 332jsd_GetScriptProfileData(JSDContext* jsdc, JSDScript *script)
 333{
 334    if (!script->profileData)
 335        script->profileData = (JSDProfileData*)calloc(1, sizeof(JSDProfileData));
 336
 337    return script->profileData;
 338}
 339
 340uint32_t
 341jsd_GetScriptFlags(JSDContext *jsdc, JSDScript *script)
 342{
 343    return script->flags;
 344}
 345
 346void
 347jsd_SetScriptFlags(JSDContext *jsdc, JSDScript *script, uint32_t flags)
 348{
 349    script->flags = flags;
 350}
 351
 352uintN
 353jsd_GetScriptCallCount(JSDContext* jsdc, JSDScript *script)
 354{
 355    if (script->profileData)
 356        return script->profileData->callCount;
 357
 358    return 0;
 359}
 360
 361uintN
 362jsd_GetScriptMaxRecurseDepth(JSDContext* jsdc, JSDScript *script)
 363{
 364    if (script->profileData)
 365        return script->profileData->maxRecurseDepth;
 366
 367    return 0;
 368}
 369
 370jsdouble
 371jsd_GetScriptMinExecutionTime(JSDContext* jsdc, JSDScript *script)
 372{
 373    if (script->profileData)
 374        return script->profileData->minExecutionTime;
 375
 376    return 0.0;
 377}
 378
 379jsdouble
 380jsd_GetScriptMaxExecutionTime(JSDContext* jsdc, JSDScript *script)
 381{
 382    if (script->profileData)
 383        return script->profileData->maxExecutionTime;
 384
 385    return 0.0;
 386}
 387
 388jsdouble
 389jsd_GetScriptTotalExecutionTime(JSDContext* jsdc, JSDScript *script)
 390{
 391    if (script->profileData)
 392        return script->profileData->totalExecutionTime;
 393
 394    return 0.0;
 395}
 396
 397jsdouble
 398jsd_GetScriptMinOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
 399{
 400    if (script->profileData)
 401        return script->profileData->minOwnExecutionTime;
 402
 403    return 0.0;
 404}
 405
 406jsdouble
 407jsd_GetScriptMaxOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
 408{
 409    if (script->profileData)
 410        return script->profileData->maxOwnExecutionTime;
 411
 412    return 0.0;
 413}
 414
 415jsdouble
 416jsd_GetScriptTotalOwnExecutionTime(JSDContext* jsdc, JSDScript *script)
 417{
 418    if (script->profileData)
 419        return script->profileData->totalOwnExecutionTime;
 420
 421    return 0.0;
 422}
 423
 424void
 425jsd_ClearScriptProfileData(JSDContext* jsdc, JSDScript *script)
 426{
 427    if (script->profileData)
 428    {
 429        free(script->profileData);
 430        script->profileData = NULL;
 431    }
 432}    
 433
 434JSScript *
 435jsd_GetJSScript (JSDContext *jsdc, JSDScript *script)
 436{
 437    return script->script;
 438}
 439
 440JSFunction *
 441jsd_GetJSFunction (JSDContext *jsdc, JSDScript *script)
 442{
 443    return JS_GetScriptFunction(jsdc->dumbContext, script->script);
 444}
 445
 446JSDScript*
 447jsd_IterateScripts(JSDContext* jsdc, JSDScript **iterp)
 448{
 449    JSDScript *jsdscript = *iterp;
 450    
 451    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 452
 453    if( !jsdscript )
 454        jsdscript = (JSDScript *)jsdc->scripts.next;
 455    if( jsdscript == (JSDScript *)&jsdc->scripts )
 456        return NULL;
 457    *iterp = (JSDScript*) jsdscript->links.next;
 458    return jsdscript;
 459}
 460
 461void *
 462jsd_SetScriptPrivate(JSDScript *jsdscript, void *data)
 463{
 464    void *rval = jsdscript->data;
 465    jsdscript->data = data;
 466    return rval;
 467}
 468
 469void *
 470jsd_GetScriptPrivate(JSDScript *jsdscript)
 471{
 472    return jsdscript->data;
 473}
 474
 475JSBool
 476jsd_IsActiveScript(JSDContext* jsdc, JSDScript *jsdscript)
 477{
 478    JSDScript *current;
 479
 480    JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc));
 481
 482    for( current = (JSDScript *)jsdc->scripts.next;
 483         current != (JSDScript *)&jsdc->scripts;
 484         current = (JSDScript *)current->links.next )
 485    {
 486        if(jsdscript == current)
 487            return JS_TRUE;
 488    }
 489    return JS_FALSE;
 490}        
 491
 492const char*
 493jsd_GetScriptFilename(JSDContext* jsdc, JSDScript *jsdscript)
 494{
 495    return jsdscript->url;
 496}
 497
 498JSString*
 499jsd_GetScriptFunctionId(JSDContext* jsdc, JSDScript *jsdscript)
 500{
 501    JSString* str;
 502    JSFunction *fun = jsd_GetJSFunction(jsdc, jsdscript);
 503
 504    if( ! fun )
 505        return NULL;
 506    str = JS_GetFunctionId(fun);
 507
 508    /* For compatibility we return "anonymous", not an empty string here. */
 509    return str ? str : JS_GetAnonymousString(jsdc->jsrt);
 510}
 511
 512uintN
 513jsd_GetScriptBaseLineNumber(JSDContext* jsdc, JSDScript *jsdscript)
 514{
 515    return jsdscript->lineBase;
 516}
 517
 518uintN
 519jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
 520{
 521    if( NOT_SET_YET == (int)jsdscript->lineExtent )
 522        jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
 523    return jsdscript->lineExtent;
 524}
 525
 526uintptr_t
 527jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
 528{
 529    uintptr_t pc;
 530    JSCrossCompartmentCall *call;
 531
 532    if( !jsdscript )
 533        return 0;
 534#ifdef LIVEWIRE
 535    if( jsdscript->lwscript )
 536    {
 537        uintN newline;
 538        jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
 539        if( line != newline )
 540            line = newline;
 541    }
 542#endif
 543
 544    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 545    if(!call)
 546        return 0;
 547    pc = (uintptr_t) JS_LineNumberToPC(jsdc->dumbContext, jsdscript->script, line );
 548    JS_LeaveCrossCompartmentCall(call);
 549    return pc;
 550}
 551
 552uintN
 553jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
 554{
 555    JSCrossCompartmentCall *call;
 556    uintN first = jsdscript->lineBase;
 557    uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
 558    uintN line = 0;
 559
 560    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 561    if(!call)
 562        return 0;
 563    if (pc)
 564        line = JS_PCToLineNumber(jsdc->dumbContext, jsdscript->script, (jsbytecode*)pc);
 565    JS_LeaveCrossCompartmentCall(call);
 566
 567    if( line < first )
 568        return first;
 569    if( line > last )
 570        return last;
 571
 572#ifdef LIVEWIRE
 573    if( jsdscript && jsdscript->lwscript )
 574    {
 575        uintN newline;
 576        jsdlw_ProcessedToRawLineNumber(jsdc, jsdscript, line, &newline);
 577        line = newline;
 578    }
 579#endif
 580
 581    return line;    
 582}
 583
 584JSBool
 585jsd_GetLinePCs(JSDContext* jsdc, JSDScript* jsdscript,
 586               uintN startLine, uintN maxLines,
 587               uintN* count, uintN** retLines, uintptr_t** retPCs)
 588{
 589    JSCrossCompartmentCall *call;
 590    uintN first = jsdscript->lineBase;
 591    uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
 592    JSBool ok;
 593    uintN *lines;
 594    jsbytecode **pcs;
 595    uintN i;
 596
 597    if (last < startLine)
 598        return JS_TRUE;
 599
 600    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 601    if (!call)
 602        return JS_FALSE;
 603
 604    ok = JS_GetLinePCs(jsdc->dumbContext, jsdscript->script,
 605                       startLine, maxLines,
 606                       count, retLines, &pcs);
 607
 608    if (ok) {
 609        if (retPCs) {
 610            for (i = 0; i < *count; ++i) {
 611                (*retPCs)[i] = (*pcs)[i];
 612            }
 613        }
 614
 615        JS_free(jsdc->dumbContext, pcs);
 616    }
 617
 618    JS_LeaveCrossCompartmentCall(call);
 619    return ok;
 620}
 621
 622JSBool
 623jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata)
 624{
 625    JSD_LOCK();
 626    jsdc->scriptHook = hook;
 627    jsdc->scriptHookData = callerdata;
 628    JSD_UNLOCK();
 629    return JS_TRUE;
 630}
 631
 632JSBool
 633jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
 634{
 635    JSD_LOCK();
 636    if( hook )
 637        *hook = jsdc->scriptHook;
 638    if( callerdata )
 639        *callerdata = jsdc->scriptHookData;
 640    JSD_UNLOCK();
 641    return JS_TRUE;
 642}    
 643
 644JSBool
 645jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
 646{
 647    JSCrossCompartmentCall *call;
 648    JSBool rv;
 649    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 650    if(!call)
 651        return JS_FALSE;
 652    JSD_LOCK();
 653    rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
 654    JSD_UNLOCK();
 655    JS_LeaveCrossCompartmentCall(call);
 656    return rv;
 657}
 658
 659
 660/***************************************************************************/
 661
 662void
 663jsd_NewScriptHookProc( 
 664                JSContext   *cx,
 665                const char  *filename,      /* URL this script loads from */
 666                uintN       lineno,         /* line where this script starts */
 667                JSScript    *script,
 668                JSFunction  *fun,                
 669                void*       callerdata )
 670{
 671    JSDScript* jsdscript = NULL;
 672    JSDContext* jsdc = (JSDContext*) callerdata;
 673    JSD_ScriptHookProc      hook;
 674    void*                   hookData;
 675    
 676    JSD_ASSERT_VALID_CONTEXT(jsdc);
 677
 678    if( JSD_IS_DANGEROUS_THREAD(jsdc) )
 679        return;
 680    
 681    JSD_LOCK_SCRIPTS(jsdc);
 682    jsdscript = _newJSDScript(jsdc, cx, script);
 683    JSD_UNLOCK_SCRIPTS(jsdc);
 684    if( ! jsdscript )
 685        return;
 686
 687#ifdef JSD_DUMP
 688    JSD_LOCK_SCRIPTS(jsdc);
 689    _dumpJSDScript(jsdc, jsdscript, "***NEW Script: ");
 690    _dumpJSDScriptList( jsdc );
 691    JSD_UNLOCK_SCRIPTS(jsdc);
 692#endif /* JSD_DUMP */
 693
 694    /* local in case jsdc->scriptHook gets cleared on another thread */
 695    JSD_LOCK();
 696    hook = jsdc->scriptHook;
 697    hookData = jsdc->scriptHookData;
 698    JSD_UNLOCK();
 699
 700    if( hook )
 701        hook(jsdc, jsdscript, JS_TRUE, hookData);
 702}
 703
 704void
 705jsd_DestroyScriptHookProc( 
 706                JSContext   *cx,
 707                JSScript    *script,
 708                void*       callerdata )
 709{
 710    JSDScript* jsdscript = NULL;
 711    JSDContext* jsdc = (JSDContext*) callerdata;
 712    JSD_ScriptHookProc      hook;
 713    void*                   hookData;
 714    
 715    JSD_ASSERT_VALID_CONTEXT(jsdc);
 716
 717    if( JSD_IS_DANGEROUS_THREAD(jsdc) )
 718        return;
 719    
 720    JSD_LOCK_SCRIPTS(jsdc);
 721    jsdscript = jsd_FindJSDScript(jsdc, script);
 722    JSD_UNLOCK_SCRIPTS(jsdc);
 723
 724    if( ! jsdscript )
 725        return;
 726
 727#ifdef JSD_DUMP
 728    JSD_LOCK_SCRIPTS(jsdc);
 729    _dumpJSDScript(jsdc, jsdscript, "***DESTROY Script: ");
 730    JSD_UNLOCK_SCRIPTS(jsdc);
 731#endif /* JSD_DUMP */
 732
 733    /* local in case hook gets cleared on another thread */
 734    JSD_LOCK();
 735    hook = jsdc->scriptHook;
 736    hookData = jsdc->scriptHookData;
 737    JSD_UNLOCK();
 738
 739    if( hook )
 740        hook(jsdc, jsdscript, JS_FALSE, hookData);
 741
 742    JSD_LOCK_SCRIPTS(jsdc);
 743    JS_HashTableRemove(jsdc->scriptsTable, (void *)script);
 744    JSD_UNLOCK_SCRIPTS(jsdc);
 745
 746#ifdef JSD_DUMP
 747    JSD_LOCK_SCRIPTS(jsdc);
 748    _dumpJSDScriptList(jsdc);
 749    JSD_UNLOCK_SCRIPTS(jsdc);
 750#endif /* JSD_DUMP */
 751}                
 752
 753
 754/***************************************************************************/
 755
 756static JSDExecHook*
 757_findHook(JSDContext* jsdc, JSDScript* jsdscript, uintptr_t pc)
 758{
 759    JSDExecHook* jsdhook;
 760    JSCList* list = &jsdscript->hooks;
 761
 762    for( jsdhook = (JSDExecHook*)list->next;
 763         jsdhook != (JSDExecHook*)list;
 764         jsdhook = (JSDExecHook*)jsdhook->links.next )
 765    {
 766        if (jsdhook->pc == pc)
 767            return jsdhook;
 768    }
 769    return NULL;
 770}
 771
 772static JSBool
 773_isActiveHook(JSDContext* jsdc, JSScript *script, JSDExecHook* jsdhook)
 774{
 775    JSDExecHook* current;
 776    JSCList* list;
 777    JSDScript* jsdscript;
 778
 779    JSD_LOCK_SCRIPTS(jsdc);
 780    jsdscript = jsd_FindJSDScript(jsdc, script);
 781    if( ! jsdscript)
 782    {
 783        JSD_UNLOCK_SCRIPTS(jsdc);
 784        return JS_FALSE;
 785    }
 786
 787    list = &jsdscript->hooks;
 788
 789    for( current = (JSDExecHook*)list->next;
 790         current != (JSDExecHook*)list;
 791         current = (JSDExecHook*)current->links.next )
 792    {
 793        if(current == jsdhook)
 794        {
 795            JSD_UNLOCK_SCRIPTS(jsdc);
 796            return JS_TRUE;
 797        }
 798    }
 799    JSD_UNLOCK_SCRIPTS(jsdc);
 800    return JS_FALSE;
 801}
 802
 803
 804JSTrapStatus
 805jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
 806                jsval closure)
 807{
 808    JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure);
 809    JSD_ExecutionHookProc hook;
 810    void* hookData;
 811    JSDContext*  jsdc;
 812    JSDScript* jsdscript;
 813
 814    JSD_LOCK();
 815
 816    if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
 817        ! _isActiveHook(jsdc, script, jsdhook) )
 818    {
 819        JSD_UNLOCK();
 820        return JSTRAP_CONTINUE;
 821    }
 822
 823    JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
 824    JS_ASSERT(!jsdhook->pc || jsdhook->pc == (uintptr_t)pc);
 825    JS_ASSERT(jsdhook->jsdscript->script == script);
 826    JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
 827
 828    hook = jsdhook->hook;
 829    hookData = jsdhook->callerdata;
 830    jsdscript = jsdhook->jsdscript;
 831
 832    /* do not use jsdhook-> after this point */
 833    JSD_UNLOCK();
 834
 835    if( ! jsdc || ! jsdc->inited )
 836        return JSTRAP_CONTINUE;
 837
 838    if( JSD_IS_DANGEROUS_THREAD(jsdc) )
 839        return JSTRAP_CONTINUE;
 840
 841#ifdef LIVEWIRE
 842    if( ! jsdlw_UserCodeAtPC(jsdc, jsdscript, (uintptr_t)pc) )
 843        return JSTRAP_CONTINUE;
 844#endif
 845
 846    return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_BREAKPOINT,
 847                                 hook, hookData, rval);
 848}
 849
 850
 851
 852JSBool
 853jsd_SetExecutionHook(JSDContext*           jsdc, 
 854                     JSDScript*            jsdscript,
 855                     uintptr_t             pc,
 856                     JSD_ExecutionHookProc hook,
 857                     void*                 callerdata)
 858{
 859    JSDExecHook* jsdhook;
 860    JSBool rv;
 861    JSCrossCompartmentCall *call;
 862
 863    JSD_LOCK();
 864    if( ! hook )
 865    {
 866        jsd_ClearExecutionHook(jsdc, jsdscript, pc);
 867        JSD_UNLOCK();
 868        return JS_TRUE;
 869    }
 870
 871    jsdhook = _findHook(jsdc, jsdscript, pc);
 872    if( jsdhook )
 873    {
 874        jsdhook->hook       = hook;
 875        jsdhook->callerdata = callerdata;
 876        JSD_UNLOCK();
 877        return JS_TRUE;
 878    }
 879    /* else... */
 880
 881    jsdhook = (JSDExecHook*)calloc(1, sizeof(JSDExecHook));
 882    if( ! jsdhook ) {
 883        JSD_UNLOCK();
 884        return JS_FALSE;
 885    }
 886    jsdhook->jsdscript  = jsdscript;
 887    jsdhook->pc         = pc;
 888    jsdhook->hook       = hook;
 889    jsdhook->callerdata = callerdata;
 890
 891    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 892    if(!call) {
 893        free(jsdhook);
 894        JSD_UNLOCK();
 895        return JS_FALSE;
 896    }
 897
 898    rv = JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
 899                    (jsbytecode*)pc, jsd_TrapHandler,
 900                    PRIVATE_TO_JSVAL(jsdhook));
 901
 902    JS_LeaveCrossCompartmentCall(call);
 903
 904    if ( ! rv ) {
 905        free(jsdhook);
 906        JSD_UNLOCK();
 907        return JS_FALSE;
 908    }
 909
 910    JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
 911    JSD_UNLOCK();
 912
 913    return JS_TRUE;
 914}
 915
 916JSBool
 917jsd_ClearExecutionHook(JSDContext*           jsdc, 
 918                       JSDScript*            jsdscript,
 919                       uintptr_t             pc)
 920{
 921    JSCrossCompartmentCall *call;
 922    JSDExecHook* jsdhook;
 923
 924    JSD_LOCK();
 925
 926    jsdhook = _findHook(jsdc, jsdscript, pc);
 927    if( ! jsdhook )
 928    {
 929        JSD_UNLOCK();
 930        return JS_FALSE;
 931    }
 932
 933    call = JS_EnterCrossCompartmentCallScript(jsdc->dumbContext, jsdscript->script);
 934    if(!call) {
 935        JSD_UNLOCK();
 936        return JS_FALSE;
 937    }
 938
 939    JS_ClearTrap(jsdc->dumbContext, jsdscript->script, 
 940                 (jsbytecode*)pc, NULL, NULL );
 941
 942    JS_LeaveCrossCompartmentCall(call);
 943
 944    JS_REMOVE_LINK(&jsdhook->links);
 945    free(jsdhook);
 946
 947    JSD_UNLOCK();
 948    return JS_TRUE;
 949}
 950
 951JSBool
 952jsd_ClearAllExecutionHooksForScript(JSDContext* jsdc, JSDScript* jsdscript)
 953{
 954    JSDExecHook* jsdhook;
 955    JSCList* list = &jsdscript->hooks;
 956
 957    JSD_LOCK();
 958
 959    while( (JSDExecHook*)list != (jsdhook = (JSDExecHook*)list->next) )
 960    {
 961        JS_REMOVE_LINK(&jsdhook->links);
 962        free(jsdhook);
 963    }
 964
 965    /* No cross-compartment call here because we may be in the middle of GC */
 966    JS_ClearScriptTraps(jsdc->dumbContext, jsdscript->script);
 967    JSD_UNLOCK();
 968
 969    return JS_TRUE;
 970}
 971
 972JSBool
 973jsd_ClearAllExecutionHooks(JSDContext* jsdc)
 974{
 975    JSDScript* jsdscript;
 976    JSDScript* iterp = NULL;
 977
 978    JSD_LOCK();
 979    while( NULL != (jsdscript = jsd_IterateScripts(jsdc, &iterp)) )
 980        jsd_ClearAllExecutionHooksForScript(jsdc, jsdscript);
 981    JSD_UNLOCK();
 982    return JS_TRUE;
 983}
 984
 985void
 986jsd_ScriptCreated(JSDContext* jsdc,
 987                  JSContext   *cx,
 988                  const char  *filename,    /* URL this script loads from */
 989                  uintN       lineno,       /* line where this script starts */
 990                  JSScript    *script,
 991                  JSFunction  *fun)
 992{
 993    jsd_NewScriptHookProc(cx, filename, lineno, script, fun, jsdc);
 994}
 995
 996void
 997jsd_ScriptDestroyed(JSDContext* jsdc,
 998                    JSContext   *cx,
 999                    JSScript    *script)
1000{
1001    jsd_DestroyScriptHookProc(cx, script, jsdc);
1002}