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