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