# lib/goog/disposable/disposable.js

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 | };