PageRenderTime 146ms CodeModel.GetById 16ms app.highlight 108ms RepoModel.GetById 9ms app.codeStats 1ms

/js/src/jsscript.cpp

http://github.com/zpao/v8monkey
C++ | 1908 lines | 1458 code | 254 blank | 196 comment | 463 complexity | 1fc336b206cecbd3c5ca178c1c20d694 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 *   Nick Fitzgerald <nfitzgerald@mozilla.com>
  27 *
  28 * Alternatively, the contents of this file may be used under the terms of
  29 * either of the GNU General Public License Version 2 or later (the "GPL"),
  30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31 * in which case the provisions of the GPL or the LGPL are applicable instead
  32 * of those above. If you wish to allow use of your version of this file only
  33 * under the terms of either the GPL or the LGPL, and not to allow others to
  34 * use your version of this file under the terms of the MPL, indicate your
  35 * decision by deleting the provisions above and replace them with the notice
  36 * and other provisions required by the GPL or the LGPL. If you do not delete
  37 * the provisions above, a recipient may use your version of this file under
  38 * the terms of any one of the MPL, the GPL or the LGPL.
  39 *
  40 * ***** END LICENSE BLOCK ***** */
  41
  42/*
  43 * JS script operations.
  44 */
  45
  46#include <string.h>
  47#include "jstypes.h"
  48#include "jsutil.h"
  49#include "jscrashreport.h"
  50#include "jsprf.h"
  51#include "jsapi.h"
  52#include "jsatom.h"
  53#include "jscntxt.h"
  54#include "jsversion.h"
  55#include "jsdbgapi.h"
  56#include "jsfun.h"
  57#include "jsgc.h"
  58#include "jsgcmark.h"
  59#include "jsinterp.h"
  60#include "jslock.h"
  61#include "jsnum.h"
  62#include "jsopcode.h"
  63#include "jsscope.h"
  64#include "jsscript.h"
  65#if JS_HAS_XDR
  66#include "jsxdrapi.h"
  67#endif
  68
  69#include "frontend/BytecodeEmitter.h"
  70#include "frontend/Parser.h"
  71#include "js/MemoryMetrics.h"
  72#include "methodjit/MethodJIT.h"
  73#include "methodjit/Retcon.h"
  74#include "vm/Debugger.h"
  75
  76#include "jsinferinlines.h"
  77#include "jsinterpinlines.h"
  78#include "jsobjinlines.h"
  79#include "jsscriptinlines.h"
  80
  81using namespace js;
  82using namespace js::gc;
  83using namespace js::frontend;
  84
  85namespace js {
  86
  87BindingKind
  88Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
  89{
  90    if (!lastBinding)
  91        return NONE;
  92
  93    Shape **spp;
  94    Shape *shape = Shape::search(cx, lastBinding, ATOM_TO_JSID(name), &spp);
  95    if (!shape)
  96        return NONE;
  97
  98    if (indexp)
  99        *indexp = shape->shortid();
 100
 101    if (shape->getter() == GetCallArg)
 102        return ARGUMENT;
 103    if (shape->getter() == GetCallUpvar)
 104        return UPVAR;
 105
 106    return shape->writable() ? VARIABLE : CONSTANT;
 107}
 108
 109bool
 110Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
 111{
 112    if (!ensureShape(cx))
 113        return false;
 114
 115    /*
 116     * We still follow 10.2.3 of ES3 and make argument and variable properties
 117     * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
 118     * avoid objects as activations, something we should do too.
 119     */
 120    uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
 121
 122    uint16_t *indexp;
 123    PropertyOp getter;
 124    StrictPropertyOp setter;
 125    uint32_t slot = CallObject::RESERVED_SLOTS;
 126
 127    if (kind == ARGUMENT) {
 128        JS_ASSERT(nvars == 0);
 129        JS_ASSERT(nupvars == 0);
 130        indexp = &nargs;
 131        getter = GetCallArg;
 132        setter = SetCallArg;
 133        slot += nargs;
 134    } else if (kind == UPVAR) {
 135        indexp = &nupvars;
 136        getter = GetCallUpvar;
 137        setter = SetCallUpvar;
 138        slot = lastBinding->maybeSlot();
 139        attrs |= JSPROP_SHARED;
 140    } else {
 141        JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
 142        JS_ASSERT(nupvars == 0);
 143
 144        indexp = &nvars;
 145        getter = GetCallVar;
 146        setter = SetCallVar;
 147        if (kind == CONSTANT)
 148            attrs |= JSPROP_READONLY;
 149        slot += nargs + nvars;
 150    }
 151
 152    if (*indexp == BINDING_COUNT_LIMIT) {
 153        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 154                             (kind == ARGUMENT)
 155                             ? JSMSG_TOO_MANY_FUN_ARGS
 156                             : JSMSG_TOO_MANY_LOCALS);
 157        return false;
 158    }
 159
 160    jsid id;
 161    if (!name) {
 162        JS_ASSERT(kind == ARGUMENT); /* destructuring */
 163        id = INT_TO_JSID(nargs);
 164    } else {
 165        id = ATOM_TO_JSID(name);
 166    }
 167
 168    StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
 169    base.updateGetterSetter(attrs, getter, setter);
 170
 171    UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
 172    if (!nbase)
 173        return NULL;
 174
 175    StackShape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
 176
 177    /* Shapes in bindings cannot be dictionaries. */
 178    Shape *shape = lastBinding->getChildBinding(cx, child);
 179    if (!shape)
 180        return false;
 181
 182    lastBinding = shape;
 183    ++*indexp;
 184    return true;
 185}
 186
 187Shape *
 188Bindings::callObjectShape(JSContext *cx) const
 189{
 190    if (!hasDup())
 191        return lastShape();
 192
 193    /*
 194     * Build a vector of non-duplicate properties in order from last added
 195     * to first (i.e., the order we normally have iterate over Shapes). Choose
 196     * the last added property in each set of dups.
 197     */
 198    Vector<const Shape *> shapes(cx);
 199    HashSet<jsid> seen(cx);
 200    if (!seen.init())
 201        return NULL;
 202
 203    for (Shape::Range r = lastShape()->all(); !r.empty(); r.popFront()) {
 204        const Shape &s = r.front();
 205        HashSet<jsid>::AddPtr p = seen.lookupForAdd(s.propid());
 206        if (!p) {
 207            if (!seen.add(p, s.propid()))
 208                return NULL;
 209            if (!shapes.append(&s))
 210                return NULL;
 211        }
 212    }
 213
 214    /*
 215     * Now build the Shape without duplicate properties.
 216     */
 217    RootedVarShape shape(cx);
 218    shape = initialShape(cx);
 219    for (int i = shapes.length() - 1; i >= 0; --i) {
 220        shape = shape->getChildBinding(cx, shapes[i]);
 221        if (!shape)
 222            return NULL;
 223    }
 224
 225    return shape;
 226}
 227
 228bool
 229Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
 230{
 231    JS_ASSERT(lastBinding);
 232    JS_ASSERT(hasLocalNames());
 233
 234    Vector<JSAtom *> &names = *namesp;
 235    JS_ASSERT(names.empty());
 236
 237    uintN n = countLocalNames();
 238    if (!names.growByUninitialized(n))
 239        return false;
 240
 241#ifdef DEBUG
 242    JSAtom * const POISON = reinterpret_cast<JSAtom *>(0xdeadbeef);
 243    for (uintN i = 0; i < n; i++)
 244        names[i] = POISON;
 245#endif
 246
 247    for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) {
 248        const Shape &shape = r.front();
 249        uintN index = uint16_t(shape.shortid());
 250
 251        if (shape.getter() == GetCallArg) {
 252            JS_ASSERT(index < nargs);
 253        } else if (shape.getter() == GetCallUpvar) {
 254            JS_ASSERT(index < nupvars);
 255            index += nargs + nvars;
 256        } else {
 257            JS_ASSERT(index < nvars);
 258            index += nargs;
 259        }
 260
 261        if (JSID_IS_ATOM(shape.propid())) {
 262            names[index] = JSID_TO_ATOM(shape.propid());
 263        } else {
 264            JS_ASSERT(JSID_IS_INT(shape.propid()));
 265            JS_ASSERT(shape.getter() == GetCallArg);
 266            names[index] = NULL;
 267        }
 268    }
 269
 270#ifdef DEBUG
 271    for (uintN i = 0; i < n; i++)
 272        JS_ASSERT(names[i] != POISON);
 273#endif
 274
 275    return true;
 276}
 277
 278const Shape *
 279Bindings::lastArgument() const
 280{
 281    JS_ASSERT(lastBinding);
 282
 283    const js::Shape *shape = lastVariable();
 284    if (nvars > 0) {
 285        while (shape->previous() && shape->getter() != GetCallArg)
 286            shape = shape->previous();
 287    }
 288    return shape;
 289}
 290
 291const Shape *
 292Bindings::lastVariable() const
 293{
 294    JS_ASSERT(lastBinding);
 295
 296    const js::Shape *shape = lastUpvar();
 297    if (nupvars > 0) {
 298        while (shape->getter() == GetCallUpvar)
 299            shape = shape->previous();
 300    }
 301    return shape;
 302}
 303
 304const Shape *
 305Bindings::lastUpvar() const
 306{
 307    JS_ASSERT(lastBinding);
 308    return lastBinding;
 309}
 310
 311void
 312Bindings::makeImmutable()
 313{
 314    JS_ASSERT(lastBinding);
 315    JS_ASSERT(!lastBinding->inDictionary());
 316}
 317
 318void
 319Bindings::trace(JSTracer *trc)
 320{
 321    if (lastBinding)
 322        MarkShape(trc, lastBinding, "shape");
 323}
 324
 325#ifdef JS_CRASH_DIAGNOSTICS
 326
 327void
 328CheckScript(JSScript *script, JSScript *prev)
 329{
 330    if (script->cookie1[0] != JS_SCRIPT_COOKIE || script->cookie2[0] != JS_SCRIPT_COOKIE) {
 331        crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
 332        crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
 333        JS_OPT_ASSERT(false);
 334    }
 335}
 336
 337#endif /* JS_CRASH_DIAGNOSTICS */
 338
 339} /* namespace js */
 340
 341#if JS_HAS_XDR
 342
 343enum ScriptBits {
 344    NoScriptRval,
 345    SavedCallerFun,
 346    StrictModeCode,
 347    UsesEval,
 348    UsesArguments
 349};
 350
 351static const char *
 352SaveScriptFilename(JSContext *cx, const char *filename);
 353
 354JSBool
 355js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
 356{
 357    JSScript *oldscript;
 358    JSBool ok;
 359    uint32_t length, lineno, nslots;
 360    uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
 361    uint32_t prologLength, version, encodedClosedCount;
 362    uint16_t nClosedArgs = 0, nClosedVars = 0;
 363    uint32_t nTypeSets = 0;
 364    uint32_t encodeable, sameOriginPrincipals;
 365    JSSecurityCallbacks *callbacks;
 366    uint32_t scriptBits = 0;
 367
 368    JSContext *cx = xdr->cx;
 369    JSScript *script = *scriptp;
 370    nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
 371    jssrcnote *notes = NULL;
 372    XDRScriptState *state = xdr->state;
 373
 374    JS_ASSERT(state);
 375
 376    /* Should not XDR scripts optimized for a single global object. */
 377    JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset));
 378
 379    /* XDR arguments, local vars, and upvars. */
 380    uint16_t nargs, nvars, nupvars;
 381#if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
 382    nargs = nvars = nupvars = Bindings::BINDING_COUNT_LIMIT;
 383#endif
 384    uint32_t argsVars, paddingUpvars;
 385    if (xdr->mode == JSXDR_ENCODE) {
 386        nargs = script->bindings.countArgs();
 387        nvars = script->bindings.countVars();
 388        nupvars = script->bindings.countUpvars();
 389        argsVars = (nargs << 16) | nvars;
 390        paddingUpvars = nupvars;
 391    }
 392    if (!JS_XDRUint32(xdr, &argsVars) || !JS_XDRUint32(xdr, &paddingUpvars))
 393        return false;
 394    if (xdr->mode == JSXDR_DECODE) {
 395        nargs = argsVars >> 16;
 396        nvars = argsVars & 0xFFFF;
 397        JS_ASSERT((paddingUpvars >> 16) == 0);
 398        nupvars = paddingUpvars & 0xFFFF;
 399    }
 400    JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
 401    JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
 402    JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
 403
 404    Bindings bindings(cx);
 405    uint32_t nameCount = nargs + nvars + nupvars;
 406    if (nameCount > 0) {
 407        LifoAllocScope las(&cx->tempLifoAlloc());
 408
 409        /*
 410         * To xdr the names we prefix the names with a bitmap descriptor and
 411         * then xdr the names as strings. For argument names (indexes below
 412         * nargs) the corresponding bit in the bitmap is unset when the name
 413         * is null. Such null names are not encoded or decoded. For variable
 414         * names (indexes starting from nargs) bitmap's bit is set when the
 415         * name is declared as const, not as ordinary var.
 416         * */
 417        uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
 418        uint32_t *bitmap = cx->tempLifoAlloc().newArray<uint32_t>(bitmapLength);
 419        if (!bitmap) {
 420            js_ReportOutOfMemory(cx);
 421            return false;
 422        }
 423
 424        Vector<JSAtom *> names(cx);
 425        if (xdr->mode == JSXDR_ENCODE) {
 426            if (!script->bindings.getLocalNameArray(cx, &names))
 427                return false;
 428            PodZero(bitmap, bitmapLength);
 429            for (uintN i = 0; i < nameCount; i++) {
 430                if (i < nargs && names[i])
 431                    bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
 432            }
 433        }
 434        for (uintN i = 0; i < bitmapLength; ++i) {
 435            if (!JS_XDRUint32(xdr, &bitmap[i]))
 436                return false;
 437        }
 438
 439        for (uintN i = 0; i < nameCount; i++) {
 440            if (i < nargs &&
 441                !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))))
 442            {
 443                if (xdr->mode == JSXDR_DECODE) {
 444                    uint16_t dummy;
 445                    if (!bindings.addDestructuring(cx, &dummy))
 446                        return false;
 447                } else {
 448                    JS_ASSERT(!names[i]);
 449                }
 450                continue;
 451            }
 452
 453            JSAtom *name;
 454            if (xdr->mode == JSXDR_ENCODE)
 455                name = names[i];
 456            if (!js_XDRAtom(xdr, &name))
 457                return false;
 458            if (xdr->mode == JSXDR_DECODE) {
 459                BindingKind kind = (i < nargs)
 460                                   ? ARGUMENT
 461                                   : (i < uintN(nargs + nvars))
 462                                   ? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
 463                                      JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
 464                                      ? CONSTANT
 465                                      : VARIABLE)
 466                                   : UPVAR;
 467                if (!bindings.add(cx, name, kind))
 468                    return false;
 469            }
 470        }
 471    }
 472
 473    if (xdr->mode == JSXDR_DECODE) {
 474        if (!bindings.ensureShape(cx))
 475            return false;
 476        bindings.makeImmutable();
 477    }
 478
 479    if (xdr->mode == JSXDR_ENCODE)
 480        length = script->length;
 481    if (!JS_XDRUint32(xdr, &length))
 482        return JS_FALSE;
 483
 484    if (xdr->mode == JSXDR_ENCODE) {
 485        prologLength = script->mainOffset;
 486        JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
 487        version = (uint32_t)script->getVersion() | (script->nfixed << 16);
 488        lineno = (uint32_t)script->lineno;
 489        nslots = (uint32_t)script->nslots;
 490        nslots = (uint32_t)((script->staticLevel << 16) | script->nslots);
 491        natoms = script->natoms;
 492
 493        notes = script->notes();
 494        nsrcnotes = script->numNotes();
 495
 496        if (JSScript::isValidOffset(script->objectsOffset))
 497            nobjects = script->objects()->length;
 498        if (JSScript::isValidOffset(script->upvarsOffset))
 499            JS_ASSERT(script->bindings.countUpvars() == script->upvars()->length);
 500        if (JSScript::isValidOffset(script->regexpsOffset))
 501            nregexps = script->regexps()->length;
 502        if (JSScript::isValidOffset(script->trynotesOffset))
 503            ntrynotes = script->trynotes()->length;
 504        if (JSScript::isValidOffset(script->constOffset))
 505            nconsts = script->consts()->length;
 506
 507        nClosedArgs = script->nClosedArgs;
 508        nClosedVars = script->nClosedVars;
 509        encodedClosedCount = (nClosedArgs << 16) | nClosedVars;
 510
 511        nTypeSets = script->nTypeSets;
 512
 513        if (script->noScriptRval)
 514            scriptBits |= (1 << NoScriptRval);
 515        if (script->savedCallerFun)
 516            scriptBits |= (1 << SavedCallerFun);
 517        if (script->strictModeCode)
 518            scriptBits |= (1 << StrictModeCode);
 519        if (script->usesEval)
 520            scriptBits |= (1 << UsesEval);
 521        if (script->usesArguments)
 522            scriptBits |= (1 << UsesArguments);
 523        JS_ASSERT(!script->compileAndGo);
 524        JS_ASSERT(!script->hasSingletons);
 525    }
 526
 527    if (!JS_XDRUint32(xdr, &prologLength))
 528        return JS_FALSE;
 529    if (!JS_XDRUint32(xdr, &version))
 530        return JS_FALSE;
 531
 532    /*
 533     * To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
 534     * counts early.
 535     */
 536    if (!JS_XDRUint32(xdr, &natoms))
 537        return JS_FALSE;
 538    if (!JS_XDRUint32(xdr, &nsrcnotes))
 539        return JS_FALSE;
 540    if (!JS_XDRUint32(xdr, &ntrynotes))
 541        return JS_FALSE;
 542    if (!JS_XDRUint32(xdr, &nobjects))
 543        return JS_FALSE;
 544    if (!JS_XDRUint32(xdr, &nregexps))
 545        return JS_FALSE;
 546    if (!JS_XDRUint32(xdr, &nconsts))
 547        return JS_FALSE;
 548    if (!JS_XDRUint32(xdr, &encodedClosedCount))
 549        return JS_FALSE;
 550    if (!JS_XDRUint32(xdr, &nTypeSets))
 551        return JS_FALSE;
 552    if (!JS_XDRUint32(xdr, &scriptBits))
 553        return JS_FALSE;
 554
 555    if (xdr->mode == JSXDR_DECODE) {
 556        nClosedArgs = encodedClosedCount >> 16;
 557        nClosedVars = encodedClosedCount & 0xFFFF;
 558
 559        /* Note: version is packed into the 32b space with another 16b value. */
 560        JSVersion version_ = JSVersion(version & JS_BITMASK(16));
 561        JS_ASSERT((version_ & VersionFlags::FULL_MASK) == uintN(version_));
 562        script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
 563                                     nregexps, ntrynotes, nconsts, 0, nClosedArgs,
 564                                     nClosedVars, nTypeSets, version_);
 565        if (!script)
 566            return JS_FALSE;
 567
 568        script->bindings.transfer(cx, &bindings);
 569        JS_ASSERT(!script->mainOffset);
 570        script->mainOffset = prologLength;
 571        script->nfixed = uint16_t(version >> 16);
 572
 573        /* If we know nsrcnotes, we allocated space for notes in script. */
 574        notes = script->notes();
 575        *scriptp = script;
 576
 577        if (scriptBits & (1 << NoScriptRval))
 578            script->noScriptRval = true;
 579        if (scriptBits & (1 << SavedCallerFun))
 580            script->savedCallerFun = true;
 581        if (scriptBits & (1 << StrictModeCode))
 582            script->strictModeCode = true;
 583        if (scriptBits & (1 << UsesEval))
 584            script->usesEval = true;
 585        if (scriptBits & (1 << UsesArguments))
 586            script->usesArguments = true;
 587    }
 588
 589    /*
 590     * Control hereafter must goto error on failure, in order for the
 591     * DECODE case to destroy script.
 592     */
 593    oldscript = xdr->script;
 594
 595    xdr->script = script;
 596    ok = JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode));
 597
 598    if (!ok)
 599        goto error;
 600
 601    if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
 602        !JS_XDRUint32(xdr, &lineno) ||
 603        !JS_XDRUint32(xdr, &nslots)) {
 604        goto error;
 605    }
 606
 607    if (xdr->mode == JSXDR_DECODE && state->filename) {
 608        if (!state->filenameSaved) {
 609            const char *filename = state->filename;
 610            filename = SaveScriptFilename(xdr->cx, filename);
 611            xdr->cx->free_((void *) state->filename);
 612            state->filename = filename;
 613            state->filenameSaved = true;
 614            if (!filename)
 615                goto error;
 616        }
 617        script->filename = state->filename;
 618    }
 619
 620    JS_ASSERT_IF(xdr->mode == JSXDR_ENCODE, state->filename == script->filename);
 621
 622    callbacks = JS_GetSecurityCallbacks(cx);
 623    if (xdr->mode == JSXDR_ENCODE)
 624        encodeable = script->principals && callbacks && callbacks->principalsTranscoder;
 625
 626    if (!JS_XDRUint32(xdr, &encodeable))
 627        goto error;
 628
 629    if (encodeable) {
 630        if (!callbacks || !callbacks->principalsTranscoder) {
 631            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 632                                 JSMSG_CANT_DECODE_PRINCIPALS);
 633            goto error;
 634        }
 635
 636        if (!callbacks->principalsTranscoder(xdr, &script->principals))
 637            goto error;
 638
 639        if (xdr->mode == JSXDR_ENCODE)
 640            sameOriginPrincipals = script->principals == script->originPrincipals;
 641
 642        if (!JS_XDRUint32(xdr, &sameOriginPrincipals))
 643            goto error;
 644
 645        if (sameOriginPrincipals) {
 646            if (xdr->mode == JSXDR_DECODE) {
 647                script->originPrincipals = script->principals;
 648                JSPRINCIPALS_HOLD(cx, script->originPrincipals);
 649            }
 650        } else {
 651            if (!callbacks->principalsTranscoder(xdr, &script->originPrincipals))
 652                goto error;
 653        }
 654    }
 655
 656    if (xdr->mode == JSXDR_DECODE) {
 657        script->lineno = (uintN)lineno;
 658        script->nslots = uint16_t(nslots);
 659        script->staticLevel = uint16_t(nslots >> 16);
 660    }
 661
 662    for (i = 0; i != natoms; ++i) {
 663        if (!js_XDRAtom(xdr, &script->atoms[i]))
 664            goto error;
 665    }
 666
 667    /*
 668     * Here looping from 0-to-length to xdr objects is essential. It ensures
 669     * that block objects from the script->objects array will be written and
 670     * restored in the outer-to-inner order. js_XDRBlockObject relies on this
 671     * to restore the parent chain.
 672     */
 673    for (i = 0; i != nobjects; ++i) {
 674        HeapPtr<JSObject> *objp = &script->objects()->vector[i];
 675        uint32_t isBlock;
 676        if (xdr->mode == JSXDR_ENCODE) {
 677            JSObject *obj = *objp;
 678            JS_ASSERT(obj->isFunction() || obj->isStaticBlock());
 679            isBlock = obj->isBlock() ? 1 : 0;
 680        }
 681        if (!JS_XDRUint32(xdr, &isBlock))
 682            goto error;
 683        if (isBlock == 0) {
 684            JSObject *tmp = *objp;
 685            if (!js_XDRFunctionObject(xdr, &tmp))
 686                goto error;
 687            *objp = tmp;
 688        } else {
 689            JS_ASSERT(isBlock == 1);
 690            StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
 691            if (!js_XDRStaticBlockObject(xdr, &tmp))
 692                goto error;
 693            *objp = tmp;
 694        }
 695    }
 696    for (i = 0; i != nupvars; ++i) {
 697        if (!JS_XDRUint32(xdr, reinterpret_cast<uint32_t *>(&script->upvars()->vector[i])))
 698            goto error;
 699    }
 700    for (i = 0; i != nregexps; ++i) {
 701        JSObject *tmp = script->regexps()->vector[i];
 702        if (!js_XDRRegExpObject(xdr, &tmp))
 703            goto error;
 704        script->regexps()->vector[i] = tmp;
 705    }
 706    for (i = 0; i != nClosedArgs; ++i) {
 707        if (!JS_XDRUint32(xdr, &script->closedSlots[i]))
 708            goto error;
 709    }
 710    for (i = 0; i != nClosedVars; ++i) {
 711        if (!JS_XDRUint32(xdr, &script->closedSlots[nClosedArgs + i]))
 712            goto error;
 713    }
 714
 715    if (ntrynotes != 0) {
 716        /*
 717         * We combine tn->kind and tn->stackDepth when serializing as XDR is not
 718         * efficient when serializing small integer types.
 719         */
 720        JSTryNote *tn, *tnfirst;
 721        uint32_t kindAndDepth;
 722        JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8_t));
 723        JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16_t));
 724
 725        tnfirst = script->trynotes()->vector;
 726        JS_ASSERT(script->trynotes()->length == ntrynotes);
 727        tn = tnfirst + ntrynotes;
 728        do {
 729            --tn;
 730            if (xdr->mode == JSXDR_ENCODE) {
 731                kindAndDepth = (uint32_t(tn->kind) << 16)
 732                               | uint32_t(tn->stackDepth);
 733            }
 734            if (!JS_XDRUint32(xdr, &kindAndDepth) ||
 735                !JS_XDRUint32(xdr, &tn->start) ||
 736                !JS_XDRUint32(xdr, &tn->length)) {
 737                goto error;
 738            }
 739            if (xdr->mode == JSXDR_DECODE) {
 740                tn->kind = uint8_t(kindAndDepth >> 16);
 741                tn->stackDepth = uint16_t(kindAndDepth);
 742            }
 743        } while (tn != tnfirst);
 744    }
 745
 746    for (i = 0; i != nconsts; ++i) {
 747        Value tmp = script->consts()->vector[i];
 748        if (!JS_XDRValue(xdr, &tmp))
 749            goto error;
 750        script->consts()->vector[i] = tmp;
 751    }
 752
 753    if (xdr->mode == JSXDR_DECODE && cx->hasRunOption(JSOPTION_PCCOUNT))
 754        (void) script->initCounts(cx);
 755
 756    xdr->script = oldscript;
 757    return JS_TRUE;
 758
 759  error:
 760    if (xdr->mode == JSXDR_DECODE)
 761        *scriptp = NULL;
 762    xdr->script = oldscript;
 763    return JS_FALSE;
 764}
 765
 766#endif /* JS_HAS_XDR */
 767
 768bool
 769JSScript::initCounts(JSContext *cx)
 770{
 771    JS_ASSERT(!pcCounters);
 772
 773    size_t count = 0;
 774
 775    jsbytecode *pc, *next;
 776    for (pc = code; pc < code + length; pc = next) {
 777        count += OpcodeCounts::numCounts(JSOp(*pc));
 778        next = pc + GetBytecodeLength(pc);
 779    }
 780
 781    size_t bytes = (length * sizeof(OpcodeCounts)) + (count * sizeof(double));
 782    char *cursor = (char *) cx->calloc_(bytes);
 783    if (!cursor)
 784        return false;
 785
 786    DebugOnly<char *> base = cursor;
 787
 788    pcCounters.counts = (OpcodeCounts *) cursor;
 789    cursor += length * sizeof(OpcodeCounts);
 790
 791    for (pc = code; pc < code + length; pc = next) {
 792        pcCounters.counts[pc - code].counts = (double *) cursor;
 793        size_t capacity = OpcodeCounts::numCounts(JSOp(*pc));
 794#ifdef DEBUG
 795        pcCounters.counts[pc - code].capacity = capacity;
 796#endif
 797        cursor += capacity * sizeof(double);
 798        next = pc + GetBytecodeLength(pc);
 799    }
 800
 801    JS_ASSERT(size_t(cursor - base) == bytes);
 802
 803    /* Enable interrupts in any interpreter frames running on this script. */
 804    InterpreterFrames *frames;
 805    for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
 806        frames->enableInterruptsIfRunning(this);
 807
 808    return true;
 809}
 810
 811void
 812JSScript::destroyCounts(JSContext *cx)
 813{
 814    if (pcCounters) {
 815        cx->free_(pcCounters.counts);
 816        pcCounters.counts = NULL;
 817    }
 818}
 819
 820
 821/*
 822 * Shared script filename management.
 823 */
 824
 825static const char *
 826SaveScriptFilename(JSContext *cx, const char *filename)
 827{
 828    JSCompartment *comp = cx->compartment;
 829
 830    ScriptFilenameTable::AddPtr p = comp->scriptFilenameTable.lookupForAdd(filename);
 831    if (!p) {
 832        size_t size = offsetof(ScriptFilenameEntry, filename) + strlen(filename) + 1;
 833        ScriptFilenameEntry *entry = (ScriptFilenameEntry *) cx->malloc_(size);
 834        if (!entry)
 835            return NULL;
 836        entry->marked = false;
 837        strcpy(entry->filename, filename);
 838
 839        if (!comp->scriptFilenameTable.add(p, entry)) {
 840            Foreground::free_(entry);
 841            JS_ReportOutOfMemory(cx);
 842            return NULL;
 843        }
 844    }
 845
 846    return (*p)->filename;
 847}
 848
 849/*
 850 * Back up from a saved filename by its offset within its hash table entry.
 851 */
 852#define FILENAME_TO_SFE(fn) \
 853    ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
 854
 855void
 856js_MarkScriptFilename(const char *filename)
 857{
 858    ScriptFilenameEntry *sfe = FILENAME_TO_SFE(filename);
 859    sfe->marked = true;
 860}
 861
 862void
 863js_SweepScriptFilenames(JSCompartment *comp)
 864{
 865    ScriptFilenameTable &table = comp->scriptFilenameTable;
 866    for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront()) {
 867        ScriptFilenameEntry *entry = e.front();
 868        if (entry->marked) {
 869            entry->marked = false;
 870        } else if (!comp->rt->gcKeepAtoms) {
 871            Foreground::free_(entry);
 872            e.removeFront();
 873        }
 874    }
 875}
 876
 877/*
 878 * JSScript data structures memory alignment:
 879 *
 880 * JSScript
 881 * JSObjectArray    script objects' descriptor if JSScript.objectsOffset != 0,
 882 *                    use script->objects() to access it.
 883 * JSObjectArray    script regexps' descriptor if JSScript.regexpsOffset != 0,
 884 *                    use script->regexps() to access it.
 885 * JSTryNoteArray   script try notes' descriptor if JSScript.tryNotesOffset
 886 *                    != 0, use script->trynotes() to access it.
 887 * JSAtom *a[]      array of JSScript.natoms atoms pointed by
 888 *                    JSScript.atoms if any.
 889 * JSObject *o[]    array of script->objects()->length objects if any
 890 *                    pointed by script->objects()->vector.
 891 * JSObject *r[]    array of script->regexps()->length regexps if any
 892 *                    pointed by script->regexps()->vector.
 893 * JSTryNote t[]    array of script->trynotes()->length try notes if any
 894 *                    pointed by script->trynotes()->vector.
 895 * jsbytecode b[]   script bytecode pointed by JSScript.code.
 896 * jssrcnote  s[]   script source notes, use script->notes() to access it
 897 *
 898 * The alignment avoids gaps between entries as alignment requirement for each
 899 * subsequent structure or array is the same or divides the alignment
 900 * requirement for the previous one.
 901 *
 902 * The followings asserts checks that assuming that the alignment requirement
 903 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
 904 * it is sizeof(uint32_t) as the structure consists of 3 uint32_t fields.
 905 */
 906JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0);
 907JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0);
 908JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray));
 909JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *));
 910JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32_t) == 0);
 911JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32_t));
 912JS_STATIC_ASSERT(sizeof(uint32_t) % sizeof(jsbytecode) == 0);
 913JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
 914
 915/*
 916 * Check that uint8_t offsets is enough to reach any optional array allocated
 917 * after JSScript. For that we check that the maximum possible offset for
 918 * JSConstArray, that last optional array, still fits 1 byte and do not
 919 * coincide with INVALID_OFFSET.
 920 */
 921JS_STATIC_ASSERT(sizeof(JSObjectArray) +
 922                 sizeof(JSUpvarArray) +
 923                 sizeof(JSObjectArray) +
 924                 sizeof(JSTryNoteArray) +
 925                 sizeof(js::GlobalSlotArray)
 926                 < JSScript::INVALID_OFFSET);
 927JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
 928
 929JSScript *
 930JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
 931                    uint32_t nobjects, uint32_t nupvars, uint32_t nregexps,
 932                    uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
 933                    uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version)
 934{
 935    size_t size = sizeof(JSAtom *) * natoms;
 936    if (nobjects != 0)
 937        size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
 938    if (nupvars != 0)
 939        size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32_t);
 940    if (nregexps != 0)
 941        size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
 942    if (ntrynotes != 0)
 943        size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
 944    if (nglobals != 0)
 945        size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
 946    uint32_t totalClosed = nClosedArgs + nClosedVars;
 947    if (totalClosed != 0)
 948        size += totalClosed * sizeof(uint32_t);
 949
 950    /*
 951     * To esnure jsval alignment for the const array we place it immediately
 952     * after JSSomethingArray structs as their sizes all divide sizeof(jsval).
 953     * This works as long as the data itself is allocated with proper
 954     * alignment which we ensure below.
 955     */
 956    JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(jsval) == 0);
 957    JS_STATIC_ASSERT(sizeof(JSUpvarArray) % sizeof(jsval) == 0);
 958    JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0);
 959    JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0);
 960    JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0);
 961    if (nconsts != 0)
 962        size += sizeof(JSConstArray) + nconsts * sizeof(Value);
 963
 964    size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
 965
 966    uint8_t *data = NULL;
 967#if JS_SCRIPT_INLINE_DATA_LIMIT
 968    if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) {
 969        /*
 970         * Check that if inlineData is big enough to store const values, we
 971         * can do that without any special alignment requirements given that
 972         * the script as a GC thing is always aligned on Cell::CellSize.
 973         */
 974        JS_STATIC_ASSERT(Cell::CellSize % sizeof(Value) == 0);
 975        JS_STATIC_ASSERT(JS_SCRIPT_INLINE_DATA_LIMIT < sizeof(Value) ||
 976                         offsetof(JSScript, inlineData) % sizeof(Value) == 0);
 977    } else
 978#endif
 979    {
 980        /*
 981         * We assume that calloc aligns on sizeof(Value) if the size we ask to
 982         * allocate divides sizeof(Value).
 983         */
 984        JS_STATIC_ASSERT(sizeof(Value) == sizeof(jsdouble));
 985        data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
 986        if (!data)
 987            return NULL;
 988    }
 989
 990    JSScript *script = js_NewGCScript(cx);
 991    if (!script) {
 992        Foreground::free_(data);
 993        return NULL;
 994    }
 995
 996    PodZero(script);
 997#ifdef JS_CRASH_DIAGNOSTICS
 998    script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
 999#endif
1000#if JS_SCRIPT_INLINE_DATA_LIMIT
1001    if (!data)
1002        data = script->inlineData;
1003#endif
1004    script->data  = data;
1005    script->length = length;
1006    script->version = version;
1007    new (&script->bindings) Bindings(cx);
1008
1009    uint8_t *cursor = data;
1010    if (nobjects != 0) {
1011        script->objectsOffset = uint8_t(cursor - data);
1012        cursor += sizeof(JSObjectArray);
1013    } else {
1014        script->objectsOffset = JSScript::INVALID_OFFSET;
1015    }
1016    if (nupvars != 0) {
1017        script->upvarsOffset = uint8_t(cursor - data);
1018        cursor += sizeof(JSUpvarArray);
1019    } else {
1020        script->upvarsOffset = JSScript::INVALID_OFFSET;
1021    }
1022    if (nregexps != 0) {
1023        script->regexpsOffset = uint8_t(cursor - data);
1024        cursor += sizeof(JSObjectArray);
1025    } else {
1026        script->regexpsOffset = JSScript::INVALID_OFFSET;
1027    }
1028    if (ntrynotes != 0) {
1029        script->trynotesOffset = uint8_t(cursor - data);
1030        cursor += sizeof(JSTryNoteArray);
1031    } else {
1032        script->trynotesOffset = JSScript::INVALID_OFFSET;
1033    }
1034    if (nglobals != 0) {
1035        script->globalsOffset = uint8_t(cursor - data);
1036        cursor += sizeof(GlobalSlotArray);
1037    } else {
1038        script->globalsOffset = JSScript::INVALID_OFFSET;
1039    }
1040    JS_ASSERT(cursor - data < 0xFF);
1041    if (nconsts != 0) {
1042        script->constOffset = uint8_t(cursor - data);
1043        cursor += sizeof(JSConstArray);
1044    } else {
1045        script->constOffset = JSScript::INVALID_OFFSET;
1046    }
1047
1048    JS_STATIC_ASSERT(sizeof(JSObjectArray) +
1049                     sizeof(JSUpvarArray) +
1050                     sizeof(JSObjectArray) +
1051                     sizeof(JSTryNoteArray) +
1052                     sizeof(GlobalSlotArray) < 0xFF);
1053
1054
1055    if (nconsts != 0) {
1056        JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
1057        script->consts()->length = nconsts;
1058        script->consts()->vector = (HeapValue *)cursor;
1059        cursor += nconsts * sizeof(script->consts()->vector[0]);
1060    }
1061
1062    if (natoms != 0) {
1063        script->natoms = natoms;
1064        script->atoms = reinterpret_cast<JSAtom **>(cursor);
1065        cursor += natoms * sizeof(script->atoms[0]);
1066    }
1067
1068    if (nobjects != 0) {
1069        script->objects()->length = nobjects;
1070        script->objects()->vector = (HeapPtr<JSObject> *)cursor;
1071        cursor += nobjects * sizeof(script->objects()->vector[0]);
1072    }
1073
1074    if (nregexps != 0) {
1075        script->regexps()->length = nregexps;
1076        script->regexps()->vector = (HeapPtr<JSObject> *)cursor;
1077        cursor += nregexps * sizeof(script->regexps()->vector[0]);
1078    }
1079
1080    if (ntrynotes != 0) {
1081        script->trynotes()->length = ntrynotes;
1082        script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
1083        size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
1084#ifdef DEBUG
1085        memset(cursor, 0, vectorSize);
1086#endif
1087        cursor += vectorSize;
1088    }
1089
1090    if (nglobals != 0) {
1091        script->globals()->length = nglobals;
1092        script->globals()->vector = reinterpret_cast<GlobalSlotArray::Entry *>(cursor);
1093        cursor += nglobals * sizeof(script->globals()->vector[0]);
1094    }
1095
1096    if (totalClosed != 0) {
1097        script->nClosedArgs = nClosedArgs;
1098        script->nClosedVars = nClosedVars;
1099        script->closedSlots = reinterpret_cast<uint32_t *>(cursor);
1100        cursor += totalClosed * sizeof(uint32_t);
1101    }
1102
1103    JS_ASSERT(nTypeSets <= UINT16_MAX);
1104    script->nTypeSets = uint16_t(nTypeSets);
1105
1106    /*
1107     * NB: We allocate the vector of uint32_t upvar cookies after all vectors of
1108     * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
1109     */
1110    if (nupvars != 0) {
1111        script->upvars()->length = nupvars;
1112        script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
1113        cursor += nupvars * sizeof(script->upvars()->vector[0]);
1114    }
1115
1116    script->code = (jsbytecode *)cursor;
1117    JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
1118
1119#ifdef DEBUG
1120    script->id_ = 0;
1121#endif
1122
1123    JS_ASSERT(script->getVersion() == version);
1124    return script;
1125}
1126
1127JSScript *
1128JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
1129{
1130    uint32_t mainLength, prologLength, nfixed;
1131    JSScript *script;
1132    const char *filename;
1133    JSFunction *fun;
1134
1135    /* The counts of indexed things must be checked during code generation. */
1136    JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
1137    JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
1138    JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
1139
1140    mainLength = bce->offset();
1141    prologLength = bce->prologOffset();
1142
1143    if (!bce->bindings.ensureShape(cx))
1144        return NULL;
1145
1146    uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
1147    uint16_t nClosedArgs = uint16_t(bce->closedArgs.length());
1148    JS_ASSERT(nClosedArgs == bce->closedArgs.length());
1149    uint16_t nClosedVars = uint16_t(bce->closedVars.length());
1150    JS_ASSERT(nClosedVars == bce->closedVars.length());
1151    size_t upvarIndexCount = bce->upvarIndices.hasMap() ? bce->upvarIndices->count() : 0;
1152    script = NewScript(cx, prologLength + mainLength, nsrcnotes,
1153                       bce->atomIndices->count(), bce->objectList.length,
1154                       upvarIndexCount, bce->regexpList.length,
1155                       bce->ntrynotes, bce->constList.length(),
1156                       bce->globalUses.length(), nClosedArgs, nClosedVars,
1157                       bce->typesetCount, bce->version());
1158    if (!script)
1159        return NULL;
1160
1161    bce->bindings.makeImmutable();
1162
1163    JS_ASSERT(script->mainOffset == 0);
1164    script->mainOffset = prologLength;
1165    PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
1166    PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
1167    nfixed = bce->inFunction() ? bce->bindings.countVars() : 0;
1168    JS_ASSERT(nfixed < SLOTNO_LIMIT);
1169    script->nfixed = uint16_t(nfixed);
1170    js_InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms);
1171
1172    filename = bce->parser->tokenStream.getFilename();
1173    if (filename) {
1174        script->filename = SaveScriptFilename(cx, filename);
1175        if (!script->filename)
1176            return NULL;
1177    }
1178    script->lineno = bce->firstLine;
1179    if (script->nfixed + bce->maxStackDepth >= JS_BIT(16)) {
1180        ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET,
1181                                 "script");
1182        return NULL;
1183    }
1184    script->nslots = script->nfixed + bce->maxStackDepth;
1185    script->staticLevel = uint16_t(bce->staticLevel);
1186    script->principals = bce->parser->principals;
1187
1188    if (script->principals)
1189        JSPRINCIPALS_HOLD(cx, script->principals);
1190
1191    /* Establish invariant: principals implies originPrincipals. */
1192    script->originPrincipals = bce->parser->originPrincipals;
1193    if (!script->originPrincipals)
1194        script->originPrincipals = script->principals;
1195    if (script->originPrincipals)
1196        JSPRINCIPALS_HOLD(cx, script->originPrincipals);
1197
1198    script->sourceMap = (jschar *) bce->parser->tokenStream.releaseSourceMap();
1199
1200    if (!FinishTakingSrcNotes(cx, bce, script->notes()))
1201        return NULL;
1202    if (bce->ntrynotes != 0)
1203        FinishTakingTryNotes(bce, script->trynotes());
1204    if (bce->objectList.length != 0)
1205        bce->objectList.finish(script->objects());
1206    if (bce->regexpList.length != 0)
1207        bce->regexpList.finish(script->regexps());
1208    if (bce->constList.length() != 0)
1209        bce->constList.finish(script->consts());
1210    if (bce->flags & TCF_NO_SCRIPT_RVAL)
1211        script->noScriptRval = true;
1212    if (bce->flags & TCF_STRICT_MODE_CODE)
1213        script->strictModeCode = true;
1214    if (bce->flags & TCF_COMPILE_N_GO) {
1215        script->compileAndGo = true;
1216        const StackFrame *fp = bce->parser->callerFrame;
1217        if (fp && fp->isFunctionFrame())
1218            script->savedCallerFun = true;
1219    }
1220    if (bce->callsEval())
1221        script->usesEval = true;
1222    if (bce->flags & TCF_FUN_USES_ARGUMENTS)
1223        script->usesArguments = true;
1224    if (bce->flags & TCF_HAS_SINGLETONS)
1225        script->hasSingletons = true;
1226
1227    if (bce->hasUpvarIndices()) {
1228        JS_ASSERT(bce->upvarIndices->count() <= bce->upvarMap.length());
1229        PodCopy<UpvarCookie>(script->upvars()->vector, bce->upvarMap.begin(),
1230                             bce->upvarIndices->count());
1231        bce->upvarIndices->clear();
1232        bce->upvarMap.clear();
1233    }
1234
1235    if (bce->globalUses.length()) {
1236        PodCopy<GlobalSlotArray::Entry>(script->globals()->vector, &bce->globalUses[0],
1237                                        bce->globalUses.length());
1238    }
1239
1240    if (script->nClosedArgs)
1241        PodCopy<uint32_t>(script->closedSlots, &bce->closedArgs[0], script->nClosedArgs);
1242    if (script->nClosedVars) {
1243        PodCopy<uint32_t>(&script->closedSlots[script->nClosedArgs], &bce->closedVars[0],
1244                          script->nClosedVars);
1245    }
1246
1247    script->bindings.transfer(cx, &bce->bindings);
1248
1249    fun = NULL;
1250    if (bce->inFunction()) {
1251        /*
1252         * We initialize fun->script() to be the script constructed above
1253         * so that the debugger has a valid fun->script().
1254         */
1255        fun = bce->fun();
1256        JS_ASSERT(fun->isInterpreted());
1257        JS_ASSERT(!fun->script());
1258#ifdef DEBUG
1259        if (JSScript::isValidOffset(script->upvarsOffset))
1260            JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
1261        else
1262            JS_ASSERT(script->bindings.countUpvars() == 0);
1263#endif
1264        if (bce->flags & TCF_FUN_HEAVYWEIGHT)
1265            fun->flags |= JSFUN_HEAVYWEIGHT;
1266
1267        /*
1268         * Mark functions which will only be executed once as singletons.
1269         * Skip this for flat closures, which must be copied on executing.
1270         */
1271        bool singleton =
1272            cx->typeInferenceEnabled() &&
1273            bce->parent &&
1274            bce->parent->compiling() &&
1275            bce->parent->asBytecodeEmitter()->checkSingletonContext() &&
1276            !fun->isFlatClosure();
1277
1278        if (!script->typeSetFunction(cx, fun, singleton))
1279            return NULL;
1280
1281        fun->setScript(script);
1282        script->globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
1283    } else {
1284        /*
1285         * Initialize script->object, if necessary, so that the debugger has a
1286         * valid holder object.
1287         */
1288        if (bce->flags & TCF_NEED_SCRIPT_GLOBAL)
1289            script->globalObject = GetCurrentGlobal(cx);
1290    }
1291
1292    /* Tell the debugger about this compiled script. */
1293    js_CallNewScriptHook(cx, script, fun);
1294    if (!bce->parent) {
1295        GlobalObject *compileAndGoGlobal = NULL;
1296        if (script->compileAndGo) {
1297            compileAndGoGlobal = script->globalObject;
1298            if (!compileAndGoGlobal)
1299                compileAndGoGlobal = &bce->scopeChain()->global();
1300        }
1301        Debugger::onNewScript(cx, script, compileAndGoGlobal);
1302    }
1303
1304    if (cx->hasRunOption(JSOPTION_PCCOUNT))
1305        (void) script->initCounts(cx);
1306
1307    return script;
1308}
1309
1310size_t
1311JSScript::computedSizeOfData()
1312{
1313#if JS_SCRIPT_INLINE_DATA_LIMIT
1314    if (data == inlineData)
1315        return 0;
1316#endif
1317
1318    uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
1319    JS_ASSERT(dataEnd >= data);
1320    return dataEnd - data;
1321}
1322
1323size_t
1324JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf)
1325{
1326#if JS_SCRIPT_INLINE_DATA_LIMIT
1327    if (data == inlineData)
1328        return 0;
1329#endif
1330
1331    return mallocSizeOf(data);
1332}
1333
1334/*
1335 * Nb: srcnotes are variable-length.  This function computes the number of
1336 * srcnote *slots*, which may be greater than the number of srcnotes.
1337 */
1338uint32_t
1339JSScript::numNotes()
1340{
1341    jssrcnote *sn;
1342    jssrcnote *notes_ = notes();
1343    for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1344        continue;
1345    return sn - notes_ + 1;    /* +1 for the terminator */
1346}
1347
1348JS_FRIEND_API(void)
1349js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1350{
1351    JS_ASSERT(!script->callDestroyHook);
1352    if (JSNewScriptHook hook = cx->debugHooks->newScriptHook) {
1353        AutoKeepAtoms keep(cx->runtime);
1354        hook(cx, script->filename, script->lineno, script, fun,
1355             cx->debugHooks->newScriptHookData);
1356    }
1357    script->callDestroyHook = true;
1358}
1359
1360void
1361js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1362{
1363    if (!script->callDestroyHook)
1364        return;
1365
1366    if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook)
1367        hook(cx, script, cx->debugHooks->destroyScriptHookData);
1368    script->callDestroyHook = false;
1369    JS_ClearScriptTraps(cx, script);
1370}
1371
1372void
1373JSScript::finalize(JSContext *cx, bool background)
1374{
1375    CheckScript(this, NULL);
1376
1377    js_CallDestroyScriptHook(cx, this);
1378
1379    JS_ASSERT_IF(principals, originPrincipals);
1380    if (principals)
1381        JSPRINCIPALS_DROP(cx, principals);
1382    if (originPrincipals)
1383        JSPRINCIPALS_DROP(cx, originPrincipals);
1384
1385    if (types)
1386        types->destroy();
1387
1388#ifdef JS_METHODJIT
1389    mjit::ReleaseScriptCode(cx, this);
1390#endif
1391
1392    destroyCounts(cx);
1393
1394    if (sourceMap)
1395        cx->free_(sourceMap);
1396
1397    if (debug) {
1398        jsbytecode *end = code + length;
1399        for (jsbytecode *pc = code; pc < end; pc++) {
1400            if (BreakpointSite *site = getBreakpointSite(pc)) {
1401                /* Breakpoints are swept before finalization. */
1402                JS_ASSERT(site->firstBreakpoint() == NULL);
1403                site->clearTrap(cx, NULL, NULL);
1404                JS_ASSERT(getBreakpointSite(pc) == NULL);
1405            }
1406        }
1407        cx->free_(debug);
1408    }
1409
1410#if JS_SCRIPT_INLINE_DATA_LIMIT
1411    if (data != inlineData)
1412#endif
1413    {
1414        JS_POISON(data, 0xdb, computedSizeOfData());
1415        cx->free_(data);
1416    }
1417}
1418
1419namespace js {
1420
1421static const uint32_t GSN_CACHE_THRESHOLD = 100;
1422static const uint32_t GSN_CACHE_MAP_INIT_SIZE = 20;
1423
1424void
1425GSNCache::purge()
1426{
1427    code = NULL;
1428    if (map.initialized())
1429        map.finish();
1430}
1431
1432} /* namespace js */
1433
1434jssrcnote *
1435js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1436{
1437    size_t target = pc - script->code;
1438    if (target >= size_t(script->length))
1439        return NULL;
1440
1441    GSNCache *cache = GetGSNCache(cx);
1442    if (cache->code == script->code) {
1443        JS_ASSERT(cache->map.initialized());
1444        GSNCache::Map::Ptr p = cache->map.lookup(pc);
1445        return p ? p->value : NULL;
1446    }
1447
1448    size_t offset = 0;
1449    jssrcnote *result;
1450    for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) {
1451        if (SN_IS_TERMINATOR(sn)) {
1452            result = NULL;
1453            break;
1454        }
1455        offset += SN_DELTA(sn);
1456        if (offset == target && SN_IS_GETTABLE(sn)) {
1457            result = sn;
1458            break;
1459        }
1460    }
1461
1462    if (cache->code != script->code && script->length >= GSN_CACHE_THRESHOLD) {
1463        uintN nsrcnotes = 0;
1464        for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
1465             sn = SN_NEXT(sn)) {
1466            if (SN_IS_GETTABLE(sn))
1467                ++nsrcnotes;
1468        }
1469        if (cache->code) {
1470            JS_ASSERT(cache->map.initialized());
1471            cache->map.finish();
1472            cache->code = NULL;
1473        }
1474        if (cache->map.init(nsrcnotes)) {
1475            pc = script->code;
1476            for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
1477                 sn = SN_NEXT(sn)) {
1478                pc += SN_DELTA(sn);
1479                if (SN_IS_GETTABLE(sn))
1480                    JS_ALWAYS_TRUE(cache->map.put(pc, sn));
1481            }
1482            cache->code = script->code;
1483        }
1484    }
1485
1486    return result;
1487}
1488
1489uintN
1490js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1491{
1492    /* Cope with StackFrame.pc value prior to entering js_Interpret. */
1493    if (!pc)
1494        return 0;
1495
1496    /*
1497     * Special case: function definition needs no line number note because
1498     * the function's script contains its starting line number.
1499     */
1500    JSOp op = JSOp(*pc);
1501    if (js_CodeSpec[op].format & JOF_INDEXBASE)
1502        pc += js_CodeSpec[op].length;
1503    if (*pc == JSOP_DEFFUN)
1504        return script->getFunction(GET_UINT32_INDEX(pc))->script()->lineno;
1505
1506    /*
1507     * General case: walk through source notes accumulating their deltas,
1508     * keeping track of line-number notes, until we pass the note for pc's
1509     * offset within script->code.
1510     */
1511    uintN lineno = script->lineno;
1512    ptrdiff_t offset = 0;
1513    ptrdiff_t target = pc - script->code;
1514    for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1515        offset += SN_DELTA(sn);
1516        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
1517        if (type == SRC_SETLINE) {
1518            if (offset <= target)
1519                lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1520        } else if (type == SRC_NEWLINE) {
1521            if (offset <= target)
1522                lineno++;
1523        }
1524        if (offset > target)
1525            break;
1526    }
1527    return lineno;
1528}
1529
1530/* The line number limit is the same as the jssrcnote offset limit. */
1531#define SN_LINE_LIMIT   (SN_3BYTE_OFFSET_FLAG << 16)
1532
1533jsbytecode *
1534js_LineNumberToPC(JSScript *script, uintN target)
1535{
1536    ptrdiff_t offset = 0;
1537    ptrdiff_t best = -1;
1538    uintN lineno = script->lineno;
1539    uintN bestdiff = SN_LINE_LIMIT;
1540    for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1541        /*
1542         * Exact-match only if offset is not in the prolog; otherwise use
1543         * nearest greater-or-equal line number match.
1544         */
1545        if (lineno == target && offset >= ptrdiff_t(script->mainOffset))
1546            goto out;
1547  

Large files files are truncated, but you can click here to view the full file