Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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 : #ifndef js_Utility_h
8 : #define js_Utility_h
9 :
10 : #include "mozilla/Assertions.h"
11 : #include "mozilla/Atomics.h"
12 : #include "mozilla/Attributes.h"
13 : #include "mozilla/Compiler.h"
14 : #include "mozilla/Move.h"
15 : #include "mozilla/Scoped.h"
16 : #include "mozilla/TemplateLib.h"
17 : #include "mozilla/UniquePtr.h"
18 :
19 : #include <stdlib.h>
20 : #include <string.h>
21 :
22 : #ifdef JS_OOM_DO_BACKTRACES
23 : #include <execinfo.h>
24 : #include <stdio.h>
25 : #endif
26 :
27 : #include "jstypes.h"
28 :
29 : /* The public JS engine namespace. */
30 : namespace JS {}
31 :
32 : /* The mozilla-shared reusable template/utility namespace. */
33 : namespace mozilla {}
34 :
35 : /* The private JS engine namespace. */
36 : namespace js {}
37 :
38 : #define JS_STATIC_ASSERT(cond) static_assert(cond, "JS_STATIC_ASSERT")
39 : #define JS_STATIC_ASSERT_IF(cond, expr) MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF")
40 :
41 : extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_API(void)
42 : JS_Assert(const char* s, const char* file, int ln);
43 :
44 : /*
45 : * Custom allocator support for SpiderMonkey
46 : */
47 : #if defined JS_USE_CUSTOM_ALLOCATOR
48 : # include "jscustomallocator.h"
49 : #else
50 :
51 : namespace js {
52 : namespace oom {
53 :
54 : /*
55 : * To make testing OOM in certain helper threads more effective,
56 : * allow restricting the OOM testing to a certain helper thread
57 : * type. This allows us to fail e.g. in off-thread script parsing
58 : * without causing an OOM in the active thread first.
59 : */
60 : enum ThreadType {
61 : THREAD_TYPE_NONE = 0, // 0
62 : THREAD_TYPE_COOPERATING, // 1
63 : THREAD_TYPE_WASM, // 2
64 : THREAD_TYPE_ION, // 3
65 : THREAD_TYPE_PARSE, // 4
66 : THREAD_TYPE_COMPRESS, // 5
67 : THREAD_TYPE_GCHELPER, // 6
68 : THREAD_TYPE_GCPARALLEL, // 7
69 : THREAD_TYPE_PROMISE_TASK, // 8
70 : THREAD_TYPE_ION_FREE, // 9
71 : THREAD_TYPE_MAX // Used to check shell function arguments
72 : };
73 :
74 : /*
75 : * Getter/Setter functions to encapsulate mozilla::ThreadLocal,
76 : * implementation is in jsutil.cpp.
77 : */
78 : # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
79 : extern bool InitThreadType(void);
80 : extern void SetThreadType(ThreadType);
81 : extern JS_FRIEND_API(uint32_t) GetThreadType(void);
82 : # else
83 : inline bool InitThreadType(void) { return true; }
84 : inline void SetThreadType(ThreadType t) {};
85 : inline uint32_t GetThreadType(void) { return 0; }
86 : # endif
87 :
88 : } /* namespace oom */
89 : } /* namespace js */
90 :
91 : # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
92 :
93 : #ifdef JS_OOM_BREAKPOINT
94 : static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
95 : #define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
96 : #else
97 : #define JS_OOM_CALL_BP_FUNC() do {} while(0)
98 : #endif
99 :
100 : namespace js {
101 : namespace oom {
102 :
103 : /*
104 : * Out of memory testing support. We provide various testing functions to
105 : * simulate OOM conditions and so we can test that they are handled correctly.
106 : */
107 :
108 : extern JS_PUBLIC_DATA(uint32_t) targetThread;
109 : extern JS_PUBLIC_DATA(uint64_t) maxAllocations;
110 : extern JS_PUBLIC_DATA(uint64_t) counter;
111 : extern JS_PUBLIC_DATA(bool) failAlways;
112 :
113 : extern void
114 : SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always);
115 :
116 : extern void
117 : ResetSimulatedOOM();
118 :
119 : inline bool
120 3865459 : IsThreadSimulatingOOM()
121 : {
122 3865459 : return js::oom::targetThread && js::oom::targetThread == js::oom::GetThreadType();
123 : }
124 :
125 : inline bool
126 0 : IsSimulatedOOMAllocation()
127 : {
128 0 : return IsThreadSimulatingOOM() &&
129 0 : (counter == maxAllocations || (counter > maxAllocations && failAlways));
130 : }
131 :
132 : inline bool
133 3045421 : ShouldFailWithOOM()
134 : {
135 3045421 : if (!IsThreadSimulatingOOM())
136 3045410 : return false;
137 :
138 0 : counter++;
139 0 : if (IsSimulatedOOMAllocation()) {
140 : JS_OOM_CALL_BP_FUNC();
141 0 : return true;
142 : }
143 0 : return false;
144 : }
145 :
146 : inline bool
147 59747 : HadSimulatedOOM() {
148 59747 : return counter >= maxAllocations;
149 : }
150 :
151 : } /* namespace oom */
152 : } /* namespace js */
153 :
154 : # define JS_OOM_POSSIBLY_FAIL() \
155 : do { \
156 : if (js::oom::ShouldFailWithOOM()) \
157 : return nullptr; \
158 : } while (0)
159 :
160 : # define JS_OOM_POSSIBLY_FAIL_BOOL() \
161 : do { \
162 : if (js::oom::ShouldFailWithOOM()) \
163 : return false; \
164 : } while (0)
165 :
166 : # else
167 :
168 : # define JS_OOM_POSSIBLY_FAIL() do {} while(0)
169 : # define JS_OOM_POSSIBLY_FAIL_BOOL() do {} while(0)
170 : namespace js {
171 : namespace oom {
172 : static inline bool IsSimulatedOOMAllocation() { return false; }
173 : static inline bool ShouldFailWithOOM() { return false; }
174 : } /* namespace oom */
175 : } /* namespace js */
176 :
177 : # endif /* DEBUG || JS_OOM_BREAKPOINT */
178 :
179 : namespace js {
180 :
181 : /* Disable OOM testing in sections which are not OOM safe. */
182 : struct MOZ_RAII JS_PUBLIC_DATA(AutoEnterOOMUnsafeRegion)
183 : {
184 : MOZ_NORETURN MOZ_COLD void crash(const char* reason);
185 : MOZ_NORETURN MOZ_COLD void crash(size_t size, const char* reason);
186 :
187 : using AnnotateOOMAllocationSizeCallback = void(*)(size_t);
188 : static AnnotateOOMAllocationSizeCallback annotateOOMSizeCallback;
189 4 : static void setAnnotateOOMAllocationSizeCallback(AnnotateOOMAllocationSizeCallback callback) {
190 4 : annotateOOMSizeCallback = callback;
191 4 : }
192 :
193 : #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
194 809902 : AutoEnterOOMUnsafeRegion()
195 809902 : : oomEnabled_(oom::IsThreadSimulatingOOM() && oom::maxAllocations != UINT64_MAX),
196 809890 : oomAfter_(0)
197 : {
198 809890 : if (oomEnabled_) {
199 0 : MOZ_ALWAYS_TRUE(owner_.compareExchange(nullptr, this));
200 0 : oomAfter_ = int64_t(oom::maxAllocations) - int64_t(oom::counter);
201 0 : oom::maxAllocations = UINT64_MAX;
202 : }
203 809890 : }
204 :
205 1619754 : ~AutoEnterOOMUnsafeRegion() {
206 809877 : if (oomEnabled_) {
207 0 : MOZ_ASSERT(oom::maxAllocations == UINT64_MAX);
208 0 : int64_t maxAllocations = int64_t(oom::counter) + oomAfter_;
209 0 : MOZ_ASSERT(maxAllocations >= 0,
210 : "alloc count + oom limit exceeds range, your oom limit is probably too large");
211 0 : oom::maxAllocations = uint64_t(maxAllocations);
212 0 : MOZ_ALWAYS_TRUE(owner_.compareExchange(this, nullptr));
213 : }
214 809877 : }
215 :
216 : private:
217 : // Used to catch concurrent use from other threads.
218 : static mozilla::Atomic<AutoEnterOOMUnsafeRegion*> owner_;
219 :
220 : bool oomEnabled_;
221 : int64_t oomAfter_;
222 : #endif
223 : };
224 :
225 : } /* namespace js */
226 :
227 190229 : static inline void* js_malloc(size_t bytes)
228 : {
229 190229 : JS_OOM_POSSIBLY_FAIL();
230 190230 : return malloc(bytes);
231 : }
232 :
233 66266 : static inline void* js_calloc(size_t bytes)
234 : {
235 66266 : JS_OOM_POSSIBLY_FAIL();
236 66267 : return calloc(bytes, 1);
237 : }
238 :
239 5 : static inline void* js_calloc(size_t nmemb, size_t size)
240 : {
241 5 : JS_OOM_POSSIBLY_FAIL();
242 5 : return calloc(nmemb, size);
243 : }
244 :
245 33734 : static inline void* js_realloc(void* p, size_t bytes)
246 : {
247 : // realloc() with zero size is not portable, as some implementations may
248 : // return nullptr on success and free |p| for this. We assume nullptr
249 : // indicates failure and that |p| is still valid.
250 33734 : MOZ_ASSERT(bytes != 0);
251 :
252 33734 : JS_OOM_POSSIBLY_FAIL();
253 33734 : return realloc(p, bytes);
254 : }
255 :
256 166476 : static inline void js_free(void* p)
257 : {
258 166476 : free(p);
259 166476 : }
260 :
261 0 : static inline char* js_strdup(const char* s)
262 : {
263 0 : JS_OOM_POSSIBLY_FAIL();
264 0 : return strdup(s);
265 : }
266 : #endif/* JS_USE_CUSTOM_ALLOCATOR */
267 :
268 : #include <new>
269 :
270 : /*
271 : * Low-level memory management in SpiderMonkey:
272 : *
273 : * ** Do not use the standard malloc/free/realloc: SpiderMonkey allows these
274 : * to be redefined (via JS_USE_CUSTOM_ALLOCATOR) and Gecko even #define's
275 : * these symbols.
276 : *
277 : * ** Do not use the builtin C++ operator new and delete: these throw on
278 : * error and we cannot override them not to.
279 : *
280 : * Allocation:
281 : *
282 : * - If the lifetime of the allocation is tied to the lifetime of a GC-thing
283 : * (that is, finalizing the GC-thing will free the allocation), call one of
284 : * the following functions:
285 : *
286 : * JSContext::{malloc_,realloc_,calloc_,new_}
287 : * JSRuntime::{malloc_,realloc_,calloc_,new_}
288 : *
289 : * These functions accumulate the number of bytes allocated which is used as
290 : * part of the GC-triggering heuristic.
291 : *
292 : * The difference between the JSContext and JSRuntime versions is that the
293 : * cx version reports an out-of-memory error on OOM. (This follows from the
294 : * general SpiderMonkey idiom that a JSContext-taking function reports its
295 : * own errors.)
296 : *
297 : * - Otherwise, use js_malloc/js_realloc/js_calloc/js_new
298 : *
299 : * Deallocation:
300 : *
301 : * - Ordinarily, use js_free/js_delete.
302 : *
303 : * - For deallocations during GC finalization, use one of the following
304 : * operations on the FreeOp provided to the finalizer:
305 : *
306 : * FreeOp::{free_,delete_}
307 : *
308 : * The advantage of these operations is that the memory is batched and freed
309 : * on another thread.
310 : */
311 :
312 : /*
313 : * Given a class which should provide a 'new' method, add
314 : * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
315 : *
316 : * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
317 : * or the build will break.
318 : */
319 : #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS) \
320 : template <class T, typename... Args> \
321 : QUALIFIERS T * \
322 : NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
323 : void* memory = ALLOCATOR(sizeof(T)); \
324 : return MOZ_LIKELY(memory) \
325 : ? new(memory) T(mozilla::Forward<Args>(args)...) \
326 : : nullptr; \
327 : }
328 :
329 : /*
330 : * Given a class which should provide 'make' methods, add
331 : * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example). This
332 : * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
333 : * methods that return mozilla::UniquePtr instances that will singly-manage
334 : * ownership of the created object.
335 : *
336 : * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
337 : * or the build will break.
338 : */
339 : #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
340 : template <class T, typename... Args> \
341 : QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
342 : MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
343 : T* ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \
344 : return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
345 : }
346 :
347 4668 : JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
348 :
349 : namespace js {
350 :
351 : /*
352 : * Calculate the number of bytes needed to allocate |numElems| contiguous
353 : * instances of type |T|. Return false if the calculation overflowed.
354 : */
355 : template <typename T>
356 : MOZ_MUST_USE inline bool
357 315526 : CalculateAllocSize(size_t numElems, size_t* bytesOut)
358 : {
359 315526 : *bytesOut = numElems * sizeof(T);
360 315526 : return (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0;
361 : }
362 :
363 : /*
364 : * Calculate the number of bytes needed to allocate a single instance of type
365 : * |T| followed by |numExtra| contiguous instances of type |Extra|. Return
366 : * false if the calculation overflowed.
367 : */
368 : template <typename T, typename Extra>
369 : MOZ_MUST_USE inline bool
370 723 : CalculateAllocSizeWithExtra(size_t numExtra, size_t* bytesOut)
371 : {
372 723 : *bytesOut = sizeof(T) + numExtra * sizeof(Extra);
373 182 : return (numExtra & mozilla::tl::MulOverflowMask<sizeof(Extra)>::value) == 0 &&
374 814 : *bytesOut >= sizeof(T);
375 : }
376 :
377 : } /* namespace js */
378 :
379 : template <class T>
380 : static MOZ_ALWAYS_INLINE void
381 12032 : js_delete(const T* p)
382 : {
383 12032 : if (p) {
384 1141 : p->~T();
385 1150 : js_free(const_cast<T*>(p));
386 : }
387 12032 : }
388 :
389 : template<class T>
390 : static MOZ_ALWAYS_INLINE void
391 0 : js_delete_poison(const T* p)
392 : {
393 0 : if (p) {
394 0 : p->~T();
395 0 : memset(const_cast<T*>(p), 0x3B, sizeof(T));
396 0 : js_free(const_cast<T*>(p));
397 : }
398 0 : }
399 :
400 : template <class T>
401 : static MOZ_ALWAYS_INLINE T*
402 315 : js_pod_malloc()
403 : {
404 315 : return static_cast<T*>(js_malloc(sizeof(T)));
405 : }
406 :
407 : template <class T>
408 : static MOZ_ALWAYS_INLINE T*
409 : js_pod_calloc()
410 : {
411 : return static_cast<T*>(js_calloc(sizeof(T)));
412 : }
413 :
414 : template <class T>
415 : static MOZ_ALWAYS_INLINE T*
416 180382 : js_pod_malloc(size_t numElems)
417 : {
418 : size_t bytes;
419 180382 : if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
420 0 : return nullptr;
421 180383 : return static_cast<T*>(js_malloc(bytes));
422 : }
423 :
424 : template <class T>
425 : static MOZ_ALWAYS_INLINE T*
426 66265 : js_pod_calloc(size_t numElems)
427 : {
428 : size_t bytes;
429 66265 : if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes)))
430 0 : return nullptr;
431 66265 : return static_cast<T*>(js_calloc(bytes));
432 : }
433 :
434 : template <class T>
435 : static MOZ_ALWAYS_INLINE T*
436 33729 : js_pod_realloc(T* prior, size_t oldSize, size_t newSize)
437 : {
438 9489 : MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
439 : size_t bytes;
440 33729 : if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes)))
441 0 : return nullptr;
442 33729 : return static_cast<T*>(js_realloc(prior, bytes));
443 : }
444 :
445 : namespace js {
446 :
447 : template<typename T>
448 : struct ScopedFreePtrTraits
449 : {
450 : typedef T* type;
451 38833 : static T* empty() { return nullptr; }
452 37052 : static void release(T* ptr) { js_free(ptr); }
453 : };
454 74097 : SCOPED_TEMPLATE(ScopedJSFreePtr, ScopedFreePtrTraits)
455 :
456 : template <typename T>
457 : struct ScopedDeletePtrTraits : public ScopedFreePtrTraits<T>
458 : {
459 1283 : static void release(T* ptr) { js_delete(ptr); }
460 : };
461 2470 : SCOPED_TEMPLATE(ScopedJSDeletePtr, ScopedDeletePtrTraits)
462 :
463 : template <typename T>
464 : struct ScopedReleasePtrTraits : public ScopedFreePtrTraits<T>
465 : {
466 : static void release(T* ptr) { if (ptr) ptr->release(); }
467 : };
468 : SCOPED_TEMPLATE(ScopedReleasePtr, ScopedReleasePtrTraits)
469 :
470 : } /* namespace js */
471 :
472 : namespace JS {
473 :
474 : template<typename T>
475 : struct DeletePolicy
476 : {
477 29534 : constexpr DeletePolicy() {}
478 :
479 : template<typename U>
480 0 : MOZ_IMPLICIT DeletePolicy(DeletePolicy<U> other,
481 : typename mozilla::EnableIf<mozilla::IsConvertible<U*, T*>::value,
482 : int>::Type dummy = 0)
483 0 : {}
484 :
485 0 : void operator()(const T* ptr) {
486 0 : js_delete(const_cast<T*>(ptr));
487 0 : }
488 : };
489 :
490 : struct FreePolicy
491 : {
492 5631 : void operator()(const void* ptr) {
493 5631 : js_free(const_cast<void*>(ptr));
494 5631 : }
495 : };
496 :
497 : typedef mozilla::UniquePtr<char[], JS::FreePolicy> UniqueChars;
498 : typedef mozilla::UniquePtr<char16_t[], JS::FreePolicy> UniqueTwoByteChars;
499 :
500 : } // namespace JS
501 :
502 : namespace js {
503 :
504 : /* Integral types for all hash functions. */
505 : typedef uint32_t HashNumber;
506 : const unsigned HashNumberSizeBits = 32;
507 :
508 : namespace detail {
509 :
510 : /*
511 : * Given a raw hash code, h, return a number that can be used to select a hash
512 : * bucket.
513 : *
514 : * This function aims to produce as uniform an output distribution as possible,
515 : * especially in the most significant (leftmost) bits, even though the input
516 : * distribution may be highly nonrandom, given the constraints that this must
517 : * be deterministic and quick to compute.
518 : *
519 : * Since the leftmost bits of the result are best, the hash bucket index is
520 : * computed by doing ScrambleHashCode(h) / (2^32/N) or the equivalent
521 : * right-shift, not ScrambleHashCode(h) % N or the equivalent bit-mask.
522 : *
523 : * FIXME: OrderedHashTable uses a bit-mask; see bug 775896.
524 : */
525 : inline HashNumber
526 3811548 : ScrambleHashCode(HashNumber h)
527 : {
528 : /*
529 : * Simply returning h would not cause any hash tables to produce wrong
530 : * answers. But it can produce pathologically bad performance: The caller
531 : * right-shifts the result, keeping only the highest bits. The high bits of
532 : * hash codes are very often completely entropy-free. (So are the lowest
533 : * bits.)
534 : *
535 : * So we use Fibonacci hashing, as described in Knuth, The Art of Computer
536 : * Programming, 6.4. This mixes all the bits of the input hash code h.
537 : *
538 : * The value of goldenRatio is taken from the hex
539 : * expansion of the golden ratio, which starts 1.9E3779B9....
540 : * This value is especially good if values with consecutive hash codes
541 : * are stored in a hash table; see Knuth for details.
542 : */
543 : static const HashNumber goldenRatio = 0x9E3779B9U;
544 3811548 : return h * goldenRatio;
545 : }
546 :
547 : } /* namespace detail */
548 :
549 : } /* namespace js */
550 :
551 : /* sixgill annotation defines */
552 : #ifndef HAVE_STATIC_ANNOTATIONS
553 : # define HAVE_STATIC_ANNOTATIONS
554 : # ifdef XGILL_PLUGIN
555 : # define STATIC_PRECONDITION(COND) __attribute__((precondition(#COND)))
556 : # define STATIC_PRECONDITION_ASSUME(COND) __attribute__((precondition_assume(#COND)))
557 : # define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#COND)))
558 : # define STATIC_POSTCONDITION_ASSUME(COND) __attribute__((postcondition_assume(#COND)))
559 : # define STATIC_INVARIANT(COND) __attribute__((invariant(#COND)))
560 : # define STATIC_INVARIANT_ASSUME(COND) __attribute__((invariant_assume(#COND)))
561 : # define STATIC_ASSUME(COND) \
562 : JS_BEGIN_MACRO \
563 : __attribute__((assume_static(#COND), unused)) \
564 : int STATIC_PASTE1(assume_static_, __COUNTER__); \
565 : JS_END_MACRO
566 : # else /* XGILL_PLUGIN */
567 : # define STATIC_PRECONDITION(COND) /* nothing */
568 : # define STATIC_PRECONDITION_ASSUME(COND) /* nothing */
569 : # define STATIC_POSTCONDITION(COND) /* nothing */
570 : # define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
571 : # define STATIC_INVARIANT(COND) /* nothing */
572 : # define STATIC_INVARIANT_ASSUME(COND) /* nothing */
573 : # define STATIC_ASSUME(COND) JS_BEGIN_MACRO /* nothing */ JS_END_MACRO
574 : # endif /* XGILL_PLUGIN */
575 : # define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
576 : #endif /* HAVE_STATIC_ANNOTATIONS */
577 :
578 : #endif /* js_Utility_h */
|