summaryrefslogtreecommitdiffstats
path: root/js/public/Id.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/public/Id.h')
-rw-r--r--js/public/Id.h207
1 files changed, 207 insertions, 0 deletions
diff --git a/js/public/Id.h b/js/public/Id.h
new file mode 100644
index 000000000..d474e784f
--- /dev/null
+++ b/js/public/Id.h
@@ -0,0 +1,207 @@
+/* -*- 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==(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)
+
+#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_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 GCPolicy<jsid>
+{
+ static jsid initial() { return JSID_VOID; }
+ static void trace(JSTracer* trc, jsid* idp, const char* name) {
+ js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
+ }
+};
+
+} // namespace JS
+
+namespace js {
+
+template <>
+struct BarrierMethods<jsid>
+{
+ 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 */