js/src/jsproxy.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     7 #include "jsproxy.h"
     9 #include <string.h>
    11 #include "jsapi.h"
    12 #include "jscntxt.h"
    13 #include "jsfun.h"
    14 #include "jsgc.h"
    15 #include "jswrapper.h"
    17 #include "gc/Marking.h"
    18 #include "vm/WrapperObject.h"
    20 #include "jsatominlines.h"
    21 #include "jsinferinlines.h"
    22 #include "jsobjinlines.h"
    24 #include "vm/ObjectImpl-inl.h"
    26 using namespace js;
    27 using namespace js::gc;
    28 using mozilla::ArrayLength;
    30 void
    31 js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id)
    32 {
    33     if (JS_IsExceptionPending(cx))
    34         return;
    36     if (JSID_IS_VOID(id)) {
    37         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
    38                              JSMSG_OBJECT_ACCESS_DENIED);
    39     } else {
    40         JSString *str = IdToString(cx, id);
    41         const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
    42         JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
    43                                JSMSG_PROPERTY_ACCESS_DENIED, prop);
    44     }
    45 }
    47 #ifdef DEBUG
    48 void
    49 js::AutoEnterPolicy::recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act)
    50 {
    51     if (allowed()) {
    52         context = cx;
    53         enteredProxy.construct(proxy);
    54         enteredId.construct(id);
    55         enteredAction = act;
    56         prev = cx->runtime()->enteredPolicy;
    57         cx->runtime()->enteredPolicy = this;
    58     }
    59 }
    61 void
    62 js::AutoEnterPolicy::recordLeave()
    63 {
    64     if (!enteredProxy.empty()) {
    65         JS_ASSERT(context->runtime()->enteredPolicy == this);
    66         context->runtime()->enteredPolicy = prev;
    67     }
    68 }
    70 JS_FRIEND_API(void)
    71 js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id,
    72                         BaseProxyHandler::Action act)
    73 {
    74     MOZ_ASSERT(proxy->is<ProxyObject>());
    75     MOZ_ASSERT(cx->runtime()->enteredPolicy);
    76     MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredProxy.ref().get() == proxy);
    77     MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId.ref().get() == id);
    78     MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredAction & act);
    79 }
    80 #endif
    82 BaseProxyHandler::BaseProxyHandler(const void *family)
    83   : mFamily(family),
    84     mHasPrototype(false),
    85     mHasSecurityPolicy(false)
    86 {
    87 }
    89 BaseProxyHandler::~BaseProxyHandler()
    90 {
    91 }
    93 bool
    94 BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
    95                         bool *bp)
    96 {
    97     *bp = true;
    98     return true;
    99 }
   101 bool
   102 BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   103 {
   104     assertEnteredPolicy(cx, proxy, id, GET);
   105     Rooted<PropertyDescriptor> desc(cx);
   106     if (!getPropertyDescriptor(cx, proxy, id, &desc))
   107         return false;
   108     *bp = !!desc.object();
   109     return true;
   110 }
   112 bool
   113 BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   114 {
   115     // Note: Proxy::set needs to invoke hasOwn to determine where the setter
   116     // lives, so we allow SET operations to invoke us.
   117     assertEnteredPolicy(cx, proxy, id, GET | SET);
   118     Rooted<PropertyDescriptor> desc(cx);
   119     if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   120         return false;
   121     *bp = !!desc.object();
   122     return true;
   123 }
   125 bool
   126 BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   127                       HandleId id, MutableHandleValue vp)
   128 {
   129     assertEnteredPolicy(cx, proxy, id, GET);
   131     Rooted<PropertyDescriptor> desc(cx);
   132     if (!getPropertyDescriptor(cx, proxy, id, &desc))
   133         return false;
   134     if (!desc.object()) {
   135         vp.setUndefined();
   136         return true;
   137     }
   138     if (!desc.getter() ||
   139         (!desc.hasGetterObject() && desc.getter() == JS_PropertyStub))
   140     {
   141         vp.set(desc.value());
   142         return true;
   143     }
   144     if (desc.hasGetterObject())
   145         return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()),
   146                                     0, nullptr, vp);
   147     if (!desc.isShared())
   148         vp.set(desc.value());
   149     else
   150         vp.setUndefined();
   152     return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp);
   153 }
   155 bool
   156 BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   157                       HandleId id, bool strict, MutableHandleValue vp)
   158 {
   159     assertEnteredPolicy(cx, proxy, id, SET);
   161     // Find an own or inherited property. The code here is strange for maximum
   162     // backward compatibility with earlier code written before ES6 and before
   163     // SetPropertyIgnoringNamedGetter.
   164     Rooted<PropertyDescriptor> desc(cx);
   165     if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   166         return false;
   167     bool descIsOwn = desc.object() != nullptr;
   168     if (!descIsOwn) {
   169         if (!getPropertyDescriptor(cx, proxy, id, &desc))
   170             return false;
   171     }
   173     return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict,
   174                                           vp);
   175 }
   177 bool
   178 js::SetPropertyIgnoringNamedGetter(JSContext *cx, BaseProxyHandler *handler,
   179                                    HandleObject proxy, HandleObject receiver,
   180                                    HandleId id, MutableHandle<PropertyDescriptor> desc,
   181                                    bool descIsOwn, bool strict, MutableHandleValue vp)
   182 {
   183     /* The control-flow here differs from ::get() because of the fall-through case below. */
   184     if (descIsOwn) {
   185         JS_ASSERT(desc.object());
   187         // Check for read-only properties.
   188         if (desc.isReadonly())
   189             return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
   190         if (!desc.setter()) {
   191             // Be wary of the odd explicit undefined setter case possible through
   192             // Object.defineProperty.
   193             if (!desc.hasSetterObject())
   194                 desc.setSetter(JS_StrictPropertyStub);
   195         } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
   196             if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
   197                 return false;
   198             if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
   199                 return true;
   200             if (desc.isShared())
   201                 return true;
   202         }
   203         if (!desc.getter()) {
   204             // Same as above for the null setter case.
   205             if (!desc.hasGetterObject())
   206                 desc.setGetter(JS_PropertyStub);
   207         }
   208         desc.value().set(vp.get());
   209         return handler->defineProperty(cx, receiver, id, desc);
   210     }
   211     if (desc.object()) {
   212         // Check for read-only properties.
   213         if (desc.isReadonly())
   214             return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
   215         if (!desc.setter()) {
   216             // Be wary of the odd explicit undefined setter case possible through
   217             // Object.defineProperty.
   218             if (!desc.hasSetterObject())
   219                 desc.setSetter(JS_StrictPropertyStub);
   220         } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
   221             if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
   222                 return false;
   223             if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
   224                 return true;
   225             if (desc.isShared())
   226                 return true;
   227         }
   228         if (!desc.getter()) {
   229             // Same as above for the null setter case.
   230             if (!desc.hasGetterObject())
   231                 desc.setGetter(JS_PropertyStub);
   232         }
   233         desc.value().set(vp.get());
   234         return handler->defineProperty(cx, receiver, id, desc);
   235     }
   237     desc.object().set(receiver);
   238     desc.value().set(vp.get());
   239     desc.setAttributes(JSPROP_ENUMERATE);
   240     desc.setGetter(nullptr);
   241     desc.setSetter(nullptr); // Pick up the class getter/setter.
   242     return handler->defineProperty(cx, receiver, id, desc);
   243 }
   245 bool
   246 BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   247 {
   248     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   249     JS_ASSERT(props.length() == 0);
   251     if (!getOwnPropertyNames(cx, proxy, props))
   252         return false;
   254     /* Select only the enumerable properties through in-place iteration. */
   255     Rooted<PropertyDescriptor> desc(cx);
   256     RootedId id(cx);
   257     size_t i = 0;
   258     for (size_t j = 0, len = props.length(); j < len; j++) {
   259         JS_ASSERT(i <= j);
   260         id = props[j];
   261         AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
   262         if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   263             return false;
   264         if (desc.object() && desc.isEnumerable())
   265             props[i++] = id;
   266     }
   268     JS_ASSERT(i <= props.length());
   269     props.resize(i);
   271     return true;
   272 }
   274 bool
   275 BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
   276 {
   277     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   279     AutoIdVector props(cx);
   280     if ((flags & JSITER_OWNONLY)
   281         ? !keys(cx, proxy, props)
   282         : !enumerate(cx, proxy, props)) {
   283         return false;
   284     }
   286     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
   287 }
   289 bool
   290 BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
   291 {
   292     MOZ_ASSUME_UNREACHABLE("callable proxies should implement call trap");
   293 }
   295 bool
   296 BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
   297 {
   298     MOZ_ASSUME_UNREACHABLE("callable proxies should implement construct trap");
   299 }
   301 const char *
   302 BaseProxyHandler::className(JSContext *cx, HandleObject proxy)
   303 {
   304     return proxy->isCallable() ? "Function" : "Object";
   305 }
   307 JSString *
   308 BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
   309 {
   310     if (proxy->isCallable())
   311         return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
   312     RootedValue v(cx, ObjectValue(*proxy));
   313     ReportIsNotFunction(cx, v);
   314     return nullptr;
   315 }
   317 bool
   318 BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
   319                                   RegExpGuard *g)
   320 {
   321     MOZ_ASSUME_UNREACHABLE("This should have been a wrapped regexp");
   322 }
   324 bool
   325 BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint,
   326                                MutableHandleValue vp)
   327 {
   328     return DefaultValue(cx, proxy, hint, vp);
   329 }
   331 bool
   332 BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
   333 {
   334     ReportIncompatible(cx, args);
   335     return false;
   336 }
   338 bool
   339 BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
   340 {
   341     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   342     RootedValue val(cx, ObjectValue(*proxy.get()));
   343     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
   344                         JSDVG_SEARCH_STACK, val, js::NullPtr());
   345     return false;
   346 }
   348 bool
   349 BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
   350 {
   351     return false;
   352 }
   354 void
   355 BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
   356 {
   357 }
   359 JSObject *
   360 BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy)
   361 {
   362     return nullptr;
   363 }
   365 bool
   366 BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
   367 {
   368     MOZ_ASSUME_UNREACHABLE("Must override getPrototypeOf with lazy prototype.");
   369 }
   371 bool
   372 BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *)
   373 {
   374     // Disallow sets of protos on proxies with lazy protos, but no hook.
   375     // This keeps us away from the footgun of having the first proto set opt
   376     // you out of having dynamic protos altogether.
   377     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
   378                          "incompatible Proxy");
   379     return false;
   380 }
   382 bool
   383 BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable)
   384 {
   385     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
   386                          proxy->getClass()->name);
   387     return false;
   388 }
   390 bool
   391 BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id)
   392 {
   393     return true;
   394 }
   396 bool
   397 BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
   398                         HandleObject result)
   399 {
   400     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   402     return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
   403 }
   405 bool
   406 DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   407                                           MutableHandle<PropertyDescriptor> desc)
   408 {
   409     assertEnteredPolicy(cx, proxy, id, GET | SET);
   410     JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   411     RootedObject target(cx, proxy->as<ProxyObject>().target());
   412     return JS_GetPropertyDescriptorById(cx, target, id, desc);
   413 }
   415 bool
   416 DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   417                                              MutableHandle<PropertyDescriptor> desc)
   418 {
   419     assertEnteredPolicy(cx, proxy, id, GET | SET);
   420     RootedObject target(cx, proxy->as<ProxyObject>().target());
   421     return js::GetOwnPropertyDescriptor(cx, target, id, desc);
   422 }
   424 bool
   425 DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   426                                    MutableHandle<PropertyDescriptor> desc)
   427 {
   428     assertEnteredPolicy(cx, proxy, id, SET);
   429     RootedObject target(cx, proxy->as<ProxyObject>().target());
   430     RootedValue v(cx, desc.value());
   431     return CheckDefineProperty(cx, target, id, v, desc.getter(), desc.setter(), desc.attributes()) &&
   432            JS_DefinePropertyById(cx, target, id, v, desc.getter(), desc.setter(), desc.attributes());
   433 }
   435 bool
   436 DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
   437                                         AutoIdVector &props)
   438 {
   439     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   440     RootedObject target(cx, proxy->as<ProxyObject>().target());
   441     return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props);
   442 }
   444 bool
   445 DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   446 {
   447     assertEnteredPolicy(cx, proxy, id, SET);
   448     RootedObject target(cx, proxy->as<ProxyObject>().target());
   449     return JS_DeletePropertyById2(cx, target, id, bp);
   450 }
   452 bool
   453 DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
   454                               AutoIdVector &props)
   455 {
   456     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   457     JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   458     RootedObject target(cx, proxy->as<ProxyObject>().target());
   459     return GetPropertyNames(cx, target, 0, &props);
   460 }
   462 bool
   463 DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
   464 {
   465     assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
   466     RootedValue target(cx, proxy->as<ProxyObject>().private_());
   467     return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval());
   468 }
   470 bool
   471 DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
   472 {
   473     assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
   474     RootedValue target(cx, proxy->as<ProxyObject>().private_());
   475     return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address());
   476 }
   478 bool
   479 DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
   480                                CallArgs args)
   481 {
   482     args.setThis(ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
   483     if (!test(args.thisv())) {
   484         ReportIncompatible(cx, args);
   485         return false;
   486     }
   488     return CallNativeImpl(cx, impl, args);
   489 }
   491 bool
   492 DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
   493                                 bool *bp)
   494 {
   495     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   496     bool b;
   497     RootedObject target(cx, proxy->as<ProxyObject>().target());
   498     if (!HasInstance(cx, target, v, &b))
   499         return false;
   500     *bp = !!b;
   501     return true;
   502 }
   504 bool
   505 DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
   506 {
   507     RootedObject target(cx, proxy->as<ProxyObject>().target());
   508     return JSObject::getProto(cx, target, protop);
   509 }
   511 bool
   512 DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
   513 {
   514     RootedObject target(cx, proxy->as<ProxyObject>().target());
   515     return JSObject::setProto(cx, target, proto, bp);
   516 }
   518 bool
   519 DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
   520                                   JSContext *cx)
   521 {
   522     RootedObject target(cx, proxy->as<ProxyObject>().target());
   523     return ObjectClassIs(target, classValue, cx);
   524 }
   526 const char *
   527 DirectProxyHandler::className(JSContext *cx, HandleObject proxy)
   528 {
   529     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   530     RootedObject target(cx, proxy->as<ProxyObject>().target());
   531     return JSObject::className(cx, target);
   532 }
   534 JSString *
   535 DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy,
   536                                  unsigned indent)
   537 {
   538     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   539     RootedObject target(cx, proxy->as<ProxyObject>().target());
   540     return fun_toStringHelper(cx, target, indent);
   541 }
   543 bool
   544 DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
   545                                     RegExpGuard *g)
   546 {
   547     RootedObject target(cx, proxy->as<ProxyObject>().target());
   548     return RegExpToShared(cx, target, g);
   549 }
   551 JSObject *
   552 DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy)
   553 {
   554     return UncheckedUnwrap(proxy);
   555 }
   557 DirectProxyHandler::DirectProxyHandler(const void *family)
   558   : BaseProxyHandler(family)
   559 {
   560 }
   562 bool
   563 DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   564 {
   565     assertEnteredPolicy(cx, proxy, id, GET);
   566     JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   567     bool found;
   568     RootedObject target(cx, proxy->as<ProxyObject>().target());
   569     if (!JS_HasPropertyById(cx, target, id, &found))
   570         return false;
   571     *bp = !!found;
   572     return true;
   573 }
   575 bool
   576 DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   577 {
   578     // Note: Proxy::set needs to invoke hasOwn to determine where the setter
   579     // lives, so we allow SET operations to invoke us.
   580     assertEnteredPolicy(cx, proxy, id, GET | SET);
   581     RootedObject target(cx, proxy->as<ProxyObject>().target());
   582     Rooted<PropertyDescriptor> desc(cx);
   583     if (!JS_GetPropertyDescriptorById(cx, target, id, &desc))
   584         return false;
   585     *bp = (desc.object() == target);
   586     return true;
   587 }
   589 bool
   590 DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   591                         HandleId id, MutableHandleValue vp)
   592 {
   593     assertEnteredPolicy(cx, proxy, id, GET);
   594     RootedObject target(cx, proxy->as<ProxyObject>().target());
   595     return JSObject::getGeneric(cx, target, receiver, id, vp);
   596 }
   598 bool
   599 DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   600                         HandleId id, bool strict, MutableHandleValue vp)
   601 {
   602     assertEnteredPolicy(cx, proxy, id, SET);
   603     RootedObject target(cx, proxy->as<ProxyObject>().target());
   604     return JSObject::setGeneric(cx, target, receiver, id, vp, strict);
   605 }
   607 bool
   608 DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   609 {
   610     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   611     RootedObject target(cx, proxy->as<ProxyObject>().target());
   612     return GetPropertyNames(cx, target, JSITER_OWNONLY, &props);
   613 }
   615 bool
   616 DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
   617                             MutableHandleValue vp)
   618 {
   619     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   620     JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   621     RootedObject target(cx, proxy->as<ProxyObject>().target());
   622     return GetIterator(cx, target, flags, vp);
   623 }
   625 bool
   626 DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
   627 {
   628     RootedObject target(cx, proxy->as<ProxyObject>().target());
   629     return JSObject::isExtensible(cx, target, extensible);
   630 }
   632 bool
   633 DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
   634 {
   635     RootedObject target(cx, proxy->as<ProxyObject>().target());
   636     return JSObject::preventExtensions(cx, target);
   637 }
   639 static bool
   640 GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
   641                    MutableHandleValue fvalp)
   642 {
   643     JS_CHECK_RECURSION(cx, return false);
   645     return JSObject::getProperty(cx, handler, handler, name, fvalp);
   646 }
   648 static bool
   649 GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
   650                MutableHandleValue fvalp)
   651 {
   652     JS_ASSERT(name == cx->names().has ||
   653               name == cx->names().hasOwn ||
   654               name == cx->names().get ||
   655               name == cx->names().set ||
   656               name == cx->names().keys ||
   657               name == cx->names().iterate);
   659     return JSObject::getProperty(cx, handler, handler, name, fvalp);
   660 }
   662 static bool
   663 Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv,
   664      MutableHandleValue rval)
   665 {
   666     return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
   667 }
   669 static bool
   670 Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
   671 {
   672     rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
   673     JSString *str = ToString<CanGC>(cx, rval);
   674     if (!str)
   675         return false;
   676     rval.setString(str);
   677     return Trap(cx, handler, fval, 1, rval.address(), rval);
   678 }
   680 static bool
   681 Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_,
   682       MutableHandleValue rval)
   683 {
   684     RootedValue v(cx, v_);
   685     rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
   686     JSString *str = ToString<CanGC>(cx, rval);
   687     if (!str)
   688         return false;
   689     rval.setString(str);
   690     JS::AutoValueArray<2> argv(cx);
   691     argv[0].set(rval);
   692     argv[1].set(v);
   693     return Trap(cx, handler, fval, 2, argv.begin(), rval);
   694 }
   696 static bool
   697 ParsePropertyDescriptorObject(JSContext *cx, HandleObject obj, const Value &v,
   698                               MutableHandle<PropertyDescriptor> desc, bool complete = false)
   699 {
   700     AutoPropDescArrayRooter descs(cx);
   701     PropDesc *d = descs.append();
   702     if (!d || !d->initialize(cx, v))
   703         return false;
   704     if (complete)
   705         d->complete();
   706     desc.object().set(obj);
   707     desc.value().set(d->hasValue() ? d->value() : UndefinedValue());
   708     desc.setAttributes(d->attributes());
   709     desc.setGetter(d->getter());
   710     desc.setSetter(d->setter());
   711     return true;
   712 }
   714 static bool
   715 IndicatePropertyNotFound(MutableHandle<PropertyDescriptor> desc)
   716 {
   717     desc.object().set(nullptr);
   718     return true;
   719 }
   721 static bool
   722 ValueToBool(HandleValue v, bool *bp)
   723 {
   724     *bp = ToBoolean(v);
   725     return true;
   726 }
   728 static bool
   729 ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
   730 {
   731     JS_ASSERT(props.length() == 0);
   733     if (array.isPrimitive())
   734         return true;
   736     RootedObject obj(cx, &array.toObject());
   737     uint32_t length;
   738     if (!GetLengthProperty(cx, obj, &length))
   739         return false;
   741     RootedValue v(cx);
   742     for (uint32_t n = 0; n < length; ++n) {
   743         if (!CheckForInterrupt(cx))
   744             return false;
   745         if (!JSObject::getElement(cx, obj, obj, n, &v))
   746             return false;
   747         RootedId id(cx);
   748         if (!ValueToId<CanGC>(cx, v, &id))
   749             return false;
   750         if (!props.append(id))
   751             return false;
   752     }
   754     return true;
   755 }
   757 namespace {
   759 /* Derived class for all scripted indirect proxy handlers. */
   760 class ScriptedIndirectProxyHandler : public BaseProxyHandler
   761 {
   762   public:
   763     ScriptedIndirectProxyHandler();
   764     virtual ~ScriptedIndirectProxyHandler();
   766     /* ES5 Harmony fundamental proxy traps. */
   767     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
   768     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   769                                        MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   770     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   771                                           MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   772     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   773                                 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   774     virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
   775     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   776     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
   778     /* ES5 Harmony derived proxy traps. */
   779     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   780     virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   781     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
   782                      MutableHandleValue vp) MOZ_OVERRIDE;
   783     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
   784                      bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
   785     virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
   786     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
   787                          MutableHandleValue vp) MOZ_OVERRIDE;
   789     /* Spidermonkey extensions. */
   790     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
   791     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
   792     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
   793     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
   794                             CallArgs args) MOZ_OVERRIDE;
   795     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE;
   796     virtual bool isScripted() MOZ_OVERRIDE { return true; }
   798     static ScriptedIndirectProxyHandler singleton;
   799 };
   801 /*
   802  * Old-style indirect proxies allow callers to specify distinct scripted
   803  * [[Call]] and [[Construct]] traps. We use an intermediate object so that we
   804  * can stash this information in a single reserved slot on the proxy object.
   805  *
   806  * Note - Currently this is slightly unnecesary, because we actually have 2
   807  * extra slots, neither of which are used for ScriptedIndirectProxy. But we're
   808  * eventually moving towards eliminating one of those slots, and so we don't
   809  * want to add a dependency here.
   810  */
   811 static Class CallConstructHolder = {
   812     "CallConstructHolder",
   813     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
   814 };
   816 } /* anonymous namespace */
   818 // This variable exists solely to provide a unique address for use as an identifier.
   819 static const char sScriptedIndirectProxyHandlerFamily = 0;
   821 ScriptedIndirectProxyHandler::ScriptedIndirectProxyHandler()
   822         : BaseProxyHandler(&sScriptedIndirectProxyHandlerFamily)
   823 {
   824 }
   826 ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler()
   827 {
   828 }
   830 bool
   831 ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
   832 {
   833     // Scripted indirect proxies don't support extensibility changes.
   834     *extensible = true;
   835     return true;
   836 }
   838 bool
   839 ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
   840 {
   841     // See above.
   842     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
   843     return false;
   844 }
   846 static bool
   847 ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v)
   848 {
   849     if (v.isPrimitive()) {
   850         JSAutoByteString bytes;
   851         if (AtomToPrintableString(cx, atom, &bytes)) {
   852             RootedValue val(cx, ObjectOrNullValue(proxy));
   853             js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
   854                                  JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr());
   855         }
   856         return false;
   857     }
   858     return true;
   859 }
   861 static JSObject *
   862 GetIndirectProxyHandlerObject(JSObject *proxy)
   863 {
   864     return proxy->as<ProxyObject>().private_().toObjectOrNull();
   865 }
   867 bool
   868 ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   869                                                     MutableHandle<PropertyDescriptor> desc)
   870 {
   871     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   872     RootedValue fval(cx), value(cx);
   873     return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) &&
   874            Trap1(cx, handler, fval, id, &value) &&
   875            ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
   876             (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
   877              ParsePropertyDescriptorObject(cx, proxy, value, desc)));
   878 }
   880 bool
   881 ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   882                                                        MutableHandle<PropertyDescriptor> desc)
   883 {
   884     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   885     RootedValue fval(cx), value(cx);
   886     return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) &&
   887            Trap1(cx, handler, fval, id, &value) &&
   888            ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
   889             (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
   890              ParsePropertyDescriptorObject(cx, proxy, value, desc)));
   891 }
   893 bool
   894 ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   895                                              MutableHandle<PropertyDescriptor> desc)
   896 {
   897     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   898     RootedValue fval(cx), value(cx);
   899     return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) &&
   900            NewPropertyDescriptorObject(cx, desc, &value) &&
   901            Trap2(cx, handler, fval, id, value, &value);
   902 }
   904 bool
   905 ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
   906                                                   AutoIdVector &props)
   907 {
   908     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   909     RootedValue fval(cx), value(cx);
   910     return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) &&
   911            Trap(cx, handler, fval, 0, nullptr, &value) &&
   912            ArrayToIdVector(cx, value, props);
   913 }
   915 bool
   916 ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   917 {
   918     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   919     RootedValue fval(cx), value(cx);
   920     return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) &&
   921            Trap1(cx, handler, fval, id, &value) &&
   922            ValueToBool(value, bp);
   923 }
   925 bool
   926 ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   927 {
   928     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   929     RootedValue fval(cx), value(cx);
   930     return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) &&
   931            Trap(cx, handler, fval, 0, nullptr, &value) &&
   932            ArrayToIdVector(cx, value, props);
   933 }
   935 bool
   936 ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   937 {
   938     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   939     RootedValue fval(cx), value(cx);
   940     if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
   941         return false;
   942     if (!js_IsCallable(fval))
   943         return BaseProxyHandler::has(cx, proxy, id, bp);
   944     return Trap1(cx, handler, fval, id, &value) &&
   945            ValueToBool(value, bp);
   946 }
   948 bool
   949 ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   950 {
   951     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   952     RootedValue fval(cx), value(cx);
   953     if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
   954         return false;
   955     if (!js_IsCallable(fval))
   956         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
   957     return Trap1(cx, handler, fval, id, &value) &&
   958            ValueToBool(value, bp);
   959 }
   961 bool
   962 ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   963                                   HandleId id, MutableHandleValue vp)
   964 {
   965     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   966     RootedValue idv(cx, IdToValue(id));
   967     JSString *str = ToString<CanGC>(cx, idv);
   968     if (!str)
   969         return false;
   970     RootedValue value(cx, StringValue(str));
   971     JS::AutoValueArray<2> argv(cx);
   972     argv[0].setObjectOrNull(receiver);
   973     argv[1].set(value);
   974     RootedValue fval(cx);
   975     if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
   976         return false;
   977     if (!js_IsCallable(fval))
   978         return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
   979     return Trap(cx, handler, fval, 2, argv.begin(), vp);
   980 }
   982 bool
   983 ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   984                                   HandleId id, bool strict, MutableHandleValue vp)
   985 {
   986     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   987     RootedValue idv(cx, IdToValue(id));
   988     JSString *str = ToString<CanGC>(cx, idv);
   989     if (!str)
   990         return false;
   991     RootedValue value(cx, StringValue(str));
   992     JS::AutoValueArray<3> argv(cx);
   993     argv[0].setObjectOrNull(receiver);
   994     argv[1].set(value);
   995     argv[2].set(vp);
   996     RootedValue fval(cx);
   997     if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
   998         return false;
   999     if (!js_IsCallable(fval))
  1000         return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
  1001     return Trap(cx, handler, fval, 3, argv.begin(), &value);
  1004 bool
  1005 ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1007     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
  1008     RootedValue value(cx);
  1009     if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
  1010         return false;
  1011     if (!js_IsCallable(value))
  1012         return BaseProxyHandler::keys(cx, proxy, props);
  1013     return Trap(cx, handler, value, 0, nullptr, &value) &&
  1014            ArrayToIdVector(cx, value, props);
  1017 bool
  1018 ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  1019                                       MutableHandleValue vp)
  1021     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
  1022     RootedValue value(cx);
  1023     if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
  1024         return false;
  1025     if (!js_IsCallable(value))
  1026         return BaseProxyHandler::iterate(cx, proxy, flags, vp);
  1027     return Trap(cx, handler, value, 0, nullptr, vp) &&
  1028            ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp);
  1031 bool
  1032 ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1034     assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
  1035     RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
  1036     JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
  1037     RootedValue call(cx, ccHolder->getReservedSlot(0));
  1038     JS_ASSERT(call.isObject() && call.toObject().isCallable());
  1039     return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval());
  1042 bool
  1043 ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1045     assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
  1046     RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
  1047     JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
  1048     RootedValue construct(cx, ccHolder->getReservedSlot(1));
  1049     JS_ASSERT(construct.isObject() && construct.toObject().isCallable());
  1050     return InvokeConstructor(cx, construct, args.length(), args.array(),
  1051                              args.rval().address());
  1054 bool
  1055 ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
  1056                                          CallArgs args)
  1058     return BaseProxyHandler::nativeCall(cx, test, impl, args);
  1061 JSString *
  1062 ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
  1064     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
  1065     if (!proxy->isCallable()) {
  1066         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1067                              JSMSG_INCOMPATIBLE_PROTO,
  1068                              js_Function_str, js_toString_str,
  1069                              "object");
  1070         return nullptr;
  1072     RootedObject obj(cx, &proxy->as<ProxyObject>().extra(0).toObject().getReservedSlot(0).toObject());
  1073     return fun_toStringHelper(cx, obj, indent);
  1076 ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton;
  1078 /* Derived class for all scripted direct proxy handlers. */
  1079 class ScriptedDirectProxyHandler : public DirectProxyHandler {
  1080   public:
  1081     ScriptedDirectProxyHandler();
  1082     virtual ~ScriptedDirectProxyHandler();
  1084     /* ES5 Harmony fundamental proxy traps. */
  1085     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
  1086     virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1087                                        MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1088     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1089                                           MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1090     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  1091                                 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1092     virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
  1093     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1094     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
  1096     /* ES5 Harmony derived proxy traps. */
  1097     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1098     virtual bool hasOwn(JSContext *cx,HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1099     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1100                      MutableHandleValue vp) MOZ_OVERRIDE;
  1101     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1102                      bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
  1103     virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
  1104     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  1105                          MutableHandleValue vp) MOZ_OVERRIDE;
  1107     /* ES6 Harmony traps */
  1108     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
  1110     /* Spidermonkey extensions. */
  1111     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
  1112     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
  1113     virtual bool isScripted() MOZ_OVERRIDE { return true; }
  1115     static ScriptedDirectProxyHandler singleton;
  1116 };
  1118 // This variable exists solely to provide a unique address for use as an identifier.
  1119 static const char sScriptedDirectProxyHandlerFamily = 0;
  1121 // Aux.2 FromGenericPropertyDescriptor(Desc)
  1122 static bool
  1123 FromGenericPropertyDescriptor(JSContext *cx, PropDesc *desc, MutableHandleValue rval)
  1125     // Aux.2 step 1
  1126     if (desc->isUndefined()) {
  1127         rval.setUndefined();
  1128         return true;
  1131     // steps 3-9
  1132     if (!desc->makeObject(cx))
  1133         return false;
  1134     rval.set(desc->pd());
  1135     return true;
  1138 /*
  1139  * Aux.3 NormalizePropertyDescriptor(Attributes)
  1141  * NOTE: to minimize code duplication, the code for this function is shared with
  1142  * that for Aux.4 NormalizeAndCompletePropertyDescriptor (see below). The
  1143  * argument complete is used to distinguish between the two.
  1144  */
  1145 static bool
  1146 NormalizePropertyDescriptor(JSContext *cx, MutableHandleValue vp, bool complete = false)
  1148     // Aux.4 step 1
  1149     if (complete && vp.isUndefined())
  1150         return true;
  1152     // Aux.3 steps 1-2 / Aux.4 steps 2-3
  1153     AutoPropDescArrayRooter descs(cx);
  1154     PropDesc *desc = descs.append();
  1155     if (!desc || !desc->initialize(cx, vp.get()))
  1156         return false;
  1157     if (complete)
  1158         desc->complete();
  1159     JS_ASSERT(!vp.isPrimitive()); // due to desc->initialize
  1160     RootedObject attributes(cx, &vp.toObject());
  1162     /*
  1163      * Aux.3 step 3 / Aux.4 step 4
  1165      * NOTE: Aux.4 step 4 actually specifies FromPropertyDescriptor here.
  1166      * However, the way FromPropertyDescriptor is implemented (PropDesc::
  1167      * makeObject) is actually closer to FromGenericPropertyDescriptor,
  1168      * and is in fact used to implement the latter, so we might as well call it
  1169      * directly.
  1170      */
  1171     if (!FromGenericPropertyDescriptor(cx, desc, vp))
  1172         return false;
  1173     if (vp.isUndefined())
  1174         return true;
  1175     RootedObject descObj(cx, &vp.toObject());
  1177     // Aux.3 steps 4-5 / Aux.4 steps 5-6
  1178     AutoIdVector props(cx);
  1179     if (!GetPropertyNames(cx, attributes, 0, &props))
  1180         return false;
  1181     size_t n = props.length();
  1182     for (size_t i = 0; i < n; ++i) {
  1183         RootedId id(cx, props[i]);
  1184         if (JSID_IS_ATOM(id)) {
  1185             JSAtom *atom = JSID_TO_ATOM(id);
  1186             const JSAtomState &atomState = cx->names();
  1187             if (atom == atomState.value || atom == atomState.writable ||
  1188                 atom == atomState.get || atom == atomState.set ||
  1189                 atom == atomState.enumerable || atom == atomState.configurable)
  1191                 continue;
  1195         RootedValue v(cx);
  1196         if (!JSObject::getGeneric(cx, descObj, attributes, id, &v))
  1197             return false;
  1198         if (!JSObject::defineGeneric(cx, descObj, id, v, nullptr, nullptr, JSPROP_ENUMERATE))
  1199             return false;
  1201     return true;
  1204 // Aux.4 NormalizeAndCompletePropertyDescriptor(Attributes)
  1205 static inline bool
  1206 NormalizeAndCompletePropertyDescriptor(JSContext *cx, MutableHandleValue vp)
  1208     return NormalizePropertyDescriptor(cx, vp, true);
  1211 static inline bool
  1212 IsDataDescriptor(const PropertyDescriptor &desc)
  1214     return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER));
  1217 static inline bool
  1218 IsAccessorDescriptor(const PropertyDescriptor &desc)
  1220     return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER);
  1223 // Aux.5 ValidateProperty(O, P, Desc)
  1224 static bool
  1225 ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, PropDesc *desc, bool *bp)
  1227     // step 1
  1228     Rooted<PropertyDescriptor> current(cx);
  1229     if (!GetOwnPropertyDescriptor(cx, obj, id, &current))
  1230         return false;
  1232     /*
  1233      * steps 2-4 are redundant since ValidateProperty is never called unless
  1234      * target.[[HasOwn]](P) is true
  1235      */
  1236     JS_ASSERT(current.object());
  1238     // step 5
  1239     if (!desc->hasValue() && !desc->hasWritable() && !desc->hasGet() && !desc->hasSet() &&
  1240         !desc->hasEnumerable() && !desc->hasConfigurable())
  1242         *bp = true;
  1243         return true;
  1246     // step 6
  1247     if ((!desc->hasWritable() || desc->writable() == !current.isReadonly()) &&
  1248         (!desc->hasGet() || desc->getter() == current.getter()) &&
  1249         (!desc->hasSet() || desc->setter() == current.setter()) &&
  1250         (!desc->hasEnumerable() || desc->enumerable() == current.isEnumerable()) &&
  1251         (!desc->hasConfigurable() || desc->configurable() == !current.isPermanent()))
  1253         if (!desc->hasValue()) {
  1254             *bp = true;
  1255             return true;
  1257         bool same = false;
  1258         if (!SameValue(cx, desc->value(), current.value(), &same))
  1259             return false;
  1260         if (same) {
  1261             *bp = true;
  1262             return true;
  1266     // step 7
  1267     if (current.isPermanent()) {
  1268         if (desc->hasConfigurable() && desc->configurable()) {
  1269             *bp = false;
  1270             return true;
  1273         if (desc->hasEnumerable() &&
  1274             desc->enumerable() != current.isEnumerable())
  1276             *bp = false;
  1277             return true;
  1281     // step 8
  1282     if (desc->isGenericDescriptor()) {
  1283         *bp = true;
  1284         return true;
  1287     // step 9
  1288     if (IsDataDescriptor(current) != desc->isDataDescriptor()) {
  1289         *bp = !current.isPermanent();
  1290         return true;
  1293     // step 10
  1294     if (IsDataDescriptor(current)) {
  1295         JS_ASSERT(desc->isDataDescriptor()); // by step 9
  1296         if (current.isPermanent() && current.isReadonly()) {
  1297             if (desc->hasWritable() && desc->writable()) {
  1298                 *bp = false;
  1299                 return true;
  1302             if (desc->hasValue()) {
  1303                 bool same;
  1304                 if (!SameValue(cx, desc->value(), current.value(), &same))
  1305                     return false;
  1306                 if (!same) {
  1307                     *bp = false;
  1308                     return true;
  1313         *bp = true;
  1314         return true;
  1317     // steps 11-12
  1318     JS_ASSERT(IsAccessorDescriptor(current)); // by step 10
  1319     JS_ASSERT(desc->isAccessorDescriptor()); // by step 9
  1320     *bp = (!current.isPermanent() ||
  1321            ((!desc->hasSet() || desc->setter() == current.setter()) &&
  1322             (!desc->hasGet() || desc->getter() == current.getter())));
  1323     return true;
  1326 // Aux.6 IsSealed(O, P)
  1327 static bool
  1328 IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp)
  1330     // step 1
  1331     Rooted<PropertyDescriptor> desc(cx);
  1332     if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
  1333         return false;
  1335     // steps 2-3
  1336     *bp = desc.object() && desc.isPermanent();
  1337     return true;
  1340 static bool
  1341 HasOwn(JSContext *cx, HandleObject obj, HandleId id, bool *bp)
  1343     Rooted<PropertyDescriptor> desc(cx);
  1344     if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
  1345         return false;
  1346     *bp = (desc.object() == obj);
  1347     return true;
  1350 static bool
  1351 IdToExposableValue(JSContext *cx, HandleId id, MutableHandleValue value)
  1353     value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
  1354     JSString *name = ToString<CanGC>(cx, value);
  1355     if (!name)
  1356         return false;
  1357     value.set(StringValue(name));
  1358     return true;
  1361 // Get the [[ProxyHandler]] of a scripted direct proxy.
  1362 //
  1363 // NB: This *must* stay synched with proxy().
  1364 static JSObject *
  1365 GetDirectProxyHandlerObject(JSObject *proxy)
  1367     JS_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
  1368     return proxy->as<ProxyObject>().extra(0).toObjectOrNull();
  1371 // TrapGetOwnProperty(O, P)
  1372 static bool
  1373 TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue rval)
  1375     // step 1
  1376     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1378     // step 2
  1379     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1381     // step 3
  1382     RootedValue trap(cx);
  1383     if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyDescriptor, &trap))
  1384         return false;
  1386     // step 4
  1387     if (trap.isUndefined()) {
  1388         Rooted<PropertyDescriptor> desc(cx);
  1389         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1390             return false;
  1391         return NewPropertyDescriptorObject(cx, desc, rval);
  1394     // step 5
  1395     RootedValue value(cx);
  1396     if (!IdToExposableValue(cx, id, &value))
  1397         return false;
  1398     Value argv[] = {
  1399         ObjectValue(*target),
  1400         value
  1401     };
  1402     RootedValue trapResult(cx);
  1403     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1404         return false;
  1406     // step 6
  1407     if (!NormalizeAndCompletePropertyDescriptor(cx, &trapResult))
  1408         return false;
  1410     // step 7
  1411     if (trapResult.isUndefined()) {
  1412         bool sealed;
  1413         if (!IsSealed(cx, target, id, &sealed))
  1414             return false;
  1415         if (sealed) {
  1416             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  1417             return false;
  1420         bool extensible;
  1421         if (!JSObject::isExtensible(cx, target, &extensible))
  1422             return false;
  1423         if (!extensible) {
  1424             bool found;
  1425             if (!HasOwn(cx, target, id, &found))
  1426                 return false;
  1427             if (found) {
  1428                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1429                 return false;
  1433         rval.set(UndefinedValue());
  1434         return true;
  1437     // step 8
  1438     bool isFixed;
  1439     if (!HasOwn(cx, target, id, &isFixed))
  1440         return false;
  1442     // step 9
  1443     bool extensible;
  1444     if (!JSObject::isExtensible(cx, target, &extensible))
  1445         return false;
  1446     if (!extensible && !isFixed) {
  1447         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  1448         return false;
  1451     AutoPropDescArrayRooter descs(cx);
  1452     PropDesc *desc = descs.append();
  1453     if (!desc || !desc->initialize(cx, trapResult))
  1454         return false;
  1456     /* step 10 */
  1457     if (isFixed) {
  1458         bool valid;
  1459         if (!ValidateProperty(cx, target, id, desc, &valid))
  1460             return false;
  1462         if (!valid) {
  1463             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID);
  1464             return false;
  1468     // step 11
  1469     if (!desc->configurable() && !isFixed) {
  1470         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
  1471         return false;
  1474     // step 12
  1475     rval.set(trapResult);
  1476     return true;
  1479 // TrapDefineOwnProperty(O, P, DescObj, Throw)
  1480 static bool
  1481 TrapDefineOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
  1483     // step 1
  1484     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1486     // step 2
  1487     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1489     // step 3
  1490     RootedValue trap(cx);
  1491     if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap))
  1492         return false;
  1494     // step 4
  1495     if (trap.isUndefined()) {
  1496         Rooted<PropertyDescriptor> desc(cx);
  1497         if (!ParsePropertyDescriptorObject(cx, proxy, vp, &desc))
  1498             return false;
  1499         return JS_DefinePropertyById(cx, target, id, desc.value(), desc.getter(), desc.setter(),
  1500                                      desc.attributes());
  1503     // step 5
  1504     RootedValue normalizedDesc(cx, vp);
  1505     if (!NormalizePropertyDescriptor(cx, &normalizedDesc))
  1506         return false;
  1508     // step 6
  1509     RootedValue value(cx);
  1510     if (!IdToExposableValue(cx, id, &value))
  1511         return false;
  1512     Value argv[] = {
  1513         ObjectValue(*target),
  1514         value,
  1515         normalizedDesc
  1516     };
  1517     RootedValue trapResult(cx);
  1518     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1519         return false;
  1521     // steps 7-8
  1522     if (ToBoolean(trapResult)) {
  1523         bool isFixed;
  1524         if (!HasOwn(cx, target, id, &isFixed))
  1525             return false;
  1527         bool extensible;
  1528         if (!JSObject::isExtensible(cx, target, &extensible))
  1529             return false;
  1530         if (!extensible && !isFixed) {
  1531             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
  1532             return false;
  1535         AutoPropDescArrayRooter descs(cx);
  1536         PropDesc *desc = descs.append();
  1537         if (!desc || !desc->initialize(cx, normalizedDesc))
  1538             return false;
  1540         if (isFixed) {
  1541             bool valid;
  1542             if (!ValidateProperty(cx, target, id, desc, &valid))
  1543                 return false;
  1544             if (!valid) {
  1545                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
  1546                 return false;
  1550         if (!desc->configurable() && !isFixed) {
  1551             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
  1552             return false;
  1555         vp.set(BooleanValue(true));
  1556         return true;
  1559     // step 9
  1560     // FIXME: API does not include a Throw parameter
  1561     vp.set(BooleanValue(false));
  1562     return true;
  1565 static inline void
  1566 ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom)
  1568     RootedValue v(cx, ObjectOrNullValue(proxy));
  1569     JSAutoByteString bytes;
  1570     if (!AtomToPrintableString(cx, atom, &bytes))
  1571         return;
  1572     js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v,
  1573                          js::NullPtr(), bytes.ptr());
  1576 // This function is shared between getOwnPropertyNames, enumerate, and keys
  1577 static bool
  1578 ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v,
  1579                 AutoIdVector &props, unsigned flags, JSAtom *trapName_)
  1581     JS_ASSERT(v.isObject());
  1582     RootedObject array(cx, &v.toObject());
  1583     RootedAtom trapName(cx, trapName_);
  1585     // steps g-h
  1586     uint32_t n;
  1587     if (!GetLengthProperty(cx, array, &n))
  1588         return false;
  1590     // steps i-k
  1591     for (uint32_t i = 0; i < n; ++i) {
  1592         // step i
  1593         RootedValue v(cx);
  1594         if (!JSObject::getElement(cx, array, array, i, &v))
  1595             return false;
  1597         // step ii
  1598         RootedId id(cx);
  1599         if (!ValueToId<CanGC>(cx, v, &id))
  1600             return false;
  1602         // step iii
  1603         for (uint32_t j = 0; j < i; ++j) {
  1604             if (props[j] == id) {
  1605                 ReportInvalidTrapResult(cx, proxy, trapName);
  1606                 return false;
  1610         // step iv
  1611         bool isFixed;
  1612         if (!HasOwn(cx, target, id, &isFixed))
  1613             return false;
  1615         // step v
  1616         bool extensible;
  1617         if (!JSObject::isExtensible(cx, target, &extensible))
  1618             return false;
  1619         if (!extensible && !isFixed) {
  1620             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  1621             return false;
  1624         // step vi
  1625         if (!props.append(id))
  1626             return false;
  1629     // step l
  1630     AutoIdVector ownProps(cx);
  1631     if (!GetPropertyNames(cx, target, flags, &ownProps))
  1632         return false;
  1634     // step m
  1635     for (size_t i = 0; i < ownProps.length(); ++i) {
  1636         RootedId id(cx, ownProps[i]);
  1638         bool found = false;
  1639         for (size_t j = 0; j < props.length(); ++j) {
  1640             if (props[j] == id) {
  1641                 found = true;
  1642                break;
  1645         if (found)
  1646             continue;
  1648         // step i
  1649         bool sealed;
  1650         if (!IsSealed(cx, target, id, &sealed))
  1651             return false;
  1652         if (sealed) {
  1653             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
  1654             return false;
  1657         // step ii
  1658         bool isFixed;
  1659         if (!HasOwn(cx, target, id, &isFixed))
  1660             return false;
  1662         // step iii
  1663         bool extensible;
  1664         if (!JSObject::isExtensible(cx, target, &extensible))
  1665             return false;
  1666         if (!extensible && isFixed) {
  1667             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1668             return false;
  1672     // step n
  1673     return true;
  1676 ScriptedDirectProxyHandler::ScriptedDirectProxyHandler()
  1677         : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily)
  1681 ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler()
  1685 bool
  1686 ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
  1688     // step a
  1689     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1691     // step b
  1692     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1694     // step c
  1695     RootedValue trap(cx);
  1696     if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap))
  1697         return false;
  1699     // step d
  1700     if (trap.isUndefined())
  1701         return DirectProxyHandler::preventExtensions(cx, proxy);
  1703     // step e
  1704     Value argv[] = {
  1705         ObjectValue(*target)
  1706     };
  1707     RootedValue trapResult(cx);
  1708     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1709         return false;
  1711     // step f
  1712     bool success = ToBoolean(trapResult);
  1713     if (success) {
  1714         // step g
  1715         bool extensible;
  1716         if (!JSObject::isExtensible(cx, target, &extensible))
  1717             return false;
  1718         if (extensible) {
  1719             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
  1720             return false;
  1722         return true;
  1725     // step h
  1726     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
  1727     return false;
  1730 // FIXME: Move to Proxy::getPropertyDescriptor once ScriptedIndirectProxy is removed
  1731 bool
  1732 ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1733                                                   MutableHandle<PropertyDescriptor> desc)
  1735     JS_CHECK_RECURSION(cx, return false);
  1737     if (!GetOwnPropertyDescriptor(cx, proxy, id, desc))
  1738         return false;
  1739     if (desc.object())
  1740         return true;
  1741     RootedObject proto(cx);
  1742     if (!JSObject::getProto(cx, proxy, &proto))
  1743         return false;
  1744     if (!proto) {
  1745         JS_ASSERT(!desc.object());
  1746         return true;
  1748     return JS_GetPropertyDescriptorById(cx, proto, id, desc);
  1751 bool
  1752 ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1753                                                      MutableHandle<PropertyDescriptor> desc)
  1755     // step 1
  1756     RootedValue v(cx);
  1757     if (!TrapGetOwnProperty(cx, proxy, id, &v))
  1758         return false;
  1760     // step 2
  1761     if (v.isUndefined()) {
  1762         desc.object().set(nullptr);
  1763         return true;
  1766     // steps 3-4
  1767     return ParsePropertyDescriptorObject(cx, proxy, v, desc, true);
  1770 bool
  1771 ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  1772                                            MutableHandle<PropertyDescriptor> desc)
  1774     // step 1
  1775     AutoPropDescArrayRooter descs(cx);
  1776     PropDesc *d = descs.append();
  1777     d->initFromPropertyDescriptor(desc);
  1778     RootedValue v(cx);
  1779     if (!FromGenericPropertyDescriptor(cx, d, &v))
  1780         return false;
  1782     // step 2
  1783     return TrapDefineOwnProperty(cx, proxy, id, &v);
  1786 bool
  1787 ScriptedDirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
  1788                                                 AutoIdVector &props)
  1790     // step a
  1791     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1793     // step b
  1794     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1796     // step c
  1797     RootedValue trap(cx);
  1798     if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyNames, &trap))
  1799         return false;
  1801     // step d
  1802     if (trap.isUndefined())
  1803         return DirectProxyHandler::getOwnPropertyNames(cx, proxy, props);
  1805     // step e
  1806     Value argv[] = {
  1807         ObjectValue(*target)
  1808     };
  1809     RootedValue trapResult(cx);
  1810     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1811         return false;
  1813     // step f
  1814     if (trapResult.isPrimitive()) {
  1815         ReportInvalidTrapResult(cx, proxy, cx->names().getOwnPropertyNames);
  1816         return false;
  1819     // steps g to n are shared
  1820     return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY | JSITER_HIDDEN,
  1821                            cx->names().getOwnPropertyNames);
  1824 // Proxy.[[Delete]](P, Throw)
  1825 bool
  1826 ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1828     // step 1
  1829     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1831     // step 2
  1832     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1834     // step 3
  1835     RootedValue trap(cx);
  1836     if (!JSObject::getProperty(cx, handler, handler, cx->names().deleteProperty, &trap))
  1837         return false;
  1839     // step 4
  1840     if (trap.isUndefined())
  1841         return DirectProxyHandler::delete_(cx, proxy, id, bp);
  1843     // step 5
  1844     RootedValue value(cx);
  1845     if (!IdToExposableValue(cx, id, &value))
  1846         return false;
  1847     Value argv[] = {
  1848         ObjectValue(*target),
  1849         value
  1850     };
  1851     RootedValue trapResult(cx);
  1852     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1853         return false;
  1855     // step 6-7
  1856     if (ToBoolean(trapResult)) {
  1857         Rooted<PropertyDescriptor> desc(cx);
  1858         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1859             return false;
  1861         if (desc.object() && desc.isPermanent()) {
  1862             RootedValue v(cx, IdToValue(id));
  1863             js_ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, js::NullPtr());
  1864             return false;
  1867         *bp = true;
  1868         return true;
  1871     // step 8
  1872     // FIXME: API does not include a Throw parameter
  1873     *bp = false;
  1874     return true;
  1877 // 12.6.4 The for-in Statement, step 6
  1878 bool
  1879 ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1881     // step a
  1882     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1884     // step b
  1885     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1887     // step c
  1888     RootedValue trap(cx);
  1889     if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap))
  1890         return false;
  1892     // step d
  1893     if (trap.isUndefined())
  1894         return DirectProxyHandler::enumerate(cx, proxy, props);
  1896     // step e
  1897     Value argv[] = {
  1898         ObjectOrNullValue(target)
  1899     };
  1900     RootedValue trapResult(cx);
  1901     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1902         return false;
  1904     // step f
  1905     if (trapResult.isPrimitive()) {
  1906         JSAutoByteString bytes;
  1907         if (!AtomToPrintableString(cx, cx->names().enumerate, &bytes))
  1908             return false;
  1909         RootedValue v(cx, ObjectOrNullValue(proxy));
  1910         js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK,
  1911                              v, js::NullPtr(), bytes.ptr());
  1912         return false;
  1915     // steps g-m are shared
  1916     // FIXME: the trap should return an iterator object, see bug 783826
  1917     return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate);
  1920 // Proxy.[[HasProperty]](P)
  1921 bool
  1922 ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1924     // step 1
  1925     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1927     // step 2
  1928     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1930     // step 3
  1931     RootedValue trap(cx);
  1932     if (!JSObject::getProperty(cx, handler, handler, cx->names().has, &trap))
  1933         return false;
  1935     // step 4
  1936     if (trap.isUndefined())
  1937         return DirectProxyHandler::has(cx, proxy, id, bp);
  1939     // step 5
  1940     RootedValue value(cx);
  1941     if (!IdToExposableValue(cx, id, &value))
  1942         return false;
  1943     Value argv[] = {
  1944         ObjectOrNullValue(target),
  1945         value
  1946     };
  1947     RootedValue trapResult(cx);
  1948     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1949         return false;
  1951     // step 6
  1952     bool success = ToBoolean(trapResult);;
  1954     // step 7
  1955     if (!success) {
  1956         Rooted<PropertyDescriptor> desc(cx);
  1957         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1958             return false;
  1960         if (desc.object()) {
  1961             if (desc.isPermanent()) {
  1962                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  1963                 return false;
  1966             bool extensible;
  1967             if (!JSObject::isExtensible(cx, target, &extensible))
  1968                 return false;
  1969             if (!extensible) {
  1970                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1971                 return false;
  1976     // step 8
  1977     *bp = success;
  1978     return true;
  1981 // Proxy.[[HasOwnProperty]](P)
  1982 bool
  1983 ScriptedDirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1985     // step 1
  1986     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1988     // step 2
  1989     RootedObject target(cx, proxy->as<ProxyObject>().target());
  1991     // step 3
  1992     RootedValue trap(cx);
  1993     if (!JSObject::getProperty(cx, handler, handler, cx->names().hasOwn, &trap))
  1994         return false;
  1996     // step 4
  1997     if (trap.isUndefined())
  1998         return DirectProxyHandler::hasOwn(cx, proxy, id, bp);
  2000     // step 5
  2001     RootedValue value(cx);
  2002     if (!IdToExposableValue(cx, id, &value))
  2003         return false;
  2004     Value argv[] = {
  2005         ObjectOrNullValue(target),
  2006         value
  2007     };
  2008     RootedValue trapResult(cx);
  2009     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  2010         return false;
  2012     // step 6
  2013     bool success = ToBoolean(trapResult);
  2015     // steps 7-8
  2016     if (!success) {
  2017         bool sealed;
  2018         if (!IsSealed(cx, target, id, &sealed))
  2019             return false;
  2020         if (sealed) {
  2021             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  2022             return false;
  2025         bool extensible;
  2026         if (!JSObject::isExtensible(cx, target, &extensible))
  2027             return false;
  2028         if (!extensible) {
  2029             bool isFixed;
  2030             if (!HasOwn(cx, target, id, &isFixed))
  2031                 return false;
  2032             if (isFixed) {
  2033                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  2034                 return false;
  2037     } else {
  2038         bool extensible;
  2039         if (!JSObject::isExtensible(cx, target, &extensible))
  2040             return false;
  2041         if (!extensible) {
  2042             bool isFixed;
  2043             if (!HasOwn(cx, target, id, &isFixed))
  2044                 return false;
  2045             if (!isFixed) {
  2046                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  2047                 return false;
  2052     // step 9
  2053     *bp = !!success;
  2054     return true;
  2057 // Proxy.[[GetP]](P, Receiver)
  2058 bool
  2059 ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
  2060                                 HandleId id, MutableHandleValue vp)
  2062     // step 1
  2063     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2065     // step 2
  2066     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2068     // step 3
  2069     RootedValue trap(cx);
  2070     if (!JSObject::getProperty(cx, handler, handler, cx->names().get, &trap))
  2071         return false;
  2073     // step 4
  2074     if (trap.isUndefined())
  2075         return DirectProxyHandler::get(cx, proxy, receiver, id, vp);
  2077     // step 5
  2078     RootedValue value(cx);
  2079     if (!IdToExposableValue(cx, id, &value))
  2080         return false;
  2081     Value argv[] = {
  2082         ObjectOrNullValue(target),
  2083         value,
  2084         ObjectOrNullValue(receiver)
  2085     };
  2086     RootedValue trapResult(cx);
  2087     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  2088         return false;
  2090     // step 6
  2091     Rooted<PropertyDescriptor> desc(cx);
  2092     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  2093         return false;
  2095     // step 7
  2096     if (desc.object()) {
  2097         if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
  2098             bool same;
  2099             if (!SameValue(cx, trapResult, desc.value(), &same))
  2100                 return false;
  2101             if (!same) {
  2102                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
  2103                 return false;
  2107         if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasGetterObject()) {
  2108             if (!trapResult.isUndefined()) {
  2109                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
  2110                 return false;
  2115     // step 8
  2116     vp.set(trapResult);
  2117     return true;
  2120 // Proxy.[[SetP]](P, V, Receiver)
  2121 bool
  2122 ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
  2123                                 HandleId id, bool strict, MutableHandleValue vp)
  2125     // step 1
  2126     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2128     // step 2
  2129     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2131     // step 3
  2132     RootedValue trap(cx);
  2133     if (!JSObject::getProperty(cx, handler, handler, cx->names().set, &trap))
  2134         return false;
  2136     // step 4
  2137     if (trap.isUndefined())
  2138         return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp);
  2140     // step 5
  2141     RootedValue value(cx);
  2142     if (!IdToExposableValue(cx, id, &value))
  2143         return false;
  2144     Value argv[] = {
  2145         ObjectOrNullValue(target),
  2146         value,
  2147         vp.get(),
  2148         ObjectValue(*receiver)
  2149     };
  2150     RootedValue trapResult(cx);
  2151     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  2152         return false;
  2154     // step 6
  2155     bool success = ToBoolean(trapResult);
  2157     // step 7
  2158     if (success) {
  2159         Rooted<PropertyDescriptor> desc(cx);
  2160         if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  2161             return false;
  2163         if (desc.object()) {
  2164             if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
  2165                 bool same;
  2166                 if (!SameValue(cx, vp, desc.value(), &same))
  2167                     return false;
  2168                 if (!same) {
  2169                     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
  2170                     return false;
  2174             if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasSetterObject()) {
  2175                 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
  2176                 return false;
  2181     // step 8
  2182     vp.set(BooleanValue(success));
  2183     return true;
  2186 // 15.2.3.14 Object.keys (O), step 2
  2187 bool
  2188 ScriptedDirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  2190     // step a
  2191     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2193     // step b
  2194     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2196     // step c
  2197     RootedValue trap(cx);
  2198     if (!JSObject::getProperty(cx, handler, handler, cx->names().keys, &trap))
  2199         return false;
  2201     // step d
  2202     if (trap.isUndefined())
  2203         return DirectProxyHandler::keys(cx, proxy, props);
  2205     // step e
  2206     Value argv[] = {
  2207         ObjectOrNullValue(target)
  2208     };
  2209     RootedValue trapResult(cx);
  2210     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  2211         return false;
  2213     // step f
  2214     if (trapResult.isPrimitive()) {
  2215         JSAutoByteString bytes;
  2216         if (!AtomToPrintableString(cx, cx->names().keys, &bytes))
  2217             return false;
  2218         RootedValue v(cx, ObjectOrNullValue(proxy));
  2219         js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK,
  2220                              v, js::NullPtr(), bytes.ptr());
  2221         return false;
  2224     // steps g-n are shared
  2225     return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY, cx->names().keys);
  2228 // ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]](P)
  2229 bool
  2230 ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
  2232     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2234     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2236     RootedValue trap(cx);
  2237     if (!JSObject::getProperty(cx, handler, handler, cx->names().isExtensible, &trap))
  2238         return false;
  2240     if (trap.isUndefined())
  2241         return DirectProxyHandler::isExtensible(cx, proxy, extensible);
  2243     Value argv[] = {
  2244         ObjectValue(*target)
  2245     };
  2246     RootedValue trapResult(cx);
  2247     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  2248         return false;
  2250     bool booleanTrapResult = ToBoolean(trapResult);
  2251     bool targetResult;
  2252     if (!JSObject::isExtensible(cx, target, &targetResult))
  2253         return false;
  2255     if (targetResult != booleanTrapResult) {
  2256        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
  2257        return false;
  2260     *extensible = booleanTrapResult;
  2261     return true;
  2264 bool
  2265 ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  2266                                     MutableHandleValue vp)
  2268     // FIXME: Provide a proper implementation for this trap, see bug 787004
  2269     return DirectProxyHandler::iterate(cx, proxy, flags, vp);
  2272 bool
  2273 ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  2275     // step 1
  2276     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2278     // step 2
  2279     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2281     /*
  2282      * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get
  2283      * called for non-callable objects
  2284      */
  2286     // step 3
  2287     RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
  2288     if (!argsArray)
  2289         return false;
  2291     // step 4
  2292     RootedValue trap(cx);
  2293     if (!JSObject::getProperty(cx, handler, handler, cx->names().apply, &trap))
  2294         return false;
  2296     // step 5
  2297     if (trap.isUndefined())
  2298         return DirectProxyHandler::call(cx, proxy, args);
  2300     // step 6
  2301     Value argv[] = {
  2302         ObjectValue(*target),
  2303         args.thisv(),
  2304         ObjectValue(*argsArray)
  2305     };
  2306     RootedValue thisValue(cx, ObjectValue(*handler));
  2307     return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval());
  2310 bool
  2311 ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  2313     // step 1
  2314     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  2316     // step 2
  2317     RootedObject target(cx, proxy->as<ProxyObject>().target());
  2319     /*
  2320      * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get
  2321      * called for non-callable objects
  2322      */
  2324     // step 3
  2325     RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
  2326     if (!argsArray)
  2327         return false;
  2329     // step 4
  2330     RootedValue trap(cx);
  2331     if (!JSObject::getProperty(cx, handler, handler, cx->names().construct, &trap))
  2332         return false;
  2334     // step 5
  2335     if (trap.isUndefined())
  2336         return DirectProxyHandler::construct(cx, proxy, args);
  2338     // step 6
  2339     Value constructArgv[] = {
  2340         ObjectValue(*target),
  2341         ObjectValue(*argsArray)
  2342     };
  2343     RootedValue thisValue(cx, ObjectValue(*handler));
  2344     if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval()))
  2345         return false;
  2346     if (!args.rval().isObject()) {
  2347         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
  2348         return false;
  2350     return true;
  2353 ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
  2355 #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall)                   \
  2356     JS_BEGIN_MACRO                                                           \
  2357         RootedObject proto(cx);                                              \
  2358         if (!JSObject::getProto(cx, proxy, &proto))                          \
  2359             return false;                                                    \
  2360         if (!proto)                                                          \
  2361             return true;                                                     \
  2362         assertSameCompartment(cx, proxy, proto);                             \
  2363         return protoCall;                                                    \
  2364     JS_END_MACRO                                                             \
  2366 bool
  2367 Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  2368                              MutableHandle<PropertyDescriptor> desc)
  2370     JS_CHECK_RECURSION(cx, return false);
  2371     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2372     desc.object().set(nullptr); // default result if we refuse to perform this action
  2373     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  2374     if (!policy.allowed())
  2375         return policy.returnValue();
  2376     if (!handler->hasPrototype())
  2377         return handler->getPropertyDescriptor(cx, proxy, id, desc);
  2378     if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
  2379         return false;
  2380     if (desc.object())
  2381         return true;
  2382     INVOKE_ON_PROTOTYPE(cx, handler, proxy, JS_GetPropertyDescriptorById(cx, proto, id, desc));
  2385 bool
  2386 Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
  2388     JS_CHECK_RECURSION(cx, return false);
  2390     Rooted<PropertyDescriptor> desc(cx);
  2391     if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
  2392         return false;
  2393     return NewPropertyDescriptorObject(cx, desc, vp);
  2396 bool
  2397 Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  2398                                 MutableHandle<PropertyDescriptor> desc)
  2400     JS_CHECK_RECURSION(cx, return false);
  2402     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2403     desc.object().set(nullptr); // default result if we refuse to perform this action
  2404     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  2405     if (!policy.allowed())
  2406         return policy.returnValue();
  2407     return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
  2410 bool
  2411 Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  2412                                 MutableHandleValue vp)
  2414     JS_CHECK_RECURSION(cx, return false);
  2416     Rooted<PropertyDescriptor> desc(cx);
  2417     if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc))
  2418         return false;
  2419     return NewPropertyDescriptorObject(cx, desc, vp);
  2422 bool
  2423 Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  2424                       MutableHandle<PropertyDescriptor> desc)
  2426     JS_CHECK_RECURSION(cx, return false);
  2427     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2428     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  2429     if (!policy.allowed())
  2430         return policy.returnValue();
  2431     return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc);
  2434 bool
  2435 Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v)
  2437     JS_CHECK_RECURSION(cx, return false);
  2438     Rooted<PropertyDescriptor> desc(cx);
  2439     return ParsePropertyDescriptorObject(cx, proxy, v, &desc) &&
  2440            Proxy::defineProperty(cx, proxy, id, &desc);
  2443 bool
  2444 Proxy::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  2446     JS_CHECK_RECURSION(cx, return false);
  2447     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2448     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  2449     if (!policy.allowed())
  2450         return policy.returnValue();
  2451     return proxy->as<ProxyObject>().handler()->getOwnPropertyNames(cx, proxy, props);
  2454 bool
  2455 Proxy::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  2457     JS_CHECK_RECURSION(cx, return false);
  2458     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2459     *bp = true; // default result if we refuse to perform this action
  2460     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  2461     if (!policy.allowed())
  2462         return policy.returnValue();
  2463     return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, bp);
  2466 JS_FRIEND_API(bool)
  2467 js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others)
  2469     AutoIdVector uniqueOthers(cx);
  2470     if (!uniqueOthers.reserve(others.length()))
  2471         return false;
  2472     for (size_t i = 0; i < others.length(); ++i) {
  2473         bool unique = true;
  2474         for (size_t j = 0; j < base.length(); ++j) {
  2475             if (others[i] == base[j]) {
  2476                 unique = false;
  2477                 break;
  2480         if (unique)
  2481             uniqueOthers.append(others[i]);
  2483     return base.appendAll(uniqueOthers);
  2486 bool
  2487 Proxy::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  2489     JS_CHECK_RECURSION(cx, return false);
  2490     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2491     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  2492     if (!policy.allowed())
  2493         return policy.returnValue();
  2494     if (!handler->hasPrototype())
  2495         return proxy->as<ProxyObject>().handler()->enumerate(cx, proxy, props);
  2496     if (!handler->keys(cx, proxy, props))
  2497         return false;
  2498     AutoIdVector protoProps(cx);
  2499     INVOKE_ON_PROTOTYPE(cx, handler, proxy,
  2500                         GetPropertyNames(cx, proto, 0, &protoProps) &&
  2501                         AppendUnique(cx, props, protoProps));
  2504 bool
  2505 Proxy::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  2507     JS_CHECK_RECURSION(cx, return false);
  2508     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2509     *bp = false; // default result if we refuse to perform this action
  2510     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  2511     if (!policy.allowed())
  2512         return policy.returnValue();
  2513     if (!handler->hasPrototype())
  2514         return handler->has(cx, proxy, id, bp);
  2515     if (!handler->hasOwn(cx, proxy, id, bp))
  2516         return false;
  2517     if (*bp)
  2518         return true;
  2519     bool Bp;
  2520     INVOKE_ON_PROTOTYPE(cx, handler, proxy,
  2521                         JS_HasPropertyById(cx, proto, id, &Bp) &&
  2522                         ((*bp = Bp) || true));
  2525 bool
  2526 Proxy::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  2528     JS_CHECK_RECURSION(cx, return false);
  2529     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2530     *bp = false; // default result if we refuse to perform this action
  2531     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  2532     if (!policy.allowed())
  2533         return policy.returnValue();
  2534     return handler->hasOwn(cx, proxy, id, bp);
  2537 bool
  2538 Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  2539            MutableHandleValue vp)
  2541     JS_CHECK_RECURSION(cx, return false);
  2542     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2543     vp.setUndefined(); // default result if we refuse to perform this action
  2544     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  2545     if (!policy.allowed())
  2546         return policy.returnValue();
  2547     bool own;
  2548     if (!handler->hasPrototype()) {
  2549         own = true;
  2550     } else {
  2551         if (!handler->hasOwn(cx, proxy, id, &own))
  2552             return false;
  2554     if (own)
  2555         return handler->get(cx, proxy, receiver, id, vp);
  2556     INVOKE_ON_PROTOTYPE(cx, handler, proxy, JSObject::getGeneric(cx, proto, receiver, id, vp));
  2559 bool
  2560 Proxy::callProp(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  2561                 MutableHandleValue vp)
  2563     // The inline caches need an access point for JSOP_CALLPROP sites that accounts
  2564     // for the possibility of __noSuchMethod__
  2565     if (!Proxy::get(cx, proxy, receiver, id, vp))
  2566         return false;
  2568 #if JS_HAS_NO_SUCH_METHOD
  2569     if (MOZ_UNLIKELY(vp.isPrimitive())) {
  2570         if (!OnUnknownMethod(cx, proxy, IdToValue(id), vp))
  2571             return false;
  2573 #endif
  2575     return true;
  2578 bool
  2579 Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
  2580            MutableHandleValue vp)
  2582     JS_CHECK_RECURSION(cx, return false);
  2583     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2584     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  2585     if (!policy.allowed())
  2586         return policy.returnValue();
  2588     // If the proxy doesn't require that we consult its prototype for the
  2589     // non-own cases, we can sink to the |set| trap.
  2590     if (!handler->hasPrototype())
  2591         return handler->set(cx, proxy, receiver, id, strict, vp);
  2593     // If we have an existing (own or non-own) property with a setter, we want
  2594     // to invoke that.
  2595     Rooted<PropertyDescriptor> desc(cx);
  2596     if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
  2597         return false;
  2598     if (desc.object() && desc.setter() && desc.setter() != JS_StrictPropertyStub)
  2599         return CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp);
  2601     // Ok. Either there was no pre-existing property, or it was a value prop
  2602     // that we're going to shadow. Make a property descriptor and define it.
  2603     Rooted<PropertyDescriptor> newDesc(cx);
  2604     newDesc.value().set(vp);
  2605     return handler->defineProperty(cx, receiver, id, &newDesc);
  2608 bool
  2609 Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  2611     JS_CHECK_RECURSION(cx, return false);
  2612     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2613     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  2614     if (!policy.allowed())
  2615         return policy.returnValue();
  2616     return handler->keys(cx, proxy, props);
  2619 bool
  2620 Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
  2622     JS_CHECK_RECURSION(cx, return false);
  2623     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2624     vp.setUndefined(); // default result if we refuse to perform this action
  2625     if (!handler->hasPrototype()) {
  2626         AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  2627                                BaseProxyHandler::ENUMERATE, true);
  2628         // If the policy denies access but wants us to return true, we need
  2629         // to hand a valid (empty) iterator object to the caller.
  2630         if (!policy.allowed()) {
  2631             AutoIdVector props(cx);
  2632             return policy.returnValue() &&
  2633                    EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
  2635         return handler->iterate(cx, proxy, flags, vp);
  2637     AutoIdVector props(cx);
  2638     // The other Proxy::foo methods do the prototype-aware work for us here.
  2639     if ((flags & JSITER_OWNONLY)
  2640         ? !Proxy::keys(cx, proxy, props)
  2641         : !Proxy::enumerate(cx, proxy, props)) {
  2642         return false;
  2644     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
  2647 bool
  2648 Proxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
  2650     JS_CHECK_RECURSION(cx, return false);
  2651     return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
  2654 bool
  2655 Proxy::preventExtensions(JSContext *cx, HandleObject proxy)
  2657     JS_CHECK_RECURSION(cx, return false);
  2658     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2659     return handler->preventExtensions(cx, proxy);
  2662 bool
  2663 Proxy::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  2665     JS_CHECK_RECURSION(cx, return false);
  2666     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2668     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
  2669     // can only set our default value once we're sure that we're not calling the
  2670     // trap.
  2671     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  2672                            BaseProxyHandler::CALL, true);
  2673     if (!policy.allowed()) {
  2674         args.rval().setUndefined();
  2675         return policy.returnValue();
  2678     return handler->call(cx, proxy, args);
  2681 bool
  2682 Proxy::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  2684     JS_CHECK_RECURSION(cx, return false);
  2685     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2687     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
  2688     // can only set our default value once we're sure that we're not calling the
  2689     // trap.
  2690     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  2691                            BaseProxyHandler::CALL, true);
  2692     if (!policy.allowed()) {
  2693         args.rval().setUndefined();
  2694         return policy.returnValue();
  2697     return handler->construct(cx, proxy, args);
  2700 bool
  2701 Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
  2703     JS_CHECK_RECURSION(cx, return false);
  2704     RootedObject proxy(cx, &args.thisv().toObject());
  2705     // Note - we don't enter a policy here because our security architecture
  2706     // guards against nativeCall by overriding the trap itself in the right
  2707     // circumstances.
  2708     return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
  2711 bool
  2712 Proxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
  2714     JS_CHECK_RECURSION(cx, return false);
  2715     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2716     *bp = false; // default result if we refuse to perform this action
  2717     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
  2718     if (!policy.allowed())
  2719         return policy.returnValue();
  2720     return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
  2723 bool
  2724 Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
  2726     JS_CHECK_RECURSION(cx, return false);
  2727     return proxy->as<ProxyObject>().handler()->objectClassIs(proxy, classValue, cx);
  2730 const char *
  2731 Proxy::className(JSContext *cx, HandleObject proxy)
  2733     // Check for unbounded recursion, but don't signal an error; className
  2734     // needs to be infallible.
  2735     int stackDummy;
  2736     if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
  2737         return "too much recursion";
  2739     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2740     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  2741                            BaseProxyHandler::GET, /* mayThrow = */ false);
  2742     // Do the safe thing if the policy rejects.
  2743     if (!policy.allowed()) {
  2744         return handler->BaseProxyHandler::className(cx, proxy);
  2746     return handler->className(cx, proxy);
  2749 JSString *
  2750 Proxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
  2752     JS_CHECK_RECURSION(cx, return nullptr);
  2753     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2754     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  2755                            BaseProxyHandler::GET, /* mayThrow = */ false);
  2756     // Do the safe thing if the policy rejects.
  2757     if (!policy.allowed())
  2758         return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
  2759     return handler->fun_toString(cx, proxy, indent);
  2762 bool
  2763 Proxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
  2765     JS_CHECK_RECURSION(cx, return false);
  2766     return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
  2769 bool
  2770 Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
  2772     JS_CHECK_RECURSION(cx, return false);
  2773     return proxy->as<ProxyObject>().handler()->defaultValue(cx, proxy, hint, vp);
  2776 JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject *>(0x1);
  2778 bool
  2779 Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto)
  2781     JS_ASSERT(proxy->getTaggedProto().isLazy());
  2782     JS_CHECK_RECURSION(cx, return false);
  2783     return proxy->as<ProxyObject>().handler()->getPrototypeOf(cx, proxy, proto);
  2786 bool
  2787 Proxy::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
  2789     JS_ASSERT(proxy->getTaggedProto().isLazy());
  2790     JS_CHECK_RECURSION(cx, return false);
  2791     return proxy->as<ProxyObject>().handler()->setPrototypeOf(cx, proxy, proto, bp);
  2794 /* static */ bool
  2795 Proxy::watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
  2797     JS_CHECK_RECURSION(cx, return false);
  2798     return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
  2801 /* static */ bool
  2802 Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id)
  2804     JS_CHECK_RECURSION(cx, return false);
  2805     return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
  2808 /* static */ bool
  2809 Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
  2810              HandleObject result)
  2812     JS_CHECK_RECURSION(cx, return false);
  2813     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  2814     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
  2815                            /* mayThrow = */ true);
  2816     if (!policy.allowed()) {
  2817         if (policy.returnValue()) {
  2818             JS_ASSERT(!cx->isExceptionPending());
  2819             return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
  2821         return false;
  2823     return handler->slice(cx, proxy, begin, end, result);
  2826 JSObject *
  2827 js::proxy_innerObject(JSContext *cx, HandleObject obj)
  2829     return obj->as<ProxyObject>().private_().toObjectOrNull();
  2832 bool
  2833 js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
  2834                         MutableHandleObject objp, MutableHandleShape propp)
  2836     bool found;
  2837     if (!Proxy::has(cx, obj, id, &found))
  2838         return false;
  2840     if (found) {
  2841         MarkNonNativePropertyFound(propp);
  2842         objp.set(obj);
  2843     } else {
  2844         objp.set(nullptr);
  2845         propp.set(nullptr);
  2847     return true;
  2850 bool
  2851 js::proxy_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
  2852                          MutableHandleObject objp, MutableHandleShape propp)
  2854     RootedId id(cx, NameToId(name));
  2855     return proxy_LookupGeneric(cx, obj, id, objp, propp);
  2858 bool
  2859 js::proxy_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
  2860                         MutableHandleObject objp, MutableHandleShape propp)
  2862     RootedId id(cx);
  2863     if (!IndexToId(cx, index, &id))
  2864         return false;
  2865     return proxy_LookupGeneric(cx, obj, id, objp, propp);
  2868 bool
  2869 js::proxy_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
  2870                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  2872     Rooted<PropertyDescriptor> desc(cx);
  2873     desc.object().set(obj);
  2874     desc.value().set(value);
  2875     desc.setAttributes(attrs);
  2876     desc.setGetter(getter);
  2877     desc.setSetter(setter);
  2878     return Proxy::defineProperty(cx, obj, id, &desc);
  2881 bool
  2882 js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
  2883                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  2885     Rooted<jsid> id(cx, NameToId(name));
  2886     return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  2889 bool
  2890 js::proxy_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
  2891                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  2893     RootedId id(cx);
  2894     if (!IndexToId(cx, index, &id))
  2895         return false;
  2896     return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  2899 bool
  2900 js::proxy_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
  2901                      MutableHandleValue vp)
  2903     return Proxy::get(cx, obj, receiver, id, vp);
  2906 bool
  2907 js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
  2908                       MutableHandleValue vp)
  2910     Rooted<jsid> id(cx, NameToId(name));
  2911     return proxy_GetGeneric(cx, obj, receiver, id, vp);
  2914 bool
  2915 js::proxy_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
  2916                      MutableHandleValue vp)
  2918     RootedId id(cx);
  2919     if (!IndexToId(cx, index, &id))
  2920         return false;
  2921     return proxy_GetGeneric(cx, obj, receiver, id, vp);
  2924 bool
  2925 js::proxy_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
  2926                      MutableHandleValue vp, bool strict)
  2928     return Proxy::set(cx, obj, obj, id, strict, vp);
  2931 bool
  2932 js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
  2933                       MutableHandleValue vp, bool strict)
  2935     Rooted<jsid> id(cx, NameToId(name));
  2936     return proxy_SetGeneric(cx, obj, id, vp, strict);
  2939 bool
  2940 js::proxy_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
  2941                      MutableHandleValue vp, bool strict)
  2943     RootedId id(cx);
  2944     if (!IndexToId(cx, index, &id))
  2945         return false;
  2946     return proxy_SetGeneric(cx, obj, id, vp, strict);
  2949 bool
  2950 js::proxy_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  2952     Rooted<PropertyDescriptor> desc(cx);
  2953     if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
  2954         return false;
  2955     *attrsp = desc.attributes();
  2956     return true;
  2959 bool
  2960 js::proxy_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  2962     /* Lookup the current property descriptor so we have setter/getter/value. */
  2963     Rooted<PropertyDescriptor> desc(cx);
  2964     if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
  2965         return false;
  2966     desc.setAttributes(*attrsp);
  2967     return Proxy::defineProperty(cx, obj, id, &desc);
  2970 static bool
  2971 proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
  2973     bool deleted;
  2974     if (!Proxy::delete_(cx, obj, id, &deleted))
  2975         return false;
  2976     *succeeded = deleted;
  2977     return js_SuppressDeletedProperty(cx, obj, id);
  2980 bool
  2981 js::proxy_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded)
  2983     RootedId id(cx, NameToId(name));
  2984     return proxy_DeleteGeneric(cx, obj, id, succeeded);
  2987 bool
  2988 js::proxy_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
  2990     RootedId id(cx);
  2991     if (!IndexToId(cx, index, &id))
  2992         return false;
  2993     return proxy_DeleteGeneric(cx, obj, id, succeeded);
  2996 void
  2997 js::proxy_Trace(JSTracer *trc, JSObject *obj)
  2999     JS_ASSERT(obj->is<ProxyObject>());
  3000     ProxyObject::trace(trc, obj);
  3003 /* static */ void
  3004 ProxyObject::trace(JSTracer *trc, JSObject *obj)
  3006     ProxyObject *proxy = &obj->as<ProxyObject>();
  3008 #ifdef DEBUG
  3009     if (!trc->runtime()->gcDisableStrictProxyCheckingCount && proxy->is<WrapperObject>()) {
  3010         JSObject *referent = &proxy->private_().toObject();
  3011         if (referent->compartment() != proxy->compartment()) {
  3012             /*
  3013              * Assert that this proxy is tracked in the wrapper map. We maintain
  3014              * the invariant that the wrapped object is the key in the wrapper map.
  3015              */
  3016             Value key = ObjectValue(*referent);
  3017             WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
  3018             JS_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
  3021 #endif
  3023     // Note: If you add new slots here, make sure to change
  3024     // nuke() to cope.
  3025     MarkCrossCompartmentSlot(trc, obj, proxy->slotOfPrivate(), "private");
  3026     MarkSlot(trc, proxy->slotOfExtra(0), "extra0");
  3028     /*
  3029      * The GC can use the second reserved slot to link the cross compartment
  3030      * wrappers into a linked list, in which case we don't want to trace it.
  3031      */
  3032     if (!proxy->is<CrossCompartmentWrapperObject>())
  3033         MarkSlot(trc, proxy->slotOfExtra(1), "extra1");
  3035     /*
  3036      * Allow for people to add extra slots to "proxy" classes, without allowing
  3037      * them to set their own trace hook. Trace the extras.
  3038      */
  3039     unsigned numSlots = JSCLASS_RESERVED_SLOTS(proxy->getClass());
  3040     for (unsigned i = PROXY_MINIMUM_SLOTS; i < numSlots; i++)
  3041         MarkSlot(trc, proxy->slotOfClassSpecific(i), "class-specific");
  3044 JSObject *
  3045 js::proxy_WeakmapKeyDelegate(JSObject *obj)
  3047     JS_ASSERT(obj->is<ProxyObject>());
  3048     return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
  3051 bool
  3052 js::proxy_Convert(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
  3054     JS_ASSERT(proxy->is<ProxyObject>());
  3055     return Proxy::defaultValue(cx, proxy, hint, vp);
  3058 void
  3059 js::proxy_Finalize(FreeOp *fop, JSObject *obj)
  3061     JS_ASSERT(obj->is<ProxyObject>());
  3062     obj->as<ProxyObject>().handler()->finalize(fop, obj);
  3065 bool
  3066 js::proxy_HasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
  3068     bool b;
  3069     if (!Proxy::hasInstance(cx, proxy, v, &b))
  3070         return false;
  3071     *bp = !!b;
  3072     return true;
  3075 bool
  3076 js::proxy_Call(JSContext *cx, unsigned argc, Value *vp)
  3078     CallArgs args = CallArgsFromVp(argc, vp);
  3079     RootedObject proxy(cx, &args.callee());
  3080     JS_ASSERT(proxy->is<ProxyObject>());
  3081     return Proxy::call(cx, proxy, args);
  3084 bool
  3085 js::proxy_Construct(JSContext *cx, unsigned argc, Value *vp)
  3087     CallArgs args = CallArgsFromVp(argc, vp);
  3088     RootedObject proxy(cx, &args.callee());
  3089     JS_ASSERT(proxy->is<ProxyObject>());
  3090     return Proxy::construct(cx, proxy, args);
  3093 bool
  3094 js::proxy_Watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
  3096     return Proxy::watch(cx, obj, id, callable);
  3099 bool
  3100 js::proxy_Unwatch(JSContext *cx, HandleObject obj, HandleId id)
  3102     return Proxy::unwatch(cx, obj, id);
  3105 bool
  3106 js::proxy_Slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
  3107                 HandleObject result)
  3109     return Proxy::slice(cx, proxy, begin, end, result);
  3112 #define PROXY_CLASS(callOp, constructOp)                        \
  3113     PROXY_CLASS_DEF("Proxy",                                    \
  3114                     0, /* additional slots */                   \
  3115                     JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),    \
  3116                     callOp,                                     \
  3117                     constructOp)
  3119 const Class js::ProxyObject::uncallableClass_ = PROXY_CLASS(nullptr, nullptr);
  3120 const Class js::ProxyObject::callableClass_ = PROXY_CLASS(proxy_Call, proxy_Construct);
  3122 const Class* const js::CallableProxyClassPtr = &ProxyObject::callableClass_;
  3123 const Class* const js::UncallableProxyClassPtr = &ProxyObject::uncallableClass_;
  3125 JS_FRIEND_API(JSObject *)
  3126 js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, JSObject *proto_,
  3127                    JSObject *parent_, const ProxyOptions &options)
  3129     return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), parent_,
  3130                             options);
  3133 void
  3134 ProxyObject::renew(JSContext *cx, BaseProxyHandler *handler, Value priv)
  3136     JS_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
  3137     JS_ASSERT(getParent() == cx->global());
  3138     JS_ASSERT(getClass() == &uncallableClass_);
  3139     JS_ASSERT(!getClass()->ext.innerObject);
  3140     JS_ASSERT(getTaggedProto().isLazy());
  3142     setSlot(HANDLER_SLOT, PrivateValue(handler));
  3143     setCrossCompartmentSlot(PRIVATE_SLOT, priv);
  3144     setSlot(EXTRA_SLOT + 0, UndefinedValue());
  3145     setSlot(EXTRA_SLOT + 1, UndefinedValue());
  3148 static bool
  3149 proxy(JSContext *cx, unsigned argc, jsval *vp)
  3151     CallArgs args = CallArgsFromVp(argc, vp);
  3152     if (args.length() < 2) {
  3153         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3154                              "Proxy", "1", "s");
  3155         return false;
  3157     RootedObject target(cx, NonNullObject(cx, args[0]));
  3158     if (!target)
  3159         return false;
  3160     RootedObject handler(cx, NonNullObject(cx, args[1]));
  3161     if (!handler)
  3162         return false;
  3163     RootedValue priv(cx, ObjectValue(*target));
  3164     ProxyOptions options;
  3165     options.selectDefaultClass(target->isCallable());
  3166     ProxyObject *proxy =
  3167         ProxyObject::New(cx, &ScriptedDirectProxyHandler::singleton,
  3168                          priv, TaggedProto(TaggedProto::LazyProto), cx->global(),
  3169                          options);
  3170     if (!proxy)
  3171         return false;
  3172     proxy->setExtra(0, ObjectOrNullValue(handler));
  3173     args.rval().setObject(*proxy);
  3174     return true;
  3177 static bool
  3178 proxy_create(JSContext *cx, unsigned argc, Value *vp)
  3180     CallArgs args = CallArgsFromVp(argc, vp);
  3181     if (args.length() < 1) {
  3182         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3183                              "create", "0", "s");
  3184         return false;
  3186     JSObject *handler = NonNullObject(cx, args[0]);
  3187     if (!handler)
  3188         return false;
  3189     JSObject *proto, *parent = nullptr;
  3190     if (args.get(1).isObject()) {
  3191         proto = &args[1].toObject();
  3192         parent = proto->getParent();
  3193     } else {
  3194         JS_ASSERT(IsFunctionObject(&args.callee()));
  3195         proto = nullptr;
  3197     if (!parent)
  3198         parent = args.callee().getParent();
  3199     RootedValue priv(cx, ObjectValue(*handler));
  3200     JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton,
  3201                                      priv, proto, parent);
  3202     if (!proxy)
  3203         return false;
  3205     args.rval().setObject(*proxy);
  3206     return true;
  3209 static bool
  3210 proxy_createFunction(JSContext *cx, unsigned argc, Value *vp)
  3212     CallArgs args = CallArgsFromVp(argc, vp);
  3213     if (args.length() < 2) {
  3214         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  3215                              "createFunction", "1", "");
  3216         return false;
  3218     RootedObject handler(cx, NonNullObject(cx, args[0]));
  3219     if (!handler)
  3220         return false;
  3221     RootedObject proto(cx), parent(cx);
  3222     parent = args.callee().getParent();
  3223     proto = parent->global().getOrCreateFunctionPrototype(cx);
  3224     if (!proto)
  3225         return false;
  3226     parent = proto->getParent();
  3228     RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2));
  3229     if (!call)
  3230         return false;
  3231     RootedObject construct(cx, nullptr);
  3232     if (args.length() > 2) {
  3233         construct = ValueToCallable(cx, args[2], args.length() - 3);
  3234         if (!construct)
  3235             return false;
  3236     } else {
  3237         construct = call;
  3240     // Stash the call and construct traps on a holder object that we can stick
  3241     // in a slot on the proxy.
  3242     RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder),
  3243                                                          js::NullPtr(), cx->global()));
  3244     if (!ccHolder)
  3245         return false;
  3246     ccHolder->setReservedSlot(0, ObjectValue(*call));
  3247     ccHolder->setReservedSlot(1, ObjectValue(*construct));
  3249     RootedValue priv(cx, ObjectValue(*handler));
  3250     ProxyOptions options;
  3251     options.selectDefaultClass(true);
  3252     JSObject *proxy =
  3253         ProxyObject::New(cx, &ScriptedIndirectProxyHandler::singleton,
  3254                          priv, TaggedProto(proto), parent, options);
  3255     if (!proxy)
  3256         return false;
  3257     proxy->as<ProxyObject>().setExtra(0, ObjectValue(*ccHolder));
  3259     args.rval().setObject(*proxy);
  3260     return true;
  3263 JS_FRIEND_API(JSObject *)
  3264 js_InitProxyClass(JSContext *cx, HandleObject obj)
  3266     static const JSFunctionSpec static_methods[] = {
  3267         JS_FN("create",         proxy_create,          2, 0),
  3268         JS_FN("createFunction", proxy_createFunction,  3, 0),
  3269         JS_FS_END
  3270     };
  3272     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  3273     RootedFunction ctor(cx);
  3274     ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
  3275     if (!ctor)
  3276         return nullptr;
  3278     if (!JS_DefineFunctions(cx, ctor, static_methods))
  3279         return nullptr;
  3280     if (!JS_DefineProperty(cx, obj, "Proxy", ctor, 0,
  3281                            JS_PropertyStub, JS_StrictPropertyStub)) {
  3282         return nullptr;
  3285     global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
  3286     return ctor;

mercurial