Coverage Report

Created: 2025-12-10 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/llvm-project/libcxxabi/src/demangle/Utility.h
Line
Count
Source
1
//===--- Utility.h ----------------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// Provide some utility classes for use in the demangler.
10
// There are two copies of this file in the source tree.  The one in libcxxabi
11
// is the original and the one in llvm is the copy.  Use cp-to-llvm.sh to update
12
// the copy.  See README.txt for more details.
13
//
14
//===----------------------------------------------------------------------===//
15
16
#ifndef DEMANGLE_UTILITY_H
17
#define DEMANGLE_UTILITY_H
18
19
#include "DemangleConfig.h"
20
21
#include <array>
22
#include <cstdint>
23
#include <cstdlib>
24
#include <cstring>
25
#include <limits>
26
#include <string_view>
27
28
DEMANGLE_NAMESPACE_BEGIN
29
30
class Node;
31
32
// Stream that AST nodes write their string representation into after the AST
33
// has been parsed.
34
class OutputBuffer {
35
  char *Buffer = nullptr;
36
  size_t CurrentPosition = 0;
37
  size_t BufferCapacity = 0;
38
39
  // Ensure there are at least N more positions in the buffer.
40
0
  void grow(size_t N) {
41
0
    size_t Need = N + CurrentPosition;
42
0
    if (Need > BufferCapacity) {
43
      // Reduce the number of reallocations, with a bit of hysteresis. The
44
      // number here is chosen so the first allocation will more-than-likely not
45
      // allocate more than 1K.
46
0
      Need += 1024 - 32;
47
0
      BufferCapacity *= 2;
48
0
      if (BufferCapacity < Need)
49
0
        BufferCapacity = Need;
50
0
      Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
51
0
      if (Buffer == nullptr)
52
0
        std::abort();
53
0
    }
54
0
  }
55
56
0
  OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) {
57
0
    std::array<char, 21> Temp;
58
0
    char *TempPtr = Temp.data() + Temp.size();
59
60
    // Output at least one character.
61
0
    do {
62
0
      *--TempPtr = char('0' + N % 10);
63
0
      N /= 10;
64
0
    } while (N);
65
66
    // Add negative sign.
67
0
    if (isNeg)
68
0
      *--TempPtr = '-';
69
70
0
    return operator+=(
71
0
        std::string_view(TempPtr, Temp.data() + Temp.size() - TempPtr));
72
0
  }
73
74
public:
75
  OutputBuffer(char *StartBuf, size_t Size)
76
0
      : Buffer(StartBuf), BufferCapacity(Size) {}
77
  OutputBuffer(char *StartBuf, size_t *SizePtr)
78
0
      : OutputBuffer(StartBuf, StartBuf ? *SizePtr : 0) {}
79
  OutputBuffer() = default;
80
  // Non-copyable
81
  OutputBuffer(const OutputBuffer &) = delete;
82
  OutputBuffer &operator=(const OutputBuffer &) = delete;
83
84
0
  virtual ~OutputBuffer() = default;
85
86
0
  operator std::string_view() const {
87
0
    return std::string_view(Buffer, CurrentPosition);
88
0
  }
89
90
  /// Called by the demangler when printing the demangle tree. By
91
  /// default calls into \c Node::print{Left|Right} but can be overriden
92
  /// by clients to track additional state when printing the demangled name.
93
  virtual void printLeft(const Node &N);
94
  virtual void printRight(const Node &N);
95
96
  /// Called when we write to this object anywhere other than the end.
97
0
  virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {}
98
99
  /// Called when we make the \c CurrentPosition of this object smaller.
100
0
  virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {}
101
102
  /// If a ParameterPackExpansion (or similar type) is encountered, the offset
103
  /// into the pack that we're currently printing.
104
  unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
105
  unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
106
107
  struct {
108
    /// The depth of '(' and ')' inside the currently printed template
109
    /// arguments.
110
    unsigned ParenDepth = 0;
111
112
    /// True if we're currently printing a template argument.
113
    bool InsideTemplate = false;
114
  } TemplateTracker;
115
116
  /// Returns true if we're currently between a '(' and ')' when printing
117
  /// template args.
118
0
  bool isInParensInTemplateArgs() const {
119
0
    return TemplateTracker.ParenDepth > 0;
120
0
  }
121
122
  /// Returns true if we're printing template args.
123
0
  bool isInsideTemplateArgs() const { return TemplateTracker.InsideTemplate; }
124
125
0
  void printOpen(char Open = '(') {
126
0
    if (isInsideTemplateArgs())
127
0
      TemplateTracker.ParenDepth++;
128
0
    *this += Open;
129
0
  }
130
0
  void printClose(char Close = ')') {
131
0
    if (isInsideTemplateArgs())
132
0
      TemplateTracker.ParenDepth--;
133
0
    *this += Close;
134
0
  }
135
136
0
  OutputBuffer &operator+=(std::string_view R) {
137
0
    if (size_t Size = R.size()) {
138
0
      grow(Size);
139
0
      std::memcpy(Buffer + CurrentPosition, &*R.begin(), Size);
140
0
      CurrentPosition += Size;
141
0
    }
142
0
    return *this;
143
0
  }
144
145
0
  OutputBuffer &operator+=(char C) {
146
0
    grow(1);
147
0
    Buffer[CurrentPosition++] = C;
148
0
    return *this;
149
0
  }
150
151
0
  OutputBuffer &prepend(std::string_view R) {
152
0
    size_t Size = R.size();
153
0
    if (!Size)
154
0
      return *this;
155
0
156
0
    grow(Size);
157
0
    std::memmove(Buffer + Size, Buffer, CurrentPosition);
158
0
    std::memcpy(Buffer, &*R.begin(), Size);
159
0
    CurrentPosition += Size;
160
0
161
0
    notifyInsertion(/*Position=*/0, /*Count=*/Size);
162
0
163
0
    return *this;
164
0
  }
165
166
0
  OutputBuffer &operator<<(std::string_view R) { return (*this += R); }
167
168
0
  OutputBuffer &operator<<(char C) { return (*this += C); }
169
170
0
  OutputBuffer &operator<<(long long N) {
171
0
    return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0);
172
0
  }
173
174
0
  OutputBuffer &operator<<(unsigned long long N) {
175
0
    return writeUnsigned(N, false);
176
0
  }
177
178
0
  OutputBuffer &operator<<(long N) {
179
0
    return this->operator<<(static_cast<long long>(N));
180
0
  }
181
182
0
  OutputBuffer &operator<<(unsigned long N) {
183
0
    return this->operator<<(static_cast<unsigned long long>(N));
184
0
  }
185
186
0
  OutputBuffer &operator<<(int N) {
187
0
    return this->operator<<(static_cast<long long>(N));
188
0
  }
189
190
0
  OutputBuffer &operator<<(unsigned int N) {
191
0
    return this->operator<<(static_cast<unsigned long long>(N));
192
0
  }
193
194
0
  void insert(size_t Pos, const char *S, size_t N) {
195
0
    DEMANGLE_ASSERT(Pos <= CurrentPosition, "");
196
0
    if (N == 0)
197
0
      return;
198
0
199
0
    grow(N);
200
0
    std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
201
0
    std::memcpy(Buffer + Pos, S, N);
202
0
    CurrentPosition += N;
203
0
204
0
    notifyInsertion(Pos, N);
205
0
  }
206
207
0
  size_t getCurrentPosition() const { return CurrentPosition; }
208
0
  void setCurrentPosition(size_t NewPos) {
209
0
    notifyDeletion(CurrentPosition, NewPos);
210
0
    CurrentPosition = NewPos;
211
0
  }
212
213
0
  char back() const {
214
0
    DEMANGLE_ASSERT(CurrentPosition, "");
215
0
    return Buffer[CurrentPosition - 1];
216
0
  }
217
218
0
  bool empty() const { return CurrentPosition == 0; }
219
220
0
  char *getBuffer() { return Buffer; }
221
0
  char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
222
0
  size_t getBufferCapacity() const { return BufferCapacity; }
223
};
224
225
template <class T> class ScopedOverride {
226
  T &Loc;
227
  T Original;
228
229
public:
230
  ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}
231
232
3.62k
  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
233
3.62k
    Loc_ = std::move(NewVal);
234
3.62k
  }
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<bool>::ScopedOverride(bool&, bool)
Line
Count
Source
232
3.46k
  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
233
3.46k
    Loc_ = std::move(NewVal);
234
3.46k
  }
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<unsigned long>::ScopedOverride(unsigned long&, unsigned long)
Line
Count
Source
232
50
  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
233
50
    Loc_ = std::move(NewVal);
234
50
  }
Unexecuted instantiation: cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<unsigned int>::ScopedOverride(unsigned int&, unsigned int)
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<char const*>::ScopedOverride(char const*&, char const*)
Line
Count
Source
232
106
  ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
233
106
    Loc_ = std::move(NewVal);
234
106
  }
235
3.62k
  ~ScopedOverride() { Loc = std::move(Original); }
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<bool>::~ScopedOverride()
Line
Count
Source
235
3.46k
  ~ScopedOverride() { Loc = std::move(Original); }
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<unsigned long>::~ScopedOverride()
Line
Count
Source
235
50
  ~ScopedOverride() { Loc = std::move(Original); }
Unexecuted instantiation: cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<unsigned int>::~ScopedOverride()
cxa_demangle.cpp:(anonymous namespace)::itanium_demangle::ScopedOverride<char const*>::~ScopedOverride()
Line
Count
Source
235
106
  ~ScopedOverride() { Loc = std::move(Original); }
236
237
  ScopedOverride(const ScopedOverride &) = delete;
238
  ScopedOverride &operator=(const ScopedOverride &) = delete;
239
};
240
241
DEMANGLE_NAMESPACE_END
242
243
#endif