disposable.js 9.0 KB

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