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 */
|