summaryrefslogtreecommitdiffstats
path: root/xpcom
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xpcom/base/MemoryTelemetry.cpp114
-rw-r--r--xpcom/base/MemoryTelemetry.h20
-rw-r--r--xpcom/base/nsMemoryImpl.cpp1
-rw-r--r--xpcom/base/nsSystemInfo.cpp508
-rw-r--r--xpcom/build/PoisonIOInterposer.h14
-rw-r--r--xpcom/build/PoisonIOInterposerBase.cpp25
-rw-r--r--xpcom/build/PoisonIOInterposerWin.cpp3
-rw-r--r--xpcom/build/XREShellData.h6
-rw-r--r--xpcom/ds/StaticAtoms.py5
-rw-r--r--xpcom/ds/Tokenizer.h60
-rw-r--r--xpcom/ds/nsAtomTable.cpp6
-rw-r--r--xpcom/ds/nsTArray.h12
-rw-r--r--xpcom/idl-parser/xpidl/fixtures/xpctest.d.json2
-rwxr-xr-xxpcom/idl-parser/xpidl/xpidl.py97
-rw-r--r--xpcom/io/SpecialSystemDirectory.cpp8
-rw-r--r--xpcom/io/SpecialSystemDirectory.h6
-rw-r--r--xpcom/io/nsDirectoryService.cpp8
-rw-r--r--xpcom/io/nsDirectoryServiceDefs.h1
-rw-r--r--xpcom/io/nsIConverterInputStream.idl2
-rw-r--r--xpcom/io/nsIUnicharInputStream.idl2
-rw-r--r--xpcom/io/nsInputStreamTee.cpp1
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp12
-rw-r--r--xpcom/reflect/xptcall/md/unix/xptcstubs_asm_loongarch64.S1
-rw-r--r--xpcom/reflect/xptinfo/xptcodegen.py21
-rw-r--r--xpcom/string/moz.build3
-rw-r--r--xpcom/string/nsStringBuffer.cpp138
-rw-r--r--xpcom/string/nsStringBuffer.h93
-rw-r--r--xpcom/string/nsStringStats.cpp66
-rw-r--r--xpcom/string/nsStringStats.h32
-rw-r--r--xpcom/string/nsTLiteralString.h12
-rw-r--r--xpcom/string/nsTSubstring.cpp20
-rw-r--r--xpcom/string/nsTSubstring.h27
-rw-r--r--xpcom/tests/TestHarness.h1
-rw-r--r--xpcom/tests/gtest/TestFile.cpp2
-rw-r--r--xpcom/tests/gtest/TestHandleWatcher.cpp11
-rw-r--r--xpcom/tests/gtest/TestMozPromise.cpp2
-rw-r--r--xpcom/tests/gtest/TestStrings.cpp33
-rw-r--r--xpcom/tests/gtest/TestTimers.cpp2
-rw-r--r--xpcom/tests/gtest/TestTokenizer.cpp60
-rw-r--r--xpcom/tests/gtest/moz.build16
-rw-r--r--xpcom/tests/unit/test_windows_registry.js55
-rw-r--r--xpcom/threads/MozPromise.h110
-rw-r--r--xpcom/threads/StaticString.h102
-rw-r--r--xpcom/threads/TaskController.cpp2
-rw-r--r--xpcom/threads/moz.build1
-rw-r--r--xpcom/threads/nsIThreadManager.idl2
-rw-r--r--xpcom/threads/nsThreadManager.cpp4
47 files changed, 1191 insertions, 538 deletions
diff --git a/xpcom/base/MemoryTelemetry.cpp b/xpcom/base/MemoryTelemetry.cpp
index d89c528049..e5013b9c73 100644
--- a/xpcom/base/MemoryTelemetry.cpp
+++ b/xpcom/base/MemoryTelemetry.cpp
@@ -42,10 +42,13 @@ using mozilla::dom::AutoJSAPI;
using mozilla::dom::ContentParent;
// Do not gather data more than once a minute (ms)
-static constexpr uint32_t kTelemetryInterval = 60 * 1000;
+static constexpr uint32_t kTelemetryIntervalMS = 60 * 1000;
-static constexpr const char* kTopicCycleCollectorBegin =
- "cycle-collector-begin";
+// Do not create a timer for telemetry this many seconds after the previous one
+// fires. This exists so that we don't respond to our own timer.
+static constexpr uint32_t kTelemetryCooldownS = 10;
+
+static constexpr const char* kTopicShutdown = "content-child-shutdown";
namespace {
@@ -101,7 +104,7 @@ void MemoryTelemetry::Init() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_RELEASE_ASSERT(obs);
- obs->AddObserver(this, "content-child-shutdown", true);
+ obs->AddObserver(this, kTopicShutdown, true);
}
}
@@ -118,24 +121,66 @@ void MemoryTelemetry::Init() {
return *sInstance;
}
-nsresult MemoryTelemetry::DelayedInit() {
- if (Telemetry::CanRecordExtended()) {
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- MOZ_RELEASE_ASSERT(obs);
+void MemoryTelemetry::DelayedInit() {
+ mCanRun = true;
+ Poke();
+}
- obs->AddObserver(this, kTopicCycleCollectorBegin, true);
+void MemoryTelemetry::Poke() {
+ // Don't do anything that might delay process startup
+ if (!mCanRun) {
+ return;
}
- GatherReports();
+ if (XRE_IsContentProcess() && !Telemetry::CanRecordReleaseData()) {
+ // All memory telemetry produced by content processes is release data, so if
+ // we're not recording release data then don't setup the timers on content
+ // processes.
+ return;
+ }
- return NS_OK;
+ TimeStamp now = TimeStamp::Now();
+
+ if (mLastRun && mLastRun + TimeDuration::FromSeconds(10) < now) {
+ // If we last gathered telemetry less than ten seconds ago then Poke() does
+ // nothing. This is to prevent our own timer waking us up.
+ return;
+ }
+
+ mLastPoke = now;
+ if (!mTimer) {
+ uint32_t delay = kTelemetryIntervalMS;
+ if (mLastRun) {
+ delay = uint32_t(
+ std::min(
+ TimeDuration::FromMilliseconds(kTelemetryIntervalMS),
+ std::max(TimeDuration::FromSeconds(kTelemetryCooldownS),
+ TimeDuration::FromMilliseconds(kTelemetryIntervalMS) -
+ (now - mLastRun)))
+ .ToMilliseconds());
+ }
+ RefPtr<MemoryTelemetry> self(this);
+ auto res = NS_NewTimerWithCallback(
+ [self](nsITimer* aTimer) { self->GatherReports(); }, delay,
+ nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, "MemoryTelemetry::GatherReports");
+
+ if (res.isOk()) {
+ // Errors are ignored, if there was an error then we just don't get
+ // telemetry.
+ mTimer = res.unwrap();
+ }
+ }
}
nsresult MemoryTelemetry::Shutdown() {
+ if (mTimer) {
+ mTimer->Cancel();
+ }
+
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_RELEASE_ASSERT(obs);
- obs->RemoveObserver(this, kTopicCycleCollectorBegin);
+ obs->RemoveObserver(this, kTopicShutdown);
return NS_OK;
}
@@ -199,6 +244,9 @@ nsresult MemoryTelemetry::GatherReports(
}
});
+ mLastRun = TimeStamp::Now();
+ mTimer = nullptr;
+
RefPtr<nsMemoryReporterManager> mgr = nsMemoryReporterManager::GetOrCreate();
MOZ_DIAGNOSTIC_ASSERT(mgr);
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
@@ -385,17 +433,22 @@ void MemoryTelemetry::GatherTotalMemory() {
// Use our handle for the remote process to collect resident unique set
// size information for that process.
+ bool success = true;
for (const auto& info : infos) {
#ifdef XP_MACOSX
int64_t memory =
nsMemoryReporterManager::PhysicalFootprint(info.mHandle);
#else
- int64_t memory =
- nsMemoryReporterManager::ResidentUnique(info.mHandle);
+ int64_t memory =
+ nsMemoryReporterManager::ResidentUnique(info.mHandle);
#endif
if (memory > 0) {
childSizes.AppendElement(memory);
totalMemory += memory;
+ } else {
+ // We don't break out of the loop otherwise the cleanup code
+ // wouldn't run.
+ success = false;
}
#if defined(XP_WIN)
@@ -405,17 +458,22 @@ void MemoryTelemetry::GatherTotalMemory() {
#endif
}
+ Maybe<int64_t> mbTotal;
+ if (success) {
+ mbTotal = Some(totalMemory);
+ }
+
NS_DispatchToMainThread(NS_NewRunnableFunction(
"MemoryTelemetry::FinishGatheringTotalMemory",
- [totalMemory, childSizes = std::move(childSizes)] {
- MemoryTelemetry::Get().FinishGatheringTotalMemory(totalMemory,
+ [mbTotal, childSizes = std::move(childSizes)] {
+ MemoryTelemetry::Get().FinishGatheringTotalMemory(mbTotal,
childSizes);
}));
}));
}
nsresult MemoryTelemetry::FinishGatheringTotalMemory(
- int64_t aTotalMemory, const nsTArray<int64_t>& aChildSizes) {
+ Maybe<int64_t> aTotalMemory, const nsTArray<int64_t>& aChildSizes) {
mGatheringTotalMemory = false;
// Total memory usage can be difficult to measure both accurately and fast
@@ -424,8 +482,10 @@ nsresult MemoryTelemetry::FinishGatheringTotalMemory(
// especially on MacOS where it double-counts shared memory. For a more
// detailed explaination see:
// https://groups.google.com/a/mozilla.org/g/dev-platform/c/WGNOtjHdsdA
- HandleMemoryReport(Telemetry::MEMORY_TOTAL, nsIMemoryReporter::UNITS_BYTES,
- aTotalMemory);
+ if (aTotalMemory) {
+ HandleMemoryReport(Telemetry::MEMORY_TOTAL, nsIMemoryReporter::UNITS_BYTES,
+ aTotalMemory.value());
+ }
if (aChildSizes.Length() > 1) {
int32_t tabsCount;
@@ -502,21 +562,7 @@ nsresult MemoryTelemetry::FinishGatheringTotalMemory(
nsresult MemoryTelemetry::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
- if (strcmp(aTopic, kTopicCycleCollectorBegin) == 0) {
- auto now = TimeStamp::Now();
- if (!mLastPoll.IsNull() &&
- (now - mLastPoll).ToMilliseconds() < kTelemetryInterval) {
- return NS_OK;
- }
-
- mLastPoll = now;
-
- NS_DispatchToCurrentThreadQueue(
- NewRunnableMethod<std::function<void()>>(
- "MemoryTelemetry::GatherReports", this,
- &MemoryTelemetry::GatherReports, nullptr),
- EventQueuePriority::Idle);
- } else if (strcmp(aTopic, "content-child-shutdown") == 0) {
+ if (strcmp(aTopic, kTopicShutdown) == 0) {
if (nsCOMPtr<nsITelemetry> telemetry =
do_GetService("@mozilla.org/base/telemetry;1")) {
telemetry->FlushBatchedChildTelemetry();
diff --git a/xpcom/base/MemoryTelemetry.h b/xpcom/base/MemoryTelemetry.h
index b7c7fe8ad6..f8c4bebb1a 100644
--- a/xpcom/base/MemoryTelemetry.h
+++ b/xpcom/base/MemoryTelemetry.h
@@ -8,6 +8,7 @@
#define mozilla_MemoryTelemetry_h
#include "mozilla/TimeStamp.h"
+#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "nsIObserver.h"
#include "nsITimer.h"
@@ -40,10 +41,14 @@ class MemoryTelemetry final : public nsIObserver,
const std::function<void()>& aCompletionCallback = nullptr);
/**
- * Does expensive initialization, which should happen only after startup has
- * completed, and the event loop is idle.
+ * Called to signal that we can begin collecting telemetry.
*/
- nsresult DelayedInit();
+ void DelayedInit();
+
+ /**
+ * Notify that the browser is active and telemetry should be recorded soon.
+ */
+ void Poke();
nsresult Shutdown();
@@ -57,14 +62,19 @@ class MemoryTelemetry final : public nsIObserver,
static Result<uint32_t, nsresult> GetOpenTabsCount();
void GatherTotalMemory();
- nsresult FinishGatheringTotalMemory(int64_t aTotalMemory,
+ nsresult FinishGatheringTotalMemory(Maybe<int64_t> aTotalMemory,
const nsTArray<int64_t>& aChildSizes);
nsCOMPtr<nsIEventTarget> mThreadPool;
bool mGatheringTotalMemory = false;
- TimeStamp mLastPoll{};
+ TimeStamp mLastRun{};
+ TimeStamp mLastPoke{};
+ nsCOMPtr<nsITimer> mTimer;
+
+ // True if startup is finished and it's okay to start gathering telemetry.
+ bool mCanRun = false;
};
} // namespace mozilla
diff --git a/xpcom/base/nsMemoryImpl.cpp b/xpcom/base/nsMemoryImpl.cpp
index 4996d27c7a..1529455da2 100644
--- a/xpcom/base/nsMemoryImpl.cpp
+++ b/xpcom/base/nsMemoryImpl.cpp
@@ -15,6 +15,7 @@
#include "nsCOMPtr.h"
#include "mozilla/Services.h"
#include "mozilla/Atomics.h"
+#include "mozilla/IntegerPrintfMacros.h"
#ifdef ANDROID
# include <stdio.h>
diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp
index 2b567bda98..8bfd6577b3 100644
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -16,6 +16,7 @@
#include "mozilla/LookAndFeel.h"
#include "mozilla/Sprintf.h"
#include "mozilla/Try.h"
+#include "mozilla/Vector.h"
#include "jsapi.h"
#include "js/PropertyAndElement.h" // JS_SetProperty
#include "mozilla/dom/Promise.h"
@@ -799,51 +800,347 @@ nsresult CollectProcessInfo(ProcessInfo& info) {
std::map<nsCString, nsCString> keyValuePairs;
SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
+# if defined(__arm__) || defined(__aarch64__)
+ // The tables below were taken from
+ // https://raw.githubusercontent.com/util-linux/util-linux/e3192bfd1dd129c70f5416e1464135d8cd22c956/sys-utils/lscpu-arm.c
+
+ /* clang-format off */
+ struct id_part {
+ const int id;
+ const char* name;
+ };
+
+ static const struct id_part arm_part[] = {
+ { 0x810, "ARM810" },
+ { 0x920, "ARM920" },
+ { 0x922, "ARM922" },
+ { 0x926, "ARM926" },
+ { 0x940, "ARM940" },
+ { 0x946, "ARM946" },
+ { 0x966, "ARM966" },
+ { 0xa20, "ARM1020" },
+ { 0xa22, "ARM1022" },
+ { 0xa26, "ARM1026" },
+ { 0xb02, "ARM11 MPCore" },
+ { 0xb36, "ARM1136" },
+ { 0xb56, "ARM1156" },
+ { 0xb76, "ARM1176" },
+ { 0xc05, "Cortex-A5" },
+ { 0xc07, "Cortex-A7" },
+ { 0xc08, "Cortex-A8" },
+ { 0xc09, "Cortex-A9" },
+ { 0xc0d, "Cortex-A17" }, /* Originally A12 */
+ { 0xc0f, "Cortex-A15" },
+ { 0xc0e, "Cortex-A17" },
+ { 0xc14, "Cortex-R4" },
+ { 0xc15, "Cortex-R5" },
+ { 0xc17, "Cortex-R7" },
+ { 0xc18, "Cortex-R8" },
+ { 0xc20, "Cortex-M0" },
+ { 0xc21, "Cortex-M1" },
+ { 0xc23, "Cortex-M3" },
+ { 0xc24, "Cortex-M4" },
+ { 0xc27, "Cortex-M7" },
+ { 0xc60, "Cortex-M0+" },
+ { 0xd01, "Cortex-A32" },
+ { 0xd02, "Cortex-A34" },
+ { 0xd03, "Cortex-A53" },
+ { 0xd04, "Cortex-A35" },
+ { 0xd05, "Cortex-A55" },
+ { 0xd06, "Cortex-A65" },
+ { 0xd07, "Cortex-A57" },
+ { 0xd08, "Cortex-A72" },
+ { 0xd09, "Cortex-A73" },
+ { 0xd0a, "Cortex-A75" },
+ { 0xd0b, "Cortex-A76" },
+ { 0xd0c, "Neoverse-N1" },
+ { 0xd0d, "Cortex-A77" },
+ { 0xd0e, "Cortex-A76AE" },
+ { 0xd13, "Cortex-R52" },
+ { 0xd15, "Cortex-R82" },
+ { 0xd16, "Cortex-R52+" },
+ { 0xd20, "Cortex-M23" },
+ { 0xd21, "Cortex-M33" },
+ { 0xd22, "Cortex-M55" },
+ { 0xd23, "Cortex-M85" },
+ { 0xd40, "Neoverse-V1" },
+ { 0xd41, "Cortex-A78" },
+ { 0xd42, "Cortex-A78AE" },
+ { 0xd43, "Cortex-A65AE" },
+ { 0xd44, "Cortex-X1" },
+ { 0xd46, "Cortex-A510" },
+ { 0xd47, "Cortex-A710" },
+ { 0xd48, "Cortex-X2" },
+ { 0xd49, "Neoverse-N2" },
+ { 0xd4a, "Neoverse-E1" },
+ { 0xd4b, "Cortex-A78C" },
+ { 0xd4c, "Cortex-X1C" },
+ { 0xd4d, "Cortex-A715" },
+ { 0xd4e, "Cortex-X3" },
+ { 0xd4f, "Neoverse-V2" },
+ { 0xd80, "Cortex-A520" },
+ { 0xd81, "Cortex-A720" },
+ { 0xd82, "Cortex-X4" },
+ { 0xd84, "Neoverse-V3" },
+ { 0xd8e, "Neoverse-N3" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part brcm_part[] = {
+ { 0x0f, "Brahma-B15" },
+ { 0x100, "Brahma-B53" },
+ { 0x516, "ThunderX2" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part dec_part[] = {
+ { 0xa10, "SA110" },
+ { 0xa11, "SA1100" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part cavium_part[] = {
+ { 0x0a0, "ThunderX" },
+ { 0x0a1, "ThunderX-88XX" },
+ { 0x0a2, "ThunderX-81XX" },
+ { 0x0a3, "ThunderX-83XX" },
+ { 0x0af, "ThunderX2-99xx" },
+ { 0x0b0, "OcteonTX2" },
+ { 0x0b1, "OcteonTX2-98XX" },
+ { 0x0b2, "OcteonTX2-96XX" },
+ { 0x0b3, "OcteonTX2-95XX" },
+ { 0x0b4, "OcteonTX2-95XXN" },
+ { 0x0b5, "OcteonTX2-95XXMM" },
+ { 0x0b6, "OcteonTX2-95XXO" },
+ { 0x0b8, "ThunderX3-T110" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part apm_part[] = {
+ { 0x000, "X-Gene" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part qcom_part[] = {
+ { 0x00f, "Scorpion" },
+ { 0x02d, "Scorpion" },
+ { 0x04d, "Krait" },
+ { 0x06f, "Krait" },
+ { 0x201, "Kryo" },
+ { 0x205, "Kryo" },
+ { 0x211, "Kryo" },
+ { 0x800, "Falkor-V1/Kryo" },
+ { 0x801, "Kryo-V2" },
+ { 0x802, "Kryo-3XX-Gold" },
+ { 0x803, "Kryo-3XX-Silver" },
+ { 0x804, "Kryo-4XX-Gold" },
+ { 0x805, "Kryo-4XX-Silver" },
+ { 0xc00, "Falkor" },
+ { 0xc01, "Saphira" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part samsung_part[] = {
+ { 0x001, "exynos-m1" },
+ { 0x002, "exynos-m3" },
+ { 0x003, "exynos-m4" },
+ { 0x004, "exynos-m5" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part nvidia_part[] = {
+ { 0x000, "Denver" },
+ { 0x003, "Denver 2" },
+ { 0x004, "Carmel" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part marvell_part[] = {
+ { 0x131, "Feroceon-88FR131" },
+ { 0x581, "PJ4/PJ4b" },
+ { 0x584, "PJ4B-MP" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part apple_part[] = {
+ { 0x000, "Swift" },
+ { 0x001, "Cyclone" },
+ { 0x002, "Typhoon" },
+ { 0x003, "Typhoon/Capri" },
+ { 0x004, "Twister" },
+ { 0x005, "Twister/Elba/Malta" },
+ { 0x006, "Hurricane" },
+ { 0x007, "Hurricane/Myst" },
+ { 0x008, "Monsoon" },
+ { 0x009, "Mistral" },
+ { 0x00b, "Vortex" },
+ { 0x00c, "Tempest" },
+ { 0x00f, "Tempest-M9" },
+ { 0x010, "Vortex/Aruba" },
+ { 0x011, "Tempest/Aruba" },
+ { 0x012, "Lightning" },
+ { 0x013, "Thunder" },
+ { 0x020, "Icestorm-A14" },
+ { 0x021, "Firestorm-A14" },
+ { 0x022, "Icestorm-M1" },
+ { 0x023, "Firestorm-M1" },
+ { 0x024, "Icestorm-M1-Pro" },
+ { 0x025, "Firestorm-M1-Pro" },
+ { 0x026, "Thunder-M10" },
+ { 0x028, "Icestorm-M1-Max" },
+ { 0x029, "Firestorm-M1-Max" },
+ { 0x030, "Blizzard-A15" },
+ { 0x031, "Avalanche-A15" },
+ { 0x032, "Blizzard-M2" },
+ { 0x033, "Avalanche-M2" },
+ { 0x034, "Blizzard-M2-Pro" },
+ { 0x035, "Avalanche-M2-Pro" },
+ { 0x036, "Sawtooth-A16" },
+ { 0x037, "Everest-A16" },
+ { 0x038, "Blizzard-M2-Max" },
+ { 0x039, "Avalanche-M2-Max" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part faraday_part[] = {
+ { 0x526, "FA526" },
+ { 0x626, "FA626" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part intel_part[] = {
+ { 0x200, "i80200" },
+ { 0x210, "PXA250A" },
+ { 0x212, "PXA210A" },
+ { 0x242, "i80321-400" },
+ { 0x243, "i80321-600" },
+ { 0x290, "PXA250B/PXA26x" },
+ { 0x292, "PXA210B" },
+ { 0x2c2, "i80321-400-B0" },
+ { 0x2c3, "i80321-600-B0" },
+ { 0x2d0, "PXA250C/PXA255/PXA26x" },
+ { 0x2d2, "PXA210C" },
+ { 0x411, "PXA27x" },
+ { 0x41c, "IPX425-533" },
+ { 0x41d, "IPX425-400" },
+ { 0x41f, "IPX425-266" },
+ { 0x682, "PXA32x" },
+ { 0x683, "PXA930/PXA935" },
+ { 0x688, "PXA30x" },
+ { 0x689, "PXA31x" },
+ { 0xb11, "SA1110" },
+ { 0xc12, "IPX1200" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part fujitsu_part[] = {
+ { 0x001, "A64FX" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part hisi_part[] = {
+ { 0xd01, "TaiShan-v110" }, /* used in Kunpeng-920 SoC */
+ { 0xd02, "TaiShan-v120" }, /* used in Kirin 990A and 9000S SoCs */
+ { 0xd40, "Cortex-A76" }, /* HiSilicon uses this ID though advertises A76 */
+ { 0xd41, "Cortex-A77" }, /* HiSilicon uses this ID though advertises A77 */
+ { -1, "unknown" },
+ };
+
+ static const struct id_part ampere_part[] = {
+ { 0xac3, "Ampere-1" },
+ { 0xac4, "Ampere-1a" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part ft_part[] = {
+ { 0x303, "FTC310" },
+ { 0x660, "FTC660" },
+ { 0x661, "FTC661" },
+ { 0x662, "FTC662" },
+ { 0x663, "FTC663" },
+ { 0x664, "FTC664" },
+ { 0x862, "FTC862" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part ms_part[] = {
+ { 0xd49, "Azure-Cobalt-100" },
+ { -1, "unknown" },
+ };
+
+ static const struct id_part unknown_part[] = {
+ { -1, "unknown" },
+ };
+
+ struct hw_impl {
+ const int id;
+ const struct id_part *parts;
+ const char *name;
+ };
+
+ static const struct hw_impl hw_implementer[] = {
+ { 0x41, arm_part, "ARM" },
+ { 0x42, brcm_part, "Broadcom" },
+ { 0x43, cavium_part, "Cavium" },
+ { 0x44, dec_part, "DEC" },
+ { 0x46, fujitsu_part, "FUJITSU" },
+ { 0x48, hisi_part, "HiSilicon" },
+ { 0x49, unknown_part, "Infineon" },
+ { 0x4d, unknown_part, "Motorola/Freescale" },
+ { 0x4e, nvidia_part, "NVIDIA" },
+ { 0x50, apm_part, "APM" },
+ { 0x51, qcom_part, "Qualcomm" },
+ { 0x53, samsung_part, "Samsung" },
+ { 0x56, marvell_part, "Marvell" },
+ { 0x61, apple_part, "Apple" },
+ { 0x66, faraday_part, "Faraday" },
+ { 0x69, intel_part, "Intel" },
+ { 0x6d, ms_part, "Microsoft" },
+ { 0x70, ft_part, "Phytium" },
+ { 0xc0, ampere_part, "Ampere" },
+ { -1, unknown_part, "unknown" },
+ };
+ /* clang-format on */
+
+ // cpuFamily from "CPU implementer". Technically, this is only the vendor,
+ // but this is the closed to a family we can get.
+ (void)Tokenizer(keyValuePairs["CPU implementer"_ns])
+ .ReadHexadecimal(&cpuFamily);
+
+ // cpuModel from "CPU part". Not exactly a model number, but close enough,
+ // and that's what lscpu uses.
+ (void)Tokenizer(keyValuePairs["CPU part"_ns]).ReadHexadecimal(&cpuModel);
+
+ // cpuStepping from "CPU variant" (that's what lscpu uses).
+ (void)Tokenizer(keyValuePairs["CPU variant"_ns])
+ .ReadHexadecimal(&cpuStepping);
+
+ for (auto& hw_impl : hw_implementer) {
+ if (hw_impl.id == (int)cpuFamily) {
+ info.cpuVendor.Assign(hw_impl.name);
+ for (auto* p = &hw_impl.parts[0]; p->id != -1; ++p) {
+ if (p->id == (int)cpuModel) {
+ info.cpuName.Assign(p->name);
+ }
+ }
+ }
+ }
+# else
// cpuVendor from "vendor_id"
info.cpuVendor.Assign(keyValuePairs["vendor_id"_ns]);
// cpuName from "model name"
info.cpuName.Assign(keyValuePairs["model name"_ns]);
- {
- // cpuFamily from "cpu family"
- Tokenizer::Token t;
- Tokenizer p(keyValuePairs["cpu family"_ns]);
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cpuFamily = static_cast<int32_t>(t.AsInteger());
- }
- }
-
- {
- // cpuModel from "model"
- Tokenizer::Token t;
- Tokenizer p(keyValuePairs["model"_ns]);
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cpuModel = static_cast<int32_t>(t.AsInteger());
- }
- }
+ // cpuFamily from "cpu family"
+ (void)Tokenizer(keyValuePairs["cpu family"_ns]).ReadInteger(&cpuFamily);
- {
- // cpuStepping from "stepping"
- Tokenizer::Token t;
- Tokenizer p(keyValuePairs["stepping"_ns]);
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cpuStepping = static_cast<int32_t>(t.AsInteger());
- }
- }
+ // cpuModel from "model"
+ (void)Tokenizer(keyValuePairs["model"_ns]).ReadInteger(&cpuModel);
- {
- // physicalCPUs from "cpu cores"
- Tokenizer::Token t;
- Tokenizer p(keyValuePairs["cpu cores"_ns]);
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- physicalCPUs = static_cast<int32_t>(t.AsInteger());
- }
- }
+ // cpuStepping from "stepping"
+ (void)Tokenizer(keyValuePairs["stepping"_ns]).ReadInteger(&cpuStepping);
+# endif
}
{
@@ -852,12 +1149,8 @@ nsresult CollectProcessInfo(ProcessInfo& info) {
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
std::string line;
if (getline(input, line)) {
- Tokenizer::Token t;
- Tokenizer p(line.c_str());
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cpuSpeed = static_cast<int32_t>(t.AsInteger() / 1000);
- }
+ (void)Tokenizer(line.c_str()).ReadInteger(&cpuSpeed);
+ cpuSpeed /= 1000;
}
}
@@ -866,12 +1159,7 @@ nsresult CollectProcessInfo(ProcessInfo& info) {
std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index2/size");
std::string line;
if (getline(input, line)) {
- Tokenizer::Token t;
- Tokenizer p(line.c_str(), nullptr, "K");
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cacheSizeL2 = static_cast<int32_t>(t.AsInteger());
- }
+ (void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL2);
}
}
@@ -880,16 +1168,111 @@ nsresult CollectProcessInfo(ProcessInfo& info) {
std::ifstream input("/sys/devices/system/cpu/cpu0/cache/index3/size");
std::string line;
if (getline(input, line)) {
- Tokenizer::Token t;
- Tokenizer p(line.c_str(), nullptr, "K");
- if (p.Next(t) && t.Type() == Tokenizer::TOKEN_INTEGER &&
- t.AsInteger() <= INT32_MAX) {
- cacheSizeL3 = static_cast<int32_t>(t.AsInteger());
- }
+ (void)Tokenizer(line.c_str(), nullptr, "K").ReadInteger(&cacheSizeL3);
}
}
info.cpuCount = PR_GetNumberOfProcessors();
+ int max_cpu_bits = [&] {
+ // PR_GetNumberOfProcessors gets the value from
+ // /sys/devices/system/cpu/present, but the number of bits in the CPU masks
+ // we're going to read below can be larger (for instance, on the 32-core
+ // 64-threads Threadripper 3970X, PR_GetNumberOfProcessors returns 64, but
+ // the number of bits in the CPU masks is 128). That number of bits is
+ // correlated with the number of CPUs possible (which is different from the
+ // number of CPUs present).
+ std::ifstream input("/sys/devices/system/cpu/possible");
+ std::string line;
+ if (getline(input, line)) {
+ int num;
+ Tokenizer p(line.c_str());
+ // The expected format is `0-n` where n is the number of CPUs possible
+ // - 1.
+ if (p.ReadInteger(&num) && num == 0 && p.CheckChar('-') &&
+ p.ReadInteger(&num) && p.CheckEOF()) {
+ return num + 1;
+ }
+ }
+ // If we weren't able to get the value from /sys/devices/system/cpu/possible
+ // from some reason, fallback to cpuCount, it might work.
+ return info.cpuCount;
+ }();
+
+ // /proc/cpuinfo doesn't have a cross-architecture way of counting physical
+ // cores. On x86, one could look at the number of unique combinations of
+ // `physical id` and `core id` or `cpu cores`, but those are not present on
+ // e.g. aarch64. (and that might not even be enough for NUMA nodes, but
+ // realistically, there probably aren't a lot of people running this code
+ // on such machines)
+ // As a shortcut on x86, you'd think you could just multiply the last
+ // physical id + 1 with the last core id + 1, but at least core ids are not
+ // even necessarily adjacent. (notably, on 13th or 14th generation Intel
+ // CPUs, they go in increments of 4 for performance cores, and then 1 after
+ // hitting the first efficiency core)
+ // /sys/devices/system/cpu/cpu*/topology/core_cpus does show which logical
+ // cores are associated together, such that running the command:
+ // sort -u /sys/devices/system/cpu/cpu*/topology/core_cpus | wc -l
+ // gives a count of physical cores.
+ // There are cpuCount /sys/devices/system/cpu/cpu* directories, and they
+ // are monotonically increasing.
+ // We're going to kind of do that, but reading the actual bitmasks contained
+ // in those files.
+ constexpr int mask_bits = sizeof(uint32_t) * 8;
+
+ Vector<uint32_t> cpumasks;
+ physicalCPUs = [&] {
+ int cores = 0;
+ if (!cpumasks.appendN(0, (max_cpu_bits + mask_bits - 1) / mask_bits)) {
+ return -1;
+ }
+ for (int32_t cpu = 0; cpu < info.cpuCount; ++cpu) {
+ nsPrintfCString core_cpus(
+ "/sys/devices/system/cpu/cpu%d/topology/core_cpus", cpu);
+ std::ifstream input(core_cpus.Data());
+ // Kernel versions before 5.3 didn't have core_cpus, they had
+ // thread_siblings instead, with the same content. As of writing, kernel
+ // version 6.9 still has both, but thread_siblings has been deprecated
+ // since the introduction of core_cpus.
+ if (input.fail()) {
+ core_cpus.Truncate(core_cpus.Length() - sizeof("core_cpus") + 1);
+ core_cpus.AppendLiteral("thread_siblings");
+ input.open(core_cpus.Data());
+ }
+ std::string line;
+ if (!getline(input, line)) {
+ return -1;
+ }
+ Tokenizer p(line.c_str());
+ bool unknown_core = false;
+ // The format of the file is `bitmask0,bitmask1,..,bitmaskn`
+ // where each bitmask is 32-bits wide, and there are as many as
+ // necessary to print max_cpu_bits bits.
+ for (auto& mask : cpumasks) {
+ uint32_t m;
+ if (NS_WARN_IF(!p.ReadHexadecimal(&m, /* aPrefixed = */ false))) {
+ return -1;
+ }
+ if (!p.CheckEOF() && !p.CheckChar(',')) {
+ return -1;
+ }
+ // We're keeping track of all the CPU bits we've seen so far.
+ // If we're now seeing one that has never been set, it means
+ // we're seeing a new physical core (as opposed to a logical
+ // core). We don't want to end the loop now, though, because
+ // we also want to track all the bits we're seeing, in case
+ // subsequent masks have new bits as well.
+ if ((mask & m) != m) {
+ unknown_core = true;
+ }
+ mask |= m;
+ }
+ if (unknown_core) {
+ cores++;
+ }
+ }
+ return cores;
+ }();
+
#else
info.cpuCount = PR_GetNumberOfProcessors();
#endif
@@ -1069,11 +1452,13 @@ nsresult nsSystemInfo::Init() {
return rv;
}
- nsString pointerExplanation;
- widget::WinUtils::GetPointerExplanation(&pointerExplanation);
- rv = SetPropertyAsAString(u"pointingDevices"_ns, pointerExplanation);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
+ if (XRE_IsParentProcess()) {
+ nsString pointerExplanation;
+ widget::WinUtils::GetPointerExplanation(&pointerExplanation);
+ rv = SetPropertyAsAString(u"pointingDevices"_ns, pointerExplanation);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
#endif
@@ -1374,7 +1759,8 @@ JSObject* GetJSObjForProcessInfo(JSContext* aCx, const ProcessInfo& info) {
JS::Rooted<JS::Value> valCountInfo(aCx, JS::Int32Value(info.cpuCount));
JS_SetProperty(aCx, jsInfo, "count", valCountInfo);
- JS::Rooted<JS::Value> valCoreInfo(aCx, JS::Int32Value(info.cpuCores));
+ JS::Rooted<JS::Value> valCoreInfo(
+ aCx, info.cpuCores ? JS::Int32Value(info.cpuCores) : JS::NullValue());
JS_SetProperty(aCx, jsInfo, "cores", valCoreInfo);
JSString* strVendor =
diff --git a/xpcom/build/PoisonIOInterposer.h b/xpcom/build/PoisonIOInterposer.h
index 20adeb835b..3217027b9a 100644
--- a/xpcom/build/PoisonIOInterposer.h
+++ b/xpcom/build/PoisonIOInterposer.h
@@ -10,6 +10,12 @@
#include "mozilla/Types.h"
#include <stdio.h>
+#ifdef _WIN32
+typedef void* platform_handle_t;
+#else
+typedef int platform_handle_t;
+#endif
+
MOZ_BEGIN_EXTERN_C
/** Register file handle to be ignored by poisoning IO interposer. This function
@@ -18,7 +24,7 @@ MOZ_BEGIN_EXTERN_C
* when one of them links the static CRT). In such cases, giving file
* descriptors or FILEs
* doesn't work because _get_osfhandle fails with "invalid parameter". */
-void MozillaRegisterDebugHandle(intptr_t aHandle);
+void MozillaRegisterDebugHandle(platform_handle_t aHandle);
/** Register file descriptor to be ignored by poisoning IO interposer */
void MozillaRegisterDebugFD(int aFd);
@@ -27,7 +33,7 @@ void MozillaRegisterDebugFD(int aFd);
void MozillaRegisterDebugFILE(FILE* aFile);
/** Unregister file handle from being ignored by poisoning IO interposer */
-void MozillaUnRegisterDebugHandle(intptr_t aHandle);
+void MozillaUnRegisterDebugHandle(platform_handle_t aHandle);
/** Unregister file descriptor from being ignored by poisoning IO interposer */
void MozillaUnRegisterDebugFD(int aFd);
@@ -45,7 +51,7 @@ namespace mozilla {
/**
* Check if a file is registered as a debug file.
*/
-bool IsDebugFile(intptr_t aFileID);
+bool IsDebugFile(platform_handle_t aFileID);
/**
* Initialize IO poisoning, this is only safe to do on the main-thread when no
@@ -79,7 +85,7 @@ void ClearPoisonIOInterposer();
# ifdef __cplusplus
namespace mozilla {
-inline bool IsDebugFile(intptr_t aFileID) { return true; }
+inline bool IsDebugFile(platform_handle_t aFileID) { return true; }
inline void InitPoisonIOInterposer() {}
inline void ClearPoisonIOInterposer() {}
# ifdef XP_MACOSX
diff --git a/xpcom/build/PoisonIOInterposerBase.cpp b/xpcom/build/PoisonIOInterposerBase.cpp
index 0a25a3d1f8..268c5672a8 100644
--- a/xpcom/build/PoisonIOInterposerBase.cpp
+++ b/xpcom/build/PoisonIOInterposerBase.cpp
@@ -21,17 +21,18 @@
// Auxiliary method to convert file descriptors to ids
#if defined(XP_WIN)
# include <io.h>
-inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
+inline mozilla::Maybe<platform_handle_t> FileDescriptorToHandle(int aFd) {
intptr_t handle = _get_osfhandle(aFd);
if ((handle == -1) || (handle == -2)) {
// -1: Invalid handle. -2: stdin/out/err not associated with a stream.
return mozilla::Nothing();
}
- return mozilla::Some(handle);
+ return mozilla::Some<platform_handle_t>(
+ reinterpret_cast<platform_handle_t>(handle));
}
#else
-inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
- return mozilla::Some<intptr_t>(aFd);
+inline mozilla::Maybe<platform_handle_t> FileDescriptorToHandle(int aFd) {
+ return mozilla::Some<platform_handle_t>(static_cast<platform_handle_t>(aFd));
}
#endif /* if not XP_WIN */
@@ -161,7 +162,7 @@ class ChunkedList {
}
};
-typedef ChunkedList<intptr_t> FdList;
+typedef ChunkedList<platform_handle_t> FdList;
// Return a list used to hold the IDs of the current debug files. On unix
// an ID is a file descriptor. On Windows it is a file HANDLE.
@@ -176,7 +177,7 @@ namespace mozilla {
// Auxiliary Method to test if a file descriptor is registered to be ignored
// by the poisoning IO interposer
-bool IsDebugFile(intptr_t aFileID) {
+bool IsDebugFile(platform_handle_t aFileID) {
return getDebugFileIDs().Contains(aFileID);
}
@@ -184,7 +185,7 @@ bool IsDebugFile(intptr_t aFileID) {
extern "C" {
-void MozillaRegisterDebugHandle(intptr_t aHandle) {
+void MozillaRegisterDebugHandle(platform_handle_t aHandle) {
DebugFilesAutoLock lockedScope;
FdList& DebugFileIDs = getDebugFileIDs();
MOZ_ASSERT(!DebugFileIDs.Contains(aHandle));
@@ -192,7 +193,7 @@ void MozillaRegisterDebugHandle(intptr_t aHandle) {
}
void MozillaRegisterDebugFD(int aFd) {
- mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
+ mozilla::Maybe<platform_handle_t> handle = FileDescriptorToHandle(aFd);
if (!handle.isSome()) {
return;
}
@@ -207,7 +208,7 @@ void MozillaRegisterDebugFILE(FILE* aFile) {
MozillaRegisterDebugFD(fd);
}
-void MozillaUnRegisterDebugHandle(intptr_t aHandle) {
+void MozillaUnRegisterDebugHandle(platform_handle_t aHandle) {
DebugFilesAutoLock lockedScope;
FdList& DebugFileIDs = getDebugFileIDs();
MOZ_ASSERT(DebugFileIDs.Contains(aHandle));
@@ -215,7 +216,7 @@ void MozillaUnRegisterDebugHandle(intptr_t aHandle) {
}
void MozillaUnRegisterDebugFD(int aFd) {
- mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
+ mozilla::Maybe<platform_handle_t> handle = FileDescriptorToHandle(aFd);
if (!handle.isSome()) {
return;
}
@@ -234,11 +235,11 @@ void MozillaUnRegisterDebugFILE(FILE* aFile) {
} // extern "C"
#ifdef MOZ_REPLACE_MALLOC
-void mozilla::DebugFdRegistry::RegisterHandle(intptr_t aHandle) {
+void mozilla::DebugFdRegistry::RegisterHandle(platform_handle_t aHandle) {
MozillaRegisterDebugHandle(aHandle);
}
-void mozilla::DebugFdRegistry::UnRegisterHandle(intptr_t aHandle) {
+void mozilla::DebugFdRegistry::UnRegisterHandle(platform_handle_t aHandle) {
MozillaUnRegisterDebugHandle(aHandle);
}
#endif
diff --git a/xpcom/build/PoisonIOInterposerWin.cpp b/xpcom/build/PoisonIOInterposerWin.cpp
index ad9a11dbb1..7c37a8cfe1 100644
--- a/xpcom/build/PoisonIOInterposerWin.cpp
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -127,8 +127,7 @@ class WinIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
WinIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
: mozilla::IOInterposeObserver::Observation(
- aOp, sReference,
- !mozilla::IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle))),
+ aOp, sReference, !mozilla::IsDebugFile(aFileHandle)),
mFileHandle(aFileHandle),
mFileHandleType(GetFileType(aFileHandle)),
mHasQueriedFilename(false) {
diff --git a/xpcom/build/XREShellData.h b/xpcom/build/XREShellData.h
index a0f736f658..8fa47ab459 100644
--- a/xpcom/build/XREShellData.h
+++ b/xpcom/build/XREShellData.h
@@ -10,6 +10,9 @@
#if defined(LIBFUZZER)
# include "FuzzerRegistry.h" // LibFuzzerDriver
#endif
+#if defined(AFLFUZZ)
+# include "FuzzingInterface.h" // FuzzingTestFuncRaw
+#endif
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
namespace sandbox {
@@ -34,6 +37,9 @@ struct XREShellData {
#if defined(LIBFUZZER)
LibFuzzerDriver fuzzerDriver;
#endif
+#if defined(AFLFUZZ)
+ int (*fuzzerDriver)(FuzzingTestFuncRaw);
+#endif
};
#endif // XREShellData_h
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
index 5be3323986..d50d31a6a3 100644
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -386,6 +386,8 @@ STATIC_ATOMS = [
Atom("docNoteref", "doc-noteref"),
Atom("docNotice", "doc-notice"),
Atom("docPagebreak", "doc-pagebreak"),
+ Atom("docPagefooter", "doc-pagefooter"),
+ Atom("docPageheader", "doc-pageheader"),
Atom("docPagelist", "doc-pagelist"),
Atom("docPart", "doc-part"),
Atom("docPreface", "doc-preface"),
@@ -448,6 +450,7 @@ STATIC_ATOMS = [
Atom("figcaption", "figcaption"),
Atom("figure", "figure"),
Atom("findbar", "findbar"),
+ Atom("firstColumn", "first-column"),
Atom("firstInput", "first-input"),
Atom("fixed", "fixed"),
Atom("flags", "flags"),
@@ -969,6 +972,7 @@ STATIC_ATOMS = [
Atom("option", "option"),
Atom("_or", "or"),
Atom("order", "order"),
+ Atom("ordinal", "ordinal"),
Atom("orient", "orient"),
Atom("orientation", "orientation"),
Atom("origin_trial", "origin-trial"),
@@ -2461,6 +2465,7 @@ STATIC_ATOMS = [
Atom("DirectoryService_OS_SystemConfigDir", "SysConfD"),
# Atom("DirectoryService_OS_HomeDirectory", "Home"), # "Home" is present above
Atom("DirectoryService_OS_DesktopDirectory", "Desk"),
+ Atom("DirectoryService_OS_DocumentsDirectory", "Docs"),
Atom("DirectoryService_InitCurrentProcess_dummy", "MozBinD"),
Atom("DirectoryService_SystemDirectory", "SysD"),
Atom("DirectoryService_UserLibDirectory", "ULibDir"),
diff --git a/xpcom/ds/Tokenizer.h b/xpcom/ds/Tokenizer.h
index 713b63f269..6a4ec89d51 100644
--- a/xpcom/ds/Tokenizer.h
+++ b/xpcom/ds/Tokenizer.h
@@ -439,6 +439,66 @@ class TTokenizer : public TokenizerBase<TChar> {
}
/**
+ * This is an hexadecimal read helper. It returns false and doesn't move the
+ * read cursor when any of the following happens:
+ * - the token at the read cursor is not 0, and it's not followed by x
+ * - the token(s) that follow don't make a valid hexadecimal number
+ * - the final number doesn't fit the T type
+ * Otherwise true is returned, aValue is filled with the integral number
+ * and the cursor is moved forward.
+ */
+ template <typename T>
+ [[nodiscard]] bool ReadHexadecimal(T* aValue, bool aPrefixed = true) {
+ MOZ_RELEASE_ASSERT(aValue);
+
+ typename base::TAString::const_char_iterator rollback = mRollback;
+ typename base::TAString::const_char_iterator cursor = base::mCursor;
+ auto revert = MakeScopeExit([&] {
+ // Move to a state as if Check() call has failed
+ mRollback = rollback;
+ base::mCursor = cursor;
+ base::mHasFailed = true;
+ });
+
+ if (aPrefixed) {
+ typename base::Token t;
+ if (!Check(base::TOKEN_INTEGER, t) && t.AsInteger() != 0) {
+ return false;
+ }
+
+ if (!CheckChar([](const TChar aChar) { return aChar == 'x'; })) {
+ return false;
+ }
+ }
+
+ TChar c = 'z';
+ mozilla::CheckedInt<T> resultingNumber = 0;
+ while (ReadChar(
+ [](const TChar aChar) {
+ return (aChar >= '0' && aChar <= '9') ||
+ (aChar >= 'A' && aChar <= 'F') ||
+ (aChar >= 'a' && aChar <= 'f');
+ },
+ &c)) {
+ resultingNumber *= 16;
+ if (c <= '9') {
+ resultingNumber += static_cast<uint64_t>(c - '0');
+ } else if (c <= 'F') {
+ resultingNumber += static_cast<uint64_t>(c - 'A') + 0xa;
+ } else {
+ resultingNumber += static_cast<uint64_t>(c - 'a') + 0xa;
+ }
+ }
+ if (c == 'z' || !resultingNumber.isValid()) {
+ return false;
+ }
+
+ *aValue = resultingNumber.value();
+ revert.release();
+ return true;
+ }
+
+ /**
* Returns the read cursor position back as it was before the last call of any
* parsing method of TTokenizer (Next, Check*, Skip*, Read*) so that the last
* operation can be repeated. Rollback cannot be used multiple times, it only
diff --git a/xpcom/ds/nsAtomTable.cpp b/xpcom/ds/nsAtomTable.cpp
index a03fdcce53..379aa647d4 100644
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -82,7 +82,7 @@ nsDynamicAtom* nsDynamicAtom::Create(const nsAString& aString, uint32_t aHash) {
// We tack the chars onto the end of the nsDynamicAtom object.
const bool isAsciiLower =
::IsAsciiLowercase(aString.Data(), aString.Length());
- RefPtr<nsStringBuffer> buffer = nsStringBuffer::FromString(aString);
+ RefPtr<nsStringBuffer> buffer = aString.GetStringBuffer();
if (!buffer) {
buffer = nsStringBuffer::Create(aString.Data(), aString.Length());
if (MOZ_UNLIKELY(!buffer)) {
@@ -111,7 +111,7 @@ void nsAtom::ToString(nsAString& aString) const {
// which is what's important.
aString.AssignLiteral(AsStatic()->String(), mLength);
} else {
- AsDynamic()->StringBuffer()->ToString(mLength, aString);
+ aString.Assign(AsDynamic()->StringBuffer(), mLength);
}
}
@@ -577,7 +577,7 @@ already_AddRefed<nsAtom> nsAtomTable::Atomize(const nsACString& aUTF8String) {
nsString str;
CopyUTF8toUTF16(aUTF8String, str);
- MOZ_ASSERT(nsStringBuffer::FromString(str), "Should create a string buffer");
+ MOZ_ASSERT(str.GetStringBuffer(), "Should create a string buffer");
RefPtr<nsAtom> atom = dont_AddRef(nsDynamicAtom::Create(str, key.mHash));
he->mAtom = atom;
diff --git a/xpcom/ds/nsTArray.h b/xpcom/ds/nsTArray.h
index 56f926ccad..c088f1c4bd 100644
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -369,13 +369,6 @@ struct nsTArray_SafeElementAtHelper<mozilla::OwningNonNull<E>, Derived>
: public nsTArray_SafeElementAtSmartPtrHelper<mozilla::OwningNonNull<E>,
Derived> {};
-// Servo bindings.
-extern "C" void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
- size_t aElementSize);
-extern "C" void Gecko_ClearPODTArray(void* aArray, size_t aElementSize,
- size_t aElementAlign);
-
-//
// This class serves as a base class for nsTArray. It shouldn't be used
// directly. It holds common implementation code that does not depend on the
// element type of the nsTArray.
@@ -393,11 +386,6 @@ class nsTArray_base {
template <class E, class XAlloc>
friend class nsTArray_Impl;
- friend void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity,
- size_t aElemSize);
- friend void Gecko_ClearPODTArray(void* aTArray, size_t aElementSize,
- size_t aElementAlign);
-
protected:
typedef nsTArrayHeader Header;
diff --git a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
index 087a323dd4..b81760c0f5 100644
--- a/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
+++ b/xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
@@ -1313,7 +1313,7 @@
"callable": false,
"consts": [],
"enums": [],
- "id": "nsIXPCTestNoScriptMembers",
+ "id": "nsIXPCTestTypeScript",
"members": [
{
"name": "exposedProp",
diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py
index 11b5d05e58..59e4e355de 100755
--- a/xpcom/idl-parser/xpidl/xpidl.py
+++ b/xpcom/idl-parser/xpidl/xpidl.py
@@ -524,6 +524,8 @@ class Typedef(object):
return "%s%s" % ("*mut " if "out" in calltype else "", self.name)
def tsType(self):
+ # Make sure that underlying type is supported: doesn't throw TSNoncompat.
+ self.realtype.tsType()
return self.name
def __str__(self):
@@ -1214,19 +1216,24 @@ def ensureInfallibleIsSound(methodOrAttribute):
if methodOrAttribute.notxpcom:
raise IDLError(
- "[infallible] does not make sense for a [notxpcom] " "method or attribute",
+ "[infallible] does not make sense for a [notxpcom] method or attribute",
methodOrAttribute.location,
)
# An interface cannot be implemented by JS if it has a notxpcom or nostdcall
-# method or attribute, so it must be marked as builtinclass.
+# method or attribute, or uses a by-value native type, so it must be marked as
+# builtinclass.
def ensureBuiltinClassIfNeeded(methodOrAttribute):
iface = methodOrAttribute.iface
if not iface.attributes.scriptable or iface.attributes.builtinclass:
return
if iface.name == "nsISupports":
return
+
+ # notxpcom and nostdcall types change calling conventions, which breaks
+ # xptcall wrappers. We cannot allow XPCWrappedJS to be created for
+ # interfaces with these methods.
if methodOrAttribute.notxpcom:
raise IDLError(
(
@@ -1246,6 +1253,84 @@ def ensureBuiltinClassIfNeeded(methodOrAttribute):
methodOrAttribute.location,
)
+ # Methods with custom native parameters passed without indirection cannot be
+ # safely handled by xptcall (as it cannot know the calling stack/register
+ # layout), so require the interface to be builtinclass.
+ #
+ # Only "in" parameters and writable attributes are checked, as other
+ # parameters are always passed indirectly, so do not impact calling
+ # conventions.
+ def typeNeedsBuiltinclass(type):
+ inner = type
+ while inner.kind == "typedef":
+ inner = inner.realtype
+ return (
+ inner.kind == "native"
+ and inner.specialtype is None
+ and inner.modifier is None
+ )
+
+ if methodOrAttribute.kind == "method":
+ for p in methodOrAttribute.params:
+ if p.paramtype == "in" and typeNeedsBuiltinclass(p.realtype):
+ raise IDLError(
+ (
+ "scriptable interface '%s' must be marked [builtinclass] "
+ "because it contains method '%s' with a by-value custom native "
+ "parameter '%s'"
+ )
+ % (iface.name, methodOrAttribute.name, p.name),
+ methodOrAttribute.location,
+ )
+ elif methodOrAttribute.kind == "attribute" and not methodOrAttribute.readonly:
+ if typeNeedsBuiltinclass(methodOrAttribute.realtype):
+ raise IDLError(
+ (
+ "scriptable interface '%s' must be marked [builtinclass] because it "
+ "contains writable attribute '%s' with a by-value custom native type"
+ )
+ % (iface.name, methodOrAttribute.name),
+ methodOrAttribute.location,
+ )
+
+
+def ensureNoscriptIfNeeded(methodOrAttribute):
+ if not methodOrAttribute.isScriptable():
+ return
+
+ # NOTE: We can't check forward-declared interfaces to see if they're
+ # scriptable, as the information about whether they're scriptable is not
+ # known here.
+ def typeNeedsNoscript(type):
+ if type.kind in ["array", "legacyarray"]:
+ return typeNeedsNoscript(type.type)
+ if type.kind == "typedef":
+ return typeNeedsNoscript(type.realtype)
+ if type.kind == "native":
+ return type.specialtype is None
+ if type.kind == "interface":
+ return not type.attributes.scriptable
+ return False
+
+ if typeNeedsNoscript(methodOrAttribute.realtype):
+ raise IDLError(
+ "%s '%s' must be marked [noscript] because it has a non-scriptable type"
+ % (methodOrAttribute.kind, methodOrAttribute.name),
+ methodOrAttribute.location,
+ )
+ if methodOrAttribute.kind == "method":
+ for p in methodOrAttribute.params:
+ # iid_is arguments have their type ignored, so shouldn't be checked.
+ if not p.iid_is and typeNeedsNoscript(p.realtype):
+ raise IDLError(
+ (
+ "method '%s' must be marked [noscript] because it has a "
+ "non-scriptable parameter '%s'"
+ )
+ % (methodOrAttribute.name, p.name),
+ methodOrAttribute.location,
+ )
+
class Attribute(object):
kind = "attribute"
@@ -1334,6 +1419,7 @@ class Attribute(object):
ensureInfallibleIsSound(self)
ensureBuiltinClassIfNeeded(self)
+ ensureNoscriptIfNeeded(self)
def toIDL(self):
attribs = attlistToIDL(self.attlist)
@@ -1420,9 +1506,6 @@ class Method(object):
self.iface = iface
self.realtype = self.iface.idl.getName(self.type, self.location)
- ensureInfallibleIsSound(self)
- ensureBuiltinClassIfNeeded(self)
-
for p in self.params:
p.resolve(self)
for p in self.params:
@@ -1462,6 +1545,10 @@ class Method(object):
self.location,
)
+ ensureInfallibleIsSound(self)
+ ensureBuiltinClassIfNeeded(self)
+ ensureNoscriptIfNeeded(self)
+
def isScriptable(self):
if not self.iface.attributes.scriptable:
return False
diff --git a/xpcom/io/SpecialSystemDirectory.cpp b/xpcom/io/SpecialSystemDirectory.cpp
index 4b1055f3fe..419e783a31 100644
--- a/xpcom/io/SpecialSystemDirectory.cpp
+++ b/xpcom/io/SpecialSystemDirectory.cpp
@@ -94,7 +94,6 @@ static nsresult GetWindowsFolder(int aFolder, nsIFile** aFile) {
return NS_NewLocalFile(nsDependentString(path, len), true, aFile);
}
-# if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
/*
* Return the default save-to location for the Windows Library passed in
* through aFolderId.
@@ -122,7 +121,6 @@ static nsresult GetLibrarySaveToPath(int aFallbackFolderId,
return GetWindowsFolder(aFallbackFolderId, aFile);
}
-# endif
/**
* Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
@@ -527,6 +525,9 @@ nsresult GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
case Mac_UserDesktopDirectory: {
return GetOSXFolderType(kUserDomain, kDesktopFolderType, aFile);
}
+ case Mac_UserDocumentsDirectory: {
+ return GetOSXFolderType(kUserDomain, kDocumentsFolderType, aFile);
+ }
case Mac_LocalApplicationsDirectory: {
return GetOSXFolderType(kLocalDomain, kApplicationsFolderType, aFile);
}
@@ -674,12 +675,10 @@ nsresult GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
}
return rv;
}
-# if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
case Win_Documents: {
return GetLibrarySaveToPath(CSIDL_MYDOCUMENTS, FOLDERID_DocumentsLibrary,
aFile);
}
-# endif
#endif // XP_WIN
#if defined(XP_UNIX)
@@ -687,6 +686,7 @@ nsresult GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
return GetUnixHomeDir(aFile);
case Unix_XDG_Desktop:
+ case Unix_XDG_Documents:
case Unix_XDG_Download:
return GetUnixXDGUserDirectory(aSystemSystemDirectory, aFile);
diff --git a/xpcom/io/SpecialSystemDirectory.h b/xpcom/io/SpecialSystemDirectory.h
index e760b0ae26..f1bc101e44 100644
--- a/xpcom/io/SpecialSystemDirectory.h
+++ b/xpcom/io/SpecialSystemDirectory.h
@@ -30,6 +30,7 @@ enum SystemDirectories {
Mac_UserPreferencesDirectory = 107,
Mac_PictureDocumentsDirectory = 108,
Mac_DefaultScreenshotDirectory = 109,
+ Mac_UserDocumentsDirectory = 110,
Win_SystemDirectory = 201,
Win_WindowsDirectory = 202,
@@ -42,14 +43,13 @@ enum SystemDirectories {
Win_LocalAppdata = 224,
Win_ProgramFiles = 225,
Win_Downloads = 226,
-#if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
Win_Documents = 228,
-#endif
Unix_HomeDirectory = 303,
Unix_XDG_Desktop = 304,
+ Unix_XDG_Documents = 305,
Unix_XDG_Download = 306,
- Unix_SystemConfigDirectory = 307
+ Unix_SystemConfigDirectory = 307,
};
nsresult GetSpecialSystemDirectory(SystemDirectories aSystemSystemDirectory,
diff --git a/xpcom/io/nsDirectoryService.cpp b/xpcom/io/nsDirectoryService.cpp
index f5e841c6ea..d5b6c7710c 100644
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -367,6 +367,9 @@ nsDirectoryService::GetFile(const char* aProp, bool* aPersistent,
} else if (inAtom == nsGkAtoms::DirectoryService_OS_DesktopDirectory) {
rv = GetSpecialSystemDirectory(Mac_UserDesktopDirectory,
getter_AddRefs(localFile));
+ } else if (inAtom == nsGkAtoms::DirectoryService_OS_DocumentsDirectory) {
+ rv = GetSpecialSystemDirectory(Mac_UserDocumentsDirectory,
+ getter_AddRefs(localFile));
} else if (inAtom == nsGkAtoms::DirectoryService_LocalApplicationsDirectory) {
rv = GetSpecialSystemDirectory(Mac_LocalApplicationsDirectory,
getter_AddRefs(localFile));
@@ -399,6 +402,8 @@ nsDirectoryService::GetFile(const char* aProp, bool* aPersistent,
} else if (inAtom == nsGkAtoms::DirectoryService_OS_DesktopDirectory) {
rv = GetSpecialSystemDirectory(Win_Desktopdirectory,
getter_AddRefs(localFile));
+ } else if (inAtom == nsGkAtoms::DirectoryService_OS_DocumentsDirectory) {
+ rv = GetSpecialSystemDirectory(Win_Documents, getter_AddRefs(localFile));
} else if (inAtom == nsGkAtoms::DirectoryService_Appdata) {
rv = GetSpecialSystemDirectory(Win_Appdata, getter_AddRefs(localFile));
} else if (inAtom == nsGkAtoms::DirectoryService_LocalAppdata) {
@@ -422,6 +427,9 @@ nsDirectoryService::GetFile(const char* aProp, bool* aPersistent,
} else if (inAtom == nsGkAtoms::DirectoryService_OS_SystemConfigDir) {
rv = GetSpecialSystemDirectory(Unix_SystemConfigDirectory,
getter_AddRefs(localFile));
+ } else if (inAtom == nsGkAtoms::DirectoryService_OS_DocumentsDirectory) {
+ rv = GetSpecialSystemDirectory(Unix_XDG_Documents,
+ getter_AddRefs(localFile));
}
#endif
diff --git a/xpcom/io/nsDirectoryServiceDefs.h b/xpcom/io/nsDirectoryServiceDefs.h
index 9f0368ff06..e2458abece 100644
--- a/xpcom/io/nsDirectoryServiceDefs.h
+++ b/xpcom/io/nsDirectoryServiceDefs.h
@@ -33,6 +33,7 @@
* if there is one, otherwise it's just the same as "Home"
*/
#define NS_OS_DESKTOP_DIR "Desk"
+#define NS_OS_DOCUMENTS_DIR "Docs"
#define NS_OS_DEFAULT_DOWNLOAD_DIR "DfltDwnld"
diff --git a/xpcom/io/nsIConverterInputStream.idl b/xpcom/io/nsIConverterInputStream.idl
index ad1f9bfbc4..1474564a37 100644
--- a/xpcom/io/nsIConverterInputStream.idl
+++ b/xpcom/io/nsIConverterInputStream.idl
@@ -12,7 +12,7 @@ interface nsIInputStream;
* This allows reading unicode strings from a stream, automatically converting
* the bytes from a selected character encoding.
*/
-[scriptable, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
+[scriptable, builtinclass, uuid(FC66FFB6-5404-4908-A4A3-27F92FA0579D)]
interface nsIConverterInputStream : nsIUnicharInputStream {
/**
* Default replacement char value, U+FFFD REPLACEMENT CHARACTER.
diff --git a/xpcom/io/nsIUnicharInputStream.idl b/xpcom/io/nsIUnicharInputStream.idl
index 3ae467cc83..5f1b4a6d8e 100644
--- a/xpcom/io/nsIUnicharInputStream.idl
+++ b/xpcom/io/nsIUnicharInputStream.idl
@@ -42,7 +42,7 @@ native nsWriteUnicharSegmentFun(nsWriteUnicharSegmentFun);
* Abstract UTF-16 input stream
* @see nsIInputStream
*/
-[scriptable, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
+[scriptable, builtinclass, uuid(d5e3bd80-6723-4b92-b0c9-22f6162fd94f)]
interface nsIUnicharInputStream : nsISupports {
/**
* Reads into a caller-provided array.
diff --git a/xpcom/io/nsInputStreamTee.cpp b/xpcom/io/nsInputStreamTee.cpp
index 3c0d32e0cb..656f1dcb38 100644
--- a/xpcom/io/nsInputStreamTee.cpp
+++ b/xpcom/io/nsInputStreamTee.cpp
@@ -10,6 +10,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/Attributes.h"
+#include "mozilla/IntegerPrintfMacros.h"
#include "nsIInputStreamTee.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
diff --git a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp
index 39a6b406f4..d66f300649 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp
+++ b/xpcom/reflect/xptcall/md/unix/xptcinvoke_arm.cpp
@@ -164,6 +164,18 @@ NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
vtable = *reinterpret_cast<vtable_func **>(that);
func = vtable[methodIndex];
+/* !!! IMPORTANT !!!
+ * In the case of paramCount = 0 (and also some other cases in practice but
+ * the compiler doesn't know about them), the stack_space is not initialized.
+ * Reading the stack_space is technically undefined behavior, but practically,
+ * the values we read from there only matter to the called function when they
+ * are initialized.
+ * The asm volatile block makes the compiler ignore that the stack_space
+ * may not be initialized, avoiding it optimizing away e.g. the first loop
+ * test in invoke_copy_to_stack.
+ */
+ asm volatile(";");
+
return func(that, stack_space[base_size * 2 - 3],
stack_space[base_size * 2 - 2],
stack_space[base_size * 2 - 1]);
diff --git a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_loongarch64.S b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_loongarch64.S
index ae4e0cf73f..7dd6e3e1cd 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_loongarch64.S
+++ b/xpcom/reflect/xptcall/md/unix/xptcstubs_asm_loongarch64.S
@@ -1,3 +1,4 @@
+# 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/.
diff --git a/xpcom/reflect/xptinfo/xptcodegen.py b/xpcom/reflect/xptinfo/xptcodegen.py
index 9dd54a6f07..67860ca1f2 100644
--- a/xpcom/reflect/xptinfo/xptcodegen.py
+++ b/xpcom/reflect/xptinfo/xptcodegen.py
@@ -330,30 +330,11 @@ def link_to_cpp(interfaces, fd, header_fd):
)
)
- def is_type_reflectable(type):
- # All native types end up getting tagged as void*, or as wrapper types around void*
- if type["tag"] == "TD_VOID":
- return False
- if type["tag"] in ("TD_ARRAY", "TD_LEGACY_ARRAY"):
- return is_type_reflectable(type["element"])
- return True
-
- def is_method_reflectable(method):
- if "hidden" in method["flags"]:
- return False
-
- for param in method["params"]:
- # Reflected methods can't use non-reflectable types.
- if not is_type_reflectable(param["type"]):
- return False
-
- return True
-
def lower_method(method, ifacename, builtinclass):
methodname = "%s::%s" % (ifacename, method["name"])
isSymbol = "symbol" in method["flags"]
- reflectable = is_method_reflectable(method)
+ reflectable = "hidden" not in method["flags"]
if not reflectable and builtinclass:
# Hide the parameters of methods that can't be called from JS and
diff --git a/xpcom/string/moz.build b/xpcom/string/moz.build
index c0f8091b8f..1220a16bc8 100644
--- a/xpcom/string/moz.build
+++ b/xpcom/string/moz.build
@@ -56,7 +56,4 @@ UNIFIED_SOURCES += [
"RustStringAPI.cpp",
]
-if CONFIG["MOZ_DEBUG"]:
- UNIFIED_SOURCES += ["nsStringStats.cpp"]
-
FINAL_LIBRARY = "xul"
diff --git a/xpcom/string/nsStringBuffer.cpp b/xpcom/string/nsStringBuffer.cpp
index b5d506333f..fbfc91f633 100644
--- a/xpcom/string/nsStringBuffer.cpp
+++ b/xpcom/string/nsStringBuffer.cpp
@@ -7,74 +7,7 @@
#include "nsStringBuffer.h"
#include "mozilla/MemoryReporting.h"
-#include "nsISupportsImpl.h"
-#include "nsString.h"
-
-#ifdef DEBUG
-# include "nsStringStats.h"
-#else
-# define STRING_STAT_INCREMENT(_s)
-#endif
-
-void nsStringBuffer::AddRef() {
- // Memory synchronization is not required when incrementing a
- // reference count. The first increment of a reference count on a
- // thread is not important, since the first use of the object on a
- // thread can happen before it. What is important is the transfer
- // of the pointer to that thread, which may happen prior to the
- // first increment on that thread. The necessary memory
- // synchronization is done by the mechanism that transfers the
- // pointer between threads.
-#ifdef NS_BUILD_REFCNT_LOGGING
- uint32_t count =
-#endif
- mRefCount.fetch_add(1, std::memory_order_relaxed)
-#ifdef NS_BUILD_REFCNT_LOGGING
- + 1
-#endif
- ;
- STRING_STAT_INCREMENT(Share);
- NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
-}
-
-void nsStringBuffer::Release() {
- // Since this may be the last release on this thread, we need
- // release semantics so that prior writes on this thread are visible
- // to the thread that destroys the object when it reads mValue with
- // acquire semantics.
- uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
- NS_LOG_RELEASE(this, count, "nsStringBuffer");
- if (count == 0) {
- // We're going to destroy the object on this thread, so we need
- // acquire semantics to synchronize with the memory released by
- // the last release on other threads, that is, to ensure that
- // writes prior to that release are now visible on this thread.
- count = mRefCount.load(std::memory_order_acquire);
-
- STRING_STAT_INCREMENT(Free);
- free(this); // we were allocated with |malloc|
- }
-}
-
-/**
- * Alloc returns a pointer to a new string header with set capacity.
- */
-already_AddRefed<nsStringBuffer> nsStringBuffer::Alloc(size_t aSize) {
- NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
- NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
- sizeof(nsStringBuffer) + aSize > aSize,
- "mStorageSize will truncate");
-
- auto* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
- if (hdr) {
- STRING_STAT_INCREMENT(Alloc);
-
- hdr->mRefCount = 1;
- hdr->mStorageSize = aSize;
- NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
- }
- return already_AddRefed(hdr);
-}
+#include "mozilla/RefPtr.h"
template <typename CharT>
static already_AddRefed<nsStringBuffer> DoCreate(const CharT* aData,
@@ -101,80 +34,31 @@ already_AddRefed<nsStringBuffer> nsStringBuffer::Create(const char16_t* aData,
}
nsStringBuffer* nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize) {
- STRING_STAT_INCREMENT(Realloc);
-
- NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
- NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
- sizeof(nsStringBuffer) + aSize > aSize,
- "mStorageSize will truncate");
+ MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed");
+ MOZ_ASSERT(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
+ sizeof(nsStringBuffer) + aSize > aSize,
+ "mStorageSize will truncate");
// no point in trying to save ourselves if we hit this assertion
- NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
+ MOZ_ASSERT(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
// Treat this as a release and addref for refcounting purposes, since we
// just asserted that the refcount is 1. If we don't do that, refcount
// logging will claim we've leaked all sorts of stuff.
- NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
+ {
+ mozilla::detail::RefCountLogger::ReleaseLogger logger(aHdr);
+ logger.logRelease(0);
+ }
aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
if (aHdr) {
- NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
+ mozilla::detail::RefCountLogger::logAddRef(aHdr, 1);
aHdr->mStorageSize = aSize;
}
return aHdr;
}
-nsStringBuffer* nsStringBuffer::FromString(const nsAString& aStr) {
- if (!(aStr.mDataFlags & nsAString::DataFlags::REFCOUNTED)) {
- return nullptr;
- }
-
- return FromData(aStr.mData);
-}
-
-nsStringBuffer* nsStringBuffer::FromString(const nsACString& aStr) {
- if (!(aStr.mDataFlags & nsACString::DataFlags::REFCOUNTED)) {
- return nullptr;
- }
-
- return FromData(aStr.mData);
-}
-
-void nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
- bool aMoveOwnership) {
- char16_t* data = static_cast<char16_t*>(Data());
-
- MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
- "data should be null terminated");
-
- nsAString::DataFlags flags =
- nsAString::DataFlags::REFCOUNTED | nsAString::DataFlags::TERMINATED;
-
- if (!aMoveOwnership) {
- AddRef();
- }
- aStr.Finalize();
- aStr.SetData(data, aLen, flags);
-}
-
-void nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
- bool aMoveOwnership) {
- char* data = static_cast<char*>(Data());
-
- MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
- "data should be null terminated");
-
- nsACString::DataFlags flags =
- nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED;
-
- if (!aMoveOwnership) {
- AddRef();
- }
- aStr.Finalize();
- aStr.SetData(data, aLen, flags);
-}
-
size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(
mozilla::MallocSizeOf aMallocSizeOf) const {
return IsReadonly() ? 0 : aMallocSizeOf(this);
diff --git a/xpcom/string/nsStringBuffer.h b/xpcom/string/nsStringBuffer.h
index 43628d6668..dad41e48f7 100644
--- a/xpcom/string/nsStringBuffer.h
+++ b/xpcom/string/nsStringBuffer.h
@@ -9,10 +9,9 @@
#include <atomic>
#include "mozilla/MemoryReporting.h"
-#include "nsStringFwd.h"
-
-template <class T>
-struct already_AddRefed;
+#include "mozilla/Assertions.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/RefCounted.h"
/**
* This structure precedes the string buffers "we" allocate. It may be the
@@ -25,12 +24,12 @@ struct already_AddRefed;
*/
class nsStringBuffer {
private:
- friend class CheckStaticAtomSizes;
-
std::atomic<uint32_t> mRefCount;
uint32_t mStorageSize;
public:
+ MOZ_DECLARE_REFCOUNTED_TYPENAME(nsStringBuffer)
+
/**
* Allocates a new string buffer, with given size in bytes and a
* reference count of one. When the string buffer is no longer needed,
@@ -43,12 +42,25 @@ class nsStringBuffer {
* (i.e., it is not required that the null terminator appear in the last
* storage unit of the string buffer's data).
*
- * This guarantees that StorageSize() returns aStorageSize if the returned
+ * This guarantees that StorageSize() returns aSize if the returned
* buffer is non-null. Some callers like nsAttrValue rely on it.
*
* @return new string buffer or null if out of memory.
*/
- static already_AddRefed<nsStringBuffer> Alloc(size_t aStorageSize);
+ static already_AddRefed<nsStringBuffer> Alloc(size_t aSize) {
+ MOZ_ASSERT(aSize != 0, "zero capacity allocation not allowed");
+ MOZ_ASSERT(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
+ sizeof(nsStringBuffer) + aSize > aSize,
+ "mStorageSize will truncate");
+
+ auto* hdr = (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
+ if (hdr) {
+ hdr->mRefCount = 1;
+ hdr->mStorageSize = aSize;
+ mozilla::detail::RefCountLogger::logAddRef(hdr, 1);
+ }
+ return already_AddRefed(hdr);
+ }
/**
* Returns a string buffer initialized with the given string on it, or null on
@@ -74,16 +86,35 @@ class nsStringBuffer {
*/
static nsStringBuffer* Realloc(nsStringBuffer* aBuf, size_t aStorageSize);
- /**
- * Increment the reference count on this string buffer.
- */
- void NS_FASTCALL AddRef();
+ void AddRef() {
+ // Memory synchronization is not required when incrementing a
+ // reference count. The first increment of a reference count on a
+ // thread is not important, since the first use of the object on a
+ // thread can happen before it. What is important is the transfer
+ // of the pointer to that thread, which may happen prior to the
+ // first increment on that thread. The necessary memory
+ // synchronization is done by the mechanism that transfers the
+ // pointer between threads.
+ uint32_t count = mRefCount.fetch_add(1, std::memory_order_relaxed) + 1;
+ mozilla::detail::RefCountLogger::logAddRef(this, count);
+ }
- /**
- * Decrement the reference count on this string buffer. The string
- * buffer will be destroyed when its reference count reaches zero.
- */
- void NS_FASTCALL Release();
+ void Release() {
+ // Since this may be the last release on this thread, we need release
+ // semantics so that prior writes on this thread are visible to the thread
+ // that destroys the object when it reads mValue with acquire semantics.
+ mozilla::detail::RefCountLogger::ReleaseLogger logger(this);
+ uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
+ logger.logRelease(count);
+ if (count == 0) {
+ // We're going to destroy the object on this thread, so we need acquire
+ // semantics to synchronize with the memory released by the last release
+ // on other threads, that is, to ensure that writes prior to that release
+ // are now visible on this thread.
+ count = mRefCount.load(std::memory_order_acquire);
+ free(this); // We were allocated with malloc.
+ }
+ }
/**
* This method returns the string buffer corresponding to the given data
@@ -149,34 +180,6 @@ class nsStringBuffer {
}
/**
- * The FromString methods return a string buffer for the given string
- * object or null if the string object does not have a string buffer.
- * The reference count of the string buffer is NOT incremented by these
- * methods. If the caller wishes to hold onto the returned value, then
- * the returned string buffer must have its reference count incremented
- * via a call to the AddRef method.
- */
- static nsStringBuffer* FromString(const nsAString& aStr);
- static nsStringBuffer* FromString(const nsACString& aStr);
-
- /**
- * The ToString methods assign this string buffer to a given string
- * object. If the string object does not support sharable string
- * buffers, then its value will be set to a copy of the given string
- * buffer. Otherwise, these methods increment the reference count of the
- * given string buffer. It is important to specify the length (in
- * storage units) of the string contained in the string buffer since the
- * length of the string may be less than its storage size. The string
- * must have a null terminator at the offset specified by |len|.
- *
- * NOTE: storage size is measured in bytes even for wide strings;
- * however, string length is always measured in storage units
- * (2-byte units for wide strings).
- */
- void ToString(uint32_t aLen, nsAString& aStr, bool aMoveOwnership = false);
- void ToString(uint32_t aLen, nsACString& aStr, bool aMoveOwnership = false);
-
- /**
* This measures the size only if the StringBuffer is unshared.
*/
size_t SizeOfIncludingThisIfUnshared(
diff --git a/xpcom/string/nsStringStats.cpp b/xpcom/string/nsStringStats.cpp
deleted file mode 100644
index 7fc3d82ad5..0000000000
--- a/xpcom/string/nsStringStats.cpp
+++ /dev/null
@@ -1,66 +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/. */
-
-#include "nsStringStats.h"
-
-#include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/MemoryReporting.h"
-#include "nsString.h"
-
-#include <stdint.h>
-#include <stdio.h>
-
-#ifdef XP_WIN
-# include <windows.h>
-# include <process.h>
-#else
-# include <unistd.h>
-# include <pthread.h>
-#endif
-
-nsStringStats gStringStats;
-
-nsStringStats::~nsStringStats() {
- // this is a hack to suppress duplicate string stats printing
- // in seamonkey as a result of the string code being linked
- // into seamonkey and libxpcom! :-(
- if (!mAllocCount && !mAdoptCount) {
- return;
- }
-
- // Only print the stats if we detect a leak.
- if (mAllocCount <= mFreeCount && mAdoptCount <= mAdoptFreeCount) {
- return;
- }
-
- printf("nsStringStats\n");
- printf(" => mAllocCount: % 10d\n", int(mAllocCount));
- printf(" => mReallocCount: % 10d\n", int(mReallocCount));
- printf(" => mFreeCount: % 10d", int(mFreeCount));
- if (mAllocCount > mFreeCount) {
- printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount);
- } else {
- printf("\n");
- }
- printf(" => mShareCount: % 10d\n", int(mShareCount));
- printf(" => mAdoptCount: % 10d\n", int(mAdoptCount));
- printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount));
- if (mAdoptCount > mAdoptFreeCount) {
- printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
- } else {
- printf("\n");
- }
-
-#ifdef XP_WIN
- auto pid = uintptr_t(_getpid());
- auto tid = uintptr_t(GetCurrentThreadId());
-#else
- auto pid = uintptr_t(getpid());
- auto tid = uintptr_t(pthread_self());
-#endif
-
- printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n", pid, tid);
-}
diff --git a/xpcom/string/nsStringStats.h b/xpcom/string/nsStringStats.h
deleted file mode 100644
index a38304c2b7..0000000000
--- a/xpcom/string/nsStringStats.h
+++ /dev/null
@@ -1,32 +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/. */
-
-#ifndef nsStringStats_h
-#define nsStringStats_h
-
-#include "mozilla/Atomics.h"
-
-class nsStringStats {
- public:
- nsStringStats() = default;
-
- ~nsStringStats();
-
- using AtomicInt = mozilla::Atomic<int32_t, mozilla::SequentiallyConsistent>;
-
- AtomicInt mAllocCount{0};
- AtomicInt mReallocCount{0};
- AtomicInt mFreeCount{0};
- AtomicInt mShareCount{0};
- AtomicInt mAdoptCount{0};
- AtomicInt mAdoptFreeCount{0};
-};
-
-extern nsStringStats gStringStats;
-
-#define STRING_STAT_INCREMENT(_s) (gStringStats.m##_s##Count)++
-
-#endif // nsStringStats_h
diff --git a/xpcom/string/nsTLiteralString.h b/xpcom/string/nsTLiteralString.h
index 38ffd32bdb..0d14614583 100644
--- a/xpcom/string/nsTLiteralString.h
+++ b/xpcom/string/nsTLiteralString.h
@@ -8,6 +8,7 @@
#define nsTLiteralString_h
#include "nsTStringRepr.h"
+#include "mozilla/StaticString.h"
/**
* nsTLiteralString_CharT
@@ -78,8 +79,10 @@ class nsTLiteralString : public mozilla::detail::nsTStringRepr<T> {
* Prohibit get() on temporaries as in "x"_ns.get().
* These should be written as just "x", using a string literal directly.
*/
- const typename raw_type<T, int>::type get() const&& = delete;
- const typename raw_type<T, int>::type get() const& { return this->mData; }
+ constexpr const typename raw_type<T, int>::type get() const&& = delete;
+ constexpr const typename raw_type<T, int>::type get() const& {
+ return this->mData;
+ }
// At least older gcc versions do not accept these friend declarations,
// complaining about an "invalid argument list" here, but not where the actual
@@ -110,4 +113,9 @@ class nsTLiteralString : public mozilla::detail::nsTStringRepr<T> {
extern template class nsTLiteralString<char>;
extern template class nsTLiteralString<char16_t>;
+namespace mozilla {
+constexpr MOZ_IMPLICIT StaticString::StaticString(nsLiteralCString const& str)
+ : mStr(str.get()) {}
+} // namespace mozilla
+
#endif
diff --git a/xpcom/string/nsTSubstring.cpp b/xpcom/string/nsTSubstring.cpp
index cff2031422..c89b6773d6 100644
--- a/xpcom/string/nsTSubstring.cpp
+++ b/xpcom/string/nsTSubstring.cpp
@@ -17,12 +17,6 @@
#include "nsString.h"
#include "nsTArray.h"
-#ifdef DEBUG
-# include "nsStringStats.h"
-#else
-# define STRING_STAT_INCREMENT(_s)
-#endif
-
// It's not worthwhile to reallocate the buffer and memcpy the
// contents over when the size difference isn't large. With
// power-of-two allocation buckets and 64 as the typical inline
@@ -63,7 +57,6 @@ static void ReleaseData(void* aData, nsAString::DataFlags aFlags) {
MOZ_LOG_DTOR(aData, "StringAdopt", 1);
free(aData);
- STRING_STAT_INCREMENT(AdoptFree);
}
// otherwise, nothing to do.
}
@@ -79,7 +72,6 @@ nsTSubstring<T>::nsTSubstring(char_type* aData, size_type aLength,
AssertValid();
if (aDataFlags & DataFlags::OWNED) {
- STRING_STAT_INCREMENT(Adopt);
MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
}
}
@@ -412,6 +404,17 @@ void nsTSubstring<T>::Assign(const char_type* aData, size_type aLength) {
}
template <typename T>
+void nsTSubstring<T>::Assign(already_AddRefed<nsStringBuffer> aBuffer,
+ size_type aLength) {
+ nsStringBuffer* buffer = aBuffer.take();
+ auto* data = reinterpret_cast<char_type*>(buffer->Data());
+ MOZ_DIAGNOSTIC_ASSERT(data[aLength] == char_type(0),
+ "data should be null terminated");
+ Finalize();
+ SetData(data, aLength, DataFlags::REFCOUNTED | DataFlags::TERMINATED);
+}
+
+template <typename T>
bool nsTSubstring<T>::Assign(const char_type* aData,
const fallible_t& aFallible) {
return Assign(aData, size_type(-1), aFallible);
@@ -631,7 +634,6 @@ void nsTSubstring<T>::Adopt(char_type* aData, size_type aLength) {
SetData(aData, aLength, DataFlags::TERMINATED | DataFlags::OWNED);
- STRING_STAT_INCREMENT(Adopt);
// Treat this as construction of a "StringAdopt" object for leak
// tracking purposes.
MOZ_LOG_CTOR(this->mData, "StringAdopt", 1);
diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h
index 622b931afb..7459333dc0 100644
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -8,20 +8,17 @@
#ifndef nsTSubstring_h
#define nsTSubstring_h
-#include <iterator>
#include <type_traits>
-#include "mozilla/Casting.h"
+#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
-#include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/UniquePtr.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
-#include "mozilla/IntegerTypeTraits.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Span.h"
#include "mozilla/Try.h"
#include "mozilla/Unused.h"
+#include "nsStringBuffer.h"
#include "nsTStringRepr.h"
@@ -422,6 +419,13 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> {
[[nodiscard]] bool NS_FASTCALL Assign(const substring_tuple_type&,
const fallible_t&);
+ void Assign(nsStringBuffer* aBuffer, size_type aLength) {
+ aBuffer->AddRef();
+ Assign(already_AddRefed<nsStringBuffer>(aBuffer), aLength);
+ }
+ void NS_FASTCALL Assign(already_AddRefed<nsStringBuffer> aBuffer,
+ size_type aLength);
+
#if defined(MOZ_USE_CHAR16_WRAPPER)
template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
void Assign(char16ptr_t aData) {
@@ -1142,11 +1146,22 @@ class nsTSubstring : public mozilla::detail::nsTStringRepr<T> {
* clears the pointer without releasing the buffer.
*/
void ForgetSharedBuffer() {
- if (base_string_type::mDataFlags & DataFlags::REFCOUNTED) {
+ if (this->mDataFlags & DataFlags::REFCOUNTED) {
SetToEmptyBuffer();
}
}
+ /**
+ * If the string uses a reference-counted buffer, this method returns a
+ * pointer to it without incrementing the buffer's refcount.
+ */
+ nsStringBuffer* GetStringBuffer() const {
+ if (this->mDataFlags & DataFlags::REFCOUNTED) {
+ return nsStringBuffer::FromData(this->mData);
+ }
+ return nullptr;
+ }
+
protected:
void AssertValid() {
MOZ_DIAGNOSTIC_ASSERT(!(this->mClassFlags & ClassFlags::INVALID_MASK));
diff --git a/xpcom/tests/TestHarness.h b/xpcom/tests/TestHarness.h
index e575497746..40f90d988e 100644
--- a/xpcom/tests/TestHarness.h
+++ b/xpcom/tests/TestHarness.h
@@ -23,6 +23,7 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
+#include "mozilla/IntegerPrintfMacros.h"
#include "nsIDirectoryService.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
diff --git a/xpcom/tests/gtest/TestFile.cpp b/xpcom/tests/gtest/TestFile.cpp
index 6e95366584..fc7650ccbc 100644
--- a/xpcom/tests/gtest/TestFile.cpp
+++ b/xpcom/tests/gtest/TestFile.cpp
@@ -36,7 +36,7 @@ static void SetUseDOSDevicePathSyntax(nsIFile* aFile) {
nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(aFile, &rv);
VerifyResult(rv, "Querying nsILocalFileWin");
- MOZ_ASSERT(winFile);
+ MOZ_RELEASE_ASSERT(winFile);
winFile->SetUseDOSDevicePathSyntax(true);
}
}
diff --git a/xpcom/tests/gtest/TestHandleWatcher.cpp b/xpcom/tests/gtest/TestHandleWatcher.cpp
index c003a026a1..b3997140d1 100644
--- a/xpcom/tests/gtest/TestHandleWatcher.cpp
+++ b/xpcom/tests/gtest/TestHandleWatcher.cpp
@@ -101,8 +101,9 @@ class TestHandleWatcher : public testing::Test {
private:
static bool sIsLive; // just for confirmation
static void AssertIsLive() {
- MOZ_ASSERT(sIsLive,
- "attempted to use `class TestHandleWatcher` outside test group");
+ MOZ_RELEASE_ASSERT(
+ sIsLive,
+ "attempted to use `class TestHandleWatcher` outside test group");
}
static RefPtr<mozilla::SharedThreadPool> sPool;
@@ -339,7 +340,7 @@ struct ActivationTestSetup {
private:
nsIEventTarget* GetQueue(TargetType targetTyoe) {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
switch (targetTyoe) {
case TargetType::Main:
return NS_GetCurrentThread();
@@ -378,9 +379,9 @@ struct ActivationTestSetup {
}
bool Execute() {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
bool const spin = SpinEventLoopUntil([this] {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
return run.load();
}).ok();
return spin && watcher.IsStopped();
diff --git a/xpcom/tests/gtest/TestMozPromise.cpp b/xpcom/tests/gtest/TestMozPromise.cpp
index 9b06304139..02dbef7114 100644
--- a/xpcom/tests/gtest/TestMozPromise.cpp
+++ b/xpcom/tests/gtest/TestMozPromise.cpp
@@ -45,7 +45,7 @@ class DelayedResolveOrReject : public Runnable {
mIterations(aIterations) {}
NS_IMETHOD Run() override {
- MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ MOZ_RELEASE_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (!mPromise) {
// Canceled.
return NS_OK;
diff --git a/xpcom/tests/gtest/TestStrings.cpp b/xpcom/tests/gtest/TestStrings.cpp
index b1458ec6ce..450befc20d 100644
--- a/xpcom/tests/gtest/TestStrings.cpp
+++ b/xpcom/tests/gtest/TestStrings.cpp
@@ -1183,11 +1183,9 @@ TEST_F(Strings, stringbuffer) {
memcpy(data, kData, sizeof(kData));
nsCString str;
- buf->ToString(sizeof(kData) - 1, str);
-
- nsStringBuffer* buf2;
- buf2 = nsStringBuffer::FromString(str);
+ str.Assign(buf, sizeof(kData) - 1);
+ nsStringBuffer* buf2 = str.GetStringBuffer();
EXPECT_EQ(buf, buf2);
}
@@ -1283,9 +1281,6 @@ TEST_F(Strings, string_tointeger) {
int32_t result = nsAutoCString(t->str).ToInteger(&rv, t->radix);
EXPECT_EQ(rv, t->rv);
EXPECT_EQ(result, t->result);
- result = nsAutoCString(t->str).ToInteger(&rv, t->radix);
- EXPECT_EQ(rv, t->rv);
- EXPECT_EQ(result, t->result);
}
}
@@ -2288,6 +2283,30 @@ TEST_F(Strings, printf) {
create_printf_strings(format, (char*)anotherString);
verify_printf_strings(expectedOutput);
}
+ {
+ const char* format = "RightJustify %8s";
+ const char* expectedOutput = "RightJustify foo";
+ create_printf_strings(format, "foo");
+ verify_printf_strings(expectedOutput);
+ }
+ {
+ const char* format = "LeftJustify %-8s";
+ const char* expectedOutput = "LeftJustify foo ";
+ create_printf_strings(format, "foo");
+ verify_printf_strings(expectedOutput);
+ }
+ {
+ const char* format = "RightJustify2 %*s";
+ const char* expectedOutput = "RightJustify2 foo";
+ create_printf_strings(format, 8, "foo");
+ verify_printf_strings(expectedOutput);
+ }
+ {
+ const char* format = "LeftJustify2 %-*s";
+ const char* expectedOutput = "LeftJustify2 foo ";
+ create_printf_strings(format, 8, "foo");
+ verify_printf_strings(expectedOutput);
+ }
}
// We don't need these macros following the printf test.
diff --git a/xpcom/tests/gtest/TestTimers.cpp b/xpcom/tests/gtest/TestTimers.cpp
index ea8792e273..7bd4cb1a14 100644
--- a/xpcom/tests/gtest/TestTimers.cpp
+++ b/xpcom/tests/gtest/TestTimers.cpp
@@ -308,7 +308,7 @@ class FindExpirationTimeState final {
// Create timers, with aNumLowPriority low priority timers first in the queue
void InitTimers(uint32_t aNumLowPriority, uint32_t aType) {
// aType is just for readability.
- MOZ_ASSERT(aType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
+ MOZ_RELEASE_ASSERT(aType == nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
InitTimers(aNumLowPriority, nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, nullptr);
}
diff --git a/xpcom/tests/gtest/TestTokenizer.cpp b/xpcom/tests/gtest/TestTokenizer.cpp
index 1457b82fff..3c1043a92a 100644
--- a/xpcom/tests/gtest/TestTokenizer.cpp
+++ b/xpcom/tests/gtest/TestTokenizer.cpp
@@ -1421,6 +1421,66 @@ TEST(Tokenizer, ReadIntegers)
EXPECT_TRUE(t.CheckEOF());
}
+TEST(Tokenizer, ReadHexadecimals)
+{
+ Tokenizer t("0x100,0x0a,0xFe02dXXX,0a,0xX,0xffffffff,0x7fffffff,0x100000000");
+
+ uint32_t value32;
+ int32_t signed_value32;
+ uint64_t value64;
+
+ // "0x100,"
+ EXPECT_TRUE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(value32 == 0x100);
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0x0a,"
+ EXPECT_TRUE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(value32 == 0xa);
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0xFe02dX,"
+ EXPECT_TRUE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(value32 == 0xfe02d);
+ EXPECT_TRUE(t.CheckWord("XXX"));
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0a,"
+ EXPECT_FALSE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(t.ReadHexadecimal(&value32, /* aPrefixed = */ false));
+ EXPECT_TRUE(value32 == 0xa);
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0xX,"
+ EXPECT_FALSE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(t.Check(Tokenizer::Token::Number(0)));
+ EXPECT_TRUE(t.CheckWord("xX"));
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0xffffffff,"
+ // there is a case to be made that maybe this should be parsed as -1,
+ // but for now, this is not supported.
+ EXPECT_FALSE(t.ReadHexadecimal(&signed_value32));
+ EXPECT_FALSE(t.CheckChar(','));
+
+ EXPECT_TRUE(t.ReadHexadecimal(&value32));
+ EXPECT_TRUE(value32 == std::numeric_limits<uint32_t>::max());
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0x7fffffff,"
+ EXPECT_TRUE(t.ReadHexadecimal(&signed_value32));
+ EXPECT_TRUE(signed_value32 == std::numeric_limits<int32_t>::max());
+ EXPECT_TRUE(t.CheckChar(','));
+
+ // "0x100000000,"
+ EXPECT_FALSE(t.ReadHexadecimal(&value32));
+ EXPECT_FALSE(t.CheckEOF());
+ EXPECT_FALSE(t.CheckChar(','));
+
+ EXPECT_TRUE(t.ReadHexadecimal(&value64));
+ EXPECT_TRUE(t.CheckEOF());
+}
+
TEST(Tokenizer, CheckPhrase)
{
Tokenizer t("foo bar baz");
diff --git a/xpcom/tests/gtest/moz.build b/xpcom/tests/gtest/moz.build
index 4d8d38e89a..22a5b1751d 100644
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -66,8 +66,8 @@ UNIFIED_SOURCES += [
"TestTaskQueue.cpp",
"TestTextFormatter.cpp",
"TestThreadManager.cpp",
- "TestThreadPool.cpp",
"TestThreadPoolListener.cpp",
+ "TestThreadUtils.cpp",
"TestThrottledEventQueue.cpp",
"TestTimeStamp.cpp",
"TestTokenizer.cpp",
@@ -75,24 +75,24 @@ UNIFIED_SOURCES += [
"TestVariant.cpp",
]
+# Bug 1894540 - Fails under TSAN
+if not CONFIG["MOZ_TSAN"]:
+ UNIFIED_SOURCES += [
+ "TestThreadPool.cpp",
+ ]
+
if CONFIG["OS_TARGET"] != "Android":
UNIFIED_SOURCES += [
"TestPipes.cpp",
"TestThreads.cpp",
]
-# skip the test on windows10-aarch64 due to perma-fail, bug 1422219
-if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["TARGET_CPU"] == "aarch64"):
- UNIFIED_SOURCES += ["TestThreadUtils.cpp"]
-
# skip the test on OSX due to frequent failures (bug 1571186)
if CONFIG["OS_TARGET"] != "Darwin":
UNIFIED_SOURCES += ["TestExpirationTracker.cpp"]
# skip the test on windows10-aarch64 and Android, aarch64 due to bug 1545670
-if CONFIG["OS_TARGET"] != "Android" and not (
- CONFIG["OS_TARGET"] == "WINNT" and CONFIG["TARGET_CPU"] == "aarch64"
-):
+if CONFIG["OS_TARGET"] != "Android":
UNIFIED_SOURCES += ["TestTimers.cpp"]
diff --git a/xpcom/tests/unit/test_windows_registry.js b/xpcom/tests/unit/test_windows_registry.js
index ef5082a666..3abc9f1d2b 100644
--- a/xpcom/tests/unit/test_windows_registry.js
+++ b/xpcom/tests/unit/test_windows_registry.js
@@ -5,10 +5,47 @@
* 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/. */
+const { MockRegistry } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistry.sys.mjs"
+);
+
const nsIWindowsRegKey = Ci.nsIWindowsRegKey;
let regKeyComponent = Cc["@mozilla.org/windows-registry-key;1"];
-function run_test() {
+// We run these tests twice: once against the native Windows registry, and a
+// second time against `MockRegistry`. This gives some confidence that
+// `MockRegistry` implements the same APIs as the native Windows registry.
+
+// This order is important: the native registry test must run first, because
+// otherwise we would end up running the `MockRegistry` test twice without
+// checking it against the native Windows registry.
+
+add_task(function test_native_registry() {
+ info("Running test for native Windows registry");
+ run_one_test();
+});
+
+add_task(function test_MockRegistry() {
+ let registry = new MockRegistry();
+ registerCleanupFunction(() => {
+ registry.shutdown();
+ });
+
+ // Before, there's nothing -- just the 3 roots (HKLM, HKCU, HKCR).
+ let linesBefore = [];
+ MockRegistry.dump(null, "", linesBefore.push.bind(linesBefore));
+ strictEqual(linesBefore.length, 3);
+
+ info("Running test for MockRegistry");
+ run_one_test({ cleanup: false });
+
+ // After, there's something -- more than just the roots.
+ let linesAfter = [];
+ MockRegistry.dump(null, "", linesAfter.push.bind(linesAfter));
+ strictEqual(linesAfter.length, 8);
+});
+
+function run_one_test({ cleanup = true } = {}) {
//* create a key structure in a spot that's normally writable (somewhere under HKCU).
let testKey = regKeyComponent.createInstance(nsIWindowsRegKey);
@@ -28,11 +65,16 @@ function run_test() {
//* check that the get* functions fail with the right exception codes if we ask for the wrong type or if the value name doesn't exist at all
test_invalidread_functions(testKey);
+ //* check that removing/deleting values works
+ test_remove_functions(testKey);
+
//* check that creating/enumerating/deleting child keys works
test_childkey_functions(testKey);
//* clean up
- cleanup_test_run(testKey, keyName);
+ if (cleanup) {
+ cleanup_test_run(testKey, keyName);
+ }
}
function setup_test_run(testKey, keyName) {
@@ -152,6 +194,15 @@ function test_invalidread_functions(testKey) {
}
}
+function test_remove_functions(testKey) {
+ strictEqual(testKey.valueCount, 4);
+ testKey.removeValue(TESTDATA_INT64NAME);
+ strictEqual(testKey.valueCount, 3);
+
+ testKey.removeValue(TESTDATA_INT64NAME);
+ strictEqual(testKey.valueCount, 3);
+}
+
function test_childkey_functions(testKey) {
strictEqual(testKey.childCount, 0);
strictEqual(testKey.hasChild(TESTDATA_CHILD_KEY), false);
diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h
index c53037e119..3155d9ea63 100644
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -17,6 +17,7 @@
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
+#include "mozilla/StaticString.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include "nsIDirectTaskDispatcher.h"
@@ -231,7 +232,7 @@ class MozPromise : public MozPromiseBase {
protected:
// MozPromise is the public type, and never constructed directly. Construct
// a MozPromise::Private, defined below.
- MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
+ MozPromise(StaticString aCreationSite, bool aIsCompletionPromise)
: mCreationSite(aCreationSite),
mMutex("MozPromise Mutex"),
mHaveRequest(false),
@@ -241,7 +242,7 @@ class MozPromise : public MozPromiseBase {
mMagic4(&mMutex)
#endif
{
- PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
+ PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite.get(), this);
}
public:
@@ -257,7 +258,7 @@ class MozPromise : public MozPromiseBase {
template <typename ResolveValueType_>
[[nodiscard]] static RefPtr<MozPromise> CreateAndResolve(
- ResolveValueType_&& aResolveValue, const char* aResolveSite) {
+ ResolveValueType_&& aResolveValue, StaticString aResolveSite) {
static_assert(std::is_convertible_v<ResolveValueType_, ResolveValueT>,
"Resolve() argument must be implicitly convertible to "
"MozPromise's ResolveValueT");
@@ -269,7 +270,7 @@ class MozPromise : public MozPromiseBase {
template <typename RejectValueType_>
[[nodiscard]] static RefPtr<MozPromise> CreateAndReject(
- RejectValueType_&& aRejectValue, const char* aRejectSite) {
+ RejectValueType_&& aRejectValue, StaticString aRejectSite) {
static_assert(std::is_convertible_v<RejectValueType_, RejectValueT>,
"Reject() argument must be implicitly convertible to "
"MozPromise's RejectValueT");
@@ -281,7 +282,7 @@ class MozPromise : public MozPromiseBase {
template <typename ResolveOrRejectValueType_>
[[nodiscard]] static RefPtr<MozPromise> CreateAndResolveOrReject(
- ResolveOrRejectValueType_&& aValue, const char* aSite) {
+ ResolveOrRejectValueType_&& aValue, StaticString aSite) {
RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aSite);
p->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue), aSite);
return p;
@@ -497,7 +498,7 @@ class MozPromise : public MozPromiseBase {
RefPtr<MozPromise> mPromise;
};
- ThenValueBase(nsISerialEventTarget* aResponseTarget, const char* aCallSite)
+ ThenValueBase(nsISerialEventTarget* aResponseTarget, StaticString aCallSite)
: mResponseTarget(aResponseTarget), mCallSite(aCallSite) {
MOZ_ASSERT(aResponseTarget);
}
@@ -526,7 +527,7 @@ class MozPromise : public MozPromiseBase {
MOZ_CRASH_UNSAFE_PRINTF(
"MozPromise::ThenValue created from '%s' destroyed without being "
"either disconnected, resolved, or rejected (dispatchRv: %s)",
- mCallSite,
+ mCallSite.get(),
mDispatchRv ? GetStaticErrorName(*mDispatchRv)
: "not dispatched");
}
@@ -543,8 +544,8 @@ class MozPromise : public MozPromiseBase {
PROMISE_LOG(
"%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
"%s dispatch",
- aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
- r.get(), aPromise, this,
+ aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting",
+ mCallSite.get(), r.get(), aPromise, this,
aPromise->mUseSynchronousTaskDispatch ? "synchronous"
: aPromise->mUseDirectTaskDispatch ? "directtask"
: "normal");
@@ -631,7 +632,7 @@ class MozPromise : public MozPromiseBase {
#ifdef PROMISE_DEBUG
uint32_t mMagic1 = sMagic;
#endif
- const char* mCallSite;
+ StaticString mCallSite;
#ifdef PROMISE_DEBUG
uint32_t mMagic2 = sMagic;
#endif
@@ -706,7 +707,7 @@ class MozPromise : public MozPromiseBase {
public:
ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
- const char* aCallSite)
+ StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite),
mThisVal(aThisVal),
mResolveMethod(aResolveMethod),
@@ -767,7 +768,7 @@ class MozPromise : public MozPromiseBase {
public:
ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
ResolveRejectMethodType aResolveRejectMethod,
- const char* aCallSite)
+ StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite),
mThisVal(aThisVal),
mResolveRejectMethod(aResolveRejectMethod) {}
@@ -824,7 +825,7 @@ class MozPromise : public MozPromiseBase {
public:
ThenValue(nsISerialEventTarget* aResponseTarget,
ResolveFunction&& aResolveFunction,
- RejectFunction&& aRejectFunction, const char* aCallSite)
+ RejectFunction&& aRejectFunction, StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite) {
mResolveFunction.emplace(std::move(aResolveFunction));
mRejectFunction.emplace(std::move(aRejectFunction));
@@ -892,7 +893,7 @@ class MozPromise : public MozPromiseBase {
public:
ThenValue(nsISerialEventTarget* aResponseTarget,
ResolveRejectFunction&& aResolveRejectFunction,
- const char* aCallSite)
+ StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite) {
mResolveRejectFunction.emplace(std::move(aResolveRejectFunction));
}
@@ -945,7 +946,7 @@ class MozPromise : public MozPromiseBase {
public:
explicit MapValue(nsISerialEventTarget* aResponseTarget,
- ResolveFunction&& f, const char* aCallSite)
+ ResolveFunction&& f, StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite),
mResolveFunction(Some(std::forward<ResolveFunction>(f))) {}
@@ -991,7 +992,7 @@ class MozPromise : public MozPromiseBase {
public:
explicit MapErrValue(nsISerialEventTarget* aResponseTarget,
- RejectFunction&& f, const char* aCallSite)
+ RejectFunction&& f, StaticString aCallSite)
: ThenValueBase(aResponseTarget, aCallSite),
mRejectFunction(Some(std::forward<RejectFunction>(f))) {}
@@ -1030,7 +1031,7 @@ class MozPromise : public MozPromiseBase {
public:
void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
- const char* aCallSite) {
+ StaticString aCallSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
RefPtr<ThenValueBase> thenValue = aThenValue;
@@ -1040,7 +1041,7 @@ class MozPromise : public MozPromiseBase {
"Using an exclusive promise in a non-exclusive fashion");
mHaveRequest = true;
PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
- aCallSite, this, thenValue.get(), (int)IsPending());
+ aCallSite.get(), this, thenValue.get(), (int)IsPending());
if (!IsPending()) {
thenValue->Dispatch(this);
} else {
@@ -1072,7 +1073,7 @@ class MozPromise : public MozPromiseBase {
using PromiseType = typename ThenValueType::PromiseType;
using Private = typename PromiseType::Private;
- ThenCommand(const char* aCallSite,
+ ThenCommand(StaticString aCallSite,
already_AddRefed<ThenValueType> aThenValue,
MozPromise* aReceiver)
: mCallSite(aCallSite), mThenValue(aThenValue), mReceiver(aReceiver) {}
@@ -1137,7 +1138,7 @@ class MozPromise : public MozPromiseBase {
ThenCommand* operator->() { return this; }
private:
- const char* mCallSite;
+ StaticString mCallSite;
RefPtr<ThenValueType> mThenValue;
RefPtr<MozPromise> mReceiver;
};
@@ -1146,7 +1147,7 @@ class MozPromise : public MozPromiseBase {
template <typename ThisType, typename... Methods,
typename ThenValueType = ThenValue<ThisType*, Methods...>,
typename ReturnType = ThenCommand<ThenValueType>>
- ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
+ ReturnType Then(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
ThisType* aThisVal, Methods... aMethods) {
RefPtr<ThenValueType> thenValue =
new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
@@ -1156,7 +1157,7 @@ class MozPromise : public MozPromiseBase {
template <typename... Functions,
typename ThenValueType = ThenValue<Functions...>,
typename ReturnType = ThenCommand<ThenValueType>>
- ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
+ ReturnType Then(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
Functions&&... aFunctions) {
RefPtr<ThenValueType> thenValue =
new ThenValueType(aResponseTarget, std::move(aFunctions)..., aCallSite);
@@ -1166,7 +1167,7 @@ class MozPromise : public MozPromiseBase {
// 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,
+ auto Map(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
Function&& function) {
RefPtr<MapValue<Function>> thenValue = new MapValue<Function>(
aResponseTarget, std::forward<Function>(function), aCallSite);
@@ -1176,7 +1177,7 @@ class MozPromise : public MozPromiseBase {
// 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,
+ auto MapErr(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
Function&& function) {
RefPtr<MapErrValue<Function>> thenValue = new MapErrValue<Function>(
aResponseTarget, std::forward<Function>(function), aCallSite);
@@ -1185,7 +1186,7 @@ class MozPromise : public MozPromiseBase {
}
void ChainTo(already_AddRefed<Private> aChainedPromise,
- const char* aCallSite) {
+ StaticString aCallSite) {
MutexAutoLock lock(mMutex);
MOZ_DIAGNOSTIC_ASSERT(
!IsExclusive || !mHaveRequest,
@@ -1194,7 +1195,7 @@ class MozPromise : public MozPromiseBase {
RefPtr<Private> chainedPromise = aChainedPromise;
PROMISE_LOG(
"%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
- aCallSite, this, chainedPromise.get(), (int)IsPending());
+ aCallSite.get(), this, chainedPromise.get(), (int)IsPending());
// We want to use the same type of dispatching method with the chained
// promises.
@@ -1305,7 +1306,7 @@ class MozPromise : public MozPromiseBase {
#endif
};
- const char* mCreationSite; // For logging
+ StaticString mCreationSite; // For logging
Mutex mMutex MOZ_UNANNOTATED;
ResolveOrRejectValue mValue;
bool mUseSynchronousTaskDispatch = false;
@@ -1335,21 +1336,22 @@ template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
: public MozPromise<ResolveValueT, RejectValueT, IsExclusive> {
public:
- explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
+ explicit Private(StaticString aCreationSite,
+ bool aIsCompletionPromise = false)
: MozPromise(aCreationSite, aIsCompletionPromise) {}
template <typename ResolveValueT_>
- void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite) {
+ void Resolve(ResolveValueT_&& aResolveValue, StaticString aResolveSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
- PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite,
- this, mCreationSite);
+ PROMISE_LOG("%s resolving MozPromise (%p created at %s)",
+ aResolveSite.get(), this, mCreationSite.get());
if (!IsPending()) {
PROMISE_LOG(
"%s ignored already resolved or rejected MozPromise (%p created at "
"%s)",
- aResolveSite, this, mCreationSite);
+ aResolveSite.get(), this, mCreationSite.get());
return;
}
mValue.SetResolve(std::forward<ResolveValueT_>(aResolveValue));
@@ -1357,17 +1359,17 @@ class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
}
template <typename RejectValueT_>
- void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite) {
+ void Reject(RejectValueT_&& aRejectValue, StaticString aRejectSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
- PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this,
- mCreationSite);
+ PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite.get(),
+ this, mCreationSite.get());
if (!IsPending()) {
PROMISE_LOG(
"%s ignored already resolved or rejected MozPromise (%p created at "
"%s)",
- aRejectSite, this, mCreationSite);
+ aRejectSite.get(), this, mCreationSite.get());
return;
}
mValue.SetReject(std::forward<RejectValueT_>(aRejectValue));
@@ -1375,17 +1377,17 @@ class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
}
template <typename ResolveOrRejectValue_>
- void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite) {
+ void ResolveOrReject(ResolveOrRejectValue_&& aValue, StaticString aSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
- PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite,
- this, mCreationSite);
+ PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)",
+ aSite.get(), this, mCreationSite.get());
if (!IsPending()) {
PROMISE_LOG(
"%s ignored already resolved or rejected MozPromise (%p created at "
"%s)",
- aSite, this, mCreationSite);
+ aSite.get(), this, mCreationSite.get());
return;
}
mValue = std::forward<ResolveOrRejectValue_>(aValue);
@@ -1403,7 +1405,7 @@ class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
- aSite, this, mCreationSite);
+ aSite, this, mCreationSite.get());
MOZ_ASSERT(IsPending(),
"A Promise must not have been already resolved or rejected to "
"set dispatch state");
@@ -1420,7 +1422,7 @@ class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite,
- this, mCreationSite);
+ this, mCreationSite.get());
MOZ_ASSERT(IsPending(),
"A Promise must not have been already resolved or rejected to "
"set dispatch state");
@@ -1437,7 +1439,7 @@ class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s TaskPriority MozPromise (%p created at %s)", aSite, this,
- mCreationSite);
+ mCreationSite.get());
MOZ_ASSERT(IsPending(),
"A Promise must not have been already resolved or rejected to "
"set dispatch state");
@@ -1478,7 +1480,7 @@ class MozPromiseHolderBase {
~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise); }
- already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
+ already_AddRefed<PromiseType> Ensure(StaticString aMethodName) {
static_cast<ImplType*>(this)->Check();
if (!mPromise) {
mPromise = new (typename PromiseType::Private)(aMethodName);
@@ -1498,7 +1500,7 @@ class MozPromiseHolderBase {
}
template <typename ResolveValueType_>
- void Resolve(ResolveValueType_&& aResolveValue, const char* aMethodName) {
+ void Resolve(ResolveValueType_&& aResolveValue, StaticString aMethodName) {
static_assert(std::is_convertible_v<ResolveValueType_,
typename PromiseType::ResolveValueType>,
"Resolve() argument must be implicitly convertible to "
@@ -1513,14 +1515,14 @@ class MozPromiseHolderBase {
template <typename ResolveValueType_>
void ResolveIfExists(ResolveValueType_&& aResolveValue,
- const char* aMethodName) {
+ StaticString aMethodName) {
if (!IsEmpty()) {
Resolve(std::forward<ResolveValueType_>(aResolveValue), aMethodName);
}
}
template <typename RejectValueType_>
- void Reject(RejectValueType_&& aRejectValue, const char* aMethodName) {
+ void Reject(RejectValueType_&& aRejectValue, StaticString aMethodName) {
static_assert(std::is_convertible_v<RejectValueType_,
typename PromiseType::RejectValueType>,
"Reject() argument must be implicitly convertible to "
@@ -1534,7 +1536,7 @@ class MozPromiseHolderBase {
template <typename RejectValueType_>
void RejectIfExists(RejectValueType_&& aRejectValue,
- const char* aMethodName) {
+ StaticString aMethodName) {
if (!IsEmpty()) {
Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
}
@@ -1542,7 +1544,7 @@ class MozPromiseHolderBase {
template <typename ResolveOrRejectValueType_>
void ResolveOrReject(ResolveOrRejectValueType_&& aValue,
- const char* aMethodName) {
+ StaticString aMethodName) {
static_cast<ImplType*>(this)->Check();
MOZ_ASSERT(mPromise);
mPromise->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
@@ -1552,7 +1554,7 @@ class MozPromiseHolderBase {
template <typename ResolveOrRejectValueType_>
void ResolveOrRejectIfExists(ResolveOrRejectValueType_&& aValue,
- const char* aMethodName) {
+ StaticString aMethodName) {
if (!IsEmpty()) {
ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
aMethodName);
@@ -1718,7 +1720,7 @@ class ProxyRunnable : public CancelableRunnable {
template <typename... Storages, typename PromiseType, typename ThisType,
typename... ArgTypes, typename... ActualArgTypes>
static RefPtr<PromiseType> InvokeAsyncImpl(
- nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
+ nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
ActualArgTypes&&... aArgs) {
MOZ_ASSERT(aTarget);
@@ -1758,7 +1760,7 @@ template <typename... Storages, typename PromiseType, typename ThisType,
typename... ArgTypes, typename... ActualArgTypes,
std::enable_if_t<sizeof...(Storages) != 0, int> = 0>
static RefPtr<PromiseType> InvokeAsync(
- nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
+ nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
ActualArgTypes&&... aArgs) {
static_assert(
@@ -1777,7 +1779,7 @@ template <typename... Storages, typename PromiseType, typename ThisType,
typename... ArgTypes, typename... ActualArgTypes,
std::enable_if_t<sizeof...(Storages) == 0, int> = 0>
static RefPtr<PromiseType> InvokeAsync(
- nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
+ nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
ActualArgTypes&&... aArgs) {
static_assert(
@@ -1831,7 +1833,7 @@ constexpr static bool IsRefPtrMozPromise<RefPtr<MozPromise<T, U, B>>> = true;
// Invoke a function object (e.g., lambda) asynchronously.
// Return a promise that the function should eventually resolve or reject.
template <typename Function>
-static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
+static auto InvokeAsync(nsISerialEventTarget* aTarget, StaticString aCallerName,
Function&& aFunction) -> decltype(aFunction()) {
static_assert(!std::is_lvalue_reference_v<Function>,
"Function object must not be passed by lvalue-ref (to avoid "
diff --git a/xpcom/threads/StaticString.h b/xpcom/threads/StaticString.h
new file mode 100644
index 0000000000..26c8675b24
--- /dev/null
+++ b/xpcom/threads/StaticString.h
@@ -0,0 +1,102 @@
+/* -*- 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/. */
+
+#ifndef XPCOM_THREADS_STATICSTRING_H_
+#define XPCOM_THREADS_STATICSTRING_H_
+
+#include <cstddef>
+#include "mozilla/Attributes.h"
+
+// from "nsStringFwd.h"
+template <typename T>
+class nsTLiteralString;
+using nsLiteralCString = nsTLiteralString<char>;
+
+namespace mozilla {
+// class StaticString
+//
+// Wrapper type containing a C-style string which is guaranteed[*] to have been
+// created (potentially indirectly) from static data. Primarily intended for
+// text that may eventually be sent up via telemetry, to avoid the possibility
+// of accidentally exfiltrating PII.
+//
+// `StaticString`, like `template <size_t N> const char (&str)[N]`, can be
+// freely and implicitly converted to `const char *`, to simplify its use as a
+// drop-in replacement when detemplatizing functions.
+//
+// ### Comparison/contrast with `nsLiteralCString`
+//
+// Concretely, `StaticString` is smaller than `nsLiteralCString`, as it does not
+// store the string-length. It's also trivial to construct a `StaticString` from
+// the variable `__func__` or the preprocessor token `__FILE__`, which would
+// require additional work to be used with the latter's `_ns` constructor.
+//
+// Conventionally, the primary intended use case of `StaticString` is subtly
+// different from that of `nsLiteralCString`:
+// * `StaticString` is intended for correctness (etc.) in contexts where the
+// consumer of the string requires that it be static.
+// * `nsLiteralCString` is more for efficiency in contexts where the source
+// string _happens to be_ static, but in which the consumer does not care
+// (and so accepts `nsACString const &` or similar).
+//
+// This is not a hard rule, however, and is notably bent in dom::Promise. (See
+// comments below.)
+//
+// Both are trivially-copyable/-movable/-destructible, guaranteed non-null, and
+// can only contain static data.
+//
+// #### Footnotes
+//
+// [*] ```
+// CHORUS: "What, never?"
+// CAPTAIN: "Well... hardly ever!"
+// CHORUS: "He's hardly ever sick of C!"
+// -- Gilbert & Sullivan, _H.M.S. Pinafore_ (emended)
+// ```
+//
+class StaticString {
+ /* TODO(C++20): convert `constexpr` to `consteval` wherever possible. */
+ const char* mStr; // guaranteed nonnull
+
+ public:
+ template <size_t N>
+ constexpr MOZ_IMPLICIT StaticString(const char (&str)[N]) : mStr(str) {}
+
+ // `nsLiteralCString` has the same guarantees as `StaticString` (both in being
+ // nonnull and containing only static data), so it's safe to construct either
+ // from the other.
+ //
+ // At present we only support construction of a `StaticString` from an
+ // `nsLiteralCString`, since this is zero-cost (the converse would not be),
+ // and is probably the simplest way to support dom::Promise's interoperation
+ // with MozPromise.
+ //
+ // (A more principled approach, in some sense, would be to create a third type
+ // `StaticStringWithLength` (or whatever) acting as the lattice-join of the
+ // two, which could then decay to either one as necessary. This is overkill
+ // for our current goals... but might be worthwhile if, _e.g._, you really
+ // need to get `__func__` into an `nsLiteralCString` rather than just an
+ // `nsDependentCString` for some reason.)
+ //
+ constexpr explicit StaticString(nsLiteralCString const& str);
+
+ constexpr StaticString(StaticString const&) = default;
+ constexpr StaticString(StaticString&&) = default;
+ ~StaticString() = default;
+
+ constexpr MOZ_IMPLICIT operator const char*() const { return mStr; }
+
+ // Not normally needed, but useful for variadic logging functions.
+ constexpr const char* get() const { return mStr; }
+};
+
+// Under the covers, StaticString is as lightweight as a single pointer: it does
+// not store the length of its deta.
+static_assert(sizeof(StaticString) == sizeof(const char*));
+static_assert(alignof(StaticString) == alignof(const char*));
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/threads/TaskController.cpp b/xpcom/threads/TaskController.cpp
index d8c2d5e176..b9559bdb74 100644
--- a/xpcom/threads/TaskController.cpp
+++ b/xpcom/threads/TaskController.cpp
@@ -16,6 +16,7 @@
#include "mozilla/InputTaskManager.h"
#include "mozilla/VsyncTaskManager.h"
#include "mozilla/IOInterposer.h"
+#include "mozilla/Perfetto.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
@@ -135,6 +136,7 @@ class MOZ_RAII AutoProfileTask {
# define AUTO_PROFILE_FOLLOWING_TASK(task) \
nsAutoCString name; \
(task)->GetName(name); \
+ PERFETTO_TRACE_EVENT("task", perfetto::DynamicString{name.get()}); \
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE("Task", OTHER, name); \
mozilla::AutoProfileTask PROFILER_RAII(name, (task)->GetPriority());
#else
diff --git a/xpcom/threads/moz.build b/xpcom/threads/moz.build
index 06d10ad331..09d7649c6f 100644
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -70,6 +70,7 @@ EXPORTS.mozilla += [
"SpinEventLoopUntil.h",
"StateMirroring.h",
"StateWatching.h",
+ "StaticString.h",
"SynchronizedEventQueue.h",
"SyncRunnable.h",
"TaskController.h",
diff --git a/xpcom/threads/nsIThreadManager.idl b/xpcom/threads/nsIThreadManager.idl
index 9629cb630a..2c5de409fe 100644
--- a/xpcom/threads/nsIThreadManager.idl
+++ b/xpcom/threads/nsIThreadManager.idl
@@ -29,7 +29,7 @@ interface nsINestedEventLoopCondition : nsISupports
/**
* An interface for creating and locating nsIThread instances.
*/
-[scriptable, uuid(1be89eca-e2f7-453b-8d38-c11ba247f6f3)]
+[scriptable, builtinclass, uuid(1be89eca-e2f7-453b-8d38-c11ba247f6f3)]
interface nsIThreadManager : nsISupports
{
/**
diff --git a/xpcom/threads/nsThreadManager.cpp b/xpcom/threads/nsThreadManager.cpp
index 3ea324a6c6..52c7db1be4 100644
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -21,6 +21,7 @@
#include "mozilla/InputTaskManager.h"
#include "mozilla/Mutex.h"
#include "mozilla/NeverDestroyed.h"
+#include "mozilla/Perfetto.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/SpinEventLoopUntil.h"
@@ -270,6 +271,9 @@ nsThreadManager::nsThreadManager()
nsThreadManager::~nsThreadManager() = default;
nsresult nsThreadManager::Init() {
+ // Initialize perfetto if on Android.
+ InitPerfetto();
+
// Child processes need to initialize the thread manager before they
// initialize XPCOM in order to set up the crash reporter. This leads to
// situations where we get initialized twice.