2018-11-30 22:52:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
2018-11-30 18:39:55 +03:00
|
|
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
2013-08-22 09:26:56 +04:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#ifndef js_Id_h
|
|
|
|
#define js_Id_h
|
|
|
|
|
2019-11-15 19:49:08 +03:00
|
|
|
// [SMDOC] Property Key / JSID
|
|
|
|
//
|
2013-08-28 06:59:14 +04:00
|
|
|
// A jsid is an identifier for a property or method of an object which is
|
2015-03-13 02:09:21 +03:00
|
|
|
// either a 31-bit unsigned integer, interned string or symbol.
|
2013-08-28 06:59:14 +04:00
|
|
|
//
|
2013-12-17 06:27:43 +04:00
|
|
|
// Also, there is an additional jsid value, JSID_VOID, which does not occur in
|
2013-08-28 06:59:14 +04:00
|
|
|
// JS scripts but may be used to indicate the absence of a valid jsid. A void
|
|
|
|
// jsid is not a valid id and only arises as an exceptional API return value,
|
2013-12-17 06:27:43 +04:00
|
|
|
// such as in JS_NextProperty. Embeddings must not pass JSID_VOID into JSAPI
|
|
|
|
// entry points expecting a jsid and do not need to handle JSID_VOID in hooks
|
2013-08-28 06:59:14 +04:00
|
|
|
// receiving a jsid except when explicitly noted in the API contract.
|
|
|
|
//
|
2015-03-13 02:09:21 +03:00
|
|
|
// A jsid is not implicitly convertible to or from a Value; JS_ValueToId or
|
2013-08-28 06:59:14 +04:00
|
|
|
// JS_IdToValue must be used instead.
|
|
|
|
|
2019-02-09 11:26:27 +03:00
|
|
|
#include "mozilla/Maybe.h"
|
|
|
|
|
2013-08-22 09:26:56 +04:00
|
|
|
#include "jstypes.h"
|
|
|
|
|
2014-02-21 01:38:57 +04:00
|
|
|
#include "js/HeapAPI.h"
|
2013-09-12 03:51:17 +04:00
|
|
|
#include "js/RootingAPI.h"
|
2013-08-28 06:59:14 +04:00
|
|
|
#include "js/TypeDecls.h"
|
2013-08-22 09:26:56 +04:00
|
|
|
#include "js/Utility.h"
|
|
|
|
|
2018-05-30 14:51:58 +03:00
|
|
|
// All jsids with the low bit set are integer ids. This means the other type
|
|
|
|
// tags must all be even.
|
|
|
|
#define JSID_TYPE_INT_BIT 0x1
|
|
|
|
|
|
|
|
// Use 0 for JSID_TYPE_STRING to avoid a bitwise op for atom <-> id conversions.
|
2018-05-11 13:01:32 +03:00
|
|
|
#define JSID_TYPE_STRING 0x0
|
|
|
|
#define JSID_TYPE_VOID 0x2
|
|
|
|
#define JSID_TYPE_SYMBOL 0x4
|
2018-05-30 14:51:58 +03:00
|
|
|
#define JSID_TYPE_EMPTY 0x6
|
2018-05-11 13:01:32 +03:00
|
|
|
#define JSID_TYPE_MASK 0x7
|
|
|
|
|
2018-11-20 15:01:42 +03:00
|
|
|
namespace JS {
|
2018-05-11 13:01:32 +03:00
|
|
|
|
2018-11-20 15:01:42 +03:00
|
|
|
struct PropertyKey {
|
|
|
|
size_t asBits;
|
2018-05-11 13:01:32 +03:00
|
|
|
|
2018-11-20 15:01:42 +03:00
|
|
|
constexpr PropertyKey() : asBits(JSID_TYPE_VOID) {}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-11-20 15:01:42 +03:00
|
|
|
static constexpr MOZ_ALWAYS_INLINE PropertyKey fromRawBits(size_t bits) {
|
|
|
|
PropertyKey id;
|
2018-05-11 13:01:32 +03:00
|
|
|
id.asBits = bits;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2018-11-20 15:01:42 +03:00
|
|
|
bool operator==(const PropertyKey& rhs) const { return asBits == rhs.asBits; }
|
|
|
|
bool operator!=(const PropertyKey& rhs) const { return asBits != rhs.asBits; }
|
2018-12-22 17:17:44 +03:00
|
|
|
|
|
|
|
MOZ_ALWAYS_INLINE bool isInt() const {
|
|
|
|
return !!(asBits & JSID_TYPE_INT_BIT);
|
|
|
|
}
|
2018-12-28 18:48:06 +03:00
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
MOZ_ALWAYS_INLINE bool isString() const {
|
|
|
|
return (asBits & JSID_TYPE_MASK) == JSID_TYPE_STRING;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ALWAYS_INLINE bool isSymbol() const {
|
|
|
|
return (asBits & JSID_TYPE_MASK) == JSID_TYPE_SYMBOL;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ALWAYS_INLINE int32_t toInt() const {
|
|
|
|
MOZ_ASSERT(isInt());
|
|
|
|
uint32_t bits = static_cast<uint32_t>(asBits) >> 1;
|
|
|
|
return static_cast<int32_t>(bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ALWAYS_INLINE JSString* toString() const {
|
|
|
|
MOZ_ASSERT(isString());
|
|
|
|
// Use XOR instead of `& ~JSID_TYPE_MASK` because small immediates can be
|
|
|
|
// encoded more efficiently on some platorms.
|
|
|
|
return reinterpret_cast<JSString*>(asBits ^ JSID_TYPE_STRING);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ALWAYS_INLINE JS::Symbol* toSymbol() const {
|
|
|
|
MOZ_ASSERT(isSymbol());
|
|
|
|
return reinterpret_cast<JS::Symbol*>(asBits ^ JSID_TYPE_SYMBOL);
|
|
|
|
}
|
|
|
|
|
2015-09-29 23:39:33 +03:00
|
|
|
} JS_HAZ_GC_POINTER;
|
2018-11-20 15:01:42 +03:00
|
|
|
|
|
|
|
} // namespace JS
|
|
|
|
|
|
|
|
using jsid = JS::PropertyKey;
|
|
|
|
|
2013-12-11 03:45:41 +04:00
|
|
|
#define JSID_BITS(id) (id.asBits)
|
2013-08-22 09:26:56 +04:00
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_STRING(jsid id) { return id.isString(); }
|
2013-08-22 09:26:56 +04:00
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
static MOZ_ALWAYS_INLINE JSString* JSID_TO_STRING(jsid id) {
|
2020-04-26 20:01:39 +03:00
|
|
|
return id.toString();
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:27:16 +03:00
|
|
|
/**
|
2014-07-30 19:37:03 +04:00
|
|
|
* Only JSStrings that have been interned via the JSAPI can be turned into
|
|
|
|
* jsids by API clients.
|
|
|
|
*
|
|
|
|
* N.B. if a jsid is backed by a string which has not been interned, that
|
|
|
|
* string must be appropriately rooted to avoid being collected by the GC.
|
|
|
|
*/
|
2015-03-29 01:22:11 +03:00
|
|
|
JS_PUBLIC_API jsid INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str);
|
2014-07-30 19:37:03 +04:00
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_INT(jsid id) { return id.isInt(); }
|
2013-08-22 09:26:56 +04:00
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
static MOZ_ALWAYS_INLINE int32_t JSID_TO_INT(jsid id) { return id.toInt(); }
|
2013-08-22 09:26:56 +04:00
|
|
|
|
|
|
|
#define JSID_INT_MIN 0
|
|
|
|
#define JSID_INT_MAX INT32_MAX
|
|
|
|
|
2014-01-25 08:14:56 +04:00
|
|
|
static MOZ_ALWAYS_INLINE bool INT_FITS_IN_JSID(int32_t i) { return i >= 0; }
|
2013-08-22 09:26:56 +04:00
|
|
|
|
2014-01-25 08:14:56 +04:00
|
|
|
static MOZ_ALWAYS_INLINE jsid INT_TO_JSID(int32_t i) {
|
2013-08-22 09:26:56 +04:00
|
|
|
jsid id;
|
2014-02-18 10:24:15 +04:00
|
|
|
MOZ_ASSERT(INT_FITS_IN_JSID(i));
|
2018-05-30 14:51:58 +03:00
|
|
|
uint32_t bits = (static_cast<uint32_t>(i) << 1) | JSID_TYPE_INT_BIT;
|
2016-12-01 11:23:21 +03:00
|
|
|
JSID_BITS(id) = static_cast<size_t>(bits);
|
2013-08-22 09:26:56 +04:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_SYMBOL(jsid id) { return id.isSymbol(); }
|
2013-08-22 09:26:56 +04:00
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
static MOZ_ALWAYS_INLINE JS::Symbol* JSID_TO_SYMBOL(jsid id) {
|
2020-04-26 20:01:39 +03:00
|
|
|
return id.toSymbol();
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2015-03-29 01:22:11 +03:00
|
|
|
static MOZ_ALWAYS_INLINE jsid SYMBOL_TO_JSID(JS::Symbol* sym) {
|
2013-08-22 09:26:56 +04:00
|
|
|
jsid id;
|
2014-06-23 19:56:52 +04:00
|
|
|
MOZ_ASSERT(sym != nullptr);
|
|
|
|
MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
|
2015-03-29 01:22:11 +03:00
|
|
|
MOZ_ASSERT(!js::gc::IsInsideNursery(reinterpret_cast<js::gc::Cell*>(sym)));
|
2014-06-23 19:56:52 +04:00
|
|
|
JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
|
2013-08-22 09:26:56 +04:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2014-01-25 08:14:56 +04:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_GCTHING(jsid id) {
|
2020-04-26 20:01:39 +03:00
|
|
|
return id.isString() || id.isSymbol();
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2014-12-05 20:38:34 +03:00
|
|
|
static MOZ_ALWAYS_INLINE JS::GCCellPtr JSID_TO_GCTHING(jsid id) {
|
2015-03-29 01:22:11 +03:00
|
|
|
void* thing = (void*)(JSID_BITS(id) & ~(size_t)JSID_TYPE_MASK);
|
2018-09-06 13:11:07 +03:00
|
|
|
if (JSID_IS_STRING(id)) {
|
2015-05-22 20:40:24 +03:00
|
|
|
return JS::GCCellPtr(thing, JS::TraceKind::String);
|
2018-09-06 13:11:07 +03:00
|
|
|
}
|
2014-12-05 20:38:34 +03:00
|
|
|
MOZ_ASSERT(JSID_IS_SYMBOL(id));
|
2015-05-22 20:40:24 +03:00
|
|
|
return JS::GCCellPtr(thing, JS::TraceKind::Symbol);
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2014-01-25 08:14:56 +04:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_VOID(const jsid id) {
|
2018-05-30 14:51:58 +03:00
|
|
|
MOZ_ASSERT_IF((JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_VOID,
|
|
|
|
JSID_BITS(id) == JSID_TYPE_VOID);
|
|
|
|
return JSID_BITS(id) == JSID_TYPE_VOID;
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2014-01-25 08:14:56 +04:00
|
|
|
static MOZ_ALWAYS_INLINE bool JSID_IS_EMPTY(const jsid id) {
|
2018-05-30 14:51:58 +03:00
|
|
|
MOZ_ASSERT_IF((JSID_BITS(id) & JSID_TYPE_MASK) == JSID_TYPE_EMPTY,
|
|
|
|
JSID_BITS(id) == JSID_TYPE_EMPTY);
|
|
|
|
return JSID_BITS(id) == JSID_TYPE_EMPTY;
|
2013-08-22 09:26:56 +04:00
|
|
|
}
|
|
|
|
|
2018-05-11 13:01:32 +03:00
|
|
|
constexpr const jsid JSID_VOID;
|
2018-05-30 14:51:58 +03:00
|
|
|
constexpr const jsid JSID_EMPTY = jsid::fromRawBits(JSID_TYPE_EMPTY);
|
2013-12-17 06:27:43 +04:00
|
|
|
|
2018-11-19 20:02:47 +03:00
|
|
|
extern JS_PUBLIC_DATA const JS::HandleId JSID_VOIDHANDLE;
|
|
|
|
extern JS_PUBLIC_DATA const JS::HandleId JSID_EMPTYHANDLE;
|
2013-09-06 03:08:57 +04:00
|
|
|
|
2016-04-26 19:18:48 +03:00
|
|
|
namespace JS {
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct GCPolicy<jsid> {
|
|
|
|
static void trace(JSTracer* trc, jsid* idp, const char* name) {
|
2019-10-09 13:30:02 +03:00
|
|
|
// It's not safe to trace unbarriered pointers except as part of root
|
|
|
|
// marking.
|
|
|
|
UnsafeTraceRoot(trc, idp, name);
|
2016-04-26 19:18:48 +03:00
|
|
|
}
|
2017-09-19 14:31:31 +03:00
|
|
|
static bool isValid(jsid id) {
|
|
|
|
return !JSID_IS_GCTHING(id) ||
|
|
|
|
js::gc::IsCellPointerValid(JSID_TO_GCTHING(id).asCell());
|
|
|
|
}
|
2016-04-26 19:18:48 +03:00
|
|
|
};
|
|
|
|
|
2018-02-27 16:01:49 +03:00
|
|
|
#ifdef DEBUG
|
2018-12-07 00:28:10 +03:00
|
|
|
MOZ_ALWAYS_INLINE void AssertIdIsNotGray(jsid id) {
|
|
|
|
if (JSID_IS_GCTHING(id)) {
|
|
|
|
AssertCellIsNotGray(JSID_TO_GCTHING(id).asCell());
|
2018-09-06 13:11:07 +03:00
|
|
|
}
|
2018-02-27 16:01:49 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-04-26 19:18:48 +03:00
|
|
|
} // namespace JS
|
|
|
|
|
2013-09-12 03:51:17 +04:00
|
|
|
namespace js {
|
|
|
|
|
2015-12-28 21:11:40 +03:00
|
|
|
template <>
|
|
|
|
struct BarrierMethods<jsid> {
|
2017-07-11 18:51:37 +03:00
|
|
|
static gc::Cell* asGCThingOrNull(jsid id) {
|
2018-09-06 13:11:07 +03:00
|
|
|
if (JSID_IS_STRING(id)) {
|
2017-07-11 18:51:37 +03:00
|
|
|
return reinterpret_cast<gc::Cell*>(JSID_TO_STRING(id));
|
2018-09-06 13:11:07 +03:00
|
|
|
}
|
|
|
|
if (JSID_IS_SYMBOL(id)) {
|
2017-07-11 18:51:37 +03:00
|
|
|
return reinterpret_cast<gc::Cell*>(JSID_TO_SYMBOL(id));
|
|
|
|
}
|
|
|
|
return nullptr;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2019-09-20 13:09:19 +03:00
|
|
|
static void postWriteBarrier(jsid* idp, jsid prev, jsid next) {
|
|
|
|
MOZ_ASSERT_IF(JSID_IS_STRING(next),
|
|
|
|
!gc::IsInsideNursery(JSID_TO_STRING(next)));
|
2019-03-27 19:26:09 +03:00
|
|
|
}
|
2016-02-07 20:08:55 +03:00
|
|
|
static void exposeToJS(jsid id) {
|
2018-09-06 13:11:07 +03:00
|
|
|
if (JSID_IS_GCTHING(id)) {
|
2016-02-07 20:08:55 +03:00
|
|
|
js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2013-09-12 03:51:17 +04:00
|
|
|
};
|
|
|
|
|
2019-02-09 11:26:27 +03:00
|
|
|
// If the jsid is a GC pointer type, convert to that type and call |f| with the
|
|
|
|
// pointer and return the result wrapped in a Maybe, otherwise return None().
|
|
|
|
template <typename F>
|
|
|
|
auto MapGCThingTyped(const jsid& id, F&& f) {
|
2018-09-06 13:11:07 +03:00
|
|
|
if (JSID_IS_STRING(id)) {
|
2019-02-09 11:26:27 +03:00
|
|
|
return mozilla::Some(f(JSID_TO_STRING(id)));
|
2018-09-06 13:11:07 +03:00
|
|
|
}
|
|
|
|
if (JSID_IS_SYMBOL(id)) {
|
2019-02-09 11:26:27 +03:00
|
|
|
return mozilla::Some(f(JSID_TO_SYMBOL(id)));
|
2018-09-06 13:11:07 +03:00
|
|
|
}
|
2015-04-23 20:42:31 +03:00
|
|
|
MOZ_ASSERT(!JSID_IS_GCTHING(id));
|
2019-02-09 11:26:27 +03:00
|
|
|
using ReturnType = decltype(f(static_cast<JSString*>(nullptr)));
|
|
|
|
return mozilla::Maybe<ReturnType>();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the jsid is a GC pointer type, convert to that type and call |f| with the
|
|
|
|
// pointer. Return whether this happened.
|
|
|
|
template <typename F>
|
|
|
|
bool ApplyGCThingTyped(const jsid& id, F&& f) {
|
|
|
|
return MapGCThingTyped(id,
|
|
|
|
[&f](auto t) {
|
|
|
|
f(t);
|
|
|
|
return true;
|
|
|
|
})
|
|
|
|
.isSome();
|
2015-04-23 20:42:31 +03:00
|
|
|
}
|
|
|
|
|
2020-04-26 20:01:39 +03:00
|
|
|
template <typename Wrapper>
|
|
|
|
class WrappedPtrOperations<JS::PropertyKey, Wrapper> {
|
|
|
|
const JS::PropertyKey& id() const {
|
|
|
|
return static_cast<const Wrapper*>(this)->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool isInt() const { return id().isInt(); }
|
|
|
|
bool isString() const { return id().isString(); }
|
|
|
|
bool isSymbol() const { return id().isSymbol(); }
|
|
|
|
|
|
|
|
int32_t toInt() const { return id().toInt(); }
|
|
|
|
JSString* toString() const { return id().toString(); }
|
|
|
|
JS::Symbol* toSymbol() const { return id().toSymbol(); }
|
|
|
|
};
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace js
|
2013-09-12 03:51:17 +04:00
|
|
|
|
2013-08-22 09:26:56 +04:00
|
|
|
#endif /* js_Id_h */
|