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