tor-browser

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

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 */