Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifdef DEBUG
8 : #define ENABLE_STRING_STATS
9 : #endif
10 :
11 : #include "mozilla/Atomics.h"
12 : #include "mozilla/MemoryReporting.h"
13 :
14 : #ifdef ENABLE_STRING_STATS
15 : #include <stdio.h>
16 : #endif
17 :
18 : #include <stdlib.h>
19 : #include "nsSubstring.h"
20 : #include "nsString.h"
21 : #include "nsStringBuffer.h"
22 : #include "nsDependentString.h"
23 : #include "nsMemory.h"
24 : #include "prprf.h"
25 : #include "nsStaticAtom.h"
26 : #include "nsCOMPtr.h"
27 :
28 : #include "mozilla/IntegerPrintfMacros.h"
29 : #ifdef XP_WIN
30 : #include <windows.h>
31 : #include <process.h>
32 : #define getpid() _getpid()
33 : #define pthread_self() GetCurrentThreadId()
34 : #else
35 : #include <pthread.h>
36 : #include <unistd.h>
37 : #endif
38 :
39 : using mozilla::Atomic;
40 :
41 : // ---------------------------------------------------------------------------
42 :
43 : static const char16_t gNullChar = 0;
44 :
45 : char* const nsCharTraits<char>::sEmptyBuffer =
46 : (char*)const_cast<char16_t*>(&gNullChar);
47 : char16_t* const nsCharTraits<char16_t>::sEmptyBuffer =
48 : const_cast<char16_t*>(&gNullChar);
49 :
50 : // ---------------------------------------------------------------------------
51 :
52 : #ifdef ENABLE_STRING_STATS
53 : class nsStringStats
54 : {
55 : public:
56 3 : nsStringStats()
57 3 : : mAllocCount(0)
58 : , mReallocCount(0)
59 : , mFreeCount(0)
60 3 : , mShareCount(0)
61 : {
62 3 : }
63 :
64 0 : ~nsStringStats()
65 0 : {
66 : // this is a hack to suppress duplicate string stats printing
67 : // in seamonkey as a result of the string code being linked
68 : // into seamonkey and libxpcom! :-(
69 0 : if (!mAllocCount && !mAdoptCount) {
70 0 : return;
71 : }
72 :
73 0 : printf("nsStringStats\n");
74 0 : printf(" => mAllocCount: % 10d\n", int(mAllocCount));
75 0 : printf(" => mReallocCount: % 10d\n", int(mReallocCount));
76 0 : printf(" => mFreeCount: % 10d", int(mFreeCount));
77 0 : if (mAllocCount > mFreeCount) {
78 0 : printf(" -- LEAKED %d !!!\n", mAllocCount - mFreeCount);
79 : } else {
80 0 : printf("\n");
81 : }
82 0 : printf(" => mShareCount: % 10d\n", int(mShareCount));
83 0 : printf(" => mAdoptCount: % 10d\n", int(mAdoptCount));
84 0 : printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount));
85 0 : if (mAdoptCount > mAdoptFreeCount) {
86 0 : printf(" -- LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
87 : } else {
88 0 : printf("\n");
89 : }
90 0 : printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n",
91 0 : uintptr_t(getpid()), uintptr_t(pthread_self()));
92 0 : }
93 :
94 : Atomic<int32_t> mAllocCount;
95 : Atomic<int32_t> mReallocCount;
96 : Atomic<int32_t> mFreeCount;
97 : Atomic<int32_t> mShareCount;
98 : Atomic<int32_t> mAdoptCount;
99 : Atomic<int32_t> mAdoptFreeCount;
100 : };
101 3 : static nsStringStats gStringStats;
102 : #define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++
103 : #else
104 : #define STRING_STAT_INCREMENT(_s)
105 : #endif
106 :
107 : // ---------------------------------------------------------------------------
108 :
109 : void
110 1274998 : ReleaseData(void* aData, nsAString::DataFlags aFlags)
111 : {
112 1274998 : if (aFlags & nsAString::DataFlags::SHARED) {
113 118072 : nsStringBuffer::FromData(aData)->Release();
114 1156939 : } else if (aFlags & nsAString::DataFlags::OWNED) {
115 5357 : free(aData);
116 5357 : STRING_STAT_INCREMENT(AdoptFree);
117 : // Treat this as destruction of a "StringAdopt" object for leak
118 : // tracking purposes.
119 5357 : MOZ_LOG_DTOR(aData, "StringAdopt", 1);
120 : }
121 : // otherwise, nothing to do.
122 1275002 : }
123 :
124 : // ---------------------------------------------------------------------------
125 :
126 : // XXX or we could make nsStringBuffer be a friend of nsTAString
127 :
128 : class nsAStringAccessor : public nsAString
129 : {
130 : private:
131 : nsAStringAccessor(); // NOT IMPLEMENTED
132 :
133 : public:
134 1281 : char_type* data() const
135 : {
136 1281 : return mData;
137 : }
138 : size_type length() const
139 : {
140 : return mLength;
141 : }
142 8025 : DataFlags flags() const
143 : {
144 8025 : return mDataFlags;
145 : }
146 :
147 4675 : void set(char_type* aData, size_type aLen, DataFlags aDataFlags)
148 : {
149 4675 : ReleaseData(mData, mDataFlags);
150 4675 : mData = aData;
151 4675 : mLength = aLen;
152 4675 : mDataFlags = aDataFlags;
153 4675 : }
154 : };
155 :
156 : class nsACStringAccessor : public nsACString
157 : {
158 : private:
159 : nsACStringAccessor(); // NOT IMPLEMENTED
160 :
161 : public:
162 0 : char_type* data() const
163 : {
164 0 : return mData;
165 : }
166 : size_type length() const
167 : {
168 : return mLength;
169 : }
170 0 : DataFlags flags() const
171 : {
172 0 : return mDataFlags;
173 : }
174 :
175 0 : void set(char_type* aData, size_type aLen, DataFlags aDataFlags)
176 : {
177 0 : ReleaseData(mData, mDataFlags);
178 0 : mData = aData;
179 0 : mLength = aLen;
180 0 : mDataFlags = aDataFlags;
181 0 : }
182 : };
183 :
184 : // ---------------------------------------------------------------------------
185 :
186 : void
187 76206 : nsStringBuffer::AddRef()
188 : {
189 : // Memory synchronization is not required when incrementing a
190 : // reference count. The first increment of a reference count on a
191 : // thread is not important, since the first use of the object on a
192 : // thread can happen before it. What is important is the transfer
193 : // of the pointer to that thread, which may happen prior to the
194 : // first increment on that thread. The necessary memory
195 : // synchronization is done by the mechanism that transfers the
196 : // pointer between threads.
197 : #ifdef NS_BUILD_REFCNT_LOGGING
198 : uint32_t count =
199 : #endif
200 152412 : mRefCount.fetch_add(1, std::memory_order_relaxed)
201 : #ifdef NS_BUILD_REFCNT_LOGGING
202 76206 : + 1
203 : #endif
204 : ;
205 76206 : STRING_STAT_INCREMENT(Share);
206 76206 : NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
207 76206 : }
208 :
209 : void
210 122013 : nsStringBuffer::Release()
211 : {
212 : // Since this may be the last release on this thread, we need
213 : // release semantics so that prior writes on this thread are visible
214 : // to the thread that destroys the object when it reads mValue with
215 : // acquire semantics.
216 244026 : uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
217 122013 : NS_LOG_RELEASE(this, count, "nsStringBuffer");
218 122013 : if (count == 0) {
219 : // We're going to destroy the object on this thread, so we need
220 : // acquire semantics to synchronize with the memory released by
221 : // the last release on other threads, that is, to ensure that
222 : // writes prior to that release are now visible on this thread.
223 125164 : count = mRefCount.load(std::memory_order_acquire);
224 :
225 62582 : STRING_STAT_INCREMENT(Free);
226 62582 : free(this); // we were allocated with |malloc|
227 : }
228 122013 : }
229 :
230 : /**
231 : * Alloc returns a pointer to a new string header with set capacity.
232 : */
233 : already_AddRefed<nsStringBuffer>
234 94259 : nsStringBuffer::Alloc(size_t aSize)
235 : {
236 94259 : NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
237 94259 : NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
238 : sizeof(nsStringBuffer) + aSize > aSize,
239 : "mStorageSize will truncate");
240 :
241 : nsStringBuffer* hdr =
242 94260 : (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
243 94260 : if (hdr) {
244 94260 : STRING_STAT_INCREMENT(Alloc);
245 :
246 94260 : hdr->mRefCount = 1;
247 94260 : hdr->mStorageSize = aSize;
248 94260 : NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
249 : }
250 94260 : return dont_AddRef(hdr);
251 : }
252 :
253 : nsStringBuffer*
254 2400 : nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize)
255 : {
256 2400 : STRING_STAT_INCREMENT(Realloc);
257 :
258 2400 : NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
259 2400 : NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
260 : sizeof(nsStringBuffer) + aSize > aSize,
261 : "mStorageSize will truncate");
262 :
263 : // no point in trying to save ourselves if we hit this assertion
264 2400 : NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
265 :
266 : // Treat this as a release and addref for refcounting purposes, since we
267 : // just asserted that the refcount is 1. If we don't do that, refcount
268 : // logging will claim we've leaked all sorts of stuff.
269 2400 : NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
270 :
271 2400 : aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
272 2400 : if (aHdr) {
273 2400 : NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
274 2400 : aHdr->mStorageSize = aSize;
275 : }
276 :
277 2400 : return aHdr;
278 : }
279 :
280 : nsStringBuffer*
281 8025 : nsStringBuffer::FromString(const nsAString& aStr)
282 : {
283 : const nsAStringAccessor* accessor =
284 8025 : static_cast<const nsAStringAccessor*>(&aStr);
285 :
286 8025 : if (!(accessor->flags() & nsAString::DataFlags::SHARED)) {
287 6744 : return nullptr;
288 : }
289 :
290 1281 : return FromData(accessor->data());
291 : }
292 :
293 : nsStringBuffer*
294 0 : nsStringBuffer::FromString(const nsACString& aStr)
295 : {
296 : const nsACStringAccessor* accessor =
297 0 : static_cast<const nsACStringAccessor*>(&aStr);
298 :
299 0 : if (!(accessor->flags() & nsACString::DataFlags::SHARED)) {
300 0 : return nullptr;
301 : }
302 :
303 0 : return FromData(accessor->data());
304 : }
305 :
306 : void
307 4675 : nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
308 : bool aMoveOwnership)
309 : {
310 4675 : char16_t* data = static_cast<char16_t*>(Data());
311 :
312 4675 : nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&aStr);
313 4675 : MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
314 : "data should be null terminated");
315 :
316 : nsAString::DataFlags flags =
317 4675 : nsAString::DataFlags::SHARED | nsAString::DataFlags::TERMINATED;
318 :
319 4675 : if (!aMoveOwnership) {
320 4675 : AddRef();
321 : }
322 4675 : accessor->set(data, aLen, flags);
323 4675 : }
324 :
325 : void
326 0 : nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
327 : bool aMoveOwnership)
328 : {
329 0 : char* data = static_cast<char*>(Data());
330 :
331 0 : nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&aStr);
332 0 : MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
333 : "data should be null terminated");
334 :
335 : nsACString::DataFlags flags =
336 0 : nsACString::DataFlags::SHARED | nsACString::DataFlags::TERMINATED;
337 :
338 0 : if (!aMoveOwnership) {
339 0 : AddRef();
340 : }
341 0 : accessor->set(data, aLen, flags);
342 0 : }
343 :
344 : size_t
345 177 : nsStringBuffer::SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const
346 : {
347 177 : return IsReadonly() ? 0 : aMallocSizeOf(this);
348 : }
349 :
350 : size_t
351 0 : nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const
352 : {
353 0 : return aMallocSizeOf(this);
354 : }
355 :
356 : // ---------------------------------------------------------------------------
357 :
358 : // define nsAString
359 : #include "string-template-def-unichar.h"
360 : #include "nsTSubstring.cpp"
361 : #include "string-template-undef.h"
362 :
363 : // define nsACString
364 : #include "string-template-def-char.h"
365 : #include "nsTSubstring.cpp"
366 : #include "string-template-undef.h"
367 :
368 : // Provide rust bindings to the nsA[C]String types
369 : extern "C" {
370 :
371 : // This is a no-op on release, so we ifdef it out such that using it in release
372 : // results in a linker error.
373 : #ifdef DEBUG
374 0 : void Gecko_IncrementStringAdoptCount(void* aData)
375 : {
376 0 : MOZ_LOG_CTOR(aData, "StringAdopt", 1);
377 0 : }
378 : #elif defined(MOZ_DEBUG_RUST)
379 : void Gecko_IncrementStringAdoptCount(void *aData)
380 : {
381 : }
382 : #endif
383 :
384 0 : void Gecko_FinalizeCString(nsACString* aThis)
385 : {
386 0 : aThis->~nsACString();
387 0 : }
388 :
389 0 : void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther)
390 : {
391 0 : aThis->Assign(*aOther);
392 0 : }
393 :
394 0 : void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther)
395 : {
396 0 : aThis->Append(*aOther);
397 0 : }
398 :
399 0 : void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength)
400 : {
401 0 : aThis->SetLength(aLength);
402 0 : }
403 :
404 0 : bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther)
405 : {
406 0 : return aThis->Assign(*aOther, mozilla::fallible);
407 : }
408 :
409 0 : bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther)
410 : {
411 0 : return aThis->Append(*aOther, mozilla::fallible);
412 : }
413 :
414 0 : bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength)
415 : {
416 0 : return aThis->SetLength(aLength, mozilla::fallible);
417 : }
418 :
419 0 : char* Gecko_BeginWritingCString(nsACString* aThis)
420 : {
421 0 : return aThis->BeginWriting();
422 : }
423 :
424 0 : char* Gecko_FallibleBeginWritingCString(nsACString* aThis)
425 : {
426 0 : return aThis->BeginWriting(mozilla::fallible);
427 : }
428 :
429 0 : void Gecko_FinalizeString(nsAString* aThis)
430 : {
431 0 : aThis->~nsAString();
432 0 : }
433 :
434 0 : void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
435 : {
436 0 : aThis->Assign(*aOther);
437 0 : }
438 :
439 0 : void Gecko_AppendString(nsAString* aThis, const nsAString* aOther)
440 : {
441 0 : aThis->Append(*aOther);
442 0 : }
443 :
444 0 : void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength)
445 : {
446 0 : aThis->SetLength(aLength);
447 0 : }
448 :
449 0 : bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther)
450 : {
451 0 : return aThis->Assign(*aOther, mozilla::fallible);
452 : }
453 :
454 0 : bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther)
455 : {
456 0 : return aThis->Append(*aOther, mozilla::fallible);
457 : }
458 :
459 52 : bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength)
460 : {
461 52 : return aThis->SetLength(aLength, mozilla::fallible);
462 : }
463 :
464 26 : char16_t* Gecko_BeginWritingString(nsAString* aThis)
465 : {
466 26 : return aThis->BeginWriting();
467 : }
468 :
469 0 : char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis)
470 : {
471 0 : return aThis->BeginWriting(mozilla::fallible);
472 : }
473 :
474 : } // extern "C"
|