Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #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, ¤t)) |
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 | } |