diff options
Diffstat (limited to 'js/public/PropertySpec.h')
-rw-r--r-- | js/public/PropertySpec.h | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/js/public/PropertySpec.h b/js/public/PropertySpec.h new file mode 100644 index 0000000000..f08a090d66 --- /dev/null +++ b/js/public/PropertySpec.h @@ -0,0 +1,428 @@ +/* -*- 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 { + uintptr_t type; + union { + const char* string; + int32_t int32; + double double_; + }; + + 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) {} + + explicit constexpr ValueWrapper(double d) + : type(JSVAL_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: + uint8_t flags_; + + public: + AccessorsOrValue u; + + private: + JSPropertySpec() = delete; + + constexpr JSPropertySpec(const char* name, uint8_t flags, AccessorsOrValue u) + : name(name), flags_(flags), u(u) {} + constexpr JSPropertySpec(JS::SymbolCode 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 nativeAccessors( + JS::SymbolCode 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 selfHostedAccessors( + JS::SymbolCode 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 int32Value(JS::SymbolCode 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 stringValue(JS::SymbolCode name, + uint8_t flags, const char* s) { + return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::stringValue(s))); + } + + static constexpr JSPropertySpec doubleValue(const char* name, uint8_t flags, + double d) { + return JSPropertySpec(name, flags | JSPROP_INTERNAL_USE_BIT, + AccessorsOrValue::fromValue( + JSPropertySpec::ValueWrapper::doubleValue(d))); + } + + static constexpr JSPropertySpec sentinel() { + return JSPropertySpec(nullptr, 0, + AccessorsOrValue::fromAccessors( + JSPropertySpec::Accessor::noAccessor(), + JSPropertySpec::Accessor::noAccessor())); + } + + // JSPROP_* property attributes as defined in PropertyDescriptor.h + unsigned attributes() const { return flags_ & ~JSPROP_INTERNAL_USE_BIT; } + + 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); + } +}; + +#define JS_CHECK_ACCESSOR_FLAGS(flags) \ + (static_cast<std::enable_if_t<((flags) & ~(JSPROP_ENUMERATE | \ + JSPROP_PERMANENT)) == 0>>(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(::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( \ + ::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(::JS::SymbolCode::symbol, flags, string) +#define JS_INT32_PS(name, value, flags) \ + JSPropertySpec::int32Value(name, flags, value) +#define JS_DOUBLE_PS(name, value, flags) \ + JSPropertySpec::doubleValue(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 { + 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 |