Id.h (12534B)
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 js_Id_h 8 #define js_Id_h 9 10 // [SMDOC] PropertyKey / jsid 11 // 12 // A PropertyKey is an identifier for a property of an object which is either a 13 // 31-bit unsigned integer, interned string or symbol. 14 // 15 // Also, there is an additional PropertyKey value, PropertyKey::Void(), which 16 // does not occur in JS scripts but may be used to indicate the absence of a 17 // valid key. A void PropertyKey is not a valid key and only arises as an 18 // exceptional API return value. Embeddings must not pass a void PropertyKey 19 // into JSAPI entry points expecting a PropertyKey and do not need to handle 20 // void keys in hooks receiving a PropertyKey except when explicitly noted in 21 // the API contract. 22 // 23 // A PropertyKey is not implicitly convertible to or from a Value; JS_ValueToId 24 // or JS_IdToValue must be used instead. 25 // 26 // jsid is an alias for JS::PropertyKey. New code should use PropertyKey instead 27 // of jsid. 28 29 #include "mozilla/Maybe.h" 30 31 #include "jstypes.h" 32 33 #include "js/GCAnnotations.h" 34 #include "js/HeapAPI.h" 35 #include "js/RootingAPI.h" 36 #include "js/TraceKind.h" 37 #include "js/TracingAPI.h" 38 #include "js/TypeDecls.h" 39 40 namespace js { 41 class JS_PUBLIC_API GenericPrinter; 42 class JSONPrinter; 43 } // namespace js 44 45 namespace JS { 46 47 enum class SymbolCode : uint32_t; 48 49 class PropertyKey { 50 uintptr_t asBits_; 51 52 public: 53 // All keys with the low bit set are integer keys. This means the other type 54 // tags must all be even. These constants are public only for the JITs. 55 static constexpr uintptr_t IntTagBit = 0x1; 56 // Use 0 for StringTypeTag to avoid a bitwise op for atom <-> id conversions. 57 static constexpr uintptr_t StringTypeTag = 0x0; 58 static constexpr uintptr_t VoidTypeTag = 0x2; 59 static constexpr uintptr_t SymbolTypeTag = 0x4; 60 // (0x6 is unused) 61 static constexpr uintptr_t TypeMask = 0x7; 62 63 static constexpr uint32_t IntMin = 0; 64 static constexpr uint32_t IntMax = INT32_MAX; 65 66 constexpr PropertyKey() : asBits_(VoidTypeTag) {} 67 68 static constexpr MOZ_ALWAYS_INLINE PropertyKey fromRawBits(uintptr_t bits) { 69 PropertyKey id; 70 id.asBits_ = bits; 71 return id; 72 } 73 74 bool operator==(const PropertyKey& rhs) const { 75 return asBits_ == rhs.asBits_; 76 } 77 bool operator!=(const PropertyKey& rhs) const { 78 return asBits_ != rhs.asBits_; 79 } 80 81 MOZ_ALWAYS_INLINE bool isVoid() const { 82 MOZ_ASSERT_IF((asBits_ & TypeMask) == VoidTypeTag, asBits_ == VoidTypeTag); 83 return asBits_ == VoidTypeTag; 84 } 85 86 MOZ_ALWAYS_INLINE bool isInt() const { return !!(asBits_ & IntTagBit); } 87 88 MOZ_ALWAYS_INLINE bool isString() const { 89 return (asBits_ & TypeMask) == StringTypeTag; 90 } 91 92 MOZ_ALWAYS_INLINE bool isSymbol() const { 93 return (asBits_ & TypeMask) == SymbolTypeTag; 94 } 95 96 MOZ_ALWAYS_INLINE bool isGCThing() const { return isString() || isSymbol(); } 97 98 constexpr uintptr_t asRawBits() const { return asBits_; } 99 100 MOZ_ALWAYS_INLINE int32_t toInt() const { 101 MOZ_ASSERT(isInt()); 102 uint32_t bits = static_cast<uint32_t>(asBits_) >> 1; 103 return static_cast<int32_t>(bits); 104 } 105 106 MOZ_ALWAYS_INLINE JSString* toString() const { 107 MOZ_ASSERT(isString()); 108 // Use XOR instead of `& ~TypeMask` because small immediates can be 109 // encoded more efficiently on some platorms. 110 return reinterpret_cast<JSString*>(asBits_ ^ StringTypeTag); 111 } 112 113 MOZ_ALWAYS_INLINE JS::Symbol* toSymbol() const { 114 MOZ_ASSERT(isSymbol()); 115 return reinterpret_cast<JS::Symbol*>(asBits_ ^ SymbolTypeTag); 116 } 117 118 js::gc::Cell* toGCThing() const { 119 MOZ_ASSERT(isGCThing()); 120 return reinterpret_cast<js::gc::Cell*>(asBits_ & ~TypeMask); 121 } 122 123 GCCellPtr toGCCellPtr() const { 124 js::gc::Cell* thing = toGCThing(); 125 if (isString()) { 126 return JS::GCCellPtr(thing, JS::TraceKind::String); 127 } 128 MOZ_ASSERT(isSymbol()); 129 return JS::GCCellPtr(thing, JS::TraceKind::Symbol); 130 } 131 132 bool isPrivateName() const; 133 134 bool isWellKnownSymbol(JS::SymbolCode code) const; 135 136 // A void PropertyKey. This is equivalent to a PropertyKey created by the 137 // default constructor. 138 static constexpr PropertyKey Void() { return PropertyKey(); } 139 140 static constexpr bool fitsInInt(int32_t i) { return i >= 0; } 141 142 static constexpr PropertyKey Int(int32_t i) { 143 MOZ_ASSERT(fitsInInt(i)); 144 uint32_t bits = (static_cast<uint32_t>(i) << 1) | IntTagBit; 145 return PropertyKey::fromRawBits(bits); 146 } 147 148 static PropertyKey Symbol(JS::Symbol* sym) { 149 MOZ_ASSERT(sym != nullptr); 150 MOZ_ASSERT((uintptr_t(sym) & TypeMask) == 0); 151 MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym))); 152 return PropertyKey::fromRawBits(uintptr_t(sym) | SymbolTypeTag); 153 } 154 155 // Must not be used on atoms that are representable as integer PropertyKey. 156 // Prefer NameToId or AtomToId over this function: 157 // 158 // A PropertyName is an atom that does not contain an integer in the range 159 // [0, UINT32_MAX]. However, PropertyKey can only hold an integer in the range 160 // [0, IntMax] (where IntMax == 2^31-1). Thus, for the range of integers 161 // (IntMax, UINT32_MAX], to represent as a 'id', it must be 162 // the case id.isString() and id.toString()->isIndex(). In most 163 // cases when creating a PropertyKey, code does not have to care about 164 // this corner case because: 165 // 166 // - When given an arbitrary JSAtom*, AtomToId must be used, which checks for 167 // integer atoms representable as integer PropertyKey, and does this 168 // conversion. 169 // 170 // - When given a PropertyName*, NameToId can be used which does not need 171 // to do any dynamic checks. 172 // 173 // Thus, it is only the rare third case which needs this function, which 174 // handles any JSAtom* that is known not to be representable with an int 175 // PropertyKey. 176 static PropertyKey NonIntAtom(JSAtom* atom) { 177 MOZ_ASSERT((uintptr_t(atom) & TypeMask) == 0); 178 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom)); 179 return PropertyKey::fromRawBits(uintptr_t(atom) | StringTypeTag); 180 } 181 182 // The JSAtom/JSString type exposed to embedders is opaque. 183 static PropertyKey NonIntAtom(JSString* str) { 184 MOZ_ASSERT((uintptr_t(str) & TypeMask) == 0); 185 MOZ_ASSERT(PropertyKey::isNonIntAtom(str)); 186 return PropertyKey::fromRawBits(uintptr_t(str) | StringTypeTag); 187 } 188 189 // This API can be used by embedders to convert pinned (aka interned) strings, 190 // as created by JS_AtomizeAndPinString, into PropertyKeys. This means the 191 // string does not have to be explicitly rooted. 192 // 193 // Only use this API when absolutely necessary, otherwise use JS_StringToId. 194 static PropertyKey fromPinnedString(JSString* str); 195 196 // Internal API! 197 // All string PropertyKeys are actually atomized. 198 MOZ_ALWAYS_INLINE bool isAtom() const { return isString(); } 199 200 MOZ_ALWAYS_INLINE bool isAtom(JSAtom* atom) const { 201 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom)); 202 return *this == NonIntAtom(atom); 203 } 204 205 MOZ_ALWAYS_INLINE JSAtom* toAtom() const { 206 return reinterpret_cast<JSAtom*>(toString()); 207 } 208 MOZ_ALWAYS_INLINE JSLinearString* toLinearString() const { 209 return reinterpret_cast<JSLinearString*>(toString()); 210 } 211 212 #if defined(DEBUG) || defined(JS_JITSPEW) 213 void dump() const; 214 void dump(js::GenericPrinter& out) const; 215 void dump(js::JSONPrinter& json) const; 216 217 void dumpFields(js::JSONPrinter& json) const; 218 void dumpPropertyName(js::GenericPrinter& out) const; 219 void dumpStringContent(js::GenericPrinter& out) const; 220 #endif 221 222 private: 223 static bool isNonIntAtom(JSAtom* atom); 224 static bool isNonIntAtom(JSString* atom); 225 } JS_HAZ_GC_POINTER; 226 227 } // namespace JS 228 229 using jsid = JS::PropertyKey; 230 231 namespace JS { 232 233 // Handle<PropertyKey> version of PropertyKey::Void(). 234 extern JS_PUBLIC_DATA const JS::HandleId VoidHandlePropertyKey; 235 236 template <> 237 struct GCPolicy<jsid> : public GCPolicyBase<jsid> { 238 static void trace(JSTracer* trc, jsid* idp, const char* name) { 239 // This should only be called as part of root marking since that's the only 240 // time we should trace unbarriered GC thing pointers. This will assert if 241 // called at other times. 242 TraceRoot(trc, idp, name); 243 } 244 static bool isValid(jsid id) { 245 return !id.isGCThing() || 246 js::gc::IsCellPointerValid(id.toGCCellPtr().asCell()); 247 } 248 249 static constexpr bool mightBeInNursery() { return false; } 250 static bool isTenured(jsid id) { 251 MOZ_ASSERT_IF(id.isGCThing(), 252 !js::gc::IsInsideNursery(id.toGCCellPtr().asCell())); 253 return true; 254 } 255 }; 256 257 #ifdef DEBUG 258 MOZ_ALWAYS_INLINE void AssertIdIsNotGray(jsid id) { 259 if (id.isGCThing()) { 260 AssertCellIsNotGray(id.toGCCellPtr().asCell()); 261 } 262 } 263 #endif 264 265 /** 266 * Get one of the well-known symbols defined by ES6 as PropertyKey. This is 267 * equivalent to calling JS::GetWellKnownSymbol and then creating a PropertyKey. 268 * 269 * `which` must be in the range [0, WellKnownSymbolLimit). 270 */ 271 extern JS_PUBLIC_API PropertyKey GetWellKnownSymbolKey(JSContext* cx, 272 SymbolCode which); 273 274 /** 275 * Generate getter/setter id for given id, by adding "get " or "set " prefix. 276 */ 277 extern JS_PUBLIC_API bool ToGetterId( 278 JSContext* cx, JS::Handle<JS::PropertyKey> id, 279 JS::MutableHandle<JS::PropertyKey> getterId); 280 extern JS_PUBLIC_API bool ToSetterId( 281 JSContext* cx, JS::Handle<JS::PropertyKey> id, 282 JS::MutableHandle<JS::PropertyKey> setterId); 283 284 } // namespace JS 285 286 namespace js { 287 288 template <> 289 struct BarrierMethods<jsid> { 290 static gc::Cell* asGCThingOrNull(jsid id) { 291 if (id.isGCThing()) { 292 return id.toGCThing(); 293 } 294 return nullptr; 295 } 296 static void writeBarriers(jsid* idp, jsid prev, jsid next) { 297 if (prev.isString()) { 298 JS::IncrementalPreWriteBarrier(JS::GCCellPtr(prev.toString())); 299 } 300 if (prev.isSymbol()) { 301 JS::IncrementalPreWriteBarrier(JS::GCCellPtr(prev.toSymbol())); 302 } 303 postWriteBarrier(idp, prev, next); 304 } 305 static void postWriteBarrier(jsid* idp, jsid prev, jsid next) { 306 MOZ_ASSERT_IF(next.isString(), !gc::IsInsideNursery(next.toString())); 307 } 308 static void exposeToJS(jsid id) { 309 if (id.isGCThing()) { 310 js::gc::ExposeGCThingToActiveJS(id.toGCCellPtr()); 311 } 312 } 313 static void readBarrier(jsid id) { 314 if (id.isGCThing()) { 315 js::gc::IncrementalReadBarrier(id.toGCCellPtr()); 316 } 317 } 318 }; 319 320 // If the jsid is a GC pointer type, convert to that type and call |f| with the 321 // pointer and return the result wrapped in a Maybe, otherwise return None(). 322 template <typename F> 323 auto MapGCThingTyped(const jsid& id, F&& f) { 324 if (id.isString()) { 325 return mozilla::Some(f(id.toString())); 326 } 327 if (id.isSymbol()) { 328 return mozilla::Some(f(id.toSymbol())); 329 } 330 MOZ_ASSERT(!id.isGCThing()); 331 using ReturnType = decltype(f(static_cast<JSString*>(nullptr))); 332 return mozilla::Maybe<ReturnType>(); 333 } 334 335 // If the jsid is a GC pointer type, convert to that type and call |f| with the 336 // pointer. Return whether this happened. 337 template <typename F> 338 bool ApplyGCThingTyped(const jsid& id, F&& f) { 339 return MapGCThingTyped(id, 340 [&f](auto t) { 341 f(t); 342 return true; 343 }) 344 .isSome(); 345 } 346 347 template <typename Wrapper> 348 class WrappedPtrOperations<JS::PropertyKey, Wrapper> { 349 const JS::PropertyKey& id() const { 350 return static_cast<const Wrapper*>(this)->get(); 351 } 352 353 public: 354 bool isVoid() const { return id().isVoid(); } 355 bool isInt() const { return id().isInt(); } 356 bool isString() const { return id().isString(); } 357 bool isSymbol() const { return id().isSymbol(); } 358 bool isGCThing() const { return id().isGCThing(); } 359 360 int32_t toInt() const { return id().toInt(); } 361 JSString* toString() const { return id().toString(); } 362 JS::Symbol* toSymbol() const { return id().toSymbol(); } 363 364 bool isPrivateName() const { return id().isPrivateName(); } 365 366 bool isWellKnownSymbol(JS::SymbolCode code) const { 367 return id().isWellKnownSymbol(code); 368 } 369 370 uintptr_t asRawBits() const { return id().asRawBits(); } 371 372 // Internal API 373 bool isAtom() const { return id().isAtom(); } 374 bool isAtom(JSAtom* atom) const { return id().isAtom(atom); } 375 JSAtom* toAtom() const { return id().toAtom(); } 376 JSLinearString* toLinearString() const { return id().toLinearString(); } 377 }; 378 379 } // namespace js 380 381 #endif /* js_Id_h */