gecko-dev/js/public/PropertySpec.h

350 строки
12 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* Property descriptors and flags. */
#ifndef js_PropertySpec_h
#define js_PropertySpec_h
#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF}
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t
#include <type_traits> // std::enable_if
#include "jstypes.h" // JS_PUBLIC_API
#include "js/CallArgs.h" // JSNative
#include "js/PropertyDescriptor.h" // JSPROP_*
#include "js/RootingAPI.h" // JS::MutableHandle
#include "js/Symbol.h" // JS::SymbolCode
#include "js/Value.h" // JS::Value
struct JSContext;
struct JSJitInfo;
/**
* Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will
* allow us to pass one JSJitInfo per function with the property/function spec,
* without additional field overhead.
*/
struct JSNativeWrapper {
JSNative op = nullptr;
const JSJitInfo* info = nullptr;
JSNativeWrapper() = default;
JSNativeWrapper(const JSNativeWrapper& other) = default;
constexpr JSNativeWrapper(JSNative op, const JSJitInfo* info)
: op(op), info(info) {}
};
/**
* Description of a property. JS_DefineProperties and JS_InitClass take arrays
* of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END
* are helper macros for defining such arrays.
*/
struct JSPropertySpec {
struct SelfHostedWrapper {
// The same type as JSNativeWrapper's first field, so that the access in
// JSPropertySpec::checkAccessorsAreSelfHosted become valid.
JSNative unused = nullptr;
const char* funname;
SelfHostedWrapper() = delete;
explicit constexpr SelfHostedWrapper(const char* funname)
: funname(funname) {}
};
struct ValueWrapper {
uintptr_t type;
union {
const char* string;
int32_t int32;
};
private:
ValueWrapper() = delete;
explicit constexpr ValueWrapper(int32_t n)
: type(JSVAL_TYPE_INT32), int32(n) {}
explicit constexpr ValueWrapper(const char* s)
: type(JSVAL_TYPE_STRING), string(s) {}
public:
ValueWrapper(const ValueWrapper& other) = default;
static constexpr ValueWrapper int32Value(int32_t n) {
return ValueWrapper(n);
}
static constexpr ValueWrapper stringValue(const char* s) {
return ValueWrapper(s);
}
};
union Accessor {
JSNativeWrapper native;
SelfHostedWrapper selfHosted;
private:
Accessor() = delete;
constexpr Accessor(JSNative op, const JSJitInfo* info) : native(op, info) {}
explicit constexpr Accessor(const char* funname) : selfHosted(funname) {}
public:
Accessor(const Accessor& other) = default;
static constexpr Accessor nativeAccessor(JSNative op,
const JSJitInfo* info = nullptr) {
return Accessor(op, info);
}
static constexpr Accessor selfHostedAccessor(const char* funname) {
return Accessor(funname);
}
static constexpr Accessor noAccessor() {
return Accessor(nullptr, nullptr);
}
};
union AccessorsOrValue {
struct Accessors {
Accessor getter;
Accessor setter;
constexpr Accessors(Accessor getter, Accessor setter)
: getter(getter), setter(setter) {}
} accessors;
ValueWrapper value;
private:
AccessorsOrValue() = delete;
constexpr AccessorsOrValue(Accessor getter, Accessor setter)
: accessors(getter, setter) {}
explicit constexpr AccessorsOrValue(ValueWrapper value) : value(value) {}
public:
AccessorsOrValue(const AccessorsOrValue& other) = default;
static constexpr AccessorsOrValue fromAccessors(Accessor getter,
Accessor setter) {
return AccessorsOrValue(getter, setter);
}
static constexpr AccessorsOrValue fromValue(ValueWrapper value) {
return AccessorsOrValue(value);
}
};
const char* name;
uint8_t flags;
AccessorsOrValue u;
private:
JSPropertySpec() = delete;
constexpr JSPropertySpec(const char* name, uint8_t flags, AccessorsOrValue u)
: name(name), flags(flags), u(u) {}
public:
JSPropertySpec(const JSPropertySpec& other) = default;
static constexpr JSPropertySpec nativeAccessors(
const char* name, uint8_t flags, JSNative getter,
const JSJitInfo* getterInfo, JSNative setter = nullptr,
const JSJitInfo* setterInfo = nullptr) {
return JSPropertySpec(
name, flags,
AccessorsOrValue::fromAccessors(
JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo),
JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo)));
}
static constexpr JSPropertySpec selfHostedAccessors(
const char* name, uint8_t flags, const char* getterName,
const char* setterName = nullptr) {
return JSPropertySpec(
name, flags | JSPROP_GETTER | (setterName ? JSPROP_SETTER : 0),
AccessorsOrValue::fromAccessors(
JSPropertySpec::Accessor::selfHostedAccessor(getterName),
setterName
? JSPropertySpec::Accessor::selfHostedAccessor(setterName)
: JSPropertySpec::Accessor::noAccessor()));
}
static constexpr JSPropertySpec int32Value(const char* name, uint8_t flags,
int32_t n) {
return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
AccessorsOrValue::fromValue(
JSPropertySpec::ValueWrapper::int32Value(n)));
}
static constexpr JSPropertySpec stringValue(const char* name, uint8_t flags,
const char* s) {
return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT,
AccessorsOrValue::fromValue(
JSPropertySpec::ValueWrapper::stringValue(s)));
}
static constexpr JSPropertySpec sentinel() {
return JSPropertySpec(nullptr, 0,
AccessorsOrValue::fromAccessors(
JSPropertySpec::Accessor::noAccessor(),
JSPropertySpec::Accessor::noAccessor()));
}
bool isAccessor() const { return !(flags & JSPROP_INTERNAL_USE_BIT); }
JS_PUBLIC_API bool getValue(JSContext* cx,
JS::MutableHandle<JS::Value> value) const;
bool isSelfHosted() const {
MOZ_ASSERT(isAccessor());
#ifdef DEBUG
// Verify that our accessors match our JSPROP_GETTER flag.
if (flags & JSPROP_GETTER) {
checkAccessorsAreSelfHosted();
} else {
checkAccessorsAreNative();
}
#endif
return (flags & JSPROP_GETTER);
}
static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper),
"JSPropertySpec::getter/setter must be compact");
static_assert(offsetof(SelfHostedWrapper, unused) ==
offsetof(JSNativeWrapper, op) &&
offsetof(SelfHostedWrapper, funname) ==
offsetof(JSNativeWrapper, info),
"checkAccessorsAreNative below require that "
"SelfHostedWrapper::funname overlay "
"JSNativeWrapper::info and "
"SelfHostedWrapper::unused overlay "
"JSNativeWrapper::op");
private:
void checkAccessorsAreNative() const {
// We may have a getter or a setter or both. And whichever ones we have
// should not have a SelfHostedWrapper for the accessor.
MOZ_ASSERT_IF(u.accessors.getter.native.info, u.accessors.getter.native.op);
MOZ_ASSERT_IF(u.accessors.setter.native.info, u.accessors.setter.native.op);
}
void checkAccessorsAreSelfHosted() const {
MOZ_ASSERT(!u.accessors.getter.selfHosted.unused);
MOZ_ASSERT(!u.accessors.setter.selfHosted.unused);
}
};
// JSPropertySpec::{nativeAccessors, selfHostedAccessors,int32Value,
// stringValue} methods require symbol names to be casted to `const char*`,
// and the cast is `reinterpret_cast`.
//
// Provide a macro for the cast because of the following reasons:
//
// * `reinterpret_cast` cannot be used in constexpr
// * using non-constexpr static method in parameter disables constexpr of
// above methods
// * top-level `reinterpret_cast` doesn't disable constexpr of above methods
//
#define SYMBOL_TO_PROPERTY_NAME(symbol) \
reinterpret_cast<const char*>(uint32_t(symbol) + 1)
#define JS_CHECK_ACCESSOR_FLAGS(flags) \
(static_cast<std::enable_if<((flags) & ~(JSPROP_ENUMERATE | \
JSPROP_PERMANENT)) == 0>::type>(0), \
(flags))
#define JS_PSG(name, getter, flags) \
JSPropertySpec::nativeAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
getter, nullptr)
#define JS_PSGS(name, getter, setter, flags) \
JSPropertySpec::nativeAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
getter, nullptr, setter, nullptr)
#define JS_SYM_GET(symbol, getter, flags) \
JSPropertySpec::nativeAccessors( \
SYMBOL_TO_PROPERTY_NAME(::JS::SymbolCode::symbol), \
JS_CHECK_ACCESSOR_FLAGS(flags), getter, nullptr)
#define JS_SELF_HOSTED_GET(name, getterName, flags) \
JSPropertySpec::selfHostedAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
getterName)
#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
JSPropertySpec::selfHostedAccessors(name, JS_CHECK_ACCESSOR_FLAGS(flags), \
getterName, setterName)
#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags) \
JSPropertySpec::selfHostedAccessors( \
SYMBOL_TO_PROPERTY_NAME(::JS::SymbolCode::symbol), \
JS_CHECK_ACCESSOR_FLAGS(flags), getterName)
#define JS_STRING_PS(name, string, flags) \
JSPropertySpec::stringValue(name, flags, string)
#define JS_STRING_SYM_PS(symbol, string, flags) \
JSPropertySpec::stringValue( \
SYMBOL_TO_PROPERTY_NAME(::JS::SymbolCode::symbol), flags, string)
#define JS_INT32_PS(name, value, flags) \
JSPropertySpec::int32Value(name, flags, value)
#define JS_PS_END JSPropertySpec::sentinel()
/**
* To define a native function, set call to a JSNativeWrapper. To define a
* self-hosted function, set selfHostedName to the name of a function
* compiled during JSRuntime::initSelfHosting.
*/
struct JSFunctionSpec {
const char* name;
JSNativeWrapper call;
uint16_t nargs;
uint16_t flags;
const char* selfHostedName;
};
/*
* Terminating sentinel initializer to put at the end of a JSFunctionSpec array
* that's passed to JS_DefineFunctions or JS_InitClass.
*/
#define JS_FS_END JS_FN(nullptr, nullptr, 0, 0)
/*
* Initializer macros for a JSFunctionSpec array element. JS_FNINFO allows the
* simple adding of JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted
* function. JS_INLINABLE_FN allows specifying an InlinableNative enum value for
* natives inlined or specialized by the JIT. Finally JS_FNSPEC has slots for
* all the fields.
*
* The _SYM variants allow defining a function with a symbol key rather than a
* string key. For example, use JS_SYM_FN(iterator, ...) to define an
* @@iterator method.
*/
#define JS_FN(name, call, nargs, flags) \
JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
#define JS_INLINABLE_FN(name, call, nargs, flags, native) \
JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr)
#define JS_SYM_FN(symbol, call, nargs, flags) \
JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr)
#define JS_FNINFO(name, call, info, nargs, flags) \
JS_FNSPEC(name, call, info, nargs, flags, nullptr)
#define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \
JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \
JS_FNSPEC( \
reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
call, info, nargs, flags, selfHostedName)
#define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \
{ name, {call, info}, nargs, flags, selfHostedName }
#endif // js_PropertySpec_h