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 }