PageRenderTime 1171ms CodeModel.GetById 120ms app.highlight 520ms RepoModel.GetById 157ms app.codeStats 4ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsscript.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1891 lines | 1413 code | 233 blank | 245 comment | 330 complexity | 976c94b2ccb4ded5f3ff922ec272d6c8 MD5 | raw file
   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set ts=8 sw=4 et tw=78:
   3 *
   4 * ***** BEGIN LICENSE BLOCK *****
   5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   6 *
   7 * The contents of this file are subject to the Mozilla Public License Version
   8 * 1.1 (the "License"); you may not use this file except in compliance with
   9 * the License. You may obtain a copy of the License at
  10 * http://www.mozilla.org/MPL/
  11 *
  12 * Software distributed under the License is distributed on an "AS IS" basis,
  13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14 * for the specific language governing rights and limitations under the
  15 * License.
  16 *
  17 * The Original Code is Mozilla Communicator client code, released
  18 * March 31, 1998.
  19 *
  20 * The Initial Developer of the Original Code is
  21 * Netscape Communications Corporation.
  22 * Portions created by the Initial Developer are Copyright (C) 1998
  23 * the Initial Developer. All Rights Reserved.
  24 *
  25 * Contributor(s):
  26 *
  27 * Alternatively, the contents of this file may be used under the terms of
  28 * either of the GNU General Public License Version 2 or later (the "GPL"),
  29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30 * in which case the provisions of the GPL or the LGPL are applicable instead
  31 * of those above. If you wish to allow use of your version of this file only
  32 * under the terms of either the GPL or the LGPL, and not to allow others to
  33 * use your version of this file under the terms of the MPL, indicate your
  34 * decision by deleting the provisions above and replace them with the notice
  35 * and other provisions required by the GPL or the LGPL. If you do not delete
  36 * the provisions above, a recipient may use your version of this file under
  37 * the terms of any one of the MPL, the GPL or the LGPL.
  38 *
  39 * ***** END LICENSE BLOCK ***** */
  40
  41/*
  42 * JS script operations.
  43 */
  44#include "jsstddef.h"
  45#include <string.h>
  46#include "jstypes.h"
  47#include "jsutil.h" /* Added by JSIFY */
  48#include "jsprf.h"
  49#include "jsapi.h"
  50#include "jsatom.h"
  51#include "jscntxt.h"
  52#include "jsversion.h"
  53#include "jsdbgapi.h"
  54#include "jsemit.h"
  55#include "jsfun.h"
  56#include "jsinterp.h"
  57#include "jslock.h"
  58#include "jsnum.h"
  59#include "jsopcode.h"
  60#include "jsparse.h"
  61#include "jsscope.h"
  62#include "jsscript.h"
  63#if JS_HAS_XDR
  64#include "jsxdrapi.h"
  65#endif
  66
  67#if JS_HAS_SCRIPT_OBJECT
  68
  69static const char js_script_exec_str[]    = "Script.prototype.exec";
  70static const char js_script_compile_str[] = "Script.prototype.compile";
  71
  72/*
  73 * This routine requires that obj has been locked previously.
  74 */
  75static jsint
  76GetScriptExecDepth(JSContext *cx, JSObject *obj)
  77{
  78    jsval v;
  79
  80    JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
  81    v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass));
  82    return JSVAL_TO_INT(v);
  83}
  84
  85static void
  86AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta)
  87{
  88    jsint execDepth;
  89
  90    JS_LOCK_OBJ(cx, obj);
  91    execDepth = GetScriptExecDepth(cx, obj);
  92    LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass),
  93                        INT_TO_JSVAL(execDepth + delta));
  94    JS_UNLOCK_OBJ(cx, obj);
  95}
  96
  97#if JS_HAS_TOSOURCE
  98static JSBool
  99script_toSource(JSContext *cx, uintN argc, jsval *vp)
 100{
 101    JSObject *obj;
 102    uint32 indent;
 103    JSScript *script;
 104    size_t i, j, k, n;
 105    char buf[16];
 106    jschar *s, *t;
 107    JSString *str;
 108
 109    obj = JS_THIS_OBJECT(cx, vp);
 110    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
 111        return JS_FALSE;
 112
 113    indent = 0;
 114    if (argc != 0) {
 115        indent = js_ValueToECMAUint32(cx, &vp[2]);
 116        if (JSVAL_IS_NULL(vp[2]))
 117            return JS_FALSE;
 118    }
 119
 120    script = (JSScript *) JS_GetPrivate(cx, obj);
 121
 122    /* Let n count the source string length, j the "front porch" length. */
 123    j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
 124    n = j + 2;
 125    if (!script) {
 126        /* Let k count the constructor argument string length. */
 127        k = 0;
 128        s = NULL;               /* quell GCC overwarning */
 129    } else {
 130        str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
 131                                 (uintN)indent);
 132        if (!str)
 133            return JS_FALSE;
 134        str = js_QuoteString(cx, str, '\'');
 135        if (!str)
 136            return JS_FALSE;
 137        JSSTRING_CHARS_AND_LENGTH(str, s, k);
 138        n += k;
 139    }
 140
 141    /* Allocate the source string and copy into it. */
 142    t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
 143    if (!t)
 144        return JS_FALSE;
 145    for (i = 0; i < j; i++)
 146        t[i] = buf[i];
 147    for (j = 0; j < k; i++, j++)
 148        t[i] = s[j];
 149    t[i++] = ')';
 150    t[i++] = ')';
 151    t[i] = 0;
 152
 153    /* Create and return a JS string for t. */
 154    str = JS_NewUCString(cx, t, n);
 155    if (!str) {
 156        JS_free(cx, t);
 157        return JS_FALSE;
 158    }
 159    *vp = STRING_TO_JSVAL(str);
 160    return JS_TRUE;
 161}
 162#endif /* JS_HAS_TOSOURCE */
 163
 164static JSBool
 165script_toString(JSContext *cx, uintN argc, jsval *vp)
 166{
 167    uint32 indent;
 168    JSObject *obj;
 169    JSScript *script;
 170    JSString *str;
 171
 172    indent = 0;
 173    if (argc != 0) {
 174        indent = js_ValueToECMAUint32(cx, &vp[2]);
 175        if (JSVAL_IS_NULL(vp[2]))
 176            return JS_FALSE;
 177    }
 178
 179    obj = JS_THIS_OBJECT(cx, vp);
 180    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
 181        return JS_FALSE;
 182    script = (JSScript *) JS_GetPrivate(cx, obj);
 183    if (!script) {
 184        *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
 185        return JS_TRUE;
 186    }
 187
 188    str = JS_DecompileScript(cx, script, "Script.prototype.toString",
 189                             (uintN)indent);
 190    if (!str)
 191        return JS_FALSE;
 192    *vp = STRING_TO_JSVAL(str);
 193    return JS_TRUE;
 194}
 195
 196static JSBool
 197script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 198                   jsval *rval)
 199{
 200    JSString *str;
 201    JSObject *scopeobj;
 202    jsval v;
 203    JSScript *script, *oldscript;
 204    JSStackFrame *caller;
 205    const char *file;
 206    uintN line;
 207    JSPrincipals *principals;
 208    uint32 tcflags;
 209    jsint execDepth;
 210
 211    /* Make sure obj is a Script object. */
 212    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
 213        return JS_FALSE;
 214
 215    /* If no args, leave private undefined and return early. */
 216    if (argc == 0)
 217        goto out;
 218
 219    /* Otherwise, the first arg is the script source to compile. */
 220    str = js_ValueToString(cx, argv[0]);
 221    if (!str)
 222        return JS_FALSE;
 223    argv[0] = STRING_TO_JSVAL(str);
 224
 225    scopeobj = NULL;
 226    if (argc >= 2) {
 227        if (!js_ValueToObject(cx, argv[1], &scopeobj))
 228            return JS_FALSE;
 229        argv[1] = OBJECT_TO_JSVAL(scopeobj);
 230    }
 231
 232    /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
 233    caller = JS_GetScriptedCaller(cx, cx->fp);
 234    JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain);
 235
 236    if (caller) {
 237        if (!scopeobj) {
 238            scopeobj = js_GetScopeChain(cx, caller);
 239            if (!scopeobj)
 240                return JS_FALSE;
 241        }
 242
 243        principals = JS_EvalFramePrincipals(cx, cx->fp, caller);
 244        file = js_ComputeFilename(cx, caller, principals, &line);
 245    } else {
 246        file = NULL;
 247        line = 0;
 248        principals = NULL;
 249    }
 250
 251    /* Ensure we compile this script with the right (inner) principals. */
 252    scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str);
 253    if (!scopeobj)
 254        return JS_FALSE;
 255
 256    /*
 257     * Compile the new script using the caller's scope chain, a la eval().
 258     * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in
 259     * tcflags and use NULL for the callerFrame argument, because compilation
 260     * is here separated from execution, and the run-time scope chain may not
 261     * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
 262     * jsparse.c to optimize based on identity of run- and compile-time scope.
 263     */
 264    tcflags = 0;
 265    script = js_CompileScript(cx, scopeobj, NULL, principals, tcflags,
 266                              JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
 267                              NULL, file, line);
 268    if (!script)
 269        return JS_FALSE;
 270
 271    JS_LOCK_OBJ(cx, obj);
 272    execDepth = GetScriptExecDepth(cx, obj);
 273
 274    /*
 275     * execDepth must be 0 to allow compilation here, otherwise the JSScript
 276     * struct can be released while running.
 277     */
 278    if (execDepth > 0) {
 279        JS_UNLOCK_OBJ(cx, obj);
 280        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 281                             JSMSG_COMPILE_EXECED_SCRIPT);
 282        return JS_FALSE;
 283    }
 284
 285    /* Swap script for obj's old script, if any. */
 286    v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
 287    oldscript = (JSScript*) (!JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL);
 288    LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
 289    JS_UNLOCK_OBJ(cx, obj);
 290
 291    if (oldscript)
 292        js_DestroyScript(cx, oldscript);
 293
 294    script->u.object = obj;
 295    js_CallNewScriptHook(cx, script, NULL);
 296
 297out:
 298    /* Return the object. */
 299    *rval = OBJECT_TO_JSVAL(obj);
 300    return JS_TRUE;
 301}
 302
 303static JSBool
 304script_compile(JSContext *cx, uintN argc, jsval *vp)
 305{
 306    return script_compile_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp);
 307}
 308
 309static JSBool
 310script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 311                jsval *rval)
 312{
 313    JSObject *scopeobj, *parent;
 314    JSStackFrame *fp, *caller;
 315    JSPrincipals *principals;
 316    JSScript *script;
 317    JSBool ok;
 318
 319    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
 320        return JS_FALSE;
 321
 322    scopeobj = NULL;
 323    if (argc != 0) {
 324        if (!js_ValueToObject(cx, argv[0], &scopeobj))
 325            return JS_FALSE;
 326        argv[0] = OBJECT_TO_JSVAL(scopeobj);
 327    }
 328
 329    /*
 330     * Emulate eval() by using caller's this, var object, sharp array, etc.,
 331     * all propagated by js_Execute via a non-null fourth (down) argument to
 332     * js_Execute.  If there is no scripted caller, js_Execute uses its second
 333     * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
 334     *
 335     * Unlike eval, which the compiler detects, Script.prototype.exec may be
 336     * called from a lightweight function, or even from native code (in which
 337     * case fp->varobj and fp->scopeChain are null).  If exec is called from
 338     * a lightweight function, we will need to get a Call object representing
 339     * its frame, to act as the var object and scope chain head.
 340     */
 341    fp = cx->fp;
 342    caller = JS_GetScriptedCaller(cx, fp);
 343    if (caller && !caller->varobj) {
 344        /* Called from a lightweight function. */
 345        JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags));
 346
 347        /* Scope chain links from Call object to callee's parent. */
 348        parent = OBJ_GET_PARENT(cx, caller->callee);
 349        if (!js_GetCallObject(cx, caller, parent))
 350            return JS_FALSE;
 351    }
 352
 353    if (!scopeobj) {
 354        /* No scope object passed in: try to use the caller's scope chain. */
 355        if (caller) {
 356            /*
 357             * Load caller->scopeChain after the conditional js_GetCallObject
 358             * call above, which resets scopeChain as well as varobj.
 359             */
 360            scopeobj = js_GetScopeChain(cx, caller);
 361            if (!scopeobj)
 362                return JS_FALSE;
 363        } else {
 364            /*
 365             * Called from native code, so we don't know what scope object to
 366             * use.  We could use parent (see above), but Script.prototype.exec
 367             * might be a shared/sealed "superglobal" method.  A more general
 368             * approach would use cx->globalObject, which will be the same as
 369             * exec.__parent__ in the non-superglobal case.  In the superglobal
 370             * case it's the right object: the global, not the superglobal.
 371             */
 372            scopeobj = cx->globalObject;
 373        }
 374    }
 375
 376    scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec_str);
 377    if (!scopeobj)
 378        return JS_FALSE;
 379
 380    /* Keep track of nesting depth for the script. */
 381    AdjustScriptExecDepth(cx, obj, 1);
 382
 383    /* Must get to out label after this */
 384    script = (JSScript *) JS_GetPrivate(cx, obj);
 385    if (!script) {
 386        ok = JS_FALSE;
 387        goto out;
 388    }
 389
 390    /* Belt-and-braces: check that this script object has access to scopeobj. */
 391    principals = script->principals;
 392    ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
 393                                  CLASS_ATOM(cx, Script));
 394    if (!ok)
 395        goto out;
 396
 397    ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
 398
 399out:
 400    AdjustScriptExecDepth(cx, obj, -1);
 401    return ok;
 402}
 403
 404static JSBool
 405script_exec(JSContext *cx, uintN argc, jsval *vp)
 406{
 407    return script_exec_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp);
 408}
 409
 410#endif /* JS_HAS_SCRIPT_OBJECT */
 411
 412#if JS_HAS_XDR
 413
 414JSBool
 415js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
 416{
 417    JSContext *cx;
 418    JSScript *script, *oldscript;
 419    JSBool ok;
 420    jsbytecode *code;
 421    uint32 length, lineno, nslots, magic;
 422    uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, i;
 423    uint32 prologLength, version;
 424    JSTempValueRooter tvr;
 425    JSPrincipals *principals;
 426    uint32 encodeable;
 427    JSBool filenameWasSaved;
 428    jssrcnote *notes, *sn;
 429    JSSecurityCallbacks *callbacks;
 430
 431    cx = xdr->cx;
 432    script = *scriptp;
 433    nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = 0;
 434    filenameWasSaved = JS_FALSE;
 435    notes = NULL;
 436
 437    if (xdr->mode == JSXDR_ENCODE)
 438        magic = JSXDR_MAGIC_SCRIPT_CURRENT;
 439    if (!JS_XDRUint32(xdr, &magic))
 440        return JS_FALSE;
 441    if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) {
 442        /* We do not provide binary compatibility with older scripts. */
 443        if (!hasMagic) {
 444            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 445                                 JSMSG_BAD_SCRIPT_MAGIC);
 446            return JS_FALSE;
 447        }
 448        *hasMagic = JS_FALSE;
 449        return JS_TRUE;
 450    }
 451    if (hasMagic)
 452        *hasMagic = JS_TRUE;
 453
 454    if (xdr->mode == JSXDR_ENCODE) {
 455        length = script->length;
 456        prologLength = PTRDIFF(script->main, script->code, jsbytecode);
 457        JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
 458        version = (uint32)script->version | (script->nfixed << 16);
 459        lineno = (uint32)script->lineno;
 460        nslots = (uint32)script->nslots;
 461        nslots = (uint32)((script->staticDepth << 16) | script->nslots);
 462        natoms = (uint32)script->atomMap.length;
 463
 464        /* Count the srcnotes, keeping notes pointing at the first one. */
 465        notes = SCRIPT_NOTES(script);
 466        for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
 467            continue;
 468        nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
 469        nsrcnotes++;            /* room for the terminator */
 470
 471        if (script->objectsOffset != 0)
 472            nobjects = JS_SCRIPT_OBJECTS(script)->length;
 473        if (script->upvarsOffset != 0)
 474            nupvars = JS_SCRIPT_UPVARS(script)->length;
 475        if (script->regexpsOffset != 0)
 476            nregexps = JS_SCRIPT_REGEXPS(script)->length;
 477        if (script->trynotesOffset != 0)
 478            ntrynotes = JS_SCRIPT_TRYNOTES(script)->length;
 479    }
 480
 481    if (!JS_XDRUint32(xdr, &length))
 482        return JS_FALSE;
 483    if (!JS_XDRUint32(xdr, &prologLength))
 484        return JS_FALSE;
 485    if (!JS_XDRUint32(xdr, &version))
 486        return JS_FALSE;
 487
 488    /*
 489     * To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
 490     * and trynote counts early.
 491     */
 492    if (!JS_XDRUint32(xdr, &natoms))
 493        return JS_FALSE;
 494    if (!JS_XDRUint32(xdr, &nsrcnotes))
 495        return JS_FALSE;
 496    if (!JS_XDRUint32(xdr, &ntrynotes))
 497        return JS_FALSE;
 498    if (!JS_XDRUint32(xdr, &nobjects))
 499        return JS_FALSE;
 500    if (!JS_XDRUint32(xdr, &nupvars))
 501        return JS_FALSE;
 502    if (!JS_XDRUint32(xdr, &nregexps))
 503        return JS_FALSE;
 504
 505    if (xdr->mode == JSXDR_DECODE) {
 506        script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
 507                              nregexps, ntrynotes);
 508        if (!script)
 509            return JS_FALSE;
 510
 511        script->main += prologLength;
 512        script->version = (JSVersion) (version & 0xffff);
 513        script->nfixed = (uint16) (version >> 16);
 514
 515        /* If we know nsrcnotes, we allocated space for notes in script. */
 516        notes = SCRIPT_NOTES(script);
 517        *scriptp = script;
 518        JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr);
 519    }
 520
 521    /*
 522     * Control hereafter must goto error on failure, in order for the
 523     * DECODE case to destroy script.
 524     */
 525    oldscript = xdr->script;
 526    code = script->code;
 527    if (xdr->mode == JSXDR_ENCODE) {
 528        code = js_UntrapScriptCode(cx, script);
 529        if (!code)
 530            goto error;
 531    }
 532
 533    xdr->script = script;
 534    ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode));
 535
 536    if (code != script->code)
 537        JS_free(cx, code);
 538
 539    if (!ok)
 540        goto error;
 541
 542    if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
 543        !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
 544        !JS_XDRUint32(xdr, &lineno) ||
 545        !JS_XDRUint32(xdr, &nslots)) {
 546        goto error;
 547    }
 548
 549    callbacks = JS_GetSecurityCallbacks(cx);
 550    if (xdr->mode == JSXDR_ENCODE) {
 551        principals = script->principals;
 552        encodeable = callbacks && callbacks->principalsTranscoder;
 553        if (!JS_XDRUint32(xdr, &encodeable))
 554            goto error;
 555        if (encodeable &&
 556            !callbacks->principalsTranscoder(xdr, &principals)) {
 557            goto error;
 558        }
 559    } else {
 560        if (!JS_XDRUint32(xdr, &encodeable))
 561            goto error;
 562        if (encodeable) {
 563            if (!(callbacks && callbacks->principalsTranscoder)) {
 564                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 565                                     JSMSG_CANT_DECODE_PRINCIPALS);
 566                goto error;
 567            }
 568            if (!callbacks->principalsTranscoder(xdr, &principals))
 569                goto error;
 570            script->principals = principals;
 571        }
 572    }
 573
 574    if (xdr->mode == JSXDR_DECODE) {
 575        const char *filename = script->filename;
 576        if (filename) {
 577            filename = js_SaveScriptFilename(cx, filename);
 578            if (!filename)
 579                goto error;
 580            JS_free(cx, (void *) script->filename);
 581            script->filename = filename;
 582            filenameWasSaved = JS_TRUE;
 583        }
 584        script->lineno = (uintN)lineno;
 585        script->nslots = (uint16)nslots;
 586        script->staticDepth = nslots >> 16;
 587    }
 588
 589    for (i = 0; i != natoms; ++i) {
 590        if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
 591            goto error;
 592    }
 593
 594    /*
 595     * Here looping from 0-to-length to xdr objects is essential. It ensures
 596     * that block objects from the script->objects array will be written and
 597     * restored in the outer-to-inner order. block_xdrObject relies on this to
 598     * restore the parent chain.
 599     */
 600    for (i = 0; i != nobjects; ++i) {
 601        if (!js_XDRObject(xdr, &JS_SCRIPT_OBJECTS(script)->vector[i]))
 602            goto error;
 603    }
 604    for (i = 0; i != nupvars; ++i) {
 605        if (!JS_XDRUint32(xdr, &JS_SCRIPT_UPVARS(script)->vector[i]))
 606            goto error;
 607    }
 608    for (i = 0; i != nregexps; ++i) {
 609        if (!js_XDRObject(xdr, &JS_SCRIPT_REGEXPS(script)->vector[i]))
 610            goto error;
 611    }
 612
 613    if (ntrynotes != 0) {
 614        /*
 615         * We combine tn->kind and tn->stackDepth when serializing as XDR is not
 616         * efficient when serializing small integer types.
 617         */
 618        JSTryNote *tn, *tnfirst;
 619        uint32 kindAndDepth;
 620        JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8));
 621        JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16));
 622
 623        tnfirst = JS_SCRIPT_TRYNOTES(script)->vector;
 624        JS_ASSERT(JS_SCRIPT_TRYNOTES(script)->length == ntrynotes);
 625        tn = tnfirst + ntrynotes;
 626        do {
 627            --tn;
 628            if (xdr->mode == JSXDR_ENCODE) {
 629                kindAndDepth = ((uint32)tn->kind << 16)
 630                               | (uint32)tn->stackDepth;
 631            }
 632            if (!JS_XDRUint32(xdr, &kindAndDepth) ||
 633                !JS_XDRUint32(xdr, &tn->start) ||
 634                !JS_XDRUint32(xdr, &tn->length)) {
 635                goto error;
 636            }
 637            if (xdr->mode == JSXDR_DECODE) {
 638                tn->kind = (uint8)(kindAndDepth >> 16);
 639                tn->stackDepth = (uint16)kindAndDepth;
 640            }
 641        } while (tn != tnfirst);
 642    }
 643
 644    xdr->script = oldscript;
 645    if (xdr->mode == JSXDR_DECODE)
 646        JS_POP_TEMP_ROOT(cx, &tvr);
 647    return JS_TRUE;
 648
 649  error:
 650    if (xdr->mode == JSXDR_DECODE) {
 651        JS_POP_TEMP_ROOT(cx, &tvr);
 652        if (script->filename && !filenameWasSaved) {
 653            JS_free(cx, (void *) script->filename);
 654            script->filename = NULL;
 655        }
 656        js_DestroyScript(cx, script);
 657        *scriptp = NULL;
 658    }
 659    xdr->script = oldscript;
 660    return JS_FALSE;
 661}
 662
 663#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
 664/*
 665 * These cannot be exposed to web content, and chrome does not need them, so
 666 * we take them out of the Mozilla client altogether.  Fortunately, there is
 667 * no way to serialize a native function (see fun_xdrObject in jsfun.c).
 668 */
 669
 670static JSBool
 671script_freeze(JSContext *cx, uintN argc, jsval *vp)
 672{
 673    JSObject *obj;
 674    JSXDRState *xdr;
 675    JSScript *script;
 676    JSBool ok, hasMagic;
 677    uint32 len;
 678    void *buf;
 679    JSString *str;
 680
 681    obj = JS_THIS_OBJECT(cx, vp);
 682    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
 683        return JS_FALSE;
 684    script = (JSScript *) JS_GetPrivate(cx, obj);
 685    if (!script)
 686        return JS_TRUE;
 687
 688    /* create new XDR */
 689    xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
 690    if (!xdr)
 691        return JS_FALSE;
 692
 693    /* write  */
 694    ok = js_XDRScript(xdr, &script, &hasMagic);
 695    if (!ok)
 696        goto out;
 697    if (!hasMagic) {
 698        *vp = JSVAL_VOID;
 699        goto out;
 700    }
 701
 702    buf = JS_XDRMemGetData(xdr, &len);
 703    if (!buf) {
 704        ok = JS_FALSE;
 705        goto out;
 706    }
 707
 708    JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
 709    len /= sizeof(jschar);
 710#if IS_BIG_ENDIAN
 711  {
 712    jschar *chars;
 713    uint32 i;
 714
 715    /* Swap bytes in Unichars to keep frozen strings machine-independent. */
 716    chars = (jschar *)buf;
 717    for (i = 0; i < len; i++)
 718        chars[i] = JSXDR_SWAB16(chars[i]);
 719  }
 720#endif
 721    str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
 722    if (!str) {
 723        ok = JS_FALSE;
 724        goto out;
 725    }
 726
 727    *vp = STRING_TO_JSVAL(str);
 728
 729out:
 730    JS_XDRDestroy(xdr);
 731    return ok;
 732}
 733
 734static JSBool
 735script_thaw(JSContext *cx, uintN argc, jsval *vp)
 736{
 737    JSObject *obj;
 738    JSXDRState *xdr;
 739    JSString *str;
 740    void *buf;
 741    uint32 len;
 742    jsval v;
 743    JSScript *script, *oldscript;
 744    JSBool ok, hasMagic;
 745    jsint execDepth;
 746
 747    obj = JS_THIS_OBJECT(cx, vp);
 748    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
 749        return JS_FALSE;
 750
 751    if (argc == 0)
 752        return JS_TRUE;
 753    str = js_ValueToString(cx, vp[2]);
 754    if (!str)
 755        return JS_FALSE;
 756    vp[2] = STRING_TO_JSVAL(str);
 757
 758    /* create new XDR */
 759    xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
 760    if (!xdr)
 761        return JS_FALSE;
 762
 763    JSSTRING_CHARS_AND_LENGTH(str, buf, len);
 764#if IS_BIG_ENDIAN
 765  {
 766    jschar *from, *to;
 767    uint32 i;
 768
 769    /* Swap bytes in Unichars to keep frozen strings machine-independent. */
 770    from = (jschar *)buf;
 771    to = (jschar *) JS_malloc(cx, len * sizeof(jschar));
 772    if (!to) {
 773        JS_XDRDestroy(xdr);
 774        return JS_FALSE;
 775    }
 776    for (i = 0; i < len; i++)
 777        to[i] = JSXDR_SWAB16(from[i]);
 778    buf = (char *)to;
 779  }
 780#endif
 781    len *= sizeof(jschar);
 782    JS_XDRMemSetData(xdr, buf, len);
 783
 784    /* XXXbe should magic mismatch be error, or false return value? */
 785    ok = js_XDRScript(xdr, &script, &hasMagic);
 786    if (!ok)
 787        goto out;
 788    if (!hasMagic) {
 789        *vp = JSVAL_FALSE;
 790        goto out;
 791    }
 792
 793    JS_LOCK_OBJ(cx, obj);
 794    execDepth = GetScriptExecDepth(cx, obj);
 795
 796    /*
 797     * execDepth must be 0 to allow compilation here, otherwise the JSScript
 798     * struct can be released while running.
 799     */
 800    if (execDepth > 0) {
 801        JS_UNLOCK_OBJ(cx, obj);
 802        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 803                             JSMSG_COMPILE_EXECED_SCRIPT);
 804        goto out;
 805    }
 806
 807    /* Swap script for obj's old script, if any. */
 808    v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
 809    oldscript = !JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL;
 810    LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
 811    JS_UNLOCK_OBJ(cx, obj);
 812
 813    if (oldscript)
 814        js_DestroyScript(cx, oldscript);
 815
 816    script->u.object = obj;
 817    js_CallNewScriptHook(cx, script, NULL);
 818
 819out:
 820    /*
 821     * We reset the buffer to be NULL so that it doesn't free the chars
 822     * memory owned by str (vp[2]).
 823     */
 824    JS_XDRMemSetData(xdr, NULL, 0);
 825    JS_XDRDestroy(xdr);
 826#if IS_BIG_ENDIAN
 827    JS_free(cx, buf);
 828#endif
 829    *vp = JSVAL_TRUE;
 830    return ok;
 831}
 832
 833static const char js_thaw_str[] = "thaw";
 834
 835#endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */
 836#endif /* JS_HAS_XDR */
 837
 838#if JS_HAS_SCRIPT_OBJECT
 839
 840static JSFunctionSpec script_methods[] = {
 841#if JS_HAS_TOSOURCE
 842    JS_FN(js_toSource_str,   script_toSource,   0,0),
 843#endif
 844    JS_FN(js_toString_str,   script_toString,   0,0),
 845    JS_FN("compile",         script_compile,    2,0),
 846    JS_FN("exec",            script_exec,       1,0),
 847#if JS_HAS_XDR_FREEZE_THAW
 848    JS_FN("freeze",          script_freeze,     0,0),
 849    JS_FN(js_thaw_str,       script_thaw,       1,0),
 850#endif /* JS_HAS_XDR_FREEZE_THAW */
 851    JS_FS_END
 852};
 853
 854#endif /* JS_HAS_SCRIPT_OBJECT */
 855
 856static void
 857script_finalize(JSContext *cx, JSObject *obj)
 858{
 859    JSScript *script;
 860
 861    script = (JSScript *) JS_GetPrivate(cx, obj);
 862    if (script)
 863        js_DestroyScript(cx, script);
 864}
 865
 866static JSBool
 867script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 868{
 869#if JS_HAS_SCRIPT_OBJECT
 870    return script_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
 871#else
 872    return JS_FALSE;
 873#endif
 874}
 875
 876static void
 877script_trace(JSTracer *trc, JSObject *obj)
 878{
 879    JSScript *script;
 880
 881    script = (JSScript *) JS_GetPrivate(trc->context, obj);
 882    if (script)
 883        js_TraceScript(trc, script);
 884}
 885
 886#if !JS_HAS_SCRIPT_OBJECT
 887#define JSProto_Script  JSProto_Object
 888#endif
 889
 890JS_FRIEND_DATA(JSClass) js_ScriptClass = {
 891    js_Script_str,
 892    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
 893    JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script),
 894    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 895    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   script_finalize,
 896    NULL,             NULL,             script_call,      NULL,/*XXXbe xdr*/
 897    NULL,             NULL,             JS_CLASS_TRACE(script_trace), NULL
 898};
 899
 900#if JS_HAS_SCRIPT_OBJECT
 901
 902static JSBool
 903Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 904{
 905    /* If not constructing, replace obj with a new Script object. */
 906    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 907        obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL, 0);
 908        if (!obj)
 909            return JS_FALSE;
 910
 911        /*
 912         * script_compile_sub does not use rval to root its temporaries so we
 913         * can use it to root obj.
 914         */
 915        *rval = OBJECT_TO_JSVAL(obj);
 916    }
 917
 918    if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0)))
 919        return JS_FALSE;
 920
 921    return script_compile_sub(cx, obj, argc, argv, rval);
 922}
 923
 924#if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
 925
 926static JSBool
 927script_static_thaw(JSContext *cx, uintN argc, jsval *vp)
 928{
 929    JSObject *obj;
 930
 931    obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
 932    if (!obj)
 933        return JS_FALSE;
 934    vp[1] = OBJECT_TO_JSVAL(obj);
 935    if (!script_thaw(cx, argc, vp))
 936        return JS_FALSE;
 937    *vp = OBJECT_TO_JSVAL(obj);
 938    return JS_TRUE;
 939}
 940
 941static JSFunctionSpec script_static_methods[] = {
 942    JS_FN(js_thaw_str,       script_static_thaw,     1,0),
 943    JS_FS_END
 944};
 945
 946#else  /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
 947
 948#define script_static_methods   NULL
 949
 950#endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
 951
 952JSObject *
 953js_InitScriptClass(JSContext *cx, JSObject *obj)
 954{
 955    return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
 956                        NULL, script_methods, NULL, script_static_methods);
 957}
 958
 959#endif /* JS_HAS_SCRIPT_OBJECT */
 960
 961/*
 962 * Shared script filename management.
 963 */
 964static int
 965js_compare_strings(const void *k1, const void *k2)
 966{
 967    return strcmp((const char *) k1, (const char *) k2) == 0;
 968}
 969
 970/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
 971typedef struct ScriptFilenameEntry {
 972    JSHashEntry         *next;          /* hash chain linkage */
 973    JSHashNumber        keyHash;        /* key hash function result */
 974    const void          *key;           /* ptr to filename, below */
 975    uint32              flags;          /* user-defined filename prefix flags */
 976    JSPackedBool        mark;           /* GC mark flag */
 977    char                filename[3];    /* two or more bytes, NUL-terminated */
 978} ScriptFilenameEntry;
 979
 980static void *
 981js_alloc_table_space(void *priv, size_t size)
 982{
 983    return malloc(size);
 984}
 985
 986static void
 987js_free_table_space(void *priv, void *item)
 988{
 989    free(item);
 990}
 991
 992static JSHashEntry *
 993js_alloc_sftbl_entry(void *priv, const void *key)
 994{
 995    size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
 996                    strlen((const char *) key) + 1;
 997
 998    return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
 999}
1000
1001static void
1002js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
1003{
1004    if (flag != HT_FREE_ENTRY)
1005        return;
1006    free(he);
1007}
1008
1009static JSHashAllocOps sftbl_alloc_ops = {
1010    js_alloc_table_space,   js_free_table_space,
1011    js_alloc_sftbl_entry,   js_free_sftbl_entry
1012};
1013
1014JSBool
1015js_InitRuntimeScriptState(JSRuntime *rt)
1016{
1017#ifdef JS_THREADSAFE
1018    JS_ASSERT(!rt->scriptFilenameTableLock);
1019    rt->scriptFilenameTableLock = JS_NEW_LOCK();
1020    if (!rt->scriptFilenameTableLock)
1021        return JS_FALSE;
1022#endif
1023    JS_ASSERT(!rt->scriptFilenameTable);
1024    rt->scriptFilenameTable =
1025        JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
1026                        &sftbl_alloc_ops, NULL);
1027    if (!rt->scriptFilenameTable) {
1028        js_FinishRuntimeScriptState(rt);    /* free lock if threadsafe */
1029        return JS_FALSE;
1030    }
1031    JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
1032    return JS_TRUE;
1033}
1034
1035typedef struct ScriptFilenamePrefix {
1036    JSCList     links;      /* circular list linkage for easy deletion */
1037    const char  *name;      /* pointer to pinned ScriptFilenameEntry string */
1038    size_t      length;     /* prefix string length, precomputed */
1039    uint32      flags;      /* user-defined flags to inherit from this prefix */
1040} ScriptFilenamePrefix;
1041
1042void
1043js_FinishRuntimeScriptState(JSRuntime *rt)
1044{
1045    if (rt->scriptFilenameTable) {
1046        JS_HashTableDestroy(rt->scriptFilenameTable);
1047        rt->scriptFilenameTable = NULL;
1048    }
1049#ifdef JS_THREADSAFE
1050    if (rt->scriptFilenameTableLock) {
1051        JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
1052        rt->scriptFilenameTableLock = NULL;
1053    }
1054#endif
1055}
1056
1057void
1058js_FreeRuntimeScriptState(JSRuntime *rt)
1059{
1060    ScriptFilenamePrefix *sfp;
1061
1062    if (!rt->scriptFilenameTable)
1063        return;
1064
1065    while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
1066        sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next;
1067        JS_REMOVE_LINK(&sfp->links);
1068        free(sfp);
1069    }
1070    js_FinishRuntimeScriptState(rt);
1071}
1072
1073#ifdef DEBUG_brendan
1074#define DEBUG_SFTBL
1075#endif
1076#ifdef DEBUG_SFTBL
1077size_t sftbl_savings = 0;
1078#endif
1079
1080static ScriptFilenameEntry *
1081SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
1082{
1083    JSHashTable *table;
1084    JSHashNumber hash;
1085    JSHashEntry **hep;
1086    ScriptFilenameEntry *sfe;
1087    size_t length;
1088    JSCList *head, *link;
1089    ScriptFilenamePrefix *sfp;
1090
1091    table = rt->scriptFilenameTable;
1092    hash = JS_HashString(filename);
1093    hep = JS_HashTableRawLookup(table, hash, filename);
1094    sfe = (ScriptFilenameEntry *) *hep;
1095#ifdef DEBUG_SFTBL
1096    if (sfe)
1097        sftbl_savings += strlen(sfe->filename);
1098#endif
1099
1100    if (!sfe) {
1101        sfe = (ScriptFilenameEntry *)
1102              JS_HashTableRawAdd(table, hep, hash, filename, NULL);
1103        if (!sfe)
1104            return NULL;
1105        sfe->key = strcpy(sfe->filename, filename);
1106        sfe->flags = 0;
1107        sfe->mark = JS_FALSE;
1108    }
1109
1110    /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1111    if (flags != 0) {
1112        /* Search in case filename was saved already; we must be idempotent. */
1113        sfp = NULL;
1114        length = strlen(filename);
1115        for (head = link = &rt->scriptFilenamePrefixes;
1116             link->next != head;
1117             link = link->next) {
1118            /* Lag link behind sfp to insert in non-increasing length order. */
1119            sfp = (ScriptFilenamePrefix *) link->next;
1120            if (!strcmp(sfp->name, filename))
1121                break;
1122            if (sfp->length <= length) {
1123                sfp = NULL;
1124                break;
1125            }
1126            sfp = NULL;
1127        }
1128
1129        if (!sfp) {
1130            /* No such prefix: add one now. */
1131            sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix));
1132            if (!sfp)
1133                return NULL;
1134            JS_INSERT_AFTER(&sfp->links, link);
1135            sfp->name = sfe->filename;
1136            sfp->length = length;
1137            sfp->flags = 0;
1138        }
1139
1140        /*
1141         * Accumulate flags in both sfe and sfp: sfe for later access from the
1142         * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1143         * filename entries can inherit by prefix.
1144         */
1145        sfe->flags |= flags;
1146        sfp->flags |= flags;
1147    }
1148
1149    return sfe;
1150}
1151
1152const char *
1153js_SaveScriptFilename(JSContext *cx, const char *filename)
1154{
1155    JSRuntime *rt;
1156    ScriptFilenameEntry *sfe;
1157    JSCList *head, *link;
1158    ScriptFilenamePrefix *sfp;
1159
1160    rt = cx->runtime;
1161    JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1162    sfe = SaveScriptFilename(rt, filename, 0);
1163    if (!sfe) {
1164        JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1165        JS_ReportOutOfMemory(cx);
1166        return NULL;
1167    }
1168
1169    /*
1170     * Try to inherit flags by prefix.  We assume there won't be more than a
1171     * few (dozen! ;-) prefixes, so linear search is tolerable.
1172     * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1173     */
1174    for (head = &rt->scriptFilenamePrefixes, link = head->next;
1175         link != head;
1176         link = link->next) {
1177        sfp = (ScriptFilenamePrefix *) link;
1178        if (!strncmp(sfp->name, filename, sfp->length)) {
1179            sfe->flags |= sfp->flags;
1180            break;
1181        }
1182    }
1183    JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1184    return sfe->filename;
1185}
1186
1187const char *
1188js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
1189{
1190    ScriptFilenameEntry *sfe;
1191
1192    /* This may be called very early, via the jsdbgapi.h entry point. */
1193    if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
1194        return NULL;
1195
1196    JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1197    sfe = SaveScriptFilename(rt, filename, flags);
1198    JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1199    if (!sfe)
1200        return NULL;
1201
1202    return sfe->filename;
1203}
1204
1205/*
1206 * Back up from a saved filename by its offset within its hash table entry.
1207 */
1208#define FILENAME_TO_SFE(fn) \
1209    ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1210
1211/*
1212 * The sfe->key member, redundant given sfe->filename but required by the old
1213 * jshash.c code, here gives us a useful sanity check.  This assertion will
1214 * very likely botch if someone tries to mark a string that wasn't allocated
1215 * as an sfe->filename.
1216 */
1217#define ASSERT_VALID_SFE(sfe)   JS_ASSERT((sfe)->key == (sfe)->filename)
1218
1219uint32
1220js_GetScriptFilenameFlags(const char *filename)
1221{
1222    ScriptFilenameEntry *sfe;
1223
1224    sfe = FILENAME_TO_SFE(filename);
1225    ASSERT_VALID_SFE(sfe);
1226    return sfe->flags;
1227}
1228
1229void
1230js_MarkScriptFilename(const char *filename)
1231{
1232    ScriptFilenameEntry *sfe;
1233
1234    sfe = FILENAME_TO_SFE(filename);
1235    ASSERT_VALID_SFE(sfe);
1236    sfe->mark = JS_TRUE;
1237}
1238
1239static intN
1240js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
1241{
1242    ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1243
1244    sfe->mark = JS_TRUE;
1245    return HT_ENUMERATE_NEXT;
1246}
1247
1248void
1249js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
1250{
1251    JSCList *head, *link;
1252    ScriptFilenamePrefix *sfp;
1253
1254    if (!rt->scriptFilenameTable)
1255        return;
1256
1257    if (keepAtoms) {
1258        JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1259                                     js_script_filename_marker,
1260                                     rt);
1261    }
1262    for (head = &rt->scriptFilenamePrefixes, link = head->next;
1263         link != head;
1264         link = link->next) {
1265        sfp = (ScriptFilenamePrefix *) link;
1266        js_MarkScriptFilename(sfp->name);
1267    }
1268}
1269
1270static intN
1271js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
1272{
1273    ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1274
1275    if (!sfe->mark)
1276        return HT_ENUMERATE_REMOVE;
1277    sfe->mark = JS_FALSE;
1278    return HT_ENUMERATE_NEXT;
1279}
1280
1281void
1282js_SweepScriptFilenames(JSRuntime *rt)
1283{
1284    if (!rt->scriptFilenameTable)
1285        return;
1286
1287    JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1288                                 js_script_filename_sweeper,
1289                                 rt);
1290#ifdef DEBUG_notme
1291#ifdef DEBUG_SFTBL
1292    printf("script filename table savings so far: %u\n", sftbl_savings);
1293#endif
1294#endif
1295}
1296
1297/*
1298 * JSScript data structures memory alignment:
1299 *
1300 * JSScript
1301 * JSObjectArray    script objects' descriptor if JSScript.objectsOffset != 0,
1302 *                    use JS_SCRIPT_OBJECTS(script) macro to access it.
1303 * JSObjectArray    script regexps' descriptor if JSScript.regexpsOffset != 0,
1304 *                    use JS_SCRIPT_REGEXPS(script) macro to access it.
1305 * JSTryNoteArray   script try notes' descriptor if JSScript.tryNotesOffset
1306 *                    != 0, use JS_SCRIPT_TRYNOTES(script) macro to access it.
1307 * JSAtom *a[]      array of JSScript.atomMap.length atoms pointed by
1308 *                    JSScript.atomMap.vector if any.
1309 * JSObject *o[]    array of JS_SCRIPT_OBJECTS(script)->length objects if any
1310 *                    pointed by JS_SCRIPT_OBJECTS(script)->vector.
1311 * JSObject *r[]    array of JS_SCRIPT_REGEXPS(script)->length regexps if any
1312 *                    pointed by JS_SCRIPT_REGEXPS(script)->vector.
1313 * JSTryNote t[]    array of JS_SCRIPT_TRYNOTES(script)->length try notes if any
1314 *                    pointed by JS_SCRIPT_TRYNOTES(script)->vector.
1315 * jsbytecode b[]   script bytecode pointed by JSScript.code.
1316 * jssrcnote  s[]   script source notes, use SCRIPT_NOTES(script) to access it
1317 *
1318 * The alignment avoids gaps between entries as alignment requirement for each
1319 * subsequent structure or array is the same or divides the alignment
1320 * requirement for the previous one.
1321 *
1322 * The followings asserts checks that assuming that the alignment requirement
1323 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
1324 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
1325 */
1326JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0);
1327JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0);
1328JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray));
1329JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *));
1330JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0);
1331JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32));
1332JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0);
1333JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
1334
1335/*
1336 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
1337 * sufficient.
1338 */
1339JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
1340                 sizeof(JSUpvarArray) < JS_BIT(8));
1341
1342JSScript *
1343js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
1344             uint32 nobjects, uint32 nupvars, uint32 nregexps,
1345             uint32 ntrynotes)
1346{
1347    size_t size, vectorSize;
1348    JSScript *script;
1349    uint8 *cursor;
1350
1351    size = sizeof(JSScript) +
1352           sizeof(JSAtom *) * natoms +
1353           length * sizeof(jsbytecode) +
1354           nsrcnotes * sizeof(jssrcnote);
1355    if (nobjects != 0)
1356        size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
1357    if (nupvars != 0)
1358        size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
1359    if (nregexps != 0)
1360        size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
1361    if (ntrynotes != 0)
1362        size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
1363
1364    script = (JSScript *) JS_malloc(cx, size);
1365    if (!script)
1366        return NULL;
1367    memset(script, 0, sizeof(JSScript));
1368    script->length = length;
1369    script->version = cx->version;
1370
1371    cursor = (uint8 *)script + sizeof(JSScript);
1372    if (nobjects != 0) {
1373        script->objectsOffset = (uint8)(cursor - (uint8 *)script);
1374        cursor += sizeof(JSObjectArray);
1375    }
1376    if (nupvars != 0) {
1377        script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
1378        cursor += sizeof(JSUpvarArray);
1379    }
1380    if (nregexps != 0) {
1381        script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
1382        cursor += sizeof(JSObjectArray);
1383    }
1384    if (ntrynotes != 0) {
1385        script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
1386        cursor += sizeof(JSTryNoteArray);
1387    }
1388
1389    if (natoms != 0) {
1390        script->atomMap.length = natoms;
1391        script->atomMap.vector = (JSAtom **)cursor;
1392        vectorSize = natoms * sizeof(script->atomMap.vector[0]);
1393
1394        /*
1395         * Clear object map's vector so the GC tracing can run when not yet
1396         * all atoms are copied to the array.
1397         */
1398        memset(cursor, 0, vectorSize);
1399        cursor += vectorSize;
1400    }
1401
1402    if (nobjects != 0) {
1403        JS_SCRIPT_OBJECTS(script)->length = nobjects;
1404        JS_SCRIPT_OBJECTS(script)->vector = (JSObject **)cursor;
1405        vectorSize = nobjects * sizeof(JS_SCRIPT_OBJECTS(script)->vector[0]);
1406        memset(cursor, 0, vectorSize);
1407        cursor += vectorSize;
1408    }
1409
1410    if (nupvars != 0) {
1411        JS_SCRIPT_UPVARS(script)->length = nupvars;
1412        JS_SCRIPT_UPVARS(script)->vector = (uint32 *)cursor;
1413        vectorSize = nupvars * sizeof(JS_SCRIPT_UPVARS(script)->vector[0]);
1414        memset(cursor, 0, vectorSize);
1415        cursor += vectorSize;
1416    }
1417
1418    if (nregexps != 0) {
1419        JS_SCRIPT_REGEXPS(script)->length = nregexps;
1420        JS_SCRIPT_REGEXPS(script)->vector = (JSObject **)cursor;
1421        vectorSize = nregexps * sizeof(JS_SCRIPT_REGEXPS(script)->vector[0]);
1422        memset(cursor, 0, vectorSize);
1423        cursor += vectorSize;
1424    }
1425
1426    if (ntrynotes != 0) {
1427        JS_SCRIPT_TRYNOTES(script)->length = ntrynotes;
1428        JS_SCRIPT_TRYNOTES(script)->vector = (JSTryNote *)cursor;
1429        vectorSize = ntrynotes * sizeof(JS_SCRIPT_TRYNOTES(script)->vector[0]);
1430#ifdef DEBUG
1431        memset(cursor, 0, vectorSize);
1432#endif
1433        cursor += vectorSize;
1434    }
1435
1436    script->code = script->main = (jsbytecode *)cursor;
1437    JS_ASSERT(cursor +
1438              length * sizeof(jsbytecode) +
1439              nsrcnotes * sizeof(jssrcnote) ==
1440              (uint8 *)script + size);
1441
1442#ifdef CHECK_SCRIPT_OWNER
1443    script->owner = cx->thread;
1444#endif
1445    return script;
1446}
1447
1448JSScript *
1449js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
1450{
1451    uint32 mainLength, prologLength, nsrcnotes, nfixed;
1452    JSScript *script;
1453    const char *filename;
1454    JSFunction *fun;
1455
1456    /* The counts of indexed things must be checked during code generation. */
1457    JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
1458    JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
1459    JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
1460
1461    mainLength = CG_OFFSET(cg);
1462    prologLength = CG_PROLOG_OFFSET(cg);
1463    CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1464    script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
1465                          cg->atomList.count, cg->objectList.length,
1466                          cg->upvarList.count, cg->regexpList.length,
1467                          cg->ntrynotes);
1468    if (!script)
1469        return NULL;
1470
1471    /* Now that we have script, error control flow must go to label bad. */
1472    script->main += prologLength;
1473    memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1474    memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1475    nfixed = (cg->treeContext.flags & TCF_IN_FUNCTION)
1476             ? cg->treeContext.u.fun->u.i.nvars
1477             : cg->treeContext.ngvars + cg->regexpList.length;
1478    JS_ASSERT(nfixed < SLOTNO_LIMIT);
1479    script->nfixed = (uint16) nfixed;
1480    js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
1481
1482    filename = cg->treeContext.parseContext->tokenStream.filename;
1483    if (filename) {
1484        script->filename = js_SaveScriptFilename(cx, filename);
1485        if (!script->filename)
1486            goto bad;
1487    }
1488    script->lineno = cg->firstLine;
1489    if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
1490        js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR,
1491                                    JSMSG_NEED_DIET, "script");
1492        goto bad;
1493    }
1494    script->nslots = script->nfixed + cg->maxStackDepth;
1495    script->staticDepth = cg->staticDepth;
1496    script->principals = cg->treeContext.parseContext->principals;
1497    if (script->principals)
1498        JSPRINCIPALS_HOLD(cx, script->principals);
1499
1500    if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script)))
1501        goto bad;
1502    if (cg->ntrynotes != 0)
1503        js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script));
1504    if (cg->objectList.length != 0)
1505        FinishParsedObjects(&cg->objectList, JS_SCRIPT_OBJECTS(script));
1506    if (cg->regexpList.length != 0)
1507        FinishParsedObjects(&cg->regexpList, JS_SCRIPT_REGEXPS(script));
1508    if (cg->treeContext.flags & TCF_NO_SCRIPT_RVAL)
1509        script->flags |= JSSF_NO_SCRIPT_RVAL;
1510
1511    if (cg->upvarList.count != 0) {
1512        JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
1513        memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector,
1514               cg->upvarList.count * sizeof(uint32));
1515        ATOM_LIST_INIT(&cg->upvarList);
1516        JS_free(cx, cg->upvarMap.vector);
1517        cg->upvarMap.vector = NULL;
1518    }
1519
1520    /*
1521     * We initialize fun->u.script to be the script constructed above
1522     * so that the debugger has a valid FUN_SCRIPT(fun).
1523     */
1524    fun = NULL;
1525    if (cg->treeContext.flags & TCF_IN_FUNCTION) {
1526        fun = cg->treeContext.u.fun;
1527        JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
1528        JS_ASSERT_IF(script->upvarsOffset != 0,
1529                     JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars);
1530
1531        js_FreezeLocalNames(cx, fun);
1532        fun->u.i.script = script;
1533#ifdef CHECK_SCRIPT_OWNER
1534        script->owner = NULL;
1535#endif
1536        if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
1537            fun->flags |= JSFUN_HEAVYWEIGHT;
1538    }
1539
1540    /* Tell the debugger about this compiled script. */
1541    js_CallNewScriptHook(cx, script, fun);
1542    return script;
1543
1544bad:
1545    js_DestroyScript(cx, script);
1546    return NULL;
1547}
1548
1549JS_FRIEND_API(void)
1550js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1551{
1552    JSNewScriptHook hook;
1553
1554    hook = cx->debugHooks->newScriptHook;
1555    if (hook) {
1556        JS_KEEP_ATOMS(cx->runtime);
1557        hook(cx, script->filename, script->lineno, script, fun,
1558             cx->debugHooks->newScriptHookData);
1559        JS_UNKEEP_ATOMS(cx->runtime);
1560    }
1561}
1562
1563JS_FRIEND_API(void)
1564js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1565{
1566    JSDestroyScriptHook hook;
1567
1568    hook = cx->debugHooks->destroyScriptHook;
1569    if (hook)
1570        hook(cx, script, cx->debugHooks->destroyScriptHookData);
1571}
1572
1573void
1574js_DestroyScript(JSContext *cx, JSScript *script)
1575{
1576    js_CallDestroyScriptHook(cx, script);
1577    JS_ClearScriptTraps(cx, script);
1578
1579    if (script->principals)
1580        JSPRINCIPALS_DROP(cx, script->principals);
1581
1582    if (JS_GSN_CACHE(cx).code == script->code)
1583        JS_CLEAR_GSN_CACHE(cx);
1584
1585    /*
1586     * The GC flushes all property caches, so no need to purge just the
1587     * entries for this script.
1588     *
1589     * JS_THREADSAFE note: js_FlushPropertyCacheForScript flushes only the
1590     * current thread's property cache, so a script not owned by a function
1591     * or object, which hands off lifetime management for that script to the
1592     * GC, must be used by only one thread over its lifetime.
1593     *
1594     * This should be an API-compatible change, since a script is never safe
1595     * against premature GC if shared among threads without a rooted object
1596     * wrapping it to protect the script's mapped atoms against GC. We use
1597     * script->owner to enforce this requirement via assertions.
1598     */
1599#ifdef CHECK_SCRIPT_OWNER
1600    JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
1601#endif
1602
1603    if (!cx->runtime->gcRunning) {
1604        if (!(cx->fp && (cx->fp->flags & JSFRAME_EVAL))) {
1605#ifdef CHECK_SCRIPT_OWNER
1606            JS_ASSERT(script->owner == cx->thread);
1607#endif
1608            js_FlushPropertyCacheForScript(cx, script);
1609        }
1610    }
1611
1612    JS_free(cx, script);
1613}
1614
1615void
1616js_TraceScript(JSTracer *trc, JSScript *script)
1617{
1618    JSAtomMap *map;
1619    uintN i, length;
1620    JSAtom **vector;
1621    jsval v;
1622    JSObjectArray *objarray;
1623
1624    map = &script->atomMap;
1625    length = map->length;
1626    vector = map->vector;
1627    for (i = 0; i < length; i++) {
1628        v = ATOM_KEY(vector[i]);
1629        if (JSVAL_IS_TRACEABLE(v)) {
1630            JS_SET_TRACING_INDEX(trc, "atomMap", i);
1631            JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1632        }
1633    }
1634
1635    if (script->objectsOffset != 0) {
1636        objarray = JS_SCRIPT_OBJECTS(script);
1637        i = objarray->length;
1638        do {
1639            --i;
1640            if (objarray->vector[i]) {
1641                JS_SET_TRACING_INDEX(trc, "objects", i);
1642                JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1643            }
1644        } while (i != 0);
1645    }
1646
1647    if (script->regexpsOffset != 0) {
1648        objarray = JS_SCRIPT_REGEXPS(script);
1649        i = objarray->length;
1650        do {
1651            --i;
1652            if (objarray->vector[i]) {
1653                JS_SET_TRACING_INDEX(trc, "regexps", i);
1654                JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1655            }
1656        } while (i != 0);
1657    }
1658
1659    if (script->u.object) {
1660        JS_SET_TRACING_NAME(trc, "object");
1661        JS_CallTracer(trc, script->u.object, JSTRACE_OBJECT);
1662    }
1663
1664    if (IS_GC_MARKING_TRACER(trc) && script->filename)
1665        js_MarkScriptFilename(script->filename);
1666}
1667
1668typedef struct GSNCacheEntry {
1669    JSDHashEntryHdr     hdr;
1670    jsbytecode          *pc;
1671    jssrcnote           *sn;
1672} GSNCacheEntry;
1673
1674#define GSN_CACHE_THRESHOLD     100
1675
1676jssrcnote *
1677js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1678{
1679    ptrdiff_t target, offset;
1680    GSNCacheEntry *entry;
1681    jssrcnote *sn, *result;
1682    uintN nsrcnotes;
1683
1684
1685    target = PTRDIFF(pc, script->code, jsbytecode);
1686    if ((uint32)target >= script->length)
1687        return NULL;
1688
1689    if (JS_GSN_CACHE(cx).code == script->code) {
1690        JS_METER_GSN_CACHE(cx, hits);
1691        entry = (GSNCacheEntry *)
1692                JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1693                                     JS_DHASH_LOOKUP);
1694        return entry->sn;
1695    }
1696
1697    JS_METER_GSN_CACHE(cx, misses);
1698    offset = 0;
1699    for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) {
1700        if (SN_IS_TERMINATOR(sn)) {
1701            result = NULL;
1702            break;
1703        }
1704        offset += SN_DELTA(sn);
1705        if (offset == target && SN_IS_GETTABLE(sn)) {
1706            result = sn;
1707            break;
1708        }
1709    }
1710
1711    if (JS_GSN_CACHE(cx).code != script->code &&
1712        script->length >= GSN_CACHE_THRESHOLD) {
1713        JS_CLEAR_GSN_CACHE(cx);
1714        nsrcnotes = 0;
1715        for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1716             sn = SN_NEXT(sn)) {
1717            if (SN_IS_GETTABLE(sn))
1718                ++nsrcnotes;
1719        }
1720        if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
1721                               NULL, sizeof(GSNCacheEntry),
1722                               JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) {
1723            JS_GSN_CACHE(cx).table.ops = NULL;
1724        } else {
1725            pc = script->code;
1726            for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1727                 sn = SN_NEXT(sn)) {
1728                pc += SN_DELTA(sn);
1729                if (SN_IS_GETTABLE(sn)) {
1730                    entry = (GSNCacheEntry *)
1731                            JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1732                                                 JS_DHASH_ADD);
1733                    entry->pc = pc;
1734                    entry->sn = sn;
1735                }
1736            }
1737            JS_GSN_CACHE(cx).code = script->code;
1738            JS_METER_GSN_CACHE(cx, fills);
1739        }
1740    }
1741
1742    return result;
1743}
1744
1745uintN
1746js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
1747{
1748    return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc);
1749}
1750
1751uintN
1752js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1753{
1754    JSFunction *fun;
1755    uintN lineno;
1756    ptrdiff_t offset, target;
1757    jssrcnote *sn;
1758    JSSrcNoteType type;
1759
1760    /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1761    if (!pc)
1762        return 0;
1763
1764    /*
1765     * Special case: function definition needs no line number note because
1766     * the function's script contains its starting line number.
1767     */
1768    if (js_CodeSpec[*pc].format & JOF_INDEXBASE)
1769        pc += js_CodeSpec[*pc].length;
1770    if (*pc == JSOP_DEFFUN) {
1771        GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun);
1772        return fun->u.i.script->lineno;
1773    }
1774
1775    /*
1776     * General case: walk through source notes accumulating their deltas,
1777     * keeping track of line-number notes, until we pass the note for pc's
1778     * offset within script->code.
1779     */
1780    lineno = script->lineno;
1781    offset = 0;
1782    target = PTRDIFF(pc, script->code, jsbytecode);
1783    for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1784        offset += SN_DELTA(sn);
1785        type = (JSSrcNoteType) SN_TYPE(sn);
1786        if (type == SRC_SETLINE) {
1787            if (offset <= target)
1788                lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1789        } else if (type == SRC_NEWLINE) {
1790            if (offset <= target)
1791                lineno++;
1792        }
1793        if (offset > target)
1794            break;
1795    }
1796    return lineno;
1797}
1798
1799/* The line number limit is the same as the jssrcnote offset limit. */
1800#define SN_LINE_LIMIT   (SN_3BYTE_OFFSET_FLAG << 16)
1801
1802jsbytecode *
1803js_LineNumberToPC(JSScript *script, uintN target)
1804{
1805    ptrdiff_t offset, best;
1806    uintN lineno, bestdiff, diff;
1807    jssrcnote *sn;
1808    JSSrcNoteType type;
1809
1810    offset = 0;
1811    best = -1;
1812    lineno = script->lineno;
1813    bestdiff = SN_LINE_LIMIT;
1814    for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1815        /*
1816         * Exact-match only if offset is not in the prolog; otherwise use
1817         * nearest greater-or-equal line number match.
1818         */
1819        if (lineno == target && script->code + offset >= script->main)
1820            goto out;
1821        if (lineno >= target) {
1822            diff = lineno - target;
1823            if (diff < bestdiff) {
1824                bestdiff = diff;
1825                best = offset;
1826            }
1827        }
1828        offset += SN_DELTA(sn);
1829        type = (JSSrcNoteType) SN_TYPE(sn);
1830        if (type == SRC_SETLINE) {
1831            lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1832        } else if (type == SRC_NEWLINE) {
1833            lineno++;
1834        }
1835    }
1836    if (best >= 0)
1837        offset = best;
1838out:
1839    return script->code + offset;
1840}
1841
1842JS_FRIEND_API(uintN)
1843js_GetScriptLineExtent(JSScript *script)
1844{
1845    uintN lineno;
1846    jssrcnote *sn;
1847    JSSrcNoteType type;
1848
1849    lineno = script->lineno;
1850    for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1851        type = (JSSrcNoteType) SN_TYPE(sn);
1852        if (type == SRC_SETLINE) {
1853            lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1854        } else if (type == SRC_NEWLINE) {
1855            lineno++;
1856        }
1857    }
1858    return 1 + lineno - script->lineno;
1859}
1860
1861#if JS_HAS_GENERATORS
1862
1863JSBool
1864js_IsInsideTryWithFinally(JSScript *script, jsbytecode *pc)
1865{
1866    JSTryNoteArray *tarray;
1867    JSTryNote *tn, *tnlimit;
1868    uint32 off;
1869
1870    JS_ASSERT(script->code <= pc);
1871    JS_ASSERT(pc < script->code + script->length);
1872
1873    if (!script->trynotesOffset != 0)
1874        return JS_FALSE;
1875    tarray = JS_SCRIPT_TRYNOTES(script);
1876    JS_ASSERT(tarray->length != 0);
1877
1878    tn = tarray->vector;
1879    tnlimit = tn + tarray->length;
1880    off = (uint32)(pc - script->main);
1881    do {
1882        if (off - tn->start < tn->length) {
1883            if (tn->kind == JSTRY_FINALLY)
1884                return JS_TRUE;
1885            JS_ASSERT(tn->kind == JSTRY_CATCH);
1886        }
1887    } while (++tn != tnlimit);
1888    return JS_FALSE;
1889}
1890
1891#endif