Proxy.h (31466B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef js_Proxy_h 8 #define js_Proxy_h 9 10 #include "mozilla/Maybe.h" 11 12 #include "jstypes.h" // for JS_PUBLIC_API, JS_PUBLIC_DATA 13 14 #include "js/Array.h" // JS::IsArrayAnswer 15 #include "js/CallNonGenericMethod.h" 16 #include "js/Class.h" 17 #include "js/HeapAPI.h" // for ObjectIsMarkedBlack 18 #include "js/Id.h" // for jsid 19 #include "js/Object.h" // JS::GetClass 20 #include "js/RootingAPI.h" // for Handle, MutableHandle (ptr only) 21 #include "js/shadow/Object.h" // JS::shadow::Object 22 #include "js/TypeDecls.h" // for HandleObject, HandleId, HandleValue, MutableHandleIdVector, MutableHandleValue, MutableHand... 23 #include "js/Value.h" // for Value, AssertValueIsNotGray, UndefinedValue, ObjectOrNullValue 24 25 namespace js { 26 27 class RegExpShared; 28 29 class JS_PUBLIC_API Wrapper; 30 31 /* 32 * [SMDOC] Proxy Objects 33 * 34 * A proxy is a JSObject with highly customizable behavior. ES6 specifies a 35 * single kind of proxy, but the customization mechanisms we use to implement 36 * ES6 Proxy objects are also useful wherever an object with weird behavior is 37 * wanted. Proxies are used to implement: 38 * 39 * - the scope objects used by the Debugger's frame.eval() method 40 * (see js::GetDebugEnvironment) 41 * 42 * - the khuey hack, whereby a whole compartment can be blown away 43 * even if other compartments hold references to objects in it 44 * (see js::NukeCrossCompartmentWrappers) 45 * 46 * - XPConnect security wrappers, which protect chrome from malicious content 47 * (js/xpconnect/wrappers) 48 * 49 * - DOM objects with special property behavior, like named getters 50 * (dom/bindings/Codegen.py generates these proxies from WebIDL) 51 * 52 * ### Proxies and internal methods 53 * 54 * ES2019 specifies 13 internal methods. The runtime semantics of just about 55 * everything a script can do to an object is specified in terms of these 56 * internal methods. For example: 57 * 58 * JS code ES6 internal method that gets called 59 * --------------------------- -------------------------------- 60 * obj.prop obj.[[Get]](obj, "prop") 61 * "prop" in obj obj.[[HasProperty]]("prop") 62 * new obj() obj.[[Construct]](<empty argument List>) 63 * 64 * With regard to the implementation of these internal methods, there are three 65 * very different kinds of object in SpiderMonkey. 66 * 67 * 1. Native objects cover most objects and contain both internal slots and 68 * properties. JSClassOps and ObjectOps may be used to override certain 69 * default behaviors. 70 * 71 * 2. Proxy objects are composed of internal slots and a ProxyHandler. The 72 * handler contains C++ methods that can implement these standard (and 73 * non-standard) internal methods. JSClassOps and ObjectOps for the base 74 * ProxyObject invoke the handler methods as appropriate. 75 * 76 * 3. Objects with custom layouts like TypedObjects. These rely on JSClassOps 77 * and ObjectOps to implement internal methods. 78 * 79 * Native objects with custom JSClassOps / ObjectOps are used when the object 80 * behaves very similar to a normal object such as the ArrayObject and it's 81 * length property. Most usages wrapping a C++ or other type should prefer 82 * using a Proxy. Using the proxy approach makes it much easier to create an 83 * ECMAScript and JIT compatible object, particularly if using an appropriate 84 * base class. 85 * 86 * Just about anything you do to a proxy will end up going through a C++ 87 * virtual method call. Possibly several. There's no reason the JITs and ICs 88 * can't specialize for particular proxies, based on the handler; but currently 89 * we don't do much of this, so the virtual method overhead typically is 90 * actually incurred. 91 * 92 * ### The proxy handler hierarchy 93 * 94 * A major use case for proxies is to forward each internal method call to 95 * another object, known as its target. The target can be an arbitrary JS 96 * object. Not every proxy has the notion of a target, however. 97 * 98 * To minimize code duplication, a set of abstract proxy handler classes is 99 * provided, from which other handlers may inherit. These abstract classes are 100 * organized in the following hierarchy: 101 * 102 * BaseProxyHandler 103 * | | 104 * | NurseryAllocableProxyHandler 105 * | // allocated in the nursery; disallows 106 * | // overriding finalize method 107 * | 108 * ForwardingProxyHandler // has a target and forwards internal methods 109 * | 110 * Wrapper // can be unwrapped to reveal target 111 * | // (see js::CheckedUnwrap) 112 * | 113 * CrossCompartmentWrapper // target is in another compartment; 114 * // implements membrane between compartments 115 * 116 * Example: Some DOM objects (including all the arraylike DOM objects) are 117 * implemented as proxies. Since these objects don't need to forward operations 118 * to any underlying JS object, BaseDOMProxyHandler directly subclasses 119 * BaseProxyHandler. 120 * 121 * Gecko's security wrappers are examples of cross-compartment wrappers. 122 * 123 * ### Proxy prototype chains 124 * 125 * While most ECMAScript internal methods are handled by simply calling the 126 * handler method, the [[GetPrototypeOf]] / [[SetPrototypeOf]] behaviors may 127 * follow one of two models: 128 * 129 * 1. A concrete prototype object (or null) is passed to object construction 130 * and ordinary prototype read and write applies. The prototype-related 131 * handler hooks are never called in this case. The [[Prototype]] slot is 132 * used to store the current prototype value. 133 * 134 * 2. TaggedProto::LazyProto is passed to NewProxyObject (or the 135 * ProxyOptions::lazyProto flag is set). Each read or write of the 136 * prototype will invoke the handler. This dynamic prototype behavior may 137 * be useful for wrapper-like objects. If this mode is used the 138 * getPrototype handler at a minimum must be implemented. 139 * 140 * NOTE: In this mode the [[Prototype]] internal slot is unavailable and 141 * must be simulated if needed. This is non-standard, but an 142 * appropriate handler can hide this implementation detail. 143 * 144 * One subtlety here is that ECMAScript has a notion of "ordinary" prototypes. 145 * An object that doesn't override [[GetPrototypeOf]] is considered to have an 146 * ordinary prototype. The getPrototypeIfOrdinary handler must be implemented 147 * by you or your base class. Typically model 1 will be considered "ordinary" 148 * and model 2 will not. 149 */ 150 151 /* 152 * BaseProxyHandler is the most generic kind of proxy handler. It does not make 153 * any assumptions about the target. Consequently, it does not provide any 154 * default implementation for most methods. As a convenience, a few high-level 155 * methods, like get() and set(), are given default implementations that work by 156 * calling the low-level methods, like getOwnPropertyDescriptor(). 157 * 158 * Important: If you add a method here, you should probably also add a 159 * Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an 160 * explicit override for the method in SecurityWrapper. See bug 945826 comment 161 * 0. 162 */ 163 class JS_PUBLIC_API BaseProxyHandler { 164 /* 165 * Sometimes it's desirable to designate groups of proxy handlers as 166 * "similar". For this, we use the notion of a "family": A consumer-provided 167 * opaque pointer that designates the larger group to which this proxy 168 * belongs. 169 * 170 * If it will never be important to differentiate this proxy from others as 171 * part of a distinct group, nullptr may be used instead. 172 */ 173 const void* mFamily; 174 175 /* 176 * Proxy handlers can use mHasPrototype to request the following special 177 * treatment from the JS engine: 178 * 179 * - When mHasPrototype is true, the engine never calls these methods: 180 * has, set, enumerate, iterate. Instead, for these operations, 181 * it calls the "own" methods like getOwnPropertyDescriptor, hasOwn, 182 * defineProperty, getOwnEnumerablePropertyKeys, etc., 183 * and consults the prototype chain if needed. 184 * 185 * - When mHasPrototype is true, the engine calls handler->get() only if 186 * handler->hasOwn() says an own property exists on the proxy. If not, 187 * it consults the prototype chain. 188 * 189 * This is useful because it frees the ProxyHandler from having to implement 190 * any behavior having to do with the prototype chain. 191 */ 192 bool mHasPrototype; 193 194 /* 195 * All proxies indicate whether they have any sort of interesting security 196 * policy that might prevent the caller from doing something it wants to 197 * the object. In the case of wrappers, this distinction is used to 198 * determine whether the caller may strip off the wrapper if it so desires. 199 */ 200 bool mHasSecurityPolicy; 201 202 public: 203 explicit constexpr BaseProxyHandler(const void* aFamily, 204 bool aHasPrototype = false, 205 bool aHasSecurityPolicy = false) 206 : mFamily(aFamily), 207 mHasPrototype(aHasPrototype), 208 mHasSecurityPolicy(aHasSecurityPolicy) {} 209 210 bool hasPrototype() const { return mHasPrototype; } 211 212 bool hasSecurityPolicy() const { return mHasSecurityPolicy; } 213 214 inline const void* family() const { return mFamily; } 215 static size_t offsetOfFamily() { return offsetof(BaseProxyHandler, mFamily); } 216 217 virtual bool finalizeInBackground(const JS::Value& priv) const { 218 /* 219 * Called on creation of a proxy to determine whether its finalize 220 * method can be finalized on the background thread. 221 */ 222 return true; 223 } 224 225 virtual bool canNurseryAllocate() const { 226 /* 227 * Nursery allocation is allowed if and only if it is safe to not 228 * run |finalize| when the ProxyObject dies. 229 */ 230 return false; 231 } 232 233 /* Policy enforcement methods. 234 * 235 * enter() allows the policy to specify whether the caller may perform |act| 236 * on the proxy's |id| property. In the case when |act| is CALL, |id| is 237 * generally JS::PropertyKey::isVoid. The |mayThrow| parameter indicates 238 * whether a handler that wants to throw custom exceptions when denying 239 * should do so or not. 240 * 241 * The |act| parameter to enter() specifies the action being performed. 242 * If |bp| is false, the method suggests that the caller throw (though it 243 * may still decide to squelch the error). 244 * 245 * We make these OR-able so that assertEnteredPolicy can pass a union of them. 246 * For example, get{,Own}PropertyDescriptor is invoked by calls to ::get() 247 * ::set(), in addition to being invoked on its own, so there are several 248 * valid Actions that could have been entered. 249 */ 250 typedef uint32_t Action; 251 enum { 252 NONE = 0x00, 253 GET = 0x01, 254 SET = 0x02, 255 CALL = 0x04, 256 ENUMERATE = 0x08, 257 GET_PROPERTY_DESCRIPTOR = 0x10 258 }; 259 260 virtual bool enter(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, 261 Action act, bool mayThrow, bool* bp) const; 262 263 /* Standard internal methods. */ 264 virtual bool getOwnPropertyDescriptor( 265 JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 266 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc) const = 0; 267 virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy, 268 JS::HandleId id, 269 JS::Handle<JS::PropertyDescriptor> desc, 270 JS::ObjectOpResult& result) const = 0; 271 virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy, 272 JS::MutableHandleIdVector props) const = 0; 273 virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 274 JS::ObjectOpResult& result) const = 0; 275 276 /* 277 * These methods are standard, but the engine does not normally call them. 278 * They're opt-in. See "Proxy prototype chains" above. 279 * 280 * getPrototype() crashes if called. setPrototype() throws a TypeError. 281 */ 282 virtual bool getPrototype(JSContext* cx, JS::HandleObject proxy, 283 JS::MutableHandleObject protop) const; 284 virtual bool setPrototype(JSContext* cx, JS::HandleObject proxy, 285 JS::HandleObject proto, 286 JS::ObjectOpResult& result) const; 287 288 /* Non-standard but conceptual kin to {g,s}etPrototype, so these live here. */ 289 virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, 290 bool* isOrdinary, 291 JS::MutableHandleObject protop) const = 0; 292 virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject proxy, 293 bool* succeeded) const; 294 295 virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy, 296 JS::ObjectOpResult& result) const = 0; 297 virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy, 298 bool* extensible) const = 0; 299 300 /* 301 * These standard internal methods are implemented, as a convenience, so 302 * that ProxyHandler subclasses don't have to provide every single method. 303 * 304 * The base-class implementations work by calling getOwnPropertyDescriptor() 305 * and going up the [[Prototype]] chain if necessary. The algorithm for this 306 * follows what is defined for Ordinary Objects in the ES spec. 307 * They do not follow any standard. When in doubt, override them. 308 */ 309 virtual bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 310 bool* bp) const; 311 virtual bool get(JSContext* cx, JS::HandleObject proxy, 312 JS::HandleValue receiver, JS::HandleId id, 313 JS::MutableHandleValue vp) const; 314 virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 315 JS::HandleValue v, JS::HandleValue receiver, 316 JS::ObjectOpResult& result) const; 317 318 // Use the ProxyExpando object for private fields, rather than taking the 319 // normal get/set/defineField paths. 320 virtual bool useProxyExpandoObjectForPrivateFields() const { return true; } 321 322 // For some exotic objects (WindowProxy, Location), we want to be able to 323 // throw rather than allow private fields on these objects. 324 // 325 // As a simplfying assumption, if throwOnPrivateFields returns true, 326 // we should also return true to useProxyExpandoObjectForPrivateFields. 327 virtual bool throwOnPrivateField() const { return false; } 328 329 /* 330 * [[Call]] and [[Construct]] are standard internal methods but according 331 * to the spec, they are not present on every object. 332 * 333 * SpiderMonkey never calls a proxy's call()/construct() internal method 334 * unless isCallable()/isConstructor() returns true for that proxy. 335 * 336 * BaseProxyHandler::isCallable()/isConstructor() always return false, and 337 * BaseProxyHandler::call()/construct() crash if called. So if you're 338 * creating a kind of that is never callable, you don't have to override 339 * anything, but otherwise you probably want to override all four. 340 */ 341 virtual bool call(JSContext* cx, JS::HandleObject proxy, 342 const JS::CallArgs& args) const; 343 virtual bool construct(JSContext* cx, JS::HandleObject proxy, 344 const JS::CallArgs& args) const; 345 346 /* SpiderMonkey extensions. */ 347 virtual bool enumerate(JSContext* cx, JS::HandleObject proxy, 348 JS::MutableHandleIdVector props) const; 349 virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 350 bool* bp) const; 351 virtual bool getOwnEnumerablePropertyKeys( 352 JSContext* cx, JS::HandleObject proxy, 353 JS::MutableHandleIdVector props) const; 354 virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, 355 JS::NativeImpl impl, const JS::CallArgs& args) const; 356 virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, 357 ESClass* cls) const; 358 virtual bool isArray(JSContext* cx, JS::HandleObject proxy, 359 JS::IsArrayAnswer* answer) const; 360 virtual const char* className(JSContext* cx, JS::HandleObject proxy) const; 361 virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy, 362 bool isToSource) const; 363 virtual RegExpShared* regexp_toShared(JSContext* cx, 364 JS::HandleObject proxy) const; 365 virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy, 366 JS::MutableHandleValue vp) const; 367 virtual void trace(JSTracer* trc, JSObject* proxy) const; 368 virtual void finalize(JS::GCContext* gcx, JSObject* proxy) const; 369 virtual size_t objectMoved(JSObject* proxy, JSObject* old) const; 370 371 // Allow proxies, wrappers in particular, to specify callability at runtime. 372 // Note: These do not take const JSObject*, but they do in spirit. 373 // We are not prepared to do this, as there's little const correctness 374 // in the external APIs that handle proxies. 375 virtual bool isCallable(JSObject* obj) const; 376 virtual bool isConstructor(JSObject* obj) const; 377 378 virtual bool getElements(JSContext* cx, JS::HandleObject proxy, 379 uint32_t begin, uint32_t end, 380 ElementAdder* adder) const; 381 382 virtual bool isScripted() const { return false; } 383 }; 384 385 class JS_PUBLIC_API NurseryAllocableProxyHandler : public BaseProxyHandler { 386 using BaseProxyHandler::BaseProxyHandler; 387 388 // Don't allow overriding the default finalize method. 389 void finalize(JS::GCContext* gcx, JSObject* proxy) const final { 390 BaseProxyHandler::finalize(gcx, proxy); 391 } 392 // Can allocate in the nursery as long as we use the default finalize method. 393 bool canNurseryAllocate() const override { return true; } 394 }; 395 396 extern JS_PUBLIC_DATA const JSClass ProxyClass; 397 398 inline bool IsProxy(const JSObject* obj) { 399 return reinterpret_cast<const JS::shadow::Object*>(obj)->shape->isProxy(); 400 } 401 402 namespace detail { 403 404 // Proxy slot layout 405 // ----------------- 406 // 407 // Every proxy has a ProxyValueArray that contains the following Values: 408 // 409 // - The expando slot. This is used to hold private fields should they be 410 // stamped into a non-forwarding proxy type. 411 // - The private slot. 412 // - The reserved slots. The number of slots is determined by the proxy's Class. 413 // 414 // Proxy objects store a pointer to the reserved slots (ProxyReservedSlots*). 415 // The ProxyValueArray and the private slot can be accessed using 416 // ProxyValueArray::fromReservedSlots or ProxyDataLayout::values. 417 // 418 // Storing a pointer to ProxyReservedSlots instead of ProxyValueArray has a 419 // number of advantages. In particular, it means JS::GetReservedSlot and 420 // JS::SetReservedSlot can be used with both proxies and native objects. This 421 // works because the ProxyReservedSlots* pointer is stored where native objects 422 // store their dynamic slots pointer. 423 424 struct ProxyReservedSlots { 425 JS::Value slots[1]; 426 427 static constexpr ptrdiff_t offsetOfPrivateSlot(); 428 429 static inline int offsetOfSlot(size_t slot) { 430 return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(JS::Value); 431 } 432 433 void init(size_t nreserved) { 434 for (size_t i = 0; i < nreserved; i++) { 435 slots[i] = JS::UndefinedValue(); 436 } 437 } 438 439 ProxyReservedSlots(const ProxyReservedSlots&) = delete; 440 void operator=(const ProxyReservedSlots&) = delete; 441 }; 442 443 struct ProxyValueArray { 444 JS::Value expandoSlot; 445 JS::Value privateSlot; 446 ProxyReservedSlots reservedSlots; 447 448 void init(size_t nreserved) { 449 expandoSlot = JS::ObjectOrNullValue(nullptr); 450 privateSlot = JS::UndefinedValue(); 451 reservedSlots.init(nreserved); 452 } 453 454 static MOZ_ALWAYS_INLINE ProxyValueArray* fromReservedSlots( 455 ProxyReservedSlots* slots) { 456 uintptr_t p = reinterpret_cast<uintptr_t>(slots); 457 return reinterpret_cast<ProxyValueArray*>(p - offsetOfReservedSlots()); 458 } 459 static constexpr size_t offsetOfReservedSlots() { 460 return offsetof(ProxyValueArray, reservedSlots); 461 } 462 463 static size_t allocCount(size_t nreserved) { 464 static_assert(offsetOfReservedSlots() % sizeof(JS::Value) == 0); 465 return offsetOfReservedSlots() / sizeof(JS::Value) + nreserved; 466 } 467 static size_t sizeOf(size_t nreserved) { 468 return allocCount(nreserved) * sizeof(JS::Value); 469 } 470 471 ProxyValueArray(const ProxyValueArray&) = delete; 472 void operator=(const ProxyValueArray&) = delete; 473 }; 474 475 /* static */ 476 constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() { 477 return -ptrdiff_t(ProxyValueArray::offsetOfReservedSlots()) + 478 offsetof(ProxyValueArray, privateSlot); 479 } 480 481 // All proxies share the same data layout. Following the object's shape, the 482 // proxy has a ProxyDataLayout structure with a pointer to an array of values 483 // and the proxy's handler. This is designed both so that proxies can be easily 484 // swapped with other objects (via RemapWrapper) and to mimic the layout of 485 // other objects (proxies and other objects have the same size) so that common 486 // code can access either type of object. 487 // 488 // See GetReservedOrProxyPrivateSlot below. 489 struct ProxyDataLayout { 490 ProxyReservedSlots* reservedSlots; 491 const BaseProxyHandler* handler; 492 493 MOZ_ALWAYS_INLINE ProxyValueArray* values() const { 494 return ProxyValueArray::fromReservedSlots(reservedSlots); 495 } 496 }; 497 498 #ifdef JS_64BIT 499 constexpr uint32_t ProxyDataOffset = 1 * sizeof(void*); 500 #else 501 constexpr uint32_t ProxyDataOffset = 2 * sizeof(void*); 502 #endif 503 504 inline ProxyDataLayout* GetProxyDataLayout(JSObject* obj) { 505 MOZ_ASSERT(IsProxy(obj)); 506 return reinterpret_cast<ProxyDataLayout*>(reinterpret_cast<uint8_t*>(obj) + 507 ProxyDataOffset); 508 } 509 510 inline const ProxyDataLayout* GetProxyDataLayout(const JSObject* obj) { 511 MOZ_ASSERT(IsProxy(obj)); 512 return reinterpret_cast<const ProxyDataLayout*>( 513 reinterpret_cast<const uint8_t*>(obj) + ProxyDataOffset); 514 } 515 516 JS_PUBLIC_API void SetValueInProxy(JS::Value* slot, const JS::Value& value); 517 518 inline void SetProxyReservedSlotUnchecked(JSObject* obj, size_t n, 519 const JS::Value& extra) { 520 MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(JS::GetClass(obj))); 521 522 JS::Value* vp = &GetProxyDataLayout(obj)->reservedSlots->slots[n]; 523 524 // Trigger a barrier before writing the slot. 525 if (vp->isGCThing() || extra.isGCThing()) { 526 SetValueInProxy(vp, extra); 527 } else { 528 *vp = extra; 529 } 530 } 531 532 } // namespace detail 533 534 inline const BaseProxyHandler* GetProxyHandler(const JSObject* obj) { 535 return detail::GetProxyDataLayout(obj)->handler; 536 } 537 538 inline const JS::Value& GetProxyPrivate(const JSObject* obj) { 539 return detail::GetProxyDataLayout(obj)->values()->privateSlot; 540 } 541 542 inline const JS::Value& GetProxyExpando(const JSObject* obj) { 543 return detail::GetProxyDataLayout(obj)->values()->expandoSlot; 544 } 545 546 inline JSObject* GetProxyTargetObject(const JSObject* obj) { 547 return GetProxyPrivate(obj).toObjectOrNull(); 548 } 549 550 inline const JS::Value& GetProxyReservedSlot(const JSObject* obj, size_t n) { 551 MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(JS::GetClass(obj))); 552 return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n]; 553 } 554 555 inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) { 556 detail::GetProxyDataLayout(obj)->handler = handler; 557 } 558 559 inline void SetProxyReservedSlot(JSObject* obj, size_t n, 560 const JS::Value& extra) { 561 #ifdef DEBUG 562 if (gc::detail::ObjectIsMarkedBlack(obj)) { 563 JS::AssertValueIsNotGray(extra); 564 } 565 #endif 566 567 detail::SetProxyReservedSlotUnchecked(obj, n, extra); 568 } 569 570 inline void SetProxyPrivate(JSObject* obj, const JS::Value& value) { 571 #ifdef DEBUG 572 JS::AssertObjectIsNotGray(obj); 573 JS::AssertValueIsNotGray(value); 574 #endif 575 576 JS::Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot; 577 578 // Trigger a barrier before writing the slot. 579 if (vp->isGCThing() || value.isGCThing()) { 580 detail::SetValueInProxy(vp, value); 581 } else { 582 *vp = value; 583 } 584 } 585 586 inline bool IsScriptedProxy(const JSObject* obj) { 587 return IsProxy(obj) && GetProxyHandler(obj)->isScripted(); 588 } 589 590 class MOZ_STACK_CLASS ProxyOptions { 591 protected: 592 /* protected constructor for subclass */ 593 explicit ProxyOptions(bool lazyProtoArg) 594 : lazyProto_(lazyProtoArg), clasp_(&ProxyClass) {} 595 596 public: 597 ProxyOptions() : ProxyOptions(false) {} 598 599 bool lazyProto() const { return lazyProto_; } 600 ProxyOptions& setLazyProto(bool flag) { 601 lazyProto_ = flag; 602 return *this; 603 } 604 605 const JSClass* clasp() const { return clasp_; } 606 ProxyOptions& setClass(const JSClass* claspArg) { 607 clasp_ = claspArg; 608 return *this; 609 } 610 611 private: 612 bool lazyProto_; 613 const JSClass* clasp_; 614 }; 615 616 JS_PUBLIC_API JSObject* NewProxyObject( 617 JSContext* cx, const BaseProxyHandler* handler, JS::HandleValue priv, 618 JSObject* proto, const ProxyOptions& options = ProxyOptions()); 619 620 JSObject* RenewProxyObject(JSContext* cx, JSObject* obj, 621 BaseProxyHandler* handler, const JS::Value& priv); 622 623 class JS_PUBLIC_API AutoEnterPolicy { 624 public: 625 typedef BaseProxyHandler::Action Action; 626 AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler, 627 JS::HandleObject wrapper, JS::HandleId id, Action act, 628 bool mayThrow) 629 #ifdef JS_DEBUG 630 : context(nullptr) 631 #endif 632 { 633 allow = handler->hasSecurityPolicy() 634 ? handler->enter(cx, wrapper, id, act, mayThrow, &rv) 635 : true; 636 recordEnter(cx, wrapper, id, act); 637 // We want to throw an exception if all of the following are true: 638 // * The policy disallowed access. 639 // * The policy set rv to false, indicating that we should throw. 640 // * The caller did not instruct us to ignore exceptions. 641 // * The policy did not throw itself. 642 if (!allow && !rv && mayThrow) { 643 reportErrorIfExceptionIsNotPending(cx, id); 644 } 645 } 646 647 virtual ~AutoEnterPolicy() { recordLeave(); } 648 inline bool allowed() { return allow; } 649 inline bool returnValue() { 650 MOZ_ASSERT(!allowed()); 651 return rv; 652 } 653 654 protected: 655 // no-op constructor for subclass 656 AutoEnterPolicy() 657 #ifdef JS_DEBUG 658 : context(nullptr), 659 enteredAction(BaseProxyHandler::NONE) 660 #endif 661 { 662 } 663 void reportErrorIfExceptionIsNotPending(JSContext* cx, JS::HandleId id); 664 bool allow; 665 bool rv; 666 667 #ifdef JS_DEBUG 668 JSContext* context; 669 mozilla::Maybe<JS::HandleObject> enteredProxy; 670 mozilla::Maybe<JS::HandleId> enteredId; 671 Action enteredAction; 672 673 // NB: We explicitly don't track the entered action here, because sometimes 674 // set() methods do an implicit get() during their implementation, leading 675 // to spurious assertions. 676 AutoEnterPolicy* prev; 677 void recordEnter(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 678 Action act); 679 void recordLeave(); 680 681 friend JS_PUBLIC_API void assertEnteredPolicy(JSContext* cx, JSObject* proxy, 682 jsid id, Action act); 683 #else 684 inline void recordEnter(JSContext* cx, JSObject* proxy, jsid id, Action act) { 685 } 686 inline void recordLeave() {} 687 #endif 688 689 private: 690 // This operator needs to be deleted explicitly, otherwise Visual C++ will 691 // create it automatically when it is part of the export JS API. In that 692 // case, compile would fail because HandleId is not allowed to be assigned 693 // and consequently instantiation of assign operator of mozilla::Maybe 694 // would fail. See bug 1325351 comment 16. Copy constructor is removed at 695 // the same time for consistency. 696 AutoEnterPolicy(const AutoEnterPolicy&) = delete; 697 AutoEnterPolicy& operator=(const AutoEnterPolicy&) = delete; 698 }; 699 700 #ifdef JS_DEBUG 701 class JS_PUBLIC_API AutoWaivePolicy : public AutoEnterPolicy { 702 public: 703 AutoWaivePolicy(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, 704 BaseProxyHandler::Action act) { 705 allow = true; 706 recordEnter(cx, proxy, id, act); 707 } 708 }; 709 #else 710 class JS_PUBLIC_API AutoWaivePolicy{ 711 public : AutoWaivePolicy(JSContext * cx, JS::HandleObject proxy, 712 JS::HandleId id, BaseProxyHandler::Action act){} 713 }; 714 #endif 715 716 #ifdef JS_DEBUG 717 extern JS_PUBLIC_API void assertEnteredPolicy(JSContext* cx, JSObject* obj, 718 jsid id, 719 BaseProxyHandler::Action act); 720 #else 721 inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id, 722 BaseProxyHandler::Action act) {} 723 #endif 724 725 extern JS_PUBLIC_DATA const JSClassOps ProxyClassOps; 726 extern JS_PUBLIC_DATA const js::ClassExtension ProxyClassExtension; 727 extern JS_PUBLIC_DATA const js::ObjectOps ProxyObjectOps; 728 729 template <unsigned Flags> 730 constexpr unsigned CheckProxyFlags() { 731 constexpr size_t reservedSlots = 732 (Flags >> JSCLASS_RESERVED_SLOTS_SHIFT) & JSCLASS_RESERVED_SLOTS_MASK; 733 734 // For now assert each Proxy Class has at least 1 reserved slot. This is 735 // not a hard requirement, but helps catch Classes that need an explicit 736 // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523. 737 static_assert(reservedSlots > 0, 738 "Proxy Classes must have at least 1 reserved slot"); 739 740 constexpr size_t numSlots = 741 offsetof(js::detail::ProxyValueArray, reservedSlots) / sizeof(JS::Value); 742 743 // ProxyValueArray must fit inline in the object, so assert the number of 744 // slots does not exceed MAX_FIXED_SLOTS. 745 static_assert(numSlots + reservedSlots <= JS::shadow::Object::MAX_FIXED_SLOTS, 746 "ProxyValueArray size must not exceed max JSObject size"); 747 748 // Proxies must not have the JSCLASS_SKIP_NURSERY_FINALIZE flag set: they 749 // always have finalizers, and whether they can be nursery allocated is 750 // controlled by the canNurseryAllocate() method on the proxy handler. 751 static_assert(!(Flags & JSCLASS_SKIP_NURSERY_FINALIZE), 752 "Proxies must not use JSCLASS_SKIP_NURSERY_FINALIZE; use " 753 "the canNurseryAllocate() proxy handler method instead."); 754 return Flags; 755 } 756 757 #define PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, classSpec) \ 758 {name, \ 759 JSClass::NON_NATIVE | JSCLASS_IS_PROXY | JSCLASS_DELAY_METADATA_BUILDER | \ 760 js::CheckProxyFlags<flags>(), \ 761 &js::ProxyClassOps, \ 762 classSpec, \ 763 &js::ProxyClassExtension, \ 764 &js::ProxyObjectOps} 765 766 #define PROXY_CLASS_DEF(name, flags) \ 767 PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, JS_NULL_CLASS_SPEC) 768 769 // Converts a proxy into a DeadObjectProxy that will throw exceptions on all 770 // access. This will run the proxy's finalizer to perform clean-up before the 771 // conversion happens. 772 JS_PUBLIC_API void NukeNonCCWProxy(JSContext* cx, JS::HandleObject proxy); 773 774 // This is a variant of js::NukeNonCCWProxy() for CCWs. It should only be called 775 // on CCWs that have been removed from CCW tables. 776 JS_PUBLIC_API void NukeRemovedCrossCompartmentWrapper(JSContext* cx, 777 JSObject* wrapper); 778 779 } /* namespace js */ 780 781 #endif /* js_Proxy_h */