LCOV - code coverage report
Current view: top level - js/src/builtin - WeakMapObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 124 190 65.3 %
Date: 2017-07-14 16:53:18 Functions: 19 22 86.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "builtin/WeakMapObject.h"
       8             : 
       9             : #include "jsapi.h"
      10             : #include "jscntxt.h"
      11             : 
      12             : #include "vm/SelfHosting.h"
      13             : 
      14             : #include "vm/Interpreter-inl.h"
      15             : 
      16             : using namespace js;
      17             : using namespace js::gc;
      18             : 
      19             : MOZ_ALWAYS_INLINE bool
      20         318 : IsWeakMap(HandleValue v)
      21             : {
      22         318 :     return v.isObject() && v.toObject().is<WeakMapObject>();
      23             : }
      24             : 
      25             : MOZ_ALWAYS_INLINE bool
      26          39 : WeakMap_has_impl(JSContext* cx, const CallArgs& args)
      27             : {
      28          39 :     MOZ_ASSERT(IsWeakMap(args.thisv()));
      29             : 
      30          39 :     if (!args.get(0).isObject()) {
      31           0 :         args.rval().setBoolean(false);
      32           0 :         return true;
      33             :     }
      34             : 
      35          39 :     if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
      36          34 :         JSObject* key = &args[0].toObject();
      37          34 :         if (map->has(key)) {
      38          27 :             args.rval().setBoolean(true);
      39          27 :             return true;
      40             :         }
      41             :     }
      42             : 
      43          12 :     args.rval().setBoolean(false);
      44          12 :     return true;
      45             : }
      46             : 
      47             : bool
      48          39 : js::WeakMap_has(JSContext* cx, unsigned argc, Value* vp)
      49             : {
      50          39 :     CallArgs args = CallArgsFromVp(argc, vp);
      51          39 :     return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
      52             : }
      53             : 
      54             : MOZ_ALWAYS_INLINE bool
      55          82 : WeakMap_get_impl(JSContext* cx, const CallArgs& args)
      56             : {
      57          82 :     MOZ_ASSERT(IsWeakMap(args.thisv()));
      58             : 
      59          82 :     if (!args.get(0).isObject()) {
      60           0 :         args.rval().setUndefined();
      61           0 :         return true;
      62             :     }
      63             : 
      64          82 :     if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
      65          68 :         JSObject* key = &args[0].toObject();
      66          68 :         if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
      67          62 :             args.rval().set(ptr->value());
      68          62 :             return true;
      69             :         }
      70             :     }
      71             : 
      72          20 :     args.rval().setUndefined();
      73          20 :     return true;
      74             : }
      75             : 
      76             : bool
      77          82 : js::WeakMap_get(JSContext* cx, unsigned argc, Value* vp)
      78             : {
      79          82 :     CallArgs args = CallArgsFromVp(argc, vp);
      80          82 :     return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
      81             : }
      82             : 
      83             : MOZ_ALWAYS_INLINE bool
      84           9 : WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
      85             : {
      86           9 :     MOZ_ASSERT(IsWeakMap(args.thisv()));
      87             : 
      88           9 :     if (!args.get(0).isObject()) {
      89           0 :         args.rval().setBoolean(false);
      90           0 :         return true;
      91             :     }
      92             : 
      93           9 :     if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
      94           9 :         JSObject* key = &args[0].toObject();
      95           9 :         if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
      96           9 :             map->remove(ptr);
      97           9 :             args.rval().setBoolean(true);
      98           9 :             return true;
      99             :         }
     100             :     }
     101             : 
     102           0 :     args.rval().setBoolean(false);
     103           0 :     return true;
     104             : }
     105             : 
     106             : bool
     107           9 : js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp)
     108             : {
     109           9 :     CallArgs args = CallArgsFromVp(argc, vp);
     110           9 :     return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
     111             : }
     112             : 
     113             : static bool
     114          66 : TryPreserveReflector(JSContext* cx, HandleObject obj)
     115             : {
     116         198 :     if (obj->getClass()->isWrappedNative() ||
     117         132 :         obj->getClass()->isDOMClass() ||
     118          69 :         (obj->is<ProxyObject>() &&
     119          16 :          obj->as<ProxyObject>().handler()->family() == GetDOMProxyHandlerFamily()))
     120             :     {
     121          13 :         MOZ_ASSERT(cx->runtime()->preserveWrapperCallback);
     122          13 :         if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
     123           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
     124           0 :             return false;
     125             :         }
     126             :     }
     127          66 :     return true;
     128             : }
     129             : 
     130             : static MOZ_ALWAYS_INLINE bool
     131          54 : SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
     132             :                         HandleObject key, HandleValue value)
     133             : {
     134          54 :     ObjectValueMap* map = mapObj->getMap();
     135          54 :     if (!map) {
     136          34 :         auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
     137          17 :         if (!newMap)
     138           0 :             return false;
     139          17 :         if (!newMap->init()) {
     140           0 :             JS_ReportOutOfMemory(cx);
     141           0 :             return false;
     142             :         }
     143          17 :         map = newMap.release();
     144          17 :         mapObj->setPrivate(map);
     145             :     }
     146             : 
     147             :     // Preserve wrapped native keys to prevent wrapper optimization.
     148          54 :     if (!TryPreserveReflector(cx, key))
     149           0 :         return false;
     150             : 
     151          54 :     if (JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp()) {
     152          24 :         RootedObject delegate(cx, op(key));
     153          12 :         if (delegate && !TryPreserveReflector(cx, delegate))
     154           0 :             return false;
     155             :     }
     156             : 
     157          54 :     MOZ_ASSERT(key->compartment() == mapObj->compartment());
     158          54 :     MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
     159          54 :     if (!map->put(key, value)) {
     160           0 :         JS_ReportOutOfMemory(cx);
     161           0 :         return false;
     162             :     }
     163          54 :     return true;
     164             : }
     165             : 
     166             : MOZ_ALWAYS_INLINE bool
     167          29 : WeakMap_set_impl(JSContext* cx, const CallArgs& args)
     168             : {
     169          29 :     MOZ_ASSERT(IsWeakMap(args.thisv()));
     170             : 
     171          29 :     if (!args.get(0).isObject()) {
     172           0 :         ReportNotObjectWithName(cx, "WeakMap key", args.get(0));
     173           0 :         return false;
     174             :     }
     175             : 
     176          58 :     RootedObject key(cx, &args[0].toObject());
     177          58 :     Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
     178          58 :     Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
     179             : 
     180          29 :     if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
     181           0 :         return false;
     182          29 :     args.rval().set(args.thisv());
     183          29 :     return true;
     184             : }
     185             : 
     186             : bool
     187          29 : js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
     188             : {
     189          29 :     CallArgs args = CallArgsFromVp(argc, vp);
     190          29 :     return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
     191             : }
     192             : 
     193             : JS_FRIEND_API(bool)
     194           0 : JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
     195             : {
     196           0 :     RootedObject obj(cx, objArg);
     197           0 :     obj = UncheckedUnwrap(obj);
     198           0 :     if (!obj || !obj->is<WeakMapObject>()) {
     199           0 :         ret.set(nullptr);
     200           0 :         return true;
     201             :     }
     202           0 :     RootedObject arr(cx, NewDenseEmptyArray(cx));
     203           0 :     if (!arr)
     204           0 :         return false;
     205           0 :     ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
     206           0 :     if (map) {
     207             :         // Prevent GC from mutating the weakmap while iterating.
     208           0 :         AutoSuppressGC suppress(cx);
     209           0 :         for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
     210           0 :             JS::ExposeObjectToActiveJS(r.front().key());
     211           0 :             RootedObject key(cx, r.front().key());
     212           0 :             if (!cx->compartment()->wrap(cx, &key))
     213           0 :                 return false;
     214           0 :             if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
     215           0 :                 return false;
     216             :         }
     217             :     }
     218           0 :     ret.set(arr);
     219           0 :     return true;
     220             : }
     221             : 
     222             : static void
     223           0 : WeakMap_trace(JSTracer* trc, JSObject* obj)
     224             : {
     225           0 :     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
     226           0 :         map->trace(trc);
     227           0 : }
     228             : 
     229             : static void
     230           0 : WeakMap_finalize(FreeOp* fop, JSObject* obj)
     231             : {
     232           0 :     MOZ_ASSERT(fop->maybeOnHelperThread());
     233           0 :     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
     234             : #ifdef DEBUG
     235           0 :         map->~ObjectValueMap();
     236           0 :         memset(static_cast<void*>(map), 0xdc, sizeof(*map));
     237           0 :         fop->free_(map);
     238             : #else
     239             :         fop->delete_(map);
     240             : #endif
     241             :     }
     242           0 : }
     243             : 
     244             : JS_PUBLIC_API(JSObject*)
     245           1 : JS::NewWeakMapObject(JSContext* cx)
     246             : {
     247           1 :     return NewBuiltinClassInstance(cx, &WeakMapObject::class_);
     248             : }
     249             : 
     250             : JS_PUBLIC_API(bool)
     251         522 : JS::IsWeakMapObject(JSObject* obj)
     252             : {
     253         522 :     return obj->is<WeakMapObject>();
     254             : }
     255             : 
     256             : JS_PUBLIC_API(bool)
     257         523 : JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
     258             :                     MutableHandleValue rval)
     259             : {
     260        1046 :     CHECK_REQUEST(cx);
     261         523 :     assertSameCompartment(cx, key);
     262         523 :     rval.setUndefined();
     263         523 :     ObjectValueMap* map = mapObj->as<WeakMapObject>().getMap();
     264         523 :     if (!map)
     265           1 :         return true;
     266         522 :     if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
     267             :         // Read barrier to prevent an incorrectly gray value from escaping the
     268             :         // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
     269         498 :         ExposeValueToActiveJS(ptr->value().get());
     270         498 :         rval.set(ptr->value());
     271             :     }
     272         522 :     return true;
     273             : }
     274             : 
     275             : JS_PUBLIC_API(bool)
     276          25 : JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
     277             :                     HandleValue val)
     278             : {
     279          50 :     CHECK_REQUEST(cx);
     280          25 :     assertSameCompartment(cx, key, val);
     281          50 :     Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
     282          50 :     return SetWeakMapEntryInternal(cx, rootedMap, key, val);
     283             : }
     284             : 
     285             : static bool
     286          58 : WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
     287             : {
     288          58 :     CallArgs args = CallArgsFromVp(argc, vp);
     289             : 
     290             :     // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
     291          58 :     if (!ThrowIfNotConstructing(cx, args, "WeakMap"))
     292           0 :         return false;
     293             : 
     294         116 :     RootedObject proto(cx);
     295          58 :     if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     296           0 :         return false;
     297             : 
     298         116 :     RootedObject obj(cx, NewObjectWithClassProto<WeakMapObject>(cx, proto));
     299          58 :     if (!obj)
     300           0 :         return false;
     301             : 
     302             :     // Steps 5-6, 11.
     303          58 :     if (!args.get(0).isNullOrUndefined()) {
     304           0 :         FixedInvokeArgs<1> args2(cx);
     305           0 :         args2[0].set(args[0]);
     306             : 
     307           0 :         RootedValue thisv(cx, ObjectValue(*obj));
     308           0 :         if (!CallSelfHostedFunction(cx, cx->names().WeakMapConstructorInit, thisv, args2, args2.rval()))
     309           0 :             return false;
     310             :     }
     311             : 
     312          58 :     args.rval().setObject(*obj);
     313          58 :     return true;
     314             : }
     315             : 
     316             : static const ClassOps WeakMapObjectClassOps = {
     317             :     nullptr, /* addProperty */
     318             :     nullptr, /* delProperty */
     319             :     nullptr, /* getProperty */
     320             :     nullptr, /* setProperty */
     321             :     nullptr, /* enumerate */
     322             :     nullptr, /* newEnumerate */
     323             :     nullptr, /* resolve */
     324             :     nullptr, /* mayResolve */
     325             :     WeakMap_finalize,
     326             :     nullptr, /* call */
     327             :     nullptr, /* hasInstance */
     328             :     nullptr, /* construct */
     329             :     WeakMap_trace
     330             : };
     331             : 
     332             : const Class WeakMapObject::class_ = {
     333             :     "WeakMap",
     334             :     JSCLASS_HAS_PRIVATE |
     335             :     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap) |
     336             :     JSCLASS_BACKGROUND_FINALIZE,
     337             :     &WeakMapObjectClassOps
     338             : };
     339             : 
     340             : static const JSFunctionSpec weak_map_methods[] = {
     341             :     JS_FN("has",    WeakMap_has, 1, 0),
     342             :     JS_FN("get",    WeakMap_get, 1, 0),
     343             :     JS_FN("delete", WeakMap_delete, 1, 0),
     344             :     JS_FN("set",    WeakMap_set, 2, 0),
     345             :     JS_FS_END
     346             : };
     347             : 
     348             : static JSObject*
     349          38 : InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers)
     350             : {
     351          38 :     MOZ_ASSERT(obj->isNative());
     352             : 
     353          38 :     Handle<GlobalObject*> global = obj.as<GlobalObject>();
     354             : 
     355          76 :     RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
     356          38 :     if (!proto)
     357           0 :         return nullptr;
     358             : 
     359          76 :     RootedFunction ctor(cx, GlobalObject::createConstructor(cx, WeakMap_construct,
     360         114 :                                                             cx->names().WeakMap, 0));
     361          38 :     if (!ctor)
     362           0 :         return nullptr;
     363             : 
     364          38 :     if (!LinkConstructorAndPrototype(cx, ctor, proto))
     365           0 :         return nullptr;
     366             : 
     367          38 :     if (defineMembers) {
     368          35 :         if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods))
     369           0 :             return nullptr;
     370          35 :         if (!DefineToStringTag(cx, proto, cx->names().WeakMap))
     371           0 :             return nullptr;
     372             :     }
     373             : 
     374          38 :     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto))
     375           0 :         return nullptr;
     376          38 :     return proto;
     377             : }
     378             : 
     379             : JSObject*
     380          35 : js::InitWeakMapClass(JSContext* cx, HandleObject obj)
     381             : {
     382          35 :     return InitWeakMapClass(cx, obj, true);
     383             : }
     384             : 
     385             : JSObject*
     386           3 : js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
     387             : {
     388           3 :     return InitWeakMapClass(cx, obj, false);
     389             : }
     390             : 

Generated by: LCOV version 1.13