1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
* vim: set ts=4 sw=4 et tw=99:
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/. */
11
#include "jsfriendapi.h"
14
#include "jsweakmap.h"
16
#include "gc/Marking.h"
17
#include "vm/GlobalObject.h"
19
#include "jsgcinlines.h"
20
#include "jsobjinlines.h"
27
WeakMapBase::markAllIteratively(JSTracer *tracer)
29
bool markedAny = false;
30
JSRuntime *rt = tracer->runtime;
31
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) {
32
if (m->markIteratively(tracer))
39
WeakMapBase::sweepAll(JSTracer *tracer)
41
JSRuntime *rt = tracer->runtime;
42
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
47
WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
49
JSRuntime *rt = tracer->runtime;
50
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
51
m->traceMappings(tracer);
55
WeakMapBase::resetWeakMapList(JSRuntime *rt)
57
JS_ASSERT(WeakMapNotInList != NULL);
59
WeakMapBase *m = rt->gcWeakMapList;
60
rt->gcWeakMapList = NULL;
62
WeakMapBase *n = m->next;
63
m->next = WeakMapNotInList;
69
WeakMapBase::saveWeakMapList(JSRuntime *rt, WeakMapVector &vector)
71
WeakMapBase *m = rt->gcWeakMapList;
73
if (!vector.append(m))
81
WeakMapBase::restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector)
83
JS_ASSERT(!rt->gcWeakMapList);
84
for (WeakMapBase **p = vector.begin(); p != vector.end(); p++) {
86
JS_ASSERT(m->next == WeakMapNotInList);
87
m->next = rt->gcWeakMapList;
88
rt->gcWeakMapList = m;
94
typedef WeakMap<EncapsulatedPtrObject, RelocatableValue> ObjectValueMap;
96
static ObjectValueMap *
97
GetObjectMap(JSObject *obj)
99
JS_ASSERT(obj->isWeakMap());
100
return (ObjectValueMap *)obj->getPrivate();
104
GetKeyArg(JSContext *cx, CallArgs &args)
106
Value *vp = &args[0];
107
if (vp->isPrimitive()) {
108
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
111
return &vp->toObject();
114
JS_ALWAYS_INLINE bool
115
IsWeakMap(const Value &v)
117
return v.isObject() && v.toObject().hasClass(&WeakMapClass);
120
JS_ALWAYS_INLINE bool
121
WeakMap_has_impl(JSContext *cx, CallArgs args)
123
JS_ASSERT(IsWeakMap(args.thisv()));
125
if (args.length() < 1) {
126
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
127
"WeakMap.has", "0", "s");
130
JSObject *key = GetKeyArg(cx, args);
134
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
136
args.rval().setBoolean(true);
141
args.rval().setBoolean(false);
146
WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
148
CallArgs args = CallArgsFromVp(argc, vp);
149
return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
152
JS_ALWAYS_INLINE bool
153
WeakMap_get_impl(JSContext *cx, CallArgs args)
155
JS_ASSERT(IsWeakMap(args.thisv()));
157
if (args.length() < 1) {
158
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
159
"WeakMap.get", "0", "s");
162
JSObject *key = GetKeyArg(cx, args);
166
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
167
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
168
// Read barrier to prevent an incorrectly gray value from escaping the
169
// weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
170
ExposeValueToActiveJS(ptr->value.get());
172
args.rval().set(ptr->value);
177
args.rval().set((args.length() > 1) ? args[1] : UndefinedValue());
182
WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
184
CallArgs args = CallArgsFromVp(argc, vp);
185
return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
188
JS_ALWAYS_INLINE bool
189
WeakMap_delete_impl(JSContext *cx, CallArgs args)
191
JS_ASSERT(IsWeakMap(args.thisv()));
193
if (args.length() < 1) {
194
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
195
"WeakMap.delete", "0", "s");
198
JSObject *key = GetKeyArg(cx, args);
202
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
203
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
205
args.rval().setBoolean(true);
210
args.rval().setBoolean(false);
215
WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
217
CallArgs args = CallArgsFromVp(argc, vp);
218
return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
221
JS_ALWAYS_INLINE bool
222
WeakMap_set_impl(JSContext *cx, CallArgs args)
224
JS_ASSERT(IsWeakMap(args.thisv()));
226
if (args.length() < 1) {
227
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
228
"WeakMap.set", "0", "s");
231
RootedObject key(cx, GetKeyArg(cx, args));
235
Value value = (args.length() > 1) ? args[1] : UndefinedValue();
237
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
238
ObjectValueMap *map = GetObjectMap(thisObj);
240
map = cx->new_<ObjectValueMap>(cx, thisObj.get());
243
JS_ReportOutOfMemory(cx);
246
thisObj->setPrivate(map);
249
// Preserve wrapped native keys to prevent wrapper optimization.
250
if (key->getClass()->ext.isWrappedNative) {
251
JS_ASSERT(cx->runtime->preserveWrapperCallback);
252
if (!cx->runtime->preserveWrapperCallback(cx, key)) {
253
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_WEAKMAP_KEY);
258
if (!map->put(key, value)) {
259
JS_ReportOutOfMemory(cx);
262
HashTableWriteBarrierPost(cx->compartment, map, key);
264
args.rval().setUndefined();
269
WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
271
CallArgs args = CallArgsFromVp(argc, vp);
272
return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
275
JS_FRIEND_API(JSBool)
276
JS_NondeterministicGetWeakMapKeys(JSContext *cx, JSObject *obj, JSObject **ret)
278
if (!obj || !obj->isWeakMap()) {
282
RootedObject arr(cx, NewDenseEmptyArray(cx));
285
ObjectValueMap *map = GetObjectMap(obj);
287
for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
288
if (!js_NewbornArrayPush(cx, arr, ObjectValue(*r.front().key)))
297
WeakMap_mark(JSTracer *trc, JSObject *obj)
299
if (ObjectValueMap *map = GetObjectMap(obj))
304
WeakMap_finalize(FreeOp *fop, JSObject *obj)
306
if (ObjectValueMap *map = GetObjectMap(obj)) {
309
map->~ObjectValueMap();
310
memset(static_cast<void *>(map), 0xdc, sizeof(*map));
319
WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
321
JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapClass);
329
Class js::WeakMapClass = {
331
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
332
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
333
JS_PropertyStub, /* addProperty */
334
JS_PropertyStub, /* delProperty */
335
JS_PropertyStub, /* getProperty */
336
JS_StrictPropertyStub, /* setProperty */
341
NULL, /* checkAccess */
343
NULL, /* construct */
344
NULL, /* xdrObject */
348
static JSFunctionSpec weak_map_methods[] = {
349
JS_FN("has", WeakMap_has, 1, 0),
350
JS_FN("get", WeakMap_get, 2, 0),
351
JS_FN("delete", WeakMap_delete, 1, 0),
352
JS_FN("set", WeakMap_set, 2, 0),
357
js_InitWeakMapClass(JSContext *cx, JSObject *obj)
359
JS_ASSERT(obj->isNative());
361
Rooted<GlobalObject*> global(cx, &obj->asGlobal());
363
RootedObject weakMapProto(cx, global->createBlankPrototype(cx, &WeakMapClass));
367
RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
368
CLASS_NAME(cx, WeakMap), 0));
372
if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto))
375
if (!DefinePropertiesAndBrand(cx, weakMapProto, NULL, weak_map_methods))
378
if (!DefineConstructorAndPrototype(cx, global, JSProto_WeakMap, ctor, weakMapProto))