1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
22 * Portions created by the Initial Developer are Copyright (C) 2009
23 * the Initial Developer. All Rights Reserved.
26 * Andreas Gal <gal@mozilla.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
51 #include "jsobjinlines.h"
57 static inline const Value
&
58 GetCall(JSObject
*proxy
) {
59 JS_ASSERT(proxy
->isFunctionProxy());
60 return proxy
->getSlot(JSSLOT_PROXY_CALL
);
64 GetConstruct(JSObject
*proxy
) {
65 if (proxy
->numSlots() <= JSSLOT_PROXY_CONSTRUCT
)
66 return UndefinedValue();
67 return proxy
->getSlot(JSSLOT_PROXY_CONSTRUCT
);
71 OperationInProgress(JSContext
*cx
, JSObject
*proxy
)
73 JSPendingProxyOperation
*op
= JS_THREAD_DATA(cx
)->pendingProxyOperation
;
75 if (op
->object
== proxy
)
82 JSProxyHandler::JSProxyHandler(void *family
) : mFamily(family
)
86 JSProxyHandler::~JSProxyHandler()
91 JSProxyHandler::has(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
93 JS_ASSERT(OperationInProgress(cx
, proxy
));
94 AutoPropertyDescriptorRooter
desc(cx
);
95 if (!getPropertyDescriptor(cx
, proxy
, id
, &desc
))
102 JSProxyHandler::hasOwn(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
104 JS_ASSERT(OperationInProgress(cx
, proxy
));
105 AutoPropertyDescriptorRooter
desc(cx
);
106 if (!getOwnPropertyDescriptor(cx
, proxy
, id
, &desc
))
113 JSProxyHandler::get(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
115 JS_ASSERT(OperationInProgress(cx
, proxy
));
116 AutoPropertyDescriptorRooter
desc(cx
);
117 if (!getPropertyDescriptor(cx
, proxy
, id
, &desc
))
127 if (desc
.attrs
& JSPROP_GETTER
) {
128 return InternalGetOrSet(cx
, proxy
, id
, CastAsObjectJsval(desc
.getter
),
129 JSACC_READ
, 0, 0, vp
);
131 if (desc
.attrs
& JSPROP_SHORTID
)
132 id
= INT_TO_JSID(desc
.shortid
);
133 return callJSPropertyOp(cx
, desc
.getter
, proxy
, id
, vp
);
137 JSProxyHandler::set(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
139 JS_ASSERT(OperationInProgress(cx
, proxy
));
140 AutoPropertyDescriptorRooter
desc(cx
);
141 if (!getOwnPropertyDescriptor(cx
, proxy
, id
, &desc
))
143 /* The control-flow here differs from ::get() because of the fall-through case below. */
146 if (desc
.attrs
& JSPROP_SETTER
) {
147 return InternalGetOrSet(cx
, proxy
, id
, CastAsObjectJsval(desc
.setter
),
148 JSACC_READ
, 0, 0, vp
);
150 if (desc
.attrs
& JSPROP_SHORTID
)
151 id
= INT_TO_JSID(desc
.shortid
);
152 return callJSPropertyOpSetter(cx
, desc
.setter
, proxy
, id
, vp
);
154 if (desc
.attrs
& JSPROP_READONLY
)
157 return defineProperty(cx
, proxy
, id
, &desc
);
159 if (!getPropertyDescriptor(cx
, proxy
, id
, &desc
))
163 if (desc
.attrs
& JSPROP_SETTER
) {
164 return InternalGetOrSet(cx
, proxy
, id
, CastAsObjectJsval(desc
.setter
),
165 JSACC_READ
, 0, 0, vp
);
167 if (desc
.attrs
& JSPROP_SHORTID
)
168 id
= INT_TO_JSID(desc
.shortid
);
169 return callJSPropertyOpSetter(cx
, desc
.setter
, proxy
, id
, vp
);
171 if (desc
.attrs
& JSPROP_READONLY
)
181 return defineProperty(cx
, proxy
, id
, &desc
);
185 JSProxyHandler::enumerateOwn(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
187 JS_ASSERT(OperationInProgress(cx
, proxy
));
188 JS_ASSERT(props
.length() == 0);
190 if (!getOwnPropertyNames(cx
, proxy
, props
))
193 /* Select only the enumerable properties through in-place iteration. */
194 AutoPropertyDescriptorRooter
desc(cx
);
196 for (size_t j
= 0, len
= props
.length(); j
< len
; j
++) {
199 if (!getOwnPropertyDescriptor(cx
, proxy
, id
, &desc
))
201 if (desc
.obj
&& (desc
.attrs
& JSPROP_ENUMERATE
))
205 JS_ASSERT(i
<= props
.length());
212 JSProxyHandler::iterate(JSContext
*cx
, JSObject
*proxy
, uintN flags
, Value
*vp
)
214 JS_ASSERT(OperationInProgress(cx
, proxy
));
215 AutoIdVector
props(cx
);
216 if (!enumerate(cx
, proxy
, props
))
218 return EnumeratedIdVectorToIterator(cx
, proxy
, flags
, props
, vp
);
222 JSProxyHandler::obj_toString(JSContext
*cx
, JSObject
*proxy
)
224 JS_ASSERT(proxy
->isProxy());
226 return JS_NewStringCopyZ(cx
, proxy
->isFunctionProxy()
227 ? "[object Function]"
228 : "[object Object]");
232 JSProxyHandler::fun_toString(JSContext
*cx
, JSObject
*proxy
, uintN indent
)
234 JS_ASSERT(proxy
->isProxy());
235 Value fval
= GetCall(proxy
);
236 if (proxy
->isFunctionProxy() &&
237 (fval
.isPrimitive() || !fval
.toObject().isFunction())) {
238 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
239 JSMSG_INCOMPATIBLE_PROTO
,
240 js_Function_str
, js_toString_str
,
244 return fun_toStringHelper(cx
, &fval
.toObject(), indent
);
248 JSProxyHandler::call(JSContext
*cx
, JSObject
*proxy
, uintN argc
, Value
*vp
)
250 JS_ASSERT(OperationInProgress(cx
, proxy
));
251 AutoValueRooter
rval(cx
);
252 JSBool ok
= InternalInvoke(cx
, vp
[1], GetCall(proxy
), 0, argc
, JS_ARGV(cx
, vp
),
255 JS_SET_RVAL(cx
, vp
, rval
.value());
260 JSProxyHandler::construct(JSContext
*cx
, JSObject
*proxy
,
261 uintN argc
, Value
*argv
, Value
*rval
)
263 JS_ASSERT(OperationInProgress(cx
, proxy
));
264 Value fval
= GetConstruct(proxy
);
265 if (fval
.isUndefined()) {
266 fval
= GetCall(proxy
);
267 JSObject
*obj
= JS_New(cx
, &fval
.toObject(), argc
, Jsvalify(argv
));
270 rval
->setObject(*obj
);
275 * FIXME: The Proxy proposal says to pass undefined as the this argument,
276 * but primitive this is not supported yet. See bug 576644.
278 JS_ASSERT(fval
.isObject());
279 JSObject
*thisobj
= fval
.toObject().getGlobal();
280 return InternalCall(cx
, thisobj
, fval
, argc
, argv
, rval
);
284 JSProxyHandler::finalize(JSContext
*cx
, JSObject
*proxy
)
289 JSProxyHandler::trace(JSTracer
*trc
, JSObject
*proxy
)
294 GetTrap(JSContext
*cx
, JSObject
*handler
, JSAtom
*atom
, Value
*fvalp
)
296 return handler
->getProperty(cx
, ATOM_TO_JSID(atom
), fvalp
);
300 FundamentalTrap(JSContext
*cx
, JSObject
*handler
, JSAtom
*atom
, Value
*fvalp
)
302 if (!GetTrap(cx
, handler
, atom
, fvalp
))
305 if (!js_IsCallable(*fvalp
)) {
306 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_FUNCTION
,
307 js_AtomToPrintableString(cx
, atom
));
315 DerivedTrap(JSContext
*cx
, JSObject
*handler
, JSAtom
*atom
, Value
*fvalp
)
317 JS_ASSERT(atom
== ATOM(has
) ||
318 atom
== ATOM(hasOwn
) ||
321 atom
== ATOM(enumerateOwn
) ||
322 atom
== ATOM(iterate
));
324 return GetTrap(cx
, handler
, atom
, fvalp
);
328 Trap(JSContext
*cx
, JSObject
*handler
, Value fval
, uintN argc
, Value
* argv
, Value
*rval
)
330 JS_CHECK_RECURSION(cx
, return false);
332 return InternalCall(cx
, handler
, fval
, argc
, argv
, rval
);
336 Trap1(JSContext
*cx
, JSObject
*handler
, Value fval
, jsid id
, Value
*rval
)
338 JSString
*str
= js_ValueToString(cx
, IdToValue(id
));
341 rval
->setString(str
);
342 return Trap(cx
, handler
, fval
, 1, rval
, rval
);
346 Trap2(JSContext
*cx
, JSObject
*handler
, Value fval
, jsid id
, Value v
, Value
*rval
)
348 JSString
*str
= js_ValueToString(cx
, IdToValue(id
));
351 rval
->setString(str
);
352 Value argv
[2] = { *rval
, v
};
353 return Trap(cx
, handler
, fval
, 2, argv
, rval
);
357 ParsePropertyDescriptorObject(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&v
,
358 PropertyDescriptor
*desc
)
360 AutoPropDescArrayRooter
descs(cx
);
361 PropDesc
*d
= descs
.append();
362 if (!d
|| !d
->initialize(cx
, id
, v
))
365 desc
->value
= d
->value
;
366 JS_ASSERT(!(d
->attrs
& JSPROP_SHORTID
));
367 desc
->attrs
= d
->attrs
;
368 desc
->getter
= d
->getter();
369 desc
->setter
= d
->setter();
375 MakePropertyDescriptorObject(JSContext
*cx
, jsid id
, PropertyDescriptor
*desc
, Value
*vp
)
381 uintN attrs
= desc
->attrs
;
382 Value getter
= (attrs
& JSPROP_GETTER
) ? CastAsObjectJsval(desc
->getter
) : UndefinedValue();
383 Value setter
= (attrs
& JSPROP_SETTER
) ? CastAsObjectJsval(desc
->setter
) : UndefinedValue();
384 return js_NewPropertyDescriptorObject(cx
, id
, attrs
, getter
, setter
, desc
->value
, vp
);
388 ValueToBool(JSContext
*cx
, const Value
&v
, bool *bp
)
390 *bp
= !!js_ValueToBoolean(v
);
395 ArrayToIdVector(JSContext
*cx
, const Value
&array
, AutoIdVector
&props
)
397 JS_ASSERT(props
.length() == 0);
399 if (array
.isPrimitive())
402 JSObject
*obj
= &array
.toObject();
404 if (!js_GetLengthProperty(cx
, obj
, &length
)) {
405 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_ARRAY_LENGTH
);
409 AutoIdRooter
idr(cx
);
410 AutoValueRooter
tvr(cx
);
411 for (jsuint n
= 0; n
< length
; ++n
) {
412 if (!js_IndexToId(cx
, n
, idr
.addr()))
414 if (!obj
->getProperty(cx
, idr
.id(), tvr
.addr()))
416 if (!ValueToId(cx
, tvr
.value(), idr
.addr()))
418 if (!props
.append(js_CheckForStringIndex(idr
.id())))
425 /* Derived class for all scripted proxy handlers. */
426 class JSScriptedProxyHandler
: public JSProxyHandler
{
428 JSScriptedProxyHandler();
429 virtual ~JSScriptedProxyHandler();
431 /* ES5 Harmony fundamental proxy traps. */
432 virtual bool getPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
,
433 PropertyDescriptor
*desc
);
434 virtual bool getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
,
435 PropertyDescriptor
*desc
);
436 virtual bool defineProperty(JSContext
*cx
, JSObject
*proxy
, jsid id
,
437 PropertyDescriptor
*desc
);
438 virtual bool getOwnPropertyNames(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
);
439 virtual bool delete_(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
);
440 virtual bool enumerate(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
);
441 virtual bool fix(JSContext
*cx
, JSObject
*proxy
, Value
*vp
);
443 /* ES5 Harmony derived proxy traps. */
444 virtual bool has(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
);
445 virtual bool hasOwn(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
);
446 virtual bool get(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
);
447 virtual bool set(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
);
448 virtual bool enumerateOwn(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
);
449 virtual bool iterate(JSContext
*cx
, JSObject
*proxy
, uintN flags
, Value
*vp
);
451 static JSScriptedProxyHandler singleton
;
454 static int sScriptedProxyHandlerFamily
= 0;
456 JSScriptedProxyHandler::JSScriptedProxyHandler() : JSProxyHandler(&sScriptedProxyHandlerFamily
)
460 JSScriptedProxyHandler::~JSScriptedProxyHandler()
465 ReturnedValueMustNotBePrimitive(JSContext
*cx
, JSObject
*proxy
, JSAtom
*atom
, const Value
&v
)
467 if (v
.isPrimitive()) {
468 js_ReportValueError2(cx
, JSMSG_BAD_TRAP_RETURN_VALUE
,
469 JSDVG_SEARCH_STACK
, ObjectOrNullValue(proxy
), NULL
,
470 js_AtomToPrintableString(cx
, atom
));
477 GetProxyHandlerObject(JSContext
*cx
, JSObject
*proxy
)
479 JS_ASSERT(OperationInProgress(cx
, proxy
));
480 return proxy
->getProxyPrivate().toObjectOrNull();
484 JSScriptedProxyHandler::getPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
,
485 PropertyDescriptor
*desc
)
487 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
488 AutoValueRooter
tvr(cx
);
489 return FundamentalTrap(cx
, handler
, ATOM(getPropertyDescriptor
), tvr
.addr()) &&
490 Trap1(cx
, handler
, tvr
.value(), id
, tvr
.addr()) &&
491 ReturnedValueMustNotBePrimitive(cx
, proxy
, ATOM(getPropertyDescriptor
), tvr
.value()) &&
492 ParsePropertyDescriptorObject(cx
, proxy
, id
, tvr
.value(), desc
);
496 JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
,
497 PropertyDescriptor
*desc
)
499 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
500 AutoValueRooter
tvr(cx
);
501 return FundamentalTrap(cx
, handler
, ATOM(getOwnPropertyDescriptor
), tvr
.addr()) &&
502 Trap1(cx
, handler
, tvr
.value(), id
, tvr
.addr()) &&
503 ReturnedValueMustNotBePrimitive(cx
, proxy
, ATOM(getPropertyDescriptor
), tvr
.value()) &&
504 ParsePropertyDescriptorObject(cx
, proxy
, id
, tvr
.value(), desc
);
508 JSScriptedProxyHandler::defineProperty(JSContext
*cx
, JSObject
*proxy
, jsid id
,
509 PropertyDescriptor
*desc
)
511 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
512 AutoValueRooter
tvr(cx
);
513 AutoValueRooter
fval(cx
);
514 return FundamentalTrap(cx
, handler
, ATOM(defineProperty
), fval
.addr()) &&
515 MakePropertyDescriptorObject(cx
, id
, desc
, tvr
.addr()) &&
516 Trap2(cx
, handler
, fval
.value(), id
, tvr
.value(), tvr
.addr());
520 JSScriptedProxyHandler::getOwnPropertyNames(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
522 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
523 AutoValueRooter
tvr(cx
);
524 return FundamentalTrap(cx
, handler
, ATOM(getOwnPropertyNames
), tvr
.addr()) &&
525 Trap(cx
, handler
, tvr
.value(), 0, NULL
, tvr
.addr()) &&
526 ArrayToIdVector(cx
, tvr
.value(), props
);
530 JSScriptedProxyHandler::delete_(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
532 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
533 AutoValueRooter
tvr(cx
);
534 return FundamentalTrap(cx
, handler
, ATOM(delete), tvr
.addr()) &&
535 Trap1(cx
, handler
, tvr
.value(), id
, tvr
.addr()) &&
536 ValueToBool(cx
, tvr
.value(), bp
);
540 JSScriptedProxyHandler::enumerate(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
542 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
543 AutoValueRooter
tvr(cx
);
544 return FundamentalTrap(cx
, handler
, ATOM(enumerate
), tvr
.addr()) &&
545 Trap(cx
, handler
, tvr
.value(), 0, NULL
, tvr
.addr()) &&
546 ArrayToIdVector(cx
, tvr
.value(), props
);
550 JSScriptedProxyHandler::fix(JSContext
*cx
, JSObject
*proxy
, Value
*vp
)
552 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
553 return FundamentalTrap(cx
, handler
, ATOM(fix
), vp
) &&
554 Trap(cx
, handler
, *vp
, 0, NULL
, vp
);
558 JSScriptedProxyHandler::has(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
560 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
561 AutoValueRooter
tvr(cx
);
562 if (!DerivedTrap(cx
, handler
, ATOM(has
), tvr
.addr()))
564 if (!js_IsCallable(tvr
.value()))
565 return JSProxyHandler::has(cx
, proxy
, id
, bp
);
566 return Trap1(cx
, handler
, tvr
.value(), id
, tvr
.addr()) &&
567 ValueToBool(cx
, tvr
.value(), bp
);
571 JSScriptedProxyHandler::hasOwn(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
573 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
574 AutoValueRooter
tvr(cx
);
575 if (!DerivedTrap(cx
, handler
, ATOM(hasOwn
), tvr
.addr()))
577 if (!js_IsCallable(tvr
.value()))
578 return JSProxyHandler::hasOwn(cx
, proxy
, id
, bp
);
579 return Trap1(cx
, handler
, tvr
.value(), id
, tvr
.addr()) &&
580 ValueToBool(cx
, tvr
.value(), bp
);
584 JSScriptedProxyHandler::get(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
586 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
587 JSString
*str
= js_ValueToString(cx
, IdToValue(id
));
590 AutoValueRooter
tvr(cx
, StringValue(str
));
591 Value argv
[] = { ObjectOrNullValue(receiver
), tvr
.value() };
592 AutoValueRooter
fval(cx
);
593 if (!DerivedTrap(cx
, handler
, ATOM(get
), fval
.addr()))
595 if (!js_IsCallable(fval
.value()))
596 return JSProxyHandler::get(cx
, proxy
, receiver
, id
, vp
);
597 return Trap(cx
, handler
, fval
.value(), 2, argv
, vp
);
601 JSScriptedProxyHandler::set(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
603 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
604 JSString
*str
= js_ValueToString(cx
, IdToValue(id
));
607 AutoValueRooter
tvr(cx
, StringValue(str
));
608 Value argv
[] = { ObjectOrNullValue(receiver
), tvr
.value(), *vp
};
609 AutoValueRooter
fval(cx
);
610 if (!DerivedTrap(cx
, handler
, ATOM(set
), fval
.addr()))
612 if (!js_IsCallable(fval
.value()))
613 return JSProxyHandler::set(cx
, proxy
, receiver
, id
, vp
);
614 return Trap(cx
, handler
, fval
.value(), 3, argv
, tvr
.addr());
618 JSScriptedProxyHandler::enumerateOwn(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
620 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
621 AutoValueRooter
tvr(cx
);
622 if (!DerivedTrap(cx
, handler
, ATOM(enumerateOwn
), tvr
.addr()))
624 if (!js_IsCallable(tvr
.value()))
625 return JSProxyHandler::enumerateOwn(cx
, proxy
, props
);
626 return Trap(cx
, handler
, tvr
.value(), 0, NULL
, tvr
.addr()) &&
627 ArrayToIdVector(cx
, tvr
.value(), props
);
631 JSScriptedProxyHandler::iterate(JSContext
*cx
, JSObject
*proxy
, uintN flags
, Value
*vp
)
633 JSObject
*handler
= GetProxyHandlerObject(cx
, proxy
);
634 AutoValueRooter
tvr(cx
);
635 if (!DerivedTrap(cx
, handler
, ATOM(iterate
), tvr
.addr()))
637 if (!js_IsCallable(tvr
.value()))
638 return JSProxyHandler::iterate(cx
, proxy
, flags
, vp
);
639 return Trap(cx
, handler
, tvr
.value(), 0, NULL
, vp
) &&
640 ReturnedValueMustNotBePrimitive(cx
, proxy
, ATOM(iterate
), *vp
);
643 JSScriptedProxyHandler
JSScriptedProxyHandler::singleton
;
645 class AutoPendingProxyOperation
{
647 JSPendingProxyOperation op
;
649 AutoPendingProxyOperation(JSContext
*cx
, JSObject
*proxy
) : data(JS_THREAD_DATA(cx
)) {
650 op
.next
= data
->pendingProxyOperation
;
652 data
->pendingProxyOperation
= &op
;
655 ~AutoPendingProxyOperation() {
656 JS_ASSERT(data
->pendingProxyOperation
== &op
);
657 data
->pendingProxyOperation
= op
.next
;
662 JSProxy::getPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
, PropertyDescriptor
*desc
)
664 AutoPendingProxyOperation
pending(cx
, proxy
);
665 return proxy
->getProxyHandler()->getPropertyDescriptor(cx
, proxy
, id
, desc
);
669 JSProxy::getPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
, Value
*vp
)
671 AutoPendingProxyOperation
pending(cx
, proxy
);
672 AutoPropertyDescriptorRooter
desc(cx
);
673 return JSProxy::getPropertyDescriptor(cx
, proxy
, id
, &desc
) &&
674 MakePropertyDescriptorObject(cx
, id
, &desc
, vp
);
678 JSProxy::getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
,
679 PropertyDescriptor
*desc
)
681 AutoPendingProxyOperation
pending(cx
, proxy
);
682 return proxy
->getProxyHandler()->getOwnPropertyDescriptor(cx
, proxy
, id
, desc
);
686 JSProxy::getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*proxy
, jsid id
, Value
*vp
)
688 AutoPendingProxyOperation
pending(cx
, proxy
);
689 AutoPropertyDescriptorRooter
desc(cx
);
690 return JSProxy::getOwnPropertyDescriptor(cx
, proxy
, id
, &desc
) &&
691 MakePropertyDescriptorObject(cx
, id
, &desc
, vp
);
695 JSProxy::defineProperty(JSContext
*cx
, JSObject
*proxy
, jsid id
, PropertyDescriptor
*desc
)
697 AutoPendingProxyOperation
pending(cx
, proxy
);
698 return proxy
->getProxyHandler()->defineProperty(cx
, proxy
, id
, desc
);
702 JSProxy::defineProperty(JSContext
*cx
, JSObject
*proxy
, jsid id
, const Value
&v
)
704 AutoPendingProxyOperation
pending(cx
, proxy
);
705 AutoPropertyDescriptorRooter
desc(cx
);
706 return ParsePropertyDescriptorObject(cx
, proxy
, id
, v
, &desc
) &&
707 JSProxy::defineProperty(cx
, proxy
, id
, &desc
);
711 JSProxy::getOwnPropertyNames(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
713 AutoPendingProxyOperation
pending(cx
, proxy
);
714 return proxy
->getProxyHandler()->getOwnPropertyNames(cx
, proxy
, props
);
718 JSProxy::delete_(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
720 AutoPendingProxyOperation
pending(cx
, proxy
);
721 return proxy
->getProxyHandler()->delete_(cx
, proxy
, id
, bp
);
725 JSProxy::enumerate(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
727 AutoPendingProxyOperation
pending(cx
, proxy
);
728 return proxy
->getProxyHandler()->enumerate(cx
, proxy
, props
);
732 JSProxy::fix(JSContext
*cx
, JSObject
*proxy
, Value
*vp
)
734 AutoPendingProxyOperation
pending(cx
, proxy
);
735 return proxy
->getProxyHandler()->fix(cx
, proxy
, vp
);
739 JSProxy::has(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
741 AutoPendingProxyOperation
pending(cx
, proxy
);
742 return proxy
->getProxyHandler()->has(cx
, proxy
, id
, bp
);
746 JSProxy::hasOwn(JSContext
*cx
, JSObject
*proxy
, jsid id
, bool *bp
)
748 AutoPendingProxyOperation
pending(cx
, proxy
);
749 return proxy
->getProxyHandler()->hasOwn(cx
, proxy
, id
, bp
);
753 JSProxy::get(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
755 AutoPendingProxyOperation
pending(cx
, proxy
);
756 return proxy
->getProxyHandler()->get(cx
, proxy
, receiver
, id
, vp
);
760 JSProxy::set(JSContext
*cx
, JSObject
*proxy
, JSObject
*receiver
, jsid id
, Value
*vp
)
762 AutoPendingProxyOperation
pending(cx
, proxy
);
763 return proxy
->getProxyHandler()->set(cx
, proxy
, receiver
, id
, vp
);
767 JSProxy::enumerateOwn(JSContext
*cx
, JSObject
*proxy
, AutoIdVector
&props
)
769 AutoPendingProxyOperation
pending(cx
, proxy
);
770 return proxy
->getProxyHandler()->enumerateOwn(cx
, proxy
, props
);
774 JSProxy::iterate(JSContext
*cx
, JSObject
*proxy
, uintN flags
, Value
*vp
)
776 AutoPendingProxyOperation
pending(cx
, proxy
);
777 return proxy
->getProxyHandler()->iterate(cx
, proxy
, flags
, vp
);
781 JSProxy::call(JSContext
*cx
, JSObject
*proxy
, uintN argc
, Value
*vp
)
783 AutoPendingProxyOperation
pending(cx
, proxy
);
784 return proxy
->getProxyHandler()->call(cx
, proxy
, argc
, vp
);
788 JSProxy::construct(JSContext
*cx
, JSObject
*proxy
, uintN argc
, Value
*argv
, Value
*rval
)
790 AutoPendingProxyOperation
pending(cx
, proxy
);
791 return proxy
->getProxyHandler()->construct(cx
, proxy
, argc
, argv
, rval
);
795 JSProxy::obj_toString(JSContext
*cx
, JSObject
*proxy
)
797 AutoPendingProxyOperation
pending(cx
, proxy
);
798 return proxy
->getProxyHandler()->obj_toString(cx
, proxy
);
802 JSProxy::fun_toString(JSContext
*cx
, JSObject
*proxy
, uintN indent
)
804 AutoPendingProxyOperation
pending(cx
, proxy
);
805 return proxy
->getProxyHandler()->fun_toString(cx
, proxy
, indent
);
809 proxy_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
813 if (!JSProxy::has(cx
, obj
, id
, &found
))
817 *propp
= (JSProperty
*)0x1;
827 proxy_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
828 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
830 AutoPropertyDescriptorRooter
desc(cx
);
833 desc
.attrs
= (attrs
& (~JSPROP_SHORTID
));
834 desc
.getter
= getter
;
835 desc
.setter
= setter
;
837 return JSProxy::defineProperty(cx
, obj
, id
, &desc
);
841 proxy_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
843 return JSProxy::get(cx
, obj
, obj
, id
, vp
);
847 proxy_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
849 return JSProxy::set(cx
, obj
, obj
, id
, vp
);
853 proxy_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
855 AutoPropertyDescriptorRooter
desc(cx
);
856 if (!JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, &desc
))
858 *attrsp
= desc
.attrs
;
863 proxy_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
865 /* Lookup the current property descriptor so we have setter/getter/value. */
866 AutoPropertyDescriptorRooter
desc(cx
);
867 if (!JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, &desc
))
869 desc
.attrs
= (*attrsp
& (~JSPROP_SHORTID
));
870 return JSProxy::defineProperty(cx
, obj
, id
, &desc
);
874 proxy_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
)
877 if (!JSProxy::delete_(cx
, obj
, id
, &deleted
))
879 rval
->setBoolean(deleted
);
884 proxy_TraceObject(JSTracer
*trc
, JSObject
*obj
)
886 JSContext
*cx
= trc
->context
;
888 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
889 js_TraceWatchPoints(trc
, obj
);
891 obj
->getProxyHandler()->trace(trc
, obj
);
892 MarkValue(trc
, obj
->getProxyPrivate(), "private");
893 if (obj
->isFunctionProxy()) {
894 MarkValue(trc
, GetCall(obj
), "call");
895 MarkValue(trc
, GetConstruct(obj
), "construct");
900 proxy_Finalize(JSContext
*cx
, JSObject
*obj
)
902 JS_ASSERT(obj
->isProxy());
903 if (!obj
->getSlot(JSSLOT_PROXY_HANDLER
).isUndefined())
904 obj
->getProxyHandler()->finalize(cx
, obj
);
907 JS_FRIEND_API(Class
) ObjectProxyClass
= {
909 Class::NON_NATIVE
| JSCLASS_HAS_RESERVED_SLOTS(2),
910 PropertyStub
, /* addProperty */
911 PropertyStub
, /* delProperty */
912 PropertyStub
, /* getProperty */
913 PropertyStub
, /* setProperty */
918 NULL
, /* reserved0 */
919 NULL
, /* checkAccess */
921 NULL
, /* construct */
922 NULL
, /* xdrObject */
923 NULL
, /* hasInstance */
927 proxy_LookupProperty
,
928 proxy_DefineProperty
,
933 proxy_DeleteProperty
,
934 NULL
, /* enumerate */
937 NULL
, /* thisObject */
938 proxy_Finalize
, /* clear */
943 proxy_Call(JSContext
*cx
, uintN argc
, Value
*vp
)
945 JSObject
*proxy
= &JS_CALLEE(cx
, vp
).toObject();
946 JS_ASSERT(proxy
->isProxy());
947 return JSProxy::call(cx
, proxy
, argc
, vp
);
951 proxy_Construct(JSContext
*cx
, JSObject
* /*obj*/, uintN argc
, Value
*argv
, Value
*rval
)
953 JSObject
*proxy
= &argv
[-2].toObject();
954 JS_ASSERT(proxy
->isProxy());
955 return JSProxy::construct(cx
, proxy
, argc
, argv
, rval
);
959 proxy_TypeOf_fun(JSContext
*cx
, JSObject
*obj
)
961 return JSTYPE_FUNCTION
;
964 #define proxy_HasInstance js_FunctionClass.hasInstance
966 JS_FRIEND_API(Class
) FunctionProxyClass
= {
968 Class::NON_NATIVE
| JSCLASS_HAS_RESERVED_SLOTS(4) | Class::CALL_IS_FAST
,
969 PropertyStub
, /* addProperty */
970 PropertyStub
, /* delProperty */
971 PropertyStub
, /* getProperty */
972 PropertyStub
, /* setProperty */
977 NULL
, /* reserved0 */
978 NULL
, /* checkAccess */
979 CastCallOpAsNative(proxy_Call
),
981 NULL
, /* xdrObject */
986 proxy_LookupProperty
,
987 proxy_DefineProperty
,
992 proxy_DeleteProperty
,
993 NULL
, /* enumerate */
996 NULL
, /* thisObject */
1001 JS_FRIEND_API(JSObject
*)
1002 NewProxyObject(JSContext
*cx
, JSProxyHandler
*handler
, const Value
&priv
, JSObject
*proto
,
1003 JSObject
*parent
, JSObject
*call
, JSObject
*construct
)
1005 bool fun
= call
|| construct
;
1006 Class
*clasp
= fun
? &FunctionProxyClass
: &ObjectProxyClass
;
1007 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
);
1008 if (!obj
|| (construct
&& !js_EnsureReservedSlots(cx
, obj
, 0)))
1010 obj
->setSlot(JSSLOT_PROXY_HANDLER
, PrivateValue(handler
));
1011 obj
->setSlot(JSSLOT_PROXY_PRIVATE
, priv
);
1013 obj
->setSlot(JSSLOT_PROXY_CALL
, call
? ObjectValue(*call
) : UndefinedValue());
1015 obj
->setSlot(JSSLOT_PROXY_CONSTRUCT
, construct
? ObjectValue(*construct
) : UndefinedValue());
1021 NonNullObject(JSContext
*cx
, const Value
&v
)
1023 if (v
.isPrimitive()) {
1024 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1027 return &v
.toObject();
1031 proxy_create(JSContext
*cx
, uintN argc
, Value
*vp
)
1034 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1035 "create", "0", "s");
1039 if (!(handler
= NonNullObject(cx
, vp
[2])))
1041 JSObject
*proto
, *parent
= NULL
;
1042 if (argc
> 1 && vp
[3].isObject()) {
1043 proto
= &vp
[3].toObject();
1044 parent
= proto
->getParent();
1046 JS_ASSERT(IsFunctionObject(vp
[0]));
1050 parent
= vp
[0].toObject().getParent();
1051 JSObject
*proxy
= NewProxyObject(cx
, &JSScriptedProxyHandler::singleton
, ObjectValue(*handler
),
1056 vp
->setObject(*proxy
);
1061 proxy_createFunction(JSContext
*cx
, uintN argc
, Value
*vp
)
1064 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1065 "createFunction", "1", "");
1069 if (!(handler
= NonNullObject(cx
, vp
[2])))
1071 JSObject
*proto
, *parent
;
1072 parent
= vp
[0].toObject().getParent();
1073 if (!js_GetClassPrototype(cx
, parent
, JSProto_Function
, &proto
))
1075 parent
= proto
->getParent();
1077 JSObject
*call
= js_ValueToCallableObject(cx
, &vp
[3], JSV2F_SEARCH_STACK
);
1080 JSObject
*construct
= NULL
;
1082 construct
= js_ValueToCallableObject(cx
, &vp
[4], JSV2F_SEARCH_STACK
);
1087 JSObject
*proxy
= NewProxyObject(cx
, &JSScriptedProxyHandler::singleton
,
1088 ObjectValue(*handler
),
1089 proto
, parent
, call
, construct
);
1093 vp
->setObject(*proxy
);
1100 proxy_isTrapping(JSContext
*cx
, uintN argc
, Value
*vp
)
1103 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1104 "isTrapping", "0", "s");
1108 if (!(obj
= NonNullObject(cx
, vp
[2])))
1110 vp
->setBoolean(obj
->isProxy());
1115 proxy_fix(JSContext
*cx
, uintN argc
, Value
*vp
)
1118 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1123 if (!(obj
= NonNullObject(cx
, vp
[2])))
1125 if (obj
->isProxy()) {
1127 if (!FixProxy(cx
, obj
, &flag
))
1129 vp
->setBoolean(flag
);
1131 vp
->setBoolean(true);
1138 static JSFunctionSpec static_methods
[] = {
1139 JS_FN("create", proxy_create
, 2, 0),
1140 JS_FN("createFunction", proxy_createFunction
, 3, 0),
1142 JS_FN("isTrapping", proxy_isTrapping
, 1, 0),
1143 JS_FN("fix", proxy_fix
, 1, 0),
1148 extern Class CallableObjectClass
;
1150 static const uint32 JSSLOT_CALLABLE_CALL
= JSSLOT_PRIVATE
;
1151 static const uint32 JSSLOT_CALLABLE_CONSTRUCT
= JSSLOT_PRIVATE
+ 1;
1154 callable_Call(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
1156 JSObject
*callable
= &argv
[-2].toObject();
1157 JS_ASSERT(callable
->getClass() == &CallableObjectClass
);
1158 const Value
&fval
= callable
->fslots
[JSSLOT_CALLABLE_CALL
];
1159 return InternalCall(cx
, obj
, fval
, argc
, argv
, rval
);
1163 callable_Construct(JSContext
*cx
, JSObject
*obj
, uintN argc
, Value
*argv
, Value
*rval
)
1165 JSObject
*callable
= &argv
[-2].toObject();
1166 JS_ASSERT(callable
->getClass() == &CallableObjectClass
);
1167 Value fval
= callable
->fslots
[JSSLOT_CALLABLE_CONSTRUCT
];
1168 if (fval
.isUndefined()) {
1169 /* We don't have an explicit constructor so allocate a new object and use the call. */
1170 fval
= callable
->fslots
[JSSLOT_CALLABLE_CALL
];
1171 JS_ASSERT(fval
.isObject());
1173 /* callable is the constructor, so get callable.prototype is the proto of the new object. */
1174 if (!callable
->getProperty(cx
, ATOM_TO_JSID(ATOM(classPrototype
)), rval
))
1178 if (rval
->isObject()) {
1179 proto
= &rval
->toObject();
1181 if (!js_GetClassPrototype(cx
, NULL
, JSProto_Object
, &proto
))
1185 JSObject
*newobj
= NewNativeClassInstance(cx
, &js_ObjectClass
, proto
, proto
->getParent());
1189 rval
->setObject(*newobj
);
1191 /* If the call returns an object, return that, otherwise the original newobj. */
1192 if (!InternalCall(cx
, newobj
, callable
->fslots
[JSSLOT_CALLABLE_CALL
],
1193 argc
, argv
, rval
)) {
1196 if (rval
->isPrimitive())
1197 rval
->setObject(*newobj
);
1201 return InternalCall(cx
, obj
, fval
, argc
, argv
, rval
);
1204 Class CallableObjectClass
= {
1206 JSCLASS_HAS_RESERVED_SLOTS(2),
1207 PropertyStub
, /* addProperty */
1208 PropertyStub
, /* delProperty */
1209 PropertyStub
, /* getProperty */
1210 PropertyStub
, /* setProperty */
1214 NULL
, /* finalize */
1215 NULL
, /* reserved0 */
1216 NULL
, /* checkAccess */
1221 JS_FRIEND_API(JSBool
)
1222 FixProxy(JSContext
*cx
, JSObject
*proxy
, JSBool
*bp
)
1224 AutoValueRooter
tvr(cx
);
1225 if (!JSProxy::fix(cx
, proxy
, tvr
.addr()))
1227 if (tvr
.value().isUndefined()) {
1232 if (OperationInProgress(cx
, proxy
)) {
1233 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_PROXY_FIX
);
1238 if (!(props
= NonNullObject(cx
, tvr
.value())))
1241 JSObject
*proto
= proxy
->getProto();
1242 JSObject
*parent
= proxy
->getParent();
1243 Class
*clasp
= proxy
->isFunctionProxy() ? &CallableObjectClass
: &js_ObjectClass
;
1245 /* Make a blank object from the recipe fix provided to us. */
1246 JSObject
*newborn
= NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
);
1249 AutoObjectRooter
tvr2(cx
, newborn
);
1251 if (clasp
== &CallableObjectClass
) {
1252 newborn
->fslots
[JSSLOT_CALLABLE_CALL
] = GetCall(proxy
);
1253 newborn
->fslots
[JSSLOT_CALLABLE_CONSTRUCT
] = GetConstruct(proxy
);
1257 AutoPendingProxyOperation
pending(cx
, proxy
);
1258 if (!js_PopulateObject(cx
, newborn
, props
))
1262 /* Trade spaces between the newborn object and the proxy. */
1263 proxy
->swap(newborn
);
1265 /* The GC will dispose of the proxy object. */
1273 Class js_ProxyClass
= {
1275 JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy
),
1276 PropertyStub
, /* addProperty */
1277 PropertyStub
, /* delProperty */
1278 PropertyStub
, /* getProperty */
1279 PropertyStub
, /* setProperty */
1285 JS_FRIEND_API(JSObject
*)
1286 js_InitProxyClass(JSContext
*cx
, JSObject
*obj
)
1288 JSObject
*module
= NewNonFunction
<WithProto::Class
>(cx
, &js_ProxyClass
, NULL
, obj
);
1291 if (!JS_DefineProperty(cx
, obj
, "Proxy", OBJECT_TO_JSVAL(module
),
1292 JS_PropertyStub
, JS_PropertyStub
, 0)) {
1295 if (!JS_DefineFunctions(cx
, module
, static_methods
))