1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 // A jsid is an identifier for a property or method of an object which is
11 // either a 31-bit unsigned integer, interned string or symbol.
12 //
13 // Also, there is an additional jsid value, JSID_VOID, which does not occur in
14 // JS scripts but may be used to indicate the absence of a valid jsid.  A void
15 // jsid is not a valid id and only arises as an exceptional API return value,
16 // such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI
17 // entry points expecting a jsid and do not need to handle JSID_VOID in hooks
18 // receiving a jsid except when explicitly noted in the API contract.
19 //
20 // A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
21 // JS_IdToValue must be used instead.
22 
23 #include "jstypes.h"
24 
25 #include "js/HeapAPI.h"
26 #include "js/RootingAPI.h"
27 #include "js/TypeDecls.h"
28 #include "js/Utility.h"
29 
30 struct jsid {
31   size_t asBits;
32   bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
33   bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
34 } JS_HAZ_GC_POINTER;
35 #define JSID_BITS(id) (id.asBits)
36 
37 #define JSID_TYPE_STRING 0x0
38 #define JSID_TYPE_INT 0x1
39 #define JSID_TYPE_VOID 0x2
40 #define JSID_TYPE_SYMBOL 0x4
41 #define JSID_TYPE_MASK 0x7
42 
43 // Avoid using canonical 'id' for jsid parameters since this is a magic word in
44 // Objective-C++ which, apparently, wants to be able to #include jsapi.h.
45 #define id iden
46 
JSID_IS_STRING(jsid id)47 static MOZ_ALWAYS_INLINE bool JSID_IS_STRING(jsid id) {
48   return (JSID_BITS(id) & JSID_TYPE_MASK) == 0;
49 }
50 
JSID_TO_STRING(jsid id)51 static MOZ_ALWAYS_INLINE JSString* JSID_TO_STRING(jsid id) {
52   MOZ_ASSERT(JSID_IS_STRING(id));
53   return (JSString*)JSID_BITS(id);
54 }
55 
56 /**
57  * Only JSStrings that have been interned via the JSAPI can be turned into
58  * jsids by API clients.
59  *
60  * N.B. if a jsid is backed by a string which has not been interned, that
61  * string must be appropriately rooted to avoid being collected by the GC.
62  */
63 JS_PUBLIC_API jsid INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str);
64 
JSID_IS_INT(jsid id)65 static MOZ_ALWAYS_INLINE bool JSID_IS_INT(jsid id) {
66   return !!(JSID_BITS(id) & JSID_TYPE_INT);
67 }
68 
JSID_TO_INT(jsid id)69 static MOZ_ALWAYS_INLINE int32_t JSID_TO_INT(jsid id) {
70   MOZ_ASSERT(JSID_IS_INT(id));
71   uint32_t bits = static_cast<uint32_t>(JSID_BITS(id)) >> 1;
72   return static_cast<int32_t>(bits);
73 }
74 
75 #define JSID_INT_MIN 0
76 #define JSID_INT_MAX INT32_MAX
77 
INT_FITS_IN_JSID(int32_t i)78 static MOZ_ALWAYS_INLINE bool INT_FITS_IN_JSID(int32_t i) { return i >= 0; }
79 
INT_TO_JSID(int32_t i)80 static MOZ_ALWAYS_INLINE jsid INT_TO_JSID(int32_t i) {
81   jsid id;
82   MOZ_ASSERT(INT_FITS_IN_JSID(i));
83   uint32_t bits = (static_cast<uint32_t>(i) << 1) | JSID_TYPE_INT;
84   JSID_BITS(id) = static_cast<size_t>(bits);
85   return id;
86 }
87 
JSID_IS_SYMBOL(jsid id)88 static MOZ_ALWAYS_INLINE bool JSID_IS_SYMBOL(jsid id) {
89   return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL &&
90          JSID_BITS(id) != JSID_TYPE_SYMBOL;
91 }
92 
JSID_TO_SYMBOL(jsid id)93 static MOZ_ALWAYS_INLINE JS::Symbol* JSID_TO_SYMBOL(jsid id) {
94   MOZ_ASSERT(JSID_IS_SYMBOL(id));
95   return (JS::Symbol*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
96 }
97 
SYMBOL_TO_JSID(JS::Symbol * sym)98 static MOZ_ALWAYS_INLINE jsid SYMBOL_TO_JSID(JS::Symbol* sym) {
99   jsid id;
100   MOZ_ASSERT(sym != nullptr);
101   MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
102   MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
103   JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
104   return id;
105 }
106 
JSID_IS_GCTHING(jsid id)107 static MOZ_ALWAYS_INLINE bool JSID_IS_GCTHING(jsid id) {
108   return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id);
109 }
110 
JSID_TO_GCTHING(jsid id)111 static MOZ_ALWAYS_INLINE JS::GCCellPtr JSID_TO_GCTHING(jsid id) {
112   void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
113   if (JSID_IS_STRING(id)) return JS::GCCellPtr(thing, JS::TraceKind::String);
114   MOZ_ASSERT(JSID_IS_SYMBOL(id));
115   return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
116 }
117 
JSID_IS_VOID(const jsid id)118 static MOZ_ALWAYS_INLINE bool JSID_IS_VOID(const jsid id) {
119   MOZ_ASSERT_IF(((size_t)JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
120                 JSID_BITS(id) == JSID_TYPE_VOID);
121   return (size_t)JSID_BITS(id) == JSID_TYPE_VOID;
122 }
123 
JSID_IS_EMPTY(const jsid id)124 static MOZ_ALWAYS_INLINE bool JSID_IS_EMPTY(const jsid id) {
125   return (size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL;
126 }
127 
128 extern JS_PUBLIC_DATA const jsid JSID_VOID;
129 extern JS_PUBLIC_DATA const jsid JSID_EMPTY;
130 
131 extern JS_PUBLIC_DATA const JS::HandleId JSID_VOIDHANDLE;
132 extern JS_PUBLIC_DATA const JS::HandleId JSID_EMPTYHANDLE;
133 
134 namespace JS {
135 
136 template <>
137 struct GCPolicy<jsid> {
138   static jsid initial() { return JSID_VOID; }
139   static void trace(JSTracer* trc, jsid* idp, const char* name) {
140     js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
141   }
142   static bool isValid(jsid id) {
143     return !JSID_IS_GCTHING(id) ||
144            js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
145   }
146 };
147 
148 #ifdef DEBUG
149 MOZ_ALWAYS_INLINE bool IdIsNotGray(jsid id) {
150   if (!JSID_IS_GCTHING(id)) return true;
151 
152   return CellIsNotGray(JSID_TO_GCTHING(id).asCell());
153 }
154 #endif
155 
156 }  // namespace JS
157 
158 namespace js {
159 
160 template <>
161 struct BarrierMethods<jsid> {
162   static gc::Cell* asGCThingOrNull(jsid id) {
163     if (JSID_IS_STRING(id))
164       return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id));
165     if (JSID_IS_SYMBOL(id))
166       return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id));
167     return nullptr;
168   }
169   static void postBarrier(jsid* idp, jsid prev, jsid next) {}
170   static void exposeToJS(jsid id) {
171     if (JSID_IS_GCTHING(id))
172       js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
173   }
174 };
175 
176 // If the jsid is a GC pointer type, convert to that type and call |f| with
177 // the pointer. If the jsid is not a GC type, calls F::defaultValue.
178 template <typename F, typename... Args>
179 auto DispatchTyped(F f, const jsid& id, Args&&... args)
180     -> decltype(f(static_cast<JSString*>(nullptr),
181                   mozilla::Forward<Args>(args)...)) {
182   if (JSID_IS_STRING(id))
183     return f(JSID_TO_STRING(id), mozilla::Forward<Args>(args)...);
184   if (JSID_IS_SYMBOL(id))
185     return f(JSID_TO_SYMBOL(id), mozilla::Forward<Args>(args)...);
186   MOZ_ASSERT(!JSID_IS_GCTHING(id));
187   return F::defaultValue(id);
188 }
189 
190 #undef id
191 
192 }  // namespace js
193 
194 #endif /* js_Id_h */
195