Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / js / src / jsapi-tests / testWeakMap.cpp
blob95a291a8a0af4d0f1f9ee9fdfc6941eb7d064859
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "gc/Zone.h"
9 #include "js/Array.h" // JS::GetArrayLength
10 #include "js/GlobalObject.h" // JS_NewGlobalObject
11 #include "js/PropertyAndElement.h" // JS_DefineProperty
12 #include "js/WeakMap.h"
13 #include "jsapi-tests/tests.h"
14 #include "vm/Realm.h"
16 using namespace js;
18 JSObject* keyDelegate = nullptr;
20 BEGIN_TEST(testWeakMap_basicOperations) {
21 JS::RootedObject map(cx, JS::NewWeakMapObject(cx));
22 CHECK(IsWeakMapObject(map));
24 JS::RootedObject key(cx, newKey());
25 CHECK(key);
26 CHECK(!IsWeakMapObject(key));
28 JS::RootedValue r(cx);
29 CHECK(GetWeakMapEntry(cx, map, key, &r));
30 CHECK(r.isUndefined());
32 CHECK(checkSize(map, 0));
34 JS::RootedValue val(cx, JS::Int32Value(1));
35 CHECK(SetWeakMapEntry(cx, map, key, val));
37 CHECK(GetWeakMapEntry(cx, map, key, &r));
38 CHECK(r == val);
39 CHECK(checkSize(map, 1));
41 JS_GC(cx);
43 CHECK(GetWeakMapEntry(cx, map, key, &r));
44 CHECK(r == val);
45 CHECK(checkSize(map, 1));
47 key = nullptr;
48 JS_GC(cx);
50 CHECK(checkSize(map, 0));
52 return true;
55 JSObject* newKey() { return JS_NewPlainObject(cx); }
57 bool checkSize(JS::HandleObject map, uint32_t expected) {
58 JS::RootedObject keys(cx);
59 CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
61 uint32_t length;
62 CHECK(JS::GetArrayLength(cx, keys, &length));
63 CHECK(length == expected);
65 return true;
67 END_TEST(testWeakMap_basicOperations)
69 BEGIN_TEST(testWeakMap_keyDelegates) {
70 AutoLeaveZeal nozeal(cx);
72 AutoGCParameter param(cx, JSGC_INCREMENTAL_GC_ENABLED, true);
73 JS_GC(cx);
74 JS::RootedObject map(cx, JS::NewWeakMapObject(cx));
75 CHECK(map);
77 JS::RootedObject delegate(cx, newDelegate());
78 JS::RootedObject key(cx, delegate);
79 if (!JS_WrapObject(cx, &key)) {
80 return false;
82 CHECK(key);
83 CHECK(delegate);
85 keyDelegate = delegate;
87 JS::RootedObject delegateRoot(cx);
89 JSAutoRealm ar(cx, delegate);
90 delegateRoot = JS_NewPlainObject(cx);
91 CHECK(delegateRoot);
92 JS::RootedValue delegateValue(cx, JS::ObjectValue(*delegate));
93 CHECK(JS_DefineProperty(cx, delegateRoot, "delegate", delegateValue, 0));
95 delegate = nullptr;
98 * Perform an incremental GC, introducing an unmarked CCW to force the map
99 * zone to finish marking before the delegate zone.
101 CHECK(newCCW(map, delegateRoot));
102 performIncrementalGC();
103 #ifdef DEBUG
104 CHECK(map->zone()->lastSweepGroupIndex() <
105 delegateRoot->zone()->lastSweepGroupIndex());
106 #endif
108 /* Add our entry to the weakmap. */
109 JS::RootedValue val(cx, JS::Int32Value(1));
110 CHECK(SetWeakMapEntry(cx, map, key, val));
111 CHECK(checkSize(map, 1));
114 * Check the delegate keeps the entry alive even if the key is not reachable.
116 key = nullptr;
117 CHECK(newCCW(map, delegateRoot));
118 performIncrementalGC();
119 CHECK(checkSize(map, 1));
122 * Check that the zones finished marking at the same time, which is
123 * necessary because of the presence of the delegate and the CCW.
125 #ifdef DEBUG
126 CHECK(map->zone()->lastSweepGroupIndex() ==
127 delegateRoot->zone()->lastSweepGroupIndex());
128 #endif
130 /* Check that when the delegate becomes unreachable the entry is removed. */
131 delegateRoot = nullptr;
132 keyDelegate = nullptr;
133 JS_GC(cx);
134 CHECK(checkSize(map, 0));
136 return true;
139 static size_t DelegateObjectMoved(JSObject* obj, JSObject* old) {
140 if (!keyDelegate) {
141 return 0; // Object got moved before we set keyDelegate to point to it.
144 MOZ_RELEASE_ASSERT(keyDelegate == old);
145 keyDelegate = obj;
146 return 0;
149 JSObject* newKey() {
150 static const JSClass keyClass = {
151 "keyWithDelegate", JSCLASS_HAS_RESERVED_SLOTS(1),
152 JS_NULL_CLASS_OPS, JS_NULL_CLASS_SPEC,
153 JS_NULL_CLASS_EXT, JS_NULL_OBJECT_OPS};
155 JS::RootedObject key(cx, JS_NewObject(cx, &keyClass));
156 if (!key) {
157 return nullptr;
160 return key;
163 JSObject* newCCW(JS::HandleObject sourceZone, JS::HandleObject destZone) {
165 * Now ensure that this zone will be swept first by adding a cross
166 * compartment wrapper to a new object in the same zone as the
167 * delegate object.
169 JS::RootedObject object(cx);
171 JSAutoRealm ar(cx, destZone);
172 object = JS_NewPlainObject(cx);
173 if (!object) {
174 return nullptr;
178 JSAutoRealm ar(cx, sourceZone);
179 if (!JS_WrapObject(cx, &object)) {
180 return nullptr;
184 // In order to test the SCC algorithm, we need the wrapper/wrappee to be
185 // tenured.
186 cx->runtime()->gc.evictNursery();
188 return object;
191 JSObject* newDelegate() {
192 static const JSClassOps delegateClassOps = {
193 nullptr, // addProperty
194 nullptr, // delProperty
195 nullptr, // enumerate
196 nullptr, // newEnumerate
197 nullptr, // resolve
198 nullptr, // mayResolve
199 nullptr, // finalize
200 nullptr, // call
201 nullptr, // construct
202 JS_GlobalObjectTraceHook, // trace
205 static const js::ClassExtension delegateClassExtension = {
206 DelegateObjectMoved, // objectMovedOp
209 static const JSClass delegateClass = {
210 "delegate",
211 JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
212 &delegateClassOps,
213 JS_NULL_CLASS_SPEC,
214 &delegateClassExtension,
215 JS_NULL_OBJECT_OPS};
217 /* Create the global object. */
218 JS::RealmOptions options;
219 JS::RootedObject global(cx,
220 JS_NewGlobalObject(cx, &delegateClass, nullptr,
221 JS::FireOnNewGlobalHook, options));
222 if (!global) {
223 return nullptr;
226 JS_SetReservedSlot(global, 0, JS::Int32Value(42));
227 return global;
230 bool checkSize(JS::HandleObject map, uint32_t expected) {
231 JS::RootedObject keys(cx);
232 CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
234 uint32_t length;
235 CHECK(JS::GetArrayLength(cx, keys, &length));
236 CHECK(length == expected);
238 return true;
241 void performIncrementalGC() {
242 JSRuntime* rt = cx->runtime();
243 js::SliceBudget budget(js::WorkBudget(1000));
244 rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
246 // Wait until we've started marking before finishing the GC
247 // non-incrementally.
248 while (rt->gc.state() == gc::State::Prepare) {
249 rt->gc.debugGCSlice(budget);
251 if (JS::IsIncrementalGCInProgress(cx)) {
252 rt->gc.finishGC(JS::GCReason::DEBUG_GC);
255 END_TEST(testWeakMap_keyDelegates)