diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index b326f8db75f..134d299df38 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -416,7 +416,8 @@ js::XDRScript(XDRState *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 *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 *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 *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 *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 *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 *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 *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(¬e->index) || + !xdr->codeUint32(¬e->start) || + !xdr->codeUint32(¬e->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(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(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 @@ -1809,7 +1853,7 @@ JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nobj /* static */ bool JSScript::fullyInitTrivial(ExclusiveContext *cx, Handle 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(dst, src, src->trynotes()->vector); + if (nblockscopes != 0) + dst->blockScopes()->vector = Rebase(dst, src, src->blockScopes()->vector); return dst; } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index ea762a95e6c..21e00a1af31 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -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 OBJECTS, REGEXPS, TRYNOTES, + BLOCK_SCOPES, ARRAY_KIND_BITS }; @@ -564,7 +577,7 @@ class JSScript : public js::gc::BarrieredCell 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 // 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 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 script, js::frontend::BytecodeEmitter *bce); // Initialize a no-op script. @@ -913,6 +927,7 @@ class JSScript : public js::gc::BarrieredCell 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 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 return reinterpret_cast(data + trynotesOffset()); } + js::BlockScopeArray *blockScopes() { + JS_ASSERT(hasBlockScopes()); + return reinterpret_cast(data + blockScopesOffset()); + } + bool hasLoops(); js::HeapPtrAtom &getAtom(size_t index) const { diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index a587d5c0ed2..ec08bc4e4f5 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -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: