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 :
42 : #include <string.h>
43 : #include "jsapi.h"
44 : #include "jscntxt.h"
45 : #include "jsfriendapi.h"
46 : #include "jsgc.h"
47 : #include "jsobj.h"
48 : #include "jsgcmark.h"
49 : #include "jsweakmap.h"
50 :
51 : #include "vm/GlobalObject.h"
52 :
53 : #include "jsgcinlines.h"
54 : #include "jsobjinlines.h"
55 :
56 : #include "vm/MethodGuard-inl.h"
57 :
58 : using namespace js;
59 :
60 : namespace js {
61 :
62 : bool
63 102504 : WeakMapBase::markAllIteratively(JSTracer *tracer)
64 : {
65 102504 : bool markedAny = false;
66 102504 : JSRuntime *rt = tracer->runtime;
67 122276 : for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) {
68 19772 : if (m->markIteratively(tracer))
69 610 : markedAny = true;
70 : }
71 102504 : return markedAny;
72 : }
73 :
74 : void
75 51092 : WeakMapBase::sweepAll(JSTracer *tracer)
76 : {
77 51092 : JSRuntime *rt = tracer->runtime;
78 59820 : for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
79 8728 : m->sweep(tracer);
80 51092 : }
81 :
82 : void
83 1910 : WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
84 : {
85 1910 : JSRuntime *rt = tracer->runtime;
86 1911 : for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
87 1 : m->traceMappings(tracer);
88 1910 : }
89 :
90 : void
91 51092 : WeakMapBase::resetWeakMapList(JSRuntime *rt)
92 : {
93 : JS_ASSERT(WeakMapNotInList != NULL);
94 :
95 51092 : WeakMapBase *m = rt->gcWeakMapList;
96 51092 : rt->gcWeakMapList = NULL;
97 110912 : while (m) {
98 8728 : WeakMapBase *n = m->next;
99 8728 : m->next = WeakMapNotInList;
100 8728 : m = n;
101 : }
102 51092 : }
103 :
104 : bool
105 0 : WeakMapBase::saveWeakMapList(JSRuntime *rt, WeakMapVector &vector)
106 : {
107 0 : WeakMapBase *m = rt->gcWeakMapList;
108 0 : while (m) {
109 0 : if (!vector.append(m))
110 0 : return false;
111 0 : m = m->next;
112 : }
113 0 : return true;
114 : }
115 :
116 : void
117 0 : WeakMapBase::restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector)
118 : {
119 0 : JS_ASSERT(!rt->gcWeakMapList);
120 0 : for (WeakMapBase **p = vector.begin(); p != vector.end(); p++) {
121 0 : WeakMapBase *m = *p;
122 0 : JS_ASSERT(m->next == WeakMapNotInList);
123 0 : m->next = rt->gcWeakMapList;
124 0 : rt->gcWeakMapList = m;
125 : }
126 0 : }
127 :
128 : } /* namespace js */
129 :
130 : typedef WeakMap<HeapPtr<JSObject>, HeapValue> ObjectValueMap;
131 :
132 : static ObjectValueMap *
133 5705 : GetObjectMap(JSObject *obj)
134 : {
135 5705 : JS_ASSERT(obj->isWeakMap());
136 5705 : return (ObjectValueMap *)obj->getPrivate();
137 : }
138 :
139 : static JSObject *
140 1160 : GetKeyArg(JSContext *cx, CallArgs &args)
141 : {
142 1160 : Value *vp = &args[0];
143 1160 : if (vp->isPrimitive()) {
144 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
145 0 : return NULL;
146 : }
147 1160 : JSObject &key = vp->toObject();
148 :
149 : // If the key is from another compartment, and we store the wrapper as the key
150 : // the wrapper might be GC-ed since it is not strong referenced (Bug 673468).
151 : // To avoid this we always use the unwrapped object as the key instead of its
152 : // security wrapper. This also means that if the keys are ever exposed they must
153 : // be re-wrapped (see: JS_NondeterministicGetWeakMapKeys).
154 1160 : return JS_UnwrapObject(&key);
155 : }
156 :
157 : static JSBool
158 509 : WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
159 : {
160 509 : CallArgs args = CallArgsFromVp(argc, vp);
161 :
162 : bool ok;
163 509 : JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_has, &WeakMapClass, &ok);
164 509 : if (!obj)
165 63 : return ok;
166 :
167 446 : if (args.length() < 1) {
168 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
169 0 : "WeakMap.has", "0", "s");
170 0 : return false;
171 : }
172 446 : JSObject *key = GetKeyArg(cx, args);
173 446 : if (!key)
174 0 : return false;
175 :
176 446 : ObjectValueMap *map = GetObjectMap(obj);
177 446 : if (map) {
178 334 : ObjectValueMap::Ptr ptr = map->lookup(key);
179 334 : if (ptr) {
180 70 : args.rval() = BooleanValue(true);
181 70 : return true;
182 : }
183 : }
184 :
185 376 : args.rval() = BooleanValue(false);
186 376 : return true;
187 : }
188 :
189 : static JSBool
190 269 : WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
191 : {
192 269 : CallArgs args = CallArgsFromVp(argc, vp);
193 :
194 : bool ok;
195 269 : JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_get, &WeakMapClass, &ok);
196 269 : if (!obj)
197 63 : return ok;
198 :
199 206 : if (args.length() < 1) {
200 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
201 0 : "WeakMap.get", "0", "s");
202 0 : return false;
203 : }
204 206 : JSObject *key = GetKeyArg(cx, args);
205 206 : if (!key)
206 0 : return false;
207 :
208 206 : ObjectValueMap *map = GetObjectMap(obj);
209 206 : if (map) {
210 178 : ObjectValueMap::Ptr ptr = map->lookup(key);
211 178 : if (ptr) {
212 175 : args.rval() = ptr->value;
213 175 : return true;
214 : }
215 : }
216 :
217 31 : args.rval() = (args.length() > 1) ? args[1] : UndefinedValue();
218 31 : return true;
219 : }
220 :
221 : static JSBool
222 96 : WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
223 : {
224 96 : CallArgs args = CallArgsFromVp(argc, vp);
225 :
226 : bool ok;
227 96 : JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_delete, &WeakMapClass, &ok);
228 96 : if (!obj)
229 63 : return ok;
230 :
231 33 : if (args.length() < 1) {
232 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
233 0 : "WeakMap.delete", "0", "s");
234 0 : return false;
235 : }
236 33 : JSObject *key = GetKeyArg(cx, args);
237 33 : if (!key)
238 0 : return false;
239 :
240 33 : ObjectValueMap *map = GetObjectMap(obj);
241 33 : if (map) {
242 6 : ObjectValueMap::Ptr ptr = map->lookup(key);
243 6 : if (ptr) {
244 6 : map->remove(ptr);
245 6 : args.rval() = BooleanValue(true);
246 6 : return true;
247 : }
248 : }
249 :
250 27 : args.rval() = BooleanValue(false);
251 27 : return true;
252 : }
253 :
254 : static JSBool
255 538 : WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
256 : {
257 538 : CallArgs args = CallArgsFromVp(argc, vp);
258 :
259 : bool ok;
260 538 : JSObject *obj = NonGenericMethodGuard(cx, args, WeakMap_set, &WeakMapClass, &ok);
261 538 : if (!obj)
262 63 : return ok;
263 :
264 475 : if (args.length() < 1) {
265 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
266 0 : "WeakMap.set", "0", "s");
267 0 : return false;
268 : }
269 475 : JSObject *key = GetKeyArg(cx, args);
270 475 : if (!key)
271 0 : return false;
272 :
273 475 : Value value = (args.length() > 1) ? args[1] : UndefinedValue();
274 :
275 475 : ObjectValueMap *map = GetObjectMap(obj);
276 475 : if (!map) {
277 122 : map = cx->new_<ObjectValueMap>(cx, obj);
278 122 : if (!map->init()) {
279 0 : cx->delete_(map);
280 0 : goto out_of_memory;
281 : }
282 122 : obj->setPrivate(map);
283 : }
284 :
285 475 : if (!map->put(key, value))
286 0 : goto out_of_memory;
287 :
288 : // Preserve wrapped native keys to prevent wrapper optimization.
289 475 : if (key->getClass()->ext.isWrappedNative) {
290 0 : if (!cx->runtime->preserveWrapperCallback ||
291 0 : !cx->runtime->preserveWrapperCallback(cx, key)) {
292 0 : JS_ReportWarning(cx, "Failed to preserve wrapper of wrapped native weak map key.");
293 : }
294 : }
295 :
296 475 : args.rval().setUndefined();
297 475 : return true;
298 :
299 : out_of_memory:
300 0 : JS_ReportOutOfMemory(cx);
301 0 : return false;
302 : }
303 :
304 : JS_FRIEND_API(JSBool)
305 0 : JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret)
306 : {
307 0 : if (!obj || !obj->isWeakMap()) {
308 0 : *ret = NULL;
309 0 : return true;
310 : }
311 0 : JSObject *arr = NewDenseEmptyArray(cx);
312 0 : if (!arr)
313 0 : return false;
314 0 : ObjectValueMap *map = GetObjectMap(obj);
315 0 : if (map) {
316 0 : for (ObjectValueMap::Range r = map->nondeterministicAll(); !r.empty(); r.popFront()) {
317 0 : JSObject *key = r.front().key;
318 : // Re-wrapping the key (see comment of GetKeyArg)
319 0 : if (!JS_WrapObject(cx, &key))
320 0 : return false;
321 :
322 0 : if (!js_NewbornArrayPush(cx, arr, ObjectValue(*key)))
323 0 : return false;
324 : }
325 : }
326 0 : *ret = arr;
327 0 : return true;
328 : }
329 :
330 : static void
331 3387 : WeakMap_mark(JSTracer *trc, JSObject *obj)
332 : {
333 3387 : if (ObjectValueMap *map = GetObjectMap(obj))
334 22 : map->trace(trc);
335 3387 : }
336 :
337 : static void
338 1158 : WeakMap_finalize(JSContext *cx, JSObject *obj)
339 : {
340 1158 : if (ObjectValueMap *map = GetObjectMap(obj)) {
341 122 : map->check();
342 : #ifdef DEBUG
343 122 : map->~ObjectValueMap();
344 122 : memset(static_cast<void *>(map), 0xdc, sizeof(*map));
345 122 : cx->free_(map);
346 : #else
347 : cx->delete_(map);
348 : #endif
349 : }
350 1158 : }
351 :
352 : static JSBool
353 309 : WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
354 : {
355 309 : JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapClass);
356 309 : if (!obj)
357 0 : return false;
358 :
359 309 : vp->setObject(*obj);
360 309 : return true;
361 : }
362 :
363 : Class js::WeakMapClass = {
364 : "WeakMap",
365 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
366 : JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
367 : JS_PropertyStub, /* addProperty */
368 : JS_PropertyStub, /* delProperty */
369 : JS_PropertyStub, /* getProperty */
370 : JS_StrictPropertyStub, /* setProperty */
371 : JS_EnumerateStub,
372 : JS_ResolveStub,
373 : JS_ConvertStub,
374 : WeakMap_finalize,
375 : NULL, /* checkAccess */
376 : NULL, /* call */
377 : NULL, /* construct */
378 : NULL, /* xdrObject */
379 : WeakMap_mark
380 : };
381 :
382 : static JSFunctionSpec weak_map_methods[] = {
383 : JS_FN("has", WeakMap_has, 1, 0),
384 : JS_FN("get", WeakMap_get, 2, 0),
385 : JS_FN("delete", WeakMap_delete, 1, 0),
386 : JS_FN("set", WeakMap_set, 2, 0),
387 : JS_FS_END
388 : };
389 :
390 : JSObject *
391 850 : js_InitWeakMapClass(JSContext *cx, JSObject *obj)
392 : {
393 850 : JS_ASSERT(obj->isNative());
394 :
395 850 : GlobalObject *global = &obj->asGlobal();
396 :
397 850 : JSObject *weakMapProto = global->createBlankPrototype(cx, &WeakMapClass);
398 850 : if (!weakMapProto)
399 0 : return NULL;
400 :
401 : JSFunction *ctor = global->createConstructor(cx, WeakMap_construct, &WeakMapClass,
402 850 : CLASS_ATOM(cx, WeakMap), 0);
403 850 : if (!ctor)
404 0 : return NULL;
405 :
406 850 : if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto))
407 0 : return NULL;
408 :
409 850 : if (!DefinePropertiesAndBrand(cx, weakMapProto, NULL, weak_map_methods))
410 0 : return NULL;
411 :
412 850 : if (!DefineConstructorAndPrototype(cx, global, JSProto_WeakMap, ctor, weakMapProto))
413 0 : return NULL;
414 850 : return weakMapProto;
415 : }
|