mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
240 lines
6.5 KiB
C++
240 lines
6.5 KiB
C++
/* -*- 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"
|
|
|
|
#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
|
|
|
|
struct jsid
|
|
{
|
|
size_t asBits;
|
|
|
|
constexpr jsid() : asBits(JSID_TYPE_VOID) {}
|
|
|
|
static constexpr jsid fromRawBits(size_t bits) {
|
|
jsid id;
|
|
id.asBits = bits;
|
|
return id;
|
|
}
|
|
|
|
bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
|
|
bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
|
|
} JS_HAZ_GC_POINTER;
|
|
#define JSID_BITS(id) (id.asBits)
|
|
|
|
// 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_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));
|
|
uint32_t bits = static_cast<uint32_t>(JSID_BITS(id)) >> 1;
|
|
return static_cast<int32_t>(bits);
|
|
}
|
|
|
|
#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));
|
|
uint32_t bits = (static_cast<uint32_t>(i) << 1) | JSID_TYPE_INT;
|
|
JSID_BITS(id) = static_cast<size_t>(bits);
|
|
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;
|
|
}
|
|
|
|
constexpr 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 GCPolicy<jsid>
|
|
{
|
|
static jsid initial() { return JSID_VOID; }
|
|
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
|
js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
|
|
}
|
|
static bool isValid(jsid id) {
|
|
return !JSID_IS_GCTHING(id) || js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
|
|
}
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
MOZ_ALWAYS_INLINE bool
|
|
IdIsNotGray(jsid id)
|
|
{
|
|
if (!JSID_IS_GCTHING(id))
|
|
return true;
|
|
|
|
return CellIsNotGray(JSID_TO_GCTHING(id).asCell());
|
|
}
|
|
#endif
|
|
|
|
} // namespace JS
|
|
|
|
namespace js {
|
|
|
|
template <>
|
|
struct BarrierMethods<jsid>
|
|
{
|
|
static gc::Cell* asGCThingOrNull(jsid id) {
|
|
if (JSID_IS_STRING(id))
|
|
return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id));
|
|
if (JSID_IS_SYMBOL(id))
|
|
return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id));
|
|
return nullptr;
|
|
}
|
|
static void postBarrier(jsid* idp, jsid prev, jsid next) {}
|
|
static void exposeToJS(jsid id) {
|
|
if (JSID_IS_GCTHING(id))
|
|
js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
|
|
}
|
|
};
|
|
|
|
// 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, const 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 */
|