From 375f661a518dfb277c6219909561493884659ae8 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Sun, 31 Jul 2011 20:50:42 +0200 Subject: [PATCH 001/172] bug 684569 - remove support for custom chunk allocation. r=nnethercote --HG-- rename : js/src/jsapi-tests/testGCChunkAlloc.cpp => js/src/jsapi-tests/testGCOutOfMemory.cpp --- js/src/jsapi-tests/Makefile.in | 2 +- js/src/jsapi-tests/testGCChunkAlloc.cpp | 131 ----------------------- js/src/jsapi-tests/testGCOutOfMemory.cpp | 68 ++++++++++++ js/src/jsapi.cpp | 3 +- js/src/jscntxt.h | 8 -- js/src/jsgc.cpp | 4 +- js/src/jsgcchunk.cpp | 6 +- js/src/jsgcchunk.h | 36 +------ js/src/xpconnect/src/xpcjsruntime.cpp | 36 ------- 9 files changed, 76 insertions(+), 218 deletions(-) delete mode 100644 js/src/jsapi-tests/testGCChunkAlloc.cpp create mode 100644 js/src/jsapi-tests/testGCOutOfMemory.cpp diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index 4e57b175927..98a251841c5 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -65,7 +65,7 @@ CPPSRCS = \ testExternalStrings.cpp \ testFuncCallback.cpp \ testFunctionProperties.cpp \ - testGCChunkAlloc.cpp \ + testGCOutOfMemory.cpp \ testGetPropertyDefault.cpp \ testIndexToString.cpp \ testIntString.cpp \ diff --git a/js/src/jsapi-tests/testGCChunkAlloc.cpp b/js/src/jsapi-tests/testGCChunkAlloc.cpp deleted file mode 100644 index d126c1d53b8..00000000000 --- a/js/src/jsapi-tests/testGCChunkAlloc.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=99: - * - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - * Contributor: Igor Bukanov - */ - -#include "tests.h" -#include "jsgcchunk.h" -#include "jscntxt.h" - -/* We allow to allocate 2 (system/user) chunks. */ - -static const int SYSTEM = 0; -static const int USER = 1; -static const int N_POOLS = 2; - -class CustomGCChunkAllocator: public js::GCChunkAllocator { - public: - CustomGCChunkAllocator() { pool[SYSTEM] = NULL; pool[USER] = NULL; } - void *pool[N_POOLS]; - - private: - - virtual void *doAlloc() { - if (!pool[SYSTEM] && !pool[USER]) - return NULL; - void *chunk = NULL; - if (pool[SYSTEM]) { - chunk = pool[SYSTEM]; - pool[SYSTEM] = NULL; - } else { - chunk = pool[USER]; - pool[USER] = NULL; - } - return chunk; - } - - virtual void doFree(void *chunk) { - JS_ASSERT(!pool[SYSTEM] || !pool[USER]); - if (!pool[SYSTEM]) { - pool[SYSTEM] = chunk; - } else { - pool[USER] = chunk; - } - } -}; - -static CustomGCChunkAllocator customGCChunkAllocator; - -static unsigned errorCount = 0; - -static void -ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) -{ - ++errorCount; -} - -BEGIN_TEST(testGCChunkAlloc) -{ - JS_SetErrorReporter(cx, ErrorCounter); - - jsvalRoot root(cx); - - /* - * We loop until out-of-memory happens during the chunk allocation. But - * we have to disable the jit since it cannot tolerate OOM during the - * chunk allocation. - */ - JS_ToggleOptions(cx, JSOPTION_JIT); - - static const char source[] = - "var max = 0; (function() {" - " var array = [];" - " for (; ; ++max)" - " array.push({});" - "})();"; - JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, - root.addr()); - - /* Check that we get OOM. */ - CHECK(!ok); - CHECK(!JS_IsExceptionPending(cx)); - CHECK_EQUAL(errorCount, 1); - CHECK(!customGCChunkAllocator.pool[SYSTEM]); - CHECK(!customGCChunkAllocator.pool[USER]); - JS_GC(cx); - JS_ToggleOptions(cx, JSOPTION_JIT); - EVAL("(function() {" - " var array = [];" - " for (var i = max >> 1; i != 0;) {" - " --i;" - " array.push({});" - " }" - "})();", root.addr()); - CHECK_EQUAL(errorCount, 1); - return true; -} - -virtual JSRuntime * createRuntime() { - /* - * To test failure of chunk allocation allow to use GC twice the memory - * the single chunk contains. - */ - JSRuntime *rt = JS_NewRuntime(2 * js::GC_CHUNK_SIZE); - if (!rt) - return NULL; - - customGCChunkAllocator.pool[SYSTEM] = js::AllocGCChunk(); - customGCChunkAllocator.pool[USER] = js::AllocGCChunk(); - JS_ASSERT(customGCChunkAllocator.pool[SYSTEM]); - JS_ASSERT(customGCChunkAllocator.pool[USER]); - - rt->setCustomGCChunkAllocator(&customGCChunkAllocator); - return rt; -} - -virtual void destroyRuntime() { - JS_DestroyRuntime(rt); - - /* We should get the initial chunk back at this point. */ - JS_ASSERT(customGCChunkAllocator.pool[SYSTEM]); - JS_ASSERT(customGCChunkAllocator.pool[USER]); - js::FreeGCChunk(customGCChunkAllocator.pool[SYSTEM]); - js::FreeGCChunk(customGCChunkAllocator.pool[USER]); - customGCChunkAllocator.pool[SYSTEM] = NULL; - customGCChunkAllocator.pool[USER] = NULL; -} - -END_TEST(testGCChunkAlloc) diff --git a/js/src/jsapi-tests/testGCOutOfMemory.cpp b/js/src/jsapi-tests/testGCOutOfMemory.cpp new file mode 100644 index 00000000000..f737bb4cbcd --- /dev/null +++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Igor Bukanov + */ + +#include "tests.h" +#include "jsgcchunk.h" +#include "jscntxt.h" + +static unsigned errorCount = 0; + +static void +ErrorCounter(JSContext *cx, const char *message, JSErrorReport *report) +{ + ++errorCount; +} + +BEGIN_TEST(testGCOutOfMemory) +{ + JS_SetErrorReporter(cx, ErrorCounter); + + jsvalRoot root(cx); + + /* + * We loop until we get out-of-memory. We have to disable the jit since it + * ignores the runtime allocation limits during execution. + */ + JS_ToggleOptions(cx, JSOPTION_JIT); + + static const char source[] = + "var max = 0; (function() {" + " var array = [];" + " for (; ; ++max)" + " array.push({});" + " array = []; array.push(0);" + "})();"; + JSBool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, + root.addr()); + + /* Check that we get OOM. */ + CHECK(!ok); + CHECK(!JS_IsExceptionPending(cx)); + CHECK_EQUAL(errorCount, 1); + JS_GC(cx); + JS_ToggleOptions(cx, JSOPTION_JIT); + EVAL("(function() {" + " var array = [];" + " for (var i = max >> 2; i != 0;) {" + " --i;" + " array.push({});" + " }" + "})();", root.addr()); + CHECK_EQUAL(errorCount, 1); + return true; +} + +virtual JSRuntime * createRuntime() { + return JS_NewRuntime(256 * 1024); +} + +virtual void destroyRuntime() { + JS_DestroyRuntime(rt); +} + +END_TEST(testGCOutOfMemory) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2fb68bdfefa..e740e8c9d07 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -638,8 +638,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun) static JSBool js_NewRuntimeWasCalled = JS_FALSE; JSRuntime::JSRuntime() - : gcChunkAllocator(&defaultGCChunkAllocator), - trustedPrincipals_(NULL) + : trustedPrincipals_(NULL) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ JS_INIT_CLIST(&contextList); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 2f30ccfc640..dd23ace50e7 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -512,14 +512,6 @@ struct JSRuntime { volatile ptrdiff_t gcMallocBytes; public: - js::GCChunkAllocator *gcChunkAllocator; - - void setCustomGCChunkAllocator(js::GCChunkAllocator *allocator) { - JS_ASSERT(allocator); - JS_ASSERT(state == JSRTS_DOWN); - gcChunkAllocator = allocator; - } - /* * The trace operation and its data argument to trace embedding-specific * GC roots. diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index aa42f0f457d..3bfae14d3f6 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -534,7 +534,7 @@ Chunk::releaseArena(ArenaHeader *aheader) inline Chunk * AllocateGCChunk(JSRuntime *rt) { - Chunk *p = (Chunk *)rt->gcChunkAllocator->alloc(); + Chunk *p = static_cast(AllocGCChunk()); #ifdef MOZ_GCTIMER if (p) JS_ATOMIC_INCREMENT(&newChunkCount); @@ -549,7 +549,7 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p) #ifdef MOZ_GCTIMER JS_ATOMIC_INCREMENT(&destroyChunkCount); #endif - rt->gcChunkAllocator->free_(p); + FreeGCChunk(p); } /* The caller must hold the GC lock. */ diff --git a/js/src/jsgcchunk.cpp b/js/src/jsgcchunk.cpp index 551f6b7ebfa..2529166fe9c 100644 --- a/js/src/jsgcchunk.cpp +++ b/js/src/jsgcchunk.cpp @@ -272,8 +272,6 @@ UnmapPages(void *addr, size_t size) namespace js { -GCChunkAllocator defaultGCChunkAllocator; - inline void * FindChunkStart(void *p) { @@ -282,7 +280,7 @@ FindChunkStart(void *p) return reinterpret_cast(addr); } -JS_FRIEND_API(void *) +void * AllocGCChunk() { void *p; @@ -329,7 +327,7 @@ AllocGCChunk() return p; } -JS_FRIEND_API(void) +void FreeGCChunk(void *p) { JS_ASSERT(p); diff --git a/js/src/jsgcchunk.h b/js/src/jsgcchunk.h index 94d37ae2d32..df648ce9545 100644 --- a/js/src/jsgcchunk.h +++ b/js/src/jsgcchunk.h @@ -49,44 +49,12 @@ const size_t GC_CHUNK_SHIFT = 20; const size_t GC_CHUNK_SIZE = size_t(1) << GC_CHUNK_SHIFT; const size_t GC_CHUNK_MASK = GC_CHUNK_SIZE - 1; -JS_FRIEND_API(void *) +void * AllocGCChunk(); -JS_FRIEND_API(void) +void FreeGCChunk(void *p); -class GCChunkAllocator { - public: - GCChunkAllocator() {} - - void *alloc() { - void *chunk = doAlloc(); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - return chunk; - } - - void free_(void *chunk) { - JS_ASSERT(chunk); - JS_ASSERT(!(reinterpret_cast(chunk) & GC_CHUNK_MASK)); - doFree(chunk); - } - - private: - virtual void *doAlloc() { - return AllocGCChunk(); - } - - virtual void doFree(void *chunk) { - FreeGCChunk(chunk); - } - - /* No copy or assignment semantics. */ - GCChunkAllocator(const GCChunkAllocator &); - void operator=(const GCChunkAllocator &); -}; - -extern GCChunkAllocator defaultGCChunkAllocator; - } #endif /* jsgchunk_h__ */ diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 3860dce81dd..66855f17600 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1445,41 +1445,7 @@ MakeMemoryReporterPath(const nsACString &pathPrefix, } // anonymous namespace -class XPConnectGCChunkAllocator - : public js::GCChunkAllocator -{ -public: - XPConnectGCChunkAllocator() {} - -private: - virtual void *doAlloc() { - void *chunk; -#ifdef MOZ_MEMORY - // posix_memalign returns zero on success, nonzero on failure. - if (posix_memalign(&chunk, js::GC_CHUNK_SIZE, js::GC_CHUNK_SIZE)) - chunk = 0; -#else - chunk = js::AllocGCChunk(); -#endif - return chunk; - } - - virtual void doFree(void *chunk) { -#ifdef MOZ_MEMORY - free(chunk); -#else - js::FreeGCChunk(chunk); -#endif - } -}; - -static XPConnectGCChunkAllocator gXPCJSChunkAllocator; - -#ifdef MOZ_MEMORY -#define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_HEAP -#else #define JS_GC_HEAP_KIND nsIMemoryReporter::KIND_NONHEAP -#endif // We have per-compartment GC heap totals, so we can't put the total GC heap // size in the explicit allocations tree. But it's a useful figure, so put it @@ -2087,8 +2053,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) mJSRuntime->setActivityCallback(ActivityCallback, this); - mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator); - NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount)); NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount)); From f44c93b1c5eaf42f24397d20cea777888919de04 Mon Sep 17 00:00:00 2001 From: Kev Needham Date: Wed, 31 Aug 2011 18:54:56 -0700 Subject: [PATCH 002/172] Bug 671307: Add Twitter to default search plugins for en-US, r=gavin --- browser/locales/en-US/searchplugins/list.txt | 1 + browser/locales/en-US/searchplugins/twitter.xml | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 browser/locales/en-US/searchplugins/twitter.xml diff --git a/browser/locales/en-US/searchplugins/list.txt b/browser/locales/en-US/searchplugins/list.txt index 7c7d9ee7a1e..2a1141a984f 100644 --- a/browser/locales/en-US/searchplugins/list.txt +++ b/browser/locales/en-US/searchplugins/list.txt @@ -2,5 +2,6 @@ amazondotcom bing eBay google +twitter wikipedia yahoo diff --git a/browser/locales/en-US/searchplugins/twitter.xml b/browser/locales/en-US/searchplugins/twitter.xml new file mode 100644 index 00000000000..9f1850a59c7 --- /dev/null +++ b/browser/locales/en-US/searchplugins/twitter.xml @@ -0,0 +1,11 @@ + +Twitter +Realtime Twitter Search +UTF-8 +data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A/v7+D/7+/j/+/v5g/v7+YP7+/mD+/v5I/v7+KP///wD///8A////AP///wD///8A////AP///wD+/v4H/v7+UPbv4pHgx47B1K9Y3tWwWN7Ur1je3sKCx+rbuKj+/v5n/v7+GP///wD///8A////AP///wD+/v4Y+fbweM2ycMe2iB7/vI0f/8STIf/KlyL/zJki/8yZIv/LmCL/0ahK5/Hp1JH+/v4Y////AP///wD///8A7OTTaquHN+CujkXPs5ZTv6N6G/+2iB7/xpUh/8yZIv/MmSL/zJki/8yZIv/Kmy738OjUi////wD///8A////AMKtfY7w6+Ef////AP///wD///8A3sqbp8iWIf/MmSL/zJki/8yZIv/MmSL/y5gi/8mePO7+/v4w////AP///wD///8A////AP///wD+/v4H/v7+V9CtWN3KmCL/zJki/8yZIv/MmSL/zJki/8yZIv/JlyH/5tSqp/7+/mD+/v4/////AP///wD///8A+PXvJtGyZdXNnS/3y5gi/8qYIv/LmCL/zJki/8yZIv/MmSL/y5gi/82iPO7LqVfe0byMmf///wD///8A/v7+D/Do1JHKmy73ypci/8KSIP+/jyD/xpQh/8uYIv/MmSL/zJki/8qYIv+/jyD/rIEd/9nKqH7///8A////APPu4TzAlSz3wZEg/7mLH/+sgR3/uZdGz7mLH//JlyH/zJki/8yZIv/GlSH/to0r9eXbxD/Vx6dg////AP7+/h/p38WhtIsq9al/HP+kfyjuybaKgf///wCzjzjlwJAg/8qYIv/JlyH/u4wf/8CkYrn///8A////AP///wDj2sRMnHUa/7meYa7Vx6dg////AP///wD///8A2MmnYK6DHf++jiD/vo4g/62CHf/k2sQ/////AP///wD///8A8OvhH/f07w////8A////AP///wD///8A////AP///wC/p3Cfpnwc/66GKvPg1LZ8////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ANXHp2DJtoqByLWKgf///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AADgPwAAwA8AAIAHAAB4BwAA+AMAAPAAAADgAQAA4AMAAMEDAADPhwAA/48AAP/nAAD//wAA//8AAA== +https://twitter.com/search/ + + + + + From b94c2b063e3c3df0ec76479944810c49e7b8033b Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Tue, 6 Sep 2011 14:56:45 +0200 Subject: [PATCH 003/172] Bug 684574 - InternalInvalidateThebesLayersInSubtree should recurse on the subdocument root frame. r=roc --- layout/base/FrameLayerBuilder.cpp | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index a1d5990ca08..dd1c29c8e94 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1903,26 +1903,29 @@ InternalInvalidateThebesLayersInSubtree(nsIFrame* aFrame) foundContainerLayer = PR_TRUE; } - nsIFrame* frame = aFrame; - while (frame) { - nsIFrame::ChildListIterator lists(frame); - for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - if (InternalInvalidateThebesLayersInSubtree(childFrames.get())) { - foundContainerLayer = PR_TRUE; - } + nsAutoTArray childListArray; + if (!aFrame->GetFirstPrincipalChild()) { + nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(aFrame); + if (subdocumentFrame) { + // Descend into the subdocument + nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); + if (root) { + childListArray.AppendElement(nsIFrame::ChildList( + nsFrameList(root, nsLayoutUtils::GetLastSibling(root)), + nsIFrame::kPrincipalList)); } } - if (frame == aFrame && !frame->GetFirstPrincipalChild()) { - nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(frame); - if (subdocumentFrame) { - // Descend into the subdocument - frame = subdocumentFrame->GetSubdocumentRootFrame(); - continue; + } + + aFrame->GetChildLists(&childListArray); + nsIFrame::ChildListArrayIterator lists(childListArray); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + if (InternalInvalidateThebesLayersInSubtree(childFrames.get())) { + foundContainerLayer = PR_TRUE; } } - break; } if (!foundContainerLayer) { From 4923d068c0a63d0d51504bf59cdecfc85e7cd9e8 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 6 Sep 2011 14:32:26 +0100 Subject: [PATCH 004/172] Backout a422b9ff0a9e (bug 591780 part 1) for causing 8% Ts regression on multiple platforms; a=khuey --- browser/base/content/nsContextMenu.js | 6 +- editor/composer/src/nsEditorSpellCheck.cpp | 14 +- editor/idl/nsIEditorSpellCheck.idl | 11 +- editor/libeditor/base/Makefile.in | 1 - editor/libeditor/base/nsEditor.cpp | 63 +----- editor/libeditor/base/nsEditor.h | 5 - editor/txtsvc/public/nsISpellChecker.h | 12 +- extensions/spellcheck/Makefile.in | 4 - .../spellcheck/hunspell/src/Makefile.in | 2 - .../spellcheck/hunspell/src/mozHunspell.cpp | 94 +------- .../spellcheck/hunspell/src/mozHunspell.h | 6 - .../idl/mozISpellCheckingEngine.idl | 33 +-- .../spellcheck/src/mozInlineSpellChecker.cpp | 12 +- .../spellcheck/src/mozInlineSpellChecker.h | 4 +- extensions/spellcheck/src/mozSpellChecker.cpp | 212 ++++++++---------- extensions/spellcheck/src/mozSpellChecker.h | 10 +- .../spellcheck/src/mozSpellCheckerFactory.cpp | 34 ++- extensions/spellcheck/tests/Makefile.in | 48 ---- .../spellcheck/tests/chrome/Makefile.in | 54 ----- .../spellcheck/tests/chrome/base/Makefile.in | 52 ----- .../spellcheck/tests/chrome/base/base_utf.aff | 198 ---------------- .../spellcheck/tests/chrome/base/base_utf.dic | 29 --- .../spellcheck/tests/chrome/map/Makefile.in | 52 ----- .../spellcheck/tests/chrome/map/maputf.aff | 11 - .../spellcheck/tests/chrome/map/maputf.dic | 4 - .../chrome/test_add_remove_dictionaries.xul | 110 --------- 26 files changed, 162 insertions(+), 919 deletions(-) delete mode 100644 extensions/spellcheck/tests/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/base/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/base/base_utf.aff delete mode 100644 extensions/spellcheck/tests/chrome/base/base_utf.dic delete mode 100644 extensions/spellcheck/tests/chrome/map/Makefile.in delete mode 100644 extensions/spellcheck/tests/chrome/map/maputf.aff delete mode 100644 extensions/spellcheck/tests/chrome/map/maputf.dic delete mode 100644 extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 4ab61794d09..b4f9aacc043 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -321,8 +321,10 @@ nsContextMenu.prototype = { var onMisspelling = InlineSpellCheckerUI.overMisspelling; this.showItem("spell-check-enabled", canSpell); this.showItem("spell-separator", canSpell || this.onEditableArea); - document.getElementById("spell-check-enabled") - .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); + if (canSpell) { + document.getElementById("spell-check-enabled") + .setAttribute("checked", InlineSpellCheckerUI.enabled); + } this.showItem("spell-add-to-dictionary", onMisspelling); diff --git a/editor/composer/src/nsEditorSpellCheck.cpp b/editor/composer/src/nsEditorSpellCheck.cpp index cd8961d6492..ecaa421472e 100644 --- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -650,14 +650,6 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary) return mSpellChecker->SetCurrentDictionary(aDictionary); } -NS_IMETHODIMP -nsEditorSpellCheck::GetSpellChecker(nsISpellChecker **aSpellChecker) -{ - *aSpellChecker = mSpellChecker; - NS_IF_ADDREF(*aSpellChecker); - return NS_OK; -} - NS_IMETHODIMP nsEditorSpellCheck::UninitSpellChecker() { @@ -826,7 +818,11 @@ nsEditorSpellCheck::UpdateCurrentDictionary() if (NS_FAILED(rv) || currentDictionary.IsEmpty()) { rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US")); if (NS_FAILED(rv)) { - mSpellChecker->CheckCurrentDictionary(); + nsTArray dictList; + rv = mSpellChecker->GetDictionaryList(&dictList); + if (NS_SUCCEEDED(rv) && dictList.Length() > 0) { + SetCurrentDictionary(dictList[0]); + } } } } diff --git a/editor/idl/nsIEditorSpellCheck.idl b/editor/idl/nsIEditorSpellCheck.idl index 0ccb5fa4195..f06eabff715 100644 --- a/editor/idl/nsIEditorSpellCheck.idl +++ b/editor/idl/nsIEditorSpellCheck.idl @@ -40,20 +40,11 @@ interface nsIEditor; interface nsITextServicesFilter; -%{C++ -#include "nsISpellChecker.h" -%} -[ptr] native nsISpellChecker(nsISpellChecker); -[scriptable, uuid(334946c3-0e93-4aac-b662-e1b56f95d68b)] +[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)] interface nsIEditorSpellCheck : nsISupports { - /** - * Get the spell checker used by this editor. - */ - [noscript] readonly attribute nsISpellChecker spellChecker; - /** * Returns true if we can enable spellchecking. If there are no available * dictionaries, this will return false. diff --git a/editor/libeditor/base/Makefile.in b/editor/libeditor/base/Makefile.in index 66b7463345a..acedb86f499 100644 --- a/editor/libeditor/base/Makefile.in +++ b/editor/libeditor/base/Makefile.in @@ -94,5 +94,4 @@ INCLUDES += \ -I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/events/src \ -I$(topsrcdir)/layout/style \ - -I$(topsrcdir)/extensions/spellcheck/src \ $(NULL) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 21562c45454..d49f5acc50c 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -24,7 +24,6 @@ * Daniel Glazman * Masayuki Nakano * Mats Palmgren - * Jesper Kristensen * * 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"), @@ -49,11 +48,6 @@ #include "nsFocusManager.h" #include "nsUnicharUtils.h" #include "nsReadableUtils.h" -#include "nsIObserverService.h" -#include "mozilla/Services.h" -#include "mozISpellCheckingEngine.h" -#include "nsIEditorSpellCheck.h" -#include "mozInlineSpellChecker.h" #include "nsIDOMText.h" #include "nsIDOMElement.h" @@ -213,7 +207,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport) NS_INTERFACE_MAP_ENTRY(nsIEditor) - NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor) NS_INTERFACE_MAP_END @@ -307,13 +300,6 @@ nsEditor::PostCreate() // update the UI with our state NotifyDocumentListeners(eDocumentCreated); NotifyDocumentListeners(eDocumentStateChanged); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - PR_FALSE); - } } // update nsTextStateManager and caret if we have focus @@ -426,12 +412,6 @@ nsEditor::PreDestroy(PRBool aDestroyingFrames) if (mDidPreDestroy) return NS_OK; - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION); - } - // Let spellchecker clean up its observers etc. It is important not to // actually free the spellchecker here, since the spellchecker could have // caused flush notifications, which could have gotten here if a textbox @@ -1303,13 +1283,6 @@ NS_IMETHODIMP nsEditor::GetInlineSpellChecker(PRBool autoCreate, return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK; } - // We don't want to show the spell checking UI if there are no spell check dictionaries available. - PRBool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking(); - if (!canSpell) { - *aInlineSpellChecker = nsnull; - return NS_ERROR_FAILURE; - } - nsresult rv; if (!mInlineSpellChecker && autoCreate) { mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv); @@ -1328,49 +1301,17 @@ NS_IMETHODIMP nsEditor::GetInlineSpellChecker(PRBool autoCreate, return NS_OK; } -NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic, - const PRUnichar *aData) -{ - NS_ASSERTION(!strcmp(aTopic, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION), - "Unexpected observer topic"); - - // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes - SyncRealTimeSpell(); - - // When nsIEditorSpellCheck::GetCurrentDictionary changes - if (mInlineSpellChecker) { - // if the current dictionary is no longer available, find another one - nsCOMPtr editorSpellCheck; - mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck)); - if (editorSpellCheck) { - nsCOMPtr spellChecker; - editorSpellCheck->GetSpellChecker(getter_AddRefs(spellChecker)); - spellChecker->CheckCurrentDictionary(); - } - - // update the inline spell checker to reflect the new current dictionary - mInlineSpellChecker->SpellCheckRange(nsnull); // causes recheck - } - - return NS_OK; -} - NS_IMETHODIMP nsEditor::SyncRealTimeSpell() { NS_TIME_FUNCTION; PRBool enable = GetDesiredSpellCheckState(); - // Initializes mInlineSpellChecker nsCOMPtr spellChecker; GetInlineSpellChecker(enable, getter_AddRefs(spellChecker)); - if (mInlineSpellChecker) { - // We might have a mInlineSpellChecker even if there are no dictionaries - // available since we don't destroy the mInlineSpellChecker when the last - // dictionariy is removed, but in that case spellChecker is null - mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker); + if (spellChecker) { + spellChecker->SetEnableRealTimeSpell(enable); } return NS_OK; diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index e5fc4a8b2a4..1ef527abf67 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -66,7 +66,6 @@ #include "nsStubMutationObserver.h" #include "nsIViewManager.h" #include "nsCycleCollectionParticipant.h" -#include "nsIObserver.h" class nsIDOMCharacterData; class nsIDOMRange; @@ -101,7 +100,6 @@ class nsIDOMNSEvent; class nsEditor : public nsIEditor, public nsIEditorIMESupport, public nsSupportsWeakReference, - public nsIObserver, public nsIPhonetic { public: @@ -155,9 +153,6 @@ public: /* ------------ nsIEditorIMESupport methods -------------- */ NS_DECL_NSIEDITORIMESUPPORT - /* ------------ nsIObserver methods -------------- */ - NS_DECL_NSIOBSERVER - // nsIPhonetic NS_DECL_NSIPHONETIC diff --git a/editor/txtsvc/public/nsISpellChecker.h b/editor/txtsvc/public/nsISpellChecker.h index b3545d5dad4..d1644df77fe 100644 --- a/editor/txtsvc/public/nsISpellChecker.h +++ b/editor/txtsvc/public/nsISpellChecker.h @@ -44,9 +44,9 @@ #define NS_SPELLCHECKER_CONTRACTID "@mozilla.org/spellchecker;1" #define NS_ISPELLCHECKER_IID \ -{ /* 27bff957-b486-40ae-9f5d-af0cdd211868 */ \ -0x27bff957, 0xb486, 0x40ae, \ - { 0x9f, 0x5d, 0xaf, 0x0c, 0xdd, 0x21, 0x18, 0x68 } } +{ /* E75AC48C-E948-452E-8DB3-30FEE29FE3D2 */ \ +0xe75ac48c, 0xe948, 0x452e, \ + { 0x8d, 0xb3, 0x30, 0xfe, 0xe2, 0x9f, 0xe3, 0xd2 } } class nsITextServicesDocument; class nsString; @@ -146,12 +146,6 @@ public: * empty string, spellchecker will be disabled. */ NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0; - - /** - * Call this on any change in installed dictionaries to ensure that the spell - * checker is not using a current dictionary which is no longer available. - */ - NS_IMETHOD CheckCurrentDictionary() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsISpellChecker, NS_ISPELLCHECKER_IID) diff --git a/extensions/spellcheck/Makefile.in b/extensions/spellcheck/Makefile.in index b270c3e9006..e7895eacba6 100644 --- a/extensions/spellcheck/Makefile.in +++ b/extensions/spellcheck/Makefile.in @@ -44,8 +44,4 @@ include $(DEPTH)/config/autoconf.mk MODULE = spellchecker DIRS = idl locales hunspell src -ifdef ENABLE_TESTS -DIRS += tests -endif - include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/hunspell/src/Makefile.in b/extensions/spellcheck/hunspell/src/Makefile.in index c53c279bc10..7ca1316e308 100644 --- a/extensions/spellcheck/hunspell/src/Makefile.in +++ b/extensions/spellcheck/hunspell/src/Makefile.in @@ -71,8 +71,6 @@ endif include $(topsrcdir)/config/rules.mk -INCLUDES += -I$(topsrcdir)/extensions/spellcheck/src - ifdef MOZ_NATIVE_HUNSPELL CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS) endif diff --git a/extensions/spellcheck/hunspell/src/mozHunspell.cpp b/extensions/spellcheck/hunspell/src/mozHunspell.cpp index af67fd350c8..89826d2dab6 100644 --- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp @@ -41,7 +41,6 @@ * Harri Pitkanen * Andras Timar * Tor Lillqvist - * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -71,8 +70,6 @@ #include "nsUnicharUtilCIID.h" #include "nsUnicharUtils.h" #include "nsCRT.h" -#include "mozInlineSpellChecker.h" -#include "mozilla/Services.h" #include #include "nsIMemoryReporter.h" @@ -125,7 +122,8 @@ mozHunspell::Init() LoadDictionaryList(); - nsCOMPtr obs = mozilla::services::GetObserverService(); + nsCOMPtr obs = + do_GetService("@mozilla.org/observer-service;1"); if (obs) { obs->AddObserver(this, "profile-do-change", PR_TRUE); } @@ -149,6 +147,9 @@ NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); + if (mDictionary.IsEmpty()) + return NS_ERROR_NOT_INITIALIZED; + *aDictionary = ToNewUnicode(mDictionary); return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } @@ -160,6 +161,9 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); + if (mDictionary.Equals(aDictionary)) + return NS_OK; + nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary)); if (!affFile) return NS_ERROR_FILE_NOT_FOUND; @@ -174,9 +178,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) nsresult rv = affFile->GetNativePath(affFileName); NS_ENSURE_SUCCESS(rv, rv); - if (mAffixFileName.Equals(affFileName.get())) - return NS_OK; - dictFileName = affFileName; PRInt32 dotPos = dictFileName.RFindChar('.'); if (dotPos == -1) @@ -190,7 +191,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) delete mHunspell; mDictionary = aDictionary; - mAffixFileName = affFileName; mHunspell = new Hunspell(affFileName.get(), dictFileName.get()); @@ -222,13 +222,6 @@ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) else mLanguage = Substring(mDictionary, 0, pos); - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nsnull, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - nsnull); - } - return NS_OK; } @@ -340,14 +333,6 @@ NS_IMETHODIMP mozHunspell::GetDictionaryList(PRUnichar ***aDictionaries, return NS_OK; } -static PLDHashOperator -FindFirstString(const nsAString& aString, nsIFile* aFile, void* aClosure) -{ - nsAString *dic = (nsAString*) aClosure; - dic->Assign(aString); - return PL_DHASH_STOP; -} - void mozHunspell::LoadDictionaryList() { @@ -360,7 +345,6 @@ mozHunspell::LoadDictionaryList() if (!dirSvc) return; - // find built in dictionaries nsCOMPtr dictDir; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY, NS_GET_IID(nsIFile), getter_AddRefs(dictDir)); @@ -388,7 +372,6 @@ mozHunspell::LoadDictionaryList() } } - // find dictionaries from extensions requiring restart nsCOMPtr dictDirs; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs)); @@ -404,51 +387,6 @@ mozHunspell::LoadDictionaryList() if (dictDir) LoadDictionariesFromDir(dictDir); } - - // find dictionaries from restartless extensions - for (PRUint32 i = 0; i < mDynamicDirectories.Count(); i++) { - LoadDictionariesFromDir(mDynamicDirectories[i]); - } - - // Now we have finished updating the list of dictionaries, update the current - // dictionary and any editors which may use it. - mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); - - // If the current dictionary has gone, try to replace it with another - // dictionary of the same language - if (!mDictionary.IsEmpty()) { - rv = SetDictionary(mDictionary.get()); - if (NS_SUCCEEDED(rv)) - return; - } - - // If we didn't find a dictionary equal to the current dictionary or we had - // no current dictionary, just pick an arbitrary dictionary. - nsAutoString firstDictionary; - mDictionaries.EnumerateRead(FindFirstString, &firstDictionary); - if (!firstDictionary.IsEmpty()) { - rv = SetDictionary(firstDictionary.get()); - if (NS_SUCCEEDED(rv)) - return; - } - - // If there are no dictionaries, set no current dictionary - if (!mDictionary.IsEmpty()) { - delete mHunspell; - mHunspell = nsnull; - mDictionary.AssignLiteral(""); - mAffixFileName.AssignLiteral(""); - mLanguage.AssignLiteral(""); - mDecoder = nsnull; - mEncoder = nsnull; - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nsnull, - SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, - nsnull); - } - } } NS_IMETHODIMP @@ -604,19 +542,3 @@ mozHunspell::Observe(nsISupports* aSubj, const char *aTopic, return NS_OK; } - -/* void addDirectory(in nsIFile dir); */ -NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir) -{ - mDynamicDirectories.AppendObject(aDir); - LoadDictionaryList(); - return NS_OK; -} - -/* void removeDirectory(in nsIFile dir); */ -NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir) -{ - mDynamicDirectories.RemoveObject(aDir); - LoadDictionaryList(); - return NS_OK; -} diff --git a/extensions/spellcheck/hunspell/src/mozHunspell.h b/extensions/spellcheck/hunspell/src/mozHunspell.h index 4b81b06cddc..5b76d0c3601 100644 --- a/extensions/spellcheck/hunspell/src/mozHunspell.h +++ b/extensions/spellcheck/hunspell/src/mozHunspell.h @@ -41,7 +41,6 @@ * Harri Pitkanen * Andras Timar * Tor Lillqvist - * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -65,7 +64,6 @@ #include "mozIPersonalDictionary.h" #include "nsString.h" #include "nsCOMPtr.h" -#include "nsCOMArray.h" #include "nsIObserver.h" #include "nsIUnicodeEncoder.h" #include "nsIUnicodeDecoder.h" @@ -111,10 +109,6 @@ protected: nsInterfaceHashtable mDictionaries; nsString mDictionary; nsString mLanguage; - nsCString mAffixFileName; - - // dynamic dirs used to search for dictionaries - nsCOMArray mDynamicDirectories; Hunspell *mHunspell; diff --git a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl index 3a9e7dd1919..e880d87353e 100644 --- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl +++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl @@ -20,7 +20,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -41,7 +40,7 @@ interface nsIFile; interface mozIPersonalDictionary; -[scriptable, uuid(8ba643a4-7ddc-4662-b976-7ec123843f10)] +[scriptable, uuid(6eb307d6-3567-481a-971a-feb666b8ae72)] /** * This interface represents a SpellChecker. @@ -50,22 +49,11 @@ interface mozIPersonalDictionary; interface mozISpellCheckingEngine : nsISupports { /** * The name of the current dictionary - * - * Whenever getDictionaryList is not empty, this attribute contains a value - * from that list. Whenever getDictionaryList is empty, this attribute - * contains the empty string. Setting this attribute to a value not in - * getDictionaryList will throw NS_ERROR_FILE_NOT_FOUND. - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ attribute wstring dictionary; /** * The language this spellchecker is using when checking - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ readonly attribute wstring language; @@ -101,17 +89,11 @@ interface mozISpellCheckingEngine : nsISupports { /** * check a word - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ boolean check(in wstring word); /** * get a list of suggestions for a misspelled word - * - * The spellcheck engine will send a notification with - * "spellcheck-dictionary-update" as topic when this changes. */ void suggest(in wstring word,[array, size_is(count)] out wstring suggestions, out PRUint32 count); @@ -119,22 +101,9 @@ interface mozISpellCheckingEngine : nsISupports { * Load dictionaries from the specified dir */ void loadDictionariesFromDir(in nsIFile dir); - - /** - * Add dictionaries from a directory to the spell checker - */ - void addDirectory(in nsIFile dir); - - /** - * Remove dictionaries from a directory from the spell checker - */ - void removeDirectory(in nsIFile dir); }; %{C++ #define DICTIONARY_SEARCH_DIRECTORY "DictD" #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL" - -#define SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION \ - "spellcheck-dictionary-update" %} diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 12d9b9bc3d4..e159d38e567 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -600,9 +600,9 @@ nsresult mozInlineSpellChecker::Cleanup(PRBool aDestroyingFrames) // do that and caches the result so we don't have to keep allocating those // objects if there are no dictionaries or spellchecking. // -// Whenever dictionaries are added or removed at runtime, this value must be -// updated before an observer notification is sent out about the change, to -// avoid editors getting a wrong cached result. +// This caching will prevent adding dictionaries at runtime if we start out +// with no dictionaries! Installing dictionaries as extensions will require +// a restart anyway, so it shouldn't be a problem. PRBool // static mozInlineSpellChecker::CanEnableInlineSpellChecking() @@ -625,12 +625,6 @@ mozInlineSpellChecker::CanEnableInlineSpellChecking() return (gCanEnableSpellChecking == SpellCheck_Available); } -void // static -mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking() -{ - gCanEnableSpellChecking = SpellCheck_Uninitialized; -} - // mozInlineSpellChecker::RegisterEventListeners // // The inline spell checker listens to mouse events and keyboard navigation+ // events. diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.h b/extensions/spellcheck/src/mozInlineSpellChecker.h index f371ed72f8e..183422fd02f 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.h +++ b/extensions/spellcheck/src/mozInlineSpellChecker.h @@ -229,10 +229,8 @@ public: NS_DECL_NSIDOMEVENTLISTENER NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener) - // returns true if there are any spell checking dictionaries available + // returns true if it looks likely that we can enable real-time spell checking static PRBool CanEnableInlineSpellChecking(); - // update the cached value whenever the list of available dictionaries changes - static void UpdateCanEnableInlineSpellChecking(); nsresult Blur(nsIDOMEvent* aEvent); nsresult MouseClick(nsIDOMEvent* aMouseEvent); diff --git a/extensions/spellcheck/src/mozSpellChecker.cpp b/extensions/spellcheck/src/mozSpellChecker.cpp index b23927e02cf..96832848549 100644 --- a/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/extensions/spellcheck/src/mozSpellChecker.cpp @@ -18,7 +18,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -78,6 +77,9 @@ mozSpellChecker::Init() mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); mSpellCheckingEngine = nsnull; + mCurrentEngineContractId = nsnull; + mDictionariesMap.Init(); + InitSpellCheckDictionaryMap(); return NS_OK; } @@ -305,45 +307,35 @@ mozSpellChecker::GetPersonalDictionary(nsTArray *aWordList) return NS_OK; } +struct AppendNewStruct +{ + nsTArray *dictionaryList; + PRBool failed; +}; + +static PLDHashOperator +AppendNewString(const nsAString& aString, nsCString*, void* aClosure) +{ + AppendNewStruct *ans = (AppendNewStruct*) aClosure; + + if (!ans->dictionaryList->AppendElement(aString)) + { + ans->failed = PR_TRUE; + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + NS_IMETHODIMP mozSpellChecker::GetDictionaryList(nsTArray *aDictionaryList) { - nsresult rv; + AppendNewStruct ans = {aDictionaryList, PR_FALSE}; - // For catching duplicates - nsClassHashtable dictionaries; - dictionaries.Init(); + mDictionariesMap.EnumerateRead(AppendNewString, &ans); - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - PRUint32 count = 0; - PRUnichar **words = NULL; - engine->GetDictionaryList(&words, &count); - for (PRUint32 k = 0; k < count; k++) { - nsAutoString dictName; - - dictName.Assign(words[k]); - - // Skip duplicate dictionaries. Only take the first one - // for each name. - if (dictionaries.Get(dictName, NULL)) - continue; - - dictionaries.Put(dictName, NULL); - - if (!aDictionaryList->AppendElement(dictName)) { - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); - return NS_ERROR_OUT_OF_MEMORY; - } - } - - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); - } + if (ans.failed) + return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } @@ -364,87 +356,44 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary) NS_IMETHODIMP mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary) { - mSpellCheckingEngine = nsnull; + nsresult rv; + nsCString *contractId; if (aDictionary.IsEmpty()) { + mCurrentEngineContractId = nsnull; + mSpellCheckingEngine = nsnull; return NS_OK; } - nsresult rv; - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - rv = engine->SetDictionary(PromiseFlatString(aDictionary).get()); - if (NS_SUCCEEDED(rv)) { - mSpellCheckingEngine = engine; - - nsCOMPtr personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); - mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); - - return NS_OK; - } + if (!mDictionariesMap.Get(aDictionary, &contractId)){ + NS_WARNING("Dictionary not found"); + return NS_ERROR_NOT_AVAILABLE; } - // We could not find any engine with the requested dictionary - return NS_ERROR_NOT_AVAILABLE; -} + if (!mCurrentEngineContractId || !mCurrentEngineContractId->Equals(*contractId)){ + mSpellCheckingEngine = do_GetService(contractId->get(), &rv); + if (NS_FAILED(rv)) + return rv; -NS_IMETHODIMP -mozSpellChecker::CheckCurrentDictionary() -{ - // Check if the current engine has any dictionaries available. If not, - // the last dictionary has just been uninstalled, and we need to stop using - // the engine. - if (mSpellCheckingEngine) { - nsXPIDLString dictname; - - mSpellCheckingEngine->GetDictionary(getter_Copies(dictname)); - - // We still have a dictionary, so keep using that. - if (!dictname.IsEmpty()) { - return NS_OK; - } - - // Our current dictionary has gone, so we cannot use the engine anymore. - mSpellCheckingEngine = nsnull; + mCurrentEngineContractId = contractId; } - // We have no current engine. Pick one. - nsresult rv; - nsCOMArray spellCheckingEngines; - rv = GetEngineList(&spellCheckingEngines); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { - nsCOMPtr engine = spellCheckingEngines[i]; - - nsXPIDLString dictname; - - engine->GetDictionary(getter_Copies(dictname)); - - if (!dictname.IsEmpty()) { - mSpellCheckingEngine = engine; - - nsCOMPtr personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); - mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); - - nsXPIDLString language; - nsresult rv; - nsCOMPtr serv = do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &rv); - if(serv && NS_SUCCEEDED(rv)) { - serv->GetUtil(language.get(), getter_AddRefs(mConverter)); - } - - return NS_OK; - } + nsresult res; + res = mSpellCheckingEngine->SetDictionary(PromiseFlatString(aDictionary).get()); + if(NS_FAILED(res)){ + NS_WARNING("Dictionary load failed"); + return res; } - // There are no dictionaries available - return NS_OK; + mSpellCheckingEngine->SetPersonalDictionary(mPersonalDictionary); + + nsXPIDLString language; + + nsCOMPtr serv(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &res)); + if(serv && NS_SUCCEEDED(res)){ + res = serv->GetUtil(language.get(),getter_AddRefs(mConverter)); + } + return res; } nsresult @@ -528,10 +477,11 @@ mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *ou } nsresult -mozSpellChecker::GetEngineList(nsCOMArray* aSpellCheckingEngines) +mozSpellChecker::InitSpellCheckDictionaryMap() { nsresult rv; PRBool hasMoreEngines; + nsTArray contractIds; nsCOMPtr catMgr = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); if (!catMgr) @@ -558,24 +508,52 @@ mozSpellChecker::GetEngineList(nsCOMArray* aSpellChecki if (NS_FAILED(rv)) return rv; + contractIds.AppendElement(contractId); + } + + contractIds.AppendElement(NS_LITERAL_CSTRING(DEFAULT_SPELL_CHECKER)); + + // Retrieve dictionaries from all available spellcheckers and + // fill mDictionariesMap hash (only the first dictionary with the + // each name is used). + for (PRUint32 i=0;i < contractIds.Length();i++){ + PRUint32 count,k; + PRUnichar **words; + + const nsCString& contractId = contractIds[i]; + // Try to load spellchecker engine. Ignore errors silently // except for the last one (HunSpell). nsCOMPtr engine = do_GetService(contractId.get(), &rv); - if (NS_SUCCEEDED(rv)) { - aSpellCheckingEngines->AppendObject(engine); - } - } + if (NS_FAILED(rv)){ + // Fail if not succeeded to load HunSpell. Ignore errors + // for external spellcheck engines. + if (i==contractIds.Length()-1){ + return rv; + } - // Try to load HunSpell spellchecker engine. - nsCOMPtr engine = - do_GetService(DEFAULT_SPELL_CHECKER, &rv); - if (NS_FAILED(rv)) { - // Fail if not succeeded to load HunSpell. Ignore errors - // for external spellcheck engines. - return rv; + continue; + } + + engine->GetDictionaryList(&words,&count); + for(k=0;kAppendObject(engine); return NS_OK; } diff --git a/extensions/spellcheck/src/mozSpellChecker.h b/extensions/spellcheck/src/mozSpellChecker.h index 10f52f21a9a..142c0ee15f4 100644 --- a/extensions/spellcheck/src/mozSpellChecker.h +++ b/extensions/spellcheck/src/mozSpellChecker.h @@ -20,7 +20,6 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com - * Jesper Kristensen * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -40,7 +39,6 @@ #define mozSpellChecker_h__ #include "nsCOMPtr.h" -#include "nsCOMArray.h" #include "nsISpellChecker.h" #include "nsString.h" #include "nsITextServicesDocument.h" @@ -77,13 +75,17 @@ public: NS_IMETHOD GetDictionaryList(nsTArray *aDictionaryList); NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary); NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary); - NS_IMETHOD CheckCurrentDictionary(); protected: nsCOMPtr mConverter; nsCOMPtr mTsDoc; nsCOMPtr mPersonalDictionary; + // Hastable maps directory name to the spellchecker contract ID + nsClassHashtable mDictionariesMap; + + nsString mDictionaryName; + nsCString *mCurrentEngineContractId; nsCOMPtr mSpellCheckingEngine; PRBool mFromStart; @@ -91,6 +93,6 @@ protected: nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex); - nsresult GetEngineList(nsCOMArray *aDictionaryList); + nsresult InitSpellCheckDictionaryMap(); }; #endif // mozSpellChecker_h__ diff --git a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp index e4f917a8307..8ab97debbca 100644 --- a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp +++ b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp @@ -59,7 +59,39 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(mozHunspellDirProvider) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozSpellChecker, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellI18NManager) -NS_GENERIC_FACTORY_CONSTRUCTOR(mozInlineSpellChecker) + +// This special constructor for the inline spell checker asks the inline +// spell checker if we can create spell checking objects at all (ie, if there +// are any dictionaries loaded) before trying to create one. The static +// CanEnableInlineSpellChecking caches the value so this will be faster (we +// have to run this code for every edit box we create, as well as for every +// right click in those edit boxes). +static nsresult +mozInlineSpellCheckerConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + if (! mozInlineSpellChecker::CanEnableInlineSpellChecking()) + return NS_ERROR_FAILURE; + + nsresult rv; + + *aResult = NULL; + if (NULL != aOuter) { + rv = NS_ERROR_NO_AGGREGATION; + return rv; + } + + mozInlineSpellChecker* inst = new mozInlineSpellChecker(); + if (NULL == inst) { + rv = NS_ERROR_OUT_OF_MEMORY; + return rv; + } + NS_ADDREF(inst); + rv = inst->QueryInterface(aIID, aResult); + NS_RELEASE(inst); + + return rv; +} NS_DEFINE_NAMED_CID(MOZ_HUNSPELL_CID); NS_DEFINE_NAMED_CID(HUNSPELLDIRPROVIDER_CID); diff --git a/extensions/spellcheck/tests/Makefile.in b/extensions/spellcheck/tests/Makefile.in deleted file mode 100644 index 686fe393fd4..00000000000 --- a/extensions/spellcheck/tests/Makefile.in +++ /dev/null @@ -1,48 +0,0 @@ -# -# ***** 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.org code. -# -# 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 ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests - -include $(DEPTH)/config/autoconf.mk - -DIRS = chrome - -include $(topsrcdir)/config/rules.mk diff --git a/extensions/spellcheck/tests/chrome/Makefile.in b/extensions/spellcheck/tests/chrome/Makefile.in deleted file mode 100644 index da60413beb7..00000000000 --- a/extensions/spellcheck/tests/chrome/Makefile.in +++ /dev/null @@ -1,54 +0,0 @@ -# -# ***** 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.org code. -# -# 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 ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome - -include $(DEPTH)/config/autoconf.mk - -DIRS = base map - -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = test_add_remove_dictionaries.xul \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/base/Makefile.in b/extensions/spellcheck/tests/chrome/base/Makefile.in deleted file mode 100644 index 04159b2ca7a..00000000000 --- a/extensions/spellcheck/tests/chrome/base/Makefile.in +++ /dev/null @@ -1,52 +0,0 @@ -# -# ***** 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.org code. -# -# 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 ***** - -DEPTH = ../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome/base - -include $(DEPTH)/config/autoconf.mk -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = base_utf.dic \ - base_utf.aff \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/base/base_utf.aff b/extensions/spellcheck/tests/chrome/base/base_utf.aff deleted file mode 100644 index 493157b3018..00000000000 --- a/extensions/spellcheck/tests/chrome/base/base_utf.aff +++ /dev/null @@ -1,198 +0,0 @@ -# OpenOffice.org’s en_US.aff file -# with Unicode apostrophe: ’ - -SET UTF-8 -TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ' - -MAXNGRAMSUGS 1 -WORDCHARS .'’ - -PFX A Y 1 -PFX A 0 re . - -PFX I Y 1 -PFX I 0 in . - -PFX U Y 1 -PFX U 0 un . - -PFX C Y 1 -PFX C 0 de . - -PFX E Y 1 -PFX E 0 dis . - -PFX F Y 1 -PFX F 0 con . - -PFX K Y 1 -PFX K 0 pro . - -SFX V N 2 -SFX V e ive e -SFX V 0 ive [^e] - -SFX N Y 3 -SFX N e ion e -SFX N y ication y -SFX N 0 en [^ey] - -SFX X Y 3 -SFX X e ions e -SFX X y ications y -SFX X 0 ens [^ey] - -SFX H N 2 -SFX H y ieth y -SFX H 0 th [^y] - -SFX Y Y 1 -SFX Y 0 ly . - -SFX G Y 2 -SFX G e ing e -SFX G 0 ing [^e] - -SFX J Y 2 -SFX J e ings e -SFX J 0 ings [^e] - -SFX D Y 4 -SFX D 0 d e -SFX D y ied [^aeiou]y -SFX D 0 ed [^ey] -SFX D 0 ed [aeiou]y - -SFX T N 4 -SFX T 0 st e -SFX T y iest [^aeiou]y -SFX T 0 est [aeiou]y -SFX T 0 est [^ey] - -SFX R Y 4 -SFX R 0 r e -SFX R y ier [^aeiou]y -SFX R 0 er [aeiou]y -SFX R 0 er [^ey] - -SFX Z Y 4 -SFX Z 0 rs e -SFX Z y iers [^aeiou]y -SFX Z 0 ers [aeiou]y -SFX Z 0 ers [^ey] - -SFX S Y 4 -SFX S y ies [^aeiou]y -SFX S 0 s [aeiou]y -SFX S 0 es [sxzh] -SFX S 0 s [^sxzhy] - -SFX P Y 3 -SFX P y iness [^aeiou]y -SFX P 0 ness [aeiou]y -SFX P 0 ness [^y] - -SFX M Y 1 -SFX M 0 's . - -SFX B Y 3 -SFX B 0 able [^aeiou] -SFX B 0 able ee -SFX B e able [^aeiou]e - -SFX L Y 1 -SFX L 0 ment . - -REP 88 -REP a ei -REP ei a -REP a ey -REP ey a -REP ai ie -REP ie ai -REP are air -REP are ear -REP are eir -REP air are -REP air ere -REP ere air -REP ere ear -REP ere eir -REP ear are -REP ear air -REP ear ere -REP eir are -REP eir ere -REP ch te -REP te ch -REP ch ti -REP ti ch -REP ch tu -REP tu ch -REP ch s -REP s ch -REP ch k -REP k ch -REP f ph -REP ph f -REP gh f -REP f gh -REP i igh -REP igh i -REP i uy -REP uy i -REP i ee -REP ee i -REP j di -REP di j -REP j gg -REP gg j -REP j ge -REP ge j -REP s ti -REP ti s -REP s ci -REP ci s -REP k cc -REP cc k -REP k qu -REP qu k -REP kw qu -REP o eau -REP eau o -REP o ew -REP ew o -REP oo ew -REP ew oo -REP ew ui -REP ui ew -REP oo ui -REP ui oo -REP ew u -REP u ew -REP oo u -REP u oo -REP u oe -REP oe u -REP u ieu -REP ieu u -REP ue ew -REP ew ue -REP uff ough -REP oo ieu -REP ieu oo -REP ier ear -REP ear ier -REP ear air -REP air ear -REP w qu -REP qu w -REP z ss -REP ss z -REP shun tion -REP shun sion -REP shun cion -McDonalds’sá/w -McDonald’sszá/g3) st:McDonald’s po:noun_prs is:TRANS -McDonald’sszal/g4) st:McDonald’s po:noun_prs is:INSTR -McDonald’ssal/w diff --git a/extensions/spellcheck/tests/chrome/base/base_utf.dic b/extensions/spellcheck/tests/chrome/base/base_utf.dic deleted file mode 100644 index b2b536d2854..00000000000 --- a/extensions/spellcheck/tests/chrome/base/base_utf.dic +++ /dev/null @@ -1,29 +0,0 @@ -28 -created/U -create/XKVNGADS -imply/GNSDX -natural/PUY -like/USPBY -convey/BDGS -look/GZRDS -text -hello -said -sawyer -NASA -rotten -day -tomorrow -seven -FAQ/SM -can’t -doesn’t -etc -won’t -lip -text -horrifying -speech -suggest -uncreate/V -Hunspell diff --git a/extensions/spellcheck/tests/chrome/map/Makefile.in b/extensions/spellcheck/tests/chrome/map/Makefile.in deleted file mode 100644 index f9da7553bee..00000000000 --- a/extensions/spellcheck/tests/chrome/map/Makefile.in +++ /dev/null @@ -1,52 +0,0 @@ -# -# ***** 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.org code. -# -# 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 ***** - -DEPTH = ../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = extensions/spellcheck/tests/chrome/map - -include $(DEPTH)/config/autoconf.mk -include $(topsrcdir)/config/rules.mk - -_TEST_FILES = maputf.dic \ - maputf.aff \ - $(NULL) - -libs:: $(_TEST_FILES) - $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff --git a/extensions/spellcheck/tests/chrome/map/maputf.aff b/extensions/spellcheck/tests/chrome/map/maputf.aff deleted file mode 100644 index 30edb2a7850..00000000000 --- a/extensions/spellcheck/tests/chrome/map/maputf.aff +++ /dev/null @@ -1,11 +0,0 @@ -# With MAP suggestion, Hunspell can add missing accents to a word. - -SET UTF-8 - -# switch off ngram suggestion for testing -MAXNGRAMSUGS 0 - -MAP 3 -MAP uúü -MAP öóo -MAP ß(ss) diff --git a/extensions/spellcheck/tests/chrome/map/maputf.dic b/extensions/spellcheck/tests/chrome/map/maputf.dic deleted file mode 100644 index 1c6fa8d0589..00000000000 --- a/extensions/spellcheck/tests/chrome/map/maputf.dic +++ /dev/null @@ -1,4 +0,0 @@ -3 -Frühstück -tükörfúró -groß diff --git a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul b/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul deleted file mode 100644 index 8299c2a9313..00000000000 --- a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul +++ /dev/null @@ -1,110 +0,0 @@ - - - - - Add and remove dictionaries test - - - - From 7ace54bf1749463e1b21ba4d9d072ff9b8091e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Tue, 6 Sep 2011 16:22:57 +0200 Subject: [PATCH 005/172] Bug 684444 - Clicking on a background tab's close button selects the tab before closing it. r=enn --- browser/base/content/tabbrowser.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 36834523204..a90760bcfde 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -4039,11 +4039,14 @@ this.style.MozUserFocus = ''; - + From 4cf9767a35f194a67f0c42ab753dcb7af91a7d81 Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Tue, 6 Sep 2011 10:44:55 -0400 Subject: [PATCH 006/172] Bug 507186, check source of command events so that checkboxes with an attached command will update preferences properly, r=neil --- toolkit/content/tests/chrome/Makefile.in | 1 + .../content/tests/chrome/test_preferences.xul | 14 ++++++++ .../window_preferences_commandretarget.xul | 36 +++++++++++++++++++ toolkit/content/widgets/preferences.xml | 4 ++- 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 toolkit/content/tests/chrome/window_preferences_commandretarget.xul diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index 65d11630b67..9220e32b07e 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -84,6 +84,7 @@ _TEST_FILES = findbar_window.xul \ window_preferences.xul \ window_preferences2.xul \ window_preferences3.xul \ + window_preferences_commandretarget.xul \ test_autocomplete2.xul \ test_autocomplete3.xul \ test_autocomplete4.xul \ diff --git a/toolkit/content/tests/chrome/test_preferences.xul b/toolkit/content/tests/chrome/test_preferences.xul index 40de9e96422..e972980aade 100644 --- a/toolkit/content/tests/chrome/test_preferences.xul +++ b/toolkit/content/tests/chrome/test_preferences.xul @@ -421,6 +421,14 @@ WritePrefsToPreferences(aPrefWindow, kPrefValueSet2); } + function RunCheckCommandRedirect(aPrefWindow) + { + GetXULElement(aPrefWindow, "checkbox").click(); + ok(GetXULElement(aPrefWindow, "tests.static_preference_bool").value, "redirected command bool"); + GetXULElement(aPrefWindow, "checkbox").click(); + ok(!GetXULElement(aPrefWindow, "tests.static_preference_bool").value, "redirected command bool"); + } + function RunResetPrefTest(aPrefWindow) { // try resetting the prefs to default values @@ -502,10 +510,16 @@ ok(found.file_data === expected.file_data, "non-instant reset deferred file" ); } + function RunTestCommandRedirect() + { + openDialog("window_preferences_commandretarget.xul", "", "modal", RunCheckCommandRedirect, true); + } + function RunTest() { RunTestInstant(); RunTestNonInstant(); + RunTestCommandRedirect(); SimpleTest.finish(); } ]]> diff --git a/toolkit/content/tests/chrome/window_preferences_commandretarget.xul b/toolkit/content/tests/chrome/window_preferences_commandretarget.xul new file mode 100644 index 00000000000..77c6fd18ce7 --- /dev/null +++ b/toolkit/content/tests/chrome/window_preferences_commandretarget.xul @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/toolkit/content/widgets/preferences.xml b/toolkit/content/widgets/preferences.xml index 18310c3883f..f24a5dbdf20 100644 --- a/toolkit/content/widgets/preferences.xml +++ b/toolkit/content/widgets/preferences.xml @@ -1253,7 +1253,9 @@ // This "command" event handler tracks changes made to preferences by - // the user in this window. + // the user in this window. + if (event.sourceEvent) + event = event.sourceEvent; this.userChangedValue(event.target); From 1ee6dfd0be3d46919a034b6f6a504c1e6097cca3 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Tue, 6 Sep 2011 08:26:35 -0700 Subject: [PATCH 007/172] Bug 684783 - Fix intermittent test_errorhandler.js. r=philikon --- services/sync/tests/unit/test_errorhandler.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/services/sync/tests/unit/test_errorhandler.js b/services/sync/tests/unit/test_errorhandler.js index c785406f33d..c8d975e55e7 100644 --- a/services/sync/tests/unit/test_errorhandler.js +++ b/services/sync/tests/unit/test_errorhandler.js @@ -767,7 +767,7 @@ add_test(function test_sync_engine_generic_fail() { let engine = Engines.get("catapult"); engine.enabled = true; engine.sync = function sync() { - Svc.Obs.notify("weave:engine:sync:error", "", "steam"); + Svc.Obs.notify("weave:engine:sync:error", "", "catapult"); }; let log = Log4Moz.repository.getLogger("Sync.ErrorHandler"); @@ -776,6 +776,12 @@ add_test(function test_sync_engine_generic_fail() { Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() { Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog); + // Put these checks here, not after sync(), so that we aren't racing the + // log handler... which resets everything just a few lines below! + _("Status.engines: " + JSON.stringify(Status.engines)); + do_check_eq(Status.engines["catapult"], ENGINE_UNKNOWN_FAIL); + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); + // Test Error log was written on SYNC_FAILED_PARTIAL. let entries = logsdir.directoryEntries; do_check_true(entries.hasMoreElements()); @@ -789,14 +795,11 @@ add_test(function test_sync_engine_generic_fail() { server.stop(run_next_test); }); - do_check_eq(Status.engines["steam"], undefined); + do_check_eq(Status.engines["catapult"], undefined); do_check_true(setUp()); Service.sync(); - - do_check_eq(Status.engines["steam"], ENGINE_UNKNOWN_FAIL); - do_check_eq(Status.service, SYNC_FAILED_PARTIAL); }); // This test should be the last one since it monkeypatches the engine object @@ -808,7 +811,7 @@ add_test(function test_engine_applyFailed() { engine.enabled = true; delete engine.exception; engine.sync = function sync() { - Svc.Obs.notify("weave:engine:sync:applied", {newFailed:1}, "steam"); + Svc.Obs.notify("weave:engine:sync:applied", {newFailed:1}, "catapult"); }; let log = Log4Moz.repository.getLogger("Sync.ErrorHandler"); @@ -817,6 +820,9 @@ add_test(function test_engine_applyFailed() { Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() { Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog); + do_check_eq(Status.engines["catapult"], ENGINE_APPLY_FAIL); + do_check_eq(Status.service, SYNC_FAILED_PARTIAL); + // Test Error log was written on SYNC_FAILED_PARTIAL. let entries = logsdir.directoryEntries; do_check_true(entries.hasMoreElements()); @@ -830,12 +836,9 @@ add_test(function test_engine_applyFailed() { server.stop(run_next_test); }); - do_check_eq(Status.engines["steam"], undefined); + do_check_eq(Status.engines["catapult"], undefined); do_check_true(setUp()); Service.sync(); - - do_check_eq(Status.engines["steam"], ENGINE_APPLY_FAIL); - do_check_eq(Status.service, SYNC_FAILED_PARTIAL); }); From d2b57dbde10dcf02dc3bf91f5455556a50963e9a Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Tue, 6 Sep 2011 11:57:58 -0400 Subject: [PATCH 008/172] Bug 684592 - In about:memory, update descriptions of heap-dirty and heap-committed, and only report heap-committed on Windows. rs=njn --HG-- extra : rebase_source : 66b94efceb15920edcb6a27380450e52b25c5847 --- xpcom/base/nsMemoryReporterManager.cpp | 41 ++++++++++++++------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 2b7d629f059..f06a3de091d 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -308,30 +308,22 @@ NS_MEMORY_REPORTER_IMPLEMENT(HeapCommitted, KIND_OTHER, UNITS_BYTES, GetHeapCommitted, - "This number reported only for completeness; it is not particularly " - "meaningful. On Windows, all mapped memory is committed (because jemalloc's " - "MALLOC_DECOMMIT flag is set). Thus heap-committed should equal " - "heap-allocated + heap-unallocated. Elsewhere, jemalloc uses " - "madvise(DONT_NEED) to instruct the OS to drop the physical memory backing " - "pages the allocator doesn't need. In this case, jemalloc counts the memory " - "as 'committed', but it's not taking up any space in physical memory or in " - "the swap file.") + "Memory mapped by the heap allocator that is committed, i.e. in physical " + "memory or paged to disk. When heap-committed is larger than " + "heap-allocated, the difference between the two values is likely due to " + "external fragmentation; that is, the allocator allocated a large block of " + "memory and is unable to decommit it because a small part of that block is " + "currently in use.") NS_MEMORY_REPORTER_IMPLEMENT(HeapDirty, "heap-dirty", KIND_OTHER, UNITS_BYTES, GetHeapDirty, - "Memory mapped by the heap allocator that was once part of an allocation " - "but which is now not allocated to the application. Since the application " - "may have modified the memory while it was allocated, we count this memory " - "as \"committed\", that is, as taking up space in physical memory or the " - "swap file. If memory is fragmented, and the allocator is unable to return " - "some mostly-free pages to the operating system because they contain a few " - "live objects, you might see a large value here. But even in the absence " - "of fragmentation, the allocator might not return some dirty memory to the " - "OS as an optimization, under the assumption that the application will need " - "the memory again soon.") + "Memory which the allocator could return to the operating system, but " + "hasn't. The allocator keeps this memory around as an optimization, so it " + "doesn't have to ask the OS the next time it needs to fulfill a request. " + "This value is typically not larger than a few megabytes.") #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) #include @@ -446,8 +438,19 @@ nsMemoryReporterManager::Init() REGISTER(Private); #endif -#if defined(HAVE_JEMALLOC_STATS) +#if defined(HAVE_JEMALLOC_STATS) && defined(XP_WIN) + // heap-committed is only meaningful where we have MALLOC_DECOMMIT defined + // (currently, just on Windows). Elsewhere, it's the same as + // stats->mapped, which is heap-allocated + heap-unallocated. + // + // Ideally, we'd check for MALLOC_DECOMMIT in the #if defined above, but + // MALLOC_DECOMMIT is defined in jemalloc.c, not a header, so we'll just + // have to settle for the OS check for now. + REGISTER(HeapCommitted); +#endif + +#if defined(HAVE_JEMALLOC_STATS) REGISTER(HeapDirty); #elif defined(XP_MACOSX) && !defined(MOZ_MEMORY) REGISTER(HeapZone0Committed); From 8c1a4598d046ce0eab0a98d5e6ec00643c17307b Mon Sep 17 00:00:00 2001 From: "bjarne@runitsoft.com" Date: Tue, 6 Sep 2011 12:23:26 -0400 Subject: [PATCH 009/172] Bug 667593 - Intermittent runtime abort in netwerk/test/unit/test_bug650955.js r=michal.novotny --- netwerk/cache/nsDiskCacheDevice.cpp | 81 +++++++++++++++++------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/netwerk/cache/nsDiskCacheDevice.cpp b/netwerk/cache/nsDiskCacheDevice.cpp index 8749050786b..8b1454000c3 100644 --- a/netwerk/cache/nsDiskCacheDevice.cpp +++ b/netwerk/cache/nsDiskCacheDevice.cpp @@ -86,6 +86,38 @@ static const char DISK_CACHE_DEVICE_ID[] = { "disk" }; using namespace mozilla; +class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable { +public: + nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device, + nsCacheEntry * entry, + nsDiskCacheBinding * binding) + : mCanceled(PR_FALSE), + mEntry(entry), + mDevice(device), + mBinding(binding) + { + } + + NS_IMETHOD Run() + { + nsCacheServiceAutoLock lock; +#ifdef PR_LOGGING + CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this)); +#endif + if (!mCanceled) { + (void) mDevice->DeactivateEntry_Private(mEntry, mBinding); + } + return NS_OK; + } + + void CancelEvent() { mCanceled = PR_TRUE; } +private: + PRBool mCanceled; + nsCacheEntry *mEntry; + nsDiskCacheDevice *mDevice; + nsDiskCacheBinding *mBinding; +}; + /****************************************************************************** * nsDiskCacheEvictor * @@ -141,6 +173,12 @@ nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord * mapRecord) nsDiskCacheBinding * binding = mBindery->FindActiveBinding(mapRecord->HashNumber()); if (binding) { + // If the entry is pending deactivation, cancel deactivation and doom + // the entry + if (binding->mDeactivateEvent) { + binding->mDeactivateEvent->CancelEvent(); + binding->mDeactivateEvent = nsnull; + } // We are currently using this entry, so all we can do is doom it. // Since we're enumerating the records, we don't want to call // DeleteRecord when nsCacheService::DoomEntry() calls us back. @@ -453,39 +491,6 @@ nsDiskCacheDevice::GetDeviceID() return DISK_CACHE_DEVICE_ID; } -class nsDiskCacheDeviceDeactivateEntryEvent : public nsRunnable { -public: - nsDiskCacheDeviceDeactivateEntryEvent(nsDiskCacheDevice *device, - nsCacheEntry * entry, - nsDiskCacheBinding * binding) - : mCanceled(PR_FALSE), - mEntry(entry), - mDevice(device), - mBinding(binding) - { - } - - NS_IMETHOD Run() - { - nsCacheServiceAutoLock lock; -#ifdef PR_LOGGING - CACHE_LOG_DEBUG(("nsDiskCacheDeviceDeactivateEntryEvent[%p]\n", this)); -#endif - if (!mCanceled) { - (void) mDevice->DeactivateEntry_Private(mEntry, mBinding); - } - return NS_OK; - } - - void CancelEvent() { mCanceled = PR_TRUE; } -private: - PRBool mCanceled; - nsCacheEntry *mEntry; - nsDiskCacheDevice *mDevice; - nsDiskCacheBinding *mBinding; -}; - - /** * FindEntry - * @@ -635,6 +640,11 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * entry) if (binding) { NS_ASSERTION(!binding->mCacheEntry->Key()->Equals(*entry->Key()), "BindEntry called for already bound entry!"); + // If the entry is pending deactivation, cancel deactivation + if (binding->mDeactivateEvent) { + binding->mDeactivateEvent->CancelEvent(); + binding->mDeactivateEvent = nsnull; + } nsCacheService::DoomEntry(binding->mCacheEntry); binding = nsnull; } @@ -676,6 +686,11 @@ nsDiskCacheDevice::BindEntry(nsCacheEntry * entry) // XXX if debug : compare keys for hashNumber collision if (!oldBinding->mCacheEntry->IsDoomed()) { + // If the old entry is pending deactivation, cancel deactivation + if (oldBinding->mDeactivateEvent) { + oldBinding->mDeactivateEvent->CancelEvent(); + oldBinding->mDeactivateEvent = nsnull; + } // we've got a live one! nsCacheService::DoomEntry(oldBinding->mCacheEntry); // storage will be delete when oldBinding->mCacheEntry is Deactivated From 0038e2e573253945a4489d723428da50d26cdf2f Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 6 Sep 2011 18:35:43 +0200 Subject: [PATCH 010/172] Bug 684807 - Annotate crashtest assertions on Android; r=philor --- content/xbl/crashtests/crashtests.list | 2 +- docshell/base/crashtests/crashtests.list | 4 ++-- dom/base/crashtests/crashtests.list | 4 ++-- editor/composer/src/crashtests/crashtests.list | 2 +- editor/libeditor/base/crashtests/crashtests.list | 4 ++-- layout/base/crashtests/crashtests.list | 14 +++++++------- layout/forms/crashtests/crashtests.list | 4 ++-- layout/generic/crashtests/crashtests.list | 12 ++++++------ layout/tables/crashtests/crashtests.list | 4 ++-- .../base/src/tree/src/crashtests/crashtests.list | 2 +- parser/htmlparser/tests/crashtests/crashtests.list | 2 +- security/manager/ssl/crashtests/crashtests.list | 2 +- testing/crashtest/sanity/crashtests.list | 2 +- 13 files changed, 29 insertions(+), 29 deletions(-) diff --git a/content/xbl/crashtests/crashtests.list b/content/xbl/crashtests/crashtests.list index 5fe71ece792..1bd563aeeb7 100644 --- a/content/xbl/crashtests/crashtests.list +++ b/content/xbl/crashtests/crashtests.list @@ -31,7 +31,7 @@ load 464863-1.xhtml load 472260-1.xhtml load 477878-1.html load 492978-1.xul -load 493123-1.xhtml +asserts-if(Android,2) load 493123-1.xhtml load 495354-1.xhtml load 507628-1.xhtml load 507991-1.xhtml diff --git a/docshell/base/crashtests/crashtests.list b/docshell/base/crashtests/crashtests.list index 84788db96a6..eddb1e67a83 100644 --- a/docshell/base/crashtests/crashtests.list +++ b/docshell/base/crashtests/crashtests.list @@ -5,8 +5,8 @@ load 430124-1.html load 430628-1.html load 432114-1.html load 432114-2.html -load 436900-1.html -asserts(0-2) load 436900-2.html # bug 566159 +asserts-if(Android,2) load 436900-1.html +asserts(0-3) load 436900-2.html # bug 566159 load 500328-1.html load 514779-1.xhtml load 614499-1.html diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index b84d1c26afc..f1f83c0c3cd 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -1,4 +1,4 @@ -load 90613-1.html +asserts-if(Android,1) load 90613-1.html load 244933-1.html load 275912-1.html load 327571-1.html @@ -21,7 +21,7 @@ load 473284.xul load 499006-1.html load 499006-2.html load 502617.html -asserts(1) load 504224.html # bug 564098 +asserts(1-2) load 504224.html # bug 564098 load 603531.html load 601247.html load 609560-1.xhtml diff --git a/editor/composer/src/crashtests/crashtests.list b/editor/composer/src/crashtests/crashtests.list index 487d96b45a5..c42ade4e0f9 100644 --- a/editor/composer/src/crashtests/crashtests.list +++ b/editor/composer/src/crashtests/crashtests.list @@ -1,4 +1,4 @@ -load 351236-1.html +asserts-if(Android,2) load 351236-1.html load 407062-1.html load 419563-1.xhtml skip-if(winWidget) load 428844-1.html # bug 471185 diff --git a/editor/libeditor/base/crashtests/crashtests.list b/editor/libeditor/base/crashtests/crashtests.list index 84c1cb7318e..0619a5624f2 100644 --- a/editor/libeditor/base/crashtests/crashtests.list +++ b/editor/libeditor/base/crashtests/crashtests.list @@ -6,5 +6,5 @@ load 407256-1.html load 430624-1.html load 459613.html load 475132-1.xhtml -load 633709.xhtml -asserts(6) load 636074-1.html # Bug 439258, charged to the wrong test due to bug 635550 +asserts-if(Android,6) load 633709.xhtml +asserts-if(!Android,6) load 636074-1.html # Bug 439258, charged to the wrong test due to bug 635550 diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 2e735578588..bcf17aad7d3 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -41,10 +41,10 @@ load 265736-1.html load 265736-2.html asserts(2) load 265899-1.html # bug 575011 load 265973-1.html -asserts(8-12) load 265986-1.html # Bug 512405 -asserts(4) load 265999-1.html # bug 575011 +asserts(6-12) load 265986-1.html # Bug 512405 +asserts(2-4) load 265999-1.html # bug 575011 load 266222-1.html -asserts(5-7) load 266360-1.html # bug 575011 / bug 576358 +asserts(3-7) load 266360-1.html # bug 575011 / bug 576358 asserts(4) load 266445-1.html # Bug 575011 load 268157-1.html load 269566-1.html @@ -257,12 +257,12 @@ load 469861-2.xhtml load 471594-1.xhtml load 479114-1.html load 477333-1.xhtml -load 477731-1.html +asserts-if(Android,6) load 477731-1.html # 479360-1.xhtml will assert 6 times due to bug 439258 and then make the test # after the test after it also assert 6 times. -asserts(6) load 479360-1.xhtml # Bug 439258 -load 480686-1.html -asserts(6) load 481806-1.html # Bug 439258 +asserts-if(!Android,6) load 479360-1.xhtml # Bug 439258 +asserts-if(Android,6) load 480686-1.html +asserts-if(!Android,6) load 481806-1.html # Bug 439258 load 483604-1.xhtml load 485501-1.html load 487544-1.html diff --git a/layout/forms/crashtests/crashtests.list b/layout/forms/crashtests/crashtests.list index 398467eec6b..4633fbe2817 100644 --- a/layout/forms/crashtests/crashtests.list +++ b/layout/forms/crashtests/crashtests.list @@ -20,11 +20,11 @@ load 367587-1.html load 370703-1.html load 370940-1.html load 373586-1.xhtml -asserts(8-10) load 378413-1.xhtml # bug 424225, bug 402850? +asserts(5-10) load 378413-1.xhtml # bug 424225, bug 402850? load 380116-1.xhtml load 382212-1.xhtml load 382610-1.html -asserts-if(winWidget,1) load 383887-1.html # bug 576434 +asserts-if(winWidget||Android,1) load 383887-1.html # bug 576434 load 386554-1.html load 388374-1.xhtml load 388374-2.html diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 75b32fa62ad..b82f81092c7 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -71,7 +71,7 @@ load 372376-1.xhtml load 373859-1.html load 373868-1.xhtml load 379217-1.xhtml -asserts(2) load 379217-2.xhtml # Bug 439204 +asserts(1-2) load 379217-2.xhtml # Bug 439204 load 379917-1.xhtml load 380012-1.html load 381152-1.html @@ -124,7 +124,7 @@ load 393956-4.html load 394237-1.html load 394820-1.html load 394818-1.html -load 394818-2.html +asserts-if(Android,1) load 394818-2.html load 395316-1.html load 395450-1.xhtml load 397007-1.html @@ -183,8 +183,8 @@ load 413085-2.html load 413582-1.xhtml load 413582-2.html load 413712-1.xhtml -load 414061-1.html -asserts(6) load 414180-1.xul # Bug 439258 +asserts-if(Android,6) load 414061-1.html +asserts-if(!Android,6) load 414180-1.xul # Bug 439258 load 414719-1.html load 415685-1.html load 416264-1.html @@ -280,7 +280,7 @@ load 477928.html load 478131-1.html load 478170-1.html load 478185-1.html -asserts(1) load 479938-1.html # Bug 575011 +asserts-if(!Android,1) load 479938-1.html # Bug 575011 load 480345-1.html skip-if(Android) load 481921.html load 489462-1.html @@ -368,7 +368,7 @@ load text-overflow-bug666751-2.html asserts(2) load text-overflow-bug670564.xhtml # asserts(2) for bug 436470 load text-overflow-bug671796.xhtml load 667025.html -asserts(14) load 673770.html # bug 569193 and bug 459597 +asserts(14) asserts-if(Android,8) load 673770.html # bug 569193 and bug 459597 load 679933-1.html load 682649-1.html load 683702-1.xhtml diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index 3ef81e68757..d87cde5ed38 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -80,7 +80,7 @@ load 399209-1.xhtml load 403249-1.html load 403579-1.html load 404301-1.xhtml -asserts(2) load 408753-1.xhtml # Bug 512749 +asserts(1-2) load 408753-1.xhtml # Bug 512749 load 410426-1.html load 411582.xhtml load 413091.xhtml @@ -105,6 +105,6 @@ load 573354-1.xhtml load 576890-1.html load 576890-2.html load 576890-3.html -asserts(0-1) load 595758-1.xhtml # Bug 453871 +asserts(0-3) load 595758-1.xhtml # Bug 453871 load 595758-2.xhtml load 678447-1.html diff --git a/layout/xul/base/src/tree/src/crashtests/crashtests.list b/layout/xul/base/src/tree/src/crashtests/crashtests.list index 0a7e8bc76c4..276951f2bcc 100644 --- a/layout/xul/base/src/tree/src/crashtests/crashtests.list +++ b/layout/xul/base/src/tree/src/crashtests/crashtests.list @@ -1,7 +1,7 @@ load 307298-1.xul load 309732-1.xul load 309732-2.xul -load 366583-1.xul +asserts-if(Android,3) load 366583-1.xul asserts-if(winWidget,0-4) load 380217-1.xul # bug 616710 load 382444-1.html load 391178-1.xhtml diff --git a/parser/htmlparser/tests/crashtests/crashtests.list b/parser/htmlparser/tests/crashtests/crashtests.list index 972766e6301..30f0ca0342b 100644 --- a/parser/htmlparser/tests/crashtests/crashtests.list +++ b/parser/htmlparser/tests/crashtests/crashtests.list @@ -30,7 +30,7 @@ load 269095-1.html load 286733-1.html load 286733-2.html load 299036-1.html -load 328751-1.html +asserts-if(Android,1) load 328751-1.html load 408939-1.html load 423373-1.html load 445171-1.html diff --git a/security/manager/ssl/crashtests/crashtests.list b/security/manager/ssl/crashtests/crashtests.list index e0939a84107..ef7f0aa9aa8 100644 --- a/security/manager/ssl/crashtests/crashtests.list +++ b/security/manager/ssl/crashtests/crashtests.list @@ -1,2 +1,2 @@ -asserts-if(browserIsRemote,1) load 327524-1.html # bug 582297 +asserts-if(browserIsRemote,0-1) load 327524-1.html # bug 582297 asserts-if(browserIsRemote,1) load 398665-1.html # bug 582297 diff --git a/testing/crashtest/sanity/crashtests.list b/testing/crashtest/sanity/crashtests.list index 35b67063d3d..0711d1a457a 100644 --- a/testing/crashtest/sanity/crashtests.list +++ b/testing/crashtest/sanity/crashtests.list @@ -1,2 +1,2 @@ -load data:text/html, +asserts-if(Android,10) load data:text/html, load data:text/html,PASS From 54a297e878565cbb733c9fc575ad2cfe62964da7 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 6 Sep 2011 09:06:07 -0700 Subject: [PATCH 011/172] Bug 674998 - Remove InvokeSessionGuard; it's bug-prone and we should self-host instead (r=bhackett) --- js/src/jsarray.cpp | 51 +++++++++++-------- js/src/jsinterp.cpp | 106 +++------------------------------------ js/src/jsinterp.h | 26 ---------- js/src/jsinterpinlines.h | 102 ------------------------------------- js/src/jsprvtd.h | 1 - js/src/jsstr.cpp | 40 +++++++-------- js/src/vm/Stack.cpp | 7 --- 7 files changed, 55 insertions(+), 278 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index bbd60f37f09..58c98a40799 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1836,10 +1836,11 @@ js_MergeSort(void *src, size_t nel, size_t elsize, struct CompareArgs { JSContext *context; - InvokeSessionGuard session; + InvokeArgsGuard args; + Value fval; - CompareArgs(JSContext *cx) - : context(cx) + CompareArgs(JSContext *cx, Value fval) + : context(cx), fval(fval) {} }; @@ -1860,15 +1861,21 @@ sort_compare(void *arg, const void *a, const void *b, int *result) if (!JS_CHECK_OPERATION_LIMIT(cx)) return JS_FALSE; - InvokeSessionGuard &session = ca->session; - session[0] = *av; - session[1] = *bv; + InvokeArgsGuard &args = ca->args; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &args)) + return JS_FALSE; + + args.calleeHasBeenReset(); + args.calleev() = ca->fval; + args.thisv() = UndefinedValue(); + args[0] = *av; + args[1] = *bv; - if (!session.invoke(cx)) + if (!Invoke(cx, args)) return JS_FALSE; jsdouble cmp; - if (!ToNumber(cx, session.rval(), &cmp)) + if (!ToNumber(cx, args.rval(), &cmp)) return JS_FALSE; /* Clamp cmp to -1, 0, 1. */ @@ -2105,10 +2112,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) } while (++i != newlen); } } else { - CompareArgs ca(cx); - if (!ca.session.start(cx, fval, UndefinedValue(), 2)) - return false; - + CompareArgs ca(cx, fval); if (!js_MergeSort(vec, size_t(newlen), sizeof(Value), comparator_stack_cast(sort_compare), &ca, mergesort_tmp, @@ -2962,16 +2966,13 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) */ argc = 3 + REDUCE_MODE(mode); - InvokeSessionGuard session; - if (!session.start(cx, ObjectValue(*callable), thisv, argc)) - return JS_FALSE; - MUST_FLOW_THROUGH("out"); JSBool ok = JS_TRUE; JSBool cond; Value objv = ObjectValue(*obj); AutoValueRooter tvr(cx); + InvokeArgsGuard args; for (jsuint i = start; i != end; i += step) { JSBool hole; ok = JS_CHECK_OPERATION_LIMIT(cx) && @@ -2981,23 +2982,29 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp) if (hole) continue; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) + return false; + /* * Push callable and 'this', then args. We must do this for every * iteration around the loop since Invoke clobbers its arguments. */ + args.calleeHasBeenReset(); + args.calleev() = ObjectValue(*callable); + args.thisv() = thisv; uintN argi = 0; if (REDUCE_MODE(mode)) - session[argi++] = *vp; - session[argi++] = tvr.value(); - session[argi++] = Int32Value(i); - session[argi] = objv; + args[argi++] = *vp; + args[argi++] = tvr.value(); + args[argi++] = Int32Value(i); + args[argi] = objv; /* Do the call. */ - ok = session.invoke(cx); + ok = Invoke(cx, args); if (!ok) break; - const Value &rval = session.rval(); + const Value &rval = args.rval(); if (mode > MAP) cond = js_ValueToBoolean(rval); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 69a96faae0e..cca992cceb6 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -623,8 +623,6 @@ js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp) bool js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct) { - /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */ - CallArgs args = argsRef; JS_ASSERT(args.argc() <= StackSpace::ARGS_LENGTH_MAX); @@ -661,6 +659,12 @@ js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construc if (fun->isNative()) return CallJSNative(cx, fun->u.n.native, args); +#ifdef JS_TRACER + if (TRACE_RECORDER(cx)) + AbortRecording(cx, "attempt to reenter VM while recording"); + LeaveTrace(cx); +#endif + TypeMonitorCall(cx, args, construct); /* Get pointer to new frame/slots, prepare arguments. */ @@ -685,104 +689,6 @@ js::InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construc return ok; } -bool -InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc) -{ -#ifdef JS_TRACER - if (TRACE_RECORDER(cx)) - AbortRecording(cx, "attempt to reenter VM while recording"); - LeaveTrace(cx); -#endif - - /* Always push arguments, regardless of optimized/normal invoke. */ - ContextStack &stack = cx->stack; - if (!stack.pushInvokeArgs(cx, argc, &args_)) - return false; - - /* Callees may clobber 'this' or 'callee'. */ - savedCallee_ = args_.calleev() = calleev; - savedThis_ = args_.thisv() = thisv; - - /* If anyone (through jsdbgapi) finds this frame, make it safe. */ - MakeRangeGCSafe(args_.argv(), args_.argc()); - - do { - /* Hoist dynamic checks from scripted Invoke. */ - if (!calleev.isObject()) - break; - JSObject &callee = calleev.toObject(); - if (callee.getClass() != &FunctionClass) - break; - JSFunction *fun = callee.getFunctionPrivate(); - if (fun->isNative()) - break; - script_ = fun->script(); - if (fun->isHeavyweight()) - break; - - /* - * The frame will remain pushed even when the callee isn't active which - * will affect the observable current global, so avoid any change. - */ - if (callee.getGlobal() != GetGlobalForScopeChain(cx)) - break; - - /* Push the stack frame once for the session. */ - if (!stack.pushInvokeFrame(cx, args_, INITIAL_NONE, &ifg_)) - return false; - - /* - * Update the 'this' type of the callee according to the value given, - * along with the types of any missing arguments. These will be the - * same across all calls. - */ - TypeScript::SetThis(cx, script_, thisv); - for (unsigned i = argc; i < fun->nargs; i++) - TypeScript::SetArgument(cx, script_, i, types::Type::UndefinedType()); - - StackFrame *fp = ifg_.fp(); -#ifdef JS_METHODJIT - /* Hoist dynamic checks from RunScript. */ - mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, false, - mjit::CompileRequest_JIT); - if (status == mjit::Compile_Error) - return false; - if (status != mjit::Compile_Okay) - break; - /* Cannot also cache the raw code pointer; it can change. */ - - /* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */ - JS_CHECK_RECURSION(cx, return false); - stackLimit_ = stack.space().getStackLimit(cx, REPORT_ERROR); - if (!stackLimit_) - return false; - - stop_ = script_->code + script_->length - 1; - JS_ASSERT(*stop_ == JSOP_STOP); -#endif - - /* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */ - nformals_ = fp->numFormalArgs(); - formals_ = fp->formalArgs(); - actuals_ = args_.argv(); - JS_ASSERT(actuals_ == fp->actualArgs()); - return true; - } while (0); - - /* - * Use the normal invoke path. - * - * The callee slot gets overwritten during an unoptimized Invoke, so we - * cache it here and restore it before every Invoke call. The 'this' value - * does not get overwritten, so we can fill it here once. - */ - if (ifg_.pushed()) - ifg_.pop(); - formals_ = actuals_ = args_.argv(); - nformals_ = (unsigned)-1; - return true; -} - bool js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv, Value *rval) diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 8351ad1047d..f4e013db1b2 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -187,32 +187,6 @@ extern bool InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv, Value *rval); -/* - * Natives like sort/forEach/replace call Invoke repeatedly with the same - * callee, this, and number of arguments. To optimize this, such natives can - * start an "invoke session" to factor out much of the dynamic setup logic - * required by a normal Invoke. Usage is: - * - * InvokeSessionGuard session(cx); - * if (!session.start(cx, callee, thisp, argc, &session)) - * ... - * - * while (...) { - * // write actual args (not callee, this) - * session[0] = ... - * ... - * session[argc - 1] = ... - * - * if (!session.invoke(cx, session)) - * ... - * - * ... = session.rval(); - * } - * - * // session ended by ~InvokeSessionGuard - */ -class InvokeSessionGuard; - /* * InvokeConstructor* implement a function call from a constructor context * (e.g. 'new') handling the the creation of the new 'this' object. diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 4667700a9e9..d2907ae948e 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -71,108 +71,6 @@ class AutoPreserveEnumerators { } }; -class InvokeSessionGuard -{ - InvokeArgsGuard args_; - InvokeFrameGuard ifg_; - Value savedCallee_, savedThis_; - Value *formals_, *actuals_; - unsigned nformals_; - JSScript *script_; - Value *stackLimit_; - jsbytecode *stop_; - - bool optimized() const { return ifg_.pushed(); } - - public: - InvokeSessionGuard() : args_(), ifg_() {} - ~InvokeSessionGuard() {} - - bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc); - bool invoke(JSContext *cx); - - bool started() const { - return args_.pushed(); - } - - Value &operator[](unsigned i) const { - JS_ASSERT(i < argc()); - Value &arg = i < nformals_ ? formals_[i] : actuals_[i]; - JS_ASSERT_IF(optimized(), &arg == &ifg_.fp()->canonicalActualArg(i)); - JS_ASSERT_IF(!optimized(), &arg == &args_[i]); - return arg; - } - - uintN argc() const { - return args_.argc(); - } - - const Value &rval() const { - return optimized() ? ifg_.fp()->returnValue() : args_.rval(); - } -}; - -inline bool -InvokeSessionGuard::invoke(JSContext *cx) -{ - /* N.B. Must be kept in sync with Invoke */ - - /* Refer to canonical (callee, this) for optimized() sessions. */ - formals_[-2] = savedCallee_; - formals_[-1] = savedThis_; - - /* Prevent spurious accessing-callee-after-rval assert. */ - args_.calleeHasBeenReset(); - - if (!optimized()) - return Invoke(cx, args_); - - /* - * Update the types of each argument. The 'this' type and missing argument - * types were handled when the invoke session was created. - */ - for (unsigned i = 0; i < Min(argc(), nformals_); i++) - types::TypeScript::SetArgument(cx, script_, i, (*this)[i]); - -#ifdef JS_METHODJIT - mjit::JITScript *jit = script_->getJIT(false /* !constructing */); - if (!jit) { - /* Watch in case the code was thrown away due a recompile. */ - mjit::CompileStatus status = mjit::TryCompile(cx, script_, false); - if (status == mjit::Compile_Error) - return false; - JS_ASSERT(status == mjit::Compile_Okay); - jit = script_->getJIT(false); - } - void *code; - if (!(code = jit->invokeEntry)) - return Invoke(cx, args_); -#endif - - /* Clear any garbage left from the last Invoke. */ - StackFrame *fp = ifg_.fp(); - fp->resetCallFrame(script_); - - JSBool ok; - { - AutoPreserveEnumerators preserve(cx); - args_.setActive(); /* From js::Invoke(InvokeArgsGuard) overload. */ - Probes::enterJSFun(cx, fp->fun(), script_); -#ifdef JS_METHODJIT - ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_, /* partial = */ false); - cx->regs().pc = stop_; -#else - cx->regs().pc = script_->code; - ok = Interpret(cx, cx->fp()); -#endif - Probes::exitJSFun(cx, fp->fun(), script_); - args_.setInactive(); - } - - /* Don't clobber callee with rval; rval gets read from fp->rval. */ - return ok; -} - namespace detail { template class PrimitiveBehavior { }; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 4a8adc15b5c..9331dac8387 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -134,7 +134,6 @@ class AutoStringRooter; class ExecuteArgsGuard; class InvokeFrameGuard; class InvokeArgsGuard; -class InvokeSessionGuard; class StringBuffer; class TraceRecorder; struct TraceMonitor; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 03c5d75e354..61e963c13c5 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1622,8 +1622,7 @@ struct ReplaceData jsint leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ bool calledBack; /* record whether callback has been called */ - InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */ - InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */ + InvokeArgsGuard args; /* arguments for lambda call */ StringBuffer sb; /* buffer built during DoMatch */ }; @@ -1750,6 +1749,10 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t JSObject *lambda = rdata.lambda; if (lambda) { + PreserveRegExpStatics staticsGuard(res); + if (!staticsGuard.init(cx)) + return false; + /* * In the lambda case, not only do we find the replacement string's * length, we compute repstr and return it via rdata for use within @@ -1761,36 +1764,33 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t uintN p = res->parenCount(); uintN argc = 1 + p + 2; - InvokeSessionGuard &session = rdata.session; - if (!session.started()) { - Value lambdav = ObjectValue(*lambda); - if (!session.start(cx, lambdav, UndefinedValue(), argc)) - return false; - } - - PreserveRegExpStatics staticsGuard(res); - if (!staticsGuard.init(cx)) + InvokeArgsGuard &args = rdata.args; + if (!args.pushed() && !cx->stack.pushInvokeArgs(cx, argc, &args)) return false; + args.calleeHasBeenReset(); + args.calleev() = ObjectValue(*lambda); + args.thisv() = UndefinedValue(); + /* Push $&, $1, $2, ... */ uintN argi = 0; - if (!res->createLastMatch(cx, &session[argi++])) + if (!res->createLastMatch(cx, &args[argi++])) return false; for (size_t i = 0; i < res->parenCount(); ++i) { - if (!res->createParen(cx, i + 1, &session[argi++])) + if (!res->createParen(cx, i + 1, &args[argi++])) return false; } /* Push match index and input string. */ - session[argi++].setInt32(res->matchStart()); - session[argi].setString(rdata.str); + args[argi++].setInt32(res->matchStart()); + args[argi].setString(rdata.str); - if (!session.invoke(cx)) + if (!Invoke(cx, args)) return false; /* root repstr: rdata is on the stack, so scanned by conservative gc. */ - JSString *repstr = ValueToString_TestForStringInline(cx, session.rval()); + JSString *repstr = ValueToString_TestForStringInline(cx, args.rval()); if (!repstr) return false; rdata.repstr = repstr->ensureLinear(cx); @@ -2082,10 +2082,10 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata /* lambda(matchStr, matchStart, textstr) */ static const uint32 lambdaArgc = 3; - if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot)) + if (!cx->stack.pushInvokeArgs(cx, lambdaArgc, &rdata.args)) return false; - CallArgs &args = rdata.singleShot; + CallArgs &args = rdata.args; args.calleev().setObject(*rdata.lambda); args.thisv().setUndefined(); @@ -2094,7 +2094,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata sp[1].setInt32(fm.match()); sp[2].setString(rdata.str); - if (!Invoke(cx, rdata.singleShot)) + if (!Invoke(cx, rdata.args)) return false; JSString *repstr = js_ValueToString(cx, args.rval()); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index b72a7123f99..c6340afcfa1 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1018,13 +1018,6 @@ StackIter::settleOnNewState() continue; } - /* Censor pushed-but-not-active frames from InvokeSessionGuard. */ - if (containsCall && !calls_->active() && fp_->hasArgs() && - calls_->argv() == fp_->actualArgs()) { - popFrame(); - continue; - } - /* * As an optimization, there is no CallArgsList element pushed for * natives called directly by a script (compiled or interpreted). From ef3c437b57801d6df76e64fa7987a2b9b9b44f18 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Tue, 6 Sep 2011 19:06:47 +0100 Subject: [PATCH 012/172] Bug 684790 - Add crisp-edges to anim-css-strokewidth-1 tests to prevent intermittent Android failures. r=test-only --- .../reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-by-em-px.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-by-pct-pct.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-by-px-em.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-by-px-px.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-em-em.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-em-px.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-pct-pct.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-px-em.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-by-px-px.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-em-em.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-em-px.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-pct-pct.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-px-em.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-from-to-px-px.svg | 2 +- layout/reftests/svg/smil/style/anim-css-strokewidth-1-ref.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-to-em-em.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-to-em-px.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg | 2 +- .../svg/smil/style/anim-css-strokewidth-1-to-pct-pct.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-to-px-em.svg | 2 +- .../reftests/svg/smil/style/anim-css-strokewidth-1-to-px-px.svg | 2 +- 25 files changed, 25 insertions(+), 25 deletions(-) diff --git a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg index 006898a6a5e..1472123ee76 100644 --- a/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg +++ b/layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-em-em.svg @@ -4,7 +4,7 @@ class="reftest-wait" onload="go()"> + Mozilla Bug 545812 @@ -38,6 +39,23 @@ function run() { prevEnabled = SpecialPowers.getBoolPref("full-screen-api.enabled"); SpecialPowers.setBoolPref("full-screen-api.enabled", true); + // Test requesting full-screen mode in a long-running user-generated event handler. + // The request in the key handler should not be granted. + window.addEventListener("keypress", keyHandler, false); + synthesizeKey("VK_A", {}); +} + +function keyHandler(event) { + window.removeEventListener("keypress", keyHandler, false); + + // Busy loop until 2s has passed. We should then be past the 1 second threshold, and so + // our request for full-screen mode should be rejected. + var end = (new Date()).getTime() + 2000; + while ((new Date()).getTime() < end) { + ; // Wait... + } + document.body.mozRequestFullScreen(); + prevTrusted = SpecialPowers.getBoolPref("full-screen-api.allow-trusted-requests-only"); // Request full-screen from a non trusted context (this script isn't a user diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a5a578ac00d..cb7c0ee2f9c 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3351,6 +3351,10 @@ pref("alerts.disableSlidingEffect", false); pref("full-screen-api.enabled", false); pref("full-screen-api.allow-trusted-requests-only", true); pref("full-screen-api.key-input-restricted", true); + +// Time limit, in milliseconds, for nsEventStateManager::IsHandlingUserInput(). +// Used to detect long running handlers of user-generated events. +pref("dom.event.handling-user-input-time-limit", 1000); //3D Transforms pref("layout.3d-transforms.enabled", false); From a14c147d1bb87cdbf0b44be5506b159f0394a772 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Tue, 6 Sep 2011 15:19:31 -0500 Subject: [PATCH 025/172] Back out a38f82d29d97 (bug 652914) for causing reftest failures. --- layout/base/nsCSSRendering.cpp | 36 +++++++++++++++++++++++----------- layout/base/nsCSSRendering.h | 4 +--- layout/base/nsPresShell.cpp | 8 ++------ 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index f71e8571fd4..7bac24f2419 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1700,12 +1700,12 @@ DrawBackgroundColor(BackgroundClipState& aClipState, gfxContext *aCtx, aCtx->Restore(); } -nscolor -nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, - nsStyleContext* aStyleContext, - nsIFrame* aFrame, - PRBool& aDrawBackgroundImage, - PRBool& aDrawBackgroundColor) +static nscolor +DetermineBackgroundColorInternal(nsPresContext* aPresContext, + nsStyleContext* aStyleContext, + nsIFrame* aFrame, + PRBool& aDrawBackgroundImage, + PRBool& aDrawBackgroundColor) { aDrawBackgroundImage = PR_TRUE; aDrawBackgroundColor = PR_TRUE; @@ -1737,6 +1737,20 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, return bgColor; } +nscolor +nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, + nsStyleContext* aStyleContext, + nsIFrame* aFrame) +{ + PRBool drawBackgroundImage; + PRBool drawBackgroundColor; + return DetermineBackgroundColorInternal(aPresContext, + aStyleContext, + aFrame, + drawBackgroundImage, + drawBackgroundColor); +} + static gfxFloat ConvertGradientValueToPixels(const nsStyleCoord& aCoord, gfxFloat aFillLength, @@ -2304,11 +2318,11 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, PRBool drawBackgroundImage; PRBool drawBackgroundColor; - nscolor bgColor = DetermineBackgroundColor(aPresContext, - aBackgroundSC, - aForFrame, - drawBackgroundImage, - drawBackgroundColor); + nscolor bgColor = DetermineBackgroundColorInternal(aPresContext, + aBackgroundSC, + aForFrame, + drawBackgroundImage, + drawBackgroundColor); // At this point, drawBackgroundImage and drawBackgroundColor are // true if and only if we are actually supposed to paint an image or diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index bac9d7febd2..a57298ae595 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -211,9 +211,7 @@ struct nsCSSRendering { static nscolor DetermineBackgroundColor(nsPresContext* aPresContext, nsStyleContext* aStyleContext, - nsIFrame* aFrame, - PRBool& aDrawBackgroundImage, - PRBool& aDrawBackgroundColor); + nsIFrame* aFrame); /** * Render the background for an element using css rendering rules diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 91437466ae5..bacda5c31e0 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5846,14 +5846,10 @@ void PresShell::UpdateCanvasBackground() // style frame but we don't have access to the canvasframe here. It isn't // a problem because only a few frames can return something other than true // and none of them would be a canvas frame or root element style frame. - PRBool drawBackgroundImage; - PRBool drawBackgroundColor; mCanvasBackgroundColor = nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle, - rootStyleFrame, - drawBackgroundImage, - drawBackgroundColor); - if (drawBackgroundColor && GetPresContext()->IsRootContentDocument() && + rootStyleFrame); + if (GetPresContext()->IsRootContentDocument() && !IsTransparentContainerElement(mPresContext)) { mCanvasBackgroundColor = NS_ComposeColors(mPresContext->DefaultBackgroundColor(), mCanvasBackgroundColor); From 122978d46632b82321dbb47e0eac364eb9e1b745 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Tue, 6 Sep 2011 16:13:56 +0200 Subject: [PATCH 026/172] bug 684796 - fixing assertions for GC thing iterators. r=bhackett1024 --- js/src/jit-test/tests/basic/bug684796.js | 2 ++ js/src/jsgcinlines.h | 2 +- js/src/shell/js.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug684796.js diff --git a/js/src/jit-test/tests/basic/bug684796.js b/js/src/jit-test/tests/basic/bug684796.js new file mode 100644 index 00000000000..f541abdf7fc --- /dev/null +++ b/js/src/jit-test/tests/basic/bug684796.js @@ -0,0 +1,2 @@ +if (typeof mjitdatastats == "function") + mjitdatastats(); diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index ff64de8fc32..a7d1c40ee63 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -270,6 +270,7 @@ class CellIterImpl } void init(JSCompartment *comp, AllocKind kind) { + JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind)); firstThingOffset = Arena::firstThingOffset(kind); thingSize = Arena::thingSize(kind); aheader = comp->arenas.getFirstArena(kind); @@ -322,7 +323,6 @@ class CellIterUnderGC : public CellIterImpl { public: CellIterUnderGC(JSCompartment *comp, AllocKind kind) { JS_ASSERT(comp->rt->gcRunning); - comp->arenas.checkEmptyFreeList(kind); init(comp, kind); } }; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 9bda7b85f5c..31b38034a46 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -4086,7 +4086,7 @@ MJitDataStats(JSContext *cx, uintN argc, jsval *vp) { #ifdef JS_METHODJIT size_t n = 0; - IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallback); + IterateCells(cx, NULL, gc::FINALIZE_SCRIPT, &n, SumJitDataSizeCallback); JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n)); #else JS_SET_RVAL(cx, vp, JSVAL_VOID); From ffdc71e1a9f8b62e3f69b88f275f38b38a63fdc0 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 6 Sep 2011 14:41:37 -0700 Subject: [PATCH 027/172] Bug 684358: In nsSVGFEImageElement, replace GetOurDocument call with GetOwnerDoc, to bypass an unnecessary QI. r=roc --- content/svg/content/src/nsSVGFilters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index dcd770dd861..8d91b972359 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -5435,7 +5435,7 @@ nsSVGFEImageElement::LoadSVGImage(PRBool aForce, PRBool aNotify) NS_MakeAbsoluteURI(href, href, baseURI); // Make sure we don't get in a recursive death-spiral - nsIDocument* doc = GetOurDocument(); + nsIDocument* doc = GetOwnerDoc(); if (doc) { nsCOMPtr hrefAsURI; if (NS_SUCCEEDED(StringToURI(href, doc, getter_AddRefs(hrefAsURI)))) { From 01f1da4a4a700771473da3baa6510ee326141cf7 Mon Sep 17 00:00:00 2001 From: Atul Aggarwal Date: Thu, 1 Sep 2011 03:06:26 +0530 Subject: [PATCH 028/172] Bug 661962: Remove mostly-unused variable 'rv' from nsINIParser::Init (moving it inside the #ifdef block where it's used). r=bsmedberg --- xpcom/glue/nsINIParser.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/xpcom/glue/nsINIParser.cpp b/xpcom/glue/nsINIParser.cpp index b0f7be7e4df..6db34112afa 100644 --- a/xpcom/glue/nsINIParser.cpp +++ b/xpcom/glue/nsINIParser.cpp @@ -88,8 +88,6 @@ private: nsresult nsINIParser::Init(nsILocalFile* aFile) { - nsresult rv; - /* open the file. Don't use OpenANSIFileDesc, because you mustn't pass FILE* across shared library boundaries, which may be using different CRTs */ @@ -98,13 +96,13 @@ nsINIParser::Init(nsILocalFile* aFile) #ifdef XP_WIN nsAutoString path; - rv = aFile->GetPath(path); + nsresult rv = aFile->GetPath(path); NS_ENSURE_SUCCESS(rv, rv); fd = _wfopen(path.get(), READ_BINARYMODE); #else nsCAutoString path; - rv = aFile->GetNativePath(path); + aFile->GetNativePath(path); fd = fopen(path.get(), READ_BINARYMODE); #endif From 8976b9c5993b32a441cd1dc4e9398b3bb66dcad2 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 6 Sep 2011 16:50:49 -0700 Subject: [PATCH 029/172] Bug 683040: Override new method IsToAnimation instead of old method TreatSingleValueAsStatic, to handle "to animation" for animateMotion. r=birtles --- .../src/SVGMotionSMILAnimationFunction.cpp | 23 +++---- .../src/SVGMotionSMILAnimationFunction.h | 3 +- .../motion/animateMotion-to-overridden-1.svg | 61 +++++++++++++++++++ layout/reftests/svg/smil/motion/reftest.list | 1 + 4 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg diff --git a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp index c51b5d06f97..81647e54923 100644 --- a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp +++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp @@ -383,6 +383,18 @@ SVGMotionSMILAnimationFunction:: CheckKeyPoints(); } +PRBool +SVGMotionSMILAnimationFunction::IsToAnimation() const +{ + // Rely on inherited method, but not if we have an child or a |path| + // attribute, because they'll override any 'to' attr we might have. + // NOTE: We can't rely on mPathSourceType, because it might not have been + // set to a useful value yet (or it might be stale). + return !GetFirstMpathChild(&mAnimationElement->AsElement()) && + !HasAttr(nsGkAtoms::path) && + nsSMILAnimationFunction::IsToAnimation(); +} + void SVGMotionSMILAnimationFunction::CheckKeyPoints() { @@ -485,15 +497,4 @@ SVGMotionSMILAnimationFunction::UnsetRotate() mHasChanged = PR_TRUE; } -PRBool -SVGMotionSMILAnimationFunction::TreatSingleValueAsStatic() const -{ - // has two more ways that we could be just sampling a single - // value -- via path attribute and the element, with a path - // description that just includes a single "move" command. - return (mPathSourceType == ePathSourceType_ValuesAttr || - mPathSourceType == ePathSourceType_PathAttr || - mPathSourceType == ePathSourceType_Mpath); -} - } // namespace mozilla diff --git a/content/svg/content/src/SVGMotionSMILAnimationFunction.h b/content/svg/content/src/SVGMotionSMILAnimationFunction.h index 7a5c53c27ad..5d192bffe22 100644 --- a/content/svg/content/src/SVGMotionSMILAnimationFunction.h +++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.h @@ -85,7 +85,8 @@ protected: NS_OVERRIDE virtual nsresult GetValues(const nsISMILAttr& aSMILAttr, nsSMILValueArray& aResult); NS_OVERRIDE virtual void CheckValueListDependentAttrs(PRUint32 aNumValues); - NS_OVERRIDE virtual PRBool TreatSingleValueAsStatic() const; + + NS_OVERRIDE virtual PRBool IsToAnimation() const; void CheckKeyPoints(); nsresult SetKeyPoints(const nsAString& aKeyPoints, nsAttrValue& aResult); diff --git a/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg b/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg new file mode 100644 index 00000000000..cca246567c3 --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-to-overridden-1.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/reftest.list b/layout/reftests/svg/smil/motion/reftest.list index 5735f2597dd..9e0fe60f29d 100644 --- a/layout/reftests/svg/smil/motion/reftest.list +++ b/layout/reftests/svg/smil/motion/reftest.list @@ -6,6 +6,7 @@ == animateMotion-rotate-1a.svg lime.svg == animateMotion-rotate-1b.svg lime.svg == animateMotion-rotate-2.svg lime.svg +== animateMotion-to-overridden-1.svg lime.svg == animateMotion-values-linear-1.svg animateMotion-values-linear-1-ref.svg == animateMotion-values-paced-1a.svg animateMotion-values-paced-1-ref.svg == animateMotion-values-paced-1b.svg animateMotion-values-paced-1-ref.svg From 67b38c4820edc7460009bb56043840b5b0c391b4 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Tue, 6 Sep 2011 16:50:57 -0700 Subject: [PATCH 030/172] Bug 683040 followup: Extra reftests for animateMotion & indefinite to-animation. r=birtles --HG-- rename : layout/reftests/svg/smil/anim-indefinite-to-1.svg => layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg rename : layout/reftests/svg/smil/anim-indefinite-to-2.svg => layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg --- .../svg/smil/motion/animateMotion-indefinite-to-1.svg | 10 ++++++++++ .../svg/smil/motion/animateMotion-indefinite-to-2.svg | 11 +++++++++++ layout/reftests/svg/smil/motion/reftest.list | 2 ++ 3 files changed, 23 insertions(+) create mode 100644 layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg create mode 100644 layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg diff --git a/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg new file mode 100644 index 00000000000..12c62da2c8c --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-1.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg new file mode 100644 index 00000000000..82f5a61a055 --- /dev/null +++ b/layout/reftests/svg/smil/motion/animateMotion-indefinite-to-2.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/layout/reftests/svg/smil/motion/reftest.list b/layout/reftests/svg/smil/motion/reftest.list index 9e0fe60f29d..b9ca38d62ad 100644 --- a/layout/reftests/svg/smil/motion/reftest.list +++ b/layout/reftests/svg/smil/motion/reftest.list @@ -3,6 +3,8 @@ == animateMotion-by-1.svg lime.svg == animateMotion-from-to-1.svg lime.svg +== animateMotion-indefinite-to-1.svg lime.svg +== animateMotion-indefinite-to-2.svg lime.svg == animateMotion-rotate-1a.svg lime.svg == animateMotion-rotate-1b.svg lime.svg == animateMotion-rotate-2.svg lime.svg From d5d6426ad8c44d3ef0776bdad01ca1167fd3aab8 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Tue, 6 Sep 2011 16:52:00 -0700 Subject: [PATCH 031/172] Bug 677427: netwerk/test/unit/test_bug650955.js fails on Android; r=bjarne --- netwerk/test/unit/test_bug650955.js | 22 ++++++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 2 -- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/netwerk/test/unit/test_bug650955.js b/netwerk/test/unit/test_bug650955.js index ae7667cfb63..9d4d533d01c 100644 --- a/netwerk/test/unit/test_bug650955.js +++ b/netwerk/test/unit/test_bug650955.js @@ -72,7 +72,29 @@ function nextTest() { function InitializeCacheDevices(memDevice, diskDevice) { this.start = function() { prefService.setBoolPref("browser.cache.memory.enable", memDevice); + if (memDevice) { + try { + cap = prefService.getIntPref("browser.cache.memory.capacity"); + } + catch(ex) { + cap = 0; + } + if (cap == 0) { + prefService.setIntPref("browser.cache.memory.capacity", 1024); + } + } prefService.setBoolPref("browser.cache.disk.enable", diskDevice); + if (diskDevice) { + try { + cap = prefService.getIntPref("browser.cache.disk.capacity"); + } + catch(ex) { + cap = 0; + } + if (cap == 0) { + prefService.setIntPref("browser.cache.disk.capacity", 1024); + } + } var channel = setupChannel("/bug650995", "Initial value"); channel.asyncOpen(new ChannelListener( nextTest, null), diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index e7f65ce09b5..c479ea0f4f0 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -67,8 +67,6 @@ skip-if = os == "android" [test_bug618835.js] [test_bug633743.js] [test_bug650955.js] -# Bug 677427: test fails consistently on Android -fail-if = os == "android" [test_bug652761.js] [test_bug651100.js] # Bug 675044: test fails consistently on Android From 08eeeda3367d8846e63efe617c46f64083e07766 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Wed, 7 Sep 2011 09:20:40 +0900 Subject: [PATCH 032/172] Bug 682184 part 1 - Rename nsSMILTimeValue::IsResolved to IsDefinite; r=dholbert --- content/smil/nsSMILAnimationController.cpp | 2 +- content/smil/nsSMILAnimationFunction.cpp | 4 +- content/smil/nsSMILInterval.cpp | 2 +- content/smil/nsSMILTimeContainer.cpp | 4 +- content/smil/nsSMILTimeValue.cpp | 6 +-- content/smil/nsSMILTimeValue.h | 49 ++++-------------- content/smil/nsSMILTimeValueSpec.cpp | 10 ++-- content/smil/nsSMILTimedElement.cpp | 50 +++++++++---------- .../svg/content/src/nsSVGAnimationElement.cpp | 4 +- 9 files changed, 52 insertions(+), 79 deletions(-) diff --git a/content/smil/nsSMILAnimationController.cpp b/content/smil/nsSMILAnimationController.cpp index 871aad10644..5d3614c8c4e 100644 --- a/content/smil/nsSMILAnimationController.cpp +++ b/content/smil/nsSMILAnimationController.cpp @@ -592,7 +592,7 @@ nsSMILAnimationController::DoMilestoneSamples() nsSMILTimeValue containerTimeValue = container->ParentToContainerTime(sampleTime); - if (!containerTimeValue.IsResolved()) + if (!containerTimeValue.IsDefinite()) continue; // Clamp the converted container time to non-negative values. diff --git a/content/smil/nsSMILAnimationFunction.cpp b/content/smil/nsSMILAnimationFunction.cpp index b7866cc93a5..7d8d965401d 100644 --- a/content/smil/nsSMILAnimationFunction.cpp +++ b/content/smil/nsSMILAnimationFunction.cpp @@ -252,7 +252,7 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr, // If this interval is active, we must have a non-negative mSampleTime NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive, "Negative sample time for active animation"); - NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() || + NS_ABORT_IF_FALSE(mSimpleDuration.IsDefinite() || mSimpleDuration.IsIndefinite() || mLastValue, "Unresolved simple duration for active or frozen animation"); @@ -408,7 +408,7 @@ nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues, // its starting point. double simpleProgress = 0.0; - if (mSimpleDuration.IsResolved()) { + if (mSimpleDuration.IsDefinite()) { nsSMILTime dur = mSimpleDuration.GetMillis(); NS_ABORT_IF_FALSE(dur >= 0, "Simple duration should not be negative"); diff --git a/content/smil/nsSMILInterval.cpp b/content/smil/nsSMILInterval.cpp index bc92760125a..5812b15ad60 100644 --- a/content/smil/nsSMILInterval.cpp +++ b/content/smil/nsSMILInterval.cpp @@ -110,7 +110,7 @@ nsSMILInterval::End() void nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin) { - NS_ABORT_IF_FALSE(aBegin.Time().IsResolved(), + NS_ABORT_IF_FALSE(aBegin.Time().IsDefinite(), "Attempting to set unresolved begin time on interval"); NS_ABORT_IF_FALSE(!mBeginFixed, "Attempting to set begin time but the begin point is fixed"); diff --git a/content/smil/nsSMILTimeContainer.cpp b/content/smil/nsSMILTimeContainer.cpp index 436f281df3d..f0fad398765 100644 --- a/content/smil/nsSMILTimeContainer.cpp +++ b/content/smil/nsSMILTimeContainer.cpp @@ -261,7 +261,7 @@ nsSMILTimeContainer::GetNextMilestoneInParentTime( nsSMILTimeValue parentTime = ContainerToParentTime(mMilestoneEntries.Top().mMilestone.mTime); - if (!parentTime.IsResolved()) + if (!parentTime.IsDefinite()) return PR_FALSE; aNextMilestone = nsSMILMilestone(parentTime.GetMillis(), @@ -279,7 +279,7 @@ nsSMILTimeContainer::PopMilestoneElementsAtMilestone( return PR_FALSE; nsSMILTimeValue containerTime = ParentToContainerTime(aMilestone.mTime); - if (!containerTime.IsResolved()) + if (!containerTime.IsDefinite()) return PR_FALSE; nsSMILMilestone containerMilestone(containerTime.GetMillis(), diff --git a/content/smil/nsSMILTimeValue.cpp b/content/smil/nsSMILTimeValue.cpp index 5536fc24be2..13bc48cb0de 100644 --- a/content/smil/nsSMILTimeValue.cpp +++ b/content/smil/nsSMILTimeValue.cpp @@ -53,12 +53,12 @@ nsSMILTimeValue::CompareTo(const nsSMILTimeValue& aOther) const { PRInt8 result; - if (mState == STATE_RESOLVED) { - result = (aOther.mState == STATE_RESOLVED) + if (mState == STATE_DEFINITE) { + result = (aOther.mState == STATE_DEFINITE) ? Cmp(mMilliseconds, aOther.mMilliseconds) : -1; } else if (mState == STATE_INDEFINITE) { - if (aOther.mState == STATE_RESOLVED) + if (aOther.mState == STATE_DEFINITE) result = 1; else if (aOther.mState == STATE_INDEFINITE) result = 0; diff --git a/content/smil/nsSMILTimeValue.h b/content/smil/nsSMILTimeValue.h index 248104a7634..841020548be 100644 --- a/content/smil/nsSMILTimeValue.h +++ b/content/smil/nsSMILTimeValue.h @@ -66,36 +66,9 @@ * * Objects of this class may be in one of three states: * - * 1) The time is resolved and has a millisecond value - * 2) The time is indefinite - * 3) The time in unresolved - * - * There is considerable chance for confusion with regards to the indefinite - * state. Is it resolved? We adopt the convention that it is NOT resolved (but - * nor is it unresolved). This simplifies implementation as you can then write: - * - * if (time.IsResolved()) - * x = time.GetMillis() - * - * instead of: - * - * if (time.IsResolved() && !time.IsIndefinite()) - * x = time.GetMillis() - * - * Testing if a time is unresolved becomes more complicated but this is tested - * much less often. - * - * In summary: - * - * State | GetMillis | IsResolved | IsIndefinite - * --------------+--------------------+--------------------+------------------- - * Resolved | The millisecond | PR_TRUE | PR_FALSE - * | time | | - * --------------+--------------------+--------------------+------------------- - * Indefinite | LL_MAXINT | PR_FALSE | PR_TRUE - * --------------+--------------------+--------------------+------------------- - * Unresolved | LL_MAXINT | PR_FALSE | PR_FALSE - * + * 1) The time is resolved and has a definite millisecond value + * 2) The time is resolved and indefinite + * 3) The time is unresolved */ class nsSMILTimeValue @@ -110,7 +83,7 @@ public: // Creates a resolved time value explicit nsSMILTimeValue(nsSMILTime aMillis) : mMilliseconds(aMillis), - mState(STATE_RESOLVED) + mState(STATE_DEFINITE) { } // Named constructor to create an indefinite time value @@ -128,24 +101,24 @@ public: mMilliseconds = kUnresolvedMillis; } - PRBool IsResolved() const { return mState == STATE_RESOLVED; } void SetUnresolved() { mState = STATE_UNRESOLVED; mMilliseconds = kUnresolvedMillis; } + PRBool IsDefinite() const { return mState == STATE_DEFINITE; } nsSMILTime GetMillis() const { - NS_ABORT_IF_FALSE(mState == STATE_RESOLVED, - "GetMillis() called for unresolved time"); + NS_ABORT_IF_FALSE(mState == STATE_DEFINITE, + "GetMillis() called for unresolved or indefinite time"); - return mState == STATE_RESOLVED ? mMilliseconds : kUnresolvedMillis; + return mState == STATE_DEFINITE ? mMilliseconds : kUnresolvedMillis; } void SetMillis(nsSMILTime aMillis) { - mState = STATE_RESOLVED; + mState = STATE_DEFINITE; mMilliseconds = aMillis; } @@ -172,9 +145,9 @@ public: private: static nsSMILTime kUnresolvedMillis; - nsSMILTime mMilliseconds; + nsSMILTime mMilliseconds; enum { - STATE_RESOLVED, + STATE_DEFINITE, STATE_INDEFINITE, STATE_UNRESOLVED } mState; diff --git a/content/smil/nsSMILTimeValueSpec.cpp b/content/smil/nsSMILTimeValueSpec.cpp index 3fccf5e22ba..776cf1b49f4 100644 --- a/content/smil/nsSMILTimeValueSpec.cpp +++ b/content/smil/nsSMILTimeValueSpec.cpp @@ -182,7 +182,7 @@ nsSMILTimeValueSpec::HandleNewInterval(nsSMILInterval& aInterval, ConvertBetweenTimeContainers(baseInstance.Time(), aSrcContainer); // Apply offset - if (newTime.IsResolved()) { + if (newTime.IsDefinite()) { newTime.SetMillis(newTime.GetMillis() + mParams.mOffset.GetMillis()); } @@ -218,7 +218,7 @@ nsSMILTimeValueSpec::HandleChangedInstanceTime( ConvertBetweenTimeContainers(aBaseTime.Time(), aSrcContainer); // Apply offset - if (updatedTime.IsResolved()) { + if (updatedTime.IsDefinite()) { updatedTime.SetMillis(updatedTime.GetMillis() + mParams.mOffset.GetMillis()); } @@ -509,7 +509,7 @@ nsSMILTimeValueSpec::ConvertBetweenTimeContainers( { // If the source time is either indefinite or unresolved the result is going // to be the same - if (!aSrcTime.IsResolved()) + if (!aSrcTime.IsDefinite()) return aSrcTime; // Convert from source time container to our parent time container @@ -530,8 +530,8 @@ nsSMILTimeValueSpec::ConvertBetweenTimeContainers( // time. Just return the indefinite time. return docTime; - NS_ABORT_IF_FALSE(docTime.IsResolved(), - "ContainerToParentTime gave us an unresolved time"); + NS_ABORT_IF_FALSE(docTime.IsDefinite(), + "ContainerToParentTime gave us an unresolved time"); return dstContainer->ParentToContainerTime(docTime.GetMillis()); } diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index c754dc93749..3adb68b2866 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -918,7 +918,7 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) return NS_ERROR_FAILURE; } - if (duration.IsResolved() && duration.GetMillis() == 0L) { + if (duration.IsDefinite() && duration.GetMillis() == 0L) { mSimpleDur.SetIndefinite(); return NS_ERROR_FAILURE; } @@ -932,7 +932,7 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) // mSimpleDur should never be unresolved. ParseClockValue will either set // duration to resolved/indefinite/media or will return a failure code. - NS_ASSERTION(duration.IsResolved() || duration.IsIndefinite(), + NS_ASSERTION(duration.IsDefinite() || duration.IsIndefinite(), "Setting unresolved simple duration"); mSimpleDur = duration; @@ -961,7 +961,7 @@ nsSMILTimedElement::SetMin(const nsAString& aMinSpec) duration.SetMillis(0L); } - if (NS_FAILED(rv) || !duration.IsResolved()) { + if (NS_FAILED(rv) || !duration.IsDefinite()) { mMin.SetMillis(0L); return NS_ERROR_FAILURE; } @@ -997,12 +997,12 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) if (isMedia) duration.SetIndefinite(); - if (NS_FAILED(rv) || (!duration.IsResolved() && !duration.IsIndefinite())) { + if (NS_FAILED(rv) || (!duration.IsDefinite() && !duration.IsIndefinite())) { mMax.SetIndefinite(); return NS_ERROR_FAILURE; } - if (duration.IsResolved() && duration.GetMillis() <= 0L) { + if (duration.IsDefinite() && duration.GetMillis() <= 0L) { mMax.SetIndefinite(); return NS_ERROR_FAILURE; } @@ -1074,7 +1074,7 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) rv = nsSMILParserUtils::ParseClockValue(aRepeatDurSpec, &duration, nsSMILParserUtils::kClockValueAllowIndefinite); - if (NS_FAILED(rv) || (!duration.IsResolved() && !duration.IsIndefinite())) { + if (NS_FAILED(rv) || (!duration.IsDefinite() && !duration.IsIndefinite())) { mRepeatDur.SetUnresolved(); return NS_ERROR_FAILURE; } @@ -1608,7 +1608,7 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval, const nsSMILInstanceTime* aFixedBeginTime, nsSMILInterval& aResult) const { - NS_ABORT_IF_FALSE(!aFixedBeginTime || aFixedBeginTime->Time().IsResolved(), + NS_ABORT_IF_FALSE(!aFixedBeginTime || aFixedBeginTime->Time().IsDefinite(), "Unresolved begin time specified for interval start"); static const nsSMILTimeValue zeroTime(0L); @@ -1655,13 +1655,13 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval, do { tempBegin = GetNextGreaterOrEqual(mBeginInstances, beginAfter, beginPos); - if (!tempBegin || !tempBegin->Time().IsResolved()) { + if (!tempBegin || !tempBegin->Time().IsDefinite()) { return PR_FALSE; } } while (aReplacedInterval && tempBegin->GetBaseTime() == aReplacedInterval->Begin()); } - NS_ABORT_IF_FALSE(tempBegin && tempBegin->Time().IsResolved() && + NS_ABORT_IF_FALSE(tempBegin && tempBegin->Time().IsDefinite() && tempBegin->Time() >= beginAfter, "Got a bad begin time while fetching next interval"); @@ -1714,7 +1714,7 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval, // If we get two zero-length intervals in a row we will potentially have an // infinite loop so we break it here by searching for the next begin time // greater than tempEnd on the next time around. - if (tempEnd->Time().IsResolved() && tempBegin->Time() == tempEnd->Time()) { + if (tempEnd->Time().IsDefinite() && tempBegin->Time() == tempEnd->Time()) { if (prevIntervalWasZeroDur) { beginAfter.SetMillis(tempEnd->Time().GetMillis() + 1); prevIntervalWasZeroDur = PR_FALSE; @@ -1781,9 +1781,9 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, { nsSMILTimeValue result; - NS_ABORT_IF_FALSE(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(), + NS_ABORT_IF_FALSE(mSimpleDur.IsDefinite() || mSimpleDur.IsIndefinite(), "Unresolved simple duration in CalcActiveEnd"); - NS_ABORT_IF_FALSE(aBegin.IsResolved(), + NS_ABORT_IF_FALSE(aBegin.IsDefinite(), "Unresolved begin time in CalcActiveEnd"); if (mRepeatDur.IsIndefinite()) { @@ -1792,10 +1792,10 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, result = GetRepeatDuration(); } - if (aEnd.IsResolved()) { + if (aEnd.IsDefinite()) { nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis(); - if (result.IsResolved()) { + if (result.IsDefinite()) { result.SetMillis(NS_MIN(result.GetMillis(), activeDur)); } else { result.SetMillis(activeDur); @@ -1804,7 +1804,7 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, result = ApplyMinAndMax(result); - if (result.IsResolved()) { + if (result.IsDefinite()) { nsSMILTime activeEnd = result.GetMillis() + aBegin.GetMillis(); result.SetMillis(activeEnd); } @@ -1817,19 +1817,19 @@ nsSMILTimedElement::GetRepeatDuration() const { nsSMILTimeValue result; - if (mRepeatCount.IsDefinite() && mRepeatDur.IsResolved()) { - if (mSimpleDur.IsResolved()) { + if (mRepeatCount.IsDefinite() && mRepeatDur.IsDefinite()) { + if (mSimpleDur.IsDefinite()) { nsSMILTime activeDur = nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); result.SetMillis(NS_MIN(activeDur, mRepeatDur.GetMillis())); } else { result = mRepeatDur; } - } else if (mRepeatCount.IsDefinite() && mSimpleDur.IsResolved()) { + } else if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) { nsSMILTime activeDur = nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())); result.SetMillis(activeDur); - } else if (mRepeatDur.IsResolved()) { + } else if (mRepeatDur.IsDefinite()) { result = mRepeatDur; } else if (mRepeatCount.IsIndefinite()) { result.SetIndefinite(); @@ -1843,7 +1843,7 @@ nsSMILTimedElement::GetRepeatDuration() const nsSMILTimeValue nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const { - if (!aDuration.IsResolved() && !aDuration.IsIndefinite()) { + if (!aDuration.IsDefinite() && !aDuration.IsIndefinite()) { return aDuration; } @@ -1871,7 +1871,7 @@ nsSMILTimedElement::ActiveTimeToSimpleTime(nsSMILTime aActiveTime, { nsSMILTime result; - NS_ASSERTION(mSimpleDur.IsResolved() || mSimpleDur.IsIndefinite(), + NS_ASSERTION(mSimpleDur.IsDefinite() || mSimpleDur.IsIndefinite(), "Unresolved simple duration in ActiveTimeToSimpleTime"); NS_ASSERTION(aActiveTime >= 0, "Expecting non-negative active time"); // Note that a negative aActiveTime will give us a negative value for @@ -2035,7 +2035,7 @@ nsSMILTimedElement::SampleFillValue() const nsSMILInterval* prevInterval = GetPreviousInterval(); NS_ABORT_IF_FALSE(prevInterval, "Attempting to sample fill value but there is no previous interval"); - NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsResolved() && + NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() && prevInterval->End()->IsFixedTime(), "Attempting to sample fill value but the endpoint of the previous " "interval is not resolved and fixed"); @@ -2136,7 +2136,7 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const { // Work out what comes next: the interval end or the next repeat iteration nsSMILTimeValue nextRepeat; - if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsResolved()) { + if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) { nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + (mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis()); } @@ -2152,7 +2152,7 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const } // Apply the previously calculated milestone - if (nextMilestone.IsResolved()) { + if (nextMilestone.IsDefinite()) { aNextMilestone.mIsEnd = nextMilestone != nextRepeat; aNextMilestone.mTime = nextMilestone.GetMillis(); return PR_TRUE; @@ -2261,7 +2261,7 @@ nsSMILTimedElement::HaveResolvedEndTimes() const // mEndInstances is sorted so if the first time is not resolved then none of // them are - return mEndInstances[0]->Time().IsResolved(); + return mEndInstances[0]->Time().IsDefinite(); } PRBool diff --git a/content/svg/content/src/nsSVGAnimationElement.cpp b/content/svg/content/src/nsSVGAnimationElement.cpp index 245a53ab5cd..f585c20657e 100644 --- a/content/svg/content/src/nsSVGAnimationElement.cpp +++ b/content/svg/content/src/nsSVGAnimationElement.cpp @@ -216,7 +216,7 @@ nsSVGAnimationElement::GetStartTime(float* retval) FlushAnimations(); nsSMILTimeValue startTime = mTimedElement.GetStartTime(); - if (!startTime.IsResolved()) + if (!startTime.IsDefinite()) return NS_ERROR_DOM_INVALID_STATE_ERR; *retval = float(double(startTime.GetMillis()) / PR_MSEC_PER_SEC); @@ -246,7 +246,7 @@ nsSVGAnimationElement::GetSimpleDuration(float* retval) // Not necessary to call FlushAnimations() for this nsSMILTimeValue simpleDur = mTimedElement.GetSimpleDuration(); - if (!simpleDur.IsResolved()) { + if (!simpleDur.IsDefinite()) { *retval = 0.f; return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } From a819b6b88ccb96a8696964102ec224a5d9b6db5c Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Wed, 7 Sep 2011 09:20:40 +0900 Subject: [PATCH 033/172] Bug 682184 part 2 - Add nsSMILTimeValue::IsResolved; r=dholbert --- content/smil/nsSMILAnimationFunction.cpp | 3 +-- content/smil/nsSMILInterval.cpp | 2 +- content/smil/nsSMILTimeValue.h | 12 +++++++++ content/smil/nsSMILTimeValueSpec.cpp | 2 +- content/smil/nsSMILTimedElement.cpp | 33 ++++++++++++------------ content/smil/nsSMILTimedElement.h | 2 +- 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/content/smil/nsSMILAnimationFunction.cpp b/content/smil/nsSMILAnimationFunction.cpp index 7d8d965401d..d6ce123831e 100644 --- a/content/smil/nsSMILAnimationFunction.cpp +++ b/content/smil/nsSMILAnimationFunction.cpp @@ -252,8 +252,7 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr, // If this interval is active, we must have a non-negative mSampleTime NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive, "Negative sample time for active animation"); - NS_ABORT_IF_FALSE(mSimpleDuration.IsDefinite() || - mSimpleDuration.IsIndefinite() || mLastValue, + NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() || mLastValue, "Unresolved simple duration for active or frozen animation"); // If we want to add but don't have a base value then just fail outright. diff --git a/content/smil/nsSMILInterval.cpp b/content/smil/nsSMILInterval.cpp index 5812b15ad60..81aa905c2aa 100644 --- a/content/smil/nsSMILInterval.cpp +++ b/content/smil/nsSMILInterval.cpp @@ -111,7 +111,7 @@ void nsSMILInterval::SetBegin(nsSMILInstanceTime& aBegin) { NS_ABORT_IF_FALSE(aBegin.Time().IsDefinite(), - "Attempting to set unresolved begin time on interval"); + "Attempting to set unresolved or indefinite begin time on interval"); NS_ABORT_IF_FALSE(!mBeginFixed, "Attempting to set begin time but the begin point is fixed"); // Check that we're not making an instance time dependent on itself. Such an diff --git a/content/smil/nsSMILTimeValue.h b/content/smil/nsSMILTimeValue.h index 841020548be..58e6c0e039f 100644 --- a/content/smil/nsSMILTimeValue.h +++ b/content/smil/nsSMILTimeValue.h @@ -69,6 +69,17 @@ * 1) The time is resolved and has a definite millisecond value * 2) The time is resolved and indefinite * 3) The time is unresolved + * + * In summary: + * + * State | GetMillis | IsDefinite | IsIndefinite | IsResolved + * -----------+-----------------+------------+--------------+------------ + * Definite | nsSMILTimeValue | PR_TRUE | PR_FALSE | PR_TRUE + * -----------+-----------------+------------+--------------+------------ + * Indefinite | -- | PR_FALSE | PR_TRUE | PR_TRUE + * -----------+-----------------+------------+--------------+------------ + * Unresolved | -- | PR_FALSE | PR_FALSE | PR_FALSE + * */ class nsSMILTimeValue @@ -101,6 +112,7 @@ public: mMilliseconds = kUnresolvedMillis; } + PRBool IsResolved() const { return mState != STATE_UNRESOLVED; } void SetUnresolved() { mState = STATE_UNRESOLVED; diff --git a/content/smil/nsSMILTimeValueSpec.cpp b/content/smil/nsSMILTimeValueSpec.cpp index 776cf1b49f4..faa88d8c277 100644 --- a/content/smil/nsSMILTimeValueSpec.cpp +++ b/content/smil/nsSMILTimeValueSpec.cpp @@ -531,7 +531,7 @@ nsSMILTimeValueSpec::ConvertBetweenTimeContainers( return docTime; NS_ABORT_IF_FALSE(docTime.IsDefinite(), - "ContainerToParentTime gave us an unresolved time"); + "ContainerToParentTime gave us an unresolved or indefinite time"); return dstContainer->ParentToContainerTime(docTime.GetMillis()); } diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index 3adb68b2866..9a781e8b32d 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -932,7 +932,7 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) // mSimpleDur should never be unresolved. ParseClockValue will either set // duration to resolved/indefinite/media or will return a failure code. - NS_ASSERTION(duration.IsDefinite() || duration.IsIndefinite(), + NS_ABORT_IF_FALSE(duration.IsResolved(), "Setting unresolved simple duration"); mSimpleDur = duration; @@ -997,7 +997,7 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) if (isMedia) duration.SetIndefinite(); - if (NS_FAILED(rv) || (!duration.IsDefinite() && !duration.IsIndefinite())) { + if (NS_FAILED(rv) || !duration.IsResolved()) { mMax.SetIndefinite(); return NS_ERROR_FAILURE; } @@ -1074,7 +1074,7 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) rv = nsSMILParserUtils::ParseClockValue(aRepeatDurSpec, &duration, nsSMILParserUtils::kClockValueAllowIndefinite); - if (NS_FAILED(rv) || (!duration.IsDefinite() && !duration.IsIndefinite())) { + if (NS_FAILED(rv) || !duration.IsResolved()) { mRepeatDur.SetUnresolved(); return NS_ERROR_FAILURE; } @@ -1609,7 +1609,7 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval, nsSMILInterval& aResult) const { NS_ABORT_IF_FALSE(!aFixedBeginTime || aFixedBeginTime->Time().IsDefinite(), - "Unresolved begin time specified for interval start"); + "Unresolved or indefinite begin time specified for interval start"); static const nsSMILTimeValue zeroTime(0L); if (mRestartMode == RESTART_NEVER && aPrevInterval) @@ -1688,15 +1688,14 @@ nsSMILTimedElement::GetNextInterval(const nsSMILInterval* aPrevInterval, // a) We never had any end attribute to begin with (and hence we should // just use the active duration after allowing for the possibility of // an end instance provided by a DOM call), OR - // b) We have no resolved (not incl. indefinite) end instances - // (SMIL only says "if the instance list is empty"--but if we have - // indefinite/unresolved instance times then there must be a good - // reason we haven't used them (since they'll be >= tempBegin) such as - // avoiding creating a self-referential loop. In any case, the interval - // should be allowed to be open.), OR + // b) We have no definite end instances (SMIL only says "if the instance + // list is empty"--but if we have indefinite/unresolved instance times + // then there must be a good reason we haven't used them (since they + // will be >= tempBegin) such as avoiding creating a self-referential + // loop. In any case, the interval should be allowed to be open.), OR // c) We have end events which leave the interval open-ended. PRBool openEndedIntervalOk = mEndSpecs.IsEmpty() || - !HaveResolvedEndTimes() || + !HaveDefiniteEndTimes() || EndHasEventConditions(); if (!tempEnd && !openEndedIntervalOk) return PR_FALSE; // Bad interval @@ -1781,10 +1780,10 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin, { nsSMILTimeValue result; - NS_ABORT_IF_FALSE(mSimpleDur.IsDefinite() || mSimpleDur.IsIndefinite(), + NS_ABORT_IF_FALSE(mSimpleDur.IsResolved(), "Unresolved simple duration in CalcActiveEnd"); NS_ABORT_IF_FALSE(aBegin.IsDefinite(), - "Unresolved begin time in CalcActiveEnd"); + "Indefinite or unresolved begin time in CalcActiveEnd"); if (mRepeatDur.IsIndefinite()) { result.SetIndefinite(); @@ -1843,7 +1842,7 @@ nsSMILTimedElement::GetRepeatDuration() const nsSMILTimeValue nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const { - if (!aDuration.IsDefinite() && !aDuration.IsIndefinite()) { + if (!aDuration.IsResolved()) { return aDuration; } @@ -1871,7 +1870,7 @@ nsSMILTimedElement::ActiveTimeToSimpleTime(nsSMILTime aActiveTime, { nsSMILTime result; - NS_ASSERTION(mSimpleDur.IsDefinite() || mSimpleDur.IsIndefinite(), + NS_ASSERTION(mSimpleDur.IsResolved(), "Unresolved simple duration in ActiveTimeToSimpleTime"); NS_ASSERTION(aActiveTime >= 0, "Expecting non-negative active time"); // Note that a negative aActiveTime will give us a negative value for @@ -2254,12 +2253,12 @@ nsSMILTimedElement::GetPreviousInterval() const } PRBool -nsSMILTimedElement::HaveResolvedEndTimes() const +nsSMILTimedElement::HaveDefiniteEndTimes() const { if (mEndInstances.IsEmpty()) return PR_FALSE; - // mEndInstances is sorted so if the first time is not resolved then none of + // mEndInstances is sorted so if the first time is not definite then none of // them are return mEndInstances[0]->Time().IsDefinite(); } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 9ecd40dcf69..487c1783ebe 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -525,7 +525,7 @@ protected: const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInterval* GetPreviousInterval() const; PRBool HasPlayed() const { return !mOldIntervals.IsEmpty(); } - PRBool HaveResolvedEndTimes() const; + PRBool HaveDefiniteEndTimes() const; PRBool EndHasEventConditions() const; // Reset the current interval by first passing ownership to a temporary From 5315b70beeee36d8e326978e2c34872b13039b79 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Fri, 5 Aug 2011 18:22:11 -0400 Subject: [PATCH 034/172] Bug 674290 - Expose contents of /proc/self/maps and smaps in about:memory. r=njn --- .../aboutmemory/content/aboutMemory.css | 12 + .../aboutmemory/content/aboutMemory.js | 209 ++++++-- .../tests/chrome/test_aboutmemory.xul | 121 ++++- xpcom/base/Makefile.in | 5 + xpcom/base/MapsMemoryReporter.cpp | 454 ++++++++++++++++++ xpcom/base/MapsMemoryReporter.h | 58 +++ xpcom/base/nsIMemoryReporter.idl | 16 +- xpcom/base/nsMemoryReporterManager.cpp | 45 +- xpcom/build/Makefile.in | 4 + xpcom/build/nsXPComInit.cpp | 3 + 10 files changed, 845 insertions(+), 82 deletions(-) create mode 100644 xpcom/base/MapsMemoryReporter.cpp create mode 100644 xpcom/base/MapsMemoryReporter.h diff --git a/toolkit/components/aboutmemory/content/aboutMemory.css b/toolkit/components/aboutmemory/content/aboutMemory.css index 6cdee2d90b6..fabdfa67daa 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.css +++ b/toolkit/components/aboutmemory/content/aboutMemory.css @@ -69,3 +69,15 @@ -moz-user-select: none; /* no need to include this when cutting+pasting */ } +h2.tree { + cursor: pointer; + background: #ddd; + padding-left: .1em; +} + +pre.tree { +} + +pre.collapsed { + display: none; +} diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index ebde903c825..8a6bab35216 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -58,6 +58,55 @@ const UNITS_PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE; const kUnknown = -1; // used for _amount if a memory reporter failed +const kTreeDescriptions = { + 'explicit' : + "This tree covers explicit memory allocations by the application, " + + "both at the operating system level (via calls to functions such as " + + "VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " + + "(via functions such as malloc, calloc, realloc, memalign, operator " + + "new, and operator new[]). It excludes memory that is mapped implicitly " + + "such as code and data segments, and thread stacks. It also excludes " + + "heap memory that has been freed by the application but is still being " + + "held onto by the heap allocator. It is not guaranteed to cover every " + + "explicit allocation, but it does cover most (including the entire " + + "heap), and therefore it is the single best number to focus on when " + + "trying to reduce memory usage.", + + 'resident': + "This tree shows how much space in physical memory each of the " + + "process's mappings is currently using (the mapping's 'resident set size', " + + "or 'RSS'). This is a good measure of the 'cost' of the mapping, although " + + "it does not take into account the fact that shared libraries may be mapped " + + "by multiple processes but appear only once in physical memory. " + + "Note that the 'resident' value here might not equal the value for " + + "'resident' under 'Other Measurements' because the two measurements are not " + + "taken at exactly the same time.", + + 'vsize': + "This tree shows how much virtual addres space each of the process's " + + "mappings takes up (the mapping's 'vsize'). A mapping may have a large " + + "vsize but use only a small amount of physical memory; the resident set size " + + "of a mapping is a better measure of the mapping's 'cost'. Note that the " + + "'vsize' value here might not equal the value for 'vsize' under 'Other " + + "Measurements' because the two measurements are not taken at exactly the " + + "same time.", + + 'swap': + "This tree shows how much space in the swap file each of the process's " + + "mappings is currently using. Mappings which are not in the swap file " + + "(i.e., nodes which would have a value of 0 in this tree) are omitted." +}; + +const kTreeNames = { + 'explicit': 'Explicit Allocations', + 'resident': 'Resident Set Size (RSS) Breakdown', + 'vsize': 'Virtual Size Breakdown', + 'swap': 'Swap Usage Breakdown', + 'other': 'Other Measurements' +}; + +const kMapTreePaths = ['map/resident', 'map/vsize', 'map/swap']; + function onLoad() { var os = Cc["@mozilla.org/observer-service;1"]. @@ -135,6 +184,17 @@ function sendHeapMinNotifications() sendHeapMinNotificationsInner(); } +function toggleTreeVisibility(aEvent) +{ + var headerElem = aEvent.target; + + // Replace "header-" with "pre-" in the header element's id to get the id of + // the corresponding pre element. + var treeElem = $(headerElem.id.replace(/^header-/, 'pre-')); + + treeElem.classList.toggle('collapsed'); +} + function Reporter(aPath, aKind, aUnits, aAmount, aDescription) { this._path = aPath; @@ -261,7 +321,8 @@ function update() text += "
" + "Hover the pointer over the name of a memory " + - "reporter to see a detailed description of what it measures." + "reporter to see a detailed description of what it measures. Click a " + + "heading to expand or collapse its tree." + "
"; var div = document.createElement("div"); @@ -312,23 +373,41 @@ TreeNode.compare = function(a, b) { * * @param aReporters * The table of Reporters, indexed by path. + * @param aTreeName + * The name of the tree being built. * @return The built tree. */ -function buildTree(aReporters) +function buildTree(aReporters, aTreeName) { - const treeName = "explicit"; - - // We want to process all reporters that begin with |treeName|. First we + // We want to process all reporters that begin with |aTreeName|. First we // build the tree but only fill the properties that we can with a top-down // traversal. + + // Is there any reporter which matches aTreeName? If not, we'll create a + // dummy one. + var foundReporter = false; + for (var path in aReporters) { + if (aReporters[path].treeNameMatches(aTreeName)) { + foundReporter = true; + break; + } + } + + if (!foundReporter) { + // We didn't find any reporters for this tree, so create an empty one. Its + // description will be set later. + aReporters[aTreeName] = + new Reporter(aTreeName, KIND_NONHEAP, UNITS_BYTES, 0, ''); + } + var t = new TreeNode("falseRoot"); for (var path in aReporters) { // Add any missing nodes in the tree implied by the path. var r = aReporters[path]; - if (r.treeNameMatches(treeName)) { + if (r.treeNameMatches(aTreeName)) { assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP, "reporters in the tree must have KIND_HEAP or KIND_NONHEAP"); - assert(r._units === UNITS_BYTES); + assert(r._units === UNITS_BYTES, "r._units === UNITS_BYTES"); var names = r._path.split('/'); var u = t; for (var i = 0; i < names.length; i++) { @@ -349,8 +428,9 @@ function buildTree(aReporters) } } } + // Using falseRoot makes the above code simpler. Now discard it, leaving - // treeName at the root. + // aTreeName at the root. t = t._kids[0]; // Next, fill in the remaining properties bottom-up. @@ -360,7 +440,7 @@ function buildTree(aReporters) var path = aPrepath ? aPrepath + '/' + aT._name : aT._name; if (aT._kids.length === 0) { // Leaf node. Must have a reporter. - assert(aT._kind !== undefined); + assert(aT._kind !== undefined, "aT._kind !== undefined"); aT._description = getDescription(aReporters, path); var amount = getBytes(aReporters, path); if (amount !== kUnknown) { @@ -400,11 +480,32 @@ function buildTree(aReporters) aT._description = "The sum of all entries below '" + aT._name + "'."; } } - assert(aT._amount !== kUnknown); + assert(aT._amount !== kUnknown, "aT._amount !== kUnknown"); return aT._amount; } + fillInTree(t, ""); + // Reduce the depth of the tree by the number of occurrences of '/' in + // aTreeName. (Thus the tree named 'foo/bar/baz' will be rooted at 'baz'.) + var slashCount = 0; + for (var i = 0; i < aTreeName.length; i++) { + if (aTreeName[i] == '/') { + assert(t._kids.length == 1, "Not expecting multiple kids here."); + t = t._kids[0]; + } + } + + // Set the description on the root node. + t._description = kTreeDescriptions[t._name]; + + return t; +} + +/** + * Do some work which only makes sense for the 'explicit' tree. + */ +function fixUpExplicitTree(aT, aReporters) { // Determine how many bytes are reported by heap reporters. Be careful // with non-leaf reporters; if we count a non-leaf reporter we don't want // to count any of its child reporters. @@ -429,7 +530,7 @@ function buildTree(aReporters) var unknownHeapUsedBytes = 0; var hasProblem = true; if (heapUsedBytes !== kUnknown) { - unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t); + unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(aT); hasProblem = false; } var heapUnclassified = new TreeNode("heap-unclassified"); @@ -446,10 +547,8 @@ function buildTree(aReporters) heapUnclassified._hasProblem = true; } - t._kids.push(heapUnclassified); - t._amount += unknownHeapUsedBytes; - - return t; + aT._kids.push(heapUnclassified); + aT._amount += unknownHeapUsedBytes; } /** @@ -515,16 +614,26 @@ function filterTree(aTotalBytes, aT) */ function genProcessText(aProcess, aReporters) { - var tree = buildTree(aReporters); - filterTree(tree._amount, tree); + var explicitTree = buildTree(aReporters, 'explicit'); + fixUpExplicitTree(explicitTree, aReporters); + filterTree(explicitTree._amount, explicitTree); + var explicitText = genTreeText(explicitTree, aProcess); - // Nb: the newlines give nice spacing if we cut+paste into a text buffer. - var text = ""; - text += "

" + aProcess + " Process

\n\n"; - text += genTreeText(tree); - text += genOtherText(aReporters); - text += "
"; - return text; + var mapTreeText = ''; + kMapTreePaths.forEach(function(t) { + var tree = buildTree(aReporters, t); + filterTree(tree._amount, tree); + mapTreeText += genTreeText(tree, aProcess); + }); + + // We have to call genOtherText after we process all the trees, because it + // looks at all the reporters which aren't part of a tree. + var otherText = genOtherText(aReporters, aProcess); + + // The newlines give nice spacing if we cut+paste into a text buffer. + return "

" + aProcess + " Process

\n\n" + + explicitText + mapTreeText + otherText + + "
"; } /** @@ -735,12 +844,15 @@ function genMrNameText(aKind, aDesc, aName, aHasProblem, aNMerged) * * @param aT * The tree. + * @param aProcess + * The process the tree corresponds to. * @return The generated text. */ -function genTreeText(aT) +function genTreeText(aT, aProcess) { var treeBytes = aT._amount; var rootStringLength = aT.toString().length; + var isExplicitTree = aT._name == 'explicit'; /** * Generates the text for a particular tree, without a heading. @@ -808,8 +920,11 @@ function genTreeText(aT) } perc = "(" + perc + "%) "; + // We don't want to show '(nonheap)' on a tree like 'map/vsize', since the + // whole tree is non-heap. + var kind = isExplicitTree ? aT._kind : undefined; var text = indent + genMrValueText(tString) + " " + perc + - genMrNameText(aT._kind, aT._description, aT._name, + genMrNameText(kind, aT._description, aT._name, aT._hasProblem, aT._nMerged); for (var i = 0; i < aT._kids.length; i++) { @@ -822,22 +937,10 @@ function genTreeText(aT) } var text = genTreeText2(aT, [], rootStringLength); - // Nb: the newlines give nice spacing if we cut+paste into a text buffer. - const desc = - "This tree covers explicit memory allocations by the application, " + - "both at the operating system level (via calls to functions such as " + - "VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " + - "(via functions such as malloc, calloc, realloc, memalign, operator " + - "new, and operator new[]). It excludes memory that is mapped implicitly " + - "such as code and data segments, and thread stacks. It also excludes " + - "heap memory that has been freed by the application but is still being " + - "held onto by the heap allocator. It is not guaranteed to cover every " + - "explicit allocation, but it does cover most (including the entire " + - "heap), and therefore it is the single best number to focus on when " + - "trying to reduce memory usage."; - - return "

Explicit Allocations

\n" + "
" + text + "
\n"; + + // The explicit tree is not collapsed, but all other trees are, so pass + // !isExplicitTree for genSectionMarkup's aCollapsed parameter. + return genSectionMarkup(aProcess, aT._name, text, !isExplicitTree); } function OtherReporter(aPath, aUnits, aAmount, aDescription, @@ -880,9 +983,11 @@ OtherReporter.compare = function(a, b) { * * @param aReportersByProcess * Table of Reporters for this process, indexed by _path. + * @param aProcess + * The process these reporters correspond to. * @return The generated text. */ -function genOtherText(aReportersByProcess) +function genOtherText(aReportersByProcess, aProcess) { // Generate an array of Reporter-like elements, stripping out all the // Reporters that have already been handled. Also find the width of the @@ -918,8 +1023,22 @@ function genOtherText(aReportersByProcess) // Nb: the newlines give nice spacing if we cut+paste into a text buffer. const desc = "This list contains other memory measurements that cross-cut " + "the requested memory measurements above." - return "

Other Measurements

\n" + - "
" + text + "
\n"; + + return genSectionMarkup(aProcess, 'other', text, false); +} + +function genSectionMarkup(aProcess, aName, aText, aCollapsed) +{ + var headerId = 'header-' + aProcess + '-' + aName; + var preId = 'pre-' + aProcess + '-' + aName; + var elemClass = (aCollapsed ? 'collapsed' : '') + ' tree'; + + // Ugh. + return '

' + + kTreeNames[aName] + + '

\n' + + '
' + aText + '
\n'; } function assert(aCond, aMsg) diff --git a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul index 257fd8140a4..9d67a2d2bed 100644 --- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul @@ -105,6 +105,17 @@ f("perc2", OTHER, PERCENTAGE, 10000); f("perc1", OTHER, PERCENTAGE, 4567); } + }, + { collectReports: function(cbObj, closure) { + // The amounts are given in pages, so multiply here by 4kb. + function f(p, a) { cbObj.callback("", p, NONHEAP, BYTES, a * 4 * KB, "(desc)", closure); } + f("map/vsize/a", 24); + f("map/swap/a", 1); + f("map/swap/a", 2); + f("map/vsize/a", 19); + f("map/swap/b/c", 10); + f("map/resident/a", 42); + } } ]; for (var i = 0; i < fakeReporters.length; i++) { @@ -192,6 +203,20 @@ Explicit Allocations\n\ ├───11.00 MB (01.76%) -- heap-unclassified\n\ └────0.58 MB (00.09%) -- (2 omitted)\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +0.16 MB (100.0%) -- resident\n\ +└──0.16 MB (100.0%) -- a\n\ +\n\ +Virtual Size Breakdown\n\ +0.17 MB (100.0%) -- vsize\n\ +└──0.17 MB (100.0%) -- a [2]\n\ +\n\ +Swap Usage Breakdown\n\ +0.05 MB (100.0%) -- swap\n\ +├──0.04 MB (76.92%) -- b\n\ +│ └──0.04 MB (76.92%) -- c\n\ +└──0.01 MB (23.08%) -- a [2]\n\ +\n\ Other Measurements\n\ 500.00 MB -- heap-allocated\n\ 100.00 MB -- heap-unallocated\n\ @@ -213,6 +238,15 @@ Explicit Allocations\n\ ├────200.00 MB (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mo...)\n\ └────101.00 MB (10.10%) -- heap-unclassified\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +0.00 MB (100.0%) -- resident\n\ +\n\ +Virtual Size Breakdown\n\ +0.00 MB (100.0%) -- vsize\n\ +\n\ +Swap Usage Breakdown\n\ +0.00 MB (100.0%) -- swap\n\ +\n\ Other Measurements\n\ 666.00 MB -- danger\n\ 1,000.00 MB -- heap-allocated\n\ @@ -229,6 +263,15 @@ Explicit Allocations\n\ │ └────0.00 MB (00.00%) -- (1 omitted)\n\ └────0.00 MB (00.00%) -- (2 omitted)\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +0.00 MB (100.0%) -- resident\n\ +\n\ +Virtual Size Breakdown\n\ +0.00 MB (100.0%) -- vsize\n\ +\n\ +Swap Usage Breakdown\n\ +0.00 MB (100.0%) -- swap\n\ +\n\ Other Measurements\n\ 0.00 MB -- heap-allocated [*]\n\ 0.00 MB -- other1 [*]\n\ @@ -264,6 +307,20 @@ Explicit Allocations\n\ ├──────510,976 B (00.08%) -- d\n\ └──────102,400 B (00.02%) -- e\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +172,032 B (100.0%) -- resident\n\ +└──172,032 B (100.0%) -- a\n\ +\n\ +Virtual Size Breakdown\n\ +176,128 B (100.0%) -- vsize\n\ +└──176,128 B (100.0%) -- a [2]\n\ +\n\ +Swap Usage Breakdown\n\ +53,248 B (100.0%) -- swap\n\ +├──40,960 B (76.92%) -- b\n\ +│ └──40,960 B (76.92%) -- c\n\ +└──12,288 B (23.08%) -- a [2]\n\ +\n\ Other Measurements\n\ 524,288,000 B -- heap-allocated\n\ 104,857,600 B -- heap-unallocated\n\ @@ -285,6 +342,15 @@ Explicit Allocations\n\ ├────209,715,200 B (20.00%) -- compartment(this-will-be-truncated-in-non-verbose-mode-abcdefghijklmnopqrstuvwxyz)\n\ └────105,906,176 B (10.10%) -- heap-unclassified\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +0 B (100.0%) -- resident\n\ +\n\ +Virtual Size Breakdown\n\ +0 B (100.0%) -- vsize\n\ +\n\ +Swap Usage Breakdown\n\ +0 B (100.0%) -- swap\n\ +\n\ Other Measurements\n\ 698,351,616 B -- danger\n\ 1,048,576,000 B -- heap-allocated\n\ @@ -302,6 +368,15 @@ Explicit Allocations\n\ ├────────────0 B (00.00%) -- b [*]\n\ └────────────0 B (00.00%) -- heap-unclassified [*]\n\ \n\ +Resident Set Size (RSS) Breakdown\n\ +0 B (100.0%) -- resident\n\ +\n\ +Virtual Size Breakdown\n\ +0 B (100.0%) -- vsize\n\ +\n\ +Swap Usage Breakdown\n\ +0 B (100.0%) -- swap\n\ +\n\ Other Measurements\n\ 0 B -- heap-allocated [*]\n\ 0 B -- other1 [*]\n\ @@ -328,22 +403,44 @@ Other Measurements\n\ SimpleTest.finish(); } + function check(actual, expected) { + var a = actual.QueryInterface(Ci.nsISupportsString).data; + if (a != expected) { + dump("*******ACTUAL*******\n"); + dump(a); + dump("******EXPECTED******\n"); + dump(expected); + dump("********************\n"); + return false; + } + return true; + } + // Cut+paste the entire page and check that the cut text matches what we // expect. This tests the output in general and also that the cutting and // pasting works as expected. function test(aFrame, aExpectedText, aNext) { - document.querySelector("#" + aFrame).focus(); - SimpleTest.waitForClipboard(aExpectedText, - function() { - synthesizeKey("A", {accelKey: true}); - synthesizeKey("C", {accelKey: true}); - }, - aNext, - function() { - ok(false, "pasted text doesn't match for " + aFrame); - finish(); - } - ); + // Click all h2.collapsed elements so they expand. + var win = document.querySelector("#" + aFrame).contentWindow; + var nodes = win.document.querySelectorAll("pre.collapsed"); + for (var i = 0; i < nodes.length; i++) { + nodes[i].classList.toggle('collapsed'); + } + + SimpleTest.executeSoon(function() { + document.querySelector("#" + aFrame).focus(); + SimpleTest.waitForClipboard(function(actual) { return check(actual, aExpectedText) }, + function() { + synthesizeKey("A", {accelKey: true}); + synthesizeKey("C", {accelKey: true}); + }, + aNext, + function() { + ok(false, "pasted text doesn't match for " + aFrame); + finish(); + } + ); + }); } addLoadEvent(function() { diff --git a/xpcom/base/Makefile.in b/xpcom/base/Makefile.in index 972e71c18de..a95a4250c07 100644 --- a/xpcom/base/Makefile.in +++ b/xpcom/base/Makefile.in @@ -72,6 +72,10 @@ CPPSRCS = \ FunctionTimer.cpp \ $(NULL) +ifeq ($(OS_ARCH),Linux) +CPPSRCS += MapsMemoryReporter.cpp +endif + ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) CPPSRCS += nsMacUtilsImpl.cpp endif @@ -94,6 +98,7 @@ EXPORTS_NAMESPACES = mozilla EXPORTS_mozilla = \ FunctionTimer.h \ + MapsMemoryReporter.h \ $(NULL) ifeq (windows,$(MOZ_WIDGET_TOOLKIT)) diff --git a/xpcom/base/MapsMemoryReporter.cpp b/xpcom/base/MapsMemoryReporter.cpp new file mode 100644 index 00000000000..e67183c73cd --- /dev/null +++ b/xpcom/base/MapsMemoryReporter.cpp @@ -0,0 +1,454 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 ci et: */ +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Lebar + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +#include "mozilla/MapsMemoryReporter.h" +#include "nsIMemoryReporter.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsHashSets.h" +#include + +namespace mozilla { +namespace MapsMemoryReporter { + +#if !defined(XP_LINUX) +#error "This doesn't have a prayer of working if we're not on Linux." +#endif + +// mozillaLibraries is a list of all the shared libraries we build. This list +// is used for determining whether a library is a "Mozilla library" or a +// "third-party library". But even if this list is missing items, about:memory +// will identify a library in the same directory as libxul.so as a "Mozilla +// library". +const nsCString mozillaLibraries[] = +{ + NS_LITERAL_CSTRING("libfreebl3.so"), + NS_LITERAL_CSTRING("libmozalloc.so"), + NS_LITERAL_CSTRING("libmozsqlite3.so"), + NS_LITERAL_CSTRING("libnspr4.so"), + NS_LITERAL_CSTRING("libnss3.so"), + NS_LITERAL_CSTRING("libnssckbi.so"), + NS_LITERAL_CSTRING("libnssdbm3.so"), + NS_LITERAL_CSTRING("libnssutil3.so"), + NS_LITERAL_CSTRING("libplc4.so"), + NS_LITERAL_CSTRING("libplds4.so"), + NS_LITERAL_CSTRING("libsmime3.so"), + NS_LITERAL_CSTRING("libsoftokn3.so"), + NS_LITERAL_CSTRING("libssl3.so"), + NS_LITERAL_CSTRING("libxpcom.so"), + NS_LITERAL_CSTRING("libxul.so") +}; + +namespace { + +bool EndsWithLiteral(const nsCString &aHaystack, const char *aNeedle) +{ + PRInt32 idx = aHaystack.RFind(aNeedle); + if (idx == -1) { + return false; + } + + return idx + strlen(aNeedle) == aHaystack.Length(); +} + +void GetDirname(const nsCString &aPath, nsACString &aOut) +{ + PRInt32 idx = aPath.RFind("/"); + if (idx == -1) { + aOut.Truncate(); + } + else { + aOut.Assign(Substring(aPath, 0, idx)); + } +} + +void GetBasename(const nsCString &aPath, nsACString &aOut) +{ + PRInt32 idx = aPath.RFind("/"); + if (idx == -1) { + aOut.Assign(aPath); + } + else { + aOut.Assign(Substring(aPath, idx + 1)); + } +} + +} // anonymous namespace + +class MapsReporter : public nsIMemoryMultiReporter +{ +public: + MapsReporter(); + + NS_DECL_ISUPPORTS + + NS_IMETHOD + CollectReports(nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure); + +private: + // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the + // the directory containing libxul. + nsresult FindLibxul(); + + nsresult + ParseMapping(FILE *aFile, + nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure); + + void + GetReporterNameAndDescription(const char *aPath, + const char *aPermissions, + nsACString &aName, + nsACString &aDesc); + + nsresult + ParseMapBody(FILE *aFile, + const nsACString &aName, + const nsACString &aDescription, + nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure); + + nsCString mLibxulDir; + nsCStringHashSet mMozillaLibraries; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(MapsReporter, nsIMemoryMultiReporter) + +MapsReporter::MapsReporter() +{ + const PRUint32 len = NS_ARRAY_LENGTH(mozillaLibraries); + mMozillaLibraries.Init(len); + for (PRUint32 i = 0; i < len; i++) { + mMozillaLibraries.Put(mozillaLibraries[i]); + } +} + +NS_IMETHODIMP +MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure) +{ + FILE *f = fopen("/proc/self/smaps", "r"); + if (!f) + return NS_ERROR_FAILURE; + + while (true) { + nsresult rv = ParseMapping(f, aCallback, aClosure); + if (NS_FAILED(rv)) + break; + } + + fclose(f); + return NS_OK; +} + +nsresult +MapsReporter::FindLibxul() +{ + mLibxulDir.Truncate(); + + // Note that we're scanning /proc/self/*maps*, not smaps, here. + FILE *f = fopen("/proc/self/maps", "r"); + if (!f) + return NS_ERROR_FAILURE; + + while (true) { + // Skip any number of non-slash characters, then capture starting with the + // slash to the newline. This is the path part of /proc/self/maps. + char path[1025]; + int numRead = fscanf(f, "%*[^/]%1024[^\n]", path); + if (numRead != 1) { + break; + } + + nsCAutoString pathStr; + pathStr.Append(path); + + nsCAutoString basename; + GetBasename(pathStr, basename); + + if (basename.EqualsLiteral("libxul.so")) { + GetDirname(pathStr, mLibxulDir); + break; + } + } + + fclose(f); + return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK; +} + +nsresult +MapsReporter::ParseMapping( + FILE *aFile, + nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure) +{ + // We need to use native types in order to get good warnings from fscanf, so + // let's make sure that the native types have the sizes we expect. + PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64)); + PR_STATIC_ASSERT(sizeof(int) == sizeof(PRInt32)); + + if (mLibxulDir.IsEmpty()) { + NS_ENSURE_SUCCESS(FindLibxul(), NS_ERROR_FAILURE); + } + + // The first line of an entry in /proc/self/smaps looks just like an entry + // in /proc/maps: + // + // address perms offset dev inode pathname + // 02366000-025d8000 rw-p 00000000 00:00 0 [heap] + + const int argCount = 8; + + unsigned long long addrStart, addrEnd; + char perms[5]; + unsigned long long offset; + unsigned int devMajor, devMinor, inode; + char path[1025]; + + // A path might not be present on this line; set it to the empty string. + path[0] = '\0'; + + // This is a bit tricky. Whitespace in a scanf pattern matches *any* + // whitespace, including newlines. We want this pattern to match a line + // with or without a path, but we don't want to look to a new line for the + // path. Thus we have %u%1024[^\n] at the end of the pattern. This will + // capture into the path some leading whitespace, which we'll later trim off. + int numRead = fscanf(aFile, "%llx-%llx %4s %llx %u:%u %u%1024[^\n]", + &addrStart, &addrEnd, perms, &offset, &devMajor, + &devMinor, &inode, path); + + // Eat up any whitespace at the end of this line, including the newline. + fscanf(aFile, " "); + + // We might or might not have a path, but the rest of the arguments should be + // there. + if (numRead != argCount && numRead != argCount - 1) + return NS_ERROR_FAILURE; + + nsCAutoString name, description; + GetReporterNameAndDescription(path, perms, name, description); + + while (true) { + nsresult rv = ParseMapBody(aFile, name, description, aCallback, aClosure); + if (NS_FAILED(rv)) + break; + } + + return NS_OK; +} + +void +MapsReporter::GetReporterNameAndDescription( + const char *aPath, + const char *aPerms, + nsACString &aName, + nsACString &aDesc) +{ + aName.Truncate(); + aDesc.Truncate(); + + // If aPath points to a file, we have its absolute path, plus some + // whitespace. Truncate this to its basename, and put the absolute path in + // the description. + nsCAutoString absPath; + absPath.Append(aPath); + absPath.StripChars(" "); + + nsCAutoString basename; + GetBasename(absPath, basename); + + if (basename.EqualsLiteral("[heap]")) { + aName.Append("anonymous/anonymous, within brk()"); + aDesc.Append("Memory in anonymous mappings within the boundaries " + "defined by brk() / sbrk(). This is likely to be just " + "a portion of the application's heap; the remainder " + "lives in other anonymous mappings. This node corresponds to " + "'[heap]' in /proc/self/smaps."); + } + else if (basename.EqualsLiteral("[stack]")) { + aName.Append("main thread's stack"); + aDesc.Append("The stack size of the process's main thread. This node " + "corresponds to '[stack]' in /proc/self/smaps."); + } + else if (basename.EqualsLiteral("[vdso]")) { + aName.Append("vdso"); + aDesc.Append("The virtual dynamically-linked shared object, also known as " + "the 'vsyscall page'. This is a memory region mapped by the " + "operating system for the purpose of allowing processes to " + "perform some privileged actions without the overhead of a " + "syscall."); + } + else if (!basename.IsEmpty()) { + NS_ASSERTION(!mLibxulDir.IsEmpty(), "mLibxulDir should not be empty."); + + nsCAutoString dirname; + GetDirname(absPath, dirname); + + // Hack: A file is a shared library if the basename contains ".so" and its + // dirname contains "/lib", or if the basename ends with ".so". + if (EndsWithLiteral(basename, ".so") || + (basename.Find(".so") != -1 && dirname.Find("/lib") != -1)) { + aName.Append("shared-libraries/"); + if (dirname.Equals(mLibxulDir) || mMozillaLibraries.Contains(basename)) { + aName.Append("shared-libraries-mozilla/"); + } + else { + aName.Append("shared-libraries-other/"); + } + } + else { + aName.Append("other-files/"); + if (EndsWithLiteral(basename, ".xpi")) { + aName.Append("extensions/"); + } + else if (dirname.Find("/fontconfig") != -1) { + aName.Append("fontconfig/"); + } + } + + aName.Append(basename); + aDesc.Append(absPath); + } + else { + aName.Append("anonymous/anonymous, outside brk()"); + aDesc.Append("Memory in anonymous mappings outside the boundaries defined " + "by brk() / sbrk()."); + } + + aName.Append(" ["); + aName.Append(aPerms); + aName.Append("]"); + + // Modify the description to include an explanation of the permissions. + aDesc.Append(" ("); + if (strstr(aPerms, "rw")) { + aDesc.Append("read/write, "); + } + else if (strchr(aPerms, 'r')) { + aDesc.Append("read-only, "); + } + else if (strchr(aPerms, 'w')) { + aDesc.Append("write-only, "); + } + else { + aDesc.Append("not readable, not writable, "); + } + + if (strchr(aPerms, 'x')) { + aDesc.Append("executable, "); + } + else { + aDesc.Append("not executable, "); + } + + if (strchr(aPerms, 's')) { + aDesc.Append("shared"); + } + else if (strchr(aPerms, 'p')) { + aDesc.Append("private"); + } + else { + aDesc.Append("not shared or private??"); + } + aDesc.Append(")"); +} + +nsresult +MapsReporter::ParseMapBody( + FILE *aFile, + const nsACString &aName, + const nsACString &aDescription, + nsIMemoryMultiReporterCallback *aCallback, + nsISupports *aClosure) +{ + PR_STATIC_ASSERT(sizeof(long long) == sizeof(PRInt64)); + + const int argCount = 2; + + char desc[1025]; + unsigned long long size; + if (fscanf(aFile, "%1024[a-zA-Z_]: %llu kB\n", + desc, &size) != argCount) { + return NS_ERROR_FAILURE; + } + + // Don't report nodes with size 0. + if (size == 0) + return NS_OK; + + const char* category; + if (strcmp(desc, "Size") == 0) { + category = "vsize"; + } + else if (strcmp(desc, "Rss") == 0) { + category = "resident"; + } + else if (strcmp(desc, "Swap") == 0) { + category = "swap"; + } + else { + // Don't report this category. + return NS_OK; + } + + nsCAutoString path; + path.Append("map/"); + path.Append(category); + path.Append("/"); + path.Append(aName); + + aCallback->Callback(NS_LITERAL_CSTRING(""), + path, + nsIMemoryReporter::KIND_NONHEAP, + nsIMemoryReporter::UNITS_BYTES, + PRInt64(size) * 1024, // convert from kB to bytes + aDescription, aClosure); + + return NS_OK; +} + +void Init() +{ + nsCOMPtr reporter = new MapsReporter(); + NS_RegisterMemoryMultiReporter(reporter); +} + +} // namespace MapsMemoryReporter +} // namespace mozilla diff --git a/xpcom/base/MapsMemoryReporter.h b/xpcom/base/MapsMemoryReporter.h new file mode 100644 index 00000000000..e5c6f2aa04a --- /dev/null +++ b/xpcom/base/MapsMemoryReporter.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 ci et: */ +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Lebar + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 mozilla_MapsMemoryReporter_h_ +#define mozilla_MapsMemoryReporter_h_ + +namespace mozilla { +namespace MapsMemoryReporter { + +// This only works on Linux, but to make callers' lives easier, we stub out +// empty functions on other platforms. + +#if defined(XP_LINUX) + void Init(); +#else + void Init() {} +#endif + +} // namespace MapsMemoryReporter +} // namespace mozilla + +#endif diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 6236f92c912..b53250a9c5a 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -64,7 +64,7 @@ interface nsIMemoryReporter : nsISupports /* * The path that this memory usage should be reported under. Paths are - * '/'-delimited, eg. "a/b/c". There are two categories of paths. + * '/'-delimited, eg. "a/b/c". There are three categories of paths. * * - Paths starting with "explicit" represent regions of memory that have * been explicitly allocated with an OS-level allocation (eg. @@ -91,6 +91,14 @@ interface nsIMemoryReporter : nsISupports * So in the example above, |a| may not count any allocations counted by * |d|, and vice versa. * + * - Paths starting with "map" represent regions of virtual memory that the + * process has mapped. The reporter immediately beneath "map" describes + * the type of measurement; for instance, the reporter "map/rss/[stack]" + * might report how much of the process's stack is currently in physical + * memory. + * + * Reporters in this category must have kind NONHEAP and units BYTES. + * * - All other paths represent cross-cutting values and may overlap with any * other reporter. */ @@ -108,11 +116,11 @@ interface nsIMemoryReporter : nsISupports * live on the heap. Such memory is commonly allocated by calling one of * the OS's memory-mapping functions (e.g. mmap, VirtualAlloc, or * vm_allocate). Reporters in this category must have units UNITS_BYTES - * and must have a path starting with "explicit". + * and must have a path starting with "explicit" or "map". * * - OTHER: reporters which don't fit into either of these categories. Such - * reporters must have a path that does not start with "explicit" and may - * have any units. + * reporters must have a path that does not start with "explicit" or "map" + * and may have any units. */ const PRInt32 KIND_NONHEAP = 0; const PRInt32 KIND_HEAP = 1; diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index f06a3de091d..3870ca3c922 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -575,7 +575,10 @@ public: const nsACString &aDescription, nsISupports *aWrappedMRs) { - if (aKind == nsIMemoryReporter::KIND_NONHEAP && aAmount != PRInt64(-1)) { + if (aKind == nsIMemoryReporter::KIND_NONHEAP && + PromiseFlatCString(aPath).Find("explicit") == 0 && + aAmount != PRInt64(-1)) { + MemoryReportsWrapper *wrappedMRs = static_cast(aWrappedMRs); MemoryReport mr(aPath, aAmount); @@ -608,7 +611,7 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) PRInt64 heapUsed = PRInt64(-1); // Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla - // reporters. + // "explicit" reporters. nsCOMPtr e; EnumerateReporters(getter_AddRefs(e)); @@ -621,10 +624,14 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) nsresult rv = r->GetKind(&kind); NS_ENSURE_SUCCESS(rv, rv); - if (kind == nsIMemoryReporter::KIND_NONHEAP) { - nsCString path; - rv = r->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); + nsCString path; + rv = r->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + // We're only interested in NONHEAP explicit reporters and + // the 'heap-allocated' reporter. + if (kind == nsIMemoryReporter::KIND_NONHEAP && + path.Find("explicit") == 0) { PRInt64 amount; rv = r->GetAmount(&amount); @@ -636,20 +643,14 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) MemoryReport mr(path, amount); nonheap.AppendElement(mr); } - } else { - nsCString path; - rv = r->GetPath(path); + } else if (path.Equals("heap-allocated")) { + rv = r->GetAmount(&heapUsed); NS_ENSURE_SUCCESS(rv, rv); - - if (path.Equals("heap-allocated")) { - rv = r->GetAmount(&heapUsed); - NS_ENSURE_SUCCESS(rv, rv); - // If "heap-allocated" fails, we give up, because the result - // would be horribly inaccurate. - if (heapUsed == PRInt64(-1)) { - *aExplicit = PRInt64(-1); - return NS_OK; - } + // If "heap-allocated" fails, we give up, because the result + // would be horribly inaccurate. + if (heapUsed == PRInt64(-1)) { + *aExplicit = PRInt64(-1); + return NS_OK; } } } @@ -659,6 +660,8 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) EnumerateMultiReporters(getter_AddRefs(e2)); nsRefPtr wrappedMRs = new MemoryReportsWrapper(&nonheap); + + // This callback adds only NONHEAP explicit reporters. nsRefPtr cb = new MemoryReportCallback(); while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) { @@ -669,8 +672,8 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) // Ignore (by zeroing its amount) any reporter that is a child of another // reporter. Eg. if we have "explicit/a" and "explicit/a/b", zero the - // latter. This is quadratic in the number of NONHEAP reporters, but there - // shouldn't be many. + // latter. This is quadratic in the number of explicit NONHEAP reporters, + // but there shouldn't be many. for (PRUint32 i = 0; i < nonheap.Length(); i++) { const nsCString &iPath = nonheap[i].path; for (PRUint32 j = i + 1; j < nonheap.Length(); j++) { diff --git a/xpcom/build/Makefile.in b/xpcom/build/Makefile.in index cb1f0209cdc..c2c1241ffdf 100644 --- a/xpcom/build/Makefile.in +++ b/xpcom/build/Makefile.in @@ -55,6 +55,10 @@ EXPORT_LIBRARY = 1 GRE_MODULE = 1 MOZILLA_INTERNAL_API = 1 +ifeq ($(OS_ARCH),Linux) +DEFINES += -DXP_LINUX +endif + CPPSRCS = \ $(XPCOM_GLUE_SRC_LCPPSRCS) \ $(XPCOM_GLUENS_SRC_LCPPSRCS) \ diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp index a95f0097c4e..51d62140a76 100644 --- a/xpcom/build/nsXPComInit.cpp +++ b/xpcom/build/nsXPComInit.cpp @@ -150,6 +150,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **) #include "base/message_loop.h" #include "mozilla/ipc/BrowserProcessSubThread.h" +#include "mozilla/MapsMemoryReporter.h" using base::AtExitManager; using mozilla::ipc::BrowserProcessSubThread; @@ -527,6 +528,8 @@ NS_InitXPCOM2(nsIServiceManager* *result, ScheduleMediaCacheRemover(); #endif + mozilla::MapsMemoryReporter::Init(); + return NS_OK; } From a622e3512899ca1e9178fd732d2567e95bf1361e Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Sat, 6 Aug 2011 16:05:25 -0500 Subject: [PATCH 035/172] Bug 672893 - Don't #include jscompartment.h in xpconnect. r=cdleary. --- js/jsd/jsd_xpc.cpp | 3 -- js/src/Makefile.in | 1 - js/src/jsfriendapi.cpp | 48 +++++++++++++++++++ js/src/jsfriendapi.h | 39 +++++++++++++++ js/src/jsprvtd.h | 1 + .../xpconnect/loader/mozJSComponentLoader.cpp | 5 +- js/src/xpconnect/src/nsXPConnect.cpp | 8 ++-- js/src/xpconnect/src/xpcjsruntime.cpp | 4 +- js/src/xpconnect/src/xpcprivate.h | 2 +- .../xpconnect/src/xpcwrappednativescope.cpp | 2 +- js/src/xpconnect/wrappers/AccessCheck.cpp | 5 +- js/src/xpconnect/wrappers/AccessCheck.h | 3 ++ .../xpconnect/wrappers/CrossOriginWrapper.cpp | 7 +-- js/src/xpconnect/wrappers/WrapperFactory.cpp | 3 +- js/src/xpconnect/wrappers/XrayWrapper.cpp | 6 +-- 15 files changed, 110 insertions(+), 27 deletions(-) diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 444ee452ff2..11dc1a35807 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -60,9 +60,6 @@ #include "nsIScriptContext.h" #include "nsIJSContextStack.h" -/* XXX private JS headers. */ -#include "jscompartment.h" - /* * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the * script hook. This was a hack to avoid some js engine problems that should diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 3c022ffed53..fb922fc6650 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -191,7 +191,6 @@ INSTALLED_HEADERS = \ jscell.h \ jsgcchunk.h \ jsgcstats.h \ - jscompartment.h \ jshash.h \ jsinfer.h \ jsinferinlines.h \ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 8b6802cf363..6bdcb72d515 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -44,6 +44,7 @@ #include "jsobjinlines.h" using namespace js; +using namespace JS; JS_FRIEND_API(JSString *) JS_GetAnonymousString(JSRuntime *rt) @@ -112,6 +113,53 @@ JS_ObjectCountDynamicSlots(JSObject *obj) return 0; } +JS_FRIEND_API(JSPrincipals *) +JS_GetCompartmentPrincipals(JSCompartment *compartment) +{ + return compartment->principals; +} + +JS_FRIEND_API(JSBool) +JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc) +{ + return cx->compartment->wrap(cx, desc); +} + +AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; +} + +AutoPreserveCompartment::~AutoPreserveCompartment() +{ + /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */ + cx->compartment = oldCompartment; +} + +AutoSwitchCompartment::AutoSwitchCompartment(JSContext *cx, JSCompartment *newCompartment + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->setCompartment(newCompartment); +} + +AutoSwitchCompartment::AutoSwitchCompartment(JSContext *cx, JSObject *target + JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) + : cx(cx), oldCompartment(cx->compartment) +{ + JS_GUARD_OBJECT_NOTIFIER_INIT; + cx->setCompartment(target->compartment()); +} + +AutoSwitchCompartment::~AutoSwitchCompartment() +{ + /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */ + cx->compartment = oldCompartment; +} + /* * The below code is for temporary telemetry use. It can be removed when * sufficient data has been harvested. diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 8dd9e1b7f29..a3ee0151e3d 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -93,6 +93,45 @@ extern JS_FRIEND_API(void) JS_GetTypeInferenceObjectStats(/*TypeObject*/ void *object, TypeInferenceMemoryStats *stats); +extern JS_FRIEND_API(JSPrincipals *) +JS_GetCompartmentPrincipals(JSCompartment *compartment); + +#ifdef __cplusplus + +extern JS_FRIEND_API(JSBool) +JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc); + +#endif + JS_END_EXTERN_C +#ifdef __cplusplus + +namespace JS { + +class JS_FRIEND_API(AutoPreserveCompartment) { + private: + JSContext *cx; + JSCompartment *oldCompartment; + public: + AutoPreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoPreserveCompartment(); + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +class JS_FRIEND_API(AutoSwitchCompartment) { + private: + JSContext *cx; + JSCompartment *oldCompartment; + public: + AutoSwitchCompartment(JSContext *cx, JSCompartment *newCompartment + JS_GUARD_OBJECT_NOTIFIER_PARAM); + AutoSwitchCompartment(JSContext *cx, JSObject *target JS_GUARD_OBJECT_NOTIFIER_PARAM); + ~AutoSwitchCompartment(); + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +} +#endif + #endif /* jsfriendapi_h___ */ diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 4a8adc15b5c..b35a110609d 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -185,6 +185,7 @@ class InlineMap; class PropertyCache; struct PropertyCacheEntry; +struct PropertyDescriptor; struct Shape; struct EmptyShape; diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index e303d0c5ae2..4ad289fc4c4 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -74,7 +74,6 @@ #include "nsNetUtil.h" #include "nsDOMFile.h" #include "jsxdrapi.h" -#include "jscompartment.h" #include "jsprf.h" // For reporting errors with the console service #include "nsIScriptError.h" @@ -752,8 +751,8 @@ mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile, JSCLContextHelper cx(this); // preserve caller's compartment - js::PreserveCompartment pc(cx); - + JS::AutoPreserveCompartment pc(cx); + rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals); NS_ENSURE_SUCCESS(rv, rv); diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index a82aad49fce..6be8d3fa0d7 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1063,7 +1063,7 @@ CreateNewCompartment(JSContext *cx, JSClass *clasp, nsIPrincipal *principal, *global = tempGlobal; *compartment = tempGlobal->compartment(); - js::SwitchToCompartment sc(cx, *compartment); + JS::AutoSwitchCompartment sc(cx, *compartment); JS_SetCompartmentPrivate(cx, *compartment, priv_holder.forget()); return true; } @@ -1095,7 +1095,7 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp, } else { - js::SwitchToCompartment sc(cx, *compartment); + JS::AutoSwitchCompartment sc(cx, *compartment); JSObject *tempGlobal = JS_NewGlobalObject(cx, clasp); if(!tempGlobal) @@ -1132,7 +1132,7 @@ xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp, } else { - js::SwitchToCompartment sc(cx, *compartment); + JS::AutoSwitchCompartment sc(cx, *compartment); JSObject *tempGlobal = JS_NewGlobalObject(cx, clasp); if(!tempGlobal) @@ -2565,7 +2565,7 @@ nsXPConnect::CheckForDebugMode(JSRuntime *rt) { js::CompartmentVector &vector = rt->compartments; for (JSCompartment **p = vector.begin(); p != vector.end(); ++p) { JSCompartment *comp = *p; - if (!comp->principals) { + if (!JS_GetCompartmentPrincipals(comp)) { /* Ignore special compartments (atoms, JSD compartments) */ continue; } diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 02cfac7dd8f..3543033c312 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -105,8 +105,8 @@ WrappedJSDyingJSObjectFinder(JSDHashTable *table, JSDHashEntryHdr *hdr, { if(wrapper->IsSubjectToFinalization()) { - js::SwitchToCompartment sc(data->cx, - wrapper->GetJSObjectPreserveColor()); + JS::AutoSwitchCompartment sc(data->cx, + wrapper->GetJSObjectPreserveColor()); if(JS_IsAboutToBeFinalized(data->cx, wrapper->GetJSObjectPreserveColor())) data->array->AppendElement(wrapper); diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 21443d5fb1c..c388df7ed25 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -58,8 +58,8 @@ #include "jsinterp.h" #include "jscntxt.h" #include "jsdbgapi.h" +#include "jsfriendapi.h" #include "jsgc.h" -#include "jscompartment.h" #include "nscore.h" #include "nsXPCOM.h" #include "nsAutoPtr.h" diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 52eabcdfbf1..4def5c1612a 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -442,7 +442,7 @@ XPCWrappedNativeScope::FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt) { XPCWrappedNativeScope* next = cur->mNext; - js::SwitchToCompartment sc(cx, cur->mGlobalJSObject); + JS::AutoSwitchCompartment sc(cx, cur->mGlobalJSObject); if(cur->mGlobalJSObject && JS_IsAboutToBeFinalized(cx, cur->mGlobalJSObject)) diff --git a/js/src/xpconnect/wrappers/AccessCheck.cpp b/js/src/xpconnect/wrappers/AccessCheck.cpp index 2ae500539f7..21f814e5644 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.cpp +++ b/js/src/xpconnect/wrappers/AccessCheck.cpp @@ -53,10 +53,11 @@ namespace xpc { -static nsIPrincipal * +nsIPrincipal * GetCompartmentPrincipal(JSCompartment *compartment) { - return compartment->principals ? static_cast(compartment->principals)->nsIPrincipalPtr : 0; + JSPrincipals *prin = JS_GetCompartmentPrincipals(compartment); + return prin ? static_cast(prin)->nsIPrincipalPtr : nsnull; } bool diff --git a/js/src/xpconnect/wrappers/AccessCheck.h b/js/src/xpconnect/wrappers/AccessCheck.h index 8074ee440f1..65c48be1f61 100644 --- a/js/src/xpconnect/wrappers/AccessCheck.h +++ b/js/src/xpconnect/wrappers/AccessCheck.h @@ -44,6 +44,9 @@ class nsIPrincipal; namespace xpc { +nsIPrincipal * +GetCompartmentPrincipal(JSCompartment *compartment); + class AccessCheck { public: static bool isSameOrigin(JSCompartment *a, JSCompartment *b); diff --git a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp index a4ae6967ee0..3734c1eb676 100644 --- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp +++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp @@ -42,6 +42,7 @@ #include "XPCWrapper.h" #include "CrossOriginWrapper.h" +#include "AccessCheck.h" #include "WrapperFactory.h" namespace xpc { @@ -62,12 +63,6 @@ CrossOriginWrapper::~CrossOriginWrapper() { } -static nsIPrincipal * -GetCompartmentPrincipal(JSCompartment *compartment) -{ - return static_cast(compartment->principals)->nsIPrincipalPtr; -} - bool CrossOriginWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set, js::PropertyDescriptor *desc) diff --git a/js/src/xpconnect/wrappers/WrapperFactory.cpp b/js/src/xpconnect/wrappers/WrapperFactory.cpp index 7a6b1f54301..1737a34ecf1 100644 --- a/js/src/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp @@ -265,7 +265,8 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO JSObject *xrayHolder = nsnull; JSWrapper *wrapper; - CompartmentPrivate *targetdata = static_cast(target->data); + CompartmentPrivate *targetdata = + static_cast(JS_GetCompartmentPrivate(cx, target)); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { wrapper = &JSCrossCompartmentWrapper::singleton; diff --git a/js/src/xpconnect/wrappers/XrayWrapper.cpp b/js/src/xpconnect/wrappers/XrayWrapper.cpp index 742f9af4d88..11930a4cdbe 100644 --- a/js/src/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp @@ -556,7 +556,7 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid if (desc->obj) desc->obj = wrapper; - return cx->compartment->wrap(cx, desc_in); + return JS_WrapPropertyDescriptor(cx, desc_in); } if (!this->resolveOwnProperty(cx, wrapper, id, set, desc_in)) @@ -627,7 +627,7 @@ XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, js } desc->obj = (desc->obj == wnObject) ? wrapper : nsnull; - return cx->compartment->wrap(cx, desc_in); + return JS_WrapPropertyDescriptor(cx, desc_in); } return this->resolveOwnProperty(cx, wrapper, id, set, desc_in); @@ -649,7 +649,7 @@ XrayWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, if (!ac.enter(cx, wnObject)) return false; - if (!cx->compartment->wrap(cx, desc)) + if (!JS_WrapPropertyDescriptor(cx, desc)) return false; return JS_DefinePropertyById(cx, wnObject, id, jsdesc->value, jsdesc->getter, jsdesc->setter, From 53fcc3230a7f98b0e05fef63dc147ecac9dc4549 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 1 Sep 2011 21:47:07 +0200 Subject: [PATCH 036/172] bug 684010 - remove JSScrit::isAboutToBeFinalized. r=bhackett1024 --HG-- extra : rebase_source : 7281e691bf5cbe62772e7671baa59d514829ff5f --- js/src/jsinfer.cpp | 4 ++-- js/src/jsinferinlines.h | 8 -------- js/src/jsscript.h | 2 -- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index d45a70a96a3..b858e076308 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -5480,7 +5480,7 @@ TypeCompartment::sweep(JSContext *cx) const AllocationSiteKey &key = e.front().key; TypeObject *object = e.front().value; - if (key.script->isAboutToBeFinalized(cx) || !object->isMarked()) + if (IsAboutToBeFinalized(cx, key.script) || !object->isMarked()) e.removeFront(); } } @@ -5520,7 +5520,7 @@ TypeScript::Sweep(JSContext *cx, JSScript *script) unsigned num = NumTypeSets(script); TypeSet *typeArray = script->types->typeArray(); - if (script->isAboutToBeFinalized(cx)) { + if (IsAboutToBeFinalized(cx, script)) { /* Release all memory associated with the persistent type sets. */ for (unsigned i = 0; i < num; i++) typeArray[i].clearObjects(); diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index f7b1239a9ad..7f3b30d04ac 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1218,14 +1218,6 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key) } } /* namespace js::types */ -inline bool -JSScript::isAboutToBeFinalized(JSContext *cx) -{ - return isCachedEval || - (u.object && IsAboutToBeFinalized(cx, u.object)) || - (hasFunction && IsAboutToBeFinalized(cx, function())); -} - inline bool JSScript::ensureHasTypes(JSContext *cx) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index edb1cf5ccfa..6a2c6674d6c 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -629,8 +629,6 @@ struct JSScript : public js::gc::Cell { inline bool hasAnalysis(); inline js::analyze::ScriptAnalysis *analysis(); - inline bool isAboutToBeFinalized(JSContext *cx); - private: bool makeTypes(JSContext *cx); bool makeAnalysis(JSContext *cx); From 3b06c78f9588df6e9c72ccdfb7da4ddb2d2cb1b5 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 18 Aug 2011 09:16:08 +0200 Subject: [PATCH 037/172] bug 681884 - faster slow path of GC allocations. r=wmccloskey --- js/src/jsarray.cpp | 4 +- js/src/jscell.h | 32 + js/src/jscntxt.h | 26 +- js/src/jscompartment.cpp | 20 +- js/src/jscompartment.h | 9 +- js/src/jsemit.cpp | 4 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 889 ++++++++++++-------------- js/src/jsgc.h | 447 +++++++------ js/src/jsgcinlines.h | 99 ++- js/src/jsgcstats.cpp | 3 +- js/src/jsgcstats.h | 3 +- js/src/jsinfer.cpp | 8 +- js/src/jsinfer.h | 7 +- js/src/jsinterp.cpp | 2 +- js/src/jsobj.cpp | 29 +- js/src/jsobj.h | 4 +- js/src/jsobjinlines.h | 56 +- js/src/jsproxy.cpp | 2 +- js/src/jsscopeinlines.h | 2 +- js/src/methodjit/BaseAssembler.h | 9 +- js/src/methodjit/StubCalls.cpp | 2 +- js/src/vm/String-inl.h | 4 +- js/src/vm/String.cpp | 6 +- js/src/xpconnect/src/xpcjsruntime.cpp | 5 +- 25 files changed, 843 insertions(+), 831 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3ac30aabdc5..95bc34c3e42 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1068,7 +1068,7 @@ JSObject::makeDenseArraySlow(JSContext *cx) js::Shape *oldMap = lastProp; /* Create a native scope. */ - js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind()); + gc::AllocKind kind = getAllocKind(); if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getProto()->getNewType(cx), kind)) return false; @@ -3256,7 +3256,7 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto) { JS_ASSERT_IF(proto, proto->isArray()); - gc::FinalizeKind kind = GuessObjectGCKind(length, true); + gc::AllocKind kind = GuessObjectGCKind(length, true); JSObject *obj = detail::NewObject(cx, &js_ArrayClass, proto, NULL, kind); if (!obj) return NULL; diff --git a/js/src/jscell.h b/js/src/jscell.h index d380a76dac7..d5d0595f067 100644 --- a/js/src/jscell.h +++ b/js/src/jscell.h @@ -50,6 +50,37 @@ namespace gc { struct ArenaHeader; struct Chunk; +/* The GC allocation kinds. */ +enum AllocKind { + FINALIZE_OBJECT0, + FINALIZE_OBJECT0_BACKGROUND, + FINALIZE_OBJECT2, + FINALIZE_OBJECT2_BACKGROUND, + FINALIZE_OBJECT4, + FINALIZE_OBJECT4_BACKGROUND, + FINALIZE_OBJECT8, + FINALIZE_OBJECT8_BACKGROUND, + FINALIZE_OBJECT12, + FINALIZE_OBJECT12_BACKGROUND, + FINALIZE_OBJECT16, + FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, + FINALIZE_FUNCTION, + FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, + FINALIZE_SCRIPT, + FINALIZE_SHAPE, + FINALIZE_TYPE_OBJECT, +#if JS_HAS_XML_SUPPORT + FINALIZE_XML, +#endif + FINALIZE_SHORT_STRING, + FINALIZE_STRING, + FINALIZE_EXTERNAL_STRING, + FINALIZE_LAST = FINALIZE_EXTERNAL_STRING +}; + +const size_t FINALIZE_LIMIT = FINALIZE_LAST + 1; + /* * Live objects are marked black. How many other additional colors are available * depends on the size of the GCThing. @@ -67,6 +98,7 @@ struct Cell { inline uintptr_t address() const; inline ArenaHeader *arenaHeader() const; inline Chunk *chunk() const; + inline AllocKind getAllocKind() const; JS_ALWAYS_INLINE bool isMarked(uint32 color = BLACK) const; JS_ALWAYS_INLINE bool markIfUnmarked(uint32 color = BLACK) const; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index f55286cfe42..2f30ccfc640 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -427,7 +427,7 @@ struct JSRuntime { int64 gcNextFullGCTime; int64 gcJitReleaseTime; JSGCMode gcMode; - volatile bool gcIsNeeded; + volatile jsuword gcIsNeeded; js::WeakMapBase *gcWeakMapList; /* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */ @@ -1794,17 +1794,33 @@ class AutoXMLRooter : private AutoGCRooter { class AutoLockGC { public: - explicit AutoLockGC(JSRuntime *rt + explicit AutoLockGC(JSRuntime *rt = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM) - : rt(rt) + : runtime(rt) { JS_GUARD_OBJECT_NOTIFIER_INIT; + if (rt) + JS_LOCK_GC(rt); + } + + bool locked() const { + return !!runtime; + } + + void lock(JSRuntime *rt) { + JS_ASSERT(rt); + JS_ASSERT(!runtime); + runtime = rt; JS_LOCK_GC(rt); } - ~AutoLockGC() { JS_UNLOCK_GC(rt); } + + ~AutoLockGC() { + if (runtime) + JS_UNLOCK_GC(runtime); + } private: - JSRuntime *rt; + JSRuntime *runtime; JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 73f9c775121..187f6d7f535 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -129,9 +129,6 @@ JSCompartment::~JSCompartment() bool JSCompartment::init(JSContext *cx) { - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].init(); - activeAnalysis = activeInference = false; types.init(cx); @@ -140,7 +137,6 @@ JSCompartment::init(JSContext *cx) JS_InitArenaPool(&pool, "analysis", 4096 - ARENA_HEADER_SIZE_HACK, 8); - freeLists.init(); if (!crossCompartmentWrappers.init()) return false; @@ -188,16 +184,6 @@ JSCompartment::getMjitCodeStats(size_t& method, size_t& regexp, size_t& unused) } #endif -bool -JSCompartment::arenaListsAreEmpty() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (!arenas[i].isEmpty()) - return false; - } - return true; -} - static bool IsCrossCompartmentWrapper(JSObject *wrapper) { @@ -505,10 +491,10 @@ JSCompartment::markTypes(JSTracer *trc) MarkScript(trc, script, "mark_types_script"); } - for (unsigned thingKind = FINALIZE_OBJECT0; + for (size_t thingKind = FINALIZE_OBJECT0; thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST; thingKind++) { - for (CellIterUnderGC i(this, FinalizeKind(thingKind)); !i.done(); i.next()) { + for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) { JSObject *object = i.get(); if (!object->isNewborn() && object->hasSingletonType()) MarkObject(trc, *object, "mark_types_singleton"); @@ -652,7 +638,7 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) void JSCompartment::purge(JSContext *cx) { - freeLists.purge(); + arenas.purge(); dtoaCache.purge(); /* diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 0252d8f404f..ce0b87b111c 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -394,8 +394,7 @@ struct JS_FRIEND_API(JSCompartment) { JSRuntime *rt; JSPrincipals *principals; - js::gc::ArenaList arenas[js::gc::FINALIZE_LIMIT]; - js::gc::FreeLists freeLists; + js::gc::ArenaLists arenas; uint32 gcBytes; uint32 gcTriggerBytes; @@ -535,12 +534,6 @@ struct JS_FRIEND_API(JSCompartment) { void markTypes(JSTracer *trc); void sweep(JSContext *cx, uint32 releaseInterval); void purge(JSContext *cx); - void finishArenaLists(); - void finalizeObjectArenaLists(JSContext *cx); - void finalizeStringArenaLists(JSContext *cx); - void finalizeShapeArenaLists(JSContext *cx); - void finalizeScriptArenaLists(JSContext *cx); - bool arenaListsAreEmpty(); void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); void reduceGCTriggerBytes(uint32 amount); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 765746429d2..21d5385fbb5 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4891,7 +4891,7 @@ JSParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp) case TOK_RC: { JS_ASSERT((pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST)); - gc::FinalizeKind kind = GuessObjectGCKind(pn_count, false); + gc::AllocKind kind = GuessObjectGCKind(pn_count, false); JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return false; @@ -7084,7 +7084,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) */ JSObject *obj = NULL; if (!cg->hasSharps() && cg->compileAndGo()) { - gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false); + gc::AllocKind kind = GuessObjectGCKind(pn->pn_count, false); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return JS_FALSE; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index a2b73c55acc..7d852a502db 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -773,7 +773,7 @@ NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *c Bindings &bindings = script->bindings; size_t argsVars = bindings.countArgsAndVars(); size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars; - gc::FinalizeKind kind = gc::GetGCObjectKind(slots); + gc::AllocKind kind = gc::GetGCObjectKind(slots); JSObject *callobj = js_NewGCObject(cx, kind); if (!callobj) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8c808c392d5..90f0c1f7ff0 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -110,7 +110,7 @@ namespace js { namespace gc { /* This array should be const, but that doesn't link right under GCC. */ -FinalizeKind slotsToThingKind[] = { +AllocKind slotsToThingKind[] = { /* 0 */ FINALIZE_OBJECT0, FINALIZE_OBJECT2, FINALIZE_OBJECT2, FINALIZE_OBJECT4, /* 4 */ FINALIZE_OBJECT4, FINALIZE_OBJECT8, FINALIZE_OBJECT8, FINALIZE_OBJECT8, /* 8 */ FINALIZE_OBJECT8, FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12, @@ -120,7 +120,7 @@ FinalizeKind slotsToThingKind[] = { JS_STATIC_ASSERT(JS_ARRAY_LENGTH(slotsToThingKind) == SLOTS_TO_THING_KIND_LIMIT); -const uint8 GCThingSizeMap[] = { +const uint32 Arena::ThingSizes[] = { sizeof(JSObject), /* FINALIZE_OBJECT0 */ sizeof(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ sizeof(JSObject_Slots2), /* FINALIZE_OBJECT2 */ @@ -145,7 +145,34 @@ const uint8 GCThingSizeMap[] = { sizeof(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ }; -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(GCThingSizeMap) == FINALIZE_LIMIT); +#define OFFSET(type) uint32(sizeof(ArenaHeader) + (ArenaSize - sizeof(ArenaHeader)) % sizeof(type)) + +const uint32 Arena::FirstThingOffsets[] = { + OFFSET(JSObject), /* FINALIZE_OBJECT0 */ + OFFSET(JSObject), /* FINALIZE_OBJECT0_BACKGROUND */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2 */ + OFFSET(JSObject_Slots2), /* FINALIZE_OBJECT2_BACKGROUND */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4 */ + OFFSET(JSObject_Slots4), /* FINALIZE_OBJECT4_BACKGROUND */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8 */ + OFFSET(JSObject_Slots8), /* FINALIZE_OBJECT8_BACKGROUND */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12 */ + OFFSET(JSObject_Slots12), /* FINALIZE_OBJECT12_BACKGROUND */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */ + OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */ + OFFSET(JSFunction), /* FINALIZE_FUNCTION */ + OFFSET(JSScript), /* FINALIZE_SCRIPT */ + OFFSET(Shape), /* FINALIZE_SHAPE */ + OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */ +#if JS_HAS_XML_SUPPORT + OFFSET(JSXML), /* FINALIZE_XML */ +#endif + OFFSET(JSShortString), /* FINALIZE_SHORT_STRING */ + OFFSET(JSString), /* FINALIZE_STRING */ + OFFSET(JSExternalString), /* FINALIZE_EXTERNAL_STRING */ +}; + +#undef OFFSET #ifdef DEBUG void @@ -168,7 +195,7 @@ ArenaHeader::checkSynchronizedWithFreeList() const FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets); if (firstSpan.isEmpty()) return; - FreeSpan *list = &compartment->freeLists.lists[getThingKind()]; + const FreeSpan *list = compartment->arenas.getFreeList(getAllocKind()); if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress()) return; @@ -180,18 +207,28 @@ ArenaHeader::checkSynchronizedWithFreeList() const } #endif +/* static */ void +Arena::staticAsserts() +{ + JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT); + JS_STATIC_ASSERT(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT); +} + template inline bool -Arena::finalize(JSContext *cx) +Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize) { /* Enforce requirements on size of T. */ - JS_STATIC_ASSERT(sizeof(T) % Cell::CellSize == 0); - JS_STATIC_ASSERT(sizeof(T) <= 255); + JS_ASSERT(thingSize % Cell::CellSize == 0); + JS_ASSERT(thingSize <= 255); JS_ASSERT(aheader.allocated()); + JS_ASSERT(thingKind == aheader.getAllocKind()); + JS_ASSERT(thingSize == aheader.getThingSize()); JS_ASSERT(!aheader.getMarkingDelay()->link); - uintptr_t thing = thingsStart(sizeof(T)); + uintptr_t thing = thingsStart(thingKind); uintptr_t lastByte = thingsEnd() - 1; FreeSpan nextFree(aheader.getFirstFreeSpan()); @@ -204,13 +241,13 @@ Arena::finalize(JSContext *cx) #ifdef DEBUG size_t nmarked = 0; #endif - for (;; thing += sizeof(T)) { + for (;; thing += thingSize) { JS_ASSERT(thing <= lastByte + 1); if (thing == nextFree.first) { JS_ASSERT(nextFree.last <= lastByte); if (nextFree.last == lastByte) break; - JS_ASSERT(Arena::isAligned(nextFree.last, sizeof(T))); + JS_ASSERT(Arena::isAligned(nextFree.last, thingSize)); if (!newFreeSpanStart) newFreeSpanStart = thing; thing = nextFree.last; @@ -224,90 +261,134 @@ Arena::finalize(JSContext *cx) nmarked++; #endif if (newFreeSpanStart) { - JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T)); + JS_ASSERT(thing >= thingsStart(thingKind) + thingSize); newListTail->first = newFreeSpanStart; - newListTail->last = thing - sizeof(T); - newListTail = newListTail->nextSpanUnchecked(sizeof(T)); + newListTail->last = thing - thingSize; + newListTail = newListTail->nextSpanUnchecked(thingSize); newFreeSpanStart = 0; } } else { if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); + JS_POISON(t, JS_FREE_PATTERN, thingSize); } } } if (allClear) { JS_ASSERT(newListTail == &newListHead); - JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T))); + JS_ASSERT(newFreeSpanStart == thingsStart(thingKind)); return true; } newListTail->first = newFreeSpanStart ? newFreeSpanStart : nextFree.first; - JS_ASSERT(Arena::isAligned(newListTail->first, sizeof(T))); + JS_ASSERT(Arena::isAligned(newListTail->first, thingSize)); newListTail->last = lastByte; #ifdef DEBUG size_t nfree = 0; for (const FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) { span->checkSpan(); - JS_ASSERT(Arena::isAligned(span->first, sizeof(T))); - JS_ASSERT(Arena::isAligned(span->last, sizeof(T))); - nfree += (span->last - span->first) / sizeof(T) + 1; - JS_ASSERT(nfree + nmarked <= thingsPerArena(sizeof(T))); + JS_ASSERT(Arena::isAligned(span->first, thingSize)); + JS_ASSERT(Arena::isAligned(span->last, thingSize)); + nfree += (span->last - span->first) / thingSize + 1; + JS_ASSERT(nfree + nmarked <= thingsPerArena(thingSize)); } - nfree += (newListTail->last + 1 - newListTail->first) / sizeof(T); - JS_ASSERT(nfree + nmarked == thingsPerArena(sizeof(T))); + nfree += (newListTail->last + 1 - newListTail->first) / thingSize; + JS_ASSERT(nfree + nmarked == thingsPerArena(thingSize)); #endif aheader.setFirstFreeSpan(&newListHead); return false; } -/* - * Finalize arenas from the list. On return listHeadp points to the list of - * non-empty arenas. - */ template -static void -FinalizeArenas(JSContext *cx, ArenaHeader **listHeadp) +inline void +FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) { - ArenaHeader **ap = listHeadp; + /* + * Release empty arenas and move non-full arenas with some free things into + * a separated list that we append to al after the loop to ensure that any + * arena before al->cursor is full. + */ + JS_ASSERT_IF(!al->head, al->cursor == &al->head); + ArenaLists::ArenaList available; + ArenaHeader **ap = &al->head; + size_t thingSize = Arena::thingSize(thingKind); while (ArenaHeader *aheader = *ap) { - bool allClear = aheader->getArena()->finalize(cx); + bool allClear = aheader->getArena()->finalize(cx, thingKind, thingSize); if (allClear) { *ap = aheader->next; aheader->chunk()->releaseArena(aheader); + } else if (aheader->hasFreeThings()) { + *ap = aheader->next; + *available.cursor = aheader; + available.cursor = &aheader->next; } else { ap = &aheader->next; } } + + /* Terminate the available list and append it to al. */ + *available.cursor = NULL; + *ap = available.head; + al->cursor = ap; + JS_ASSERT_IF(!al->head, al->cursor == &al->head); } -#ifdef DEBUG -bool -checkArenaListAllUnmarked(JSCompartment *comp) +/* + * Finalize the list. On return al->cursor points to the first non-empty arena + * after the al->head. + */ +static void +FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind) { - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) { - if (comp->arenas[i].markedThingsInArenaList()) - return false; - } - return true; -} + switch(thingKind) { + case FINALIZE_OBJECT0: + case FINALIZE_OBJECT0_BACKGROUND: + case FINALIZE_OBJECT2: + case FINALIZE_OBJECT2_BACKGROUND: + case FINALIZE_OBJECT4: + case FINALIZE_OBJECT4_BACKGROUND: + case FINALIZE_OBJECT8: + case FINALIZE_OBJECT8_BACKGROUND: + case FINALIZE_OBJECT12: + case FINALIZE_OBJECT12_BACKGROUND: + case FINALIZE_OBJECT16: + case FINALIZE_OBJECT16_BACKGROUND: + case FINALIZE_FUNCTION: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SCRIPT: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SHAPE: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_TYPE_OBJECT: + FinalizeTypedArenas(cx, al, thingKind); + break; +#if JS_HAS_XML_SUPPORT + case FINALIZE_XML: + FinalizeTypedArenas(cx, al, thingKind); + break; #endif + case FINALIZE_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_SHORT_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + case FINALIZE_EXTERNAL_STRING: + FinalizeTypedArenas(cx, al, thingKind); + break; + } +} } /* namespace gc */ } /* namespace js */ -void -JSCompartment::finishArenaLists() -{ - for (unsigned i = 0; i < FINALIZE_LIMIT; i++) - arenas[i].releaseAll(i); -} - void Chunk::init(JSRuntime *rt) { @@ -376,15 +457,14 @@ Chunk::removeFromAvailableList() info.next = NULL; } -template ArenaHeader * -Chunk::allocateArena(JSContext *cx, unsigned thingKind) +Chunk::allocateArena(JSContext *cx, AllocKind thingKind) { JSCompartment *comp = cx->compartment; JS_ASSERT(hasAvailableArenas()); ArenaHeader *aheader = info.emptyArenaListHead; info.emptyArenaListHead = aheader->next; - aheader->init(comp, thingKind, thingSize); + aheader->init(comp, thingKind); --info.numFree; if (!hasAvailableArenas()) @@ -406,9 +486,9 @@ Chunk::releaseArena(ArenaHeader *aheader) JS_ASSERT(aheader->allocated()); JSRuntime *rt = info.runtime; #ifdef JS_THREADSAFE - Maybe maybeLock; + AutoLockGC maybeLock; if (rt->gcHelperThread.sweeping) - maybeLock.construct(info.runtime); + maybeLock.lock(info.runtime); #endif JSCompartment *comp = aheader->compartment; @@ -472,7 +552,8 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p) rt->gcChunkAllocator->free_(p); } -inline Chunk * +/* The caller must hold the GC lock. */ +static Chunk * PickChunk(JSContext *cx) { JSCompartment *comp = cx->compartment; @@ -651,45 +732,6 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr) } } -template -inline ConservativeGCTest -MarkArenaPtrConservatively(JSTracer *trc, ArenaHeader *aheader, uintptr_t addr) -{ - JS_ASSERT(aheader->allocated()); - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - - uintptr_t offset = addr & ArenaMask; - uintptr_t minOffset = Arena::thingsStartOffset(sizeof(T)); - if (offset < minOffset) - return CGCT_NOTARENA; - - /* addr can point inside the thing so we must align the address. */ - uintptr_t shift = (offset - minOffset) % sizeof(T); - addr -= shift; - - /* - * Check if the thing is free. We must use the list of free spans as at - * this point we no longer have the mark bits from the previous GC run and - * we must account for newly allocated things. - */ - if (InFreeList(aheader, addr)) - return CGCT_NOTLIVE; - - T *thing = reinterpret_cast(addr); - MarkRoot(trc, thing, "machine stack"); - -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - if (IS_GC_MARKING_TRACER(trc)) { - GCMarker *marker = static_cast(trc); - if (marker->conservativeDumpFileName) - marker->conservativeRoots.append(thing); - if (shift) - marker->conservativeStats.unaligned++; - } -#endif - return CGCT_VALID; -} - /* * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets * thingKind accordingly. Otherwise returns the reason for rejection. @@ -737,66 +779,44 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) if (!aheader->allocated()) return CGCT_FREEARENA; - ConservativeGCTest test; - unsigned thingKind = aheader->getThingKind(); + AllocKind thingKind = aheader->getAllocKind(); + uintptr_t offset = addr & ArenaMask; + uintptr_t minOffset = Arena::firstThingOffset(thingKind); + if (offset < minOffset) + return CGCT_NOTARENA; - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_EXTERNAL_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHORT_STRING: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_FUNCTION: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SCRIPT: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_SHAPE: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; - case FINALIZE_TYPE_OBJECT: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - test = MarkArenaPtrConservatively(trc, aheader, addr); - break; + /* addr can point inside the thing so we must align the address. */ + uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); + addr -= shift; + + /* + * Check if the thing is free. We must use the list of free spans as at + * this point we no longer have the mark bits from the previous GC run and + * we must account for newly allocated things. + */ + if (InFreeList(aheader, addr)) + return CGCT_NOTLIVE; + + void *thing = reinterpret_cast(addr); + +#ifdef DEBUG + const char pattern[] = "machine_stack %lx"; + char nameBuf[sizeof(pattern) - 3 + sizeof(addr) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) addr); + JS_SET_TRACING_NAME(trc, nameBuf); #endif - default: - test = CGCT_WRONGTAG; - JS_NOT_REACHED("wrong tag"); - } + MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); - return test; +#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS + if (IS_GC_MARKING_TRACER(trc)) { + GCMarker *marker = static_cast(trc); + if (marker->conservativeDumpFileName) + marker->conservativeRoots.append(thing); + if (shift) + marker->conservativeStats.unaligned++; + } +#endif + return CGCT_VALID; } static void @@ -926,11 +946,8 @@ void js_FinishGC(JSRuntime *rt) { /* Delete all remaining Compartments. */ - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { - JSCompartment *comp = *c; - comp->finishArenaLists(); - Foreground::delete_(comp); - } + for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) + Foreground::delete_(*c); rt->compartments.clear(); rt->atomsCompartment = NULL; @@ -1137,227 +1154,263 @@ JSCompartment::reduceGCTriggerBytes(uint32 amount) { namespace js { namespace gc { -inline ArenaHeader * -ArenaList::searchForFreeArena() +inline void * +ArenaLists::allocateFromArena(JSContext *cx, AllocKind thingKind) { - while (ArenaHeader *aheader = *cursor) { - cursor = &aheader->next; - if (aheader->hasFreeThings()) - return aheader; - } - return NULL; -} + Chunk *chunk = NULL; -template -inline ArenaHeader * -ArenaList::getArenaWithFreeList(JSContext *cx, unsigned thingKind) -{ - Chunk *chunk; + ArenaList *al = &arenaLists[thingKind]; + AutoLockGC maybeLock; #ifdef JS_THREADSAFE - /* - * We cannot search the arena list for free things while the - * background finalization runs and can modify head or cursor at any - * moment. - */ - if (backgroundFinalizeState == BFS_DONE) { - check_arena_list: - if (ArenaHeader *aheader = searchForFreeArena()) - return aheader; - } - - AutoLockGC lock(cx->runtime); - - for (;;) { - if (backgroundFinalizeState == BFS_JUST_FINISHED) { - /* - * Before we took the GC lock or while waiting for the background - * finalization to finish the latter added new arenas to the list. - * Check the list again for free things outside the GC lock. - */ - JS_ASSERT(*cursor); - backgroundFinalizeState = BFS_DONE; - goto check_arena_list; - } - - JS_ASSERT(!*cursor); - chunk = PickChunk(cx); - if (chunk || backgroundFinalizeState == BFS_DONE) - break; - + volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind]; + if (*bfs != BFS_DONE) { /* - * If the background finalization still runs, wait for it to - * finish and retry to check if it populated the arena list or - * added new empty arenas. + * We cannot search the arena list for free things while the + * background finalization runs and can modify head or cursor at any + * moment. So we always allocate a new arena in that case. */ - JS_ASSERT(backgroundFinalizeState == BFS_RUN); - cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime, false); - JS_ASSERT(backgroundFinalizeState == BFS_JUST_FINISHED || - backgroundFinalizeState == BFS_DONE); + maybeLock.lock(cx->runtime); + for (;;) { + if (*bfs == BFS_DONE) + break; + + if (*bfs == BFS_JUST_FINISHED) { + /* + * Before we took the GC lock or while waiting for the + * background finalization to finish the latter added new + * arenas to the list. + */ + *bfs = BFS_DONE; + break; + } + + JS_ASSERT(!*al->cursor); + chunk = PickChunk(cx); + if (chunk) + break; + + /* + * If the background finalization still runs, wait for it to + * finish and retry to check if it populated the arena list or + * added new empty arenas. + */ + JS_ASSERT(*bfs == BFS_RUN); + cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime, false); + JS_ASSERT(*bfs == BFS_JUST_FINISHED || *bfs == BFS_DONE); + } } - -#else /* !JS_THREADSAFE */ - - if (ArenaHeader *aheader = searchForFreeArena()) - return aheader; - chunk = PickChunk(cx); - -#endif /* !JS_THREADSAFE */ +#endif /* JS_THREADSAFE */ if (!chunk) { - GCREASON(CHUNK); - TriggerGC(cx->runtime); - return NULL; + if (ArenaHeader *aheader = *al->cursor) { + JS_ASSERT(aheader->hasFreeThings()); + + /* + * The empty arenas are returned to the chunk and should not present on + * the list. + */ + JS_ASSERT(!aheader->isEmpty()); + al->cursor = &aheader->next; + + /* + * Move the free span stored in the arena to the free list and + * allocate from it. + */ + freeLists[thingKind] = aheader->getFirstFreeSpan(); + aheader->setAsFullyUsed(); + return freeLists[thingKind].infallibleAllocate(Arena::thingSize(thingKind)); + } + + /* Make sure we hold the GC lock before we call PickChunk. */ + if (!maybeLock.locked()) + maybeLock.lock(cx->runtime); + chunk = PickChunk(cx); + if (!chunk) + return NULL; } /* - * While we still hold the GC lock get the arena from the chunk and add it - * to the head of the list before the cursor to prevent checking the arena - * for the free things. + * While we still hold the GC lock get an arena from some chunk, mark it + * as full as its single free span is moved to the free lits, and insert + * it to the list as a fully allocated arena. + * + * We add the arena before the the head, not after the tail pointed by the + * cursor, so after the GC the most recently added arena will be used first + * for allocations improving cache locality. */ - ArenaHeader *aheader = chunk->allocateArena(cx, thingKind); - aheader->next = head; - if (cursor == &head) - cursor = &aheader->next; - head = aheader; - return aheader; + JS_ASSERT(!*al->cursor); + ArenaHeader *aheader = chunk->allocateArena(cx, thingKind); + aheader->next = al->head; + if (!al->head) { + JS_ASSERT(al->cursor == &al->head); + al->cursor = &aheader->next; + } + al->head = aheader; + + /* See comments before allocateFromNewArena about this assert. */ + JS_ASSERT(!aheader->hasFreeThings()); + uintptr_t arenaAddr = aheader->arenaAddress(); + return freeLists[thingKind].allocateFromNewArena(arenaAddr, + Arena::firstThingOffset(thingKind), + Arena::thingSize(thingKind)); } -template void -ArenaList::finalizeNow(JSContext *cx) +ArenaLists::finalizeNow(JSContext *cx, AllocKind thingKind) { #ifdef JS_THREADSAFE - JS_ASSERT(backgroundFinalizeState == BFS_DONE); + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); #endif - FinalizeArenas(cx, &head); - cursor = &head; + FinalizeArenas(cx, &arenaLists[thingKind], thingKind); } -#ifdef JS_THREADSAFE -template inline void -ArenaList::finalizeLater(JSContext *cx) +ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind) { - JS_ASSERT_IF(head, - head->getThingKind() == FINALIZE_OBJECT0_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT2_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT4_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT8_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT12_BACKGROUND || - head->getThingKind() == FINALIZE_OBJECT16_BACKGROUND || - head->getThingKind() == FINALIZE_FUNCTION || - head->getThingKind() == FINALIZE_SHORT_STRING || - head->getThingKind() == FINALIZE_STRING); + JS_ASSERT(thingKind == FINALIZE_OBJECT0_BACKGROUND || + thingKind == FINALIZE_OBJECT2_BACKGROUND || + thingKind == FINALIZE_OBJECT4_BACKGROUND || + thingKind == FINALIZE_OBJECT8_BACKGROUND || + thingKind == FINALIZE_OBJECT12_BACKGROUND || + thingKind == FINALIZE_OBJECT16_BACKGROUND || + thingKind == FINALIZE_FUNCTION || + thingKind == FINALIZE_SHORT_STRING || + thingKind == FINALIZE_STRING); + +#ifdef JS_THREADSAFE JS_ASSERT(!cx->runtime->gcHelperThread.sweeping); + ArenaList *al = &arenaLists[thingKind]; + if (!al->head) { + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE); + JS_ASSERT(al->cursor == &al->head); + return; + } + /* * The state can be just-finished if we have not allocated any GC things * from the arena list after the previous background finalization. */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE || - backgroundFinalizeState == BFS_JUST_FINISHED); + JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE || + backgroundFinalizeState[thingKind] == BFS_JUST_FINISHED); - if (head && cx->gcBackgroundFree) { + if (cx->gcBackgroundFree) { /* * To ensure the finalization order even during the background GC we * must use infallibleAppend so arenas scheduled for background * finalization would not be finalized now if the append fails. */ - cx->gcBackgroundFree->finalizeVector.infallibleAppend(head); - head = NULL; - cursor = &head; - backgroundFinalizeState = BFS_RUN; + cx->gcBackgroundFree->finalizeVector.infallibleAppend(al->head); + al->clear(); + backgroundFinalizeState[thingKind] = BFS_RUN; } else { - JS_ASSERT_IF(!head, cursor == &head); - backgroundFinalizeState = BFS_DONE; - finalizeNow(cx); + FinalizeArenas(cx, al, thingKind); + backgroundFinalizeState[thingKind] = BFS_DONE; } + +#else /* !JS_THREADSAFE */ + + finalizeNow(cx, thingKind); + +#endif } /*static*/ void -ArenaList::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) +ArenaLists::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) { JS_ASSERT(listHead); - unsigned thingKind = listHead->getThingKind(); + AllocKind thingKind = listHead->getAllocKind(); JSCompartment *comp = listHead->compartment; - ArenaList *al = &comp->arenas[thingKind]; - - switch (thingKind) { - default: - JS_NOT_REACHED("wrong kind"); - break; - case FINALIZE_OBJECT0_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT2_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT4_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT8_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT12_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_OBJECT16_BACKGROUND: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_FUNCTION: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_STRING: - FinalizeArenas(cx, &listHead); - break; - case FINALIZE_SHORT_STRING: - FinalizeArenas(cx, &listHead); - break; - } + ArenaList finalized; + finalized.head = listHead; + FinalizeArenas(cx, &finalized, thingKind); /* * After we finish the finalization al->cursor must point to the end of * the head list as we emptied the list before the background finalization * and the allocation adds new arenas before the cursor. */ + ArenaLists *lists = &comp->arenas; + ArenaList *al = &lists->arenaLists[thingKind]; + AutoLockGC lock(cx->runtime); - JS_ASSERT(al->backgroundFinalizeState == BFS_RUN); + JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN); JS_ASSERT(!*al->cursor); - if (listHead) { - *al->cursor = listHead; - al->backgroundFinalizeState = BFS_JUST_FINISHED; + + /* + * We must set the state to BFS_JUST_FINISHED if we touch arenaList list, + * even if we add to the list only fully allocated arenas without any free + * things. It ensures that the allocation thread takes the GC lock and all + * writes to the free list elements are propagated. As we always take the + * GC lock when allocating new arenas from the chunks we can set the state + * to BFS_DONE if we have released all finalized arenas back to their + * chunks. + */ + if (finalized.head) { + *al->cursor = finalized.head; + if (finalized.cursor != &finalized.head) + al->cursor = finalized.cursor; + lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED; } else { - al->backgroundFinalizeState = BFS_DONE; + lists->backgroundFinalizeState[thingKind] = BFS_DONE; } } -#endif /* JS_THREADSAFE */ - -#ifdef DEBUG -bool -CheckAllocation(JSContext *cx) +void +ArenaLists::finalizeObjects(JSContext *cx) { + finalizeNow(cx, FINALIZE_OBJECT0); + finalizeNow(cx, FINALIZE_OBJECT2); + finalizeNow(cx, FINALIZE_OBJECT4); + finalizeNow(cx, FINALIZE_OBJECT8); + finalizeNow(cx, FINALIZE_OBJECT12); + finalizeNow(cx, FINALIZE_OBJECT16); + #ifdef JS_THREADSAFE - JS_ASSERT(cx->thread()); -#endif - JS_ASSERT(!cx->runtime->gcRunning); - return true; -} + finalizeLater(cx, FINALIZE_OBJECT0_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT2_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT4_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT8_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT12_BACKGROUND); + finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND); #endif -inline bool -NeedLastDitchGC(JSContext *cx) + /* + * We must finalize Function instances after finalizing any other objects + * even if we use the background finalization for the latter. See comments + * in JSObject::finalizeUpvarsIfFlatClosure. + */ + finalizeLater(cx, FINALIZE_FUNCTION); + +#if JS_HAS_XML_SUPPORT + finalizeNow(cx, FINALIZE_XML); +#endif +} + +void +ArenaLists::finalizeStrings(JSContext *cx) { - JSRuntime *rt = cx->runtime; - return rt->gcIsNeeded; + finalizeLater(cx, FINALIZE_SHORT_STRING); + finalizeLater(cx, FINALIZE_STRING); + + finalizeNow(cx, FINALIZE_EXTERNAL_STRING); } -/* - * Return false only if the GC run but could not bring its memory usage under - * JSRuntime::gcMaxBytes. - */ -static bool +void +ArenaLists::finalizeShapes(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SHAPE); + finalizeNow(cx, FINALIZE_TYPE_OBJECT); +} + +void +ArenaLists::finalizeScripts(JSContext *cx) +{ + finalizeNow(cx, FINALIZE_SCRIPT); +} + +static void RunLastDitchGC(JSContext *cx) { JSRuntime *rt = cx->runtime; @@ -1375,37 +1428,35 @@ RunLastDitchGC(JSContext *cx) if (rt->gcBytes >= rt->gcMaxBytes) cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime); #endif - - return rt->gcBytes < rt->gcMaxBytes; } -static inline bool +inline bool IsGCAllowed(JSContext *cx) { return !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota; } -template -inline void * -RefillTypedFreeList(JSContext *cx, unsigned thingKind) +/* static */ void * +ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { - JS_ASSERT(!cx->runtime->gcRunning); + JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty()); /* * For compatibility with older code we tolerate calling the allocator * during the GC in optimized builds. */ - if (cx->runtime->gcRunning) + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + if (rt->gcRunning) return NULL; - JSCompartment *compartment = cx->compartment; - JS_ASSERT(compartment->freeLists.lists[thingKind].isEmpty()); - - bool canGC = IsGCAllowed(cx); - bool runGC = canGC && JS_UNLIKELY(NeedLastDitchGC(cx)); + bool runGC = !!rt->gcIsNeeded; for (;;) { - if (runGC) { - if (!RunLastDitchGC(cx)) + if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { + RunLastDitchGC(cx); + + /* Report OOM of the GC failed to free enough memory. */ + if (rt->gcBytes > rt->gcMaxBytes) break; /* @@ -1413,22 +1464,24 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind) * things and populate the free list. If that happens, just * return that list head. */ - if (void *thing = compartment->freeLists.getNext(thingKind, sizeof(T))) + size_t thingSize = Arena::thingSize(thingKind); + if (void *thing = cx->compartment->arenas.allocateFromFreeList(thingKind, thingSize)) return thing; } - ArenaHeader *aheader = - compartment->arenas[thingKind].getArenaWithFreeList(cx, thingKind); - if (aheader) { - JS_ASSERT(sizeof(T) == aheader->getThingSize()); - return compartment->freeLists.populate(aheader, thingKind, sizeof(T)); - } + void *thing = cx->compartment->arenas.allocateFromArena(cx, thingKind); + if (JS_LIKELY(!!thing)) + return thing; /* - * We failed to allocate any arena. Run the GC if we can unless we - * have done it already. + * We failed to allocate. Run the GC if we can unless we have done it + * already. Otherwise report OOM but first schedule a new GC soon. */ - if (!canGC || runGC) + if (runGC || !IsGCAllowed(cx)) { + AutoLockGC lock(rt); + GCREASON(REFILL); + TriggerGC(rt); break; + } runGC = true; } @@ -1436,52 +1489,6 @@ RefillTypedFreeList(JSContext *cx, unsigned thingKind) return NULL; } -void * -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) -{ - switch (thingKind) { - case FINALIZE_OBJECT0: - case FINALIZE_OBJECT0_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT2: - case FINALIZE_OBJECT2_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT4: - case FINALIZE_OBJECT4_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT8: - case FINALIZE_OBJECT8_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT12: - case FINALIZE_OBJECT12_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_OBJECT16: - case FINALIZE_OBJECT16_BACKGROUND: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_EXTERNAL_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHORT_STRING: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_FUNCTION: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SCRIPT: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_SHAPE: - return RefillTypedFreeList(cx, thingKind); - case FINALIZE_TYPE_OBJECT: - return RefillTypedFreeList(cx, thingKind); -#if JS_HAS_XML_SUPPORT - case FINALIZE_XML: - return RefillTypedFreeList(cx, thingKind); -#endif - default: - JS_NOT_REACHED("bad finalize kind"); - return 0; - } -} - } /* namespace gc */ } /* namespace js */ @@ -1583,11 +1590,12 @@ GCMarker::delayMarkingChildren(const void *thing) static void MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader) { - JSGCTraceKind traceKind = GetFinalizableTraceKind(aheader->getThingKind()); + AllocKind thingKind = aheader->getAllocKind(); + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); size_t thingSize = aheader->getThingSize(); Arena *a = aheader->getArena(); uintptr_t end = a->thingsEnd(); - for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) { + for (uintptr_t thing = a->thingsStart(thingKind); thing != end; thing += thingSize) { Cell *t = reinterpret_cast(thing); if (t->isMarked()) JS_TraceChildren(trc, t, traceKind); @@ -1973,67 +1981,6 @@ MaybeGC(JSContext *cx) } /* namespace js */ -void -JSCompartment::finalizeObjectArenaLists(JSContext *cx) -{ - arenas[FINALIZE_OBJECT0]. finalizeNow(cx); - arenas[FINALIZE_OBJECT2]. finalizeNow(cx); - arenas[FINALIZE_OBJECT4]. finalizeNow(cx); - arenas[FINALIZE_OBJECT8]. finalizeNow(cx); - arenas[FINALIZE_OBJECT12].finalizeNow(cx); - arenas[FINALIZE_OBJECT16].finalizeNow(cx); - -#ifdef JS_THREADSAFE - arenas[FINALIZE_OBJECT0_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT2_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT4_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT8_BACKGROUND]. finalizeLater(cx); - arenas[FINALIZE_OBJECT12_BACKGROUND].finalizeLater(cx); - arenas[FINALIZE_OBJECT16_BACKGROUND].finalizeLater(cx); -#endif - - /* - * We must finalize Function instances after finalizing any other objects - * even if we use the background finalization for the latter. See comments - * in JSObject::finalizeUpvarsIfFlatClosure. - */ -#ifdef JS_THREADSAFE - arenas[FINALIZE_FUNCTION].finalizeLater(cx); -#else - arenas[FINALIZE_FUNCTION].finalizeNow(cx); -#endif - -#if JS_HAS_XML_SUPPORT - arenas[FINALIZE_XML].finalizeNow(cx); -#endif -} - -void -JSCompartment::finalizeStringArenaLists(JSContext *cx) -{ -#ifdef JS_THREADSAFE - arenas[FINALIZE_SHORT_STRING].finalizeLater(cx); - arenas[FINALIZE_STRING].finalizeLater(cx); -#else - arenas[FINALIZE_SHORT_STRING].finalizeNow(cx); - arenas[FINALIZE_STRING].finalizeNow(cx); -#endif - arenas[FINALIZE_EXTERNAL_STRING].finalizeNow(cx); -} - -void -JSCompartment::finalizeShapeArenaLists(JSContext *cx) -{ - arenas[FINALIZE_TYPE_OBJECT].finalizeNow(cx); - arenas[FINALIZE_SHAPE].finalizeNow(cx); -} - -void -JSCompartment::finalizeScriptArenaLists(JSContext *cx) -{ - arenas[FINALIZE_SCRIPT].finalizeNow(cx); -} - #ifdef JS_THREADSAFE namespace js { @@ -2125,9 +2072,9 @@ GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind) void GCHelperThread::waitBackgroundSweepEnd(JSRuntime *rt, bool gcUnlocked) { - Maybe lock; + AutoLockGC maybeLock; if (gcUnlocked) - lock.construct(rt); + maybeLock.lock(rt); while (sweeping) PR_WaitCondVar(sweepingDone, PR_INTERVAL_NO_TIMEOUT); } @@ -2158,10 +2105,10 @@ GCHelperThread::doSweep() /* * We must finalize in the insert order, see comments in - * finalizeObjectArenaLists. + * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaList::backgroundFinalize(cx, *i); + ArenaLists::backgroundFinalize(cx, *i); finalizeVector.resize(0); ExpireGCChunks(cx->runtime, lastGCKind); cx = NULL; @@ -2232,9 +2179,9 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) JSCompartment *compartment = *read++; if (!compartment->hold && - (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) + (compartment->arenas.arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) { - compartment->freeLists.checkEmpty(); + compartment->arenas.checkEmptyFreeLists(); if (callback) JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); if (compartment->principals) @@ -2334,7 +2281,8 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM /* Make sure that we didn't mark an object in another compartment */ if (comp) { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, checkArenaListAllUnmarked(*c)); + JS_ASSERT_IF(*c != comp && *c != rt->atomsCompartment, + (*c)->arenas.checkArenaListAllUnmarked()); } #endif @@ -2372,13 +2320,13 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM if (comp) { Probes::GCStartSweepPhase(comp); comp->sweep(cx, 0); - comp->finalizeObjectArenaLists(cx); + comp->arenas.finalizeObjects(cx); GCTIMESTAMP(sweepObjectEnd); - comp->finalizeStringArenaLists(cx); + comp->arenas.finalizeStrings(cx); GCTIMESTAMP(sweepStringEnd); - comp->finalizeScriptArenaLists(cx); + comp->arenas.finalizeScripts(cx); GCTIMESTAMP(sweepScriptEnd); - comp->finalizeShapeArenaLists(cx); + comp->arenas.finalizeShapes(cx); GCTIMESTAMP(sweepShapeEnd); Probes::GCEndSweepPhase(comp); } else { @@ -2393,24 +2341,24 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM SweepCrossCompartmentWrappers(cx); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { Probes::GCStartSweepPhase(*c); - (*c)->finalizeObjectArenaLists(cx); + (*c)->arenas.finalizeObjects(cx); } GCTIMESTAMP(sweepObjectEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - (*c)->finalizeStringArenaLists(cx); + (*c)->arenas.finalizeStrings(cx); GCTIMESTAMP(sweepStringEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { - (*c)->finalizeScriptArenaLists(cx); + (*c)->arenas.finalizeScripts(cx); } GCTIMESTAMP(sweepScriptEnd); for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) { - (*c)->finalizeShapeArenaLists(cx); + (*c)->arenas.finalizeShapes(cx); Probes::GCEndSweepPhase(*c); } @@ -2808,12 +2756,12 @@ class AutoCopyFreeListToArenas { AutoCopyFreeListToArenas(JSRuntime *rt) : rt(rt) { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->freeLists.copyToArenas(); + (*c)->arenas.copyFreeListsToArenas(); } ~AutoCopyFreeListToArenas() { for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) - (*c)->freeLists.clearInArenas(); + (*c)->arenas.clearFreeListsInArenas(); } }; @@ -2904,19 +2852,18 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, JSCompartment *compartment = *c; (*compartmentCallback)(cx, data, compartment); - for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) { - JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind); - size_t thingSize = GCThingSizeMap[thingKind]; + for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { + JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind)); + size_t thingSize = Arena::thingSize(AllocKind(thingKind)); IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize); IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize); - - ForEachArenaAndCell(compartment, (FinalizeKind) thingKind, arenaOp, cellOp); + ForEachArenaAndCell(compartment, AllocKind(thingKind), arenaOp, cellOp); } } } void -IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind, +IterateCells(JSContext *cx, JSCompartment *compartment, AllocKind thingKind, void *data, IterateCellCallback cellCallback) { /* :XXX: Any way to common this preamble with IterateCompartmentsArenasCells? */ @@ -2936,8 +2883,8 @@ IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind, AutoCopyFreeListToArenas copy(rt); - JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind); - size_t thingSize = GCThingSizeMap[thingKind]; + JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind); + size_t thingSize = Arena::thingSize(thingKind); if (compartment) { for (CellIterUnderGC i(compartment, thingKind); !i.done(); i.next()) diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 6b12dda1fc0..37727111e2f 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -80,43 +80,12 @@ namespace gc { struct Arena; struct MarkingDelay; -/* The kind of GC thing with a finalizer. */ -enum FinalizeKind { - FINALIZE_OBJECT0, - FINALIZE_OBJECT0_BACKGROUND, - FINALIZE_OBJECT2, - FINALIZE_OBJECT2_BACKGROUND, - FINALIZE_OBJECT4, - FINALIZE_OBJECT4_BACKGROUND, - FINALIZE_OBJECT8, - FINALIZE_OBJECT8_BACKGROUND, - FINALIZE_OBJECT12, - FINALIZE_OBJECT12_BACKGROUND, - FINALIZE_OBJECT16, - FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND, - FINALIZE_FUNCTION, - FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, - FINALIZE_SCRIPT, - FINALIZE_SHAPE, - FINALIZE_TYPE_OBJECT, -#if JS_HAS_XML_SUPPORT - FINALIZE_XML, -#endif - FINALIZE_SHORT_STRING, - FINALIZE_STRING, - FINALIZE_EXTERNAL_STRING, - FINALIZE_LIMIT -}; - /* * This must be an upper bound, but we do not need the least upper bound, so * we just exclude non-background objects. */ const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - (FINALIZE_OBJECT_LAST + 1) / 2; -extern JS_FRIEND_DATA(const uint8) GCThingSizeMap[]; - const size_t ArenaShift = 12; const size_t ArenaSize = size_t(1) << ArenaShift; const size_t ArenaMask = ArenaSize - 1; @@ -174,7 +143,7 @@ struct FreeSpan { * To minimize the size of the arena header the first span is encoded * there as offsets from the arena start. */ - static size_t encodeOffsets(size_t firstOffset, size_t lastOffset = ArenaSize - 1) { + static size_t encodeOffsets(size_t firstOffset, size_t lastOffset) { /* Check that we can pack the offsets into uint16. */ JS_STATIC_ASSERT(ArenaShift < 16); JS_ASSERT(firstOffset <= ArenaSize); @@ -183,7 +152,11 @@ struct FreeSpan { return firstOffset | (lastOffset << 16); } - static const size_t EmptyOffsets = ArenaSize | ((ArenaSize - 1) << 16); + /* + * Encoded offsets for a full arena when its first span is the last one + * and empty. + */ + static const size_t FullArenaOffsets = ArenaSize | ((ArenaSize - 1) << 16); static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) { JS_ASSERT(!(arenaAddr & ArenaMask)); @@ -287,6 +260,37 @@ struct FreeSpan { return reinterpret_cast(thing); } + /* A version of allocate when we know that the span is not empty. */ + JS_ALWAYS_INLINE void *infallibleAllocate(size_t thingSize) { + JS_ASSERT(thingSize % Cell::CellSize == 0); + checkSpan(); + uintptr_t thing = first; + if (thing < last) { + first = thing + thingSize; + } else { + JS_ASSERT(thing == last); + *this = *reinterpret_cast(thing); + } + checkSpan(); + return reinterpret_cast(thing); + } + + /* + * Allocate from a newly allocated arena. We do not move the free list + * from the arena. Rather we set the arena up as fully used during the + * initialization so to allocate we simply return the first thing in the + * arena and set the free list to point to the second. + */ + JS_ALWAYS_INLINE void *allocateFromNewArena(uintptr_t arenaAddr, size_t firstThingOffset, + size_t thingSize) { + JS_ASSERT(!(arenaAddr & ArenaMask)); + uintptr_t thing = arenaAddr | firstThingOffset; + first = thing + thingSize; + last = arenaAddr | ArenaMask; + checkSpan(); + return reinterpret_cast(thing); + } + void checkSpan() const { #ifdef DEBUG /* We do not allow spans at the end of the address space. */ @@ -365,13 +369,13 @@ struct ArenaHeader { size_t firstFreeSpanOffsets; /* - * One of FinalizeKind constants or FINALIZE_LIMIT when the arena does not + * One of AllocKind constants or FINALIZE_LIMIT when the arena does not * contain any GC things and is on the list of empty arenas in the GC - * chunk. The later allows to quickly check if the arena is allocated + * chunk. The latter allows to quickly check if the arena is allocated * during the conservative GC scanning without searching the arena in the * list. */ - unsigned thingKind; + unsigned allocKind; friend struct FreeLists; @@ -380,14 +384,15 @@ struct ArenaHeader { inline Chunk *chunk() const; void setAsNotAllocated() { - thingKind = FINALIZE_LIMIT; + allocKind = FINALIZE_LIMIT; } bool allocated() const { - return thingKind < FINALIZE_LIMIT; + JS_ASSERT(allocKind <= FINALIZE_LIMIT); + return allocKind < FINALIZE_LIMIT; } - inline void init(JSCompartment *comp, unsigned thingKind, size_t thingSize); + inline void init(JSCompartment *comp, AllocKind kind); uintptr_t arenaAddress() const { return address(); @@ -397,17 +402,21 @@ struct ArenaHeader { return reinterpret_cast(arenaAddress()); } - unsigned getThingKind() const { + AllocKind getAllocKind() const { JS_ASSERT(allocated()); - return thingKind; + return AllocKind(allocKind); } + inline size_t getThingSize() const; + bool hasFreeThings() const { - return firstFreeSpanOffsets != FreeSpan::EmptyOffsets; + return firstFreeSpanOffsets != FreeSpan::FullArenaOffsets; } + inline bool isEmpty() const; + void setAsFullyUsed() { - firstFreeSpanOffsets = FreeSpan::EmptyOffsets; + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; } FreeSpan getFirstFreeSpan() const { @@ -424,10 +433,6 @@ struct ArenaHeader { inline MarkingDelay *getMarkingDelay() const; - size_t getThingSize() const { - return GCThingSizeMap[getThingKind()]; - } - #ifdef DEBUG void checkSynchronizedWithFreeList() const; #endif @@ -446,13 +451,24 @@ struct Arena { * +-------------+-----+----+----+-----+----+ * * <----------------------------------------> = ArenaSize bytes - * <-------------------> = thingsStartOffset + * <-------------------> = first thing offset */ ArenaHeader aheader; uint8_t data[ArenaSize - sizeof(ArenaHeader)]; - static void staticAsserts() { - JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize); + private: + static JS_FRIEND_DATA(const uint32) ThingSizes[]; + static JS_FRIEND_DATA(const uint32) FirstThingOffsets[]; + + public: + static void staticAsserts(); + + static size_t thingSize(AllocKind kind) { + return ThingSizes[kind]; + } + + static size_t firstThingOffset(AllocKind kind) { + return FirstThingOffsets[kind]; } static size_t thingsPerArena(size_t thingSize) { @@ -461,9 +477,6 @@ struct Arena { /* We should be able to fit FreeSpan in any GC thing. */ JS_ASSERT(thingSize >= sizeof(FreeSpan)); - /* GCThingSizeMap assumes that any thing fits uint8. */ - JS_ASSERT(thingSize < 256); - return (ArenaSize - sizeof(ArenaHeader)) / thingSize; } @@ -471,10 +484,6 @@ struct Arena { return thingsPerArena(thingSize) * thingSize; } - static size_t thingsStartOffset(size_t thingSize) { - return ArenaSize - thingsSpan(thingSize); - } - static bool isAligned(uintptr_t thing, size_t thingSize) { /* Things ends at the arena end. */ uintptr_t tailOffset = (ArenaSize - thing) & ArenaMask; @@ -485,8 +494,8 @@ struct Arena { return aheader.address(); } - uintptr_t thingsStart(size_t thingSize) { - return address() | thingsStartOffset(thingSize); + uintptr_t thingsStart(AllocKind thingKind) { + return address() | firstThingOffset(thingKind); } uintptr_t thingsEnd() { @@ -494,7 +503,7 @@ struct Arena { } template - bool finalize(JSContext *cx); + bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize); }; /* @@ -641,8 +650,7 @@ struct Chunk { inline void addToAvailableList(JSCompartment *compartment); inline void removeFromAvailableList(); - template - ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind); + ArenaHeader *allocateArena(JSContext *cx, AllocKind kind); void releaseArena(ArenaHeader *aheader); }; @@ -676,6 +684,12 @@ Cell::chunk() const return reinterpret_cast(addr); } +AllocKind +Cell::getAllocKind() const +{ + return arenaHeader()->getAllocKind(); +} + #ifdef DEBUG inline bool Cell::isAligned() const @@ -685,13 +699,15 @@ Cell::isAligned() const #endif inline void -ArenaHeader::init(JSCompartment *comp, unsigned kind, size_t thingSize) +ArenaHeader::init(JSCompartment *comp, AllocKind kind) { JS_ASSERT(!allocated()); JS_ASSERT(!getMarkingDelay()->link); compartment = comp; - thingKind = kind; - firstFreeSpanOffsets = FreeSpan::encodeOffsets(Arena::thingsStartOffset(thingSize)); + allocKind = kind; + + /* See comments in FreeSpan::allocateFromNewArena. */ + firstFreeSpanOffsets = FreeSpan::FullArenaOffsets; } inline uintptr_t @@ -709,6 +725,22 @@ ArenaHeader::chunk() const return Chunk::fromAddress(address()); } +inline bool +ArenaHeader::isEmpty() const +{ + /* Arena is empty if its first span covers the whole arena. */ + JS_ASSERT(allocated()); + size_t firstThingOffset = Arena::firstThingOffset(getAllocKind()); + return firstFreeSpanOffsets == FreeSpan::encodeOffsets(firstThingOffset, ArenaMask); +} + +inline size_t +ArenaHeader::getThingSize() const +{ + JS_ASSERT(allocated()); + return Arena::thingSize(getAllocKind()); +} + JS_ALWAYS_INLINE void ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32 color, uintptr_t **wordp, uintptr_t *maskp) @@ -779,7 +811,7 @@ const float GC_HEAP_GROWTH_FACTOR = 3.0f; static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000; static inline JSGCTraceKind -GetFinalizableTraceKind(size_t thingKind) +MapAllocToTraceKind(AllocKind thingKind) { static const JSGCTraceKind map[FINALIZE_LIMIT] = { JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */ @@ -805,8 +837,6 @@ GetFinalizableTraceKind(size_t thingKind) JSTRACE_STRING, /* FINALIZE_STRING */ JSTRACE_STRING, /* FINALIZE_EXTERNAL_STRING */ }; - - JS_ASSERT(thingKind < FINALIZE_LIMIT); return map[thingKind]; } @@ -819,11 +849,46 @@ GetGCThingRuntime(void *thing) return reinterpret_cast(thing)->chunk()->info.runtime; } -/* The arenas in a list have uniform kind. */ -class ArenaList { +struct ArenaLists { + + /* + * ArenaList::head points to the start of the list. Normally cursor points + * to the first arena in the list with some free things and all arenas + * before cursor are fully allocated. However, as the arena currently being + * allocated from is considered full while its list of free spans is moved + * into the freeList, during the GC or cell enumeration, when an + * unallocated freeList is moved back to the arena, we can see an arena + * with some free cells before the cursor. The cursor is an indirect + * pointer to allow for efficient list insertion at the cursor point and + * other list manipulations. + */ + struct ArenaList { + ArenaHeader *head; + ArenaHeader **cursor; + + ArenaList() { + clear(); + } + + void clear() { + head = NULL; + cursor = &head; + } + }; + private: - ArenaHeader *head; /* list start */ - ArenaHeader **cursor; /* arena with free things */ + /* + * For each arena kind its free list is represented as the first span with + * free things. Initially all the spans are initialized as empty. After we + * find a new arena with available things we move its first free span into + * the list and set the arena as fully allocated. way we do not need to + * update the arena header after the initial allocation. When starting the + * GC we only move the head of the of the list of spans back to the arena + * only for the arena that was not fully allocated. + */ + FreeSpan freeLists[FINALIZE_LIMIT]; + + ArenaList arenaLists[FINALIZE_LIMIT]; #ifdef JS_THREADSAFE /* @@ -848,116 +913,95 @@ class ArenaList { BFS_JUST_FINISHED }; - volatile BackgroundFinalizeState backgroundFinalizeState; + volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT]; #endif public: - void init() { - head = NULL; - cursor = &head; + ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + freeLists[i].initAsEmpty(); #ifdef JS_THREADSAFE - backgroundFinalizeState = BFS_DONE; + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + backgroundFinalizeState[i] = BFS_DONE; #endif } - ArenaHeader *getHead() { return head; } - - inline ArenaHeader *searchForFreeArena(); - - template - inline ArenaHeader *getArenaWithFreeList(JSContext *cx, unsigned thingKind); - - template - void finalizeNow(JSContext *cx); - + ~ArenaLists() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { #ifdef JS_THREADSAFE - template - inline void finalizeLater(JSContext *cx); - - static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead); - - bool willBeFinalizedLater() const { - return backgroundFinalizeState == BFS_RUN; - } - - bool doneBackgroundFinalize() const { - return backgroundFinalizeState == BFS_DONE; - } + /* + * We can only call this during the shutdown after the last GC when + * the background finalization is disabled. + */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE); #endif + ArenaHeader **headp = &arenaLists[i].head; + while (ArenaHeader *aheader = *headp) { + *headp = aheader->next; + aheader->chunk()->releaseArena(aheader); + } + } + } + + const FreeSpan *getFreeList(AllocKind thingKind) const { + return &freeLists[thingKind]; + } + + ArenaHeader *getFirstArena(AllocKind thingKind) const { + return arenaLists[thingKind].head; + } + + bool arenaListsAreEmpty() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { +#ifdef JS_THREADSAFE + /* + * The arena cannot be empty if the background finalization is not yet + * done. + */ + if (backgroundFinalizeState[i] != BFS_DONE) + return false; +#endif + if (arenaLists[i].head) + return false; + } + return true; + } #ifdef DEBUG - bool markedThingsInArenaList() { + bool checkArenaListAllUnmarked() const { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { # ifdef JS_THREADSAFE - /* The background finalization must have stopped at this point. */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE || - backgroundFinalizeState == BFS_JUST_FINISHED); + /* The background finalization must have stopped at this point. */ + JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE || + backgroundFinalizeState[i] == BFS_JUST_FINISHED); # endif - for (ArenaHeader *aheader = head; aheader; aheader = aheader->next) { - if (!aheader->chunk()->bitmap.noBitsSet(aheader)) - return true; + for (ArenaHeader *aheader = arenaLists[i].head; aheader; aheader = aheader->next) { + if (!aheader->chunk()->bitmap.noBitsSet(aheader)) + return false; + } } - return false; + return true; } -#endif /* DEBUG */ - - void releaseAll(unsigned thingKind) { -# ifdef JS_THREADSAFE - /* - * We can only call this during the shutdown after the last GC when - * the background finalization is disabled. - */ - JS_ASSERT(backgroundFinalizeState == BFS_DONE); -# endif - while (ArenaHeader *aheader = head) { - head = aheader->next; - aheader->chunk()->releaseArena(aheader); - } - cursor = &head; - } - - bool isEmpty() const { -#ifdef JS_THREADSAFE - /* - * The arena cannot be empty if the background finalization is not yet - * done. - */ - if (backgroundFinalizeState != BFS_DONE) - return false; #endif - return !head; - } -}; -struct FreeLists { - /* - * For each arena kind its free list is represented as the first span with - * free things. Initially all the spans are zeroed to be treated as empty - * spans by the allocation code. After we find a new arena with available - * things we copy its first free span into the list and set the arena as - * if it has no free things. This way we do not need to update the arena - * header after the initial allocation. When starting the GC We only move - * the head of the of the list of spans back to the arena only for the - * arena that was not fully allocated. - */ - FreeSpan lists[FINALIZE_LIMIT]; - - void init() { - for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i) - lists[i].initAsEmpty(); +#ifdef JS_THREADSAFE + bool doneBackgroundFinalize(AllocKind kind) const { + return backgroundFinalizeState[kind] == BFS_DONE; } +#endif /* * Return the free list back to the arena so the GC finalization will not * run the finalizers over unitialized bytes from free things. */ void purge() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) { - FreeSpan *list = &lists[i]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) { + FreeSpan *headSpan = &freeLists[i]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(list); - list->initAsEmpty(); + aheader->setFirstFreeSpan(headSpan); + headSpan->initAsEmpty(); } } } @@ -967,17 +1011,17 @@ struct FreeLists { * the proper value in ArenaHeader::freeList when accessing the latter * outside the GC. */ - void copyToArenas() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) - copyToArena(FinalizeKind(i)); + void copyFreeListsToArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + copyFreeListToArena(AllocKind(i)); } - void copyToArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); + void copyFreeListToArena(AllocKind thingKind) { + FreeSpan *headSpan = &freeLists[thingKind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); JS_ASSERT(!aheader->hasFreeThings()); - aheader->setFirstFreeSpan(list); + aheader->setFirstFreeSpan(headSpan); } } @@ -985,17 +1029,17 @@ struct FreeLists { * Clear the free lists in arenas that were temporarily set there using * copyToArenas. */ - void clearInArenas() { - for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) - clearInArena(FinalizeKind(i)); + void clearFreeListsInArenas() { + for (size_t i = 0; i != FINALIZE_LIMIT; ++i) + clearFreeListInArena(AllocKind(i)); } - void clearInArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (!list->isEmpty()) { - ArenaHeader *aheader = list->arenaHeader(); - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list)); + void clearFreeListInArena(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (!headSpan->isEmpty()) { + ArenaHeader *aheader = headSpan->arenaHeader(); + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); aheader->setAsFullyUsed(); } } @@ -1004,45 +1048,54 @@ struct FreeLists { * Check that the free list is either empty or were synchronized with the * arena using copyToArena(). */ - bool isSynchronizedWithArena(FinalizeKind thingKind) { - FreeSpan *list = &lists[thingKind]; - if (list->isEmpty()) + bool isSynchronizedFreeList(AllocKind kind) { + FreeSpan *headSpan = &freeLists[kind]; + if (headSpan->isEmpty()) return true; - ArenaHeader *aheader = list->arenaHeader(); + ArenaHeader *aheader = headSpan->arenaHeader(); if (aheader->hasFreeThings()) { /* * If the arena has a free list, it must be the same as one in * lists. - */ - JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list)); + */ + JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(headSpan)); return true; } return false; } - JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) { - return lists[thingKind].allocate(thingSize); + JS_ALWAYS_INLINE void *allocateFromFreeList(AllocKind thingKind, size_t thingSize) { + return freeLists[thingKind].allocate(thingSize); } - void *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) { - FreeSpan *list = &lists[thingKind]; - *list = aheader->getFirstFreeSpan(); - aheader->setAsFullyUsed(); - void *t = list->allocate(thingSize); - JS_ASSERT(t); - return t; - } + static void *refillFreeList(JSContext *cx, AllocKind thingKind); - void checkEmpty() { + void checkEmptyFreeLists() { #ifdef DEBUG - for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i) - JS_ASSERT(lists[i].isEmpty()); + for (size_t i = 0; i != JS_ARRAY_LENGTH(freeLists); ++i) + JS_ASSERT(freeLists[i].isEmpty()); #endif } -}; -extern void * -RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); + void checkEmptyFreeList(AllocKind kind) { + JS_ASSERT(freeLists[kind].isEmpty()); + } + + void finalizeObjects(JSContext *cx); + void finalizeStrings(JSContext *cx); + void finalizeShapes(JSContext *cx); + void finalizeScripts(JSContext *cx); + +#ifdef JS_THREADSAFE + static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead); + + private: + inline void finalizeNow(JSContext *cx, AllocKind thingKind); + inline void finalizeLater(JSContext *cx, AllocKind thingKind); + + inline void *allocateFromArena(JSContext *cx, AllocKind thingKind); +#endif +}; /* * Initial allocation size for data structures holding chunks is set to hold @@ -1254,7 +1307,7 @@ class GCHelperThread { Vector finalizeVector; - friend class js::gc::ArenaList; + friend struct js::gc::ArenaLists; JS_FRIEND_API(void) replenishAndFreeLater(void *ptr); @@ -1520,7 +1573,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data, * the given compartment or for all compartments if it is null. */ extern JS_FRIEND_API(void) -IterateCells(JSContext *cx, JSCompartment *compartment, gc::FinalizeKind thingKind, +IterateCells(JSContext *cx, JSCompartment *compartment, gc::AllocKind thingKind, void *data, IterateCellCallback cellCallback); } /* namespace js */ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index a215a4835f4..ff64de8fc32 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -119,17 +119,17 @@ GetGCThingTraceKind(const void *thing) if (JSAtom::isStatic(thing)) return JSTRACE_STRING; const Cell *cell = reinterpret_cast(thing); - return GetFinalizableTraceKind(cell->arenaHeader()->getThingKind()); + return MapAllocToTraceKind(cell->getAllocKind()); } /* Capacity for slotsToThingKind */ const size_t SLOTS_TO_THING_KIND_LIMIT = 17; /* Get the best kind to use when making an object with the given slot count. */ -static inline FinalizeKind +static inline AllocKind GetGCObjectKind(size_t numSlots, bool isArray = false) { - extern FinalizeKind slotsToThingKind[]; + extern AllocKind slotsToThingKind[]; if (numSlots >= SLOTS_TO_THING_KIND_LIMIT) { /* @@ -144,37 +144,36 @@ GetGCObjectKind(size_t numSlots, bool isArray = false) } static inline bool -IsBackgroundFinalizeKind(FinalizeKind kind) +IsBackgroundAllocKind(AllocKind kind) { JS_ASSERT(kind <= FINALIZE_OBJECT_LAST); return kind % 2 == 1; } -static inline FinalizeKind -GetBackgroundFinalizeKind(FinalizeKind kind) +static inline AllocKind +GetBackgroundAllocKind(AllocKind kind) { - JS_ASSERT(!IsBackgroundFinalizeKind(kind)); - return (FinalizeKind) (kind + 1); + JS_ASSERT(!IsBackgroundAllocKind(kind)); + return (AllocKind) (kind + 1); } +/* + * Try to get the next larger size for an object, keeping BACKGROUND + * consistent. + */ static inline bool -CanBumpFinalizeKind(FinalizeKind kind) +TryIncrementAllocKind(AllocKind *kindp) { - JS_ASSERT(kind <= FINALIZE_OBJECT_LAST); - return (kind + 2) <= FINALIZE_OBJECT_LAST; -} - -/* Get the next larger size for an object, keeping BACKGROUND consistent. */ -static inline FinalizeKind -BumpFinalizeKind(FinalizeKind kind) -{ - JS_ASSERT(CanBumpFinalizeKind(kind)); - return (FinalizeKind) (kind + 2); + size_t next = size_t(*kindp) + 2; + if (next > size_t(FINALIZE_OBJECT_LAST)) + return false; + *kindp = AllocKind(next); + return true; } /* Get the number of fixed slots and initial capacity associated with a kind. */ static inline size_t -GetGCKindSlots(FinalizeKind thingKind) +GetGCKindSlots(AllocKind thingKind) { /* Using a switch in hopes that thingKind will usually be a compile-time constant. */ switch (thingKind) { @@ -229,11 +228,11 @@ GCPoke(JSContext *cx, Value oldval) */ template void -ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, +ForEachArenaAndCell(JSCompartment *compartment, AllocKind thingKind, ArenaOp arenaOp, CellOp cellOp) { - size_t thingSize = GCThingSizeMap[thingKind]; - ArenaHeader *aheader = compartment->arenas[thingKind].getHead(); + size_t thingSize = Arena::thingSize(thingKind); + ArenaHeader *aheader = compartment->arenas.getFirstArena(thingKind); for (; aheader; aheader = aheader->next) { Arena *arena = aheader->getArena(); @@ -241,7 +240,7 @@ ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, FreeSpan firstSpan(aheader->getFirstFreeSpan()); const FreeSpan *span = &firstSpan; - for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) { + for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) { JS_ASSERT(thing <= arena->thingsEnd()); if (thing == span->first) { if (!span->hasNext()) @@ -258,6 +257,7 @@ ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind, class CellIterImpl { + size_t firstThingOffset; size_t thingSize; ArenaHeader *aheader; FreeSpan firstSpan; @@ -269,9 +269,10 @@ class CellIterImpl CellIterImpl() { } - void init(JSCompartment *comp, FinalizeKind thingKind) { - thingSize = GCThingSizeMap[thingKind]; - aheader = comp->arenas[thingKind].getHead(); + void init(JSCompartment *comp, AllocKind kind) { + firstThingOffset = Arena::firstThingOffset(kind); + thingSize = Arena::thingSize(kind); + aheader = comp->arenas.getFirstArena(kind); firstSpan.initAsEmpty(); span = &firstSpan; thing = span->first; @@ -308,7 +309,7 @@ class CellIterImpl } firstSpan = aheader->getFirstFreeSpan(); span = &firstSpan; - thing = aheader->getArena()->thingsStart(thingSize); + thing = aheader->arenaAddress() | firstThingOffset; aheader = aheader->next; } cell = reinterpret_cast(thing); @@ -319,10 +320,10 @@ class CellIterImpl class CellIterUnderGC : public CellIterImpl { public: - CellIterUnderGC(JSCompartment *comp, FinalizeKind thingKind) { + CellIterUnderGC(JSCompartment *comp, AllocKind kind) { JS_ASSERT(comp->rt->gcRunning); - JS_ASSERT(comp->freeLists.lists[thingKind].isEmpty()); - init(comp, thingKind); + comp->arenas.checkEmptyFreeList(kind); + init(comp, kind); } }; @@ -333,29 +334,29 @@ class CellIterUnderGC : public CellIterImpl { */ class CellIter: public CellIterImpl { - FreeLists *lists; - FinalizeKind thingKind; + ArenaLists *lists; + AllocKind kind; #ifdef DEBUG size_t *counter; #endif public: - CellIter(JSContext *cx, JSCompartment *comp, FinalizeKind thingKind) - : lists(&comp->freeLists), - thingKind(thingKind) { + CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind) + : lists(&comp->arenas), + kind(kind) { #ifdef JS_THREADSAFE - JS_ASSERT(comp->arenas[thingKind].doneBackgroundFinalize()); + JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind)); #endif - if (lists->isSynchronizedWithArena(thingKind)) { + if (lists->isSynchronizedFreeList(kind)) { lists = NULL; } else { JS_ASSERT(!comp->rt->gcRunning); - lists->copyToArena(thingKind); + lists->copyFreeListToArena(kind); } #ifdef DEBUG counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck; ++*counter; #endif - init(comp, thingKind); + init(comp, kind); } ~CellIter() { @@ -364,7 +365,7 @@ class CellIter: public CellIterImpl --*counter; #endif if (lists) - lists->clearInArena(thingKind); + lists->clearFreeListInArena(kind); } }; @@ -385,14 +386,12 @@ inline void EmptyCellOp(Cell *t) {} template inline T * -NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize) +NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize) { - JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT); - JS_ASSERT(thingSize == js::gc::GCThingSizeMap[thingKind]); + JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind)); #ifdef JS_THREADSAFE JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment), - (thingKind == js::gc::FINALIZE_STRING) || - (thingKind == js::gc::FINALIZE_SHORT_STRING)); + kind == js::gc::FINALIZE_STRING || kind == js::gc::FINALIZE_SHORT_STRING); #endif JS_ASSERT(!cx->runtime->gcRunning); JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck); @@ -402,15 +401,15 @@ NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize) js::gc::RunDebugGC(cx); #endif - void *t = cx->compartment->freeLists.getNext(thingKind, thingSize); - return static_cast(t ? t : js::gc::RefillFinalizableFreeList(cx, thingKind)); + void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); + return static_cast(t ? t : js::gc::ArenaLists::refillFreeList(cx, kind)); } inline JSObject * -js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind) +js_NewGCObject(JSContext *cx, js::gc::AllocKind kind) { JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); - JSObject *obj = NewGCThing(cx, kind, js::gc::GCThingSizeMap[kind]); + JSObject *obj = NewGCThing(cx, kind, js::gc::Arena::thingSize(kind)); if (obj) obj->earlyInit(js::gc::GetGCKindSlots(kind)); return obj; diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 4ba1744bf54..dd73c50b42f 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -71,7 +71,6 @@ ConservativeGCStats::dump(FILE *fp) fprintf(fp, " not withing a chunk: %lu\n", ULSTAT(counter[CGCT_NOTCHUNK])); fprintf(fp, " not within arena range: %lu\n", ULSTAT(counter[CGCT_NOTARENA])); fprintf(fp, " points to free arena: %lu\n", ULSTAT(counter[CGCT_FREEARENA])); - fprintf(fp, " excluded, wrong tag: %lu\n", ULSTAT(counter[CGCT_WRONGTAG])); fprintf(fp, " excluded, not live: %lu\n", ULSTAT(counter[CGCT_NOTLIVE])); fprintf(fp, " valid GC things: %lu\n", ULSTAT(counter[CGCT_VALID])); fprintf(fp, " valid but not aligned: %lu\n", ULSTAT(unaligned)); @@ -204,7 +203,7 @@ GCMarker::dumpConservativeRoots() volatile GCTimer::JSGCReason gcReason = GCTimer::NOREASON; const char *gcReasons[] = {" API", "Maybe", "LastC", "DestC", "Compa", "LastD", - "Malloc", "Alloc", "Chunk", "Shape", " None"}; + "Malloc", "Refill", "Chunk", "Shape", " None"}; jsrefcount newChunkCount = 0; jsrefcount destroyChunkCount = 0; diff --git a/js/src/jsgcstats.h b/js/src/jsgcstats.h index 4925006c9a7..1ed5726ce91 100644 --- a/js/src/jsgcstats.h +++ b/js/src/jsgcstats.h @@ -99,7 +99,6 @@ enum ConservativeGCTest CGCT_NOTARENA, /* not within arena range in a chunk */ CGCT_NOTCHUNK, /* not within a valid chunk */ CGCT_FREEARENA, /* within arena containing only free things */ - CGCT_WRONGTAG, /* tagged pointer but wrong type */ CGCT_NOTLIVE, /* gcthing is not allocated */ CGCT_END }; @@ -162,7 +161,7 @@ struct GCTimer LASTDITCH, TOOMUCHMALLOC, ALLOCTRIGGER, - CHUNK, + REFILL, SHAPE, NOREASON }; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index b858e076308..7d35c447393 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2042,9 +2042,9 @@ TypeCompartment::nukeTypes(JSContext *cx) */ #ifdef JS_THREADSAFE - Maybe maybeLock; + AutoLockGC maybeLock; if (!cx->runtime->gcMarkAndSweep) - maybeLock.construct(cx->runtime); + maybeLock.lock(cx->runtime); #endif inferenceEnabled = false; @@ -4411,7 +4411,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) return; } - gc::FinalizeKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); + gc::AllocKind kind = gc::GetGCObjectKind(baseobj->slotSpan()); /* We should not have overflowed the maximum number of fixed slots for an object. */ JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan()); @@ -4441,7 +4441,7 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSScript *script) } type->newScript->script = script; - type->newScript->finalizeKind = unsigned(kind); + type->newScript->allocKind = kind; type->newScript->shape = baseobj->lastProperty(); type->newScript->initializerList = (TypeNewScript::Initializer *) diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 99ed6309ea9..90e3506d078 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -646,8 +646,8 @@ struct TypeNewScript { JSScript *script; - /* Finalize kind to use for newly constructed objects. */ - /* gc::FinalizeKind */ unsigned finalizeKind; + /* Allocation kind to use for newly constructed objects. */ + gc::AllocKind allocKind; /* * Shape to use for newly constructed objects. Reflects all definite @@ -806,8 +806,7 @@ struct TypeObject : gc::Cell * used as the scope of a new object whose prototype is |proto|. */ inline bool canProvideEmptyShape(js::Class *clasp); - inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind); + inline js::EmptyShape *getEmptyShape(JSContext *cx, js::Class *aclasp, gc::AllocKind kind); /* * Get or create a property of this object. Only call this for properties which diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 1a8e04a1b5b..b4187a0d265 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -5238,7 +5238,7 @@ BEGIN_CASE(JSOP_NEWINIT) if (i == JSProto_Array) { obj = NewDenseEmptyArray(cx); } else { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 54ba25276b8..6a28e0c3be7 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2913,7 +2913,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) if (!obj) { /* Make an object whether this was called with 'new' or not. */ JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined()); - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) return JS_FALSE; @@ -2928,7 +2928,7 @@ js_Object(JSContext *cx, uintN argc, Value *vp) JSObject * js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent, - gc::FinalizeKind kind, const Shape *shape) + gc::AllocKind kind, const Shape *shape) { JSObject *res = NewObjectWithType(cx, type, parent, kind); if (!res) @@ -2979,7 +2979,7 @@ js_CreateThis(JSContext *cx, JSObject *callee) JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL; JSObject *parent = callee->getParent(); - gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp); + gc::AllocKind kind = NewObjectGCKind(cx, newclasp); JSObject *obj = NewObject(cx, newclasp, proto, parent, kind); if (obj) obj->syncSpecialEquality(); @@ -2995,14 +2995,14 @@ CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject * * which reflects any properties that will definitely be added to the * object before it is read from. */ - gc::FinalizeKind kind = gc::FinalizeKind(type->newScript->finalizeKind); + gc::AllocKind kind = type->newScript->allocKind; JSObject *res = NewObjectWithType(cx, type, parent, kind); if (res) res->setMap((Shape *) type->newScript->shape); return res; } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); return NewObjectWithType(cx, type, parent, kind); } @@ -3018,7 +3018,7 @@ js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *pro return NULL; res = CreateThisForFunctionWithType(cx, type, callee->getParent()); } else { - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); res = NewNonFunction(cx, &js_ObjectClass, proto, callee->getParent(), kind); } @@ -3077,7 +3077,7 @@ JSObject* FASTCALL js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj) { if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind); } @@ -3129,7 +3129,7 @@ js_CreateThisFromTrace(JSContext *cx, JSObject *ctor, uintN protoSlot) return NULL; } - gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass); + gc::AllocKind kind = NewObjectGCKind(cx, &js_ObjectClass); return NewNativeClassInstance(cx, &js_ObjectClass, proto, parent, kind); } JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, OBJECT, UINTN, 0, @@ -3405,7 +3405,7 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp) JS_ASSERT(proto->isStaticBlock()); size_t count = OBJ_BLOCK_COUNT(cx, proto); - gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1); + gc::AllocKind kind = gc::GetGCObjectKind(count + 1); TypeObject *type = proto->getNewType(cx); if (!type) @@ -3615,9 +3615,7 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } } - JSObject *clone = NewObject(cx, getClass(), - proto, parent, - gc::FinalizeKind(finalizeKind())); + JSObject *clone = NewObject(cx, getClass(), proto, parent, getAllocKind()); if (!clone) return NULL; if (isNative()) { @@ -4364,16 +4362,15 @@ JSObject::allocSlots(JSContext *cx, size_t newcap) * objects are constructed. */ if (!hasLazyType() && type()->newScript) { - gc::FinalizeKind kind = gc::FinalizeKind(type()->newScript->finalizeKind); + gc::AllocKind kind = type()->newScript->allocKind; unsigned newScriptSlots = gc::GetGCKindSlots(kind); - if (newScriptSlots == numFixedSlots() && gc::CanBumpFinalizeKind(kind)) { - kind = gc::BumpFinalizeKind(kind); + if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) { JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, type()->newScript->shape); if (!obj) return false; - type()->newScript->finalizeKind = kind; + type()->newScript->allocKind = kind; type()->newScript->shape = obj->lastProperty(); type()->markStateChange(cx); } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 540bf04c388..51ab6da8946 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -664,8 +664,6 @@ struct JSObject : js::gc::Cell { inline bool hasPropertyTable() const; - /* gc::FinalizeKind */ unsigned finalizeKind() const; - uint32 numSlots() const { return uint32(capacity); } inline size_t structSize() const; @@ -1279,7 +1277,7 @@ struct JSObject : js::gc::Cell { js::types::TypeObject *type, JSObject *parent, void *priv, - /* gc::FinalizeKind */ unsigned kind); + js::gc::AllocKind kind); inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 8fcc22281d1..029778b7942 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -402,12 +402,6 @@ JSObject::setPrimitiveThis(const js::Value &pthis) setFixedSlot(JSSLOT_PRIMITIVE_THIS, pthis); } -inline /* gc::FinalizeKind */ unsigned -JSObject::finalizeKind() const -{ - return js::gc::FinalizeKind(arenaHeader()->getThingKind()); -} - inline bool JSObject::hasSlotsArray() const { @@ -964,7 +958,7 @@ JSObject::initSharingEmptyShape(JSContext *cx, js::types::TypeObject *type, JSObject *parent, void *privateValue, - /* js::gc::FinalizeKind */ unsigned kind) + js::gc::AllocKind kind) { init(cx, aclasp, type, parent, privateValue, false); @@ -1245,7 +1239,7 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri static inline bool InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::TypeObject *type, - gc::FinalizeKind kind) + gc::AllocKind kind) { JS_ASSERT(clasp->isNative()); @@ -1273,7 +1267,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::Ty } static inline bool -CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) +CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp) { #ifdef JS_THREADSAFE JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST); @@ -1281,10 +1275,10 @@ CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) * a different thread, we change the finalize kind. For example, * FINALIZE_OBJECT0 calls the finalizer on the main thread, * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread. - * IsBackgroundFinalizeKind is called to prevent recursively incrementing + * IsBackgroundAllocKind is called to prevent recursively incrementing * the finalize kind; kind may already be a background finalize kind. */ - if (!gc::IsBackgroundFinalizeKind(kind) && + if (!gc::IsBackgroundAllocKind(kind) && (!clasp->finalize || clasp->flags & JSCLASS_CONCURRENT_FINALIZER)) { return true; } @@ -1300,7 +1294,7 @@ CanBeFinalizedInBackground(gc::FinalizeKind kind, Class *clasp) */ static inline JSObject * NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, - JSObject *parent, gc::FinalizeKind kind) + JSObject *parent, gc::AllocKind kind) { JS_ASSERT(proto); JS_ASSERT(parent); @@ -1316,7 +1310,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, */ if (CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); @@ -1343,7 +1337,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, static inline JSObject * NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewNativeClassInstance(cx, clasp, proto, parent, kind); } @@ -1358,7 +1352,7 @@ FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject * right default proto and parent for clasp in cx. */ static inline JSObject * -NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) +NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::AllocKind kind) { VOUCH_DOES_NOT_REQUIRE_STACK(); @@ -1392,7 +1386,7 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind) static inline JSObject * NewBuiltinClassInstance(JSContext *cx, Class *clasp) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewBuiltinClassInstance(cx, clasp, kind); } @@ -1457,7 +1451,7 @@ namespace detail template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { /* Bootstrap the ur-object, and make it the default prototype object. */ if (withProto == WithProto::Class && !proto) { @@ -1478,7 +1472,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, */ if (!isFunction && CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind); if (!obj) @@ -1530,7 +1524,7 @@ NewFunction(JSContext *cx, JSObject *parent) template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { return detail::NewObject(cx, clasp, proto, parent, kind); } @@ -1539,14 +1533,14 @@ template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return detail::NewObject(cx, clasp, proto, parent, kind); } template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, - gc::FinalizeKind kind) + gc::AllocKind kind) { if (clasp == &js_FunctionClass) return detail::NewObject(cx, clasp, proto, parent, kind); @@ -1557,7 +1551,7 @@ template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); return NewObject(cx, clasp, proto, parent, kind); } @@ -1566,12 +1560,12 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) * avoid losing creation site information for objects made by scripted 'new'. */ static JS_ALWAYS_INLINE JSObject * -NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::FinalizeKind kind) +NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind) { JS_ASSERT(type == type->proto->newType); if (CanBeFinalizedInBackground(kind, &js_ObjectClass)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); if (!obj) @@ -1597,14 +1591,14 @@ out: extern JSObject * NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent, - gc::FinalizeKind kind, const Shape *shape); + gc::AllocKind kind, const Shape *shape); /* * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of * the object, zero if the final size is unknown. This should only be used for * objects that do not require any fixed slots. */ -static inline gc::FinalizeKind +static inline gc::AllocKind GuessObjectGCKind(size_t numSlots, bool isArray) { if (numSlots) @@ -1616,7 +1610,7 @@ GuessObjectGCKind(size_t numSlots, bool isArray) * Get the GC kind to use for scripted 'new' on the given class. * FIXME bug 547327: estimate the size from the allocation site. */ -static inline gc::FinalizeKind +static inline gc::AllocKind NewObjectGCKind(JSContext *cx, js::Class *clasp) { if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass) @@ -1628,17 +1622,16 @@ NewObjectGCKind(JSContext *cx, js::Class *clasp) static JS_ALWAYS_INLINE JSObject* NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto, - /*gc::FinalizeKind*/ unsigned _kind) + gc::AllocKind kind) { JS_ASSERT(clasp->isNative()); - gc::FinalizeKind kind = gc::FinalizeKind(_kind); types::TypeObject *type = proto->getNewType(cx); if (!type) return NULL; if (CanBeFinalizedInBackground(kind, clasp)) - kind = GetBackgroundFinalizeKind(kind); + kind = GetBackgroundAllocKind(kind); JSObject* obj = js_NewGCObject(cx, kind); if (!obj) @@ -1656,8 +1649,7 @@ CopyInitializerObject(JSContext *cx, JSObject *baseobj, types::TypeObject *type) JS_ASSERT(baseobj->getClass() == &js_ObjectClass); JS_ASSERT(!baseobj->inDictionaryMode()); - gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind()); - JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); + JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, baseobj->getAllocKind()); if (!obj || !obj->ensureSlots(cx, baseobj->numSlots())) return NULL; diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 44f157bdd27..70b1cd0ebe6 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1421,7 +1421,7 @@ FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp) * Make a blank object from the recipe fix provided to us. This must have * number of fixed slots as the proxy so that we can swap their contents. */ - gc::FinalizeKind kind = gc::FinalizeKind(proxy->arenaHeader()->getThingKind()); + gc::AllocKind kind = proxy->getAllocKind(); JSObject *newborn = NewNonFunction(cx, clasp, proto, parent, kind); if (!newborn) return false; diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index ea13c6943a0..6740e358e7c 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -68,7 +68,7 @@ js::Shape::freeTable(JSContext *cx) inline js::EmptyShape * js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp, - /* gc::FinalizeKind */ unsigned kind) + gc::AllocKind kind) { JS_ASSERT(!singleton); diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 5c4ad7c3dcf..dce486714ae 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -1236,10 +1236,10 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist */ Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject) { - unsigned thingKind = templateObject->arenaHeader()->getThingKind(); + gc::AllocKind allocKind = templateObject->getAllocKind(); - JS_ASSERT(thingKind >= gc::FINALIZE_OBJECT0 && thingKind <= gc::FINALIZE_OBJECT_LAST); - size_t thingSize = gc::GCThingSizeMap[thingKind]; + JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); + size_t thingSize = gc::Arena::thingSize(allocKind); JS_ASSERT(cx->typeInferenceEnabled()); JS_ASSERT(!templateObject->hasSlotsArray()); @@ -1253,7 +1253,8 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist * Inline FreeSpan::allocate. Only the case where the current freelist * span is not empty is handled. */ - gc::FreeSpan *list = &cx->compartment->freeLists.lists[thingKind]; + gc::FreeSpan *list = const_cast + (cx->compartment->arenas.getFreeList(allocKind)); loadPtr(&list->first, result); Jump jump = branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 752680127f9..31d46143809 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1350,7 +1350,7 @@ stubs::NewInitObject(VMFrame &f, JSObject *baseobj) TypeObject *type = (TypeObject *) f.scratch; if (!baseobj) { - gc::FinalizeKind kind = GuessObjectGCKind(0, false); + gc::AllocKind kind = GuessObjectGCKind(0, false); JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind); if (!obj) THROW(); diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 5feff394756..827e18ec29d 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -416,10 +416,10 @@ inline void JSAtom::finalize(JSRuntime *rt) { JS_ASSERT(isAtom()); - if (arenaHeader()->getThingKind() == js::gc::FINALIZE_STRING) + if (getAllocKind() == js::gc::FINALIZE_STRING) asFlat().finalize(rt); else - JS_ASSERT(arenaHeader()->getThingKind() == js::gc::FINALIZE_SHORT_STRING); + JS_ASSERT(getAllocKind() == js::gc::FINALIZE_SHORT_STRING); } inline void diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 0974c65e64b..daad19597dd 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=79 ft=cpp: * * ***** BEGIN LICENSE BLOCK ***** @@ -49,7 +49,7 @@ using namespace js; bool JSString::isShort() const { - bool is_short = arenaHeader()->getThingKind() == gc::FINALIZE_SHORT_STRING; + bool is_short = (getAllocKind() == gc::FINALIZE_SHORT_STRING); JS_ASSERT_IF(is_short, isFlat()); return is_short; } @@ -69,7 +69,7 @@ JSString::isInline() const bool JSString::isExternal() const { - bool is_external = arenaHeader()->getThingKind() == gc::FINALIZE_EXTERNAL_STRING; + bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING); JS_ASSERT_IF(is_external, isFixed()); return is_external; } diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 8ba371b73e8..02cfac7dd8f 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -1324,13 +1324,14 @@ ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena, IterateData *data = static_cast(vdata); data->currCompartmentStats->gcHeapArenaHeaders += sizeof(js::gc::ArenaHeader); + size_t allocationSpace = arena->thingsSpan(thingSize); data->currCompartmentStats->gcHeapArenaPadding += - arena->thingsStartOffset(thingSize) - sizeof(js::gc::ArenaHeader); + js::gc::ArenaSize - allocationSpace - sizeof(js::gc::ArenaHeader); // We don't call the callback on unused things. So we compute the // unused space like this: arenaUnused = maxArenaUnused - arenaUsed. // We do this by setting arenaUnused to maxArenaUnused here, and then // subtracting thingSize for every used cell, in CellCallback(). - data->currCompartmentStats->gcHeapArenaUnused += arena->thingsSpan(thingSize); + data->currCompartmentStats->gcHeapArenaUnused += allocationSpace; } void From dd3d708aa6340d4710d38583e2a1fefe17df6cbb Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 31 Aug 2011 18:10:16 -0400 Subject: [PATCH 038/172] Bug 639720. Get rid of the Window class setter so that SETNAME on the global is faster in the browser. r=mrbkap --- dom/base/nsDOMClassInfo.cpp | 131 +++++++++--------- dom/base/nsDOMClassInfo.h | 2 - dom/tests/mochitest/dom-level0/Makefile.in | 1 + .../dom-level0/test_location_setters.html | 78 +++++++++++ 4 files changed, 146 insertions(+), 66 deletions(-) create mode 100644 dom/tests/mochitest/dom-level0/test_location_setters.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 94f0342d261..909145ef494 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -48,6 +48,7 @@ #include "AccessCheck.h" #include "xpcprivate.h" +#include "XPCWrapper.h" #include "nscore.h" #include "nsDOMClassInfo.h" @@ -516,7 +517,6 @@ static const char kDOMStringBundleURL[] = #define WINDOW_SCRIPTABLE_FLAGS \ (nsIXPCScriptable::WANT_GETPROPERTY | \ - nsIXPCScriptable::WANT_SETPROPERTY | \ nsIXPCScriptable::WANT_PRECREATE | \ nsIXPCScriptable::WANT_FINALIZE | \ nsIXPCScriptable::WANT_EQUALITY | \ @@ -5269,39 +5269,6 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } -NS_IMETHODIMP -nsWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval) -{ - if (id == sLocation_id) { - JSAutoRequest ar(cx); - - JSString *val = ::JS_ValueToString(cx, *vp); - NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - - nsCOMPtr window = do_QueryWrappedNative(wrapper); - NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED); - - nsCOMPtr location; - nsresult rv = window->GetLocation(getter_AddRefs(location)); - NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && location, rv); - - nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, - vp, getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - nsDependentJSString depStr; - NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); - - rv = location->SetHref(depStr); - - return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; - } - - return NS_OK; -} - NS_IMETHODIMP nsWindowSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRBool *_retval) @@ -6392,6 +6359,68 @@ static JSNewResolveOp sOtherResolveFuncs[] = { mozilla::dom::workers::ResolveWorkerClasses }; +template +static nsresult +LocationSetterGuts(JSContext *cx, JSObject *obj, jsval *vp) +{ + // This function duplicates some of the logic in XPC_WN_HelperSetProperty + XPCWrappedNative *wrapper = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); + + // The error checks duplicate code in THROW_AND_RETURN_IF_BAD_WRAPPER + NS_ENSURE_TRUE(wrapper, NS_ERROR_XPC_BAD_OP_ON_WN_PROTO); + NS_ENSURE_TRUE(wrapper->IsValid(), NS_ERROR_XPC_HAS_BEEN_SHUTDOWN); + + nsCOMPtr xpcomObj = do_QueryWrappedNative(wrapper); + NS_ENSURE_TRUE(xpcomObj, NS_ERROR_UNEXPECTED); + + nsCOMPtr location; + nsresult rv = xpcomObj->GetLocation(getter_AddRefs(location)); + NS_ENSURE_SUCCESS(rv, rv); + + JSString *val = ::JS_ValueToString(cx, *vp); + NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); + + nsDependentJSString depStr; + NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); + + rv = location->SetHref(depStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr holder; + return WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, + &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp, + getter_AddRefs(holder)); +} + +template +static JSBool +LocationSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, + jsval *vp) +{ + nsresult rv = LocationSetterGuts(cx, obj, vp); + if (NS_FAILED(rv)) { + if (!::JS_IsExceptionPending(cx)) { + nsDOMClassInfo::ThrowJSException(cx, rv); + } + return JS_FALSE; + } + + return JS_TRUE; +} + +static JSBool +LocationSetterUnwrapper(JSContext *cx, JSObject *obj, jsid id, JSBool strict, + jsval *vp) +{ + JSObject *wrapped = XPCWrapper::UnsafeUnwrapSecurityWrapper(obj); + if (wrapped) { + obj = wrapped; + } + + return LocationSetter(cx, obj, id, strict, vp); +} + NS_IMETHODIMP nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, PRUint32 flags, @@ -6544,7 +6573,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); JSBool ok = JS_WrapValue(cx, &v) && - JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, + JS_DefinePropertyById(cx, obj, id, v, nsnull, + LocationSetterUnwrapper, JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { @@ -8177,7 +8207,8 @@ nsDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSAutoRequest ar(cx); - JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, nsnull, + JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, nsnull, + LocationSetter, JSPROP_PERMANENT | JSPROP_ENUMERATE); if (!ok) { @@ -8222,34 +8253,6 @@ NS_IMETHODIMP nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, PRBool *_retval) { - if (id == sLocation_id) { - nsCOMPtr doc = do_QueryWrappedNative(wrapper); - NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); - - nsCOMPtr location; - nsresult rv = doc->GetLocation(getter_AddRefs(location)); - NS_ENSURE_SUCCESS(rv, rv); - - if (location) { - JSAutoRequest ar(cx); - - JSString *val = ::JS_ValueToString(cx, *vp); - NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED); - - nsDependentJSString depStr; - NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED); - - rv = location->SetHref(depStr); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr holder; - rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location, - &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp, - getter_AddRefs(holder)); - return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; - } - } - if (id == sDocumentURIObject_id && IsPrivilegedScript()) { // We don't want privileged script that can read this property to set it, // but _do_ want to allow everyone else to set a value they can then read. diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 7f0e94bba1a..5efb38084cb 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -407,8 +407,6 @@ public: #endif NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, PRBool *_retval); - NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, - JSObject *obj, jsid id, jsval *vp, PRBool *_retval); NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRBool *_retval); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, diff --git a/dom/tests/mochitest/dom-level0/Makefile.in b/dom/tests/mochitest/dom-level0/Makefile.in index 55ace1c5d62..a08896bb4a8 100644 --- a/dom/tests/mochitest/dom-level0/Makefile.in +++ b/dom/tests/mochitest/dom-level0/Makefile.in @@ -55,6 +55,7 @@ _TEST_FILES = \ test_location.html \ test_innerWidthHeight_script.html \ innerWidthHeight_script.html \ + test_location_setters.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/tests/mochitest/dom-level0/test_location_setters.html b/dom/tests/mochitest/dom-level0/test_location_setters.html new file mode 100644 index 00000000000..eb159c7ff2d --- /dev/null +++ b/dom/tests/mochitest/dom-level0/test_location_setters.html @@ -0,0 +1,78 @@ + + + + + Test for Bug 639720 + + + + +Mozilla Bug 639720 +

+ +

+ +
+
+
+ + From 08f5a5a5bda4da149cd5b833e228211b8efd9bb1 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 2 Sep 2011 16:52:13 -0500 Subject: [PATCH 039/172] Bug 677957 - Fix peculiarly dynamically-nested for-in loops. ("Assertion failure: !cx->iterValue.isMagic(JS_NO_ITER_VALUE), at jsiter.cpp:1017") r=dvander. --- js/src/jit-test/tests/basic/bug677957-1.js | 4 +++ js/src/jit-test/tests/basic/bug677957-2.js | 13 ++++++++ js/src/jit-test/tests/basic/testBug683470.js | 24 +++++++------- js/src/jsemit.cpp | 15 ++++++--- js/src/jsinterp.cpp | 7 ++--- js/src/jsopcode.cpp | 33 +++++++++++++++++--- js/src/jsopcode.tbl | 7 +++-- js/src/jstracer.cpp | 2 +- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 7 ++--- js/src/methodjit/Compiler.h | 2 +- js/src/methodjit/StubCalls.cpp | 8 ++--- js/src/methodjit/StubCalls.h | 2 +- 13 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug677957-1.js create mode 100644 js/src/jit-test/tests/basic/bug677957-2.js diff --git a/js/src/jit-test/tests/basic/bug677957-1.js b/js/src/jit-test/tests/basic/bug677957-1.js new file mode 100644 index 00000000000..5e90b2a7633 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug677957-1.js @@ -0,0 +1,4 @@ +function test() { + for each (var i in []) {} +} +for each (new test().p in [0]) {} diff --git a/js/src/jit-test/tests/basic/bug677957-2.js b/js/src/jit-test/tests/basic/bug677957-2.js new file mode 100644 index 00000000000..7778dd02a79 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug677957-2.js @@ -0,0 +1,13 @@ +var x = {f: 1, g: 0}; +function f() { + for each (new f().nosuch.prop in x) + throw 'FAIL'; +} + +var e; +try { + f(); +} catch (exc) { + e = exc; +} +assertEq(e instanceof InternalError, true); diff --git a/js/src/jit-test/tests/basic/testBug683470.js b/js/src/jit-test/tests/basic/testBug683470.js index c9ed7a35fb1..8b78deaae17 100644 --- a/js/src/jit-test/tests/basic/testBug683470.js +++ b/js/src/jit-test/tests/basic/testBug683470.js @@ -1,15 +1,15 @@ // |jit-test| debug f = (function() { - function b() { - "use strict"; - Object.defineProperty(this, "x", ({})); - } - for each(let d in [0, 0]) { - try { - b(d); - } catch (e) {} - } -}) -trap(f, 54, undefined); -f() + function b() { + "use strict"; + Object.defineProperty(this, "x", ({})); + } + for each(let d in [0, 0]) { + try { + b(d); + } catch (e) {} + } +}); +trap(f, 53, undefined); +f(); diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index 21d5385fbb5..bfcf19eb7d3 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4640,7 +4640,8 @@ EmitAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs, JSOp op, JS case TOK_LP: if (!js_EmitTree(cx, cg, lhs)) return false; - offset++; + JS_ASSERT(lhs->pn_xflags & PNX_SETCALL); + offset += 2; break; #if JS_HAS_XML_SUPPORT case TOK_UNARYOP: @@ -4717,8 +4718,13 @@ EmitAssignment(JSContext *cx, JSCodeGenerator *cg, JSParseNode *lhs, JSOp op, JS if (!js_EmitTree(cx, cg, rhs)) return false; } else { - /* The value to assign is the next enumeration value in a for-in loop. */ - if (js_Emit2(cx, cg, JSOP_ITERNEXT, offset) < 0) + /* + * The value to assign is the next enumeration value in a for-in loop. + * That value is produced by a JSOP_ITERNEXT op, previously emitted. + * If offset == 1, that slot is already at the top of the + * stack. Otherwise, rearrange the stack to put that value on top. + */ + if (offset != 1 && js_Emit2(cx, cg, JSOP_PICK, offset - 1) < 0) return false; } @@ -5421,7 +5427,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) SET_STATEMENT_TOP(&stmtInfo, top); if (EmitTraceOp(cx, cg, NULL) < 0) return JS_FALSE; - #ifdef DEBUG intN loopDepth = cg->stackDepth; #endif @@ -5432,6 +5437,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * so that the decompiler can distinguish 'for (x in y)' from * 'for (var x in y)'. */ + if (js_Emit1(cx, cg, JSOP_ITERNEXT) < 0) + return false; if (!EmitAssignment(cx, cg, pn2->pn_kid2, JSOP_NOP, NULL)) return false; tmp2 = CG_OFFSET(cg); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index b4187a0d265..3fb1447526b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2697,11 +2697,10 @@ END_CASE(JSOP_MOREITER) BEGIN_CASE(JSOP_ITERNEXT) { - Value *itervp = regs.sp - GET_INT8(regs.pc); - JS_ASSERT(itervp >= regs.fp()->base()); - JS_ASSERT(itervp->isObject()); + JS_ASSERT(regs.sp - 1 >= regs.fp()->base()); + JS_ASSERT(regs.sp[-1].isObject()); PUSH_NULL(); - if (!IteratorNext(cx, &itervp->toObject(), ®s.sp[-1])) + if (!IteratorNext(cx, ®s.sp[-2].toObject(), ®s.sp[-1])) goto error; } END_CASE(JSOP_ITERNEXT) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 04480ebd528..0f577bf8e2f 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -2723,6 +2723,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) sn = NULL; break; + case JSOP_PICK: + { + uintN i = pc[1]; + LOCAL_ASSERT(ss->top > i + 1); + uintN bottom = ss->top - (i + 1); + + ptrdiff_t pickedOffset = ss->offsets[bottom]; + memmove(ss->offsets + bottom, ss->offsets + bottom + 1, + i * sizeof(ss->offsets[0])); + ss->offsets[ss->top - 1] = pickedOffset; + + jsbytecode pickedOpcode = ss->opcodes[bottom]; + memmove(ss->opcodes + bottom, ss->opcodes + bottom + 1, + i * sizeof(ss->opcodes[0])); + ss->opcodes[ss->top - 1] = pickedOpcode; + + todo = -2; + break; + } + case JSOP_ENTERWITH: LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); rval = POP_STR(); @@ -3231,7 +3251,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * <> * iter * pc: goto/gotox C [src_for_in(B, D)] - * A: <> + * A: trace + * iternext + * <> * B: pop [maybe a src_decl_var/let] * <> * C: moreiter @@ -3254,11 +3276,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts * to check that they are there. */ + LOCAL_ASSERT(pc[-JSOP_ITER_LENGTH] == JSOP_ITER); + LOCAL_ASSERT(pc[js_CodeSpec[op].length] == JSOP_TRACE); + LOCAL_ASSERT(pc[js_CodeSpec[op].length + JSOP_TRACE_LENGTH] == JSOP_ITERNEXT); cond = GetJumpOffset(pc, pc); next = js_GetSrcNoteOffset(sn, 0); tail = js_GetSrcNoteOffset(sn, 1); - JS_ASSERT(pc[next] == JSOP_POP); - JS_ASSERT(pc[cond] == JSOP_MOREITER); + LOCAL_ASSERT(pc[next] == JSOP_POP); + LOCAL_ASSERT(pc[cond] == JSOP_MOREITER); DECOMPILE_CODE(pc + oplen, next - oplen); lval = POP_STR(); @@ -3266,7 +3291,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * This string "" comes from jsopcode.tbl. It stands * for the result pushed by JSOP_ITERNEXT. */ - JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = ") == 0); + LOCAL_ASSERT(strcmp(lval + strlen(lval) - 9, " = ") == 0); const_cast(lval)[strlen(lval) - 9] = '\0'; LOCAL_ASSERT(ss->top >= 1); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index cb9c52712e1..8143f5b5d22 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -199,8 +199,9 @@ OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|J OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) /* - * Host object extension: given 'o.item(i) = j', the left-hand side compiles - * JSOP_SETCALL after JSOP_CALL, JSOP_EVAL, JSOP_FUNAPPLY, or JSOP_FUNCALL. + * Sometimes web pages do 'o.Item(i) = j'. This is not an early SyntaxError, + * for web compatibility. Instead we emit JSOP_SETCALL after the function call, + * an opcode that always throws. */ OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 1, 1, 2, 18, JOF_BYTE) @@ -218,7 +219,7 @@ OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 1, 1, 2, 18, JOF_BYTE) */ OPDEF(JSOP_ITER, 75, "iter", NULL, 2, 1, 1, 0, JOF_UINT8) OPDEF(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, 0, JOF_BYTE) -OPDEF(JSOP_ITERNEXT, 77, "iternext", "", 2, 0, 1, 0, JOF_UINT8) +OPDEF(JSOP_ITERNEXT, 77, "iternext", "", 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_ENDITER, 78, "enditer", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_FUNAPPLY, 79, "funapply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 6ffe74f5664..6b872cd5398 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -14862,7 +14862,7 @@ JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_ITERNEXT() { LIns* v_ins; - Value &iterobj_val = stackval(-GET_INT8(cx->regs().pc)); + Value &iterobj_val = stackval(-1); CHECK_STATUS_A(unboxNextValue(iterobj_val, v_ins)); stack(0, v_ins); return ARECORD_CONTINUE; diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index b9f20f434b7..3b05a0152f2 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -222,7 +222,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * before deserialization of bytecode. If the saved version does not match * the current version, abort deserialization and invalidate the file. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 93) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 94) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 539f7f25810..b149a25a1c7 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1682,7 +1682,7 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_ARGUMENTS) BEGIN_CASE(JSOP_ITERNEXT) - iterNext(GET_INT8(PC)); + iterNext(); END_CASE(JSOP_ITERNEXT) BEGIN_CASE(JSOP_DUP) @@ -5583,9 +5583,9 @@ mjit::Compiler::iter(uintN flags) * of a for-in loop to put the next value on the stack. */ void -mjit::Compiler::iterNext(ptrdiff_t offset) +mjit::Compiler::iterNext() { - FrameEntry *fe = frame.peek(-offset); + FrameEntry *fe = frame.peek(-1); RegisterID reg = frame.tempRegForData(fe); /* Is it worth trying to pin this longer? Prolly not. */ @@ -5629,7 +5629,6 @@ mjit::Compiler::iterNext(ptrdiff_t offset) frame.freeReg(T2); stubcc.leave(); - stubcc.masm.move(Imm32(offset), Registers::ArgReg1); OOL_STUBCALL(stubs::IterNext, REJOIN_FALLTHROUGH); frame.pushUntypedPayload(JSVAL_TYPE_STRING, T3); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 0c689a64398..5db2e55e2e8 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -578,7 +578,7 @@ class Compiler : public BaseCompiler bool constantFoldBranch(jsbytecode *target, bool taken); bool emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused); bool iter(uintN flags); - void iterNext(ptrdiff_t offset); + void iterNext(); bool iterMore(jsbytecode *target); void iterEnd(); MaybeJump loadDouble(FrameEntry *fe, FPRegisterID *fpReg, bool *allocated); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 31d46143809..37288b30145 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1843,12 +1843,12 @@ stubs::InitMethod(VMFrame &f, JSAtom *atom) } void JS_FASTCALL -stubs::IterNext(VMFrame &f, int32 offset) +stubs::IterNext(VMFrame &f) { - JS_ASSERT(f.regs.sp - offset >= f.fp()->base()); - JS_ASSERT(f.regs.sp[-offset].isObject()); + JS_ASSERT(f.regs.sp - 1 >= f.fp()->base()); + JS_ASSERT(f.regs.sp[-1].isObject()); - JSObject *iterobj = &f.regs.sp[-offset].toObject(); + JSObject *iterobj = &f.regs.sp[-1].toObject(); f.regs.sp[0].setNull(); f.regs.sp++; if (!js_IteratorNext(f.cx, iterobj, &f.regs.sp[-1])) diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index a59a77ba2ba..f9141dfaa64 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -192,7 +192,7 @@ void JS_FASTCALL StrictEq(VMFrame &f); void JS_FASTCALL StrictNe(VMFrame &f); void JS_FASTCALL Iter(VMFrame &f, uint32 flags); -void JS_FASTCALL IterNext(VMFrame &f, int32 offset); +void JS_FASTCALL IterNext(VMFrame &f); JSBool JS_FASTCALL IterMore(VMFrame &f); void JS_FASTCALL EndIter(VMFrame &f); From 13b1ee332aa3cff3376698b38f4e255996e73011 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 2 Sep 2011 16:52:16 -0500 Subject: [PATCH 040/172] Bug 682871 - make package fails when $(MOZILLA_DIR) is a relative path. r=Callek, sr=ted. --- toolkit/mozapps/installer/packager.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index bcbbf6e1493..b3a96c722d3 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -414,7 +414,7 @@ PRECOMPILE_GRE=$$PWD endif GENERATE_CACHE = \ - $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$(PRECOMPILE_GRE)" -a "$$PWD" -f $(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js -e "populate_startupcache('$(PRECOMPILE_DIR)', 'omni.jar', 'startupCache.zip');" && \ + $(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$(PRECOMPILE_GRE)" -a "$$PWD" -f $(call core_abspath,$(MOZILLA_DIR)/toolkit/mozapps/installer/precompile_cache.js) -e "populate_startupcache('$(PRECOMPILE_DIR)', 'omni.jar', 'startupCache.zip');" && \ rm -rf jsloader && \ $(UNZIP) startupCache.zip && \ rm startupCache.zip && \ From 5333be4d3fb1a8fa2e3d865d143aeb77cfee974a Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Fri, 2 Sep 2011 15:06:23 -0700 Subject: [PATCH 041/172] Fix for bug 660453 (Don't use txExecutionState if init fails). --- content/xslt/src/xslt/txExecutionState.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/content/xslt/src/xslt/txExecutionState.cpp b/content/xslt/src/xslt/txExecutionState.cpp index ccaef57164a..6321609adfa 100644 --- a/content/xslt/src/xslt/txExecutionState.cpp +++ b/content/xslt/src/xslt/txExecutionState.cpp @@ -86,7 +86,9 @@ txLoadedDocumentsHash::~txLoadedDocumentsHash() txExecutionState::txExecutionState(txStylesheet* aStylesheet, PRBool aDisableLoads) - : mStylesheet(aStylesheet), + : mOutputHandler(nsnull), + mResultHandler(nsnull), + mStylesheet(aStylesheet), mNextInstruction(nsnull), mLocalVariables(nsnull), mRecursionDepth(0), @@ -199,6 +201,9 @@ txExecutionState::end(nsresult aResult) if (NS_SUCCEEDED(aResult)) { popTemplateRule(); } + else if (!mOutputHandler) { + return NS_OK; + } return mOutputHandler->endDocument(aResult); } From cef491c4491e21aedb63751a2623eda6c5cb898f Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 2 Sep 2011 15:33:21 -0700 Subject: [PATCH 042/172] Bug 681621 - Displayport is wrong after MozScrolledAreaChanged event when page is scrolled to the right [r=stechz] --- mobile/chrome/content/bindings/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/chrome/content/bindings/browser.js b/mobile/chrome/content/bindings/browser.js index 6f9e0732274..8a2a6fd6111 100644 --- a/mobile/chrome/content/bindings/browser.js +++ b/mobile/chrome/content/bindings/browser.js @@ -629,7 +629,7 @@ let ContentScroll = { sendAsyncMessage("MozScrolledAreaChanged", { width: aEvent.width, height: aEvent.height, - left: aEvent.x + left: aEvent.x + content.scrollX }); // Send event only after painting to make sure content views in the parent process have From d05a66958e132a7206ac5ae764fe72f0fe615841 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 2 Sep 2011 16:48:49 -0700 Subject: [PATCH 043/172] Bug 684115 - Test what happens when chrome takes an XHR from one origin and sticks it in another origin. r=sicking --- dom/tests/mochitest/chrome/Makefile.in | 1 + .../mochitest/chrome/test_moving_xhr.xul | 41 +++++++++++++++++++ dom/tests/mochitest/general/Makefile.in | 1 + .../mochitest/general/file_moving_xhr.html | 26 ++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 dom/tests/mochitest/chrome/test_moving_xhr.xul create mode 100644 dom/tests/mochitest/general/file_moving_xhr.html diff --git a/dom/tests/mochitest/chrome/Makefile.in b/dom/tests/mochitest/chrome/Makefile.in index 4d55d443d07..8f3c1a0eaf3 100644 --- a/dom/tests/mochitest/chrome/Makefile.in +++ b/dom/tests/mochitest/chrome/Makefile.in @@ -69,6 +69,7 @@ _TEST_FILES = \ test_cyclecollector.xul \ test_resize_move_windows.xul \ test_popup_blocker_chrome.xul \ + test_moving_xhr.xul \ $(NULL) ifeq (WINNT,$(OS_ARCH)) diff --git a/dom/tests/mochitest/chrome/test_moving_xhr.xul b/dom/tests/mochitest/chrome/test_moving_xhr.xul new file mode 100644 index 00000000000..c02737b0d36 --- /dev/null +++ b/dom/tests/mochitest/chrome/test_moving_xhr.xul @@ -0,0 +1,41 @@ + + + + + + + + + + Mozilla Bug 654370 + + + + + +