MapObject.h (14348B)
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 #ifndef builtin_MapObject_h 8 #define builtin_MapObject_h 9 10 #include "mozilla/MemoryReporting.h" 11 12 #include "builtin/OrderedHashTableObject.h" 13 #include "vm/JSObject.h" 14 #include "vm/NativeObject.h" 15 16 namespace js { 17 18 /* 19 * Comparing two ropes for equality can fail. The js::HashTable template 20 * requires infallible hash() and match() operations. Therefore we require 21 * all values to be converted to hashable form before being used as a key 22 * in a Map or Set object. 23 * 24 * All values except ropes are hashable as-is. 25 */ 26 class HashableValue { 27 Value value; 28 29 public: 30 HashableValue() : value(UndefinedValue()) {} 31 explicit HashableValue(JSWhyMagic whyMagic) : value(MagicValue(whyMagic)) {} 32 33 [[nodiscard]] bool setValue(JSContext* cx, const Value& v); 34 HashNumber hash(const mozilla::HashCodeScrambler& hcs) const; 35 36 // Value equality. Separate BigInt instances may compare equal. 37 bool equals(const HashableValue& other) const; 38 39 // Bitwise equality. 40 bool operator==(const HashableValue& other) const { 41 return value == other.value; 42 } 43 bool operator!=(const HashableValue& other) const { 44 return !(*this == other); 45 } 46 47 const Value& get() const { return value; } 48 operator Value() const { return get(); } 49 50 void trace(JSTracer* trc) { 51 TraceManuallyBarrieredEdge(trc, &value, "HashableValue"); 52 } 53 }; 54 55 template <typename Wrapper> 56 class WrappedPtrOperations<HashableValue, Wrapper> { 57 public: 58 Value get() const { return static_cast<const Wrapper*>(this)->get().get(); } 59 }; 60 61 template <typename Wrapper> 62 class MutableWrappedPtrOperations<HashableValue, Wrapper> 63 : public WrappedPtrOperations<HashableValue, Wrapper> { 64 public: 65 [[nodiscard]] bool setValue(JSContext* cx, HandleValue v) { 66 return static_cast<Wrapper*>(this)->get().setValue(cx, v); 67 } 68 }; 69 70 template <> 71 struct InternalBarrierMethods<HashableValue> { 72 static bool isMarkable(const HashableValue& v) { return v.get().isGCThing(); } 73 74 static void preBarrier(const HashableValue& v) { 75 if (isMarkable(v)) { 76 gc::ValuePreWriteBarrier(v.get()); 77 } 78 } 79 80 #ifdef DEBUG 81 static void assertThingIsNotGray(const HashableValue& v) { 82 JS::AssertValueIsNotGray(v.get()); 83 } 84 #endif 85 }; 86 87 struct HashableValueHasher { 88 using Key = PreBarriered<HashableValue>; 89 using Lookup = HashableValue; 90 91 static HashNumber hash(const Lookup& v, 92 const mozilla::HashCodeScrambler& hcs) { 93 return v.hash(hcs); 94 } 95 static bool match(const Key& k, const Lookup& l) { return k.get().equals(l); } 96 static bool isEmpty(const Key& v) { 97 return v.get().get().isMagic(JS_HASH_KEY_EMPTY); 98 } 99 static void makeEmpty(Key* vp) { vp->set(HashableValue(JS_HASH_KEY_EMPTY)); } 100 }; 101 102 template <typename ObjectT> 103 class OrderedHashTableRef; 104 105 struct UnbarrieredHashPolicy; 106 107 class MapObject : public OrderedHashMapObject { 108 public: 109 using Table = OrderedHashMapImpl<PreBarriered<HashableValue>, HeapPtr<Value>, 110 HashableValueHasher>; 111 112 // PreBarrieredTable has the same memory layout as Table but doesn't have 113 // wrappers that perform post barriers on the keys/values. Used when the 114 // MapObject is in the nursery. 115 using PreBarrieredTable = 116 OrderedHashMapImpl<PreBarriered<HashableValue>, PreBarriered<Value>, 117 HashableValueHasher>; 118 119 // UnbarrieredTable has the same memory layout as Table but doesn't have any 120 // wrappers that perform barriers on the keys/values. Used to allocate and 121 // delete the table and when updating the nursery allocated keys map during 122 // minor GC. 123 using UnbarrieredTable = 124 OrderedHashMapImpl<Value, Value, UnbarrieredHashPolicy>; 125 126 friend class OrderedHashTableRef<MapObject>; 127 128 enum { 129 NurseryKeysSlot = Table::SlotCount, 130 RegisteredNurseryIteratorsSlot, 131 SlotCount 132 }; 133 134 using IteratorKind = TableIteratorObject::Kind; 135 136 static const JSClass class_; 137 static const JSClass protoClass_; 138 139 [[nodiscard]] bool getKeysAndValuesInterleaved( 140 JS::MutableHandle<GCVector<JS::Value>> entries); 141 [[nodiscard]] static bool entries(JSContext* cx, unsigned argc, Value* vp); 142 143 static MapObject* createWithProto(JSContext* cx, HandleObject proto, 144 NewObjectKind newKind); 145 static MapObject* create(JSContext* cx, HandleObject proto = nullptr); 146 static MapObject* createFromIterable( 147 JSContext* cx, Handle<JSObject*> proto, Handle<Value> iterable, 148 Handle<MapObject*> allocatedFromJit = nullptr); 149 150 // Publicly exposed Map calls for JSAPI access (webidl maplike/setlike 151 // interfaces, etc.) 152 uint32_t size(); 153 [[nodiscard]] bool get(JSContext* cx, const Value& key, 154 MutableHandleValue rval); 155 [[nodiscard]] bool has(JSContext* cx, const Value& key, bool* rval); 156 [[nodiscard]] bool getOrInsert(JSContext* cx, const Value& key, 157 const Value& val, MutableHandleValue rval); 158 [[nodiscard]] bool delete_(JSContext* cx, const Value& key, bool* rval); 159 160 // Set call for public JSAPI exposure. Does not actually return map object 161 // as stated in spec, expects caller to return a value. for instance, with 162 // webidl maplike/setlike, should return interface object. 163 [[nodiscard]] bool set(JSContext* cx, const Value& key, const Value& val); 164 void clear(JSContext* cx); 165 [[nodiscard]] static bool iterator(JSContext* cx, IteratorKind kind, 166 Handle<MapObject*> obj, 167 MutableHandleValue iter); 168 169 void clearNurseryIteratorsBeforeMinorGC(); 170 171 // Sweeps a map that had nursery memory associated with it after a minor 172 // GC. This may finalize the map if it was in the nursery and has died. 173 // 174 // Returns a pointer to the map if it still has nursery memory associated with 175 // it, or nullptr. 176 static MapObject* sweepAfterMinorGC(JS::GCContext* gcx, MapObject* mapobj); 177 178 size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf); 179 180 [[nodiscard]] static bool get(JSContext* cx, unsigned argc, Value* vp); 181 [[nodiscard]] static bool set(JSContext* cx, unsigned argc, Value* vp); 182 [[nodiscard]] static bool has(JSContext* cx, unsigned argc, Value* vp); 183 184 private: 185 static const ClassSpec classSpec_; 186 static const JSClassOps classOps_; 187 static const ClassExtension classExtension_; 188 189 static const JSPropertySpec properties[]; 190 static const JSFunctionSpec methods[]; 191 static const JSPropertySpec staticProperties[]; 192 static const JSFunctionSpec staticMethods[]; 193 194 [[nodiscard]] bool setWithHashableKey(JSContext* cx, const HashableValue& key, 195 const Value& value); 196 197 [[nodiscard]] bool tryOptimizeCtorWithIterable(JSContext* cx, 198 const Value& iterableVal, 199 bool* optimized); 200 201 static bool finishInit(JSContext* cx, HandleObject ctor, HandleObject proto); 202 203 static void trace(JSTracer* trc, JSObject* obj); 204 static size_t objectMoved(JSObject* obj, JSObject* old); 205 206 [[nodiscard]] static bool construct(JSContext* cx, unsigned argc, Value* vp); 207 208 static bool is(HandleValue v); 209 static bool is(HandleObject o); 210 211 [[nodiscard]] static bool iterator_impl(JSContext* cx, const CallArgs& args, 212 IteratorKind kind); 213 214 [[nodiscard]] static bool size_impl(JSContext* cx, const CallArgs& args); 215 [[nodiscard]] static bool size(JSContext* cx, unsigned argc, Value* vp); 216 [[nodiscard]] static bool get_impl(JSContext* cx, const CallArgs& args); 217 [[nodiscard]] static bool has_impl(JSContext* cx, const CallArgs& args); 218 [[nodiscard]] static bool set_impl(JSContext* cx, const CallArgs& args); 219 [[nodiscard]] static bool getOrInsert(JSContext* cx, unsigned argc, 220 Value* vp); 221 [[nodiscard]] static bool getOrInsert_impl(JSContext* cx, 222 const CallArgs& args); 223 [[nodiscard]] static bool delete_impl(JSContext* cx, const CallArgs& args); 224 [[nodiscard]] static bool delete_(JSContext* cx, unsigned argc, Value* vp); 225 [[nodiscard]] static bool keys_impl(JSContext* cx, const CallArgs& args); 226 [[nodiscard]] static bool keys(JSContext* cx, unsigned argc, Value* vp); 227 [[nodiscard]] static bool values_impl(JSContext* cx, const CallArgs& args); 228 [[nodiscard]] static bool values(JSContext* cx, unsigned argc, Value* vp); 229 [[nodiscard]] static bool entries_impl(JSContext* cx, const CallArgs& args); 230 [[nodiscard]] static bool clear_impl(JSContext* cx, const CallArgs& args); 231 [[nodiscard]] static bool clear(JSContext* cx, unsigned argc, Value* vp); 232 }; 233 234 class MapIteratorObject : public TableIteratorObject { 235 public: 236 static const JSClass class_; 237 238 static const JSFunctionSpec methods[]; 239 static MapIteratorObject* create(JSContext* cx, Handle<MapObject*> mapobj, 240 Kind kind); 241 static void finalize(JS::GCContext* gcx, JSObject* obj); 242 static size_t objectMoved(JSObject* obj, JSObject* old); 243 244 [[nodiscard]] static bool next(MapIteratorObject* mapIterator, 245 ArrayObject* resultPairObj); 246 247 static JSObject* createResultPair(JSContext* cx); 248 249 private: 250 MapObject* target() const; 251 }; 252 253 class SetObject : public OrderedHashSetObject { 254 public: 255 using Table = 256 OrderedHashSetImpl<PreBarriered<HashableValue>, HashableValueHasher>; 257 using UnbarrieredTable = OrderedHashSetImpl<Value, UnbarrieredHashPolicy>; 258 259 friend class OrderedHashTableRef<SetObject>; 260 261 enum { 262 NurseryKeysSlot = Table::SlotCount, 263 RegisteredNurseryIteratorsSlot, 264 SlotCount 265 }; 266 267 using IteratorKind = TableIteratorObject::Kind; 268 269 static const JSClass class_; 270 static const JSClass protoClass_; 271 272 [[nodiscard]] bool keys(JS::MutableHandle<GCVector<JS::Value>> keys); 273 [[nodiscard]] static bool values(JSContext* cx, unsigned argc, Value* vp); 274 [[nodiscard]] bool add(JSContext* cx, const Value& key); 275 276 // Publicly exposed Set calls for JSAPI access (webidl maplike/setlike 277 // interfaces, etc.) 278 static SetObject* createWithProto(JSContext* cx, HandleObject proto, 279 NewObjectKind newKind); 280 static SetObject* create(JSContext* cx, HandleObject proto = nullptr); 281 static SetObject* createFromIterable( 282 JSContext* cx, Handle<JSObject*> proto, Handle<Value> iterable, 283 Handle<SetObject*> allocatedFromJit = nullptr); 284 285 uint32_t size(); 286 [[nodiscard]] static bool size(JSContext* cx, unsigned argc, Value* vp); 287 [[nodiscard]] static bool add(JSContext* cx, unsigned argc, Value* vp); 288 [[nodiscard]] static bool has(JSContext* cx, unsigned argc, Value* vp); 289 [[nodiscard]] bool has(JSContext* cx, const Value& key, bool* rval); 290 void clear(JSContext* cx); 291 [[nodiscard]] static bool iterator(JSContext* cx, IteratorKind kind, 292 Handle<SetObject*> obj, 293 MutableHandleValue iter); 294 [[nodiscard]] static bool delete_(JSContext* cx, unsigned argc, Value* vp); 295 [[nodiscard]] bool delete_(JSContext* cx, const Value& key, bool* rval); 296 297 [[nodiscard]] static bool copy(JSContext* cx, unsigned argc, Value* vp); 298 299 void clearNurseryIteratorsBeforeMinorGC(); 300 301 // Sweeps a set that had nursery memory associated with it after a minor 302 // GC. This may finalize the set if it was in the nursery and has died. 303 // 304 // Returns a pointer to the set if it still has nursery memory associated with 305 // it, or nullptr. 306 static SetObject* sweepAfterMinorGC(JS::GCContext* gcx, SetObject* setobj); 307 308 size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf); 309 310 private: 311 static const ClassSpec classSpec_; 312 static const JSClassOps classOps_; 313 static const ClassExtension classExtension_; 314 315 static const JSPropertySpec properties[]; 316 static const JSFunctionSpec methods[]; 317 static const JSPropertySpec staticProperties[]; 318 319 [[nodiscard]] bool addHashableValue(JSContext* cx, 320 const HashableValue& value); 321 322 [[nodiscard]] bool tryOptimizeCtorWithIterable(JSContext* cx, 323 const Value& iterableVal, 324 bool* optimized); 325 326 static bool finishInit(JSContext* cx, HandleObject ctor, HandleObject proto); 327 328 static void trace(JSTracer* trc, JSObject* obj); 329 static size_t objectMoved(JSObject* obj, JSObject* old); 330 331 static bool construct(JSContext* cx, unsigned argc, Value* vp); 332 333 static bool is(HandleValue v); 334 static bool is(HandleObject o); 335 336 [[nodiscard]] static bool iterator_impl(JSContext* cx, const CallArgs& args, 337 IteratorKind kind); 338 339 [[nodiscard]] static bool size_impl(JSContext* cx, const CallArgs& args); 340 [[nodiscard]] static bool has_impl(JSContext* cx, const CallArgs& args); 341 [[nodiscard]] static bool add_impl(JSContext* cx, const CallArgs& args); 342 [[nodiscard]] static bool delete_impl(JSContext* cx, const CallArgs& args); 343 [[nodiscard]] static bool values_impl(JSContext* cx, const CallArgs& args); 344 [[nodiscard]] static bool entries_impl(JSContext* cx, const CallArgs& args); 345 [[nodiscard]] static bool entries(JSContext* cx, unsigned argc, Value* vp); 346 [[nodiscard]] static bool clear_impl(JSContext* cx, const CallArgs& args); 347 [[nodiscard]] static bool clear(JSContext* cx, unsigned argc, Value* vp); 348 }; 349 350 class SetIteratorObject : public TableIteratorObject { 351 public: 352 static const JSClass class_; 353 354 static const JSFunctionSpec methods[]; 355 static SetIteratorObject* create(JSContext* cx, Handle<SetObject*> setobj, 356 Kind kind); 357 static void finalize(JS::GCContext* gcx, JSObject* obj); 358 static size_t objectMoved(JSObject* obj, JSObject* old); 359 360 [[nodiscard]] static bool next(SetIteratorObject* setIterator, 361 ArrayObject* resultObj); 362 363 static JSObject* createResult(JSContext* cx); 364 365 private: 366 SetObject* target() const; 367 }; 368 369 } /* namespace js */ 370 371 #endif /* builtin_MapObject_h */