PropertySpec.h (17245B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* Property descriptors and flags. */ 7 8 #ifndef js_PropertySpec_h 9 #define js_PropertySpec_h 10 11 #include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} 12 13 #include <stddef.h> // size_t 14 #include <stdint.h> // uint8_t, uint16_t, int32_t, uint32_t, uintptr_t 15 #include <type_traits> // std::enable_if 16 17 #include "jstypes.h" // JS_PUBLIC_API 18 19 #include "js/CallArgs.h" // JSNative 20 #include "js/PropertyDescriptor.h" // JSPROP_* 21 #include "js/RootingAPI.h" // JS::MutableHandle 22 #include "js/Symbol.h" // JS::SymbolCode, PropertySpecNameIsSymbol 23 #include "js/Value.h" // JS::Value 24 25 struct JS_PUBLIC_API JSContext; 26 class JSJitInfo; 27 28 /** 29 * Wrapper to relace JSNative for JSPropertySpecs and JSFunctionSpecs. This will 30 * allow us to pass one JSJitInfo per function with the property/function spec, 31 * without additional field overhead. 32 */ 33 struct JSNativeWrapper { 34 JSNative op = nullptr; 35 const JSJitInfo* info = nullptr; 36 37 JSNativeWrapper() = default; 38 39 JSNativeWrapper(const JSNativeWrapper& other) = default; 40 41 constexpr JSNativeWrapper(JSNative op, const JSJitInfo* info) 42 : op(op), info(info) {} 43 }; 44 45 /** 46 * Description of a property. JS_DefineProperties and JS_InitClass take arrays 47 * of these and define many properties at once. JS_PSG, JS_PSGS and JS_PS_END 48 * are helper macros for defining such arrays. 49 */ 50 struct JSPropertySpec { 51 struct SelfHostedWrapper { 52 // The same type as JSNativeWrapper's first field, so that the access in 53 // JSPropertySpec::checkAccessorsAreSelfHosted become valid. 54 JSNative unused = nullptr; 55 56 const char* funname; 57 58 SelfHostedWrapper() = delete; 59 60 explicit constexpr SelfHostedWrapper(const char* funname) 61 : funname(funname) {} 62 }; 63 64 struct ValueWrapper { 65 enum class Type : uint8_t { String, Int32, Double }; 66 Type type; 67 union { 68 const char* string; 69 int32_t int32; 70 double double_; 71 }; 72 73 private: 74 ValueWrapper() = delete; 75 76 explicit constexpr ValueWrapper(int32_t n) : type(Type::Int32), int32(n) {} 77 78 explicit constexpr ValueWrapper(const char* s) 79 : type(Type::String), string(s) {} 80 81 explicit constexpr ValueWrapper(double d) 82 : type(Type::Double), double_(d) {} 83 84 public: 85 ValueWrapper(const ValueWrapper& other) = default; 86 87 static constexpr ValueWrapper int32Value(int32_t n) { 88 return ValueWrapper(n); 89 } 90 91 static constexpr ValueWrapper stringValue(const char* s) { 92 return ValueWrapper(s); 93 } 94 95 static constexpr ValueWrapper doubleValue(double d) { 96 return ValueWrapper(d); 97 } 98 }; 99 100 union Accessor { 101 JSNativeWrapper native; 102 SelfHostedWrapper selfHosted; 103 104 private: 105 Accessor() = delete; 106 107 constexpr Accessor(JSNative op, const JSJitInfo* info) : native(op, info) {} 108 109 explicit constexpr Accessor(const char* funname) : selfHosted(funname) {} 110 111 public: 112 Accessor(const Accessor& other) = default; 113 114 static constexpr Accessor nativeAccessor(JSNative op, 115 const JSJitInfo* info = nullptr) { 116 return Accessor(op, info); 117 } 118 119 static constexpr Accessor selfHostedAccessor(const char* funname) { 120 return Accessor(funname); 121 } 122 123 static constexpr Accessor noAccessor() { 124 return Accessor(nullptr, nullptr); 125 } 126 }; 127 128 union AccessorsOrValue { 129 struct Accessors { 130 Accessor getter; 131 Accessor setter; 132 133 constexpr Accessors(Accessor getter, Accessor setter) 134 : getter(getter), setter(setter) {} 135 } accessors; 136 ValueWrapper value; 137 138 private: 139 AccessorsOrValue() = delete; 140 141 constexpr AccessorsOrValue(Accessor getter, Accessor setter) 142 : accessors(getter, setter) {} 143 144 explicit constexpr AccessorsOrValue(ValueWrapper value) : value(value) {} 145 146 public: 147 AccessorsOrValue(const AccessorsOrValue& other) = default; 148 149 static constexpr AccessorsOrValue fromAccessors(Accessor getter, 150 Accessor setter) { 151 return AccessorsOrValue(getter, setter); 152 } 153 154 static constexpr AccessorsOrValue fromValue(ValueWrapper value) { 155 return AccessorsOrValue(value); 156 } 157 }; 158 159 union Name { 160 private: 161 const char* string_; 162 uintptr_t symbol_; 163 164 public: 165 Name() = delete; 166 167 explicit constexpr Name(const char* str) : string_(str) {} 168 explicit constexpr Name(JS::SymbolCode symbol) 169 : symbol_(uint32_t(symbol) + 1) {} 170 171 explicit operator bool() const { return !!symbol_; } 172 173 bool isSymbol() const { return JS::PropertySpecNameIsSymbol(symbol_); } 174 JS::SymbolCode symbol() const { 175 MOZ_ASSERT(isSymbol()); 176 return JS::SymbolCode(symbol_ - 1); 177 } 178 179 bool isString() const { return !isSymbol(); } 180 const char* string() const { 181 MOZ_ASSERT(isString()); 182 return string_; 183 } 184 }; 185 186 Name name; 187 188 private: 189 // JSPROP_* property attributes as defined in PropertyDescriptor.h. 190 uint8_t attributes_; 191 192 // Whether AccessorsOrValue below stores a value, JSNative accessors, or 193 // self-hosted accessors. 194 enum class Kind : uint8_t { Value, SelfHostedAccessor, NativeAccessor }; 195 Kind kind_; 196 197 public: 198 AccessorsOrValue u; 199 200 private: 201 JSPropertySpec() = delete; 202 203 constexpr JSPropertySpec(const char* name, uint8_t attributes, Kind kind, 204 AccessorsOrValue u) 205 : name(name), attributes_(attributes), kind_(kind), u(u) {} 206 constexpr JSPropertySpec(JS::SymbolCode name, uint8_t attributes, Kind kind, 207 AccessorsOrValue u) 208 : name(name), attributes_(attributes), kind_(kind), u(u) {} 209 210 public: 211 JSPropertySpec(const JSPropertySpec& other) = default; 212 213 static constexpr JSPropertySpec nativeAccessors( 214 const char* name, uint8_t attributes, JSNative getter, 215 const JSJitInfo* getterInfo, JSNative setter = nullptr, 216 const JSJitInfo* setterInfo = nullptr) { 217 return JSPropertySpec( 218 name, attributes, Kind::NativeAccessor, 219 AccessorsOrValue::fromAccessors( 220 JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo), 221 JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo))); 222 } 223 224 static constexpr JSPropertySpec nativeAccessors( 225 JS::SymbolCode name, uint8_t attributes, JSNative getter, 226 const JSJitInfo* getterInfo, JSNative setter = nullptr, 227 const JSJitInfo* setterInfo = nullptr) { 228 return JSPropertySpec( 229 name, attributes, Kind::NativeAccessor, 230 AccessorsOrValue::fromAccessors( 231 JSPropertySpec::Accessor::nativeAccessor(getter, getterInfo), 232 JSPropertySpec::Accessor::nativeAccessor(setter, setterInfo))); 233 } 234 235 static constexpr JSPropertySpec selfHostedAccessors( 236 const char* name, uint8_t attributes, const char* getterName, 237 const char* setterName = nullptr) { 238 return JSPropertySpec( 239 name, attributes, Kind::SelfHostedAccessor, 240 AccessorsOrValue::fromAccessors( 241 JSPropertySpec::Accessor::selfHostedAccessor(getterName), 242 setterName 243 ? JSPropertySpec::Accessor::selfHostedAccessor(setterName) 244 : JSPropertySpec::Accessor::noAccessor())); 245 } 246 247 static constexpr JSPropertySpec selfHostedAccessors( 248 JS::SymbolCode name, uint8_t attributes, const char* getterName, 249 const char* setterName = nullptr) { 250 return JSPropertySpec( 251 name, attributes, Kind::SelfHostedAccessor, 252 AccessorsOrValue::fromAccessors( 253 JSPropertySpec::Accessor::selfHostedAccessor(getterName), 254 setterName 255 ? JSPropertySpec::Accessor::selfHostedAccessor(setterName) 256 : JSPropertySpec::Accessor::noAccessor())); 257 } 258 259 static constexpr JSPropertySpec int32Value(const char* name, 260 uint8_t attributes, int32_t n) { 261 return JSPropertySpec(name, attributes, Kind::Value, 262 AccessorsOrValue::fromValue( 263 JSPropertySpec::ValueWrapper::int32Value(n))); 264 } 265 266 static constexpr JSPropertySpec int32Value(JS::SymbolCode name, 267 uint8_t attributes, int32_t n) { 268 return JSPropertySpec(name, attributes, Kind::Value, 269 AccessorsOrValue::fromValue( 270 JSPropertySpec::ValueWrapper::int32Value(n))); 271 } 272 273 static constexpr JSPropertySpec stringValue(const char* name, 274 uint8_t attributes, 275 const char* s) { 276 return JSPropertySpec(name, attributes, Kind::Value, 277 AccessorsOrValue::fromValue( 278 JSPropertySpec::ValueWrapper::stringValue(s))); 279 } 280 281 static constexpr JSPropertySpec stringValue(JS::SymbolCode name, 282 uint8_t attributes, 283 const char* s) { 284 return JSPropertySpec(name, attributes, Kind::Value, 285 AccessorsOrValue::fromValue( 286 JSPropertySpec::ValueWrapper::stringValue(s))); 287 } 288 289 static constexpr JSPropertySpec doubleValue(const char* name, 290 uint8_t attributes, double d) { 291 return JSPropertySpec(name, attributes, Kind::Value, 292 AccessorsOrValue::fromValue( 293 JSPropertySpec::ValueWrapper::doubleValue(d))); 294 } 295 296 static constexpr JSPropertySpec sentinel() { 297 return JSPropertySpec(nullptr, 0, Kind::NativeAccessor, 298 AccessorsOrValue::fromAccessors( 299 JSPropertySpec::Accessor::noAccessor(), 300 JSPropertySpec::Accessor::noAccessor())); 301 } 302 303 unsigned attributes() const { return attributes_; } 304 305 bool isAccessor() const { 306 return (kind_ == Kind::NativeAccessor || kind_ == Kind::SelfHostedAccessor); 307 } 308 309 JS_PUBLIC_API bool getValue(JSContext* cx, 310 JS::MutableHandle<JS::Value> value) const; 311 312 bool isSelfHosted() const { 313 MOZ_ASSERT(isAccessor()); 314 #ifdef DEBUG 315 // Verify that our accessors match our Kind. 316 if (kind_ == Kind::SelfHostedAccessor) { 317 checkAccessorsAreSelfHosted(); 318 } else { 319 checkAccessorsAreNative(); 320 } 321 #endif 322 return kind_ == Kind::SelfHostedAccessor; 323 } 324 325 static_assert(sizeof(SelfHostedWrapper) == sizeof(JSNativeWrapper), 326 "JSPropertySpec::getter/setter must be compact"); 327 static_assert(offsetof(SelfHostedWrapper, unused) == 328 offsetof(JSNativeWrapper, op) && 329 offsetof(SelfHostedWrapper, funname) == 330 offsetof(JSNativeWrapper, info), 331 "checkAccessorsAreNative below require that " 332 "SelfHostedWrapper::funname overlay " 333 "JSNativeWrapper::info and " 334 "SelfHostedWrapper::unused overlay " 335 "JSNativeWrapper::op"); 336 337 private: 338 void checkAccessorsAreNative() const { 339 // We may have a getter or a setter or both. And whichever ones we have 340 // should not have a SelfHostedWrapper for the accessor. 341 MOZ_ASSERT_IF(u.accessors.getter.native.info, u.accessors.getter.native.op); 342 MOZ_ASSERT_IF(u.accessors.setter.native.info, u.accessors.setter.native.op); 343 } 344 345 void checkAccessorsAreSelfHosted() const { 346 MOZ_ASSERT(!u.accessors.getter.selfHosted.unused); 347 MOZ_ASSERT(!u.accessors.setter.selfHosted.unused); 348 } 349 }; 350 351 // There can be many JSPropertySpec instances so verify the size is what we 352 // expect: 353 // 354 // - Name (1 word) 355 // - attributes_ + isAccessor_ (1 word) 356 // - AccessorsOrValue (4 words, native + JSJitInfo for both getter and setter) 357 static_assert(sizeof(JSPropertySpec) == 6 * sizeof(uintptr_t)); 358 359 template <unsigned Attributes> 360 constexpr uint8_t CheckAccessorAttrs() { 361 static_assert((Attributes & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0, 362 "Unexpected flag (not JSPROP_ENUMERATE or JSPROP_PERMANENT)"); 363 return uint8_t(Attributes); 364 } 365 366 #define JS_PSG(name, getter, attributes) \ 367 JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ 368 getter, nullptr) 369 #define JS_INLINABLE_PSG(name, getter, attributes, native) \ 370 JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ 371 getter, &js::jit::JitInfo_##native) 372 #define JS_PSGS(name, getter, setter, attributes) \ 373 JSPropertySpec::nativeAccessors(name, CheckAccessorAttrs<attributes>(), \ 374 getter, nullptr, setter, nullptr) 375 #define JS_SYM_GET(symbol, getter, attributes) \ 376 JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \ 377 CheckAccessorAttrs<attributes>(), getter, \ 378 nullptr) 379 #define JS_SYM_GETSET(symbol, getter, setter, attributes) \ 380 JSPropertySpec::nativeAccessors(::JS::SymbolCode::symbol, \ 381 CheckAccessorAttrs<attributes>(), getter, \ 382 nullptr, setter, nullptr) 383 #define JS_SELF_HOSTED_GET(name, getterName, attributes) \ 384 JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \ 385 getterName) 386 #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, attributes) \ 387 JSPropertySpec::selfHostedAccessors(name, CheckAccessorAttrs<attributes>(), \ 388 getterName, setterName) 389 #define JS_SELF_HOSTED_SYM_GET(symbol, getterName, attributes) \ 390 JSPropertySpec::selfHostedAccessors( \ 391 ::JS::SymbolCode::symbol, CheckAccessorAttrs<attributes>(), getterName) 392 #define JS_STRING_PS(name, string, attributes) \ 393 JSPropertySpec::stringValue(name, attributes, string) 394 #define JS_STRING_SYM_PS(symbol, string, attributes) \ 395 JSPropertySpec::stringValue(::JS::SymbolCode::symbol, attributes, string) 396 #define JS_INT32_PS(name, value, attributes) \ 397 JSPropertySpec::int32Value(name, attributes, value) 398 #define JS_DOUBLE_PS(name, value, attributes) \ 399 JSPropertySpec::doubleValue(name, attributes, value) 400 #define JS_PS_END JSPropertySpec::sentinel() 401 402 /** 403 * To define a native function, set call to a JSNativeWrapper. To define a 404 * self-hosted function, set selfHostedName to the name of a function 405 * compiled during JSRuntime::initSelfHosting. 406 */ 407 struct JSFunctionSpec { 408 using Name = JSPropertySpec::Name; 409 410 Name name; 411 JSNativeWrapper call; 412 uint16_t nargs; 413 uint16_t flags; 414 const char* selfHostedName; 415 416 // JSPROP_* property attributes as defined in PropertyDescriptor.h 417 unsigned attributes() const { return flags; } 418 }; 419 420 /* 421 * Terminating sentinel initializer to put at the end of a JSFunctionSpec array 422 * that's passed to JS_DefineFunctions or JS_InitClass. 423 */ 424 #define JS_FS_END JS_FN(nullptr, nullptr, 0, 0) 425 426 /* 427 * Initializer macros for a JSFunctionSpec array element. 428 * 429 * - JS_FNINFO allows the simple adding of JSJitInfos. 430 * - JS_SELF_HOSTED_FN declares a self-hosted function. 431 * - JS_INLINABLE_FN allows specifying an InlinableNative enum value for natives 432 * inlined or specialized by the JIT. 433 * - JS_TRAMPOLINE_FN allows specifying a TrampolineNative enum value for 434 * natives that have a JitEntry trampoline. 435 * - JS_FNSPEC has slots for all the fields. 436 * 437 * The _SYM variants allow defining a function with a symbol key rather than a 438 * string key. For example, use JS_SYM_FN(iterator, ...) to define an 439 * @@iterator method. 440 */ 441 #define JS_FN(name, call, nargs, flags) \ 442 JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr) 443 #define JS_INLINABLE_FN(name, call, nargs, flags, native) \ 444 JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr) 445 #define JS_TRAMPOLINE_FN(name, call, nargs, flags, native) \ 446 JS_FNSPEC(name, call, &js::jit::JitInfo_##native, nargs, flags, nullptr) 447 #define JS_SYM_FN(symbol, call, nargs, flags) \ 448 JS_SYM_FNSPEC(symbol, call, nullptr, nargs, flags, nullptr) 449 #define JS_FNINFO(name, call, info, nargs, flags) \ 450 JS_FNSPEC(name, call, info, nargs, flags, nullptr) 451 #define JS_SELF_HOSTED_FN(name, selfHostedName, nargs, flags) \ 452 JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName) 453 #define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \ 454 JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName) 455 #define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \ 456 JS_FNSPEC(::JS::SymbolCode::symbol, call, info, nargs, flags, selfHostedName) 457 #define JS_FNSPEC(name, call, info, nargs, flags, selfHostedName) \ 458 {JSFunctionSpec::Name(name), {call, info}, nargs, flags, selfHostedName} 459 460 #endif // js_PropertySpec_h