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