Bug 586358: make imacpc flagged. (r=lw)
[mozilla-central.git] / js / src / jsproxy.cpp
blob86643a8ff10ad63d6fb013646789db107840c8a1
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Mozilla Foundation
22 * Portions created by the Initial Developer are Copyright (C) 2009
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
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 ***** */
42 #include <string.h>
43 #include "jsapi.h"
44 #include "jscntxt.h"
45 #include "jsprvtd.h"
46 #include "jsnum.h"
47 #include "jsobj.h"
48 #include "jsproxy.h"
49 #include "jsscope.h"
51 #include "jsobjinlines.h"
53 using namespace js;
55 namespace js {
57 static inline const Value &
58 GetCall(JSObject *proxy) {
59 JS_ASSERT(proxy->isFunctionProxy());
60 return proxy->getSlot(JSSLOT_PROXY_CALL);
63 static inline Value
64 GetConstruct(JSObject *proxy) {
65 if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
66 return UndefinedValue();
67 return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
70 static bool
71 OperationInProgress(JSContext *cx, JSObject *proxy)
73 JSPendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
74 while (op) {
75 if (op->object == proxy)
76 return true;
77 op = op->next;
79 return false;
82 JSProxyHandler::JSProxyHandler(void *family) : mFamily(family)
86 JSProxyHandler::~JSProxyHandler()
90 bool
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))
96 return false;
97 *bp = !!desc.obj;
98 return true;
101 bool
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))
107 return false;
108 *bp = !!desc.obj;
109 return true;
112 bool
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))
118 return false;
119 if (!desc.obj) {
120 vp->setUndefined();
121 return true;
123 if (!desc.getter) {
124 *vp = desc.value;
125 return true;
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);
136 bool
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))
142 return false;
143 /* The control-flow here differs from ::get() because of the fall-through case below. */
144 if (desc.obj) {
145 if (desc.setter) {
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)
155 return true;
156 desc.value = *vp;
157 return defineProperty(cx, proxy, id, &desc);
159 if (!getPropertyDescriptor(cx, proxy, id, &desc))
160 return false;
161 if (desc.obj) {
162 if (desc.setter) {
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)
172 return true;
173 /* fall through */
175 desc.obj = proxy;
176 desc.value = *vp;
177 desc.attrs = 0;
178 desc.getter = NULL;
179 desc.setter = NULL;
180 desc.shortid = 0;
181 return defineProperty(cx, proxy, id, &desc);
184 bool
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))
191 return false;
193 /* Select only the enumerable properties through in-place iteration. */
194 AutoPropertyDescriptorRooter desc(cx);
195 size_t i = 0;
196 for (size_t j = 0, len = props.length(); j < len; j++) {
197 JS_ASSERT(i <= j);
198 jsid id = props[j];
199 if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
200 return false;
201 if (desc.obj && (desc.attrs & JSPROP_ENUMERATE))
202 props[i++] = id;
205 JS_ASSERT(i <= props.length());
206 props.resize(i);
208 return true;
211 bool
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))
217 return false;
218 return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
221 JSString *
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]");
231 JSString *
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,
241 "object");
242 return NULL;
244 return fun_toStringHelper(cx, &fval.toObject(), indent);
247 bool
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),
253 rval.addr());
254 if (ok)
255 JS_SET_RVAL(cx, vp, rval.value());
256 return ok;
259 bool
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));
268 if (!obj)
269 return false;
270 rval->setObject(*obj);
271 return true;
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);
283 void
284 JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
288 void
289 JSProxyHandler::trace(JSTracer *trc, JSObject *proxy)
293 static bool
294 GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
296 return handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp);
299 static bool
300 FundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
302 if (!GetTrap(cx, handler, atom, fvalp))
303 return false;
305 if (!js_IsCallable(*fvalp)) {
306 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION,
307 js_AtomToPrintableString(cx, atom));
308 return false;
311 return true;
314 static bool
315 DerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
317 JS_ASSERT(atom == ATOM(has) ||
318 atom == ATOM(hasOwn) ||
319 atom == ATOM(get) ||
320 atom == ATOM(set) ||
321 atom == ATOM(enumerateOwn) ||
322 atom == ATOM(iterate));
324 return GetTrap(cx, handler, atom, fvalp);
327 static bool
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);
335 static bool
336 Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
338 JSString *str = js_ValueToString(cx, IdToValue(id));
339 if (!str)
340 return false;
341 rval->setString(str);
342 return Trap(cx, handler, fval, 1, rval, rval);
345 static bool
346 Trap2(JSContext *cx, JSObject *handler, Value fval, jsid id, Value v, Value *rval)
348 JSString *str = js_ValueToString(cx, IdToValue(id));
349 if (!str)
350 return false;
351 rval->setString(str);
352 Value argv[2] = { *rval, v };
353 return Trap(cx, handler, fval, 2, argv, rval);
356 static bool
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))
363 return false;
364 desc->obj = obj;
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();
370 desc->shortid = 0;
371 return true;
374 static bool
375 MakePropertyDescriptorObject(JSContext *cx, jsid id, PropertyDescriptor *desc, Value *vp)
377 if (!desc->obj) {
378 vp->setUndefined();
379 return true;
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);
387 static bool
388 ValueToBool(JSContext *cx, const Value &v, bool *bp)
390 *bp = !!js_ValueToBoolean(v);
391 return true;
394 bool
395 ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
397 JS_ASSERT(props.length() == 0);
399 if (array.isPrimitive())
400 return true;
402 JSObject *obj = &array.toObject();
403 jsuint length;
404 if (!js_GetLengthProperty(cx, obj, &length)) {
405 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
406 return false;
409 AutoIdRooter idr(cx);
410 AutoValueRooter tvr(cx);
411 for (jsuint n = 0; n < length; ++n) {
412 if (!js_IndexToId(cx, n, idr.addr()))
413 return false;
414 if (!obj->getProperty(cx, idr.id(), tvr.addr()))
415 return false;
416 if (!ValueToId(cx, tvr.value(), idr.addr()))
417 return false;
418 if (!props.append(js_CheckForStringIndex(idr.id())))
419 return false;
422 return true;
425 /* Derived class for all scripted proxy handlers. */
426 class JSScriptedProxyHandler : public JSProxyHandler {
427 public:
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()
464 static bool
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));
471 return false;
473 return true;
476 static JSObject *
477 GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
479 JS_ASSERT(OperationInProgress(cx, proxy));
480 return proxy->getProxyPrivate().toObjectOrNull();
483 bool
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);
495 bool
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);
507 bool
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());
519 bool
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);
529 bool
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);
539 bool
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);
549 bool
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);
557 bool
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()))
563 return false;
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);
570 bool
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()))
576 return false;
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);
583 bool
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));
588 if (!str)
589 return false;
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()))
594 return false;
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);
600 bool
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));
605 if (!str)
606 return false;
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()))
611 return false;
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());
617 bool
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()))
623 return false;
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);
630 bool
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()))
636 return false;
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 {
646 JSThreadData *data;
647 JSPendingProxyOperation op;
648 public:
649 AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
650 op.next = data->pendingProxyOperation;
651 op.object = proxy;
652 data->pendingProxyOperation = &op;
655 ~AutoPendingProxyOperation() {
656 JS_ASSERT(data->pendingProxyOperation == &op);
657 data->pendingProxyOperation = op.next;
661 bool
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);
668 bool
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);
677 bool
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);
685 bool
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);
694 bool
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);
701 bool
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);
710 bool
711 JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
713 AutoPendingProxyOperation pending(cx, proxy);
714 return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
717 bool
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);
724 bool
725 JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
727 AutoPendingProxyOperation pending(cx, proxy);
728 return proxy->getProxyHandler()->enumerate(cx, proxy, props);
731 bool
732 JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
734 AutoPendingProxyOperation pending(cx, proxy);
735 return proxy->getProxyHandler()->fix(cx, proxy, vp);
738 bool
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);
745 bool
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);
752 bool
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);
759 bool
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);
766 bool
767 JSProxy::enumerateOwn(JSContext *cx, JSObject *proxy, AutoIdVector &props)
769 AutoPendingProxyOperation pending(cx, proxy);
770 return proxy->getProxyHandler()->enumerateOwn(cx, proxy, props);
773 bool
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);
780 bool
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);
787 bool
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);
794 JSString *
795 JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
797 AutoPendingProxyOperation pending(cx, proxy);
798 return proxy->getProxyHandler()->obj_toString(cx, proxy);
801 JSString *
802 JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
804 AutoPendingProxyOperation pending(cx, proxy);
805 return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
808 static JSBool
809 proxy_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
810 JSProperty **propp)
812 bool found;
813 if (!JSProxy::has(cx, obj, id, &found))
814 return false;
816 if (found) {
817 *propp = (JSProperty *)0x1;
818 *objp = obj;
819 } else {
820 *objp = NULL;
821 *propp = NULL;
823 return true;
826 static JSBool
827 proxy_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
828 PropertyOp getter, PropertyOp setter, uintN attrs)
830 AutoPropertyDescriptorRooter desc(cx);
831 desc.obj = obj;
832 desc.value = *value;
833 desc.attrs = (attrs & (~JSPROP_SHORTID));
834 desc.getter = getter;
835 desc.setter = setter;
836 desc.shortid = 0;
837 return JSProxy::defineProperty(cx, obj, id, &desc);
840 static JSBool
841 proxy_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
843 return JSProxy::get(cx, obj, obj, id, vp);
846 static JSBool
847 proxy_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
849 return JSProxy::set(cx, obj, obj, id, vp);
852 static JSBool
853 proxy_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
855 AutoPropertyDescriptorRooter desc(cx);
856 if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
857 return false;
858 *attrsp = desc.attrs;
859 return true;
862 static JSBool
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))
868 return false;
869 desc.attrs = (*attrsp & (~JSPROP_SHORTID));
870 return JSProxy::defineProperty(cx, obj, id, &desc);
873 static JSBool
874 proxy_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
876 bool deleted;
877 if (!JSProxy::delete_(cx, obj, id, &deleted))
878 return false;
879 rval->setBoolean(deleted);
880 return true;
883 static void
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");
899 void
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 = {
908 "Proxy",
909 Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(2),
910 PropertyStub, /* addProperty */
911 PropertyStub, /* delProperty */
912 PropertyStub, /* getProperty */
913 PropertyStub, /* setProperty */
914 EnumerateStub,
915 ResolveStub,
916 ConvertStub,
917 NULL, /* finalize */
918 NULL, /* reserved0 */
919 NULL, /* checkAccess */
920 NULL, /* call */
921 NULL, /* construct */
922 NULL, /* xdrObject */
923 NULL, /* hasInstance */
924 NULL, /* mark */
925 JS_NULL_CLASS_EXT,
927 proxy_LookupProperty,
928 proxy_DefineProperty,
929 proxy_GetProperty,
930 proxy_SetProperty,
931 proxy_GetAttributes,
932 proxy_SetAttributes,
933 proxy_DeleteProperty,
934 NULL, /* enumerate */
935 NULL, /* typeof */
936 proxy_TraceObject,
937 NULL, /* thisObject */
938 proxy_Finalize, /* clear */
942 JSBool
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);
950 JSBool
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);
958 static JSType
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 = {
967 "Proxy",
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 */
973 EnumerateStub,
974 ResolveStub,
975 ConvertStub,
976 NULL, /* finalize */
977 NULL, /* reserved0 */
978 NULL, /* checkAccess */
979 CastCallOpAsNative(proxy_Call),
980 proxy_Construct,
981 NULL, /* xdrObject */
982 proxy_HasInstance,
983 NULL, /* mark */
984 JS_NULL_CLASS_EXT,
986 proxy_LookupProperty,
987 proxy_DefineProperty,
988 proxy_GetProperty,
989 proxy_SetProperty,
990 proxy_GetAttributes,
991 proxy_SetAttributes,
992 proxy_DeleteProperty,
993 NULL, /* enumerate */
994 proxy_TypeOf_fun,
995 proxy_TraceObject,
996 NULL, /* thisObject */
997 NULL, /* clear */
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)))
1009 return NULL;
1010 obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
1011 obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
1012 if (fun) {
1013 obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
1014 if (construct)
1015 obj->setSlot(JSSLOT_PROXY_CONSTRUCT, construct ? ObjectValue(*construct) : UndefinedValue());
1017 return obj;
1020 static JSObject *
1021 NonNullObject(JSContext *cx, const Value &v)
1023 if (v.isPrimitive()) {
1024 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1025 return NULL;
1027 return &v.toObject();
1030 static JSBool
1031 proxy_create(JSContext *cx, uintN argc, Value *vp)
1033 if (argc < 1) {
1034 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1035 "create", "0", "s");
1036 return false;
1038 JSObject *handler;
1039 if (!(handler = NonNullObject(cx, vp[2])))
1040 return false;
1041 JSObject *proto, *parent = NULL;
1042 if (argc > 1 && vp[3].isObject()) {
1043 proto = &vp[3].toObject();
1044 parent = proto->getParent();
1045 } else {
1046 JS_ASSERT(IsFunctionObject(vp[0]));
1047 proto = NULL;
1049 if (!parent)
1050 parent = vp[0].toObject().getParent();
1051 JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, ObjectValue(*handler),
1052 proto, parent);
1053 if (!proxy)
1054 return false;
1056 vp->setObject(*proxy);
1057 return true;
1060 static JSBool
1061 proxy_createFunction(JSContext *cx, uintN argc, Value *vp)
1063 if (argc < 2) {
1064 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1065 "createFunction", "1", "");
1066 return false;
1068 JSObject *handler;
1069 if (!(handler = NonNullObject(cx, vp[2])))
1070 return false;
1071 JSObject *proto, *parent;
1072 parent = vp[0].toObject().getParent();
1073 if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
1074 return false;
1075 parent = proto->getParent();
1077 JSObject *call = js_ValueToCallableObject(cx, &vp[3], JSV2F_SEARCH_STACK);
1078 if (!call)
1079 return false;
1080 JSObject *construct = NULL;
1081 if (argc > 2) {
1082 construct = js_ValueToCallableObject(cx, &vp[4], JSV2F_SEARCH_STACK);
1083 if (!construct)
1084 return false;
1087 JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton,
1088 ObjectValue(*handler),
1089 proto, parent, call, construct);
1090 if (!proxy)
1091 return false;
1093 vp->setObject(*proxy);
1094 return true;
1097 #ifdef DEBUG
1099 static JSBool
1100 proxy_isTrapping(JSContext *cx, uintN argc, Value *vp)
1102 if (argc < 1) {
1103 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1104 "isTrapping", "0", "s");
1105 return false;
1107 JSObject *obj;
1108 if (!(obj = NonNullObject(cx, vp[2])))
1109 return false;
1110 vp->setBoolean(obj->isProxy());
1111 return true;
1114 static JSBool
1115 proxy_fix(JSContext *cx, uintN argc, Value *vp)
1117 if (argc < 1) {
1118 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1119 "fix", "0", "s");
1120 return false;
1122 JSObject *obj;
1123 if (!(obj = NonNullObject(cx, vp[2])))
1124 return false;
1125 if (obj->isProxy()) {
1126 JSBool flag;
1127 if (!FixProxy(cx, obj, &flag))
1128 return false;
1129 vp->setBoolean(flag);
1130 } else {
1131 vp->setBoolean(true);
1133 return true;
1136 #endif
1138 static JSFunctionSpec static_methods[] = {
1139 JS_FN("create", proxy_create, 2, 0),
1140 JS_FN("createFunction", proxy_createFunction, 3, 0),
1141 #ifdef DEBUG
1142 JS_FN("isTrapping", proxy_isTrapping, 1, 0),
1143 JS_FN("fix", proxy_fix, 1, 0),
1144 #endif
1145 JS_FS_END
1148 extern Class CallableObjectClass;
1150 static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE;
1151 static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1;
1153 static JSBool
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);
1162 static JSBool
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))
1175 return false;
1177 JSObject *proto;
1178 if (rval->isObject()) {
1179 proto = &rval->toObject();
1180 } else {
1181 if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
1182 return false;
1185 JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
1186 if (!newobj)
1187 return false;
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)) {
1194 return false;
1196 if (rval->isPrimitive())
1197 rval->setObject(*newobj);
1199 return true;
1201 return InternalCall(cx, obj, fval, argc, argv, rval);
1204 Class CallableObjectClass = {
1205 "Function",
1206 JSCLASS_HAS_RESERVED_SLOTS(2),
1207 PropertyStub, /* addProperty */
1208 PropertyStub, /* delProperty */
1209 PropertyStub, /* getProperty */
1210 PropertyStub, /* setProperty */
1211 EnumerateStub,
1212 ResolveStub,
1213 ConvertStub,
1214 NULL, /* finalize */
1215 NULL, /* reserved0 */
1216 NULL, /* checkAccess */
1217 callable_Call,
1218 callable_Construct,
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()))
1226 return false;
1227 if (tvr.value().isUndefined()) {
1228 *bp = false;
1229 return true;
1232 if (OperationInProgress(cx, proxy)) {
1233 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX);
1234 return false;
1237 JSObject *props;
1238 if (!(props = NonNullObject(cx, tvr.value())))
1239 return false;
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);
1247 if (!newborn)
1248 return NULL;
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))
1259 return false;
1262 /* Trade spaces between the newborn object and the proxy. */
1263 proxy->swap(newborn);
1265 /* The GC will dispose of the proxy object. */
1267 *bp = true;
1268 return true;
1273 Class js_ProxyClass = {
1274 "Proxy",
1275 JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),
1276 PropertyStub, /* addProperty */
1277 PropertyStub, /* delProperty */
1278 PropertyStub, /* getProperty */
1279 PropertyStub, /* setProperty */
1280 EnumerateStub,
1281 ResolveStub,
1282 ConvertStub
1285 JS_FRIEND_API(JSObject *)
1286 js_InitProxyClass(JSContext *cx, JSObject *obj)
1288 JSObject *module = NewNonFunction<WithProto::Class>(cx, &js_ProxyClass, NULL, obj);
1289 if (!module)
1290 return NULL;
1291 if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
1292 JS_PropertyStub, JS_PropertyStub, 0)) {
1293 return NULL;
1295 if (!JS_DefineFunctions(cx, module, static_methods))
1296 return NULL;
1297 return module;