weak.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright 2014 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 Common code for weak collections.
  16. *
  17. * The helpers in this file are used for the shim implementations of
  18. * {@code goog.structs.weak.Map} and {@code goog.structs.weak.Set}, for browsers
  19. * that do not support ECMAScript 6 native WeakMap and WeakSet.
  20. *
  21. * IMPORTANT CAVEAT: On browsers that do not provide native WeakMap and WeakSet
  22. * implementations, these data structure are only partially weak, and CAN LEAK
  23. * MEMORY. Specifically, if a key is no longer held, the key-value pair (in the
  24. * case of Map) and some internal metadata (in the case of Set), can be garbage
  25. * collected; however, if a key is still held when a Map or Set is no longer
  26. * held, the value and metadata will not be collected.
  27. *
  28. * RECOMMENDATIONS: If the lifetime of the weak collection is expected to be
  29. * shorter than that of its keys, the keys should be explicitly removed from the
  30. * collection when they are disposed. If this is not possible, this library may
  31. * be inappopriate for the application.
  32. *
  33. * BROWSER COMPATIBILITY: This library is compatible with browsers with a
  34. * correct implementation of Object.defineProperty (IE9+, FF4+, SF5.1+, CH5+,
  35. * OP12+, etc).
  36. * @see goog.structs.weak.SUPPORTED_BROWSER
  37. * @see http://kangax.github.io/compat-table/es5/#Object.defineProperty
  38. *
  39. * @package
  40. */
  41. goog.provide('goog.structs.weak');
  42. goog.require('goog.userAgent');
  43. /**
  44. * Whether this browser supports weak collections, using either the native or
  45. * shim implementation.
  46. * @const {boolean}
  47. */
  48. // Only test for shim, since ES6 native WeakMap/Set imply ES5 shim dependencies
  49. goog.structs.weak.SUPPORTED_BROWSER = !!Object.defineProperty &&
  50. // IE<9 and Safari<5.1 cannot defineProperty on some objects
  51. !(goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) &&
  52. !(goog.userAgent.SAFARI && !goog.userAgent.isVersionOrHigher('534.48.3'));
  53. /**
  54. * Whether to use the browser's native WeakMap.
  55. * @const
  56. */
  57. goog.structs.weak.USE_NATIVE_WEAKMAP = 'WeakMap' in goog.global &&
  58. // Firefox<24 WeakMap disallows some objects as keys
  59. // See https://github.com/Polymer/WeakMap/issues/3
  60. !(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('24'));
  61. /**
  62. * Whether to use the browser's native WeakSet.
  63. * @const
  64. */
  65. goog.structs.weak.USE_NATIVE_WEAKSET = 'WeakSet' in goog.global;
  66. /** @const */
  67. goog.structs.weak.WEAKREFS_PROPERTY_NAME = '__shimWeakrefs__';
  68. /**
  69. * Counter used to generate unique ID for shim.
  70. * @private
  71. */
  72. goog.structs.weak.counter_ = 0;
  73. /**
  74. * Generate a unique ID for shim.
  75. * @return {string}
  76. */
  77. goog.structs.weak.generateId = function() {
  78. return (Math.random() * 1e9 >>> 0) + '' +
  79. (goog.structs.weak.counter_++ % 1e9);
  80. };
  81. /**
  82. * Checks that the key is an extensible object, otherwise throws an Error.
  83. * @param {*} key The key.
  84. */
  85. goog.structs.weak.checkKeyType = function(key) {
  86. if (!goog.isObject(key)) {
  87. throw TypeError('Invalid value used in weak collection');
  88. }
  89. if (Object.isExtensible && !Object.isExtensible(key)) {
  90. throw TypeError('Unsupported non-extensible object used as weak map key');
  91. }
  92. };
  93. /**
  94. * Adds a key-value pair to the collection with the given ID. Helper for shim
  95. * implementations of Map#set and Set#add.
  96. * @param {string} id The unique ID of the shim weak collection.
  97. * @param {*} key The key.
  98. * @param {*} value value to add.
  99. */
  100. goog.structs.weak.set = function(id, key, value) {
  101. goog.structs.weak.checkKeyType(key);
  102. if (!key.hasOwnProperty(goog.structs.weak.WEAKREFS_PROPERTY_NAME)) {
  103. // Use defineProperty to make property non-enumerable
  104. Object.defineProperty(
  105. /** @type {!Object} */ (key), goog.structs.weak.WEAKREFS_PROPERTY_NAME,
  106. {value: {}});
  107. }
  108. key[goog.structs.weak.WEAKREFS_PROPERTY_NAME][id] = value;
  109. };
  110. /**
  111. * Returns whether the collection with the given ID contains the given
  112. * key. Helper for shim implementations of Map#containsKey and Set#contains.
  113. * @param {string} id The unique ID of the shim weak collection.
  114. * @param {*} key The key to check for.
  115. * @return {boolean}
  116. */
  117. goog.structs.weak.has = function(id, key) {
  118. goog.structs.weak.checkKeyType(key);
  119. return key.hasOwnProperty(goog.structs.weak.WEAKREFS_PROPERTY_NAME) ?
  120. id in key[goog.structs.weak.WEAKREFS_PROPERTY_NAME] :
  121. false;
  122. };
  123. /**
  124. * Removes a key-value pair based on the key. Helper for shim implementations of
  125. * Map#remove and Set#remove.
  126. * @param {string} id The unique ID of the shim weak collection.
  127. * @param {*} key The key to remove.
  128. * @return {boolean} Whether object was removed.
  129. */
  130. goog.structs.weak.remove = function(id, key) {
  131. if (goog.structs.weak.has(id, key)) {
  132. delete key[goog.structs.weak.WEAKREFS_PROPERTY_NAME][id];
  133. return true;
  134. }
  135. return false;
  136. };