author | Wes Kocher <wkocher@mozilla.com> |
Wed, 26 Apr 2017 17:30:31 -0700 | |
changeset 355133 | 0b77ed3f26c5335503bc16e85b8c067382e7bb1e |
parent 355081 | 745f2d85212d7c4bc82240c5a43730d9ecd32125 (current diff) |
parent 355132 | b1bd89334ee5a2f92aa087e2c1f28f19ded0d399 (diff) |
child 355134 | 36a3a336b4455d70899eda5224f8c01eb5b985be |
child 355228 | 029cbd7f6e4382bfa57176cfa2c9725f8be92993 |
child 355372 | 674bad1624a5ac186288273126291f322f4f4e66 |
push id | 41576 |
push user | kwierso@gmail.com |
push date | Thu, 27 Apr 2017 00:43:52 +0000 |
treeherder | autoland@36a3a336b445 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 55.0a1 |
first release with | nightly linux32
0b77ed3f26c5
/
55.0a1
/
20170427100642
/
files
nightly linux64
0b77ed3f26c5
/
55.0a1
/
20170427100655
/
files
nightly mac
0b77ed3f26c5
/
55.0a1
/
20170427030231
/
files
nightly win32
0b77ed3f26c5
/
55.0a1
/
20170427030231
/
files
nightly win64
0b77ed3f26c5
/
55.0a1
/
20170427030231
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
55.0a1
/
20170427100642
/
pushlog to previous
nightly linux64
55.0a1
/
20170427100655
/
pushlog to previous
nightly mac
55.0a1
/
20170427030231
/
pushlog to previous
nightly win32
55.0a1
/
20170427030231
/
pushlog to previous
nightly win64
55.0a1
/
20170427030231
/
pushlog to previous
|
--- a/browser/modules/test/browser/browser.ini +++ b/browser/modules/test/browser/browser.ini @@ -2,16 +2,17 @@ support-files = head.js [browser_BrowserUITelemetry_buckets.js] [browser_BrowserUITelemetry_defaults.js] [browser_BrowserUITelemetry_sidebar.js] [browser_BrowserUITelemetry_syncedtabs.js] [browser_ContentSearch.js] +skip-if = (os == "mac" || os == "linux") # Bug 1308343 support-files = contentSearch.js contentSearchBadImage.xml contentSearchSuggestions.sjs contentSearchSuggestions.xml !/browser/components/search/test/head.js !/browser/components/search/test/testEngine.xml [browser_NetworkPrioritizer.js]
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp +++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp @@ -1431,18 +1431,17 @@ WriteHeapGraph(JSContext* cx, } return ok; } static unsigned long msSinceProcessCreation(const TimeStamp& now) { - bool ignored; - auto duration = now - TimeStamp::ProcessCreation(ignored); + auto duration = now - TimeStamp::ProcessCreation(); return (unsigned long) duration.ToMilliseconds(); } /* static */ already_AddRefed<nsIFile> HeapSnapshot::CreateUniqueCoreDumpFile(ErrorResult& rv, const TimeStamp& now, nsAString& outFilePath, nsAString& outSnapshotId)
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -194,16 +194,17 @@ #include "nsXULAppAPI.h" #include "nsDOMNavigationTiming.h" #include "nsISecurityUITelemetry.h" #include "nsDSURIContentListener.h" #include "nsDocShellLoadTypes.h" #include "nsDocShellTransferableHooks.h" #include "nsICommandManager.h" #include "nsIDOMNode.h" +#include "nsIClassOfService.h" #include "nsIDocShellTreeOwner.h" #include "nsIHttpChannel.h" #include "nsIIDNService.h" #include "nsIInputStreamChannel.h" #include "nsINestedURI.h" #include "nsISHContainer.h" #include "nsISHistory.h" #include "nsISecureBrowserUI.h" @@ -3019,19 +3020,17 @@ nsDocShell::PopProfileTimelineMarkers( } return NS_OK; } nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) { - bool ignore; - *aWhen = - (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds(); + *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds(); return NS_OK; } NS_IMETHODIMP nsDocShell::SetWindowDraggingAllowed(bool aValue) { RefPtr<nsDocShell> parent = GetParentDocshell(); if (!aValue && mItemType == typeChrome && !parent) { @@ -11342,16 +11341,27 @@ nsDocShell::DoURILoad(nsIURI* aURI, if (IsFrame() && win) { nsCOMPtr<Element> frameElement = win->GetFrameElementInternal(); if (frameElement) { timedChannel->SetInitiatorType(frameElement->LocalName()); } } } + // Mark the http channel as UrgentStart for top level document loading + // in active tab. + if (mIsActive || (mLoadType & (LOAD_CMD_NORMAL | LOAD_CMD_HISTORY))) { + if (httpChannel && isTopLevelDoc) { + nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); + if (cos) { + cos->AddClassFlags(nsIClassOfService::UrgentStart); + } + } + } + rv = DoChannelLoad(channel, uriLoader, aBypassClassifier); // // If the channel load failed, we failed and nsIWebProgress just ain't // gonna happen. // if (NS_SUCCEEDED(rv)) { if (aDocShell) {
--- a/docshell/base/timeline/AbstractTimelineMarker.cpp +++ b/docshell/base/timeline/AbstractTimelineMarker.cpp @@ -60,18 +60,17 @@ AbstractTimelineMarker::SetCurrentTime() { TimeStamp now = TimeStamp::Now(); SetCustomTime(now); } void AbstractTimelineMarker::SetCustomTime(const TimeStamp& aTime) { - bool isInconsistent = false; - mTime = (aTime - TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds(); + mTime = (aTime - TimeStamp::ProcessCreation()).ToMilliseconds(); } void AbstractTimelineMarker::SetCustomTime(DOMHighResTimeStamp aTime) { mTime = aTime; }
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1156,17 +1156,17 @@ nsOuterWindowProxy::className(JSContext return "Window"; } void nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) const { nsGlobalWindow* outerWindow = GetOuterWindow(proxy); if (outerWindow) { - outerWindow->ClearWrapper(); + outerWindow->ClearWrapper(proxy); // Ideally we would use OnFinalize here, but it's possible that // EnsureScriptEnvironment will later be called on the window, and we don't // want to create a new script object in that case. Therefore, we need to // write a non-null value that will reliably crash when dereferenced. outerWindow->PoisonOuterWindowProxy(proxy); } } @@ -1723,17 +1723,17 @@ nsGlobalWindow::~nsGlobalWindow() static_cast<void*>(ToCanonicalSupports(outer)), url.get()); } #endif MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, ("DOMWINDOW %p destroyed", this)); if (IsOuterWindow()) { - JSObject *proxy = GetWrapperPreserveColor(); + JSObject *proxy = GetWrapperMaybeDead(); if (proxy) { js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr)); } // An outer window is destroyed with inner windows still possibly // alive, iterate through the inner windows and null out their // back pointer to this outer, and pull them out of the list of // inner windows. @@ -3942,17 +3942,17 @@ nsGlobalWindow::DispatchDOMEvent(WidgetE aEvent, aDOMEvent, aPresContext, aEventStatus); } void nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject) { MOZ_ASSERT(IsOuterWindow()); - if (aObject == GetWrapperPreserveColor()) { + if (aObject == GetWrapperMaybeDead()) { PoisonWrapper(); } } nsresult nsGlobalWindow::SetArguments(nsIArray *aArguments) { MOZ_ASSERT(IsOuterWindow());
--- a/dom/base/nsImageLoadingContent.cpp +++ b/dom/base/nsImageLoadingContent.cpp @@ -95,18 +95,17 @@ nsImageLoadingContent::nsImageLoadingCon mStateChangerDepth(0), mCurrentRequestRegistered(false), mPendingRequestRegistered(false) { if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) { mLoadingEnabled = false; } - bool isInconsistent; - mMostRecentRequestChange = TimeStamp::ProcessCreation(isInconsistent); + mMostRecentRequestChange = TimeStamp::ProcessCreation(); } void nsImageLoadingContent::DestroyImageLoadingContent() { // Cancel our requests so they won't hold stale refs to us // NB: Don't ask to discard the images here. ClearCurrentRequest(NS_BINDING_ABORTED);
--- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -90,22 +90,35 @@ public: /** * Get the cached wrapper. * * This getter does not change the color of the JSObject meaning that the * object returned is not guaranteed to be kept alive past the next CC. * * This should only be called if you are certain that the return value won't - * be passed into a JS API function and that it won't be stored without being + * be passed into a JSAPI function and that it won't be stored without being * rooted (or otherwise signaling the stored value to the CC). */ - JSObject* GetWrapperPreserveColor() const + JSObject* GetWrapperPreserveColor() const; + + /** + * Get the cached wrapper. + * + * This getter does not check whether the wrapper is dead and in the process + * of being finalized. + * + * This should only be called if you really need to see the raw contents of + * this cache, for example as part of finalization. Don't store the result + * anywhere or pass it into JSAPI functions that may cause the value to + * escape. + */ + JSObject* GetWrapperMaybeDead() const { - return GetWrapperJSObject(); + return mWrapper; } #ifdef DEBUG private: static bool HasJSObjectMovedOp(JSObject* aWrapper); public: #endif @@ -116,24 +129,33 @@ public: MOZ_ASSERT(aWrapper, "Use ClearWrapper!"); MOZ_ASSERT(HasJSObjectMovedOp(aWrapper), "Object has not provided the hook to update the wrapper if it is moved"); SetWrapperJSObject(aWrapper); } /** - * Clear the wrapper. This should be called from the finalizer for the - * wrapper. + * Clear the cache. */ void ClearWrapper() { MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!"); + SetWrapperJSObject(nullptr); + } - SetWrapperJSObject(nullptr); + /** + * Clear the cache if it still contains a specific wrapper object. This should + * be called from the finalizer for the wrapper. + */ + void ClearWrapper(JSObject* obj) + { + if (obj == mWrapper) { + ClearWrapper(); + } } /** * Update the wrapper if the object it contains is moved. * * This method must be called from the objectMovedOp class extension hook for * any wrapper cached object. */ @@ -289,21 +311,16 @@ private: friend class nsWindowRoot; void SetIsNotDOMBinding() { MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING), "This flag should be set before creating any wrappers."); SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING); } - JSObject *GetWrapperJSObject() const - { - return mWrapper; - } - void SetWrapperJSObject(JSObject* aWrapper); FlagsType GetWrapperFlags() const { return mFlags & kWrapperFlagsMask; } bool HasWrapperFlag(FlagsType aFlag) const
--- a/dom/base/nsWrapperCacheInlines.h +++ b/dom/base/nsWrapperCacheInlines.h @@ -7,16 +7,31 @@ #ifndef nsWrapperCacheInline_h___ #define nsWrapperCacheInline_h___ #include "nsWrapperCache.h" #include "js/GCAPI.h" #include "js/TracingAPI.h" inline JSObject* +nsWrapperCache::GetWrapperPreserveColor() const +{ + JSObject* obj = mWrapper; + if (obj && js::gc::EdgeNeedsSweepUnbarriered(&obj)) { + // The object has been found to be dead and is in the process of being + // finalized, so don't let the caller see it. As an optimisation, remove it + // from the cache so we don't have to do this check in future. + const_cast<nsWrapperCache*>(this)->ClearWrapper(); + return nullptr; + } + MOZ_ASSERT(obj == mWrapper); + return obj; +} + +inline JSObject* nsWrapperCache::GetWrapper() const { JSObject* obj = GetWrapperPreserveColor(); if (obj) { JS::ExposeObjectToActiveJS(obj); } return obj; }
--- a/dom/base/test/browser.ini +++ b/dom/base/test/browser.ini @@ -1,14 +1,16 @@ [DEFAULT] support-files = audio.ogg empty.html file_audioLoop.html file_audioLoopInIframe.html + file_bug902350.html + file_bug902350_frame.html file_bug1011748_redirect.sjs file_bug1011748_OK.sjs file_bug1303838.html file_bug1303838_target.html file_bug1303838_with_iframe.html file_messagemanager_unload.html file_pluginAudio.html file_use_counter_outer.html
--- a/dom/base/test/browser_bug902350.js +++ b/dom/base/test/browser_bug902350.js @@ -1,15 +1,15 @@ /* * Mixed Content Block frame navigates for target="_top" - Test for Bug 902350 */ const PREF_ACTIVE = "security.mixed_content.block_active_content"; -const gHttpTestRoot = "https://example.com/tests/dom/base/test/"; +const gHttpTestRoot = "https://example.com/browser/dom/base/test/"; var origBlockActive; var gTestBrowser = null; registerCleanupFunction(function() { // Set preferences back to their original values Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive); });
--- a/dom/base/test/file_bug902350.html +++ b/dom/base/test/file_bug902350.html @@ -6,14 +6,14 @@ https://bugzilla.mozilla.org/show_bug.cg --> <head> <meta charset="utf-8"> <title>Test for Bug 902350</title> </head> <body> <div id="framediv"> - <iframe src="https://example.com/tests/dom/base/test/file_bug902350_frame.html" id="testing_frame"></iframe> + <iframe src="https://example.com/browser/dom/base/test/file_bug902350_frame.html" id="testing_frame"></iframe> </div> </body> </html>
--- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -116,18 +116,16 @@ support-files = file_bug708620-2.html file_bug708620.html file_bug769117.html file_bug782342.txt file_bug787778.sjs file_bug804395.jar file_bug869432.eventsource file_bug869432.eventsource^headers^ - file_bug902350.html - file_bug902350_frame.html file_bug907892.html file_bug945152.jar file_bug1274806.html file_domwindowutils_animation.html file_general_document.html file_htmlserializer_1.html file_htmlserializer_1_bodyonly.html file_htmlserializer_1_format.html
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1313,28 +1313,30 @@ GetUseXBLScope(T* aParentObject) inline bool GetUseXBLScope(const ParentObject& aParentObject) { return aParentObject.mUseXBLScope; } template<class T> inline void -ClearWrapper(T* p, nsWrapperCache* cache) +ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) { - cache->ClearWrapper(); + JS::AutoAssertGCCallback inCallback; + cache->ClearWrapper(obj); } template<class T> inline void -ClearWrapper(T* p, void*) +ClearWrapper(T* p, void*, JSObject* obj) { + JS::AutoAssertGCCallback inCallback; nsWrapperCache* cache; CallQueryInterface(p, &cache); - ClearWrapper(p, cache); + ClearWrapper(p, cache, obj); } template<class T> inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old) { JS::AutoAssertGCCallback inCallback; cache->UpdateWrapper(obj, old);
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1647,40 +1647,41 @@ class CGAddPropertyHook(CGAbstractClassH // obviously can't preserve if we're not initialized. if (self && self->GetWrapperPreserveColor()) { PreserveWrapper(self); } return true; """) -def finalizeHook(descriptor, hookName, freeOp): +def finalizeHook(descriptor, hookName, freeOp, obj): finalize = "" if descriptor.wrapperCache: - finalize += "ClearWrapper(self, self);\n" + finalize += "ClearWrapper(self, self, %s);\n" % obj if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n" if descriptor.isGlobal(): - finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp + finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), %s);\n" % (freeOp, obj) finalize += ("AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType) return CGIfWrapper(CGGeneric(finalize), "self") class CGClassFinalizeHook(CGAbstractClassHook): """ A hook for finalize, used to release our native object. """ def __init__(self, descriptor): args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')] CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 'void', args) def generate_code(self): - return finalizeHook(self.descriptor, self.name, self.args[0].name).define() + return finalizeHook(self.descriptor, self.name, + self.args[0].name, self.args[1].name).define() class CGClassObjectMovedHook(CGAbstractClassHook): """ A hook for objectMovedOp, used to update the wrapper cache when an object it is holding moves. """ def __init__(self, descriptor): @@ -12222,17 +12223,18 @@ class CGDOMJSProxyHandler_finalize(Class args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')] ClassMethod.__init__(self, "finalize", "void", args, virtual=True, override=True, const=True) self.descriptor = descriptor def getBody(self): return (("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n" % (self.descriptor.nativeType, self.descriptor.nativeType)) + - finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) + finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, + self.args[0].name, self.args[1].name).define()) class CGDOMJSProxyHandler_getElements(ClassMethod): def __init__(self, descriptor): assert descriptor.supportsIndexedProperties() args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy'),
--- a/dom/bindings/SimpleGlobalObject.cpp +++ b/dom/bindings/SimpleGlobalObject.cpp @@ -41,16 +41,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_END static void SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj) { SimpleGlobalObject* globalObject = static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj)); + globalObject->ClearWrapper(obj); NS_RELEASE(globalObject); } static void SimpleGlobal_moved(JSObject *obj, const JSObject *old) { SimpleGlobalObject* globalObject = static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
--- a/dom/bindings/SimpleGlobalObject.h +++ b/dom/bindings/SimpleGlobalObject.h @@ -82,17 +82,17 @@ private: SimpleGlobalObject(JSObject *global, GlobalType type) : mType(type) { SetWrapper(global); } virtual ~SimpleGlobalObject() { - ClearWrapper(); + MOZ_ASSERT(!GetWrapperMaybeDead()); } const GlobalType mType; }; } // namespace dom } // namespace mozilla
--- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -1468,16 +1468,21 @@ HTMLCanvasElement::InvalidateFromAsyncCa } element->InvalidateCanvasContent(nullptr); } void HTMLCanvasElement::StartVRPresentation() { + if (GetCurrentContextType() != CanvasContextType::WebGL1 && + GetCurrentContextType() != CanvasContextType::WebGL2) { + return; + } + WebGLContext* webgl = static_cast<WebGLContext*>(GetContextAtIndex(0)); if (!webgl) { return; } if (!webgl->StartVRPresentation()) { return; }
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -424,34 +424,39 @@ NS_IMPL_ISUPPORTS(UploadLastDir::Content NS_IMETHODIMP UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason) { nsCOMPtr<nsIFile> localFile; nsAutoString prefStr; if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) { prefStr = Preferences::GetString("dom.input.fallbackUploadDir"); - if (prefStr.IsEmpty()) { - // If no custom directory was set through the pref, default to - // "desktop" directory for each platform. - NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile)); - } - } - - if (!localFile) { - if (prefStr.IsEmpty() && mResult) { - nsCOMPtr<nsIVariant> pref; - mResult->GetValue(getter_AddRefs(pref)); - pref->GetAsAString(prefStr); - } + } + + if (prefStr.IsEmpty() && mResult) { + nsCOMPtr<nsIVariant> pref; + mResult->GetValue(getter_AddRefs(pref)); + pref->GetAsAString(prefStr); + } + + if (!prefStr.IsEmpty()) { localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); - localFile->InitWithPath(prefStr); - } - - mFilePicker->SetDisplayDirectory(localFile); + if (localFile && NS_WARN_IF(NS_FAILED(localFile->InitWithPath(prefStr)))) { + localFile = nullptr; + } + } + + if (localFile) { + mFilePicker->SetDisplayDirectory(localFile); + } else { + // If no custom directory was set through the pref, default to + // "desktop" directory for each platform. + mFilePicker->SetDisplaySpecialDirectory(NS_LITERAL_STRING(NS_OS_DESKTOP_DIR)); + } + mFilePicker->Open(mFpCallback); return NS_OK; } NS_IMETHODIMP UploadLastDir::ContentPrefCallback::HandleResult(nsIContentPref* pref) { mResult = pref;
--- a/dom/html/test/test_filepicker_default_directory.html +++ b/dom/html/test/test_filepicker_default_directory.html @@ -42,25 +42,28 @@ var MockFilePicker = SpecialPowers.MockF MockFilePicker.init(window); // need to show the MockFilePicker so .displayDirectory gets set var f = document.getElementById("f"); f.focus(); var testIndex = 0; var tests = [ - ["", defaultUploadDirectory.path], - [customUploadDirectory.path, customUploadDirectory.path] + ["", null, "Desk"], + [customUploadDirectory.path, customUploadDirectory.path, ""] ] MockFilePicker.showCallback = function(filepicker) { - info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path); + if (tests[testIndex][1] === null) { + is(SpecialPowers.wrap(MockFilePicker).displayDirectory, null, "DisplayDirectory is null"); + } else { + is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path, tests[testIndex][1], "DisplayDirectory matches the path"); + } - is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path, - tests[testIndex][1]); + is(SpecialPowers.wrap(MockFilePicker).displaySpecialDirectory, tests[testIndex][2], "DisplaySpecialDirectory matches the path"); if (++testIndex == tests.length) { MockFilePicker.cleanup(); SimpleTest.finish(); } else { launchNextTest(); } }
--- a/dom/ipc/FilePickerParent.cpp +++ b/dom/ipc/FilePickerParent.cpp @@ -255,16 +255,17 @@ FilePickerParent::CreateFilePicker() mozilla::ipc::IPCResult FilePickerParent::RecvOpen(const int16_t& aSelectedType, const bool& aAddToRecentDocs, const nsString& aDefaultFile, const nsString& aDefaultExtension, InfallibleTArray<nsString>&& aFilters, InfallibleTArray<nsString>&& aFilterNames, const nsString& aDisplayDirectory, + const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel) { if (!CreateFilePicker()) { Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); return IPC_OK(); } mFilePicker->SetAddToRecentDocs(aAddToRecentDocs); @@ -279,16 +280,18 @@ FilePickerParent::RecvOpen(const int16_t mFilePicker->SetOkButtonLabel(aOkButtonLabel); if (!aDisplayDirectory.IsEmpty()) { nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); if (localFile) { localFile->InitWithPath(aDisplayDirectory); mFilePicker->SetDisplayDirectory(localFile); } + } else if (!aDisplaySpecialDirectory.IsEmpty()) { + mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory); } mCallback = new FilePickerShownCallback(this); mFilePicker->Open(mCallback); return IPC_OK(); }
--- a/dom/ipc/FilePickerParent.h +++ b/dom/ipc/FilePickerParent.h @@ -47,16 +47,17 @@ class FilePickerParent : public PFilePic virtual mozilla::ipc::IPCResult RecvOpen(const int16_t& aSelectedType, const bool& aAddToRecentDocs, const nsString& aDefaultFile, const nsString& aDefaultExtension, InfallibleTArray<nsString>&& aFilters, InfallibleTArray<nsString>&& aFilterNames, const nsString& aDisplayDirectory, + const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel) override; virtual void ActorDestroy(ActorDestroyReason aWhy) override; class FilePickerShownCallback : public nsIFilePickerShownCallback { public: explicit FilePickerShownCallback(FilePickerParent* aFilePickerParent)
--- a/dom/ipc/PFilePicker.ipdl +++ b/dom/ipc/PFilePicker.ipdl @@ -36,16 +36,17 @@ union MaybeInputData protocol PFilePicker { manager PBrowser; parent: async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile, nsString defaultExtension, nsString[] filters, nsString[] filterNames, - nsString displayDirectory, nsString okButtonLabel); + nsString displayDirectory, nsString displaySpecialDirectory, + nsString okButtonLabel); child: async __delete__(MaybeInputData data, int16_t result); }; } // namespace dom } // namespace mozilla
--- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -14,16 +14,17 @@ #include "nsIFileStreams.h" #include "nsIObserverService.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIRunnable.h" #include "nsISimpleEnumerator.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" +#include "nsISupportsPrimitives.h" #include "nsITimer.h" #include "nsIURI.h" #include "nsPIDOMWindow.h" #include <algorithm> #include "GeckoProfiler.h" #include "mozilla/Atomics.h" #include "mozilla/BasePrincipal.h" @@ -365,16 +366,33 @@ public: } NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl) private: ~DirectoryLockImpl(); }; +class QuotaObject::StoragePressureRunnable final + : public Runnable +{ + const uint64_t mUsage; + +public: + explicit StoragePressureRunnable(uint64_t aUsage) + : mUsage(aUsage) + { } + +private: + ~StoragePressureRunnable() + { } + + NS_DECL_NSIRUNNABLE +}; + class QuotaManager::CreateRunnable final : public BackgroundThreadObject , public Runnable { nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks; nsString mBaseDirPath; RefPtr<QuotaManager> mManager; nsresult mResultCode; @@ -2849,16 +2867,40 @@ ShutdownObserver::Observe(nsISupports* a return NS_OK; } /******************************************************************************* * Quota object ******************************************************************************/ +NS_IMETHODIMP +QuotaObject:: +StoragePressureRunnable::Run() +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + if (NS_WARN_IF(!obsSvc)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsISupportsPRUint64> wrapper = + do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID); + if (NS_WARN_IF(!wrapper)) { + return NS_ERROR_FAILURE; + } + + wrapper->SetData(mUsage); + + obsSvc->NotifyObservers(wrapper, "QuotaManager::StoragePressure", u""); + + return NS_OK; +} + void QuotaObject::AddRef() { QuotaManager* quotaManager = QuotaManager::Get(); if (!quotaManager) { NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); ++mRefCnt; @@ -2989,18 +3031,24 @@ QuotaObject::MaybeUpdateSize(int64_t aSi // This will block the thread without holding the lock while waitting. AutoTArray<RefPtr<DirectoryLockImpl>, 10> locks; uint64_t sizeToBeFreed = quotaManager->LockedCollectOriginsForEviction(delta, locks); if (!sizeToBeFreed) { - // XXX prompt for asking to delete persistent origins if there is any - // persistent origin. + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); + + // Notify pressure event. + RefPtr<StoragePressureRunnable> storagePressureRunnable = + new StoragePressureRunnable(quotaManager->mTemporaryStorageUsage); + + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(storagePressureRunnable)); + return false; } NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); { MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
--- a/dom/quota/QuotaObject.h +++ b/dom/quota/QuotaObject.h @@ -18,16 +18,18 @@ BEGIN_QUOTA_NAMESPACE class OriginInfo; class QuotaManager; class QuotaObject { friend class OriginInfo; friend class QuotaManager; + class StoragePressureRunnable; + public: void AddRef(); void Release(); const nsAString&
--- a/dom/vr/test/mochitest.ini +++ b/dom/vr/test/mochitest.ini @@ -1,15 +1,17 @@ [DEFAULT] support-files = VRSimulationDriver.js requestPresent.js runVRTest.js WebVRHelpers.js +[test_vrDisplay_canvas2d.html] +skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 [test_vrDisplay_exitPresent.html] skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 [test_vrDisplay_getFrameData.html] skip-if = (os != "win" && release_or_beta) # Enable Linux after Bug 1310655 [test_vrDisplay_onvrdisplaydeactivate_crosscontent.html] skip-if = true [test_vrDisplay_requestPresent.html] skip-if = true
new file mode 100644 --- /dev/null +++ b/dom/vr/test/test_vrDisplay_canvas2d.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> + <head> + <title>VRDisplay Canvas2D</title> + <meta name="timeout" content="long"/> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="runVRTest.js"></script> + </head> + <body> + <script> + var vrDisplay; + + function requestPresentTest() { + async_test(function (test) { + vrDisplay.requestAnimationFrame(callback); + + function callback() { + vrDisplay.resetPose(); + vrDisplay.getLayers(); + vrDisplay.submitFrame(); + vrDisplay.getEyeParameters("right"); + test.done(); + } + }, "Finish WebVR Canvas2D requestPresentTest."); + } + + function startTest() { + promise_test((test) => { + var canvas = document.createElement('canvas'); + (document.body || document.documentElement).appendChild(canvas); + var context = canvas.getContext('2d'); + var img = document.createElement('img'); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACw="; + + return navigator.getVRDisplays().then((displays) => { + assert_equals(displays.length, 1, "displays.length must be one after attach."); + vrDisplay = displays[0]; + var frameData = new VRFrameData(); + return vrDisplay.requestPresent([{source: canvas}]).then(() => { + requestPresentTest(); + }); + }); + }, "Finish running WebVR Canvas2D test."); + } + + runVRTest(startTest); + </script> + </body> +</html> \ No newline at end of file
--- a/dom/vr/test/test_vrDisplay_exitPresent.html +++ b/dom/vr/test/test_vrDisplay_exitPresent.html @@ -13,17 +13,16 @@ function testExitPresentOnOtherIframe(content) { return content.navigator.getVRDisplays().then((displays) => { content.vrDisplay = displays[0]; return content.vrDisplay.exitPresent(); }); } var initVRPresentation = function(content) { return content.navigator.getVRDisplays().then((displays) => { - console.log("GetVRDisplay!!"); content.vrDisplay = displays[0]; content.canvas = content.document.createElement("canvas"); content.canvas.id = "vrCanvas"; return content.vrDisplay.requestPresent([{source:content.canvas}]); }); } function startTest() { var ifr1 = document.getElementById("iframe1");
--- a/dom/vr/test/test_vrDisplay_getFrameData.html +++ b/dom/vr/test/test_vrDisplay_getFrameData.html @@ -107,17 +107,17 @@ assert_true(checkValueInFloat32Array(pose1.angularVelocity, pose2.angularVelocity), "pose.angularVelocity at a frame should be equal."); assert_true(checkValueInFloat32Array(pose1.angularAcceleration, pose2.angularAcceleration), "pose.angularAcceleration at a frame should be equal."); test.done(); - }; + } }); }, "WebVR returns the same frameData within a frame fulfilled"); } function insertNewFrameData() { var poseOrient = new Float32Array([-0.208, -0.017, 0.055, -0.930]); var posePos = new Float32Array([-0.261, 0.036, -0.150]); var poseAngVel = new Float32Array([0.018, -0.001, -0.003]);
--- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -249,18 +249,17 @@ CrashStatsLogForwarder::UpdateStringsVec mIndex += 1; MOZ_ASSERT(mIndex >= 0); // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ... int32_t index = mIndex ? (mIndex-1) % (mMaxCapacity-1) + 1 : 0; MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity); MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size()); - bool ignored; - double tStamp = (TimeStamp::NowLoRes()-TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits(); + double tStamp = (TimeStamp::NowLoRes() - TimeStamp::ProcessCreation()).ToSecondsSigDigits(); // Checking for index >= mBuffer.size(), rather than index == mBuffer.size() // just out of paranoia, but we know index <= mBuffer.size(). LoggingRecordEntry newEntry(mIndex,aString,tStamp); if (index >= static_cast<int32_t>(mBuffer.size())) { mBuffer.push_back(newEntry); } else { mBuffer[index] = newEntry;
--- a/image/imgLoader.h +++ b/image/imgLoader.h @@ -43,28 +43,26 @@ class imgCacheEntry public: imgCacheEntry(imgLoader* loader, imgRequest* request, bool aForcePrincipalCheck); ~imgCacheEntry(); nsrefcnt AddRef() { NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt"); - MOZ_ASSERT(_mOwningThread.GetThread() == PR_GetCurrentThread(), - "imgCacheEntry addref isn't thread-safe!"); + NS_ASSERT_OWNINGTHREAD(imgCacheEntry); ++mRefCnt; NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this)); return mRefCnt; } nsrefcnt Release() { NS_PRECONDITION(0 != mRefCnt, "dup release"); - MOZ_ASSERT(_mOwningThread.GetThread() == PR_GetCurrentThread(), - "imgCacheEntry release isn't thread-safe!"); + NS_ASSERT_OWNINGTHREAD(imgCacheEntry); --mRefCnt; NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry"); if (mRefCnt == 0) { mRefCnt = 1; /* stabilize */ delete this; return 0; } return mRefCnt;
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc @@ -19,17 +19,17 @@ #include "mozilla/ipc/ProtocolUtils.h" // ChannelImpl is used on the IPC thread, but constructed on a different thread, // so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly // different macro. #ifdef DEBUG #define ASSERT_OWNINGTHREAD(_class) \ if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \ - NS_CheckThreadSafe(owningThread->GetThread(), #_class " not thread-safe"); \ + owningThread->AssertOwnership(#_class " not thread-safe"); \ } #else #define ASSERT_OWNINGTHREAD(_class) ((void)0) #endif namespace IPC { //------------------------------------------------------------------------------
--- a/ipc/mscom/InterceptorLog.cpp +++ b/ipc/mscom/InterceptorLog.cpp @@ -253,18 +253,17 @@ Logger::VariantToString(const VARIANT& a } } } /* static */ double Logger::GetElapsedTime() { TimeStamp ts = TimeStamp::Now(); - bool inconsistent; - TimeDuration duration = ts - TimeStamp::ProcessCreation(inconsistent); + TimeDuration duration = ts - TimeStamp::ProcessCreation(); return duration.ToMicroseconds(); } void Logger::LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface) { if (FAILED(aResult)) { return;
--- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -496,16 +496,40 @@ JavaScriptShared::ConvertID(const JSIID& to->m3[3] = from.m3_3(); to->m3[4] = from.m3_4(); to->m3[5] = from.m3_5(); to->m3[6] = from.m3_6(); to->m3[7] = from.m3_7(); } JSObject* +JavaScriptShared::findCPOWById(const ObjectId& objId) +{ + JSObject* obj = findCPOWByIdPreserveColor(objId); + if (obj) + JS::ExposeObjectToActiveJS(obj); + return obj; +} + +JSObject* +JavaScriptShared::findCPOWByIdPreserveColor(const ObjectId& objId) +{ + JSObject* obj = cpows_.findPreserveColor(objId); + if (!obj) + return nullptr; + + if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) { + cpows_.remove(objId); + return nullptr; + } + + return obj; +} + +JSObject* JavaScriptShared::findObjectById(JSContext* cx, const ObjectId& objId) { RootedObject obj(cx, objects_.find(objId)); if (!obj) { JS_ReportErrorASCII(cx, "operation not possible on dead CPOW"); return nullptr; }
--- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -168,24 +168,24 @@ class JavaScriptShared : public CPOWMana bool convertGeckoStringToId(JSContext* cx, const nsString& from, JS::MutableHandleId id); virtual bool toObjectVariant(JSContext* cx, JSObject* obj, ObjectVariant* objVarp) = 0; virtual JSObject* fromObjectVariant(JSContext* cx, const ObjectVariant& objVar) = 0; static void ConvertID(const nsID& from, JSIID* to); static void ConvertID(const JSIID& from, nsID* to); - JSObject* findCPOWById(const ObjectId& objId) { - return cpows_.find(objId); - } + JSObject* findCPOWById(const ObjectId& objId); + JSObject* findCPOWByIdPreserveColor(const ObjectId& objId); JSObject* findObjectById(JSContext* cx, const ObjectId& objId); #ifdef DEBUG bool hasCPOW(const ObjectId& objId, const JSObject* obj) { - return cpows_.has(objId, obj); + MOZ_ASSERT(obj); + return findCPOWByIdPreserveColor(objId) == obj; } #endif static bool LoggingEnabled() { return sLoggingEnabled; } static bool StackLoggingEnabled() { return sStackLoggingEnabled; } friend class Logging;
--- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -914,19 +914,22 @@ CPOWProxyHandler::isConstructor(JSObject { AuxCPOWData* aux = AuxCPOWDataOf(proxy); return aux->isConstructor; } void WrapperOwner::drop(JSObject* obj) { - ObjectId objId = idOf(obj); + // The association may have already been swept from the table but if it's + // there then remove it. + ObjectId objId = idOfUnchecked(obj); + if (cpows_.findPreserveColor(objId) == obj) + cpows_.remove(objId); - cpows_.remove(objId); if (active()) Unused << SendDropObject(objId); decref(); } void WrapperOwner::updatePointer(JSObject* obj, const JSObject* old) {
--- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -651,37 +651,60 @@ ExposeGCThingToActiveJS(JS::GCCellPtr th if (IsIncrementalBarrierNeededOnTenuredGCThing(thing)) JS::IncrementalReadBarrier(thing); else if (js::gc::detail::TenuredCellIsMarkedGray(thing.asCell())) JS::UnmarkGrayGCThingRecursively(thing); MOZ_ASSERT(!js::gc::detail::TenuredCellIsMarkedGray(thing.asCell())); } +template <typename T> +extern JS_PUBLIC_API(bool) +EdgeNeedsSweepUnbarrieredSlow(T* thingp); + +static MOZ_ALWAYS_INLINE bool +EdgeNeedsSweepUnbarriered(JSObject** objp) +{ + // This function does not handle updating nursery pointers. Raw JSObject + // pointers should be updated separately or replaced with + // JS::Heap<JSObject*> which handles this automatically. + MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting()); + if (IsInsideNursery(reinterpret_cast<Cell*>(*objp))) + return false; + + auto zone = JS::shadow::Zone::asShadowZone(detail::GetGCThingZone(uintptr_t(*objp))); + if (!zone->isGCSweepingOrCompacting()) + return false; + + return EdgeNeedsSweepUnbarrieredSlow(objp); +} + } /* namespace gc */ } /* namespace js */ namespace JS { /* * This should be called when an object that is marked gray is exposed to the JS * engine (by handing it to running JS code or writing it into live JS * data). During incremental GC, since the gray bits haven't been computed yet, * we conservatively mark the object black. */ static MOZ_ALWAYS_INLINE void ExposeObjectToActiveJS(JSObject* obj) { MOZ_ASSERT(obj); + MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj)); js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj)); } static MOZ_ALWAYS_INLINE void ExposeScriptToActiveJS(JSScript* script) { + MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script)); js::gc::ExposeGCThingToActiveJS(GCCellPtr(script)); } /* * Internal to Firefox. * * Note: this is not related to the PokeGC in nsJSEnvironment. */
--- a/js/public/HeapAPI.h +++ b/js/public/HeapAPI.h @@ -96,29 +96,39 @@ const uint32_t DefaultNurseryBytes = 16 /* Default maximum heap size in bytes to pass to JS_NewRuntime(). */ const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024; namespace shadow { struct Zone { + enum GCState : uint8_t { + NoGC, + Mark, + MarkGray, + Sweep, + Finished, + Compact + }; + protected: JSRuntime* const runtime_; JSTracer* const barrierTracer_; // A pointer to the JSRuntime's |gcMarker|. - - public: bool needsIncrementalBarrier_; + GCState gcState_; Zone(JSRuntime* runtime, JSTracer* barrierTracerArg) : runtime_(runtime), barrierTracer_(barrierTracerArg), - needsIncrementalBarrier_(false) + needsIncrementalBarrier_(false), + gcState_(NoGC) {} + public: bool needsIncrementalBarrier() const { return needsIncrementalBarrier_; } JSTracer* barrierTracer() { MOZ_ASSERT(needsIncrementalBarrier_); MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_)); return barrierTracer_; @@ -130,16 +140,25 @@ struct Zone } // Note: Unrestricted access to the zone's runtime from an arbitrary // thread can easily lead to races. Use this method very carefully. JSRuntime* runtimeFromAnyThread() const { return runtime_; } + GCState gcState() const { return gcState_; } + bool wasGCStarted() const { return gcState_ != NoGC; } + bool isGCMarkingBlack() { return gcState_ == Mark; } + bool isGCMarkingGray() { return gcState_ == MarkGray; } + bool isGCSweeping() { return gcState_ == Sweep; } + bool isGCFinished() { return gcState_ == Finished; } + bool isGCCompacting() { return gcState_ == Compact; } + bool isGCSweepingOrCompacting() { return gcState_ == Sweep || gcState_ == Compact; } + static MOZ_ALWAYS_INLINE JS::shadow::Zone* asShadowZone(JS::Zone* zone) { return reinterpret_cast<JS::shadow::Zone*>(zone); } }; } /* namespace shadow */ /**
--- a/js/public/ProfilingStack.h +++ b/js/public/ProfilingStack.h @@ -203,14 +203,11 @@ SetContextProfilingStack(JSContext* cx, uint32_t max); JS_FRIEND_API(void) EnableContextProfilingStack(JSContext* cx, bool enabled); JS_FRIEND_API(void) RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*)); -JS_FRIEND_API(jsbytecode*) -ProfilingGetPC(JSContext* cx, JSScript* script, void* ip); - } // namespace js #endif /* js_ProfilingStack_h */
--- a/js/public/Value.h +++ b/js/public/Value.h @@ -955,16 +955,20 @@ IsOptimizedPlaceholderMagicValue(const V return true; } return false; } static MOZ_ALWAYS_INLINE void ExposeValueToActiveJS(const Value& v) { +#ifdef DEBUG + Value tmp = v; + MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&tmp)); +#endif if (v.isGCThing()) js::gc::ExposeGCThingToActiveJS(GCCellPtr(v)); } /************************************************************************/ static inline MOZ_MAY_CALL_AFTER_MUST_RETURN Value NullValue()
--- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -25,18 +25,17 @@ #include "vm/NativeObject-inl.h" using namespace js; static double MillisecondsSinceStartup() { auto now = mozilla::TimeStamp::Now(); - bool ignored; - return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds(); + return (now - mozilla::TimeStamp::ProcessCreation()).ToMilliseconds(); } enum PromiseHandler { PromiseHandlerIdentity = 0, PromiseHandlerThrower, PromiseHandlerAsyncFunctionAwaitFulfilled, PromiseHandlerAsyncFunctionAwaitRejected, PromiseHandlerAsyncGeneratorAwaitFulfilled,
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -4313,19 +4313,18 @@ AflLoop(JSContext* cx, unsigned argc, Va return true; } #endif static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - bool ignore; - double when = (mozilla::TimeStamp::Now() - - mozilla::TimeStamp::ProcessCreation(ignore)).ToMilliseconds(); + double when = (mozilla::TimeStamp::Now() - + mozilla::TimeStamp::ProcessCreation()).ToMilliseconds(); args.rval().setNumber(when); return true; } static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/devtools/rootAnalysis/README.md +++ b/js/src/devtools/rootAnalysis/README.md @@ -1,14 +1,67 @@ # Spidermonkey JSAPI rooting analysis This directory contains scripts for running Brian Hackett's static GC rooting -analysis on a JS source directory. +and thread heap write safety analyses on a JS source directory. + +The easiest way to get this running is to not try to do the instrumented +compilation locally. Instead, grab the relevant files from a try server push +and analyze them locally. + +Local Analysis of Downloaded Intermediate Files + +1. Do a try push with "--upload-xdbs" appended to the try: ..." line. + +2. Create an empty directory to run the analysis. + +3. When the try job is complete, download the resulting src_body.xdb.bz2, src_comp.xdb.bz2, +and file_source.xdb.bz2 files into your directory. + +4. Build an optimized JS shell with ctypes. Note that this does not need to +match the source you are analyzing in any way; in fact, you pretty much never +need to update this once you've built it. (Though I reserve the right to use +any new JS features implemented in Spidermonkey in the future...) + + mkdir <objdir> + cd <objdir> + <srcpath>/js/src/configure --disable-debug --enable-optimize --enable-ctypes + make -j6 -s + +5. Clone and build sixgill: -To use it on SpiderMonkey: + hg clone https://hg.mozilla.org/users/sfink_mozilla.com/sixgill + cd sixgill + ./release.sh --build + +If you are on osx, the sixgill build will fail horribly. Let it, then do + + make bin/xdb.so CXX=clang++ + +6. Make a defaults.py file containing the following, with your own paths filled in: + + js = "<objdir>/dist/bin/js" + sixgill_bin = "<sixgill-dir>/bin" + +7a. For the rooting analysis, run + + python <srcdir>/js/src/devtools/rootAnalysis/analyze.py gcTypes + +7b. For the heap write analysis, run + + python <srcdir>/js/src/devtools/rootAnalysis/analyze.py heapwrites + +Also, you may wish to run with -v (aka --verbose) to see the exact commands +executed that you can cut & paste if needed. (I use them to run under the JS +debugger when I'm working on the analysis.) + +---- + +Or if you *do* want to run the full analysis locally, then you may face the +dragons. To use it on SpiderMonkey: 1. Be on Fedora/CentOS/RedHat Linux x86_64, or a Docker image of one of those. Specifically, the prebuilt GCC **won't work on Ubuntu** without the `CFLAGS` and `CXXFLAGS` settings from <http://trac.wildfiregames.com/wiki/StaticRootingAnalysis>. 2. Have the Gecko build prerequisites installed.
--- a/js/src/devtools/rootAnalysis/analyze.py +++ b/js/src/devtools/rootAnalysis/analyze.py @@ -11,16 +11,26 @@ Runs the static rooting analysis from subprocess import Popen import subprocess import os import argparse import sys import re +# Python 2/3 version independence polyfills + +anystring_t = str if sys.version_info[0] > 2 else basestring + +try: + execfile +except: + def execfile(thefile, globals): + exec(compile(open(thefile).read(), filename=thefile, mode="exec"), globals) + def env(config): e = dict(os.environ) e['PATH'] = ':'.join(p for p in (config.get('gcc_bin'), config.get('sixgill_bin'), e['PATH']) if p) e['XDB'] = '%(sixgill_bin)s/xdb.so' % config e['SOURCE'] = config['source'] e['ANALYZED_OBJDIR'] = config['objdir'] return e @@ -56,17 +66,17 @@ def print_command(command, outfile=None, outputs.append('%s="%s${%s}%s"' % (key, value[:start], key, value[end:])) else: outputs.append("%s='%s'" % (key, value)) output = ' '.join(outputs) + " " + output - print output + print(output) def generate_hazards(config, outfilename): jobs = [] for i in range(int(config['jobs'])): command = fill(('%(js)s', '%(analysis_scriptdir)s/analyzeRoots.js', '%(gcFunctions_list)s', '%(gcEdges)s', @@ -93,17 +103,17 @@ def generate_hazards(config, outfilename with open(outfilename, 'w') as output: command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(int(config['jobs'])) ] if config['verbose']: print_command(command, outfile=outfilename) subprocess.call(command, stdout=output) JOBS = { 'dbs': - (('%(ANALYSIS_SCRIPTDIR)s/run_complete', + (('%(analysis_scriptdir)s/run_complete', '--foreground', '--no-logs', '--build-root=%(objdir)s', '--wrap-dir=%(sixgill)s/scripts/wrap_gcc', '--work-dir=work', '-b', '%(sixgill_bin)s', '--buildcommand=%(buildcommand)s', '.'), @@ -155,17 +165,17 @@ def out_indexes(command): def run_job(name, config): cmdspec, outfiles = JOBS[name] print("Running " + name + " to generate " + str(outfiles)) if hasattr(cmdspec, '__call__'): cmdspec(config, outfiles) else: temp_map = {} cmdspec = fill(cmdspec, config) - if isinstance(outfiles, basestring): + if isinstance(outfiles, anystring_t): stdout_filename = '%s.tmp' % name temp_map[stdout_filename] = outfiles if config['verbose']: print_command(cmdspec, outfile=outfiles, env=env(config)) else: stdout_filename = None pc = list(cmdspec) outfile = 0 @@ -190,19 +200,19 @@ def run_job(name, config): subprocess.check_call(command, stdout=output, env=env(config)) for (temp, final) in temp_map.items(): try: os.rename(temp, final) except OSError: print("Error renaming %s -> %s" % (temp, final)) raise -config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) } +config = { 'analysis_scriptdir': os.path.dirname(__file__) } -defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'], +defaults = [ '%s/defaults.py' % config['analysis_scriptdir'], '%s/defaults.py' % os.getcwd() ] parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.') parser.add_argument('step', metavar='STEP', type=str, nargs='?', help='run starting from this step') parser.add_argument('--source', metavar='SOURCE', type=str, nargs='?', help='source code to analyze') parser.add_argument('--objdir', metavar='DIR', type=str, nargs='?', @@ -255,19 +265,24 @@ elif 'BUILD' in os.environ: else: data['buildcommand'] = 'make -j4 -s' if 'ANALYZED_OBJDIR' in os.environ: data['objdir'] = os.environ['ANALYZED_OBJDIR'] if 'SOURCE' in os.environ: data['source'] = os.environ['SOURCE'] -if not data.get('source') and data.get('sixgill_bin'): - path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp']) - data['source'] = path.replace("\n", "").replace("/js/src/jsapi.cpp", "") + +if data.get('sixgill_bin'): + if not data.get('source'): + path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp']).decode() + data['source'] = path.replace("\n", "").replace("/js/src/jsapi.cpp", "") + if not data.get('objdir'): + path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.h']).decode() + data['objdir'] = path.replace("\n", "").replace("/jsapi.h", "") steps = [ 'dbs', 'gcTypes', 'callgraph', 'gcFunctions', 'allFunctions', 'hazards', 'explain', @@ -279,17 +294,17 @@ if args.list: if outfilename: print("%s -> %s" % (step, outfilename)) else: print(step) sys.exit(0) for step in steps: command, outfiles = JOBS[step] - if isinstance(outfiles, basestring): + if isinstance(outfiles, anystring_t): data[step] = outfiles else: outfile = 0 for (i, name) in out_indexes(command): data[name] = outfiles[outfile] outfile += 1 assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files (%d) and params (%d)' % (step, outfile, len(outfiles))
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js +++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js @@ -1070,41 +1070,54 @@ function isSafeVariable(entry, variable) // Hopefully the construction code doesn't leak pointers to the object // to places where other threads might access it. if (isDirectCall(edge, /operator new/) || isDirectCall(edge, /nsCSSValue::Array::Create/)) { return true; } - // References to the contents of an array are threadsafe if the array - // itself is threadsafe. - if ((isDirectCall(edge, /operator\[\]/) || - isDirectCall(edge, /nsStyleContent::ContentAt/)) && - isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp)) - { - return true; - } - - // Watch for the coerced result of a getter_AddRefs call. - if (isDirectCall(edge, /operator /)) { - var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp); - if (otherEdge && - isDirectCall(otherEdge, /getter_AddRefs/) && - isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0])) + if ("PEdgeCallInstance" in edge) { + // References to the contents of an array are threadsafe if the array + // itself is threadsafe. + if ((isDirectCall(edge, /operator\[\]/) || + isDirectCall(edge, /nsStyleContent::ContentAt/)) && + isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp)) { return true; } - } + + // Watch for the coerced result of a getter_AddRefs call. + if (isDirectCall(edge, /operator /)) { + var otherEdge = expressionValueEdge(edge.PEdgeCallInstance.Exp); + if (otherEdge && + isDirectCall(otherEdge, /getter_AddRefs/) && + isEdgeSafeArgument(entry, otherEdge.PEdgeCallArguments.Exp[0])) + { + return true; + } + } - // Coercion via AsAString preserves safety. - if (isDirectCall(edge, /AsAString/) && - isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp)) - { - return true; + // Placement-new returns a pointer that is as safe as the pointer + // passed to it. Exp[0] is the size, Exp[1] is the pointer/address. + // Note that the invocation of the constructor is a separate call, + // and so need not be considered here. + if (isDirectCall(edge, /operator new/) && + edge.PEdgeCallInstance.Exp.length == 2 && + isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp[1])) + { + return true; + } + + // Coercion via AsAString preserves safety. + if (isDirectCall(edge, /AsAString/) && + isEdgeSafeArgument(entry, edge.PEdgeCallInstance.Exp)) + { + return true; + } } // Watch out for variables which were assigned arguments. var rhsVariable = variableAssignRhs(edge); if (rhsVariable) return isSafeVariable(entry, rhsVariable); }
--- a/js/src/devtools/rootAnalysis/utility.js +++ b/js/src/devtools/rootAnalysis/utility.js @@ -221,21 +221,23 @@ function xdbLibrary() } catch (e) { // lookup_key is for development use only and is not strictly necessary. } return api; } function cLibrary() { + var libPossibilities = ['libc.so.6', 'libc.so', 'libc.dylib']; var lib; - try { - lib = ctypes.open("libc.so.6"); - } catch(e) { - lib = ctypes.open("libc.so"); + for (const name of libPossibilities) { + try { + lib = ctypes.open("libc.so.6"); + } catch(e) { + } } return { fopen: lib.declare("fopen", ctypes.default_abi, ctypes.void_t.ptr, ctypes.char.ptr, ctypes.char.ptr), getline: lib.declare("getline", ctypes.default_abi, ctypes.ssize_t, ctypes.char.ptr.ptr, ctypes.size_t.ptr, ctypes.void_t.ptr), fclose: lib.declare("fclose", ctypes.default_abi, ctypes.int, ctypes.void_t.ptr), free: lib.declare("free", ctypes.default_abi, ctypes.void_t, ctypes.void_t.ptr), };
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -458,17 +458,17 @@ UsedNameTracker::rewind(RewindToken toke r.front().value().resetToScope(token.scriptId, token.scopeId); } FunctionBox::FunctionBox(JSContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun, uint32_t toStringStart, Directives directives, bool extraWarnings, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) : ObjectBox(fun, traceListHead), - SharedContext(cx, Kind::ObjectBox, directives, extraWarnings), + SharedContext(cx, Kind::FunctionBox, directives, extraWarnings), enclosingScope_(nullptr), namedLambdaBindings_(nullptr), functionScopeBindings_(nullptr), extraVarScopeBindings_(nullptr), functionNode(nullptr), bufStart(0), bufEnd(0), startLine(1),
--- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -226,17 +226,17 @@ class SharedContext JSContext* const context; AnyContextFlags anyCxFlags; bool strictScript; bool localStrict; bool extraWarnings; protected: enum class Kind { - ObjectBox, + FunctionBox, Global, Eval, Module }; Kind kind_; ThisBinding thisBinding_; @@ -266,25 +266,23 @@ class SharedContext inWith_(false), needsThisTDZChecks_(false) { } // If this is the outermost SharedContext, the Scope that encloses // it. Otherwise nullptr. virtual Scope* compilationEnclosingScope() const = 0; - virtual ObjectBox* toObjectBox() { return nullptr; } - bool isObjectBox() { return toObjectBox(); } - bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); } + bool isFunctionBox() const { return kind_ == Kind::FunctionBox; } inline FunctionBox* asFunctionBox(); - bool isModuleContext() { return kind_ == Kind::Module; } + bool isModuleContext() const { return kind_ == Kind::Module; } inline ModuleSharedContext* asModuleContext(); - bool isGlobalContext() { return kind_ == Kind::Global; } + bool isGlobalContext() const { return kind_ == Kind::Global; } inline GlobalSharedContext* asGlobalContext(); - bool isEvalContext() { return kind_ == Kind::Eval; } + bool isEvalContext() const { return kind_ == Kind::Eval; } inline EvalSharedContext* asEvalContext(); ThisBinding thisBinding() const { return thisBinding_; } bool allowNewTarget() const { return allowNewTarget_; } bool allowSuperProperty() const { return allowSuperProperty_; } bool allowSuperCall() const { return allowSuperCall_; } bool inWith() const { return inWith_; } @@ -449,17 +447,16 @@ class FunctionBox : public ObjectBox, pu MOZ_ASSERT(context->keepAtoms); return MutableHandle<VarScope::Data*>::fromMarkedLocation(&extraVarScopeBindings_); } void initFromLazyFunction(); void initStandaloneFunction(Scope* enclosingScope); void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind); - ObjectBox* toObjectBox() override { return this; } JSFunction* function() const { return &object->as<JSFunction>(); } Scope* compilationEnclosingScope() const override { // This method is used to distinguish the outermost SharedContext. If // a FunctionBox is the outermost SharedContext, it must be a lazy // function. MOZ_ASSERT_IF(function()->isInterpretedLazy(), enclosingScope_ == function()->lazyScript()->enclosingScope());
--- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -976,18 +976,18 @@ class GCRuntime template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase); void markWeakReferencesInCurrentGroup(gcstats::Phase phase); template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase); void markBufferedGrayRoots(JS::Zone* zone); void markGrayReferencesInCurrentGroup(gcstats::Phase phase); void markAllWeakReferences(gcstats::Phase phase); void markAllGrayReferences(gcstats::Phase phase); - void beginSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock); - void groupZonesForSweeping(AutoLockForExclusiveAccess& lock); + void beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock); + void groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock); MOZ_MUST_USE bool findInterZoneEdges(); void getNextSweepGroup(); void endMarkingSweepGroup(); void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock); bool shouldReleaseObservedTypes(); void endSweepingSweepGroup(); IncrementalProgress performSweepActions(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock); @@ -1438,17 +1438,18 @@ GCRuntime::upcomingZealousGC() { inline bool GCRuntime::needZealousGC() { if (nextScheduled > 0 && --nextScheduled == 0) { if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::GenerationalGC) || hasZealMode(ZealMode::IncrementalRootsThenFinish) || hasZealMode(ZealMode::IncrementalMarkAllThenFinish) || hasZealMode(ZealMode::IncrementalMultipleSlices) || - hasZealMode(ZealMode::Compact)) + hasZealMode(ZealMode::Compact) || + hasZealMode(ZealMode::IncrementalSweepThenFinish)) { nextScheduled = zealFrequency; } return true; } return false; } #else
--- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -3148,18 +3148,18 @@ IsAboutToBeFinalizedInternal(T** thingp) T* thing = *thingp; JSRuntime* rt = thing->runtimeFromAnyThread(); /* Permanent atoms are never finalized by non-owning runtimes. */ if (ThingIsPermanentAtomOrWellKnownSymbol(thing) && TlsContext.get()->runtime() != rt) return false; if (IsInsideNursery(thing)) { - MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting()); - return !Nursery::getForwardedPointer(reinterpret_cast<JSObject**>(thingp)); + return JS::CurrentThreadIsHeapMinorCollecting() && + !Nursery::getForwardedPointer(reinterpret_cast<JSObject**>(thingp)); } Zone* zone = thing->asTenured().zoneFromAnyThread(); if (zone->isGCSweeping()) { return IsAboutToBeFinalizedDuringSweep(thing->asTenured()); } else if (zone->isGCCompacting() && IsForwarded(thing)) { *thingp = Forwarded(thing); return false; @@ -3225,25 +3225,33 @@ IsAboutToBeFinalized(ReadBarrieredBase<T template <typename T> JS_PUBLIC_API(bool) EdgeNeedsSweep(JS::Heap<T>* thingp) { return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeGet())); } +template <typename T> +JS_PUBLIC_API(bool) +EdgeNeedsSweepUnbarrieredSlow(T* thingp) +{ + return IsAboutToBeFinalizedInternal(ConvertToBase(thingp)); +} + // Instantiate a copy of the Tracing templates for each derived type. #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \ template bool IsMarkedUnbarriered<type>(JSRuntime*, type*); \ template bool IsMarked<type>(JSRuntime*, WriteBarrieredBase<type>*); \ template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \ template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \ template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*); #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \ - template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*); + template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*); \ + template JS_PUBLIC_API(bool) EdgeNeedsSweepUnbarrieredSlow<type>(type*); FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS) FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS) #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS } /* namespace gc */ } /* namespace js */
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -273,18 +273,17 @@ js::gc::GCRuntime::traceRuntimeForMinorG // despite having called FinishRoots already. This is because FinishRoots // does not clear the crossCompartmentWrapper map. It cannot do this // because Proxy's trace for CrossCompartmentWrappers asserts presence in // the map. And we can reach its trace function despite having finished the // roots via the edges stored by the pre-barrier verifier when we finish // the verifier for the last time. gcstats::AutoPhase ap(stats(), gcstats::PHASE_MARK_ROOTS); - // FIXME: As per bug 1298816 comment 12, we should be able to remove this. - jit::JitRuntime::TraceJitcodeGlobalTable(trc); + jit::JitRuntime::TraceJitcodeGlobalTableForMinorGC(trc); traceRuntimeCommon(trc, TraceRuntime, lock); } void js::TraceRuntime(JSTracer* trc) { MOZ_ASSERT(!trc->isMarkingTracer());
--- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -742,18 +742,17 @@ Statistics::formatJsonSliceDescription(u { TimeDuration duration = slice.duration(); lldiv_t durationParts = SplitDurationMS(duration); TimeDuration when = slice.start - slices[0].start; lldiv_t whenParts = SplitDurationMS(when); char budgetDescription[200]; slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1); int64_t pageFaults = slice.endFaults - slice.startFaults; - bool ignore; - TimeStamp originTime = TimeStamp::ProcessCreation(ignore); + TimeStamp originTime = TimeStamp::ProcessCreation(); const char* format = "\"slice\":%d," "\"pause\":%llu.%03llu," "\"when\":%llu.%03llu," "\"reason\":\"%s\"," "\"initial_state\":\"%s\"," "\"final_state\":\"%s\"," @@ -1024,19 +1023,18 @@ LongestPhaseSelfTime(const Statistics::P void Statistics::printStats() { if (aborted) { fprintf(fp, "OOM during GC statistics collection. The report is unavailable for this GC.\n"); } else { UniqueChars msg = formatDetailedMessage(); if (msg) { - bool ignoredInconsistency; double secSinceStart = - (slices[0].start - TimeStamp::ProcessCreation(ignoredInconsistency)).ToSeconds(); + (slices[0].start - TimeStamp::ProcessCreation()).ToSeconds(); fprintf(fp, "GC(T+%.3fs) %s\n", secSinceStart, msg.get()); } } fflush(fp); } void Statistics::beginGC(JSGCInvocationKind kind)
--- a/js/src/gc/Verifier.cpp +++ b/js/src/gc/Verifier.cpp @@ -652,17 +652,18 @@ CheckGrayMarkingTracer::checkCell(Cell* return; TenuredCell* tenuredCell = &cell->asTenured(); TenuredCell* tenuredParent = &parent->asTenured(); if (tenuredParent->isMarked(BLACK) && !tenuredParent->isMarked(GRAY) && tenuredCell->isMarked(GRAY)) { failures++; - fprintf(stderr, "Found black to gray edge %p\n", cell); + fprintf(stderr, "Found black to gray edge to %s %p\n", + GCTraceKindToAscii(cell->getTraceKind()), cell); dumpCellPath(); } } bool CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock) { if (!traceHeap(lock))
--- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -50,17 +50,16 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* baseShapes_(group, this, BaseShapeSet()), initialShapes_(group, this, InitialShapeSet()), data(group, nullptr), isSystem(group, false), #ifdef DEBUG gcLastSweepGroupIndex(group, 0), #endif jitZone_(group, nullptr), - gcState_(NoGC), gcScheduled_(false), gcPreserveCode_(group, false), jitUsingBarriers_(group, false), keepShapeTables_(group, false), listNext_(group, NotOnList) { /* Ensure that there are no vtables to mess us up here. */ MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
--- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -210,24 +210,16 @@ struct Zone : public JS::shadow::Zone, void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; } bool isPreservingCode() const { return gcPreserveCode_; } bool canCollect(); void notifyObservingDebuggers(); - enum GCState { - NoGC, - Mark, - MarkGray, - Sweep, - Finished, - Compact - }; void setGCState(GCState state) { MOZ_ASSERT(CurrentThreadIsHeapBusy()); MOZ_ASSERT_IF(state != NoGC, canCollect()); gcState_ = state; if (state == Finished) notifyObservingDebuggers(); } @@ -252,25 +244,16 @@ struct Zone : public JS::shadow::Zone, bool isGCMarking() { if (CurrentThreadIsHeapCollecting()) return gcState_ == Mark || gcState_ == MarkGray; else return needsIncrementalBarrier(); } - GCState gcState() const { return gcState_; } - bool wasGCStarted() const { return gcState_ != NoGC; } - bool isGCMarkingBlack() { return gcState_ == Mark; } - bool isGCMarkingGray() { return gcState_ == MarkGray; } - bool isGCSweeping() { return gcState_ == Sweep; } - bool isGCFinished() { return gcState_ == Finished; } - bool isGCCompacting() { return gcState_ == Compact; } - bool isGCSweepingOrCompacting() { return gcState_ == Sweep || gcState_ == Compact; } - // Get a number that is incremented whenever this zone is collected, and // possibly at other times too. uint64_t gcNumber(); bool compileBarriers() const { return compileBarriers(needsIncrementalBarrier()); } bool compileBarriers(bool needsIncrementalBarrier) const { return needsIncrementalBarrier || runtimeFromActiveCooperatingThread()->hasZealMode(js::gc::ZealMode::VerifierPre); @@ -616,17 +599,16 @@ struct Zone : public JS::shadow::Zone, } void setKeepShapeTables(bool b) { keepShapeTables_ = b; } private: js::ZoneGroupData<js::jit::JitZone*> jitZone_; - js::UnprotectedData<GCState> gcState_; js::ActiveThreadData<bool> gcScheduled_; js::ZoneGroupData<bool> gcPreserveCode_; js::ZoneGroupData<bool> jitUsingBarriers_; js::ZoneGroupData<bool> keepShapeTables_; // Allow zones to be linked into a list friend class js::gc::ZoneList; static Zone * const NotOnList;
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testBug1359612.js @@ -0,0 +1,9 @@ +load(libdir + 'asm.js'); + +asmLink(asmCompile('stdlib', 'foreign', USE_ASM + ` + var ff = foreign.ff; + function f() { + ff(+1); + } + return f +`), this, { ff: Math.log1p });
--- a/js/src/jit-test/tests/gc/incremental-state.js +++ b/js/src/jit-test/tests/gc/incremental-state.js @@ -22,42 +22,45 @@ gcslice(1); assertEq(gcstate(), "Mark"); gcslice(1000000); assertEq(gcstate(), "Mark"); gcslice(1000000); while (gcstate() == "Finalize") { gcslice(1); } while (gcstate() == "Decommit") { gcslice(1); } assertEq(gcstate(), "NotActive"); -// Zeal mode 8: Incremental GC in two main slices: +// Zeal mode 8: Incremental GC in two slices: // 1) mark roots // 2) mark and sweep -// *) finalize. gczeal(8, 0); gcslice(1); assertEq(gcstate(), "Mark"); gcslice(1); -while (gcstate() == "Finalize") { gcslice(1); } -while (gcstate() == "Decommit") { gcslice(1); } assertEq(gcstate(), "NotActive"); -// Zeal mode 9: Incremental GC in two main slices: +// Zeal mode 9: Incremental GC in two slices: // 1) mark roots and marking // 2) new marking and sweeping -// *) finalize. gczeal(9, 0); gcslice(1); assertEq(gcstate(), "Mark"); gcslice(1); -while (gcstate() == "Finalize") { gcslice(1); } -while (gcstate() == "Decommit") { gcslice(1); } assertEq(gcstate(), "NotActive"); // Zeal mode 10: Incremental GC in multiple slices (always yeilds before // sweeping). This test uses long slices to prove that this zeal mode yields // in sweeping, where normal IGC (above) does not. gczeal(10, 0); gcslice(1000000); assertEq(gcstate(), "Sweep"); gcslice(1000000); while (gcstate() == "Finalize") { gcslice(1); } while (gcstate() == "Decommit") { gcslice(1); } assertEq(gcstate(), "NotActive"); + +// Zeal mode 17: Incremental GC in two slices: +// 1) mark everything and start sweeping +// 2) finish sweeping +gczeal(17, 0); +gcslice(1); +assertEq(gcstate(), "Sweep"); +gcslice(1); +assertEq(gcstate(), "NotActive");
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/wasm/regress/builtin-import-sigs.js @@ -0,0 +1,15 @@ +var code = wasmTextToBinary(`(module + (import $one "" "builtin") + (import $two "" "builtin" (param i32)) + (import $three "" "builtin" (result i32)) + (import $four "" "builtin" (result f32) (param f32 f32 f32 f32 f32 f32 f32 f32 f32 f32 f32 f32)) + (func (export "run") + (call $one) + (call $two (i32.const 0)) + (drop (call $three)) + (drop (call $four (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0) (f32.const 0))) + ) +)`); +var m = new WebAssembly.Module(code); +var i = new WebAssembly.Instance(m, {'':{builtin:Math.sin}}); +i.exports.run();
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -609,23 +609,23 @@ JitRuntime::Trace(JSTracer* trc, AutoLoc Zone* zone = trc->runtime()->atomsCompartment(lock)->zone(); for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) { JitCode* code = i; TraceRoot(trc, &code, "wrapper"); } } /* static */ void -JitRuntime::TraceJitcodeGlobalTable(JSTracer* trc) +JitRuntime::TraceJitcodeGlobalTableForMinorGC(JSTracer* trc) { if (trc->runtime()->geckoProfiler().enabled() && trc->runtime()->hasJitRuntime() && trc->runtime()->jitRuntime()->hasJitcodeGlobalTable()) { - trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->trace(trc); + trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->traceForMinorGC(trc); } } /* static */ bool JitRuntime::MarkJitcodeGlobalTableIteratively(GCMarker* marker) { if (marker->runtime()->hasJitRuntime() && marker->runtime()->jitRuntime()->hasJitcodeGlobalTable())
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -12011,18 +12011,20 @@ IonBuilder::jsop_lambda(JSFunction* fun) AbortReasonOr<Ok> IonBuilder::jsop_lambda_arrow(JSFunction* fun) { MOZ_ASSERT(analysis().usesEnvironmentChain()); MOZ_ASSERT(fun->isArrow()); MOZ_ASSERT(!fun->isNative()); MDefinition* newTargetDef = current->pop(); + MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun); + current->add(cst); MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(), - newTargetDef, fun); + newTargetDef, cst); current->add(ins); current->push(ins); return resumeAfter(ins); } AbortReasonOr<Ok> IonBuilder::jsop_setfunname(uint8_t prefixKind)
--- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -181,17 +181,17 @@ class JitRuntime } public: explicit JitRuntime(JSRuntime* rt); ~JitRuntime(); MOZ_MUST_USE bool initialize(JSContext* cx, js::AutoLockForExclusiveAccess& lock); static void Trace(JSTracer* trc, js::AutoLockForExclusiveAccess& lock); - static void TraceJitcodeGlobalTable(JSTracer* trc); + static void TraceJitcodeGlobalTableForMinorGC(JSTracer* trc); static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker); static void SweepJitcodeGlobalTable(JSRuntime* rt); ExecutableAllocator& execAlloc() { return execAlloc_.ref(); } ExecutableAllocator& backedgeExecAlloc() { return backedgeExecAlloc_.ref();
--- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -447,17 +447,17 @@ JitcodeGlobalTable::lookupForSamplerInfa JitcodeGlobalEntry& rejoinEntry = RejoinEntry(rt, entry->ionCacheEntry(), ptr); rejoinEntry.setGeneration(sampleBufferGen); } // JitcodeGlobalEntries are marked at the end of the mark phase. A read // barrier is not needed. Any JS frames sampled during the sweep phase of // the GC must be on stack, and on-stack frames must already be marked at // the beginning of the sweep phase. It's not possible to assert this here - // as we may not be off thread when called from the gecko profiler. + // as we may be off main thread when called from the gecko profiler. return *entry; } JitcodeGlobalEntry* JitcodeGlobalTable::lookupInternal(void* ptr) { JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr); @@ -523,25 +523,34 @@ JitcodeGlobalTable::addEntry(const Jitco searchTowerEntry->tower_->setNext(level, newEntry); } else { newTower->setNext(level, startTower_[level]); startTower_[level] = newEntry; } } skiplistSize_++; // verifySkiplist(); - disabled for release. + + // Any entries that may directly contain nursery pointers must be marked + // during a minor GC to update those pointers. + if (entry.canHoldNurseryPointers()) + addToNurseryList(&newEntry->ionEntry()); + return true; } void JitcodeGlobalTable::removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt) { MOZ_ASSERT(!TlsContext.get()->isProfilerSamplingEnabled()); + if (entry.canHoldNurseryPointers()) + removeFromNurseryList(&entry.ionEntry()); + // Unlink query entry. for (int level = entry.tower_->height() - 1; level >= 0; level--) { JitcodeGlobalEntry* prevTowerEntry = prevTower[level]; if (prevTowerEntry) { MOZ_ASSERT(prevTowerEntry->tower_->next(level) == &entry); prevTowerEntry->tower_->setNext(level, entry.tower_->next(level)); } else { startTower_[level] = entry.tower_->next(level); @@ -710,37 +719,46 @@ JitcodeGlobalTable::verifySkiplist() MOZ_ASSERT(count == skiplistSize_); } #endif // DEBUG void JitcodeGlobalTable::setAllEntriesAsExpired(JSRuntime* rt) { AutoSuppressProfilerSampling suppressSampling(TlsContext.get()); - for (Range r(*this); !r.empty(); r.popFront()) - r.front()->setAsExpired(); + for (Range r(*this); !r.empty(); r.popFront()) { + auto entry = r.front(); + if (entry->canHoldNurseryPointers()) + removeFromNurseryList(&entry->ionEntry()); + entry->setAsExpired(); + } } struct Unconditionally { template <typename T> static bool ShouldTrace(JSRuntime* rt, T* thingp) { return true; } }; void -JitcodeGlobalTable::trace(JSTracer* trc) +JitcodeGlobalTable::traceForMinorGC(JSTracer* trc) { - // Trace all entries unconditionally. This is done during minor collection - // to tenure and update object pointers. + // Trace only entries that can directly contain nursery pointers. MOZ_ASSERT(trc->runtime()->geckoProfiler().enabled()); + MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting()); AutoSuppressProfilerSampling suppressSampling(TlsContext.get()); - for (Range r(*this); !r.empty(); r.popFront()) - r.front()->trace<Unconditionally>(trc); + JitcodeGlobalEntry::IonEntry* entry = nurseryEntries_; + while (entry) { + entry->trace<Unconditionally>(trc); + JitcodeGlobalEntry::IonEntry* prev = entry; + entry = entry->nextNursery_; + removeFromNurseryList(prev); + } } struct IfUnmarked { template <typename T> static bool ShouldTrace(JSRuntime* rt, T* thingp) { return !IsMarkedUnbarriered(rt, thingp); } }; @@ -792,16 +810,18 @@ JitcodeGlobalTable::markIteratively(GCMa // If an entry is not sampled, reset its generation to the invalid // generation, and conditionally mark the rest of the entry if its // JitCode is not already marked. This conditional marking ensures // that so long as the JitCode *may* be sampled, we keep any // information that may be handed out to the sampler, like tracked // types used by optimizations and scripts used for pc to line number // mapping, alive as well. if (!entry->isSampled(gen, lapCount)) { + if (entry->canHoldNurseryPointers()) + removeFromNurseryList(&entry->ionEntry()); entry->setAsExpired(); if (!entry->baseEntry().isJitcodeMarkedFromAnyThread(marker->runtime())) continue; } // The table is runtime-wide. Not all zones may be participating in // the GC. if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
--- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -233,16 +233,22 @@ class JitcodeGlobalEntry const IonTrackedOptimizationsRegionTable* optsRegionTable_; const IonTrackedOptimizationsTypesTable* optsTypesTable_; const IonTrackedOptimizationsAttemptsTable* optsAttemptsTable_; // The types table above records type sets, which have been gathered // into one vector here. IonTrackedTypeVector* optsAllTypes_; + // Linked list pointers to allow traversing through all entries that + // could possibly contain nursery pointers. Note that the contained + // pointers can be mutated into nursery pointers at any time. + IonEntry* prevNursery_; + IonEntry* nextNursery_; + struct ScriptNamePair { JSScript* script; char* str; }; struct SizedScriptList { uint32_t size; ScriptNamePair pairs[1]; @@ -267,16 +273,17 @@ class JitcodeGlobalEntry MOZ_ASSERT(regionTable); BaseEntry::init(Ion, code, nativeStartAddr, nativeEndAddr); regionTable_ = regionTable; scriptList_ = scriptList; optsRegionTable_ = nullptr; optsTypesTable_ = nullptr; optsAllTypes_ = nullptr; optsAttemptsTable_ = nullptr; + prevNursery_ = nextNursery_ = nullptr; } void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable* regionTable, const IonTrackedOptimizationsTypesTable* typesTable, const IonTrackedOptimizationsAttemptsTable* attemptsTable, IonTrackedTypeVector* allTypes) { optsRegionTable_ = regionTable; @@ -558,41 +565,41 @@ class JitcodeGlobalEntry public: JitcodeGlobalEntry() : tower_(nullptr) { base_.init(); } explicit JitcodeGlobalEntry(const IonEntry& ion) - : tower_(nullptr) + : JitcodeGlobalEntry() { ion_ = ion; } explicit JitcodeGlobalEntry(const BaselineEntry& baseline) - : tower_(nullptr) + : JitcodeGlobalEntry() { baseline_ = baseline; } explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache) - : tower_(nullptr) + : JitcodeGlobalEntry() { ionCache_ = ionCache; } explicit JitcodeGlobalEntry(const DummyEntry& dummy) - : tower_(nullptr) + : JitcodeGlobalEntry() { dummy_ = dummy; } explicit JitcodeGlobalEntry(const QueryEntry& query) - : tower_(nullptr) + : JitcodeGlobalEntry() { query_ = query; } static JitcodeGlobalEntry MakeQuery(void* ptr) { QueryEntry query; query.init(ptr); return JitcodeGlobalEntry(query); @@ -835,16 +842,20 @@ class JitcodeGlobalEntry case Dummy: break; default: MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); } return false; } + bool canHoldNurseryPointers() const { + return isIon() && ionEntry().hasTrackedOptimizations(); + } + mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( JSRuntime *rt, void* addr, uint32_t* entryOffsetOut) { switch (kind()) { case Ion: return ionEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut); @@ -1005,23 +1016,25 @@ class JitcodeGlobalTable { private: static const size_t LIFO_CHUNK_SIZE = 16 * 1024; LifoAlloc alloc_; JitcodeGlobalEntry* freeEntries_; uint32_t rand_; uint32_t skiplistSize_; + JitcodeGlobalEntry::IonEntry* nurseryEntries_; JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT]; JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT]; public: JitcodeGlobalTable() - : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0) + : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0), + nurseryEntries_(nullptr) { for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) startTower_[i] = nullptr; for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) freeTowers_[i] = nullptr; } ~JitcodeGlobalTable() {} @@ -1054,17 +1067,17 @@ class JitcodeGlobalTable MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry, JSRuntime* rt) { return addEntry(JitcodeGlobalEntry(entry), rt); } void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); void setAllEntriesAsExpired(JSRuntime* rt); - void trace(JSTracer* trc); + void traceForMinorGC(JSTracer* trc); MOZ_MUST_USE bool markIteratively(GCMarker* marker); void sweep(JSRuntime* rt); private: MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt); JitcodeGlobalEntry* lookupInternal(void* ptr); @@ -1086,16 +1099,39 @@ class JitcodeGlobalTable JitcodeGlobalEntry* allocateEntry(); #ifdef DEBUG void verifySkiplist(); #else void verifySkiplist() {} #endif + void addToNurseryList(JitcodeGlobalEntry::IonEntry* entry) { + MOZ_ASSERT(entry->prevNursery_ == nullptr); + MOZ_ASSERT(entry->nextNursery_ == nullptr); + + entry->nextNursery_ = nurseryEntries_; + if (nurseryEntries_) + nurseryEntries_->prevNursery_ = entry; + nurseryEntries_ = entry; + } + + void removeFromNurseryList(JitcodeGlobalEntry::IonEntry* entry) { + // Splice out of list to be scanned on a minor GC. + if (entry->prevNursery_) + entry->prevNursery_->nextNursery_ = entry->nextNursery_; + if (entry->nextNursery_) + entry->nextNursery_->prevNursery_ = entry->prevNursery_; + + if (nurseryEntries_ == entry) + nurseryEntries_ = entry->nextNursery_; + + entry->prevNursery_ = entry->nextNursery_ = nullptr; + } + public: class Range { protected: JitcodeGlobalTable& table_; JitcodeGlobalEntry* cur_; public:
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8518,39 +8518,47 @@ class MLambda return true; } bool appendRoots(MRootList& roots) const override { return info_.appendRoots(roots); } }; class MLambdaArrow - : public MBinaryInstruction, - public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data + : public MTernaryInstruction, + public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>::Data { const LambdaFunctionInfo info_; MLambdaArrow(CompilerConstraintList* constraints, MDefinition* envChain, - MDefinition* newTarget_, JSFunction* fun) - : MBinaryInstruction(envChain, newTarget_), info_(fun) + MDefinition* newTarget, MConstant* cst) + : MTernaryInstruction(envChain, newTarget, cst), + info_(&cst->toObject().as<JSFunction>()) { setResultType(MIRType::Object); - MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun)); - if (!fun->isSingleton()) - setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); + MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun)); + if (!info().fun->isSingleton()) + setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun)); } public: INSTRUCTION_HEADER(LambdaArrow) TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, environmentChain), (1, newTargetDef)) + MConstant* functionOperand() const { + return getOperand(2)->toConstant(); + } const LambdaFunctionInfo& info() const { return info_; } + MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override; + bool canRecoverOnBailout() const override { + return true; + } bool appendRoots(MRootList& roots) const override { return info_.appendRoots(roots); } }; class MSetFunName : public MAryInstruction<2>, public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
--- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1473,16 +1473,45 @@ RLambda::recover(JSContext* cx, Snapshot RootedValue result(cx); result.setObject(*resultObject); iter.storeInstructionResult(result); return true; } bool +MLambdaArrow::writeRecoverData(CompactBufferWriter& writer) const +{ + MOZ_ASSERT(canRecoverOnBailout()); + writer.writeUnsigned(uint32_t(RInstruction::Recover_LambdaArrow)); + return true; +} + +RLambdaArrow::RLambdaArrow(CompactBufferReader& reader) +{ +} + +bool +RLambdaArrow::recover(JSContext* cx, SnapshotIterator& iter) const +{ + RootedObject scopeChain(cx, &iter.read().toObject()); + RootedValue newTarget(cx, iter.read()); + RootedFunction fun(cx, &iter.read().toObject().as<JSFunction>()); + + JSObject* resultObject = js::LambdaArrow(cx, fun, scopeChain, newTarget); + if (!resultObject) + return false; + + RootedValue result(cx); + result.setObject(*resultObject); + iter.storeInstructionResult(result); + return true; +} + +bool MSimdBox::writeRecoverData(CompactBufferWriter& writer) const { MOZ_ASSERT(canRecoverOnBailout()); writer.writeUnsigned(uint32_t(RInstruction::Recover_SimdBox)); static_assert(unsigned(SimdType::Count) < 0x100, "assuming SimdType fits in 8 bits"); writer.writeByte(uint8_t(simdType())); return true; }
--- a/js/src/jit/Recover.h +++ b/js/src/jit/Recover.h @@ -101,16 +101,17 @@ namespace jit { _(TruncateToInt32) \ _(NewObject) \ _(NewTypedArray) \ _(NewArray) \ _(NewArrayIterator) \ _(NewDerivedTypedObject) \ _(CreateThisWithTemplate) \ _(Lambda) \ + _(LambdaArrow) \ _(SimdBox) \ _(ObjectState) \ _(ArrayState) \ _(AtomicIsLockFree) \ _(AssertRecoveredOnBailout) class RResumePoint; class SnapshotIterator; @@ -621,16 +622,24 @@ class RCreateThisWithTemplate final : pu class RLambda final : public RInstruction { public: RINSTRUCTION_HEADER_NUM_OP_(Lambda, 2) MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override; }; +class RLambdaArrow final : public RInstruction +{ + public: + RINSTRUCTION_HEADER_NUM_OP_(LambdaArrow, 3) + + MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override; +}; + class RSimdBox final : public RInstruction { private: uint8_t type_; public: RINSTRUCTION_HEADER_NUM_OP_(SimdBox, 1)
--- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -96,18 +96,19 @@ EmulateStateOf<MemoryView>::run(MemoryVi } static bool IsObjectEscaped(MInstruction* ins, JSObject* objDefault = nullptr); // Returns False if the lambda is not escaped and if it is optimizable by // ScalarReplacementOfObject. static bool -IsLambdaEscaped(MLambda* lambda, JSObject* obj) +IsLambdaEscaped(MInstruction* lambda, JSObject* obj) { + MOZ_ASSERT(lambda->isLambda() || lambda->isLambdaArrow()); JitSpewDef(JitSpew_Escape, "Check lambda\n", lambda); JitSpewIndent spewIndent(JitSpew_Escape); // The scope chain is not escaped if none of the Lambdas which are // capturing it are escaped. for (MUseIterator i(lambda->usesBegin()); i != lambda->usesEnd(); i++) { MNode* consumer = (*i)->consumer(); if (!consumer->isDefinition()) { @@ -237,20 +238,20 @@ IsObjectEscaped(MInstruction* ins, JSObj } if (IsObjectEscaped(def->toInstruction(), obj)) { JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def); return true; } break; } - case MDefinition::Op_Lambda: { - MLambda* lambda = def->toLambda(); - if (IsLambdaEscaped(lambda, obj)) { - JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", lambda); + case MDefinition::Op_Lambda: + case MDefinition::Op_LambdaArrow: { + if (IsLambdaEscaped(def->toInstruction(), obj)) { + JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def); return true; } break; } // This instruction is a no-op used to verify that scalar replacement // is working as expected in jit-test. case MDefinition::Op_AssertRecoveredOnBailout: @@ -307,16 +308,17 @@ class ObjectMemoryView : public MDefinit void visitStoreFixedSlot(MStoreFixedSlot* ins); void visitLoadFixedSlot(MLoadFixedSlot* ins); void visitPostWriteBarrier(MPostWriteBarrier* ins); void visitStoreSlot(MStoreSlot* ins); void visitLoadSlot(MLoadSlot* ins); void visitGuardShape(MGuardShape* ins); void visitFunctionEnvironment(MFunctionEnvironment* ins); void visitLambda(MLambda* ins); + void visitLambdaArrow(MLambdaArrow* ins); void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins); void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins); void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins); void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins); void visitStoreUnboxedString(MStoreUnboxedString* ins); void visitLoadUnboxedString(MLoadUnboxedString* ins); private: @@ -475,17 +477,17 @@ ObjectMemoryView::assertSuccess() // Resume points have been replaced by the object state. if (ins->isResumePoint() || (def = ins->toDefinition())->isRecoveredOnBailout()) { MOZ_ASSERT(obj_->isIncompleteObject()); continue; } // The only remaining uses would be removed by DCE, which will also // recover the object on bailouts. - MOZ_ASSERT(def->isSlots() || def->isLambda()); + MOZ_ASSERT(def->isSlots() || def->isLambda() || def->isLambdaArrow()); MOZ_ASSERT(!def->hasDefUses()); } } #endif void ObjectMemoryView::visitResumePoint(MResumePoint* rp) { @@ -637,18 +639,25 @@ ObjectMemoryView::visitGuardShape(MGuard ins->block()->discard(ins); } void ObjectMemoryView::visitFunctionEnvironment(MFunctionEnvironment* ins) { // Skip function environment which are not aliases of the NewCallObject. MDefinition* input = ins->input(); - if (!input->isLambda() || input->toLambda()->environmentChain() != obj_) + if (input->isLambda()) { + if (input->toLambda()->environmentChain() != obj_) + return; + } else if (input->isLambdaArrow()) { + if (input->toLambdaArrow()->environmentChain() != obj_) + return; + } else { return; + } // Replace the function environment by the scope chain of the lambda. ins->replaceAllUsesWith(obj_); // Remove original instruction. ins->block()->discard(ins); } @@ -658,16 +667,25 @@ ObjectMemoryView::visitLambda(MLambda* i if (ins->environmentChain() != obj_) return; // In order to recover the lambda we need to recover the scope chain, as the // lambda is holding it. ins->setIncompleteObject(); } +void +ObjectMemoryView::visitLambdaArrow(MLambdaArrow* ins) +{ + if (ins->environmentChain() != obj_) + return; + + ins->setIncompleteObject(); +} + static size_t GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset) { int32_t idx = index->toConstant()->toInt32(); MOZ_ASSERT(idx >= 0); MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData()); return idx * width + baseOffset - UnboxedPlainObject::offsetOfData(); }
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -677,17 +677,19 @@ MacroAssembler::callWithABIPre(uint32_t* // ARM64 /really/ wants the stack to always be aligned. Since we're already tracking it // getting it aligned for an abi call is pretty easy. MOZ_ASSERT(dynamicAlignment_); stackForCall += ComputeByteAlignment(stackForCall, StackAlignment); *stackAdjust = stackForCall; reserveStack(*stackAdjust); { - moveResolver_.resolve(); + enoughMemory_ &= moveResolver_.resolve(); + if (!enoughMemory_) + return; MoveEmitter emitter(*this); emitter.emit(moveResolver_); emitter.finish(); } // Call boundaries communicate stack via sp. syncStackPtr(); }
--- a/js/src/jit/arm64/vixl/Debugger-vixl.cpp +++ b/js/src/jit/arm64/vixl/Debugger-vixl.cpp @@ -25,16 +25,17 @@ // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "js-config.h" #ifdef JS_SIMULATOR_ARM64 #include "jit/arm64/vixl/Debugger-vixl.h" +#include "mozilla/Unused.h" #include "mozilla/Vector.h" #include "jsalloc.h" namespace vixl { // List of commands supported by the debugger. #define DEBUG_COMMAND_LIST(C) \ @@ -1104,36 +1105,37 @@ bool DebugCommand::Match(const char* nam } } return false; } DebugCommand* DebugCommand::Parse(char* line) { + using mozilla::Unused; TokenVector args; for (char* chunk = strtok(line, " \t"); chunk != NULL; chunk = strtok(NULL, " \t")) { char* dot = strchr(chunk, '.'); if (dot != NULL) { // 'Token.format'. Token* format = FormatToken::Tokenize(dot + 1); if (format != NULL) { *dot = '\0'; - args.append(Token::Tokenize(chunk)); - args.append(format); + Unused << args.append(Token::Tokenize(chunk)); + Unused << args.append(format); } else { // Error while parsing the format, push the UnknownToken so an error // can be accurately reported. - args.append(Token::Tokenize(chunk)); + Unused << args.append(Token::Tokenize(chunk)); } } else { - args.append(Token::Tokenize(chunk)); + Unused << args.append(Token::Tokenize(chunk)); } } if (args.empty()) { return NULL; } if (!args[0]->IsIdentifier()) {
--- a/js/src/jit/arm64/vixl/Decoder-vixl.cpp +++ b/js/src/jit/arm64/vixl/Decoder-vixl.cpp @@ -107,49 +107,49 @@ void Decoder::DecodeInstruction(const In // Advanced SIMD. case 0xE: case 0xF: DecodeFP(instr); break; } } } void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.append(new_visitor); + MOZ_ALWAYS_TRUE(visitors_.append(new_visitor)); } void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.insert(visitors_.begin(), new_visitor); + MOZ_ALWAYS_TRUE(visitors_.insert(visitors_.begin(), new_visitor)); } void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, DecoderVisitor* registered_visitor) { for (auto it = visitors_.begin(); it != visitors_.end(); it++) { if (*it == registered_visitor) { - visitors_.insert(it, new_visitor); + MOZ_ALWAYS_TRUE(visitors_.insert(it, new_visitor)); return; } } // We reached the end of the list without finding registered_visitor. - visitors_.append(new_visitor); + MOZ_ALWAYS_TRUE(visitors_.append(new_visitor)); } void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, DecoderVisitor* registered_visitor) { for (auto it = visitors_.begin(); it != visitors_.end(); it++) { if (*it == registered_visitor) { it++; - visitors_.insert(it, new_visitor); + MOZ_ALWAYS_TRUE(visitors_.insert(it, new_visitor)); return; } } // We reached the end of the list without finding registered_visitor. - visitors_.append(new_visitor); + MOZ_ALWAYS_TRUE(visitors_.append(new_visitor)); } void Decoder::RemoveVisitor(DecoderVisitor* visitor) { visitors_.erase(std::remove(visitors_.begin(), visitors_.end(), visitor), visitors_.end()); }
--- a/js/src/jit/arm64/vixl/Instrument-vixl.cpp +++ b/js/src/jit/arm64/vixl/Instrument-vixl.cpp @@ -21,16 +21,18 @@ // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "jit/arm64/vixl/Instrument-vixl.h" +#include "mozilla/Unused.h" + namespace vixl { Counter::Counter(const char* name, CounterType type) : count_(0), enabled_(false), type_(type) { VIXL_ASSERT(name != NULL); strncpy(name_, name, kCounterNameMaxLength); } @@ -134,17 +136,17 @@ Instrument::Instrument(const char* dataf // Dump an instrumentation description comment at the top of the file. fprintf(output_stream_, "# counters=%d\n", num_counters); fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_); // Construct Counter objects from counter description array. for (int i = 0; i < num_counters; i++) { if (Counter* counter = js_new<Counter>(kCounterList[i].name, kCounterList[i].type)) - counters_.append(counter); + mozilla::Unused << counters_.append(counter); } DumpCounterNames(); } Instrument::~Instrument() { // Dump any remaining instruction data to the output file.
--- a/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp +++ b/js/src/jit/arm64/vixl/MozSimulator-vixl.cpp @@ -24,16 +24,17 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "mozilla/DebugOnly.h" #include "jit/arm64/vixl/Debugger-vixl.h" #include "jit/arm64/vixl/Simulator-vixl.h" #include "jit/IonTypes.h" +#include "js/Utility.h" #include "threading/LockGuard.h" #include "vm/Runtime.h" #include "wasm/WasmCode.h" js::jit::SimulatorProcess* js::jit::SimulatorProcess::singleton_ = nullptr; namespace vixl { @@ -422,19 +423,22 @@ void Simulator::VisitException(const Ins } case SVC: // The SVC instruction is hijacked by the JIT as a pseudo-instruction // causing the Simulator to execute host-native code for callWithABI. switch (instr->ImmException()) { case kCallRtRedirected: VisitCallRedirection(instr); return; - case kMarkStackPointer: - spStack_.append(xreg(31, Reg31IsStackPointer)); + case kMarkStackPointer: { + js::AutoEnterOOMUnsafeRegion oomUnsafe; + if (!spStack_.append(xreg(31, Reg31IsStackPointer))) + oomUnsafe.crash("tracking stack for ARM64 simulator"); return; + } case kCheckStackPointer: { int64_t current = xreg(31, Reg31IsStackPointer); int64_t expected = spStack_.popCopy(); VIXL_ASSERT(current == expected); return; } default: VIXL_UNIMPLEMENTED();
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -407,18 +407,19 @@ CodeGeneratorShared::encodeAllocation(LS } // This MDefinition is recovered, thus it should be listed in the // LRecoverInfo. MOZ_ASSERT(it != end && mir == *it); // Lambda should have a default value readable for iterating over the // inner frames. - if (mir->isLambda()) { - MConstant* constant = mir->toLambda()->functionOperand(); + if (mir->isLambda() || mir->isLambdaArrow()) { + MConstant* constant = mir->isLambda() ? mir->toLambda()->functionOperand() + : mir->toLambdaArrow()->functionOperand(); uint32_t cstIndex; masm.propagateOOM(graph.addConstantToPool(constant->toJSValue(), &cstIndex)); alloc = RValueAllocation::RecoverInstruction(index, cstIndex); break; } alloc = RValueAllocation::RecoverInstruction(index); break;
--- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp +++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp @@ -155,39 +155,40 @@ virtual void uninit() override { JS_RemoveFinalizeCallback(cx, FinalizeCallback); JSAPITest::uninit(); } bool checkSingleGroup() { CHECK(FinalizeCalls < BufferSize); - CHECK(FinalizeCalls == 3); + CHECK(FinalizeCalls == 4); return true; } bool checkMultipleGroups() { CHECK(FinalizeCalls < BufferSize); - CHECK(FinalizeCalls % 2 == 1); - CHECK((FinalizeCalls - 1) / 2 > 1); + CHECK(FinalizeCalls % 3 == 1); + CHECK((FinalizeCalls - 1) / 3 > 1); return true; } bool checkFinalizeStatus() { /* * The finalize callback should be called twice for each sweep group * finalized, with status JSFINALIZE_GROUP_START and JSFINALIZE_GROUP_END, * and then once more with JSFINALIZE_COLLECTION_END. */ - for (unsigned i = 0; i < FinalizeCalls - 1; i += 2) { - CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_START); - CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_END); + for (unsigned i = 0; i < FinalizeCalls - 1; i += 3) { + CHECK(StatusBuffer[i] == JSFINALIZE_GROUP_PREPARE); + CHECK(StatusBuffer[i + 1] == JSFINALIZE_GROUP_START); + CHECK(StatusBuffer[i + 2] == JSFINALIZE_GROUP_END); } CHECK(StatusBuffer[FinalizeCalls - 1] == JSFINALIZE_COLLECTION_END); return true; } bool checkFinalizeIsZoneGC(bool isZoneGC)
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -558,25 +558,30 @@ typedef void typedef void (* JSObjectsTenuredCallback)(JSContext* cx, void* data); typedef enum JSFinalizeStatus { /** * Called when preparing to sweep a group of zones, before anything has been * swept. The collector will not yield to the mutator before calling the - * callback with JSFINALIZE_GROUP_END status. + * callback with JSFINALIZE_GROUP_START status. + */ + JSFINALIZE_GROUP_PREPARE, + + /** + * Called after preparing to sweep a group of zones. Weak references to + * unmarked things have been removed at this point, but no GC things have + * been swept. The collector may yield to the mutator after this point. */ JSFINALIZE_GROUP_START, /** - * Called when preparing to sweep a group of zones. Weak references to - * unmarked things have been removed and things that are not swept - * incrementally have been finalized at this point. The collector may yield - * to the mutator after this point. + * Called after sweeping a group of zones. All dead GC things have been + * swept at this point. */ JSFINALIZE_GROUP_END, /** * Called at the end of collection when everything has been swept. */ JSFINALIZE_COLLECTION_END } JSFinalizeStatus;
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -618,16 +618,23 @@ js::ZoneGlobalsAreAllGray(JS::Zone* zone for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { JSObject* obj = comp->unsafeUnbarrieredMaybeGlobal(); if (!obj || !JS::ObjectIsMarkedGray(obj)) return false; } return true; } +JS_FRIEND_API(bool) +js::IsObjectZoneSweepingOrCompacting(JSObject* obj) +{ + MOZ_ASSERT(obj); + return MaybeForwarded(obj)->zone()->isGCSweepingOrCompacting(); +} + namespace { struct VisitGrayCallbackFunctor { GCThingCallback callback_; void* closure_; VisitGrayCallbackFunctor(GCThingCallback callback, void* closure) : callback_(callback), closure_(closure) {}
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -468,16 +468,19 @@ extern JS_FRIEND_API(void) TraceWeakMaps(WeakMapTracer* trc); extern JS_FRIEND_API(bool) AreGCGrayBitsValid(JSContext* cx); extern JS_FRIEND_API(bool) ZoneGlobalsAreAllGray(JS::Zone* zone); +extern JS_FRIEND_API(bool) +IsObjectZoneSweepingOrCompacting(JSObject* obj); + typedef void (*GCThingCallback)(void* closure, JS::GCCellPtr thing); extern JS_FRIEND_API(void) VisitGrayWrapperTargets(JS::Zone* zone, GCThingCallback callback, void* closure); extern JS_FRIEND_API(JSObject*) GetWeakmapKeyDelegate(JSObject* key);
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -320,16 +320,30 @@ FOR_EACH_ALLOCKIND(EXPAND_THINGS_PER_ARE struct js::gc::FinalizePhase { gcstats::Phase statsPhase; AllocKinds kinds; }; /* + * Finalization order for objects swept incrementally on the active thread. + */ +static const FinalizePhase ForegroundObjectFinalizePhase = { + gcstats::PHASE_SWEEP_OBJECT, { + AllocKind::OBJECT0, + AllocKind::OBJECT2, + AllocKind::OBJECT4, + AllocKind::OBJECT8, + AllocKind::OBJECT12, + AllocKind::OBJECT16 + } +}; + +/* * Finalization order for GC things swept incrementally on the active thread. */ static const FinalizePhase IncrementalFinalizePhases[] = { { gcstats::PHASE_SWEEP_STRING, { AllocKind::EXTERNAL_STRING } }, @@ -948,17 +962,27 @@ const char* gc::ZealModeHelpText = " 8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n" " 9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n" " 10: (IncrementalMultipleSlices) Incremental GC in multiple slices\n" " 11: (IncrementalMarkingValidator) Verify incremental marking\n" " 12: (ElementsBarrier) Always use the individual element post-write barrier, regardless of elements size\n" " 13: (CheckHashTablesOnMinorGC) Check internal hashtables on minor GC\n" " 14: (Compact) Perform a shrinking collection every N allocations\n" " 15: (CheckHeapAfterGC) Walk the heap to check its integrity after every GC\n" - " 16: (CheckNursery) Check nursery integrity on minor GC\n"; + " 16: (CheckNursery) Check nursery integrity on minor GC\n" + " 17: (IncrementalSweepThenFinish) Incremental GC in two slices: 1) start sweeping 2) finish collection\n"; + +// The set of zeal modes that control incremental slices. These modes are +// mutually exclusive. +static const mozilla::EnumSet<ZealMode> IncrementalSliceZealModes = { + ZealMode::IncrementalRootsThenFinish, + ZealMode::IncrementalMarkAllThenFinish, + ZealMode::IncrementalMultipleSlices, + ZealMode::IncrementalSweepThenFinish +}; void GCRuntime::setZeal(uint8_t zeal, uint32_t frequency) { MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit)); if (verifyPreData) VerifyBarriers(rt, PreBarrierVerifier); @@ -969,24 +993,21 @@ GCRuntime::setZeal(uint8_t zeal, uint32_ } ZealMode zealMode = ZealMode(zeal); if (zealMode == ZealMode::GenerationalGC) { for (ZoneGroupsIter group(rt); !group.done(); group.next()) group->nursery().enterZealMode(); } - // Zeal modes 8-10 are mutually exclusive. If we're setting one of those, - // we first reset all of them. - if (zealMode >= ZealMode::IncrementalRootsThenFinish && - zealMode <= ZealMode::IncrementalMultipleSlices) - { - clearZealMode(ZealMode::IncrementalRootsThenFinish); - clearZealMode(ZealMode::IncrementalMarkAllThenFinish); - clearZealMode(ZealMode::IncrementalMultipleSlices); + // Some modes are mutually exclusive. If we're setting one of those, we + // first reset all of them. + if (IncrementalSliceZealModes.contains(zealMode)) { + for (auto mode : IncrementalSliceZealModes) + clearZealMode(mode); } bool schedule = zealMode >= ZealMode::Alloc; if (zeal != 0) zealModeBits |= 1 << unsigned(zeal); else zealModeBits = 0; zealFrequency = frequency; @@ -2741,39 +2762,16 @@ ArenaLists::~ArenaLists() ReleaseArenaList(runtime_, incrementalSweptArenas.ref().head(), lock); for (auto i : ObjectAllocKinds()) ReleaseArenaList(runtime_, savedObjectArenas(i).head(), lock); ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock); } void -ArenaLists::finalizeNow(FreeOp* fop, AllocKind thingKind, Arena** empty) -{ - MOZ_ASSERT(!IsBackgroundFinalized(thingKind)); - MOZ_ASSERT(backgroundFinalizeState(thingKind) == BFS_DONE); - MOZ_ASSERT(empty); - - Arena* arenas = arenaLists(thingKind).head(); - if (!arenas) - return; - arenaLists(thingKind).clear(); - - size_t thingsPerArena = Arena::thingsPerArena(thingKind); - SortedArenaList finalizedSorted(thingsPerArena); - - auto unlimited = SliceBudget::unlimited(); - FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, unlimited, KEEP_ARENAS); - MOZ_ASSERT(!arenas); - - finalizedSorted.extractEmpty(empty); - arenaLists(thingKind) = finalizedSorted.toArenaList(); -} - -void ArenaLists::queueForForegroundSweep(FreeOp* fop, const FinalizePhase& phase) { gcstats::AutoPhase ap(fop->runtime()->gc.stats(), phase.statsPhase); for (auto kind : phase.kinds) queueForForegroundSweep(fop, kind); } void @@ -2855,49 +2853,16 @@ ArenaLists::backgroundFinalize(FreeOp* f lists->arenaListsToSweep(thingKind) = nullptr; } lists->backgroundFinalizeState(thingKind) = BFS_DONE; } void -ArenaLists::queueForegroundObjectsForSweep(FreeOp* fop) -{ - gcstats::AutoPhase ap(fop->runtime()->gc.stats(), gcstats::PHASE_SWEEP_OBJECT); - -#ifdef DEBUG - for (auto i : ObjectAllocKinds()) - MOZ_ASSERT(savedObjectArenas(i).isEmpty()); - MOZ_ASSERT(savedEmptyObjectArenas == nullptr); -#endif - - // Foreground finalized objects must be finalized at the beginning of the - // sweep phase, before control can return to the mutator. Otherwise, - // mutator behavior can resurrect certain objects whose references would - // otherwise have been erased by the finalizer. - finalizeNow(fop, AllocKind::OBJECT0, &savedEmptyObjectArenas.ref()); - finalizeNow(fop, AllocKind::OBJECT2, &savedEmptyObjectArenas.ref()); - finalizeNow(fop, AllocKind::OBJECT4, &savedEmptyObjectArenas.ref()); - finalizeNow(fop, AllocKind::OBJECT8, &savedEmptyObjectArenas.ref()); - finalizeNow(fop, AllocKind::OBJECT12, &savedEmptyObjectArenas.ref()); - finalizeNow(fop, AllocKind::OBJECT16, &savedEmptyObjectArenas.ref()); - - // Prevent the arenas from having new objects allocated into them. We need - // to know which objects are marked while we incrementally sweep dead - // references from type information. - savedObjectArenas(AllocKind::OBJECT0) = arenaLists(AllocKind::OBJECT0).copyAndClear(); - savedObjectArenas(AllocKind::OBJECT2) = arenaLists(AllocKind::OBJECT2).copyAndClear(); - savedObjectArenas(AllocKind::OBJECT4) = arenaLists(AllocKind::OBJECT4).copyAndClear(); - savedObjectArenas(AllocKind::OBJECT8) = arenaLists(AllocKind::OBJECT8).copyAndClear(); - savedObjectArenas(AllocKind::OBJECT12) = arenaLists(AllocKind::OBJECT12).copyAndClear(); - savedObjectArenas(AllocKind::OBJECT16) = arenaLists(AllocKind::OBJECT16).copyAndClear(); -} - -void ArenaLists::mergeForegroundSweptObjectArenas() { AutoLockGC lock(runtime_); ReleaseArenaList(runtime_, savedEmptyObjectArenas, lock); savedEmptyObjectArenas = nullptr; mergeSweptArenas(AllocKind::OBJECT0); mergeSweptArenas(AllocKind::OBJECT2); @@ -4590,28 +4555,37 @@ GCRuntime::findInterZoneEdges() zone->setHasDeadProxies(false); } } return true; } void -GCRuntime::groupZonesForSweeping(AutoLockForExclusiveAccess& lock) +GCRuntime::groupZonesForSweeping(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock) { #ifdef DEBUG for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) MOZ_ASSERT(zone->gcSweepGroupEdges().empty()); #endif JSContext* cx = TlsContext.get(); ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode], lock); if (!isIncremental || !findInterZoneEdges()) finder.useOneComponent(); +#ifdef JS_GC_ZEAL + // Use one component for IncrementalSweepThenFinish zeal mode. + if (isIncremental && reason == JS::gcreason::DEBUG_GC && + hasZealMode(ZealMode::IncrementalSweepThenFinish)) + { + finder.useOneComponent(); + } +#endif + for (GCZonesIter zone(rt); !zone.done(); zone.next()) { MOZ_ASSERT(zone->isGCMarking()); finder.addNode(zone); } sweepGroups = finder.getResultsList(); currentSweepGroup = sweepGroups; sweepGroupIndex = 0; @@ -5167,28 +5141,29 @@ GCRuntime::beginSweepingSweepGroup(AutoL /* No need to look up any more weakmap keys from this sweep group. */ AutoEnterOOMUnsafeRegion oomUnsafe; if (!zone->gcWeakKeys().clear()) oomUnsafe.crash("clearing weak keys in beginSweepingSweepGroup()"); } { gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_START); - callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START); + callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_PREPARE); { gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_ZONES_CALLBACK); callWeakPointerZonesCallbacks(); } { gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_COMPARTMENT_CALLBACK); for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) callWeakPointerCompartmentCallbacks(comp); } } + callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START); } if (sweepingAtoms) { AutoLockHelperThreadState helperLock; startTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS, helperLock); } { @@ -5291,52 +5266,48 @@ GCRuntime::beginSweepingSweepGroup(AutoL joinTask(task, gcstats::PHASE_SWEEP_MISC, helperLock); } /* * Queue all GC things in all zones for sweeping, either in the * foreground or on the background thread. * * Note that order is important here for the background case. - * - * Objects are finalized immediately but this may change in the future. */ for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(stats(), sweepGroupIndex); - zone->arenas.queueForegroundObjectsForSweep(&fop); - } - for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { - gcstats::AutoSCC scc(stats(), sweepGroupIndex); + zone->arenas.queueForForegroundSweep(&fop, ForegroundObjectFinalizePhase); for (unsigned i = 0; i < ArrayLength(IncrementalFinalizePhases); ++i) zone->arenas.queueForForegroundSweep(&fop, IncrementalFinalizePhases[i]); } for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(stats(), sweepGroupIndex); for (unsigned i = 0; i < ArrayLength(BackgroundFinalizePhases); ++i) zone->arenas.queueForBackgroundSweep(&fop, BackgroundFinalizePhases[i]); } for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { gcstats::AutoSCC scc(stats(), sweepGroupIndex); zone->arenas.queueForegroundThingsForSweep(&fop); } sweepPhaseIndex = 0; sweepZone = currentSweepGroup; sweepActionIndex = 0; - - { - gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_END); - callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END); - } } void GCRuntime::endSweepingSweepGroup() { + { + gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_END); + FreeOp fop(rt); + callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END); + } + /* Update the GC state for zones we have swept. */ for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { MOZ_ASSERT(zone->isGCSweeping()); AutoLockGC lock(rt); zone->setGCState(Zone::Finished); zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind, tunables, schedulingState, lock); } @@ -5353,17 +5324,17 @@ GCRuntime::endSweepingSweepGroup() /* Reset the list of arenas marked as being allocated during sweep phase. */ while (Arena* arena = arenasAllocatedDuringSweep) { arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep(); arena->unsetAllocDuringSweep(); } } void -GCRuntime::beginSweepPhase(bool destroyingRuntime, AutoLockForExclusiveAccess& lock) +GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock) { /* * Sweep phase. * * Finalize as we sweep, outside of lock but with CurrentThreadIsHeapBusy() * true so that any attempt to allocate a GC-thing from a finalizer will * fail, rather than nest badly and leave the unmarked newborn to be swept. */ @@ -5374,50 +5345,59 @@ GCRuntime::beginSweepPhase(bool destroyi releaseHeldRelocatedArenas(); computeNonIncrementalMarkingForValidation(lock); gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP); sweepOnBackgroundThread = - !destroyingRuntime && !TraceEnabled() && CanUseExtraThreads(); + reason != JS::gcreason::DESTROY_RUNTIME && !TraceEnabled() && CanUseExtraThreads(); releaseObservedTypes = shouldReleaseObservedTypes(); AssertNoWrappersInGrayList(rt); DropStringWrappers(rt); - groupZonesForSweeping(lock); + groupZonesForSweeping(reason, lock); endMarkingSweepGroup(); beginSweepingSweepGroup(lock); } bool ArenaLists::foregroundFinalize(FreeOp* fop, AllocKind thingKind, SliceBudget& sliceBudget, SortedArenaList& sweepList) { + MOZ_ASSERT_IF(IsObjectAllocKind(thingKind), savedObjectArenas(thingKind).isEmpty()); + if (!arenaListsToSweep(thingKind) && incrementalSweptArenas.ref().isEmpty()) return true; + KeepArenasEnum keepArenas = IsObjectAllocKind(thingKind) ? KEEP_ARENAS : RELEASE_ARENAS; if (!FinalizeArenas(fop, &arenaListsToSweep(thingKind), sweepList, - thingKind, sliceBudget, RELEASE_ARENAS)) + thingKind, sliceBudget, keepArenas)) { incrementalSweptArenaKind = thingKind; incrementalSweptArenas = sweepList.toArenaList(); return false; } // Clear any previous incremental sweep state we may have saved. incrementalSweptArenas.ref().clear(); - // Join |arenaLists[thingKind]| and |sweepList| into a single list. - ArenaList finalized = sweepList.toArenaList(); - arenaLists(thingKind) = - finalized.insertListWithCursorAtEnd(arenaLists(thingKind)); + if (IsObjectAllocKind(thingKind)) { + // Delay releasing of object arenas until types have been swept. + sweepList.extractEmpty(&savedEmptyObjectArenas.ref()); + savedObjectArenas(thingKind) = sweepList.toArenaList(); + } else { + // Join |arenaLists[thingKind]| and |sweepList| into a single list. + ArenaList finalized = sweepList.toArenaList(); + arenaLists(thingKind) = + finalized.insertListWithCursorAtEnd(arenaLists(thingKind)); + } return true; } IncrementalProgress GCRuntime::drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase) { /* Run a marking slice and return whether the stack is now empty. */ @@ -5565,16 +5545,20 @@ AddSweepAction(bool* ok, SweepAction::Fu } /* static */ bool GCRuntime::initializeSweepActions() { bool ok = true; AddSweepPhase(&ok); + for (auto kind : ForegroundObjectFinalizePhase.kinds) + AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind); + + AddSweepPhase(&ok); AddSweepAction(&ok, GCRuntime::sweepTypeInformation); AddSweepAction(&ok, GCRuntime::mergeSweptObjectArenas); for (const auto& finalizePhase : IncrementalFinalizePhases) { AddSweepPhase(&ok); for (auto kind : finalizePhase.kinds) AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind); } @@ -6109,17 +6093,18 @@ GCRuntime::incrementalCollectSlice(Slice useZeal = true; } #endif MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental); isIncremental = !budget.isUnlimited(); if (useZeal && (hasZealMode(ZealMode::IncrementalRootsThenFinish) || - hasZealMode(ZealMode::IncrementalMarkAllThenFinish))) + hasZealMode(ZealMode::IncrementalMarkAllThenFinish) || + hasZealMode(ZealMode::IncrementalSweepThenFinish))) { /* * Yields between slices occurs at predetermined points in these modes; * the budget is not used. */ budget.makeUnlimited(); } @@ -6187,26 +6172,30 @@ GCRuntime::incrementalCollectSlice(Slice } incrementalState = State::Sweep; /* * This runs to completion, but we don't continue if the budget is * now exhasted. */ - beginSweepPhase(destroyingRuntime, lock); + beginSweepPhase(reason, lock); if (budget.isOverBudget()) break; /* * Always yield here when running in incremental multi-slice zeal * mode, so RunDebugGC can reset the slice buget. */ - if (isIncremental && useZeal && hasZealMode(ZealMode::IncrementalMultipleSlices)) + if (isIncremental && useZeal && + (hasZealMode(ZealMode::IncrementalMultipleSlices) || + hasZealMode(ZealMode::IncrementalSweepThenFinish))) + { break; + } MOZ_FALLTHROUGH; case State::Sweep: if (performSweepActions(budget, lock) == NotFinished) break; endSweepPhase(destroyingRuntime, lock); @@ -6215,17 +6204,17 @@ GCRuntime::incrementalCollectSlice(Slice MOZ_FALLTHROUGH; case State::Finalize: { gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD); // Yield until background finalization is done. - if (isIncremental) { + if (!budget.isUnlimited()) { // Poll for end of background sweeping AutoLockGC lock(rt); if (isBackgroundSweeping()) break; } else { waitBackgroundSweepEnd(); } } @@ -6239,17 +6228,17 @@ GCRuntime::incrementalCollectSlice(Slice FreeOp fop(rt); sweepZoneGroups(&fop, destroyingRuntime); } MOZ_ASSERT(!startedCompacting); incrementalState = State::Compact; // Always yield before compacting since it is not incremental. - if (isCompacting && isIncremental) + if (isCompacting && !budget.isUnlimited()) break; MOZ_FALLTHROUGH; case State::Compact: if (isCompacting) { if (!startedCompacting) beginCompactPhase(); @@ -6265,17 +6254,17 @@ GCRuntime::incrementalCollectSlice(Slice MOZ_FALLTHROUGH; case State::Decommit: { gcstats::AutoPhase ap(stats(), gcstats::PHASE_WAIT_BACKGROUND_THREAD); // Yield until background decommit is done. - if (isIncremental && decommitTask.isRunning()) + if (!budget.isUnlimited() && decommitTask.isRunning()) break; decommitTask.join(); } finishCollection(reason); incrementalState = State::NotActive; break; @@ -7149,17 +7138,18 @@ GCRuntime::runDebugGC() if (hasZealMode(ZealMode::GenerationalGC)) return minorGC(JS::gcreason::DEBUG_GC); PrepareForDebugGC(rt); auto budget = SliceBudget::unlimited(); if (hasZealMode(ZealMode::IncrementalRootsThenFinish) || hasZealMode(ZealMode::IncrementalMarkAllThenFinish) || - hasZealMode(ZealMode::IncrementalMultipleSlices)) + hasZealMode(ZealMode::IncrementalMultipleSlices) || + hasZealMode(ZealMode::IncrementalSweepThenFinish)) { js::gc::State initialState = incrementalState; if (hasZealMode(ZealMode::IncrementalMultipleSlices)) { /* * Start with a small slice limit and double it every slice. This * ensure that we get multiple slices, and collection runs to * completion. */
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -802,18 +802,16 @@ class ArenaLists enum KeepArenasEnum { RELEASE_ARENAS, KEEP_ARENAS }; private: inline void queueForForegroundSweep(FreeOp* fop, const FinalizePhase& phase); inline void queueForBackgroundSweep(FreeOp* fop, const FinalizePhase& phase); - - inline void finalizeNow(FreeOp* fop, AllocKind thingKind, Arena** empty = nullptr); inline void queueForForegroundSweep(FreeOp* fop, AllocKind thingKind); inline void queueForBackgroundSweep(FreeOp* fop, AllocKind thingKind); inline void mergeSweptArenas(AllocKind thingKind); TenuredCell* allocateFromArena(JS::Zone* zone, AllocKind thingKind, ShouldCheckThresholds checkThresholds, AutoMaybeStartBackgroundAllocation& maybeStartBGAlloc); inline TenuredCell* allocateFromArenaInner(JS::Zone* zone, Arena* arena, AllocKind kind); @@ -1178,23 +1176,24 @@ inline void CheckValueAfterMovingGC(cons D(IncrementalRootsThenFinish, 8) \ D(IncrementalMarkAllThenFinish, 9) \ D(IncrementalMultipleSlices, 10) \ D(IncrementalMarkingValidator, 11) \ D(ElementsBarrier, 12) \ D(CheckHashTablesOnMinorGC, 13) \ D(Compact, 14) \ D(CheckHeapAfterGC, 15) \ - D(CheckNursery, 16) + D(CheckNursery, 16) \ + D(IncrementalSweepThenFinish, 17) enum class ZealMode { #define ZEAL_MODE(name, value) name = value, JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE) #undef ZEAL_MODE - Limit = 16 + Limit = 17 }; enum VerifierType { PreBarrierVerifier }; #ifdef JS_GC_ZEAL
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -607,16 +607,18 @@ if CONFIG['JS_HAS_CTYPES']: ] if CONFIG['MOZ_VTUNE']: SOURCES += [ 'vtune/ittnotify_static.c', 'vtune/jitprofiling.c', 'vtune/VTuneWrapper.cpp', ] + if CONFIG['CC_TYPE'] != 'msvc': + SOURCES['vtune/ittnotify_static.c'].flags += ['-Wno-varargs'] if CONFIG['HAVE_LINUX_PERF_EVENT_H']: SOURCES += [ 'perf/pm_linux.cpp' ] if CONFIG['LINUX_HEADERS_INCLUDES']: SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']] else:
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3215,26 +3215,30 @@ Debugger::trace(JSTracer* trc) } /* static */ void Debugger::sweepAll(FreeOp* fop) { JSRuntime* rt = fop->runtime(); for (ZoneGroupsIter group(rt); !group.done(); group.next()) { - for (Debugger* dbg : group->debuggerList()) { + Debugger* dbg = group->debuggerList().getFirst(); + while (dbg) { + Debugger* next = dbg->getNext(); if (IsAboutToBeFinalized(&dbg->object)) { /* * dbg is being GC'd. Detach it from its debuggees. The debuggee * might be GC'd too. Since detaching requires access to both * objects, this must be done before finalize time. */ for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e); + fop->delete_(dbg); } + dbg = next; } } } /* static */ void Debugger::detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global) { const GlobalObject::DebuggerVector* debuggers = global->getDebuggers(); @@ -3266,47 +3270,35 @@ Debugger::findZoneEdges(Zone* zone, js:: dbg->wasmInstanceSources.hasKeyInZone(zone)) { finder.addEdgeTo(w); } } } } -/* static */ void -Debugger::finalize(FreeOp* fop, JSObject* obj) -{ - MOZ_ASSERT(fop->onActiveCooperatingThread()); - - Debugger* dbg = fromJSObject(obj); - if (!dbg) - return; - fop->delete_(dbg); -} - const ClassOps Debugger::classOps_ = { nullptr, /* addProperty */ nullptr, /* delProperty */ nullptr, /* getProperty */ nullptr, /* setProperty */ nullptr, /* enumerate */ nullptr, /* resolve */ nullptr, /* mayResolve */ - Debugger::finalize, + nullptr, /* finalize */ nullptr, /* call */ nullptr, /* hasInstance */ nullptr, /* construct */ Debugger::traceObject }; const Class Debugger::class_ = { "Debugger", JSCLASS_HAS_PRIVATE | - JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT) | - JSCLASS_FOREGROUND_FINALIZE, + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), &Debugger::classOps_ }; static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args, const char* fnname) { JSObject* thisobj = NonNullObject(cx, args.thisv()); if (!thisobj) @@ -3899,17 +3891,18 @@ Debugger::construct(JSContext* cx, unsig return false; RootedNativeObject proto(cx, &v.toObject().as<NativeObject>()); MOZ_ASSERT(proto->getClass() == &Debugger::class_); /* * Make the new Debugger object. Each one has a reference to * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The * rest of the reserved slots are for hooks; they default to undefined. */ - RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto)); + RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &Debugger::class_, proto, + TenuredObject)); if (!obj) return false; for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++) obj->setReservedSlot(slot, proto->getReservedSlot(slot)); obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue()); // Debuggers currently require single threaded execution. A debugger may be // used to debug content in other zone groups, and may be used to observe @@ -11791,18 +11784,17 @@ GarbageCollectionEvent::toJSObject(JSCon { return nullptr; } RootedArrayObject slicesArray(cx, NewDenseEmptyArray(cx)); if (!slicesArray) return nullptr; - bool ignored; // Ignore inconsistencies in process creation timestamp. - TimeStamp originTime = TimeStamp::ProcessCreation(ignored); + TimeStamp originTime = TimeStamp::ProcessCreation(); size_t idx = 0; for (auto range = collections.all(); !range.empty(); range.popFront()) { RootedPlainObject collectionObj(cx, NewBuiltinClassInstance<PlainObject>(cx)); if (!collectionObj) return nullptr; RootedValue start(cx), end(cx);
--- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -584,17 +584,16 @@ class Debugger : private mozilla::Linked const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval, JSTrapStatus& statusp, MutableHandleValue vp); GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v); static void traceObject(JSTracer* trc, JSObject* obj); void trace(JSTracer* trc); void traceForMovingGC(JSTracer* trc); - static void finalize(FreeOp* fop, JSObject* obj); void traceCrossCompartmentEdges(JSTracer* tracer); static const ClassOps classOps_; public: static const Class class_; private:
--- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -201,18 +201,18 @@ DebuggerMemory::drainAllocationsLog(JSCo // we must edit them with great care. Use the queue entry in place, and // then pop and delete together. Debugger::AllocationsLogEntry& entry = dbg->allocationsLog.front(); RootedValue frame(cx, ObjectOrNullValue(entry.frame)); if (!DefineProperty(cx, obj, cx->names().frame, frame)) return false; - bool ignore; - double when = (entry.when - mozilla::TimeStamp::ProcessCreation(ignore)).ToMilliseconds(); + double when = (entry.when - + mozilla::TimeStamp::ProcessCreation()).ToMilliseconds(); RootedValue timestampValue(cx, NumberValue(when)); if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue)) return false; RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className))); if (!className) return false; RootedValue classNameValue(cx, StringValue(className));
--- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -566,22 +566,16 @@ js::EnableContextProfilingStack(JSContex JS_FRIEND_API(void) js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*)) { MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled()); cx->runtime()->geckoProfiler().setEventMarker(fn); } -JS_FRIEND_API(jsbytecode*) -js::ProfilingGetPC(JSContext* cx, JSScript* script, void* ip) -{ - return cx->runtime()->geckoProfiler().ipToPC(script, size_t(ip)); -} - AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) : cx_(cx), previouslyEnabled_(cx->isProfilerSamplingEnabled()), prohibitContextChange_(cx->runtime()) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; if (previouslyEnabled_)
--- a/js/src/vm/GeckoProfiler.h +++ b/js/src/vm/GeckoProfiler.h @@ -183,18 +183,16 @@ class GeckoProfiler stack_[*size_ - 1].setPC(pc); } } /* Enter wasm code */ void beginPseudoJS(const char* string, void* sp); void endPseudoJS() { pop(); } - jsbytecode* ipToPC(JSScript* script, size_t ip) { return nullptr; } - void setProfilingStack(ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, uint32_t max); void setEventMarker(void (*fn)(const char*)); const char* profileString(JSScript* script, JSFunction* maybeFun); void onScriptFinalized(JSScript* script); void markEvent(const char* event); /* meant to be used for testing, not recommended to call in normal code */
--- a/js/src/vm/Initialization.cpp +++ b/js/src/vm/Initialization.cpp @@ -83,18 +83,17 @@ JS::detail::InitWithFailureDiagnostic(bo "how do we have live runtimes before JS_Init?"); PRMJ_NowInit(); // The first invocation of `ProcessCreation` creates a temporary thread // and crashes if that fails, i.e. because we're out of memory. To prevent // that from happening at some later time, get it out of the way during // startup. - bool ignored; - mozilla::TimeStamp::ProcessCreation(ignored); + mozilla::TimeStamp::ProcessCreation(); #ifdef DEBUG CheckMessageParameterCounts(); #endif RETURN_IF_FAIL(js::TlsContext.init()); #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
--- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -1220,16 +1220,18 @@ class NativeObject : public ShapedObject } void setPrivate(void* data) { void** pprivate = &privateRef(numFixedSlots()); privateWriteBarrierPre(pprivate); *pprivate = data; } void setPrivateGCThing(gc::Cell* cell) { + MOZ_ASSERT_IF(IsMarkedBlack(this), + !JS::GCThingIsMarkedGray(JS::GCCellPtr(cell, cell->getTraceKind()))); void** pprivate = &privateRef(numFixedSlots()); privateWriteBarrierPre(pprivate); *pprivate = reinterpret_cast<void*>(cell); privateWriteBarrierPost(pprivate); } void setPrivateUnbarriered(void* data) { void** pprivate = &privateRef(numFixedSlots());
--- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -1222,16 +1222,21 @@ bool JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj) { if (!cloneDataPolicy.isSharedArrayBufferAllowed()) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_NOT_CLONABLE, "SharedArrayBuffer"); return false; } + // We must not transfer buffer pointers cross-process. The cloneDataPolicy + // should guard against this; check that it does. + + MOZ_RELEASE_ASSERT(scope <= JS::StructuredCloneScope::SameProcessDifferentThread); + Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>()); SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject(); if (!refsHeld.acquire(context(), rawbuf)) return false; intptr_t p = reinterpret_cast<intptr_t>(rawbuf); return out.writePair(SCTAG_SHARED_ARRAY_BUFFER_OBJECT, static_cast<uint32_t>(sizeof(p))) && @@ -1958,16 +1963,21 @@ JSStructuredCloneReader::readSharedArray // transmission point, but that's tricky, and it will be a very rare problem // in any case. Just fail at the receiving end if we can't handle it. if (!context()->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_DISABLED); return false; } + // We must not transfer buffer pointers cross-process. The cloneDataPolicy + // in the sender should guard against this; check that it does. + + MOZ_RELEASE_ASSERT(storedScope <= JS::StructuredCloneScope::SameProcessDifferentThread); + // The new object will have a new reference to the rawbuf. if (!rawbuf->addReference()) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO); return false; } JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf);
--- a/js/src/wasm/WasmBuiltins.cpp +++ b/js/src/wasm/WasmBuiltins.cpp @@ -899,51 +899,56 @@ wasm::SymbolicAddressTarget(SymbolicAddr if (!NeedsBuiltinThunk(sym)) return funcPtr; const BuiltinThunks& thunks = *builtinThunks; uint32_t codeRangeIndex = thunks.symbolicAddressToCodeRange[sym]; return thunks.codeBase + thunks.codeRanges[codeRangeIndex].begin(); } -static ABIFunctionType -ToABIFunctionType(const Sig& sig) +static Maybe<ABIFunctionType> +ToBuiltinABIFunctionType(const Sig& sig) { const ValTypeVector& args = sig.args(); ExprType ret = sig.ret(); uint32_t abiType; switch (ret) { case ExprType::F32: abiType = ArgType_Float32 << RetType_Shift; break; case ExprType::F64: abiType = ArgType_Double << RetType_Shift; break; - default: MOZ_CRASH("unhandled ret type"); + default: return Nothing(); } + if ((args.length() + 1) > (sizeof(uint32_t) * 8 / ArgType_Shift)) + return Nothing(); + for (size_t i = 0; i < args.length(); i++) { switch (args[i]) { case ValType::F32: abiType |= (ArgType_Float32 << (ArgType_Shift * (i + 1))); break; case ValType::F64: abiType |= (ArgType_Double << (ArgType_Shift * (i + 1))); break; - default: MOZ_CRASH("unhandled arg type"); + default: return Nothing(); } } - return ABIFunctionType(abiType); + return Some(ABIFunctionType(abiType)); } void* wasm::MaybeGetBuiltinThunk(HandleFunction f, const Sig& sig, JSContext* cx) { MOZ_ASSERT(builtinThunks); if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative) return nullptr; - InlinableNative native = f->jitInfo()->inlinableNative; - ABIFunctionType abiType = ToABIFunctionType(sig); - TypedNative typedNative(native, abiType); + Maybe<ABIFunctionType> abiType = ToBuiltinABIFunctionType(sig); + if (!abiType) + return nullptr; + + TypedNative typedNative(f->jitInfo()->inlinableNative, *abiType); const BuiltinThunks& thunks = *builtinThunks; auto p = thunks.typedNativeToCodeRange.readonlyThreadsafeLookup(typedNative); if (!p) return nullptr; return thunks.codeBase + thunks.codeRanges[p->value()].begin(); }
--- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -135,18 +135,19 @@ ModuleGenerator::initWasm(const CompileA MOZ_ASSERT(!env_->isAsmJS()); metadata_ = js_new<Metadata>(); if (!metadata_) return false; MOZ_ASSERT(!isAsmJS()); - metadata_->debugEnabled = args.debugEnabled && BaselineCanCompile(); - compileMode_ = args.alwaysBaseline || metadata_->debugEnabled + bool canBaseline = BaselineCanCompile(); + metadata_->debugEnabled = args.debugEnabled && canBaseline; + compileMode_ = ((args.alwaysBaseline || metadata_->debugEnabled) && canBaseline) ? CompileMode::Baseline : CompileMode::Ion; // For wasm, the Vectors are correctly-sized and already initialized. numSigs_ = env_->sigs.length(); numTables_ = env_->tables.length();
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -410,17 +410,17 @@ sandbox_finalize(js::FreeOp* fop, JSObje { nsIScriptObjectPrincipal* sop = static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj)); if (!sop) { // sop can be null if CreateSandboxObject fails in the middle. return; } - static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(); + static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(obj); DestroyProtoAndIfaceCache(obj); DeferredFinalize(sop); } static void sandbox_moved(JSObject* obj, const JSObject* old) { // Note that this hook can be called before the private pointer is set. In
--- a/js/xpconnect/src/SandboxPrivate.h +++ b/js/xpconnect/src/SandboxPrivate.h @@ -38,19 +38,19 @@ public: return mPrincipal; } JSObject* GetGlobalJSObject() override { return GetWrapper(); } - void ForgetGlobalObject() + void ForgetGlobalObject(JSObject* obj) { - ClearWrapper(); + ClearWrapper(obj); } virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override { MOZ_CRASH("SandboxPrivate doesn't use DOM bindings!"); } void ObjectMoved(JSObject* obj, const JSObject* old)
--- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3351,18 +3351,17 @@ nsXPCComponents_Utils::AllowCPOWsInAddon return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::Now(double* aRetval) { - bool isInconsistent = false; - TimeStamp start = TimeStamp::ProcessCreation(isInconsistent); + TimeStamp start = TimeStamp::ProcessCreation(); *aRetval = (TimeStamp::Now() - start).ToMilliseconds(); return NS_OK; } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -808,37 +808,44 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp* bool isZoneGC, void* data) { XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance(); if (!self) return; switch (status) { - case JSFINALIZE_GROUP_START: + case JSFINALIZE_GROUP_PREPARE: { MOZ_ASSERT(!self->mDoingFinalization, "bad state"); MOZ_ASSERT(!self->mGCIsRunning, "bad state"); self->mGCIsRunning = true; self->mDoingFinalization = true; + + break; + } + case JSFINALIZE_GROUP_START: + { + MOZ_ASSERT(self->mDoingFinalization, "bad state"); + + MOZ_ASSERT(self->mGCIsRunning, "bad state"); + self->mGCIsRunning = false; + break; } case JSFINALIZE_GROUP_END: { + // Sweep scopes needing cleanup + XPCWrappedNativeScope::KillDyingScopes(); + MOZ_ASSERT(self->mDoingFinalization, "bad state"); self->mDoingFinalization = false; - // Sweep scopes needing cleanup - XPCWrappedNativeScope::KillDyingScopes(); - - MOZ_ASSERT(self->mGCIsRunning, "bad state"); - self->mGCIsRunning = false; - break; } case JSFINALIZE_COLLECTION_END: { MOZ_ASSERT(!self->mGCIsRunning, "bad state"); self->mGCIsRunning = true; // For now we only have one context. Eventually we'll need to @@ -908,17 +915,17 @@ XPCJSRuntime::WeakPointerZonesCallback(J { // Called before each sweeping slice -- after processing any final marking // triggered by barriers -- to clear out any references to things that are // about to be finalized and update any pointers to moved GC things. XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data); self->mWrappedJSMap->UpdateWeakPointersAfterGC(); - XPCWrappedNativeScope::UpdateWeakPointersAfterGC(); + XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC(); } /* static */ void XPCJSRuntime::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data) { // Called immediately after the ZoneGroup weak pointer callback, but only // once for each compartment that is being swept. CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
--- a/js/xpconnect/src/XPCMaps.h +++ b/js/xpconnect/src/XPCMaps.h @@ -142,16 +142,18 @@ public: MOZ_ASSERT(!wrapperInMap || wrapperInMap == wrapper, "About to remove a different wrapper with the same " "nsISupports identity! This will most likely cause serious " "problems!"); #endif mTable.Remove(wrapper->GetIdentityObject()); } + inline void Clear() { mTable.Clear(); } + inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: Native2WrappedNativeMap(); // no implementation @@ -361,16 +363,18 @@ public: } inline void Remove(nsIClassInfo* info) { NS_PRECONDITION(info,"bad param"); mTable.Remove(info); } + inline void Clear() { mTable.Clear(); } + inline uint32_t Count() { return mTable.EntryCount(); } PLDHashTable::Iterator Iter() { return mTable.Iter(); } size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; private: ClassInfo2WrappedNativeProtoMap(); // no implementation
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -564,24 +564,24 @@ XPCWrappedNative::~XPCWrappedNative() Destroy(); } void XPCWrappedNative::Destroy() { mScriptable = nullptr; +#ifdef DEBUG + // Check that this object has already been swept from the map. XPCWrappedNativeScope* scope = GetScope(); if (scope) { Native2WrappedNativeMap* map = scope->GetWrappedNativeMap(); - - // Post-1.9 we should not remove this wrapper from the map if it is - // uninitialized. - map->Remove(this); + MOZ_ASSERT(map->Find(GetIdentityObject()) != this); } +#endif if (mIdentity) { XPCJSRuntime* rt = GetRuntime(); if (rt && rt->GetDoingFinalization()) { DeferredFinalize(mIdentity.forget().take()); } else { mIdentity = nullptr; } @@ -857,17 +857,17 @@ XPCWrappedNative::FlatJSObjectFinalized( } to->SetInterface(nullptr); } nsWrapperCache* cache = nullptr; CallQueryInterface(mIdentity, &cache); if (cache) - cache->ClearWrapper(); + cache->ClearWrapper(mFlatJSObject.unbarrieredGetPtr()); mFlatJSObject = nullptr; mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); MOZ_ASSERT(mIdentity, "bad pointer!"); #ifdef XP_WIN // Try to detect free'd pointer MOZ_ASSERT(*(int*)mIdentity.get() != 0xdddddddd, "bad pointer!");
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp +++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp @@ -98,20 +98,21 @@ XPCWrappedNativeProto::CallPostCreatePro return true; } void XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj) { MOZ_ASSERT(obj == mJSProtoObject, "huh?"); - // Only remove this proto from the map if it is the one in the map. +#ifdef DEBUG + // Check that this object has already been swept from the map. ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap(); - if (map->Find(mClassInfo) == this) - map->Remove(mClassInfo); + MOZ_ASSERT(map->Find(mClassInfo) != this); +#endif GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this); mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime()); } void XPCWrappedNativeProto::JSProtoObjectMoved(JSObject* obj, const JSObject* old)
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -474,19 +474,16 @@ XPCWrappedNativeScope::~XPCWrappedNative // XXX we should assert that we are dead or that xpconnect has shutdown // XXX might not want to do this at xpconnect shutdown time??? mComponents = nullptr; if (mXrayExpandos.initialized()) mXrayExpandos.destroy(); JSContext* cx = dom::danger::GetJSContext(); - mContentXBLScope.finalize(cx); - for (size_t i = 0; i < mAddonScopes.Length(); i++) - mAddonScopes[i].finalize(cx); mGlobalJSObject.finalize(cx); } // static void XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc) { // Do JS::TraceEdge for all wrapped natives with external references, as @@ -527,58 +524,118 @@ XPCWrappedNativeScope::SuspectAllWrapper for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront()) SuspectDOMExpandos(r.front().unbarrieredGet(), cb); } } } // static void -XPCWrappedNativeScope::UpdateWeakPointersAfterGC() +XPCWrappedNativeScope::UpdateWeakPointersInAllScopesAfterGC() { // If this is called from the finalization callback in JSGC_MARK_END then // JSGC_FINALIZE_END must always follow it calling // FinishedFinalizationPhaseOfGC and clearing gDyingScopes in // KillDyingScopes. MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END"); - XPCWrappedNativeScope* prev = nullptr; - XPCWrappedNativeScope* cur = gScopes; + XPCWrappedNativeScope** scopep = &gScopes; + while (*scopep) { + XPCWrappedNativeScope* cur = *scopep; + cur->UpdateWeakPointersAfterGC(); + if (cur->mGlobalJSObject) { + scopep = &cur->mNext; + } else { + // The scope's global is dead so move it to the dying scopes list. + *scopep = cur->mNext; + cur->mNext = gDyingScopes; + gDyingScopes = cur; + } + } +} - while (cur) { - // Sweep waivers. - if (cur->mWaiverWrapperMap) - cur->mWaiverWrapperMap->Sweep(); +static inline void +AssertSameCompartment(DebugOnly<JSCompartment*>& comp, JSObject* obj) +{ + MOZ_ASSERT_IF(obj, js::GetObjectCompartment(obj) == comp); +} - XPCWrappedNativeScope* next = cur->mNext; +static inline void +AssertSameCompartment(DebugOnly<JSCompartment*>& comp, const JS::ObjectPtr& obj) +{ +#ifdef DEBUG + AssertSameCompartment(comp, obj.unbarrieredGet()); +#endif +} - if (cur->mContentXBLScope) - cur->mContentXBLScope.updateWeakPointerAfterGC(); - for (size_t i = 0; i < cur->mAddonScopes.Length(); i++) - cur->mAddonScopes[i].updateWeakPointerAfterGC(); +void +XPCWrappedNativeScope::UpdateWeakPointersAfterGC() +{ + // Sweep waivers. + if (mWaiverWrapperMap) + mWaiverWrapperMap->Sweep(); + + if (!js::IsObjectZoneSweepingOrCompacting(mGlobalJSObject.unbarrieredGet())) + return; - // Check for finalization of the global object or update our pointer if - // it was moved. - if (cur->mGlobalJSObject) { - cur->mGlobalJSObject.updateWeakPointerAfterGC(); - if (!cur->mGlobalJSObject) { - // Move this scope from the live list to the dying list. - if (prev) - prev->mNext = next; - else - gScopes = next; - cur->mNext = gDyingScopes; - gDyingScopes = cur; - cur = nullptr; - } - } + // Update our pointer to the global object in case it was moved or + // finalized. + mGlobalJSObject.updateWeakPointerAfterGC(); + if (!mGlobalJSObject) { + JSContext* cx = dom::danger::GetJSContext(); + mContentXBLScope.finalize(cx); + for (size_t i = 0; i < mAddonScopes.Length(); i++) + mAddonScopes[i].finalize(cx); + GetWrappedNativeMap()->Clear(); + mWrappedNativeProtoMap->Clear(); + return; + } + + DebugOnly<JSCompartment*> comp = + js::GetObjectCompartment(mGlobalJSObject.unbarrieredGet()); - if (cur) - prev = cur; - cur = next; +#ifdef DEBUG + // These are traced, so no updates are necessary. + if (mContentXBLScope) { + JSObject* prev = mContentXBLScope.unbarrieredGet(); + mContentXBLScope.updateWeakPointerAfterGC(); + MOZ_ASSERT(prev == mContentXBLScope.unbarrieredGet()); + AssertSameCompartment(comp, mContentXBLScope); + } + for (size_t i = 0; i < mAddonScopes.Length(); i++) { + JSObject* prev = mAddonScopes[i].unbarrieredGet(); + mAddonScopes[i].updateWeakPointerAfterGC(); + MOZ_ASSERT(prev == mAddonScopes[i].unbarrieredGet()); + AssertSameCompartment(comp, mAddonScopes[i]); + } +#endif + + // Sweep mWrappedNativeMap for dying flat JS objects. Moving has already + // been handled by XPCWrappedNative::FlatJSObjectMoved. + for (auto iter = GetWrappedNativeMap()->Iter(); !iter.Done(); iter.Next()) { + auto entry = static_cast<Native2WrappedNativeMap::Entry*>(iter.Get()); + XPCWrappedNative* wrapper = entry->value; + JSObject* obj = wrapper->GetFlatJSObjectPreserveColor(); + JS_UpdateWeakPointerAfterGCUnbarriered(&obj); + MOZ_ASSERT(!obj || obj == wrapper->GetFlatJSObjectPreserveColor()); + AssertSameCompartment(comp, obj); + if (!obj) + iter.Remove(); + } + + // Sweep mWrappedNativeProtoMap for dying prototype JSObjects. Moving has + // already been handled by XPCWrappedNativeProto::JSProtoObjectMoved. + for (auto i = mWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) { + auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(i.Get()); + JSObject* obj = entry->value->GetJSProtoObjectPreserveColor(); + JS_UpdateWeakPointerAfterGCUnbarriered(&obj); + AssertSameCompartment(comp, obj); + MOZ_ASSERT(!obj || obj == entry->value->GetJSProtoObjectPreserveColor()); + if (!obj) + i.Remove(); } } // static void XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs() { for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -957,16 +957,19 @@ public: static void SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb); static void SweepAllWrappedNativeTearOffs(); static void + UpdateWeakPointersInAllScopesAfterGC(); + + void UpdateWeakPointersAfterGC(); static void KillDyingScopes(); static void DebugDumpAllScopes(int16_t depth); @@ -1439,16 +1442,19 @@ public: GetScope() const {return mScope;} XPCJSRuntime* GetRuntime() const {return mScope->GetRuntime();} JSObject* GetJSProtoObject() const { return mJSProtoObject; } + JSObject* + GetJSProtoObjectPreserveColor() const { return mJSProtoObject.unbarrieredGet(); } + nsIClassInfo* GetClassInfo() const {return mClassInfo;} XPCNativeSet* GetSet() const {return mSet;} nsIXPCScriptable* GetScriptable() const { return mScriptable; }
--- a/layout/forms/test/test_bug536567_perwindowpb.html +++ b/layout/forms/test/test_bug536567_perwindowpb.html @@ -165,17 +165,20 @@ function testOnWindow(aIsPrivate, aCallb win.setTimeout(function() { win.gBrowser.loadURI(contentPage); }, 0); }); } MockFilePicker.showCallback = function(filepicker) { var test = tests[testIndex]; var returned = -1; for (var i = 0; i < dirs.length; i++) { - if (dirs[i].path == MockFilePicker.displayDirectory.path) { + var dir = MockFilePicker.displayDirectory + ? MockFilePicker.displayDirectory + : Services.dirsvc.get(MockFilePicker.displaySpecialDirectory, Ci.nsILocalFile); + if (dirs[i].path == dir.path) { returned = i; break; } } if (test[1] == -1) { ok(false, "We should never get an unknown directory back"); } else { is(returned, test[1], 'test ' + testIndex);
--- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1787,16 +1787,20 @@ nsImageFrame::BuildDisplayList(nsDisplay // decoded yet. And we are not going to ask the image to draw, so this // may be the only chance to tell it that it should decode. if (currentRequest) { uint32_t status = 0; currentRequest->GetImageStatus(&status); if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) { MaybeDecodeForPredictedSize(); } + // Increase loading priority if the image is ready to be displayed. + if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)){ + currentRequest->BoostPriority(imgIRequest::CATEGORY_DISPLAY); + } } } else { aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayImage(aBuilder, this, mImage, mPrevImage)); // If we were previously displaying an icon, we're not anymore if (mDisplayingIcon) { gIconLoad->RemoveIconObserver(this);
--- a/layout/style/nsCSSAnonBoxes.cpp +++ b/layout/style/nsCSSAnonBoxes.cpp @@ -51,16 +51,17 @@ bool nsCSSAnonBoxes::IsAnonBox(nsIAtom * return nsAtomListUtils::IsMember(aAtom, CSSAnonBoxes_info, ArrayLength(CSSAnonBoxes_info)); } #ifdef MOZ_XUL /* static */ bool nsCSSAnonBoxes::IsTreePseudoElement(nsIAtom* aPseudo) { + MOZ_ASSERT(nsCSSAnonBoxes::IsAnonBox(aPseudo)); return StringBeginsWith(nsDependentAtomString(aPseudo), NS_LITERAL_STRING(":-moz-tree-")); } #endif /* static*/ nsCSSAnonBoxes::NonInheriting nsCSSAnonBoxes::NonInheritingTypeForPseudoTag(nsIAtom* aPseudo) {
--- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -2167,17 +2167,17 @@ nsStyleSet::ResolveNonInheritingAnonymou nullptr, eNoFlags); cache = retval; return retval.forget(); } #ifdef MOZ_XUL already_AddRefed<nsStyleContext> nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement, - nsIAtom* aPseudoTag, + nsICSSAnonBoxPseudo* aPseudoTag, nsStyleContext* aParentContext, nsICSSPseudoComparator* aComparator) { NS_ENSURE_FALSE(mInShutdown, nullptr); NS_ASSERTION(aPseudoTag, "must have pseudo tag"); NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag), "Unexpected pseudo");
--- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -293,17 +293,17 @@ class nsStyleSet final ResolveNonInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag); #ifdef MOZ_XUL // Get a style context for a XUL tree pseudo. aPseudoTag is the // pseudo-tag to use and must be non-null. aParentContent must be // non-null. aComparator must be non-null. already_AddRefed<nsStyleContext> ResolveXULTreePseudoStyle(mozilla::dom::Element* aParentElement, - nsIAtom* aPseudoTag, + nsICSSAnonBoxPseudo* aPseudoTag, nsStyleContext* aParentContext, nsICSSPseudoComparator* aComparator); #endif // Append all the currently-active font face rules to aArray. Return // true for success and false for failure. bool AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray);
--- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -1052,17 +1052,17 @@ nsTreeBodyFrame::GetCellAt(int32_t aX, i // Check if the coordinates are above our visible space. if (point.y < 0) { *aRow = -1; return NS_OK; } nsTreeColumn* col; - nsIAtom* child; + nsICSSAnonBoxPseudo* child; GetCellAt(point.x, point.y, aRow, &col, &child); if (col) { NS_ADDREF(*aCol = col); if (child == nsCSSAnonBoxes::moztreecell) aChildElt.AssignLiteral("cell"); else if (child == nsCSSAnonBoxes::moztreetwisty) aChildElt.AssignLiteral("twisty"); @@ -1479,17 +1479,17 @@ nsTreeBodyFrame::AdjustForCellText(nsAut aTextRect.x += (aTextRect.width - width) / 2; } break; } aTextRect.width = width; } -nsIAtom* +nsICSSAnonBoxPseudo* nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, int32_t aRowIndex, nsTreeColumn* aColumn) { NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed"); // Obtain the properties for our cell. PrefillPropertyArray(aRowIndex, aColumn); @@ -1629,17 +1629,18 @@ nsTreeBodyFrame::GetItemWithinCellAt(nsc if (aX >= textRect.x && aX < textRect.x + textRect.width) return nsCSSAnonBoxes::moztreecelltext; else return nsCSSAnonBoxes::moztreecell; } void nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, - nsTreeColumn** aCol, nsIAtom** aChildElt) + nsTreeColumn** aCol, + nsICSSAnonBoxPseudo** aChildElt) { *aCol = nullptr; *aChildElt = nullptr; *aRow = GetRowAt(aX, aY); if (*aRow < 0) return; @@ -2523,17 +2524,17 @@ nsTreeBodyFrame::GetCursor(const nsPoint nsIFrame::Cursor& aCursor) { // Check the GetScriptHandlingObject so we don't end up running code when // the document is a zombie. bool dummy; if (mView && GetContent()->GetComposedDoc()->GetScriptHandlingObject(dummy)) { int32_t row; nsTreeColumn* col; - nsIAtom* child; + nsICSSAnonBoxPseudo* child; GetCellAt(aPoint.x, aPoint.y, &row, &col, &child); if (child) { // Our scratch array is already prefilled. nsStyleContext* childContext = GetPseudoStyleContext(child); FillCursorInformationFromStyle(childContext->StyleUserInterface(), aCursor); @@ -4470,17 +4471,17 @@ nsTreeBodyFrame::ThumbMoved(nsScrollbarF } if (weakFrame.IsAlive()) { UpdateScrollbars(parts); } } // The style cache. nsStyleContext* -nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement) +nsTreeBodyFrame::GetPseudoStyleContext(nsICSSAnonBoxPseudo* aPseudoElement) { return mStyleCache.GetStyleContext(this, PresContext(), mContent, mStyleContext, aPseudoElement, mScratchArray); } // Our comparator for resolving our complex pseudos bool
--- a/layout/xul/tree/nsTreeBodyFrame.h +++ b/layout/xul/tree/nsTreeBodyFrame.h @@ -313,23 +313,25 @@ protected: void AdjustForCellText(nsAutoString& aText, int32_t aRowIndex, nsTreeColumn* aColumn, nsRenderingContext& aRenderingContext, nsFontMetrics& aFontMetrics, nsRect& aTextRect); // A helper used when hit testing. - nsIAtom* GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, - int32_t aRowIndex, nsTreeColumn* aColumn); + nsICSSAnonBoxPseudo* GetItemWithinCellAt(nscoord aX, + const nsRect& aCellRect, + int32_t aRowIndex, + nsTreeColumn* aColumn); // An internal hit test. aX and aY are expected to be in twips in the // coordinate system of this frame. void GetCellAt(nscoord aX, nscoord aY, int32_t* aRow, nsTreeColumn** aCol, - nsIAtom** aChildElt); + nsICSSAnonBoxPseudo** aChildElt); // Retrieve the area for the twisty for a cell. nsITheme* GetTwistyRect(int32_t aRowIndex, nsTreeColumn* aColumn, nsRect& aImageRect, nsRect& aTwistyRect, nsPresContext* aPresContext, nsStyleContext* aTwistyContext); @@ -357,17 +359,17 @@ protected: // Calculates our width/height once border and padding have been removed. void CalcInnerBox(); // Calculate the total width of our scrollable portion nscoord CalcHorzWidth(const ScrollParts& aParts); // Looks up a style context in the style cache. On a cache miss we resolve // the pseudo-styles passed in and place them into the cache. - nsStyleContext* GetPseudoStyleContext(nsIAtom* aPseudoElement); + nsStyleContext* GetPseudoStyleContext(nsICSSAnonBoxPseudo* aPseudoElement); // Retrieves the scrollbars and scrollview relevant to this treebody. We // traverse the frame tree under our base element, in frame order, looking // for the first relevant vertical scrollbar, horizontal scrollbar, and // scrollable frame (with associated content and scrollable view). These // are all volatile and should not be retained. ScrollParts GetScrollParts();
--- a/layout/xul/tree/nsTreeStyleCache.cpp +++ b/layout/xul/tree/nsTreeStyleCache.cpp @@ -31,19 +31,21 @@ nsTreeStyleCache::Transition::Hash() con // The style context cache impl nsStyleContext* nsTreeStyleCache::GetStyleContext(nsICSSPseudoComparator* aComparator, nsPresContext* aPresContext, nsIContent* aContent, nsStyleContext* aContext, - nsIAtom* aPseudoElement, + nsICSSAnonBoxPseudo* aPseudoElement, const AtomArray & aInputWord) { + MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoElement)); + uint32_t count = aInputWord.Length(); // Go ahead and init the transition table. if (!mTransitionTable) { // Automatic miss. Build the table mTransitionTable = new TransitionTable(); }
--- a/layout/xul/tree/nsTreeStyleCache.h +++ b/layout/xul/tree/nsTreeStyleCache.h @@ -35,17 +35,17 @@ public: mCache = nullptr; mNextState = 0; } nsStyleContext* GetStyleContext(nsICSSPseudoComparator* aComparator, nsPresContext* aPresContext, nsIContent* aContent, nsStyleContext* aContext, - nsIAtom* aPseudoElement, + nsICSSAnonBoxPseudo* aPseudoElement, const AtomArray & aInputWord); protected: typedef uint32_t DFAState; class Transition final { public:
--- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -259,17 +259,17 @@ </intent-filter> </receiver> #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in #ifdef MOZ_ANDROID_SEARCH_ACTIVITY #include ../search/manifests/SearchAndroidManifest_activities.xml.in #endif -#if MOZ_CRASHREPORTER +#ifdef MOZ_CRASHREPORTER <activity android:name="org.mozilla.gecko.CrashReporter" android:process="@ANDROID_PACKAGE_NAME@.CrashReporter" android:label="@string/crash_reporter_title" android:icon="@drawable/crash_reporter" android:theme="@style/Gecko" android:exported="false" android:excludeFromRecents="true"> <intent-filter>
--- a/mobile/android/base/AppConstants.java.in +++ b/mobile/android/base/AppConstants.java.in @@ -186,17 +186,17 @@ public class AppConstants { public static final boolean MOZ_TELEMETRY_REPORTING = //#ifdef MOZ_TELEMETRY_REPORTING true; //#else false; //#endif public static final boolean MOZ_CRASHREPORTER = -//#if MOZ_CRASHREPORTER +//#ifdef MOZ_CRASHREPORTER true; //#else false; //#endif public static final boolean MOZ_DATA_REPORTING = //#ifdef MOZ_DATA_REPORTING true;
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -3,38 +3,16 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # We call mach -> Make -> gradle -> mach, which races to find and # create .mozconfig files and to generate targets. ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE .NOTPARALLEL: endif -MOZ_BUILDID := $(shell awk '{print $$3}' $(DEPTH)/buildid.h) - -# Set the appropriate version code, based on the existance of the -# MOZ_APP_ANDROID_VERSION_CODE variable. -ifdef MOZ_APP_ANDROID_VERSION_CODE - ANDROID_VERSION_CODE:=$(MOZ_APP_ANDROID_VERSION_CODE) -else - ANDROID_VERSION_CODE:=$(shell $(PYTHON) \ - $(topsrcdir)/python/mozbuild/mozbuild/android_version_code.py \ - --verbose \ - --with-android-cpu-arch=$(ANDROID_CPU_ARCH) \ - $(if $(MOZ_ANDROID_MIN_SDK_VERSION),--with-android-min-sdk=$(MOZ_ANDROID_MIN_SDK_VERSION)) \ - $(if $(MOZ_ANDROID_MAX_SDK_VERSION),--with-android-max-sdk=$(MOZ_ANDROID_MAX_SDK_VERSION)) \ - $(MOZ_BUILDID)) -endif - -DEFINES += \ - -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \ - -DMOZ_ANDROID_SHARED_ID="$(MOZ_ANDROID_SHARED_ID)" \ - -DMOZ_BUILDID=$(MOZ_BUILDID) \ - $(NULL) - GARBAGE += \ classes.dex \ gecko.ap_ \ res/values/strings.xml \ res/raw/browsersearch.json \ res/raw/suggestedsites.json \ .aapt.deps \ GeneratedJNINatives.h \ @@ -304,17 +282,17 @@ classycle_jar := $(topsrcdir)/mobile/and -Xmx512m -Xms128m \ -jar $(ANDROID_SDK_ROOT)/tools/proguard/lib/proguard.jar \ @$(proguard_config_dir)/proguard.cfg \ -optimizationpasses $(PROGUARD_PASSES) \ -injars $(subst ::,:,$(all_jars_classpath)):bundled-jars-nodebug \ -outjars jars-proguarded \ -libraryjars $(library_jars) -ANNOTATION_PROCESSOR_JAR_FILES := $(DEPTH)/build/annotationProcessors/annotationProcessors.jar +ANNOTATION_PROCESSOR_JAR_FILES := $(abspath $(DEPTH)/build/annotationProcessors/annotationProcessors.jar) # This annotation processing step also generates # GeneratedJNIWrappers.h and GeneratedJNINatives.h GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) $(GECKOVIEW_JARS) $(JAVA) -classpath $(geckoview_jars_classpath):$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) \ org.mozilla.gecko.annotationProcessors.AnnotationProcessor \ Generated $(GECKOVIEW_JARS) @@ -370,17 +348,17 @@ android_res_files := $(filter-out $(not_ # suggestedsites.json. The trailing semi-colon defines an empty # recipe: defining no recipe at all causes Make to treat the target # differently, in a way that defeats our dependencies. res/values/strings.xml: .locales.deps ; res/raw/browsersearch.json: .locales.deps ; res/raw/suggestedsites.json: .locales.deps ; all_resources = \ - $(DEPTH)/mobile/android/base/AndroidManifest.xml \ + AndroidManifest.xml \ $(android_res_files) \ $(ANDROID_GENERATED_RESFILES) \ $(NULL) # All of generated/org/mozilla/gecko/R.java, gecko.ap_, and R.txt are # produced by aapt; this saves aapt invocations. The trailing # semi-colon defines an empty recipe; defining no recipe at all causes # Make to treat the target differently, in a way that defeats our @@ -474,18 +452,18 @@ endef # .aapt.deps: $(all_resources) $(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./)) ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE .aapt.nodeps: FORCE cp $(gradle_dir)/app/intermediates/res/resources-automation-debug.ap_ gecko-nodeps.ap_ else -# .aapt.nodeps: $(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE -$(eval $(call aapt_command,.aapt.nodeps,$(DEPTH)/mobile/android/base/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) +# .aapt.nodeps: AndroidManifest.xml FORCE +$(eval $(call aapt_command,.aapt.nodeps,AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/)) endif # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk update-generated-wrappers: @cp $(CURDIR)/GeneratedJNIWrappers.cpp \ $(CURDIR)/GeneratedJNIWrappers.h \ @@ -510,17 +488,17 @@ update-fennec-wrappers: $(MAKE) -C ../../../faster $(MAKE) -C ../installer stage-package $(MKDIR) -p $(@D) rsync --update $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) $@ $(RM) $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) # Targets built very early during a Gradle build. gradle-targets: $(foreach f,$(constants_PP_JAVAFILES),$(f)) -gradle-targets: $(DEPTH)/mobile/android/base/AndroidManifest.xml +gradle-targets: AndroidManifest.xml gradle-targets: $(ANDROID_GENERATED_RESFILES) ifndef MOZILLA_OFFICIAL # Local developers update omni.ja during their builds. There's a # chicken-and-egg problem here. gradle-omnijar: $(abspath $(DIST)/fennec/$(OMNIJAR_NAME)) else # In automation, omni.ja is built only during packaging.
--- a/mobile/android/base/generate_build_config.py +++ b/mobile/android/base/generate_build_config.py @@ -23,16 +23,17 @@ from __future__ import ( from collections import defaultdict import os import sys import buildconfig from mozbuild import preprocessor +from mozbuild.android_version_code import android_version_code def _defines(): CONFIG = defaultdict(lambda: None) CONFIG.update(buildconfig.substs) DEFINES = dict(buildconfig.defines) for var in ('MOZ_ANDROID_ACTIVITY_STREAM' @@ -40,16 +41,17 @@ def _defines(): 'MOZ_ANDROID_BEAM', 'MOZ_ANDROID_CUSTOM_TABS', 'MOZ_ANDROID_DOWNLOADS_INTEGRATION', 'MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE', 'MOZ_ANDROID_EXCLUDE_FONTS', 'MOZ_ANDROID_GCM', 'MOZ_ANDROID_MLS_STUMBLER', 'MOZ_ANDROID_SEARCH_ACTIVITY', + 'MOZ_CRASHREPORTER', 'MOZ_DEBUG', 'MOZ_INSTALL_TRACKING', 'MOZ_LOCALE_SWITCHER', 'MOZ_NATIVE_DEVICES', 'MOZ_SWITCHBOARD'): if CONFIG[var]: DEFINES[var] = 1 @@ -57,28 +59,28 @@ def _defines(): 'MOZ_PKG_SPECIAL', 'MOZ_UPDATER'): if CONFIG[var]: DEFINES[var] = CONFIG[var] for var in ('ANDROID_CPU_ARCH', 'ANDROID_PACKAGE_NAME', 'GRE_MILESTONE', + 'MOZ_ANDROID_SHARED_ID', 'MOZ_ANDROID_APPLICATION_CLASS', 'MOZ_ANDROID_BROWSER_INTENT_CLASS', 'MOZ_ANDROID_SEARCH_INTENT_CLASS', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME', 'MOZ_APP_ID', 'MOZ_APP_NAME', 'MOZ_APP_UA_NAME', 'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'MOZ_CHILD_PROCESS_NAME', - 'MOZ_CRASHREPORTER', 'MOZ_MOZILLA_API_KEY', 'MOZ_UPDATE_CHANNEL', 'OMNIJAR_NAME', 'OS_TARGET', 'TARGET_XPCOM_ABI'): DEFINES[var] = CONFIG[var] # Mangle our package name to avoid Bug 750548. @@ -96,18 +98,40 @@ def _defines(): # It's okay to use MOZ_ADJUST_SDK_KEY here because this doesn't # leak the value to build logs. if CONFIG['MOZ_INSTALL_TRACKING']: DEFINES['MOZ_INSTALL_TRACKING_ADJUST_SDK_APP_TOKEN'] = CONFIG['MOZ_ADJUST_SDK_KEY'] DEFINES['MOZ_BUILDID'] = open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2] + # Set the appropriate version code if not set by MOZ_APP_ANDROID_VERSION_CODE. + if CONFIG.get('MOZ_APP_ANDROID_VERSION_CODE'): + DEFINES['ANDROID_VERSION_CODE'] = \ + CONFIG.get('MOZ_APP_ANDROID_VERSION_CODE') + else: + min_sdk = int(CONFIG.get('MOZ_ANDROID_MIN_SDK_VERSION') or '0') or None + max_sdk = int(CONFIG.get('MOZ_ANDROID_MAX_SDK_VERSION') or '0') or None + DEFINES['ANDROID_VERSION_CODE'] = android_version_code( + buildid=DEFINES['MOZ_BUILDID'], + cpu_arch=CONFIG['ANDROID_CPU_ARCH'], + min_sdk=min_sdk, + max_sdk=max_sdk) + return DEFINES def generate_java(output_file, input_filename): includes = preprocessor.preprocess(includes=[input_filename], defines=_defines(), output=output_file, - marker="//#") + marker='//#') includes.add(os.path.join(buildconfig.topobjdir, 'buildid.h')) return includes + + +def generate_android_manifest(output_file, input_filename): + includes = preprocessor.preprocess(includes=[input_filename], + defines=_defines(), + output=output_file, + marker='#') + includes.add(os.path.join(buildconfig.topobjdir, 'buildid.h')) + return includes
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -114,28 +114,32 @@ with Files('resources/menu/*activitystre with Files('resources/menu/browsersearch_contextmenu.xml'): BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen') DIRS += ['locales'] GENERATED_FILES += [ '../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java', + 'AndroidManifest.xml', 'generated/preprocessed/org/mozilla/gecko/AdjustConstants.java', 'generated/preprocessed/org/mozilla/gecko/AppConstants.java', ] w = GENERATED_FILES['../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java'] w.script = 'generate_build_config.py:generate_java' w.inputs += ['../geckoview/BuildConfig.java.in'] x = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AdjustConstants.java'] x.script = 'generate_build_config.py:generate_java' x.inputs += ['AdjustConstants.java.in'] y = GENERATED_FILES['generated/preprocessed/org/mozilla/gecko/AppConstants.java'] y.script = 'generate_build_config.py:generate_java' y.inputs += ['AppConstants.java.in'] +z = GENERATED_FILES['AndroidManifest.xml'] +z.script = 'generate_build_config.py:generate_android_manifest' +z.inputs += ['AndroidManifest.xml.in'] include('android-services.mozbuild') geckoview_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/main/' geckoview_thirdparty_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/' thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/' constants_jar = add_java_jar('constants') @@ -1224,55 +1228,16 @@ if CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRE # If you change this, also change its equivalent in mobile/android/bouncer. if not CONFIG['MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER']: # If we are packaging the bouncer, it will have the distribution, so don't put # it in the main APK as well. ANDROID_ASSETS_DIRS += [ '%' + CONFIG['MOZ_ANDROID_DISTRIBUTION_DIRECTORY'] + '/assets', ] -# We do not expose MOZ_ADJUST_SDK_KEY here because that # would leak the value -# to build logs. Instead we expose the token quietly where appropriate in -# Makefile.in. -for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_DEBUG', - 'MOZ_ANDROID_SEARCH_ACTIVITY', 'MOZ_NATIVE_DEVICES', 'MOZ_ANDROID_MLS_STUMBLER', - 'MOZ_ANDROID_DOWNLOADS_INTEGRATION', 'MOZ_INSTALL_TRACKING', - 'MOZ_ANDROID_GCM', 'MOZ_ANDROID_EXCLUDE_FONTS', 'MOZ_LOCALE_SWITCHER', - 'MOZ_ANDROID_BEAM', 'MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE', - 'MOZ_SWITCHBOARD', 'MOZ_ANDROID_CUSTOM_TABS', - 'MOZ_ANDROID_ACTIVITY_STREAM'): - if CONFIG[var]: - DEFINES[var] = 1 - -for var in ('MOZ_UPDATER', 'MOZ_PKG_SPECIAL', 'MOZ_ANDROID_GCM_SENDERID'): - if CONFIG[var]: - DEFINES[var] = CONFIG[var] - -for var in ('ANDROID_PACKAGE_NAME', 'ANDROID_CPU_ARCH', - 'GRE_MILESTONE', 'MOZ_APP_BASENAME', 'MOZ_MOZILLA_API_KEY', - 'MOZ_APP_DISPLAYNAME', 'MOZ_APP_UA_NAME', 'MOZ_APP_ID', 'MOZ_APP_NAME', - 'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'MOZ_CHILD_PROCESS_NAME', - 'MOZ_ANDROID_APPLICATION_CLASS', 'MOZ_ANDROID_BROWSER_INTENT_CLASS', 'MOZ_ANDROID_SEARCH_INTENT_CLASS', - 'MOZ_CRASHREPORTER', 'MOZ_UPDATE_CHANNEL', 'OMNIJAR_NAME', - 'OS_TARGET', 'TARGET_XPCOM_ABI'): - DEFINES[var] = CONFIG[var] - -# Mangle our package name to avoid Bug 750548. -DEFINES['MANGLED_ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'].replace('fennec', 'f3nn3c') -DEFINES['MOZ_APP_ABI'] = CONFIG['TARGET_XPCOM_ABI'] -if not CONFIG['COMPILE_ENVIRONMENT']: - # These should really come from the included binaries, but that's not easy. - DEFINES['MOZ_APP_ABI'] = 'arm-eabi-gcc3' # Observe quote differences here ... - DEFINES['TARGET_XPCOM_ABI'] = '"arm-eabi-gcc3"' # ... and here. - -if '-march=armv7' in CONFIG['OS_CFLAGS']: - DEFINES['MOZ_MIN_CPU_VERSION'] = 7 -else: - DEFINES['MOZ_MIN_CPU_VERSION'] = 5 - if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']: # The Search Activity is mostly independent of Fennec proper, but # it does depend on Geckoview. Therefore, we build it as a jar # that depends on the Geckoview jars. search_source_dir = SRCDIR + '/../search' include('../search/search_activity_sources.mozbuild') search_activity = add_java_jar('search-activity') @@ -1286,25 +1251,19 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY'] 'gecko-R.jar', 'gecko-browser.jar', 'gecko-mozglue.jar', 'gecko-thirdparty.jar', 'gecko-util.jar', 'gecko-view.jar', ] +DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'] FINAL_TARGET_PP_FILES += ['package-name.txt.in'] -DEFINES['OBJDIR'] = OBJDIR -DEFINES['TOPOBJDIR'] = TOPOBJDIR - -OBJDIR_PP_FILES.mobile.android.base += [ - 'AndroidManifest.xml.in', -] - gvjar.sources += ['generated/org/mozilla/gecko/' + x for x in [ 'IGeckoEditableChild.java', 'IGeckoEditableParent.java', 'media/ICodec.java', 'media/ICodecCallbacks.java', 'media/IMediaDrmBridge.java', 'media/IMediaDrmBridgeCallbacks.java', 'media/IMediaManager.java',
--- a/mobile/android/components/FilePicker.js +++ b/mobile/android/components/FilePicker.js @@ -19,16 +19,17 @@ function FilePicker() { FilePicker.prototype = { _mimeTypeFilter: 0, _extensionsFilter: "", _defaultString: "", _domWin: null, _domFile: null, _defaultExtension: null, _displayDirectory: null, + _displaySpecialDirectory: null, _filePath: null, _promptActive: false, _filterIndex: 0, _addToRecentDocs: false, _title: "", init: function(aParent, aTitle, aMode) { this._domWin = aParent; @@ -117,16 +118,24 @@ FilePicker.prototype = { get displayDirectory() { return this._displayDirectory; }, set displayDirectory(dir) { this._displayDirectory = dir; }, + get displaySpecialDirectory() { + return this._displaySpecialDirectory; + }, + + set displaySpecialDirectory(dir) { + this._displaySpecialDirectory = dir; + }, + get file() { if (!this._filePath) { return null; } return new FileUtils.File(this._filePath); },
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js +++ b/mobile/android/components/geckoview/GeckoViewPrompt.js @@ -1012,16 +1012,23 @@ FilePickerDelegate.prototype = { get displayDirectory() { return null; }, set displayDirectory(aValue) { }, + get displaySpecialDirectory() { + return ""; + }, + + set displaySpecialDirectory(aValue) { + }, + get addToRecentDocs() { return false; }, set addToRecentDocs(aValue) { }, get okButtonLabel() {
--- a/mobile/android/geckoview/proguard-rules.txt +++ b/mobile/android/geckoview/proguard-rules.txt @@ -3,17 +3,17 @@ # Preserve all annotations. -keepattributes *Annotation* # Preserve all public classes, and their public and protected fields and # methods. -keep public class * { - public protected *; + static public protected *; } # Preserve all .class method names. -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java @@ -226,17 +226,18 @@ public class GeckoView extends LayerView } else if ("GeckoView:PageStop".equals(event)) { if (mProgressListener != null) { mProgressListener.onPageStop(GeckoView.this, message.getBoolean("success")); } } else if ("GeckoView:Prompt".equals(event)) { handlePromptEvent(GeckoView.this, message, callback); } else if ("GeckoView:SecurityChanged".equals(event)) { if (mProgressListener != null) { - mProgressListener.onSecurityChange(GeckoView.this, message.getInt("status")); + int state = message.getInt("status") & ProgressListener.STATE_ALL; + mProgressListener.onSecurityChange(GeckoView.this, state); } } } } protected Window mWindow; private boolean mStateSaved; private final Listener mListener = new Listener(); @@ -252,38 +253,50 @@ public class GeckoView extends LayerView // TODO: Convert custom attributes to GeckoViewSettings init(context, null); } public GeckoView(Context context, final GeckoViewSettings settings) { super(context); final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher()); + init(context, settings); + } + + public GeckoView(Context context, AttributeSet attrs, final GeckoViewSettings settings) { + super(context, attrs); + + final GeckoViewSettings newSettings = new GeckoViewSettings(settings, getEventDispatcher()); init(context, newSettings); } - private void init(final Context context, final GeckoViewSettings settings) { + public static final void preload(Context context) { + final GeckoProfile profile = GeckoProfile.get( + context.getApplicationContext()); + if (GeckoAppShell.getApplicationContext() == null) { GeckoAppShell.setApplicationContext(context.getApplicationContext()); } + if (GeckoThread.initMainProcess(profile, + /* args */ null, + /* debugging */ false)) { + GeckoThread.launch(); + } + } + + private void init(final Context context, final GeckoViewSettings settings) { // Set the GeckoInterface if the context is an activity and the // GeckoInterface has not already been set if (context instanceof Activity && getGeckoInterface() == null) { setGeckoInterface(new BaseGeckoInterface(context)); GeckoAppShell.setContextGetter(this); } - final GeckoProfile profile = GeckoProfile.get( - context.getApplicationContext()); - if (GeckoThread.initMainProcess(profile, - /* args */ null, - /* debugging */ false)) { - GeckoThread.launch(); - } + preload(context); // Perform common initialization for Fennec/GeckoView. GeckoAppShell.setLayerView(this); initializeView(); mListener.registerListeners(); if (settings == null) { @@ -570,17 +583,17 @@ public class GeckoView extends LayerView return mProgressListener; } /** * Set the navigation callback handler. * This will replace the current handler. * @param navigation An implementation of NavigationListener. */ - public void setNavigationDelegate(NavigationListener listener) { + public void setNavigationListener(NavigationListener listener) { if (mNavigationListener == listener) { return; } if (listener == null) { mEventDispatcher.dispatch("GeckoViewNavigation:Inactive", null); } else if (mNavigationListener == null) { mEventDispatcher.dispatch("GeckoViewNavigation:Active", null); } @@ -1008,16 +1021,17 @@ public class GeckoView extends LayerView public EventDispatcher getEventDispatcher() { return mEventDispatcher; } public interface ProgressListener { static final int STATE_IS_BROKEN = 1; static final int STATE_IS_SECURE = 2; static final int STATE_IS_INSECURE = 4; + /* package */ final int STATE_ALL = STATE_IS_BROKEN | STATE_IS_SECURE | STATE_IS_INSECURE; /** * A View has started loading content from the network. * @param view The GeckoView that initiated the callback. * @param url The resource being loaded. */ void onPageStart(GeckoView view, String url);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java @@ -25,33 +25,33 @@ public class DynamicToolbarAnimator { DISABLED(0), RELAYOUT(1), ACTION_MODE(2), FULL_SCREEN(3), CARET_DRAG(4), PAGE_LOADING(5), CUSTOM_TAB(6); - public final int mValue; - PinReason(final int value) { - mValue = value; + public final int value; + PinReason(final int aValue) { + value = aValue; } } public interface MetricsListener { public void onMetricsChanged(ImmutableViewportMetrics viewport); } public interface ToolbarChromeProxy { public Bitmap getBitmapOfToolbarChrome(); public boolean isToolbarChromeVisible(); public void toggleToolbarChrome(boolean aShow); } - private final Set<PinReason> pinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class)); + private final Set<PinReason> mPinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class)); private final GeckoLayerClient mTarget; private LayerView.Compositor mCompositor; private final List<MetricsListener> mListeners; private ToolbarChromeProxy mToolbarChromeProxy; private int mMaxToolbarHeight; private boolean mCompositorControllerOpen; @@ -98,32 +98,32 @@ public class DynamicToolbarAnimator { } return 0; } /** * If true, scroll changes will not affect translation. */ public boolean isPinned() { - return !pinFlags.isEmpty(); + return !mPinFlags.isEmpty(); } public boolean isPinnedBy(PinReason reason) { - return pinFlags.contains(reason); + return mPinFlags.contains(reason); } public void setPinned(boolean pinned, PinReason reason) { - if ((mCompositor != null) && (pinned != pinFlags.contains(reason))) { - mCompositor.setPinned(pinned, reason.mValue); + if ((mCompositor != null) && (pinned != mPinFlags.contains(reason))) { + mCompositor.setPinned(pinned, reason.value); } if (pinned) { - pinFlags.add(reason); + mPinFlags.add(reason); } else { - pinFlags.remove(reason); + mPinFlags.remove(reason); } } public void showToolbar(boolean immediately) { if (mCompositor != null) { mCompositor.sendToolbarAnimatorMessage(immediately ? LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY : LayerView.REQUEST_SHOW_TOOLBAR_ANIMATED); } @@ -155,18 +155,18 @@ public class DynamicToolbarAnimator { private void dumpStateToCompositor() { if ((mCompositor != null) && mCompositorControllerOpen) { mCompositor.setMaxToolbarHeight(mMaxToolbarHeight); if ((mToolbarChromeProxy != null) && mToolbarChromeProxy.isToolbarChromeVisible()) { mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_SHOW_TOOLBAR_IMMEDIATELY); } else { mCompositor.sendToolbarAnimatorMessage(LayerView.REQUEST_HIDE_TOOLBAR_IMMEDIATELY); } - for (PinReason reason : pinFlags) { - mCompositor.setPinned(true, reason.mValue); + for (PinReason reason : PinReason.values()) { + mCompositor.setPinned(mPinFlags.contains(reason), reason.value); } } else if ((mCompositor != null) && !mCompositorControllerOpen) { // Ask the UiCompositorControllerChild if it is open since the open message can // sometimes be sent to a different instance of the LayerView such as when // Fennec is being used in custom tabs. mCompositor.sendToolbarAnimatorMessage(LayerView.IS_COMPOSITOR_CONTROLLER_OPEN); } }
--- a/mozglue/misc/TimeStamp.cpp +++ b/mozglue/misc/TimeStamp.cpp @@ -42,19 +42,21 @@ struct TimeStampInitialization { TimeStamp::Shutdown(); }; }; static TimeStampInitialization sInitOnce; MFBT_API TimeStamp -TimeStamp::ProcessCreation(bool& aIsInconsistent) +TimeStamp::ProcessCreation(bool* aIsInconsistent) { - aIsInconsistent = false; + if (aIsInconsistent) { + *aIsInconsistent = false; + } if (sInitOnce.mProcessCreation.IsNull()) { char* mozAppRestart = getenv("MOZ_APP_RESTART"); TimeStamp ts; /* When calling PR_SetEnv() with an empty value the existing variable may * be unset or set to the empty string depending on the underlying platform * thus we have to check if the variable is present and not empty. */ @@ -67,17 +69,19 @@ TimeStamp::ProcessCreation(bool& aIsInco uint64_t uptime = ComputeProcessUptime(); ts = now - TimeDuration::FromMicroseconds(uptime); if ((ts > sInitOnce.mFirstTimeStamp) || (uptime == 0)) { /* If the process creation timestamp was inconsistent replace it with * the first one instead and notify that a telemetry error was * detected. */ - aIsInconsistent = true; + if (aIsInconsistent) { + *aIsInconsistent = true; + } ts = sInitOnce.mFirstTimeStamp; } } sInitOnce.mProcessCreation = ts; } return sInitOnce.mProcessCreation;
--- a/mozglue/misc/TimeStamp.h +++ b/mozglue/misc/TimeStamp.h @@ -469,22 +469,22 @@ public: /** * Return a timestamp representing the time when the current process was * created which will be comparable with other timestamps taken with this * class. If the actual process creation time is detected to be inconsistent * the @a aIsInconsistent parameter will be set to true, the returned * timestamp however will still be valid though inaccurate. * - * @param aIsInconsistent Set to true if an inconsistency was detected in the - * process creation time + * @param aIsInconsistent If non-null, set to true if an inconsistency was + * detected in the process creation time * @returns A timestamp representing the time when the process was created, * this timestamp is always valid even when errors are reported */ - static MFBT_API TimeStamp ProcessCreation(bool& aIsInconsistent); + static MFBT_API TimeStamp ProcessCreation(bool* aIsInconsistent = nullptr); /** * Records a process restart. After this call ProcessCreation() will return * the time when the browser was restarted instead of the actual time when * the process was created. */ static MFBT_API void RecordProcessRestart();
--- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -26,16 +26,17 @@ #include "nsServiceManagerUtils.h" #include "nsStreamUtils.h" #include "nsIPipe.h" #include "prerror.h" #include "nsThreadUtils.h" #include "nsIDNSRecord.h" #include "nsIDNSService.h" #include "nsICancelable.h" +#include "nsWrapperCacheInlines.h" #ifdef MOZ_WIDGET_GONK #include "NetStatistics.h" #endif namespace mozilla { namespace net {
--- a/taskcluster/ci/android-stuff/kind.yml +++ b/taskcluster/ci/android-stuff/kind.yml @@ -166,17 +166,19 @@ jobs: optimizations: - - files-changed - - "mobile/android/**/*.java" - "mobile/android/**/*.jpeg" - "mobile/android/**/*.jpg" - "mobile/android/**/*.png" - "mobile/android/**/*.svg" - "mobile/android/**/*.xml" # Manifest & android resources - - "mobile/android/**/build.gradle" + - "mobile/android/**/*.gradle" + - "mobile/android/**/Makefile.in" + - "mobile/android/**/moz.build" android-checkstyle: description: "Android checkstyle" attributes: build_platform: android-checkstyle build_type: opt treeherder: platform: android-4-0-armv7-api15/opt @@ -215,18 +217,20 @@ jobs: - "bin/build.sh" max-run-time: 36000 scopes: - docker-worker:relengapi-proxy:tooltool.download.internal - docker-worker:relengapi-proxy:tooltool.download.public optimizations: - - files-changed - - "mobile/android/**/checkstyle.xml" + - "mobile/android/**/*.java" - "mobile/android/**/*.gradle" - - "mobile/android/**/*.java" + - "mobile/android/**/Makefile.in" + - "mobile/android/**/moz.build" android-findbugs: description: "Android findbugs" attributes: build_platform: android-findbugs build_type: opt treeherder: platform: android-4-0-armv7-api15/opt @@ -264,10 +268,12 @@ jobs: - "/bin/bash" - "bin/build.sh" max-run-time: 36000 scopes: - docker-worker:relengapi-proxy:tooltool.download.internal - docker-worker:relengapi-proxy:tooltool.download.public optimizations: - - files-changed - - - "mobile/android/**/*.gradle" - - "mobile/android/**/*.java" + - - "mobile/android/**/*.java" + - "mobile/android/**/*.gradle" + - "mobile/android/**/Makefile.in" + - "mobile/android/**/moz.build"
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -1914,23 +1914,31 @@ or run without that action (ie: --no-{ac yield { 'name': 'sccache hit rate', 'value': hits, 'extraOptions': self.perfherder_resource_options(), 'subtests': [], } - for stat in ['cache_write_errors', 'requests_not_cacheable']: - yield { - 'name': 'sccache %s' % stat, - 'value': stats['stats'][stat], - 'extraOptions': self.perfherder_resource_options(), - 'subtests': [], - } + yield { + 'name': 'sccache cache_write_errors', + 'value': stats['stats']['cache_write_errors'], + 'extraOptions': self.perfherder_resource_options(), + 'alertThreshold': 50.0, + 'subtests': [], + } + + yield { + 'name': 'sccache requests_not_cacheable', + 'value': stats['stats']['requests_not_cacheable'], + 'extraOptions': self.perfherder_resource_options(), + 'alertThreshold': 50.0, + 'subtests': [], + } def get_firefox_version(self): versionFilePath = os.path.join( self.query_abs_dirs()['abs_src_dir'], 'browser/config/version.txt') with open(versionFilePath, 'r') as versionFile: return versionFile.readline().strip() def generate_build_stats(self):
--- a/testing/specialpowers/content/MockFilePicker.jsm +++ b/testing/specialpowers/content/MockFilePicker.jsm @@ -67,16 +67,17 @@ this.MockFilePicker = { registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory); } }, reset: function() { this.appendFilterCallback = null; this.appendFiltersCallback = null; this.displayDirectory = null; + this.displaySpecialDirectory = ""; this.filterIndex = 0; this.mode = null; this.returnData = []; this.returnValue = null; this.showCallback = null; this.afterOpenCallback = null; this.shown = false; this.showing = false; @@ -174,16 +175,17 @@ MockFilePickerInstance.prototype = { if (typeof MockFilePicker.appendFiltersCallback == "function") MockFilePicker.appendFiltersCallback(this, aFilterMask); }, defaultString: "", defaultExtension: "", parent: null, filterIndex: 0, displayDirectory: null, + displaySpecialDirectory: "", get file() { if (MockFilePicker.returnData.length >= 1) { return MockFilePicker.returnData[0].nsIFile; } return null; }, @@ -264,16 +266,17 @@ MockFilePickerInstance.prototype = { // Nothing else has to be done. MockFilePicker.pendingPromises = []; if (result == Ci.nsIFilePicker.returnCancel) { return result; } MockFilePicker.displayDirectory = this.displayDirectory; + MockFilePicker.displaySpecialDirectory = this.displaySpecialDirectory; MockFilePicker.shown = true; if (typeof MockFilePicker.showCallback == "function") { try { var returnValue = MockFilePicker.showCallback(this); if (typeof returnValue != "undefined") { return returnValue; } } catch(ex) {
--- a/toolkit/components/filepicker/content/filepicker.js +++ b/toolkit/components/filepicker/content/filepicker.js @@ -42,16 +42,17 @@ function filepickerLoad() { if (window.arguments) { var o = window.arguments[0]; retvals = o.retvals; /* set this to a global var so we can set return values */ const title = o.title; filePickerMode = o.mode; if (o.displayDirectory) { var directory = o.displayDirectory.path; } + var specialDirectory = o.displaySpecialDirectory; const initialText = o.defaultString; var filterTitles = o.filters.titles; var filterTypes = o.filters.types; var numFilters = filterTitles.length; document.title = title; allowURLs = o.allowURLs; @@ -128,31 +129,33 @@ function filepickerLoad() { // Start out with the ok button disabled since nothing will be // selected and nothing will be in the text field. okButton.disabled = filePickerMode != nsIFilePicker.modeGetFolder; // This allows the window to show onscreen before we begin // loading the file list - setTimeout(setInitialDirectory, 0, directory); + setTimeout(setInitialDirectory, 0, { directory, specialDirectory }); } -function setInitialDirectory(directory) { +function setInitialDirectory(directories) { // Start in the user's home directory var dirService = Components.classes[NS_DIRECTORYSERVICE_CONTRACTID] .getService(nsIProperties); - homeDir = dirService.get("Home", Components.interfaces.nsIFile); + homeDir = dirService.get(directories.specialDirectory + ? directories.specialDirectory : "Home", + Components.interfaces.nsIFile); - if (directory) { - sfile.initWithPath(directory); + if (directories.directory) { + sfile.initWithPath(directories.directory); if (!sfile.exists() || !sfile.isDirectory()) - directory = false; + directories.directory = false; } - if (!directory) { + if (!directories.directory) { sfile.initWithPath(homeDir.path); } gotoDirectory(sfile); } function onFilterChanged(target) { // Do this on a timeout callback so the filter list can roll up
--- a/toolkit/components/filepicker/nsFilePicker.js +++ b/toolkit/components/filepicker/nsFilePicker.js @@ -50,16 +50,17 @@ function nsFilePicker() { filterBundle = srGetStrBundle("chrome://global/content/filepicker.properties"); /* attributes */ this.mDefaultString = ""; this.mFilterIndex = 0; this.mFilterTitles = new Array(); this.mFilters = new Array(); this.mDisplayDirectory = null; + this.mDisplaySpecialDirectory = null; if (lastDirectory) { try { var dir = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile); dir.initWithPath(lastDirectory); this.mDisplayDirectory = dir; } catch (e) {} } } @@ -82,16 +83,24 @@ nsFilePicker.prototype = { a.clone().QueryInterface(nsILocalFile); }, get displayDirectory() { return this.mDisplayDirectory && this.mDisplayDirectory.clone() .QueryInterface(nsILocalFile); }, + /* attribute AString displaySpecialDirectory; */ + set displaySpecialDirectory(a) { + this.mDisplaySpecialDirectory = a; + }, + get displaySpecialDirectory() { + return this.mDisplaySpecialDirectory; + }, + /* readonly attribute nsILocalFile file; */ get file() { return this.mFilesEnumerator.mFiles[0]; }, /* readonly attribute nsISimpleEnumerator files; */ get files() { return this.mFilesEnumerator; }, /* we don't support directories, yet */ get domFileOrDirectory() { @@ -251,16 +260,17 @@ nsFilePicker.prototype = { }); }, show() { var o = {}; o.title = this.mTitle; o.mode = this.mMode; o.displayDirectory = this.mDisplayDirectory; + o.displaySpecialDirectory = this.mDisplaySpecialDirectory; o.defaultString = this.mDefaultString; o.filterIndex = this.mFilterIndex; o.filters = {}; o.filters.titles = this.mFilterTitles; o.filters.types = this.mFilters; o.allowURLs = this.mAllowURLs; o.retvals = {};
--- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -753,17 +753,17 @@ nsAppStartup::GetStartupInfo(JSContext* TimeStamp procTime = StartupTimeline::Get(StartupTimeline::PROCESS_CREATION); TimeStamp now = TimeStamp::Now(); PRTime absNow = PR_Now(); if (procTime.IsNull()) { bool error = false; - procTime = TimeStamp::ProcessCreation(error); + procTime = TimeStamp::ProcessCreation(&error); if (error) { Telemetry::Accumulate(Telemetry::STARTUP_MEASUREMENT_ERRORS, StartupTimeline::PROCESS_CREATION); } StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, procTime); }
--- a/toolkit/components/telemetry/TelemetryCommon.cpp +++ b/toolkit/components/telemetry/TelemetryCommon.cpp @@ -78,17 +78,17 @@ CanRecordInProcess(RecordedProcessType p ((processType != GeckoProcessType_Default) && recordAllChild); } nsresult MsSinceProcessStart(double* aResult) { bool error; *aResult = (TimeStamp::NowLoRes() - - TimeStamp::ProcessCreation(error)).ToMilliseconds(); + TimeStamp::ProcessCreation(&error)).ToMilliseconds(); if (error) { return NS_ERROR_NOT_AVAILABLE; } return NS_OK; } void LogToBrowserConsole(uint32_t aLogLevel, const nsAString& aMsg)
--- a/toolkit/components/telemetry/TelemetryEvent.cpp +++ b/toolkit/components/telemetry/TelemetryEvent.cpp @@ -583,18 +583,17 @@ TelemetryEvent::RecordChildEvents(GeckoP MOZ_ASSERT(XRE_IsParentProcess()); StaticMutexAutoLock locker(gTelemetryEventsMutex); for (uint32_t i = 0; i < aEvents.Length(); ++i) { const mozilla::Telemetry::ChildEventData e = aEvents[i]; // Timestamps from child processes are absolute. We fix them up here to be // relative to the main process start time. // This allows us to put events from all processes on the same timeline. - bool inconsistent = false; - double relativeTimestamp = (e.timestamp - TimeStamp::ProcessCreation(inconsistent)).ToMilliseconds(); + double relativeTimestamp = (e.timestamp - TimeStamp::ProcessCreation()).ToMilliseconds(); ::RecordEvent(locker, aProcessType, relativeTimestamp, e.category, e.method, e.object, e.value, e.extra); } return NS_OK; } nsresult TelemetryEvent::RecordEvent(const nsACString& aCategory, const nsACString& aMethod,
--- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -1036,19 +1036,18 @@ bool MinidumpCallback( XP_TTOA(timeSinceLastCrash, timeSinceLastCrashString, 10); } // write crash time to file if (lastCrashTimeFilename[0] != 0) { PlatformWriter lastCrashFile(lastCrashTimeFilename); WriteString(lastCrashFile, crashTimeString); } - bool ignored = false; - double uptimeTS = (TimeStamp::NowLoRes()- - TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits(); + double uptimeTS = (TimeStamp::NowLoRes() - + TimeStamp::ProcessCreation()).ToSecondsSigDigits(); char uptimeTSString[64]; SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString)); // Write crash event file. // Minidump IDs are UUIDs (36) + NULL. static char id_ascii[37]; #ifdef XP_LINUX @@ -3210,19 +3209,18 @@ WriteExtraData(nsIFile* extraFile, time_t crashTime = time(nullptr); char crashTimeString[32]; XP_TTOA(crashTime, crashTimeString, 10); WriteAnnotation(fd, nsDependentCString("CrashTime"), nsDependentCString(crashTimeString)); - bool ignored = false; - double uptimeTS = (TimeStamp::NowLoRes()- - TimeStamp::ProcessCreation(ignored)).ToSecondsSigDigits(); + double uptimeTS = (TimeStamp::NowLoRes() - + TimeStamp::ProcessCreation()).ToSecondsSigDigits(); char uptimeTSString[64]; SimpleNoCLibDtoA(uptimeTS, uptimeTSString, sizeof(uptimeTSString)); WriteAnnotation(fd, nsDependentCString("UptimeTS"), nsDependentCString(uptimeTSString)); }
--- a/toolkit/library/gtest/rust/moz.build +++ b/toolkit/library/gtest/rust/moz.build @@ -1,23 +1,9 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -features = [] -if CONFIG['MOZ_STYLO']: - features += ['servo'] - - if CONFIG['MOZ_STYLO_BINDGEN']: - features += ['bindgen'] +include('../../rust/gkrust-features.mozbuild') - if CONFIG['MOZ_DEBUG']: - features += ['gecko_debug'] - -if CONFIG['MOZ_BUILD_WEBRENDER']: - features += ['quantum_render'] - -if CONFIG['MOZ_PULSEAUDIO']: - features += ['cubeb_pulse_rust'] - -RustLibrary('gkrust-gtest', features, '../..') +RustLibrary('gkrust-gtest', gkrust_features, '../..')
new file mode 100644 --- /dev/null +++ b/toolkit/library/rust/gkrust-features.mozbuild @@ -0,0 +1,22 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +gkrust_features = [] +if CONFIG['MOZ_STYLO']: + gkrust_features += ['servo'] + + if CONFIG['MOZ_STYLO_BINDGEN']: + gkrust_features += ['bindgen'] + + if CONFIG['MOZ_DEBUG']: + gkrust_features += ['gecko_debug'] + +if CONFIG['MOZ_BUILD_WEBRENDER']: + gkrust_features += ['quantum_render'] + +if CONFIG['MOZ_PULSEAUDIO']: + gkrust_features += ['cubeb_pulse_rust'] +
--- a/toolkit/library/rust/moz.build +++ b/toolkit/library/rust/moz.build @@ -1,23 +1,9 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -features = [] -if CONFIG['MOZ_STYLO']: - features += ['servo'] - - if CONFIG['MOZ_STYLO_BINDGEN']: - features += ['bindgen'] +include('gkrust-features.mozbuild') - if CONFIG['MOZ_DEBUG']: - features += ['gecko_debug'] - -if CONFIG['MOZ_BUILD_WEBRENDER']: - features += ['quantum_render'] - -if CONFIG['MOZ_PULSEAUDIO']: - features += ['cubeb_pulse_rust'] - -RustLibrary('gkrust', features, '..') +RustLibrary('gkrust', gkrust_features, '..')
--- a/tools/memory-profiler/MemoryProfiler.cpp +++ b/tools/memory-profiler/MemoryProfiler.cpp @@ -90,18 +90,17 @@ MemoryProfiler::InitOnce() if (!initialized) { MallocHook::Initialize(); sLock = PR_NewLock(); sProfileContextCount = 0; sJSContextProfilerMap = new JSContextProfilerMap(); ClearOnShutdown(&sJSContextProfilerMap); ClearOnShutdown(&sNativeProfiler); std::srand(PR_Now()); - bool ignored; - sStartTime = TimeStamp::ProcessCreation(ignored); + sStartTime = TimeStamp::ProcessCreation(); initialized = true; } } NS_IMETHODIMP MemoryProfiler::StartProfiler() { InitOnce();
--- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -468,18 +468,17 @@ AddDynamicCodeLocationTag(ProfileBuffer* aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0])))); } } static const int SAMPLER_MAX_STRING_LENGTH = 128; static void AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer, - volatile js::ProfileEntry& entry, PseudoStack* stack, - void* lastpc) + volatile js::ProfileEntry& entry, PseudoStack* stack) { // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and // should not be recorded in the profile. if (entry.hasFlag(js::ProfileEntry::BEGIN_PSEUDO_JS)) { return; } int lineno = -1; @@ -504,25 +503,16 @@ AddPseudoEntry(PSLockRef aLock, ProfileB // That will happen to the preceding tag. AddDynamicCodeLocationTag(aBuffer, sampleLabel); if (entry.isJs()) { JSScript* script = entry.script(); if (script) { if (!entry.pc()) { // The JIT only allows the top-most entry to have a nullptr pc. MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]); - - // If stack-walking was disabled, then that's just unfortunate. - if (lastpc) { - jsbytecode* jspc = js::ProfilingGetPC(stack->mContext, script, - lastpc); - if (jspc) { - lineno = JS_PCToLineNumber(script, jspc); - } - } } else { lineno = JS_PCToLineNumber(script, entry.pc()); } } } else { lineno = entry.line(); } } else { @@ -701,17 +691,17 @@ MergeStacksIntoProfile(PSLockRef aLock, jsStackAddr != nativeStackAddr); MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr && nativeStackAddr != jsStackAddr); // Check to see if pseudoStack frame is top-most. if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) { MOZ_ASSERT(pseudoIndex < pseudoCount); volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex]; - AddPseudoEntry(aLock, aBuffer, pseudoFrame, pseudoStack, nullptr); + AddPseudoEntry(aLock, aBuffer, pseudoFrame, pseudoStack); pseudoIndex++; continue; } // Check to see if JS jit stack frame is top-most if (jsStackAddr > nativeStackAddr) { MOZ_ASSERT(jsIndex >= 0); const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex]; @@ -1983,18 +1973,17 @@ profiler_init(void* aStackTop) { PSAutoLock lock(gPSMutex); // We've passed the possible failure point. Instantiate gPS, which // indicates that the profiler has initialized successfully. gPS = new PS(); - bool ignore; - gPS->SetProcessStartTime(lock, mozilla::TimeStamp::ProcessCreation(ignore)); + gPS->SetProcessStartTime(lock, mozilla::TimeStamp::ProcessCreation()); locked_register_thread(lock, kMainThreadName, aStackTop); // Platform-specific initialization. PlatformInit(lock); #ifdef MOZ_TASK_TRACER mozilla::tasktracer::InitTaskTracer();
--- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -290,43 +290,81 @@ NS_IMETHODIMP nsBaseFilePicker::GetFiles files.AppendObject(file); return NS_NewArrayEnumerator(aFiles, files); } // Set the display directory NS_IMETHODIMP nsBaseFilePicker::SetDisplayDirectory(nsIFile *aDirectory) { + // if displaySpecialDirectory has been previously called, let's abort this + // operation. + if (!mDisplaySpecialDirectory.IsEmpty()) { + return NS_OK; + } + if (!aDirectory) { mDisplayDirectory = nullptr; return NS_OK; } nsCOMPtr<nsIFile> directory; nsresult rv = aDirectory->Clone(getter_AddRefs(directory)); if (NS_FAILED(rv)) return rv; mDisplayDirectory = do_QueryInterface(directory, &rv); return rv; } // Get the display directory NS_IMETHODIMP nsBaseFilePicker::GetDisplayDirectory(nsIFile **aDirectory) { *aDirectory = nullptr; + + // if displaySpecialDirectory has been previously called, let's abort this + // operation. + if (!mDisplaySpecialDirectory.IsEmpty()) { + return NS_OK; + } + if (!mDisplayDirectory) return NS_OK; nsCOMPtr<nsIFile> directory; nsresult rv = mDisplayDirectory->Clone(getter_AddRefs(directory)); if (NS_FAILED(rv)) { return rv; } directory.forget(aDirectory); return NS_OK; } +// Set the display special directory +NS_IMETHODIMP nsBaseFilePicker::SetDisplaySpecialDirectory(const nsAString& aDirectory) +{ + // if displayDirectory has been previously called, let's abort this operation. + if (mDisplayDirectory && mDisplaySpecialDirectory.IsEmpty()) { + return NS_OK; + } + + mDisplaySpecialDirectory = aDirectory; + if (mDisplaySpecialDirectory.IsEmpty()) { + mDisplayDirectory = nullptr; + return NS_OK; + } + + return NS_GetSpecialDirectory(NS_ConvertUTF16toUTF8(mDisplaySpecialDirectory).get(), + getter_AddRefs(mDisplayDirectory)); +} + +// Get the display special directory +NS_IMETHODIMP nsBaseFilePicker::GetDisplaySpecialDirectory(nsAString& aDirectory) +{ + aDirectory = mDisplaySpecialDirectory; + return NS_OK; +} + NS_IMETHODIMP nsBaseFilePicker::GetAddToRecentDocs(bool *aFlag) { *aFlag = mAddToRecentDocs; return NS_OK; } NS_IMETHODIMP
--- a/widget/nsBaseFilePicker.h +++ b/widget/nsBaseFilePicker.h @@ -29,30 +29,33 @@ public: NS_IMETHOD Open(nsIFilePickerShownCallback *aCallback); NS_IMETHOD AppendFilters(int32_t filterMask); NS_IMETHOD GetFilterIndex(int32_t *aFilterIndex); NS_IMETHOD SetFilterIndex(int32_t aFilterIndex); NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles); NS_IMETHOD GetDisplayDirectory(nsIFile * *aDisplayDirectory); NS_IMETHOD SetDisplayDirectory(nsIFile * aDisplayDirectory); + NS_IMETHOD GetDisplaySpecialDirectory(nsAString& aDisplayDirectory); + NS_IMETHOD SetDisplaySpecialDirectory(const nsAString& aDisplayDirectory); NS_IMETHOD GetAddToRecentDocs(bool *aFlag); NS_IMETHOD SetAddToRecentDocs(bool aFlag); NS_IMETHOD GetMode(int16_t *aMode); NS_IMETHOD SetOkButtonLabel(const nsAString& aLabel); NS_IMETHOD GetOkButtonLabel(nsAString& aLabel); NS_IMETHOD GetDomFileOrDirectory(nsISupports** aValue); NS_IMETHOD GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aValue); protected: virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle) = 0; bool mAddToRecentDocs; nsCOMPtr<nsIFile> mDisplayDirectory; + nsString mDisplaySpecialDirectory; nsCOMPtr<nsPIDOMWindowOuter> mParent; int16_t mMode; nsString mOkButtonLabel; }; #endif // nsBaseFilePicker_h__
--- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -139,17 +139,18 @@ nsFilePickerProxy::Open(nsIFilePickerSho mDisplayDirectory->GetPath(displayDirectory); } if (!mIPCActive) { return NS_ERROR_FAILURE; } SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension, - mFilters, mFilterNames, displayDirectory, mOkButtonLabel); + mFilters, mFilterNames, displayDirectory, mDisplaySpecialDirectory, + mOkButtonLabel); return NS_OK; } mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) {
--- a/widget/nsIFilePicker.idl +++ b/widget/nsIFilePicker.idl @@ -107,22 +107,35 @@ interface nsIFilePicker : nsISupports * The filter which is currently selected in the File Picker dialog * * @return Returns the index (0 based) of the selected filter in the filter list. */ attribute long filterIndex; /** * Set the directory that the file open/save dialog initially displays + * Note that, if displaySpecialDirectory has been already set, this value will + * be ignored. * * @param displayDirectory the name of the directory * */ attribute nsIFile displayDirectory; + /** + * Set the directory that the file open/save dialog initially displays using + * one of the special name as such as 'Desk', 'TmpD', and so on. + * Note that, if displayDirectory has been already set, this value will be + * ignored. + * + * @param displaySpecialDirectory the name of the special directory + * + */ + attribute AString displaySpecialDirectory; + /** * Get the nsIFile for the file or directory. * * @return Returns the file currently selected */ readonly attribute nsIFile file;
--- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1176,17 +1176,17 @@ CycleCollectedJSRuntime::GarbageCollect( void CycleCollectedJSRuntime::JSObjectsTenured() { MOZ_ASSERT(mJSContext); for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) { nsWrapperCache* cache = iter.Get(); - JSObject* wrapper = cache->GetWrapperPreserveColor(); + JSObject* wrapper = cache->GetWrapperMaybeDead(); MOZ_DIAGNOSTIC_ASSERT(wrapper); if (!JS::ObjectIsTenured(wrapper)) { MOZ_ASSERT(!cache->PreservingWrapper()); const JSClass* jsClass = js::GetObjectJSClass(wrapper); jsClass->doFinalize(nullptr, wrapper); } } @@ -1200,18 +1200,18 @@ for (auto iter = mPreservedNurseryObject mPreservedNurseryObjects.Clear(); } void CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache) { MOZ_ASSERT(mJSContext); MOZ_ASSERT(aCache); - MOZ_ASSERT(aCache->GetWrapperPreserveColor()); - MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperPreserveColor())); + MOZ_ASSERT(aCache->GetWrapperMaybeDead()); + MOZ_ASSERT(!JS::ObjectIsTenured(aCache->GetWrapperMaybeDead())); mNurseryObjects.InfallibleAppend(aCache); } void CycleCollectedJSRuntime::NurseryWrapperPreserved(JSObject* aWrapper) { MOZ_ASSERT(mJSContext);
--- a/xpcom/base/nsDebug.h +++ b/xpcom/base/nsDebug.h @@ -356,25 +356,16 @@ inline void MOZ_PretendNoReturn() NS_ENSURE_FALSE(outer, NS_ERROR_NO_AGGREGATION) /*****************************************************************************/ #if (defined(DEBUG) || (defined(NIGHTLY_BUILD) && !defined(MOZ_PROFILING))) && !defined(XPCOM_GLUE_AVOID_NSPR) #define MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED 1 #endif -#ifdef XPCOM_GLUE - #define NS_CheckThreadSafe(owningThread, msg) -#else - #define NS_CheckThreadSafe(owningThread, msg) \ - if (MOZ_UNLIKELY(owningThread != PR_GetCurrentThread())) { \ - MOZ_CRASH(msg); \ - } -#endif - #ifdef MOZILLA_INTERNAL_API void NS_ABORT_OOM(size_t aSize); #else inline void NS_ABORT_OOM(size_t) { MOZ_CRASH(); } #endif
--- a/xpcom/base/nsISupportsImpl.cpp +++ b/xpcom/base/nsISupportsImpl.cpp @@ -1,15 +1,16 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupportsImpl.h" +#include "mozilla/Assertions.h" nsresult NS_FASTCALL NS_TableDrivenQI(void* aThis, REFNSIID aIID, void** aInstancePtr, const QITableEntry* aEntries) { do { if (aIID.Equals(*aEntries->iid)) { nsISupports* r = reinterpret_cast<nsISupports*>( @@ -20,8 +21,19 @@ NS_TableDrivenQI(void* aThis, REFNSIID a } ++aEntries; } while (aEntries->iid); *aInstancePtr = nullptr; return NS_ERROR_NO_INTERFACE; } + +#ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED +void +nsAutoOwningThread::AssertCurrentThreadOwnsMe(const char* msg) const +{ + if (MOZ_UNLIKELY(mThread != PR_GetCurrentThread())) { + // `msg` is a string literal by construction. + MOZ_CRASH_UNSAFE_OOL(msg); + } +} +#endif
--- a/xpcom/base/nsISupportsImpl.h +++ b/xpcom/base/nsISupportsImpl.h @@ -8,20 +8,19 @@ #ifndef nsISupportsImpl_h__ #define nsISupportsImpl_h__ #include "nscore.h" #include "nsISupportsBase.h" #include "nsISupportsUtils.h" - #if !defined(XPCOM_GLUE_AVOID_NSPR) -#include "prthread.h" /* needed for thread-safety checks */ -#endif // !XPCOM_GLUE_AVOID_NSPR +#include "prthread.h" /* needed for cargo-culting headers */ +#endif #include "nsDebug.h" #include "nsXPCOM.h" #include <atomic> #include "mozilla/Attributes.h" #include "mozilla/Assertions.h" #include "mozilla/Compiler.h" #include "mozilla/Likely.h" @@ -46,37 +45,52 @@ ToCanonicalSupports(nsISupports* aSuppor return nullptr; } //////////////////////////////////////////////////////////////////////////////// // Macros to help detect thread-safety: #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED +#include "prthread.h" /* needed for thread-safety checks */ + class nsAutoOwningThread { public: nsAutoOwningThread() { mThread = PR_GetCurrentThread(); } - void* GetThread() const { return mThread; } + + // We move the actual assertion checks out-of-line to minimize code bloat, + // but that means we have to pass a non-literal string to + // MOZ_CRASH_UNSAFE_OOL. To make that more safe, the public interface + // requires a literal string and passes that to the private interface; we + // can then be assured that we effectively are passing a literal string + // to MOZ_CRASH_UNSAFE_OOL. + template<int N> + void AssertOwnership(const char (&aMsg)[N]) const + { + AssertCurrentThreadOwnsMe(aMsg); + } private: + void AssertCurrentThreadOwnsMe(const char* aMsg) const; + void* mThread; }; #define NS_DECL_OWNINGTHREAD nsAutoOwningThread _mOwningThread; #define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) \ - NS_CheckThreadSafe(agg->_mOwningThread.GetThread(), #_class " not thread-safe") + agg->_mOwningThread.AssertOwnership(#_class " not thread-safe") #define NS_ASSERT_OWNINGTHREAD(_class) NS_ASSERT_OWNINGTHREAD_AGGREGATE(this, _class) -#else // !DEBUG && !(NIGHTLY_BUILD && !MOZ_PROFILING) +#else // !MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED #define NS_DECL_OWNINGTHREAD /* nothing */ #define NS_ASSERT_OWNINGTHREAD_AGGREGATE(agg, _class) ((void)0) #define NS_ASSERT_OWNINGTHREAD(_class) ((void)0) -#endif // DEBUG || (NIGHTLY_BUILD && !MOZ_PROFILING) +#endif // MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED // Macros for reference-count and constructor logging #if defined(NS_BUILD_REFCNT_LOGGING) #define NS_LOG_ADDREF(_p, _rc, _type, _size) \ NS_LogAddRef((_p), (_rc), (_type), (uint32_t) (_size))
--- a/xpcom/base/nsWeakReference.cpp +++ b/xpcom/base/nsWeakReference.cpp @@ -11,19 +11,19 @@ #include "nsWeakReference.h" #include "nsCOMPtr.h" #include "nsDebug.h" #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED #define MOZ_WEAKREF_DECL_OWNINGTHREAD nsAutoOwningThread _mWeakRefOwningThread; #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD \ - NS_CheckThreadSafe(_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe") + _mWeakRefOwningThread.AssertOwnership("nsWeakReference not thread-safe") #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) \ - NS_CheckThreadSafe((that)->_mWeakRefOwningThread.GetThread(), "nsWeakReference not thread-safe") + (that)->_mWeakRefOwningThread.AssertOwnership("nsWeakReference not thread-safe") #else #define MOZ_WEAKREF_DECL_OWNINGTHREAD #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD do { } while (false) #define MOZ_WEAKREF_ASSERT_OWNINGTHREAD_DELEGATED(that) do { } while (false) #endif
--- a/xpcom/threads/HangMonitor.cpp +++ b/xpcom/threads/HangMonitor.cpp @@ -178,17 +178,17 @@ GetChromeHangReport(Telemetry::Processed } aStack = Telemetry::GetStackAndModules(rawStack); // Record system uptime (in minutes) at the time of the hang aSystemUptime = ((GetTickCount() / 1000) - (gTimeout * 2)) / 60; // Record Firefox uptime (in minutes) at the time of the hang bool error; - TimeStamp processCreation = TimeStamp::ProcessCreation(error); + TimeStamp processCreation = TimeStamp::ProcessCreation(&error); if (!error) { TimeDuration td = TimeStamp::Now() - processCreation; aFirefoxUptime = (static_cast<int32_t>(td.ToSeconds()) - (gTimeout * 2)) / 60; } else { aFirefoxUptime = -1; } }