Bug 932276 - Reserve space in JSScript for an optional block scope array. r=jorendorff

This commit is contained in:
Andy Wingo 2013-10-29 14:39:58 +01:00
parent 7ee3876782
commit 22d47fa02a
3 changed files with 100 additions and 30 deletions

View File

@ -416,7 +416,8 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
};
uint32_t length, lineno, nslots;
uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
uint32_t natoms, nsrcnotes, i;
uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes;
uint32_t prologLength, version;
uint32_t funLength = 0;
uint32_t nTypeSets = 0;
@ -424,7 +425,8 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
JSContext *cx = xdr->cx();
RootedScript script(cx);
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
natoms = nsrcnotes = 0;
nconsts = nobjects = nregexps = ntrynotes = nblockscopes = 0;
/* XDR arguments and vars. */
uint16_t nargs = 0, nvars = 0;
@ -468,6 +470,8 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
nregexps = script->regexps()->length;
if (script->hasTrynotes())
ntrynotes = script->trynotes()->length;
if (script->hasBlockScopes())
nblockscopes = script->blockScopes()->length;
nTypeSets = script->nTypeSets;
funLength = script->funLength;
@ -512,21 +516,20 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
if (!xdr->codeUint32(&version))
return false;
/*
* To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
* counts early.
*/
// To fuse allocations, we need lengths of all embedded arrays early.
if (!xdr->codeUint32(&natoms))
return false;
if (!xdr->codeUint32(&nsrcnotes))
return false;
if (!xdr->codeUint32(&ntrynotes))
if (!xdr->codeUint32(&nconsts))
return false;
if (!xdr->codeUint32(&nobjects))
return false;
if (!xdr->codeUint32(&nregexps))
return false;
if (!xdr->codeUint32(&nconsts))
if (!xdr->codeUint32(&ntrynotes))
return false;
if (!xdr->codeUint32(&nblockscopes))
return false;
if (!xdr->codeUint32(&nTypeSets))
return false;
@ -569,8 +572,11 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
return false;
if (mode == XDR_DECODE) {
if (!JSScript::partiallyInit(cx, script, nobjects, nregexps, ntrynotes, nconsts, nTypeSets))
if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes,
nblockscopes, nTypeSets))
{
return false;
}
JS_ASSERT(!script->mainOffset);
script->mainOffset = prologLength;
@ -664,6 +670,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
return false;
}
if (nconsts) {
HeapValue *vector = script->consts()->vector;
for (i = 0; i != nconsts; ++i) {
if (!XDRScriptConst(xdr, &vector[i]))
return false;
}
}
/*
* Here looping from 0-to-length to xdr objects is essential to ensure that
* all references to enclosing blocks (via FindBlockIndex below) happen
@ -740,6 +754,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
*objp = tmp;
}
}
for (i = 0; i != nregexps; ++i) {
if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i]))
return false;
@ -776,11 +791,13 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
} while (tn != tnfirst);
}
if (nconsts) {
HeapValue *vector = script->consts()->vector;
for (i = 0; i != nconsts; ++i) {
if (!XDRScriptConst(xdr, &vector[i]))
return false;
for (i = 0; i < nblockscopes; ++i) {
BlockScopeNote *note = &script->blockScopes()->vector[i];
if (!xdr->codeUint32(&note->index) ||
!xdr->codeUint32(&note->start) ||
!xdr->codeUint32(&note->length))
{
return false;
}
}
@ -1580,6 +1597,7 @@ js::FreeScriptData(JSRuntime *rt)
* ObjectArray Objects objects()
* ObjectArray Regexps regexps()
* TryNoteArray Try notes trynotes()
* BlockScopeArray Scope notes blockScopes()
*
* Then are the elements of several arrays.
* - Most of these arrays have headers listed above (if present). For each of
@ -1595,6 +1613,7 @@ js::FreeScriptData(JSRuntime *rt)
* Objects objects()->vector objects()->length
* Regexps regexps()->vector regexps()->length
* Try notes trynotes()->vector trynotes()->length
* Scope notes blockScopes()->vector blockScopes()->length
*
* IMPORTANT: This layout has two key properties.
* - It ensures that everything has sufficient alignment; in particular, the
@ -1640,6 +1659,7 @@ js::FreeScriptData(JSRuntime *rt)
JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray));
JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray)); /* there are two of these */
JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray));
JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray));
/* These assertions ensure there is no padding required between array elements. */
JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue));
@ -1649,9 +1669,15 @@ JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, BlockScopeNote));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, BlockScopeNote));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, BlockScopeNote));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, BlockScopeNote));
JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, uint32_t));
static inline size_t
ScriptDataSize(uint32_t nbindings, uint32_t nobjects, uint32_t nregexps,
uint32_t ntrynotes, uint32_t nconsts)
ScriptDataSize(uint32_t nbindings, uint32_t nconsts, uint32_t nobjects, uint32_t nregexps,
uint32_t ntrynotes, uint32_t nblockscopes)
{
size_t size = 0;
@ -1663,6 +1689,8 @@ ScriptDataSize(uint32_t nbindings, uint32_t nobjects, uint32_t nregexps,
size += sizeof(ObjectArray) + nregexps * sizeof(JSObject *);
if (ntrynotes != 0)
size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
if (nblockscopes != 0)
size += sizeof(BlockScopeArray) + nblockscopes * sizeof(BlockScopeNote);
if (nbindings != 0) {
// Make sure bindings are sufficiently aligned.
@ -1736,10 +1764,12 @@ AllocScriptData(ExclusiveContext *cx, size_t size)
}
/* static */ bool
JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nobjects,
uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts, uint32_t nTypeSets)
JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts,
uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes,
uint32_t nblockscopes, uint32_t nTypeSets)
{
size_t size = ScriptDataSize(script->bindings.count(), nobjects, nregexps, ntrynotes, nconsts);
size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes,
nblockscopes);
script->data = AllocScriptData(cx, size);
if (!script->data)
return false;
@ -1765,6 +1795,10 @@ JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nobj
script->setHasArray(TRYNOTES);
cursor += sizeof(TryNoteArray);
}
if (nblockscopes != 0) {
script->setHasArray(BLOCK_SCOPES);
cursor += sizeof(BlockScopeArray);
}
if (nconsts != 0) {
JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
@ -1795,6 +1829,16 @@ JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nobj
cursor += vectorSize;
}
if (nblockscopes != 0) {
script->blockScopes()->length = nblockscopes;
script->blockScopes()->vector = reinterpret_cast<BlockScopeNote *>(cursor);
size_t vectorSize = nblockscopes * sizeof(script->blockScopes()->vector[0]);
#ifdef DEBUG
memset(cursor, 0, vectorSize);
#endif
cursor += vectorSize;
}
if (script->bindings.count() != 0) {
// Make sure bindings are sufficiently aligned.
cursor = reinterpret_cast<uint8_t*>
@ -1809,7 +1853,7 @@ JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nobj
/* static */ bool
JSScript::fullyInitTrivial(ExclusiveContext *cx, Handle<JSScript*> script)
{
if (!partiallyInit(cx, script, 0, 0, 0, 0, 0))
if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0))
return false;
SharedScriptData *ssd = SharedScriptData::new_(cx, 1, 1, 0);
@ -1834,9 +1878,10 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
uint32_t prologLength = bce->prologOffset();
uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
uint32_t natoms = bce->atomIndices->count();
uint32_t nblockscopes = 0;
if (!partiallyInit(cx, script,
bce->objectList.length, bce->regexpList.length, bce->tryNoteList.length(),
bce->constList.length(), bce->typesetCount))
bce->constList.length(), bce->objectList.length, bce->regexpList.length,
bce->tryNoteList.length(), nblockscopes, bce->typesetCount))
{
return false;
}
@ -1873,6 +1918,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr;
// FIXME: Initialize blockScopes here.
if (bce->tryNoteList.length() != 0)
bce->tryNoteList.finish(script->trynotes());
if (bce->objectList.length != 0)
@ -2279,6 +2325,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
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 nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
/* Script data */
@ -2444,6 +2491,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
}
if (ntrynotes != 0)
dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
if (nblockscopes != 0)
dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector);
return dst;
}

View File

@ -81,19 +81,31 @@ struct JSTryNote {
namespace js {
struct BlockScopeNote {
uint32_t index; // Index of StaticScopeObject in the object array.
uint32_t start; // Bytecode offset at which this scope starts.
uint32_t length; // Bytecode length of scope.
uint32_t padding; // Pad to 64-bit boundary.
};
struct ConstArray {
js::HeapValue *vector; /* array of indexed constant values */
uint32_t length;
};
struct ObjectArray {
js::HeapPtrObject *vector; /* array of indexed objects */
uint32_t length; /* count of indexed objects */
js::HeapPtrObject *vector; // Array of indexed objects.
uint32_t length; // Count of indexed objects.
};
struct TryNoteArray {
JSTryNote *vector; /* array of indexed try notes */
uint32_t length; /* count of indexed try notes */
JSTryNote *vector; // Array of indexed try notes.
uint32_t length; // Count of indexed try notes.
};
struct BlockScopeArray {
BlockScopeNote *vector; // Array of indexed BlockScopeNote records.
uint32_t length; // Count of indexed try notes.
};
/*
@ -552,6 +564,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
OBJECTS,
REGEXPS,
TRYNOTES,
BLOCK_SCOPES,
ARRAY_KIND_BITS
};
@ -564,7 +577,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
uint8_t generatorKindBits_:2;
// Unused padding; feel free to steal these if you need them.
uint8_t padToByte_:2;
uint8_t padToByte_:1;
// 1-bit fields.
@ -650,8 +663,9 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
// successfully creating any kind (function or other) of new JSScript.
// However, callers of fullyInitFromEmitter() do not need to do this.
static bool partiallyInit(js::ExclusiveContext *cx, JS::Handle<JSScript*> script,
uint32_t nobjects, uint32_t nregexps,
uint32_t ntrynotes, uint32_t nconsts, uint32_t nTypeSets);
uint32_t nconsts, uint32_t nobjects, uint32_t nregexps,
uint32_t ntrynotes, uint32_t nblockscopes,
uint32_t nTypeSets);
static bool fullyInitFromEmitter(js::ExclusiveContext *cx, JS::Handle<JSScript*> script,
js::frontend::BytecodeEmitter *bce);
// Initialize a no-op script.
@ -913,6 +927,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
bool hasObjects() { return hasArray(OBJECTS); }
bool hasRegexps() { return hasArray(REGEXPS); }
bool hasTrynotes() { return hasArray(TRYNOTES); }
bool hasBlockScopes() { return hasArray(BLOCK_SCOPES); }
#define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0))
@ -920,6 +935,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
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 blockScopesOffset(){ return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); }
js::ConstArray *consts() {
JS_ASSERT(hasConsts());
@ -941,6 +957,11 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset());
}
js::BlockScopeArray *blockScopes() {
JS_ASSERT(hasBlockScopes());
return reinterpret_cast<js::BlockScopeArray *>(data + blockScopesOffset());
}
bool hasLoops();
js::HeapPtrAtom &getAtom(size_t index) const {

View File

@ -22,7 +22,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 153);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 154);
class XDRBuffer {
public: