Coverage Report

Created: 2018-09-25 14:53

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