LCOV - code coverage report
Current view: top level - js/public - Proxy.h (source / functions) Hit Total Coverage
Test: output.info Lines: 105 111 94.6 %
Date: 2017-07-14 16:53:18 Functions: 38 43 88.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13