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/. */
10 #include "mozilla/Maybe.h"
12 #include "jstypes.h" // for JS_PUBLIC_API, JS_PUBLIC_DATA
14 #include "js/Array.h" // JS::IsArrayAnswer
15 #include "js/CallNonGenericMethod.h"
17 #include "js/HeapAPI.h" // for ObjectIsMarkedBlack
18 #include "js/Id.h" // for jsid
19 #include "js/Object.h" // JS::GetClass
20 #include "js/RootingAPI.h" // for Handle, MutableHandle (ptr only)
21 #include "js/shadow/Object.h" // JS::shadow::Object
22 #include "js/TypeDecls.h" // for HandleObject, HandleId, HandleValue, MutableHandleIdVector, MutableHandleValue, MutableHand...
23 #include "js/Value.h" // for Value, AssertValueIsNotGray, UndefinedValue, ObjectOrNullValue
29 class JS_PUBLIC_API Wrapper
;
32 * [SMDOC] Proxy Objects
34 * A proxy is a JSObject with highly customizable behavior. ES6 specifies a
35 * single kind of proxy, but the customization mechanisms we use to implement
36 * ES6 Proxy objects are also useful wherever an object with weird behavior is
37 * wanted. Proxies are used to implement:
39 * - the scope objects used by the Debugger's frame.eval() method
40 * (see js::GetDebugEnvironment)
42 * - the khuey hack, whereby a whole compartment can be blown away
43 * even if other compartments hold references to objects in it
44 * (see js::NukeCrossCompartmentWrappers)
46 * - XPConnect security wrappers, which protect chrome from malicious content
47 * (js/xpconnect/wrappers)
49 * - DOM objects with special property behavior, like named getters
50 * (dom/bindings/Codegen.py generates these proxies from WebIDL)
52 * ### Proxies and internal methods
54 * ES2019 specifies 13 internal methods. The runtime semantics of just about
55 * everything a script can do to an object is specified in terms of these
56 * internal methods. For example:
58 * JS code ES6 internal method that gets called
59 * --------------------------- --------------------------------
60 * obj.prop obj.[[Get]](obj, "prop")
61 * "prop" in obj obj.[[HasProperty]]("prop")
62 * new obj() obj.[[Construct]](<empty argument List>)
64 * With regard to the implementation of these internal methods, there are three
65 * very different kinds of object in SpiderMonkey.
67 * 1. Native objects cover most objects and contain both internal slots and
68 * properties. JSClassOps and ObjectOps may be used to override certain
71 * 2. Proxy objects are composed of internal slots and a ProxyHandler. The
72 * handler contains C++ methods that can implement these standard (and
73 * non-standard) internal methods. JSClassOps and ObjectOps for the base
74 * ProxyObject invoke the handler methods as appropriate.
76 * 3. Objects with custom layouts like TypedObjects. These rely on JSClassOps
77 * and ObjectOps to implement internal methods.
79 * Native objects with custom JSClassOps / ObjectOps are used when the object
80 * behaves very similar to a normal object such as the ArrayObject and it's
81 * length property. Most usages wrapping a C++ or other type should prefer
82 * using a Proxy. Using the proxy approach makes it much easier to create an
83 * ECMAScript and JIT compatible object, particularly if using an appropriate
86 * Just about anything you do to a proxy will end up going through a C++
87 * virtual method call. Possibly several. There's no reason the JITs and ICs
88 * can't specialize for particular proxies, based on the handler; but currently
89 * we don't do much of this, so the virtual method overhead typically is
92 * ### The proxy handler hierarchy
94 * A major use case for proxies is to forward each internal method call to
95 * another object, known as its target. The target can be an arbitrary JS
96 * object. Not every proxy has the notion of a target, however.
98 * To minimize code duplication, a set of abstract proxy handler classes is
99 * provided, from which other handlers may inherit. These abstract classes are
100 * organized in the following hierarchy:
104 * | NurseryAllocableProxyHandler
105 * | // allocated in the nursery; disallows
106 * | // overriding finalize method
108 * ForwardingProxyHandler // has a target and forwards internal methods
110 * Wrapper // can be unwrapped to reveal target
111 * | // (see js::CheckedUnwrap)
113 * CrossCompartmentWrapper // target is in another compartment;
114 * // implements membrane between compartments
116 * Example: Some DOM objects (including all the arraylike DOM objects) are
117 * implemented as proxies. Since these objects don't need to forward operations
118 * to any underlying JS object, BaseDOMProxyHandler directly subclasses
121 * Gecko's security wrappers are examples of cross-compartment wrappers.
123 * ### Proxy prototype chains
125 * While most ECMAScript internal methods are handled by simply calling the
126 * handler method, the [[GetPrototypeOf]] / [[SetPrototypeOf]] behaviors may
127 * follow one of two models:
129 * 1. A concrete prototype object (or null) is passed to object construction
130 * and ordinary prototype read and write applies. The prototype-related
131 * handler hooks are never called in this case. The [[Prototype]] slot is
132 * used to store the current prototype value.
134 * 2. TaggedProto::LazyProto is passed to NewProxyObject (or the
135 * ProxyOptions::lazyProto flag is set). Each read or write of the
136 * prototype will invoke the handler. This dynamic prototype behavior may
137 * be useful for wrapper-like objects. If this mode is used the
138 * getPrototype handler at a minimum must be implemented.
140 * NOTE: In this mode the [[Prototype]] internal slot is unavailable and
141 * must be simulated if needed. This is non-standard, but an
142 * appropriate handler can hide this implementation detail.
144 * One subtlety here is that ECMAScript has a notion of "ordinary" prototypes.
145 * An object that doesn't override [[GetPrototypeOf]] is considered to have an
146 * ordinary prototype. The getPrototypeIfOrdinary handler must be implemented
147 * by you or your base class. Typically model 1 will be considered "ordinary"
148 * and model 2 will not.
152 * BaseProxyHandler is the most generic kind of proxy handler. It does not make
153 * any assumptions about the target. Consequently, it does not provide any
154 * default implementation for most methods. As a convenience, a few high-level
155 * methods, like get() and set(), are given default implementations that work by
156 * calling the low-level methods, like getOwnPropertyDescriptor().
158 * Important: If you add a method here, you should probably also add a
159 * Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an
160 * explicit override for the method in SecurityWrapper. See bug 945826 comment
163 class JS_PUBLIC_API BaseProxyHandler
{
165 * Sometimes it's desirable to designate groups of proxy handlers as
166 * "similar". For this, we use the notion of a "family": A consumer-provided
167 * opaque pointer that designates the larger group to which this proxy
170 * If it will never be important to differentiate this proxy from others as
171 * part of a distinct group, nullptr may be used instead.
176 * Proxy handlers can use mHasPrototype to request the following special
177 * treatment from the JS engine:
179 * - When mHasPrototype is true, the engine never calls these methods:
180 * has, set, enumerate, iterate. Instead, for these operations,
181 * it calls the "own" methods like getOwnPropertyDescriptor, hasOwn,
182 * defineProperty, getOwnEnumerablePropertyKeys, etc.,
183 * and consults the prototype chain if needed.
185 * - When mHasPrototype is true, the engine calls handler->get() only if
186 * handler->hasOwn() says an own property exists on the proxy. If not,
187 * it consults the prototype chain.
189 * This is useful because it frees the ProxyHandler from having to implement
190 * any behavior having to do with the prototype chain.
195 * All proxies indicate whether they have any sort of interesting security
196 * policy that might prevent the caller from doing something it wants to
197 * the object. In the case of wrappers, this distinction is used to
198 * determine whether the caller may strip off the wrapper if it so desires.
200 bool mHasSecurityPolicy
;
203 explicit constexpr BaseProxyHandler(const void* aFamily
,
204 bool aHasPrototype
= false,
205 bool aHasSecurityPolicy
= false)
207 mHasPrototype(aHasPrototype
),
208 mHasSecurityPolicy(aHasSecurityPolicy
) {}
210 bool hasPrototype() const { return mHasPrototype
; }
212 bool hasSecurityPolicy() const { return mHasSecurityPolicy
; }
214 inline const void* family() const { return mFamily
; }
215 static size_t offsetOfFamily() { return offsetof(BaseProxyHandler
, mFamily
); }
217 virtual bool finalizeInBackground(const JS::Value
& priv
) const {
219 * Called on creation of a proxy to determine whether its finalize
220 * method can be finalized on the background thread.
225 virtual bool canNurseryAllocate() const {
227 * Nursery allocation is allowed if and only if it is safe to not
228 * run |finalize| when the ProxyObject dies.
233 /* Policy enforcement methods.
235 * enter() allows the policy to specify whether the caller may perform |act|
236 * on the proxy's |id| property. In the case when |act| is CALL, |id| is
237 * generally JS::PropertyKey::isVoid. The |mayThrow| parameter indicates
238 * whether a handler that wants to throw custom exceptions when denying
239 * should do so or not.
241 * The |act| parameter to enter() specifies the action being performed.
242 * If |bp| is false, the method suggests that the caller throw (though it
243 * may still decide to squelch the error).
245 * We make these OR-able so that assertEnteredPolicy can pass a union of them.
246 * For example, get{,Own}PropertyDescriptor is invoked by calls to ::get()
247 * ::set(), in addition to being invoked on its own, so there are several
248 * valid Actions that could have been entered.
250 typedef uint32_t Action
;
257 GET_PROPERTY_DESCRIPTOR
= 0x10
260 virtual bool enter(JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
261 Action act
, bool mayThrow
, bool* bp
) const;
263 /* Standard internal methods. */
264 virtual bool getOwnPropertyDescriptor(
265 JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
266 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
) const = 0;
267 virtual bool defineProperty(JSContext
* cx
, JS::HandleObject proxy
,
269 JS::Handle
<JS::PropertyDescriptor
> desc
,
270 JS::ObjectOpResult
& result
) const = 0;
271 virtual bool ownPropertyKeys(JSContext
* cx
, JS::HandleObject proxy
,
272 JS::MutableHandleIdVector props
) const = 0;
273 virtual bool delete_(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
274 JS::ObjectOpResult
& result
) const = 0;
277 * These methods are standard, but the engine does not normally call them.
278 * They're opt-in. See "Proxy prototype chains" above.
280 * getPrototype() crashes if called. setPrototype() throws a TypeError.
282 virtual bool getPrototype(JSContext
* cx
, JS::HandleObject proxy
,
283 JS::MutableHandleObject protop
) const;
284 virtual bool setPrototype(JSContext
* cx
, JS::HandleObject proxy
,
285 JS::HandleObject proto
,
286 JS::ObjectOpResult
& result
) const;
288 /* Non-standard but conceptual kin to {g,s}etPrototype, so these live here. */
289 virtual bool getPrototypeIfOrdinary(JSContext
* cx
, JS::HandleObject proxy
,
291 JS::MutableHandleObject protop
) const = 0;
292 virtual bool setImmutablePrototype(JSContext
* cx
, JS::HandleObject proxy
,
293 bool* succeeded
) const;
295 virtual bool preventExtensions(JSContext
* cx
, JS::HandleObject proxy
,
296 JS::ObjectOpResult
& result
) const = 0;
297 virtual bool isExtensible(JSContext
* cx
, JS::HandleObject proxy
,
298 bool* extensible
) const = 0;
301 * These standard internal methods are implemented, as a convenience, so
302 * that ProxyHandler subclasses don't have to provide every single method.
304 * The base-class implementations work by calling getOwnPropertyDescriptor()
305 * and going up the [[Prototype]] chain if necessary. The algorithm for this
306 * follows what is defined for Ordinary Objects in the ES spec.
307 * They do not follow any standard. When in doubt, override them.
309 virtual bool has(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
311 virtual bool get(JSContext
* cx
, JS::HandleObject proxy
,
312 JS::HandleValue receiver
, JS::HandleId id
,
313 JS::MutableHandleValue vp
) const;
314 virtual bool set(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
315 JS::HandleValue v
, JS::HandleValue receiver
,
316 JS::ObjectOpResult
& result
) const;
318 // Use the ProxyExpando object for private fields, rather than taking the
319 // normal get/set/defineField paths.
320 virtual bool useProxyExpandoObjectForPrivateFields() const { return true; }
322 // For some exotic objects (WindowProxy, Location), we want to be able to
323 // throw rather than allow private fields on these objects.
325 // As a simplfying assumption, if throwOnPrivateFields returns true,
326 // we should also return true to useProxyExpandoObjectForPrivateFields.
327 virtual bool throwOnPrivateField() const { return false; }
330 * [[Call]] and [[Construct]] are standard internal methods but according
331 * to the spec, they are not present on every object.
333 * SpiderMonkey never calls a proxy's call()/construct() internal method
334 * unless isCallable()/isConstructor() returns true for that proxy.
336 * BaseProxyHandler::isCallable()/isConstructor() always return false, and
337 * BaseProxyHandler::call()/construct() crash if called. So if you're
338 * creating a kind of that is never callable, you don't have to override
339 * anything, but otherwise you probably want to override all four.
341 virtual bool call(JSContext
* cx
, JS::HandleObject proxy
,
342 const JS::CallArgs
& args
) const;
343 virtual bool construct(JSContext
* cx
, JS::HandleObject proxy
,
344 const JS::CallArgs
& args
) const;
346 /* SpiderMonkey extensions. */
347 virtual bool enumerate(JSContext
* cx
, JS::HandleObject proxy
,
348 JS::MutableHandleIdVector props
) const;
349 virtual bool hasOwn(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
351 virtual bool getOwnEnumerablePropertyKeys(
352 JSContext
* cx
, JS::HandleObject proxy
,
353 JS::MutableHandleIdVector props
) const;
354 virtual bool nativeCall(JSContext
* cx
, JS::IsAcceptableThis test
,
355 JS::NativeImpl impl
, const JS::CallArgs
& args
) const;
356 virtual bool getBuiltinClass(JSContext
* cx
, JS::HandleObject proxy
,
358 virtual bool isArray(JSContext
* cx
, JS::HandleObject proxy
,
359 JS::IsArrayAnswer
* answer
) const;
360 virtual const char* className(JSContext
* cx
, JS::HandleObject proxy
) const;
361 virtual JSString
* fun_toString(JSContext
* cx
, JS::HandleObject proxy
,
362 bool isToSource
) const;
363 virtual RegExpShared
* regexp_toShared(JSContext
* cx
,
364 JS::HandleObject proxy
) const;
365 virtual bool boxedValue_unbox(JSContext
* cx
, JS::HandleObject proxy
,
366 JS::MutableHandleValue vp
) const;
367 virtual void trace(JSTracer
* trc
, JSObject
* proxy
) const;
368 virtual void finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const;
369 virtual size_t objectMoved(JSObject
* proxy
, JSObject
* old
) const;
371 // Allow proxies, wrappers in particular, to specify callability at runtime.
372 // Note: These do not take const JSObject*, but they do in spirit.
373 // We are not prepared to do this, as there's little const correctness
374 // in the external APIs that handle proxies.
375 virtual bool isCallable(JSObject
* obj
) const;
376 virtual bool isConstructor(JSObject
* obj
) const;
378 virtual bool getElements(JSContext
* cx
, JS::HandleObject proxy
,
379 uint32_t begin
, uint32_t end
,
380 ElementAdder
* adder
) const;
382 virtual bool isScripted() const { return false; }
385 class JS_PUBLIC_API NurseryAllocableProxyHandler
: public BaseProxyHandler
{
386 using BaseProxyHandler::BaseProxyHandler
;
388 // Don't allow overriding the default finalize method.
389 void finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const final
{
390 BaseProxyHandler::finalize(gcx
, proxy
);
392 // Can allocate in the nursery as long as we use the default finalize method.
393 bool canNurseryAllocate() const override
{ return true; }
396 extern JS_PUBLIC_DATA
const JSClass ProxyClass
;
398 inline bool IsProxy(const JSObject
* obj
) {
399 return reinterpret_cast<const JS::shadow::Object
*>(obj
)->shape
->isProxy();
407 // Every proxy has a ProxyValueArray that contains the following Values:
409 // - The expando slot. This is used to hold private fields should they be
410 // stamped into a non-forwarding proxy type.
411 // - The private slot.
412 // - The reserved slots. The number of slots is determined by the proxy's Class.
414 // Proxy objects store a pointer to the reserved slots (ProxyReservedSlots*).
415 // The ProxyValueArray and the private slot can be accessed using
416 // ProxyValueArray::fromReservedSlots or ProxyDataLayout::values.
418 // Storing a pointer to ProxyReservedSlots instead of ProxyValueArray has a
419 // number of advantages. In particular, it means JS::GetReservedSlot and
420 // JS::SetReservedSlot can be used with both proxies and native objects. This
421 // works because the ProxyReservedSlots* pointer is stored where native objects
422 // store their dynamic slots pointer.
424 struct ProxyReservedSlots
{
427 static constexpr ptrdiff_t offsetOfPrivateSlot();
429 static inline int offsetOfSlot(size_t slot
) {
430 return offsetof(ProxyReservedSlots
, slots
[0]) + slot
* sizeof(JS::Value
);
433 void init(size_t nreserved
) {
434 for (size_t i
= 0; i
< nreserved
; i
++) {
435 slots
[i
] = JS::UndefinedValue();
439 ProxyReservedSlots(const ProxyReservedSlots
&) = delete;
440 void operator=(const ProxyReservedSlots
&) = delete;
443 struct ProxyValueArray
{
444 JS::Value expandoSlot
;
445 JS::Value privateSlot
;
446 ProxyReservedSlots reservedSlots
;
448 void init(size_t nreserved
) {
449 expandoSlot
= JS::ObjectOrNullValue(nullptr);
450 privateSlot
= JS::UndefinedValue();
451 reservedSlots
.init(nreserved
);
454 static MOZ_ALWAYS_INLINE ProxyValueArray
* fromReservedSlots(
455 ProxyReservedSlots
* slots
) {
456 uintptr_t p
= reinterpret_cast<uintptr_t>(slots
);
457 return reinterpret_cast<ProxyValueArray
*>(p
- offsetOfReservedSlots());
459 static constexpr size_t offsetOfReservedSlots() {
460 return offsetof(ProxyValueArray
, reservedSlots
);
463 static size_t allocCount(size_t nreserved
) {
464 static_assert(offsetOfReservedSlots() % sizeof(JS::Value
) == 0);
465 return offsetOfReservedSlots() / sizeof(JS::Value
) + nreserved
;
467 static size_t sizeOf(size_t nreserved
) {
468 return allocCount(nreserved
) * sizeof(JS::Value
);
471 ProxyValueArray(const ProxyValueArray
&) = delete;
472 void operator=(const ProxyValueArray
&) = delete;
476 constexpr ptrdiff_t ProxyReservedSlots::offsetOfPrivateSlot() {
477 return -ptrdiff_t(ProxyValueArray::offsetOfReservedSlots()) +
478 offsetof(ProxyValueArray
, privateSlot
);
481 // All proxies share the same data layout. Following the object's shape, the
482 // proxy has a ProxyDataLayout structure with a pointer to an array of values
483 // and the proxy's handler. This is designed both so that proxies can be easily
484 // swapped with other objects (via RemapWrapper) and to mimic the layout of
485 // other objects (proxies and other objects have the same size) so that common
486 // code can access either type of object.
488 // See GetReservedOrProxyPrivateSlot below.
489 struct ProxyDataLayout
{
490 ProxyReservedSlots
* reservedSlots
;
491 const BaseProxyHandler
* handler
;
493 MOZ_ALWAYS_INLINE ProxyValueArray
* values() const {
494 return ProxyValueArray::fromReservedSlots(reservedSlots
);
499 constexpr uint32_t ProxyDataOffset
= 1 * sizeof(void*);
501 constexpr uint32_t ProxyDataOffset
= 2 * sizeof(void*);
504 inline ProxyDataLayout
* GetProxyDataLayout(JSObject
* obj
) {
505 MOZ_ASSERT(IsProxy(obj
));
506 return reinterpret_cast<ProxyDataLayout
*>(reinterpret_cast<uint8_t*>(obj
) +
510 inline const ProxyDataLayout
* GetProxyDataLayout(const JSObject
* obj
) {
511 MOZ_ASSERT(IsProxy(obj
));
512 return reinterpret_cast<const ProxyDataLayout
*>(
513 reinterpret_cast<const uint8_t*>(obj
) + ProxyDataOffset
);
516 JS_PUBLIC_API
void SetValueInProxy(JS::Value
* slot
, const JS::Value
& value
);
518 inline void SetProxyReservedSlotUnchecked(JSObject
* obj
, size_t n
,
519 const JS::Value
& extra
) {
520 MOZ_ASSERT(n
< JSCLASS_RESERVED_SLOTS(JS::GetClass(obj
)));
522 JS::Value
* vp
= &GetProxyDataLayout(obj
)->reservedSlots
->slots
[n
];
524 // Trigger a barrier before writing the slot.
525 if (vp
->isGCThing() || extra
.isGCThing()) {
526 SetValueInProxy(vp
, extra
);
532 } // namespace detail
534 inline const BaseProxyHandler
* GetProxyHandler(const JSObject
* obj
) {
535 return detail::GetProxyDataLayout(obj
)->handler
;
538 inline const JS::Value
& GetProxyPrivate(const JSObject
* obj
) {
539 return detail::GetProxyDataLayout(obj
)->values()->privateSlot
;
542 inline const JS::Value
& GetProxyExpando(const JSObject
* obj
) {
543 return detail::GetProxyDataLayout(obj
)->values()->expandoSlot
;
546 inline JSObject
* GetProxyTargetObject(const JSObject
* obj
) {
547 return GetProxyPrivate(obj
).toObjectOrNull();
550 inline const JS::Value
& GetProxyReservedSlot(const JSObject
* obj
, size_t n
) {
551 MOZ_ASSERT(n
< JSCLASS_RESERVED_SLOTS(JS::GetClass(obj
)));
552 return detail::GetProxyDataLayout(obj
)->reservedSlots
->slots
[n
];
555 inline void SetProxyHandler(JSObject
* obj
, const BaseProxyHandler
* handler
) {
556 detail::GetProxyDataLayout(obj
)->handler
= handler
;
559 inline void SetProxyReservedSlot(JSObject
* obj
, size_t n
,
560 const JS::Value
& extra
) {
562 if (gc::detail::ObjectIsMarkedBlack(obj
)) {
563 JS::AssertValueIsNotGray(extra
);
567 detail::SetProxyReservedSlotUnchecked(obj
, n
, extra
);
570 inline void SetProxyPrivate(JSObject
* obj
, const JS::Value
& value
) {
572 JS::AssertObjectIsNotGray(obj
);
573 JS::AssertValueIsNotGray(value
);
576 JS::Value
* vp
= &detail::GetProxyDataLayout(obj
)->values()->privateSlot
;
578 // Trigger a barrier before writing the slot.
579 if (vp
->isGCThing() || value
.isGCThing()) {
580 detail::SetValueInProxy(vp
, value
);
586 inline bool IsScriptedProxy(const JSObject
* obj
) {
587 return IsProxy(obj
) && GetProxyHandler(obj
)->isScripted();
590 class MOZ_STACK_CLASS ProxyOptions
{
592 /* protected constructor for subclass */
593 explicit ProxyOptions(bool lazyProtoArg
)
594 : lazyProto_(lazyProtoArg
), clasp_(&ProxyClass
) {}
597 ProxyOptions() : ProxyOptions(false) {}
599 bool lazyProto() const { return lazyProto_
; }
600 ProxyOptions
& setLazyProto(bool flag
) {
605 const JSClass
* clasp() const { return clasp_
; }
606 ProxyOptions
& setClass(const JSClass
* claspArg
) {
613 const JSClass
* clasp_
;
616 JS_PUBLIC_API JSObject
* NewProxyObject(
617 JSContext
* cx
, const BaseProxyHandler
* handler
, JS::HandleValue priv
,
618 JSObject
* proto
, const ProxyOptions
& options
= ProxyOptions());
620 JSObject
* RenewProxyObject(JSContext
* cx
, JSObject
* obj
,
621 BaseProxyHandler
* handler
, const JS::Value
& priv
);
623 class JS_PUBLIC_API AutoEnterPolicy
{
625 typedef BaseProxyHandler::Action Action
;
626 AutoEnterPolicy(JSContext
* cx
, const BaseProxyHandler
* handler
,
627 JS::HandleObject wrapper
, JS::HandleId id
, Action act
,
633 allow
= handler
->hasSecurityPolicy()
634 ? handler
->enter(cx
, wrapper
, id
, act
, mayThrow
, &rv
)
636 recordEnter(cx
, wrapper
, id
, act
);
637 // We want to throw an exception if all of the following are true:
638 // * The policy disallowed access.
639 // * The policy set rv to false, indicating that we should throw.
640 // * The caller did not instruct us to ignore exceptions.
641 // * The policy did not throw itself.
642 if (!allow
&& !rv
&& mayThrow
) {
643 reportErrorIfExceptionIsNotPending(cx
, id
);
647 virtual ~AutoEnterPolicy() { recordLeave(); }
648 inline bool allowed() { return allow
; }
649 inline bool returnValue() {
650 MOZ_ASSERT(!allowed());
655 // no-op constructor for subclass
659 enteredAction(BaseProxyHandler::NONE
)
663 void reportErrorIfExceptionIsNotPending(JSContext
* cx
, JS::HandleId id
);
669 mozilla::Maybe
<JS::HandleObject
> enteredProxy
;
670 mozilla::Maybe
<JS::HandleId
> enteredId
;
671 Action enteredAction
;
673 // NB: We explicitly don't track the entered action here, because sometimes
674 // set() methods do an implicit get() during their implementation, leading
675 // to spurious assertions.
676 AutoEnterPolicy
* prev
;
677 void recordEnter(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
681 friend JS_PUBLIC_API
void assertEnteredPolicy(JSContext
* cx
, JSObject
* proxy
,
682 jsid id
, Action act
);
684 inline void recordEnter(JSContext
* cx
, JSObject
* proxy
, jsid id
, Action act
) {
686 inline void recordLeave() {}
690 // This operator needs to be deleted explicitly, otherwise Visual C++ will
691 // create it automatically when it is part of the export JS API. In that
692 // case, compile would fail because HandleId is not allowed to be assigned
693 // and consequently instantiation of assign operator of mozilla::Maybe
694 // would fail. See bug 1325351 comment 16. Copy constructor is removed at
695 // the same time for consistency.
696 AutoEnterPolicy(const AutoEnterPolicy
&) = delete;
697 AutoEnterPolicy
& operator=(const AutoEnterPolicy
&) = delete;
701 class JS_PUBLIC_API AutoWaivePolicy
: public AutoEnterPolicy
{
703 AutoWaivePolicy(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
704 BaseProxyHandler::Action act
) {
706 recordEnter(cx
, proxy
, id
, act
);
710 class JS_PUBLIC_API AutoWaivePolicy
{
712 AutoWaivePolicy(JSContext
* cx
, JS::HandleObject proxy
, JS::HandleId id
,
713 BaseProxyHandler::Action act
) {}
718 extern JS_PUBLIC_API
void assertEnteredPolicy(JSContext
* cx
, JSObject
* obj
,
720 BaseProxyHandler::Action act
);
722 inline void assertEnteredPolicy(JSContext
* cx
, JSObject
* obj
, jsid id
,
723 BaseProxyHandler::Action act
) {}
726 extern JS_PUBLIC_DATA
const JSClassOps ProxyClassOps
;
727 extern JS_PUBLIC_DATA
const js::ClassExtension ProxyClassExtension
;
728 extern JS_PUBLIC_DATA
const js::ObjectOps ProxyObjectOps
;
730 template <unsigned Flags
>
731 constexpr unsigned CheckProxyFlags() {
732 constexpr size_t reservedSlots
=
733 (Flags
>> JSCLASS_RESERVED_SLOTS_SHIFT
) & JSCLASS_RESERVED_SLOTS_MASK
;
735 // For now assert each Proxy Class has at least 1 reserved slot. This is
736 // not a hard requirement, but helps catch Classes that need an explicit
737 // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
738 static_assert(reservedSlots
> 0,
739 "Proxy Classes must have at least 1 reserved slot");
741 constexpr size_t numSlots
=
742 offsetof(js::detail::ProxyValueArray
, reservedSlots
) / sizeof(JS::Value
);
744 // ProxyValueArray must fit inline in the object, so assert the number of
745 // slots does not exceed MAX_FIXED_SLOTS.
746 static_assert(numSlots
+ reservedSlots
<= JS::shadow::Object::MAX_FIXED_SLOTS
,
747 "ProxyValueArray size must not exceed max JSObject size");
749 // Proxies must not have the JSCLASS_SKIP_NURSERY_FINALIZE flag set: they
750 // always have finalizers, and whether they can be nursery allocated is
751 // controlled by the canNurseryAllocate() method on the proxy handler.
752 static_assert(!(Flags
& JSCLASS_SKIP_NURSERY_FINALIZE
),
753 "Proxies must not use JSCLASS_SKIP_NURSERY_FINALIZE; use "
754 "the canNurseryAllocate() proxy handler method instead.");
758 #define PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, classSpec) \
760 JSClass::NON_NATIVE | JSCLASS_IS_PROXY | JSCLASS_DELAY_METADATA_BUILDER | \
761 js::CheckProxyFlags<flags>(), \
762 &js::ProxyClassOps, \
764 &js::ProxyClassExtension, \
767 #define PROXY_CLASS_DEF(name, flags) \
768 PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, JS_NULL_CLASS_SPEC)
770 // Converts a proxy into a DeadObjectProxy that will throw exceptions on all
771 // access. This will run the proxy's finalizer to perform clean-up before the
772 // conversion happens.
773 JS_PUBLIC_API
void NukeNonCCWProxy(JSContext
* cx
, JS::HandleObject proxy
);
775 // This is a variant of js::NukeNonCCWProxy() for CCWs. It should only be called
776 // on CCWs that have been removed from CCW tables.
777 JS_PUBLIC_API
void NukeRemovedCrossCompartmentWrapper(JSContext
* cx
,
782 #endif /* js_Proxy_h */