js/src/jsproxy.h

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef jsproxy_h
michael@0 8 #define jsproxy_h
michael@0 9
michael@0 10 #include "mozilla/Maybe.h"
michael@0 11
michael@0 12 #include "jsfriendapi.h"
michael@0 13
michael@0 14 #include "js/CallNonGenericMethod.h"
michael@0 15 #include "js/Class.h"
michael@0 16
michael@0 17 namespace js {
michael@0 18
michael@0 19 using JS::AutoIdVector;
michael@0 20 using JS::CallArgs;
michael@0 21 using JS::HandleId;
michael@0 22 using JS::HandleObject;
michael@0 23 using JS::HandleValue;
michael@0 24 using JS::IsAcceptableThis;
michael@0 25 using JS::MutableHandle;
michael@0 26 using JS::MutableHandleObject;
michael@0 27 using JS::MutableHandleValue;
michael@0 28 using JS::NativeImpl;
michael@0 29 using JS::PrivateValue;
michael@0 30 using JS::Value;
michael@0 31
michael@0 32 class RegExpGuard;
michael@0 33 class JS_FRIEND_API(Wrapper);
michael@0 34
michael@0 35 /*
michael@0 36 * A proxy is a JSObject that implements generic behavior by providing custom
michael@0 37 * implementations for each object trap. The implementation for each trap is
michael@0 38 * provided by a C++ object stored on the proxy, known as its handler.
michael@0 39 *
michael@0 40 * A major use case for proxies is to forward each trap to another object,
michael@0 41 * known as its target. The target can be an arbitrary C++ object. Not every
michael@0 42 * proxy has the notion of a target, however.
michael@0 43 *
michael@0 44 * Proxy traps are grouped into fundamental and derived traps. Every proxy has
michael@0 45 * to at least provide implementations for the fundamental traps, but the
michael@0 46 * derived traps can be implemented in terms of the fundamental ones
michael@0 47 * BaseProxyHandler provides implementations of the derived traps in terms of
michael@0 48 * the (pure virtual) fundamental traps.
michael@0 49 *
michael@0 50 * In addition to the normal traps, there are two models for proxy prototype
michael@0 51 * chains. First, proxies may opt to use the standard prototype mechanism used
michael@0 52 * throughout the engine. To do so, simply pass a prototype to NewProxyObject()
michael@0 53 * at creation time. All prototype accesses will then "just work" to treat the
michael@0 54 * proxy as a "normal" object. Alternatively, if instead the proxy wishes to
michael@0 55 * implement more complicated prototype semantics (if, for example, it wants to
michael@0 56 * delegate the prototype lookup to a wrapped object), it may pass Proxy::LazyProto
michael@0 57 * as the prototype at create time and opt in to the trapped prototype system,
michael@0 58 * which guarantees that their trap will be called on any and every prototype
michael@0 59 * chain access of the object.
michael@0 60 *
michael@0 61 * This system is implemented with two traps: {get,set}PrototypeOf. The default
michael@0 62 * implementation of setPrototypeOf throws a TypeError. Since it is not possible
michael@0 63 * to create an object without a sense of prototype chain, handler implementors
michael@0 64 * must provide a getPrototypeOf trap if opting in to the dynamic prototype system.
michael@0 65 *
michael@0 66 * To minimize code duplication, a set of abstract proxy handler classes is
michael@0 67 * provided, from which other handlers may inherit. These abstract classes
michael@0 68 * are organized in the following hierarchy:
michael@0 69 *
michael@0 70 * BaseProxyHandler
michael@0 71 * |
michael@0 72 * DirectProxyHandler
michael@0 73 * |
michael@0 74 * Wrapper
michael@0 75 */
michael@0 76
michael@0 77 /*
michael@0 78 * BaseProxyHandler is the most generic kind of proxy handler. It does not make
michael@0 79 * any assumptions about the target. Consequently, it does not provide any
michael@0 80 * default implementation for the fundamental traps. It does, however, implement
michael@0 81 * the derived traps in terms of the fundamental ones. This allows consumers of
michael@0 82 * this class to define any custom behavior they want.
michael@0 83 *
michael@0 84 * Important: If you add a trap here, you should probably also add a Proxy::foo
michael@0 85 * entry point with an AutoEnterPolicy. If you don't, you need an explicit
michael@0 86 * override for the trap in SecurityWrapper. See bug 945826 comment 0.
michael@0 87 */
michael@0 88 class JS_FRIEND_API(BaseProxyHandler)
michael@0 89 {
michael@0 90 const void *mFamily;
michael@0 91
michael@0 92 /*
michael@0 93 * Proxy handlers can use mHasPrototype to request the following special
michael@0 94 * treatment from the JS engine:
michael@0 95 *
michael@0 96 * - When mHasPrototype is true, the engine never calls these methods:
michael@0 97 * getPropertyDescriptor, has, set, enumerate, iterate. Instead, for
michael@0 98 * these operations, it calls the "own" traps like
michael@0 99 * getOwnPropertyDescriptor, hasOwn, defineProperty, keys, etc., and
michael@0 100 * consults the prototype chain if needed.
michael@0 101 *
michael@0 102 * - When mHasPrototype is true, the engine calls handler->get() only if
michael@0 103 * handler->hasOwn() says an own property exists on the proxy. If not,
michael@0 104 * it consults the prototype chain.
michael@0 105 *
michael@0 106 * This is useful because it frees the ProxyHandler from having to implement
michael@0 107 * any behavior having to do with the prototype chain.
michael@0 108 */
michael@0 109 bool mHasPrototype;
michael@0 110
michael@0 111 /*
michael@0 112 * All proxies indicate whether they have any sort of interesting security
michael@0 113 * policy that might prevent the caller from doing something it wants to
michael@0 114 * the object. In the case of wrappers, this distinction is used to
michael@0 115 * determine whether the caller may strip off the wrapper if it so desires.
michael@0 116 */
michael@0 117 bool mHasSecurityPolicy;
michael@0 118
michael@0 119 protected:
michael@0 120 // Subclasses may set this in their constructor.
michael@0 121 void setHasPrototype(bool aHasPrototype) { mHasPrototype = aHasPrototype; }
michael@0 122 void setHasSecurityPolicy(bool aHasPolicy) { mHasSecurityPolicy = aHasPolicy; }
michael@0 123
michael@0 124 public:
michael@0 125 explicit BaseProxyHandler(const void *family);
michael@0 126 virtual ~BaseProxyHandler();
michael@0 127
michael@0 128 bool hasPrototype() {
michael@0 129 return mHasPrototype;
michael@0 130 }
michael@0 131
michael@0 132 bool hasSecurityPolicy() {
michael@0 133 return mHasSecurityPolicy;
michael@0 134 }
michael@0 135
michael@0 136 inline const void *family() {
michael@0 137 return mFamily;
michael@0 138 }
michael@0 139 static size_t offsetOfFamily() {
michael@0 140 return offsetof(BaseProxyHandler, mFamily);
michael@0 141 }
michael@0 142
michael@0 143 virtual bool finalizeInBackground(Value priv) {
michael@0 144 /*
michael@0 145 * Called on creation of a proxy to determine whether its finalize
michael@0 146 * method can be finalized on the background thread.
michael@0 147 */
michael@0 148 return true;
michael@0 149 }
michael@0 150
michael@0 151 /* Policy enforcement traps.
michael@0 152 *
michael@0 153 * enter() allows the policy to specify whether the caller may perform |act|
michael@0 154 * on the proxy's |id| property. In the case when |act| is CALL, |id| is
michael@0 155 * generally JSID_VOID.
michael@0 156 *
michael@0 157 * The |act| parameter to enter() specifies the action being performed.
michael@0 158 * If |bp| is false, the trap suggests that the caller throw (though it
michael@0 159 * may still decide to squelch the error).
michael@0 160 *
michael@0 161 * We make these OR-able so that assertEnteredPolicy can pass a union of them.
michael@0 162 * For example, get{,Own}PropertyDescriptor is invoked by both calls to ::get()
michael@0 163 * and ::set() (since we need to look up the accessor), so its
michael@0 164 * assertEnteredPolicy would pass GET | SET.
michael@0 165 */
michael@0 166 typedef uint32_t Action;
michael@0 167 enum {
michael@0 168 NONE = 0x00,
michael@0 169 GET = 0x01,
michael@0 170 SET = 0x02,
michael@0 171 CALL = 0x04,
michael@0 172 ENUMERATE = 0x08
michael@0 173 };
michael@0 174
michael@0 175 virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
michael@0 176 bool *bp);
michael@0 177
michael@0 178 /* ES5 Harmony fundamental proxy traps. */
michael@0 179 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) = 0;
michael@0 180 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 181 MutableHandle<JSPropertyDescriptor> desc) = 0;
michael@0 182 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
michael@0 183 HandleId id, MutableHandle<JSPropertyDescriptor> desc) = 0;
michael@0 184 virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 185 MutableHandle<JSPropertyDescriptor> desc) = 0;
michael@0 186 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
michael@0 187 AutoIdVector &props) = 0;
michael@0 188 virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) = 0;
michael@0 189 virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) = 0;
michael@0 190
michael@0 191 /* ES5 Harmony derived proxy traps. */
michael@0 192 virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
michael@0 193 virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
michael@0 194 virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
michael@0 195 HandleId id, MutableHandleValue vp);
michael@0 196 virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
michael@0 197 HandleId id, bool strict, MutableHandleValue vp);
michael@0 198 virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
michael@0 199 virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
michael@0 200 MutableHandleValue vp);
michael@0 201
michael@0 202 /* Spidermonkey extensions. */
michael@0 203 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) = 0;
michael@0 204 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
michael@0 205 virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
michael@0 206 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
michael@0 207 virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
michael@0 208 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
michael@0 209 virtual const char *className(JSContext *cx, HandleObject proxy);
michael@0 210 virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
michael@0 211 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
michael@0 212 virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
michael@0 213 virtual void finalize(JSFreeOp *fop, JSObject *proxy);
michael@0 214 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
michael@0 215 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
michael@0 216
michael@0 217 // These two hooks must be overridden, or not overridden, in tandem -- no
michael@0 218 // overriding just one!
michael@0 219 virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
michael@0 220 JS::HandleObject callable);
michael@0 221 virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id);
michael@0 222
michael@0 223 virtual bool slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
michael@0 224 HandleObject result);
michael@0 225
michael@0 226 /* See comment for weakmapKeyDelegateOp in js/Class.h. */
michael@0 227 virtual JSObject *weakmapKeyDelegate(JSObject *proxy);
michael@0 228 virtual bool isScripted() { return false; }
michael@0 229 };
michael@0 230
michael@0 231 /*
michael@0 232 * DirectProxyHandler includes a notion of a target object. All traps are
michael@0 233 * reimplemented such that they forward their behavior to the target. This
michael@0 234 * allows consumers of this class to forward to another object as transparently
michael@0 235 * and efficiently as possible.
michael@0 236 *
michael@0 237 * Important: If you add a trap implementation here, you probably also need to
michael@0 238 * add an override in CrossCompartmentWrapper. If you don't, you risk
michael@0 239 * compartment mismatches. See bug 945826 comment 0.
michael@0 240 */
michael@0 241 class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
michael@0 242 {
michael@0 243 public:
michael@0 244 explicit DirectProxyHandler(const void *family);
michael@0 245
michael@0 246 /* ES5 Harmony fundamental proxy traps. */
michael@0 247 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
michael@0 248 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 249 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
michael@0 250 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 251 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
michael@0 252 virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 253 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
michael@0 254 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
michael@0 255 AutoIdVector &props) MOZ_OVERRIDE;
michael@0 256 virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 257 bool *bp) MOZ_OVERRIDE;
michael@0 258 virtual bool enumerate(JSContext *cx, HandleObject proxy,
michael@0 259 AutoIdVector &props) MOZ_OVERRIDE;
michael@0 260
michael@0 261 /* ES5 Harmony derived proxy traps. */
michael@0 262 virtual bool has(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 263 bool *bp) MOZ_OVERRIDE;
michael@0 264 virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 265 bool *bp) MOZ_OVERRIDE;
michael@0 266 virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
michael@0 267 HandleId id, MutableHandleValue vp) MOZ_OVERRIDE;
michael@0 268 virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
michael@0 269 HandleId id, bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
michael@0 270 virtual bool keys(JSContext *cx, HandleObject proxy,
michael@0 271 AutoIdVector &props) MOZ_OVERRIDE;
michael@0 272 virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
michael@0 273 MutableHandleValue vp) MOZ_OVERRIDE;
michael@0 274
michael@0 275 /* Spidermonkey extensions. */
michael@0 276 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
michael@0 277 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
michael@0 278 virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
michael@0 279 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
michael@0 280 CallArgs args) MOZ_OVERRIDE;
michael@0 281 virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
michael@0 282 bool *bp) MOZ_OVERRIDE;
michael@0 283 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
michael@0 284 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
michael@0 285 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
michael@0 286 JSContext *cx) MOZ_OVERRIDE;
michael@0 287 virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
michael@0 288 virtual JSString *fun_toString(JSContext *cx, HandleObject proxy,
michael@0 289 unsigned indent) MOZ_OVERRIDE;
michael@0 290 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy,
michael@0 291 RegExpGuard *g) MOZ_OVERRIDE;
michael@0 292 virtual JSObject *weakmapKeyDelegate(JSObject *proxy);
michael@0 293 };
michael@0 294
michael@0 295 /*
michael@0 296 * Dispatch point for handlers that executes the appropriate C++ or scripted traps.
michael@0 297 *
michael@0 298 * Important: All proxy traps need either (a) an AutoEnterPolicy in their
michael@0 299 * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
michael@0 300 * 945826 comment 0.
michael@0 301 */
michael@0 302 class Proxy
michael@0 303 {
michael@0 304 public:
michael@0 305 /* ES5 Harmony fundamental proxy traps. */
michael@0 306 static bool preventExtensions(JSContext *cx, HandleObject proxy);
michael@0 307 static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 308 MutableHandle<JSPropertyDescriptor> desc);
michael@0 309 static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 310 MutableHandleValue vp);
michael@0 311 static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 312 MutableHandle<JSPropertyDescriptor> desc);
michael@0 313 static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 314 MutableHandleValue vp);
michael@0 315 static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 316 MutableHandle<JSPropertyDescriptor> desc);
michael@0 317 static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v);
michael@0 318 static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
michael@0 319 static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
michael@0 320 static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props);
michael@0 321
michael@0 322 /* ES5 Harmony derived proxy traps. */
michael@0 323 static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
michael@0 324 static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
michael@0 325 static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
michael@0 326 MutableHandleValue vp);
michael@0 327 static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
michael@0 328 bool strict, MutableHandleValue vp);
michael@0 329 static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
michael@0 330 static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
michael@0 331
michael@0 332 /* Spidermonkey extensions. */
michael@0 333 static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
michael@0 334 static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
michael@0 335 static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
michael@0 336 static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
michael@0 337 static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
michael@0 338 static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
michael@0 339 static const char *className(JSContext *cx, HandleObject proxy);
michael@0 340 static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
michael@0 341 static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
michael@0 342 static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
michael@0 343 static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
michael@0 344 static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
michael@0 345
michael@0 346 static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable);
michael@0 347 static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id);
michael@0 348
michael@0 349 static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end,
michael@0 350 HandleObject result);
michael@0 351
michael@0 352 /* IC entry path for handling __noSuchMethod__ on access. */
michael@0 353 static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,
michael@0 354 MutableHandleValue vp);
michael@0 355 };
michael@0 356
michael@0 357 // Use these in places where you don't want to #include vm/ProxyObject.h.
michael@0 358 extern JS_FRIEND_DATA(const js::Class* const) CallableProxyClassPtr;
michael@0 359 extern JS_FRIEND_DATA(const js::Class* const) UncallableProxyClassPtr;
michael@0 360
michael@0 361 inline bool IsProxy(JSObject *obj)
michael@0 362 {
michael@0 363 return GetObjectClass(obj)->isProxy();
michael@0 364 }
michael@0 365
michael@0 366 /*
michael@0 367 * These are part of the API.
michael@0 368 *
michael@0 369 * NOTE: PROXY_PRIVATE_SLOT is 0 because that way slot 0 is usable by API
michael@0 370 * clients for both proxy and non-proxy objects. So an API client that only
michael@0 371 * needs to store one slot's worth of data doesn't need to branch on what sort
michael@0 372 * of object it has.
michael@0 373 */
michael@0 374 const uint32_t PROXY_PRIVATE_SLOT = 0;
michael@0 375 const uint32_t PROXY_HANDLER_SLOT = 1;
michael@0 376 const uint32_t PROXY_EXTRA_SLOT = 2;
michael@0 377 const uint32_t PROXY_MINIMUM_SLOTS = 4;
michael@0 378
michael@0 379 inline BaseProxyHandler *
michael@0 380 GetProxyHandler(JSObject *obj)
michael@0 381 {
michael@0 382 JS_ASSERT(IsProxy(obj));
michael@0 383 return (BaseProxyHandler *) GetReservedSlot(obj, PROXY_HANDLER_SLOT).toPrivate();
michael@0 384 }
michael@0 385
michael@0 386 inline const Value &
michael@0 387 GetProxyPrivate(JSObject *obj)
michael@0 388 {
michael@0 389 JS_ASSERT(IsProxy(obj));
michael@0 390 return GetReservedSlot(obj, PROXY_PRIVATE_SLOT);
michael@0 391 }
michael@0 392
michael@0 393 inline JSObject *
michael@0 394 GetProxyTargetObject(JSObject *obj)
michael@0 395 {
michael@0 396 JS_ASSERT(IsProxy(obj));
michael@0 397 return GetProxyPrivate(obj).toObjectOrNull();
michael@0 398 }
michael@0 399
michael@0 400 inline const Value &
michael@0 401 GetProxyExtra(JSObject *obj, size_t n)
michael@0 402 {
michael@0 403 JS_ASSERT(IsProxy(obj));
michael@0 404 return GetReservedSlot(obj, PROXY_EXTRA_SLOT + n);
michael@0 405 }
michael@0 406
michael@0 407 inline void
michael@0 408 SetProxyHandler(JSObject *obj, BaseProxyHandler *handler)
michael@0 409 {
michael@0 410 JS_ASSERT(IsProxy(obj));
michael@0 411 SetReservedSlot(obj, PROXY_HANDLER_SLOT, PrivateValue(handler));
michael@0 412 }
michael@0 413
michael@0 414 inline void
michael@0 415 SetProxyExtra(JSObject *obj, size_t n, const Value &extra)
michael@0 416 {
michael@0 417 JS_ASSERT(IsProxy(obj));
michael@0 418 JS_ASSERT(n <= 1);
michael@0 419 SetReservedSlot(obj, PROXY_EXTRA_SLOT + n, extra);
michael@0 420 }
michael@0 421
michael@0 422 inline bool
michael@0 423 IsScriptedProxy(JSObject *obj)
michael@0 424 {
michael@0 425 return IsProxy(obj) && GetProxyHandler(obj)->isScripted();
michael@0 426 }
michael@0 427
michael@0 428 class MOZ_STACK_CLASS ProxyOptions {
michael@0 429 protected:
michael@0 430 /* protected constructor for subclass */
michael@0 431 ProxyOptions(bool singletonArg, const Class *claspArg)
michael@0 432 : singleton_(singletonArg),
michael@0 433 clasp_(claspArg)
michael@0 434 {}
michael@0 435
michael@0 436 public:
michael@0 437 ProxyOptions() : singleton_(false),
michael@0 438 clasp_(UncallableProxyClassPtr)
michael@0 439 {}
michael@0 440
michael@0 441 bool singleton() const { return singleton_; }
michael@0 442 ProxyOptions &setSingleton(bool flag) {
michael@0 443 singleton_ = flag;
michael@0 444 return *this;
michael@0 445 }
michael@0 446
michael@0 447 const Class *clasp() const {
michael@0 448 return clasp_;
michael@0 449 }
michael@0 450 ProxyOptions &setClass(const Class *claspArg) {
michael@0 451 clasp_ = claspArg;
michael@0 452 return *this;
michael@0 453 }
michael@0 454 ProxyOptions &selectDefaultClass(bool callable) {
michael@0 455 const Class *classp = callable? CallableProxyClassPtr :
michael@0 456 UncallableProxyClassPtr;
michael@0 457 return setClass(classp);
michael@0 458 }
michael@0 459
michael@0 460 private:
michael@0 461 bool singleton_;
michael@0 462 const Class *clasp_;
michael@0 463 };
michael@0 464
michael@0 465 JS_FRIEND_API(JSObject *)
michael@0 466 NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv,
michael@0 467 JSObject *proto, JSObject *parent, const ProxyOptions &options = ProxyOptions());
michael@0 468
michael@0 469 JSObject *
michael@0 470 RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv);
michael@0 471
michael@0 472 class JS_FRIEND_API(AutoEnterPolicy)
michael@0 473 {
michael@0 474 public:
michael@0 475 typedef BaseProxyHandler::Action Action;
michael@0 476 AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler,
michael@0 477 HandleObject wrapper, HandleId id, Action act, bool mayThrow)
michael@0 478 #ifdef JS_DEBUG
michael@0 479 : context(nullptr)
michael@0 480 #endif
michael@0 481 {
michael@0 482 allow = handler->hasSecurityPolicy() ? handler->enter(cx, wrapper, id, act, &rv)
michael@0 483 : true;
michael@0 484 recordEnter(cx, wrapper, id, act);
michael@0 485 // We want to throw an exception if all of the following are true:
michael@0 486 // * The policy disallowed access.
michael@0 487 // * The policy set rv to false, indicating that we should throw.
michael@0 488 // * The caller did not instruct us to ignore exceptions.
michael@0 489 // * The policy did not throw itself.
michael@0 490 if (!allow && !rv && mayThrow)
michael@0 491 reportErrorIfExceptionIsNotPending(cx, id);
michael@0 492 }
michael@0 493
michael@0 494 virtual ~AutoEnterPolicy() { recordLeave(); }
michael@0 495 inline bool allowed() { return allow; }
michael@0 496 inline bool returnValue() { JS_ASSERT(!allowed()); return rv; }
michael@0 497
michael@0 498 protected:
michael@0 499 // no-op constructor for subclass
michael@0 500 AutoEnterPolicy()
michael@0 501 #ifdef JS_DEBUG
michael@0 502 : context(nullptr)
michael@0 503 , enteredAction(BaseProxyHandler::NONE)
michael@0 504 #endif
michael@0 505 {};
michael@0 506 void reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id);
michael@0 507 bool allow;
michael@0 508 bool rv;
michael@0 509
michael@0 510 #ifdef JS_DEBUG
michael@0 511 JSContext *context;
michael@0 512 mozilla::Maybe<HandleObject> enteredProxy;
michael@0 513 mozilla::Maybe<HandleId> enteredId;
michael@0 514 Action enteredAction;
michael@0 515
michael@0 516 // NB: We explicitly don't track the entered action here, because sometimes
michael@0 517 // SET traps do an implicit GET during their implementation, leading to
michael@0 518 // spurious assertions.
michael@0 519 AutoEnterPolicy *prev;
michael@0 520 void recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act);
michael@0 521 void recordLeave();
michael@0 522
michael@0 523 friend JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, Action act);
michael@0 524 #else
michael@0 525 inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id, Action act) {}
michael@0 526 inline void recordLeave() {}
michael@0 527 #endif
michael@0 528
michael@0 529 };
michael@0 530
michael@0 531 #ifdef JS_DEBUG
michael@0 532 class JS_FRIEND_API(AutoWaivePolicy) : public AutoEnterPolicy {
michael@0 533 public:
michael@0 534 AutoWaivePolicy(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 535 BaseProxyHandler::Action act)
michael@0 536 {
michael@0 537 allow = true;
michael@0 538 recordEnter(cx, proxy, id, act);
michael@0 539 }
michael@0 540 };
michael@0 541 #else
michael@0 542 class JS_FRIEND_API(AutoWaivePolicy) {
michael@0 543 public:
michael@0 544 AutoWaivePolicy(JSContext *cx, HandleObject proxy, HandleId id,
michael@0 545 BaseProxyHandler::Action act)
michael@0 546 {}
michael@0 547 };
michael@0 548 #endif
michael@0 549
michael@0 550 #ifdef JS_DEBUG
michael@0 551 extern JS_FRIEND_API(void)
michael@0 552 assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id,
michael@0 553 BaseProxyHandler::Action act);
michael@0 554 #else
michael@0 555 inline void assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id,
michael@0 556 BaseProxyHandler::Action act)
michael@0 557 {};
michael@0 558 #endif
michael@0 559
michael@0 560 } /* namespace js */
michael@0 561
michael@0 562 extern JS_FRIEND_API(JSObject *)
michael@0 563 js_InitProxyClass(JSContext *cx, JS::HandleObject obj);
michael@0 564
michael@0 565 #endif /* jsproxy_h */

mercurial