js/src/jsweakmap.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jsweakmap.h"
     9 #include <string.h>
    11 #include "jsapi.h"
    12 #include "jscntxt.h"
    13 #include "jsfriendapi.h"
    14 #include "jsobj.h"
    15 #include "jswrapper.h"
    17 #include "vm/GlobalObject.h"
    18 #include "vm/WeakMapObject.h"
    20 #include "jsobjinlines.h"
    22 using namespace js;
    24 WeakMapBase::WeakMapBase(JSObject *memOf, JSCompartment *c)
    25   : memberOf(memOf),
    26     compartment(c),
    27     next(WeakMapNotInList)
    28 {
    29     JS_ASSERT_IF(memberOf, memberOf->compartment() == c);
    30 }
    32 WeakMapBase::~WeakMapBase()
    33 {
    34     JS_ASSERT(next == WeakMapNotInList);
    35 }
    37 bool
    38 WeakMapBase::markCompartmentIteratively(JSCompartment *c, JSTracer *tracer)
    39 {
    40     bool markedAny = false;
    41     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
    42         if (m->markIteratively(tracer))
    43             markedAny = true;
    44     }
    45     return markedAny;
    46 }
    48 void
    49 WeakMapBase::sweepCompartment(JSCompartment *c)
    50 {
    51     for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
    52         m->sweep();
    53 }
    55 void
    56 WeakMapBase::traceAllMappings(WeakMapTracer *tracer)
    57 {
    58     JSRuntime *rt = tracer->runtime;
    59     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
    60         for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
    61             m->traceMappings(tracer);
    62     }
    63 }
    65 void
    66 WeakMapBase::resetCompartmentWeakMapList(JSCompartment *c)
    67 {
    68     JS_ASSERT(WeakMapNotInList != nullptr);
    70     WeakMapBase *m = c->gcWeakMapList;
    71     c->gcWeakMapList = nullptr;
    72     while (m) {
    73         WeakMapBase *n = m->next;
    74         m->next = WeakMapNotInList;
    75         m = n;
    76     }
    77 }
    79 bool
    80 WeakMapBase::saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector)
    81 {
    82     WeakMapBase *m = c->gcWeakMapList;
    83     while (m) {
    84         if (!vector.append(m))
    85             return false;
    86         m = m->next;
    87     }
    88     return true;
    89 }
    91 void
    92 WeakMapBase::restoreCompartmentWeakMapLists(WeakMapVector &vector)
    93 {
    94     for (WeakMapBase **p = vector.begin(); p != vector.end(); p++) {
    95         WeakMapBase *m = *p;
    96         JS_ASSERT(m->next == WeakMapNotInList);
    97         JSCompartment *c = m->compartment;
    98         m->next = c->gcWeakMapList;
    99         c->gcWeakMapList = m;
   100     }
   101 }
   103 void
   104 WeakMapBase::removeWeakMapFromList(WeakMapBase *weakmap)
   105 {
   106     JSCompartment *c = weakmap->compartment;
   107     for (WeakMapBase **p = &c->gcWeakMapList; *p; p = &(*p)->next) {
   108         if (*p == weakmap) {
   109             *p = (*p)->next;
   110             weakmap->next = WeakMapNotInList;
   111             break;
   112         }
   113     }
   114 }
   116 static JSObject *
   117 GetKeyArg(JSContext *cx, CallArgs &args)
   118 {
   119     if (args[0].isPrimitive()) {
   120         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
   121         return nullptr;
   122     }
   123     return &args[0].toObject();
   124 }
   126 MOZ_ALWAYS_INLINE bool
   127 IsWeakMap(HandleValue v)
   128 {
   129     return v.isObject() && v.toObject().is<WeakMapObject>();
   130 }
   132 MOZ_ALWAYS_INLINE bool
   133 WeakMap_has_impl(JSContext *cx, CallArgs args)
   134 {
   135     JS_ASSERT(IsWeakMap(args.thisv()));
   137     if (args.length() < 1) {
   138         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   139                              "WeakMap.has", "0", "s");
   140         return false;
   141     }
   142     JSObject *key = GetKeyArg(cx, args);
   143     if (!key)
   144         return false;
   146     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
   147         if (map->has(key)) {
   148             args.rval().setBoolean(true);
   149             return true;
   150         }
   151     }
   153     args.rval().setBoolean(false);
   154     return true;
   155 }
   157 static bool
   158 WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
   159 {
   160     CallArgs args = CallArgsFromVp(argc, vp);
   161     return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
   162 }
   164 MOZ_ALWAYS_INLINE bool
   165 WeakMap_clear_impl(JSContext *cx, CallArgs args)
   166 {
   167     JS_ASSERT(IsWeakMap(args.thisv()));
   169     // We can't js_delete the weakmap because the data gathered during GC
   170     // is used by the Cycle Collector
   171     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap())
   172         map->clear();
   174     args.rval().setUndefined();
   175     return true;
   176 }
   178 static bool
   179 WeakMap_clear(JSContext *cx, unsigned argc, Value *vp)
   180 {
   181     CallArgs args = CallArgsFromVp(argc, vp);
   182     return CallNonGenericMethod<IsWeakMap, WeakMap_clear_impl>(cx, args);
   183 }
   185 MOZ_ALWAYS_INLINE bool
   186 WeakMap_get_impl(JSContext *cx, CallArgs args)
   187 {
   188     JS_ASSERT(IsWeakMap(args.thisv()));
   190     if (args.length() < 1) {
   191         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   192                              "WeakMap.get", "0", "s");
   193         return false;
   194     }
   195     JSObject *key = GetKeyArg(cx, args);
   196     if (!key)
   197         return false;
   199     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
   200         if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
   201             // Read barrier to prevent an incorrectly gray value from escaping the
   202             // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
   203             ExposeValueToActiveJS(ptr->value().get());
   205             args.rval().set(ptr->value());
   206             return true;
   207         }
   208     }
   210     args.rval().set((args.length() > 1) ? args[1] : UndefinedValue());
   211     return true;
   212 }
   214 static bool
   215 WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
   216 {
   217     CallArgs args = CallArgsFromVp(argc, vp);
   218     return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
   219 }
   221 MOZ_ALWAYS_INLINE bool
   222 WeakMap_delete_impl(JSContext *cx, CallArgs args)
   223 {
   224     JS_ASSERT(IsWeakMap(args.thisv()));
   226     if (args.length() < 1) {
   227         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   228                              "WeakMap.delete", "0", "s");
   229         return false;
   230     }
   231     JSObject *key = GetKeyArg(cx, args);
   232     if (!key)
   233         return false;
   235     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
   236         if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
   237             map->remove(ptr);
   238             args.rval().setBoolean(true);
   239             return true;
   240         }
   241     }
   243     args.rval().setBoolean(false);
   244     return true;
   245 }
   247 static bool
   248 WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
   249 {
   250     CallArgs args = CallArgsFromVp(argc, vp);
   251     return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
   252 }
   254 static bool
   255 TryPreserveReflector(JSContext *cx, HandleObject obj)
   256 {
   257     if (obj->getClass()->ext.isWrappedNative ||
   258         (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) ||
   259         (obj->is<ProxyObject>() &&
   260          obj->as<ProxyObject>().handler()->family() == GetDOMProxyHandlerFamily()))
   261     {
   262         JS_ASSERT(cx->runtime()->preserveWrapperCallback);
   263         if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
   264             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
   265             return false;
   266         }
   267     }
   268     return true;
   269 }
   271 static inline void
   272 WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
   273 {
   274 #ifdef JSGC_GENERATIONAL
   275     /*
   276      * Strip the barriers from the type before inserting into the store buffer.
   277      * This will automatically ensure that barriers do not fire during GC.
   278      *
   279      * Some compilers complain about instantiating the WeakMap class for
   280      * unbarriered type arguments, so we cast to a HashMap instead.  Because of
   281      * WeakMap's multiple inheritace, We need to do this in two stages, first to
   282      * the HashMap base class and then to the unbarriered version.
   283      */
   284     ObjectValueMap::Base *baseHashMap = static_cast<ObjectValueMap::Base *>(weakMap);
   286     typedef HashMap<JSObject *, Value> UnbarrieredMap;
   287     UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
   289     typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
   290     if (key && IsInsideNursery(rt, key))
   291         rt->gcStoreBuffer.putGeneric(Ref((unbarrieredMap), key));
   292 #endif
   293 }
   295 MOZ_ALWAYS_INLINE bool
   296 SetWeakMapEntryInternal(JSContext *cx, Handle<WeakMapObject*> mapObj,
   297                         HandleObject key, HandleValue value)
   298 {
   299     ObjectValueMap *map = mapObj->getMap();
   300     if (!map) {
   301         map = cx->new_<ObjectValueMap>(cx, mapObj.get());
   302         if (!map)
   303             return false;
   304         if (!map->init()) {
   305             js_delete(map);
   306             JS_ReportOutOfMemory(cx);
   307             return false;
   308         }
   309         mapObj->setPrivate(map);
   310     }
   312     // Preserve wrapped native keys to prevent wrapper optimization.
   313     if (!TryPreserveReflector(cx, key))
   314         return false;
   316     if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
   317         RootedObject delegate(cx, op(key));
   318         if (delegate && !TryPreserveReflector(cx, delegate))
   319             return false;
   320     }
   322     JS_ASSERT(key->compartment() == mapObj->compartment());
   323     JS_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
   324     if (!map->put(key, value)) {
   325         JS_ReportOutOfMemory(cx);
   326         return false;
   327     }
   328     WeakMapPostWriteBarrier(cx->runtime(), map, key.get());
   329     return true;
   330 }
   332 MOZ_ALWAYS_INLINE bool
   333 WeakMap_set_impl(JSContext *cx, CallArgs args)
   334 {
   335     JS_ASSERT(IsWeakMap(args.thisv()));
   337     if (args.length() < 1) {
   338         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
   339                              "WeakMap.set", "0", "s");
   340         return false;
   341     }
   342     RootedObject key(cx, GetKeyArg(cx, args));
   343     if (!key)
   344         return false;
   346     RootedValue value(cx, (args.length() > 1) ? args[1] : UndefinedValue());
   347     Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
   348     Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
   350     args.rval().setUndefined();
   351     return SetWeakMapEntryInternal(cx, map, key, value);
   352 }
   354 static bool
   355 WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
   356 {
   357     CallArgs args = CallArgsFromVp(argc, vp);
   358     return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
   359 }
   361 JS_FRIEND_API(bool)
   362 JS_NondeterministicGetWeakMapKeys(JSContext *cx, HandleObject objArg, MutableHandleObject ret)
   363 {
   364     RootedObject obj(cx, objArg);
   365     obj = UncheckedUnwrap(obj);
   366     if (!obj || !obj->is<WeakMapObject>()) {
   367         ret.set(nullptr);
   368         return true;
   369     }
   370     RootedObject arr(cx, NewDenseEmptyArray(cx));
   371     if (!arr)
   372         return false;
   373     ObjectValueMap *map = obj->as<WeakMapObject>().getMap();
   374     if (map) {
   375         // Prevent GC from mutating the weakmap while iterating.
   376         gc::AutoSuppressGC suppress(cx);
   377         for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
   378             RootedObject key(cx, r.front().key());
   379             if (!cx->compartment()->wrap(cx, &key))
   380                 return false;
   381             if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
   382                 return false;
   383         }
   384     }
   385     ret.set(arr);
   386     return true;
   387 }
   389 static void
   390 WeakMap_mark(JSTracer *trc, JSObject *obj)
   391 {
   392     if (ObjectValueMap *map = obj->as<WeakMapObject>().getMap())
   393         map->trace(trc);
   394 }
   396 static void
   397 WeakMap_finalize(FreeOp *fop, JSObject *obj)
   398 {
   399     if (ObjectValueMap *map = obj->as<WeakMapObject>().getMap()) {
   400         map->check();
   401 #ifdef DEBUG
   402         map->~ObjectValueMap();
   403         memset(static_cast<void *>(map), 0xdc, sizeof(*map));
   404         fop->free_(map);
   405 #else
   406         fop->delete_(map);
   407 #endif
   408     }
   409 }
   411 JS_PUBLIC_API(JSObject*)
   412 JS::NewWeakMapObject(JSContext *cx)
   413 {
   414     return NewBuiltinClassInstance(cx, &WeakMapObject::class_);
   415 }
   417 JS_PUBLIC_API(bool)
   418 JS::IsWeakMapObject(JSObject *obj)
   419 {
   420     return obj->is<WeakMapObject>();
   421 }
   423 JS_PUBLIC_API(bool)
   424 JS::GetWeakMapEntry(JSContext *cx, HandleObject mapObj, HandleObject key,
   425                     MutableHandleValue rval)
   426 {
   427     CHECK_REQUEST(cx);
   428     assertSameCompartment(cx, key);
   429     rval.setUndefined();
   430     ObjectValueMap *map = mapObj->as<WeakMapObject>().getMap();
   431     if (!map)
   432         return true;
   433     if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
   434         // Read barrier to prevent an incorrectly gray value from escaping the
   435         // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
   436         ExposeValueToActiveJS(ptr->value().get());
   437         rval.set(ptr->value());
   438     }
   439     return true;
   440 }
   442 JS_PUBLIC_API(bool)
   443 JS::SetWeakMapEntry(JSContext *cx, HandleObject mapObj, HandleObject key,
   444                     HandleValue val)
   445 {
   446     CHECK_REQUEST(cx);
   447     assertSameCompartment(cx, key, val);
   448     Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
   449     return SetWeakMapEntryInternal(cx, rootedMap, key, val);
   450 }
   452 static bool
   453 WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
   454 {
   455     CallArgs args = CallArgsFromVp(argc, vp);
   456     JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapObject::class_);
   457     if (!obj)
   458         return false;
   460     args.rval().setObject(*obj);
   461     return true;
   462 }
   464 const Class WeakMapObject::class_ = {
   465     "WeakMap",
   466     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
   467     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
   468     JS_PropertyStub,         /* addProperty */
   469     JS_DeletePropertyStub,   /* delProperty */
   470     JS_PropertyStub,         /* getProperty */
   471     JS_StrictPropertyStub,   /* setProperty */
   472     JS_EnumerateStub,
   473     JS_ResolveStub,
   474     JS_ConvertStub,
   475     WeakMap_finalize,
   476     nullptr,                 /* call        */
   477     nullptr,                 /* construct   */
   478     nullptr,                 /* xdrObject   */
   479     WeakMap_mark
   480 };
   482 static const JSFunctionSpec weak_map_methods[] = {
   483     JS_FN("has",    WeakMap_has, 1, 0),
   484     JS_FN("get",    WeakMap_get, 2, 0),
   485     JS_FN("delete", WeakMap_delete, 1, 0),
   486     JS_FN("set",    WeakMap_set, 2, 0),
   487     JS_FN("clear",  WeakMap_clear, 0, 0),
   488     JS_FS_END
   489 };
   491 JSObject *
   492 js_InitWeakMapClass(JSContext *cx, HandleObject obj)
   493 {
   494     JS_ASSERT(obj->isNative());
   496     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
   498     RootedObject weakMapProto(cx, global->createBlankPrototype(cx, &WeakMapObject::class_));
   499     if (!weakMapProto)
   500         return nullptr;
   502     RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
   503                                                       cx->names().WeakMap, 0));
   504     if (!ctor)
   505         return nullptr;
   507     if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto))
   508         return nullptr;
   510     if (!DefinePropertiesAndBrand(cx, weakMapProto, nullptr, weak_map_methods))
   511         return nullptr;
   513     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, weakMapProto))
   514         return nullptr;
   515     return weakMapProto;
   516 }

mercurial