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