|
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 /* JS::Anchor implementation. */ |
|
8 |
|
9 #ifndef js_Anchor_h |
|
10 #define js_Anchor_h |
|
11 |
|
12 #include "mozilla/Attributes.h" |
|
13 |
|
14 #include "js/TypeDecls.h" |
|
15 |
|
16 namespace JS { |
|
17 |
|
18 /* |
|
19 * Protecting non-Value, non-JSObject *, non-JSString * values from collection |
|
20 * |
|
21 * Most of the time, the garbage collector's conservative stack scanner works |
|
22 * behind the scenes, finding all live values and protecting them from being |
|
23 * collected. However, when JSAPI client code obtains a pointer to data the |
|
24 * scanner does not know about, owned by an object the scanner does know about, |
|
25 * Care Must Be Taken. |
|
26 * |
|
27 * The scanner recognizes only a select set of types: pointers to JSObjects and |
|
28 * similar things (JSFunctions, and so on), pointers to JSStrings, and Values. |
|
29 * So while the scanner finds all live |JSString| pointers, it does not notice |
|
30 * |jschar| pointers. |
|
31 * |
|
32 * So suppose we have: |
|
33 * |
|
34 * void f(JSString *str) { |
|
35 * const jschar *ch = JS_GetStringCharsZ(str); |
|
36 * ... do stuff with ch, but no uses of str ...; |
|
37 * } |
|
38 * |
|
39 * After the call to |JS_GetStringCharsZ|, there are no further uses of |
|
40 * |str|, which means that the compiler is within its rights to not store |
|
41 * it anywhere. But because the stack scanner will not notice |ch|, there |
|
42 * is no longer any live value in this frame that would keep the string |
|
43 * alive. If |str| is the last reference to that |JSString|, and the |
|
44 * collector runs while we are using |ch|, the string's array of |jschar|s |
|
45 * may be freed out from under us. |
|
46 * |
|
47 * Note that there is only an issue when 1) we extract a thing X the scanner |
|
48 * doesn't recognize from 2) a thing Y the scanner does recognize, and 3) if Y |
|
49 * gets garbage-collected, then X gets freed. If we have code like this: |
|
50 * |
|
51 * void g(JSObject *obj) { |
|
52 * JS::Value x; |
|
53 * JS_GetProperty(obj, "x", &x); |
|
54 * ... do stuff with x ... |
|
55 * } |
|
56 * |
|
57 * there's no problem, because the value we've extracted, x, is a Value, a |
|
58 * type that the conservative scanner recognizes. |
|
59 * |
|
60 * Conservative GC frees us from the obligation to explicitly root the types it |
|
61 * knows about, but when we work with derived values like |ch|, we must root |
|
62 * their owners, as the derived value alone won't keep them alive. |
|
63 * |
|
64 * A JS::Anchor is a kind of GC root that allows us to keep the owners of |
|
65 * derived values like |ch| alive throughout the Anchor's lifetime. We could |
|
66 * fix the above code as follows: |
|
67 * |
|
68 * void f(JSString *str) { |
|
69 * JS::Anchor<JSString *> a_str(str); |
|
70 * const jschar *ch = JS_GetStringCharsZ(str); |
|
71 * ... do stuff with ch, but no uses of str ...; |
|
72 * } |
|
73 * |
|
74 * This simply ensures that |str| will be live until |a_str| goes out of scope. |
|
75 * As long as we don't retain a pointer to the string's characters for longer |
|
76 * than that, we have avoided all garbage collection hazards. |
|
77 */ |
|
78 template<typename T> class AnchorPermitted; |
|
79 template<> class AnchorPermitted<JSObject *> { }; |
|
80 template<> class AnchorPermitted<const JSObject *> { }; |
|
81 template<> class AnchorPermitted<JSFunction *> { }; |
|
82 template<> class AnchorPermitted<const JSFunction *> { }; |
|
83 template<> class AnchorPermitted<JSString *> { }; |
|
84 template<> class AnchorPermitted<const JSString *> { }; |
|
85 template<> class AnchorPermitted<Value> { }; |
|
86 template<> class AnchorPermitted<const JSScript *> { }; |
|
87 template<> class AnchorPermitted<JSScript *> { }; |
|
88 |
|
89 template<typename T> |
|
90 class Anchor : AnchorPermitted<T> |
|
91 { |
|
92 public: |
|
93 Anchor() { } |
|
94 explicit Anchor(T t) { hold = t; } |
|
95 inline ~Anchor(); |
|
96 |
|
97 private: |
|
98 T hold; |
|
99 |
|
100 /* |
|
101 * Rooting analysis considers use of operator= to be a use of an anchor. |
|
102 * For simplicity, Anchor is treated as if it contained a GC thing, from |
|
103 * construction. Thus if we had |
|
104 * |
|
105 * void operator=(const T &t) { hold = t; } |
|
106 * |
|
107 * and this code |
|
108 * |
|
109 * JS::Anchor<JSString*> anchor; |
|
110 * stuff that could GC, producing |str|; |
|
111 * anchor = str; |
|
112 * |
|
113 * the last line would be seen as a hazard, because the final = would "use" |
|
114 * |anchor| that is a GC thing -- which could have been moved around by the |
|
115 * GC. The workaround is to structure your code so that JS::Anchor is |
|
116 * always constructed, living for however long the corresponding value must |
|
117 * live. |
|
118 */ |
|
119 void operator=(const T &t) MOZ_DELETE; |
|
120 |
|
121 Anchor(const Anchor &other) MOZ_DELETE; |
|
122 void operator=(const Anchor &other) MOZ_DELETE; |
|
123 }; |
|
124 |
|
125 template<typename T> |
|
126 inline Anchor<T>::~Anchor() |
|
127 { |
|
128 #ifdef __GNUC__ |
|
129 /* |
|
130 * No code is generated for this. But because this is marked 'volatile', G++ will |
|
131 * assume it has important side-effects, and won't delete it. (G++ never looks at |
|
132 * the actual text and notices it's empty.) And because we have passed |hold| to |
|
133 * it, GCC will keep |hold| alive until this point. |
|
134 * |
|
135 * The "memory" clobber operand ensures that G++ will not move prior memory |
|
136 * accesses after the asm --- it's a barrier. Unfortunately, it also means that |
|
137 * G++ will assume that all memory has changed after the asm, as it would for a |
|
138 * call to an unknown function. I don't know of a way to avoid that consequence. |
|
139 */ |
|
140 asm volatile("":: "g" (hold) : "memory"); |
|
141 #else |
|
142 /* |
|
143 * An adequate portable substitute, for non-structure types. |
|
144 * |
|
145 * The compiler promises that, by the end of an expression statement, the |
|
146 * last-stored value to a volatile object is the same as it would be in an |
|
147 * unoptimized, direct implementation (the "abstract machine" whose behavior the |
|
148 * language spec describes). However, the compiler is still free to reorder |
|
149 * non-volatile accesses across this store --- which is what we must prevent. So |
|
150 * assigning the held value to a volatile variable, as we do here, is not enough. |
|
151 * |
|
152 * In our case, however, garbage collection only occurs at function calls, so it |
|
153 * is sufficient to ensure that the destructor's store isn't moved earlier across |
|
154 * any function calls that could collect. It is hard to imagine the compiler |
|
155 * analyzing the program so thoroughly that it could prove that such motion was |
|
156 * safe. In practice, compilers treat calls to the collector as opaque operations |
|
157 * --- in particular, as operations which could access volatile variables, across |
|
158 * which this destructor must not be moved. |
|
159 * |
|
160 * ("Objection, your honor! *Alleged* killer whale!") |
|
161 * |
|
162 * The disadvantage of this approach is that it does generate code for the store. |
|
163 * We do need to use Anchors in some cases where cycles are tight. |
|
164 * |
|
165 * Note that there is a Anchor<Value>::~Anchor() specialization in Value.h. |
|
166 */ |
|
167 volatile T sink; |
|
168 sink = hold; |
|
169 #endif /* defined(__GNUC__) */ |
|
170 } |
|
171 |
|
172 } // namespace JS |
|
173 |
|
174 #endif /* js_Anchor_h */ |