Commit 3b18320e authored by Nicholas Nethercote's avatar Nicholas Nethercote
Browse files

Bug 739512: Patch 3: shrink the representation of optional arrays in JSScript. r=luke.

--HG--
extra : rebase_source : df3e7190d8afbff1dfe95c44b838d6460b2c11b6
parent 73f8d42b
Loading
Loading
Loading
Loading
+26 −61
Original line number Diff line number Diff line
@@ -1019,19 +1019,20 @@ js::FreeScriptFilenames(JSRuntime *rt)
 *
 * First are some optional array headers.  They are optional because they
 * often aren't needed, i.e. the corresponding arrays often have zero elements.
 * Each header has an offset in JSScript that indicates its location within
 * |data|; that offset is INVALID_OFFSET if the array header is not present.
 * Each header also has an accessor function in JSScript.
 * Each header has a bit in JSScript::hasArrayBits that indicates if it's
 * present within |data|;  from this the offset of each present array header
 * can be computed.  Each header has an accessor function in JSScript that
 * encapsulates this offset computation.
 *
 * Array type       Array elements  Offset            Accessor
 * ----------       --------------  ------            --------
 * ConstArray       Consts          constsOffset      consts()
 * ObjectArray      Objects         objectsOffset     objects()
 * ObjectArray      Regexps         regexpsOffset     regexps()
 * TryNoteArray     Try notes       tryNotesOffset    trynotes()
 * GlobalSlotArray  Globals         globalsOffset     globals()
 * ClosedSlotArray  ClosedArgs      closedArgsOffset  closedArgs()
 * ClosedSlotArray  ClosedVars      closedVarsOffset  closedVars()
 * Array type       Array elements  Accessor
 * ----------       --------------  --------
 * ConstArray       Consts          consts()
 * ObjectArray      Objects         objects()
 * ObjectArray      Regexps         regexps()
 * TryNoteArray     Try notes       trynotes()
 * GlobalSlotArray  Globals         globals()
 * ClosedSlotArray  ClosedArgs      closedArgs()
 * ClosedSlotArray  ClosedVars      closedVars()
 *
 * Then are the elements of several arrays.  
 * - Most of these arrays have headers listed above (if present).  For each of
@@ -1099,21 +1100,6 @@ JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, jsbytecode));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(jsbytecode, jssrcnote));

/*
 * Check that uint8_t offsets is enough to reach any optional array allocated
 * within |data|. For that we check that the maximum possible offset for the
 * closedVars array -- the last optional array -- still fits in 1 byte and does
 * not coincide with INVALID_OFFSET.
 */
JS_STATIC_ASSERT(sizeof(ConstArray) +
                 sizeof(ObjectArray) +
                 sizeof(ObjectArray) +
                 sizeof(TryNoteArray) +
                 sizeof(GlobalSlotArray) +
                 sizeof(ClosedSlotArray)
                 < JSScript::INVALID_OFFSET);
JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);

static inline size_t
ScriptDataSize(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
               uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
@@ -1180,47 +1166,32 @@ JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t

    uint8_t *cursor = data;
    if (nconsts != 0) {
        script->constsOffset = uint8_t(cursor - data);
        script->setHasArray(CONSTS);
        cursor += sizeof(ConstArray);
    } else {
        script->constsOffset = JSScript::INVALID_OFFSET;
    }
    if (nobjects != 0) {
        script->objectsOffset = uint8_t(cursor - data);
        script->setHasArray(OBJECTS);
        cursor += sizeof(ObjectArray);
    } else {
        script->objectsOffset = JSScript::INVALID_OFFSET;
    }
    if (nregexps != 0) {
        script->regexpsOffset = uint8_t(cursor - data);
        script->setHasArray(REGEXPS);
        cursor += sizeof(ObjectArray);
    } else {
        script->regexpsOffset = JSScript::INVALID_OFFSET;
    }
    if (ntrynotes != 0) {
        script->trynotesOffset = uint8_t(cursor - data);
        script->setHasArray(TRYNOTES);
        cursor += sizeof(TryNoteArray);
    } else {
        script->trynotesOffset = JSScript::INVALID_OFFSET;
    }
    if (nglobals != 0) {
        script->globalsOffset = uint8_t(cursor - data);
        script->setHasArray(GLOBALS);
        cursor += sizeof(GlobalSlotArray);
    } else {
        script->globalsOffset = JSScript::INVALID_OFFSET;
    }
    if (nClosedArgs != 0) {
        script->closedArgsOffset = uint8_t(cursor - data);
        script->setHasArray(CLOSED_ARGS);
        cursor += sizeof(ClosedSlotArray);
    } else {
        script->closedArgsOffset = JSScript::INVALID_OFFSET;
    }
    JS_ASSERT(cursor - data < 0xFF);
    if (nClosedVars != 0) {
        script->closedVarsOffset = uint8_t(cursor - data);
        script->setHasArray(CLOSED_VARS);
        cursor += sizeof(ClosedSlotArray);
    } else {
        script->closedVarsOffset = JSScript::INVALID_OFFSET;
    }

    if (nconsts != 0) {
@@ -1765,13 +1736,13 @@ js::CloneScript(JSContext *cx, JSScript *src)
{
    /* NB: Keep this in sync with XDRScript. */

    uint32_t nconsts = JSScript::isValidOffset(src->constsOffset) ? src->consts()->length : 0;
    uint32_t nobjects = JSScript::isValidOffset(src->objectsOffset) ? src->objects()->length : 0;
    uint32_t nregexps = JSScript::isValidOffset(src->regexpsOffset) ? src->regexps()->length : 0;
    uint32_t ntrynotes = JSScript::isValidOffset(src->trynotesOffset) ? src->trynotes()->length : 0;
    uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
    uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
    uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
    uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
    uint32_t nClosedArgs = src->numClosedArgs();
    uint32_t nClosedVars = src->numClosedVars();
    JS_ASSERT(!JSScript::isValidOffset(src->globalsOffset));
    JS_ASSERT(!src->hasGlobals());
    uint32_t nglobals = 0;

    /* Script data */
@@ -1883,13 +1854,7 @@ js::CloneScript(JSContext *cx, JSScript *src)
        if (src->analyzedArgsUsage())
            dst->setNeedsArgsObj(src->needsArgsObj());
    }
    dst->constsOffset = src->constsOffset;
    dst->objectsOffset = src->objectsOffset;
    dst->regexpsOffset = src->regexpsOffset;
    dst->trynotesOffset = src->trynotesOffset;
    dst->globalsOffset = src->globalsOffset;
    dst->closedArgsOffset = src->closedArgsOffset;
    dst->closedVarsOffset = src->closedVarsOffset;
    dst->cloneHasArray(src);
    dst->noScriptRval = src->noScriptRval;
    dst->savedCallerFun = src->savedCallerFun;
    dst->strictModeCode = src->strictModeCode;
+45 −33
Original line number Diff line number Diff line
@@ -488,11 +488,6 @@ struct JSScript : public js::gc::Cell
    uint32_t        idpad;
#endif

#if JS_BITS_PER_WORD == 32
  private:
    uint32_t        pad32;
#endif

    // 16-bit fields.

  private:
@@ -513,19 +508,25 @@ struct JSScript : public js::gc::Cell

    // 8-bit fields.

  private:
    // The bits in this field indicate the presence/non-presence of several
    // optional arrays in |data|.  See the comments above NewScript() for
    // details.
    uint8_t         hasArrayBits;

    // The kinds of the optional arrays.
  public:
    // Offsets to various array structures from the end of this script, or
    // JSScript::INVALID_OFFSET if the array has length 0.
    uint8_t         constsOffset;   /* offset to the array of constants */
    uint8_t         objectsOffset;  /* offset to the array of nested function,
                                       block, scope, xml and one-time regexps
                                       objects */
    uint8_t         regexpsOffset;  /* offset to the array of to-be-cloned
                                       regexps  */
    uint8_t         trynotesOffset; /* offset to the array of try notes */
    uint8_t         globalsOffset;  /* offset to the array of global slots */
    uint8_t         closedArgsOffset; /* offset to the array of closed args */
    uint8_t         closedVarsOffset; /* offset to the array of closed vars */
    enum ArrayKind {
        CONSTS,
        OBJECTS,
        REGEXPS,
        TRYNOTES,
        GLOBALS,
        CLOSED_ARGS,
        CLOSED_VARS,
        LIMIT
    };
    JS_STATIC_ASSERT(sizeof(hasArrayBits) * 8 >= LIMIT);

    // 1-bit fields.

@@ -743,50 +744,61 @@ struct JSScript : public js::gc::Cell
    /* Script notes are allocated right after the code. */
    jssrcnote *notes() { return (jssrcnote *)(code + length); }

    static const uint8_t INVALID_OFFSET = 0xFF;
    static bool isValidOffset(uint8_t offset) { return offset != INVALID_OFFSET; }
    bool hasArray(ArrayKind kind)           { return (hasArrayBits & (1 << kind)); }
    void setHasArray(ArrayKind kind)        { hasArrayBits |= (1 << kind); }
    void cloneHasArray(JSScript *script)    { hasArrayBits = script->hasArrayBits; }

    bool hasConsts()        { return hasArray(CONSTS);      }
    bool hasObjects()       { return hasArray(OBJECTS);     }
    bool hasRegexps()       { return hasArray(REGEXPS);     }
    bool hasTrynotes()      { return hasArray(TRYNOTES);    }
    bool hasGlobals()       { return hasArray(GLOBALS);     }
    bool hasClosedArgs()    { return hasArray(CLOSED_ARGS); }
    bool hasClosedVars()    { return hasArray(CLOSED_VARS); }

    #define OFF(fooOff, hasFoo, t)   (fooOff() + (hasFoo() ? sizeof(t) : 0))

    bool hasConsts()        { return isValidOffset(constsOffset);     }
    bool hasObjects()       { return isValidOffset(objectsOffset);    }
    bool hasRegexps()       { return isValidOffset(regexpsOffset);    }
    bool hasTrynotes()      { return isValidOffset(trynotesOffset);   }
    bool hasGlobals()       { return isValidOffset(globalsOffset);    }
    bool hasClosedArgs()    { return isValidOffset(closedArgsOffset); }
    bool hasClosedVars()    { return isValidOffset(closedVarsOffset); }
    size_t constsOffset()     { return 0; }
    size_t objectsOffset()    { return OFF(constsOffset,     hasConsts,     js::ConstArray);      }
    size_t regexpsOffset()    { return OFF(objectsOffset,    hasObjects,    js::ObjectArray);     }
    size_t trynotesOffset()   { return OFF(regexpsOffset,    hasRegexps,    js::ObjectArray);     }
    size_t globalsOffset()    { return OFF(trynotesOffset,   hasTrynotes,   js::TryNoteArray);    }
    size_t closedArgsOffset() { return OFF(globalsOffset,    hasGlobals,    js::GlobalSlotArray); }
    size_t closedVarsOffset() { return OFF(closedArgsOffset, hasClosedArgs, js::ClosedSlotArray); }

    js::ConstArray *consts() {
        JS_ASSERT(hasConsts());
        return reinterpret_cast<js::ConstArray *>(data + constsOffset);
        return reinterpret_cast<js::ConstArray *>(data + constsOffset());
    }

    js::ObjectArray *objects() {
        JS_ASSERT(hasObjects());
        return reinterpret_cast<js::ObjectArray *>(data + objectsOffset);
        return reinterpret_cast<js::ObjectArray *>(data + objectsOffset());
    }

    js::ObjectArray *regexps() {
        JS_ASSERT(hasRegexps());
        return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset);
        return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset());
    }

    js::TryNoteArray *trynotes() {
        JS_ASSERT(hasTrynotes());
        return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset);
        return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
    }

    js::GlobalSlotArray *globals() {
        JS_ASSERT(hasGlobals());
        return reinterpret_cast<js::GlobalSlotArray *>(data + globalsOffset);
        return reinterpret_cast<js::GlobalSlotArray *>(data + globalsOffset());
    }

    js::ClosedSlotArray *closedArgs() {
        JS_ASSERT(hasClosedArgs());
        return reinterpret_cast<js::ClosedSlotArray *>(data + closedArgsOffset);
        return reinterpret_cast<js::ClosedSlotArray *>(data + closedArgsOffset());
    }

    js::ClosedSlotArray *closedVars() {
        JS_ASSERT(hasClosedVars());
        return reinterpret_cast<js::ClosedSlotArray *>(data + closedVarsOffset);
        return reinterpret_cast<js::ClosedSlotArray *>(data + closedVarsOffset());
    }

    uint32_t numClosedArgs() {
+1 −1
Original line number Diff line number Diff line
@@ -885,7 +885,7 @@ Class js::BlockClass = {
static uint32_t
FindObjectIndex(JSScript *script, StaticBlockObject *maybeBlock)
{
    if (!maybeBlock || !JSScript::isValidOffset(script->objectsOffset))
    if (!maybeBlock || !script->hasObjects())
        return NO_PARENT_INDEX;

    ObjectArray *objects = script->objects();