Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/string/nsSubstring.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsAString.h"
20
#include "nsString.h"
21
#include "nsStringBuffer.h"
22
#include "nsDependentString.h"
23
#include "nsPrintfCString.h"
24
#include "nsMemory.h"
25
#include "prprf.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
#ifdef STRING_BUFFER_CANARY
40
#define CHECK_STRING_BUFFER_CANARY(c)                                     \
41
42.0M
  do {                                                                    \
42
42.0M
    if ((c) != CANARY_OK) {                                               \
43
0
      MOZ_CRASH_UNSAFE_PRINTF("Bad canary value 0x%x", c);                  \
44
0
    }                                                                     \
45
42.0M
  } while(0)
46
#else
47
#define CHECK_STRING_BUFFER_CANARY(c)                                     \
48
  do {                                                                    \
49
  } while(0)
50
#endif
51
52
using mozilla::Atomic;
53
54
// ---------------------------------------------------------------------------
55
56
static const char16_t gNullChar = 0;
57
58
char* const nsCharTraits<char>::sEmptyBuffer =
59
  (char*)const_cast<char16_t*>(&gNullChar);
60
char16_t* const nsCharTraits<char16_t>::sEmptyBuffer =
61
  const_cast<char16_t*>(&gNullChar);
62
63
// ---------------------------------------------------------------------------
64
65
#ifdef ENABLE_STRING_STATS
66
class nsStringStats
67
{
68
public:
69
  nsStringStats()
70
    : mAllocCount(0)
71
    , mReallocCount(0)
72
    , mFreeCount(0)
73
    , mShareCount(0)
74
  {
75
  }
76
77
  ~nsStringStats()
78
  {
79
    // this is a hack to suppress duplicate string stats printing
80
    // in seamonkey as a result of the string code being linked
81
    // into seamonkey and libxpcom! :-(
82
    if (!mAllocCount && !mAdoptCount) {
83
      return;
84
    }
85
86
    printf("nsStringStats\n");
87
    printf(" => mAllocCount:     % 10d\n", int(mAllocCount));
88
    printf(" => mReallocCount:   % 10d\n", int(mReallocCount));
89
    printf(" => mFreeCount:      % 10d", int(mFreeCount));
90
    if (mAllocCount > mFreeCount) {
91
      printf("  --  LEAKED %d !!!\n", mAllocCount - mFreeCount);
92
    } else {
93
      printf("\n");
94
    }
95
    printf(" => mShareCount:     % 10d\n", int(mShareCount));
96
    printf(" => mAdoptCount:     % 10d\n", int(mAdoptCount));
97
    printf(" => mAdoptFreeCount: % 10d", int(mAdoptFreeCount));
98
    if (mAdoptCount > mAdoptFreeCount) {
99
      printf("  --  LEAKED %d !!!\n", mAdoptCount - mAdoptFreeCount);
100
    } else {
101
      printf("\n");
102
    }
103
    printf(" => Process ID: %" PRIuPTR ", Thread ID: %" PRIuPTR "\n",
104
           uintptr_t(getpid()), uintptr_t(pthread_self()));
105
  }
106
107
  typedef Atomic<int32_t,
108
                 mozilla::SequentiallyConsistent,
109
                 mozilla::recordreplay::Behavior::DontPreserve> AtomicInt;
110
111
  AtomicInt mAllocCount;
112
  AtomicInt mReallocCount;
113
  AtomicInt mFreeCount;
114
  AtomicInt mShareCount;
115
  AtomicInt mAdoptCount;
116
  AtomicInt mAdoptFreeCount;
117
};
118
static nsStringStats gStringStats;
119
#define STRING_STAT_INCREMENT(_s) (gStringStats.m ## _s ## Count)++
120
#else
121
#define STRING_STAT_INCREMENT(_s)
122
#endif
123
124
// ---------------------------------------------------------------------------
125
126
void
127
ReleaseData(void* aData, nsAString::DataFlags aFlags)
128
331M
{
129
331M
  if (aFlags & nsAString::DataFlags::REFCOUNTED) {
130
42.0M
    nsStringBuffer::FromData(aData)->Release();
131
289M
  } else if (aFlags & nsAString::DataFlags::OWNED) {
132
2.73M
    free(aData);
133
2.73M
    STRING_STAT_INCREMENT(AdoptFree);
134
2.73M
    // Treat this as destruction of a "StringAdopt" object for leak
135
2.73M
    // tracking purposes.
136
2.73M
    MOZ_LOG_DTOR(aData, "StringAdopt", 1);
137
2.73M
  }
138
331M
  // otherwise, nothing to do.
139
331M
}
140
141
// ---------------------------------------------------------------------------
142
143
// XXX or we could make nsStringBuffer be a friend of nsTAString
144
145
class nsAStringAccessor : public nsAString
146
{
147
private:
148
  nsAStringAccessor(); // NOT IMPLEMENTED
149
150
public:
151
  char_type* data() const
152
6
  {
153
6
    return mData;
154
6
  }
155
  size_type length() const
156
0
  {
157
0
    return mLength;
158
0
  }
159
  DataFlags flags() const
160
6
  {
161
6
    return mDataFlags;
162
6
  }
163
164
  void set(char_type* aData, size_type aLen, DataFlags aDataFlags)
165
0
  {
166
0
    ReleaseData(mData, mDataFlags);
167
0
    SetData(aData, aLen, aDataFlags);
168
0
  }
169
};
170
171
class nsACStringAccessor : public nsACString
172
{
173
private:
174
  nsACStringAccessor(); // NOT IMPLEMENTED
175
176
public:
177
  char_type* data() const
178
0
  {
179
0
    return mData;
180
0
  }
181
  size_type length() const
182
0
  {
183
0
    return mLength;
184
0
  }
185
  DataFlags flags() const
186
0
  {
187
0
    return mDataFlags;
188
0
  }
189
190
  void set(char_type* aData, size_type aLen, DataFlags aDataFlags)
191
0
  {
192
0
    ReleaseData(mData, mDataFlags);
193
0
    SetData(aData, aLen, aDataFlags);
194
0
  }
195
};
196
197
// ---------------------------------------------------------------------------
198
199
void
200
nsStringBuffer::AddRef()
201
19.9M
{
202
19.9M
  // Memory synchronization is not required when incrementing a
203
19.9M
  // reference count.  The first increment of a reference count on a
204
19.9M
  // thread is not important, since the first use of the object on a
205
19.9M
  // thread can happen before it.  What is important is the transfer
206
19.9M
  // of the pointer to that thread, which may happen prior to the
207
19.9M
  // first increment on that thread.  The necessary memory
208
19.9M
  // synchronization is done by the mechanism that transfers the
209
19.9M
  // pointer between threads.
210
#ifdef NS_BUILD_REFCNT_LOGGING
211
  uint32_t count =
212
#endif
213
    mRefCount.fetch_add(1, std::memory_order_relaxed)
214
#ifdef NS_BUILD_REFCNT_LOGGING
215
    + 1
216
#endif
217
    ;
218
19.9M
  STRING_STAT_INCREMENT(Share);
219
19.9M
  NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
220
19.9M
}
221
222
void
223
nsStringBuffer::Release()
224
42.0M
{
225
42.0M
  CHECK_STRING_BUFFER_CANARY(mCanary);
226
42.0M
227
42.0M
  // Since this may be the last release on this thread, we need
228
42.0M
  // release semantics so that prior writes on this thread are visible
229
42.0M
  // to the thread that destroys the object when it reads mValue with
230
42.0M
  // acquire semantics.
231
42.0M
  uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
232
42.0M
  NS_LOG_RELEASE(this, count, "nsStringBuffer");
233
42.0M
  if (count == 0) {
234
22.1M
    // We're going to destroy the object on this thread, so we need
235
22.1M
    // acquire semantics to synchronize with the memory released by
236
22.1M
    // the last release on other threads, that is, to ensure that
237
22.1M
    // writes prior to that release are now visible on this thread.
238
22.1M
    count = mRefCount.load(std::memory_order_acquire);
239
22.1M
#ifdef STRING_BUFFER_CANARY
240
22.1M
    mCanary = CANARY_POISON;
241
22.1M
#endif
242
22.1M
243
22.1M
    STRING_STAT_INCREMENT(Free);
244
22.1M
    free(this); // we were allocated with |malloc|
245
22.1M
  }
246
42.0M
}
247
248
/**
249
 * Alloc returns a pointer to a new string header with set capacity.
250
 */
251
already_AddRefed<nsStringBuffer>
252
nsStringBuffer::Alloc(size_t aSize)
253
22.2M
{
254
22.2M
  NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
255
22.2M
  NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
256
22.2M
               sizeof(nsStringBuffer) + aSize > aSize,
257
22.2M
               "mStorageSize will truncate");
258
22.2M
259
22.2M
  nsStringBuffer* hdr =
260
22.2M
    (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
261
22.2M
  if (hdr) {
262
22.2M
    STRING_STAT_INCREMENT(Alloc);
263
22.2M
264
22.2M
    hdr->mRefCount = 1;
265
22.2M
    hdr->mStorageSize = aSize;
266
22.2M
#ifdef STRING_BUFFER_CANARY
267
22.2M
    hdr->mCanary = CANARY_OK;
268
22.2M
#endif
269
22.2M
    NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
270
22.2M
  }
271
22.2M
  return dont_AddRef(hdr);
272
22.2M
}
273
274
nsStringBuffer*
275
nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize)
276
0
{
277
0
  STRING_STAT_INCREMENT(Realloc);
278
0
279
0
  CHECK_STRING_BUFFER_CANARY(aHdr->mCanary);
280
0
  NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
281
0
  NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
282
0
               sizeof(nsStringBuffer) + aSize > aSize,
283
0
               "mStorageSize will truncate");
284
0
285
0
  // no point in trying to save ourselves if we hit this assertion
286
0
  NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
287
0
288
0
  // Treat this as a release and addref for refcounting purposes, since we
289
0
  // just asserted that the refcount is 1.  If we don't do that, refcount
290
0
  // logging will claim we've leaked all sorts of stuff.
291
0
  NS_LOG_RELEASE(aHdr, 0, "nsStringBuffer");
292
0
293
0
  aHdr = (nsStringBuffer*)realloc(aHdr, sizeof(nsStringBuffer) + aSize);
294
0
  if (aHdr) {
295
0
    NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
296
0
    aHdr->mStorageSize = aSize;
297
0
  }
298
0
299
0
  return aHdr;
300
0
}
301
302
nsStringBuffer*
303
nsStringBuffer::FromString(const nsAString& aStr)
304
6
{
305
6
  const nsAStringAccessor* accessor =
306
6
    static_cast<const nsAStringAccessor*>(&aStr);
307
6
308
6
  if (!(accessor->flags() & nsAString::DataFlags::REFCOUNTED)) {
309
0
    return nullptr;
310
0
  }
311
6
312
6
  return FromData(accessor->data());
313
6
}
314
315
nsStringBuffer*
316
nsStringBuffer::FromString(const nsACString& aStr)
317
0
{
318
0
  const nsACStringAccessor* accessor =
319
0
    static_cast<const nsACStringAccessor*>(&aStr);
320
0
321
0
  if (!(accessor->flags() & nsACString::DataFlags::REFCOUNTED)) {
322
0
    return nullptr;
323
0
  }
324
0
325
0
  return FromData(accessor->data());
326
0
}
327
328
void
329
nsStringBuffer::ToString(uint32_t aLen, nsAString& aStr,
330
                         bool aMoveOwnership)
331
0
{
332
0
  char16_t* data = static_cast<char16_t*>(Data());
333
0
334
0
  nsAStringAccessor* accessor = static_cast<nsAStringAccessor*>(&aStr);
335
0
  MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char16_t(0),
336
0
                        "data should be null terminated");
337
0
338
0
  nsAString::DataFlags flags =
339
0
    nsAString::DataFlags::REFCOUNTED | nsAString::DataFlags::TERMINATED;
340
0
341
0
  if (!aMoveOwnership) {
342
0
    AddRef();
343
0
  }
344
0
  accessor->set(data, aLen, flags);
345
0
}
346
347
void
348
nsStringBuffer::ToString(uint32_t aLen, nsACString& aStr,
349
                         bool aMoveOwnership)
350
0
{
351
0
  char* data = static_cast<char*>(Data());
352
0
353
0
  nsACStringAccessor* accessor = static_cast<nsACStringAccessor*>(&aStr);
354
0
  MOZ_DIAGNOSTIC_ASSERT(data[aLen] == char(0),
355
0
                        "data should be null terminated");
356
0
357
0
  nsACString::DataFlags flags =
358
0
    nsACString::DataFlags::REFCOUNTED | nsACString::DataFlags::TERMINATED;
359
0
360
0
  if (!aMoveOwnership) {
361
0
    AddRef();
362
0
  }
363
0
  accessor->set(data, aLen, flags);
364
0
}
365
366
size_t
367
nsStringBuffer::SizeOfIncludingThisIfUnshared(mozilla::MallocSizeOf aMallocSizeOf) const
368
0
{
369
0
  return IsReadonly() ? 0 : aMallocSizeOf(this);
370
0
}
371
372
size_t
373
nsStringBuffer::SizeOfIncludingThisEvenIfShared(mozilla::MallocSizeOf aMallocSizeOf) const
374
0
{
375
0
  return aMallocSizeOf(this);
376
0
}
377
378
#ifdef STRING_BUFFER_CANARY
379
void
380
nsStringBuffer::FromDataCanaryCheckFailed() const
381
0
{
382
0
  MOZ_CRASH_UNSAFE_PRINTF("Bad canary value 0x%x in FromData", mCanary);
383
0
}
384
#endif
385
386
// ---------------------------------------------------------------------------
387
388
// define nsAString
389
#include "nsTSubstring.cpp"
390
391
// Provide rust bindings to the nsA[C]String types
392
extern "C" {
393
394
// This is a no-op on release, so we ifdef it out such that using it in release
395
// results in a linker error.
396
#ifdef DEBUG
397
void Gecko_IncrementStringAdoptCount(void* aData)
398
{
399
  MOZ_LOG_CTOR(aData, "StringAdopt", 1);
400
}
401
#elif defined(MOZ_DEBUG_RUST)
402
void Gecko_IncrementStringAdoptCount(void *aData)
403
{
404
}
405
#endif
406
407
void Gecko_FinalizeCString(nsACString* aThis)
408
4.86k
{
409
4.86k
  aThis->~nsACString();
410
4.86k
}
411
412
void Gecko_AssignCString(nsACString* aThis, const nsACString* aOther)
413
0
{
414
0
  aThis->Assign(*aOther);
415
0
}
416
417
void Gecko_TakeFromCString(nsACString* aThis, nsACString* aOther)
418
0
{
419
0
  aThis->Assign(std::move(*aOther));
420
0
}
421
422
void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther)
423
4.86k
{
424
4.86k
  aThis->Append(*aOther);
425
4.86k
}
426
427
void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength)
428
3.38k
{
429
3.38k
  aThis->SetLength(aLength);
430
3.38k
}
431
432
bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther)
433
0
{
434
0
  return aThis->Assign(*aOther, mozilla::fallible);
435
0
}
436
437
bool Gecko_FallibleTakeFromCString(nsACString* aThis, nsACString* aOther)
438
0
{
439
0
  return aThis->Assign(std::move(*aOther), mozilla::fallible);
440
0
}
441
442
bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther)
443
0
{
444
0
  return aThis->Append(*aOther, mozilla::fallible);
445
0
}
446
447
bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength)
448
0
{
449
0
  return aThis->SetLength(aLength, mozilla::fallible);
450
0
}
451
452
char* Gecko_BeginWritingCString(nsACString* aThis)
453
0
{
454
0
  return aThis->BeginWriting();
455
0
}
456
457
char* Gecko_FallibleBeginWritingCString(nsACString* aThis)
458
0
{
459
0
  return aThis->BeginWriting(mozilla::fallible);
460
0
}
461
462
uint32_t
463
Gecko_StartBulkWriteCString(nsACString* aThis,
464
                            uint32_t aCapacity,
465
                            uint32_t aUnitsToPreserve,
466
                            bool aAllowShrinking)
467
3.10M
{
468
3.10M
  return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking).unwrapOr(UINT32_MAX);
469
3.10M
}
470
471
void Gecko_FinalizeString(nsAString* aThis)
472
0
{
473
0
  aThis->~nsAString();
474
0
}
475
476
void Gecko_AssignString(nsAString* aThis, const nsAString* aOther)
477
0
{
478
0
  aThis->Assign(*aOther);
479
0
}
480
481
void Gecko_TakeFromString(nsAString* aThis, nsAString* aOther)
482
0
{
483
0
  aThis->Assign(std::move(*aOther));
484
0
}
485
486
void Gecko_AppendString(nsAString* aThis, const nsAString* aOther)
487
0
{
488
0
  aThis->Append(*aOther);
489
0
}
490
491
void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength)
492
1
{
493
1
  aThis->SetLength(aLength);
494
1
}
495
496
bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther)
497
0
{
498
0
  return aThis->Assign(*aOther, mozilla::fallible);
499
0
}
500
501
bool Gecko_FallibleTakeFromString(nsAString* aThis, nsAString* aOther)
502
0
{
503
0
  return aThis->Assign(std::move(*aOther), mozilla::fallible);
504
0
}
505
506
bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther)
507
0
{
508
0
  return aThis->Append(*aOther, mozilla::fallible);
509
0
}
510
511
bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength)
512
0
{
513
0
  return aThis->SetLength(aLength, mozilla::fallible);
514
0
}
515
516
char16_t* Gecko_BeginWritingString(nsAString* aThis)
517
0
{
518
0
  return aThis->BeginWriting();
519
0
}
520
521
char16_t* Gecko_FallibleBeginWritingString(nsAString* aThis)
522
0
{
523
0
  return aThis->BeginWriting(mozilla::fallible);
524
0
}
525
526
uint32_t
527
Gecko_StartBulkWriteString(nsAString* aThis,
528
                           uint32_t aCapacity,
529
                           uint32_t aUnitsToPreserve,
530
                           bool aAllowShrinking)
531
78.4k
{
532
78.4k
  return aThis->StartBulkWriteImpl(aCapacity, aUnitsToPreserve, aAllowShrinking).unwrapOr(UINT32_MAX);
533
78.4k
}
534
535
} // extern "C"