GuardObjects.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* vim: set ts=8 sts=2 et sw=2 tw=80: */
  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. /* Implementation of macros to ensure correct use of RAII Auto* objects. */
  7. #ifndef mozilla_GuardObjects_h
  8. #define mozilla_GuardObjects_h
  9. #include "mozilla/Assertions.h"
  10. #include "mozilla/Move.h"
  11. #include "mozilla/Types.h"
  12. #ifdef __cplusplus
  13. #ifdef DEBUG
  14. /**
  15. * A custom define is used rather than |mozPoisonValue()| due to cascading
  16. * build failures relating to how mfbt is linked on different operating
  17. * systems. See bug 1160253.
  18. */
  19. #define MOZ_POISON uintptr_t(-1)
  20. namespace mozilla {
  21. namespace detail {
  22. /*
  23. * The following classes are designed to cause assertions to detect
  24. * inadvertent use of guard objects as temporaries. In other words,
  25. * when we have a guard object whose only purpose is its constructor and
  26. * destructor (and is never otherwise referenced), the intended use
  27. * might be:
  28. *
  29. * AutoRestore savePainting(mIsPainting);
  30. *
  31. * but is is easy to accidentally write:
  32. *
  33. * AutoRestore(mIsPainting);
  34. *
  35. * which compiles just fine, but runs the destructor well before the
  36. * intended time.
  37. *
  38. * They work by adding (#ifdef DEBUG) an additional parameter to the
  39. * guard object's constructor, with a default value, so that users of
  40. * the guard object's API do not need to do anything. The default value
  41. * of this parameter is a temporary object. C++ (ISO/IEC 14882:1998),
  42. * section 12.2 [class.temporary], clauses 4 and 5 seem to assume a
  43. * guarantee that temporaries are destroyed in the reverse of their
  44. * construction order, but I actually can't find a statement that that
  45. * is true in the general case (beyond the two specific cases mentioned
  46. * there). However, it seems to be true.
  47. *
  48. * These classes are intended to be used only via the macros immediately
  49. * below them:
  50. *
  51. * MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER declares (ifdef DEBUG) a member
  52. * variable, and should be put where a declaration of a private
  53. * member variable would be placed.
  54. * MOZ_GUARD_OBJECT_NOTIFIER_PARAM should be placed at the end of the
  55. * parameters to each constructor of the guard object; it declares
  56. * (ifdef DEBUG) an additional parameter. (But use the *_ONLY_PARAM
  57. * variant for constructors that take no other parameters.)
  58. * MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL should likewise be used in
  59. * the implementation of such constructors when they are not inline.
  60. * MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT should be used in
  61. * the implementation of such constructors to pass the parameter to
  62. * a base class that also uses these macros
  63. * MOZ_GUARD_OBJECT_NOTIFIER_INIT is a statement that belongs in each
  64. * constructor. It uses the parameter declared by
  65. * MOZ_GUARD_OBJECT_NOTIFIER_PARAM.
  66. *
  67. * For more details, and examples of using these macros, see
  68. * https://developer.mozilla.org/en/Using_RAII_classes_in_Mozilla
  69. */
  70. class GuardObjectNotifier
  71. {
  72. private:
  73. bool* mStatementDone;
  74. public:
  75. GuardObjectNotifier()
  76. : mStatementDone(reinterpret_cast<bool*>(MOZ_POISON))
  77. {
  78. }
  79. ~GuardObjectNotifier()
  80. {
  81. // Assert that the GuardObjectNotifier has been properly initialized by
  82. // using the |MOZ_GUARD_OBJECT_NOTIFIER_INIT| macro. A poison value is
  83. // used rather than a null check to appease static analyzers that were
  84. // (incorrectly) detecting null pointer dereferences.
  85. MOZ_ASSERT(mStatementDone != reinterpret_cast<bool*>(MOZ_POISON));
  86. *mStatementDone = true;
  87. }
  88. void setStatementDone(bool* aStatementIsDone)
  89. {
  90. mStatementDone = aStatementIsDone;
  91. }
  92. };
  93. class GuardObjectNotificationReceiver
  94. {
  95. private:
  96. bool mStatementDone;
  97. public:
  98. GuardObjectNotificationReceiver() : mStatementDone(false) { }
  99. ~GuardObjectNotificationReceiver() {
  100. /*
  101. * Assert that the guard object was not used as a temporary. (Note that
  102. * this assert might also fire if init is not called because the guard
  103. * object's implementation is not using the above macros correctly.)
  104. */
  105. MOZ_ASSERT(mStatementDone);
  106. }
  107. void init(GuardObjectNotifier& aNotifier)
  108. {
  109. aNotifier.setStatementDone(&mStatementDone);
  110. }
  111. };
  112. } /* namespace detail */
  113. } /* namespace mozilla */
  114. #undef MOZ_POISON
  115. #endif /* DEBUG */
  116. #ifdef DEBUG
  117. # define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER \
  118. mozilla::detail::GuardObjectNotificationReceiver _mCheckNotUsedAsTemporary;
  119. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM \
  120. , mozilla::detail::GuardObjectNotifier&& _notifier = \
  121. mozilla::detail::GuardObjectNotifier()
  122. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM \
  123. mozilla::detail::GuardObjectNotifier&& _notifier = \
  124. mozilla::detail::GuardObjectNotifier()
  125. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL \
  126. , mozilla::detail::GuardObjectNotifier&& _notifier
  127. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL \
  128. mozilla::detail::GuardObjectNotifier&& _notifier
  129. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT \
  130. , mozilla::Move(_notifier)
  131. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT \
  132. mozilla::Move(_notifier)
  133. # define MOZ_GUARD_OBJECT_NOTIFIER_INIT \
  134. do { _mCheckNotUsedAsTemporary.init(_notifier); } while (0)
  135. #else
  136. # define MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  137. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM
  138. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM
  139. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
  140. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL
  141. # define MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT
  142. # define MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT
  143. # define MOZ_GUARD_OBJECT_NOTIFIER_INIT do { } while (0)
  144. #endif
  145. #endif /* __cplusplus */
  146. #endif /* mozilla_GuardObjects_h */