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, ¤t)) 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 +}