js/xpconnect/tests/chrome/test_weakmaps.xul

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e4436261ee74
1 <?xml version="1.0"?>
2 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
3 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
4 <!--
5 https://bugzilla.mozilla.org/show_bug.cgi?id=668855
6 -->
7 <window title="Mozilla Bug "
8 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
9 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
10
11 <!-- test results are displayed in the html:body -->
12 <body xmlns="http://www.w3.org/1999/xhtml">
13 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id="
14 target="_blank">Mozilla Bug 668855</a>
15 </body>
16
17 <!-- test code goes here -->
18 <script type="application/javascript">
19 <![CDATA[
20 /** Test for Bug 668855 **/
21
22 let Cu = Components.utils;
23 let Ci = Components.interfaces;
24
25 /* Create a weak reference, with a single-element weak map. */
26 let make_weak_ref = function (obj) {
27 let m = new WeakMap;
28 m.set(obj, {});
29 return m;
30 };
31
32 /* Check to see if a weak reference is dead. */
33 let weak_ref_dead = function (r) {
34 return Cu.nondeterministicGetWeakMapKeys(r).length == 0;
35 }
36
37 /* Deterministically grab an arbitrary DOM element. */
38 let get_live_dom = function () {
39 let elems = document.getElementsByTagName("a");
40 return elems[0];
41 };
42
43
44 /* Test case from bug 653248, adapted into a standard test.
45
46 This is a dead cycle involving a DOM edge, so the cycle collector can free it. Keys and
47 values reachable only from XPConnect must be marked gray for this to work, and the cycle collector
48 must know the proper structure of the heap.
49
50 */
51 let make_gray_loop = function () {
52 let map = new WeakMap;
53 let div = document.createElement("div");
54 let key = {};
55 div.setUserData("entrain", {m:map, k:key}, null);
56 //div.entrain = {m:map, k:key}; This is not sufficient to cause a leak in Fx9
57 map.set(key, div);
58 return make_weak_ref(map);
59 };
60
61 let weakref = make_gray_loop();
62
63
64 /* Combinations of live and dead gray maps/keys. */
65 let basic_weak_ref = null;
66 let basic_map_weak_ref = null;
67 let black_map = new WeakMap;
68 let black_key = {};
69
70 let basic_unit_tests = function () {
71 let live_dom = get_live_dom();
72 let dead_dom = document.createElement("div");
73 let live_map = new WeakMap;
74 let dead_map = new WeakMap;
75 let live_key = {};
76 let dead_key = {};
77
78 // put the live/dead maps/keys into the appropriate DOM elements
79 live_dom.basic_unit_tests = {m:live_map, k:live_key};
80 dead_dom.setUserData("hook", {m:dead_map, k:dead_key}, null);
81 // dead_dom.hook = {m:dead_map, k:dead_key};
82
83 // Create a dead value, and a weak ref to it.
84 // The loop keeps dead_dom alive unless the CC is smart enough to kill it.
85 let dead_val = {loop:dead_dom};
86 basic_weak_ref = make_weak_ref(dead_val);
87 basic_map_weak_ref = make_weak_ref(dead_map);
88
89 // set up the actual entries. most will die.
90 live_map.set(live_key, {my_key:'live_live'});
91 live_map.set(dead_key, dead_val);
92 live_map.set(black_key, {my_key:'live_black'});
93
94 dead_map.set(live_key, dead_val);
95 dead_map.set(dead_key, dead_val);
96 dead_map.set(black_key, dead_val);
97
98 black_map.set(live_key, {my_key:'black_live'});
99 black_map.set(dead_key, dead_val);
100 black_map.set(black_key, {my_key:'black_black'});
101
102 };
103
104 basic_unit_tests();
105
106
107 let check_basic_unit = function () {
108 let live_dom = get_live_dom();
109 let live_map = live_dom.basic_unit_tests.m;
110 let live_key = live_dom.basic_unit_tests.k;
111
112 // check the dead elements
113 ok(weak_ref_dead(basic_weak_ref), "Dead value was kept alive.");
114 ok(weak_ref_dead(basic_map_weak_ref), "Dead map was kept alive.");
115
116 // check the live gray map
117 is(live_map.get(live_key).my_key, 'live_live',
118 "Live key should have the same value in live map.");
119 is(live_map.get(black_key).my_key, 'live_black',
120 "Black key should have the same value in live map.");
121 is(Cu.nondeterministicGetWeakMapKeys(live_map).length, 2,
122 "Live map should have two entries.");
123
124 // check the live black map
125 is(black_map.get(live_key).my_key, 'black_live',
126 "Live key should have the same value in black map.");
127 is(black_map.get(black_key).my_key, 'black_black',
128 "Black key should have the same value in black map.");
129 is(Cu.nondeterministicGetWeakMapKeys(black_map).length, 2,
130 "Black map should have two entries.");
131
132 };
133
134
135 /* live gray chained weak map entries, involving the cycle collector. */
136 let chainm = new WeakMap;
137 let num_chains = 5;
138
139 let nested_cc_maps = function () {
140 let dom = get_live_dom();
141 for(let i = 0; i < num_chains; i++) {
142 let k = {count:i};
143 dom.key = k;
144 dom0 = document.createElement("div");
145 chainm.set(k, {d:dom0});
146 dom = document.createElement("div");
147 dom0.appendChild(dom);
148 };
149 };
150
151 let check_nested_cc_maps = function () {
152 let dom = get_live_dom();
153 let all_ok = true;
154 for(let i = 0; i < num_chains; i++) {
155 let k = dom.key;
156 all_ok = all_ok && k.count == i;
157 dom = chainm.get(k).d.firstChild;
158 };
159 ok(all_ok, "Count was invalid on a key in chained weak map entries.");
160 };
161
162 nested_cc_maps();
163
164
165 /* black weak map, chained garbage cycle involving DOM */
166 let garbage_map = new WeakMap;
167
168 let chained_garbage_maps = function () {
169 let dom0 = document.createElement("div");
170 let dom = dom0;
171 for(let i = 0; i < num_chains; i++) {
172 let k = {};
173 dom.key = k;
174 let new_dom = document.createElement("div");
175 garbage_map.set(k, {val_child:new_dom});
176 dom = document.createElement("div");
177 new_dom.appendChild(dom);
178 };
179 // tie the knot
180 dom.appendChild(dom0);
181 };
182
183 chained_garbage_maps();
184
185
186 /* black weak map, chained garbage cycle involving DOM, XPCWN keys */
187 let wn_garbage_map = new WeakMap;
188
189 let wn_chained_garbage_maps = function () {
190 let dom0 = document.createElement("div");
191 let dom = dom0;
192 for(let i = 0; i < num_chains; i++) {
193 let new_dom = document.createElement("div");
194 wn_garbage_map.set(dom, {wn_val_child:new_dom});
195 dom = document.createElement("div");
196 new_dom.appendChild(dom);
197 };
198 // tie the knot
199 dom.appendChild(dom0);
200 };
201
202 wn_chained_garbage_maps();
203
204
205 /* The cycle collector shouldn't remove a live wrapped native key. */
206
207 let wn_live_map = new WeakMap;
208
209 let make_live_map = function () {
210 let live = get_live_dom();
211 wn_live_map.set(live, {});
212 ok(wn_live_map.has(get_live_dom()), "Live map should have live DOM node before GC.");
213 }
214
215 make_live_map();
216
217 let unpreservable_native_key = function () {
218 // We only allow natives that support wrapper preservation to be used as weak
219 // map keys. We should be able to try to add unpreservable natives as keys without
220 // crashing (bug 711616), but we should throw an error (bug 761620).
221
222 let dummy_test_map = new WeakMap;
223
224 let rule_fail = false;
225 let got_rule = false;
226 try {
227 var rule = document.styleSheets[0].cssRules[0];
228 got_rule = true;
229 dummy_test_map.set(rule, 1);
230 } catch (e) {
231 rule_fail = true;
232 }
233 ok(got_rule, "Got the CSS rule");
234 ok(rule_fail, "Using a CSS rule as a weak map key should produce an exception because it can't be wrapper preserved.");
235
236 }
237
238 unpreservable_native_key();
239
240 /* set up for running precise GC/CC then checking the results */
241
242 SimpleTest.waitForExplicitFinish();
243
244 Cu.schedulePreciseGC(function () {
245 SpecialPowers.DOMWindowUtils.cycleCollect();
246 SpecialPowers.DOMWindowUtils.garbageCollect();
247 SpecialPowers.DOMWindowUtils.garbageCollect();
248
249 ok(weak_ref_dead(weakref), "Garbage gray cycle should be collected.");
250
251 check_nested_cc_maps();
252
253 is(Cu.nondeterministicGetWeakMapKeys(garbage_map).length, 0, "Chained garbage weak map entries should not leak.");
254
255 check_basic_unit();
256
257 // fixed by Bug 680937
258 is(Cu.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0,
259 "Chained garbage WN weak map entries should not leak.");
260
261 // fixed by Bug 680937
262 is(Cu.nondeterministicGetWeakMapKeys(wn_live_map).length, 1,
263 "Live weak map wrapped native key should not be removed.");
264
265 ok(wn_live_map.has(get_live_dom()), "Live map should have live dom.");
266
267 SimpleTest.finish();
268 });
269
270 ]]>
271 </script>
272 </window>

mercurial