tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WeakSetObject.cpp (8183B)


      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 * 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/WeakSetObject.h"
      8 
      9 #include "builtin/MapObject.h"
     10 #include "jit/InlinableNatives.h"
     11 #include "js/friend/ErrorMessages.h"  // JSMSG_*
     12 #include "js/PropertySpec.h"
     13 #include "vm/GlobalObject.h"
     14 #include "vm/JSContext.h"
     15 #include "vm/SelfHosting.h"
     16 
     17 #include "builtin/MapObject-inl.h"
     18 #include "builtin/WeakMapObject-inl.h"
     19 #include "vm/JSObject-inl.h"
     20 #include "vm/NativeObject-inl.h"
     21 
     22 using namespace js;
     23 
     24 /* static */ MOZ_ALWAYS_INLINE bool WeakSetObject::is(HandleValue v) {
     25  return v.isObject() && v.toObject().is<WeakSetObject>();
     26 }
     27 
     28 static bool AddWeakSetEntryImpl(JSContext* cx, Handle<WeakSetObject*> setObj,
     29                                Handle<Value> keyVal) {
     30  if (MOZ_UNLIKELY(!CanBeHeldWeakly(keyVal))) {
     31    unsigned errorNum = GetErrorNumber(false);
     32    ReportValueError(cx, errorNum, JSDVG_IGNORE_STACK, keyVal, nullptr);
     33    return false;
     34  }
     35 
     36  return WeakCollectionPutEntryInternal(cx, setObj, keyVal, TrueHandleValue);
     37 }
     38 
     39 // ES2018 draft rev 7a2d3f053ecc2336fc19f377c55d52d78b11b296
     40 // 23.4.3.1 WeakSet.prototype.add ( value )
     41 /* static */ MOZ_ALWAYS_INLINE bool WeakSetObject::add_impl(
     42    JSContext* cx, const CallArgs& args) {
     43  MOZ_ASSERT(is(args.thisv()));
     44 
     45  // Steps 4-7.
     46  Rooted<WeakSetObject*> setObj(cx,
     47                                &args.thisv().toObject().as<WeakSetObject>());
     48  if (!AddWeakSetEntryImpl(cx, setObj, args.get(0))) {
     49    return false;
     50  }
     51 
     52  // Steps 6.a.i, 8.
     53  args.rval().set(args.thisv());
     54  return true;
     55 }
     56 
     57 /* static */
     58 bool WeakSetObject::add(JSContext* cx, unsigned argc, Value* vp) {
     59  // Steps 1-3.
     60  CallArgs args = CallArgsFromVp(argc, vp);
     61  return CallNonGenericMethod<WeakSetObject::is, WeakSetObject::add_impl>(cx,
     62                                                                          args);
     63 }
     64 
     65 // ES2018 draft rev 7a2d3f053ecc2336fc19f377c55d52d78b11b296
     66 // 23.4.3.3 WeakSet.prototype.delete ( value )
     67 /* static */ MOZ_ALWAYS_INLINE bool WeakSetObject::delete_impl(
     68    JSContext* cx, const CallArgs& args) {
     69  MOZ_ASSERT(is(args.thisv()));
     70 
     71  // Step 4.
     72  if (!CanBeHeldWeakly(args.get(0))) {
     73    args.rval().setBoolean(false);
     74    return true;
     75  }
     76 
     77  // Steps 5-6.
     78  if (Map* map = args.thisv().toObject().as<WeakSetObject>().getMap()) {
     79    Value value = args[0];
     80    if (Map::Ptr ptr = map->lookup(value)) {
     81      map->remove(ptr);
     82      args.rval().setBoolean(true);
     83      return true;
     84    }
     85  }
     86 
     87  // Step 7.
     88  args.rval().setBoolean(false);
     89  return true;
     90 }
     91 
     92 /* static */
     93 bool WeakSetObject::delete_(JSContext* cx, unsigned argc, Value* vp) {
     94  // Steps 1-3.
     95  CallArgs args = CallArgsFromVp(argc, vp);
     96  return CallNonGenericMethod<WeakSetObject::is, WeakSetObject::delete_impl>(
     97      cx, args);
     98 }
     99 
    100 // ES2018 draft rev 7a2d3f053ecc2336fc19f377c55d52d78b11b296
    101 // 23.4.3.4 WeakSet.prototype.has ( value )
    102 /* static */ MOZ_ALWAYS_INLINE bool WeakSetObject::has_impl(
    103    JSContext* cx, const CallArgs& args) {
    104  MOZ_ASSERT(is(args.thisv()));
    105 
    106  // Step 5.
    107  if (!CanBeHeldWeakly(args.get(0))) {
    108    args.rval().setBoolean(false);
    109    return true;
    110  }
    111 
    112  // Steps 4, 6.
    113  if (Map* map = args.thisv().toObject().as<WeakSetObject>().getMap()) {
    114    Value value = args[0];
    115    if (map->has(value)) {
    116      args.rval().setBoolean(true);
    117      return true;
    118    }
    119  }
    120 
    121  // Step 7.
    122  args.rval().setBoolean(false);
    123  return true;
    124 }
    125 
    126 /* static */
    127 bool WeakSetObject::has(JSContext* cx, unsigned argc, Value* vp) {
    128  // Steps 1-3.
    129  CallArgs args = CallArgsFromVp(argc, vp);
    130  return CallNonGenericMethod<WeakSetObject::is, WeakSetObject::has_impl>(cx,
    131                                                                          args);
    132 }
    133 
    134 // static
    135 bool WeakSetObject::hasObject(WeakSetObject* weakSet, JSObject* obj) {
    136  AutoUnsafeCallWithABI unsafe;
    137  Map* map = weakSet->getMap();
    138  return map && map->has(ObjectValue(*obj));
    139 }
    140 
    141 const ClassSpec WeakSetObject::classSpec_ = {
    142    GenericCreateConstructor<WeakSetObject::construct, 0,
    143                             gc::AllocKind::FUNCTION>,
    144    GenericCreatePrototype<WeakSetObject>,
    145    nullptr,
    146    nullptr,
    147    WeakSetObject::methods,
    148    WeakSetObject::properties,
    149    GenericFinishInit<WhichHasRealmFuseProperty::Proto>,
    150 };
    151 
    152 const JSClass WeakSetObject::class_ = {
    153    "WeakSet",
    154    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
    155        JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet) | JSCLASS_BACKGROUND_FINALIZE,
    156    &WeakCollectionObject::classOps_,
    157    &WeakSetObject::classSpec_,
    158 };
    159 
    160 const JSClass WeakSetObject::protoClass_ = {
    161    "WeakSet.prototype",
    162    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakSet),
    163    JS_NULL_CLASS_OPS,
    164    &WeakSetObject::classSpec_,
    165 };
    166 
    167 const JSPropertySpec WeakSetObject::properties[] = {
    168    JS_STRING_SYM_PS(toStringTag, "WeakSet", JSPROP_READONLY),
    169    JS_PS_END,
    170 };
    171 
    172 const JSFunctionSpec WeakSetObject::methods[] = {
    173    JS_FN("add", add, 1, 0),
    174    JS_FN("delete", delete_, 1, 0),
    175    JS_INLINABLE_FN("has", has, 1, 0, WeakSetHas),
    176    JS_FS_END,
    177 };
    178 
    179 WeakSetObject* WeakSetObject::create(JSContext* cx,
    180                                     HandleObject proto /* = nullptr */) {
    181  return NewObjectWithClassProto<WeakSetObject>(cx, proto);
    182 }
    183 
    184 // static
    185 bool WeakSetObject::tryOptimizeCtorWithIterable(JSContext* cx,
    186                                                Handle<WeakSetObject*> obj,
    187                                                Handle<Value> iterableVal,
    188                                                bool* optimized) {
    189  MOZ_ASSERT(!iterableVal.isNullOrUndefined());
    190  MOZ_ASSERT(!*optimized);
    191 
    192  if (!CanOptimizeMapOrSetCtorWithIterable<JSProto_WeakSet>(WeakSetObject::add,
    193                                                            obj, cx)) {
    194    return true;
    195  }
    196 
    197  if (!iterableVal.isObject()) {
    198    return true;
    199  }
    200  JSObject* iterable = &iterableVal.toObject();
    201 
    202  // Fast path for `new WeakSet(array)`.
    203  if (IsOptimizableArrayForMapOrSetCtor<MapOrSet::Set>(iterable, cx)) {
    204    RootedValue keyVal(cx);
    205    Rooted<ArrayObject*> array(cx, &iterable->as<ArrayObject>());
    206    uint32_t len = array->getDenseInitializedLength();
    207 
    208    for (uint32_t index = 0; index < len; index++) {
    209      keyVal.set(array->getDenseElement(index));
    210      MOZ_ASSERT(!keyVal.isMagic(JS_ELEMENTS_HOLE));
    211 
    212      if (!AddWeakSetEntryImpl(cx, obj, keyVal)) {
    213        return false;
    214      }
    215    }
    216 
    217    *optimized = true;
    218    return true;
    219  }
    220 
    221  return true;
    222 }
    223 
    224 bool WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) {
    225  // Based on our "Set" implementation instead of the more general ES6 steps.
    226  CallArgs args = CallArgsFromVp(argc, vp);
    227 
    228  if (!ThrowIfNotConstructing(cx, args, "WeakSet")) {
    229    return false;
    230  }
    231 
    232  RootedObject proto(cx);
    233  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakSet, &proto)) {
    234    return false;
    235  }
    236 
    237  Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx, proto));
    238  if (!obj) {
    239    return false;
    240  }
    241 
    242  if (!args.get(0).isNullOrUndefined()) {
    243    Handle<Value> iterable = args[0];
    244    bool optimized = false;
    245    if (!tryOptimizeCtorWithIterable(cx, obj, iterable, &optimized)) {
    246      return false;
    247    }
    248    if (!optimized) {
    249      FixedInvokeArgs<1> args2(cx);
    250      args2[0].set(iterable);
    251 
    252      RootedValue thisv(cx, ObjectValue(*obj));
    253      if (!CallSelfHostedFunction(cx, cx->names().WeakSetConstructorInit, thisv,
    254                                  args2, args2.rval())) {
    255        return false;
    256      }
    257    }
    258  }
    259 
    260  args.rval().setObject(*obj);
    261  return true;
    262 }
    263 
    264 JS_PUBLIC_API bool JS_NondeterministicGetWeakSetKeys(JSContext* cx,
    265                                                     HandleObject objArg,
    266                                                     MutableHandleObject ret) {
    267  RootedObject obj(cx, UncheckedUnwrap(objArg));
    268  if (!obj || !obj->is<WeakSetObject>()) {
    269    ret.set(nullptr);
    270    return true;
    271  }
    272  return WeakCollectionObject::nondeterministicGetKeys(
    273      cx, obj.as<WeakCollectionObject>(), ret);
    274 }