# 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 at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// 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 and13// limitations under the License.1415/**16 * @fileoverview Implements the disposable interface. The dispose method is used17 * to clean up references and resources.18 * @author arv@google.com (Erik Arvidsson)19 */202122goog.provide('goog.Disposable');23/** @suppress {extraProvide} */24goog.provide('goog.dispose');25/** @suppress {extraProvide} */26goog.provide('goog.disposeAll');2728goog.require('goog.disposable.IDisposable');29303132/**33 * Class that provides the basic implementation for disposable objects. If your34 * class holds one or more references to COM objects, DOM nodes, or other35 * disposable objects, it should extend this class or implement the disposable36 * interface (defined in goog.disposable.IDisposable).37 * @constructor38 * @implements {goog.disposable.IDisposable}39 */40goog.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 sealing48  this.disposed_ = this.disposed_;49  this.onDisposeCallbacks_ = this.onDisposeCallbacks_;50};515253/**54 * @enum {number} Different monitoring modes for Disposable.55 */56goog.Disposable.MonitoringMode = {57  /**58   * No monitoring.59   */60  OFF: 0,61  /**62   * Creating and disposing the goog.Disposable instances is monitored. All63   * disposable objects need to call the {@code goog.Disposable} base64   * constructor. The PERMANENT mode must be switched on before creating any65   * goog.Disposable instances.66   */67  PERMANENT: 1,68  /**69   * INTERACTIVE mode can be switched on and off on the fly without producing70   * errors. It also doesn't warn if the disposable objects don't call the71   * {@code goog.Disposable} base constructor.72   */73  INTERACTIVE: 274};757677/**78 * @define {number} The monitoring mode of the goog.Disposable79 *     instances. Default is OFF. Switching on the monitoring is only80 *     recommended for debugging because it has a significant impact on81 *     performance and memory usage. If switched off, the monitoring code82 *     compiles down to 0 bytes.83 */84goog.define('goog.Disposable.MONITORING_MODE', 0);858687/**88 * @define {boolean} Whether to attach creation stack to each created disposable89 *     instance; This is only relevant for when MonitoringMode != OFF.90 */91goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);929394/**95 * Maps the unique ID of every undisposed {@code goog.Disposable} object to96 * the object itself.97 * @type {!Object.<number, !goog.Disposable>}98 * @private99 */100goog.Disposable.instances_ = {};101102103/**104 * @return {!Array.<!goog.Disposable>} All {@code goog.Disposable} objects that105 *     haven't been disposed of.106 */107goog.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};116117118/**119 * Clears the registry of undisposed objects but doesn't dispose of them.120 */121goog.Disposable.clearUndisposedObjects = function() {122  goog.Disposable.instances_ = {};123};124125126/**127 * Whether the object has been disposed of.128 * @type {boolean}129 * @private130 */131goog.Disposable.prototype.disposed_ = false;132133134/**135 * Callbacks to invoke when this object is disposed.136 * @type {Array.<!Function>}137 * @private138 */139goog.Disposable.prototype.onDisposeCallbacks_;140141142/**143 * If monitoring the goog.Disposable instances is enabled, stores the creation144 * stack trace of the Disposable instance.145 * @const {string}146 */147goog.Disposable.prototype.creationStack;148149150/**151 * @return {boolean} Whether the object has been disposed of.152 * @override153 */154goog.Disposable.prototype.isDisposed = function() {155  return this.disposed_;156};157158159/**160 * @return {boolean} Whether the object has been disposed of.161 * @deprecated Use {@link #isDisposed} instead.162 */163goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;164165166/**167 * Disposes of the object. If the object hasn't already been disposed of, calls168 * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should169 * override {@link #disposeInternal} in order to delete references to COM170 * objects, DOM nodes, and other disposable objects. Reentrant.171 *172 * @return {void} Nothing.173 * @override174 */175goog.Disposable.prototype.dispose = function() {176  if (!this.disposed_) {177    // Set disposed_ to true first, in case during the chain of disposal this178    // 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};194195196/**197 * Associates a disposable object with this object so that they will be disposed198 * together.199 * @param {goog.disposable.IDisposable} disposable that will be disposed when200 *     this object is disposed.201 */202goog.Disposable.prototype.registerDisposable = function(disposable) {203  this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));204};205206207/**208 * Invokes a callback function when this object is disposed. Callbacks are209 * 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 T213 */214goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {215  if (!this.onDisposeCallbacks_) {216    this.onDisposeCallbacks_ = [];217  }218219  this.onDisposeCallbacks_.push(220      goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);221};222223224/**225 * Deletes or nulls out any references to COM objects, DOM nodes, or other226 * disposable objects. Classes that extend {@code goog.Disposable} should227 * override this method.228 * Not reentrant. To avoid calling it twice, it must only be called from the229 * subclass' {@code disposeInternal} method. Everywhere else the public230 * {@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, like244 *     // in C++, to avoid hard-to-catch issues.245 *     mypackage.MyClass.base(this, 'disposeInternal');246 *   };247 * </pre>248 * @protected249 */250goog.Disposable.prototype.disposeInternal = function() {251  if (this.onDisposeCallbacks_) {252    while (this.onDisposeCallbacks_.length) {253      this.onDisposeCallbacks_.shift()();254    }255  }256};257258259/**260 * Returns True if we can verify the object is disposed.261 * Calls {@code isDisposed} on the argument if it supports it.  If obj262 * 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 */266goog.Disposable.isDisposed = function(obj) {267  if (obj && typeof obj.isDisposed == 'function') {268    return obj.isDisposed();269  }270  return false;271};272273274/**275 * Calls {@code dispose} on the argument if it supports it. If obj is not an276 *     object with a dispose() method, this is a no-op.277 * @param {*} obj The object to dispose of.278 */279goog.dispose = function(obj) {280  if (obj && typeof obj.dispose == 'function') {281    obj.dispose();282  }283};284285286/**287 * Calls {@code dispose} on each member of the list that supports it. (If the288 * member is an ArrayLike, then {@code goog.disposeAll()} will be called289 * recursively on each of its members.) If the member is not an object with a290 * {@code dispose()} method, then it is ignored.291 * @param {...*} var_args The list.292 */293goog.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};