js/src/jsproxy.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jsproxy.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3287 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "jsproxy.h"
    1.11 +
    1.12 +#include <string.h>
    1.13 +
    1.14 +#include "jsapi.h"
    1.15 +#include "jscntxt.h"
    1.16 +#include "jsfun.h"
    1.17 +#include "jsgc.h"
    1.18 +#include "jswrapper.h"
    1.19 +
    1.20 +#include "gc/Marking.h"
    1.21 +#include "vm/WrapperObject.h"
    1.22 +
    1.23 +#include "jsatominlines.h"
    1.24 +#include "jsinferinlines.h"
    1.25 +#include "jsobjinlines.h"
    1.26 +
    1.27 +#include "vm/ObjectImpl-inl.h"
    1.28 +
    1.29 +using namespace js;
    1.30 +using namespace js::gc;
    1.31 +using mozilla::ArrayLength;
    1.32 +
    1.33 +void
    1.34 +js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id)
    1.35 +{
    1.36 +    if (JS_IsExceptionPending(cx))
    1.37 +        return;
    1.38 +
    1.39 +    if (JSID_IS_VOID(id)) {
    1.40 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
    1.41 +                             JSMSG_OBJECT_ACCESS_DENIED);
    1.42 +    } else {
    1.43 +        JSString *str = IdToString(cx, id);
    1.44 +        const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
    1.45 +        JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
    1.46 +                               JSMSG_PROPERTY_ACCESS_DENIED, prop);
    1.47 +    }
    1.48 +}
    1.49 +
    1.50 +#ifdef DEBUG
    1.51 +void
    1.52 +js::AutoEnterPolicy::recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act)
    1.53 +{
    1.54 +    if (allowed()) {
    1.55 +        context = cx;
    1.56 +        enteredProxy.construct(proxy);
    1.57 +        enteredId.construct(id);
    1.58 +        enteredAction = act;
    1.59 +        prev = cx->runtime()->enteredPolicy;
    1.60 +        cx->runtime()->enteredPolicy = this;
    1.61 +    }
    1.62 +}
    1.63 +
    1.64 +void
    1.65 +js::AutoEnterPolicy::recordLeave()
    1.66 +{
    1.67 +    if (!enteredProxy.empty()) {
    1.68 +        JS_ASSERT(context->runtime()->enteredPolicy == this);
    1.69 +        context->runtime()->enteredPolicy = prev;
    1.70 +    }
    1.71 +}
    1.72 +
    1.73 +JS_FRIEND_API(void)
    1.74 +js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id,
    1.75 +                        BaseProxyHandler::Action act)
    1.76 +{
    1.77 +    MOZ_ASSERT(proxy->is<ProxyObject>());
    1.78 +    MOZ_ASSERT(cx->runtime()->enteredPolicy);
    1.79 +    MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredProxy.ref().get() == proxy);
    1.80 +    MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId.ref().get() == id);
    1.81 +    MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredAction & act);
    1.82 +}
    1.83 +#endif
    1.84 +
    1.85 +BaseProxyHandler::BaseProxyHandler(const void *family)
    1.86 +  : mFamily(family),
    1.87 +    mHasPrototype(false),
    1.88 +    mHasSecurityPolicy(false)
    1.89 +{
    1.90 +}
    1.91 +
    1.92 +BaseProxyHandler::~BaseProxyHandler()
    1.93 +{
    1.94 +}
    1.95 +
    1.96 +bool
    1.97 +BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
    1.98 +                        bool *bp)
    1.99 +{
   1.100 +    *bp = true;
   1.101 +    return true;
   1.102 +}
   1.103 +
   1.104 +bool
   1.105 +BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.106 +{
   1.107 +    assertEnteredPolicy(cx, proxy, id, GET);
   1.108 +    Rooted<PropertyDescriptor> desc(cx);
   1.109 +    if (!getPropertyDescriptor(cx, proxy, id, &desc))
   1.110 +        return false;
   1.111 +    *bp = !!desc.object();
   1.112 +    return true;
   1.113 +}
   1.114 +
   1.115 +bool
   1.116 +BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.117 +{
   1.118 +    // Note: Proxy::set needs to invoke hasOwn to determine where the setter
   1.119 +    // lives, so we allow SET operations to invoke us.
   1.120 +    assertEnteredPolicy(cx, proxy, id, GET | SET);
   1.121 +    Rooted<PropertyDescriptor> desc(cx);
   1.122 +    if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   1.123 +        return false;
   1.124 +    *bp = !!desc.object();
   1.125 +    return true;
   1.126 +}
   1.127 +
   1.128 +bool
   1.129 +BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.130 +                      HandleId id, MutableHandleValue vp)
   1.131 +{
   1.132 +    assertEnteredPolicy(cx, proxy, id, GET);
   1.133 +
   1.134 +    Rooted<PropertyDescriptor> desc(cx);
   1.135 +    if (!getPropertyDescriptor(cx, proxy, id, &desc))
   1.136 +        return false;
   1.137 +    if (!desc.object()) {
   1.138 +        vp.setUndefined();
   1.139 +        return true;
   1.140 +    }
   1.141 +    if (!desc.getter() ||
   1.142 +        (!desc.hasGetterObject() && desc.getter() == JS_PropertyStub))
   1.143 +    {
   1.144 +        vp.set(desc.value());
   1.145 +        return true;
   1.146 +    }
   1.147 +    if (desc.hasGetterObject())
   1.148 +        return InvokeGetterOrSetter(cx, receiver, ObjectValue(*desc.getterObject()),
   1.149 +                                    0, nullptr, vp);
   1.150 +    if (!desc.isShared())
   1.151 +        vp.set(desc.value());
   1.152 +    else
   1.153 +        vp.setUndefined();
   1.154 +
   1.155 +    return CallJSPropertyOp(cx, desc.getter(), receiver, id, vp);
   1.156 +}
   1.157 +
   1.158 +bool
   1.159 +BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.160 +                      HandleId id, bool strict, MutableHandleValue vp)
   1.161 +{
   1.162 +    assertEnteredPolicy(cx, proxy, id, SET);
   1.163 +
   1.164 +    // Find an own or inherited property. The code here is strange for maximum
   1.165 +    // backward compatibility with earlier code written before ES6 and before
   1.166 +    // SetPropertyIgnoringNamedGetter.
   1.167 +    Rooted<PropertyDescriptor> desc(cx);
   1.168 +    if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   1.169 +        return false;
   1.170 +    bool descIsOwn = desc.object() != nullptr;
   1.171 +    if (!descIsOwn) {
   1.172 +        if (!getPropertyDescriptor(cx, proxy, id, &desc))
   1.173 +            return false;
   1.174 +    }
   1.175 +
   1.176 +    return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict,
   1.177 +                                          vp);
   1.178 +}
   1.179 +
   1.180 +bool
   1.181 +js::SetPropertyIgnoringNamedGetter(JSContext *cx, BaseProxyHandler *handler,
   1.182 +                                   HandleObject proxy, HandleObject receiver,
   1.183 +                                   HandleId id, MutableHandle<PropertyDescriptor> desc,
   1.184 +                                   bool descIsOwn, bool strict, MutableHandleValue vp)
   1.185 +{
   1.186 +    /* The control-flow here differs from ::get() because of the fall-through case below. */
   1.187 +    if (descIsOwn) {
   1.188 +        JS_ASSERT(desc.object());
   1.189 +
   1.190 +        // Check for read-only properties.
   1.191 +        if (desc.isReadonly())
   1.192 +            return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
   1.193 +        if (!desc.setter()) {
   1.194 +            // Be wary of the odd explicit undefined setter case possible through
   1.195 +            // Object.defineProperty.
   1.196 +            if (!desc.hasSetterObject())
   1.197 +                desc.setSetter(JS_StrictPropertyStub);
   1.198 +        } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
   1.199 +            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
   1.200 +                return false;
   1.201 +            if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
   1.202 +                return true;
   1.203 +            if (desc.isShared())
   1.204 +                return true;
   1.205 +        }
   1.206 +        if (!desc.getter()) {
   1.207 +            // Same as above for the null setter case.
   1.208 +            if (!desc.hasGetterObject())
   1.209 +                desc.setGetter(JS_PropertyStub);
   1.210 +        }
   1.211 +        desc.value().set(vp.get());
   1.212 +        return handler->defineProperty(cx, receiver, id, desc);
   1.213 +    }
   1.214 +    if (desc.object()) {
   1.215 +        // Check for read-only properties.
   1.216 +        if (desc.isReadonly())
   1.217 +            return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true;
   1.218 +        if (!desc.setter()) {
   1.219 +            // Be wary of the odd explicit undefined setter case possible through
   1.220 +            // Object.defineProperty.
   1.221 +            if (!desc.hasSetterObject())
   1.222 +                desc.setSetter(JS_StrictPropertyStub);
   1.223 +        } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) {
   1.224 +            if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp))
   1.225 +                return false;
   1.226 +            if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler)
   1.227 +                return true;
   1.228 +            if (desc.isShared())
   1.229 +                return true;
   1.230 +        }
   1.231 +        if (!desc.getter()) {
   1.232 +            // Same as above for the null setter case.
   1.233 +            if (!desc.hasGetterObject())
   1.234 +                desc.setGetter(JS_PropertyStub);
   1.235 +        }
   1.236 +        desc.value().set(vp.get());
   1.237 +        return handler->defineProperty(cx, receiver, id, desc);
   1.238 +    }
   1.239 +
   1.240 +    desc.object().set(receiver);
   1.241 +    desc.value().set(vp.get());
   1.242 +    desc.setAttributes(JSPROP_ENUMERATE);
   1.243 +    desc.setGetter(nullptr);
   1.244 +    desc.setSetter(nullptr); // Pick up the class getter/setter.
   1.245 +    return handler->defineProperty(cx, receiver, id, desc);
   1.246 +}
   1.247 +
   1.248 +bool
   1.249 +BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   1.250 +{
   1.251 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.252 +    JS_ASSERT(props.length() == 0);
   1.253 +
   1.254 +    if (!getOwnPropertyNames(cx, proxy, props))
   1.255 +        return false;
   1.256 +
   1.257 +    /* Select only the enumerable properties through in-place iteration. */
   1.258 +    Rooted<PropertyDescriptor> desc(cx);
   1.259 +    RootedId id(cx);
   1.260 +    size_t i = 0;
   1.261 +    for (size_t j = 0, len = props.length(); j < len; j++) {
   1.262 +        JS_ASSERT(i <= j);
   1.263 +        id = props[j];
   1.264 +        AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
   1.265 +        if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
   1.266 +            return false;
   1.267 +        if (desc.object() && desc.isEnumerable())
   1.268 +            props[i++] = id;
   1.269 +    }
   1.270 +
   1.271 +    JS_ASSERT(i <= props.length());
   1.272 +    props.resize(i);
   1.273 +
   1.274 +    return true;
   1.275 +}
   1.276 +
   1.277 +bool
   1.278 +BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
   1.279 +{
   1.280 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.281 +
   1.282 +    AutoIdVector props(cx);
   1.283 +    if ((flags & JSITER_OWNONLY)
   1.284 +        ? !keys(cx, proxy, props)
   1.285 +        : !enumerate(cx, proxy, props)) {
   1.286 +        return false;
   1.287 +    }
   1.288 +
   1.289 +    return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
   1.290 +}
   1.291 +
   1.292 +bool
   1.293 +BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
   1.294 +{
   1.295 +    MOZ_ASSUME_UNREACHABLE("callable proxies should implement call trap");
   1.296 +}
   1.297 +
   1.298 +bool
   1.299 +BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
   1.300 +{
   1.301 +    MOZ_ASSUME_UNREACHABLE("callable proxies should implement construct trap");
   1.302 +}
   1.303 +
   1.304 +const char *
   1.305 +BaseProxyHandler::className(JSContext *cx, HandleObject proxy)
   1.306 +{
   1.307 +    return proxy->isCallable() ? "Function" : "Object";
   1.308 +}
   1.309 +
   1.310 +JSString *
   1.311 +BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
   1.312 +{
   1.313 +    if (proxy->isCallable())
   1.314 +        return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
   1.315 +    RootedValue v(cx, ObjectValue(*proxy));
   1.316 +    ReportIsNotFunction(cx, v);
   1.317 +    return nullptr;
   1.318 +}
   1.319 +
   1.320 +bool
   1.321 +BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
   1.322 +                                  RegExpGuard *g)
   1.323 +{
   1.324 +    MOZ_ASSUME_UNREACHABLE("This should have been a wrapped regexp");
   1.325 +}
   1.326 +
   1.327 +bool
   1.328 +BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint,
   1.329 +                               MutableHandleValue vp)
   1.330 +{
   1.331 +    return DefaultValue(cx, proxy, hint, vp);
   1.332 +}
   1.333 +
   1.334 +bool
   1.335 +BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
   1.336 +{
   1.337 +    ReportIncompatible(cx, args);
   1.338 +    return false;
   1.339 +}
   1.340 +
   1.341 +bool
   1.342 +BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
   1.343 +{
   1.344 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   1.345 +    RootedValue val(cx, ObjectValue(*proxy.get()));
   1.346 +    js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
   1.347 +                        JSDVG_SEARCH_STACK, val, js::NullPtr());
   1.348 +    return false;
   1.349 +}
   1.350 +
   1.351 +bool
   1.352 +BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
   1.353 +{
   1.354 +    return false;
   1.355 +}
   1.356 +
   1.357 +void
   1.358 +BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
   1.359 +{
   1.360 +}
   1.361 +
   1.362 +JSObject *
   1.363 +BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy)
   1.364 +{
   1.365 +    return nullptr;
   1.366 +}
   1.367 +
   1.368 +bool
   1.369 +BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
   1.370 +{
   1.371 +    MOZ_ASSUME_UNREACHABLE("Must override getPrototypeOf with lazy prototype.");
   1.372 +}
   1.373 +
   1.374 +bool
   1.375 +BaseProxyHandler::setPrototypeOf(JSContext *cx, HandleObject, HandleObject, bool *)
   1.376 +{
   1.377 +    // Disallow sets of protos on proxies with lazy protos, but no hook.
   1.378 +    // This keeps us away from the footgun of having the first proto set opt
   1.379 +    // you out of having dynamic protos altogether.
   1.380 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL,
   1.381 +                         "incompatible Proxy");
   1.382 +    return false;
   1.383 +}
   1.384 +
   1.385 +bool
   1.386 +BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable)
   1.387 +{
   1.388 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
   1.389 +                         proxy->getClass()->name);
   1.390 +    return false;
   1.391 +}
   1.392 +
   1.393 +bool
   1.394 +BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id)
   1.395 +{
   1.396 +    return true;
   1.397 +}
   1.398 +
   1.399 +bool
   1.400 +BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
   1.401 +                        HandleObject result)
   1.402 +{
   1.403 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   1.404 +
   1.405 +    return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
   1.406 +}
   1.407 +
   1.408 +bool
   1.409 +DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.410 +                                          MutableHandle<PropertyDescriptor> desc)
   1.411 +{
   1.412 +    assertEnteredPolicy(cx, proxy, id, GET | SET);
   1.413 +    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   1.414 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.415 +    return JS_GetPropertyDescriptorById(cx, target, id, desc);
   1.416 +}
   1.417 +
   1.418 +bool
   1.419 +DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.420 +                                             MutableHandle<PropertyDescriptor> desc)
   1.421 +{
   1.422 +    assertEnteredPolicy(cx, proxy, id, GET | SET);
   1.423 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.424 +    return js::GetOwnPropertyDescriptor(cx, target, id, desc);
   1.425 +}
   1.426 +
   1.427 +bool
   1.428 +DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   1.429 +                                   MutableHandle<PropertyDescriptor> desc)
   1.430 +{
   1.431 +    assertEnteredPolicy(cx, proxy, id, SET);
   1.432 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.433 +    RootedValue v(cx, desc.value());
   1.434 +    return CheckDefineProperty(cx, target, id, v, desc.getter(), desc.setter(), desc.attributes()) &&
   1.435 +           JS_DefinePropertyById(cx, target, id, v, desc.getter(), desc.setter(), desc.attributes());
   1.436 +}
   1.437 +
   1.438 +bool
   1.439 +DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
   1.440 +                                        AutoIdVector &props)
   1.441 +{
   1.442 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.443 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.444 +    return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props);
   1.445 +}
   1.446 +
   1.447 +bool
   1.448 +DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.449 +{
   1.450 +    assertEnteredPolicy(cx, proxy, id, SET);
   1.451 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.452 +    return JS_DeletePropertyById2(cx, target, id, bp);
   1.453 +}
   1.454 +
   1.455 +bool
   1.456 +DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
   1.457 +                              AutoIdVector &props)
   1.458 +{
   1.459 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.460 +    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   1.461 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.462 +    return GetPropertyNames(cx, target, 0, &props);
   1.463 +}
   1.464 +
   1.465 +bool
   1.466 +DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
   1.467 +{
   1.468 +    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
   1.469 +    RootedValue target(cx, proxy->as<ProxyObject>().private_());
   1.470 +    return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval());
   1.471 +}
   1.472 +
   1.473 +bool
   1.474 +DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
   1.475 +{
   1.476 +    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
   1.477 +    RootedValue target(cx, proxy->as<ProxyObject>().private_());
   1.478 +    return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address());
   1.479 +}
   1.480 +
   1.481 +bool
   1.482 +DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
   1.483 +                               CallArgs args)
   1.484 +{
   1.485 +    args.setThis(ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
   1.486 +    if (!test(args.thisv())) {
   1.487 +        ReportIncompatible(cx, args);
   1.488 +        return false;
   1.489 +    }
   1.490 +
   1.491 +    return CallNativeImpl(cx, impl, args);
   1.492 +}
   1.493 +
   1.494 +bool
   1.495 +DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
   1.496 +                                bool *bp)
   1.497 +{
   1.498 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   1.499 +    bool b;
   1.500 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.501 +    if (!HasInstance(cx, target, v, &b))
   1.502 +        return false;
   1.503 +    *bp = !!b;
   1.504 +    return true;
   1.505 +}
   1.506 +
   1.507 +bool
   1.508 +DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
   1.509 +{
   1.510 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.511 +    return JSObject::getProto(cx, target, protop);
   1.512 +}
   1.513 +
   1.514 +bool
   1.515 +DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
   1.516 +{
   1.517 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.518 +    return JSObject::setProto(cx, target, proto, bp);
   1.519 +}
   1.520 +
   1.521 +bool
   1.522 +DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
   1.523 +                                  JSContext *cx)
   1.524 +{
   1.525 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.526 +    return ObjectClassIs(target, classValue, cx);
   1.527 +}
   1.528 +
   1.529 +const char *
   1.530 +DirectProxyHandler::className(JSContext *cx, HandleObject proxy)
   1.531 +{
   1.532 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   1.533 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.534 +    return JSObject::className(cx, target);
   1.535 +}
   1.536 +
   1.537 +JSString *
   1.538 +DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy,
   1.539 +                                 unsigned indent)
   1.540 +{
   1.541 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
   1.542 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.543 +    return fun_toStringHelper(cx, target, indent);
   1.544 +}
   1.545 +
   1.546 +bool
   1.547 +DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
   1.548 +                                    RegExpGuard *g)
   1.549 +{
   1.550 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.551 +    return RegExpToShared(cx, target, g);
   1.552 +}
   1.553 +
   1.554 +JSObject *
   1.555 +DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy)
   1.556 +{
   1.557 +    return UncheckedUnwrap(proxy);
   1.558 +}
   1.559 +
   1.560 +DirectProxyHandler::DirectProxyHandler(const void *family)
   1.561 +  : BaseProxyHandler(family)
   1.562 +{
   1.563 +}
   1.564 +
   1.565 +bool
   1.566 +DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.567 +{
   1.568 +    assertEnteredPolicy(cx, proxy, id, GET);
   1.569 +    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   1.570 +    bool found;
   1.571 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.572 +    if (!JS_HasPropertyById(cx, target, id, &found))
   1.573 +        return false;
   1.574 +    *bp = !!found;
   1.575 +    return true;
   1.576 +}
   1.577 +
   1.578 +bool
   1.579 +DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.580 +{
   1.581 +    // Note: Proxy::set needs to invoke hasOwn to determine where the setter
   1.582 +    // lives, so we allow SET operations to invoke us.
   1.583 +    assertEnteredPolicy(cx, proxy, id, GET | SET);
   1.584 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.585 +    Rooted<PropertyDescriptor> desc(cx);
   1.586 +    if (!JS_GetPropertyDescriptorById(cx, target, id, &desc))
   1.587 +        return false;
   1.588 +    *bp = (desc.object() == target);
   1.589 +    return true;
   1.590 +}
   1.591 +
   1.592 +bool
   1.593 +DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.594 +                        HandleId id, MutableHandleValue vp)
   1.595 +{
   1.596 +    assertEnteredPolicy(cx, proxy, id, GET);
   1.597 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.598 +    return JSObject::getGeneric(cx, target, receiver, id, vp);
   1.599 +}
   1.600 +
   1.601 +bool
   1.602 +DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.603 +                        HandleId id, bool strict, MutableHandleValue vp)
   1.604 +{
   1.605 +    assertEnteredPolicy(cx, proxy, id, SET);
   1.606 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.607 +    return JSObject::setGeneric(cx, target, receiver, id, vp, strict);
   1.608 +}
   1.609 +
   1.610 +bool
   1.611 +DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   1.612 +{
   1.613 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.614 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.615 +    return GetPropertyNames(cx, target, JSITER_OWNONLY, &props);
   1.616 +}
   1.617 +
   1.618 +bool
   1.619 +DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
   1.620 +                            MutableHandleValue vp)
   1.621 +{
   1.622 +    assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
   1.623 +    JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
   1.624 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.625 +    return GetIterator(cx, target, flags, vp);
   1.626 +}
   1.627 +
   1.628 +bool
   1.629 +DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
   1.630 +{
   1.631 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.632 +    return JSObject::isExtensible(cx, target, extensible);
   1.633 +}
   1.634 +
   1.635 +bool
   1.636 +DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
   1.637 +{
   1.638 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
   1.639 +    return JSObject::preventExtensions(cx, target);
   1.640 +}
   1.641 +
   1.642 +static bool
   1.643 +GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
   1.644 +                   MutableHandleValue fvalp)
   1.645 +{
   1.646 +    JS_CHECK_RECURSION(cx, return false);
   1.647 +
   1.648 +    return JSObject::getProperty(cx, handler, handler, name, fvalp);
   1.649 +}
   1.650 +
   1.651 +static bool
   1.652 +GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name,
   1.653 +               MutableHandleValue fvalp)
   1.654 +{
   1.655 +    JS_ASSERT(name == cx->names().has ||
   1.656 +              name == cx->names().hasOwn ||
   1.657 +              name == cx->names().get ||
   1.658 +              name == cx->names().set ||
   1.659 +              name == cx->names().keys ||
   1.660 +              name == cx->names().iterate);
   1.661 +
   1.662 +    return JSObject::getProperty(cx, handler, handler, name, fvalp);
   1.663 +}
   1.664 +
   1.665 +static bool
   1.666 +Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv,
   1.667 +     MutableHandleValue rval)
   1.668 +{
   1.669 +    return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
   1.670 +}
   1.671 +
   1.672 +static bool
   1.673 +Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
   1.674 +{
   1.675 +    rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
   1.676 +    JSString *str = ToString<CanGC>(cx, rval);
   1.677 +    if (!str)
   1.678 +        return false;
   1.679 +    rval.setString(str);
   1.680 +    return Trap(cx, handler, fval, 1, rval.address(), rval);
   1.681 +}
   1.682 +
   1.683 +static bool
   1.684 +Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_,
   1.685 +      MutableHandleValue rval)
   1.686 +{
   1.687 +    RootedValue v(cx, v_);
   1.688 +    rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
   1.689 +    JSString *str = ToString<CanGC>(cx, rval);
   1.690 +    if (!str)
   1.691 +        return false;
   1.692 +    rval.setString(str);
   1.693 +    JS::AutoValueArray<2> argv(cx);
   1.694 +    argv[0].set(rval);
   1.695 +    argv[1].set(v);
   1.696 +    return Trap(cx, handler, fval, 2, argv.begin(), rval);
   1.697 +}
   1.698 +
   1.699 +static bool
   1.700 +ParsePropertyDescriptorObject(JSContext *cx, HandleObject obj, const Value &v,
   1.701 +                              MutableHandle<PropertyDescriptor> desc, bool complete = false)
   1.702 +{
   1.703 +    AutoPropDescArrayRooter descs(cx);
   1.704 +    PropDesc *d = descs.append();
   1.705 +    if (!d || !d->initialize(cx, v))
   1.706 +        return false;
   1.707 +    if (complete)
   1.708 +        d->complete();
   1.709 +    desc.object().set(obj);
   1.710 +    desc.value().set(d->hasValue() ? d->value() : UndefinedValue());
   1.711 +    desc.setAttributes(d->attributes());
   1.712 +    desc.setGetter(d->getter());
   1.713 +    desc.setSetter(d->setter());
   1.714 +    return true;
   1.715 +}
   1.716 +
   1.717 +static bool
   1.718 +IndicatePropertyNotFound(MutableHandle<PropertyDescriptor> desc)
   1.719 +{
   1.720 +    desc.object().set(nullptr);
   1.721 +    return true;
   1.722 +}
   1.723 +
   1.724 +static bool
   1.725 +ValueToBool(HandleValue v, bool *bp)
   1.726 +{
   1.727 +    *bp = ToBoolean(v);
   1.728 +    return true;
   1.729 +}
   1.730 +
   1.731 +static bool
   1.732 +ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
   1.733 +{
   1.734 +    JS_ASSERT(props.length() == 0);
   1.735 +
   1.736 +    if (array.isPrimitive())
   1.737 +        return true;
   1.738 +
   1.739 +    RootedObject obj(cx, &array.toObject());
   1.740 +    uint32_t length;
   1.741 +    if (!GetLengthProperty(cx, obj, &length))
   1.742 +        return false;
   1.743 +
   1.744 +    RootedValue v(cx);
   1.745 +    for (uint32_t n = 0; n < length; ++n) {
   1.746 +        if (!CheckForInterrupt(cx))
   1.747 +            return false;
   1.748 +        if (!JSObject::getElement(cx, obj, obj, n, &v))
   1.749 +            return false;
   1.750 +        RootedId id(cx);
   1.751 +        if (!ValueToId<CanGC>(cx, v, &id))
   1.752 +            return false;
   1.753 +        if (!props.append(id))
   1.754 +            return false;
   1.755 +    }
   1.756 +
   1.757 +    return true;
   1.758 +}
   1.759 +
   1.760 +namespace {
   1.761 +
   1.762 +/* Derived class for all scripted indirect proxy handlers. */
   1.763 +class ScriptedIndirectProxyHandler : public BaseProxyHandler
   1.764 +{
   1.765 +  public:
   1.766 +    ScriptedIndirectProxyHandler();
   1.767 +    virtual ~ScriptedIndirectProxyHandler();
   1.768 +
   1.769 +    /* ES5 Harmony fundamental proxy traps. */
   1.770 +    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
   1.771 +    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.772 +                                       MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   1.773 +    virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.774 +                                          MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   1.775 +    virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   1.776 +                                MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
   1.777 +    virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
   1.778 +    virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   1.779 +    virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
   1.780 +
   1.781 +    /* ES5 Harmony derived proxy traps. */
   1.782 +    virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   1.783 +    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
   1.784 +    virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
   1.785 +                     MutableHandleValue vp) MOZ_OVERRIDE;
   1.786 +    virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
   1.787 +                     bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
   1.788 +    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
   1.789 +    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
   1.790 +                         MutableHandleValue vp) MOZ_OVERRIDE;
   1.791 +
   1.792 +    /* Spidermonkey extensions. */
   1.793 +    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
   1.794 +    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
   1.795 +    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
   1.796 +    virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
   1.797 +                            CallArgs args) MOZ_OVERRIDE;
   1.798 +    virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE;
   1.799 +    virtual bool isScripted() MOZ_OVERRIDE { return true; }
   1.800 +
   1.801 +    static ScriptedIndirectProxyHandler singleton;
   1.802 +};
   1.803 +
   1.804 +/*
   1.805 + * Old-style indirect proxies allow callers to specify distinct scripted
   1.806 + * [[Call]] and [[Construct]] traps. We use an intermediate object so that we
   1.807 + * can stash this information in a single reserved slot on the proxy object.
   1.808 + *
   1.809 + * Note - Currently this is slightly unnecesary, because we actually have 2
   1.810 + * extra slots, neither of which are used for ScriptedIndirectProxy. But we're
   1.811 + * eventually moving towards eliminating one of those slots, and so we don't
   1.812 + * want to add a dependency here.
   1.813 + */
   1.814 +static Class CallConstructHolder = {
   1.815 +    "CallConstructHolder",
   1.816 +    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
   1.817 +};
   1.818 +
   1.819 +} /* anonymous namespace */
   1.820 +
   1.821 +// This variable exists solely to provide a unique address for use as an identifier.
   1.822 +static const char sScriptedIndirectProxyHandlerFamily = 0;
   1.823 +
   1.824 +ScriptedIndirectProxyHandler::ScriptedIndirectProxyHandler()
   1.825 +        : BaseProxyHandler(&sScriptedIndirectProxyHandlerFamily)
   1.826 +{
   1.827 +}
   1.828 +
   1.829 +ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler()
   1.830 +{
   1.831 +}
   1.832 +
   1.833 +bool
   1.834 +ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
   1.835 +{
   1.836 +    // Scripted indirect proxies don't support extensibility changes.
   1.837 +    *extensible = true;
   1.838 +    return true;
   1.839 +}
   1.840 +
   1.841 +bool
   1.842 +ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
   1.843 +{
   1.844 +    // See above.
   1.845 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
   1.846 +    return false;
   1.847 +}
   1.848 +
   1.849 +static bool
   1.850 +ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v)
   1.851 +{
   1.852 +    if (v.isPrimitive()) {
   1.853 +        JSAutoByteString bytes;
   1.854 +        if (AtomToPrintableString(cx, atom, &bytes)) {
   1.855 +            RootedValue val(cx, ObjectOrNullValue(proxy));
   1.856 +            js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
   1.857 +                                 JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr());
   1.858 +        }
   1.859 +        return false;
   1.860 +    }
   1.861 +    return true;
   1.862 +}
   1.863 +
   1.864 +static JSObject *
   1.865 +GetIndirectProxyHandlerObject(JSObject *proxy)
   1.866 +{
   1.867 +    return proxy->as<ProxyObject>().private_().toObjectOrNull();
   1.868 +}
   1.869 +
   1.870 +bool
   1.871 +ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.872 +                                                    MutableHandle<PropertyDescriptor> desc)
   1.873 +{
   1.874 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.875 +    RootedValue fval(cx), value(cx);
   1.876 +    return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) &&
   1.877 +           Trap1(cx, handler, fval, id, &value) &&
   1.878 +           ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
   1.879 +            (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
   1.880 +             ParsePropertyDescriptorObject(cx, proxy, value, desc)));
   1.881 +}
   1.882 +
   1.883 +bool
   1.884 +ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
   1.885 +                                                       MutableHandle<PropertyDescriptor> desc)
   1.886 +{
   1.887 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.888 +    RootedValue fval(cx), value(cx);
   1.889 +    return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) &&
   1.890 +           Trap1(cx, handler, fval, id, &value) &&
   1.891 +           ((value.get().isUndefined() && IndicatePropertyNotFound(desc)) ||
   1.892 +            (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
   1.893 +             ParsePropertyDescriptorObject(cx, proxy, value, desc)));
   1.894 +}
   1.895 +
   1.896 +bool
   1.897 +ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
   1.898 +                                             MutableHandle<PropertyDescriptor> desc)
   1.899 +{
   1.900 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.901 +    RootedValue fval(cx), value(cx);
   1.902 +    return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) &&
   1.903 +           NewPropertyDescriptorObject(cx, desc, &value) &&
   1.904 +           Trap2(cx, handler, fval, id, value, &value);
   1.905 +}
   1.906 +
   1.907 +bool
   1.908 +ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
   1.909 +                                                  AutoIdVector &props)
   1.910 +{
   1.911 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.912 +    RootedValue fval(cx), value(cx);
   1.913 +    return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) &&
   1.914 +           Trap(cx, handler, fval, 0, nullptr, &value) &&
   1.915 +           ArrayToIdVector(cx, value, props);
   1.916 +}
   1.917 +
   1.918 +bool
   1.919 +ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.920 +{
   1.921 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.922 +    RootedValue fval(cx), value(cx);
   1.923 +    return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) &&
   1.924 +           Trap1(cx, handler, fval, id, &value) &&
   1.925 +           ValueToBool(value, bp);
   1.926 +}
   1.927 +
   1.928 +bool
   1.929 +ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
   1.930 +{
   1.931 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.932 +    RootedValue fval(cx), value(cx);
   1.933 +    return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) &&
   1.934 +           Trap(cx, handler, fval, 0, nullptr, &value) &&
   1.935 +           ArrayToIdVector(cx, value, props);
   1.936 +}
   1.937 +
   1.938 +bool
   1.939 +ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.940 +{
   1.941 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.942 +    RootedValue fval(cx), value(cx);
   1.943 +    if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
   1.944 +        return false;
   1.945 +    if (!js_IsCallable(fval))
   1.946 +        return BaseProxyHandler::has(cx, proxy, id, bp);
   1.947 +    return Trap1(cx, handler, fval, id, &value) &&
   1.948 +           ValueToBool(value, bp);
   1.949 +}
   1.950 +
   1.951 +bool
   1.952 +ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
   1.953 +{
   1.954 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.955 +    RootedValue fval(cx), value(cx);
   1.956 +    if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
   1.957 +        return false;
   1.958 +    if (!js_IsCallable(fval))
   1.959 +        return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
   1.960 +    return Trap1(cx, handler, fval, id, &value) &&
   1.961 +           ValueToBool(value, bp);
   1.962 +}
   1.963 +
   1.964 +bool
   1.965 +ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.966 +                                  HandleId id, MutableHandleValue vp)
   1.967 +{
   1.968 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.969 +    RootedValue idv(cx, IdToValue(id));
   1.970 +    JSString *str = ToString<CanGC>(cx, idv);
   1.971 +    if (!str)
   1.972 +        return false;
   1.973 +    RootedValue value(cx, StringValue(str));
   1.974 +    JS::AutoValueArray<2> argv(cx);
   1.975 +    argv[0].setObjectOrNull(receiver);
   1.976 +    argv[1].set(value);
   1.977 +    RootedValue fval(cx);
   1.978 +    if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
   1.979 +        return false;
   1.980 +    if (!js_IsCallable(fval))
   1.981 +        return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
   1.982 +    return Trap(cx, handler, fval, 2, argv.begin(), vp);
   1.983 +}
   1.984 +
   1.985 +bool
   1.986 +ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
   1.987 +                                  HandleId id, bool strict, MutableHandleValue vp)
   1.988 +{
   1.989 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
   1.990 +    RootedValue idv(cx, IdToValue(id));
   1.991 +    JSString *str = ToString<CanGC>(cx, idv);
   1.992 +    if (!str)
   1.993 +        return false;
   1.994 +    RootedValue value(cx, StringValue(str));
   1.995 +    JS::AutoValueArray<3> argv(cx);
   1.996 +    argv[0].setObjectOrNull(receiver);
   1.997 +    argv[1].set(value);
   1.998 +    argv[2].set(vp);
   1.999 +    RootedValue fval(cx);
  1.1000 +    if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
  1.1001 +        return false;
  1.1002 +    if (!js_IsCallable(fval))
  1.1003 +        return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
  1.1004 +    return Trap(cx, handler, fval, 3, argv.begin(), &value);
  1.1005 +}
  1.1006 +
  1.1007 +bool
  1.1008 +ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.1009 +{
  1.1010 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
  1.1011 +    RootedValue value(cx);
  1.1012 +    if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
  1.1013 +        return false;
  1.1014 +    if (!js_IsCallable(value))
  1.1015 +        return BaseProxyHandler::keys(cx, proxy, props);
  1.1016 +    return Trap(cx, handler, value, 0, nullptr, &value) &&
  1.1017 +           ArrayToIdVector(cx, value, props);
  1.1018 +}
  1.1019 +
  1.1020 +bool
  1.1021 +ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  1.1022 +                                      MutableHandleValue vp)
  1.1023 +{
  1.1024 +    RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
  1.1025 +    RootedValue value(cx);
  1.1026 +    if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
  1.1027 +        return false;
  1.1028 +    if (!js_IsCallable(value))
  1.1029 +        return BaseProxyHandler::iterate(cx, proxy, flags, vp);
  1.1030 +    return Trap(cx, handler, value, 0, nullptr, vp) &&
  1.1031 +           ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp);
  1.1032 +}
  1.1033 +
  1.1034 +bool
  1.1035 +ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.1036 +{
  1.1037 +    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
  1.1038 +    RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
  1.1039 +    JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
  1.1040 +    RootedValue call(cx, ccHolder->getReservedSlot(0));
  1.1041 +    JS_ASSERT(call.isObject() && call.toObject().isCallable());
  1.1042 +    return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval());
  1.1043 +}
  1.1044 +
  1.1045 +bool
  1.1046 +ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.1047 +{
  1.1048 +    assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
  1.1049 +    RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
  1.1050 +    JS_ASSERT(ccHolder->getClass() == &CallConstructHolder);
  1.1051 +    RootedValue construct(cx, ccHolder->getReservedSlot(1));
  1.1052 +    JS_ASSERT(construct.isObject() && construct.toObject().isCallable());
  1.1053 +    return InvokeConstructor(cx, construct, args.length(), args.array(),
  1.1054 +                             args.rval().address());
  1.1055 +}
  1.1056 +
  1.1057 +bool
  1.1058 +ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
  1.1059 +                                         CallArgs args)
  1.1060 +{
  1.1061 +    return BaseProxyHandler::nativeCall(cx, test, impl, args);
  1.1062 +}
  1.1063 +
  1.1064 +JSString *
  1.1065 +ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
  1.1066 +{
  1.1067 +    assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
  1.1068 +    if (!proxy->isCallable()) {
  1.1069 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
  1.1070 +                             JSMSG_INCOMPATIBLE_PROTO,
  1.1071 +                             js_Function_str, js_toString_str,
  1.1072 +                             "object");
  1.1073 +        return nullptr;
  1.1074 +    }
  1.1075 +    RootedObject obj(cx, &proxy->as<ProxyObject>().extra(0).toObject().getReservedSlot(0).toObject());
  1.1076 +    return fun_toStringHelper(cx, obj, indent);
  1.1077 +}
  1.1078 +
  1.1079 +ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton;
  1.1080 +
  1.1081 +/* Derived class for all scripted direct proxy handlers. */
  1.1082 +class ScriptedDirectProxyHandler : public DirectProxyHandler {
  1.1083 +  public:
  1.1084 +    ScriptedDirectProxyHandler();
  1.1085 +    virtual ~ScriptedDirectProxyHandler();
  1.1086 +
  1.1087 +    /* ES5 Harmony fundamental proxy traps. */
  1.1088 +    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
  1.1089 +    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.1090 +                                       MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1.1091 +    virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.1092 +                                          MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1.1093 +    virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  1.1094 +                                MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE;
  1.1095 +    virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
  1.1096 +    virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1.1097 +    virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
  1.1098 +
  1.1099 +    /* ES5 Harmony derived proxy traps. */
  1.1100 +    virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1.1101 +    virtual bool hasOwn(JSContext *cx,HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
  1.1102 +    virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1.1103 +                     MutableHandleValue vp) MOZ_OVERRIDE;
  1.1104 +    virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1.1105 +                     bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
  1.1106 +    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
  1.1107 +    virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  1.1108 +                         MutableHandleValue vp) MOZ_OVERRIDE;
  1.1109 +
  1.1110 +    /* ES6 Harmony traps */
  1.1111 +    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
  1.1112 +
  1.1113 +    /* Spidermonkey extensions. */
  1.1114 +    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
  1.1115 +    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
  1.1116 +    virtual bool isScripted() MOZ_OVERRIDE { return true; }
  1.1117 +
  1.1118 +    static ScriptedDirectProxyHandler singleton;
  1.1119 +};
  1.1120 +
  1.1121 +// This variable exists solely to provide a unique address for use as an identifier.
  1.1122 +static const char sScriptedDirectProxyHandlerFamily = 0;
  1.1123 +
  1.1124 +// Aux.2 FromGenericPropertyDescriptor(Desc)
  1.1125 +static bool
  1.1126 +FromGenericPropertyDescriptor(JSContext *cx, PropDesc *desc, MutableHandleValue rval)
  1.1127 +{
  1.1128 +    // Aux.2 step 1
  1.1129 +    if (desc->isUndefined()) {
  1.1130 +        rval.setUndefined();
  1.1131 +        return true;
  1.1132 +    }
  1.1133 +
  1.1134 +    // steps 3-9
  1.1135 +    if (!desc->makeObject(cx))
  1.1136 +        return false;
  1.1137 +    rval.set(desc->pd());
  1.1138 +    return true;
  1.1139 +}
  1.1140 +
  1.1141 +/*
  1.1142 + * Aux.3 NormalizePropertyDescriptor(Attributes)
  1.1143 + *
  1.1144 + * NOTE: to minimize code duplication, the code for this function is shared with
  1.1145 + * that for Aux.4 NormalizeAndCompletePropertyDescriptor (see below). The
  1.1146 + * argument complete is used to distinguish between the two.
  1.1147 + */
  1.1148 +static bool
  1.1149 +NormalizePropertyDescriptor(JSContext *cx, MutableHandleValue vp, bool complete = false)
  1.1150 +{
  1.1151 +    // Aux.4 step 1
  1.1152 +    if (complete && vp.isUndefined())
  1.1153 +        return true;
  1.1154 +
  1.1155 +    // Aux.3 steps 1-2 / Aux.4 steps 2-3
  1.1156 +    AutoPropDescArrayRooter descs(cx);
  1.1157 +    PropDesc *desc = descs.append();
  1.1158 +    if (!desc || !desc->initialize(cx, vp.get()))
  1.1159 +        return false;
  1.1160 +    if (complete)
  1.1161 +        desc->complete();
  1.1162 +    JS_ASSERT(!vp.isPrimitive()); // due to desc->initialize
  1.1163 +    RootedObject attributes(cx, &vp.toObject());
  1.1164 +
  1.1165 +    /*
  1.1166 +     * Aux.3 step 3 / Aux.4 step 4
  1.1167 +     *
  1.1168 +     * NOTE: Aux.4 step 4 actually specifies FromPropertyDescriptor here.
  1.1169 +     * However, the way FromPropertyDescriptor is implemented (PropDesc::
  1.1170 +     * makeObject) is actually closer to FromGenericPropertyDescriptor,
  1.1171 +     * and is in fact used to implement the latter, so we might as well call it
  1.1172 +     * directly.
  1.1173 +     */
  1.1174 +    if (!FromGenericPropertyDescriptor(cx, desc, vp))
  1.1175 +        return false;
  1.1176 +    if (vp.isUndefined())
  1.1177 +        return true;
  1.1178 +    RootedObject descObj(cx, &vp.toObject());
  1.1179 +
  1.1180 +    // Aux.3 steps 4-5 / Aux.4 steps 5-6
  1.1181 +    AutoIdVector props(cx);
  1.1182 +    if (!GetPropertyNames(cx, attributes, 0, &props))
  1.1183 +        return false;
  1.1184 +    size_t n = props.length();
  1.1185 +    for (size_t i = 0; i < n; ++i) {
  1.1186 +        RootedId id(cx, props[i]);
  1.1187 +        if (JSID_IS_ATOM(id)) {
  1.1188 +            JSAtom *atom = JSID_TO_ATOM(id);
  1.1189 +            const JSAtomState &atomState = cx->names();
  1.1190 +            if (atom == atomState.value || atom == atomState.writable ||
  1.1191 +                atom == atomState.get || atom == atomState.set ||
  1.1192 +                atom == atomState.enumerable || atom == atomState.configurable)
  1.1193 +            {
  1.1194 +                continue;
  1.1195 +            }
  1.1196 +        }
  1.1197 +
  1.1198 +        RootedValue v(cx);
  1.1199 +        if (!JSObject::getGeneric(cx, descObj, attributes, id, &v))
  1.1200 +            return false;
  1.1201 +        if (!JSObject::defineGeneric(cx, descObj, id, v, nullptr, nullptr, JSPROP_ENUMERATE))
  1.1202 +            return false;
  1.1203 +    }
  1.1204 +    return true;
  1.1205 +}
  1.1206 +
  1.1207 +// Aux.4 NormalizeAndCompletePropertyDescriptor(Attributes)
  1.1208 +static inline bool
  1.1209 +NormalizeAndCompletePropertyDescriptor(JSContext *cx, MutableHandleValue vp)
  1.1210 +{
  1.1211 +    return NormalizePropertyDescriptor(cx, vp, true);
  1.1212 +}
  1.1213 +
  1.1214 +static inline bool
  1.1215 +IsDataDescriptor(const PropertyDescriptor &desc)
  1.1216 +{
  1.1217 +    return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER));
  1.1218 +}
  1.1219 +
  1.1220 +static inline bool
  1.1221 +IsAccessorDescriptor(const PropertyDescriptor &desc)
  1.1222 +{
  1.1223 +    return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER);
  1.1224 +}
  1.1225 +
  1.1226 +// Aux.5 ValidateProperty(O, P, Desc)
  1.1227 +static bool
  1.1228 +ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, PropDesc *desc, bool *bp)
  1.1229 +{
  1.1230 +    // step 1
  1.1231 +    Rooted<PropertyDescriptor> current(cx);
  1.1232 +    if (!GetOwnPropertyDescriptor(cx, obj, id, &current))
  1.1233 +        return false;
  1.1234 +
  1.1235 +    /*
  1.1236 +     * steps 2-4 are redundant since ValidateProperty is never called unless
  1.1237 +     * target.[[HasOwn]](P) is true
  1.1238 +     */
  1.1239 +    JS_ASSERT(current.object());
  1.1240 +
  1.1241 +    // step 5
  1.1242 +    if (!desc->hasValue() && !desc->hasWritable() && !desc->hasGet() && !desc->hasSet() &&
  1.1243 +        !desc->hasEnumerable() && !desc->hasConfigurable())
  1.1244 +    {
  1.1245 +        *bp = true;
  1.1246 +        return true;
  1.1247 +    }
  1.1248 +
  1.1249 +    // step 6
  1.1250 +    if ((!desc->hasWritable() || desc->writable() == !current.isReadonly()) &&
  1.1251 +        (!desc->hasGet() || desc->getter() == current.getter()) &&
  1.1252 +        (!desc->hasSet() || desc->setter() == current.setter()) &&
  1.1253 +        (!desc->hasEnumerable() || desc->enumerable() == current.isEnumerable()) &&
  1.1254 +        (!desc->hasConfigurable() || desc->configurable() == !current.isPermanent()))
  1.1255 +    {
  1.1256 +        if (!desc->hasValue()) {
  1.1257 +            *bp = true;
  1.1258 +            return true;
  1.1259 +        }
  1.1260 +        bool same = false;
  1.1261 +        if (!SameValue(cx, desc->value(), current.value(), &same))
  1.1262 +            return false;
  1.1263 +        if (same) {
  1.1264 +            *bp = true;
  1.1265 +            return true;
  1.1266 +        }
  1.1267 +    }
  1.1268 +
  1.1269 +    // step 7
  1.1270 +    if (current.isPermanent()) {
  1.1271 +        if (desc->hasConfigurable() && desc->configurable()) {
  1.1272 +            *bp = false;
  1.1273 +            return true;
  1.1274 +        }
  1.1275 +
  1.1276 +        if (desc->hasEnumerable() &&
  1.1277 +            desc->enumerable() != current.isEnumerable())
  1.1278 +        {
  1.1279 +            *bp = false;
  1.1280 +            return true;
  1.1281 +        }
  1.1282 +    }
  1.1283 +
  1.1284 +    // step 8
  1.1285 +    if (desc->isGenericDescriptor()) {
  1.1286 +        *bp = true;
  1.1287 +        return true;
  1.1288 +    }
  1.1289 +
  1.1290 +    // step 9
  1.1291 +    if (IsDataDescriptor(current) != desc->isDataDescriptor()) {
  1.1292 +        *bp = !current.isPermanent();
  1.1293 +        return true;
  1.1294 +    }
  1.1295 +
  1.1296 +    // step 10
  1.1297 +    if (IsDataDescriptor(current)) {
  1.1298 +        JS_ASSERT(desc->isDataDescriptor()); // by step 9
  1.1299 +        if (current.isPermanent() && current.isReadonly()) {
  1.1300 +            if (desc->hasWritable() && desc->writable()) {
  1.1301 +                *bp = false;
  1.1302 +                return true;
  1.1303 +            }
  1.1304 +
  1.1305 +            if (desc->hasValue()) {
  1.1306 +                bool same;
  1.1307 +                if (!SameValue(cx, desc->value(), current.value(), &same))
  1.1308 +                    return false;
  1.1309 +                if (!same) {
  1.1310 +                    *bp = false;
  1.1311 +                    return true;
  1.1312 +                }
  1.1313 +            }
  1.1314 +        }
  1.1315 +
  1.1316 +        *bp = true;
  1.1317 +        return true;
  1.1318 +    }
  1.1319 +
  1.1320 +    // steps 11-12
  1.1321 +    JS_ASSERT(IsAccessorDescriptor(current)); // by step 10
  1.1322 +    JS_ASSERT(desc->isAccessorDescriptor()); // by step 9
  1.1323 +    *bp = (!current.isPermanent() ||
  1.1324 +           ((!desc->hasSet() || desc->setter() == current.setter()) &&
  1.1325 +            (!desc->hasGet() || desc->getter() == current.getter())));
  1.1326 +    return true;
  1.1327 +}
  1.1328 +
  1.1329 +// Aux.6 IsSealed(O, P)
  1.1330 +static bool
  1.1331 +IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp)
  1.1332 +{
  1.1333 +    // step 1
  1.1334 +    Rooted<PropertyDescriptor> desc(cx);
  1.1335 +    if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
  1.1336 +        return false;
  1.1337 +
  1.1338 +    // steps 2-3
  1.1339 +    *bp = desc.object() && desc.isPermanent();
  1.1340 +    return true;
  1.1341 +}
  1.1342 +
  1.1343 +static bool
  1.1344 +HasOwn(JSContext *cx, HandleObject obj, HandleId id, bool *bp)
  1.1345 +{
  1.1346 +    Rooted<PropertyDescriptor> desc(cx);
  1.1347 +    if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
  1.1348 +        return false;
  1.1349 +    *bp = (desc.object() == obj);
  1.1350 +    return true;
  1.1351 +}
  1.1352 +
  1.1353 +static bool
  1.1354 +IdToExposableValue(JSContext *cx, HandleId id, MutableHandleValue value)
  1.1355 +{
  1.1356 +    value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
  1.1357 +    JSString *name = ToString<CanGC>(cx, value);
  1.1358 +    if (!name)
  1.1359 +        return false;
  1.1360 +    value.set(StringValue(name));
  1.1361 +    return true;
  1.1362 +}
  1.1363 +
  1.1364 +// Get the [[ProxyHandler]] of a scripted direct proxy.
  1.1365 +//
  1.1366 +// NB: This *must* stay synched with proxy().
  1.1367 +static JSObject *
  1.1368 +GetDirectProxyHandlerObject(JSObject *proxy)
  1.1369 +{
  1.1370 +    JS_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
  1.1371 +    return proxy->as<ProxyObject>().extra(0).toObjectOrNull();
  1.1372 +}
  1.1373 +
  1.1374 +// TrapGetOwnProperty(O, P)
  1.1375 +static bool
  1.1376 +TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue rval)
  1.1377 +{
  1.1378 +    // step 1
  1.1379 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1380 +
  1.1381 +    // step 2
  1.1382 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1383 +
  1.1384 +    // step 3
  1.1385 +    RootedValue trap(cx);
  1.1386 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyDescriptor, &trap))
  1.1387 +        return false;
  1.1388 +
  1.1389 +    // step 4
  1.1390 +    if (trap.isUndefined()) {
  1.1391 +        Rooted<PropertyDescriptor> desc(cx);
  1.1392 +        if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1.1393 +            return false;
  1.1394 +        return NewPropertyDescriptorObject(cx, desc, rval);
  1.1395 +    }
  1.1396 +
  1.1397 +    // step 5
  1.1398 +    RootedValue value(cx);
  1.1399 +    if (!IdToExposableValue(cx, id, &value))
  1.1400 +        return false;
  1.1401 +    Value argv[] = {
  1.1402 +        ObjectValue(*target),
  1.1403 +        value
  1.1404 +    };
  1.1405 +    RootedValue trapResult(cx);
  1.1406 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1407 +        return false;
  1.1408 +
  1.1409 +    // step 6
  1.1410 +    if (!NormalizeAndCompletePropertyDescriptor(cx, &trapResult))
  1.1411 +        return false;
  1.1412 +
  1.1413 +    // step 7
  1.1414 +    if (trapResult.isUndefined()) {
  1.1415 +        bool sealed;
  1.1416 +        if (!IsSealed(cx, target, id, &sealed))
  1.1417 +            return false;
  1.1418 +        if (sealed) {
  1.1419 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  1.1420 +            return false;
  1.1421 +        }
  1.1422 +
  1.1423 +        bool extensible;
  1.1424 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.1425 +            return false;
  1.1426 +        if (!extensible) {
  1.1427 +            bool found;
  1.1428 +            if (!HasOwn(cx, target, id, &found))
  1.1429 +                return false;
  1.1430 +            if (found) {
  1.1431 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1.1432 +                return false;
  1.1433 +            }
  1.1434 +        }
  1.1435 +
  1.1436 +        rval.set(UndefinedValue());
  1.1437 +        return true;
  1.1438 +    }
  1.1439 +
  1.1440 +    // step 8
  1.1441 +    bool isFixed;
  1.1442 +    if (!HasOwn(cx, target, id, &isFixed))
  1.1443 +        return false;
  1.1444 +
  1.1445 +    // step 9
  1.1446 +    bool extensible;
  1.1447 +    if (!JSObject::isExtensible(cx, target, &extensible))
  1.1448 +        return false;
  1.1449 +    if (!extensible && !isFixed) {
  1.1450 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  1.1451 +        return false;
  1.1452 +    }
  1.1453 +
  1.1454 +    AutoPropDescArrayRooter descs(cx);
  1.1455 +    PropDesc *desc = descs.append();
  1.1456 +    if (!desc || !desc->initialize(cx, trapResult))
  1.1457 +        return false;
  1.1458 +
  1.1459 +    /* step 10 */
  1.1460 +    if (isFixed) {
  1.1461 +        bool valid;
  1.1462 +        if (!ValidateProperty(cx, target, id, desc, &valid))
  1.1463 +            return false;
  1.1464 +
  1.1465 +        if (!valid) {
  1.1466 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID);
  1.1467 +            return false;
  1.1468 +        }
  1.1469 +    }
  1.1470 +
  1.1471 +    // step 11
  1.1472 +    if (!desc->configurable() && !isFixed) {
  1.1473 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
  1.1474 +        return false;
  1.1475 +    }
  1.1476 +
  1.1477 +    // step 12
  1.1478 +    rval.set(trapResult);
  1.1479 +    return true;
  1.1480 +}
  1.1481 +
  1.1482 +// TrapDefineOwnProperty(O, P, DescObj, Throw)
  1.1483 +static bool
  1.1484 +TrapDefineOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
  1.1485 +{
  1.1486 +    // step 1
  1.1487 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1488 +
  1.1489 +    // step 2
  1.1490 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1491 +
  1.1492 +    // step 3
  1.1493 +    RootedValue trap(cx);
  1.1494 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap))
  1.1495 +        return false;
  1.1496 +
  1.1497 +    // step 4
  1.1498 +    if (trap.isUndefined()) {
  1.1499 +        Rooted<PropertyDescriptor> desc(cx);
  1.1500 +        if (!ParsePropertyDescriptorObject(cx, proxy, vp, &desc))
  1.1501 +            return false;
  1.1502 +        return JS_DefinePropertyById(cx, target, id, desc.value(), desc.getter(), desc.setter(),
  1.1503 +                                     desc.attributes());
  1.1504 +    }
  1.1505 +
  1.1506 +    // step 5
  1.1507 +    RootedValue normalizedDesc(cx, vp);
  1.1508 +    if (!NormalizePropertyDescriptor(cx, &normalizedDesc))
  1.1509 +        return false;
  1.1510 +
  1.1511 +    // step 6
  1.1512 +    RootedValue value(cx);
  1.1513 +    if (!IdToExposableValue(cx, id, &value))
  1.1514 +        return false;
  1.1515 +    Value argv[] = {
  1.1516 +        ObjectValue(*target),
  1.1517 +        value,
  1.1518 +        normalizedDesc
  1.1519 +    };
  1.1520 +    RootedValue trapResult(cx);
  1.1521 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1522 +        return false;
  1.1523 +
  1.1524 +    // steps 7-8
  1.1525 +    if (ToBoolean(trapResult)) {
  1.1526 +        bool isFixed;
  1.1527 +        if (!HasOwn(cx, target, id, &isFixed))
  1.1528 +            return false;
  1.1529 +
  1.1530 +        bool extensible;
  1.1531 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.1532 +            return false;
  1.1533 +        if (!extensible && !isFixed) {
  1.1534 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
  1.1535 +            return false;
  1.1536 +        }
  1.1537 +
  1.1538 +        AutoPropDescArrayRooter descs(cx);
  1.1539 +        PropDesc *desc = descs.append();
  1.1540 +        if (!desc || !desc->initialize(cx, normalizedDesc))
  1.1541 +            return false;
  1.1542 +
  1.1543 +        if (isFixed) {
  1.1544 +            bool valid;
  1.1545 +            if (!ValidateProperty(cx, target, id, desc, &valid))
  1.1546 +                return false;
  1.1547 +            if (!valid) {
  1.1548 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
  1.1549 +                return false;
  1.1550 +            }
  1.1551 +        }
  1.1552 +
  1.1553 +        if (!desc->configurable() && !isFixed) {
  1.1554 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
  1.1555 +            return false;
  1.1556 +        }
  1.1557 +
  1.1558 +        vp.set(BooleanValue(true));
  1.1559 +        return true;
  1.1560 +    }
  1.1561 +
  1.1562 +    // step 9
  1.1563 +    // FIXME: API does not include a Throw parameter
  1.1564 +    vp.set(BooleanValue(false));
  1.1565 +    return true;
  1.1566 +}
  1.1567 +
  1.1568 +static inline void
  1.1569 +ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom)
  1.1570 +{
  1.1571 +    RootedValue v(cx, ObjectOrNullValue(proxy));
  1.1572 +    JSAutoByteString bytes;
  1.1573 +    if (!AtomToPrintableString(cx, atom, &bytes))
  1.1574 +        return;
  1.1575 +    js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v,
  1.1576 +                         js::NullPtr(), bytes.ptr());
  1.1577 +}
  1.1578 +
  1.1579 +// This function is shared between getOwnPropertyNames, enumerate, and keys
  1.1580 +static bool
  1.1581 +ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v,
  1.1582 +                AutoIdVector &props, unsigned flags, JSAtom *trapName_)
  1.1583 +{
  1.1584 +    JS_ASSERT(v.isObject());
  1.1585 +    RootedObject array(cx, &v.toObject());
  1.1586 +    RootedAtom trapName(cx, trapName_);
  1.1587 +
  1.1588 +    // steps g-h
  1.1589 +    uint32_t n;
  1.1590 +    if (!GetLengthProperty(cx, array, &n))
  1.1591 +        return false;
  1.1592 +
  1.1593 +    // steps i-k
  1.1594 +    for (uint32_t i = 0; i < n; ++i) {
  1.1595 +        // step i
  1.1596 +        RootedValue v(cx);
  1.1597 +        if (!JSObject::getElement(cx, array, array, i, &v))
  1.1598 +            return false;
  1.1599 +
  1.1600 +        // step ii
  1.1601 +        RootedId id(cx);
  1.1602 +        if (!ValueToId<CanGC>(cx, v, &id))
  1.1603 +            return false;
  1.1604 +
  1.1605 +        // step iii
  1.1606 +        for (uint32_t j = 0; j < i; ++j) {
  1.1607 +            if (props[j] == id) {
  1.1608 +                ReportInvalidTrapResult(cx, proxy, trapName);
  1.1609 +                return false;
  1.1610 +            }
  1.1611 +        }
  1.1612 +
  1.1613 +        // step iv
  1.1614 +        bool isFixed;
  1.1615 +        if (!HasOwn(cx, target, id, &isFixed))
  1.1616 +            return false;
  1.1617 +
  1.1618 +        // step v
  1.1619 +        bool extensible;
  1.1620 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.1621 +            return false;
  1.1622 +        if (!extensible && !isFixed) {
  1.1623 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  1.1624 +            return false;
  1.1625 +        }
  1.1626 +
  1.1627 +        // step vi
  1.1628 +        if (!props.append(id))
  1.1629 +            return false;
  1.1630 +    }
  1.1631 +
  1.1632 +    // step l
  1.1633 +    AutoIdVector ownProps(cx);
  1.1634 +    if (!GetPropertyNames(cx, target, flags, &ownProps))
  1.1635 +        return false;
  1.1636 +
  1.1637 +    // step m
  1.1638 +    for (size_t i = 0; i < ownProps.length(); ++i) {
  1.1639 +        RootedId id(cx, ownProps[i]);
  1.1640 +
  1.1641 +        bool found = false;
  1.1642 +        for (size_t j = 0; j < props.length(); ++j) {
  1.1643 +            if (props[j] == id) {
  1.1644 +                found = true;
  1.1645 +               break;
  1.1646 +            }
  1.1647 +        }
  1.1648 +        if (found)
  1.1649 +            continue;
  1.1650 +
  1.1651 +        // step i
  1.1652 +        bool sealed;
  1.1653 +        if (!IsSealed(cx, target, id, &sealed))
  1.1654 +            return false;
  1.1655 +        if (sealed) {
  1.1656 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
  1.1657 +            return false;
  1.1658 +        }
  1.1659 +
  1.1660 +        // step ii
  1.1661 +        bool isFixed;
  1.1662 +        if (!HasOwn(cx, target, id, &isFixed))
  1.1663 +            return false;
  1.1664 +
  1.1665 +        // step iii
  1.1666 +        bool extensible;
  1.1667 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.1668 +            return false;
  1.1669 +        if (!extensible && isFixed) {
  1.1670 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1.1671 +            return false;
  1.1672 +        }
  1.1673 +    }
  1.1674 +
  1.1675 +    // step n
  1.1676 +    return true;
  1.1677 +}
  1.1678 +
  1.1679 +ScriptedDirectProxyHandler::ScriptedDirectProxyHandler()
  1.1680 +        : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily)
  1.1681 +{
  1.1682 +}
  1.1683 +
  1.1684 +ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler()
  1.1685 +{
  1.1686 +}
  1.1687 +
  1.1688 +bool
  1.1689 +ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
  1.1690 +{
  1.1691 +    // step a
  1.1692 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1693 +
  1.1694 +    // step b
  1.1695 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1696 +
  1.1697 +    // step c
  1.1698 +    RootedValue trap(cx);
  1.1699 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap))
  1.1700 +        return false;
  1.1701 +
  1.1702 +    // step d
  1.1703 +    if (trap.isUndefined())
  1.1704 +        return DirectProxyHandler::preventExtensions(cx, proxy);
  1.1705 +
  1.1706 +    // step e
  1.1707 +    Value argv[] = {
  1.1708 +        ObjectValue(*target)
  1.1709 +    };
  1.1710 +    RootedValue trapResult(cx);
  1.1711 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1712 +        return false;
  1.1713 +
  1.1714 +    // step f
  1.1715 +    bool success = ToBoolean(trapResult);
  1.1716 +    if (success) {
  1.1717 +        // step g
  1.1718 +        bool extensible;
  1.1719 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.1720 +            return false;
  1.1721 +        if (extensible) {
  1.1722 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
  1.1723 +            return false;
  1.1724 +        }
  1.1725 +        return true;
  1.1726 +    }
  1.1727 +
  1.1728 +    // step h
  1.1729 +    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
  1.1730 +    return false;
  1.1731 +}
  1.1732 +
  1.1733 +// FIXME: Move to Proxy::getPropertyDescriptor once ScriptedIndirectProxy is removed
  1.1734 +bool
  1.1735 +ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.1736 +                                                  MutableHandle<PropertyDescriptor> desc)
  1.1737 +{
  1.1738 +    JS_CHECK_RECURSION(cx, return false);
  1.1739 +
  1.1740 +    if (!GetOwnPropertyDescriptor(cx, proxy, id, desc))
  1.1741 +        return false;
  1.1742 +    if (desc.object())
  1.1743 +        return true;
  1.1744 +    RootedObject proto(cx);
  1.1745 +    if (!JSObject::getProto(cx, proxy, &proto))
  1.1746 +        return false;
  1.1747 +    if (!proto) {
  1.1748 +        JS_ASSERT(!desc.object());
  1.1749 +        return true;
  1.1750 +    }
  1.1751 +    return JS_GetPropertyDescriptorById(cx, proto, id, desc);
  1.1752 +}
  1.1753 +
  1.1754 +bool
  1.1755 +ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.1756 +                                                     MutableHandle<PropertyDescriptor> desc)
  1.1757 +{
  1.1758 +    // step 1
  1.1759 +    RootedValue v(cx);
  1.1760 +    if (!TrapGetOwnProperty(cx, proxy, id, &v))
  1.1761 +        return false;
  1.1762 +
  1.1763 +    // step 2
  1.1764 +    if (v.isUndefined()) {
  1.1765 +        desc.object().set(nullptr);
  1.1766 +        return true;
  1.1767 +    }
  1.1768 +
  1.1769 +    // steps 3-4
  1.1770 +    return ParsePropertyDescriptorObject(cx, proxy, v, desc, true);
  1.1771 +}
  1.1772 +
  1.1773 +bool
  1.1774 +ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  1.1775 +                                           MutableHandle<PropertyDescriptor> desc)
  1.1776 +{
  1.1777 +    // step 1
  1.1778 +    AutoPropDescArrayRooter descs(cx);
  1.1779 +    PropDesc *d = descs.append();
  1.1780 +    d->initFromPropertyDescriptor(desc);
  1.1781 +    RootedValue v(cx);
  1.1782 +    if (!FromGenericPropertyDescriptor(cx, d, &v))
  1.1783 +        return false;
  1.1784 +
  1.1785 +    // step 2
  1.1786 +    return TrapDefineOwnProperty(cx, proxy, id, &v);
  1.1787 +}
  1.1788 +
  1.1789 +bool
  1.1790 +ScriptedDirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy,
  1.1791 +                                                AutoIdVector &props)
  1.1792 +{
  1.1793 +    // step a
  1.1794 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1795 +
  1.1796 +    // step b
  1.1797 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1798 +
  1.1799 +    // step c
  1.1800 +    RootedValue trap(cx);
  1.1801 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyNames, &trap))
  1.1802 +        return false;
  1.1803 +
  1.1804 +    // step d
  1.1805 +    if (trap.isUndefined())
  1.1806 +        return DirectProxyHandler::getOwnPropertyNames(cx, proxy, props);
  1.1807 +
  1.1808 +    // step e
  1.1809 +    Value argv[] = {
  1.1810 +        ObjectValue(*target)
  1.1811 +    };
  1.1812 +    RootedValue trapResult(cx);
  1.1813 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1814 +        return false;
  1.1815 +
  1.1816 +    // step f
  1.1817 +    if (trapResult.isPrimitive()) {
  1.1818 +        ReportInvalidTrapResult(cx, proxy, cx->names().getOwnPropertyNames);
  1.1819 +        return false;
  1.1820 +    }
  1.1821 +
  1.1822 +    // steps g to n are shared
  1.1823 +    return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY | JSITER_HIDDEN,
  1.1824 +                           cx->names().getOwnPropertyNames);
  1.1825 +}
  1.1826 +
  1.1827 +// Proxy.[[Delete]](P, Throw)
  1.1828 +bool
  1.1829 +ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.1830 +{
  1.1831 +    // step 1
  1.1832 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1833 +
  1.1834 +    // step 2
  1.1835 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1836 +
  1.1837 +    // step 3
  1.1838 +    RootedValue trap(cx);
  1.1839 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().deleteProperty, &trap))
  1.1840 +        return false;
  1.1841 +
  1.1842 +    // step 4
  1.1843 +    if (trap.isUndefined())
  1.1844 +        return DirectProxyHandler::delete_(cx, proxy, id, bp);
  1.1845 +
  1.1846 +    // step 5
  1.1847 +    RootedValue value(cx);
  1.1848 +    if (!IdToExposableValue(cx, id, &value))
  1.1849 +        return false;
  1.1850 +    Value argv[] = {
  1.1851 +        ObjectValue(*target),
  1.1852 +        value
  1.1853 +    };
  1.1854 +    RootedValue trapResult(cx);
  1.1855 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1856 +        return false;
  1.1857 +
  1.1858 +    // step 6-7
  1.1859 +    if (ToBoolean(trapResult)) {
  1.1860 +        Rooted<PropertyDescriptor> desc(cx);
  1.1861 +        if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1.1862 +            return false;
  1.1863 +
  1.1864 +        if (desc.object() && desc.isPermanent()) {
  1.1865 +            RootedValue v(cx, IdToValue(id));
  1.1866 +            js_ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, js::NullPtr());
  1.1867 +            return false;
  1.1868 +        }
  1.1869 +
  1.1870 +        *bp = true;
  1.1871 +        return true;
  1.1872 +    }
  1.1873 +
  1.1874 +    // step 8
  1.1875 +    // FIXME: API does not include a Throw parameter
  1.1876 +    *bp = false;
  1.1877 +    return true;
  1.1878 +}
  1.1879 +
  1.1880 +// 12.6.4 The for-in Statement, step 6
  1.1881 +bool
  1.1882 +ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.1883 +{
  1.1884 +    // step a
  1.1885 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1886 +
  1.1887 +    // step b
  1.1888 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1889 +
  1.1890 +    // step c
  1.1891 +    RootedValue trap(cx);
  1.1892 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap))
  1.1893 +        return false;
  1.1894 +
  1.1895 +    // step d
  1.1896 +    if (trap.isUndefined())
  1.1897 +        return DirectProxyHandler::enumerate(cx, proxy, props);
  1.1898 +
  1.1899 +    // step e
  1.1900 +    Value argv[] = {
  1.1901 +        ObjectOrNullValue(target)
  1.1902 +    };
  1.1903 +    RootedValue trapResult(cx);
  1.1904 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1905 +        return false;
  1.1906 +
  1.1907 +    // step f
  1.1908 +    if (trapResult.isPrimitive()) {
  1.1909 +        JSAutoByteString bytes;
  1.1910 +        if (!AtomToPrintableString(cx, cx->names().enumerate, &bytes))
  1.1911 +            return false;
  1.1912 +        RootedValue v(cx, ObjectOrNullValue(proxy));
  1.1913 +        js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK,
  1.1914 +                             v, js::NullPtr(), bytes.ptr());
  1.1915 +        return false;
  1.1916 +    }
  1.1917 +
  1.1918 +    // steps g-m are shared
  1.1919 +    // FIXME: the trap should return an iterator object, see bug 783826
  1.1920 +    return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate);
  1.1921 +}
  1.1922 +
  1.1923 +// Proxy.[[HasProperty]](P)
  1.1924 +bool
  1.1925 +ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.1926 +{
  1.1927 +    // step 1
  1.1928 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1929 +
  1.1930 +    // step 2
  1.1931 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1932 +
  1.1933 +    // step 3
  1.1934 +    RootedValue trap(cx);
  1.1935 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().has, &trap))
  1.1936 +        return false;
  1.1937 +
  1.1938 +    // step 4
  1.1939 +    if (trap.isUndefined())
  1.1940 +        return DirectProxyHandler::has(cx, proxy, id, bp);
  1.1941 +
  1.1942 +    // step 5
  1.1943 +    RootedValue value(cx);
  1.1944 +    if (!IdToExposableValue(cx, id, &value))
  1.1945 +        return false;
  1.1946 +    Value argv[] = {
  1.1947 +        ObjectOrNullValue(target),
  1.1948 +        value
  1.1949 +    };
  1.1950 +    RootedValue trapResult(cx);
  1.1951 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.1952 +        return false;
  1.1953 +
  1.1954 +    // step 6
  1.1955 +    bool success = ToBoolean(trapResult);;
  1.1956 +
  1.1957 +    // step 7
  1.1958 +    if (!success) {
  1.1959 +        Rooted<PropertyDescriptor> desc(cx);
  1.1960 +        if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1.1961 +            return false;
  1.1962 +
  1.1963 +        if (desc.object()) {
  1.1964 +            if (desc.isPermanent()) {
  1.1965 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  1.1966 +                return false;
  1.1967 +            }
  1.1968 +
  1.1969 +            bool extensible;
  1.1970 +            if (!JSObject::isExtensible(cx, target, &extensible))
  1.1971 +                return false;
  1.1972 +            if (!extensible) {
  1.1973 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1.1974 +                return false;
  1.1975 +            }
  1.1976 +        }
  1.1977 +    }
  1.1978 +
  1.1979 +    // step 8
  1.1980 +    *bp = success;
  1.1981 +    return true;
  1.1982 +}
  1.1983 +
  1.1984 +// Proxy.[[HasOwnProperty]](P)
  1.1985 +bool
  1.1986 +ScriptedDirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.1987 +{
  1.1988 +    // step 1
  1.1989 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.1990 +
  1.1991 +    // step 2
  1.1992 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.1993 +
  1.1994 +    // step 3
  1.1995 +    RootedValue trap(cx);
  1.1996 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().hasOwn, &trap))
  1.1997 +        return false;
  1.1998 +
  1.1999 +    // step 4
  1.2000 +    if (trap.isUndefined())
  1.2001 +        return DirectProxyHandler::hasOwn(cx, proxy, id, bp);
  1.2002 +
  1.2003 +    // step 5
  1.2004 +    RootedValue value(cx);
  1.2005 +    if (!IdToExposableValue(cx, id, &value))
  1.2006 +        return false;
  1.2007 +    Value argv[] = {
  1.2008 +        ObjectOrNullValue(target),
  1.2009 +        value
  1.2010 +    };
  1.2011 +    RootedValue trapResult(cx);
  1.2012 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.2013 +        return false;
  1.2014 +
  1.2015 +    // step 6
  1.2016 +    bool success = ToBoolean(trapResult);
  1.2017 +
  1.2018 +    // steps 7-8
  1.2019 +    if (!success) {
  1.2020 +        bool sealed;
  1.2021 +        if (!IsSealed(cx, target, id, &sealed))
  1.2022 +            return false;
  1.2023 +        if (sealed) {
  1.2024 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
  1.2025 +            return false;
  1.2026 +        }
  1.2027 +
  1.2028 +        bool extensible;
  1.2029 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.2030 +            return false;
  1.2031 +        if (!extensible) {
  1.2032 +            bool isFixed;
  1.2033 +            if (!HasOwn(cx, target, id, &isFixed))
  1.2034 +                return false;
  1.2035 +            if (isFixed) {
  1.2036 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
  1.2037 +                return false;
  1.2038 +            }
  1.2039 +        }
  1.2040 +    } else {
  1.2041 +        bool extensible;
  1.2042 +        if (!JSObject::isExtensible(cx, target, &extensible))
  1.2043 +            return false;
  1.2044 +        if (!extensible) {
  1.2045 +            bool isFixed;
  1.2046 +            if (!HasOwn(cx, target, id, &isFixed))
  1.2047 +                return false;
  1.2048 +            if (!isFixed) {
  1.2049 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
  1.2050 +                return false;
  1.2051 +            }
  1.2052 +        }
  1.2053 +    }
  1.2054 +
  1.2055 +    // step 9
  1.2056 +    *bp = !!success;
  1.2057 +    return true;
  1.2058 +}
  1.2059 +
  1.2060 +// Proxy.[[GetP]](P, Receiver)
  1.2061 +bool
  1.2062 +ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
  1.2063 +                                HandleId id, MutableHandleValue vp)
  1.2064 +{
  1.2065 +    // step 1
  1.2066 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2067 +
  1.2068 +    // step 2
  1.2069 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2070 +
  1.2071 +    // step 3
  1.2072 +    RootedValue trap(cx);
  1.2073 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().get, &trap))
  1.2074 +        return false;
  1.2075 +
  1.2076 +    // step 4
  1.2077 +    if (trap.isUndefined())
  1.2078 +        return DirectProxyHandler::get(cx, proxy, receiver, id, vp);
  1.2079 +
  1.2080 +    // step 5
  1.2081 +    RootedValue value(cx);
  1.2082 +    if (!IdToExposableValue(cx, id, &value))
  1.2083 +        return false;
  1.2084 +    Value argv[] = {
  1.2085 +        ObjectOrNullValue(target),
  1.2086 +        value,
  1.2087 +        ObjectOrNullValue(receiver)
  1.2088 +    };
  1.2089 +    RootedValue trapResult(cx);
  1.2090 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.2091 +        return false;
  1.2092 +
  1.2093 +    // step 6
  1.2094 +    Rooted<PropertyDescriptor> desc(cx);
  1.2095 +    if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1.2096 +        return false;
  1.2097 +
  1.2098 +    // step 7
  1.2099 +    if (desc.object()) {
  1.2100 +        if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
  1.2101 +            bool same;
  1.2102 +            if (!SameValue(cx, trapResult, desc.value(), &same))
  1.2103 +                return false;
  1.2104 +            if (!same) {
  1.2105 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
  1.2106 +                return false;
  1.2107 +            }
  1.2108 +        }
  1.2109 +
  1.2110 +        if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasGetterObject()) {
  1.2111 +            if (!trapResult.isUndefined()) {
  1.2112 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
  1.2113 +                return false;
  1.2114 +            }
  1.2115 +        }
  1.2116 +    }
  1.2117 +
  1.2118 +    // step 8
  1.2119 +    vp.set(trapResult);
  1.2120 +    return true;
  1.2121 +}
  1.2122 +
  1.2123 +// Proxy.[[SetP]](P, V, Receiver)
  1.2124 +bool
  1.2125 +ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
  1.2126 +                                HandleId id, bool strict, MutableHandleValue vp)
  1.2127 +{
  1.2128 +    // step 1
  1.2129 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2130 +
  1.2131 +    // step 2
  1.2132 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2133 +
  1.2134 +    // step 3
  1.2135 +    RootedValue trap(cx);
  1.2136 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().set, &trap))
  1.2137 +        return false;
  1.2138 +
  1.2139 +    // step 4
  1.2140 +    if (trap.isUndefined())
  1.2141 +        return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp);
  1.2142 +
  1.2143 +    // step 5
  1.2144 +    RootedValue value(cx);
  1.2145 +    if (!IdToExposableValue(cx, id, &value))
  1.2146 +        return false;
  1.2147 +    Value argv[] = {
  1.2148 +        ObjectOrNullValue(target),
  1.2149 +        value,
  1.2150 +        vp.get(),
  1.2151 +        ObjectValue(*receiver)
  1.2152 +    };
  1.2153 +    RootedValue trapResult(cx);
  1.2154 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.2155 +        return false;
  1.2156 +
  1.2157 +    // step 6
  1.2158 +    bool success = ToBoolean(trapResult);
  1.2159 +
  1.2160 +    // step 7
  1.2161 +    if (success) {
  1.2162 +        Rooted<PropertyDescriptor> desc(cx);
  1.2163 +        if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
  1.2164 +            return false;
  1.2165 +
  1.2166 +        if (desc.object()) {
  1.2167 +            if (IsDataDescriptor(desc) && desc.isPermanent() && desc.isReadonly()) {
  1.2168 +                bool same;
  1.2169 +                if (!SameValue(cx, vp, desc.value(), &same))
  1.2170 +                    return false;
  1.2171 +                if (!same) {
  1.2172 +                    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
  1.2173 +                    return false;
  1.2174 +                }
  1.2175 +            }
  1.2176 +
  1.2177 +            if (IsAccessorDescriptor(desc) && desc.isPermanent() && !desc.hasSetterObject()) {
  1.2178 +                JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
  1.2179 +                return false;
  1.2180 +            }
  1.2181 +        }
  1.2182 +    }
  1.2183 +
  1.2184 +    // step 8
  1.2185 +    vp.set(BooleanValue(success));
  1.2186 +    return true;
  1.2187 +}
  1.2188 +
  1.2189 +// 15.2.3.14 Object.keys (O), step 2
  1.2190 +bool
  1.2191 +ScriptedDirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.2192 +{
  1.2193 +    // step a
  1.2194 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2195 +
  1.2196 +    // step b
  1.2197 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2198 +
  1.2199 +    // step c
  1.2200 +    RootedValue trap(cx);
  1.2201 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().keys, &trap))
  1.2202 +        return false;
  1.2203 +
  1.2204 +    // step d
  1.2205 +    if (trap.isUndefined())
  1.2206 +        return DirectProxyHandler::keys(cx, proxy, props);
  1.2207 +
  1.2208 +    // step e
  1.2209 +    Value argv[] = {
  1.2210 +        ObjectOrNullValue(target)
  1.2211 +    };
  1.2212 +    RootedValue trapResult(cx);
  1.2213 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.2214 +        return false;
  1.2215 +
  1.2216 +    // step f
  1.2217 +    if (trapResult.isPrimitive()) {
  1.2218 +        JSAutoByteString bytes;
  1.2219 +        if (!AtomToPrintableString(cx, cx->names().keys, &bytes))
  1.2220 +            return false;
  1.2221 +        RootedValue v(cx, ObjectOrNullValue(proxy));
  1.2222 +        js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK,
  1.2223 +                             v, js::NullPtr(), bytes.ptr());
  1.2224 +        return false;
  1.2225 +    }
  1.2226 +
  1.2227 +    // steps g-n are shared
  1.2228 +    return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY, cx->names().keys);
  1.2229 +}
  1.2230 +
  1.2231 +// ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]](P)
  1.2232 +bool
  1.2233 +ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
  1.2234 +{
  1.2235 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2236 +
  1.2237 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2238 +
  1.2239 +    RootedValue trap(cx);
  1.2240 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().isExtensible, &trap))
  1.2241 +        return false;
  1.2242 +
  1.2243 +    if (trap.isUndefined())
  1.2244 +        return DirectProxyHandler::isExtensible(cx, proxy, extensible);
  1.2245 +
  1.2246 +    Value argv[] = {
  1.2247 +        ObjectValue(*target)
  1.2248 +    };
  1.2249 +    RootedValue trapResult(cx);
  1.2250 +    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
  1.2251 +        return false;
  1.2252 +
  1.2253 +    bool booleanTrapResult = ToBoolean(trapResult);
  1.2254 +    bool targetResult;
  1.2255 +    if (!JSObject::isExtensible(cx, target, &targetResult))
  1.2256 +        return false;
  1.2257 +
  1.2258 +    if (targetResult != booleanTrapResult) {
  1.2259 +       JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
  1.2260 +       return false;
  1.2261 +    }
  1.2262 +
  1.2263 +    *extensible = booleanTrapResult;
  1.2264 +    return true;
  1.2265 +}
  1.2266 +
  1.2267 +bool
  1.2268 +ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
  1.2269 +                                    MutableHandleValue vp)
  1.2270 +{
  1.2271 +    // FIXME: Provide a proper implementation for this trap, see bug 787004
  1.2272 +    return DirectProxyHandler::iterate(cx, proxy, flags, vp);
  1.2273 +}
  1.2274 +
  1.2275 +bool
  1.2276 +ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.2277 +{
  1.2278 +    // step 1
  1.2279 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2280 +
  1.2281 +    // step 2
  1.2282 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2283 +
  1.2284 +    /*
  1.2285 +     * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get
  1.2286 +     * called for non-callable objects
  1.2287 +     */
  1.2288 +
  1.2289 +    // step 3
  1.2290 +    RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
  1.2291 +    if (!argsArray)
  1.2292 +        return false;
  1.2293 +
  1.2294 +    // step 4
  1.2295 +    RootedValue trap(cx);
  1.2296 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().apply, &trap))
  1.2297 +        return false;
  1.2298 +
  1.2299 +    // step 5
  1.2300 +    if (trap.isUndefined())
  1.2301 +        return DirectProxyHandler::call(cx, proxy, args);
  1.2302 +
  1.2303 +    // step 6
  1.2304 +    Value argv[] = {
  1.2305 +        ObjectValue(*target),
  1.2306 +        args.thisv(),
  1.2307 +        ObjectValue(*argsArray)
  1.2308 +    };
  1.2309 +    RootedValue thisValue(cx, ObjectValue(*handler));
  1.2310 +    return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval());
  1.2311 +}
  1.2312 +
  1.2313 +bool
  1.2314 +ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.2315 +{
  1.2316 +    // step 1
  1.2317 +    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
  1.2318 +
  1.2319 +    // step 2
  1.2320 +    RootedObject target(cx, proxy->as<ProxyObject>().target());
  1.2321 +
  1.2322 +    /*
  1.2323 +     * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get
  1.2324 +     * called for non-callable objects
  1.2325 +     */
  1.2326 +
  1.2327 +    // step 3
  1.2328 +    RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array()));
  1.2329 +    if (!argsArray)
  1.2330 +        return false;
  1.2331 +
  1.2332 +    // step 4
  1.2333 +    RootedValue trap(cx);
  1.2334 +    if (!JSObject::getProperty(cx, handler, handler, cx->names().construct, &trap))
  1.2335 +        return false;
  1.2336 +
  1.2337 +    // step 5
  1.2338 +    if (trap.isUndefined())
  1.2339 +        return DirectProxyHandler::construct(cx, proxy, args);
  1.2340 +
  1.2341 +    // step 6
  1.2342 +    Value constructArgv[] = {
  1.2343 +        ObjectValue(*target),
  1.2344 +        ObjectValue(*argsArray)
  1.2345 +    };
  1.2346 +    RootedValue thisValue(cx, ObjectValue(*handler));
  1.2347 +    if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval()))
  1.2348 +        return false;
  1.2349 +    if (!args.rval().isObject()) {
  1.2350 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
  1.2351 +        return false;
  1.2352 +    }
  1.2353 +    return true;
  1.2354 +}
  1.2355 +
  1.2356 +ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
  1.2357 +
  1.2358 +#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall)                   \
  1.2359 +    JS_BEGIN_MACRO                                                           \
  1.2360 +        RootedObject proto(cx);                                              \
  1.2361 +        if (!JSObject::getProto(cx, proxy, &proto))                          \
  1.2362 +            return false;                                                    \
  1.2363 +        if (!proto)                                                          \
  1.2364 +            return true;                                                     \
  1.2365 +        assertSameCompartment(cx, proxy, proto);                             \
  1.2366 +        return protoCall;                                                    \
  1.2367 +    JS_END_MACRO                                                             \
  1.2368 +
  1.2369 +bool
  1.2370 +Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.2371 +                             MutableHandle<PropertyDescriptor> desc)
  1.2372 +{
  1.2373 +    JS_CHECK_RECURSION(cx, return false);
  1.2374 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2375 +    desc.object().set(nullptr); // default result if we refuse to perform this action
  1.2376 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  1.2377 +    if (!policy.allowed())
  1.2378 +        return policy.returnValue();
  1.2379 +    if (!handler->hasPrototype())
  1.2380 +        return handler->getPropertyDescriptor(cx, proxy, id, desc);
  1.2381 +    if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
  1.2382 +        return false;
  1.2383 +    if (desc.object())
  1.2384 +        return true;
  1.2385 +    INVOKE_ON_PROTOTYPE(cx, handler, proxy, JS_GetPropertyDescriptorById(cx, proto, id, desc));
  1.2386 +}
  1.2387 +
  1.2388 +bool
  1.2389 +Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
  1.2390 +{
  1.2391 +    JS_CHECK_RECURSION(cx, return false);
  1.2392 +
  1.2393 +    Rooted<PropertyDescriptor> desc(cx);
  1.2394 +    if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
  1.2395 +        return false;
  1.2396 +    return NewPropertyDescriptorObject(cx, desc, vp);
  1.2397 +}
  1.2398 +
  1.2399 +bool
  1.2400 +Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.2401 +                                MutableHandle<PropertyDescriptor> desc)
  1.2402 +{
  1.2403 +    JS_CHECK_RECURSION(cx, return false);
  1.2404 +
  1.2405 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2406 +    desc.object().set(nullptr); // default result if we refuse to perform this action
  1.2407 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  1.2408 +    if (!policy.allowed())
  1.2409 +        return policy.returnValue();
  1.2410 +    return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
  1.2411 +}
  1.2412 +
  1.2413 +bool
  1.2414 +Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
  1.2415 +                                MutableHandleValue vp)
  1.2416 +{
  1.2417 +    JS_CHECK_RECURSION(cx, return false);
  1.2418 +
  1.2419 +    Rooted<PropertyDescriptor> desc(cx);
  1.2420 +    if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc))
  1.2421 +        return false;
  1.2422 +    return NewPropertyDescriptorObject(cx, desc, vp);
  1.2423 +}
  1.2424 +
  1.2425 +bool
  1.2426 +Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
  1.2427 +                      MutableHandle<PropertyDescriptor> desc)
  1.2428 +{
  1.2429 +    JS_CHECK_RECURSION(cx, return false);
  1.2430 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2431 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  1.2432 +    if (!policy.allowed())
  1.2433 +        return policy.returnValue();
  1.2434 +    return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc);
  1.2435 +}
  1.2436 +
  1.2437 +bool
  1.2438 +Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v)
  1.2439 +{
  1.2440 +    JS_CHECK_RECURSION(cx, return false);
  1.2441 +    Rooted<PropertyDescriptor> desc(cx);
  1.2442 +    return ParsePropertyDescriptorObject(cx, proxy, v, &desc) &&
  1.2443 +           Proxy::defineProperty(cx, proxy, id, &desc);
  1.2444 +}
  1.2445 +
  1.2446 +bool
  1.2447 +Proxy::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.2448 +{
  1.2449 +    JS_CHECK_RECURSION(cx, return false);
  1.2450 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2451 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  1.2452 +    if (!policy.allowed())
  1.2453 +        return policy.returnValue();
  1.2454 +    return proxy->as<ProxyObject>().handler()->getOwnPropertyNames(cx, proxy, props);
  1.2455 +}
  1.2456 +
  1.2457 +bool
  1.2458 +Proxy::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.2459 +{
  1.2460 +    JS_CHECK_RECURSION(cx, return false);
  1.2461 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2462 +    *bp = true; // default result if we refuse to perform this action
  1.2463 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  1.2464 +    if (!policy.allowed())
  1.2465 +        return policy.returnValue();
  1.2466 +    return proxy->as<ProxyObject>().handler()->delete_(cx, proxy, id, bp);
  1.2467 +}
  1.2468 +
  1.2469 +JS_FRIEND_API(bool)
  1.2470 +js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others)
  1.2471 +{
  1.2472 +    AutoIdVector uniqueOthers(cx);
  1.2473 +    if (!uniqueOthers.reserve(others.length()))
  1.2474 +        return false;
  1.2475 +    for (size_t i = 0; i < others.length(); ++i) {
  1.2476 +        bool unique = true;
  1.2477 +        for (size_t j = 0; j < base.length(); ++j) {
  1.2478 +            if (others[i] == base[j]) {
  1.2479 +                unique = false;
  1.2480 +                break;
  1.2481 +            }
  1.2482 +        }
  1.2483 +        if (unique)
  1.2484 +            uniqueOthers.append(others[i]);
  1.2485 +    }
  1.2486 +    return base.appendAll(uniqueOthers);
  1.2487 +}
  1.2488 +
  1.2489 +bool
  1.2490 +Proxy::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.2491 +{
  1.2492 +    JS_CHECK_RECURSION(cx, return false);
  1.2493 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2494 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  1.2495 +    if (!policy.allowed())
  1.2496 +        return policy.returnValue();
  1.2497 +    if (!handler->hasPrototype())
  1.2498 +        return proxy->as<ProxyObject>().handler()->enumerate(cx, proxy, props);
  1.2499 +    if (!handler->keys(cx, proxy, props))
  1.2500 +        return false;
  1.2501 +    AutoIdVector protoProps(cx);
  1.2502 +    INVOKE_ON_PROTOTYPE(cx, handler, proxy,
  1.2503 +                        GetPropertyNames(cx, proto, 0, &protoProps) &&
  1.2504 +                        AppendUnique(cx, props, protoProps));
  1.2505 +}
  1.2506 +
  1.2507 +bool
  1.2508 +Proxy::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.2509 +{
  1.2510 +    JS_CHECK_RECURSION(cx, return false);
  1.2511 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2512 +    *bp = false; // default result if we refuse to perform this action
  1.2513 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  1.2514 +    if (!policy.allowed())
  1.2515 +        return policy.returnValue();
  1.2516 +    if (!handler->hasPrototype())
  1.2517 +        return handler->has(cx, proxy, id, bp);
  1.2518 +    if (!handler->hasOwn(cx, proxy, id, bp))
  1.2519 +        return false;
  1.2520 +    if (*bp)
  1.2521 +        return true;
  1.2522 +    bool Bp;
  1.2523 +    INVOKE_ON_PROTOTYPE(cx, handler, proxy,
  1.2524 +                        JS_HasPropertyById(cx, proto, id, &Bp) &&
  1.2525 +                        ((*bp = Bp) || true));
  1.2526 +}
  1.2527 +
  1.2528 +bool
  1.2529 +Proxy::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
  1.2530 +{
  1.2531 +    JS_CHECK_RECURSION(cx, return false);
  1.2532 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2533 +    *bp = false; // default result if we refuse to perform this action
  1.2534 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  1.2535 +    if (!policy.allowed())
  1.2536 +        return policy.returnValue();
  1.2537 +    return handler->hasOwn(cx, proxy, id, bp);
  1.2538 +}
  1.2539 +
  1.2540 +bool
  1.2541 +Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1.2542 +           MutableHandleValue vp)
  1.2543 +{
  1.2544 +    JS_CHECK_RECURSION(cx, return false);
  1.2545 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2546 +    vp.setUndefined(); // default result if we refuse to perform this action
  1.2547 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
  1.2548 +    if (!policy.allowed())
  1.2549 +        return policy.returnValue();
  1.2550 +    bool own;
  1.2551 +    if (!handler->hasPrototype()) {
  1.2552 +        own = true;
  1.2553 +    } else {
  1.2554 +        if (!handler->hasOwn(cx, proxy, id, &own))
  1.2555 +            return false;
  1.2556 +    }
  1.2557 +    if (own)
  1.2558 +        return handler->get(cx, proxy, receiver, id, vp);
  1.2559 +    INVOKE_ON_PROTOTYPE(cx, handler, proxy, JSObject::getGeneric(cx, proto, receiver, id, vp));
  1.2560 +}
  1.2561 +
  1.2562 +bool
  1.2563 +Proxy::callProp(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
  1.2564 +                MutableHandleValue vp)
  1.2565 +{
  1.2566 +    // The inline caches need an access point for JSOP_CALLPROP sites that accounts
  1.2567 +    // for the possibility of __noSuchMethod__
  1.2568 +    if (!Proxy::get(cx, proxy, receiver, id, vp))
  1.2569 +        return false;
  1.2570 +
  1.2571 +#if JS_HAS_NO_SUCH_METHOD
  1.2572 +    if (MOZ_UNLIKELY(vp.isPrimitive())) {
  1.2573 +        if (!OnUnknownMethod(cx, proxy, IdToValue(id), vp))
  1.2574 +            return false;
  1.2575 +    }
  1.2576 +#endif
  1.2577 +
  1.2578 +    return true;
  1.2579 +}
  1.2580 +
  1.2581 +bool
  1.2582 +Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
  1.2583 +           MutableHandleValue vp)
  1.2584 +{
  1.2585 +    JS_CHECK_RECURSION(cx, return false);
  1.2586 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2587 +    AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
  1.2588 +    if (!policy.allowed())
  1.2589 +        return policy.returnValue();
  1.2590 +
  1.2591 +    // If the proxy doesn't require that we consult its prototype for the
  1.2592 +    // non-own cases, we can sink to the |set| trap.
  1.2593 +    if (!handler->hasPrototype())
  1.2594 +        return handler->set(cx, proxy, receiver, id, strict, vp);
  1.2595 +
  1.2596 +    // If we have an existing (own or non-own) property with a setter, we want
  1.2597 +    // to invoke that.
  1.2598 +    Rooted<PropertyDescriptor> desc(cx);
  1.2599 +    if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
  1.2600 +        return false;
  1.2601 +    if (desc.object() && desc.setter() && desc.setter() != JS_StrictPropertyStub)
  1.2602 +        return CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp);
  1.2603 +
  1.2604 +    // Ok. Either there was no pre-existing property, or it was a value prop
  1.2605 +    // that we're going to shadow. Make a property descriptor and define it.
  1.2606 +    Rooted<PropertyDescriptor> newDesc(cx);
  1.2607 +    newDesc.value().set(vp);
  1.2608 +    return handler->defineProperty(cx, receiver, id, &newDesc);
  1.2609 +}
  1.2610 +
  1.2611 +bool
  1.2612 +Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
  1.2613 +{
  1.2614 +    JS_CHECK_RECURSION(cx, return false);
  1.2615 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2616 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
  1.2617 +    if (!policy.allowed())
  1.2618 +        return policy.returnValue();
  1.2619 +    return handler->keys(cx, proxy, props);
  1.2620 +}
  1.2621 +
  1.2622 +bool
  1.2623 +Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
  1.2624 +{
  1.2625 +    JS_CHECK_RECURSION(cx, return false);
  1.2626 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2627 +    vp.setUndefined(); // default result if we refuse to perform this action
  1.2628 +    if (!handler->hasPrototype()) {
  1.2629 +        AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  1.2630 +                               BaseProxyHandler::ENUMERATE, true);
  1.2631 +        // If the policy denies access but wants us to return true, we need
  1.2632 +        // to hand a valid (empty) iterator object to the caller.
  1.2633 +        if (!policy.allowed()) {
  1.2634 +            AutoIdVector props(cx);
  1.2635 +            return policy.returnValue() &&
  1.2636 +                   EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
  1.2637 +        }
  1.2638 +        return handler->iterate(cx, proxy, flags, vp);
  1.2639 +    }
  1.2640 +    AutoIdVector props(cx);
  1.2641 +    // The other Proxy::foo methods do the prototype-aware work for us here.
  1.2642 +    if ((flags & JSITER_OWNONLY)
  1.2643 +        ? !Proxy::keys(cx, proxy, props)
  1.2644 +        : !Proxy::enumerate(cx, proxy, props)) {
  1.2645 +        return false;
  1.2646 +    }
  1.2647 +    return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
  1.2648 +}
  1.2649 +
  1.2650 +bool
  1.2651 +Proxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
  1.2652 +{
  1.2653 +    JS_CHECK_RECURSION(cx, return false);
  1.2654 +    return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
  1.2655 +}
  1.2656 +
  1.2657 +bool
  1.2658 +Proxy::preventExtensions(JSContext *cx, HandleObject proxy)
  1.2659 +{
  1.2660 +    JS_CHECK_RECURSION(cx, return false);
  1.2661 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2662 +    return handler->preventExtensions(cx, proxy);
  1.2663 +}
  1.2664 +
  1.2665 +bool
  1.2666 +Proxy::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.2667 +{
  1.2668 +    JS_CHECK_RECURSION(cx, return false);
  1.2669 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2670 +
  1.2671 +    // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
  1.2672 +    // can only set our default value once we're sure that we're not calling the
  1.2673 +    // trap.
  1.2674 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  1.2675 +                           BaseProxyHandler::CALL, true);
  1.2676 +    if (!policy.allowed()) {
  1.2677 +        args.rval().setUndefined();
  1.2678 +        return policy.returnValue();
  1.2679 +    }
  1.2680 +
  1.2681 +    return handler->call(cx, proxy, args);
  1.2682 +}
  1.2683 +
  1.2684 +bool
  1.2685 +Proxy::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
  1.2686 +{
  1.2687 +    JS_CHECK_RECURSION(cx, return false);
  1.2688 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2689 +
  1.2690 +    // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
  1.2691 +    // can only set our default value once we're sure that we're not calling the
  1.2692 +    // trap.
  1.2693 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  1.2694 +                           BaseProxyHandler::CALL, true);
  1.2695 +    if (!policy.allowed()) {
  1.2696 +        args.rval().setUndefined();
  1.2697 +        return policy.returnValue();
  1.2698 +    }
  1.2699 +
  1.2700 +    return handler->construct(cx, proxy, args);
  1.2701 +}
  1.2702 +
  1.2703 +bool
  1.2704 +Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
  1.2705 +{
  1.2706 +    JS_CHECK_RECURSION(cx, return false);
  1.2707 +    RootedObject proxy(cx, &args.thisv().toObject());
  1.2708 +    // Note - we don't enter a policy here because our security architecture
  1.2709 +    // guards against nativeCall by overriding the trap itself in the right
  1.2710 +    // circumstances.
  1.2711 +    return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
  1.2712 +}
  1.2713 +
  1.2714 +bool
  1.2715 +Proxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
  1.2716 +{
  1.2717 +    JS_CHECK_RECURSION(cx, return false);
  1.2718 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2719 +    *bp = false; // default result if we refuse to perform this action
  1.2720 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
  1.2721 +    if (!policy.allowed())
  1.2722 +        return policy.returnValue();
  1.2723 +    return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
  1.2724 +}
  1.2725 +
  1.2726 +bool
  1.2727 +Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
  1.2728 +{
  1.2729 +    JS_CHECK_RECURSION(cx, return false);
  1.2730 +    return proxy->as<ProxyObject>().handler()->objectClassIs(proxy, classValue, cx);
  1.2731 +}
  1.2732 +
  1.2733 +const char *
  1.2734 +Proxy::className(JSContext *cx, HandleObject proxy)
  1.2735 +{
  1.2736 +    // Check for unbounded recursion, but don't signal an error; className
  1.2737 +    // needs to be infallible.
  1.2738 +    int stackDummy;
  1.2739 +    if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), &stackDummy))
  1.2740 +        return "too much recursion";
  1.2741 +
  1.2742 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2743 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  1.2744 +                           BaseProxyHandler::GET, /* mayThrow = */ false);
  1.2745 +    // Do the safe thing if the policy rejects.
  1.2746 +    if (!policy.allowed()) {
  1.2747 +        return handler->BaseProxyHandler::className(cx, proxy);
  1.2748 +    }
  1.2749 +    return handler->className(cx, proxy);
  1.2750 +}
  1.2751 +
  1.2752 +JSString *
  1.2753 +Proxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
  1.2754 +{
  1.2755 +    JS_CHECK_RECURSION(cx, return nullptr);
  1.2756 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2757 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
  1.2758 +                           BaseProxyHandler::GET, /* mayThrow = */ false);
  1.2759 +    // Do the safe thing if the policy rejects.
  1.2760 +    if (!policy.allowed())
  1.2761 +        return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
  1.2762 +    return handler->fun_toString(cx, proxy, indent);
  1.2763 +}
  1.2764 +
  1.2765 +bool
  1.2766 +Proxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
  1.2767 +{
  1.2768 +    JS_CHECK_RECURSION(cx, return false);
  1.2769 +    return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
  1.2770 +}
  1.2771 +
  1.2772 +bool
  1.2773 +Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
  1.2774 +{
  1.2775 +    JS_CHECK_RECURSION(cx, return false);
  1.2776 +    return proxy->as<ProxyObject>().handler()->defaultValue(cx, proxy, hint, vp);
  1.2777 +}
  1.2778 +
  1.2779 +JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject *>(0x1);
  1.2780 +
  1.2781 +bool
  1.2782 +Proxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject proto)
  1.2783 +{
  1.2784 +    JS_ASSERT(proxy->getTaggedProto().isLazy());
  1.2785 +    JS_CHECK_RECURSION(cx, return false);
  1.2786 +    return proxy->as<ProxyObject>().handler()->getPrototypeOf(cx, proxy, proto);
  1.2787 +}
  1.2788 +
  1.2789 +bool
  1.2790 +Proxy::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
  1.2791 +{
  1.2792 +    JS_ASSERT(proxy->getTaggedProto().isLazy());
  1.2793 +    JS_CHECK_RECURSION(cx, return false);
  1.2794 +    return proxy->as<ProxyObject>().handler()->setPrototypeOf(cx, proxy, proto, bp);
  1.2795 +}
  1.2796 +
  1.2797 +/* static */ bool
  1.2798 +Proxy::watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
  1.2799 +{
  1.2800 +    JS_CHECK_RECURSION(cx, return false);
  1.2801 +    return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
  1.2802 +}
  1.2803 +
  1.2804 +/* static */ bool
  1.2805 +Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id)
  1.2806 +{
  1.2807 +    JS_CHECK_RECURSION(cx, return false);
  1.2808 +    return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
  1.2809 +}
  1.2810 +
  1.2811 +/* static */ bool
  1.2812 +Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
  1.2813 +             HandleObject result)
  1.2814 +{
  1.2815 +    JS_CHECK_RECURSION(cx, return false);
  1.2816 +    BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
  1.2817 +    AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
  1.2818 +                           /* mayThrow = */ true);
  1.2819 +    if (!policy.allowed()) {
  1.2820 +        if (policy.returnValue()) {
  1.2821 +            JS_ASSERT(!cx->isExceptionPending());
  1.2822 +            return js::SliceSlowly(cx, proxy, proxy, begin, end, result);
  1.2823 +        }
  1.2824 +        return false;
  1.2825 +    }
  1.2826 +    return handler->slice(cx, proxy, begin, end, result);
  1.2827 +}
  1.2828 +
  1.2829 +JSObject *
  1.2830 +js::proxy_innerObject(JSContext *cx, HandleObject obj)
  1.2831 +{
  1.2832 +    return obj->as<ProxyObject>().private_().toObjectOrNull();
  1.2833 +}
  1.2834 +
  1.2835 +bool
  1.2836 +js::proxy_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1.2837 +                        MutableHandleObject objp, MutableHandleShape propp)
  1.2838 +{
  1.2839 +    bool found;
  1.2840 +    if (!Proxy::has(cx, obj, id, &found))
  1.2841 +        return false;
  1.2842 +
  1.2843 +    if (found) {
  1.2844 +        MarkNonNativePropertyFound(propp);
  1.2845 +        objp.set(obj);
  1.2846 +    } else {
  1.2847 +        objp.set(nullptr);
  1.2848 +        propp.set(nullptr);
  1.2849 +    }
  1.2850 +    return true;
  1.2851 +}
  1.2852 +
  1.2853 +bool
  1.2854 +js::proxy_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
  1.2855 +                         MutableHandleObject objp, MutableHandleShape propp)
  1.2856 +{
  1.2857 +    RootedId id(cx, NameToId(name));
  1.2858 +    return proxy_LookupGeneric(cx, obj, id, objp, propp);
  1.2859 +}
  1.2860 +
  1.2861 +bool
  1.2862 +js::proxy_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
  1.2863 +                        MutableHandleObject objp, MutableHandleShape propp)
  1.2864 +{
  1.2865 +    RootedId id(cx);
  1.2866 +    if (!IndexToId(cx, index, &id))
  1.2867 +        return false;
  1.2868 +    return proxy_LookupGeneric(cx, obj, id, objp, propp);
  1.2869 +}
  1.2870 +
  1.2871 +bool
  1.2872 +js::proxy_DefineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
  1.2873 +                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.2874 +{
  1.2875 +    Rooted<PropertyDescriptor> desc(cx);
  1.2876 +    desc.object().set(obj);
  1.2877 +    desc.value().set(value);
  1.2878 +    desc.setAttributes(attrs);
  1.2879 +    desc.setGetter(getter);
  1.2880 +    desc.setSetter(setter);
  1.2881 +    return Proxy::defineProperty(cx, obj, id, &desc);
  1.2882 +}
  1.2883 +
  1.2884 +bool
  1.2885 +js::proxy_DefineProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
  1.2886 +                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.2887 +{
  1.2888 +    Rooted<jsid> id(cx, NameToId(name));
  1.2889 +    return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  1.2890 +}
  1.2891 +
  1.2892 +bool
  1.2893 +js::proxy_DefineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue value,
  1.2894 +                        PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
  1.2895 +{
  1.2896 +    RootedId id(cx);
  1.2897 +    if (!IndexToId(cx, index, &id))
  1.2898 +        return false;
  1.2899 +    return proxy_DefineGeneric(cx, obj, id, value, getter, setter, attrs);
  1.2900 +}
  1.2901 +
  1.2902 +bool
  1.2903 +js::proxy_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
  1.2904 +                     MutableHandleValue vp)
  1.2905 +{
  1.2906 +    return Proxy::get(cx, obj, receiver, id, vp);
  1.2907 +}
  1.2908 +
  1.2909 +bool
  1.2910 +js::proxy_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
  1.2911 +                      MutableHandleValue vp)
  1.2912 +{
  1.2913 +    Rooted<jsid> id(cx, NameToId(name));
  1.2914 +    return proxy_GetGeneric(cx, obj, receiver, id, vp);
  1.2915 +}
  1.2916 +
  1.2917 +bool
  1.2918 +js::proxy_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
  1.2919 +                     MutableHandleValue vp)
  1.2920 +{
  1.2921 +    RootedId id(cx);
  1.2922 +    if (!IndexToId(cx, index, &id))
  1.2923 +        return false;
  1.2924 +    return proxy_GetGeneric(cx, obj, receiver, id, vp);
  1.2925 +}
  1.2926 +
  1.2927 +bool
  1.2928 +js::proxy_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
  1.2929 +                     MutableHandleValue vp, bool strict)
  1.2930 +{
  1.2931 +    return Proxy::set(cx, obj, obj, id, strict, vp);
  1.2932 +}
  1.2933 +
  1.2934 +bool
  1.2935 +js::proxy_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
  1.2936 +                      MutableHandleValue vp, bool strict)
  1.2937 +{
  1.2938 +    Rooted<jsid> id(cx, NameToId(name));
  1.2939 +    return proxy_SetGeneric(cx, obj, id, vp, strict);
  1.2940 +}
  1.2941 +
  1.2942 +bool
  1.2943 +js::proxy_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
  1.2944 +                     MutableHandleValue vp, bool strict)
  1.2945 +{
  1.2946 +    RootedId id(cx);
  1.2947 +    if (!IndexToId(cx, index, &id))
  1.2948 +        return false;
  1.2949 +    return proxy_SetGeneric(cx, obj, id, vp, strict);
  1.2950 +}
  1.2951 +
  1.2952 +bool
  1.2953 +js::proxy_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  1.2954 +{
  1.2955 +    Rooted<PropertyDescriptor> desc(cx);
  1.2956 +    if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
  1.2957 +        return false;
  1.2958 +    *attrsp = desc.attributes();
  1.2959 +    return true;
  1.2960 +}
  1.2961 +
  1.2962 +bool
  1.2963 +js::proxy_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
  1.2964 +{
  1.2965 +    /* Lookup the current property descriptor so we have setter/getter/value. */
  1.2966 +    Rooted<PropertyDescriptor> desc(cx);
  1.2967 +    if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
  1.2968 +        return false;
  1.2969 +    desc.setAttributes(*attrsp);
  1.2970 +    return Proxy::defineProperty(cx, obj, id, &desc);
  1.2971 +}
  1.2972 +
  1.2973 +static bool
  1.2974 +proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
  1.2975 +{
  1.2976 +    bool deleted;
  1.2977 +    if (!Proxy::delete_(cx, obj, id, &deleted))
  1.2978 +        return false;
  1.2979 +    *succeeded = deleted;
  1.2980 +    return js_SuppressDeletedProperty(cx, obj, id);
  1.2981 +}
  1.2982 +
  1.2983 +bool
  1.2984 +js::proxy_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded)
  1.2985 +{
  1.2986 +    RootedId id(cx, NameToId(name));
  1.2987 +    return proxy_DeleteGeneric(cx, obj, id, succeeded);
  1.2988 +}
  1.2989 +
  1.2990 +bool
  1.2991 +js::proxy_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
  1.2992 +{
  1.2993 +    RootedId id(cx);
  1.2994 +    if (!IndexToId(cx, index, &id))
  1.2995 +        return false;
  1.2996 +    return proxy_DeleteGeneric(cx, obj, id, succeeded);
  1.2997 +}
  1.2998 +
  1.2999 +void
  1.3000 +js::proxy_Trace(JSTracer *trc, JSObject *obj)
  1.3001 +{
  1.3002 +    JS_ASSERT(obj->is<ProxyObject>());
  1.3003 +    ProxyObject::trace(trc, obj);
  1.3004 +}
  1.3005 +
  1.3006 +/* static */ void
  1.3007 +ProxyObject::trace(JSTracer *trc, JSObject *obj)
  1.3008 +{
  1.3009 +    ProxyObject *proxy = &obj->as<ProxyObject>();
  1.3010 +
  1.3011 +#ifdef DEBUG
  1.3012 +    if (!trc->runtime()->gcDisableStrictProxyCheckingCount && proxy->is<WrapperObject>()) {
  1.3013 +        JSObject *referent = &proxy->private_().toObject();
  1.3014 +        if (referent->compartment() != proxy->compartment()) {
  1.3015 +            /*
  1.3016 +             * Assert that this proxy is tracked in the wrapper map. We maintain
  1.3017 +             * the invariant that the wrapped object is the key in the wrapper map.
  1.3018 +             */
  1.3019 +            Value key = ObjectValue(*referent);
  1.3020 +            WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
  1.3021 +            JS_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy));
  1.3022 +        }
  1.3023 +    }
  1.3024 +#endif
  1.3025 +
  1.3026 +    // Note: If you add new slots here, make sure to change
  1.3027 +    // nuke() to cope.
  1.3028 +    MarkCrossCompartmentSlot(trc, obj, proxy->slotOfPrivate(), "private");
  1.3029 +    MarkSlot(trc, proxy->slotOfExtra(0), "extra0");
  1.3030 +
  1.3031 +    /*
  1.3032 +     * The GC can use the second reserved slot to link the cross compartment
  1.3033 +     * wrappers into a linked list, in which case we don't want to trace it.
  1.3034 +     */
  1.3035 +    if (!proxy->is<CrossCompartmentWrapperObject>())
  1.3036 +        MarkSlot(trc, proxy->slotOfExtra(1), "extra1");
  1.3037 +
  1.3038 +    /*
  1.3039 +     * Allow for people to add extra slots to "proxy" classes, without allowing
  1.3040 +     * them to set their own trace hook. Trace the extras.
  1.3041 +     */
  1.3042 +    unsigned numSlots = JSCLASS_RESERVED_SLOTS(proxy->getClass());
  1.3043 +    for (unsigned i = PROXY_MINIMUM_SLOTS; i < numSlots; i++)
  1.3044 +        MarkSlot(trc, proxy->slotOfClassSpecific(i), "class-specific");
  1.3045 +}
  1.3046 +
  1.3047 +JSObject *
  1.3048 +js::proxy_WeakmapKeyDelegate(JSObject *obj)
  1.3049 +{
  1.3050 +    JS_ASSERT(obj->is<ProxyObject>());
  1.3051 +    return obj->as<ProxyObject>().handler()->weakmapKeyDelegate(obj);
  1.3052 +}
  1.3053 +
  1.3054 +bool
  1.3055 +js::proxy_Convert(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
  1.3056 +{
  1.3057 +    JS_ASSERT(proxy->is<ProxyObject>());
  1.3058 +    return Proxy::defaultValue(cx, proxy, hint, vp);
  1.3059 +}
  1.3060 +
  1.3061 +void
  1.3062 +js::proxy_Finalize(FreeOp *fop, JSObject *obj)
  1.3063 +{
  1.3064 +    JS_ASSERT(obj->is<ProxyObject>());
  1.3065 +    obj->as<ProxyObject>().handler()->finalize(fop, obj);
  1.3066 +}
  1.3067 +
  1.3068 +bool
  1.3069 +js::proxy_HasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
  1.3070 +{
  1.3071 +    bool b;
  1.3072 +    if (!Proxy::hasInstance(cx, proxy, v, &b))
  1.3073 +        return false;
  1.3074 +    *bp = !!b;
  1.3075 +    return true;
  1.3076 +}
  1.3077 +
  1.3078 +bool
  1.3079 +js::proxy_Call(JSContext *cx, unsigned argc, Value *vp)
  1.3080 +{
  1.3081 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.3082 +    RootedObject proxy(cx, &args.callee());
  1.3083 +    JS_ASSERT(proxy->is<ProxyObject>());
  1.3084 +    return Proxy::call(cx, proxy, args);
  1.3085 +}
  1.3086 +
  1.3087 +bool
  1.3088 +js::proxy_Construct(JSContext *cx, unsigned argc, Value *vp)
  1.3089 +{
  1.3090 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.3091 +    RootedObject proxy(cx, &args.callee());
  1.3092 +    JS_ASSERT(proxy->is<ProxyObject>());
  1.3093 +    return Proxy::construct(cx, proxy, args);
  1.3094 +}
  1.3095 +
  1.3096 +bool
  1.3097 +js::proxy_Watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
  1.3098 +{
  1.3099 +    return Proxy::watch(cx, obj, id, callable);
  1.3100 +}
  1.3101 +
  1.3102 +bool
  1.3103 +js::proxy_Unwatch(JSContext *cx, HandleObject obj, HandleId id)
  1.3104 +{
  1.3105 +    return Proxy::unwatch(cx, obj, id);
  1.3106 +}
  1.3107 +
  1.3108 +bool
  1.3109 +js::proxy_Slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
  1.3110 +                HandleObject result)
  1.3111 +{
  1.3112 +    return Proxy::slice(cx, proxy, begin, end, result);
  1.3113 +}
  1.3114 +
  1.3115 +#define PROXY_CLASS(callOp, constructOp)                        \
  1.3116 +    PROXY_CLASS_DEF("Proxy",                                    \
  1.3117 +                    0, /* additional slots */                   \
  1.3118 +                    JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),    \
  1.3119 +                    callOp,                                     \
  1.3120 +                    constructOp)
  1.3121 +
  1.3122 +const Class js::ProxyObject::uncallableClass_ = PROXY_CLASS(nullptr, nullptr);
  1.3123 +const Class js::ProxyObject::callableClass_ = PROXY_CLASS(proxy_Call, proxy_Construct);
  1.3124 +
  1.3125 +const Class* const js::CallableProxyClassPtr = &ProxyObject::callableClass_;
  1.3126 +const Class* const js::UncallableProxyClassPtr = &ProxyObject::uncallableClass_;
  1.3127 +
  1.3128 +JS_FRIEND_API(JSObject *)
  1.3129 +js::NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, JSObject *proto_,
  1.3130 +                   JSObject *parent_, const ProxyOptions &options)
  1.3131 +{
  1.3132 +    return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), parent_,
  1.3133 +                            options);
  1.3134 +}
  1.3135 +
  1.3136 +void
  1.3137 +ProxyObject::renew(JSContext *cx, BaseProxyHandler *handler, Value priv)
  1.3138 +{
  1.3139 +    JS_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
  1.3140 +    JS_ASSERT(getParent() == cx->global());
  1.3141 +    JS_ASSERT(getClass() == &uncallableClass_);
  1.3142 +    JS_ASSERT(!getClass()->ext.innerObject);
  1.3143 +    JS_ASSERT(getTaggedProto().isLazy());
  1.3144 +
  1.3145 +    setSlot(HANDLER_SLOT, PrivateValue(handler));
  1.3146 +    setCrossCompartmentSlot(PRIVATE_SLOT, priv);
  1.3147 +    setSlot(EXTRA_SLOT + 0, UndefinedValue());
  1.3148 +    setSlot(EXTRA_SLOT + 1, UndefinedValue());
  1.3149 +}
  1.3150 +
  1.3151 +static bool
  1.3152 +proxy(JSContext *cx, unsigned argc, jsval *vp)
  1.3153 +{
  1.3154 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.3155 +    if (args.length() < 2) {
  1.3156 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  1.3157 +                             "Proxy", "1", "s");
  1.3158 +        return false;
  1.3159 +    }
  1.3160 +    RootedObject target(cx, NonNullObject(cx, args[0]));
  1.3161 +    if (!target)
  1.3162 +        return false;
  1.3163 +    RootedObject handler(cx, NonNullObject(cx, args[1]));
  1.3164 +    if (!handler)
  1.3165 +        return false;
  1.3166 +    RootedValue priv(cx, ObjectValue(*target));
  1.3167 +    ProxyOptions options;
  1.3168 +    options.selectDefaultClass(target->isCallable());
  1.3169 +    ProxyObject *proxy =
  1.3170 +        ProxyObject::New(cx, &ScriptedDirectProxyHandler::singleton,
  1.3171 +                         priv, TaggedProto(TaggedProto::LazyProto), cx->global(),
  1.3172 +                         options);
  1.3173 +    if (!proxy)
  1.3174 +        return false;
  1.3175 +    proxy->setExtra(0, ObjectOrNullValue(handler));
  1.3176 +    args.rval().setObject(*proxy);
  1.3177 +    return true;
  1.3178 +}
  1.3179 +
  1.3180 +static bool
  1.3181 +proxy_create(JSContext *cx, unsigned argc, Value *vp)
  1.3182 +{
  1.3183 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.3184 +    if (args.length() < 1) {
  1.3185 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  1.3186 +                             "create", "0", "s");
  1.3187 +        return false;
  1.3188 +    }
  1.3189 +    JSObject *handler = NonNullObject(cx, args[0]);
  1.3190 +    if (!handler)
  1.3191 +        return false;
  1.3192 +    JSObject *proto, *parent = nullptr;
  1.3193 +    if (args.get(1).isObject()) {
  1.3194 +        proto = &args[1].toObject();
  1.3195 +        parent = proto->getParent();
  1.3196 +    } else {
  1.3197 +        JS_ASSERT(IsFunctionObject(&args.callee()));
  1.3198 +        proto = nullptr;
  1.3199 +    }
  1.3200 +    if (!parent)
  1.3201 +        parent = args.callee().getParent();
  1.3202 +    RootedValue priv(cx, ObjectValue(*handler));
  1.3203 +    JSObject *proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton,
  1.3204 +                                     priv, proto, parent);
  1.3205 +    if (!proxy)
  1.3206 +        return false;
  1.3207 +
  1.3208 +    args.rval().setObject(*proxy);
  1.3209 +    return true;
  1.3210 +}
  1.3211 +
  1.3212 +static bool
  1.3213 +proxy_createFunction(JSContext *cx, unsigned argc, Value *vp)
  1.3214 +{
  1.3215 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.3216 +    if (args.length() < 2) {
  1.3217 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
  1.3218 +                             "createFunction", "1", "");
  1.3219 +        return false;
  1.3220 +    }
  1.3221 +    RootedObject handler(cx, NonNullObject(cx, args[0]));
  1.3222 +    if (!handler)
  1.3223 +        return false;
  1.3224 +    RootedObject proto(cx), parent(cx);
  1.3225 +    parent = args.callee().getParent();
  1.3226 +    proto = parent->global().getOrCreateFunctionPrototype(cx);
  1.3227 +    if (!proto)
  1.3228 +        return false;
  1.3229 +    parent = proto->getParent();
  1.3230 +
  1.3231 +    RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2));
  1.3232 +    if (!call)
  1.3233 +        return false;
  1.3234 +    RootedObject construct(cx, nullptr);
  1.3235 +    if (args.length() > 2) {
  1.3236 +        construct = ValueToCallable(cx, args[2], args.length() - 3);
  1.3237 +        if (!construct)
  1.3238 +            return false;
  1.3239 +    } else {
  1.3240 +        construct = call;
  1.3241 +    }
  1.3242 +
  1.3243 +    // Stash the call and construct traps on a holder object that we can stick
  1.3244 +    // in a slot on the proxy.
  1.3245 +    RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder),
  1.3246 +                                                         js::NullPtr(), cx->global()));
  1.3247 +    if (!ccHolder)
  1.3248 +        return false;
  1.3249 +    ccHolder->setReservedSlot(0, ObjectValue(*call));
  1.3250 +    ccHolder->setReservedSlot(1, ObjectValue(*construct));
  1.3251 +
  1.3252 +    RootedValue priv(cx, ObjectValue(*handler));
  1.3253 +    ProxyOptions options;
  1.3254 +    options.selectDefaultClass(true);
  1.3255 +    JSObject *proxy =
  1.3256 +        ProxyObject::New(cx, &ScriptedIndirectProxyHandler::singleton,
  1.3257 +                         priv, TaggedProto(proto), parent, options);
  1.3258 +    if (!proxy)
  1.3259 +        return false;
  1.3260 +    proxy->as<ProxyObject>().setExtra(0, ObjectValue(*ccHolder));
  1.3261 +
  1.3262 +    args.rval().setObject(*proxy);
  1.3263 +    return true;
  1.3264 +}
  1.3265 +
  1.3266 +JS_FRIEND_API(JSObject *)
  1.3267 +js_InitProxyClass(JSContext *cx, HandleObject obj)
  1.3268 +{
  1.3269 +    static const JSFunctionSpec static_methods[] = {
  1.3270 +        JS_FN("create",         proxy_create,          2, 0),
  1.3271 +        JS_FN("createFunction", proxy_createFunction,  3, 0),
  1.3272 +        JS_FS_END
  1.3273 +    };
  1.3274 +
  1.3275 +    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  1.3276 +    RootedFunction ctor(cx);
  1.3277 +    ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
  1.3278 +    if (!ctor)
  1.3279 +        return nullptr;
  1.3280 +
  1.3281 +    if (!JS_DefineFunctions(cx, ctor, static_methods))
  1.3282 +        return nullptr;
  1.3283 +    if (!JS_DefineProperty(cx, obj, "Proxy", ctor, 0,
  1.3284 +                           JS_PropertyStub, JS_StrictPropertyStub)) {
  1.3285 +        return nullptr;
  1.3286 +    }
  1.3287 +
  1.3288 +    global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
  1.3289 +    return ctor;
  1.3290 +}

mercurial