js/src/jsproxy.cpp

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

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

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

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

mercurial