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