| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef js_Id_h |
| #define js_Id_h |
| |
| // A jsid is an identifier for a property or method of an object which is |
| // either a 31-bit unsigned integer, interned string or symbol. |
| // |
| // Also, there is an additional jsid value, JSID_VOID, which does not occur in |
| // JS scripts but may be used to indicate the absence of a valid jsid. A void |
| // jsid is not a valid id and only arises as an exceptional API return value, |
| // such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI |
| // entry points expecting a jsid and do not need to handle JSID_VOID in hooks |
| // receiving a jsid except when explicitly noted in the API contract. |
| // |
| // A jsid is not implicitly convertible to or from a Value; JS_ValueToId or |
| // JS_IdToValue must be used instead. |
| |
| #include "jstypes.h" |
| |
| #include "js/HeapAPI.h" |
| #include "js/RootingAPI.h" |
| #include "js/TypeDecls.h" |
| #include "js/Utility.h" |
| |
| struct jsid |
| { |
| size_t asBits; |
| bool operator==(jsid rhs) const { return asBits == rhs.asBits; } |
| bool operator!=(jsid rhs) const { return asBits != rhs.asBits; } |
| }; |
| #define JSID_BITS(id) (id.asBits) |
| |
| #define JSID_TYPE_STRING 0x0 |
| #define JSID_TYPE_INT 0x1 |
| #define JSID_TYPE_VOID 0x2 |
| #define JSID_TYPE_SYMBOL 0x4 |
| #define JSID_TYPE_MASK 0x7 |
| |
| // Avoid using canonical 'id' for jsid parameters since this is a magic word in |
| // Objective-C++ which, apparently, wants to be able to #include jsapi.h. |
| #define id iden |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_STRING(jsid id) |
| { |
| return (JSID_BITS(id) & JSID_TYPE_MASK) == 0; |
| } |
| |
| static MOZ_ALWAYS_INLINE JSString* |
| JSID_TO_STRING(jsid id) |
| { |
| MOZ_ASSERT(JSID_IS_STRING(id)); |
| return (JSString*)JSID_BITS(id); |
| } |
| |
| /** |
| * Only JSStrings that have been interned via the JSAPI can be turned into |
| * jsids by API clients. |
| * |
| * N.B. if a jsid is backed by a string which has not been interned, that |
| * string must be appropriately rooted to avoid being collected by the GC. |
| */ |
| JS_PUBLIC_API(jsid) |
| INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str); |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_ZERO(jsid id) |
| { |
| return JSID_BITS(id) == 0; |
| } |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_INT(jsid id) |
| { |
| return !!(JSID_BITS(id) & JSID_TYPE_INT); |
| } |
| |
| static MOZ_ALWAYS_INLINE int32_t |
| JSID_TO_INT(jsid id) |
| { |
| MOZ_ASSERT(JSID_IS_INT(id)); |
| return ((uint32_t)JSID_BITS(id)) >> 1; |
| } |
| |
| #define JSID_INT_MIN 0 |
| #define JSID_INT_MAX INT32_MAX |
| |
| static MOZ_ALWAYS_INLINE bool |
| INT_FITS_IN_JSID(int32_t i) |
| { |
| return i >= 0; |
| } |
| |
| static MOZ_ALWAYS_INLINE jsid |
| INT_TO_JSID(int32_t i) |
| { |
| jsid id; |
| MOZ_ASSERT(INT_FITS_IN_JSID(i)); |
| JSID_BITS(id) = ((i << 1) | JSID_TYPE_INT); |
| return id; |
| } |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_SYMBOL(jsid id) |
| { |
| return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL && |
| JSID_BITS(id) != JSID_TYPE_SYMBOL; |
| } |
| |
| static MOZ_ALWAYS_INLINE JS::Symbol* |
| JSID_TO_SYMBOL(jsid id) |
| { |
| MOZ_ASSERT(JSID_IS_SYMBOL(id)); |
| return (JS::Symbol*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK); |
| } |
| |
| static MOZ_ALWAYS_INLINE jsid |
| SYMBOL_TO_JSID(JS::Symbol* sym) |
| { |
| jsid id; |
| MOZ_ASSERT(sym != nullptr); |
| MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0); |
| MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym))); |
| JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL); |
| return id; |
| } |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_GCTHING(jsid id) |
| { |
| return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); |
| } |
| |
| static MOZ_ALWAYS_INLINE JS::GCCellPtr |
| JSID_TO_GCTHING(jsid id) |
| { |
| void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK); |
| if (JSID_IS_STRING(id)) |
| return JS::GCCellPtr(thing, JS::TraceKind::String); |
| MOZ_ASSERT(JSID_IS_SYMBOL(id)); |
| return JS::GCCellPtr(thing, JS::TraceKind::Symbol); |
| } |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_VOID(const jsid id) |
| { |
| MOZ_ASSERT_IF(((size_t)JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID, |
| JSID_BITS(id) == JSID_TYPE_VOID); |
| return (size_t)JSID_BITS(id) == JSID_TYPE_VOID; |
| } |
| |
| static MOZ_ALWAYS_INLINE bool |
| JSID_IS_EMPTY(const jsid id) |
| { |
| return (size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL; |
| } |
| |
| extern JS_PUBLIC_DATA(const jsid) JSID_VOID; |
| extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY; |
| |
| extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE; |
| extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE; |
| |
| namespace js { |
| |
| template <> |
| struct GCMethods<jsid> |
| { |
| static jsid initial() { return JSID_VOID; } |
| static void postBarrier(jsid* idp, jsid prev, jsid next) {} |
| }; |
| |
| // If the jsid is a GC pointer type, convert to that type and call |f| with |
| // the pointer. If the jsid is not a GC type, calls F::defaultValue. |
| template <typename F, typename... Args> |
| auto |
| DispatchTyped(F f, jsid& id, Args&&... args) |
| -> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...)) |
| { |
| if (JSID_IS_STRING(id)) |
| return f(JSID_TO_STRING(id), mozilla::Forward<Args>(args)...); |
| if (JSID_IS_SYMBOL(id)) |
| return f(JSID_TO_SYMBOL(id), mozilla::Forward<Args>(args)...); |
| MOZ_ASSERT(!JSID_IS_GCTHING(id)); |
| return F::defaultValue(id); |
| } |
| |
| #undef id |
| |
| } // namespace js |
| |
| #endif /* js_Id_h */ |