GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
// Copyright Joyent, Inc. and other Node contributors. |
||
2 |
// |
||
3 |
// Permission is hereby granted, free of charge, to any person obtaining a |
||
4 |
// copy of this software and associated documentation files (the |
||
5 |
// "Software"), to deal in the Software without restriction, including |
||
6 |
// without limitation the rights to use, copy, modify, merge, publish, |
||
7 |
// distribute, sublicense, and/or sell copies of the Software, and to permit |
||
8 |
// persons to whom the Software is furnished to do so, subject to the |
||
9 |
// following conditions: |
||
10 |
// |
||
11 |
// The above copyright notice and this permission notice shall be included |
||
12 |
// in all copies or substantial portions of the Software. |
||
13 |
// |
||
14 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
15 |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
16 |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
||
17 |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
||
18 |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||
19 |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
||
20 |
// USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
21 |
|||
22 |
#ifndef SRC_UTIL_H_ |
||
23 |
#define SRC_UTIL_H_ |
||
24 |
|||
25 |
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
26 |
|||
27 |
#include "v8.h" |
||
28 |
|||
29 |
#include "node.h" |
||
30 |
#include "node_exit_code.h" |
||
31 |
|||
32 |
#include <climits> |
||
33 |
#include <cstddef> |
||
34 |
#include <cstdio> |
||
35 |
#include <cstdlib> |
||
36 |
#include <cstring> |
||
37 |
|||
38 |
#include <array> |
||
39 |
#include <limits> |
||
40 |
#include <memory> |
||
41 |
#include <string> |
||
42 |
#include <string_view> |
||
43 |
#include <type_traits> |
||
44 |
#include <set> |
||
45 |
#include <unordered_map> |
||
46 |
#include <utility> |
||
47 |
#include <vector> |
||
48 |
|||
49 |
#ifdef __GNUC__ |
||
50 |
#define MUST_USE_RESULT __attribute__((warn_unused_result)) |
||
51 |
#else |
||
52 |
#define MUST_USE_RESULT |
||
53 |
#endif |
||
54 |
|||
55 |
namespace node { |
||
56 |
|||
57 |
// Maybe remove kPathSeparator when cpp17 is ready |
||
58 |
#ifdef _WIN32 |
||
59 |
constexpr char kPathSeparator = '\\'; |
||
60 |
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ |
||
61 |
#define PATH_MAX_BYTES (MAX_PATH * 4) |
||
62 |
#else |
||
63 |
constexpr char kPathSeparator = '/'; |
||
64 |
#define PATH_MAX_BYTES (PATH_MAX) |
||
65 |
#endif |
||
66 |
|||
67 |
// These should be used in our code as opposed to the native |
||
68 |
// versions as they abstract out some platform and or |
||
69 |
// compiler version specific functionality |
||
70 |
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in |
||
71 |
// that the standard allows them to either return a unique pointer or a |
||
72 |
// nullptr for zero-sized allocation requests. Normalize by always using |
||
73 |
// a nullptr. |
||
74 |
template <typename T> |
||
75 |
inline T* UncheckedRealloc(T* pointer, size_t n); |
||
76 |
template <typename T> |
||
77 |
inline T* UncheckedMalloc(size_t n); |
||
78 |
template <typename T> |
||
79 |
inline T* UncheckedCalloc(size_t n); |
||
80 |
|||
81 |
// Same things, but aborts immediately instead of returning nullptr when |
||
82 |
// no memory is available. |
||
83 |
template <typename T> |
||
84 |
inline T* Realloc(T* pointer, size_t n); |
||
85 |
template <typename T> |
||
86 |
inline T* Malloc(size_t n); |
||
87 |
template <typename T> |
||
88 |
inline T* Calloc(size_t n); |
||
89 |
|||
90 |
inline char* Malloc(size_t n); |
||
91 |
inline char* Calloc(size_t n); |
||
92 |
inline char* UncheckedMalloc(size_t n); |
||
93 |
inline char* UncheckedCalloc(size_t n); |
||
94 |
|||
95 |
template <typename T> |
||
96 |
inline T MultiplyWithOverflowCheck(T a, T b); |
||
97 |
|||
98 |
namespace per_process { |
||
99 |
// Tells whether the per-process V8::Initialize() is called and |
||
100 |
// if it is safe to call v8::Isolate::TryGetCurrent(). |
||
101 |
extern bool v8_initialized; |
||
102 |
} // namespace per_process |
||
103 |
|||
104 |
// Used by the allocation functions when allocation fails. |
||
105 |
// Thin wrapper around v8::Isolate::LowMemoryNotification() that checks |
||
106 |
// whether V8 is initialized. |
||
107 |
void LowMemoryNotification(); |
||
108 |
|||
109 |
// The reason that Assert() takes a struct argument instead of individual |
||
110 |
// const char*s is to ease instruction cache pressure in calls from CHECK. |
||
111 |
struct AssertionInfo { |
||
112 |
const char* file_line; // filename:line |
||
113 |
const char* message; |
||
114 |
const char* function; |
||
115 |
}; |
||
116 |
[[noreturn]] void NODE_EXTERN_PRIVATE Assert(const AssertionInfo& info); |
||
117 |
[[noreturn]] void NODE_EXTERN_PRIVATE Abort(); |
||
118 |
void DumpBacktrace(FILE* fp); |
||
119 |
|||
120 |
// Windows 8+ does not like abort() in Release mode |
||
121 |
#ifdef _WIN32 |
||
122 |
#define ABORT_NO_BACKTRACE() _exit(static_cast<int>(node::ExitCode::kAbort)) |
||
123 |
#else |
||
124 |
#define ABORT_NO_BACKTRACE() abort() |
||
125 |
#endif |
||
126 |
|||
127 |
#define ABORT() node::Abort() |
||
128 |
|||
129 |
#define ERROR_AND_ABORT(expr) \ |
||
130 |
do { \ |
||
131 |
/* Make sure that this struct does not end up in inline code, but */ \ |
||
132 |
/* rather in a read-only data section when modifying this code. */ \ |
||
133 |
static const node::AssertionInfo args = { \ |
||
134 |
__FILE__ ":" STRINGIFY(__LINE__), #expr, PRETTY_FUNCTION_NAME \ |
||
135 |
}; \ |
||
136 |
node::Assert(args); \ |
||
137 |
} while (0) |
||
138 |
|||
139 |
#ifdef __GNUC__ |
||
140 |
#define LIKELY(expr) __builtin_expect(!!(expr), 1) |
||
141 |
#define UNLIKELY(expr) __builtin_expect(!!(expr), 0) |
||
142 |
#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__ |
||
143 |
#else |
||
144 |
#define LIKELY(expr) expr |
||
145 |
#define UNLIKELY(expr) expr |
||
146 |
#define PRETTY_FUNCTION_NAME "" |
||
147 |
#endif |
||
148 |
|||
149 |
#define STRINGIFY_(x) #x |
||
150 |
#define STRINGIFY(x) STRINGIFY_(x) |
||
151 |
|||
152 |
#define CHECK(expr) \ |
||
153 |
do { \ |
||
154 |
if (UNLIKELY(!(expr))) { \ |
||
155 |
ERROR_AND_ABORT(expr); \ |
||
156 |
} \ |
||
157 |
} while (0) |
||
158 |
|||
159 |
#define CHECK_EQ(a, b) CHECK((a) == (b)) |
||
160 |
#define CHECK_GE(a, b) CHECK((a) >= (b)) |
||
161 |
#define CHECK_GT(a, b) CHECK((a) > (b)) |
||
162 |
#define CHECK_LE(a, b) CHECK((a) <= (b)) |
||
163 |
#define CHECK_LT(a, b) CHECK((a) < (b)) |
||
164 |
#define CHECK_NE(a, b) CHECK((a) != (b)) |
||
165 |
#define CHECK_NULL(val) CHECK((val) == nullptr) |
||
166 |
#define CHECK_NOT_NULL(val) CHECK((val) != nullptr) |
||
167 |
#define CHECK_IMPLIES(a, b) CHECK(!(a) || (b)) |
||
168 |
|||
169 |
#ifdef DEBUG |
||
170 |
#define DCHECK(expr) CHECK(expr) |
||
171 |
#define DCHECK_EQ(a, b) CHECK((a) == (b)) |
||
172 |
#define DCHECK_GE(a, b) CHECK((a) >= (b)) |
||
173 |
#define DCHECK_GT(a, b) CHECK((a) > (b)) |
||
174 |
#define DCHECK_LE(a, b) CHECK((a) <= (b)) |
||
175 |
#define DCHECK_LT(a, b) CHECK((a) < (b)) |
||
176 |
#define DCHECK_NE(a, b) CHECK((a) != (b)) |
||
177 |
#define DCHECK_NULL(val) CHECK((val) == nullptr) |
||
178 |
#define DCHECK_NOT_NULL(val) CHECK((val) != nullptr) |
||
179 |
#define DCHECK_IMPLIES(a, b) CHECK(!(a) || (b)) |
||
180 |
#else |
||
181 |
#define DCHECK(expr) |
||
182 |
#define DCHECK_EQ(a, b) |
||
183 |
#define DCHECK_GE(a, b) |
||
184 |
#define DCHECK_GT(a, b) |
||
185 |
#define DCHECK_LE(a, b) |
||
186 |
#define DCHECK_LT(a, b) |
||
187 |
#define DCHECK_NE(a, b) |
||
188 |
#define DCHECK_NULL(val) |
||
189 |
#define DCHECK_NOT_NULL(val) |
||
190 |
#define DCHECK_IMPLIES(a, b) |
||
191 |
#endif |
||
192 |
|||
193 |
|||
194 |
#define UNREACHABLE(...) \ |
||
195 |
ERROR_AND_ABORT("Unreachable code reached" __VA_OPT__(": ") __VA_ARGS__) |
||
196 |
|||
197 |
// ECMA262 20.1.2.6 Number.MAX_SAFE_INTEGER (2^53-1) |
||
198 |
constexpr int64_t kMaxSafeJsInteger = 9007199254740991; |
||
199 |
|||
200 |
inline bool IsSafeJsInt(v8::Local<v8::Value> v); |
||
201 |
|||
202 |
// TAILQ-style intrusive list node. |
||
203 |
template <typename T> |
||
204 |
class ListNode; |
||
205 |
|||
206 |
// TAILQ-style intrusive list head. |
||
207 |
template <typename T, ListNode<T> (T::*M)> |
||
208 |
class ListHead; |
||
209 |
|||
210 |
template <typename T> |
||
211 |
class ListNode { |
||
212 |
public: |
||
213 |
inline ListNode(); |
||
214 |
inline ~ListNode(); |
||
215 |
inline void Remove(); |
||
216 |
inline bool IsEmpty() const; |
||
217 |
|||
218 |
ListNode(const ListNode&) = delete; |
||
219 |
ListNode& operator=(const ListNode&) = delete; |
||
220 |
|||
221 |
private: |
||
222 |
template <typename U, ListNode<U> (U::*M)> friend class ListHead; |
||
223 |
friend int GenDebugSymbols(); |
||
224 |
ListNode* prev_; |
||
225 |
ListNode* next_; |
||
226 |
}; |
||
227 |
|||
228 |
template <typename T, ListNode<T> (T::*M)> |
||
229 |
class ListHead { |
||
230 |
public: |
||
231 |
class Iterator { |
||
232 |
public: |
||
233 |
inline T* operator*() const; |
||
234 |
inline const Iterator& operator++(); |
||
235 |
inline bool operator!=(const Iterator& that) const; |
||
236 |
|||
237 |
private: |
||
238 |
friend class ListHead; |
||
239 |
inline explicit Iterator(ListNode<T>* node); |
||
240 |
ListNode<T>* node_; |
||
241 |
}; |
||
242 |
|||
243 |
12661 |
inline ListHead() = default; |
|
244 |
inline ~ListHead(); |
||
245 |
inline void PushBack(T* element); |
||
246 |
inline void PushFront(T* element); |
||
247 |
inline bool IsEmpty() const; |
||
248 |
inline T* PopFront(); |
||
249 |
inline Iterator begin() const; |
||
250 |
inline Iterator end() const; |
||
251 |
|||
252 |
ListHead(const ListHead&) = delete; |
||
253 |
ListHead& operator=(const ListHead&) = delete; |
||
254 |
|||
255 |
private: |
||
256 |
friend int GenDebugSymbols(); |
||
257 |
ListNode<T> head_; |
||
258 |
}; |
||
259 |
|||
260 |
// The helper is for doing safe downcasts from base types to derived types. |
||
261 |
template <typename Inner, typename Outer> |
||
262 |
class ContainerOfHelper { |
||
263 |
public: |
||
264 |
inline ContainerOfHelper(Inner Outer::*field, Inner* pointer); |
||
265 |
template <typename TypeName> |
||
266 |
inline operator TypeName*() const; |
||
267 |
private: |
||
268 |
Outer* const pointer_; |
||
269 |
}; |
||
270 |
|||
271 |
// Calculate the address of the outer (i.e. embedding) struct from |
||
272 |
// the interior pointer to a data member. |
||
273 |
template <typename Inner, typename Outer> |
||
274 |
constexpr ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field, |
||
275 |
Inner* pointer); |
||
276 |
|||
277 |
class KVStore { |
||
278 |
public: |
||
279 |
6591 |
KVStore() = default; |
|
280 |
5918 |
virtual ~KVStore() = default; |
|
281 |
KVStore(const KVStore&) = delete; |
||
282 |
KVStore& operator=(const KVStore&) = delete; |
||
283 |
KVStore(KVStore&&) = delete; |
||
284 |
KVStore& operator=(KVStore&&) = delete; |
||
285 |
|||
286 |
virtual v8::MaybeLocal<v8::String> Get(v8::Isolate* isolate, |
||
287 |
v8::Local<v8::String> key) const = 0; |
||
288 |
virtual v8::Maybe<std::string> Get(const char* key) const = 0; |
||
289 |
virtual void Set(v8::Isolate* isolate, |
||
290 |
v8::Local<v8::String> key, |
||
291 |
v8::Local<v8::String> value) = 0; |
||
292 |
virtual int32_t Query(v8::Isolate* isolate, |
||
293 |
v8::Local<v8::String> key) const = 0; |
||
294 |
virtual int32_t Query(const char* key) const = 0; |
||
295 |
virtual void Delete(v8::Isolate* isolate, v8::Local<v8::String> key) = 0; |
||
296 |
virtual v8::Local<v8::Array> Enumerate(v8::Isolate* isolate) const = 0; |
||
297 |
|||
298 |
virtual std::shared_ptr<KVStore> Clone(v8::Isolate* isolate) const; |
||
299 |
virtual v8::Maybe<bool> AssignFromObject(v8::Local<v8::Context> context, |
||
300 |
v8::Local<v8::Object> entries); |
||
301 |
|||
302 |
static std::shared_ptr<KVStore> CreateMapKVStore(); |
||
303 |
}; |
||
304 |
|||
305 |
// Convenience wrapper around v8::String::NewFromOneByte(). |
||
306 |
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, |
||
307 |
const char* data, |
||
308 |
int length = -1); |
||
309 |
|||
310 |
// For the people that compile with -funsigned-char. |
||
311 |
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, |
||
312 |
const signed char* data, |
||
313 |
int length = -1); |
||
314 |
|||
315 |
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, |
||
316 |
const unsigned char* data, |
||
317 |
int length = -1); |
||
318 |
|||
319 |
// Used to be a macro, hence the uppercase name. |
||
320 |
template <int N> |
||
321 |
3257603 |
inline v8::Local<v8::String> FIXED_ONE_BYTE_STRING( |
|
322 |
v8::Isolate* isolate, |
||
323 |
const char(&data)[N]) { |
||
324 |
3257603 |
return OneByteString(isolate, data, N - 1); |
|
325 |
} |
||
326 |
|||
327 |
template <std::size_t N> |
||
328 |
684 |
inline v8::Local<v8::String> FIXED_ONE_BYTE_STRING( |
|
329 |
v8::Isolate* isolate, |
||
330 |
const std::array<char, N>& arr) { |
||
331 |
684 |
return OneByteString(isolate, arr.data(), N - 1); |
|
332 |
} |
||
333 |
|||
334 |
|||
335 |
|||
336 |
// Swaps bytes in place. nbytes is the number of bytes to swap and must be a |
||
337 |
// multiple of the word size (checked by function). |
||
338 |
inline void SwapBytes16(char* data, size_t nbytes); |
||
339 |
inline void SwapBytes32(char* data, size_t nbytes); |
||
340 |
inline void SwapBytes64(char* data, size_t nbytes); |
||
341 |
|||
342 |
// tolower() is locale-sensitive. Use ToLower() instead. |
||
343 |
inline char ToLower(char c); |
||
344 |
inline std::string ToLower(const std::string& in); |
||
345 |
|||
346 |
// toupper() is locale-sensitive. Use ToUpper() instead. |
||
347 |
inline char ToUpper(char c); |
||
348 |
inline std::string ToUpper(const std::string& in); |
||
349 |
|||
350 |
// strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead. |
||
351 |
inline bool StringEqualNoCase(const char* a, const char* b); |
||
352 |
|||
353 |
// strncasecmp() is locale-sensitive. Use StringEqualNoCaseN() instead. |
||
354 |
inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length); |
||
355 |
|||
356 |
template <typename T, size_t N> |
||
357 |
8751454 |
constexpr size_t arraysize(const T (&)[N]) { |
|
358 |
8751454 |
return N; |
|
359 |
} |
||
360 |
|||
361 |
template <typename T, size_t N> |
||
362 |
constexpr size_t strsize(const T (&)[N]) { |
||
363 |
return N - 1; |
||
364 |
} |
||
365 |
|||
366 |
// Allocates an array of member type T. For up to kStackStorageSize items, |
||
367 |
// the stack is used, otherwise malloc(). |
||
368 |
template <typename T, size_t kStackStorageSize = 1024> |
||
369 |
class MaybeStackBuffer { |
||
370 |
public: |
||
371 |
6046 |
const T* out() const { |
|
372 |
6046 |
return buf_; |
|
373 |
} |
||
374 |
|||
375 |
5199160 |
T* out() { |
|
376 |
5199160 |
return buf_; |
|
377 |
} |
||
378 |
|||
379 |
// operator* for compatibility with `v8::String::(Utf8)Value` |
||
380 |
6692050 |
T* operator*() { |
|
381 |
6692050 |
return buf_; |
|
382 |
} |
||
383 |
|||
384 |
30424 |
const T* operator*() const { |
|
385 |
30424 |
return buf_; |
|
386 |
} |
||
387 |
|||
388 |
2899554 |
T& operator[](size_t index) { |
|
389 |
✗✓✗✓ ✗✗ |
2899554 |
CHECK_LT(index, length()); |
390 |
2899554 |
return buf_[index]; |
|
391 |
} |
||
392 |
|||
393 |
11187 |
const T& operator[](size_t index) const { |
|
394 |
✗✓ | 11187 |
CHECK_LT(index, length()); |
395 |
11187 |
return buf_[index]; |
|
396 |
} |
||
397 |
|||
398 |
6925539 |
size_t length() const { |
|
399 |
6925539 |
return length_; |
|
400 |
} |
||
401 |
|||
402 |
// Current maximum capacity of the buffer with which SetLength() can be used |
||
403 |
// without first calling AllocateSufficientStorage(). |
||
404 |
15155149 |
size_t capacity() const { |
|
405 |
15155149 |
return capacity_; |
|
406 |
} |
||
407 |
|||
408 |
// Make sure enough space for `storage` entries is available. |
||
409 |
// This method can be called multiple times throughout the lifetime of the |
||
410 |
// buffer, but once this has been called Invalidate() cannot be used. |
||
411 |
// Content of the buffer in the range [0, length()) is preserved. |
||
412 |
5336659 |
void AllocateSufficientStorage(size_t storage) { |
|
413 |
✗✓✗✓ ✗✓✗✗ ✗✓ |
5336659 |
CHECK(!IsInvalidated()); |
414 |
✓✓✓✓ ✓✓✗✗ ✓✓ |
5336659 |
if (storage > capacity()) { |
415 |
150989 |
bool was_allocated = IsAllocated(); |
|
416 |
✓✓✓✓ ✗✓✗✗ ✗✓ |
150989 |
T* allocated_ptr = was_allocated ? buf_ : nullptr; |
417 |
150989 |
buf_ = Realloc(allocated_ptr, storage); |
|
418 |
150989 |
capacity_ = storage; |
|
419 |
✓✓✓✓ ✓✓✓✓ ✓✗✗✓ ✗✗✗✗ ✓✗✗✓ |
150989 |
if (!was_allocated && length_ > 0) |
420 |
2 |
memcpy(buf_, buf_st_, length_ * sizeof(buf_[0])); |
|
421 |
} |
||
422 |
|||
423 |
5336659 |
length_ = storage; |
|
424 |
5336659 |
} |
|
425 |
|||
426 |
5101305 |
void SetLength(size_t length) { |
|
427 |
// capacity() returns how much memory is actually available. |
||
428 |
✗✓✗✓ |
5101305 |
CHECK_LE(length, capacity()); |
429 |
5101305 |
length_ = length; |
|
430 |
5101305 |
} |
|
431 |
|||
432 |
4702497 |
void SetLengthAndZeroTerminate(size_t length) { |
|
433 |
// capacity() returns how much memory is actually available. |
||
434 |
✗✓✗✓ |
4702497 |
CHECK_LE(length + 1, capacity()); |
435 |
4702497 |
SetLength(length); |
|
436 |
|||
437 |
// T() is 0 for integer types, nullptr for pointers, etc. |
||
438 |
4702497 |
buf_[length] = T(); |
|
439 |
4702497 |
} |
|
440 |
|||
441 |
// Make dereferencing this object return nullptr. |
||
442 |
// This method can be called multiple times throughout the lifetime of the |
||
443 |
// buffer, but once this has been called AllocateSufficientStorage() cannot |
||
444 |
// be used. |
||
445 |
2 |
void Invalidate() { |
|
446 |
✗✓ | 2 |
CHECK(!IsAllocated()); |
447 |
2 |
capacity_ = 0; |
|
448 |
2 |
length_ = 0; |
|
449 |
2 |
buf_ = nullptr; |
|
450 |
2 |
} |
|
451 |
|||
452 |
// If the buffer is stored in the heap rather than on the stack. |
||
453 |
6353179 |
bool IsAllocated() const { |
|
454 |
✓✓✓✓ ✓✗✓✓ ✓✗✓✓ ✓✗✓✓ ✗✗✗✗ |
6353179 |
return !IsInvalidated() && buf_ != buf_st_; |
455 |
} |
||
456 |
|||
457 |
// If Invalidate() has been called. |
||
458 |
11691710 |
bool IsInvalidated() const { |
|
459 |
11691710 |
return buf_ == nullptr; |
|
460 |
} |
||
461 |
|||
462 |
// Release ownership of the malloc'd buffer. |
||
463 |
// Note: This does not free the buffer. |
||
464 |
373 |
void Release() { |
|
465 |
✗✓✗✓ |
373 |
CHECK(IsAllocated()); |
466 |
373 |
buf_ = buf_st_; |
|
467 |
373 |
length_ = 0; |
|
468 |
373 |
capacity_ = arraysize(buf_st_); |
|
469 |
373 |
} |
|
470 |
|||
471 |
6197342 |
MaybeStackBuffer() |
|
472 |
✓✓✓✓ ✗✗ |
134185758 |
: length_(0), capacity_(arraysize(buf_st_)), buf_(buf_st_) { |
473 |
// Default to a zero-length, null-terminated buffer. |
||
474 |
6197342 |
buf_[0] = T(); |
|
475 |
6197342 |
} |
|
476 |
|||
477 |
485392 |
explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() { |
|
478 |
485392 |
AllocateSufficientStorage(storage); |
|
479 |
485392 |
} |
|
480 |
|||
481 |
6197319 |
~MaybeStackBuffer() { |
|
482 |
✓✓✓✓ ✓✓✓✓ ✗✗ |
6197319 |
if (IsAllocated()) |
483 |
150614 |
free(buf_); |
|
484 |
6197319 |
} |
|
485 |
|||
486 |
private: |
||
487 |
size_t length_; |
||
488 |
// capacity of the malloc'ed buf_ |
||
489 |
size_t capacity_; |
||
490 |
T* buf_; |
||
491 |
T buf_st_[kStackStorageSize]; |
||
492 |
}; |
||
493 |
|||
494 |
// Provides access to an ArrayBufferView's storage, either the original, |
||
495 |
// or for small data, a copy of it. This object's lifetime is bound to the |
||
496 |
// original ArrayBufferView's lifetime. |
||
497 |
template <typename T, size_t kStackStorageSize = 64> |
||
498 |
class ArrayBufferViewContents { |
||
499 |
public: |
||
500 |
622 |
ArrayBufferViewContents() = default; |
|
501 |
|||
502 |
ArrayBufferViewContents(const ArrayBufferViewContents&) = delete; |
||
503 |
void operator=(const ArrayBufferViewContents&) = delete; |
||
504 |
|||
505 |
explicit inline ArrayBufferViewContents(v8::Local<v8::Value> value); |
||
506 |
explicit inline ArrayBufferViewContents(v8::Local<v8::Object> value); |
||
507 |
explicit inline ArrayBufferViewContents(v8::Local<v8::ArrayBufferView> abv); |
||
508 |
inline void Read(v8::Local<v8::ArrayBufferView> abv); |
||
509 |
|||
510 |
188917 |
inline const T* data() const { return data_; } |
|
511 |
483095 |
inline size_t length() const { return length_; } |
|
512 |
|||
513 |
private: |
||
514 |
// Declaring operator new and delete as deleted is not spec compliant. |
||
515 |
// Therefore, declare them private instead to disable dynamic alloc. |
||
516 |
void* operator new(size_t size); |
||
517 |
void* operator new[](size_t size); |
||
518 |
void operator delete(void*, size_t); |
||
519 |
void operator delete[](void*, size_t); |
||
520 |
|||
521 |
T stack_storage_[kStackStorageSize]; |
||
522 |
T* data_ = nullptr; |
||
523 |
size_t length_ = 0; |
||
524 |
}; |
||
525 |
|||
526 |
3134960 |
class Utf8Value : public MaybeStackBuffer<char> { |
|
527 |
public: |
||
528 |
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value); |
||
529 |
|||
530 |
1085 |
inline std::string ToString() const { return std::string(out(), length()); } |
|
531 |
|||
532 |
4961 |
inline bool operator==(const char* a) const { |
|
533 |
4961 |
return strcmp(out(), a) == 0; |
|
534 |
} |
||
535 |
}; |
||
536 |
|||
537 |
1197501 |
class TwoByteValue : public MaybeStackBuffer<uint16_t> { |
|
538 |
public: |
||
539 |
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value); |
||
540 |
}; |
||
541 |
|||
542 |
238333 |
class BufferValue : public MaybeStackBuffer<char> { |
|
543 |
public: |
||
544 |
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value); |
||
545 |
|||
546 |
inline std::string ToString() const { return std::string(out(), length()); } |
||
547 |
}; |
||
548 |
|||
549 |
#define SPREAD_BUFFER_ARG(val, name) \ |
||
550 |
CHECK((val)->IsArrayBufferView()); \ |
||
551 |
v8::Local<v8::ArrayBufferView> name = (val).As<v8::ArrayBufferView>(); \ |
||
552 |
const size_t name##_offset = name->ByteOffset(); \ |
||
553 |
const size_t name##_length = name->ByteLength(); \ |
||
554 |
char* const name##_data = \ |
||
555 |
static_cast<char*>(name->Buffer()->Data()) + name##_offset; \ |
||
556 |
if (name##_length > 0) CHECK_NE(name##_data, nullptr); |
||
557 |
|||
558 |
// Use this when a variable or parameter is unused in order to explicitly |
||
559 |
// silence a compiler warning about that. |
||
560 |
4990158 |
template <typename T> inline void USE(T&&) {} |
|
561 |
|||
562 |
template <typename Fn> |
||
563 |
struct OnScopeLeaveImpl { |
||
564 |
Fn fn_; |
||
565 |
bool active_; |
||
566 |
|||
567 |
1800984 |
explicit OnScopeLeaveImpl(Fn&& fn) : fn_(std::move(fn)), active_(true) {} |
|
568 |
✓✗✓✗ ✓✗ |
1799677 |
~OnScopeLeaveImpl() { if (active_) fn_(); } |
569 |
|||
570 |
OnScopeLeaveImpl(const OnScopeLeaveImpl& other) = delete; |
||
571 |
OnScopeLeaveImpl& operator=(const OnScopeLeaveImpl& other) = delete; |
||
572 |
OnScopeLeaveImpl(OnScopeLeaveImpl&& other) |
||
573 |
: fn_(std::move(other.fn_)), active_(other.active_) { |
||
574 |
other.active_ = false; |
||
575 |
} |
||
576 |
OnScopeLeaveImpl& operator=(OnScopeLeaveImpl&& other) { |
||
577 |
if (this == &other) return *this; |
||
578 |
this->~OnScopeLeave(); |
||
579 |
new (this)OnScopeLeaveImpl(std::move(other)); |
||
580 |
return *this; |
||
581 |
} |
||
582 |
}; |
||
583 |
|||
584 |
// Run a function when exiting the current scope. Used like this: |
||
585 |
// auto on_scope_leave = OnScopeLeave([&] { |
||
586 |
// // ... run some code ... |
||
587 |
// }); |
||
588 |
template <typename Fn> |
||
589 |
1800984 |
inline MUST_USE_RESULT OnScopeLeaveImpl<Fn> OnScopeLeave(Fn&& fn) { |
|
590 |
1800984 |
return OnScopeLeaveImpl<Fn>{std::move(fn)}; |
|
591 |
} |
||
592 |
|||
593 |
// Simple RAII wrapper for contiguous data that uses malloc()/free(). |
||
594 |
template <typename T> |
||
595 |
struct MallocedBuffer { |
||
596 |
T* data; |
||
597 |
size_t size; |
||
598 |
|||
599 |
T* release() { |
||
600 |
T* ret = data; |
||
601 |
data = nullptr; |
||
602 |
return ret; |
||
603 |
} |
||
604 |
|||
605 |
void Truncate(size_t new_size) { |
||
606 |
CHECK(new_size <= size); |
||
607 |
size = new_size; |
||
608 |
} |
||
609 |
|||
610 |
void Realloc(size_t new_size) { |
||
611 |
Truncate(new_size); |
||
612 |
data = UncheckedRealloc(data, new_size); |
||
613 |
} |
||
614 |
|||
615 |
78728 |
inline bool is_empty() const { return data == nullptr; } |
|
616 |
|||
617 |
115036 |
MallocedBuffer() : data(nullptr), size(0) {} |
|
618 |
988 |
explicit MallocedBuffer(size_t size) : data(Malloc<T>(size)), size(size) {} |
|
619 |
78743 |
MallocedBuffer(T* data, size_t size) : data(data), size(size) {} |
|
620 |
193751 |
MallocedBuffer(MallocedBuffer&& other) : data(other.data), size(other.size) { |
|
621 |
193751 |
other.data = nullptr; |
|
622 |
193751 |
} |
|
623 |
78767 |
MallocedBuffer& operator=(MallocedBuffer&& other) { |
|
624 |
78767 |
this->~MallocedBuffer(); |
|
625 |
78767 |
return *new(this) MallocedBuffer(std::move(other)); |
|
626 |
} |
||
627 |
388488 |
~MallocedBuffer() { |
|
628 |
388488 |
free(data); |
|
629 |
388488 |
} |
|
630 |
MallocedBuffer(const MallocedBuffer&) = delete; |
||
631 |
MallocedBuffer& operator=(const MallocedBuffer&) = delete; |
||
632 |
}; |
||
633 |
|||
634 |
template <typename T> |
||
635 |
24651 |
class NonCopyableMaybe { |
|
636 |
public: |
||
637 |
5004 |
NonCopyableMaybe() : empty_(true) {} |
|
638 |
4317 |
explicit NonCopyableMaybe(T&& value) |
|
639 |
: empty_(false), |
||
640 |
4317 |
value_(std::move(value)) {} |
|
641 |
|||
642 |
4287 |
bool IsEmpty() const { |
|
643 |
4287 |
return empty_; |
|
644 |
} |
||
645 |
|||
646 |
1504 |
const T* get() const { |
|
647 |
✓✓ | 1504 |
return empty_ ? nullptr : &value_; |
648 |
} |
||
649 |
|||
650 |
24 |
const T* operator->() const { |
|
651 |
✗✓ | 24 |
CHECK(!empty_); |
652 |
24 |
return &value_; |
|
653 |
} |
||
654 |
|||
655 |
4222 |
T&& Release() { |
|
656 |
✗✓ | 4222 |
CHECK_EQ(empty_, false); |
657 |
4222 |
empty_ = true; |
|
658 |
4222 |
return std::move(value_); |
|
659 |
} |
||
660 |
|||
661 |
private: |
||
662 |
bool empty_; |
||
663 |
T value_; |
||
664 |
}; |
||
665 |
|||
666 |
// Test whether some value can be called with (). |
||
667 |
template <typename T, typename = void> |
||
668 |
struct is_callable : std::is_function<T> { }; |
||
669 |
|||
670 |
template <typename T> |
||
671 |
struct is_callable<T, typename std::enable_if< |
||
672 |
std::is_same<decltype(void(&T::operator())), void>::value |
||
673 |
>::type> : std::true_type { }; |
||
674 |
|||
675 |
template <typename T, void (*function)(T*)> |
||
676 |
struct FunctionDeleter { |
||
677 |
118086 |
void operator()(T* pointer) const { function(pointer); } |
|
678 |
typedef std::unique_ptr<T, FunctionDeleter> Pointer; |
||
679 |
}; |
||
680 |
|||
681 |
template <typename T, void (*function)(T*)> |
||
682 |
using DeleteFnPtr = typename FunctionDeleter<T, function>::Pointer; |
||
683 |
|||
684 |
std::vector<std::string> SplitString(const std::string& in, |
||
685 |
char delim, |
||
686 |
bool skipEmpty = true); |
||
687 |
|||
688 |
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, |
||
689 |
std::string_view str, |
||
690 |
v8::Isolate* isolate = nullptr); |
||
691 |
template <typename T, typename test_for_number = |
||
692 |
typename std::enable_if<std::numeric_limits<T>::is_specialized, bool>::type> |
||
693 |
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, |
||
694 |
const T& number, |
||
695 |
v8::Isolate* isolate = nullptr); |
||
696 |
template <typename T> |
||
697 |
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, |
||
698 |
const std::vector<T>& vec, |
||
699 |
v8::Isolate* isolate = nullptr); |
||
700 |
template <typename T> |
||
701 |
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, |
||
702 |
const std::set<T>& set, |
||
703 |
v8::Isolate* isolate = nullptr); |
||
704 |
template <typename T, typename U> |
||
705 |
inline v8::MaybeLocal<v8::Value> ToV8Value(v8::Local<v8::Context> context, |
||
706 |
const std::unordered_map<T, U>& map, |
||
707 |
v8::Isolate* isolate = nullptr); |
||
708 |
|||
709 |
// These macros expects a `Isolate* isolate` and a `Local<Context> context` |
||
710 |
// to be in the scope. |
||
711 |
#define READONLY_PROPERTY(obj, name, value) \ |
||
712 |
do { \ |
||
713 |
obj->DefineOwnProperty( \ |
||
714 |
context, FIXED_ONE_BYTE_STRING(isolate, name), value, v8::ReadOnly) \ |
||
715 |
.Check(); \ |
||
716 |
} while (0) |
||
717 |
|||
718 |
#define READONLY_DONT_ENUM_PROPERTY(obj, name, var) \ |
||
719 |
do { \ |
||
720 |
obj->DefineOwnProperty( \ |
||
721 |
context, \ |
||
722 |
OneByteString(isolate, name), \ |
||
723 |
var, \ |
||
724 |
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontEnum)) \ |
||
725 |
.Check(); \ |
||
726 |
} while (0) |
||
727 |
|||
728 |
#define READONLY_FALSE_PROPERTY(obj, name) \ |
||
729 |
READONLY_PROPERTY(obj, name, v8::False(isolate)) |
||
730 |
|||
731 |
#define READONLY_TRUE_PROPERTY(obj, name) \ |
||
732 |
READONLY_PROPERTY(obj, name, v8::True(isolate)) |
||
733 |
|||
734 |
#define READONLY_STRING_PROPERTY(obj, name, str) \ |
||
735 |
READONLY_PROPERTY(obj, name, ToV8Value(context, str).ToLocalChecked()) |
||
736 |
|||
737 |
// Variation on NODE_DEFINE_CONSTANT that sets a String value. |
||
738 |
#define NODE_DEFINE_STRING_CONSTANT(target, name, constant) \ |
||
739 |
do { \ |
||
740 |
v8::Isolate* isolate = target->GetIsolate(); \ |
||
741 |
v8::Local<v8::String> constant_name = \ |
||
742 |
v8::String::NewFromUtf8(isolate, name).ToLocalChecked(); \ |
||
743 |
v8::Local<v8::String> constant_value = \ |
||
744 |
v8::String::NewFromUtf8(isolate, constant).ToLocalChecked(); \ |
||
745 |
v8::PropertyAttribute constant_attributes = \ |
||
746 |
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); \ |
||
747 |
target \ |
||
748 |
->DefineOwnProperty(isolate->GetCurrentContext(), \ |
||
749 |
constant_name, \ |
||
750 |
constant_value, \ |
||
751 |
constant_attributes) \ |
||
752 |
.Check(); \ |
||
753 |
} while (0) |
||
754 |
|||
755 |
enum class Endianness { LITTLE, BIG }; |
||
756 |
|||
757 |
8932 |
inline Endianness GetEndianness() { |
|
758 |
// Constant-folded by the compiler. |
||
759 |
const union { |
||
760 |
uint8_t u8[2]; |
||
761 |
uint16_t u16; |
||
762 |
8932 |
} u = {{1, 0}}; |
|
763 |
✓✗ | 8932 |
return u.u16 == 1 ? Endianness::LITTLE : Endianness::BIG; |
764 |
} |
||
765 |
|||
766 |
1 |
inline bool IsLittleEndian() { |
|
767 |
1 |
return GetEndianness() == Endianness::LITTLE; |
|
768 |
} |
||
769 |
|||
770 |
8931 |
inline bool IsBigEndian() { |
|
771 |
8931 |
return GetEndianness() == Endianness::BIG; |
|
772 |
} |
||
773 |
|||
774 |
// Round up a to the next highest multiple of b. |
||
775 |
template <typename T> |
||
776 |
170642 |
constexpr T RoundUp(T a, T b) { |
|
777 |
✓✓ | 170642 |
return a % b != 0 ? a + b - (a % b) : a; |
778 |
} |
||
779 |
|||
780 |
// Align ptr to an `alignment`-bytes boundary. |
||
781 |
template <typename T, typename U> |
||
782 |
24048 |
constexpr T* AlignUp(T* ptr, U alignment) { |
|
783 |
return reinterpret_cast<T*>( |
||
784 |
24048 |
RoundUp(reinterpret_cast<uintptr_t>(ptr), alignment)); |
|
785 |
} |
||
786 |
|||
787 |
71851 |
class SlicedArguments : public MaybeStackBuffer<v8::Local<v8::Value>> { |
|
788 |
public: |
||
789 |
inline explicit SlicedArguments( |
||
790 |
const v8::FunctionCallbackInfo<v8::Value>& args, size_t start = 0); |
||
791 |
}; |
||
792 |
|||
793 |
// Convert a v8::PersistentBase, e.g. v8::Global, to a Local, with an extra |
||
794 |
// optimization for strong persistent handles. |
||
795 |
class PersistentToLocal { |
||
796 |
public: |
||
797 |
// If persistent.IsWeak() == false, then do not call persistent.Reset() |
||
798 |
// while the returned Local<T> is still in scope, it will destroy the |
||
799 |
// reference to the object. |
||
800 |
template <class TypeName> |
||
801 |
4700816 |
static inline v8::Local<TypeName> Default( |
|
802 |
v8::Isolate* isolate, |
||
803 |
const v8::PersistentBase<TypeName>& persistent) { |
||
804 |
✓✓✓✗ ✗✗ |
4700816 |
if (persistent.IsWeak()) { |
805 |
2351969 |
return PersistentToLocal::Weak(isolate, persistent); |
|
806 |
} else { |
||
807 |
2348847 |
return PersistentToLocal::Strong(persistent); |
|
808 |
} |
||
809 |
} |
||
810 |
|||
811 |
// Unchecked conversion from a non-weak Persistent<T> to Local<T>, |
||
812 |
// use with care! |
||
813 |
// |
||
814 |
// Do not call persistent.Reset() while the returned Local<T> is still in |
||
815 |
// scope, it will destroy the reference to the object. |
||
816 |
template <class TypeName> |
||
817 |
18151346 |
static inline v8::Local<TypeName> Strong( |
|
818 |
const v8::PersistentBase<TypeName>& persistent) { |
||
819 |
DCHECK(!persistent.IsWeak()); |
||
820 |
return *reinterpret_cast<v8::Local<TypeName>*>( |
||
821 |
18151346 |
const_cast<v8::PersistentBase<TypeName>*>(&persistent)); |
|
822 |
} |
||
823 |
|||
824 |
template <class TypeName> |
||
825 |
2371452 |
static inline v8::Local<TypeName> Weak( |
|
826 |
v8::Isolate* isolate, |
||
827 |
const v8::PersistentBase<TypeName>& persistent) { |
||
828 |
2371452 |
return v8::Local<TypeName>::New(isolate, persistent); |
|
829 |
} |
||
830 |
}; |
||
831 |
|||
832 |
// Can be used as a key for std::unordered_map when lookup performance is more |
||
833 |
// important than size and the keys are statically used to avoid redundant hash |
||
834 |
// computations. |
||
835 |
class FastStringKey { |
||
836 |
public: |
||
837 |
constexpr explicit FastStringKey(const char* name); |
||
838 |
|||
839 |
struct Hash { |
||
840 |
constexpr size_t operator()(const FastStringKey& key) const; |
||
841 |
}; |
||
842 |
constexpr bool operator==(const FastStringKey& other) const; |
||
843 |
|||
844 |
constexpr const char* c_str() const; |
||
845 |
|||
846 |
private: |
||
847 |
static constexpr size_t HashImpl(const char* str); |
||
848 |
|||
849 |
const char* name_; |
||
850 |
size_t cached_hash_; |
||
851 |
}; |
||
852 |
|||
853 |
// Like std::static_pointer_cast but for unique_ptr with the default deleter. |
||
854 |
template <typename T, typename U> |
||
855 |
10752 |
std::unique_ptr<T> static_unique_pointer_cast(std::unique_ptr<U>&& ptr) { |
|
856 |
10752 |
return std::unique_ptr<T>(static_cast<T*>(ptr.release())); |
|
857 |
} |
||
858 |
|||
859 |
#define MAYBE_FIELD_PTR(ptr, field) ptr == nullptr ? nullptr : &(ptr->field) |
||
860 |
|||
861 |
// Returns a non-zero code if it fails to open or read the file, |
||
862 |
// aborts if it fails to close the file. |
||
863 |
int ReadFileSync(std::string* result, const char* path); |
||
864 |
|||
865 |
v8::Local<v8::FunctionTemplate> NewFunctionTemplate( |
||
866 |
v8::Isolate* isolate, |
||
867 |
v8::FunctionCallback callback, |
||
868 |
v8::Local<v8::Signature> signature = v8::Local<v8::Signature>(), |
||
869 |
v8::ConstructorBehavior behavior = v8::ConstructorBehavior::kAllow, |
||
870 |
v8::SideEffectType side_effect = v8::SideEffectType::kHasSideEffect, |
||
871 |
const v8::CFunction* c_function = nullptr); |
||
872 |
|||
873 |
// Convenience methods for NewFunctionTemplate(). |
||
874 |
void SetMethod(v8::Local<v8::Context> context, |
||
875 |
v8::Local<v8::Object> that, |
||
876 |
const char* name, |
||
877 |
v8::FunctionCallback callback); |
||
878 |
|||
879 |
void SetFastMethod(v8::Local<v8::Context> context, |
||
880 |
v8::Local<v8::Object> that, |
||
881 |
const char* name, |
||
882 |
v8::FunctionCallback slow_callback, |
||
883 |
const v8::CFunction* c_function); |
||
884 |
|||
885 |
void SetProtoMethod(v8::Isolate* isolate, |
||
886 |
v8::Local<v8::FunctionTemplate> that, |
||
887 |
const char* name, |
||
888 |
v8::FunctionCallback callback); |
||
889 |
|||
890 |
void SetInstanceMethod(v8::Isolate* isolate, |
||
891 |
v8::Local<v8::FunctionTemplate> that, |
||
892 |
const char* name, |
||
893 |
v8::FunctionCallback callback); |
||
894 |
|||
895 |
// Safe variants denote the function has no side effects. |
||
896 |
void SetMethodNoSideEffect(v8::Local<v8::Context> context, |
||
897 |
v8::Local<v8::Object> that, |
||
898 |
const char* name, |
||
899 |
v8::FunctionCallback callback); |
||
900 |
void SetProtoMethodNoSideEffect(v8::Isolate* isolate, |
||
901 |
v8::Local<v8::FunctionTemplate> that, |
||
902 |
const char* name, |
||
903 |
v8::FunctionCallback callback); |
||
904 |
|||
905 |
enum class SetConstructorFunctionFlag { |
||
906 |
NONE, |
||
907 |
SET_CLASS_NAME, |
||
908 |
}; |
||
909 |
|||
910 |
void SetConstructorFunction(v8::Local<v8::Context> context, |
||
911 |
v8::Local<v8::Object> that, |
||
912 |
const char* name, |
||
913 |
v8::Local<v8::FunctionTemplate> tmpl, |
||
914 |
SetConstructorFunctionFlag flag = |
||
915 |
SetConstructorFunctionFlag::SET_CLASS_NAME); |
||
916 |
|||
917 |
void SetConstructorFunction(v8::Local<v8::Context> context, |
||
918 |
v8::Local<v8::Object> that, |
||
919 |
v8::Local<v8::String> name, |
||
920 |
v8::Local<v8::FunctionTemplate> tmpl, |
||
921 |
SetConstructorFunctionFlag flag = |
||
922 |
SetConstructorFunctionFlag::SET_CLASS_NAME); |
||
923 |
|||
924 |
} // namespace node |
||
925 |
|||
926 |
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
927 |
|||
928 |
#endif // SRC_UTIL_H_ |
Generated by: GCOVR (Version 4.2) |