Line data Source code
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 : {
32 : size_t asBits;
33 9024800 : bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
34 16322 : bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
35 : } JS_HAZ_GC_POINTER;
36 : #define JSID_BITS(id) (id.asBits)
37 :
38 : #define JSID_TYPE_STRING 0x0
39 : #define JSID_TYPE_INT 0x1
40 : #define JSID_TYPE_VOID 0x2
41 : #define JSID_TYPE_SYMBOL 0x4
42 : #define JSID_TYPE_MASK 0x7
43 :
44 : // Avoid using canonical 'id' for jsid parameters since this is a magic word in
45 : // Objective-C++ which, apparently, wants to be able to #include jsapi.h.
46 : #define id iden
47 :
48 : static MOZ_ALWAYS_INLINE bool
49 2026196 : JSID_IS_STRING(jsid id)
50 : {
51 2026196 : return (JSID_BITS(id) & JSID_TYPE_MASK) == 0;
52 : }
53 :
54 : static MOZ_ALWAYS_INLINE JSString*
55 886022 : JSID_TO_STRING(jsid id)
56 : {
57 886022 : MOZ_ASSERT(JSID_IS_STRING(id));
58 886022 : return (JSString*)JSID_BITS(id);
59 : }
60 :
61 : /**
62 : * Only JSStrings that have been interned via the JSAPI can be turned into
63 : * jsids by API clients.
64 : *
65 : * N.B. if a jsid is backed by a string which has not been interned, that
66 : * string must be appropriately rooted to avoid being collected by the GC.
67 : */
68 : JS_PUBLIC_API(jsid)
69 : INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str);
70 :
71 : static MOZ_ALWAYS_INLINE bool
72 1308981 : JSID_IS_INT(jsid id)
73 : {
74 1308981 : return !!(JSID_BITS(id) & JSID_TYPE_INT);
75 : }
76 :
77 : static MOZ_ALWAYS_INLINE int32_t
78 25158 : JSID_TO_INT(jsid id)
79 : {
80 25158 : MOZ_ASSERT(JSID_IS_INT(id));
81 25158 : uint32_t bits = static_cast<uint32_t>(JSID_BITS(id)) >> 1;
82 25158 : return static_cast<int32_t>(bits);
83 : }
84 :
85 : #define JSID_INT_MIN 0
86 : #define JSID_INT_MAX INT32_MAX
87 :
88 : static MOZ_ALWAYS_INLINE bool
89 18558 : INT_FITS_IN_JSID(int32_t i)
90 : {
91 18558 : return i >= 0;
92 : }
93 :
94 : static MOZ_ALWAYS_INLINE jsid
95 17199 : INT_TO_JSID(int32_t i)
96 : {
97 : jsid id;
98 17199 : MOZ_ASSERT(INT_FITS_IN_JSID(i));
99 17199 : uint32_t bits = (static_cast<uint32_t>(i) << 1) | JSID_TYPE_INT;
100 17199 : JSID_BITS(id) = static_cast<size_t>(bits);
101 17199 : return id;
102 : }
103 :
104 : static MOZ_ALWAYS_INLINE bool
105 206829 : JSID_IS_SYMBOL(jsid id)
106 : {
107 251569 : return (JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL &&
108 251569 : JSID_BITS(id) != JSID_TYPE_SYMBOL;
109 : }
110 :
111 : static MOZ_ALWAYS_INLINE JS::Symbol*
112 19723 : JSID_TO_SYMBOL(jsid id)
113 : {
114 19723 : MOZ_ASSERT(JSID_IS_SYMBOL(id));
115 19723 : return (JS::Symbol*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
116 : }
117 :
118 : static MOZ_ALWAYS_INLINE jsid
119 10422 : SYMBOL_TO_JSID(JS::Symbol* sym)
120 : {
121 : jsid id;
122 10422 : MOZ_ASSERT(sym != nullptr);
123 10422 : MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
124 10422 : MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
125 10422 : JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
126 10422 : return id;
127 : }
128 :
129 : static MOZ_ALWAYS_INLINE bool
130 5388 : JSID_IS_GCTHING(jsid id)
131 : {
132 5388 : return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id);
133 : }
134 :
135 : static MOZ_ALWAYS_INLINE JS::GCCellPtr
136 0 : JSID_TO_GCTHING(jsid id)
137 : {
138 0 : void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
139 0 : if (JSID_IS_STRING(id))
140 0 : return JS::GCCellPtr(thing, JS::TraceKind::String);
141 0 : MOZ_ASSERT(JSID_IS_SYMBOL(id));
142 0 : return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
143 : }
144 :
145 : static MOZ_ALWAYS_INLINE bool
146 8614149 : JSID_IS_VOID(const jsid id)
147 : {
148 8614149 : MOZ_ASSERT_IF(((size_t)JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
149 : JSID_BITS(id) == JSID_TYPE_VOID);
150 8614149 : return (size_t)JSID_BITS(id) == JSID_TYPE_VOID;
151 : }
152 :
153 : static MOZ_ALWAYS_INLINE bool
154 13288312 : JSID_IS_EMPTY(const jsid id)
155 : {
156 13288312 : return (size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL;
157 : }
158 :
159 : extern JS_PUBLIC_DATA(const jsid) JSID_VOID;
160 : extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY;
161 :
162 : extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
163 : extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
164 :
165 : namespace JS {
166 :
167 : template <>
168 : struct GCPolicy<jsid>
169 : {
170 186459 : static jsid initial() { return JSID_VOID; }
171 0 : static void trace(JSTracer* trc, jsid* idp, const char* name) {
172 0 : js::UnsafeTraceManuallyBarrieredEdge(trc, idp, name);
173 0 : }
174 : };
175 :
176 : } // namespace JS
177 :
178 : namespace js {
179 :
180 : template <>
181 : struct BarrierMethods<jsid>
182 : {
183 0 : static gc::Cell* asGCThingOrNull(jsid id) {
184 0 : if (JSID_IS_STRING(id))
185 0 : return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id));
186 0 : if (JSID_IS_SYMBOL(id))
187 0 : return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id));
188 0 : return nullptr;
189 : }
190 0 : static void postBarrier(jsid* idp, jsid prev, jsid next) {}
191 0 : static void exposeToJS(jsid id) {
192 0 : if (JSID_IS_GCTHING(id))
193 0 : js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
194 0 : }
195 : };
196 :
197 : // If the jsid is a GC pointer type, convert to that type and call |f| with
198 : // the pointer. If the jsid is not a GC type, calls F::defaultValue.
199 : template <typename F, typename... Args>
200 : auto
201 6162 : DispatchTyped(F f, const jsid& id, Args&&... args)
202 : -> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...))
203 : {
204 6162 : if (JSID_IS_STRING(id))
205 4824 : return f(JSID_TO_STRING(id), mozilla::Forward<Args>(args)...);
206 1338 : if (JSID_IS_SYMBOL(id))
207 38 : return f(JSID_TO_SYMBOL(id), mozilla::Forward<Args>(args)...);
208 1300 : MOZ_ASSERT(!JSID_IS_GCTHING(id));
209 1300 : return F::defaultValue(id);
210 : }
211 :
212 : #undef id
213 :
214 : } // namespace js
215 :
216 : #endif /* js_Id_h */
|