diff options
Diffstat (limited to 'xpcom')
63 files changed, 1145 insertions, 650 deletions
diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h index bbe47a57a5..631afc1504 100644 --- a/xpcom/base/CycleCollectedJSContext.h +++ b/xpcom/base/CycleCollectedJSContext.h @@ -296,6 +296,7 @@ class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue { MOZ_CAN_RUN_SCRIPT_BOUNDARY void runJobs(JSContext* cx) override; bool empty() const override; + bool isDrainingStopped() const override { return false; } class SavedMicroTaskQueue; js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) override; diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index c3f9d56857..6833dee791 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1563,17 +1563,27 @@ void CycleCollectedJSRuntime::GarbageCollect(JS::GCOptions aOptions, void CycleCollectedJSRuntime::JSObjectsTenured() { JSContext* cx = CycleCollectedJSContext::Get()->Context(); - for (auto iter = mNurseryObjects.Iter(); !iter.Done(); iter.Next()) { + + NurseryObjectsVector objects; + std::swap(objects, mNurseryObjects); + + for (auto iter = objects.Iter(); !iter.Done(); iter.Next()) { nsWrapperCache* cache = iter.Get(); JSObject* wrapper = cache->GetWrapperMaybeDead(); MOZ_DIAGNOSTIC_ASSERT(wrapper); - if (!JS::ObjectIsTenured(wrapper)) { + + if (!js::gc::IsInsideNursery(wrapper)) { + continue; + } + + if (js::gc::IsDeadNurseryObject(wrapper)) { MOZ_ASSERT(!cache->PreservingWrapper()); js::gc::FinalizeDeadNurseryObject(cx, wrapper); + continue; } - } - mNurseryObjects.Clear(); + mNurseryObjects.InfallibleAppend(cache); + } } void CycleCollectedJSRuntime::NurseryWrapperAdded(nsWrapperCache* aCache) { diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index 6f03d3ee99..af837e1ae7 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -473,8 +473,9 @@ class CycleCollectedJSRuntime { OOMState mLargeAllocationFailureState; static const size_t kSegmentSize = 512; - SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy> - mNurseryObjects; + using NurseryObjectsVector = + SegmentedVector<nsWrapperCache*, kSegmentSize, InfallibleAllocPolicy>; + NurseryObjectsVector mNurseryObjects; nsTHashSet<JS::Zone*> mZonesWaitingForGC; diff --git a/xpcom/base/DarwinObjectPtr.h b/xpcom/base/DarwinObjectPtr.h new file mode 100644 index 0000000000..3d5842553b --- /dev/null +++ b/xpcom/base/DarwinObjectPtr.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014-2021 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL 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. + */ + +// Adapted from +// https://github.com/WebKit/WebKit/blob/ad340677c00a4b4b6a299c93a6e18cd073b3f4e9/Source/WTF/wtf/OSObjectPtr.h + +#ifndef mozilla_DarwinObjectPtr_h +#define mozilla_DarwinObjectPtr_h + +#include <os/object.h> +#include <utility> +#include "mozilla/Attributes.h" + +// Because ARC enablement is a compile-time choice, and we compile this header +// both ways, we need a separate copy of our code when ARC is enabled. +#if __has_feature(objc_arc) +# define AdoptDarwinObject AdoptDarwinObjectArc +# define RetainDarwinObject RetainDarwinObjectArc +# define ReleaseDarwinObject ReleaseDarwinObjectArc +#endif + +namespace mozilla { + +template <typename> +class DarwinObjectPtr; +template <typename T> +[[nodiscard]] DarwinObjectPtr<T> AdoptDarwinObject(T); + +template <typename T> +static inline void RetainDarwinObject(T aPtr) { +#if !__has_feature(objc_arc) + os_retain(aPtr); +#endif +} + +template <typename T> +static inline void ReleaseDarwinObject(T aPtr) { +#if !__has_feature(objc_arc) + os_release(aPtr); +#endif +} + +template <typename T> +class DarwinObjectPtr { + public: + DarwinObjectPtr() : mPtr(nullptr) {} + + ~DarwinObjectPtr() { + if (mPtr) { + ReleaseDarwinObject(mPtr); + } + } + + T get() const { return mPtr; } + + explicit operator bool() const { return mPtr; } + bool operator!() const { return !mPtr; } + + DarwinObjectPtr(const DarwinObjectPtr& aOther) : mPtr(aOther.mPtr) { + if (mPtr) { + RetainDarwinObject(mPtr); + } + } + + DarwinObjectPtr(DarwinObjectPtr&& aOther) : mPtr(std::move(aOther.mPtr)) { + aOther.mPtr = nullptr; + } + + MOZ_IMPLICIT DarwinObjectPtr(T aPtr) : mPtr(std::move(aPtr)) { + if (mPtr) { + RetainDarwinObject(mPtr); + } + } + + DarwinObjectPtr& operator=(const DarwinObjectPtr& aOther) { + DarwinObjectPtr ptr = aOther; + swap(ptr); + return *this; + } + + DarwinObjectPtr& operator=(DarwinObjectPtr&& aOther) { + DarwinObjectPtr ptr = std::move(aOther); + swap(ptr); + return *this; + } + + DarwinObjectPtr& operator=(std::nullptr_t) { + if (mPtr) { + ReleaseDarwinObject(mPtr); + } + mPtr = nullptr; + return *this; + } + + DarwinObjectPtr& operator=(T aOther) { + DarwinObjectPtr ptr = std::move(aOther); + swap(ptr); + return *this; + } + + void swap(DarwinObjectPtr& aOther) { std::swap(mPtr, aOther.mPtr); } + + [[nodiscard]] T forget() { return std::exchange(mPtr, nullptr); } + + friend DarwinObjectPtr AdoptDarwinObject<T>(T); + + private: + struct AdoptDarwinObjectTag {}; + DarwinObjectPtr(AdoptDarwinObjectTag, T aPtr) : mPtr(std::move(aPtr)) {} + + T mPtr; +}; + +template <typename T> +inline DarwinObjectPtr<T> AdoptDarwinObject(T aPtr) { + return DarwinObjectPtr<T>{typename DarwinObjectPtr<T>::AdoptDarwinObjectTag{}, + std::move(aPtr)}; +} + +} // namespace mozilla + +#endif diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index 16870a9dbc..b7779a67db 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -41,6 +41,7 @@ if CONFIG["OS_ARCH"] == "Darwin": "nsObjCExceptions.h", ] EXPORTS.mozilla += [ + "DarwinObjectPtr.h", "MacStringHelpers.h", ] UNIFIED_SOURCES += [ diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 9c39dee475..7908fe8037 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -159,6 +159,7 @@ #include "mozilla/HashFunctions.h" #include "mozilla/HashTable.h" #include "mozilla/HoldDropJSObjects.h" +#include "mozilla/Maybe.h" /* This must occur *after* base/process_util.h to avoid typedefs conflicts. */ #include <stdint.h> #include <stdio.h> @@ -269,6 +270,11 @@ static void SuspectUsingNurseryPurpleBuffer( // MOZ_CC_LOG_DIRECTORY: The directory in which logs are placed (such as // logs from MOZ_CC_LOG_ALL and MOZ_CC_LOG_SHUTDOWN, or other uses // of nsICycleCollectorListener) +// +// MOZ_CC_DISABLE_GC_LOG: If defined, don't make a GC log whenever we make a +// cycle collector log. This can be useful for leaks that go away when shutdown +// gets slower, when the JS heap is not involved in the leak. The default is to +// make the GC log. // Various parameters of this collector can be tuned using environment // variables. @@ -279,13 +285,15 @@ struct nsCycleCollectorParams { bool mAllTracesAll; bool mAllTracesShutdown; bool mLogThisThread; + bool mLogGC; int32_t mLogShutdownSkip = 0; nsCycleCollectorParams() : mLogAll(PR_GetEnv("MOZ_CC_LOG_ALL") != nullptr), mLogShutdown(PR_GetEnv("MOZ_CC_LOG_SHUTDOWN") != nullptr), mAllTracesAll(false), - mAllTracesShutdown(false) { + mAllTracesShutdown(false), + mLogGC(!PR_GetEnv("MOZ_CC_DISABLE_GC_LOG")) { if (const char* lssEnv = PR_GetEnv("MOZ_CC_LOG_SHUTDOWN_SKIP")) { mLogShutdown = true; nsDependentCString lssString(lssEnv); @@ -352,6 +360,8 @@ struct nsCycleCollectorParams { bool AllTracesThisCC(bool aIsShutdown) { return mAllTracesAll || (aIsShutdown && mAllTracesShutdown); } + + bool LogThisGC() const { return mLogGC; } }; #ifdef COLLECT_TIME_DEBUG @@ -1376,10 +1386,12 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { public: NS_DECL_ISUPPORTS - nsCycleCollectorLogSinkToFile() - : mProcessIdentifier(base::GetCurrentProcId()), - mGCLog("gc-edges"), - mCCLog("cc-edges") {} + explicit nsCycleCollectorLogSinkToFile(bool aLogGC) + : mProcessIdentifier(base::GetCurrentProcId()), mCCLog("cc-edges") { + if (aLogGC) { + mGCLog.emplace("gc-edges"); + } + } NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override { aIdentifier = mFilenameIdentifier; @@ -1402,7 +1414,10 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { } NS_IMETHOD GetGcLog(nsIFile** aPath) override { - NS_IF_ADDREF(*aPath = mGCLog.mFile); + if (mGCLog.isNothing()) { + return NS_ERROR_UNEXPECTED; + } + NS_IF_ADDREF(*aPath = mGCLog.ref().mFile); return NS_OK; } @@ -1414,13 +1429,21 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override { nsresult rv; - if (mGCLog.mStream || mCCLog.mStream) { + if (mCCLog.mStream) { return NS_ERROR_UNEXPECTED; } - rv = OpenLog(&mGCLog); - NS_ENSURE_SUCCESS(rv, rv); - *aGCLog = mGCLog.mStream; + if (mGCLog.isSome()) { + if (mGCLog.ref().mStream) { + return NS_ERROR_UNEXPECTED; + } + + rv = OpenLog(&mGCLog.ref()); + NS_ENSURE_SUCCESS(rv, rv); + *aGCLog = mGCLog.ref().mStream; + } else { + *aGCLog = nullptr; + } rv = OpenLog(&mCCLog); NS_ENSURE_SUCCESS(rv, rv); @@ -1430,10 +1453,13 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { } NS_IMETHOD CloseGCLog() override { - if (!mGCLog.mStream) { + if (mGCLog.isNothing()) { + return NS_OK; + } + if (!mGCLog.ref().mStream) { return NS_ERROR_UNEXPECTED; } - CloseLog(&mGCLog, u"Garbage"_ns); + CloseLog(&mGCLog.ref(), u"Garbage"_ns); return NS_OK; } @@ -1447,9 +1473,9 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { private: ~nsCycleCollectorLogSinkToFile() { - if (mGCLog.mStream) { - MozillaUnRegisterDebugFILE(mGCLog.mStream); - fclose(mGCLog.mStream); + if (mGCLog.isSome() && mGCLog.ref().mStream) { + MozillaUnRegisterDebugFILE(mGCLog.ref().mStream); + fclose(mGCLog.ref().mStream); } if (mCCLog.mStream) { MozillaUnRegisterDebugFILE(mCCLog.mStream); @@ -1566,7 +1592,7 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink { int32_t mProcessIdentifier; nsString mFilenameIdentifier; - FileInfo mGCLog; + Maybe<FileInfo> mGCLog; FileInfo mCCLog; }; @@ -1576,8 +1602,8 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener { ~nsCycleCollectorLogger() { ClearDescribers(); } public: - nsCycleCollectorLogger() - : mLogSink(nsCycleCollector_createLogSink()), + explicit nsCycleCollectorLogger(bool aLogGC) + : mLogSink(nsCycleCollector_createLogSink(aLogGC)), mWantAllTraces(false), mDisableLog(false), mWantAfterProcessing(false), @@ -1646,13 +1672,14 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener { rv = mLogSink->Open(&gcLog, &mCCLog); NS_ENSURE_SUCCESS(rv, rv); // Dump the JS heap. - CollectorData* data = sCollectorData.get(); - if (data && data->mContext) { - data->mContext->Runtime()->DumpJSHeap(gcLog); + if (gcLog) { + CollectorData* data = sCollectorData.get(); + if (data && data->mContext) { + data->mContext->Runtime()->DumpJSHeap(gcLog); + } + rv = mLogSink->CloseGCLog(); + NS_ENSURE_SUCCESS(rv, rv); } - rv = mLogSink->CloseGCLog(); - NS_ENSURE_SUCCESS(rv, rv); - fprintf(mCCLog, "# WantAllTraces=%s\n", mWantAllTraces ? "true" : "false"); return NS_OK; } @@ -1822,7 +1849,8 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener { NS_IMPL_ISUPPORTS(nsCycleCollectorLogger, nsICycleCollectorListener) already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger() { - nsCOMPtr<nsICycleCollectorListener> logger = new nsCycleCollectorLogger(); + nsCOMPtr<nsICycleCollectorListener> logger = + new nsCycleCollectorLogger(/* aLogGC = */ true); return logger.forget(); } @@ -3283,7 +3311,11 @@ void nsCycleCollector::SuspectNurseryEntries() { while (gNurseryPurpleBufferEntryCount) { NurseryPurpleBufferEntry& entry = gNurseryPurpleBufferEntry[--gNurseryPurpleBufferEntryCount]; - mPurpleBuf.Put(entry.mPtr, entry.mParticipant, entry.mRefCnt); + if (!entry.mRefCnt->IsPurple() && IsIdle()) { + entry.mRefCnt->RemoveFromPurpleBuffer(); + } else { + mPurpleBuf.Put(entry.mPtr, entry.mParticipant, entry.mRefCnt); + } } } @@ -3628,7 +3660,7 @@ void nsCycleCollector::BeginCollection( aManualListener = nullptr; if (!mLogger && mParams.LogThisCC(mShutdownCount)) { - mLogger = new nsCycleCollectorLogger(); + mLogger = new nsCycleCollectorLogger(mParams.LogThisGC()); if (mParams.AllTracesThisCC(isShutdown)) { mLogger->SetAllTraces(); } @@ -3963,8 +3995,10 @@ bool nsCycleCollector_doDeferredDeletionWithBudget(js::SliceBudget& aBudget) { return data->mCollector->FreeSnowWhiteWithBudget(aBudget); } -already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink() { - nsCOMPtr<nsICycleCollectorLogSink> sink = new nsCycleCollectorLogSinkToFile(); +already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink( + bool aLogGC) { + nsCOMPtr<nsICycleCollectorLogSink> sink = + new nsCycleCollectorLogSinkToFile(aLogGC); return sink.forget(); } diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h index 1c583e04cf..e25647175e 100644 --- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -49,7 +49,8 @@ void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false, bool nsCycleCollector_doDeferredDeletion(); bool nsCycleCollector_doDeferredDeletionWithBudget(js::SliceBudget& aBudget); -already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink(); +already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink( + bool aLogGC); already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger(); // Run a cycle collection and return whether anything was collected. diff --git a/xpcom/base/nsICycleCollectorListener.idl b/xpcom/base/nsICycleCollectorListener.idl index 5e0b059a13..c93f3c9174 100644 --- a/xpcom/base/nsICycleCollectorListener.idl +++ b/xpcom/base/nsICycleCollectorListener.idl @@ -72,8 +72,11 @@ interface nsICycleCollectorHandler : nsISupports [scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)] interface nsICycleCollectorLogSink : nsISupports { + // aGCLog can be null, if the sink was not configured to create a GC log. [noscript] void open(out FILE aGCLog, out FILE aCCLog); + void closeGCLog(); + void closeCCLog(); // This string will appear somewhere in the log's filename. diff --git a/xpcom/base/nsIDebug2.idl b/xpcom/base/nsIDebug2.idl index 4672fea7ea..f60a8eafa5 100644 --- a/xpcom/base/nsIDebug2.idl +++ b/xpcom/base/nsIDebug2.idl @@ -33,7 +33,7 @@ interface nsIDebug2 : nsISupports * Whether a debugger is currently attached. * Supports Windows + Mac */ - readonly attribute bool isDebuggerAttached; + readonly attribute boolean isDebuggerAttached; /** * Show an assertion and trigger nsIDebug2.break(). diff --git a/xpcom/base/nsIMacPreferencesReader.idl b/xpcom/base/nsIMacPreferencesReader.idl index 470fd92ec9..29461f862a 100644 --- a/xpcom/base/nsIMacPreferencesReader.idl +++ b/xpcom/base/nsIMacPreferencesReader.idl @@ -22,7 +22,7 @@ interface nsIMacPreferencesReader : nsISupports * * @return true if macOS policies are enabled, false otherwise. */ - bool policiesEnabled(); + boolean policiesEnabled(); /** * This method reads and returns the macOS preferences. diff --git a/xpcom/base/nsIMemoryInfoDumper.idl b/xpcom/base/nsIMemoryInfoDumper.idl index 79b03a709d..83290b57ab 100644 --- a/xpcom/base/nsIMemoryInfoDumper.idl +++ b/xpcom/base/nsIMemoryInfoDumper.idl @@ -36,7 +36,7 @@ interface nsIDumpGCAndCCLogsCallback : nsISupports */ void onDump(in nsIFile aGCLog, in nsIFile aCCLog, - in bool aIsParent); + in boolean aIsParent); /** * Called when GC/CC logging has finished, after all calls to |onDump|. @@ -153,8 +153,8 @@ interface nsIMemoryInfoDumper : nsISupports * */ void dumpGCAndCCLogsToFile(in AString aIdentifier, - in bool aDumpAllTraces, - in bool aDumpChildProcesses, + in boolean aDumpAllTraces, + in boolean aDumpChildProcesses, in nsIDumpGCAndCCLogsCallback aCallback); /** @@ -162,6 +162,6 @@ interface nsIMemoryInfoDumper : nsISupports * sink object instead of accessing the filesystem directly, and * dumps the current process only. */ - void dumpGCAndCCLogsToSink(in bool aDumpAllTraces, + void dumpGCAndCCLogsToSink(in boolean aDumpAllTraces, in nsICycleCollectorLogSink aSink); }; diff --git a/xpcom/base/nsINIParser.cpp b/xpcom/base/nsINIParser.cpp index 1447aa6fbf..df3f1fcc0d 100644 --- a/xpcom/base/nsINIParser.cpp +++ b/xpcom/base/nsINIParser.cpp @@ -243,7 +243,6 @@ nsresult nsINIParser::DeleteString(const char* aSection, const char* aKey) { mSections.Remove(aSection); } else { mSections.InsertOrUpdate(aSection, std::move(val->next)); - delete val; } return NS_OK; } diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index 28af408178..433919146a 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -323,7 +323,7 @@ nsMemoryInfoDumper::DumpGCAndCCLogsToFile( for (uint32_t i = 0; i < children.Length(); i++) { ContentParent* cp = children[i]; nsCOMPtr<nsICycleCollectorLogSink> logSink = - nsCycleCollector_createLogSink(); + nsCycleCollector_createLogSink(/* aLogGC = */ true); logSink->SetFilenameIdentifier(identifier); logSink->SetProcessIdentifier(cp->Pid()); diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index aaf1baeea2..a3a2a247f6 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1247,7 +1247,7 @@ NS_IMPL_ISUPPORTS(PageFaultsHardReporter, nsIMemoryReporter) #ifdef HAVE_JEMALLOC_STATS static size_t HeapOverhead(const jemalloc_stats_t& aStats) { - return aStats.waste + aStats.bookkeeping + aStats.page_cache + + return aStats.waste + aStats.bookkeeping + aStats.pages_dirty + aStats.bin_unused; } @@ -1278,7 +1278,7 @@ class JemallocHeapReporter final : public nsIMemoryReporter { // clang-format off MOZ_COLLECT_REPORT( - "heap-committed/allocated", KIND_OTHER, UNITS_BYTES, stats.allocated, + "heap/committed/allocated", KIND_OTHER, UNITS_BYTES, stats.allocated, "Memory mapped by the heap allocator that is currently allocated to the " "application. This may exceed the amount of memory requested by the " "application because the allocator regularly rounds up request sizes. (The " @@ -1286,14 +1286,14 @@ class JemallocHeapReporter final : public nsIMemoryReporter { MOZ_COLLECT_REPORT( "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated, -"The same as 'heap-committed/allocated'."); +"The same as 'heap/committed/allocated'."); - // We mark this and the other heap-overhead reporters as KIND_NONHEAP + // We mark this and the other heap/committed/overhead reporters as KIND_NONHEAP // because KIND_HEAP memory means "counted in heap-allocated", which // this is not. for (auto& bin : bin_stats) { MOZ_ASSERT(bin.size); - nsPrintfCString path("explicit/heap-overhead/bin-unused/bin-%zu", + nsPrintfCString path("heap/committed/bin-unused/bin-%zu", bin.size); aHandleReport->Callback(EmptyCString(), path, KIND_NONHEAP, UNITS_BYTES, bin.bytes_unused, @@ -1304,36 +1304,63 @@ class JemallocHeapReporter final : public nsIMemoryReporter { if (stats.waste > 0) { MOZ_COLLECT_REPORT( - "explicit/heap-overhead/waste", KIND_NONHEAP, UNITS_BYTES, + "heap/committed/waste", KIND_NONHEAP, UNITS_BYTES, stats.waste, "Committed bytes which do not correspond to an active allocation and which the " "allocator is not intentionally keeping alive (i.e., not " -"'explicit/heap-overhead/{bookkeeping,page-cache,bin-unused}')."); +"'heap/{bookkeeping,unused-pages,bin-unused}')."); } MOZ_COLLECT_REPORT( - "explicit/heap-overhead/bookkeeping", KIND_NONHEAP, UNITS_BYTES, + "heap/committed/bookkeeping", KIND_NONHEAP, UNITS_BYTES, stats.bookkeeping, "Committed bytes which the heap allocator uses for internal data structures."); MOZ_COLLECT_REPORT( - "explicit/heap-overhead/page-cache", KIND_NONHEAP, UNITS_BYTES, - stats.page_cache, + "heap/committed/unused-pages/dirty", KIND_NONHEAP, UNITS_BYTES, + stats.pages_dirty, "Memory which the allocator could return to the operating system, but hasn't. " "The allocator keeps this memory around as an optimization, so it doesn't " "have to ask the OS the next time it needs to fulfill a request. This value " "is typically not larger than a few megabytes."); MOZ_COLLECT_REPORT( - "heap-committed/overhead", KIND_OTHER, UNITS_BYTES, - HeapOverhead(stats), -"The sum of 'explicit/heap-overhead/*'."); + "heap/decommitted/unused-pages/fresh", KIND_OTHER, UNITS_BYTES, stats.pages_fresh, +"Amount of memory currently mapped but has never been used."); + // A duplicate entry in the decommitted part of the tree. + MOZ_COLLECT_REPORT( + "decommitted/heap/unused-pages/fresh", KIND_OTHER, UNITS_BYTES, stats.pages_fresh, +"Amount of memory currently mapped but has never been used."); +// On MacOS madvised memory is still counted in the resident set until the OS +// actually decommits it. +#ifdef XP_MACOSX +#define MADVISED_GROUP "committed" +#else +#define MADVISED_GROUP "decommitted" +#endif + MOZ_COLLECT_REPORT( + "heap/" MADVISED_GROUP "/unused-pages/madvised", KIND_OTHER, UNITS_BYTES, + stats.pages_madvised, +"Amount of memory currently mapped, not used and that the OS should remove " +"from the application's resident set."); + // A duplicate entry in the decommitted part of the tree. MOZ_COLLECT_REPORT( - "heap-mapped", KIND_OTHER, UNITS_BYTES, stats.mapped, -"Amount of memory currently mapped. Includes memory that is uncommitted, i.e. " -"neither in physical memory nor paged to disk."); + "decommitted/heap/unused-pages/madvised", KIND_OTHER, UNITS_BYTES, stats.pages_madvised, +"Amount of memory currently mapped, not used and that the OS should remove " +"from the application's resident set."); + { + size_t decommitted = stats.mapped - stats.allocated - stats.waste - stats.pages_dirty - stats.pages_fresh - stats.bookkeeping - stats.bin_unused; + MOZ_COLLECT_REPORT( + "heap/decommitted/unmapped", KIND_OTHER, UNITS_BYTES, decommitted, + "Amount of memory currently mapped but not committed, " + "neither in physical memory nor paged to disk."); + MOZ_COLLECT_REPORT( + "decommitted/heap/decommitted", KIND_OTHER, UNITS_BYTES, decommitted, + "Amount of memory currently mapped but not committed, " + "neither in physical memory nor paged to disk."); + } MOZ_COLLECT_REPORT( "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize, "Size of chunks."); @@ -1343,11 +1370,11 @@ class JemallocHeapReporter final : public nsIMemoryReporter { mozilla::phc::PHCMemoryUsage(usage); MOZ_COLLECT_REPORT( - "explicit/heap-overhead/phc/metadata", KIND_NONHEAP, UNITS_BYTES, + "explicit/phc/metadata", KIND_NONHEAP, UNITS_BYTES, usage.mMetadataBytes, "Memory used by PHC to store stacks and other metadata for each allocation"); MOZ_COLLECT_REPORT( - "explicit/heap-overhead/phc/fragmentation", KIND_NONHEAP, UNITS_BYTES, + "explicit/phc/fragmentation", KIND_NONHEAP, UNITS_BYTES, usage.mFragmentationBytes, "The amount of memory lost due to rounding up allocations to the next page " "size. " diff --git a/xpcom/base/nsrootidl.idl b/xpcom/base/nsrootidl.idl index 292ea54890..364d0b75eb 100644 --- a/xpcom/base/nsrootidl.idl +++ b/xpcom/base/nsrootidl.idl @@ -11,7 +11,6 @@ #include "nscore.h" #include "nsID.h" -typedef int64_t PRTime; /* * Forward declarations for new string types @@ -29,27 +28,9 @@ class Promise; } // namespace dom } // namespace mozilla -/* - * Start commenting out the C++ versions of the below in the output header - */ -#if 0 %} -// [substitute] typedefs emit the underlying builtin type directly, and -// avoid polluting bindings for other languages with C++ stdint types. - -[substitute] typedef boolean bool ; -[substitute] typedef octet uint8_t ; -[substitute] typedef unsigned short uint16_t ; -[substitute] typedef unsigned long uint32_t ; -[substitute] typedef unsigned long long uint64_t ; -[substitute] typedef short int16_t ; -[substitute] typedef long int32_t ; -[substitute] typedef long long int64_t ; - - typedef unsigned short char16_t ; - typedef unsigned long nsresult ; - typedef long long PRTime ; +typedef long long PRTime; // If we ever want to use `size_t` in scriptable interfaces, this will need to // be built into the xpidl compiler, as the size varies based on platform. @@ -99,10 +80,3 @@ class Promise; native jsid(jsid); [ptr, promise] native Promise(ignored); - -%{C++ -/* - * End commenting out the C++ versions of the above in the output header - */ -#endif -%} diff --git a/xpcom/build/PoisonIOInterposerMac.cpp b/xpcom/build/PoisonIOInterposerMac.cpp index 21eca58757..46dc751221 100644 --- a/xpcom/build/PoisonIOInterposerMac.cpp +++ b/xpcom/build/PoisonIOInterposerMac.cpp @@ -328,10 +328,17 @@ void InitPoisonIOInterposer() { if (!d->Function) { continue; } + + // Disable the interposer on arm64 until there's + // a mach_override_ptr implementation. #ifndef __aarch64__ + // Temporarily disable the interposer on macOS x64 + // while dynamic code disablement rides the trains. +# ifdef MOZ_INTERPOSER_FORCE_MACOS_X64 DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper, &d->Buffer); MOZ_ASSERT(t == err_none); +# endif // MOZ_INTERPOSER_FORCE_MACOS_X64 #endif } } diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp index 4d6572a501..6275ca9c53 100644 --- a/xpcom/build/XPCOMInit.cpp +++ b/xpcom/build/XPCOMInit.cpp @@ -233,6 +233,11 @@ static void InitializeJS() { JS::SetAVXEnabled(mozilla::StaticPrefs::javascript_options_wasm_simd_avx()); #endif + if (XRE_IsParentProcess() && + mozilla::StaticPrefs::javascript_options_main_process_disable_jit()) { + JS::DisableJitBackend(); + } + // Set all JS::Prefs. SET_JS_PREFS_FROM_BROWSER_PREFS; @@ -320,6 +325,8 @@ NS_InitXPCOM(nsIServiceManager** aResult, nsIFile* aBinDirectory, if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Initialise the profiler AUTO_PROFILER_INIT2; // Set up the timer globals/timer thread @@ -459,6 +466,11 @@ NS_InitXPCOM(nsIServiceManager** aResult, nsIFile* aBinDirectory, // to the directory service. nsDirectoryService::gService->RegisterCategoryProviders(); + // Now that both the profiler and directory services have been started + // we can find the download directory, where the profiler can write + // profiles if necessary + profiler_lookup_download_directory(); + // Init mozilla::SharedThreadPool (which needs the service manager). mozilla::SharedThreadPool::InitStatics(); diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl index 1ebef34ecf..268c2a28e6 100644 --- a/xpcom/components/nsIClassInfo.idl +++ b/xpcom/components/nsIClassInfo.idl @@ -28,7 +28,7 @@ interface nsIClassInfo : nsISupports * Return an object to assist XPConnect in supplying JavaScript-specific * behavior to callers of the instance object, or null if not needed. */ - nsIXPCScriptable getScriptableHelper(); + [noscript] nsIXPCScriptable getScriptableHelper(); /** * A contract ID through which an instance of this class can be created diff --git a/xpcom/docs/index.rst b/xpcom/docs/index.rst index bf4e3157c4..09a51835bc 100644 --- a/xpcom/docs/index.rst +++ b/xpcom/docs/index.rst @@ -12,6 +12,7 @@ These pages contain documentation for Mozilla's Cross-Platform Component Object thread-safety huntingleaks collections + sorting.md xpidl writing-xpcom-interface hashtables diff --git a/xpcom/docs/sorting.md b/xpcom/docs/sorting.md new file mode 100644 index 0000000000..0ac7cbc05a --- /dev/null +++ b/xpcom/docs/sorting.md @@ -0,0 +1,75 @@ +# Sorting and `nsTArray` + +## `std::sort` and friends + +Given that sorting is hard and in order to leave the hardest part to others, we base the ``nsTArray`` sort functions on ``std::sort`` and friends. + +The standard sorting functions are implemented as type safe templates, allowing for aggressive compiler optimizations (mostly inlining). The std libraries provide three different sort implementations: + +### ``std::sort`` or ``nsTArray<T>::Sort`` + + Advantages: + + - It provides both fast average performance and (asymptotically) optimal worst-case performance. + + Caveats: + + - It may call the comparator function for the exact same element, expecting it returns ``false`` in that case ("a not lower than a"); + - The order of elements considered equal may change wrt the initial state. + - It uses the move assignment/construction operators eventually defined on the element's class, which may have unexpected side effects if they are not trivial. + +### ``std::make/sort_heap`` + + We do not provide a wrapper in ``nsTArray`` for heap sort. It is assumed that ``std::sort`` is the better choice, anyways. + +### ``std::stable_sort`` or ``nsTArray<T>::StableSort`` + + Advantages: + + - It preserves the order of equal elements before and after sorting. + + Caveats: + + - Might try to (fallible) allocate additional temporary memory to reduce its overhead. + - Potentially slow, O(N·(log(N)<sup>2</sup>)) if no additional memory can be used. + +## Some more implementation notes + +It is important to mention that different usage scenarios may still require +different approaches. While O(n*log(n)) of ``std::sort`` is optimal from an algorithmically point of view, the design of the element classes used or the frequency of calling ``Sort`` itself can influence performance significantly, in particular for higher N. Let's look at some typical use cases. + +### If the list is expected to be always sorted + - If appending of items is sparse - use ``InsertElementSorted`` which has + O(log(N)) complexity to determine the position but might end up needing + up to N-1 element moves on each insert (or the equivalent ``memmove``). + - If instead you do bulk addings of N items, you should just sort once + afterwards. Check for outer loops that might still result in multiple + ``Sort`` calls without ever reading the array in between. + - If the frequency of changes is high and random - consider moving away from + ``nsTArray`` in favor of ``LinkedList``. + +### If we just need a short-living, sorted representation from time to time + - The elements are small and simple - just sort the array itself when needed. + - Otherwise use a temporary list containing ``T*`` or ``RefPtr<T>`` or an + index into the original array and sort that. + +### If the items are held only by the list +- The items ``T`` of the list are huge and/or complex - consider using a ``UniquePtr<T>`` list with explicit creation of the object at each insert. If the frequency of changes to the list is not very high, the overhead for the single allocations and deletes is probably bearable (and comparable to a ``LinkedList``). + +### If the comparator function is expensive +- There is an easy way to transform the calculation into a scalar - precalculate it and have an additional field or an extra array for that information. + +### If the sorting is expected to be stable +- Elements considered equal by the comparator are expected to keep their relative order - do not try to add clever conditions to the comparator yourself but use ``nsTArray<T>::StableSort`` (or ``std::stable_sort``) to get the best performance. + +### *Remember:* The sort condition must be stable in every regard +``std::sort`` reacts very bad on unstable conditions. The comparator must: + - Yield the same result before and after moving an entry in the array. + - Yield the inverse result (well, except for equality) if the input elements are just swapped. + - Be tolerant for being called to compare the very same element with itself. + - The comparator function itself shall not change the state of any element + in a way that might affect the comparison results. + - During sort execution, no other thread shall change objects in or referenced by elements in the array in a way that might affect the comparison results. + +### *Remember:* Mutating operations on ``nsTArray`` are not atomic +This is also true for `Sort` and `StableSort`, of course. If you want to use the same ``nsTArray`` instance from different threads, you need to ensure the synchronization yourself. diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 2a0a25431e..5be3323986 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -634,7 +634,6 @@ STATIC_ATOMS = [ Atom("lowest", "lowest"), Atom("lowsrc", "lowsrc"), Atom("ltr", "ltr"), - Atom("lwtheme", "lwtheme"), Atom("main", "main"), Atom("map", "map"), Atom("manifest", "manifest"), @@ -773,7 +772,6 @@ STATIC_ATOMS = [ Atom("onbeforeunload", "onbeforeunload"), Atom("onblocked", "onblocked"), Atom("onblur", "onblur"), - Atom("onbounce", "onbounce"), Atom("onboundschange", "onboundschange"), Atom("onbroadcast", "onbroadcast"), Atom("onbufferedamountlow", "onbufferedamountlow"), @@ -929,6 +927,7 @@ STATIC_ATOMS = [ Atom("ontypechange", "ontypechange"), Atom("onterminate", "onterminate"), Atom("ontext", "ontext"), + Atom("ontextInput", "ontextInput"), Atom("ontoggle", "ontoggle"), Atom("ontonechange", "ontonechange"), Atom("ontouchstart", "ontouchstart"), @@ -2253,6 +2252,7 @@ STATIC_ATOMS = [ Atom("_moz_windows_accent_color_in_tabs", "-moz-windows-accent-color-in-tabs"), Atom("_moz_mac_big_sur_theme", "-moz-mac-big-sur-theme"), Atom("_moz_mac_rtl", "-moz-mac-rtl"), + Atom("_moz_mac_titlebar_height", "-moz-mac-titlebar-height"), Atom("_moz_platform", "-moz-platform"), Atom("_moz_gtk_theme_family", "-moz-gtk-theme-family"), Atom("_moz_menubar_drag", "-moz-menubar-drag"), @@ -2487,6 +2487,7 @@ STATIC_ATOMS = [ PseudoElementAtom("PseudoElement_firstLine", ":first-line"), PseudoElementAtom("PseudoElement_highlight", ":highlight"), PseudoElementAtom("PseudoElement_selection", ":selection"), + PseudoElementAtom("PseudoElement_targetText", ":target-text"), PseudoElementAtom("PseudoElement_mozFocusInner", ":-moz-focus-inner"), PseudoElementAtom("PseudoElement_mozNumberSpinBox", ":-moz-number-spin-box"), PseudoElementAtom("PseudoElement_mozNumberSpinUp", ":-moz-number-spin-up"), diff --git a/xpcom/ds/moz.build b/xpcom/ds/moz.build index 91833b5558..29622b6915 100644 --- a/xpcom/ds/moz.build +++ b/xpcom/ds/moz.build @@ -65,7 +65,6 @@ EXPORTS += [ "nsPersistentProperties.h", "nsPointerHashKeys.h", "nsProperties.h", - "nsQuickSort.h", "nsRefCountedHashtable.h", "nsRefPtrHashtable.h", "nsSimpleEnumerator.h", @@ -120,7 +119,6 @@ UNIFIED_SOURCES += [ "nsObserverService.cpp", "nsPersistentProperties.cpp", "nsProperties.cpp", - "nsQuickSort.cpp", "nsSimpleEnumerator.cpp", "nsStaticNameTable.cpp", "nsStringEnumerator.cpp", diff --git a/xpcom/ds/nsQuickSort.cpp b/xpcom/ds/nsQuickSort.cpp deleted file mode 100644 index 1fb3dede88..0000000000 --- a/xpcom/ds/nsQuickSort.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * 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. - */ - -/* We need this because Solaris' version of qsort is broken and - * causes array bounds reads. - */ - -#include <stdlib.h> -#include "nsAlgorithm.h" -#include "nsQuickSort.h" - -extern "C" { - -#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc)) -# ifndef INLINE -# define INLINE inline -# endif -#else -# define INLINE -#endif - -typedef int cmp_t(const void*, const void*, void*); -static INLINE char* med3(char*, char*, char*, cmp_t*, void*); -static INLINE void swapfunc(char*, char*, int, int); - -/* - * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". - */ -#define swapcode(TYPE, parmi, parmj, n) \ - { \ - long i = (n) / sizeof(TYPE); \ - TYPE* pi = (TYPE*)(parmi); \ - TYPE* pj = (TYPE*)(parmj); \ - do { \ - TYPE t = *pi; \ - *pi++ = *pj; \ - *pj++ = t; \ - } while (--i > 0); \ - } - -#define SWAPINIT(a, es) \ - swaptype = ((char*)a - (char*)0) % sizeof(long) || es % sizeof(long) ? 2 \ - : es == sizeof(long) ? 0 \ - : 1; - -static INLINE void swapfunc(char* a, char* b, int n, int swaptype) { - if (swaptype <= 1) swapcode(long, a, b, n) else swapcode(char, a, b, n) -} - -#define swap(a, b) \ - if (swaptype == 0) { \ - long t = *(long*)(a); \ - *(long*)(a) = *(long*)(b); \ - *(long*)(b) = t; \ - } else \ - swapfunc((char*)a, (char*)b, (int)es, swaptype) - -#define vecswap(a, b, n) \ - if ((n) > 0) swapfunc((char*)a, (char*)b, (int)n, swaptype) - -static INLINE char* med3(char* a, char* b, char* c, cmp_t* cmp, void* data) { - return cmp(a, b, data) < 0 - ? (cmp(b, c, data) < 0 ? b : (cmp(a, c, data) < 0 ? c : a)) - : (cmp(b, c, data) > 0 ? b : (cmp(a, c, data) < 0 ? a : c)); -} - -void NS_QuickSort(void* a, unsigned int n, unsigned int es, cmp_t* cmp, - void* data) { - char *pa, *pb, *pc, *pd, *pl, *pm, *pn; - int d, r, swaptype; - -loop: - SWAPINIT(a, es); - /* Use insertion sort when input is small */ - if (n < 7) { - for (pm = (char*)a + es; pm < (char*)a + n * es; pm += es) - for (pl = pm; pl > (char*)a && cmp(pl - es, pl, data) > 0; pl -= es) - swap(pl, pl - es); - return; - } - /* Choose pivot */ - pm = (char*)a + (n / 2) * es; - if (n > 7) { - pl = (char*)a; - pn = (char*)a + (n - 1) * es; - if (n > 40) { - d = (n / 8) * es; - pl = med3(pl, pl + d, pl + 2 * d, cmp, data); - pm = med3(pm - d, pm, pm + d, cmp, data); - pn = med3(pn - 2 * d, pn - d, pn, cmp, data); - } - pm = med3(pl, pm, pn, cmp, data); - } - swap(a, pm); - pa = pb = (char*)a + es; - - pc = pd = (char*)a + (n - 1) * es; - /* loop invariants: - * [a, pa) = pivot - * [pa, pb) < pivot - * [pb, pc + es) unprocessed - * [pc + es, pd + es) > pivot - * [pd + es, pn) = pivot - */ - for (;;) { - while (pb <= pc && (r = cmp(pb, a, data)) <= 0) { - if (r == 0) { - swap(pa, pb); - pa += es; - } - pb += es; - } - while (pb <= pc && (r = cmp(pc, a, data)) >= 0) { - if (r == 0) { - swap(pc, pd); - pd -= es; - } - pc -= es; - } - if (pb > pc) break; - swap(pb, pc); - pb += es; - pc -= es; - } - /* Move pivot values */ - pn = (char*)a + n * es; - r = XPCOM_MIN(pa - (char*)a, pb - pa); - vecswap(a, pb - r, r); - r = XPCOM_MIN<size_t>(pd - pc, pn - pd - es); - vecswap(pb, pn - r, r); - /* Recursively process partitioned items */ - if ((r = pb - pa) > (int)es) NS_QuickSort(a, r / es, es, cmp, data); - if ((r = pd - pc) > (int)es) { - /* Iterate rather than recurse to save stack space */ - a = pn - r; - n = r / es; - goto loop; - } - /* NS_QuickSort(pn - r, r / es, es, cmp, data);*/ -} -} - -#undef INLINE -#undef swapcode -#undef SWAPINIT -#undef swap -#undef vecswap diff --git a/xpcom/ds/nsQuickSort.h b/xpcom/ds/nsQuickSort.h deleted file mode 100644 index 6526c3a205..0000000000 --- a/xpcom/ds/nsQuickSort.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- 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/. */ - -/* We need this because Solaris' version of qsort is broken and - * causes array bounds reads. - */ - -#ifndef nsQuickSort_h___ -#define nsQuickSort_h___ - -#include "nscore.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * You most probably want to not use this but std::sort or std::stable_sort - * It will be removed eventually from the tree, see bug 1839052. - * - * Quicksort is recursive without limits and thus may make overflow the stack. - * And while being close to optimal for randomized data for some edge cases - * it can reach quadratic performance O(n*n) instead of O(n*log(n)). - * - * Parameters: - * 1. the array to sort - * 2. the number of elements in the array - * 3. the size of each array element - * 4. comparison function taking two elements and parameter #5 and - * returning an integer: - * + less than zero if the first element should be before the second - * + 0 if the order of the elements does not matter - * + greater than zero if the second element should be before the first - * 5. extra data to pass to comparison function - */ -[[deprecated("Use std::sort or std::make/sort_heap instead.")]] void -NS_QuickSort(void*, unsigned int, unsigned int, - int (*)(const void*, const void*, void*), void*); - -#ifdef __cplusplus -} -#endif - -#endif /* nsQuickSort_h___ */ diff --git a/xpcom/ds/nsTArray.h b/xpcom/ds/nsTArray.h index f17624b721..56f926ccad 100644 --- a/xpcom/ds/nsTArray.h +++ b/xpcom/ds/nsTArray.h @@ -2379,9 +2379,10 @@ class nsTArray_Impl static_assert(std::is_move_constructible_v<value_type>); ::detail::CompareWrapper<Comparator, value_type> comp(aComp); - std::sort(begin(), end(), [&comp](const auto& left, const auto& right) { - return comp.LessThan(left, right); - }); + std::sort(Elements(), Elements() + Length(), + [&comp](const auto& left, const auto& right) { + return comp.LessThan(left, right); + }); } // A variation on the Sort method defined above that assumes that diff --git a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json index fb3cb7e6b7..087a323dd4 100644 --- a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json +++ b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json @@ -95,14 +95,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -259,14 +251,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -373,14 +357,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -434,14 +410,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -1223,14 +1191,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -1301,14 +1261,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] }, @@ -1388,14 +1340,6 @@ [ "PRTime", "i64" - ], - [ - "char16_t", - "u16" - ], - [ - "nsresult", - "u32" ] ] } diff --git a/xpcom/idl-parser/xpidl/header.py b/xpcom/idl-parser/xpidl/header.py index 7f6c65dfc0..ed179b1bad 100644 --- a/xpcom/idl-parser/xpidl/header.py +++ b/xpcom/idl-parser/xpidl/header.py @@ -339,8 +339,6 @@ def print_header(idl, fd, filename, relpath): write_interface(p, fd) continue if p.kind == "typedef": - if p.substitute: - continue printComments(fd, p.doccomments, "") fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType("in"), p.name)) diff --git a/xpcom/idl-parser/xpidl/jsonxpt.py b/xpcom/idl-parser/xpidl/jsonxpt.py index 342188f645..4a7fea7bf5 100644 --- a/xpcom/idl-parser/xpidl/jsonxpt.py +++ b/xpcom/idl-parser/xpidl/jsonxpt.py @@ -17,6 +17,7 @@ TypeMap = { # builtins "boolean": "TD_BOOL", "void": "TD_VOID", + "int8_t": "TD_INT8", "int16_t": "TD_INT16", "int32_t": "TD_INT32", "int64_t": "TD_INT64", @@ -24,19 +25,14 @@ TypeMap = { "uint16_t": "TD_UINT16", "uint32_t": "TD_UINT32", "uint64_t": "TD_UINT64", - "octet": "TD_UINT8", - "short": "TD_INT16", - "long": "TD_INT32", - "long long": "TD_INT64", - "unsigned short": "TD_UINT16", - "unsigned long": "TD_UINT32", - "unsigned long long": "TD_UINT64", + "nsresult": "TD_UINT32", "float": "TD_FLOAT", "double": "TD_DOUBLE", "char": "TD_CHAR", "string": "TD_PSTRING", "wchar": "TD_WCHAR", "wstring": "TD_PWSTRING", + "char16_t": "TD_UINT16", # special types "nsid": "TD_NSID", "astring": "TD_ASTRING", @@ -51,7 +47,7 @@ def flags(*flags): return [flag for flag, cond in flags if cond] -def get_type(type, calltype, iid_is=None, size_is=None): +def get_type(type, calltype, iid_is=None, size_is=None, needs_scriptable=None): while isinstance(type, xpidl.Typedef): type = type.realtype @@ -67,7 +63,7 @@ def get_type(type, calltype, iid_is=None, size_is=None): # This allows Arrays of InterfaceIs types to work. return { "tag": "TD_ARRAY", - "element": get_type(type.type, calltype, iid_is), + "element": get_type(type.type, calltype, iid_is, None, needs_scriptable), } if isinstance(type, xpidl.LegacyArray): @@ -76,10 +72,12 @@ def get_type(type, calltype, iid_is=None, size_is=None): return { "tag": "TD_LEGACY_ARRAY", "size_is": size_is, - "element": get_type(type.type, calltype, iid_is), + "element": get_type(type.type, calltype, iid_is, None, needs_scriptable), } if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward): + if isinstance(needs_scriptable, set): + needs_scriptable.add(type.name) return { "tag": "TD_INTERFACE_TYPE", "name": type.name, @@ -136,7 +134,7 @@ def mk_method(method, params, getter=0, setter=0, optargc=0, hasretval=0, symbol "flags": flags( ("getter", getter), ("setter", setter), - ("hidden", method.noscript or method.notxpcom), + ("hidden", not method.isScriptable()), ("optargc", optargc), ("jscontext", method.implicit_jscontext), ("hasretval", hasretval), @@ -167,6 +165,9 @@ def build_interface(iface): consts = [] methods = [] + # Interfaces referenced from scriptable members need to be [scriptable]. + needs_scriptable = set() + def build_const(c): consts.append( { @@ -186,7 +187,7 @@ def build_interface(iface): } ) - def build_method(m): + def build_method(m, needs_scriptable=None): params = [] for p in m.params: params.append( @@ -196,6 +197,7 @@ def build_interface(iface): p.paramtype, iid_is=attr_param_idx(p, m, "iid_is"), size_is=attr_param_idx(p, m, "size_is"), + needs_scriptable=needs_scriptable, ), in_=p.paramtype.count("in"), out=p.paramtype.count("out"), @@ -206,33 +208,36 @@ def build_interface(iface): hasretval = len(m.params) > 0 and m.params[-1].retval if not m.notxpcom and m.realtype.name != "void": hasretval = True - params.append(mk_param(get_type(m.realtype, "out"), out=1)) + type = get_type(m.realtype, "out", needs_scriptable=needs_scriptable) + params.append(mk_param(type, out=1)) methods.append( mk_method(m, params, optargc=m.optional_argc, hasretval=hasretval) ) - def build_attr(a): + def build_attr(a, needs_scriptable=None): assert a.realtype.name != "void" + # Write the getter getter_params = [] if not a.notxpcom: - getter_params.append(mk_param(get_type(a.realtype, "out"), out=1)) + type = get_type(a.realtype, "out", needs_scriptable=needs_scriptable) + getter_params.append(mk_param(type, out=1)) methods.append(mk_method(a, getter_params, getter=1, hasretval=1)) # And maybe the setter if not a.readonly: - param = mk_param(get_type(a.realtype, "in"), in_=1) - methods.append(mk_method(a, [param], setter=1)) + type = get_type(a.realtype, "in", needs_scriptable=needs_scriptable) + methods.append(mk_method(a, [mk_param(type, in_=1)], setter=1)) for member in iface.members: if isinstance(member, xpidl.ConstMember): build_const(member) elif isinstance(member, xpidl.Attribute): - build_attr(member) + build_attr(member, member.isScriptable() and needs_scriptable) elif isinstance(member, xpidl.Method): - build_method(member) + build_method(member, member.isScriptable() and needs_scriptable) elif isinstance(member, xpidl.CEnum): build_cenum(member) elif isinstance(member, xpidl.CDATA): @@ -240,12 +245,24 @@ def build_interface(iface): else: raise Exception("Unexpected interface member: %s" % member) + for ref in set(needs_scriptable): + p = iface.idl.getName(xpidl.TypeId(ref), None) + if isinstance(p, xpidl.Interface): + needs_scriptable.remove(ref) + if not p.attributes.scriptable: + raise Exception( + f"Scriptable member in {iface.name} references non-scriptable {ref}. " + "The interface must be marked as [scriptable], " + "or the referencing member with [noscript]." + ) + return { "name": iface.name, "uuid": iface.attributes.uuid, "methods": methods, "consts": consts, "parent": iface.base, + "needs_scriptable": sorted(needs_scriptable), "flags": flags( ("function", iface.attributes.function), ("builtinclass", iface.attributes.builtinclass), diff --git a/xpcom/idl-parser/xpidl/runtests.py b/xpcom/idl-parser/xpidl/runtests.py index b889d40010..940f845b9b 100755 --- a/xpcom/idl-parser/xpidl/runtests.py +++ b/xpcom/idl-parser/xpidl/runtests.py @@ -102,7 +102,7 @@ void bar(); self.assertEqual(xpidl.TypeId("long"), m.params[2].type) self.assertEqual("in", m.params[2].paramtype) self.assertTrue(isinstance(m.params[2].realtype, xpidl.LegacyArray)) - self.assertEqual("long", m.params[2].realtype.type.name) + self.assertEqual("int32_t", m.params[2].realtype.type.name) def testAttribute(self): i = self.p.parse( diff --git a/xpcom/idl-parser/xpidl/rust.py b/xpcom/idl-parser/xpidl/rust.py index 5d906d85ea..71ad5e98c8 100644 --- a/xpcom/idl-parser/xpidl/rust.py +++ b/xpcom/idl-parser/xpidl/rust.py @@ -363,10 +363,6 @@ def print_rust_bindings(idl, fd, relpath): if p.kind == "typedef": try: - # Skip bool and C++ stdint typedefs marked with [substitute]. - if p.substitute: - continue - if printdoccomments: fd.write( "/// `typedef %s %s;`\n///\n" diff --git a/xpcom/idl-parser/xpidl/typescript.py b/xpcom/idl-parser/xpidl/typescript.py index 614a328edf..9c374141d1 100644 --- a/xpcom/idl-parser/xpidl/typescript.py +++ b/xpcom/idl-parser/xpidl/typescript.py @@ -68,7 +68,7 @@ def ts_interface(iface): def ts_typedefs(idl): for p in idl.getNames(): - if isinstance(p, xpidl.Typedef) and not p.substitute: + if isinstance(p, xpidl.Typedef): try: yield (p.name, p.realtype.tsType()) except xpidl.TSNoncompat: diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py index 19d042d1b0..11b5d05e58 100755 --- a/xpcom/idl-parser/xpidl/xpidl.py +++ b/xpcom/idl-parser/xpidl/xpidl.py @@ -184,19 +184,26 @@ class Builtin(object): builtinNames = [ Builtin("boolean", "bool", "bool", "boolean"), Builtin("void", "void", "libc::c_void", "void"), - Builtin("octet", "uint8_t", "u8", "u8", False, True), - Builtin("short", "int16_t", "i16", "i16", True, True), - Builtin("long", "int32_t", "i32", "i32", True, True), - Builtin("long long", "int64_t", "i64", "i64", True, True), - Builtin("unsigned short", "uint16_t", "u16", "u16", False, True), - Builtin("unsigned long", "uint32_t", "u32", "u32", False, True), - Builtin("unsigned long long", "uint64_t", "u64", "u64", False, True), + Builtin("int8_t", "int8_t", "i8", "i8", True, True), + Builtin("int16_t", "int16_t", "i16", "i16", True, True), + Builtin("int32_t", "int32_t", "i32", "i32", True, True), + Builtin("int64_t", "int64_t", "i64", "i64", True, True), + Builtin("uint8_t", "uint8_t", "u8", "u8", False, True), + Builtin("uint16_t", "uint16_t", "u16", "u16", False, True), + Builtin("uint32_t", "uint32_t", "u32", "u32", False, True), + Builtin("uint64_t", "uint64_t", "u64", "u64", False, True), + Builtin("nsresult", "nsresult", "nserror::nsresult", "nsresult"), Builtin("float", "float", "libc::c_float", "float"), Builtin("double", "double", "libc::c_double", "double"), Builtin("char", "char", "libc::c_char", "string"), Builtin("string", "char *", "*const libc::c_char", "string"), Builtin("wchar", "char16_t", "u16", "string"), Builtin("wstring", "char16_t *", "*const u16", "string"), + # NOTE: char16_t is the same type as `wchar` in C++, however it is reflected + # into JS as an integer, allowing it to be used in constants. + # This inconsistency sucks, but reflects existing usage so unfortunately + # isn't very easy to change. + Builtin("char16_t", "char16_t", "u16", "u16", False, True), # As seen in mfbt/RefCountType.h, this type has special handling to # maintain binary compatibility with MSCOM's IUnknown that cannot be # expressed in XPIDL. @@ -208,10 +215,24 @@ builtinNames = [ ), ] +# Allow using more C-style names for the basic integer types. +builtinAlias = [ + ("octet", "uint8_t"), + ("unsigned short", "uint16_t"), + ("unsigned long", "uint32_t"), + ("unsigned long long", "uint64_t"), + ("short", "int16_t"), + ("long", "int32_t"), + ("long long", "int64_t"), +] + builtinMap = {} for b in builtinNames: builtinMap[b.name] = b +for alias, name in builtinAlias: + builtinMap[alias] = builtinMap[name] + class Location(object): _line = None @@ -486,14 +507,6 @@ class Typedef(object): self.location = location self.doccomments = doccomments - # C++ stdint types and the bool typedef from nsrootidl.idl are marked - # with [substitute], and emit as the underlying builtin type directly. - self.substitute = False - for name, value, aloc in attlist: - if name != "substitute" or value is not None: - raise IDLError(f"Unexpected attribute {name}({value})", aloc) - self.substitute = True - def __eq__(self, other): return self.name == other.name and self.type == other.type @@ -505,24 +518,12 @@ class Typedef(object): raise IDLError("Unsupported typedef target type", self.location) def nativeType(self, calltype): - if self.substitute: - return self.realtype.nativeType(calltype) - return "%s %s" % (self.name, "*" if "out" in calltype else "") def rustType(self, calltype): - if self.substitute: - return self.realtype.rustType(calltype) - - if self.name == "nsresult": - return "%s::nserror::nsresult" % ("*mut " if "out" in calltype else "") - return "%s%s" % ("*mut " if "out" in calltype else "", self.name) def tsType(self): - if self.substitute: - return self.realtype.tsType() - return self.name def __str__(self): @@ -1081,7 +1082,7 @@ class ConstMember(object): basetype = basetype.realtype if not isinstance(basetype, Builtin) or not basetype.maybeConst: raise IDLError( - "const may only be a short or long type, not %s" % self.type, + "const may only be an integer type, not %s" % self.type.name, self.location, ) @@ -1440,9 +1441,9 @@ class Method(object): "size_is parameter of an input must also be an input", p.location, ) - if getBuiltinOrNativeTypeName(size_param.realtype) != "unsigned long": + if getBuiltinOrNativeTypeName(size_param.realtype) != "uint32_t": raise IDLError( - "size_is parameter must have type 'unsigned long'", + "size_is parameter must have type 'uint32_t'", p.location, ) if p.iid_is: @@ -1688,8 +1689,8 @@ TypeId = namedtuple("TypeId", "name params") # Make str(TypeId) produce a nicer value -TypeId.__str__ = ( - lambda self: "%s<%s>" % (self.name, ", ".join(str(p) for p in self.params)) +TypeId.__str__ = lambda self: ( + "%s<%s>" % (self.name, ", ".join(str(p) for p in self.params)) if self.params is not None else self.name ) diff --git a/xpcom/io/nsIFile.idl b/xpcom/io/nsIFile.idl index 535a7a88ee..89c2155e94 100644 --- a/xpcom/io/nsIFile.idl +++ b/xpcom/io/nsIFile.idl @@ -106,7 +106,7 @@ interface nsIFile : nsISupports * ancestor directories (and return an error instead). */ [must_use] void create(in unsigned long type, in unsigned long permissions, - [optional,default(false)] in bool skipAncestors); + [optional,default(false)] in boolean skipAncestors); /** * Accessor to the leaf name of the file itself. diff --git a/xpcom/io/nsILocalFileMac.idl b/xpcom/io/nsILocalFileMac.idl index 38559517e7..a9de769b86 100644 --- a/xpcom/io/nsILocalFileMac.idl +++ b/xpcom/io/nsILocalFileMac.idl @@ -176,7 +176,7 @@ interface nsILocalFileMac : nsIFile * * @return Whether or not the extended attribute is present. */ - bool hasXAttr(in ACString aAttrName); + boolean hasXAttr(in ACString aAttrName); /** * Get the value of the extended attribute. diff --git a/xpcom/io/nsILocalFileWin.idl b/xpcom/io/nsILocalFileWin.idl index a3f80d391b..1195e2aa58 100644 --- a/xpcom/io/nsILocalFileWin.idl +++ b/xpcom/io/nsILocalFileWin.idl @@ -53,7 +53,7 @@ interface nsILocalFileWin : nsIFile * Throws NS_ERROR_FILE_INVALID_PATH for an invalid file. * Throws NS_ERROR_FAILURE if the set or get fails. */ - attribute bool readOnly; + attribute boolean readOnly; /** * Setting this to true will prepend the prefix "\\?\" to all parsed file diff --git a/xpcom/io/nsIRandomAccessStream.idl b/xpcom/io/nsIRandomAccessStream.idl index 20421def17..87bc3626f3 100644 --- a/xpcom/io/nsIRandomAccessStream.idl +++ b/xpcom/io/nsIRandomAccessStream.idl @@ -58,5 +58,5 @@ interface nsIRandomAccessStream : nsISeekableStream [notxpcom, nostdcall] RandomAccessStreamParams serialize(in nsIInterfaceRequestor aCallbacks); - [notxpcom, nostdcall] bool deserialize(inout RandomAccessStreamParamsRef params); + [notxpcom, nostdcall] boolean deserialize(inout RandomAccessStreamParamsRef params); }; diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index 13fea1d2ca..3d62a69a10 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -326,6 +326,8 @@ static nsresult ConvertWinError(DWORD aWinErr) { [[fallthrough]]; // to NS_ERROR_FILE_DEVICE_FAILURE case ERROR_DEV_NOT_EXIST: [[fallthrough]]; // to NS_ERROR_FILE_DEVICE_FAILURE + case ERROR_INVALID_FUNCTION: + [[fallthrough]]; // to NS_ERROR_FILE_DEVICE_FAILURE case ERROR_IO_DEVICE: rv = NS_ERROR_FILE_DEVICE_FAILURE; break; @@ -671,10 +673,12 @@ static nsresult OpenDir(const nsString& aName, nsDir** aDir) { filename.ReplaceChar(L'/', L'\\'); - // FindFirstFileW Will have a last error of ERROR_DIRECTORY if + // FindFirstFileExW Will have a last error of ERROR_DIRECTORY if // <file_path>\* is passed in. If <unknown_path>\* is passed in then // ERROR_PATH_NOT_FOUND will be the last error. - d->handle = ::FindFirstFileW(filename.get(), &(d->data)); + d->handle = ::FindFirstFileExW(filename.get(), FindExInfoBasic, &(d->data), + FindExSearchNameMatch, nullptr, + FIND_FIRST_EX_LARGE_FETCH); if (d->handle == INVALID_HANDLE_VALUE) { delete d; diff --git a/xpcom/reflect/xptinfo/xptcodegen.py b/xpcom/reflect/xptinfo/xptcodegen.py index d2859703b8..9dd54a6f07 100644 --- a/xpcom/reflect/xptinfo/xptcodegen.py +++ b/xpcom/reflect/xptinfo/xptcodegen.py @@ -9,6 +9,7 @@ import json from collections import OrderedDict import buildconfig +from mozbuild.util import memoize from perfecthash import PerfectHash # Pick a nice power-of-two size for our intermediate PHF tables. @@ -135,6 +136,7 @@ def split_iid(iid): # Get the individual components out of an IID string. return tuple(split_at_idxs(iid, (8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2))) +@memoize def iid_bytes(iid): # Get the byte representation of the IID for hashing. bs = bytearray() for num in split_iid(iid): @@ -283,10 +285,6 @@ def link_to_cpp(interfaces, fd, header_fd): tag = type["tag"] d1 = d2 = 0 - # TD_VOID is used for types that can't be represented in JS, so they - # should not be represented in the XPT info. - assert tag != "TD_VOID" - if tag == "TD_LEGACY_ARRAY": d1 = type["size_is"] d2 = lower_extra_type(type["element"]) @@ -351,15 +349,15 @@ def link_to_cpp(interfaces, fd, header_fd): return True - def lower_method(method, ifacename): + def lower_method(method, ifacename, builtinclass): methodname = "%s::%s" % (ifacename, method["name"]) isSymbol = "symbol" in method["flags"] reflectable = is_method_reflectable(method) - if not reflectable: - # Hide the parameters of methods that can't be called from JS to - # reduce the size of the file. + if not reflectable and builtinclass: + # Hide the parameters of methods that can't be called from JS and + # are on builtinclass interfaces to reduce the size of the file. paramidx = name = numparams = 0 else: if isSymbol: @@ -440,6 +438,8 @@ def link_to_cpp(interfaces, fd, header_fd): assert method_cnt < 250, "%s has too many methods" % iface["name"] assert const_cnt < 256, "%s has too many constants" % iface["name"] + builtinclass = "builtinclass" in iface["flags"] + # Store the lowered interface as 'cxx' on the iface object. iface["cxx"] = nsXPTInterfaceInfo( "%d = %s" % (iface["idx"], iface["name"]), @@ -451,14 +451,14 @@ def link_to_cpp(interfaces, fd, header_fd): mConsts=len(consts), mNumConsts=const_cnt, # Flags - mBuiltinClass="builtinclass" in iface["flags"], + mBuiltinClass=builtinclass, mMainProcessScriptableOnly="main_process_only" in iface["flags"], mFunction="function" in iface["flags"], ) # Lower methods and constants used by this interface for method in iface["methods"]: - lower_method(method, iface["name"]) + lower_method(method, iface["name"], builtinclass) for const in iface["consts"]: lower_const(const, iface["name"]) @@ -625,6 +625,16 @@ def link_and_write(files, outfile, outheader): iids.add(interface["uuid"]) names.add(interface["name"]) + # All forwards referenced from scriptable members must be known (as scriptable). + for iface in interfaces: + for ref in iface["needs_scriptable"]: + if not ref in names: + raise Exception( + f"Scriptable member in {iface['name']} references unknown {ref}. " + "Forward must be a known and [scriptable] interface, " + "or the referencing member marked with [noscript]." + ) + link_to_cpp(interfaces, outfile, outheader) diff --git a/xpcom/rust/gecko_logger/src/lib.rs b/xpcom/rust/gecko_logger/src/lib.rs index 5da85b9d89..4be9bef7c8 100644 --- a/xpcom/rust/gecko_logger/src/lib.rs +++ b/xpcom/rust/gecko_logger/src/lib.rs @@ -10,7 +10,6 @@ extern crate lazy_static; use app_services_logger::{AppServicesLogger, LOGGERS_BY_TARGET}; use log::Log; use log::{Level, LevelFilter}; -use std::boxed::Box; use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::os::raw::c_char; diff --git a/xpcom/rust/gtest/moz_task/test.rs b/xpcom/rust/gtest/moz_task/test.rs index fa4c0af852..b6fbbe5a8c 100644 --- a/xpcom/rust/gtest/moz_task/test.rs +++ b/xpcom/rust/gtest/moz_task/test.rs @@ -2,7 +2,6 @@ * 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/. */ -use moz_task; use std::{ future::Future, pin::Pin, diff --git a/xpcom/rust/xpcom/src/refptr.rs b/xpcom/rust/xpcom/src/refptr.rs index 8549b6d2f0..399692671d 100644 --- a/xpcom/rust/xpcom/src/refptr.rs +++ b/xpcom/rust/xpcom/src/refptr.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::interfaces::nsISupports; -use libc; use nserror::{nsresult, NS_OK}; use std::cell::Cell; use std::convert::TryInto; diff --git a/xpcom/string/nsTSubstring.cpp b/xpcom/string/nsTSubstring.cpp index ae9fda73c8..cff2031422 100644 --- a/xpcom/string/nsTSubstring.cpp +++ b/xpcom/string/nsTSubstring.cpp @@ -1322,6 +1322,9 @@ int_type ToIntegerCommon(const nsTSubstring<T>& aSrc, nsresult* aErrorCode, break; // clang-format on case '-': + if constexpr (!std::is_signed_v<int_type>) { + return 0; + } negate = true; break; default: @@ -1389,6 +1392,12 @@ int32_t nsTSubstring<T>::ToInteger(nsresult* aErrorCode, return ToIntegerCommon<T, int32_t>(*this, aErrorCode, aRadix); } +template <typename T> +uint32_t nsTSubstring<T>::ToUnsignedInteger(nsresult* aErrorCode, + uint32_t aRadix) const { + return ToIntegerCommon<T, uint32_t>(*this, aErrorCode, aRadix); +} + /** * nsTSubstring::ToInteger64 */ diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h index 0b4022823f..622b931afb 100644 --- a/xpcom/string/nsTSubstring.h +++ b/xpcom/string/nsTSubstring.h @@ -383,6 +383,14 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> { int32_t ToInteger(nsresult* aErrorCode, uint32_t aRadix = 10) const; /** + * Perform string to uint conversion. + * @param aErrorCode will contain error if one occurs + * @param aRadix is the radix to use. Only 10 and 16 are supported. + * @return int rep of string value, and possible (out) error code + */ + uint32_t ToUnsignedInteger(nsresult* aErrorCode, uint32_t aRadix = 10) const; + + /** * Perform string to 64-bit int conversion. * @param aErrorCode will contain error if one occurs * @param aRadix is the radix to use. Only 10 and 16 are supported. diff --git a/xpcom/system/nsICrashReporter.idl b/xpcom/system/nsICrashReporter.idl index 6fd0967e75..dbbc36f0e0 100644 --- a/xpcom/system/nsICrashReporter.idl +++ b/xpcom/system/nsICrashReporter.idl @@ -27,7 +27,7 @@ interface nsICrashReporter : nsISupports * because the JS engine relies on proper exception handler chaining. */ [noscript] - void setEnabled(in bool enabled); + void setEnabled(in boolean enabled); /** * Get or set the URL to which crash reports will be submitted. diff --git a/xpcom/system/nsIDeviceSensors.idl b/xpcom/system/nsIDeviceSensors.idl index a173421ebf..488e9428a8 100644 --- a/xpcom/system/nsIDeviceSensors.idl +++ b/xpcom/system/nsIDeviceSensors.idl @@ -39,7 +39,7 @@ interface nsIDeviceSensors : nsISupports /** * Returns true if the given window has any listeners of the given type */ - bool hasWindowListener(in unsigned long aType, in nsIDOMWindow aWindow); + boolean hasWindowListener(in unsigned long aType, in nsIDOMWindow aWindow); // Holds pointers, not AddRef objects -- it is up to the caller // to call RemoveWindowListener before the window is deleted. diff --git a/xpcom/tests/gtest/TestAllocReplacement.cpp b/xpcom/tests/gtest/TestAllocReplacement.cpp index 4b2c41b0f3..e0f0d001d5 100644 --- a/xpcom/tests/gtest/TestAllocReplacement.cpp +++ b/xpcom/tests/gtest/TestAllocReplacement.cpp @@ -73,34 +73,3 @@ TEST(AllocReplacement, posix_memalign_check) }); } #endif - -#if defined(XP_WIN) -# include <windows.h> - -# undef ASSERT_ALLOCATION_HAPPENED -# define ASSERT_ALLOCATION_HAPPENED(lambda) \ - ASSERT_TRUE(ValidateHookedAllocation( \ - lambda, [](void* p) { HeapFree(GetProcessHeap(), 0, p); })); - -TEST(AllocReplacement, HeapAlloc_check) -{ - ASSERT_ALLOCATION_HAPPENED([] { - HANDLE h = GetProcessHeap(); - return HeapAlloc(h, 0, kAllocAmount); - }); -} - -TEST(AllocReplacement, HeapReAlloc_check) -{ - ASSERT_ALLOCATION_HAPPENED([] { - HANDLE h = GetProcessHeap(); - void* p = HeapAlloc(h, 0, kAllocAmount / 2); - - if (!p) { - return static_cast<void*>(nullptr); - } - - return HeapReAlloc(h, 0, p, kAllocAmount); - }); -} -#endif diff --git a/xpcom/tests/gtest/TestINIParser.cpp b/xpcom/tests/gtest/TestINIParser.cpp new file mode 100644 index 0000000000..8c156f23dd --- /dev/null +++ b/xpcom/tests/gtest/TestINIParser.cpp @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsCOMPtr.h" +#include "gtest/gtest.h" +#include "mozilla/gtest/MozAssertions.h" + +#include "nsINIParser.h" + +TEST(INIParser, DeleteString) +{ + nsINIParser* parser = new nsINIParser(); + nsresult rv = parser->InitFromString( + "[sec]\r\ +key1=val1\r\ +key2=val2\r\ +key3=val3\r\ +key4=val4"_ns); + EXPECT_NS_SUCCEEDED(rv); + + rv = parser->DeleteString("sec", "key3"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteString("sec", "key4"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteString("sec", "key1"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteString("sec", "key2"); + EXPECT_NS_SUCCEEDED(rv); + + delete parser; +} + +TEST(INIParser, DeleteSection) +{ + nsINIParser* parser = new nsINIParser(); + nsresult rv = parser->InitFromString( + "[sec1]\r\ +key=val\r\ +\r\ +[sec2]\r\ +key=val\r\ +[sec3]\r\ +key=val\r\ +[sec4]\r\ +key=val"_ns); + EXPECT_NS_SUCCEEDED(rv); + + rv = parser->DeleteSection("sec3"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteSection("sec4"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteSection("sec1"); + EXPECT_NS_SUCCEEDED(rv); + rv = parser->DeleteSection("sec2"); + EXPECT_NS_SUCCEEDED(rv); + + delete parser; +} diff --git a/xpcom/tests/gtest/TestMozPromise.cpp b/xpcom/tests/gtest/TestMozPromise.cpp index bb7273cc1f..9b06304139 100644 --- a/xpcom/tests/gtest/TestMozPromise.cpp +++ b/xpcom/tests/gtest/TestMozPromise.cpp @@ -753,4 +753,54 @@ TEST(MozPromise, ChainToDirectTaskDispatch) NS_ProcessPendingEvents(nullptr); } +TEST(MozPromise, Map) +{ + int value = 0; + bool ran_err = false; + + InvokeAsync(GetCurrentSerialEventTarget(), "test", + [&]() { return TestPromise::CreateAndResolve(18, "test"); }) + ->Map(GetCurrentSerialEventTarget(), "test", + [](int val) { return val + 0x18; }) + ->MapErr(GetCurrentSerialEventTarget(), "test", + [&](double val) { + ran_err = true; + return Ok{}; + }) + ->Map(GetCurrentSerialEventTarget(), "test", [&](int val) { + value = val; + return Ok{}; + }); + + NS_ProcessPendingEvents(nullptr); + + EXPECT_EQ(value, 42); + EXPECT_EQ(ran_err, false); +} + +TEST(MozPromise, MapErr) +{ + bool ran_ok = false; + double result = 0.0; + + InvokeAsync(GetCurrentSerialEventTarget(), "test", + [&]() { return TestPromise::CreateAndReject(1.0, "test"); }) + ->Map(GetCurrentSerialEventTarget(), "test", + [&](int val) { + ran_ok = true; + return 1; + }) + ->MapErr(GetCurrentSerialEventTarget(), "test", + [](double val) { return val * 2; }) + ->MapErr(GetCurrentSerialEventTarget(), "test", [&](double val) { + result = val; + return Ok{}; + }); + + NS_ProcessPendingEvents(nullptr); + + EXPECT_EQ(result, 2.0); + EXPECT_EQ(ran_ok, false); +} + #undef DO_FAIL diff --git a/xpcom/tests/gtest/TestStrings.cpp b/xpcom/tests/gtest/TestStrings.cpp index 7e0f986d29..b1458ec6ce 100644 --- a/xpcom/tests/gtest/TestStrings.cpp +++ b/xpcom/tests/gtest/TestStrings.cpp @@ -34,12 +34,6 @@ } \ }); -// Disable the C++ 2a warning. See bug #1509926 -#if defined(__clang__) && (__clang_major__ >= 6) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wc++2a-compat" -#endif - namespace TestStrings { using mozilla::BlackBox; @@ -1295,6 +1289,32 @@ TEST_F(Strings, string_tointeger) { } } +struct ToUnsignedIntegerTest { + const char* str; + uint32_t radix; + uint32_t result; + nsresult rv; +}; + +static const ToUnsignedIntegerTest kToUnsignedIntegerTests[] = { + {"123", 10, 123, NS_OK}, + {"7b", 16, 123, NS_OK}, + {"90194313659", 10, 0, NS_ERROR_ILLEGAL_VALUE}, + {"ffffffff", 16, 0xffffffff, NS_OK}, + {"4294967295", 10, 4294967295, NS_OK}, + {"8abc1234", 16, 0x8abc1234, NS_OK}, + {"-194313659", 10, 0, NS_ERROR_ILLEGAL_VALUE}, + {nullptr, 0, 0, NS_OK}}; + +TEST_F(Strings, string_to_unsigned_integer) { + nsresult rv; + for (const ToUnsignedIntegerTest* t = kToUnsignedIntegerTests; t->str; ++t) { + uint32_t result = nsAutoCString(t->str).ToUnsignedInteger(&rv, t->radix); + EXPECT_EQ(rv, t->rv); + EXPECT_EQ(result, t->result); + } +} + static void test_parse_string_helper(const char* str, char separator, int len, const char* s1, const char* s2) { nsCString data(str); @@ -2795,7 +2815,3 @@ static_assert(*testStringA.EndReading() == 0); static_assert(testStringA.EndReading() - testStringA.BeginReading() == 1); } // namespace TestStrings - -#if defined(__clang__) && (__clang_major__ >= 6) -# pragma clang diagnostic pop -#endif diff --git a/xpcom/tests/gtest/TestTaskController.cpp b/xpcom/tests/gtest/TestTaskController.cpp new file mode 100644 index 0000000000..c7d71ae0f8 --- /dev/null +++ b/xpcom/tests/gtest/TestTaskController.cpp @@ -0,0 +1,214 @@ +/* -*- 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 "gtest/gtest.h" + +#include <stdint.h> // uint32_t + +#include "nsString.h" // nsACString +#include "nsThreadUtils.h" // NS_ProcessNextEvent +#include "mozilla/Atomics.h" // Atomic +#include "mozilla/EventQueue.h" // EventQueuePriority +#include "mozilla/Mutex.h" // Mutex, MutexAutoLock +#include "mozilla/RefPtr.h" // RefPtr, do_AddRef +#include "mozilla/TaskController.h" // TaskController, Task +#include "prthread.h" // PR_Sleep + +using namespace mozilla; + +namespace TestTaskController { + +class Logger { + public: + Logger() : mMutex("Logger") {} + + void Add(const char* aText) { + MutexAutoLock lock(mMutex); + + mLog += aText; + } + + const nsAutoCString& GetLog() const { return mLog; } + + private: + nsAutoCString mLog; + Mutex mMutex; +}; + +class ReschedulingTask : public Task { + static constexpr uint32_t LoopCount = 3; + + public: + explicit ReschedulingTask(Kind aKind, Logger* aLogger, const char* aName) + : Task(aKind, EventQueuePriority::Normal), + mCount(0), + mIsDone(false), + mLogger(aLogger), + mName(aName) {} + + TaskResult Run() override { + mLogger->Add(mName); + + mCount++; + + if (mCount < LoopCount) { + return TaskResult::Incomplete; + } + + mIsDone = true; + + return TaskResult::Complete; + } + +#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY + bool GetName(nsACString& aName) override { + aName.AssignLiteral("AsyncScriptCompileTask"); + return true; + } +#endif + + bool IsDone() const { return mIsDone; } + + private: + Atomic<uint32_t> mCount; + Atomic<bool> mIsDone; + Logger* mLogger; + const char* mName; +}; + +using namespace mozilla; + +TEST(TaskController, RescheduleOnMainThread) +{ + Logger logger; + + RefPtr<ReschedulingTask> mainThreadTask = + new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "1"); + + TaskController::Get()->AddTask(do_AddRef(mainThreadTask)); + + while (NS_ProcessNextEvent(nullptr, false)) { + } + + ASSERT_TRUE(mainThreadTask->IsDone()); + + ASSERT_TRUE(logger.GetLog() == "111"); +} + +TEST(TaskController, RescheduleOffMainThread) +{ + Logger logger; + + RefPtr<ReschedulingTask> offThreadTask = + new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger, "1"); + + TaskController::Get()->AddTask(do_AddRef(offThreadTask)); + + uint32_t count = 0; + while (!offThreadTask->IsDone() && count < 100) { + PR_Sleep(PR_MillisecondsToInterval(100)); + count++; + } + ASSERT_TRUE(offThreadTask->IsDone()); + + ASSERT_TRUE(logger.GetLog() == "111"); +} + +TEST(TaskController, RescheduleMainAndOffMainThreads) +{ + Logger logger; + + RefPtr<ReschedulingTask> offThreadTask = + new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger, "1"); + RefPtr<ReschedulingTask> mainThreadTask = + new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "2"); + + mainThreadTask->AddDependency(offThreadTask.get()); + + TaskController::Get()->AddTask(do_AddRef(offThreadTask)); + TaskController::Get()->AddTask(do_AddRef(mainThreadTask)); + + uint32_t count = 0; + while (!offThreadTask->IsDone() && count < 100) { + PR_Sleep(PR_MillisecondsToInterval(100)); + count++; + } + ASSERT_TRUE(offThreadTask->IsDone()); + + // At this point, the main thread task shouldn't have run. + ASSERT_TRUE(logger.GetLog() == "111"); + + while (NS_ProcessNextEvent(nullptr, false)) { + } + + ASSERT_TRUE(mainThreadTask->IsDone()); + + ASSERT_TRUE(logger.GetLog() == "111222"); +} + +TEST(TaskController, RescheduleOrder) +{ + Logger logger; + + RefPtr<ReschedulingTask> mainThreadTask1 = + new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "1"); + RefPtr<ReschedulingTask> mainThreadTask2 = + new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "2"); + RefPtr<ReschedulingTask> mainThreadTask3 = + new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "3"); + + TaskController::Get()->AddTask(do_AddRef(mainThreadTask1)); + TaskController::Get()->AddTask(do_AddRef(mainThreadTask2)); + TaskController::Get()->AddTask(do_AddRef(mainThreadTask3)); + + while (NS_ProcessNextEvent(nullptr, false)) { + } + + ASSERT_TRUE(mainThreadTask1->IsDone()); + ASSERT_TRUE(mainThreadTask2->IsDone()); + ASSERT_TRUE(mainThreadTask3->IsDone()); + + // Rescheduled tasks should be added to the beginning of the queue. + ASSERT_TRUE(logger.GetLog() == "111222333"); +} + +TEST(TaskController, RescheduleOrderOffMainThread) +{ + Logger logger1; + Logger logger2; + Logger logger3; + + RefPtr<ReschedulingTask> offThreadTask1 = + new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger1, "1"); + RefPtr<ReschedulingTask> offThreadTask2 = + new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger2, "2"); + RefPtr<ReschedulingTask> offThreadTask3 = + new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger3, "3"); + + TaskController::Get()->AddTask(do_AddRef(offThreadTask1)); + TaskController::Get()->AddTask(do_AddRef(offThreadTask2)); + TaskController::Get()->AddTask(do_AddRef(offThreadTask3)); + + uint32_t count = 0; + while (!(offThreadTask1->IsDone() && offThreadTask2->IsDone() && + offThreadTask3->IsDone()) && + count < 100) { + PR_Sleep(PR_MillisecondsToInterval(100)); + count++; + } + + ASSERT_TRUE(offThreadTask1->IsDone()); + ASSERT_TRUE(offThreadTask2->IsDone()); + ASSERT_TRUE(offThreadTask3->IsDone()); + + // Rescheduled tasks should be enqueued. + // The order between off-thread tasks are not deterministic. + ASSERT_TRUE(logger1.GetLog() == "111"); + ASSERT_TRUE(logger2.GetLog() == "222"); + ASSERT_TRUE(logger3.GetLog() == "333"); +} + +} // namespace TestTaskController diff --git a/xpcom/tests/gtest/moz.build b/xpcom/tests/gtest/moz.build index 6fd509d7b2..4d8d38e89a 100644 --- a/xpcom/tests/gtest/moz.build +++ b/xpcom/tests/gtest/moz.build @@ -26,6 +26,7 @@ UNIFIED_SOURCES += [ "TestGCPostBarriers.cpp", "TestID.cpp", "TestIDUtils.cpp", + "TestINIParser.cpp", "TestInputStreamLengthHelper.cpp", "TestJSHolderMap.cpp", "TestLogCommandLineHandler.cpp", @@ -61,6 +62,7 @@ UNIFIED_SOURCES += [ "TestSynchronization.cpp", "TestTArray.cpp", "TestTArray2.cpp", + "TestTaskController.cpp", "TestTaskQueue.cpp", "TestTextFormatter.cpp", "TestThreadManager.cpp", @@ -143,10 +145,8 @@ if ( "TestSTLWrappers.cpp", ] -# Compile TestAllocReplacement separately so Windows headers don't pollute -# the global namespace for other files. if CONFIG["MOZ_MEMORY"]: - SOURCES += [ + UNIFIED_SOURCES += [ "TestAllocReplacement.cpp", ] diff --git a/xpcom/tests/unit/test_bug325418.js b/xpcom/tests/unit/test_bug325418.js index 5840aacf74..c86f8d5845 100644 --- a/xpcom/tests/unit/test_bug325418.js +++ b/xpcom/tests/unit/test_bug325418.js @@ -8,7 +8,7 @@ var gStartTime2; var timer; var observer1 = { - observe: function observeTC1(subject, topic, data) { + observe: function observeTC1(subject, topic) { if (topic == "timer-callback") { // Stop timer, so it doesn't repeat (if test runs slowly). timer.cancel(); @@ -30,7 +30,7 @@ var observer1 = { }; var observer2 = { - observe: function observeTC2(subject, topic, data) { + observe: function observeTC2(subject, topic) { if (topic == "timer-callback") { // Stop timer, so it doesn't repeat (if test runs slowly). timer.cancel(); diff --git a/xpcom/tests/unit/test_nsIProcess.js b/xpcom/tests/unit/test_nsIProcess.js index 582d10440c..e8a382b29f 100644 --- a/xpcom/tests/unit/test_nsIProcess.js +++ b/xpcom/tests/unit/test_nsIProcess.js @@ -128,7 +128,7 @@ function test_notify_blocking() { process.init(file); process.runAsync([], 0, { - observe(subject, topic, data) { + observe(subject, topic) { process = subject.QueryInterface(Ci.nsIProcess); Assert.equal(topic, "process-failed"); Assert.equal(process.exitValue, 42); @@ -145,7 +145,7 @@ function test_notify_nonblocking() { process.init(file); process.runAsync(TEST_ARGS, TEST_ARGS.length, { - observe(subject, topic, data) { + observe(subject, topic) { process = subject.QueryInterface(Ci.nsIProcess); Assert.equal(topic, "process-finished"); Assert.equal(process.exitValue, 0); @@ -162,7 +162,7 @@ function test_notify_killed() { process.init(file); process.runAsync([], 0, { - observe(subject, topic, data) { + observe(subject, topic) { process = subject.QueryInterface(Ci.nsIProcess); Assert.equal(topic, "process-failed"); do_test_finished(); diff --git a/xpcom/tests/unit/test_windows_registry.js b/xpcom/tests/unit/test_windows_registry.js index 691c0461c3..ef5082a666 100644 --- a/xpcom/tests/unit/test_windows_registry.js +++ b/xpcom/tests/unit/test_windows_registry.js @@ -172,7 +172,7 @@ function test_childkey_functions(testKey) { strictEqual(testKey.hasChild(TESTDATA_CHILD_KEY), false); } -function cleanup_test_run(testKey, keyName) { +function cleanup_test_run(testKey) { info("Cleaning up test."); for (var i = 0; i < testKey.childCount; i++) { diff --git a/xpcom/threads/AbstractThread.h b/xpcom/threads/AbstractThread.h index b53bcf8ca3..bbc17cc5b0 100644 --- a/xpcom/threads/AbstractThread.h +++ b/xpcom/threads/AbstractThread.h @@ -4,14 +4,14 @@ * 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/. */ -#if !defined(AbstractThread_h_) -# define AbstractThread_h_ - -# include "mozilla/AlreadyAddRefed.h" -# include "mozilla/ThreadLocal.h" -# include "nscore.h" -# include "nsISerialEventTarget.h" -# include "nsISupports.h" +#ifndef XPCOM_THREADS_ABSTRACTTHREAD_H_ +#define XPCOM_THREADS_ABSTRACTTHREAD_H_ + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/ThreadLocal.h" +#include "nscore.h" +#include "nsISerialEventTarget.h" +#include "nsISupports.h" class nsIEventTarget; class nsIRunnable; diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h index af4ac657fd..c53037e119 100644 --- a/xpcom/threads/MozPromise.h +++ b/xpcom/threads/MozPromise.h @@ -4,44 +4,45 @@ * 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/. */ -#if !defined(MozPromise_h_) -# define MozPromise_h_ - -# include <type_traits> -# include <utility> - -# include "mozilla/ErrorNames.h" -# include "mozilla/Logging.h" -# include "mozilla/Maybe.h" -# include "mozilla/Monitor.h" -# include "mozilla/Mutex.h" -# include "mozilla/RefPtr.h" -# include "mozilla/UniquePtr.h" -# include "mozilla/Variant.h" -# include "nsIDirectTaskDispatcher.h" -# include "nsISerialEventTarget.h" -# include "nsTArray.h" -# include "nsThreadUtils.h" - -# ifdef MOZ_WIDGET_ANDROID -# include "mozilla/jni/GeckoResultUtils.h" -# endif - -# if MOZ_DIAGNOSTIC_ASSERT_ENABLED -# define PROMISE_DEBUG -# endif - -# ifdef PROMISE_DEBUG -# define PROMISE_ASSERT MOZ_RELEASE_ASSERT -# else -# define PROMISE_ASSERT(...) \ - do { \ - } while (0) -# endif - -# if DEBUG -# include "nsPrintfCString.h" -# endif +#ifndef XPCOM_THREADS_MOZPROMISE_H_ +#define XPCOM_THREADS_MOZPROMISE_H_ + +#include <type_traits> +#include <utility> + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorNames.h" +#include "mozilla/Logging.h" +#include "mozilla/Maybe.h" +#include "mozilla/Monitor.h" +#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Variant.h" +#include "nsIDirectTaskDispatcher.h" +#include "nsISerialEventTarget.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/jni/GeckoResultUtils.h" +#endif + +#if MOZ_DIAGNOSTIC_ASSERT_ENABLED +# define PROMISE_DEBUG +#endif + +#ifdef PROMISE_DEBUG +# define PROMISE_ASSERT MOZ_RELEASE_ASSERT +#else +# define PROMISE_ASSERT(...) \ + do { \ + } while (0) +#endif + +#if DEBUG +# include "nsPrintfCString.h" +#endif namespace mozilla { @@ -51,8 +52,8 @@ class Promise; extern LazyLogModule gMozPromiseLog; -# define PROMISE_LOG(x, ...) \ - MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__)) +#define PROMISE_LOG(x, ...) \ + MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__)) namespace detail { template <typename F> @@ -235,10 +236,10 @@ class MozPromise : public MozPromiseBase { mMutex("MozPromise Mutex"), mHaveRequest(false), mIsCompletionPromise(aIsCompletionPromise) -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG , mMagic4(&mMutex) -# endif +#endif { PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this); } @@ -501,12 +502,12 @@ class MozPromise : public MozPromiseBase { MOZ_ASSERT(aResponseTarget); } -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG ~ThenValueBase() { mMagic1 = 0; mMagic2 = 0; } -# endif +#endif void AssertIsDead() { PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic); @@ -520,7 +521,7 @@ class MozPromise : public MozPromiseBase { if (MozPromiseBase* p = CompletionPromise()) { p->AssertIsDead(); } else { -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED if (MOZ_UNLIKELY(!Request::mDisconnected)) { MOZ_CRASH_UNSAFE_PRINTF( "MozPromise::ThenValue created from '%s' destroyed without being " @@ -529,7 +530,7 @@ class MozPromise : public MozPromiseBase { mDispatchRv ? GetStaticErrorName(*mDispatchRv) : "not dispatched"); } -# endif +#endif } } @@ -620,23 +621,23 @@ class MozPromise : public MozPromiseBase { } void SetDispatchRv(nsresult aRv) { -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED mDispatchRv = Some(aRv); -# endif +#endif } nsCOMPtr<nsISerialEventTarget> mResponseTarget; // May be released on any thread. -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic1 = sMagic; -# endif +#endif const char* mCallSite; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic2 = sMagic; -# endif -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#endif +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED Maybe<nsresult> mDispatchRv; -# endif +#endif }; /* @@ -935,6 +936,98 @@ class MozPromise : public MozPromiseBase { RefPtr<typename PromiseType::Private> mCompletionPromise; }; + template <typename ResolveFunction> + class MapValue final : public ThenValueBase { + friend class ThenCommand<MapValue>; + constexpr static const bool SupportChaining = true; + using ResolveValueT_ = std::invoke_result_t<ResolveFunction, ResolveValueT>; + using PromiseType = MozPromise<ResolveValueT_, RejectValueT, IsExclusive>; + + public: + explicit MapValue(nsISerialEventTarget* aResponseTarget, + ResolveFunction&& f, const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite), + mResolveFunction(Some(std::forward<ResolveFunction>(f))) {} + + protected: + void Disconnect() override { + ThenValueBase::Disconnect(); + mResolveFunction.reset(); + } + + MozPromiseBase* CompletionPromise() const override { + return mCompletionPromise; + } + + void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override { + // Note that promise-chaining is always supported here; this function can + // only transform from MozPromise<A, B, k> to MozPromise<A2, B, k>. + auto value = MaybeMove(aValue); + typename PromiseType::ResolveOrRejectValue output; + + if (value.IsResolve()) { + output.SetResolve((*mResolveFunction)(std::move(value.ResolveValue()))); + } else { + output.SetReject(std::move(value.RejectValue())); + } + + if (mCompletionPromise) { + mCompletionPromise->ResolveOrReject(std::move(output), + ThenValueBase::mCallSite); + } + } + + private: + Maybe<ResolveFunction> mResolveFunction; + RefPtr<typename PromiseType::Private> mCompletionPromise; + }; + + template <typename RejectFunction> + class MapErrValue final : public ThenValueBase { + friend class ThenCommand<MapErrValue>; + constexpr static const bool SupportChaining = true; + using RejectValueT_ = std::invoke_result_t<RejectFunction, RejectValueT>; + using PromiseType = MozPromise<ResolveValueT, RejectValueT_, IsExclusive>; + + public: + explicit MapErrValue(nsISerialEventTarget* aResponseTarget, + RejectFunction&& f, const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite), + mRejectFunction(Some(std::forward<RejectFunction>(f))) {} + + protected: + void Disconnect() override { + ThenValueBase::Disconnect(); + mRejectFunction.reset(); + } + + MozPromiseBase* CompletionPromise() const override { + return mCompletionPromise; + } + + void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override { + // Note that promise-chaining is always supported here; this function can + // only transform from MozPromise<A, B, k> to MozPromise<A, B2, k>. + auto value = MaybeMove(aValue); + typename PromiseType::ResolveOrRejectValue output; + + if (value.IsResolve()) { + output.SetResolve(std::move(value.ResolveValue())); + } else { + output.SetReject((*mRejectFunction)(std::move(value.RejectValue()))); + } + + if (mCompletionPromise) { + mCompletionPromise->ResolveOrReject(std::move(output), + ThenValueBase::mCallSite); + } + } + + private: + Maybe<RejectFunction> mRejectFunction; + RefPtr<typename PromiseType::Private> mCompletionPromise; + }; + public: void ThenInternal(already_AddRefed<ThenValueBase> aThenValue, const char* aCallSite) { @@ -957,17 +1050,21 @@ class MozPromise : public MozPromiseBase { protected: /* - * A command object to store all information needed to make a request to - * the promise. This allows us to delay the request until further use is - * known (whether it is ->Then() again for more promise chaining or ->Track() - * to terminate chaining and issue the request). + * A command object to store all information needed to make a request to the + * promise. This allows us to delay the request until further use is known + * (whether it is ->Then() again for more promise chaining or ->Track() to + * terminate chaining and issue the request). * - * This allows a unified syntax for promise chaining and disconnection - * and feels more like its JS counterpart. + * This allows a unified syntax for promise chaining and disconnection, and + * feels more like its JS counterpart. + * + * Note that a ThenCommand is always exclusive, even if its source or result + * promises are not. To attach multiple continuations, explicitly convert it + * to a promise first. */ template <typename ThenValueType> - class ThenCommand { - // Allow Promise1::ThenCommand to access the private constructor, + class MOZ_TEMPORARY_CLASS ThenCommand { + // Allow Promise1::ThenCommand to access the private constructor // Promise2::ThenCommand(ThenCommand&&). template <typename, typename, bool> friend class MozPromise; @@ -1016,6 +1113,20 @@ class MozPromise : public MozPromiseBase { std::forward<Ts>(aArgs)...); } + template <typename... Ts> + auto Map(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().Map( + std::forward<Ts>(aArgs)...)) { + return static_cast<RefPtr<PromiseType>>(*this)->Map( + std::forward<Ts>(aArgs)...); + } + + template <typename... Ts> + auto MapErr(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().MapErr( + std::forward<Ts>(aArgs)...)) { + return static_cast<RefPtr<PromiseType>>(*this)->MapErr( + std::forward<Ts>(aArgs)...); + } + void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder) { aRequestHolder.Track(do_AddRef(mThenValue)); mReceiver->ThenInternal(mThenValue.forget(), mCallSite); @@ -1052,6 +1163,27 @@ class MozPromise : public MozPromiseBase { return ReturnType(aCallSite, thenValue.forget(), this); } + // Shorthand for a `Then` which simply forwards the reject-value, but performs + // some additional work with the resolve-value. + template <typename Function> + auto Map(nsISerialEventTarget* aResponseTarget, const char* aCallSite, + Function&& function) { + RefPtr<MapValue<Function>> thenValue = new MapValue<Function>( + aResponseTarget, std::forward<Function>(function), aCallSite); + return ThenCommand<MapValue<Function>>(aCallSite, thenValue.forget(), this); + } + + // Shorthand for a `Then` which simply forwards the resolve-value, but + // performs some additional work with the reject-value. + template <typename Function> + auto MapErr(nsISerialEventTarget* aResponseTarget, const char* aCallSite, + Function&& function) { + RefPtr<MapErrValue<Function>> thenValue = new MapErrValue<Function>( + aResponseTarget, std::forward<Function>(function), aCallSite); + return ThenCommand<MapErrValue<Function>>(aCallSite, thenValue.forget(), + this); + } + void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite) { MutexAutoLock lock(mMutex); @@ -1088,7 +1220,7 @@ class MozPromise : public MozPromiseBase { } } -# ifdef MOZ_WIDGET_ANDROID +#ifdef MOZ_WIDGET_ANDROID // Creates a C++ MozPromise from its Java counterpart, GeckoResult. [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult( java::GeckoResult::Param aGeckoResult) { @@ -1103,7 +1235,7 @@ class MozPromise : public MozPromiseBase { aGeckoResult->NativeThen(resolve, reject); return p; } -# endif +#endif // Note we expose the function AssertIsDead() instead of IsDead() since // checking IsDead() is a data race in the situation where the request is not @@ -1165,12 +1297,12 @@ class MozPromise : public MozPromiseBase { MOZ_ASSERT(mThenValues.IsEmpty()); MOZ_ASSERT(mChainedPromises.IsEmpty()); } -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG mMagic1 = 0; mMagic2 = 0; mMagic3 = 0; mMagic4 = nullptr; -# endif +#endif }; const char* mCreationSite; // For logging @@ -1179,24 +1311,24 @@ class MozPromise : public MozPromiseBase { bool mUseSynchronousTaskDispatch = false; bool mUseDirectTaskDispatch = false; uint32_t mPriority = nsIRunnablePriority::PRIORITY_NORMAL; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic1 = sMagic; -# endif +#endif // Try shows we never have more than 3 elements when IsExclusive is false. // So '3' is a good value to avoid heap allocation in most cases. AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic2 = sMagic; -# endif +#endif nsTArray<RefPtr<Private>> mChainedPromises; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic3 = sMagic; -# endif +#endif bool mHaveRequest; const bool mIsCompletionPromise; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG void* mMagic4; -# endif +#endif }; template <typename ResolveValueT, typename RejectValueT, bool IsExclusive> @@ -1718,9 +1850,9 @@ static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName, return p; } -# undef PROMISE_LOG -# undef PROMISE_ASSERT -# undef PROMISE_DEBUG +#undef PROMISE_LOG +#undef PROMISE_ASSERT +#undef PROMISE_DEBUG } // namespace mozilla diff --git a/xpcom/threads/StateMirroring.h b/xpcom/threads/StateMirroring.h index 9f8ded70f4..887fe6edb1 100644 --- a/xpcom/threads/StateMirroring.h +++ b/xpcom/threads/StateMirroring.h @@ -4,22 +4,22 @@ * 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/. */ -#if !defined(StateMirroring_h_) -# define StateMirroring_h_ - -# include <cstddef> -# include "mozilla/AbstractThread.h" -# include "mozilla/AlreadyAddRefed.h" -# include "mozilla/Assertions.h" -# include "mozilla/Logging.h" -# include "mozilla/Maybe.h" -# include "mozilla/RefPtr.h" -# include "mozilla/StateWatching.h" -# include "nsCOMPtr.h" -# include "nsIRunnable.h" -# include "nsISupports.h" -# include "nsTArray.h" -# include "nsThreadUtils.h" +#ifndef XPCOM_THREADS_STATEMIRRORING_H_ +#define XPCOM_THREADS_STATEMIRRORING_H_ + +#include <cstddef> +#include "mozilla/AbstractThread.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Assertions.h" +#include "mozilla/Logging.h" +#include "mozilla/Maybe.h" +#include "mozilla/RefPtr.h" +#include "mozilla/StateWatching.h" +#include "nsCOMPtr.h" +#include "nsIRunnable.h" +#include "nsISupports.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" /* * The state-mirroring machinery allows pieces of interesting state to be @@ -51,9 +51,9 @@ namespace mozilla { // Mirror<T> and Canonical<T> inherit WatchTarget, so we piggy-back on the // logging that WatchTarget already does. Given that, it makes sense to share // the same log module. -# define MIRROR_LOG(x, ...) \ - MOZ_ASSERT(gStateWatchingLog); \ - MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__)) +#define MIRROR_LOG(x, ...) \ + MOZ_ASSERT(gStateWatchingLog); \ + MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__)) template <typename T> class AbstractMirror; @@ -335,9 +335,9 @@ class Mirror { void ConnectedOnCanonicalThread(AbstractCanonical<T>* aCanonical) override { MOZ_ASSERT(aCanonical->OwnerThread()->IsCurrentThreadIn()); -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED ++mIncomingConnects; -# endif +#endif OwnerThread()->DispatchStateChange( NewRunnableMethod<StoreRefPtrPassByPtr<AbstractCanonical<T>>>( "Mirror::Impl::SetCanonical", this, &Impl::SetCanonical, @@ -349,9 +349,9 @@ class Mirror { aCanonical); MOZ_ASSERT(OwnerThread()->IsCurrentThreadIn()); MOZ_ASSERT(!IsConnected()); -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED --mIncomingConnects; -# endif +#endif mCanonical = aCanonical; } @@ -413,9 +413,9 @@ class Mirror { private: T mValue; RefPtr<AbstractCanonical<T>> mCanonical; -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED std::atomic<size_t> mIncomingConnects = 0; -# endif +#endif }; public: @@ -446,7 +446,7 @@ class Mirror { RefPtr<Impl> mImpl; }; -# undef MIRROR_LOG +#undef MIRROR_LOG } // namespace mozilla diff --git a/xpcom/threads/StateWatching.h b/xpcom/threads/StateWatching.h index 3da0c63bfe..2bd5c58d3b 100644 --- a/xpcom/threads/StateWatching.h +++ b/xpcom/threads/StateWatching.h @@ -4,19 +4,19 @@ * 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/. */ -#if !defined(StateWatching_h_) -# define StateWatching_h_ - -# include <cstddef> -# include <new> -# include <utility> -# include "mozilla/AbstractThread.h" -# include "mozilla/Assertions.h" -# include "mozilla/Logging.h" -# include "mozilla/RefPtr.h" -# include "nsISupports.h" -# include "nsTArray.h" -# include "nsThreadUtils.h" +#ifndef XPCOM_THREADS_STATEWATCHING_H_ +#define XPCOM_THREADS_STATEWATCHING_H_ + +#include <cstddef> +#include <new> +#include <utility> +#include "mozilla/AbstractThread.h" +#include "mozilla/Assertions.h" +#include "mozilla/Logging.h" +#include "mozilla/RefPtr.h" +#include "nsISupports.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" /* * The state-watching machinery automates the process of responding to changes @@ -62,8 +62,8 @@ namespace mozilla { extern LazyLogModule gStateWatchingLog; -# define WATCH_LOG(x, ...) \ - MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__)) +#define WATCH_LOG(x, ...) \ + MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__)) /* * AbstractWatcher is a superclass from which all watchers must inherit. @@ -295,7 +295,7 @@ class WatchManager { RefPtr<AbstractThread> mOwnerThread; }; -# undef WATCH_LOG +#undef WATCH_LOG } // namespace mozilla diff --git a/xpcom/threads/TaskController.cpp b/xpcom/threads/TaskController.cpp index 8e3aae185a..d8c2d5e176 100644 --- a/xpcom/threads/TaskController.cpp +++ b/xpcom/threads/TaskController.cpp @@ -927,7 +927,9 @@ bool TaskController::DoExecuteNextTaskOnlyMainThreadInternal( mMainThreadTasks.insert(std::move(mCurrentTasksMT.top())); MOZ_ASSERT(insertion.second); task->mIterator = insertion.first; - manager->WillRunTask(); + if (manager) { + manager->WillRunTask(); + } } else { task->mCompleted = true; #ifdef DEBUG diff --git a/xpcom/threads/TaskDispatcher.h b/xpcom/threads/TaskDispatcher.h index 1f27c32c7d..29a27e6e37 100644 --- a/xpcom/threads/TaskDispatcher.h +++ b/xpcom/threads/TaskDispatcher.h @@ -4,19 +4,19 @@ * 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/. */ -#if !defined(TaskDispatcher_h_) -# define TaskDispatcher_h_ - -# include <queue> - -# include "mozilla/AbstractThread.h" -# include "mozilla/Maybe.h" -# include "mozilla/ProfilerRunnable.h" -# include "mozilla/UniquePtr.h" -# include "nsIDirectTaskDispatcher.h" -# include "nsISupportsImpl.h" -# include "nsTArray.h" -# include "nsThreadUtils.h" +#ifndef XPCOM_THREADS_TASKDISPATCHER_H_ +#define XPCOM_THREADS_TASKDISPATCHER_H_ + +#include <queue> + +#include "mozilla/AbstractThread.h" +#include "mozilla/Maybe.h" +#include "mozilla/ProfilerRunnable.h" +#include "mozilla/UniquePtr.h" +#include "nsIDirectTaskDispatcher.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" namespace mozilla { diff --git a/xpcom/threads/nsIDirectTaskDispatcher.idl b/xpcom/threads/nsIDirectTaskDispatcher.idl index 7d44608708..dd5142e39c 100644 --- a/xpcom/threads/nsIDirectTaskDispatcher.idl +++ b/xpcom/threads/nsIDirectTaskDispatcher.idl @@ -43,7 +43,7 @@ interface nsIDirectTaskDispatcher : nsISupports /** * Returns true if any direct tasks are pending. */ - [noscript] bool haveDirectTasks(); + [noscript] boolean haveDirectTasks(); %{C++ // Infallible version of the above. Will assert that it is successful. diff --git a/xpcom/threads/nsIThreadInternal.idl b/xpcom/threads/nsIThreadInternal.idl index ecc0f540f1..a6763e87dc 100644 --- a/xpcom/threads/nsIThreadInternal.idl +++ b/xpcom/threads/nsIThreadInternal.idl @@ -13,7 +13,7 @@ interface nsIThreadObserver; * The XPCOM thread object implements this interface, which allows a consumer * to observe dispatch activity on the thread. */ -[builtinclass, scriptable, rust_sync, uuid(a3a72e5f-71d9-4add-8f30-59a78fb6d5eb)] +[rust_sync, uuid(a3a72e5f-71d9-4add-8f30-59a78fb6d5eb)] interface nsIThreadInternal : nsIThread { /** @@ -106,5 +106,5 @@ interface nsIThreadObserver : nsISupports * |mayWait| flag was false when calling nsIThread::ProcessNextEvent(). */ void afterProcessNextEvent(in nsIThreadInternal thread, - in bool eventWasProcessed); + in boolean eventWasProcessed); }; diff --git a/xpcom/threads/nsIThreadManager.idl b/xpcom/threads/nsIThreadManager.idl index 879ec05e3a..9629cb630a 100644 --- a/xpcom/threads/nsIThreadManager.idl +++ b/xpcom/threads/nsIThreadManager.idl @@ -23,7 +23,7 @@ interface nsINestedEventLoopCondition : nsISupports /** * Returns true if the current nested event loop should stop spinning. */ - bool isDone(); + boolean isDone(); }; /** |