1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
10 // [SMDOC] PropertyKey / jsid
12 // A PropertyKey is an identifier for a property of an object which is either a
13 // 31-bit unsigned integer, interned string or symbol.
15 // Also, there is an additional PropertyKey value, PropertyKey::Void(), which
16 // does not occur in JS scripts but may be used to indicate the absence of a
17 // valid key. A void PropertyKey is not a valid key and only arises as an
18 // exceptional API return value. Embeddings must not pass a void PropertyKey
19 // into JSAPI entry points expecting a PropertyKey and do not need to handle
20 // void keys in hooks receiving a PropertyKey except when explicitly noted in
23 // A PropertyKey is not implicitly convertible to or from a Value; JS_ValueToId
24 // or JS_IdToValue must be used instead.
26 // jsid is an alias for JS::PropertyKey. New code should use PropertyKey instead
29 #include "mozilla/Maybe.h"
33 #include "js/GCAnnotations.h"
34 #include "js/HeapAPI.h"
35 #include "js/RootingAPI.h"
36 #include "js/TraceKind.h"
37 #include "js/TracingAPI.h"
38 #include "js/TypeDecls.h"
42 enum class SymbolCode
: uint32_t;
48 // All keys with the low bit set are integer keys. This means the other type
49 // tags must all be even. These constants are public only for the JITs.
50 static constexpr uintptr_t IntTagBit
= 0x1;
51 // Use 0 for StringTypeTag to avoid a bitwise op for atom <-> id conversions.
52 static constexpr uintptr_t StringTypeTag
= 0x0;
53 static constexpr uintptr_t VoidTypeTag
= 0x2;
54 static constexpr uintptr_t SymbolTypeTag
= 0x4;
56 static constexpr uintptr_t TypeMask
= 0x7;
58 static constexpr uint32_t IntMin
= 0;
59 static constexpr uint32_t IntMax
= INT32_MAX
;
61 constexpr PropertyKey() : asBits_(VoidTypeTag
) {}
63 static constexpr MOZ_ALWAYS_INLINE PropertyKey
fromRawBits(uintptr_t bits
) {
69 bool operator==(const PropertyKey
& rhs
) const {
70 return asBits_
== rhs
.asBits_
;
72 bool operator!=(const PropertyKey
& rhs
) const {
73 return asBits_
!= rhs
.asBits_
;
76 MOZ_ALWAYS_INLINE
bool isVoid() const {
77 MOZ_ASSERT_IF((asBits_
& TypeMask
) == VoidTypeTag
, asBits_
== VoidTypeTag
);
78 return asBits_
== VoidTypeTag
;
81 MOZ_ALWAYS_INLINE
bool isInt() const { return !!(asBits_
& IntTagBit
); }
83 MOZ_ALWAYS_INLINE
bool isString() const {
84 return (asBits_
& TypeMask
) == StringTypeTag
;
87 MOZ_ALWAYS_INLINE
bool isSymbol() const {
88 return (asBits_
& TypeMask
) == SymbolTypeTag
;
91 MOZ_ALWAYS_INLINE
bool isGCThing() const { return isString() || isSymbol(); }
93 constexpr uintptr_t asRawBits() const { return asBits_
; }
95 MOZ_ALWAYS_INLINE
int32_t toInt() const {
97 uint32_t bits
= static_cast<uint32_t>(asBits_
) >> 1;
98 return static_cast<int32_t>(bits
);
101 MOZ_ALWAYS_INLINE JSString
* toString() const {
102 MOZ_ASSERT(isString());
103 // Use XOR instead of `& ~TypeMask` because small immediates can be
104 // encoded more efficiently on some platorms.
105 return reinterpret_cast<JSString
*>(asBits_
^ StringTypeTag
);
108 MOZ_ALWAYS_INLINE
JS::Symbol
* toSymbol() const {
109 MOZ_ASSERT(isSymbol());
110 return reinterpret_cast<JS::Symbol
*>(asBits_
^ SymbolTypeTag
);
113 js::gc::Cell
* toGCThing() const {
114 MOZ_ASSERT(isGCThing());
115 return reinterpret_cast<js::gc::Cell
*>(asBits_
& ~TypeMask
);
118 GCCellPtr
toGCCellPtr() const {
119 js::gc::Cell
* thing
= toGCThing();
121 return JS::GCCellPtr(thing
, JS::TraceKind::String
);
123 MOZ_ASSERT(isSymbol());
124 return JS::GCCellPtr(thing
, JS::TraceKind::Symbol
);
127 bool isPrivateName() const;
129 bool isWellKnownSymbol(JS::SymbolCode code
) const;
131 // A void PropertyKey. This is equivalent to a PropertyKey created by the
132 // default constructor.
133 static constexpr PropertyKey
Void() { return PropertyKey(); }
135 static constexpr bool fitsInInt(int32_t i
) { return i
>= 0; }
137 static constexpr PropertyKey
Int(int32_t i
) {
138 MOZ_ASSERT(fitsInInt(i
));
139 uint32_t bits
= (static_cast<uint32_t>(i
) << 1) | IntTagBit
;
140 return PropertyKey::fromRawBits(bits
);
143 static PropertyKey
Symbol(JS::Symbol
* sym
) {
144 MOZ_ASSERT(sym
!= nullptr);
145 MOZ_ASSERT((uintptr_t(sym
) & TypeMask
) == 0);
146 MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell
*>(sym
)));
147 return PropertyKey::fromRawBits(uintptr_t(sym
) | SymbolTypeTag
);
150 // Must not be used on atoms that are representable as integer PropertyKey.
151 // Prefer NameToId or AtomToId over this function:
153 // A PropertyName is an atom that does not contain an integer in the range
154 // [0, UINT32_MAX]. However, PropertyKey can only hold an integer in the range
155 // [0, IntMax] (where IntMax == 2^31-1). Thus, for the range of integers
156 // (IntMax, UINT32_MAX], to represent as a 'id', it must be
157 // the case id.isString() and id.toString()->isIndex(). In most
158 // cases when creating a PropertyKey, code does not have to care about
159 // this corner case because:
161 // - When given an arbitrary JSAtom*, AtomToId must be used, which checks for
162 // integer atoms representable as integer PropertyKey, and does this
165 // - When given a PropertyName*, NameToId can be used which does not need
166 // to do any dynamic checks.
168 // Thus, it is only the rare third case which needs this function, which
169 // handles any JSAtom* that is known not to be representable with an int
171 static PropertyKey
NonIntAtom(JSAtom
* atom
) {
172 MOZ_ASSERT((uintptr_t(atom
) & TypeMask
) == 0);
173 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom
));
174 return PropertyKey::fromRawBits(uintptr_t(atom
) | StringTypeTag
);
177 // The JSAtom/JSString type exposed to embedders is opaque.
178 static PropertyKey
NonIntAtom(JSString
* str
) {
179 MOZ_ASSERT((uintptr_t(str
) & TypeMask
) == 0);
180 MOZ_ASSERT(PropertyKey::isNonIntAtom(str
));
181 return PropertyKey::fromRawBits(uintptr_t(str
) | StringTypeTag
);
184 // This API can be used by embedders to convert pinned (aka interned) strings,
185 // as created by JS_AtomizeAndPinString, into PropertyKeys. This means the
186 // string does not have to be explicitly rooted.
188 // Only use this API when absolutely necessary, otherwise use JS_StringToId.
189 static PropertyKey
fromPinnedString(JSString
* str
);
192 // All string PropertyKeys are actually atomized.
193 MOZ_ALWAYS_INLINE
bool isAtom() const { return isString(); }
195 MOZ_ALWAYS_INLINE
bool isAtom(JSAtom
* atom
) const {
196 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom
));
197 return isAtom() && toAtom() == atom
;
200 MOZ_ALWAYS_INLINE JSAtom
* toAtom() const {
201 return reinterpret_cast<JSAtom
*>(toString());
203 MOZ_ALWAYS_INLINE JSLinearString
* toLinearString() const {
204 return reinterpret_cast<JSLinearString
*>(toString());
208 static bool isNonIntAtom(JSAtom
* atom
);
209 static bool isNonIntAtom(JSString
* atom
);
214 using jsid
= JS::PropertyKey
;
218 // Handle<PropertyKey> version of PropertyKey::Void().
219 extern JS_PUBLIC_DATA
const JS::HandleId VoidHandlePropertyKey
;
222 struct GCPolicy
<jsid
> {
223 static void trace(JSTracer
* trc
, jsid
* idp
, const char* name
) {
224 // This should only be called as part of root marking since that's the only
225 // time we should trace unbarriered GC thing pointers. This will assert if
226 // called at other times.
227 TraceRoot(trc
, idp
, name
);
229 static bool isValid(jsid id
) {
230 return !id
.isGCThing() ||
231 js::gc::IsCellPointerValid(id
.toGCCellPtr().asCell());
234 static bool isTenured(jsid id
) {
235 MOZ_ASSERT_IF(id
.isGCThing(),
236 !js::gc::IsInsideNursery(id
.toGCCellPtr().asCell()));
242 MOZ_ALWAYS_INLINE
void AssertIdIsNotGray(jsid id
) {
243 if (id
.isGCThing()) {
244 AssertCellIsNotGray(id
.toGCCellPtr().asCell());
250 * Get one of the well-known symbols defined by ES6 as PropertyKey. This is
251 * equivalent to calling JS::GetWellKnownSymbol and then creating a PropertyKey.
253 * `which` must be in the range [0, WellKnownSymbolLimit).
255 extern JS_PUBLIC_API PropertyKey
GetWellKnownSymbolKey(JSContext
* cx
,
263 struct BarrierMethods
<jsid
> {
264 static gc::Cell
* asGCThingOrNull(jsid id
) {
265 if (id
.isGCThing()) {
266 return id
.toGCThing();
270 static void postWriteBarrier(jsid
* idp
, jsid prev
, jsid next
) {
271 MOZ_ASSERT_IF(next
.isString(), !gc::IsInsideNursery(next
.toString()));
273 static void exposeToJS(jsid id
) {
274 if (id
.isGCThing()) {
275 js::gc::ExposeGCThingToActiveJS(id
.toGCCellPtr());
278 static void readBarrier(jsid id
) {
279 if (id
.isGCThing()) {
280 js::gc::IncrementalReadBarrier(id
.toGCCellPtr());
285 // If the jsid is a GC pointer type, convert to that type and call |f| with the
286 // pointer and return the result wrapped in a Maybe, otherwise return None().
287 template <typename F
>
288 auto MapGCThingTyped(const jsid
& id
, F
&& f
) {
290 return mozilla::Some(f(id
.toString()));
293 return mozilla::Some(f(id
.toSymbol()));
295 MOZ_ASSERT(!id
.isGCThing());
296 using ReturnType
= decltype(f(static_cast<JSString
*>(nullptr)));
297 return mozilla::Maybe
<ReturnType
>();
300 // If the jsid is a GC pointer type, convert to that type and call |f| with the
301 // pointer. Return whether this happened.
302 template <typename F
>
303 bool ApplyGCThingTyped(const jsid
& id
, F
&& f
) {
304 return MapGCThingTyped(id
,
312 template <typename Wrapper
>
313 class WrappedPtrOperations
<JS::PropertyKey
, Wrapper
> {
314 const JS::PropertyKey
& id() const {
315 return static_cast<const Wrapper
*>(this)->get();
319 bool isVoid() const { return id().isVoid(); }
320 bool isInt() const { return id().isInt(); }
321 bool isString() const { return id().isString(); }
322 bool isSymbol() const { return id().isSymbol(); }
323 bool isGCThing() const { return id().isGCThing(); }
325 int32_t toInt() const { return id().toInt(); }
326 JSString
* toString() const { return id().toString(); }
327 JS::Symbol
* toSymbol() const { return id().toSymbol(); }
329 bool isPrivateName() const { return id().isPrivateName(); }
331 bool isWellKnownSymbol(JS::SymbolCode code
) const {
332 return id().isWellKnownSymbol(code
);
335 uintptr_t asRawBits() const { return id().asRawBits(); }
338 bool isAtom() const { return id().isAtom(); }
339 bool isAtom(JSAtom
* atom
) const { return id().isAtom(atom
); }
340 JSAtom
* toAtom() const { return id().toAtom(); }
341 JSLinearString
* toLinearString() const { return id().toLinearString(); }