Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/Proxy.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- | ||||
* vim: set ts=8 sts=4 et sw=4 tw=99: | * vim: set ts=8 sts=2 et sw=2 tw=80: | ||||
* This Source Code Form is subject to the terms of the Mozilla Public | * This Source Code Form is subject to the terms of the Mozilla Public | ||||
* License, v. 2.0. If a copy of the MPL was not distributed with this | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
#ifndef js_Proxy_h | #ifndef js_Proxy_h | ||||
#define js_Proxy_h | #define js_Proxy_h | ||||
#include "mozilla/Maybe.h" | #include "mozilla/Maybe.h" | ||||
#include "jsfriendapi.h" | #include "jsfriendapi.h" | ||||
#include "js/CallNonGenericMethod.h" | #include "js/CallNonGenericMethod.h" | ||||
#include "js/Class.h" | #include "js/Class.h" | ||||
namespace js { | namespace js { | ||||
using JS::AutoIdVector; | |||||
using JS::CallArgs; | using JS::CallArgs; | ||||
using JS::Handle; | using JS::Handle; | ||||
using JS::HandleId; | using JS::HandleId; | ||||
using JS::HandleObject; | using JS::HandleObject; | ||||
using JS::HandleValue; | using JS::HandleValue; | ||||
using JS::IsAcceptableThis; | using JS::IsAcceptableThis; | ||||
using JS::MutableHandle; | using JS::MutableHandle; | ||||
using JS::MutableHandleIdVector; | |||||
using JS::MutableHandleObject; | using JS::MutableHandleObject; | ||||
using JS::MutableHandleValue; | using JS::MutableHandleValue; | ||||
using JS::NativeImpl; | using JS::NativeImpl; | ||||
using JS::ObjectOpResult; | using JS::ObjectOpResult; | ||||
using JS::PrivateValue; | using JS::PrivateValue; | ||||
using JS::PropertyDescriptor; | using JS::PropertyDescriptor; | ||||
using JS::Value; | using JS::Value; | ||||
class RegExpShared; | class RegExpShared; | ||||
class JS_FRIEND_API Wrapper; | class JS_FRIEND_API Wrapper; | ||||
/* | /* | ||||
* [SMDOC] Proxy Objects | |||||
* | |||||
* A proxy is a JSObject with highly customizable behavior. ES6 specifies a | * A proxy is a JSObject with highly customizable behavior. ES6 specifies a | ||||
* single kind of proxy, but the customization mechanisms we use to implement | * single kind of proxy, but the customization mechanisms we use to implement | ||||
* ES6 Proxy objects are also useful wherever an object with weird behavior is | * ES6 Proxy objects are also useful wherever an object with weird behavior is | ||||
* wanted. Proxies are used to implement: | * wanted. Proxies are used to implement: | ||||
* | * | ||||
* - the scope objects used by the Debugger's frame.eval() method | * - the scope objects used by the Debugger's frame.eval() method | ||||
* (see js::GetDebugScopeForFunction) | * (see js::GetDebugEnvironment) | ||||
* | * | ||||
* - the khuey hack, whereby a whole compartment can be blown away | * - the khuey hack, whereby a whole compartment can be blown away | ||||
* even if other compartments hold references to objects in it | * even if other compartments hold references to objects in it | ||||
* (see js::NukeCrossCompartmentWrappers) | * (see js::NukeCrossCompartmentWrappers) | ||||
* | * | ||||
* - XPConnect security wrappers, which protect chrome from malicious content | * - XPConnect security wrappers, which protect chrome from malicious content | ||||
* (js/xpconnect/wrappers) | * (js/xpconnect/wrappers) | ||||
* | * | ||||
* - DOM objects with special property behavior, like named getters | * - DOM objects with special property behavior, like named getters | ||||
* (dom/bindings/Codegen.py generates these proxies from WebIDL) | * (dom/bindings/Codegen.py generates these proxies from WebIDL) | ||||
* | * | ||||
* - semi-transparent use of objects that live in other processes | * - semi-transparent use of objects that live in other processes | ||||
* (CPOWs, implemented in js/ipc) | * (CPOWs, implemented in js/ipc) | ||||
* | * | ||||
* ### Proxies and internal methods | * ### Proxies and internal methods | ||||
* | * | ||||
* ES2016 specifies 13 internal methods. The runtime semantics of just | * ES2019 specifies 13 internal methods. The runtime semantics of just about | ||||
* about everything a script can do to an object is specified in terms | * everything a script can do to an object is specified in terms of these | ||||
* of these internal methods. For example: | * internal methods. For example: | ||||
* | * | ||||
* JS code ES6 internal method that gets called | * JS code ES6 internal method that gets called | ||||
* --------------------------- -------------------------------- | * --------------------------- -------------------------------- | ||||
* obj.prop obj.[[Get]](obj, "prop") | * obj.prop obj.[[Get]](obj, "prop") | ||||
* "prop" in obj obj.[[HasProperty]]("prop") | * "prop" in obj obj.[[HasProperty]]("prop") | ||||
* new obj() obj.[[Construct]](<empty argument List>) | * new obj() obj.[[Construct]](<empty argument List>) | ||||
* | * | ||||
* With regard to the implementation of these internal methods, there are three | * With regard to the implementation of these internal methods, there are three | ||||
* very different kinds of object in SpiderMonkey. | * very different kinds of object in SpiderMonkey. | ||||
* | * | ||||
* 1. Native objects' internal methods are implemented in vm/NativeObject.cpp, | * 1. Native objects cover most objects and contain both internal slots and | ||||
* with duplicate (but functionally identical) implementations scattered | * properties. ClassOps and ObjectOps may be used to override certain | ||||
* through the ICs and JITs. | * default behaviors. | ||||
* | * | ||||
* 2. Certain non-native objects have internal methods that are implemented as | * 2. Proxy objects are composed of internal slots and a ProxyHandler. The | ||||
* magical js::ObjectOps hooks. We're trying to get rid of these. | * handler contains C++ methods that can implement these standard (and | ||||
* | * non-standard) internal methods. ClassOps and ObjectOps for the base | ||||
* 3. All other objects are proxies. A proxy's internal methods are | * ProxyObject invoke the handler methods as appropriate. | ||||
* implemented in C++, as the virtual methods of a C++ object stored on the | * | ||||
* proxy, known as its handler. | * 3. Objects with custom layouts like TypedObjects. These rely on ClassOps | ||||
* | * and ObjectOps to implement internal methods. | ||||
* This means that just about anything you do to a proxy will end up going | * | ||||
* through a C++ virtual method call. Possibly several. There's no reason the | * Native objects with custom ClassOps / ObjectOps are used when the object | ||||
* JITs and ICs can't specialize for particular proxies, based on the handler; | * behaves very similar to a normal object such as the ArrayObject and it's | ||||
* but currently we don't do much of this, so the virtual method overhead | * length property. Most usages wrapping a C++ or other type should prefer | ||||
* typically is actually incurred. | * using a Proxy. Using the proxy approach makes it much easier to create an | ||||
* ECMAScript and JIT compatible object, particularly if using an appropriate | |||||
* base class. | |||||
* | |||||
* Just about anything you do to a proxy will end up going through a C++ | |||||
* virtual method call. Possibly several. There's no reason the JITs and ICs | |||||
* can't specialize for particular proxies, based on the handler; but currently | |||||
* we don't do much of this, so the virtual method overhead typically is | |||||
* actually incurred. | |||||
* | * | ||||
* ### The proxy handler hierarchy | * ### The proxy handler hierarchy | ||||
* | * | ||||
* A major use case for proxies is to forward each internal method call to | * A major use case for proxies is to forward each internal method call to | ||||
* another object, known as its target. The target can be an arbitrary JS | * another object, known as its target. The target can be an arbitrary JS | ||||
* object. Not every proxy has the notion of a target, however. | * object. Not every proxy has the notion of a target, however. | ||||
* | * | ||||
* To minimize code duplication, a set of abstract proxy handler classes is | * To minimize code duplication, a set of abstract proxy handler classes is | ||||
* provided, from which other handlers may inherit. These abstract classes are | * provided, from which other handlers may inherit. These abstract classes are | ||||
* organized in the following hierarchy: | * organized in the following hierarchy: | ||||
* | * | ||||
* BaseProxyHandler | * BaseProxyHandler | ||||
* | | * | | ||||
* Wrapper // has a target, can be unwrapped to reveal | * ForwardingProxyHandler // has a target and forwards internal methods | ||||
* | // target (see js::CheckedUnwrap) | * | | ||||
* Wrapper // can be unwrapped to reveal target | |||||
* | // (see js::CheckedUnwrap) | |||||
* | | * | | ||||
* CrossCompartmentWrapper // target is in another compartment; | * CrossCompartmentWrapper // target is in another compartment; | ||||
* // implements membrane between compartments | * // implements membrane between compartments | ||||
* | * | ||||
* Example: Some DOM objects (including all the arraylike DOM objects) are | * Example: Some DOM objects (including all the arraylike DOM objects) are | ||||
* implemented as proxies. Since these objects don't need to forward operations | * implemented as proxies. Since these objects don't need to forward operations | ||||
* to any underlying JS object, DOMJSProxyHandler directly subclasses | * to any underlying JS object, BaseDOMProxyHandler directly subclasses | ||||
* BaseProxyHandler. | * BaseProxyHandler. | ||||
* | * | ||||
* Gecko's security wrappers are examples of cross-compartment wrappers. | * Gecko's security wrappers are examples of cross-compartment wrappers. | ||||
* | * | ||||
* ### Proxy prototype chains | * ### Proxy prototype chains | ||||
* | * | ||||
* In addition to the normal methods, there are two models for proxy prototype | * While most ECMAScript internal methods are handled by simply calling the | ||||
* chains. | * handler method, the [[GetPrototypeOf]] / [[SetPrototypeOf]] behaviors may | ||||
* | * follow one of two models: | ||||
* 1. Proxies can use the standard prototype mechanism used throughout the | * | ||||
* engine. To do so, simply pass a prototype to NewProxyObject() at | * 1. A concrete prototype object (or null) is passed to object construction | ||||
* creation time. All prototype accesses will then "just work" to treat the | * and ordinary prototype read and write applies. The prototype-related | ||||
* proxy as a "normal" object. | * handler hooks are never called in this case. The [[Prototype]] slot is | ||||
* | * used to store the current prototype value. | ||||
* 2. A proxy can implement more complicated prototype semantics (if, for | * | ||||
* example, it wants to delegate the prototype lookup to a wrapped object) | * 2. TaggedProto::LazyProto is passed to NewProxyObject (or the | ||||
* by passing Proxy::LazyProto as the prototype at create time. This | * ProxyOptions::lazyProto flag is set). Each read or write of the | ||||
* guarantees that the getPrototype() handler method will be called every | * prototype will invoke the handler. This dynamic prototype behavior may | ||||
* time the object's prototype chain is accessed. | * be useful for wrapper-like objects. If this mode is used the | ||||
* | * getPrototype handler at a minimum must be implemented. | ||||
* This system is implemented with two methods: {get,set}Prototype. The | * | ||||
* default implementation of setPrototype throws a TypeError. Since it is | * NOTE: In this mode the [[Prototype]] internal slot is unavailable and | ||||
* not possible to create an object without a sense of prototype chain, | * must be simulated if needed. This is non-standard, but an | ||||
* handlers must implement getPrototype if opting in to the dynamic | * appropriate handler can hide this implementation detail. | ||||
* prototype system. | * | ||||
* One subtlety here is that ECMAScript has a notion of "ordinary" prototypes. | |||||
* An object that doesn't override [[GetPrototypeOf]] is considered to have an | |||||
* ordinary prototype. The getPrototypeIfOrdinary handler must be implemented | |||||
* by you or your base class. Typically model 1 will be considered "ordinary" | |||||
* and model 2 will not. | |||||
*/ | */ | ||||
/* | /* | ||||
* BaseProxyHandler is the most generic kind of proxy handler. It does not make | * BaseProxyHandler is the most generic kind of proxy handler. It does not make | ||||
* any assumptions about the target. Consequently, it does not provide any | * any assumptions about the target. Consequently, it does not provide any | ||||
* default implementation for most methods. As a convenience, a few high-level | * default implementation for most methods. As a convenience, a few high-level | ||||
* methods, like get() and set(), are given default implementations that work by | * methods, like get() and set(), are given default implementations that work by | ||||
* calling the low-level methods, like getOwnPropertyDescriptor(). | * calling the low-level methods, like getOwnPropertyDescriptor(). | ||||
Show All 15 Lines | class JS_FRIEND_API BaseProxyHandler { | ||||
*/ | */ | ||||
const void* mFamily; | const void* mFamily; | ||||
/* | /* | ||||
* Proxy handlers can use mHasPrototype to request the following special | * Proxy handlers can use mHasPrototype to request the following special | ||||
* treatment from the JS engine: | * treatment from the JS engine: | ||||
* | * | ||||
* - When mHasPrototype is true, the engine never calls these methods: | * - When mHasPrototype is true, the engine never calls these methods: | ||||
* getPropertyDescriptor, has, set, enumerate, iterate. Instead, for | * has, set, enumerate, iterate. Instead, for these operations, | ||||
* these operations, it calls the "own" methods like | * it calls the "own" methods like getOwnPropertyDescriptor, hasOwn, | ||||
* getOwnPropertyDescriptor, hasOwn, defineProperty, | * defineProperty, getOwnEnumerablePropertyKeys, etc., | ||||
* getOwnEnumerablePropertyKeys, etc., and consults the prototype chain | * and consults the prototype chain if needed. | ||||
* if needed. | |||||
* | * | ||||
* - When mHasPrototype is true, the engine calls handler->get() only if | * - When mHasPrototype is true, the engine calls handler->get() only if | ||||
* handler->hasOwn() says an own property exists on the proxy. If not, | * handler->hasOwn() says an own property exists on the proxy. If not, | ||||
* it consults the prototype chain. | * it consults the prototype chain. | ||||
* | * | ||||
* This is useful because it frees the ProxyHandler from having to implement | * This is useful because it frees the ProxyHandler from having to implement | ||||
* any behavior having to do with the prototype chain. | * any behavior having to do with the prototype chain. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | public: | ||||
/* Standard internal methods. */ | /* Standard internal methods. */ | ||||
virtual bool getOwnPropertyDescriptor( | virtual bool getOwnPropertyDescriptor( | ||||
JSContext* cx, HandleObject proxy, HandleId id, | JSContext* cx, HandleObject proxy, HandleId id, | ||||
MutableHandle<PropertyDescriptor> desc) const = 0; | MutableHandle<PropertyDescriptor> desc) const = 0; | ||||
virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, | virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, | ||||
Handle<PropertyDescriptor> desc, | Handle<PropertyDescriptor> desc, | ||||
ObjectOpResult& result) const = 0; | ObjectOpResult& result) const = 0; | ||||
virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, | virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, | ||||
AutoIdVector& props) const = 0; | MutableHandleIdVector props) const = 0; | ||||
virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, | virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, | ||||
ObjectOpResult& result) const = 0; | ObjectOpResult& result) const = 0; | ||||
/* | /* | ||||
* These methods are standard, but the engine does not normally call them. | * These methods are standard, but the engine does not normally call them. | ||||
* They're opt-in. See "Proxy prototype chains" above. | * They're opt-in. See "Proxy prototype chains" above. | ||||
* | * | ||||
* getPrototype() crashes if called. setPrototype() throws a TypeError. | * getPrototype() crashes if called. setPrototype() throws a TypeError. | ||||
Show All 14 Lines | virtual bool preventExtensions(JSContext* cx, HandleObject proxy, | ||||
ObjectOpResult& result) const = 0; | ObjectOpResult& result) const = 0; | ||||
virtual bool isExtensible(JSContext* cx, HandleObject proxy, | virtual bool isExtensible(JSContext* cx, HandleObject proxy, | ||||
bool* extensible) const = 0; | bool* extensible) const = 0; | ||||
/* | /* | ||||
* These standard internal methods are implemented, as a convenience, so | * These standard internal methods are implemented, as a convenience, so | ||||
* that ProxyHandler subclasses don't have to provide every single method. | * that ProxyHandler subclasses don't have to provide every single method. | ||||
* | * | ||||
* The base-class implementations work by calling getPropertyDescriptor(). | * The base-class implementations work by calling getOwnPropertyDescriptor() | ||||
* and going up the [[Prototype]] chain if necessary. The algorithm for this | |||||
* follows what is defined for Ordinary Objects in the ES spec. | |||||
* They do not follow any standard. When in doubt, override them. | * They do not follow any standard. When in doubt, override them. | ||||
*/ | */ | ||||
virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, | virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, | ||||
bool* bp) const; | bool* bp) const; | ||||
virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, | virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, | ||||
HandleId id, MutableHandleValue vp) const; | HandleId id, MutableHandleValue vp) const; | ||||
virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, | virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, | ||||
HandleValue v, HandleValue receiver, | HandleValue v, HandleValue receiver, | ||||
Show All 12 Lines | public: | ||||
* anything, but otherwise you probably want to override all four. | * anything, but otherwise you probably want to override all four. | ||||
*/ | */ | ||||
virtual bool call(JSContext* cx, HandleObject proxy, | virtual bool call(JSContext* cx, HandleObject proxy, | ||||
const CallArgs& args) const; | const CallArgs& args) const; | ||||
virtual bool construct(JSContext* cx, HandleObject proxy, | virtual bool construct(JSContext* cx, HandleObject proxy, | ||||
const CallArgs& args) const; | const CallArgs& args) const; | ||||
/* SpiderMonkey extensions. */ | /* SpiderMonkey extensions. */ | ||||
virtual JSObject* enumerate(JSContext* cx, HandleObject proxy) const; | virtual bool enumerate(JSContext* cx, HandleObject proxy, | ||||
virtual bool getPropertyDescriptor( | MutableHandleIdVector props) const; | ||||
JSContext* cx, HandleObject proxy, HandleId id, | |||||
MutableHandle<PropertyDescriptor> desc) const; | |||||
virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, | virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, | ||||
bool* bp) const; | bool* bp) const; | ||||
virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, | virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, | ||||
AutoIdVector& props) const; | MutableHandleIdVector props) const; | ||||
virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, | virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, | ||||
const CallArgs& args) const; | const CallArgs& args) const; | ||||
virtual bool hasInstance(JSContext* cx, HandleObject proxy, | virtual bool hasInstance(JSContext* cx, HandleObject proxy, | ||||
MutableHandleValue v, bool* bp) const; | MutableHandleValue v, bool* bp) const; | ||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, | virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, | ||||
ESClass* cls) const; | ESClass* cls) const; | ||||
virtual bool isArray(JSContext* cx, HandleObject proxy, | virtual bool isArray(JSContext* cx, HandleObject proxy, | ||||
JS::IsArrayAnswer* answer) const; | JS::IsArrayAnswer* answer) const; | ||||
Show All 13 Lines | public: | ||||
// We are not prepared to do this, as there's little const correctness | // We are not prepared to do this, as there's little const correctness | ||||
// in the external APIs that handle proxies. | // in the external APIs that handle proxies. | ||||
virtual bool isCallable(JSObject* obj) const; | virtual bool isCallable(JSObject* obj) const; | ||||
virtual bool isConstructor(JSObject* obj) const; | virtual bool isConstructor(JSObject* obj) const; | ||||
virtual bool getElements(JSContext* cx, HandleObject proxy, uint32_t begin, | virtual bool getElements(JSContext* cx, HandleObject proxy, uint32_t begin, | ||||
uint32_t end, ElementAdder* adder) const; | uint32_t end, ElementAdder* adder) const; | ||||
/* See comment for weakmapKeyDelegateOp in js/Class.h. */ | |||||
virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const; | |||||
virtual bool isScripted() const { return false; } | virtual bool isScripted() const { return false; } | ||||
}; | }; | ||||
extern JS_FRIEND_DATA const js::Class* const ProxyClassPtr; | extern JS_FRIEND_DATA const js::Class ProxyClass; | ||||
inline bool IsProxy(const JSObject* obj) { | inline bool IsProxy(const JSObject* obj) { | ||||
return GetObjectClass(obj)->isProxy(); | return GetObjectClass(obj)->isProxy(); | ||||
} | } | ||||
namespace detail { | namespace detail { | ||||
// Proxy slot layout | // Proxy slot layout | ||||
Show All 19 Lines | struct ProxyReservedSlots { | ||||
static inline int offsetOfPrivateSlot(); | static inline int offsetOfPrivateSlot(); | ||||
static inline int offsetOfSlot(size_t slot) { | static inline int offsetOfSlot(size_t slot) { | ||||
return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(Value); | return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(Value); | ||||
} | } | ||||
void init(size_t nreserved) { | void init(size_t nreserved) { | ||||
for (size_t i = 0; i < nreserved; i++) slots[i] = JS::UndefinedValue(); | for (size_t i = 0; i < nreserved; i++) { | ||||
slots[i] = JS::UndefinedValue(); | |||||
} | |||||
} | } | ||||
ProxyReservedSlots(const ProxyReservedSlots&) = delete; | ProxyReservedSlots(const ProxyReservedSlots&) = delete; | ||||
void operator=(const ProxyReservedSlots&) = delete; | void operator=(const ProxyReservedSlots&) = delete; | ||||
}; | }; | ||||
struct ProxyValueArray { | struct ProxyValueArray { | ||||
Value privateSlot; | Value privateSlot; | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | return reinterpret_cast<ProxyDataLayout*>(reinterpret_cast<uint8_t*>(obj) + | ||||
ProxyDataOffset); | ProxyDataOffset); | ||||
} | } | ||||
inline const ProxyDataLayout* GetProxyDataLayout(const JSObject* obj) { | inline const ProxyDataLayout* GetProxyDataLayout(const JSObject* obj) { | ||||
MOZ_ASSERT(IsProxy(obj)); | MOZ_ASSERT(IsProxy(obj)); | ||||
return reinterpret_cast<const ProxyDataLayout*>( | return reinterpret_cast<const ProxyDataLayout*>( | ||||
reinterpret_cast<const uint8_t*>(obj) + ProxyDataOffset); | reinterpret_cast<const uint8_t*>(obj) + ProxyDataOffset); | ||||
} | } | ||||
JS_FRIEND_API void SetValueInProxy(Value* slot, const Value& value); | |||||
inline void SetProxyReservedSlotUnchecked(JSObject* obj, size_t n, | |||||
const Value& extra) { | |||||
MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); | |||||
Value* vp = &GetProxyDataLayout(obj)->reservedSlots->slots[n]; | |||||
// Trigger a barrier before writing the slot. | |||||
if (vp->isGCThing() || extra.isGCThing()) { | |||||
SetValueInProxy(vp, extra); | |||||
} else { | |||||
*vp = extra; | |||||
} | |||||
} | |||||
} // namespace detail | } // namespace detail | ||||
inline const BaseProxyHandler* GetProxyHandler(const JSObject* obj) { | inline const BaseProxyHandler* GetProxyHandler(const JSObject* obj) { | ||||
return detail::GetProxyDataLayout(obj)->handler; | return detail::GetProxyDataLayout(obj)->handler; | ||||
} | } | ||||
inline const Value& GetProxyPrivate(const JSObject* obj) { | inline const Value& GetProxyPrivate(const JSObject* obj) { | ||||
return detail::GetProxyDataLayout(obj)->values()->privateSlot; | return detail::GetProxyDataLayout(obj)->values()->privateSlot; | ||||
} | } | ||||
inline JSObject* GetProxyTargetObject(JSObject* obj) { | inline JSObject* GetProxyTargetObject(JSObject* obj) { | ||||
return GetProxyPrivate(obj).toObjectOrNull(); | return GetProxyPrivate(obj).toObjectOrNull(); | ||||
} | } | ||||
inline const Value& GetProxyReservedSlot(const JSObject* obj, size_t n) { | inline const Value& GetProxyReservedSlot(const JSObject* obj, size_t n) { | ||||
MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); | MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); | ||||
return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n]; | return detail::GetProxyDataLayout(obj)->reservedSlots->slots[n]; | ||||
} | } | ||||
inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) { | inline void SetProxyHandler(JSObject* obj, const BaseProxyHandler* handler) { | ||||
detail::GetProxyDataLayout(obj)->handler = handler; | detail::GetProxyDataLayout(obj)->handler = handler; | ||||
} | } | ||||
JS_FRIEND_API void SetValueInProxy(Value* slot, const Value& value); | |||||
inline void SetProxyReservedSlot(JSObject* obj, size_t n, const Value& extra) { | inline void SetProxyReservedSlot(JSObject* obj, size_t n, const Value& extra) { | ||||
MOZ_ASSERT(n < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); | #ifdef DEBUG | ||||
MOZ_ASSERT_IF(gc::detail::ObjectIsMarkedBlack(obj), | if (gc::detail::ObjectIsMarkedBlack(obj)) { | ||||
JS::ValueIsNotGray(extra)); | JS::AssertValueIsNotGray(extra); | ||||
} | |||||
Value* vp = &detail::GetProxyDataLayout(obj)->reservedSlots->slots[n]; | #endif | ||||
// Trigger a barrier before writing the slot. | detail::SetProxyReservedSlotUnchecked(obj, n, extra); | ||||
if (vp->isGCThing() || extra.isGCThing()) | |||||
SetValueInProxy(vp, extra); | |||||
else | |||||
*vp = extra; | |||||
} | } | ||||
inline void SetProxyPrivate(JSObject* obj, const Value& value) { | inline void SetProxyPrivate(JSObject* obj, const Value& value) { | ||||
MOZ_ASSERT_IF(gc::detail::ObjectIsMarkedBlack(obj), | #ifdef DEBUG | ||||
JS::ValueIsNotGray(value)); | if (gc::detail::ObjectIsMarkedBlack(obj)) { | ||||
JS::AssertValueIsNotGray(value); | |||||
} | |||||
#endif | |||||
Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot; | Value* vp = &detail::GetProxyDataLayout(obj)->values()->privateSlot; | ||||
// Trigger a barrier before writing the slot. | // Trigger a barrier before writing the slot. | ||||
if (vp->isGCThing() || value.isGCThing()) | if (vp->isGCThing() || value.isGCThing()) { | ||||
SetValueInProxy(vp, value); | detail::SetValueInProxy(vp, value); | ||||
else | } else { | ||||
*vp = value; | *vp = value; | ||||
} | } | ||||
} | |||||
inline bool IsScriptedProxy(const JSObject* obj) { | inline bool IsScriptedProxy(const JSObject* obj) { | ||||
return IsProxy(obj) && GetProxyHandler(obj)->isScripted(); | return IsProxy(obj) && GetProxyHandler(obj)->isScripted(); | ||||
} | } | ||||
class MOZ_STACK_CLASS ProxyOptions { | class MOZ_STACK_CLASS ProxyOptions { | ||||
protected: | protected: | ||||
/* protected constructor for subclass */ | /* protected constructor for subclass */ | ||||
explicit ProxyOptions(bool singletonArg, bool lazyProtoArg = false) | explicit ProxyOptions(bool singletonArg, bool lazyProtoArg = false) | ||||
: singleton_(singletonArg), | : singleton_(singletonArg), | ||||
lazyProto_(lazyProtoArg), | lazyProto_(lazyProtoArg), | ||||
clasp_(ProxyClassPtr) {} | clasp_(&ProxyClass) {} | ||||
public: | public: | ||||
ProxyOptions() | ProxyOptions() : singleton_(false), lazyProto_(false), clasp_(&ProxyClass) {} | ||||
: singleton_(false), lazyProto_(false), clasp_(ProxyClassPtr) {} | |||||
bool singleton() const { return singleton_; } | bool singleton() const { return singleton_; } | ||||
ProxyOptions& setSingleton(bool flag) { | ProxyOptions& setSingleton(bool flag) { | ||||
singleton_ = flag; | singleton_ = flag; | ||||
return *this; | return *this; | ||||
} | } | ||||
bool lazyProto() const { return lazyProto_; } | bool lazyProto() const { return lazyProto_; } | ||||
Show All 34 Lines | allow = handler->hasSecurityPolicy() | ||||
? handler->enter(cx, wrapper, id, act, mayThrow, &rv) | ? handler->enter(cx, wrapper, id, act, mayThrow, &rv) | ||||
: true; | : true; | ||||
recordEnter(cx, wrapper, id, act); | recordEnter(cx, wrapper, id, act); | ||||
// We want to throw an exception if all of the following are true: | // We want to throw an exception if all of the following are true: | ||||
// * The policy disallowed access. | // * The policy disallowed access. | ||||
// * The policy set rv to false, indicating that we should throw. | // * The policy set rv to false, indicating that we should throw. | ||||
// * The caller did not instruct us to ignore exceptions. | // * The caller did not instruct us to ignore exceptions. | ||||
// * The policy did not throw itself. | // * The policy did not throw itself. | ||||
if (!allow && !rv && mayThrow) reportErrorIfExceptionIsNotPending(cx, id); | if (!allow && !rv && mayThrow) { | ||||
reportErrorIfExceptionIsNotPending(cx, id); | |||||
} | |||||
} | } | ||||
virtual ~AutoEnterPolicy() { recordLeave(); } | virtual ~AutoEnterPolicy() { recordLeave(); } | ||||
inline bool allowed() { return allow; } | inline bool allowed() { return allow; } | ||||
inline bool returnValue() { | inline bool returnValue() { | ||||
MOZ_ASSERT(!allowed()); | MOZ_ASSERT(!allowed()); | ||||
return rv; | return rv; | ||||
} | } | ||||
protected: | protected: | ||||
// no-op constructor for subclass | // no-op constructor for subclass | ||||
AutoEnterPolicy() | AutoEnterPolicy() | ||||
#ifdef JS_DEBUG | #ifdef JS_DEBUG | ||||
: context(nullptr), | : context(nullptr), | ||||
enteredAction(BaseProxyHandler::NONE) | enteredAction(BaseProxyHandler::NONE) | ||||
#endif | #endif | ||||
{ | { | ||||
} | } | ||||
void reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id); | void reportErrorIfExceptionIsNotPending(JSContext* cx, HandleId id); | ||||
bool allow; | bool allow; | ||||
bool rv; | bool rv; | ||||
#ifdef JS_DEBUG | #ifdef JS_DEBUG | ||||
JSContext* context; | JSContext* context; | ||||
mozilla::Maybe<HandleObject> enteredProxy; | mozilla::Maybe<HandleObject> enteredProxy; | ||||
mozilla::Maybe<HandleId> enteredId; | mozilla::Maybe<HandleId> enteredId; | ||||
Action enteredAction; | Action enteredAction; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
extern JS_FRIEND_API void assertEnteredPolicy(JSContext* cx, JSObject* obj, | extern JS_FRIEND_API void assertEnteredPolicy(JSContext* cx, JSObject* obj, | ||||
jsid id, | jsid id, | ||||
BaseProxyHandler::Action act); | BaseProxyHandler::Action act); | ||||
#else | #else | ||||
inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id, | inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id, | ||||
BaseProxyHandler::Action act) {} | BaseProxyHandler::Action act) {} | ||||
#endif | #endif | ||||
extern JS_FRIEND_API JSObject* InitProxyClass(JSContext* cx, | |||||
JS::HandleObject obj); | |||||
extern JS_FRIEND_DATA const js::ClassOps ProxyClassOps; | extern JS_FRIEND_DATA const js::ClassOps ProxyClassOps; | ||||
extern JS_FRIEND_DATA const js::ClassExtension ProxyClassExtension; | extern JS_FRIEND_DATA const js::ClassExtension ProxyClassExtension; | ||||
extern JS_FRIEND_DATA const js::ObjectOps ProxyObjectOps; | extern JS_FRIEND_DATA const js::ObjectOps ProxyObjectOps; | ||||
template <unsigned Flags> | template <unsigned Flags> | ||||
constexpr unsigned CheckProxyFlags() { | constexpr unsigned CheckProxyFlags() { | ||||
// For now assert each Proxy Class has at least 1 reserved slot. This is | // For now assert each Proxy Class has at least 1 reserved slot. This is | ||||
// not a hard requirement, but helps catch Classes that need an explicit | // not a hard requirement, but helps catch Classes that need an explicit | ||||
Show All 35 Lines |
Wildfire Games · Phabricator