Skip to content

Commit

Permalink
Bug 712129 - Implement MOZ_STATIC_ASSERT and MOZ_STATIC_ASSERT_IF. r=…
Browse files Browse the repository at this point in the history
…luke
  • Loading branch information
jswalden committed Dec 19, 2011
1 parent 0d070ca commit 0517423
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 50 deletions.
29 changes: 2 additions & 27 deletions js/public/Utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,8 @@ JS_BEGIN_EXTERN_C
# define JS_THREADSAFE_ASSERT(expr) ((void) 0)
#endif

/*
* JS_STATIC_ASSERT
*
* A compile-time assert. "cond" must be a constant expression. The macro can
* be used only in places where an "extern" declaration is allowed.
*/
#ifdef __SUNPRO_CC
/*
* Sun Studio C++ compiler has a bug
* "sizeof expression not accepted as size of array parameter"
* It happens when js_static_assert() function is declared inside functions.
* The bug number is 6688515. It is not public yet.
* Therefore, for Sun Studio, declare js_static_assert as an array instead.
*/
# define JS_STATIC_ASSERT(cond) extern char js_static_assert[(cond) ? 1 : -1]
#else
# ifdef __COUNTER__
# define JS_STATIC_ASSERT_GLUE1(x,y) x##y
# define JS_STATIC_ASSERT_GLUE(x,y) JS_STATIC_ASSERT_GLUE1(x,y)
# define JS_STATIC_ASSERT(cond) \
typedef int JS_STATIC_ASSERT_GLUE(js_static_assert, __COUNTER__)[(cond) ? 1 : -1]
# else
# define JS_STATIC_ASSERT(cond) extern void js_static_assert(int arg[(cond) ? 1 : -1])
# endif
#endif

#define JS_STATIC_ASSERT_IF(cond, expr) JS_STATIC_ASSERT(!(cond) || (expr))
#define JS_STATIC_ASSERT(cond) MOZ_STATIC_ASSERT(cond, "JS_STATIC_ASSERT")
#define JS_STATIC_ASSERT_IF(cond, expr) MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF")

/*
* Abort the process in a non-graceful manner. This will cause a core file,
Expand Down
81 changes: 81 additions & 0 deletions mfbt/Assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,87 @@

#include "mozilla/Types.h"

/*
* MOZ_STATIC_ASSERT may be used to assert a condition *at compile time*. This
* can be useful when you make certain assumptions about what must hold for
* optimal, or even correct, behavior. For example, you might assert that the
* size of a struct is a multiple of the target architecture's word size:
*
* struct S { ... };
* MOZ_STATIC_ASSERT(sizeof(S) % sizeof(size_t) == 0,
* "S should be a multiple of word size for efficiency");
*
* This macro can be used in any location where both an extern declaration and a
* typedef could be used.
*
* Be aware of the gcc 4.2 concerns noted further down when writing patches that
* use this macro, particularly if a patch only bounces on OS X.
*/
#ifdef __cplusplus
# if defined(__clang__)
# ifndef __has_extension
# define __has_extension __has_feature /* compatibility, for older versions of clang */
# endif
# if __has_extension(cxx_static_assert)
# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), reason)
# endif
# elif defined(__GNUC__)
# if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond)
# endif
# elif defined(_MSC_VER)
# if _MSC_VER >= 1600 /* MSVC 10 */
# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond)
# endif
# elif defined(__HP_aCC)
# if __HP_aCC >= 62500 && defined(_HP_CXX0x_SOURCE)
# define MOZ_STATIC_ASSERT(cond, reason) static_assert((cond), #cond)
# endif
# endif
#endif
#ifndef MOZ_STATIC_ASSERT
# define MOZ_STATIC_ASSERT_GLUE1(x, y) x##y
# define MOZ_STATIC_ASSERT_GLUE(x, y) MOZ_STATIC_ASSERT_GLUE1(x, y)
# if defined(__SUNPRO_CC)
/*
* The Sun Studio C++ compiler is buggy when declaring, inside a function,
* another extern'd function with an array argument whose length contains a
* sizeof, triggering the error message "sizeof expression not accepted as
* size of array parameter". This bug (6688515, not public yet) would hit
* defining moz_static_assert as a function, so we always define an extern
* array for Sun Studio.
*
* We include the line number in the symbol name in a best-effort attempt
* to avoid conflicts (see below).
*/
# define MOZ_STATIC_ASSERT(cond, reason) \
extern char MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)[(cond) ? 1 : -1]
# elif defined(__COUNTER__)
/*
* If there was no preferred alternative, use a compiler-agnostic version.
*
* Note that the non-__COUNTER__ version has a bug in C++: it can't be used
* in both |extern "C"| and normal C++ in the same translation unit. (Alas
* |extern "C"| isn't allowed in a function.) The only affected compiler
* we really care about is gcc 4.2. For that compiler and others like it,
* we include the line number in the function name to do the best we can to
* avoid conflicts. These should be rare: a conflict would require use of
* MOZ_STATIC_ASSERT on the same line in separate files in the same
* translation unit, *and* the uses would have to be in code with
* different linkage, *and* the first observed use must be in C++-linkage
* code.
*/
# define MOZ_STATIC_ASSERT(cond, reason) \
typedef int MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __COUNTER__)[(cond) ? 1 : -1]
# else
# define MOZ_STATIC_ASSERT(cond, reason) \
extern void MOZ_STATIC_ASSERT_GLUE(moz_static_assert, __LINE__)(int arg[(cond) ? 1 : -1])
# endif
#endif

#define MOZ_STATIC_ASSERT_IF(cond, expr, reason) MOZ_STATIC_ASSERT(!(cond) || (expr), reason)

/*
* XXX: we're cheating here in order to avoid creating object files
* for mfbt /just/ to provide a function like FatalError() to be used
Expand Down
23 changes: 13 additions & 10 deletions xpcom/ds/nsAtomTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */

#include "mozilla/Assertions.h"

#include "nsAtomTable.h"
#include "nsStaticAtom.h"
#include "nsString.h"
Expand Down Expand Up @@ -406,16 +408,17 @@ GetAtomHashEntry(const PRUnichar* aString, PRUint32 aLength)
class CheckStaticAtomSizes
{
CheckStaticAtomSizes() {
PR_STATIC_ASSERT((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
sizeof(nsStringBuffer().mRefCount)) &&
(sizeof(nsFakeStringBuffer<1>().mSize) ==
sizeof(nsStringBuffer().mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
offsetof(nsStringBuffer, mRefCount)) &&
(offsetof(nsFakeStringBuffer<1>, mSize) ==
offsetof(nsStringBuffer, mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mStringData) ==
sizeof(nsStringBuffer)));
MOZ_STATIC_ASSERT((sizeof(nsFakeStringBuffer<1>().mRefCnt) ==
sizeof(nsStringBuffer().mRefCount)) &&
(sizeof(nsFakeStringBuffer<1>().mSize) ==
sizeof(nsStringBuffer().mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mRefCnt) ==
offsetof(nsStringBuffer, mRefCount)) &&
(offsetof(nsFakeStringBuffer<1>, mSize) ==
offsetof(nsStringBuffer, mStorageSize)) &&
(offsetof(nsFakeStringBuffer<1>, mStringData) ==
sizeof(nsStringBuffer)),
"mocked-up strings' representations should be compatible");
}
};

Expand Down
15 changes: 8 additions & 7 deletions xpcom/glue/nsTArray-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elem
// If we're on a 32-bit system and elemAlign is 8, we need to adjust our
// pointer to take into account the extra alignment in the auto array.

// Check that the auto array is padded as we expect.
PR_STATIC_ASSERT(sizeof(void*) != 4 ||
(MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 1>) ==
sizeof(void*) + sizeof(nsTArrayHeader) +
4 + sizeof(mozilla::AlignedElem<8>)));
MOZ_STATIC_ASSERT(sizeof(void*) != 4 ||
(MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 1>) ==
sizeof(void*) + sizeof(nsTArrayHeader) +
4 + sizeof(mozilla::AlignedElem<8>)),
"auto array padding wasn't what we expected");

// We don't support alignments greater than 8 bytes.
NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment.");
Expand Down Expand Up @@ -115,7 +115,8 @@ bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
// owned by this nsAutoTArray. We statically assert that elem_type's
// alignment is 8 bytes or less in nsAutoArrayBase.

PR_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4);
MOZ_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4,
"see comment above");

#ifdef DEBUG
PRPtrdiff diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
Expand Down
16 changes: 10 additions & 6 deletions xpcom/glue/nsTArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#ifndef nsTArray_h__
#define nsTArray_h__

#include "mozilla/Assertions.h"
#include "mozilla/Util.h"

#include <string.h>

#include "prtypes.h"
Expand All @@ -47,7 +50,6 @@
#include "nsQuickSort.h"
#include "nsDebug.h"
#include "nsTraceRefcnt.h"
#include "mozilla/Util.h"
#include NEW_H

//
Expand Down Expand Up @@ -1316,9 +1318,9 @@ class nsAutoArrayBase : public TArrayBase
friend class nsTArray_base;

void Init() {
// We can't handle alignments greater than 8; see
// nsTArray_base::UsesAutoArrayBuffer().
PR_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8);
MOZ_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8,
"can't handle alignments greater than 8, "
"see nsTArray_base::UsesAutoArrayBuffer()");

*base_type::PtrToHdr() = reinterpret_cast<Header*>(&mAutoBuf);
base_type::Hdr()->mLength = 0;
Expand Down Expand Up @@ -1367,8 +1369,10 @@ class nsAutoTArray : public nsAutoArrayBase<nsTArray<E, Alloc>, N>
// 64-bit system, where the compiler inserts 4 bytes of padding at the end of
// the auto array to make its size a multiple of alignof(void*) == 8 bytes.

PR_STATIC_ASSERT(sizeof(nsAutoTArray<PRUint32, 2>) ==
sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2);
MOZ_STATIC_ASSERT(sizeof(nsAutoTArray<PRUint32, 2>) ==
sizeof(void*) + sizeof(nsTArrayHeader) + sizeof(PRUint32) * 2,
"nsAutoTArray shouldn't contain any extra padding, "
"see the comment");

template<class E, PRUint32 N>
class AutoFallibleTArray : public nsAutoArrayBase<FallibleTArray<E>, N>
Expand Down

0 comments on commit 0517423

Please sign in to comment.