diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 70121998a0c..461545a8b20 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -59,6 +59,7 @@ #include "nsDOMError.h" #include "nsDOMCID.h" #include "jsdbgapi.h" +#include "jsarena.h" #include "jsfun.h" #include "jsobj.h" #include "nsIXPConnect.h" diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 5b5ebe2d5be..0649a7b41bc 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -199,33 +199,6 @@ nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } -class nsRootedJSValueArray { -public: - explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {} - - PRBool SetCapacity(JSContext *cx, size_t capacity) { - PRBool ok = vals.SetCapacity(capacity); - if (!ok) - return PR_FALSE; - // Values must be safe for the GC to inspect (they must not contain garbage). - memset(vals.Elements(), 0, vals.SizeOf()); - resetRooter(cx); - return PR_TRUE; - } - - jsval *Elements() { - return vals.Elements(); - } - -private: - void resetRooter(JSContext *cx) { - avr.changeArray(vals.Elements(), vals.Length()); - } - - nsAutoTArray vals; - js::AutoArrayRooter avr; -}; - /**************************************************************** ************************** AutoFree **************************** ****************************************************************/ @@ -245,6 +218,15 @@ private: void *mPtr; }; +class nsAutoPoolRelease { +public: + nsAutoPoolRelease(JSArenaPool *p, void *m) : mPool(p), mMark(m) {} + ~nsAutoPoolRelease() { JS_ARENA_RELEASE(mPool, mMark); } +private: + JSArenaPool *mPool; + void *mMark; +}; + // A utility function for script languages to call. Although it looks small, // the use of nsIDocShell and nsPresContext triggers a huge number of // dependencies that most languages would not otherwise need. @@ -1930,14 +1912,16 @@ nsJSContext::CallEventHandler(nsISupports* aTarget, void *aScope, void *aHandler return NS_ERROR_FAILURE; } - Maybe tempStorage; + Maybe poolRelease; + Maybe tvr; // Use |target| as the scope for wrapping the arguments, since aScope is // the safe scope in many cases, which isn't very useful. Wrapping aTarget // was OK because those typically have PreCreate methods that give them the // right scope anyway, and we want to make sure that the arguments end up // in the same scope as aTarget. - rv = ConvertSupportsTojsvals(aargv, target, &argc, &argv, tempStorage); + rv = ConvertSupportsTojsvals(aargv, target, &argc, + &argv, poolRelease, tvr); NS_ENSURE_SUCCESS(rv, rv); ++mExecuteDepth; @@ -2359,10 +2343,12 @@ nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArg JSAutoRequest ar(mContext); - Maybe tempStorage; + Maybe poolRelease; + Maybe tvr; nsresult rv; - rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc, &argv, tempStorage); + rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc, + &argv, poolRelease, tvr); NS_ENSURE_SUCCESS(rv, rv); jsval vargs; @@ -2398,7 +2384,8 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs, void *aScope, PRUint32 *aArgc, jsval **aArgv, - Maybe &aTempStorage) + Maybe &aPoolRelease, + Maybe &aRooter) { nsresult rv = NS_OK; @@ -2433,11 +2420,16 @@ nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs, argCount = 1; // the nsISupports which is not an array } + void *mark = JS_ARENA_MARK(&mContext->tempPool); + jsval *argv; + size_t nbytes = argCount * sizeof(jsval); + JS_ARENA_ALLOCATE_CAST(argv, jsval *, &mContext->tempPool, nbytes); + NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY); + memset(argv, 0, nbytes); /* initialize so GC-able */ + // Use the caller's auto guards to release and unroot. - aTempStorage.construct(mContext); - PRBool ok = aTempStorage.ref().SetCapacity(mContext, argCount); - NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); - jsval *argv = aTempStorage.ref().Elements(); + aPoolRelease.construct(&mContext->tempPool, mark); + aRooter.construct(mContext, argCount, argv); if (argsArray) { for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) { diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 7ce9a377d08..54434ee0dfe 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -48,7 +48,7 @@ #include "nsScriptNameSpaceManager.h" class nsIXPConnectJSObjectHolder; -class nsRootedJSValueArray; +class nsAutoPoolRelease; namespace js { class AutoArrayRooter; } @@ -204,7 +204,8 @@ protected: void *aScope, PRUint32 *aArgc, jsval **aArgv, - mozilla::Maybe &aPoolRelease); + mozilla::Maybe &aPoolRelease, + mozilla::Maybe &aRooter); nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv); diff --git a/js/src/Makefile.in b/js/src/Makefile.in index c595fa33607..b0fd3b5dfdc 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -101,6 +101,7 @@ CPPSRCS = \ jsalloc.cpp \ jsanalyze.cpp \ jsapi.cpp \ + jsarena.cpp \ jsarray.cpp \ jsatom.cpp \ jsbool.cpp \ @@ -160,7 +161,6 @@ CPPSRCS = \ Stack.cpp \ String.cpp \ ParseMaps.cpp \ - LifoAlloc.cpp \ Unicode.cpp \ $(NULL) @@ -174,6 +174,7 @@ INSTALLED_HEADERS = \ js.msg \ jsalloc.h \ jsapi.h \ + jsarena.h \ jsatom.h \ jsbit.h \ jsclass.h \ @@ -240,10 +241,9 @@ INSTALLED_HEADERS = \ VPATH += \ $(srcdir)/vm \ $(srcdir)/frontend \ - $(srcdir)/ds \ $(NULL) -EXPORTS_NAMESPACES = vm ds +EXPORTS_NAMESPACES = vm EXPORTS_vm = \ ArgumentsObject.h \ @@ -255,9 +255,6 @@ EXPORTS_vm = \ Unicode.h \ $(NULL) -EXPORTS_ds = \ - LifoAlloc.h - ############################################### # BEGIN include sources for low-level code shared with Gecko # diff --git a/js/src/ds/LifoAlloc.cpp b/js/src/ds/LifoAlloc.cpp deleted file mode 100644 index 94b8442d20d..00000000000 --- a/js/src/ds/LifoAlloc.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "LifoAlloc.h" - -#include - -using namespace js; - -namespace js { -namespace detail { - -BumpChunk * -BumpChunk::new_(size_t chunkSize) -{ - JS_ASSERT(RoundUpPow2(chunkSize) == chunkSize); - void *mem = js_malloc(chunkSize); - if (!mem) - return NULL; - BumpChunk *result = new (mem) BumpChunk(chunkSize - sizeof(BumpChunk)); - - /* - * We assume that the alignment of sAlign is less than that of - * the underlying memory allocator -- creating a new BumpChunk should - * always satisfy the sAlign alignment constraint. - */ - JS_ASSERT(AlignPtr(result->bump) == result->bump); - return result; -} - -void * -BumpChunk::tryAllocUnaligned(size_t n) -{ - char *oldBump = bump; - char *newBump = bump + n; - if (newBump > limit) - return NULL; - - setBump(newBump); - return oldBump; -} - -} /* namespace detail */ -} /* namespace js */ - -void -LifoAlloc::freeAll() -{ - while (first) { - BumpChunk *victim = first; - first = first->next(); - BumpChunk::delete_(victim); - } - first = latest = NULL; -} - -void -LifoAlloc::freeUnused() -{ - /* Don't free anything if we have outstanding marks. */ - if (markCount || !first) - return; - - JS_ASSERT(first && latest); - - /* Rewind through any unused chunks. */ - if (!latest->used()) { - BumpChunk *lastUsed = NULL; - for (BumpChunk *it = first; it != latest; it = it->next()) { - if (it->used()) - lastUsed = it; - } - if (!lastUsed) { - freeAll(); - return; - } - latest = lastUsed; - } - - /* Free all chunks after |latest|. */ - size_t freed = 0; - for (BumpChunk *victim = latest->next(); victim; victim = victim->next()) { - BumpChunk::delete_(victim); - freed++; - } -} - -LifoAlloc::BumpChunk * -LifoAlloc::getOrCreateChunk(size_t n) -{ - if (first) { - /* Look for existing, unused BumpChunks to satisfy the request. */ - while (latest->next()) { - latest = latest->next(); - latest->resetBump(); /* This was an unused BumpChunk on the chain. */ - if (latest->canAlloc(n)) - return latest; - } - } - - size_t defaultChunkFreeSpace = defaultChunkSize_ - sizeof(BumpChunk); - size_t chunkSize = n > defaultChunkFreeSpace - ? RoundUpPow2(n + sizeof(BumpChunk)) - : defaultChunkSize_; - - /* If we get here, we couldn't find an existing BumpChunk to fill the request. */ - BumpChunk *newChunk = BumpChunk::new_(chunkSize); - if (!newChunk) - return NULL; - if (!first) { - latest = first = newChunk; - } else { - JS_ASSERT(latest && !latest->next()); - latest->setNext(newChunk); - latest = newChunk; - } - return newChunk; -} - -void * -LifoAlloc::allocUnaligned(size_t n) -{ - void *result; - if (latest && (result = latest->tryAllocUnaligned(n))) - return result; - - return alloc(n); -} - -void * -LifoAlloc::reallocUnaligned(void *origPtr, size_t origSize, size_t incr) -{ - JS_ASSERT(first && latest); - - /* - * Maybe we can grow the latest allocation in a BumpChunk. - * - * Note: we could also realloc the whole BumpChunk in the !canAlloc - * case, but this should not be a frequently hit case. - */ - if (latest - && origPtr == (char *) latest->mark() - origSize - && latest->canAllocUnaligned(incr)) { - JS_ALWAYS_TRUE(allocUnaligned(incr)); - return origPtr; - } - - /* Otherwise, memcpy. */ - size_t newSize = origSize + incr; - void *newPtr = allocUnaligned(newSize); - return newPtr ? memcpy(newPtr, origPtr, origSize) : NULL; -} diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h deleted file mode 100644 index da367f20def..00000000000 --- a/js/src/ds/LifoAlloc.h +++ /dev/null @@ -1,339 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99 ft=cpp: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released - * June 12, 2009. - * - * The Initial Developer of the Original Code is - * the Mozilla Corporation. - * - * Contributor(s): - * Chris Leary - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef LifoAlloc_h__ -#define LifoAlloc_h__ - -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - * - * This data structure supports stacky LIFO allocation (mark/release and - * LifoAllocScope). It does not maintain one contiguous segment; instead, it - * maintains a bunch of linked memory segments. In order to prevent malloc/free - * thrashing, unused segments are deallocated when garbage collection occurs. - */ - -#include "jsutil.h" -#include "jstl.h" - -namespace js { - -namespace detail { - -static const size_t LIFO_ALLOC_ALIGN = 8; - -JS_ALWAYS_INLINE -char * -AlignPtr(void *orig) -{ - typedef typename tl::StaticAssert< - tl::FloorLog2::result == tl::CeilingLog2::result - >::result _; - - char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & -LIFO_ALLOC_ALIGN); - JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0); - return result; -} - -/* Header for a chunk of memory wrangled by the LifoAlloc. */ -class BumpChunk -{ - char *bump; - char *limit; - BumpChunk *next_; - size_t bumpSpaceSize; - - char *base() const { return limit - bumpSpaceSize; } - - explicit BumpChunk(size_t bumpSpaceSize) - : bump(reinterpret_cast(this) + sizeof(BumpChunk)), limit(bump + bumpSpaceSize), - next_(NULL), bumpSpaceSize(bumpSpaceSize) { - JS_ASSERT(bump == AlignPtr(bump)); - } - - void clobberUnused() { -#ifdef DEBUG - memset(bump, 0xcd, limit - bump); -#endif - } - - void setBump(void *ptr) { - JS_ASSERT(base() <= ptr); - JS_ASSERT(ptr <= limit); - DebugOnly prevBump = bump; - bump = static_cast(ptr); - if (prevBump < bump) - clobberUnused(); - } - - public: - BumpChunk *next() const { return next_; } - void setNext(BumpChunk *succ) { next_ = succ; } - - size_t used() const { return bump - base(); } - - void resetBump() { - setBump(reinterpret_cast(this) + sizeof(BumpChunk)); - } - - void *mark() const { return bump; } - - void release(void *mark) { - JS_ASSERT(contains(mark)); - JS_ASSERT(mark <= bump); - setBump(mark); - } - - bool contains(void *mark) const { - return base() <= mark && mark <= limit; - } - - bool canAlloc(size_t n) { - return AlignPtr(bump) + n <= limit; - } - - bool canAllocUnaligned(size_t n) { - return bump + n <= limit; - } - - /* Try to perform an allocation of size |n|, return null if not possible. */ - JS_ALWAYS_INLINE - void *tryAlloc(size_t n) { - char *aligned = AlignPtr(bump); - char *newBump = aligned + n; - if (newBump > limit) - return NULL; - - setBump(newBump); - return aligned; - } - - void *tryAllocUnaligned(size_t n); - - void *allocInfallible(size_t n) { - void *result = tryAlloc(n); - JS_ASSERT(result); - return result; - } - - static BumpChunk *new_(size_t chunkSize); - - static void delete_(BumpChunk *chunk) { -#ifdef DEBUG - memset(chunk, 0xcd, sizeof(*chunk) + chunk->bumpSpaceSize); -#endif - js_free(chunk); - } -}; - -} /* namespace detail */ - -/* - * LIFO bump allocator: used for phase-oriented and fast LIFO allocations. - * - * Note: |latest| is not necessary "last". We leave BumpChunks latent in the - * chain after they've been released to avoid thrashing before a GC. - */ -class LifoAlloc -{ - typedef detail::BumpChunk BumpChunk; - - BumpChunk *first; - BumpChunk *latest; - size_t markCount; - size_t defaultChunkSize_; - - void operator=(const LifoAlloc &); - LifoAlloc(const LifoAlloc &); - - /* - * Return a BumpChunk that can perform an allocation of at least size |n| - * and add it to the chain appropriately. - * - * Side effect: if retval is non-null, |first| and |latest| are initialized - * appropriately. - */ - BumpChunk *getOrCreateChunk(size_t n); - - void reset(size_t defaultChunkSize) { - JS_ASSERT(RoundUpPow2(defaultChunkSize) == defaultChunkSize); - first = latest = NULL; - defaultChunkSize_ = defaultChunkSize; - markCount = 0; - } - - public: - explicit LifoAlloc(size_t defaultChunkSize) { reset(defaultChunkSize); } - - /* Steal allocated chunks from |other|. */ - void steal(LifoAlloc *other) { - JS_ASSERT(!other->markCount); - PodCopy((char *) this, (char *) other, sizeof(*this)); - other->reset(defaultChunkSize_); - } - - ~LifoAlloc() { freeAll(); } - - size_t defaultChunkSize() const { return defaultChunkSize_; } - - /* Frees all held memory. */ - void freeAll(); - - /* Should be called on GC in order to release any held chunks. */ - void freeUnused(); - - JS_ALWAYS_INLINE - void *alloc(size_t n) { - void *result; - if (latest && (result = latest->tryAlloc(n))) - return result; - - if (!getOrCreateChunk(n)) - return NULL; - - return latest->allocInfallible(n); - } - - template - T *newArray(size_t count) { - void *mem = alloc(sizeof(T) * count); - if (!mem) - return NULL; - JS_STATIC_ASSERT(tl::IsPodType::result); - return (T *) mem; - } - - /* - * Create an array with uninitialized elements of type |T|. - * The caller is responsible for initialization. - */ - template - T *newArrayUninitialized(size_t count) { - return static_cast(alloc(sizeof(T) * count)); - } - - void *mark() { - markCount++; - - return latest ? latest->mark() : NULL; - } - - void release(void *mark) { - markCount--; - - if (!mark) { - latest = first; - if (latest) - latest->resetBump(); - return; - } - - /* - * Find the chunk that contains |mark|, and make sure we don't pass - * |latest| along the way -- we should be making the chain of active - * chunks shorter, not longer! - */ - BumpChunk *container = first; - while (true) { - if (container->contains(mark)) - break; - JS_ASSERT(container != latest); - container = container->next(); - } - latest = container; - latest->release(mark); - } - - /* Get the total "used" (occupied bytes) count for the arena chunks. */ - size_t used() const { - size_t accum = 0; - BumpChunk *it = first; - while (it) { - accum += it->used(); - it = it->next(); - } - return accum; - } - - /* Doesn't perform construction; useful for lazily-initialized POD types. */ - template - JS_ALWAYS_INLINE - T *newPod() { - return static_cast(alloc(sizeof(T))); - } - - JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE) - - /* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */ - - void *allocUnaligned(size_t n); - void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr); -}; - -class LifoAllocScope { - LifoAlloc *lifoAlloc; - void *mark; - bool shouldRelease; - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - - public: - explicit LifoAllocScope(LifoAlloc *lifoAlloc - JS_GUARD_OBJECT_NOTIFIER_PARAM) - : lifoAlloc(lifoAlloc), shouldRelease(true) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - mark = lifoAlloc->mark(); - } - - ~LifoAllocScope() { - if (shouldRelease) - lifoAlloc->release(mark); - } - - void releaseEarly() { - JS_ASSERT(shouldRelease); - lifoAlloc->release(mark); - shouldRelease = false; - } -}; - -} /* namespace js */ - -#endif diff --git a/js/src/frontend/ParseMaps.cpp b/js/src/frontend/ParseMaps.cpp index edc5623715a..ab2d563afda 100644 --- a/js/src/frontend/ParseMaps.cpp +++ b/js/src/frontend/ParseMaps.cpp @@ -39,7 +39,6 @@ * ***** END LICENSE BLOCK ***** */ #include "ParseMaps-inl.h" -#include "jscompartment.h" using namespace js; @@ -126,12 +125,13 @@ DumpAtomDefnMap(const AtomDefnMapPtr &map) AtomDeclNode * AtomDecls::allocNode(JSDefinition *defn) { - AtomDeclNode *p = cx->tempLifoAlloc().new_(defn); + AtomDeclNode *p; + JS_ARENA_ALLOCATE_TYPE(p, AtomDeclNode, &cx->tempPool); if (!p) { js_ReportOutOfMemory(cx); return NULL; } - return p; + return new (p) AtomDeclNode(defn); } bool diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 8da77037679..09e7a838b3e 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -109,7 +109,8 @@ Bytecode::mergeDefines(JSContext *cx, ScriptAnalysis *script, bool initial, * with progressively smaller sets of defined variables. */ if (!owned) { - uint32 *reallocArray = cx->typeLifoAlloc().newArray(defineCount); + uint32 *reallocArray = + ArenaArray(cx->compartment->pool, defineCount); if (!reallocArray) { script->setOOM(cx); return false; @@ -133,11 +134,12 @@ void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc) { printf("#%u:", script->id()); - LifoAlloc lifoAlloc(1024); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter); fprintf(stdout, "%s", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); } #endif @@ -155,7 +157,7 @@ ScriptAnalysis::addJump(JSContext *cx, unsigned offset, Bytecode *&code = codeArray[offset]; bool initial = (code == NULL); if (initial) { - code = cx->typeLifoAlloc().new_(); + code = ArenaNew(cx->compartment->pool); if (!code) { setOOM(cx); return false; @@ -276,16 +278,16 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) { JS_ASSERT(cx->compartment->activeAnalysis); JS_ASSERT(!ranBytecode()); - LifoAlloc &tla = cx->typeLifoAlloc(); + JSArenaPool &pool = cx->compartment->pool; unsigned length = script->length; unsigned nargs = script->hasFunction ? script->function()->nargs : 0; numSlots = TotalSlots(script); - codeArray = tla.newArray(length); - definedLocals = tla.newArray(script->nfixed); - escapedSlots = tla.newArray(numSlots); + codeArray = ArenaArray(pool, length); + definedLocals = ArenaArray(pool, script->nfixed); + escapedSlots = ArenaArray(pool, numSlots); if (!codeArray || !definedLocals || !escapedSlots) { setOOM(cx); @@ -369,7 +371,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) unsigned forwardCatch = 0; /* Fill in stack depth and definitions at initial bytecode. */ - Bytecode *startcode = tla.new_(); + Bytecode *startcode = ArenaNew(pool); if (!startcode) { setOOM(cx); return; @@ -701,7 +703,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) if (definedLocals[local] == LOCAL_CONDITIONALLY_DEFINED) { if (forwardJump) { /* Add this local to the variables defined after this bytecode. */ - uint32 *newArray = tla.newArray(defineCount + 1); + uint32 *newArray = ArenaArray(pool, defineCount + 1); if (!newArray) { setOOM(cx); return; @@ -793,7 +795,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) bool initial = (nextcode == NULL); if (initial) { - nextcode = tla.new_(); + nextcode = ArenaNew(pool); if (!nextcode) { setOOM(cx); return; @@ -837,9 +839,9 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) return; } - LifoAlloc &tla = cx->typeLifoAlloc(); + JSArenaPool &pool = cx->compartment->pool; - lifetimes = tla.newArray(numSlots); + lifetimes = ArenaArray(pool, numSlots); if (!lifetimes) { setOOM(cx); return; @@ -967,7 +969,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) /* Restore all saved variables. :FIXME: maybe do this precisely. */ for (unsigned i = 0; i < savedCount; i++) { LifetimeVariable &var = *saved[i]; - var.lifetime = tla.new_(offset, var.savedEnd, var.saved); + var.lifetime = ArenaNew(pool, offset, var.savedEnd, var.saved); if (!var.lifetime) { cx->free_(saved); setOOM(cx); @@ -1034,7 +1036,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) if (loop && loop->entry > loop->lastBlock) loop->lastBlock = loop->entry; - LoopAnalysis *nloop = tla.new_(); + LoopAnalysis *nloop = ArenaNew(pool); if (!nloop) { cx->free_(saved); setOOM(cx); @@ -1083,7 +1085,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) * Jumping to a place where this variable is live. Make a new * lifetime segment for the variable. */ - var.lifetime = tla.new_(offset, var.savedEnd, var.saved); + var.lifetime = ArenaNew(pool, offset, var.savedEnd, var.saved); if (!var.lifetime) { cx->free_(saved); setOOM(cx); @@ -1147,7 +1149,7 @@ ScriptAnalysis::addVariable(JSContext *cx, LifetimeVariable &var, unsigned offse } } } - var.lifetime = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); + var.lifetime = ArenaNew(cx->compartment->pool, offset, var.savedEnd, var.saved); if (!var.lifetime) { setOOM(cx); return; @@ -1164,7 +1166,7 @@ ScriptAnalysis::killVariable(JSContext *cx, LifetimeVariable &var, unsigned offs /* Make a point lifetime indicating the write. */ if (!var.saved) saved[savedCount++] = &var; - var.saved = cx->typeLifoAlloc().new_(offset, var.savedEnd, var.saved); + var.saved = ArenaNew(cx->compartment->pool, offset, var.savedEnd, var.saved); if (!var.saved) { setOOM(cx); return; @@ -1250,7 +1252,7 @@ ScriptAnalysis::extendVariable(JSContext *cx, LifetimeVariable &var, } JS_ASSERT(savedEnd <= end); if (savedEnd > segment->end) { - Lifetime *tail = cx->typeLifoAlloc().new_(savedEnd, 0, segment->next); + Lifetime *tail = ArenaNew(cx->compartment->pool, savedEnd, 0, segment->next); if (!tail) { setOOM(cx); return; @@ -1327,7 +1329,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) return; } - LifoAlloc &tla = cx->typeLifoAlloc(); + JSArenaPool &pool = cx->compartment->pool; unsigned maxDepth = script->nslots - script->nfixed; /* @@ -1488,7 +1490,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses; if (xuses) { - code->poppedValues = tla.newArray(xuses); + code->poppedValues = (SSAValue *)ArenaArray(pool, xuses); if (!code->poppedValues) { setOOM(cx); return; @@ -1511,7 +1513,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) } if (xuses) { - SSAUseChain *useChains = tla.newArray(xuses); + SSAUseChain *useChains = ArenaArray(cx->compartment->pool, xuses); if (!useChains) { setOOM(cx); return; @@ -1538,7 +1540,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx) unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs; if (xdefs) { - code->pushedUses = tla.newArray(xdefs); + code->pushedUses = ArenaArray(cx->compartment->pool, xdefs); if (!code->pushedUses) { setOOM(cx); return; @@ -1729,8 +1731,8 @@ PhiNodeCapacity(unsigned length) bool ScriptAnalysis::makePhi(JSContext *cx, uint32 slot, uint32 offset, SSAValue *pv) { - SSAPhiNode *node = cx->typeLifoAlloc().new_(); - SSAValue *options = cx->typeLifoAlloc().newArray(PhiNodeCapacity(0)); + SSAPhiNode *node = ArenaNew(cx->compartment->pool); + SSAValue *options = ArenaArray(cx->compartment->pool, PhiNodeCapacity(0)); if (!node || !options) { setOOM(cx); return false; @@ -1762,7 +1764,7 @@ ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v) if (trackUseChain(v)) { SSAUseChain *&uses = useChain(v); - SSAUseChain *use = cx->typeLifoAlloc().new_(); + SSAUseChain *use = ArenaNew(cx->compartment->pool); if (!use) { setOOM(cx); return; @@ -1780,8 +1782,8 @@ ScriptAnalysis::insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v) return; } - SSAValue *newOptions = - cx->typeLifoAlloc().newArray(PhiNodeCapacity(node->length + 1)); + SSAValue *newOptions = ArenaArray(cx->compartment->pool, + PhiNodeCapacity(node->length + 1)); if (!newOptions) { setOOM(cx); return; @@ -1920,7 +1922,7 @@ ScriptAnalysis::freezeNewValues(JSContext *cx, uint32 offset) return; } - code.newValues = cx->typeLifoAlloc().newArray(count + 1); + code.newValues = ArenaArray(cx->compartment->pool, count + 1); if (!code.newValues) { setOOM(cx); return; diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 371bf0c4471..9bbcb09ca90 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -41,13 +41,11 @@ #ifndef jsanalyze_h___ #define jsanalyze_h___ +#include "jsarena.h" #include "jscompartment.h" #include "jscntxt.h" #include "jsinfer.h" #include "jsscript.h" -#include "jstl.h" - -#include "ds/LifoAlloc.h" struct JSScript; @@ -85,6 +83,11 @@ namespace analyze { * analyses are independent from type inference. */ +class SSAValue; +struct SSAUseChain; +struct LoopAnalysis; +struct SlotValue; + /* Information about a bytecode instruction. */ class Bytecode { @@ -1369,16 +1372,4 @@ void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc); } /* namespace analyze */ } /* namespace js */ -namespace js { -namespace tl { - -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; - -} /* namespace tl */ -} /* namespace js */ - #endif // jsanalyze_h___ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 34f28d602ec..51feb91dd36 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -48,6 +48,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsdhash.h" @@ -99,7 +100,6 @@ #include "vm/Stack-inl.h" #include "vm/String-inl.h" -#include "ds/LifoAlloc.h" #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" @@ -2608,6 +2608,10 @@ JS_CompartmentGC(JSContext *cx, JSCompartment *comp) LeaveTrace(cx); + /* Don't nuke active arenas if executing or compiling. */ + if (cx->tempPool.current == &cx->tempPool.first) + JS_FinishArenaPool(&cx->tempPool); + GCREASON(PUBLIC_API); js_GC(cx, comp, GC_NORMAL); } @@ -2624,6 +2628,10 @@ JS_MaybeGC(JSContext *cx) { LeaveTrace(cx); + /* Don't nuke active arenas if executing or compiling. */ + if (cx->tempPool.current == &cx->tempPool.first) + JS_FinishArenaPool(&cx->tempPool); + MaybeGC(cx); } diff --git a/js/src/jsarena.cpp b/js/src/jsarena.cpp new file mode 100644 index 00000000000..9d41f79258e --- /dev/null +++ b/js/src/jsarena.cpp @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Lifetime-based fast allocation, inspired by much prior art, including + * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" + * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). + */ +#include +#include +#include "jsalloc.h" +#include "jstypes.h" +#include "jsstdint.h" +#include "jsbit.h" +#include "jsarena.h" +#include "jsprvtd.h" + +using namespace js; + +/* If JSArena's length is a multiple of 8, that ensures its payload is 8-aligned. */ +JS_STATIC_ASSERT(sizeof(JSArena) % 8 == 0); + +JS_PUBLIC_API(void) +JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) +{ + /* Restricting ourselves to some simple alignments keeps things simple. */ + if (align == 1 || align == 2 || align == 4 || align == 8) { + pool->mask = align - 1; + } else { + /* This shouldn't happen, but set pool->mask reasonably if it does. */ + JS_NOT_REACHED("JS_InitArenaPool: bad align"); + pool->mask = 7; + } + pool->first.next = NULL; + /* pool->first is a zero-sized dummy arena that's never allocated from. */ + pool->first.base = pool->first.avail = pool->first.limit = + JS_ARENA_ALIGN(pool, &pool->first + 1); + pool->current = &pool->first; + pool->arenasize = size; +} + +JS_PUBLIC_API(void *) +JS_ArenaAllocate(JSArenaPool *pool, size_t nb) +{ + /* + * Search pool from current forward till we find or make enough space. + * + * NB: subtract nb from a->limit in the loop condition, instead of adding + * nb to a->avail, to avoid overflow (possible when running a 32-bit + * program on a 64-bit system where the kernel maps the heap up against the + * top of the 32-bit address space, see bug 279273). Note that this + * necessitates a comparison between nb and a->limit that looks like a + * (conceptual) type error but isn't. + */ + JS_ASSERT((nb & pool->mask) == 0); + JSArena *a; + /* + * Comparing nb to a->limit looks like a (conceptual) type error, but it's + * necessary to avoid wrap-around. Yuk. + */ + for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; pool->current = a) { + JSArena **ap = &a->next; + if (!*ap) { + /* Not enough space in pool, so we must malloc. */ + size_t gross = sizeof(JSArena) + JS_MAX(nb, pool->arenasize); + a = (JSArena *) OffTheBooks::malloc_(gross); + if (!a) + return NULL; + + a->next = NULL; + a->base = a->avail = jsuword(a) + sizeof(JSArena); + /* + * Because malloc returns 8-aligned pointers and sizeof(JSArena) is + * a multiple of 8, a->base will always be 8-aligned, which should + * suffice for any valid pool. + */ + JS_ASSERT(a->base == JS_ARENA_ALIGN(pool, a->base)); + a->limit = (jsuword)a + gross; + + *ap = a; + continue; + } + a = *ap; /* move to next arena */ + } + + void* p = (void *)a->avail; + a->avail += nb; + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + return p; +} + +JS_PUBLIC_API(void *) +JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) +{ + /* If we've called JS_ArenaRealloc, the new size must be bigger than pool->arenasize. */ + JS_ASSERT(size + incr > pool->arenasize); + + /* Find the arena containing |p|. */ + JSArena *a; + JSArena **ap = &pool->first.next; + while (true) { + a = *ap; + if (JS_IS_IN_ARENA(a, p)) + break; + JS_ASSERT(a != pool->current); + ap = &a->next; + } + /* If we've called JS_ArenaRealloc, p must be at the start of an arena. */ + JS_ASSERT(a->base == jsuword(p)); + + size_t gross = sizeof(JSArena) + JS_ARENA_ALIGN(pool, size + incr); + a = (JSArena *) OffTheBooks::realloc_(a, gross); + if (!a) + return NULL; + + a->base = jsuword(a) + sizeof(JSArena); + a->avail = a->limit = jsuword(a) + gross; + /* + * Because realloc returns 8-aligned pointers and sizeof(JSArena) is a + * multiple of 8, a->base will always be 8-aligned, which should suffice + * for any valid pool. + */ + JS_ASSERT(a->base == JS_ARENA_ALIGN(pool, a->base)); + + if (a != *ap) { + /* realloc moved the allocation: update other pointers to a. */ + if (pool->current == *ap) + pool->current = a; + *ap = a; + } + + return (void *)a->base; +} + +JS_PUBLIC_API(void *) +JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) +{ + void *newp; + + /* + * If p points to an oversized allocation, it owns an entire arena, so we + * can simply realloc the arena. + */ + if (size > pool->arenasize) + return JS_ArenaRealloc(pool, p, size, incr); + + JS_ARENA_ALLOCATE(newp, pool, size + incr); + if (newp) + memcpy(newp, p, size); + return newp; +} + +/* + * Free tail arenas linked after head, which may not be the true list head. + * Reset pool->current to point to head in case it pointed at a tail arena. + */ +static void +FreeArenaList(JSArenaPool *pool, JSArena *head) +{ + JSArena **ap, *a; + + ap = &head->next; + a = *ap; + if (!a) + return; + +#ifdef DEBUG + do { + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + a->avail = a->base; + JS_CLEAR_UNUSED(a); + } while ((a = a->next) != NULL); + a = *ap; +#endif + + do { + *ap = a->next; + JS_CLEAR_ARENA(a); + UnwantedForeground::free_(a); + } while ((a = *ap) != NULL); + + pool->current = head; +} + +JS_PUBLIC_API(void) +JS_ArenaRelease(JSArenaPool *pool, char *mark) +{ + JSArena *a; + + for (a = &pool->first; a; a = a->next) { + JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); + + if (JS_IS_IN_ARENA(a, mark)) { + a->avail = JS_ARENA_ALIGN(pool, mark); + JS_ASSERT(a->avail <= a->limit); + FreeArenaList(pool, a); + return; + } + } +} + +JS_PUBLIC_API(void) +JS_FreeArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first); +} + +JS_PUBLIC_API(void) +JS_FinishArenaPool(JSArenaPool *pool) +{ + FreeArenaList(pool, &pool->first); +} + +JS_PUBLIC_API(void) +JS_ArenaFinish() +{ +} + +JS_PUBLIC_API(void) +JS_ArenaShutDown(void) +{ +} diff --git a/js/src/jsarena.h b/js/src/jsarena.h new file mode 100644 index 00000000000..e45e8e44b03 --- /dev/null +++ b/js/src/jsarena.h @@ -0,0 +1,317 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsarena_h___ +#define jsarena_h___ +/* + * Lifetime-based fast allocation, inspired by much prior art, including + * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" + * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). + * + * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). + */ +#include +#include "jstypes.h" +#include "jscompat.h" +#include "jsstaticcheck.h" + +JS_BEGIN_EXTERN_C + +typedef struct JSArena JSArena; +typedef struct JSArenaPool JSArenaPool; + +struct JSArena { + JSArena *next; /* next arena for this lifetime */ + jsuword base; /* aligned base address, follows this header */ + jsuword limit; /* one beyond last byte in arena */ + jsuword avail; /* points to next available byte */ +}; + +struct JSArenaPool { + JSArena first; /* first arena in pool list */ + JSArena *current; /* arena from which to allocate space */ + size_t arenasize; /* net exact size of a new arena */ + jsuword mask; /* alignment mask (power-of-2 - 1) */ +}; + +#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) + +#define JS_ARENA_ALLOCATE(p, pool, nb) \ + JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) + +#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ + JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) + +#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ + JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) + +/* + * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb + * from a->limit rather than adding _nb to _p, to avoid overflow (possible when + * running a 32-bit program on a 64-bit system where the kernel maps the heap + * up against the top of the 32-bit address space, see bug 279273). Note that + * this necessitates a comparison between nb and a->limit that looks like a + * (conceptual) type error but isn't. + */ +#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ + JS_BEGIN_MACRO \ + JSArena *_a = (pool)->current; \ + size_t _nb = JS_ARENA_ALIGN(pool, nb); \ + jsuword _p = _a->avail; \ + if ((guard) || _p > _a->limit - _nb) \ + _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ + else \ + _a->avail = _p + _nb; \ + p = (type) _p; \ + STATIC_ASSUME(!p || ubound((char *)p) >= nb); \ + JS_END_MACRO + +#define JS_ARENA_GROW(p, pool, size, incr) \ + JS_ARENA_GROW_CAST(p, void *, pool, size, incr) + +#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ + JS_BEGIN_MACRO \ + JSArena *_a = (pool)->current; \ + if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ + /* p was the last thing allocated in the current arena... */ \ + size_t _nb = (size) + (incr); \ + _nb = JS_ARENA_ALIGN(pool, _nb); \ + if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ + /* ... and we have space, so just extend p in-place */ \ + _a->avail = (jsuword)(p) + _nb; \ + } else if ((jsuword)(p) == _a->base) { \ + /* ... p is also the 1st thing in this arena */ \ + p = (type) JS_ArenaRealloc(pool, p, size, incr); \ + } else { \ + /* hard case */ \ + p = (type) JS_ArenaGrow(pool, p, size, incr); \ + } \ + } else { \ + /* hard case */ \ + p = (type) JS_ArenaGrow(pool, p, size, incr); \ + } \ + STATIC_ASSUME(!p || ubound((char *)p) >= size + incr); \ + JS_END_MACRO + +#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) +#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) + +/* + * Check if the mark is inside arena's allocated area. + */ +#define JS_IS_IN_ARENA(a, mark) \ + (JS_UPTRDIFF(mark, (a)->base) <= JS_UPTRDIFF((a)->avail, (a)->base)) + +#ifdef DEBUG +#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ + memset((void*)(a)->avail, JS_FREE_PATTERN, \ + (a)->limit - (a)->avail)) +#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ + (a)->limit - (jsuword)(a)) +#else +#define JS_CLEAR_UNUSED(a) /* nothing */ +#define JS_CLEAR_ARENA(a) /* nothing */ +#endif + +#define JS_ARENA_RELEASE(pool, mark) \ + JS_BEGIN_MACRO \ + char *_m = (char *)(mark); \ + JSArena *_a = (pool)->current; \ + if (_a != &(pool)->first && JS_IS_IN_ARENA(_a, _m)) { \ + _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ + JS_ASSERT(_a->avail <= _a->limit); \ + JS_CLEAR_UNUSED(_a); \ + } else { \ + JS_ArenaRelease(pool, _m); \ + } \ + JS_END_MACRO + +#define JS_ARENA_DESTROY(pool, a, pnext) \ + JS_BEGIN_MACRO \ + JS_COUNT_ARENA(pool,--); \ + if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ + *(pnext) = (a)->next; \ + JS_CLEAR_ARENA(a); \ + js::UnwantedForeground::free_(a); \ + (a) = NULL; \ + JS_END_MACRO + +/* + * Initialize an arena pool with a minimum size per arena of |size| bytes. + * |align| must be 1, 2, 4 or 8. + */ +extern JS_PUBLIC_API(void) +JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, + size_t align); + +/* + * Free the arenas in pool. The user may continue to allocate from pool + * after calling this function. There is no need to call JS_InitArenaPool() + * again unless JS_FinishArenaPool(pool) has been called. + */ +extern JS_PUBLIC_API(void) +JS_FreeArenaPool(JSArenaPool *pool); + +/* + * Free the arenas in pool and finish using it altogether. + */ +extern JS_PUBLIC_API(void) +JS_FinishArenaPool(JSArenaPool *pool); + +/* + * Deprecated do-nothing function. + */ +extern JS_PUBLIC_API(void) +JS_ArenaFinish(void); + +/* + * Deprecated do-nothing function. + */ +extern JS_PUBLIC_API(void) +JS_ArenaShutDown(void); + +/* + * Friend functions used by the JS_ARENA_*() macros. + */ +extern JS_PUBLIC_API(void *) +JS_ArenaAllocate(JSArenaPool *pool, size_t nb); + +extern JS_PUBLIC_API(void *) +JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); + +extern JS_PUBLIC_API(void *) +JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); + +extern JS_PUBLIC_API(void) +JS_ArenaRelease(JSArenaPool *pool, char *mark); + +JS_END_EXTERN_C + +#ifdef __cplusplus + +namespace js { + +template +inline T * +ArenaArray(JSArenaPool &pool, unsigned count) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, count * sizeof(T)); + return (T *) v; +} + +template +inline T * +ArenaNew(JSArenaPool &pool) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T() : NULL; +} + +template +inline T * +ArenaNew(JSArenaPool &pool, const A &a) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T(a) : NULL; +} + +template +inline T * +ArenaNew(JSArenaPool &pool, const A &a, const B &b) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T(a, b) : NULL; +} + +template +inline T * +ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T(a, b, c) : NULL; +} + +template +inline T * +ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T(a, b, c, d) : NULL; +} + +template +inline T * +ArenaNew(JSArenaPool &pool, const A &a, const B &b, const C &c, const D &d, const E &e) +{ + void *v; + JS_ARENA_ALLOCATE(v, &pool, sizeof(T)); + return v ? new (v) T(a, b, c, d, e) : NULL; +} + +inline uintN +ArenaAllocatedSize(const JSArenaPool &pool) +{ + uintN res = 0; + const JSArena *a = &pool.first; + while (a) { + res += (a->limit - (jsuword)a); + a = a->next; + } + return res; +} + +/* Move the contents of oldPool into newPool, and reset oldPool. */ +inline void +MoveArenaPool(JSArenaPool *oldPool, JSArenaPool *newPool) +{ + *newPool = *oldPool; + JS_InitArenaPool(oldPool, NULL, newPool->arenasize, newPool->mask + 1); +} + +} /* namespace js */ + +#endif /* __cplusplus */ + +#endif /* jsarena_h___ */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 1c545b71824..52ebdc41f60 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -57,6 +57,7 @@ #include "jsstdint.h" #include "jstypes.h" +#include "jsarena.h" #include "jsutil.h" #include "jsclist.h" #include "jsprf.h" @@ -111,7 +112,6 @@ ThreadData::ThreadData() maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE), #endif waiveGCQuota(false), - tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), dtoaState(NULL), nativeStackBase(GetNativeStackBase()), pendingProxyOperation(NULL), @@ -316,6 +316,9 @@ js_PurgeThreads(JSContext *cx) #endif } +static const size_t ARENA_HEADER_SIZE_HACK = 40; +static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK; + JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { @@ -342,6 +345,9 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize) JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT); VOUCH_DOES_NOT_REQUIRE_STACK(); + JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble)); + JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int)); + JS_ASSERT(cx->resolveFlags == 0); if (!cx->busyArrays.init()) { @@ -1367,6 +1373,9 @@ JSContext::~JSContext() if (parseMapPool_) Foreground::delete_(parseMapPool_); + JS_FinishArenaPool(®ExpPool); + JS_FinishArenaPool(&tempPool); + if (lastMessage) Foreground::free_(lastMessage); @@ -1493,9 +1502,25 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) return NULL; } +/* + * Release pool's arenas if the stackPool has existed for longer than the + * limit specified by gcEmptyArenaPoolLifespan. + */ +static void +FreeOldArenas(JSRuntime *rt, JSArenaPool *pool) +{ + JSArena *a = pool->current; + if (a == pool->first.next && a->avail == a->base + sizeof(int64)) { + int64 age = JS_Now() - *(int64 *) a->base; + if (age > int64(rt->gcEmptyArenaPoolLifespan) * 1000) + JS_FreeArenaPool(pool); + } +} + void JSContext::purge() { + FreeOldArenas(runtime, ®ExpPool); if (!activeCompilations) { Foreground::delete_(parseMapPool_); parseMapPool_ = NULL; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 12c98871c82..3a8b0be74e9 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -46,6 +46,7 @@ #include #include "jsprvtd.h" +#include "jsarena.h" #include "jsclist.h" #include "jsatom.h" #include "jsdhash.h" @@ -63,7 +64,6 @@ #include "jsvector.h" #include "prmjtime.h" -#include "ds/LifoAlloc.h" #include "vm/Stack.h" #include "vm/String.h" @@ -190,10 +190,6 @@ struct ThreadData { */ bool waiveGCQuota; - /* Temporary arena pool used while compiling and decompiling. */ - static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; - LifoAlloc tempLifoAlloc; - /* * The GSN cache is per thread since even multi-cx-per-thread embeddings * do not interleave js_GetSrcNote calls. @@ -228,7 +224,6 @@ struct ThreadData { } void purge(JSContext *cx) { - tempLifoAlloc.freeUnused(); gsnCache.purge(); /* FIXME: bug 506341. */ @@ -999,11 +994,17 @@ struct JSContext /* Wrap cx->exception for the current compartment. */ void wrapPendingException(); + /* Temporary arena pool used while compiling and decompiling. */ + JSArenaPool tempPool; + private: /* Lazily initialized pool of maps used during parse/emit. */ js::ParseMapPool *parseMapPool_; public: + /* Temporary arena pool used while evaluate regular expressions. */ + JSArenaPool regExpPool; + /* Top-level object and pointer to top stack frame's scope chain. */ JSObject *globalObject; @@ -1143,9 +1144,6 @@ struct JSContext bool hasWErrorOption() const { return hasRunOption(JSOPTION_WERROR); } bool hasAtLineOption() const { return hasRunOption(JSOPTION_ATLINE); } - js::LifoAlloc &tempLifoAlloc() { return JS_THREAD_DATA(this)->tempLifoAlloc; } - inline js::LifoAlloc &typeLifoAlloc(); - #ifdef JS_THREADSAFE private: JSThread *thread_; @@ -1879,6 +1877,28 @@ class AutoKeepAtoms { ~AutoKeepAtoms() { JS_UNKEEP_ATOMS(rt); } }; +class AutoArenaAllocator { + JSArenaPool *pool; + void *mark; + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + + public: + explicit AutoArenaAllocator(JSArenaPool *pool + JS_GUARD_OBJECT_NOTIFIER_PARAM) + : pool(pool), mark(JS_ARENA_MARK(pool)) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + ~AutoArenaAllocator() { JS_ARENA_RELEASE(pool, mark); } + + template + T *alloc(size_t elems) { + void *ptr; + JS_ARENA_ALLOCATE(ptr, pool, elems * sizeof(T)); + return static_cast(ptr); + } +}; + class AutoReleasePtr { JSContext *cx; void *ptr; diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 9395f955dd9..cbc21b6a1c4 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -407,12 +407,6 @@ inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment() } #endif -inline js::LifoAlloc & -JSContext::typeLifoAlloc() -{ - return compartment->typeLifoAlloc; -} - inline bool JSContext::ensureGeneratorStackSpace() { diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d2279d210cb..be8451a6411 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -74,7 +74,6 @@ JSCompartment::JSCompartment(JSRuntime *rt) gcTriggerBytes(0), gcLastBytes(0), hold(false), - typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), #ifdef JS_TRACER traceMonitor_(NULL), #endif @@ -133,6 +132,11 @@ JSCompartment::init(JSContext *cx) activeAnalysis = activeInference = false; types.init(cx); + /* Duplicated from jscntxt.cpp. :XXX: bug 675150 fix hack. */ + static const size_t ARENA_HEADER_SIZE_HACK = 40; + + JS_InitArenaPool(&pool, "analysis", 4096 - ARENA_HEADER_SIZE_HACK, 8); + if (!crossCompartmentWrappers.init()) return false; @@ -618,8 +622,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) * Clear the analysis pool, but don't release its data yet. While * sweeping types any live data will be allocated into the pool. */ - LifoAlloc oldAlloc(typeLifoAlloc.defaultChunkSize()); - oldAlloc.steal(&typeLifoAlloc); + JSArenaPool oldPool; + MoveArenaPool(&pool, &oldPool); /* * Sweep analysis information and everything depending on it from the @@ -652,6 +656,9 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) JSScript *script = i.get(); script->clearAnalysis(); } + + /* Reset the analysis pool, releasing all analysis and intermediate type data. */ + JS_FinishArenaPool(&oldPool); } active = false; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index c85d8f99c48..cfd240ac396 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -408,8 +408,7 @@ struct JS_FRIEND_API(JSCompartment) { * Cleared on every GC, unless the GC happens during analysis (indicated * by activeAnalysis, which is implied by activeInference). */ - static const size_t TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; - js::LifoAlloc typeLifoAlloc; + JSArenaPool pool; bool activeAnalysis; bool activeInference; diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 4191f41e4ef..ffd1e4e04f6 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -485,9 +485,10 @@ JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp) return NULL; /* Munge data into the API this method implements. Avert your eyes! */ - *markp = cx->tempLifoAlloc().mark(); + *markp = JS_ARENA_MARK(&cx->tempPool); - jsuword *names = cx->tempLifoAlloc().newArray(localNames.length()); + jsuword *names; + JS_ARENA_ALLOCATE_CAST(names, jsuword *, &cx->tempPool, localNames.length() * sizeof *names); if (!names) { js_ReportOutOfMemory(cx); return NULL; @@ -512,7 +513,7 @@ JS_AtomKey(JSAtom *atom) extern JS_PUBLIC_API(void) JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mark) { - cx->tempLifoAlloc().release(mark); + JS_ARENA_RELEASE(&cx->tempPool, mark); } JS_PUBLIC_API(JSScript *) @@ -2166,9 +2167,9 @@ JS_PUBLIC_API(void) JS_DumpBytecode(JSContext *cx, JSScript *script) { #if defined(DEBUG) - LifoAlloc lifoAlloc(1024); + AutoArenaAllocator mark(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); js_Disassemble(cx, script, true, &sprinter); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 23eec7d1859..fb3ff20d365 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -48,6 +48,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsbit.h" #include "jsprf.h" @@ -74,7 +75,6 @@ #include "jsscriptinlines.h" #include "frontend/ParseMaps-inl.h" -#include "ds/LifoAlloc.h" /* Allocation chunk counts, must be powers of two in general. */ #define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ @@ -515,7 +515,8 @@ AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) if (jt) { cg->jtFreeList = jt->kids[JT_LEFT]; } else { - jt = args->cx->tempLifoAlloc().new_(); + JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, + sizeof *jt); if (!jt) { js_ReportOutOfMemory(args->cx); return 0; @@ -3311,7 +3312,9 @@ EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) static Value * AllocateSwitchConstant(JSContext *cx) { - return cx->tempLifoAlloc().new_(); + Value *pv; + JS_ARENA_ALLOCATE_TYPE(pv, Value, &cx->tempPool); + return pv; } /* @@ -7749,12 +7752,14 @@ static JSBool NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind, uintN stackDepth, size_t start, size_t end) { + JSTryNode *tryNode; + JS_ASSERT((uintN)(uint16)stackDepth == stackDepth); JS_ASSERT(start <= end); JS_ASSERT((size_t)(uint32)start == start); JS_ASSERT((size_t)(uint32)end == end); - JSTryNode *tryNode = cx->tempLifoAlloc().new_(); + JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool); if (!tryNode) { js_ReportOutOfMemory(cx); return JS_FALSE; diff --git a/js/src/jsemit.h b/js/src/jsemit.h index c5295a538b3..dc3cfb16ecd 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -668,9 +668,11 @@ struct JSCodeGenerator : public JSTreeContext } /* - * Note that cgs are magic: they own the arena "top-of-stack" space above - * their tempMark points. This means that you cannot alloc from tempPool - * and save the pointer beyond the next JSCodeGenerator destructor call. + * Release cg->codePool, cg->notePool, and parser->context->tempPool to + * marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own + * the arena pool "tops-of-stack" space above their codeMark, noteMark, and + * tempMark points. This means you cannot alloc from tempPool and save the + * pointer beyond the next JSCodeGenerator destructor call. */ ~JSCodeGenerator(); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index ca48ca69f16..dd33c6db8b1 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2181,8 +2181,8 @@ Function(JSContext *cx, uintN argc, Value *vp) * for a terminating 0. Mark cx->tempPool for later release, to free * collected_args and its tokenstream in one swoop. */ - LifoAllocScope las(&cx->tempLifoAlloc()); - jschar *cp = cx->tempLifoAlloc().newArray(args_length + 1); + AutoArenaAllocator aaa(&cx->tempPool); + jschar *cp = aaa.alloc(args_length + 1); if (!cp) { js_ReportOutOfMemory(cx); return false; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index c1ff7ec927f..e79d8ad5ea9 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -352,7 +352,7 @@ TypeSet::make(JSContext *cx, const char *name) { JS_ASSERT(cx->compartment->activeInference); - TypeSet *res = cx->typeLifoAlloc().new_(); + TypeSet *res = ArenaNew(cx->compartment->pool); if (!res) { cx->compartment->types.setPendingNukeTypes(cx); return NULL; @@ -492,7 +492,7 @@ public: void TypeSet::addSubset(JSContext *cx, TypeSet *target) { - add(cx, cx->typeLifoAlloc().new_(target)); + add(cx, ArenaNew(cx->compartment->pool, target)); } /* Constraints for reads/writes on object properties. */ @@ -527,14 +527,14 @@ void TypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, jsid id) { - add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, false)); + add(cx, ArenaNew(cx->compartment->pool, script, pc, target, id, false)); } void TypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, jsid id) { - add(cx, cx->typeLifoAlloc().new_(script, pc, target, id, true)); + add(cx, ArenaNew(cx->compartment->pool, script, pc, target, id, true)); } /* @@ -575,7 +575,7 @@ TypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid i if (JSOp(*callpc) == JSOP_NEW) return; - add(cx, cx->typeLifoAlloc().new_(script, callpc, id)); + add(cx, ArenaNew(cx->compartment->pool, script, callpc, id)); } /* @@ -608,8 +608,8 @@ void TypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *objectTypes, TypeSet *valueTypes) { - add(cx, cx->typeLifoAlloc().new_(script, pc, objectTypes, - valueTypes)); + add(cx, ArenaNew(cx->compartment->pool, script, pc, + objectTypes, valueTypes)); } /* @@ -633,7 +633,7 @@ public: void TypeSet::addCall(JSContext *cx, TypeCallsite *site) { - add(cx, cx->typeLifoAlloc().new_(site)); + add(cx, ArenaNew(cx->compartment->pool, site)); } /* Constraints for arithmetic operations. */ @@ -658,7 +658,7 @@ public: void TypeSet::addArith(JSContext *cx, TypeSet *target, TypeSet *other) { - add(cx, cx->typeLifoAlloc().new_(target, other)); + add(cx, ArenaNew(cx->compartment->pool, target, other)); } /* Subset constraint which transforms primitive values into appropriate objects. */ @@ -678,7 +678,7 @@ public: void TypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target) { - add(cx, cx->typeLifoAlloc().new_(script, target)); + add(cx, ArenaNew(cx->compartment->pool, script, target)); } /* @@ -709,7 +709,7 @@ TypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc, Type if (JSOp(*callpc) == JSOP_NEW) return; - add(cx, cx->typeLifoAlloc().new_(script, callpc, type, types)); + add(cx, ArenaNew(cx->compartment->pool, script, callpc, type, types)); } /* Subset constraint which filters out primitive types. */ @@ -752,7 +752,7 @@ public: void TypeSet::addFilterPrimitives(JSContext *cx, TypeSet *target, FilterKind filter) { - add(cx, cx->typeLifoAlloc().new_(target, filter)); + add(cx, ArenaNew(cx->compartment->pool, target, filter)); } /* If id is a normal slotful 'own' property of an object, get its shape. */ @@ -876,7 +876,7 @@ public: void TypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target) { - add(cx, cx->typeLifoAlloc().new_(script, pc, target)); + add(cx, ArenaNew(cx->compartment->pool, script, pc, target)); } /* @@ -904,7 +904,7 @@ public: void TypeSet::addLazyArguments(JSContext *cx, TypeSet *target) { - add(cx, cx->typeLifoAlloc().new_(target)); + add(cx, ArenaNew(cx->compartment->pool, target)); } ///////////////////////////////////////////////////////////////////// @@ -1086,8 +1086,9 @@ TypeConstraintCallProp::newType(JSContext *cx, TypeSet *source, Type type) if (!types->hasPropagatedProperty()) object->getFromPrototypes(cx, id, types); /* Bypass addPropagateThis, we already have the callpc. */ - types->add(cx, cx->typeLifoAlloc().new_( - script, callpc, type, (TypeSet *) NULL)); + types->add(cx, ArenaNew(cx->compartment->pool, + script, callpc, type, + (TypeSet *) NULL)); } } } @@ -1388,8 +1389,8 @@ public: void TypeSet::addFreeze(JSContext *cx) { - add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript), false); + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript), false); } /* @@ -1472,8 +1473,8 @@ TypeSet::getKnownTypeTag(JSContext *cx) JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN); if (cx->compartment->types.compiledScript && (empty || type != JSVAL_TYPE_UNKNOWN)) { - add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript), false); + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript), false); } return type; @@ -1552,8 +1553,9 @@ public: TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); if (!types) return; - types->add(cx, cx->typeLifoAlloc().new_( - script, flags, &marked), false); + types->add(cx, + ArenaNew(cx->compartment->pool, + script, flags, &marked), false); return; } } else { @@ -1594,8 +1596,8 @@ TypeSet::hasObjectFlags(JSContext *cx, TypeObjectFlags flags) * Watch for new objects of different kind, and re-traverse existing types * in this set to add any needed FreezeArray constraints. */ - add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript, flags)); + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, flags)); return false; } @@ -1609,8 +1611,9 @@ TypeSet::HasObjectFlags(JSContext *cx, TypeObject *object, TypeObjectFlags flags TypeSet *types = object->getProperty(cx, JSID_EMPTY, false); if (!types) return true; - types->add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript, flags), false); + types->add(cx, + ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, flags), false); return false; } @@ -1691,9 +1694,9 @@ TypeSet::WatchObjectStateChange(JSContext *cx, TypeObject *obj) * Use a constraint which triggers recompilation when markStateChange is * called, which will set 'force' to true. */ - types->add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript, - 0)); + types->add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, + 0)); } class TypeConstraintFreezeOwnProperty : public TypeConstraint @@ -1746,7 +1749,7 @@ TypeSet::isOwnProperty(JSContext *cx, TypeObject *object, bool configurable) if (isOwnProperty(configurable)) return true; - add(cx, cx->typeLifoAlloc().new_( + add(cx, ArenaNew(cx->compartment->pool, cx->compartment->types.compiledScript, configurable), false); return false; @@ -1758,8 +1761,8 @@ TypeSet::knownNonEmpty(JSContext *cx) if (baseFlags() != 0 || baseObjectCount() != 0) return true; - add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript), false); + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript), false); return false; } @@ -1818,7 +1821,7 @@ TypeSet::getSingleton(JSContext *cx, bool freeze) return NULL; if (freeze) { - add(cx, cx->typeLifoAlloc().new_( + add(cx, ArenaNew(cx->compartment->pool, cx->compartment->types.compiledScript), false); } @@ -1876,8 +1879,9 @@ TypeSet::hasGlobalObject(JSContext *cx, JSObject *global) return false; } - add(cx, cx->typeLifoAlloc().new_( - cx->compartment->types.compiledScript, global), false); + add(cx, ArenaNew(cx->compartment->pool, + cx->compartment->types.compiledScript, + global), false); return true; } @@ -2305,7 +2309,8 @@ ScriptAnalysis::addTypeBarrier(JSContext *cx, const jsbytecode *pc, TypeSet *tar InferSpewColor(target), target, InferSpewColorReset(), TypeString(type)); - barrier = cx->typeLifoAlloc().new_(target, type, (JSObject *) NULL, JSID_VOID); + barrier = ArenaNew(cx->compartment->pool, target, type, + (JSObject *) NULL, JSID_VOID); barrier->next = code.typeBarriers; code.typeBarriers = barrier; @@ -2330,7 +2335,8 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ InferSpewColor(target), target, InferSpewColorReset(), (void *) singleton, TypeIdString(singletonId)); - TypeBarrier *barrier = cx->typeLifoAlloc().new_(target, Type::UndefinedType(), + TypeBarrier *barrier = + ArenaNew(cx->compartment->pool, target, Type::UndefinedType(), singleton, singletonId); barrier->next = code.typeBarriers; @@ -2695,7 +2701,7 @@ bool TypeObject::addProperty(JSContext *cx, jsid id, Property **pprop) { JS_ASSERT(!*pprop); - Property *base = cx->typeLifoAlloc().new_(id); + Property *base = ArenaNew(cx->compartment->pool, id); if (!base) { cx->compartment->types.setPendingNukeTypes(cx); return false; @@ -3245,7 +3251,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, if (ExtendedDef(pc)) defCount++; - TypeSet *pushed = cx->typeLifoAlloc().newArrayUninitialized(defCount); + TypeSet *pushed = ArenaArray(cx->compartment->pool, defCount); if (!pushed) return false; PodZero(pushed, defCount); @@ -3768,7 +3774,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, /* Construct the base call information about this site. */ unsigned argCount = GetUseCount(script, offset) - 2; - TypeCallsite *callsite = cx->typeLifoAlloc().new_( + TypeCallsite *callsite = ArenaNew(cx->compartment->pool, cx, script, pc, op == JSOP_NEW, argCount); if (!callsite || (argCount && !callsite->argumentTypes)) { cx->compartment->types.setPendingNukeTypes(cx); @@ -4490,7 +4496,8 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO if (!parentTypes || parentTypes->unknown()) return false; parentObject->getFromPrototypes(cx, id, parentTypes); - parentTypes->add(cx, cx->typeLifoAlloc().new_(type)); + parentTypes->add(cx, + ArenaNew(cx->compartment->pool, type)); } else if (op == JSOP_FUNCALL && uses->u.which == GET_ARGC(pc) - 1) { /* * Passed as the first parameter to Function.call. Follow control @@ -4539,9 +4546,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO * should the Function.call or callee itself change in the future. */ analysis->pushedTypes(calleev.pushedOffset(), 0)->add(cx, - cx->typeLifoAlloc().new_(type)); + ArenaNew(cx->compartment->pool, type)); analysis->pushedTypes(calleev.pushedOffset(), 1)->add(cx, - cx->typeLifoAlloc().new_(type)); + ArenaNew(cx->compartment->pool, type)); TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset); if (!initializerList->append(pushframe)) { @@ -5417,7 +5424,7 @@ JSScript::makeAnalysis(JSContext *cx) AutoEnterAnalysis enter(cx); - types->analysis = cx->typeLifoAlloc().new_(this); + types->analysis = ArenaNew(cx->compartment->pool, this); if (!types->analysis) return false; @@ -5808,7 +5815,7 @@ TypeObject::sweep(JSContext *cx) for (unsigned i = 0; i < oldCapacity; i++) { Property *prop = oldArray[i]; if (prop && prop->types.isOwnProperty(false)) { - Property *newProp = compartment->typeLifoAlloc.new_(*prop); + Property *newProp = ArenaNew(compartment->pool, *prop); if (newProp) { Property **pentry = HashSetInsert @@ -5828,7 +5835,7 @@ TypeObject::sweep(JSContext *cx) } else if (propertyCount == 1) { Property *prop = (Property *) propertySet; if (prop->types.isOwnProperty(false)) { - Property *newProp = compartment->typeLifoAlloc.new_(*prop); + Property *newProp = ArenaNew(compartment->pool, *prop); if (newProp) { propertySet = (Property **) newProp; newProp->types.sweep(cx, compartment); @@ -6102,7 +6109,7 @@ JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment, * by being copied to the replacement pool. This memory will be counted too * and deducted from the amount of temporary data. */ - stats->temporary += compartment->typeLifoAlloc.used(); + stats->temporary += ArenaAllocatedSize(compartment->pool); /* Pending arrays are cleared on GC along with the analysis pool. */ stats->temporary += sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity; diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index dc1a731c297..f36d3795a76 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -43,16 +43,29 @@ #define jsinfer_h___ #include "jsalloc.h" +#include "jsarena.h" #include "jscell.h" #include "jstl.h" #include "jsprvtd.h" #include "jshashtable.h" -#include "ds/LifoAlloc.h" +namespace js { + class CallArgs; + namespace analyze { + class ScriptAnalysis; + } + class GlobalObject; +} namespace js { namespace types { +/* Forward declarations. */ +class TypeSet; +struct TypeCallsite; +struct TypeObject; +struct TypeCompartment; + /* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */ struct TypeObjectKey { static intptr_t keyBits(TypeObjectKey *obj) { return (intptr_t) obj; } diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index a45bc87adb8..8253f6ebcac 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -820,7 +820,7 @@ HashSetInsertTry(JSCompartment *compartment, U **&values, unsigned &count, T key return &values[insertpos]; } - U **newValues = compartment->typeLifoAlloc.newArray(newCapacity); + U **newValues = ArenaArray(compartment->pool, newCapacity); if (!newValues) return NULL; PodZero(newValues, newCapacity); @@ -861,7 +861,7 @@ HashSetInsert(JSCompartment *compartment, U **&values, unsigned &count, T key) if (KEY::getKey(oldData) == key) return (U **) &values; - values = compartment->typeLifoAlloc.newArray(SET_ARRAY_SIZE); + values = ArenaArray(compartment->pool, SET_ARRAY_SIZE); if (!values) { values = (U **) oldData; return NULL; @@ -1097,7 +1097,7 @@ TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc, thisTypes(NULL), returnTypes(NULL) { /* Caller must check for failure. */ - argumentTypes = cx->typeLifoAlloc().newArray(argumentCount); + argumentTypes = ArenaArray(cx->compartment->pool, argumentCount); } ///////////////////////////////////////////////////////////////////// diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 57ec91c0238..5ba1fa86ac9 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -46,6 +46,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 822cae80ca8..cddaae5021f 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -45,6 +45,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" +#include "jsarena.h" #include "jsapi.h" #include "jsarray.h" #include "jsatom.h" diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a7b0594edaa..0b7c1df0e57 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -45,6 +45,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsbit.h" #include "jsutil.h" #include "jshash.h" diff --git a/js/src/json.cpp b/js/src/json.cpp index 3816c03faaa..84b3d916601 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -41,6 +41,7 @@ #include #include "jsapi.h" +#include "jsarena.h" #include "jsarray.h" #include "jsatom.h" #include "jsbool.h" diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 0bbd462797d..18a4975fe5b 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -50,6 +50,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jsapi.h" @@ -318,7 +319,9 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc else SprintCString(sp, " "); } - len = js_Disassemble1(cx, script, next, next - script->code, lines, sp); + len = js_Disassemble1(cx, script, next, + next - script->code, + lines, sp); if (!len) return JS_FALSE; next += len; @@ -335,22 +338,24 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp) JS_FRIEND_API(JSBool) js_DumpPC(JSContext *cx) { - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter); fprintf(stdout, "%s", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } JSBool js_DumpScript(JSContext *cx, JSScript *script) { - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); JSBool ok = js_Disassemble(cx, script, true, &sprinter); fprintf(stdout, "%s", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } @@ -362,13 +367,13 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) { if (JSVAL_IS_STRING(v)) { Sprinter sprinter; - LifoAlloc &tla = cx->tempLifoAlloc(); - LifoAllocScope las(&tla); - INIT_SPRINTER(cx, &sprinter, &tla, 0); + void *mark = JS_ARENA_MARK(&cx->tempPool); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"'); if (!nbytes) return false; nbytes = JS_sprintf_append(NULL, "%s", nbytes); + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!nbytes) return false; bytes->initBytes(nbytes); @@ -682,10 +687,11 @@ SprintEnsureBuffer(Sprinter *sp, size_t len) if (nb < 0) return JS_TRUE; base = sp->base; - if (!base) - base = static_cast(sp->pool->allocUnaligned(nb)); - else - base = static_cast(sp->pool->reallocUnaligned(base, sp->size, nb)); + if (!base) { + JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); + } else { + JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); + } if (!base) { js_ReportOutOfMemory(sp->context); return JS_FALSE; @@ -870,11 +876,16 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote) { - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark; Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); - char *bytes = QuoteString(&sprinter, str, quote); - JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; + char *bytes; + JSString *escstr; + + mark = JS_ARENA_MARK(&cx->tempPool); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + bytes = QuoteString(&sprinter, str, quote); + escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; + JS_ARENA_RELEASE(&cx->tempPool, mark); return escstr; } @@ -882,7 +893,7 @@ js_QuoteString(JSContext *cx, JSString *str, jschar quote) struct JSPrinter { Sprinter sprinter; /* base class state */ - LifoAlloc pool; /* string allocation pool */ + JSArenaPool pool; /* string allocation pool */ uintN indent; /* indentation in spaces */ bool pretty; /* pretty-print: indent, use newlines */ bool grouped; /* in parenthesized expression context */ @@ -898,11 +909,13 @@ JSPrinter * js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, uintN indent, JSBool pretty, JSBool grouped, JSBool strict) { - JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); + JSPrinter *jp; + + jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); if (!jp) return NULL; INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - new (&jp->pool) LifoAlloc(1024); + JS_InitArenaPool(&jp->pool, name, 256, 1); jp->indent = indent; jp->pretty = !!pretty; jp->grouped = !!grouped; @@ -925,7 +938,7 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, void js_DestroyPrinter(JSPrinter *jp) { - jp->pool.freeAll(); + JS_FinishArenaPool(&jp->pool); Foreground::delete_(jp->localNames); jp->sprinter.context->free_(jp); } @@ -942,7 +955,7 @@ js_GetPrinterOutput(JSPrinter *jp) str = JS_NewStringCopyZ(cx, jp->sprinter.base); if (!str) return NULL; - jp->pool.freeAll(); + JS_FreeArenaPool(&jp->pool); INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); return str; } @@ -1893,12 +1906,15 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempLifoAlloc(), PAREN_SLOP); + size_t offsetsz, opcodesz; + void *space; + + INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - size_t offsetsz = depth * sizeof(ptrdiff_t); - size_t opcodesz = depth * sizeof(jsbytecode); - void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz); + offsetsz = depth * sizeof(ptrdiff_t); + opcodesz = depth * sizeof(jsbytecode); + JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); if (!space) { js_ReportOutOfMemory(cx); return JS_FALSE; @@ -4048,6 +4064,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) #if JS_HAS_GENERATOR_EXPRS sn = js_GetSrcNote(jp->script, pc); if (sn && SN_TYPE(sn) == SRC_GENEXP) { + void *mark; Vector *innerLocalNames; Vector *outerLocalNames; JSScript *inner, *outer; @@ -4062,7 +4079,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * Therefore after InitSprintStack succeeds, we must * release to mark before returning. */ - LifoAllocScope las(&cx->tempLifoAlloc()); + mark = JS_ARENA_MARK(&cx->tempPool); if (fun->script()->bindings.hasLocalNames()) { innerLocalNames = cx->new_ >(cx); if (!innerLocalNames || @@ -4074,8 +4091,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) innerLocalNames = NULL; } inner = fun->script(); - if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) + if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) { + JS_ARENA_RELEASE(&cx->tempPool, mark); return NULL; + } ss2.inGenExp = JS_TRUE; /* @@ -4103,8 +4122,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) jp->script = outer; jp->fun = outerfun; jp->localNames = outerLocalNames; - if (!ok) + if (!ok) { + JS_ARENA_RELEASE(&cx->tempPool, mark); return NULL; + } /* * Advance over this op and its global |this| push, and @@ -4174,7 +4195,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * from cx's tempPool. */ rval = JS_strdup(cx, PopStr(&ss2, op)); - las.releaseEarly(); + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!rval) return NULL; todo = SprintCString(&ss->sprinter, rval); @@ -4812,6 +4833,8 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, uintN depth, i; SprintStack ss; JSContext *cx; + void *mark; + JSBool ok; JSScript *oldscript; char *last; @@ -4822,9 +4845,10 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, AutoScriptUntrapper untrapper(cx, script, &pc); /* Initialize a sprinter for use with the offset stack. */ - LifoAllocScope las(&cx->tempLifoAlloc()); - if (!InitSprintStack(cx, &ss, jp, depth)) - return false; + mark = JS_ARENA_MARK(&cx->tempPool); + ok = InitSprintStack(cx, &ss, jp, depth); + if (!ok) + goto out; /* * If we are called from js_DecompileValueGenerator with a portion of @@ -4848,7 +4872,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* Call recursive subroutine to do the hard work. */ oldscript = jp->script; jp->script = script; - bool ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; + ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ @@ -4859,6 +4883,9 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, js_printf(jp, "%s", last); } +out: + /* Free all temporary stuff allocated under this call. */ + JS_ARENA_RELEASE(&cx->tempPool, mark); return ok; } @@ -4971,6 +4998,7 @@ js_DecompileFunction(JSPrinter *jp) JSScript *script = fun->script(); #if JS_HAS_DESTRUCTURING SprintStack ss; + void *mark; #endif /* Print the parameters. */ @@ -4982,7 +5010,7 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING ss.printer = NULL; jp->script = script; - LifoAllocScope las(&jp->sprinter.context->tempLifoAlloc()); + mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool); #endif for (i = 0; i < fun->nargs; i++) { @@ -5033,7 +5061,7 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING jp->script = NULL; - las.releaseEarly(); + JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark); #endif if (!ok) return JS_FALSE; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 67a4b92606e..89107d97dbe 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -46,6 +46,7 @@ #include "jsprvtd.h" #include "jspubtd.h" #include "jsutil.h" +#include "jsarena.h" JS_BEGIN_EXTERN_C @@ -504,7 +505,7 @@ DecompileValueGenerator(JSContext *cx, intN spindex, const Value &v, */ struct Sprinter { JSContext *context; /* context executing the decompiler */ - LifoAlloc *pool; /* string allocation pool */ + JSArenaPool *pool; /* string allocation pool */ char *base; /* base address of buffer in pool */ size_t size; /* size of buffer allocated at base */ ptrdiff_t offset; /* offset of next free char in buffer */ diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index dd741e026ac..7b5616fb7e4 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -56,6 +56,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsapi.h" #include "jsarray.h" @@ -208,9 +209,9 @@ Parser::init(const jschar *base, size_t length, const char *filename, uintN line JSContext *cx = context; if (!cx->ensureParseMapPool()) return false; - tempPoolMark = cx->tempLifoAlloc().mark(); + tempPoolMark = JS_ARENA_MARK(&cx->tempPool); if (!tokenStream.init(base, length, filename, lineno, version)) { - cx->tempLifoAlloc().release(tempPoolMark); + JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); return false; } return true; @@ -222,7 +223,7 @@ Parser::~Parser() if (principals) JSPRINCIPALS_DROP(cx, principals); - cx->tempLifoAlloc().release(tempPoolMark); + JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark); cx->activeCompilations--; } @@ -246,7 +247,8 @@ Parser::newObjectBox(JSObject *obj) * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ - JSObjectBox *objbox = context->tempLifoAlloc().new_(); + JSObjectBox *objbox; + JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool); if (!objbox) { js_ReportOutOfMemory(context); return NULL; @@ -271,7 +273,8 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc) * containing the entries must be alive until we are done with scanning, * parsing and code generation for the whole script or top-level function. */ - JSFunctionBox *funbox = context->tempLifoAlloc().newPod(); + JSFunctionBox *funbox; + JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool); if (!funbox) { js_ReportOutOfMemory(context); return NULL; @@ -674,7 +677,8 @@ NewOrRecycledNode(JSTreeContext *tc) pn = tc->parser->nodeList; if (!pn) { JSContext *cx = tc->parser->context; - pn = cx->tempLifoAlloc().new_(); + + JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); if (!pn) js_ReportOutOfMemory(cx); } else { diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 57e1226e54b..705cdae0592 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -159,8 +159,6 @@ class UpvarCookie; class TempAllocPolicy; class RuntimeAllocPolicy; -class GlobalObject; - template @@ -185,8 +183,6 @@ template class InlineMap; -class LifoAlloc; - class PropertyCache; struct PropertyCacheEntry; @@ -214,26 +210,6 @@ typedef JSPropertyOp PropertyOp; typedef JSStrictPropertyOp StrictPropertyOp; typedef JSPropertyDescriptor PropertyDescriptor; -namespace analyze { - -struct LifetimeVariable; -class LoopAnalysis; -class ScriptAnalysis; -class SlotValue; -class SSAValue; -class SSAUseChain; - -} /* namespace analyze */ - -namespace types { - -class TypeSet; -struct TypeCallsite; -struct TypeObject; -struct TypeCompartment; - -} /* namespace types */ - } /* namespace js */ } /* export "C++" */ diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 4b3ad5f0987..d2dbf8d344e 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -149,6 +149,7 @@ class RegExp void reportPCREError(JSContext *cx, int error); #endif void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error); + static inline bool initArena(JSContext *cx); static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount); static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount); inline bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, @@ -254,6 +255,27 @@ class RegExpMatchBuilder /* RegExp inlines. */ +inline bool +RegExp::initArena(JSContext *cx) +{ + if (cx->regExpPool.first.next) + return true; + + /* + * The regular expression arena pool is special... we want to hang on to it + * until a GC is performed so rapid subsequent regexp executions don't + * thrash malloc/freeing arena chunks. + * + * Stick a timestamp at the base of that pool. + */ + int64 *timestamp; + JS_ARENA_ALLOCATE_CAST(timestamp, int64 *, &cx->regExpPool, sizeof *timestamp); + if (!timestamp) + return false; + *timestamp = JS_Now(); + return true; +} + inline void RegExp::checkMatchPairs(JSString *input, int *buf, size_t matchItemCount) { @@ -317,8 +339,11 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr, const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */ const size_t matchItemCount = pairCount * 2; - LifoAllocScope las(&cx->tempLifoAlloc()); - int *buf = cx->tempLifoAlloc().newArray(bufCount); + if (!initArena(cx)) + return false; + + AutoArenaAllocator aaa(&cx->regExpPool); + int *buf = aaa.alloc(bufCount); if (!buf) return false; diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 54f3e9e6300..338147b8098 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -54,6 +54,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsbit.h" #include "jsutil.h" #include "jsprf.h" diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index d8fdfcec992..00586eeeb7e 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -46,6 +46,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsbit.h" #include "jsclist.h" #include "jsdhash.h" diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 083714c9a45..d51ea6e3fbf 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -384,7 +384,14 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) Bindings bindings(cx); uint32 nameCount = nargs + nvars + nupvars; if (nameCount > 0) { - LifoAllocScope las(&cx->tempLifoAlloc()); + struct AutoMark { + JSArenaPool * const pool; + void * const mark; + AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { } + ~AutoMark() { + JS_ARENA_RELEASE(pool, mark); + } + } automark(&cx->tempPool); /* * To xdr the names we prefix the names with a bitmap descriptor and @@ -395,7 +402,9 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) * name is declared as const, not as ordinary var. * */ uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32); - uint32 *bitmap = cx->tempLifoAlloc().newArray(bitmapLength); + uint32 *bitmap; + JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool, + bitmapLength * sizeof *bitmap); if (!bitmap) { js_ReportOutOfMemory(cx); return false; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 7df2fcb8d95..595cc3e06cb 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -146,6 +146,8 @@ typedef struct JSConstArray { uint32 length; } JSConstArray; +struct JSArenaPool; + namespace js { struct GlobalSlotArray { diff --git a/js/src/jstl.h b/js/src/jstl.h index d62b45d6392..81cbc62a0be 100644 --- a/js/src/jstl.h +++ b/js/src/jstl.h @@ -40,7 +40,7 @@ #ifndef jstl_h_ #define jstl_h_ -#include "jsprvtd.h" +#include "jspubtd.h" #include "jsbit.h" #include "jsstaticcheck.h" #include "jsstdint.h" @@ -167,7 +167,6 @@ template <> struct IsPodType { static const bool result = template <> struct IsPodType { static const bool result = true; }; template <> struct IsPodType { static const bool result = true; }; template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; template struct IsPodType { static const bool result = true; }; /* Return the size/end of an array without using macros. */ diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 5fe8323f5bd..5d128f5bc2e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -7390,9 +7390,9 @@ TraceRecorder::monitorRecording(JSOp op) debug_only_stmt( if (LogController.lcbits & LC_TMRecorder) { - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); debug_only_print0(LC_TMRecorder, "\n"); js_Disassemble1(cx, cx->fp()->script(), cx->regs().pc, @@ -7401,6 +7401,7 @@ TraceRecorder::monitorRecording(JSOp op) !cx->fp()->hasImacropc(), &sprinter); fprintf(stdout, "%s", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); } ) @@ -10414,13 +10415,14 @@ TraceRecorder::record_EnterFrame() callDepth); debug_only_stmt( if (LogController.lcbits & LC_TMRecorder) { - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); js_Disassemble(cx, cx->fp()->script(), JS_TRUE, &sprinter); debug_only_printf(LC_TMTracer, "%s", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); debug_only_print0(LC_TMTracer, "----\n"); } ) diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 473f9add429..68de3a24b5a 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -70,8 +70,6 @@ using namespace mozilla; JS_BEGIN_EXTERN_C -#define JS_UPTRDIFF(a_, b_) (uintptr_t(a_) - uintptr_t(b_)) - #define JS_CRASH_UNLESS(__cond) \ JS_BEGIN_MACRO \ if (!(__cond)) { \ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 25a8ef41083..cff6d4e3fa2 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1462,12 +1462,13 @@ public: JS_BEGIN_MACRO \ if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \ JaegerSpew(JSpew_JSOps, " %2d ", frame.stackDepth()); \ - LifoAllocScope las(&cx->tempLifoAlloc()); \ + void *mark = JS_ARENA_MARK(&cx->tempPool); \ Sprinter sprinter; \ - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); \ + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); \ js_Disassemble1(cx, script, PC, PC - script->code, \ JS_TRUE, &sprinter); \ fprintf(stdout, "%s", sprinter.base); \ + JS_ARENA_RELEASE(&cx->tempPool, mark); \ } \ JS_END_MACRO; #else @@ -6682,7 +6683,7 @@ mjit::Compiler::jumpAndTrace(Jump j, jsbytecode *target, Jump *slow, bool *tramp if (cx->typeInferenceEnabled()) { RegisterAllocation *&alloc = analysis->getAllocation(target); if (!alloc) { - alloc = cx->typeLifoAlloc().new_(false); + alloc = ArenaNew(cx->compartment->pool, false); if (!alloc) return false; } diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index c0bd80da60d..6373a3101ae 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -575,7 +575,7 @@ RegisterAllocation * FrameState::computeAllocation(jsbytecode *target) { JS_ASSERT(cx->typeInferenceEnabled()); - RegisterAllocation *alloc = cx->typeLifoAlloc().new_(false); + RegisterAllocation *alloc = ArenaNew(cx->compartment->pool, false); if (!alloc) return NULL; @@ -853,7 +853,7 @@ FrameState::discardForJoin(RegisterAllocation *&alloc, uint32 stackDepth) * This shows up for loop entries which are not reachable from the * loop head, and for exception, switch target and trap safe points. */ - alloc = cx->typeLifoAlloc().new_(false); + alloc = ArenaNew(cx->compartment->pool, false); if (!alloc) return false; } diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index 4777446f2d7..e04d4abf513 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -157,7 +157,7 @@ LoopState::init(jsbytecode *head, Jump entry, jsbytecode *entryTarget) RegisterAllocation *&alloc = outerAnalysis->getAllocation(head); JS_ASSERT(!alloc); - alloc = cx->typeLifoAlloc().new_(true); + alloc = ArenaNew(cx->compartment->pool, true); if (!alloc) return false; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 56c478c6354..bab2454c7eb 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -50,6 +50,7 @@ #include #include "jstypes.h" #include "jsstdint.h" +#include "jsarena.h" #include "jsutil.h" #include "jsprf.h" #include "jswrapper.h" @@ -1954,13 +1955,16 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp) static JSBool Notes(JSContext *cx, uintN argc, jsval *vp) { - LifoAllocScope las(&cx->tempLifoAlloc()); + uintN i; + JSScript *script; + + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); jsval *argv = JS_ARGV(cx, vp); - for (uintN i = 0; i < argc; i++) { - JSScript *script = ValueToScript(cx, argv[i]); + for (i = 0; i < argc; i++) { + script = ValueToScript(cx, argv[i]); if (!script) continue; @@ -1968,6 +1972,7 @@ Notes(JSContext *cx, uintN argc, jsval *vp) } JSString *str = JS_NewStringCopyZ(cx, sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!str) return JS_FALSE; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2108,9 +2113,9 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); Sprinter *sp = &sprinter; bool ok = true; @@ -2134,6 +2139,7 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) } JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.base) : NULL; + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!str) return false; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2147,9 +2153,9 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); Sprinter *sp = &sprinter; bool ok = true; @@ -2174,6 +2180,7 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) if (ok) fprintf(stdout, "%s\n", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); JS_SET_RVAL(cx, vp, JSVAL_VOID); return ok; } @@ -2209,12 +2216,13 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp) if (!script) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter); if (ok) fprintf(stdout, "%s\n", sprinter.base); + JS_ARENA_RELEASE(&cx->tempPool, mark); if (!ok) return false; @@ -2258,17 +2266,18 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) pc = script->code; end = pc + script->length; - LifoAllocScope las(&cx->tempLifoAlloc()); + void *mark = JS_ARENA_MARK(&cx->tempPool); Sprinter sprinter; Sprinter *sp = &sprinter; - INIT_SPRINTER(cx, sp, &cx->tempLifoAlloc(), 0); + INIT_SPRINTER(cx, sp, &cx->tempPool, 0); /* burn the leading lines */ line2 = JS_PCToLineNumber(cx, script, pc); for (line1 = 0; line1 < line2 - 1; line1++) { char *tmp = fgets(linebuf, LINE_BUF_LEN, file); if (!tmp) { - JS_ReportError(cx, "failed to read %s fully", script->filename); + JS_ReportError(cx, "failed to read %s fully", + script->filename); ok = JS_FALSE; goto bail; } @@ -2309,6 +2318,7 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) } bail: + JS_ARENA_RELEASE(&cx->tempPool, mark); fclose(file); } JS_SET_RVAL(cx, vp, JSVAL_VOID); diff --git a/mfbt/Util.h b/mfbt/Util.h index 65b2e2c8ee9..8a343b2baae 100644 --- a/mfbt/Util.h +++ b/mfbt/Util.h @@ -183,7 +183,6 @@ struct DebugOnly DebugOnly& operator=(const T&) { return *this; } void operator++(int) {} void operator--(int) {} - bool operator<(const T&) { return false; } #endif /*