/js/src/jsweakmap.h

http://github.com/zpao/v8monkey · C Header · 350 lines · 183 code · 39 blank · 128 comment · 17 complexity · 968c38eff5daf3b9a6cce022fce110e9 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=4 sw=4 et tw=99:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
  18. * May 28, 2008.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Mozilla Foundation
  22. * Portions created by the Initial Developer are Copyright (C) 2009
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. * Andreas Gal <gal@mozilla.com>
  27. *
  28. * Alternatively, the contents of this file may be used under the terms of
  29. * either of the GNU General Public License Version 2 or later (the "GPL"),
  30. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31. * in which case the provisions of the GPL or the LGPL are applicable instead
  32. * of those above. If you wish to allow use of your version of this file only
  33. * under the terms of either the GPL or the LGPL, and not to allow others to
  34. * use your version of this file under the terms of the MPL, indicate your
  35. * decision by deleting the provisions above and replace them with the notice
  36. * and other provisions required by the GPL or the LGPL. If you do not delete
  37. * the provisions above, a recipient may use your version of this file under
  38. * the terms of any one of the MPL, the GPL or the LGPL.
  39. *
  40. * ***** END LICENSE BLOCK ***** */
  41. #ifndef jsweakmap_h___
  42. #define jsweakmap_h___
  43. #include "jsapi.h"
  44. #include "jsfriendapi.h"
  45. #include "jscntxt.h"
  46. #include "jsobj.h"
  47. #include "jsgcmark.h"
  48. #include "js/HashTable.h"
  49. namespace js {
  50. // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
  51. // a key is collected, the table entry disappears, dropping its reference to the value.
  52. //
  53. // More precisely:
  54. //
  55. // A WeakMap entry is collected if and only if either the WeakMap or the entry's key
  56. // is collected. If an entry is not collected, it remains in the WeakMap and it has a
  57. // strong reference to the value.
  58. //
  59. // You must call this table's 'mark' method when the object of which it is a part is
  60. // reached by the garbage collection tracer. Once a table is known to be live, the
  61. // implementation takes care of the iterative marking needed for weak tables and removing
  62. // table entries when collection is complete.
  63. //
  64. // You may provide your own MarkPolicy class to specify how keys and values are marked; a
  65. // policy template provides default definitions for some common key/value type
  66. // combinations.
  67. //
  68. // Details:
  69. //
  70. // The interface is as for a js::HashMap, with the following additions:
  71. //
  72. // - You must call the WeakMap's 'trace' member function when you discover that the map is
  73. // part of a live object. (You'll typically call this from the containing type's 'trace'
  74. // function.)
  75. //
  76. // - There is no AllocPolicy parameter; these are used with our garbage collector, so
  77. // RuntimeAllocPolicy is hard-wired.
  78. //
  79. // - Optional fourth and fifth parameters are the MarkPolicies for the key and value type.
  80. // A MarkPolicy has the constructor:
  81. //
  82. // MarkPolicy(JSTracer *)
  83. //
  84. // and the following member functions:
  85. //
  86. // bool isMarked(const Type &x)
  87. // Return true if x has been marked as live by the garbage collector.
  88. //
  89. // bool mark(const Type &x)
  90. // Return false if x is already marked. Otherwise, mark x and return true.
  91. //
  92. // If omitted, the MarkPolicy parameter defaults to js::DefaultMarkPolicy<Type>,
  93. // a policy template with the obvious definitions for some typical
  94. // SpiderMonkey type combinations.
  95. // A policy template holding default marking algorithms for common type combinations. This
  96. // provides default types for WeakMap's MarkPolicy template parameter.
  97. template <class Type> class DefaultMarkPolicy;
  98. // A policy template holding default tracing algorithms for common type combinations. This
  99. // provides default types for WeakMap's TracePolicy template parameter.
  100. template <class Key, class Value> class DefaultTracePolicy;
  101. // The value for the next pointer for maps not in the map list.
  102. static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
  103. // Common base class for all WeakMap specializations. The collector uses this to call
  104. // their markIteratively and sweep methods.
  105. class WeakMapBase {
  106. public:
  107. WeakMapBase(JSObject *memOf) : memberOf(memOf), next(WeakMapNotInList) { }
  108. virtual ~WeakMapBase() { }
  109. void trace(JSTracer *tracer) {
  110. if (IS_GC_MARKING_TRACER(tracer)) {
  111. // We don't do anything with a WeakMap at trace time. Rather, we wait until as
  112. // many keys as possible have been marked, and add ourselves to the list of
  113. // known-live WeakMaps to be scanned in the iterative marking phase, by
  114. // markAllIteratively.
  115. JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
  116. // Add ourselves to the list if we are not already in the list. We can already
  117. // be in the list if the weak map is marked more than once due delayed marking.
  118. if (next == WeakMapNotInList) {
  119. JSRuntime *rt = tracer->context->runtime;
  120. next = rt->gcWeakMapList;
  121. rt->gcWeakMapList = this;
  122. }
  123. } else {
  124. // If we're not actually doing garbage collection, the keys won't be marked
  125. // nicely as needed by the true ephemeral marking algorithm --- custom tracers
  126. // such as the cycle collector must use their own means for cycle detection.
  127. // So here we do a conservative approximation: pretend all keys are live.
  128. if (tracer->eagerlyTraceWeakMaps)
  129. nonMarkingTrace(tracer);
  130. }
  131. }
  132. // Garbage collector entry points.
  133. // Check all weak maps that have been marked as live so far in this garbage
  134. // collection, and mark the values of all entries that have become strong references
  135. // to them. Return true if we marked any new values, indicating that we need to make
  136. // another pass. In other words, mark my marked maps' marked members' mid-collection.
  137. static bool markAllIteratively(JSTracer *tracer);
  138. // Remove entries whose keys are dead from all weak maps marked as live in this
  139. // garbage collection.
  140. static void sweepAll(JSTracer *tracer);
  141. // Trace all delayed weak map bindings. Used by the cycle collector.
  142. static void traceAllMappings(WeakMapTracer *tracer);
  143. // Remove everything from the live weak map list.
  144. static void resetWeakMapList(JSRuntime *rt);
  145. protected:
  146. // Instance member functions called by the above. Instantiations of WeakMap override
  147. // these with definitions appropriate for their Key and Value types.
  148. virtual void nonMarkingTrace(JSTracer *tracer) = 0;
  149. virtual bool markIteratively(JSTracer *tracer) = 0;
  150. virtual void sweep(JSTracer *tracer) = 0;
  151. virtual void traceMappings(WeakMapTracer *tracer) = 0;
  152. // Object that this weak map is part of, if any.
  153. JSObject *memberOf;
  154. private:
  155. // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
  156. // collection, headed by JSRuntime::gcWeakMapList. The last element of the list
  157. // has NULL as its next. Maps not in the list have WeakMapNotInList as their
  158. // next. We must distinguish these cases to avoid creating infinite lists
  159. // when a weak map gets traced twice due to delayed marking.
  160. WeakMapBase *next;
  161. };
  162. template <class Key, class Value,
  163. class HashPolicy = DefaultHasher<Key>,
  164. class KeyMarkPolicy = DefaultMarkPolicy<Key>,
  165. class ValueMarkPolicy = DefaultMarkPolicy<Value>,
  166. class TracePolicy = DefaultTracePolicy<Key, Value> >
  167. class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase {
  168. private:
  169. typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
  170. typedef typename Base::Enum Enum;
  171. public:
  172. typedef typename Base::Range Range;
  173. explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { }
  174. explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { }
  175. // Use with caution, as result can be affected by garbage collection.
  176. Range nondeterministicAll() {
  177. return Base::all();
  178. }
  179. private:
  180. void nonMarkingTrace(JSTracer *trc) {
  181. ValueMarkPolicy vp(trc);
  182. for (Range r = Base::all(); !r.empty(); r.popFront())
  183. vp.mark(r.front().value);
  184. }
  185. bool markIteratively(JSTracer *trc) {
  186. KeyMarkPolicy kp(trc);
  187. ValueMarkPolicy vp(trc);
  188. bool markedAny = false;
  189. for (Range r = Base::all(); !r.empty(); r.popFront()) {
  190. const Key &k = r.front().key;
  191. const Value &v = r.front().value;
  192. /* If the entry is live, ensure its key and value are marked. */
  193. if (kp.isMarked(k)) {
  194. markedAny |= vp.mark(v);
  195. }
  196. JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v));
  197. }
  198. return markedAny;
  199. }
  200. void sweep(JSTracer *trc) {
  201. KeyMarkPolicy kp(trc);
  202. /* Remove all entries whose keys remain unmarked. */
  203. for (Enum e(*this); !e.empty(); e.popFront()) {
  204. if (!kp.isMarked(e.front().key))
  205. e.removeFront();
  206. }
  207. #if DEBUG
  208. ValueMarkPolicy vp(trc);
  209. /*
  210. * Once we've swept, all remaining edges should stay within the
  211. * known-live part of the graph.
  212. */
  213. for (Range r = Base::all(); !r.empty(); r.popFront()) {
  214. JS_ASSERT(kp.isMarked(r.front().key));
  215. JS_ASSERT(vp.isMarked(r.front().value));
  216. }
  217. #endif
  218. }
  219. // mapObj can be NULL, which means that the map is not part of a JSObject.
  220. void traceMappings(WeakMapTracer *tracer) {
  221. TracePolicy t(tracer);
  222. for (Range r = Base::all(); !r.empty(); r.popFront())
  223. t.traceMapping(memberOf, r.front().key, r.front().value);
  224. }
  225. };
  226. template <>
  227. class DefaultMarkPolicy<HeapValue> {
  228. private:
  229. JSTracer *tracer;
  230. public:
  231. DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
  232. bool isMarked(const HeapValue &x) {
  233. if (x.isMarkable())
  234. return !IsAboutToBeFinalized(x);
  235. return true;
  236. }
  237. bool mark(const HeapValue &x) {
  238. if (isMarked(x))
  239. return false;
  240. js::gc::MarkValue(tracer, x, "WeakMap entry");
  241. return true;
  242. }
  243. };
  244. template <>
  245. class DefaultMarkPolicy<HeapPtrObject> {
  246. private:
  247. JSTracer *tracer;
  248. public:
  249. DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
  250. bool isMarked(const HeapPtrObject &x) {
  251. return !IsAboutToBeFinalized(x);
  252. }
  253. bool mark(const HeapPtrObject &x) {
  254. if (isMarked(x))
  255. return false;
  256. js::gc::MarkObject(tracer, x, "WeakMap entry");
  257. return true;
  258. }
  259. };
  260. template <>
  261. class DefaultMarkPolicy<HeapPtrScript> {
  262. private:
  263. JSTracer *tracer;
  264. public:
  265. DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
  266. bool isMarked(const HeapPtrScript &x) {
  267. return !IsAboutToBeFinalized(x);
  268. }
  269. bool mark(const HeapPtrScript &x) {
  270. if (isMarked(x))
  271. return false;
  272. js::gc::MarkScript(tracer, x, "WeakMap entry");
  273. return true;
  274. }
  275. };
  276. // Default trace policies
  277. template <>
  278. class DefaultTracePolicy<HeapPtrObject, HeapValue> {
  279. private:
  280. WeakMapTracer *tracer;
  281. public:
  282. DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
  283. void traceMapping(JSObject *m, const HeapPtr<JSObject> &k, HeapValue &v) {
  284. if (v.isMarkable())
  285. tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind());
  286. }
  287. };
  288. template <>
  289. class DefaultTracePolicy<HeapPtrObject, HeapPtrObject> {
  290. private:
  291. WeakMapTracer *tracer;
  292. public:
  293. DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
  294. void traceMapping(JSObject *m, const HeapPtrObject &k, const HeapPtrObject &v) {
  295. tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT);
  296. }
  297. };
  298. template <>
  299. class DefaultTracePolicy<HeapPtrScript, HeapPtrObject> {
  300. private:
  301. WeakMapTracer *tracer;
  302. public:
  303. DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
  304. void traceMapping(JSObject *m, const HeapPtrScript &k, const HeapPtrObject &v) {
  305. tracer->callback(tracer, m, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT);
  306. }
  307. };
  308. }
  309. extern JSObject *
  310. js_InitWeakMapClass(JSContext *cx, JSObject *obj);
  311. #endif