diff options
Diffstat (limited to 'js/public/PropertySpec.h')
-rw-r--r-- | js/public/PropertySpec.h | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/js/public/PropertySpec.h b/js/public/PropertySpec.h new file mode 100644 index 0000000000..9d8115508d --- /dev/null +++ b/js/public/PropertySpec.h @@ -0,0 +1,447 @@ +/* -*- 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, PropertySpecNameIsSymbol +#include "js/Value.h" // JS::Value + +struct JS_PUBLIC_API JSContext; +class 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 { + enum class Type : uint8_t { String, Int32, Double }; + Type type; + union { + const char* string; + int32_t int32; + double double_; + }; + + private: + ValueWrapper() = delete; + + explicit constexpr ValueWrapper(int32_t n) : type(Type::Int32), int32(n) {} + + explicit constexpr ValueWrapper(const char* s) + : type(Type::String), string(s) {} + + explicit constexpr ValueWrapper(double d) + : type(Type::Double), double_(d) {} + + 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); + } + + static constexpr ValueWrapper doubleValue(double d) { + return ValueWrapper(d); + } + }; + + 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); + } + }; + + union Name { + private: + const char* string_; + uintptr_t symbol_; + + public: + Name() = delete; + + explicit constexpr Name(const char* str) : string_(str) {} + explicit constexpr Name(JS::SymbolCode symbol) + : symbol_(uint32_t(symbol) + 1) {} + + explicit operator bool() const { return !!symbol_; } + + bool isSymbol() const { return JS::PropertySpecNameIsSymbol(symbol_); } + JS::SymbolCode symbol() const { + MOZ_ASSERT(isSymbol()); + return JS::SymbolCode(symbol_ - 1); + } + + bool isString() const { return !isSymbol(); } + const char* string() const { + MOZ_ASSERT(isString()); + return string_; + } + }; + + Name name; + + private: + // JSPROP_* property attributes as defined in PropertyDescriptor.h. + uint8_t attributes_; + + // Whether AccessorsOrValue below stores a value, JSNative accessors, or + // self-hosted accessors. + enum class Kind : uint8_t { Value, SelfHostedAccessor, NativeAccessor }; + Kind kind_; + + public: + AccessorsOrValue u; + + private: + JSPropertySpec() = delete; + + constexpr JSPropertySpec(const char* name, uint8_t attributes, Kind kind, + AccessorsOrValue u) + : name(name), attributes_(attributes), kind_(kind), u(u) {} + constexpr JSPropertySpec(JS::SymbolCode name, uint8_t attributes, Kind kind, + AccessorsOrValue u) + : name(name), attributes_(attributes), kind_(kind), u(u) {} + + public: + JSPropertySpec(const JSPropertySpec& other) = default; + + static constexpr JSPropertySpec nativeAccessors( + const char* name, uint8_t attributes, JSNative getter, + const JSJitInfo* getterInfo, JSNative setter = nullptr, + const JSJitInfo* setterInfo = nullptr) { + return JSPropertySpec( + name, attributes, Kind::NativeAccessor, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo), + JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo))); + } + + static constexpr JSPropertySpec nativeAccessors( + JS::SymbolCode name, uint8_t attributes, JSNative getter, + const JSJitInfo* getterInfo, JSNative setter = nullptr, + const JSJitInfo* setterInfo = nullptr) { + return JSPropertySpec( + name, attributes, Kind::NativeAccessor, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo), + JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo))); + } + + static constexpr JSPropertySpec selfHostedAccessors( + const char* name, uint8_t attributes, const char* getterName, + const char* setterName = nullptr) { + return JSPropertySpec( + name, attributes, Kind::SelfHostedAccessor, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::selfHostedAccessor(getterName), + setterName + ? JSPropertySpec::Accessor::selfHostedAccessor(setterName) + : JSPropertySpec::Accessor::noAccessor())); + } + + static constexpr JSPropertySpec selfHostedAccessors( + JS::SymbolCode name, uint8_t attributes, const char* getterName, + const char* setterName = nullptr) { + return JSPropertySpec( + name, attributes, Kind::SelfHostedAccessor, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::selfHostedAccessor(getterName), + setterName + ? JSPropertySpec::Accessor::selfHostedAccessor(setterName) + : JSPropertySpec::Accessor::noAccessor())); + } + + static constexpr JSPropertySpec int32Value(const char* name, + uint8_t attributes, int32_t n) { + return JSPropertySpec(name, attributes, Kind::Value, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::int32Value(n))); + } + + static constexpr JSPropertySpec int32Value(JS::SymbolCode name, + uint8_t attributes, int32_t n) { + return JSPropertySpec(name, attributes, Kind::Value, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::int32Value(n))); + } + + static constexpr JSPropertySpec stringValue(const char* name, + uint8_t attributes, + const char* s) { + return JSPropertySpec(name, attributes, Kind::Value, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::stringValue(s))); + } + + static constexpr JSPropertySpec stringValue(JS::SymbolCode name, + uint8_t attributes, + const char* s) { + return JSPropertySpec(name, attributes, Kind::Value, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::stringValue(s))); + } + + static constexpr JSPropertySpec doubleValue(const char* name, + uint8_t attributes, double d) { + return JSPropertySpec(name, attributes, Kind::Value, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::doubleValue(d))); + } + + static constexpr JSPropertySpec sentinel() { + return JSPropertySpec(nullptr, 0, Kind::NativeAccessor, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::noAccessor(), + JSPropertySpec::Accessor::noAccessor())); + } + + unsigned attributes() const { return attributes_; } + + bool isAccessor() const { + return (kind_ == Kind::NativeAccessor || kind_ == Kind::SelfHostedAccessor); + } + + 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 Kind. + if (kind_ == Kind::SelfHostedAccessor) { + checkAccessorsAreSelfHosted(); + } else { + checkAccessorsAreNative(); + } +#endif + return kind_ == Kind::SelfHostedAccessor; + } + + 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); + } +}; + +// There can be many JSPropertySpec instances so verify the size is what we +// expect: +// +// - Name (1 word) +// - attributes_ + isAccessor_ (1 word) +// - AccessorsOrValue (4 words, native + JSJitInfo for both getter and setter) +static_assert(sizeof(JSPropertySpec) == 6 * sizeof(uintptr_t)); + +template <unsigned Attributes> +constexpr uint8_t CheckAccessorAttrs() { + static_assert((Attributes & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0, + "Unexpected flag (not JSPROP_ENUMERATE or JSPROP_PERMANENT)"); + return uint8_t(Attributes); +} + +#define JS_PSG(name, getter, attributes) \ + JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ + getter, nullptr) +#define JS_PSGS(name, getter, setter, attributes) \ + JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ + getter, nullptr, setter, nullptr) +#define JS_SYM_GET(symbol, getter, attributes) \ + JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \ + CheckAccessorAttrs<attributes>(), getter, \ + nullptr) +#define JS_SELF_HOSTED_GET(name, getterName, attributes) \ + JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \ + getterName) +#define JS_SELF_HOSTED_GETSET(name, getterName, setterName, attributes) \ + JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \ + getterName, setterName) +#define JS_SELF_HOSTED_SYM_GET(symbol, getterName, attributes) \ + JSPropertySpec::selfHostedAccessors( \ + ::JS::SymbolCode::symbol, CheckAccessorAttrs<attributes>(), getterName) +#define JS_STRING_PS(name, string, attributes) \ + JSPropertySpec::stringValue(name, attributes, string) +#define JS_STRING_SYM_PS(symbol, string, attributes) \ + JSPropertySpec::stringValue(::JS::SymbolCode::symbol, attributes, string) +#define JS_INT32_PS(name, value, attributes) \ + JSPropertySpec::int32Value(name, attributes, value) +#define JS_DOUBLE_PS(name, value, attributes) \ + JSPropertySpec::doubleValue(name, attributes, 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 { + using Name = JSPropertySpec::Name; + + Name name; + JSNativeWrapper call; + uint16_t nargs; + uint16_t flags; + const char* selfHostedName; + + // JSPROP_* property attributes as defined in PropertyDescriptor.h + unsigned attributes() const { return flags; } +}; + +/* + * 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(::JS::SymbolCode::symbol, call, info, nargs, flags, selfHostedName) +#define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \ + { JSFunctionSpec::Name(name), {call, info}, nargs, flags, selfHostedName } + +#endif // js_PropertySpec_h |