CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / js / src / jsproxy.cpp
blob3c538345d0f7ddfcfa31c1f90e3606513d9ed962
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;
54 using namespace js::gc;
56 namespace js {
58 static inline const Value &
59 GetCall(JSObject *proxy) {
60 JS_ASSERT(proxy->isFunctionProxy());
61 return proxy->getSlot(JSSLOT_PROXY_CALL);
64 static inline Value
65 GetConstruct(JSObject *proxy) {
66 if (proxy->numSlots() <= JSSLOT_PROXY_CONSTRUCT)
67 return UndefinedValue();
68 return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT);
71 static bool
72 OperationInProgress(JSContext *cx, JSObject *proxy)
74 JSPendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
75 while (op) {
76 if (op->object == proxy)
77 return true;
78 op = op->next;
80 return false;
83 JSProxyHandler::JSProxyHandler(void *family) : mFamily(family)
87 JSProxyHandler::~JSProxyHandler()
91 bool
92 JSProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
94 JS_ASSERT(OperationInProgress(cx, proxy));
95 AutoPropertyDescriptorRooter desc(cx);
96 if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
97 return false;
98 *bp = !!desc.obj;
99 return true;
102 bool
103 JSProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
105 JS_ASSERT(OperationInProgress(cx, proxy));
106 AutoPropertyDescriptorRooter desc(cx);
107 if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc))
108 return false;
109 *bp = !!desc.obj;
110 return true;
113 bool
114 JSProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
116 JS_ASSERT(OperationInProgress(cx, proxy));
117 AutoPropertyDescriptorRooter desc(cx);
118 if (!getPropertyDescriptor(cx, proxy, id, false, &desc))
119 return false;
120 if (!desc.obj) {
121 vp->setUndefined();
122 return true;
124 if (!desc.getter ||
125 (!(desc.attrs & JSPROP_GETTER) && desc.getter == PropertyStub)) {
126 *vp = desc.value;
127 return true;
129 if (desc.attrs & JSPROP_GETTER) {
130 return ExternalGetOrSet(cx, receiver, id, CastAsObjectJsval(desc.getter),
131 JSACC_READ, 0, NULL, vp);
133 if (!(desc.attrs & JSPROP_SHARED))
134 *vp = desc.value;
135 else
136 vp->setUndefined();
137 if (desc.attrs & JSPROP_SHORTID)
138 id = INT_TO_JSID(desc.shortid);
139 return CallJSPropertyOp(cx, desc.getter, receiver, id, vp);
142 bool
143 JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
144 Value *vp)
146 JS_ASSERT(OperationInProgress(cx, proxy));
147 AutoPropertyDescriptorRooter desc(cx);
148 if (!getOwnPropertyDescriptor(cx, proxy, id, true, &desc))
149 return false;
150 /* The control-flow here differs from ::get() because of the fall-through case below. */
151 if (desc.obj) {
152 if (desc.attrs & JSPROP_READONLY)
153 return true;
154 if (desc.setter && ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub)) {
155 if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
156 return false;
157 if (desc.attrs & JSPROP_SHARED)
158 return true;
160 if (!desc.getter)
161 desc.getter = PropertyStub;
162 if (!desc.setter)
163 desc.setter = StrictPropertyStub;
164 desc.value = *vp;
165 return defineProperty(cx, receiver, id, &desc);
167 if (!getPropertyDescriptor(cx, proxy, id, true, &desc))
168 return false;
169 if (desc.obj) {
170 if (desc.attrs & JSPROP_READONLY)
171 return true;
172 if (desc.setter && ((desc.attrs & JSPROP_SETTER) || desc.setter != StrictPropertyStub)) {
173 if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp))
174 return false;
175 if (desc.attrs & JSPROP_SHARED)
176 return true;
178 if (!desc.getter)
179 desc.getter = PropertyStub;
180 if (!desc.setter)
181 desc.setter = StrictPropertyStub;
182 return defineProperty(cx, receiver, id, &desc);
185 desc.obj = receiver;
186 desc.value = *vp;
187 desc.attrs = JSPROP_ENUMERATE;
188 desc.shortid = 0;
189 desc.getter = NULL;
190 desc.setter = NULL; // Pick up the class getter/setter.
191 return defineProperty(cx, receiver, id, &desc);
194 bool
195 JSProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
197 JS_ASSERT(OperationInProgress(cx, proxy));
198 JS_ASSERT(props.length() == 0);
200 if (!getOwnPropertyNames(cx, proxy, props))
201 return false;
203 /* Select only the enumerable properties through in-place iteration. */
204 AutoPropertyDescriptorRooter desc(cx);
205 size_t i = 0;
206 for (size_t j = 0, len = props.length(); j < len; j++) {
207 JS_ASSERT(i <= j);
208 jsid id = props[j];
209 if (!getOwnPropertyDescriptor(cx, proxy, id, false, &desc))
210 return false;
211 if (desc.obj && (desc.attrs & JSPROP_ENUMERATE))
212 props[i++] = id;
215 JS_ASSERT(i <= props.length());
216 props.resize(i);
218 return true;
221 bool
222 JSProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
224 JS_ASSERT(OperationInProgress(cx, proxy));
225 AutoIdVector props(cx);
226 if ((flags & JSITER_OWNONLY)
227 ? !keys(cx, proxy, props)
228 : !enumerate(cx, proxy, props)) {
229 return false;
231 return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
234 JSString *
235 JSProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
237 JS_ASSERT(proxy->isProxy());
239 return JS_NewStringCopyZ(cx, proxy->isFunctionProxy()
240 ? "[object Function]"
241 : "[object Object]");
244 JSString *
245 JSProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
247 JS_ASSERT(proxy->isProxy());
248 Value fval = GetCall(proxy);
249 if (proxy->isFunctionProxy() &&
250 (fval.isPrimitive() || !fval.toObject().isFunction())) {
251 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
252 JSMSG_INCOMPATIBLE_PROTO,
253 js_Function_str, js_toString_str,
254 "object");
255 return NULL;
257 return fun_toStringHelper(cx, &fval.toObject(), indent);
260 bool
261 JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
263 JS_ASSERT(OperationInProgress(cx, proxy));
264 AutoValueRooter rval(cx);
265 JSBool ok = ExternalInvoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp),
266 rval.addr());
267 if (ok)
268 JS_SET_RVAL(cx, vp, rval.value());
269 return ok;
272 bool
273 JSProxyHandler::construct(JSContext *cx, JSObject *proxy,
274 uintN argc, Value *argv, Value *rval)
276 JS_ASSERT(OperationInProgress(cx, proxy));
277 Value fval = GetConstruct(proxy);
278 if (fval.isUndefined())
279 return ExternalInvokeConstructor(cx, GetCall(proxy), argc, argv, rval);
280 return ExternalInvoke(cx, UndefinedValue(), fval, argc, argv, rval);
283 bool
284 JSProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
286 JS_ASSERT(OperationInProgress(cx, proxy));
287 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
288 JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
289 return false;
292 JSType
293 JSProxyHandler::typeOf(JSContext *cx, JSObject *proxy)
295 JS_ASSERT(OperationInProgress(cx, proxy));
296 return proxy->isFunctionProxy() ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
299 void
300 JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
304 void
305 JSProxyHandler::trace(JSTracer *trc, JSObject *proxy)
309 static bool
310 GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
312 JS_CHECK_RECURSION(cx, return false);
314 return handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp);
317 static bool
318 GetFundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
320 if (!GetTrap(cx, handler, atom, fvalp))
321 return false;
323 if (!js_IsCallable(*fvalp)) {
324 JSAutoByteString bytes;
325 if (js_AtomToPrintableString(cx, atom, &bytes))
326 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION, bytes.ptr());
327 return false;
330 return true;
333 static bool
334 GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
336 JS_ASSERT(atom == ATOM(has) ||
337 atom == ATOM(hasOwn) ||
338 atom == ATOM(get) ||
339 atom == ATOM(set) ||
340 atom == ATOM(keys) ||
341 atom == ATOM(iterate));
343 return GetTrap(cx, handler, atom, fvalp);
346 static bool
347 Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval)
349 return ExternalInvoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
352 static bool
353 Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
355 JSString *str = js_ValueToString(cx, IdToValue(id));
356 if (!str)
357 return false;
358 rval->setString(str);
359 return Trap(cx, handler, fval, 1, rval, rval);
362 static bool
363 Trap2(JSContext *cx, JSObject *handler, Value fval, jsid id, Value v, Value *rval)
365 JSString *str = js_ValueToString(cx, IdToValue(id));
366 if (!str)
367 return false;
368 rval->setString(str);
369 Value argv[2] = { *rval, v };
370 return Trap(cx, handler, fval, 2, argv, rval);
373 static bool
374 ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, const Value &v,
375 PropertyDescriptor *desc)
377 AutoPropDescArrayRooter descs(cx);
378 PropDesc *d = descs.append();
379 if (!d || !d->initialize(cx, id, v))
380 return false;
381 desc->obj = obj;
382 desc->value = d->value;
383 JS_ASSERT(!(d->attrs & JSPROP_SHORTID));
384 desc->attrs = d->attrs;
385 desc->getter = d->getter();
386 desc->setter = d->setter();
387 desc->shortid = 0;
388 return true;
391 static bool
392 IndicatePropertyNotFound(JSContext *cx, PropertyDescriptor *desc)
394 desc->obj = NULL;
395 return true;
398 static bool
399 MakePropertyDescriptorObject(JSContext *cx, jsid id, PropertyDescriptor *desc, Value *vp)
401 if (!desc->obj) {
402 vp->setUndefined();
403 return true;
405 uintN attrs = desc->attrs;
406 Value getter = (attrs & JSPROP_GETTER) ? CastAsObjectJsval(desc->getter) : UndefinedValue();
407 Value setter = (attrs & JSPROP_SETTER) ? CastAsObjectJsval(desc->setter) : UndefinedValue();
408 return js_NewPropertyDescriptorObject(cx, id, attrs, getter, setter, desc->value, vp);
411 static bool
412 ValueToBool(JSContext *cx, const Value &v, bool *bp)
414 *bp = !!js_ValueToBoolean(v);
415 return true;
418 bool
419 ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
421 JS_ASSERT(props.length() == 0);
423 if (array.isPrimitive())
424 return true;
426 JSObject *obj = &array.toObject();
427 jsuint length;
428 if (!js_GetLengthProperty(cx, obj, &length))
429 return false;
431 AutoIdRooter idr(cx);
432 AutoValueRooter tvr(cx);
433 for (jsuint n = 0; n < length; ++n) {
434 if (!JS_CHECK_OPERATION_LIMIT(cx))
435 return false;
436 if (!js_IndexToId(cx, n, idr.addr()))
437 return false;
438 if (!obj->getProperty(cx, idr.id(), tvr.addr()))
439 return false;
440 if (!ValueToId(cx, tvr.value(), idr.addr()))
441 return false;
442 if (!props.append(js_CheckForStringIndex(idr.id())))
443 return false;
446 return true;
449 /* Derived class for all scripted proxy handlers. */
450 class JSScriptedProxyHandler : public JSProxyHandler {
451 public:
452 JSScriptedProxyHandler();
453 virtual ~JSScriptedProxyHandler();
455 /* ES5 Harmony fundamental proxy traps. */
456 virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
457 PropertyDescriptor *desc);
458 virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
459 PropertyDescriptor *desc);
460 virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id,
461 PropertyDescriptor *desc);
462 virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props);
463 virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
464 virtual bool enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props);
465 virtual bool fix(JSContext *cx, JSObject *proxy, Value *vp);
467 /* ES5 Harmony derived proxy traps. */
468 virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
469 virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp);
470 virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp);
471 virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
472 Value *vp);
473 virtual bool keys(JSContext *cx, JSObject *proxy, AutoIdVector &props);
474 virtual bool iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp);
476 static JSScriptedProxyHandler singleton;
479 static int sScriptedProxyHandlerFamily = 0;
481 JSScriptedProxyHandler::JSScriptedProxyHandler() : JSProxyHandler(&sScriptedProxyHandlerFamily)
485 JSScriptedProxyHandler::~JSScriptedProxyHandler()
489 static bool
490 ReturnedValueMustNotBePrimitive(JSContext *cx, JSObject *proxy, JSAtom *atom, const Value &v)
492 if (v.isPrimitive()) {
493 JSAutoByteString bytes;
494 if (js_AtomToPrintableString(cx, atom, &bytes)) {
495 js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
496 JSDVG_SEARCH_STACK, ObjectOrNullValue(proxy), NULL, bytes.ptr());
498 return false;
500 return true;
503 static JSObject *
504 GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
506 JS_ASSERT(OperationInProgress(cx, proxy));
507 return proxy->getProxyPrivate().toObjectOrNull();
510 bool
511 JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
512 PropertyDescriptor *desc)
514 JSObject *handler = GetProxyHandlerObject(cx, proxy);
515 AutoValueRooter tvr(cx);
516 return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), tvr.addr()) &&
517 Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
518 ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
519 (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
520 ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
523 bool
524 JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
525 PropertyDescriptor *desc)
527 JSObject *handler = GetProxyHandlerObject(cx, proxy);
528 AutoValueRooter tvr(cx);
529 return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), tvr.addr()) &&
530 Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
531 ((tvr.value().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
532 (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), tvr.value()) &&
533 ParsePropertyDescriptorObject(cx, proxy, id, tvr.value(), desc)));
536 bool
537 JSScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
538 PropertyDescriptor *desc)
540 JSObject *handler = GetProxyHandlerObject(cx, proxy);
541 AutoValueRooter tvr(cx);
542 AutoValueRooter fval(cx);
543 return GetFundamentalTrap(cx, handler, ATOM(defineProperty), fval.addr()) &&
544 MakePropertyDescriptorObject(cx, id, desc, tvr.addr()) &&
545 Trap2(cx, handler, fval.value(), id, tvr.value(), tvr.addr());
548 bool
549 JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
551 JSObject *handler = GetProxyHandlerObject(cx, proxy);
552 AutoValueRooter tvr(cx);
553 return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), tvr.addr()) &&
554 Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
555 ArrayToIdVector(cx, tvr.value(), props);
558 bool
559 JSScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
561 JSObject *handler = GetProxyHandlerObject(cx, proxy);
562 AutoValueRooter tvr(cx);
563 return GetFundamentalTrap(cx, handler, ATOM(delete), tvr.addr()) &&
564 Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
565 ValueToBool(cx, tvr.value(), bp);
568 bool
569 JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
571 JSObject *handler = GetProxyHandlerObject(cx, proxy);
572 AutoValueRooter tvr(cx);
573 return GetFundamentalTrap(cx, handler, ATOM(enumerate), tvr.addr()) &&
574 Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
575 ArrayToIdVector(cx, tvr.value(), props);
578 bool
579 JSScriptedProxyHandler::fix(JSContext *cx, JSObject *proxy, Value *vp)
581 JSObject *handler = GetProxyHandlerObject(cx, proxy);
582 return GetFundamentalTrap(cx, handler, ATOM(fix), vp) &&
583 Trap(cx, handler, *vp, 0, NULL, vp);
586 bool
587 JSScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
589 JSObject *handler = GetProxyHandlerObject(cx, proxy);
590 AutoValueRooter tvr(cx);
591 if (!GetDerivedTrap(cx, handler, ATOM(has), tvr.addr()))
592 return false;
593 if (!js_IsCallable(tvr.value()))
594 return JSProxyHandler::has(cx, proxy, id, bp);
595 return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
596 ValueToBool(cx, tvr.value(), bp);
599 bool
600 JSScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
602 JSObject *handler = GetProxyHandlerObject(cx, proxy);
603 AutoValueRooter tvr(cx);
604 if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), tvr.addr()))
605 return false;
606 if (!js_IsCallable(tvr.value()))
607 return JSProxyHandler::hasOwn(cx, proxy, id, bp);
608 return Trap1(cx, handler, tvr.value(), id, tvr.addr()) &&
609 ValueToBool(cx, tvr.value(), bp);
612 bool
613 JSScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
615 JSObject *handler = GetProxyHandlerObject(cx, proxy);
616 JSString *str = js_ValueToString(cx, IdToValue(id));
617 if (!str)
618 return false;
619 AutoValueRooter tvr(cx, StringValue(str));
620 Value argv[] = { ObjectOrNullValue(receiver), tvr.value() };
621 AutoValueRooter fval(cx);
622 if (!GetDerivedTrap(cx, handler, ATOM(get), fval.addr()))
623 return false;
624 if (!js_IsCallable(fval.value()))
625 return JSProxyHandler::get(cx, proxy, receiver, id, vp);
626 return Trap(cx, handler, fval.value(), 2, argv, vp);
629 bool
630 JSScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
631 Value *vp)
633 JSObject *handler = GetProxyHandlerObject(cx, proxy);
634 JSString *str = js_ValueToString(cx, IdToValue(id));
635 if (!str)
636 return false;
637 AutoValueRooter tvr(cx, StringValue(str));
638 Value argv[] = { ObjectOrNullValue(receiver), tvr.value(), *vp };
639 AutoValueRooter fval(cx);
640 if (!GetDerivedTrap(cx, handler, ATOM(set), fval.addr()))
641 return false;
642 if (!js_IsCallable(fval.value()))
643 return JSProxyHandler::set(cx, proxy, receiver, id, strict, vp);
644 return Trap(cx, handler, fval.value(), 3, argv, tvr.addr());
647 bool
648 JSScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
650 JSObject *handler = GetProxyHandlerObject(cx, proxy);
651 AutoValueRooter tvr(cx);
652 if (!GetDerivedTrap(cx, handler, ATOM(keys), tvr.addr()))
653 return false;
654 if (!js_IsCallable(tvr.value()))
655 return JSProxyHandler::keys(cx, proxy, props);
656 return Trap(cx, handler, tvr.value(), 0, NULL, tvr.addr()) &&
657 ArrayToIdVector(cx, tvr.value(), props);
660 bool
661 JSScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
663 JSObject *handler = GetProxyHandlerObject(cx, proxy);
664 AutoValueRooter tvr(cx);
665 if (!GetDerivedTrap(cx, handler, ATOM(iterate), tvr.addr()))
666 return false;
667 if (!js_IsCallable(tvr.value()))
668 return JSProxyHandler::iterate(cx, proxy, flags, vp);
669 return Trap(cx, handler, tvr.value(), 0, NULL, vp) &&
670 ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(iterate), *vp);
673 JSScriptedProxyHandler JSScriptedProxyHandler::singleton;
675 class AutoPendingProxyOperation {
676 JSThreadData *data;
677 JSPendingProxyOperation op;
678 public:
679 AutoPendingProxyOperation(JSContext *cx, JSObject *proxy) : data(JS_THREAD_DATA(cx)) {
680 op.next = data->pendingProxyOperation;
681 op.object = proxy;
682 data->pendingProxyOperation = &op;
685 ~AutoPendingProxyOperation() {
686 JS_ASSERT(data->pendingProxyOperation == &op);
687 data->pendingProxyOperation = op.next;
691 bool
692 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
693 PropertyDescriptor *desc)
695 JS_CHECK_RECURSION(cx, return false);
696 AutoPendingProxyOperation pending(cx, proxy);
697 return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc);
700 bool
701 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
703 JS_CHECK_RECURSION(cx, return false);
704 AutoPendingProxyOperation pending(cx, proxy);
705 AutoPropertyDescriptorRooter desc(cx);
706 return JSProxy::getPropertyDescriptor(cx, proxy, id, set, &desc) &&
707 MakePropertyDescriptorObject(cx, id, &desc, vp);
710 bool
711 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
712 PropertyDescriptor *desc)
714 JS_CHECK_RECURSION(cx, return false);
715 AutoPendingProxyOperation pending(cx, proxy);
716 return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
719 bool
720 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
722 JS_CHECK_RECURSION(cx, return false);
723 AutoPendingProxyOperation pending(cx, proxy);
724 AutoPropertyDescriptorRooter desc(cx);
725 return JSProxy::getOwnPropertyDescriptor(cx, proxy, id, set, &desc) &&
726 MakePropertyDescriptorObject(cx, id, &desc, vp);
729 bool
730 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
732 JS_CHECK_RECURSION(cx, return false);
733 AutoPendingProxyOperation pending(cx, proxy);
734 return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
737 bool
738 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
740 JS_CHECK_RECURSION(cx, return false);
741 AutoPendingProxyOperation pending(cx, proxy);
742 AutoPropertyDescriptorRooter desc(cx);
743 return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
744 JSProxy::defineProperty(cx, proxy, id, &desc);
747 bool
748 JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
750 JS_CHECK_RECURSION(cx, return false);
751 AutoPendingProxyOperation pending(cx, proxy);
752 return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
755 bool
756 JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
758 JS_CHECK_RECURSION(cx, return false);
759 AutoPendingProxyOperation pending(cx, proxy);
760 return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
763 bool
764 JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
766 JS_CHECK_RECURSION(cx, return false);
767 AutoPendingProxyOperation pending(cx, proxy);
768 return proxy->getProxyHandler()->enumerate(cx, proxy, props);
771 bool
772 JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
774 JS_CHECK_RECURSION(cx, return false);
775 AutoPendingProxyOperation pending(cx, proxy);
776 return proxy->getProxyHandler()->fix(cx, proxy, vp);
779 bool
780 JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
782 JS_CHECK_RECURSION(cx, return false);
783 AutoPendingProxyOperation pending(cx, proxy);
784 return proxy->getProxyHandler()->has(cx, proxy, id, bp);
787 bool
788 JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
790 JS_CHECK_RECURSION(cx, return false);
791 AutoPendingProxyOperation pending(cx, proxy);
792 return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
795 bool
796 JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
798 JS_CHECK_RECURSION(cx, return false);
799 AutoPendingProxyOperation pending(cx, proxy);
800 return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp);
803 bool
804 JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict, Value *vp)
806 JS_CHECK_RECURSION(cx, return false);
807 AutoPendingProxyOperation pending(cx, proxy);
808 return proxy->getProxyHandler()->set(cx, proxy, receiver, id, strict, vp);
811 bool
812 JSProxy::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
814 JS_CHECK_RECURSION(cx, return false);
815 AutoPendingProxyOperation pending(cx, proxy);
816 return proxy->getProxyHandler()->keys(cx, proxy, props);
819 bool
820 JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
822 JS_CHECK_RECURSION(cx, return false);
823 AutoPendingProxyOperation pending(cx, proxy);
824 return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
827 bool
828 JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
830 JS_CHECK_RECURSION(cx, return false);
831 AutoPendingProxyOperation pending(cx, proxy);
832 return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
835 bool
836 JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval)
838 JS_CHECK_RECURSION(cx, return false);
839 AutoPendingProxyOperation pending(cx, proxy);
840 return proxy->getProxyHandler()->construct(cx, proxy, argc, argv, rval);
843 bool
844 JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
846 JS_CHECK_RECURSION(cx, return false);
847 AutoPendingProxyOperation pending(cx, proxy);
848 return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp);
851 JSType
852 JSProxy::typeOf(JSContext *cx, JSObject *proxy)
854 // FIXME: API doesn't allow us to report error (bug 618906).
855 JS_CHECK_RECURSION(cx, return JSTYPE_OBJECT);
856 AutoPendingProxyOperation pending(cx, proxy);
857 return proxy->getProxyHandler()->typeOf(cx, proxy);
860 JSString *
861 JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
863 JS_CHECK_RECURSION(cx, return NULL);
864 AutoPendingProxyOperation pending(cx, proxy);
865 return proxy->getProxyHandler()->obj_toString(cx, proxy);
868 JSString *
869 JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
871 JS_CHECK_RECURSION(cx, return NULL);
872 AutoPendingProxyOperation pending(cx, proxy);
873 return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
876 static JSObject *
877 proxy_innerObject(JSContext *cx, JSObject *obj)
879 return obj->getProxyPrivate().toObjectOrNull();
882 static JSBool
883 proxy_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
884 JSProperty **propp)
886 bool found;
887 if (!JSProxy::has(cx, obj, id, &found))
888 return false;
890 if (found) {
891 *propp = (JSProperty *)0x1;
892 *objp = obj;
893 } else {
894 *objp = NULL;
895 *propp = NULL;
897 return true;
900 static JSBool
901 proxy_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
902 PropertyOp getter, StrictPropertyOp setter, uintN attrs)
904 AutoPropertyDescriptorRooter desc(cx);
905 desc.obj = obj;
906 desc.value = *value;
907 desc.attrs = (attrs & (~JSPROP_SHORTID));
908 desc.getter = getter;
909 desc.setter = setter;
910 desc.shortid = 0;
911 return JSProxy::defineProperty(cx, obj, id, &desc);
914 static JSBool
915 proxy_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
917 return JSProxy::get(cx, obj, receiver, id, vp);
920 static JSBool
921 proxy_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
923 return JSProxy::set(cx, obj, obj, id, strict, vp);
926 static JSBool
927 proxy_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
929 AutoPropertyDescriptorRooter desc(cx);
930 if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, &desc))
931 return false;
932 *attrsp = desc.attrs;
933 return true;
936 static JSBool
937 proxy_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
939 /* Lookup the current property descriptor so we have setter/getter/value. */
940 AutoPropertyDescriptorRooter desc(cx);
941 if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, true, &desc))
942 return false;
943 desc.attrs = (*attrsp & (~JSPROP_SHORTID));
944 return JSProxy::defineProperty(cx, obj, id, &desc);
947 static JSBool
948 proxy_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
950 // TODO: throwing away strict
951 bool deleted;
952 if (!JSProxy::delete_(cx, obj, id, &deleted))
953 return false;
954 rval->setBoolean(deleted);
955 return true;
958 static void
959 proxy_TraceObject(JSTracer *trc, JSObject *obj)
961 JSContext *cx = trc->context;
963 if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
964 js_TraceWatchPoints(trc, obj);
966 obj->getProxyHandler()->trace(trc, obj);
967 MarkValue(trc, obj->getProxyPrivate(), "private");
968 MarkValue(trc, obj->getProxyExtra(), "extra");
969 if (obj->isFunctionProxy()) {
970 MarkValue(trc, GetCall(obj), "call");
971 MarkValue(trc, GetConstruct(obj), "construct");
975 static void
976 proxy_Finalize(JSContext *cx, JSObject *obj)
978 JS_ASSERT(obj->isProxy());
979 if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
980 obj->getProxyHandler()->finalize(cx, obj);
983 static JSBool
984 proxy_HasInstance(JSContext *cx, JSObject *proxy, const Value *v, JSBool *bp)
986 AutoPendingProxyOperation pending(cx, proxy);
987 bool b;
988 if (!JSProxy::hasInstance(cx, proxy, v, &b))
989 return false;
990 *bp = !!b;
991 return true;
994 static JSType
995 proxy_TypeOf(JSContext *cx, JSObject *proxy)
997 JS_ASSERT(proxy->isProxy());
998 return JSProxy::typeOf(cx, proxy);
1001 JS_FRIEND_API(Class) ObjectProxyClass = {
1002 "Proxy",
1003 Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3),
1004 PropertyStub, /* addProperty */
1005 PropertyStub, /* delProperty */
1006 PropertyStub, /* getProperty */
1007 StrictPropertyStub, /* setProperty */
1008 EnumerateStub,
1009 ResolveStub,
1010 ConvertStub,
1011 NULL, /* finalize */
1012 NULL, /* reserved0 */
1013 NULL, /* checkAccess */
1014 NULL, /* call */
1015 NULL, /* construct */
1016 NULL, /* xdrObject */
1017 proxy_HasInstance, /* hasInstance */
1018 NULL, /* mark */
1019 JS_NULL_CLASS_EXT,
1021 proxy_LookupProperty,
1022 proxy_DefineProperty,
1023 proxy_GetProperty,
1024 proxy_SetProperty,
1025 proxy_GetAttributes,
1026 proxy_SetAttributes,
1027 proxy_DeleteProperty,
1028 NULL, /* enumerate */
1029 proxy_TypeOf,
1030 proxy_TraceObject,
1031 NULL, /* fix */
1032 NULL, /* thisObject */
1033 proxy_Finalize, /* clear */
1037 JS_FRIEND_API(Class) OuterWindowProxyClass = {
1038 "Proxy",
1039 Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(3),
1040 PropertyStub, /* addProperty */
1041 PropertyStub, /* delProperty */
1042 PropertyStub, /* getProperty */
1043 StrictPropertyStub, /* setProperty */
1044 EnumerateStub,
1045 ResolveStub,
1046 ConvertStub,
1047 NULL, /* finalize */
1048 NULL, /* reserved0 */
1049 NULL, /* checkAccess */
1050 NULL, /* call */
1051 NULL, /* construct */
1052 NULL, /* xdrObject */
1053 NULL, /* hasInstance */
1054 NULL, /* mark */
1056 NULL, /* equality */
1057 NULL, /* outerObject */
1058 proxy_innerObject,
1059 NULL /* unused */
1062 proxy_LookupProperty,
1063 proxy_DefineProperty,
1064 proxy_GetProperty,
1065 proxy_SetProperty,
1066 proxy_GetAttributes,
1067 proxy_SetAttributes,
1068 proxy_DeleteProperty,
1069 NULL, /* enumerate */
1070 NULL, /* typeof */
1071 proxy_TraceObject,
1072 NULL, /* fix */
1073 NULL, /* thisObject */
1074 proxy_Finalize, /* clear */
1078 JSBool
1079 proxy_Call(JSContext *cx, uintN argc, Value *vp)
1081 JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
1082 JS_ASSERT(proxy->isProxy());
1083 return JSProxy::call(cx, proxy, argc, vp);
1086 JSBool
1087 proxy_Construct(JSContext *cx, uintN argc, Value *vp)
1089 JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
1090 JS_ASSERT(proxy->isProxy());
1091 Value rval;
1092 bool ok = JSProxy::construct(cx, proxy, argc, JS_ARGV(cx, vp), &rval);
1093 *vp = rval;
1094 return ok;
1097 JS_FRIEND_API(Class) FunctionProxyClass = {
1098 "Proxy",
1099 Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(5),
1100 PropertyStub, /* addProperty */
1101 PropertyStub, /* delProperty */
1102 PropertyStub, /* getProperty */
1103 StrictPropertyStub, /* setProperty */
1104 EnumerateStub,
1105 ResolveStub,
1106 ConvertStub,
1107 NULL, /* finalize */
1108 NULL, /* reserved0 */
1109 NULL, /* checkAccess */
1110 proxy_Call,
1111 proxy_Construct,
1112 NULL, /* xdrObject */
1113 js_FunctionClass.hasInstance,
1114 NULL, /* mark */
1115 JS_NULL_CLASS_EXT,
1117 proxy_LookupProperty,
1118 proxy_DefineProperty,
1119 proxy_GetProperty,
1120 proxy_SetProperty,
1121 proxy_GetAttributes,
1122 proxy_SetAttributes,
1123 proxy_DeleteProperty,
1124 NULL, /* enumerate */
1125 proxy_TypeOf,
1126 proxy_TraceObject,
1127 NULL, /* fix */
1128 NULL, /* thisObject */
1129 NULL, /* clear */
1133 JS_FRIEND_API(JSObject *)
1134 NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto,
1135 JSObject *parent, JSObject *call, JSObject *construct)
1137 bool fun = call || construct;
1138 Class *clasp;
1139 if (fun)
1140 clasp = &FunctionProxyClass;
1141 else
1142 clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
1144 JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
1145 if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
1146 return NULL;
1147 obj->setSlot(JSSLOT_PROXY_HANDLER, PrivateValue(handler));
1148 obj->setSlot(JSSLOT_PROXY_PRIVATE, priv);
1149 if (fun) {
1150 obj->setSlot(JSSLOT_PROXY_CALL, call ? ObjectValue(*call) : UndefinedValue());
1151 if (construct) {
1152 obj->setSlot(JSSLOT_PROXY_CONSTRUCT, ObjectValue(*construct));
1155 return obj;
1158 static JSObject *
1159 NonNullObject(JSContext *cx, const Value &v)
1161 if (v.isPrimitive()) {
1162 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1163 return NULL;
1165 return &v.toObject();
1168 static JSBool
1169 proxy_create(JSContext *cx, uintN argc, Value *vp)
1171 if (argc < 1) {
1172 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1173 "create", "0", "s");
1174 return false;
1176 JSObject *handler = NonNullObject(cx, vp[2]);
1177 if (!handler)
1178 return false;
1179 JSObject *proto, *parent = NULL;
1180 if (argc > 1 && vp[3].isObject()) {
1181 proto = &vp[3].toObject();
1182 parent = proto->getParent();
1183 } else {
1184 JS_ASSERT(IsFunctionObject(vp[0]));
1185 proto = NULL;
1187 if (!parent)
1188 parent = vp[0].toObject().getParent();
1189 JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton, ObjectValue(*handler),
1190 proto, parent);
1191 if (!proxy)
1192 return false;
1194 vp->setObject(*proxy);
1195 return true;
1198 static JSBool
1199 proxy_createFunction(JSContext *cx, uintN argc, Value *vp)
1201 if (argc < 2) {
1202 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1203 "createFunction", "1", "");
1204 return false;
1206 JSObject *handler = NonNullObject(cx, vp[2]);
1207 if (!handler)
1208 return false;
1209 JSObject *proto, *parent;
1210 parent = vp[0].toObject().getParent();
1211 if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
1212 return false;
1213 parent = proto->getParent();
1215 JSObject *call = js_ValueToCallableObject(cx, &vp[3], JSV2F_SEARCH_STACK);
1216 if (!call)
1217 return false;
1218 JSObject *construct = NULL;
1219 if (argc > 2) {
1220 construct = js_ValueToCallableObject(cx, &vp[4], JSV2F_SEARCH_STACK);
1221 if (!construct)
1222 return false;
1225 JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton,
1226 ObjectValue(*handler),
1227 proto, parent, call, construct);
1228 if (!proxy)
1229 return false;
1231 vp->setObject(*proxy);
1232 return true;
1235 #ifdef DEBUG
1237 static JSBool
1238 proxy_isTrapping(JSContext *cx, uintN argc, Value *vp)
1240 if (argc < 1) {
1241 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1242 "isTrapping", "0", "s");
1243 return false;
1245 JSObject *obj = NonNullObject(cx, vp[2]);
1246 if (!obj)
1247 return false;
1248 vp->setBoolean(obj->isProxy());
1249 return true;
1252 static JSBool
1253 proxy_fix(JSContext *cx, uintN argc, Value *vp)
1255 if (argc < 1) {
1256 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1257 "fix", "0", "s");
1258 return false;
1260 JSObject *obj = NonNullObject(cx, vp[2]);
1261 if (!obj)
1262 return false;
1263 if (obj->isProxy()) {
1264 JSBool flag;
1265 if (!FixProxy(cx, obj, &flag))
1266 return false;
1267 vp->setBoolean(flag);
1268 } else {
1269 vp->setBoolean(true);
1271 return true;
1274 #endif
1276 static JSFunctionSpec static_methods[] = {
1277 JS_FN("create", proxy_create, 2, 0),
1278 JS_FN("createFunction", proxy_createFunction, 3, 0),
1279 #ifdef DEBUG
1280 JS_FN("isTrapping", proxy_isTrapping, 1, 0),
1281 JS_FN("fix", proxy_fix, 1, 0),
1282 #endif
1283 JS_FS_END
1286 extern Class CallableObjectClass;
1288 static const uint32 JSSLOT_CALLABLE_CALL = 0;
1289 static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1;
1291 static JSBool
1292 callable_Call(JSContext *cx, uintN argc, Value *vp)
1294 JSObject *callable = &JS_CALLEE(cx, vp).toObject();
1295 JS_ASSERT(callable->getClass() == &CallableObjectClass);
1296 const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
1297 const Value &thisval = vp[1];
1298 Value rval;
1299 bool ok = ExternalInvoke(cx, thisval, fval, argc, JS_ARGV(cx, vp), &rval);
1300 *vp = rval;
1301 return ok;
1304 static JSBool
1305 callable_Construct(JSContext *cx, uintN argc, Value *vp)
1307 JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject());
1308 if (!thisobj)
1309 return false;
1311 JSObject *callable = &vp[0].toObject();
1312 JS_ASSERT(callable->getClass() == &CallableObjectClass);
1313 Value fval = callable->getSlot(JSSLOT_CALLABLE_CONSTRUCT);
1314 if (fval.isUndefined()) {
1315 /* We don't have an explicit constructor so allocate a new object and use the call. */
1316 fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
1317 JS_ASSERT(fval.isObject());
1319 /* callable is the constructor, so get callable.prototype is the proto of the new object. */
1320 Value protov;
1321 if (!callable->getProperty(cx, ATOM_TO_JSID(ATOM(classPrototype)), &protov))
1322 return false;
1324 JSObject *proto;
1325 if (protov.isObject()) {
1326 proto = &protov.toObject();
1327 } else {
1328 if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
1329 return false;
1332 JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
1333 if (!newobj)
1334 return false;
1336 /* If the call returns an object, return that, otherwise the original newobj. */
1337 Value rval;
1338 if (!ExternalInvoke(cx, ObjectValue(*newobj), callable->getSlot(JSSLOT_CALLABLE_CALL),
1339 argc, vp + 2, &rval)) {
1340 return false;
1342 if (rval.isPrimitive())
1343 vp->setObject(*newobj);
1344 else
1345 *vp = rval;
1346 return true;
1349 Value rval;
1350 bool ok = ExternalInvoke(cx, ObjectValue(*thisobj), fval, argc, vp + 2, &rval);
1351 *vp = rval;
1352 return ok;
1355 Class CallableObjectClass = {
1356 "Function",
1357 JSCLASS_HAS_RESERVED_SLOTS(2),
1358 PropertyStub, /* addProperty */
1359 PropertyStub, /* delProperty */
1360 PropertyStub, /* getProperty */
1361 StrictPropertyStub, /* setProperty */
1362 EnumerateStub,
1363 ResolveStub,
1364 ConvertStub,
1365 NULL, /* finalize */
1366 NULL, /* reserved0 */
1367 NULL, /* checkAccess */
1368 callable_Call,
1369 callable_Construct,
1372 JS_FRIEND_API(JSBool)
1373 FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
1375 AutoValueRooter tvr(cx);
1376 if (!JSProxy::fix(cx, proxy, tvr.addr()))
1377 return false;
1378 if (tvr.value().isUndefined()) {
1379 *bp = false;
1380 return true;
1383 if (OperationInProgress(cx, proxy)) {
1384 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX);
1385 return false;
1388 JSObject *props = NonNullObject(cx, tvr.value());
1389 if (!props)
1390 return false;
1392 JSObject *proto = proxy->getProto();
1393 JSObject *parent = proxy->getParent();
1394 Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
1397 * Make a blank object from the recipe fix provided to us. This must have
1398 * number of fixed slots as the proxy so that we can swap their contents.
1400 gc::FinalizeKind kind = gc::FinalizeKind(proxy->arena()->header()->thingKind);
1401 JSObject *newborn = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
1402 if (!newborn)
1403 return false;
1404 AutoObjectRooter tvr2(cx, newborn);
1406 if (clasp == &CallableObjectClass) {
1407 newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
1408 newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy));
1412 AutoPendingProxyOperation pending(cx, proxy);
1413 if (!js_PopulateObject(cx, newborn, props))
1414 return false;
1417 /* Trade contents between the newborn object and the proxy. */
1418 if (!proxy->swap(cx, newborn))
1419 return false;
1421 /* The GC will dispose of the proxy object. */
1423 *bp = true;
1424 return true;
1429 Class js_ProxyClass = {
1430 "Proxy",
1431 JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),
1432 PropertyStub, /* addProperty */
1433 PropertyStub, /* delProperty */
1434 PropertyStub, /* getProperty */
1435 StrictPropertyStub, /* setProperty */
1436 EnumerateStub,
1437 ResolveStub,
1438 ConvertStub
1441 JS_FRIEND_API(JSObject *)
1442 js_InitProxyClass(JSContext *cx, JSObject *obj)
1444 JSObject *module = NewNonFunction<WithProto::Class>(cx, &js_ProxyClass, NULL, obj);
1445 if (!module)
1446 return NULL;
1447 if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
1448 JS_PropertyStub, JS_StrictPropertyStub, 0)) {
1449 return NULL;
1451 if (!JS_DefineFunctions(cx, module, static_methods))
1452 return NULL;
1453 return module;