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 /*
8  * PR assertion checker.
9  */
10 
11 #ifndef jsutil_h
12 #define jsutil_h
13 
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Compiler.h"
16 #include "mozilla/GuardObjects.h"
17 #include "mozilla/HashFunctions.h"
18 #include "mozilla/MathAlgorithms.h"
19 #include "mozilla/PodOperations.h"
20 
21 #include <limits.h>
22 
23 #include "js/Initialization.h"
24 #include "js/Utility.h"
25 #include "js/Value.h"
26 
27 #define JS_ALWAYS_TRUE(expr) MOZ_ALWAYS_TRUE(expr)
28 #define JS_ALWAYS_FALSE(expr) MOZ_ALWAYS_FALSE(expr)
29 
30 #if defined(JS_DEBUG)
31 #define JS_DIAGNOSTICS_ASSERT(expr) MOZ_ASSERT(expr)
32 #elif defined(JS_CRASH_DIAGNOSTICS)
33 #define JS_DIAGNOSTICS_ASSERT(expr)         \
34   do {                                      \
35     if (MOZ_UNLIKELY(!(expr))) MOZ_CRASH(); \
36   } while (0)
37 #else
38 #define JS_DIAGNOSTICS_ASSERT(expr) ((void)0)
39 #endif
40 
js_memcpy(void * dst_,const void * src_,size_t len)41 static MOZ_ALWAYS_INLINE void* js_memcpy(void* dst_, const void* src_,
42                                          size_t len) {
43   char* dst = (char*)dst_;
44   const char* src = (const char*)src_;
45   MOZ_ASSERT_IF(dst >= src, (size_t)(dst - src) >= len);
46   MOZ_ASSERT_IF(src >= dst, (size_t)(src - dst) >= len);
47 
48   return memcpy(dst, src, len);
49 }
50 
51 namespace js {
52 
53 // An internal version of JS_IsInitialized() that returns whether SpiderMonkey
54 // is currently initialized or is in the process of being initialized.
IsInitialized()55 inline bool IsInitialized() {
56   using namespace JS::detail;
57   return libraryInitState == InitState::Initializing ||
58          libraryInitState == InitState::Running;
59 }
60 
61 template <class T>
Reverse(T * beg,T * end)62 static inline void Reverse(T* beg, T* end) {
63   while (beg != end) {
64     if (--end == beg) return;
65     T tmp = *beg;
66     *beg = *end;
67     *end = tmp;
68     ++beg;
69   }
70 }
71 
72 template <class T, class Pred>
RemoveIf(T * begin,T * end,Pred pred)73 static inline T* RemoveIf(T* begin, T* end, Pred pred) {
74   T* result = begin;
75   for (T* p = begin; p != end; p++) {
76     if (!pred(*p)) *result++ = *p;
77   }
78   return result;
79 }
80 
81 template <class Container, class Pred>
EraseIf(Container & c,Pred pred)82 static inline size_t EraseIf(Container& c, Pred pred) {
83   auto newEnd = RemoveIf(c.begin(), c.end(), pred);
84   size_t removed = c.end() - newEnd;
85   c.shrinkBy(removed);
86   return removed;
87 }
88 
89 template <class T>
Find(T * beg,T * end,const T & v)90 static inline T* Find(T* beg, T* end, const T& v) {
91   for (T* p = beg; p != end; ++p) {
92     if (*p == v) return p;
93   }
94   return end;
95 }
96 
97 template <class Container>
Find(Container & c,const typename Container::ElementType & v)98 static inline typename Container::ElementType* Find(
99     Container& c, const typename Container::ElementType& v) {
100   return Find(c.begin(), c.end(), v);
101 }
102 
103 template <typename InputIterT, typename CallableT>
ForEach(InputIterT begin,InputIterT end,CallableT f)104 void ForEach(InputIterT begin, InputIterT end, CallableT f) {
105   for (; begin != end; ++begin) f(*begin);
106 }
107 
108 template <class Container1, class Container2>
EqualContainers(const Container1 & lhs,const Container2 & rhs)109 static inline bool EqualContainers(const Container1& lhs,
110                                    const Container2& rhs) {
111   if (lhs.length() != rhs.length()) return false;
112   for (size_t i = 0, n = lhs.length(); i < n; i++) {
113     if (lhs[i] != rhs[i]) return false;
114   }
115   return true;
116 }
117 
118 template <class Container>
119 static inline HashNumber AddContainerToHash(const Container& c,
120                                             HashNumber hn = 0) {
121   for (size_t i = 0; i < c.length(); i++)
122     hn = mozilla::AddToHash(hn, HashNumber(c[i]));
123   return hn;
124 }
125 
126 template <class T>
Min(T t1,T t2)127 static inline T Min(T t1, T t2) {
128   return t1 < t2 ? t1 : t2;
129 }
130 
131 template <class T>
Max(T t1,T t2)132 static inline T Max(T t1, T t2) {
133   return t1 > t2 ? t1 : t2;
134 }
135 
136 template <typename T>
137 class MOZ_RAII AutoScopedAssign {
138  public:
AutoScopedAssign(T * addr,const T & value MOZ_GUARD_OBJECT_NOTIFIER_PARAM)139   AutoScopedAssign(T* addr, const T& value MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
140       : addr_(addr), old(*addr_) {
141     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
142     *addr_ = value;
143   }
144 
~AutoScopedAssign()145   ~AutoScopedAssign() { *addr_ = old; }
146 
147  private:
148   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
149   T* addr_;
150   T old;
151 };
152 
153 template <typename T, typename U>
ComputeByteAlignment(T bytes,U alignment)154 static inline U ComputeByteAlignment(T bytes, U alignment) {
155   static_assert(mozilla::IsUnsigned<U>::value,
156                 "alignment amount must be unsigned");
157 
158   MOZ_ASSERT(mozilla::IsPowerOfTwo(alignment));
159   return (alignment - (bytes % alignment)) % alignment;
160 }
161 
162 template <typename T, typename U>
AlignBytes(T bytes,U alignment)163 static inline T AlignBytes(T bytes, U alignment) {
164   static_assert(mozilla::IsUnsigned<U>::value,
165                 "alignment amount must be unsigned");
166 
167   return bytes + ComputeByteAlignment(bytes, alignment);
168 }
169 
170 /*****************************************************************************/
171 
172 /* A bit array is an array of bits represented by an array of words (size_t). */
173 
174 static const size_t BitArrayElementBits = sizeof(size_t) * CHAR_BIT;
175 
NumWordsForBitArrayOfLength(size_t length)176 static inline unsigned NumWordsForBitArrayOfLength(size_t length) {
177   return (length + (BitArrayElementBits - 1)) / BitArrayElementBits;
178 }
179 
BitArrayIndexToWordIndex(size_t length,size_t bitIndex)180 static inline unsigned BitArrayIndexToWordIndex(size_t length,
181                                                 size_t bitIndex) {
182   unsigned wordIndex = bitIndex / BitArrayElementBits;
183   MOZ_ASSERT(wordIndex < length);
184   return wordIndex;
185 }
186 
BitArrayIndexToWordMask(size_t i)187 static inline size_t BitArrayIndexToWordMask(size_t i) {
188   return size_t(1) << (i % BitArrayElementBits);
189 }
190 
IsBitArrayElementSet(const size_t * array,size_t length,size_t i)191 static inline bool IsBitArrayElementSet(const size_t* array, size_t length,
192                                         size_t i) {
193   return array[BitArrayIndexToWordIndex(length, i)] &
194          BitArrayIndexToWordMask(i);
195 }
196 
IsAnyBitArrayElementSet(const size_t * array,size_t length)197 static inline bool IsAnyBitArrayElementSet(const size_t* array, size_t length) {
198   unsigned numWords = NumWordsForBitArrayOfLength(length);
199   for (unsigned i = 0; i < numWords; ++i) {
200     if (array[i]) return true;
201   }
202   return false;
203 }
204 
SetBitArrayElement(size_t * array,size_t length,size_t i)205 static inline void SetBitArrayElement(size_t* array, size_t length, size_t i) {
206   array[BitArrayIndexToWordIndex(length, i)] |= BitArrayIndexToWordMask(i);
207 }
208 
ClearBitArrayElement(size_t * array,size_t length,size_t i)209 static inline void ClearBitArrayElement(size_t* array, size_t length,
210                                         size_t i) {
211   array[BitArrayIndexToWordIndex(length, i)] &= ~BitArrayIndexToWordMask(i);
212 }
213 
ClearAllBitArrayElements(size_t * array,size_t length)214 static inline void ClearAllBitArrayElements(size_t* array, size_t length) {
215   for (unsigned i = 0; i < length; ++i) array[i] = 0;
216 }
217 
218 } /* namespace js */
219 
220 namespace mozilla {
221 
222 /**
223  * Set the first |aNElem| T elements in |aDst| to |aSrc|.
224  */
225 template <typename T>
PodSet(T * aDst,const T & aSrc,size_t aNElem)226 static MOZ_ALWAYS_INLINE void PodSet(T* aDst, const T& aSrc, size_t aNElem) {
227   for (const T* dstend = aDst + aNElem; aDst < dstend; ++aDst) *aDst = aSrc;
228 }
229 
230 } /* namespace mozilla */
231 
232 /*
233  * Patterns used by SpiderMonkey to overwrite unused memory. If you are
234  * accessing an object with one of these pattern, you probably have a dangling
235  * pointer. These values should be odd, see the comment in IsThingPoisoned.
236  *
237  * Note: new patterns should also be added to the array in IsThingPoisoned!
238  */
239 #define JS_FRESH_NURSERY_PATTERN 0x2F
240 #define JS_SWEPT_NURSERY_PATTERN 0x2B
241 #define JS_ALLOCATED_NURSERY_PATTERN 0x2D
242 #define JS_FRESH_TENURED_PATTERN 0x4F
243 #define JS_MOVED_TENURED_PATTERN 0x49
244 #define JS_SWEPT_TENURED_PATTERN 0x4B
245 #define JS_ALLOCATED_TENURED_PATTERN 0x4D
246 #define JS_FREED_HEAP_PTR_PATTERN 0x6B
247 
248 /*
249  * Ensure JS_SWEPT_CODE_PATTERN is a byte pattern that will crash immediately
250  * when executed, so either an undefined instruction or an instruction that's
251  * illegal in user mode.
252  */
253 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
254     defined(JS_CODEGEN_NONE)
255 #define JS_SWEPT_CODE_PATTERN 0xED  // IN instruction, crashes in user mode.
256 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
257 #define JS_SWEPT_CODE_PATTERN 0xA3  // undefined instruction
258 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
259 #define JS_SWEPT_CODE_PATTERN 0x01  // undefined instruction
260 #else
261 #error "JS_SWEPT_CODE_PATTERN not defined for this platform"
262 #endif
263 
Poison(void * ptr,uint8_t value,size_t num)264 static inline void* Poison(void* ptr, uint8_t value, size_t num) {
265   static bool disablePoison = bool(getenv("JSGC_DISABLE_POISONING"));
266   if (disablePoison) return ptr;
267 
268     // Without a valid Value tag, a poisoned Value may look like a valid
269     // floating point number. To ensure that we crash more readily when
270     // observing a poisoned Value, we make the poison an invalid ObjectValue.
271     // Unfortunately, this adds about 2% more overhead, so we can only enable
272     // it in debug.
273 #if defined(DEBUG)
274   uintptr_t poison;
275   memset(&poison, value, sizeof(poison));
276 #if defined(JS_PUNBOX64)
277   poison = poison & ((uintptr_t(1) << JSVAL_TAG_SHIFT) - 1);
278 #endif
279   JS::Value v = js::PoisonedObjectValue(poison);
280 
281   size_t value_count = num / sizeof(v);
282   size_t byte_count = num % sizeof(v);
283   mozilla::PodSet(reinterpret_cast<JS::Value*>(ptr), v, value_count);
284   if (byte_count) {
285     uint8_t* bytes = static_cast<uint8_t*>(ptr);
286     uint8_t* end = bytes + num;
287     mozilla::PodSet(end - byte_count, value, byte_count);
288   }
289 #else   // !DEBUG
290   memset(ptr, value, num);
291 #endif  // !DEBUG
292   return ptr;
293 }
294 
295 /* Crash diagnostics by default in debug and on nightly channel. */
296 #if (defined(DEBUG) || defined(NIGHTLY_BUILD)) && !defined(MOZ_ASAN)
297 #define JS_CRASH_DIAGNOSTICS 1
298 #endif
299 
300 /* Enable poisoning in crash-diagnostics and zeal builds. */
301 #if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
302 #define JS_POISON(p, val, size) Poison(p, val, size)
303 #define JS_GC_POISONING 1
304 #else
305 #define JS_POISON(p, val, size) ((void)0)
306 #endif
307 
308 /* Enable even more poisoning in purely debug builds. */
309 #if defined(DEBUG)
310 #define JS_EXTRA_POISON(p, val, size) Poison(p, val, size)
311 #else
312 #define JS_EXTRA_POISON(p, val, size) ((void)0)
313 #endif
314 
315 /* Basic stats */
316 #ifdef DEBUG
317 #define JS_BASIC_STATS 1
318 #endif
319 #ifdef JS_BASIC_STATS
320 #include <stdio.h>
321 typedef struct JSBasicStats {
322   uint32_t num;
323   uint32_t max;
324   double sum;
325   double sqsum;
326   uint32_t logscale; /* logarithmic scale: 0 (linear), 2, 10 */
327   uint32_t hist[11];
328 } JSBasicStats;
329 #define JS_INIT_STATIC_BASIC_STATS                     \
330   {                                                    \
331     0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } \
332   }
333 #define JS_BASIC_STATS_INIT(bs) memset((bs), 0, sizeof(JSBasicStats))
334 #define JS_BASIC_STATS_ACCUM(bs, val) JS_BasicStatsAccum(bs, val)
335 #define JS_MeanAndStdDevBS(bs, sigma) \
336   JS_MeanAndStdDev((bs)->num, (bs)->sum, (bs)->sqsum, sigma)
337 extern void JS_BasicStatsAccum(JSBasicStats* bs, uint32_t val);
338 extern double JS_MeanAndStdDev(uint32_t num, double sum, double sqsum,
339                                double* sigma);
340 extern void JS_DumpBasicStats(JSBasicStats* bs, const char* title, FILE* fp);
341 extern void JS_DumpHistogram(JSBasicStats* bs, FILE* fp);
342 #else
343 #define JS_BASIC_STATS_ACCUM(bs, val)
344 #endif
345 
346 /* A jsbitmap_t is a long integer that can be used for bitmaps. */
347 typedef size_t jsbitmap;
348 #define JS_BITMAP_NBITS (sizeof(jsbitmap) * CHAR_BIT)
349 #define JS_TEST_BIT(_map, _bit)       \
350   ((_map)[(_bit) / JS_BITMAP_NBITS] & \
351    (jsbitmap(1) << ((_bit) % JS_BITMAP_NBITS)))
352 #define JS_SET_BIT(_map, _bit)         \
353   ((_map)[(_bit) / JS_BITMAP_NBITS] |= \
354    (jsbitmap(1) << ((_bit) % JS_BITMAP_NBITS)))
355 #define JS_CLEAR_BIT(_map, _bit)       \
356   ((_map)[(_bit) / JS_BITMAP_NBITS] &= \
357    ~(jsbitmap(1) << ((_bit) % JS_BITMAP_NBITS)))
358 
359 /* Wrapper for various macros to stop warnings coming from their expansions. */
360 #if defined(__clang__)
361 #define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr)                                \
362   JS_BEGIN_MACRO                                                             \
363     _Pragma("clang diagnostic push") /* If these _Pragmas cause warnings for \
364                                         you, try disabling ccache. */        \
365         _Pragma("clang diagnostic ignored \"-Wunused-value\"") {             \
366       expr;                                                                  \
367     }                                                                        \
368     _Pragma("clang diagnostic pop")                                          \
369   JS_END_MACRO
370 #elif MOZ_IS_GCC
371 
372 #define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr)                                 \
373   JS_BEGIN_MACRO                                                              \
374     _Pragma("GCC diagnostic push")                                            \
375         _Pragma("GCC diagnostic ignored \"-Wunused-but-set-variable\"") expr; \
376     _Pragma("GCC diagnostic pop")                                             \
377   JS_END_MACRO
378 #endif
379 
380 #if !defined(JS_SILENCE_UNUSED_VALUE_IN_EXPR)
381 #define JS_SILENCE_UNUSED_VALUE_IN_EXPR(expr) \
382   JS_BEGIN_MACRO                              \
383     expr;                                     \
384   JS_END_MACRO
385 #endif
386 
387 #endif /* jsutil_h */
388