/* -*- 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 // size_t #include // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t #include // 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 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 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(), \ getter, nullptr) #define JS_PSGS(name, getter, setter, attributes) \ JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs(), \ getter, nullptr, setter, nullptr) #define JS_SYM_GET(symbol, getter, attributes) \ JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \ CheckAccessorAttrs(), getter, \ nullptr) #define JS_SELF_HOSTED_GET(name, getterName, attributes) \ JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs(), \ getterName) #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, attributes) \ JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs(), \ getterName, setterName) #define JS_SELF_HOSTED_SYM_GET(symbol, getterName, attributes) \ JSPropertySpec::selfHostedAccessors( \ ::JS::SymbolCode::symbol, CheckAccessorAttrs(), 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