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