Bug 1874684 - Part 31: Correctly reject invalid durations in some RoundDuration calls...
[gecko.git] / js / public / Id.h
blob087f9e61c9a79793d7e4b11294fd85e8f3dcb8f8
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/. */
7 #ifndef js_Id_h
8 #define js_Id_h
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
21 // the API contract.
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
27 // of jsid.
29 #include "mozilla/Maybe.h"
31 #include "jstypes.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"
40 namespace js {
41 class JS_PUBLIC_API GenericPrinter;
42 class JSONPrinter;
43 } // namespace js
45 namespace JS {
47 enum class SymbolCode : uint32_t;
49 class PropertyKey {
50 uintptr_t asBits_;
52 public:
53 // All keys with the low bit set are integer keys. This means the other type
54 // tags must all be even. These constants are public only for the JITs.
55 static constexpr uintptr_t IntTagBit = 0x1;
56 // Use 0 for StringTypeTag to avoid a bitwise op for atom <-> id conversions.
57 static constexpr uintptr_t StringTypeTag = 0x0;
58 static constexpr uintptr_t VoidTypeTag = 0x2;
59 static constexpr uintptr_t SymbolTypeTag = 0x4;
60 // (0x6 is unused)
61 static constexpr uintptr_t TypeMask = 0x7;
63 static constexpr uint32_t IntMin = 0;
64 static constexpr uint32_t IntMax = INT32_MAX;
66 constexpr PropertyKey() : asBits_(VoidTypeTag) {}
68 static constexpr MOZ_ALWAYS_INLINE PropertyKey fromRawBits(uintptr_t bits) {
69 PropertyKey id;
70 id.asBits_ = bits;
71 return id;
74 bool operator==(const PropertyKey& rhs) const {
75 return asBits_ == rhs.asBits_;
77 bool operator!=(const PropertyKey& rhs) const {
78 return asBits_ != rhs.asBits_;
81 MOZ_ALWAYS_INLINE bool isVoid() const {
82 MOZ_ASSERT_IF((asBits_ & TypeMask) == VoidTypeTag, asBits_ == VoidTypeTag);
83 return asBits_ == VoidTypeTag;
86 MOZ_ALWAYS_INLINE bool isInt() const { return !!(asBits_ & IntTagBit); }
88 MOZ_ALWAYS_INLINE bool isString() const {
89 return (asBits_ & TypeMask) == StringTypeTag;
92 MOZ_ALWAYS_INLINE bool isSymbol() const {
93 return (asBits_ & TypeMask) == SymbolTypeTag;
96 MOZ_ALWAYS_INLINE bool isGCThing() const { return isString() || isSymbol(); }
98 constexpr uintptr_t asRawBits() const { return asBits_; }
100 MOZ_ALWAYS_INLINE int32_t toInt() const {
101 MOZ_ASSERT(isInt());
102 uint32_t bits = static_cast<uint32_t>(asBits_) >> 1;
103 return static_cast<int32_t>(bits);
106 MOZ_ALWAYS_INLINE JSString* toString() const {
107 MOZ_ASSERT(isString());
108 // Use XOR instead of `& ~TypeMask` because small immediates can be
109 // encoded more efficiently on some platorms.
110 return reinterpret_cast<JSString*>(asBits_ ^ StringTypeTag);
113 MOZ_ALWAYS_INLINE JS::Symbol* toSymbol() const {
114 MOZ_ASSERT(isSymbol());
115 return reinterpret_cast<JS::Symbol*>(asBits_ ^ SymbolTypeTag);
118 js::gc::Cell* toGCThing() const {
119 MOZ_ASSERT(isGCThing());
120 return reinterpret_cast<js::gc::Cell*>(asBits_ & ~TypeMask);
123 GCCellPtr toGCCellPtr() const {
124 js::gc::Cell* thing = toGCThing();
125 if (isString()) {
126 return JS::GCCellPtr(thing, JS::TraceKind::String);
128 MOZ_ASSERT(isSymbol());
129 return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
132 bool isPrivateName() const;
134 bool isWellKnownSymbol(JS::SymbolCode code) const;
136 // A void PropertyKey. This is equivalent to a PropertyKey created by the
137 // default constructor.
138 static constexpr PropertyKey Void() { return PropertyKey(); }
140 static constexpr bool fitsInInt(int32_t i) { return i >= 0; }
142 static constexpr PropertyKey Int(int32_t i) {
143 MOZ_ASSERT(fitsInInt(i));
144 uint32_t bits = (static_cast<uint32_t>(i) << 1) | IntTagBit;
145 return PropertyKey::fromRawBits(bits);
148 static PropertyKey Symbol(JS::Symbol* sym) {
149 MOZ_ASSERT(sym != nullptr);
150 MOZ_ASSERT((uintptr_t(sym) & TypeMask) == 0);
151 MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
152 return PropertyKey::fromRawBits(uintptr_t(sym) | SymbolTypeTag);
155 // Must not be used on atoms that are representable as integer PropertyKey.
156 // Prefer NameToId or AtomToId over this function:
158 // A PropertyName is an atom that does not contain an integer in the range
159 // [0, UINT32_MAX]. However, PropertyKey can only hold an integer in the range
160 // [0, IntMax] (where IntMax == 2^31-1). Thus, for the range of integers
161 // (IntMax, UINT32_MAX], to represent as a 'id', it must be
162 // the case id.isString() and id.toString()->isIndex(). In most
163 // cases when creating a PropertyKey, code does not have to care about
164 // this corner case because:
166 // - When given an arbitrary JSAtom*, AtomToId must be used, which checks for
167 // integer atoms representable as integer PropertyKey, and does this
168 // conversion.
170 // - When given a PropertyName*, NameToId can be used which does not need
171 // to do any dynamic checks.
173 // Thus, it is only the rare third case which needs this function, which
174 // handles any JSAtom* that is known not to be representable with an int
175 // PropertyKey.
176 static PropertyKey NonIntAtom(JSAtom* atom) {
177 MOZ_ASSERT((uintptr_t(atom) & TypeMask) == 0);
178 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom));
179 return PropertyKey::fromRawBits(uintptr_t(atom) | StringTypeTag);
182 // The JSAtom/JSString type exposed to embedders is opaque.
183 static PropertyKey NonIntAtom(JSString* str) {
184 MOZ_ASSERT((uintptr_t(str) & TypeMask) == 0);
185 MOZ_ASSERT(PropertyKey::isNonIntAtom(str));
186 return PropertyKey::fromRawBits(uintptr_t(str) | StringTypeTag);
189 // This API can be used by embedders to convert pinned (aka interned) strings,
190 // as created by JS_AtomizeAndPinString, into PropertyKeys. This means the
191 // string does not have to be explicitly rooted.
193 // Only use this API when absolutely necessary, otherwise use JS_StringToId.
194 static PropertyKey fromPinnedString(JSString* str);
196 // Internal API!
197 // All string PropertyKeys are actually atomized.
198 MOZ_ALWAYS_INLINE bool isAtom() const { return isString(); }
200 MOZ_ALWAYS_INLINE bool isAtom(JSAtom* atom) const {
201 MOZ_ASSERT(PropertyKey::isNonIntAtom(atom));
202 return isAtom() && toAtom() == atom;
205 MOZ_ALWAYS_INLINE JSAtom* toAtom() const {
206 return reinterpret_cast<JSAtom*>(toString());
208 MOZ_ALWAYS_INLINE JSLinearString* toLinearString() const {
209 return reinterpret_cast<JSLinearString*>(toString());
212 #if defined(DEBUG) || defined(JS_JITSPEW)
213 void dump() const;
214 void dump(js::GenericPrinter& out) const;
215 void dump(js::JSONPrinter& json) const;
217 void dumpFields(js::JSONPrinter& json) const;
218 void dumpPropertyName(js::GenericPrinter& out) const;
219 void dumpStringContent(js::GenericPrinter& out) const;
220 #endif
222 private:
223 static bool isNonIntAtom(JSAtom* atom);
224 static bool isNonIntAtom(JSString* atom);
225 } JS_HAZ_GC_POINTER;
227 } // namespace JS
229 using jsid = JS::PropertyKey;
231 namespace JS {
233 // Handle<PropertyKey> version of PropertyKey::Void().
234 extern JS_PUBLIC_DATA const JS::HandleId VoidHandlePropertyKey;
236 template <>
237 struct GCPolicy<jsid> {
238 static void trace(JSTracer* trc, jsid* idp, const char* name) {
239 // This should only be called as part of root marking since that's the only
240 // time we should trace unbarriered GC thing pointers. This will assert if
241 // called at other times.
242 TraceRoot(trc, idp, name);
244 static bool isValid(jsid id) {
245 return !id.isGCThing() ||
246 js::gc::IsCellPointerValid(id.toGCCellPtr().asCell());
249 static bool isTenured(jsid id) {
250 MOZ_ASSERT_IF(id.isGCThing(),
251 !js::gc::IsInsideNursery(id.toGCCellPtr().asCell()));
252 return true;
256 #ifdef DEBUG
257 MOZ_ALWAYS_INLINE void AssertIdIsNotGray(jsid id) {
258 if (id.isGCThing()) {
259 AssertCellIsNotGray(id.toGCCellPtr().asCell());
262 #endif
265 * Get one of the well-known symbols defined by ES6 as PropertyKey. This is
266 * equivalent to calling JS::GetWellKnownSymbol and then creating a PropertyKey.
268 * `which` must be in the range [0, WellKnownSymbolLimit).
270 extern JS_PUBLIC_API PropertyKey GetWellKnownSymbolKey(JSContext* cx,
271 SymbolCode which);
274 * Generate getter/setter id for given id, by adding "get " or "set " prefix.
276 extern JS_PUBLIC_API bool ToGetterId(
277 JSContext* cx, JS::Handle<JS::PropertyKey> id,
278 JS::MutableHandle<JS::PropertyKey> getterId);
279 extern JS_PUBLIC_API bool ToSetterId(
280 JSContext* cx, JS::Handle<JS::PropertyKey> id,
281 JS::MutableHandle<JS::PropertyKey> setterId);
283 } // namespace JS
285 namespace js {
287 template <>
288 struct BarrierMethods<jsid> {
289 static gc::Cell* asGCThingOrNull(jsid id) {
290 if (id.isGCThing()) {
291 return id.toGCThing();
293 return nullptr;
295 static void postWriteBarrier(jsid* idp, jsid prev, jsid next) {
296 MOZ_ASSERT_IF(next.isString(), !gc::IsInsideNursery(next.toString()));
298 static void exposeToJS(jsid id) {
299 if (id.isGCThing()) {
300 js::gc::ExposeGCThingToActiveJS(id.toGCCellPtr());
303 static void readBarrier(jsid id) {
304 if (id.isGCThing()) {
305 js::gc::IncrementalReadBarrier(id.toGCCellPtr());
310 // If the jsid is a GC pointer type, convert to that type and call |f| with the
311 // pointer and return the result wrapped in a Maybe, otherwise return None().
312 template <typename F>
313 auto MapGCThingTyped(const jsid& id, F&& f) {
314 if (id.isString()) {
315 return mozilla::Some(f(id.toString()));
317 if (id.isSymbol()) {
318 return mozilla::Some(f(id.toSymbol()));
320 MOZ_ASSERT(!id.isGCThing());
321 using ReturnType = decltype(f(static_cast<JSString*>(nullptr)));
322 return mozilla::Maybe<ReturnType>();
325 // If the jsid is a GC pointer type, convert to that type and call |f| with the
326 // pointer. Return whether this happened.
327 template <typename F>
328 bool ApplyGCThingTyped(const jsid& id, F&& f) {
329 return MapGCThingTyped(id,
330 [&f](auto t) {
331 f(t);
332 return true;
334 .isSome();
337 template <typename Wrapper>
338 class WrappedPtrOperations<JS::PropertyKey, Wrapper> {
339 const JS::PropertyKey& id() const {
340 return static_cast<const Wrapper*>(this)->get();
343 public:
344 bool isVoid() const { return id().isVoid(); }
345 bool isInt() const { return id().isInt(); }
346 bool isString() const { return id().isString(); }
347 bool isSymbol() const { return id().isSymbol(); }
348 bool isGCThing() const { return id().isGCThing(); }
350 int32_t toInt() const { return id().toInt(); }
351 JSString* toString() const { return id().toString(); }
352 JS::Symbol* toSymbol() const { return id().toSymbol(); }
354 bool isPrivateName() const { return id().isPrivateName(); }
356 bool isWellKnownSymbol(JS::SymbolCode code) const {
357 return id().isWellKnownSymbol(code);
360 uintptr_t asRawBits() const { return id().asRawBits(); }
362 // Internal API
363 bool isAtom() const { return id().isAtom(); }
364 bool isAtom(JSAtom* atom) const { return id().isAtom(atom); }
365 JSAtom* toAtom() const { return id().toAtom(); }
366 JSLinearString* toLinearString() const { return id().toLinearString(); }
369 } // namespace js
371 #endif /* js_Id_h */