Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 */ |