1 | // Copyright 2005 The Closure Library Authors. All Rights Reserved. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS-IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | /** |
16 | * @fileoverview Implements the disposable interface. The dispose method is used |
17 | * to clean up references and resources. |
18 | * @author arv@google.com (Erik Arvidsson) |
19 | */ |
20 | |
21 | |
22 | goog.provide('goog.Disposable'); |
23 | /** @suppress {extraProvide} */ |
24 | goog.provide('goog.dispose'); |
25 | /** @suppress {extraProvide} */ |
26 | goog.provide('goog.disposeAll'); |
27 | |
28 | goog.require('goog.disposable.IDisposable'); |
29 | |
30 | |
31 | |
32 | /** |
33 | * Class that provides the basic implementation for disposable objects. If your |
34 | * class holds one or more references to COM objects, DOM nodes, or other |
35 | * disposable objects, it should extend this class or implement the disposable |
36 | * interface (defined in goog.disposable.IDisposable). |
37 | * @constructor |
38 | * @implements {goog.disposable.IDisposable} |
39 | */ |
40 | goog.Disposable = function() { |
41 | if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) { |
42 | if (goog.Disposable.INCLUDE_STACK_ON_CREATION) { |
43 | this.creationStack = new Error().stack; |
44 | } |
45 | goog.Disposable.instances_[goog.getUid(this)] = this; |
46 | } |
47 | // Support sealing |
48 | this.disposed_ = this.disposed_; |
49 | this.onDisposeCallbacks_ = this.onDisposeCallbacks_; |
50 | }; |
51 | |
52 | |
53 | /** |
54 | * @enum {number} Different monitoring modes for Disposable. |
55 | */ |
56 | goog.Disposable.MonitoringMode = { |
57 | /** |
58 | * No monitoring. |
59 | */ |
60 | OFF: 0, |
61 | /** |
62 | * Creating and disposing the goog.Disposable instances is monitored. All |
63 | * disposable objects need to call the {@code goog.Disposable} base |
64 | * constructor. The PERMANENT mode must be switched on before creating any |
65 | * goog.Disposable instances. |
66 | */ |
67 | PERMANENT: 1, |
68 | /** |
69 | * INTERACTIVE mode can be switched on and off on the fly without producing |
70 | * errors. It also doesn't warn if the disposable objects don't call the |
71 | * {@code goog.Disposable} base constructor. |
72 | */ |
73 | INTERACTIVE: 2 |
74 | }; |
75 | |
76 | |
77 | /** |
78 | * @define {number} The monitoring mode of the goog.Disposable |
79 | * instances. Default is OFF. Switching on the monitoring is only |
80 | * recommended for debugging because it has a significant impact on |
81 | * performance and memory usage. If switched off, the monitoring code |
82 | * compiles down to 0 bytes. |
83 | */ |
84 | goog.define('goog.Disposable.MONITORING_MODE', 0); |
85 | |
86 | |
87 | /** |
88 | * @define {boolean} Whether to attach creation stack to each created disposable |
89 | * instance; This is only relevant for when MonitoringMode != OFF. |
90 | */ |
91 | goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true); |
92 | |
93 | |
94 | /** |
95 | * Maps the unique ID of every undisposed {@code goog.Disposable} object to |
96 | * the object itself. |
97 | * @type {!Object.<number, !goog.Disposable>} |
98 | * @private |
99 | */ |
100 | goog.Disposable.instances_ = {}; |
101 | |
102 | |
103 | /** |
104 | * @return {!Array.<!goog.Disposable>} All {@code goog.Disposable} objects that |
105 | * haven't been disposed of. |
106 | */ |
107 | goog.Disposable.getUndisposedObjects = function() { |
108 | var ret = []; |
109 | for (var id in goog.Disposable.instances_) { |
110 | if (goog.Disposable.instances_.hasOwnProperty(id)) { |
111 | ret.push(goog.Disposable.instances_[Number(id)]); |
112 | } |
113 | } |
114 | return ret; |
115 | }; |
116 | |
117 | |
118 | /** |
119 | * Clears the registry of undisposed objects but doesn't dispose of them. |
120 | */ |
121 | goog.Disposable.clearUndisposedObjects = function() { |
122 | goog.Disposable.instances_ = {}; |
123 | }; |
124 | |
125 | |
126 | /** |
127 | * Whether the object has been disposed of. |
128 | * @type {boolean} |
129 | * @private |
130 | */ |
131 | goog.Disposable.prototype.disposed_ = false; |
132 | |
133 | |
134 | /** |
135 | * Callbacks to invoke when this object is disposed. |
136 | * @type {Array.<!Function>} |
137 | * @private |
138 | */ |
139 | goog.Disposable.prototype.onDisposeCallbacks_; |
140 | |
141 | |
142 | /** |
143 | * If monitoring the goog.Disposable instances is enabled, stores the creation |
144 | * stack trace of the Disposable instance. |
145 | * @const {string} |
146 | */ |
147 | goog.Disposable.prototype.creationStack; |
148 | |
149 | |
150 | /** |
151 | * @return {boolean} Whether the object has been disposed of. |
152 | * @override |
153 | */ |
154 | goog.Disposable.prototype.isDisposed = function() { |
155 | return this.disposed_; |
156 | }; |
157 | |
158 | |
159 | /** |
160 | * @return {boolean} Whether the object has been disposed of. |
161 | * @deprecated Use {@link #isDisposed} instead. |
162 | */ |
163 | goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed; |
164 | |
165 | |
166 | /** |
167 | * Disposes of the object. If the object hasn't already been disposed of, calls |
168 | * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should |
169 | * override {@link #disposeInternal} in order to delete references to COM |
170 | * objects, DOM nodes, and other disposable objects. Reentrant. |
171 | * |
172 | * @return {void} Nothing. |
173 | * @override |
174 | */ |
175 | goog.Disposable.prototype.dispose = function() { |
176 | if (!this.disposed_) { |
177 | // Set disposed_ to true first, in case during the chain of disposal this |
178 | // gets disposed recursively. |
179 | this.disposed_ = true; |
180 | this.disposeInternal(); |
181 | if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) { |
182 | var uid = goog.getUid(this); |
183 | if (goog.Disposable.MONITORING_MODE == |
184 | goog.Disposable.MonitoringMode.PERMANENT && |
185 | !goog.Disposable.instances_.hasOwnProperty(uid)) { |
186 | throw Error(this + ' did not call the goog.Disposable base ' + |
187 | 'constructor or was disposed of after a clearUndisposedObjects ' + |
188 | 'call'); |
189 | } |
190 | delete goog.Disposable.instances_[uid]; |
191 | } |
192 | } |
193 | }; |
194 | |
195 | |
196 | /** |
197 | * Associates a disposable object with this object so that they will be disposed |
198 | * together. |
199 | * @param {goog.disposable.IDisposable} disposable that will be disposed when |
200 | * this object is disposed. |
201 | */ |
202 | goog.Disposable.prototype.registerDisposable = function(disposable) { |
203 | this.addOnDisposeCallback(goog.partial(goog.dispose, disposable)); |
204 | }; |
205 | |
206 | |
207 | /** |
208 | * Invokes a callback function when this object is disposed. Callbacks are |
209 | * invoked in the order in which they were added. |
210 | * @param {function(this:T):?} callback The callback function. |
211 | * @param {T=} opt_scope An optional scope to call the callback in. |
212 | * @template T |
213 | */ |
214 | goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) { |
215 | if (!this.onDisposeCallbacks_) { |
216 | this.onDisposeCallbacks_ = []; |
217 | } |
218 | |
219 | this.onDisposeCallbacks_.push( |
220 | goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback); |
221 | }; |
222 | |
223 | |
224 | /** |
225 | * Deletes or nulls out any references to COM objects, DOM nodes, or other |
226 | * disposable objects. Classes that extend {@code goog.Disposable} should |
227 | * override this method. |
228 | * Not reentrant. To avoid calling it twice, it must only be called from the |
229 | * subclass' {@code disposeInternal} method. Everywhere else the public |
230 | * {@code dispose} method must be used. |
231 | * For example: |
232 | * <pre> |
233 | * mypackage.MyClass = function() { |
234 | * mypackage.MyClass.base(this, 'constructor'); |
235 | * // Constructor logic specific to MyClass. |
236 | * ... |
237 | * }; |
238 | * goog.inherits(mypackage.MyClass, goog.Disposable); |
239 | * |
240 | * mypackage.MyClass.prototype.disposeInternal = function() { |
241 | * // Dispose logic specific to MyClass. |
242 | * ... |
243 | * // Call superclass's disposeInternal at the end of the subclass's, like |
244 | * // in C++, to avoid hard-to-catch issues. |
245 | * mypackage.MyClass.base(this, 'disposeInternal'); |
246 | * }; |
247 | * </pre> |
248 | * @protected |
249 | */ |
250 | goog.Disposable.prototype.disposeInternal = function() { |
251 | if (this.onDisposeCallbacks_) { |
252 | while (this.onDisposeCallbacks_.length) { |
253 | this.onDisposeCallbacks_.shift()(); |
254 | } |
255 | } |
256 | }; |
257 | |
258 | |
259 | /** |
260 | * Returns True if we can verify the object is disposed. |
261 | * Calls {@code isDisposed} on the argument if it supports it. If obj |
262 | * is not an object with an isDisposed() method, return false. |
263 | * @param {*} obj The object to investigate. |
264 | * @return {boolean} True if we can verify the object is disposed. |
265 | */ |
266 | goog.Disposable.isDisposed = function(obj) { |
267 | if (obj && typeof obj.isDisposed == 'function') { |
268 | return obj.isDisposed(); |
269 | } |
270 | return false; |
271 | }; |
272 | |
273 | |
274 | /** |
275 | * Calls {@code dispose} on the argument if it supports it. If obj is not an |
276 | * object with a dispose() method, this is a no-op. |
277 | * @param {*} obj The object to dispose of. |
278 | */ |
279 | goog.dispose = function(obj) { |
280 | if (obj && typeof obj.dispose == 'function') { |
281 | obj.dispose(); |
282 | } |
283 | }; |
284 | |
285 | |
286 | /** |
287 | * Calls {@code dispose} on each member of the list that supports it. (If the |
288 | * member is an ArrayLike, then {@code goog.disposeAll()} will be called |
289 | * recursively on each of its members.) If the member is not an object with a |
290 | * {@code dispose()} method, then it is ignored. |
291 | * @param {...*} var_args The list. |
292 | */ |
293 | goog.disposeAll = function(var_args) { |
294 | for (var i = 0, len = arguments.length; i < len; ++i) { |
295 | var disposable = arguments[i]; |
296 | if (goog.isArrayLike(disposable)) { |
297 | goog.disposeAll.apply(null, disposable); |
298 | } else { |
299 | goog.dispose(disposable); |
300 | } |
301 | } |
302 | }; |