Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsproxy.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
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/
11  *
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.
16  *
17  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * May 28, 2008.
19  *
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.
24  *
25  * Contributor(s):
26  *   Andreas Gal <gal@mozilla.com>
27  *
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.
39  *
40  * ***** END LICENSE BLOCK ***** */
41
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"
50
51 #include "jsobjinlines.h"
52
53 using namespace js;
54 using namespace js::gc;
55
56 namespace js {
57
58 static inline const Value &
59 GetCall(JSObject *proxy) {
60     JS_ASSERT(proxy->isFunctionProxy());
61     return proxy->getSlot(JSSLOT_PROXY_CALL);
62 }
63
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);
69 }
70
71 static bool
72 OperationInProgress(JSContext *cx, JSObject *proxy)
73 {
74     JSPendingProxyOperation *op = JS_THREAD_DATA(cx)->pendingProxyOperation;
75     while (op) {
76         if (op->object == proxy)
77             return true;
78         op = op->next;
79     }
80     return false;
81 }
82
83 JSProxyHandler::JSProxyHandler(void *family) : mFamily(family)
84 {
85 }
86
87 JSProxyHandler::~JSProxyHandler()
88 {
89 }
90
91 bool
92 JSProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
93 {
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;
100 }
101
102 bool
103 JSProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
104 {
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;
111 }
112
113 bool
114 JSProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
115 {
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;
123     }
124     if (!desc.getter ||
125         (!(desc.attrs & JSPROP_GETTER) && desc.getter == PropertyStub)) {
126         *vp = desc.value;
127         return true;
128     }
129     if (desc.attrs & JSPROP_GETTER) {
130         return ExternalGetOrSet(cx, receiver, id, CastAsObjectJsval(desc.getter),
131                                 JSACC_READ, 0, NULL, vp);
132     }
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);
140 }
141
142 bool
143 JSProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
144                     Value *vp)
145 {
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;
159         }
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);
166     }
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;
177         }
178         if (!desc.getter)
179             desc.getter = PropertyStub;
180         if (!desc.setter)
181             desc.setter = StrictPropertyStub;
182         return defineProperty(cx, receiver, id, &desc);
183     }
184
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);
192 }
193
194 bool
195 JSProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
196 {
197     JS_ASSERT(OperationInProgress(cx, proxy));
198     JS_ASSERT(props.length() == 0);
199
200     if (!getOwnPropertyNames(cx, proxy, props))
201         return false;
202
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;
213     }
214
215     JS_ASSERT(i <= props.length());
216     props.resize(i);
217
218     return true;
219 }
220
221 bool
222 JSProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
223 {
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;
230     }
231     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
232 }
233
234 JSString *
235 JSProxyHandler::obj_toString(JSContext *cx, JSObject *proxy)
236 {
237     JS_ASSERT(proxy->isProxy());
238
239     return JS_NewStringCopyZ(cx, proxy->isFunctionProxy()
240                                  ? "[object Function]"
241                                  : "[object Object]");
242 }
243
244 JSString *
245 JSProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
246 {
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;
256     }
257     return fun_toStringHelper(cx, &fval.toObject(), indent);
258 }
259
260 bool
261 JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
262 {
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;
270 }
271
272 bool
273 JSProxyHandler::construct(JSContext *cx, JSObject *proxy,
274                           uintN argc, Value *argv, Value *rval)
275 {
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);
281 }
282
283 bool
284 JSProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
285 {
286     JS_ASSERT(OperationInProgress(cx, proxy));
287     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
288                         JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
289     return false;
290 }
291
292 JSType
293 JSProxyHandler::typeOf(JSContext *cx, JSObject *proxy)
294 {
295     JS_ASSERT(OperationInProgress(cx, proxy));
296     return proxy->isFunctionProxy() ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
297 }
298
299 void
300 JSProxyHandler::finalize(JSContext *cx, JSObject *proxy)
301 {
302 }
303
304 void
305 JSProxyHandler::trace(JSTracer *trc, JSObject *proxy)
306 {
307 }
308
309 static bool
310 GetTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
311 {
312     JS_CHECK_RECURSION(cx, return false);
313
314     return handler->getProperty(cx, ATOM_TO_JSID(atom), fvalp);
315 }
316
317 static bool
318 GetFundamentalTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
319 {
320     if (!GetTrap(cx, handler, atom, fvalp))
321         return false;
322
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;
328     }
329
330     return true;
331 }
332
333 static bool
334 GetDerivedTrap(JSContext *cx, JSObject *handler, JSAtom *atom, Value *fvalp)
335 {
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));
342
343     return GetTrap(cx, handler, atom, fvalp);
344 }
345
346 static bool
347 Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval)
348 {
349     return ExternalInvoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
350 }
351
352 static bool
353 Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
354 {
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);
360 }
361
362 static bool
363 Trap2(JSContext *cx, JSObject *handler, Value fval, jsid id, Value v, Value *rval)
364 {
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);
371 }
372
373 static bool
374 ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, const Value &v,
375                               PropertyDescriptor *desc)
376 {
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;
389 }
390
391 static bool
392 IndicatePropertyNotFound(JSContext *cx, PropertyDescriptor *desc)
393 {
394     desc->obj = NULL;
395     return true;
396 }
397
398 static bool
399 MakePropertyDescriptorObject(JSContext *cx, jsid id, PropertyDescriptor *desc, Value *vp)
400 {
401     if (!desc->obj) {
402         vp->setUndefined();
403         return true;
404     }
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);
409 }
410
411 static bool
412 ValueToBool(JSContext *cx, const Value &v, bool *bp)
413 {
414     *bp = !!js_ValueToBoolean(v);
415     return true;
416 }
417
418 bool
419 ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
420 {
421     JS_ASSERT(props.length() == 0);
422
423     if (array.isPrimitive())
424         return true;
425
426     JSObject *obj = &array.toObject();
427     jsuint length;
428     if (!js_GetLengthProperty(cx, obj, &length))
429         return false;
430
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;
444     }
445
446     return true;
447 }
448
449 /* Derived class for all scripted proxy handlers. */
450 class JSScriptedProxyHandler : public JSProxyHandler {
451   public:
452     JSScriptedProxyHandler();
453     virtual ~JSScriptedProxyHandler();
454
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);
466
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);
475
476     static JSScriptedProxyHandler singleton;
477 };
478
479 static int sScriptedProxyHandlerFamily = 0;
480
481 JSScriptedProxyHandler::JSScriptedProxyHandler() : JSProxyHandler(&sScriptedProxyHandlerFamily)
482 {
483 }
484
485 JSScriptedProxyHandler::~JSScriptedProxyHandler()
486 {
487 }
488
489 static bool
490 ReturnedValueMustNotBePrimitive(JSContext *cx, JSObject *proxy, JSAtom *atom, const Value &v)
491 {
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());
497         }
498         return false;
499     }
500     return true;
501 }
502
503 static JSObject *
504 GetProxyHandlerObject(JSContext *cx, JSObject *proxy)
505 {
506     JS_ASSERT(OperationInProgress(cx, proxy));
507     return proxy->getProxyPrivate().toObjectOrNull();
508 }
509
510 bool
511 JSScriptedProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
512                                               PropertyDescriptor *desc)
513 {
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)));
521 }
522
523 bool
524 JSScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
525                                                  PropertyDescriptor *desc)
526 {
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)));
534 }
535
536 bool
537 JSScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
538                                        PropertyDescriptor *desc)
539 {
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());
546 }
547
548 bool
549 JSScriptedProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
550 {
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);
556 }
557
558 bool
559 JSScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
560 {
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);
566 }
567
568 bool
569 JSScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
570 {
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);
576 }
577
578 bool
579 JSScriptedProxyHandler::fix(JSContext *cx, JSObject *proxy, Value *vp)
580 {
581     JSObject *handler = GetProxyHandlerObject(cx, proxy);
582     return GetFundamentalTrap(cx, handler, ATOM(fix), vp) &&
583            Trap(cx, handler, *vp, 0, NULL, vp);
584 }
585
586 bool
587 JSScriptedProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
588 {
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);
597 }
598
599 bool
600 JSScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
601 {
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);
610 }
611
612 bool
613 JSScriptedProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
614 {
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);
627 }
628
629 bool
630 JSScriptedProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
631                             Value *vp)
632 {
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());
645 }
646
647 bool
648 JSScriptedProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
649 {
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);
658 }
659
660 bool
661 JSScriptedProxyHandler::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
662 {
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);
671 }
672
673 JSScriptedProxyHandler JSScriptedProxyHandler::singleton;
674
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;
683     }
684
685     ~AutoPendingProxyOperation() {
686         JS_ASSERT(data->pendingProxyOperation == &op);
687         data->pendingProxyOperation = op.next;
688     }
689 };
690
691 bool
692 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
693                                PropertyDescriptor *desc)
694 {
695     JS_CHECK_RECURSION(cx, return false);
696     AutoPendingProxyOperation pending(cx, proxy);
697     return proxy->getProxyHandler()->getPropertyDescriptor(cx, proxy, id, set, desc);
698 }
699
700 bool
701 JSProxy::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
702 {
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);
708 }
709
710 bool
711 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
712                                   PropertyDescriptor *desc)
713 {
714     JS_CHECK_RECURSION(cx, return false);
715     AutoPendingProxyOperation pending(cx, proxy);
716     return proxy->getProxyHandler()->getOwnPropertyDescriptor(cx, proxy, id, set, desc);
717 }
718
719 bool
720 JSProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set, Value *vp)
721 {
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);
727 }
728
729 bool
730 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
731 {
732     JS_CHECK_RECURSION(cx, return false);
733     AutoPendingProxyOperation pending(cx, proxy);
734     return proxy->getProxyHandler()->defineProperty(cx, proxy, id, desc);
735 }
736
737 bool
738 JSProxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
739 {
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);
745 }
746
747 bool
748 JSProxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
749 {
750     JS_CHECK_RECURSION(cx, return false);
751     AutoPendingProxyOperation pending(cx, proxy);
752     return proxy->getProxyHandler()->getOwnPropertyNames(cx, proxy, props);
753 }
754
755 bool
756 JSProxy::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
757 {
758     JS_CHECK_RECURSION(cx, return false);
759     AutoPendingProxyOperation pending(cx, proxy);
760     return proxy->getProxyHandler()->delete_(cx, proxy, id, bp);
761 }
762
763 bool
764 JSProxy::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
765 {
766     JS_CHECK_RECURSION(cx, return false);
767     AutoPendingProxyOperation pending(cx, proxy);
768     return proxy->getProxyHandler()->enumerate(cx, proxy, props);
769 }
770
771 bool
772 JSProxy::fix(JSContext *cx, JSObject *proxy, Value *vp)
773 {
774     JS_CHECK_RECURSION(cx, return false);
775     AutoPendingProxyOperation pending(cx, proxy);
776     return proxy->getProxyHandler()->fix(cx, proxy, vp);
777 }
778
779 bool
780 JSProxy::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
781 {
782     JS_CHECK_RECURSION(cx, return false);
783     AutoPendingProxyOperation pending(cx, proxy);
784     return proxy->getProxyHandler()->has(cx, proxy, id, bp);
785 }
786
787 bool
788 JSProxy::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
789 {
790     JS_CHECK_RECURSION(cx, return false);
791     AutoPendingProxyOperation pending(cx, proxy);
792     return proxy->getProxyHandler()->hasOwn(cx, proxy, id, bp);
793 }
794
795 bool
796 JSProxy::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
797 {
798     JS_CHECK_RECURSION(cx, return false);
799     AutoPendingProxyOperation pending(cx, proxy);
800     return proxy->getProxyHandler()->get(cx, proxy, receiver, id, vp);
801 }
802
803 bool
804 JSProxy::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict, Value *vp)
805 {
806     JS_CHECK_RECURSION(cx, return false);
807     AutoPendingProxyOperation pending(cx, proxy);
808     return proxy->getProxyHandler()->set(cx, proxy, receiver, id, strict, vp);
809 }
810
811 bool
812 JSProxy::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
813 {
814     JS_CHECK_RECURSION(cx, return false);
815     AutoPendingProxyOperation pending(cx, proxy);
816     return proxy->getProxyHandler()->keys(cx, proxy, props);
817 }
818
819 bool
820 JSProxy::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
821 {
822     JS_CHECK_RECURSION(cx, return false);
823     AutoPendingProxyOperation pending(cx, proxy);
824     return proxy->getProxyHandler()->iterate(cx, proxy, flags, vp);
825 }
826
827 bool
828 JSProxy::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
829 {
830     JS_CHECK_RECURSION(cx, return false);
831     AutoPendingProxyOperation pending(cx, proxy);
832     return proxy->getProxyHandler()->call(cx, proxy, argc, vp);
833 }
834
835 bool
836 JSProxy::construct(JSContext *cx, JSObject *proxy, uintN argc, Value *argv, Value *rval)
837 {
838     JS_CHECK_RECURSION(cx, return false);
839     AutoPendingProxyOperation pending(cx, proxy);
840     return proxy->getProxyHandler()->construct(cx, proxy, argc, argv, rval);
841 }
842
843 bool
844 JSProxy::hasInstance(JSContext *cx, JSObject *proxy, const js::Value *vp, bool *bp)
845 {
846     JS_CHECK_RECURSION(cx, return false);
847     AutoPendingProxyOperation pending(cx, proxy);
848     return proxy->getProxyHandler()->hasInstance(cx, proxy, vp, bp);
849 }
850
851 JSType
852 JSProxy::typeOf(JSContext *cx, JSObject *proxy)
853 {
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);
858 }
859
860 JSString *
861 JSProxy::obj_toString(JSContext *cx, JSObject *proxy)
862 {
863     JS_CHECK_RECURSION(cx, return NULL);
864     AutoPendingProxyOperation pending(cx, proxy);
865     return proxy->getProxyHandler()->obj_toString(cx, proxy);
866 }
867
868 JSString *
869 JSProxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
870 {
871     JS_CHECK_RECURSION(cx, return NULL);
872     AutoPendingProxyOperation pending(cx, proxy);
873     return proxy->getProxyHandler()->fun_toString(cx, proxy, indent);
874 }
875
876 static JSObject *
877 proxy_innerObject(JSContext *cx, JSObject *obj)
878 {
879     return obj->getProxyPrivate().toObjectOrNull();
880 }
881
882 static JSBool
883 proxy_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
884                      JSProperty **propp)
885 {
886     bool found;
887     if (!JSProxy::has(cx, obj, id, &found))
888         return false;
889
890     if (found) {
891         *propp = (JSProperty *)0x1;
892         *objp = obj;
893     } else {
894         *objp = NULL;
895         *propp = NULL;
896     }
897     return true;
898 }
899
900 static JSBool
901 proxy_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
902                      PropertyOp getter, StrictPropertyOp setter, uintN attrs)
903 {
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);
912 }
913
914 static JSBool
915 proxy_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
916 {
917     return JSProxy::get(cx, obj, receiver, id, vp);
918 }
919
920 static JSBool
921 proxy_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
922 {
923     return JSProxy::set(cx, obj, obj, id, strict, vp);
924 }
925
926 static JSBool
927 proxy_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
928 {
929     AutoPropertyDescriptorRooter desc(cx);
930     if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, &desc))
931         return false;
932     *attrsp = desc.attrs;
933     return true;
934 }
935
936 static JSBool
937 proxy_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
938 {
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);
945 }
946
947 static JSBool
948 proxy_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
949 {
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;
956 }
957
958 static void
959 proxy_TraceObject(JSTracer *trc, JSObject *obj)
960 {
961     JSContext *cx = trc->context;
962
963     if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
964         js_TraceWatchPoints(trc, obj);
965
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");
972     }
973 }
974
975 static void
976 proxy_Finalize(JSContext *cx, JSObject *obj)
977 {
978     JS_ASSERT(obj->isProxy());
979     if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
980         obj->getProxyHandler()->finalize(cx, obj);
981 }
982
983 static JSBool
984 proxy_HasInstance(JSContext *cx, JSObject *proxy, const Value *v, JSBool *bp)
985 {
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;
992 }
993
994 static JSType
995 proxy_TypeOf(JSContext *cx, JSObject *proxy)
996 {
997     JS_ASSERT(proxy->isProxy());
998     return JSProxy::typeOf(cx, proxy);
999 }
1000
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,
1020     {
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 */
1034     }
1035 };
1036
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        */
1055     {
1056         NULL,             /* equality    */
1057         NULL,             /* outerObject */
1058         proxy_innerObject,
1059         NULL        /* unused */
1060     },
1061     {
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 */
1075     }
1076 };
1077
1078 JSBool
1079 proxy_Call(JSContext *cx, uintN argc, Value *vp)
1080 {
1081     JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
1082     JS_ASSERT(proxy->isProxy());
1083     return JSProxy::call(cx, proxy, argc, vp);
1084 }
1085
1086 JSBool
1087 proxy_Construct(JSContext *cx, uintN argc, Value *vp)
1088 {
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;
1095 }
1096
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,
1116     {
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           */
1130     }
1131 };
1132
1133 JS_FRIEND_API(JSObject *)
1134 NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto,
1135                JSObject *parent, JSObject *call, JSObject *construct)
1136 {
1137     bool fun = call || construct;
1138     Class *clasp;
1139     if (fun)
1140         clasp = &FunctionProxyClass;
1141     else
1142         clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
1143
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));
1153         }
1154     }
1155     return obj;
1156 }
1157
1158 static JSObject *
1159 NonNullObject(JSContext *cx, const Value &v)
1160 {
1161     if (v.isPrimitive()) {
1162         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1163         return NULL;
1164     }
1165     return &v.toObject();
1166 }
1167
1168 static JSBool
1169 proxy_create(JSContext *cx, uintN argc, Value *vp)
1170 {
1171     if (argc < 1) {
1172         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1173                              "create", "0", "s");
1174         return false;
1175     }
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;
1186     }
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;
1193
1194     vp->setObject(*proxy);
1195     return true;
1196 }
1197
1198 static JSBool
1199 proxy_createFunction(JSContext *cx, uintN argc, Value *vp)
1200 {
1201     if (argc < 2) {
1202         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1203                              "createFunction", "1", "");
1204         return false;
1205     }
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();
1214
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;
1223     }
1224
1225     JSObject *proxy = NewProxyObject(cx, &JSScriptedProxyHandler::singleton,
1226                                      ObjectValue(*handler),
1227                                      proto, parent, call, construct);
1228     if (!proxy)
1229         return false;
1230
1231     vp->setObject(*proxy);
1232     return true;
1233 }
1234
1235 #ifdef DEBUG
1236
1237 static JSBool
1238 proxy_isTrapping(JSContext *cx, uintN argc, Value *vp)
1239 {
1240     if (argc < 1) {
1241         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1242                              "isTrapping", "0", "s");
1243         return false;
1244     }
1245     JSObject *obj = NonNullObject(cx, vp[2]);
1246     if (!obj)
1247         return false;
1248     vp->setBoolean(obj->isProxy());
1249     return true;
1250 }
1251
1252 static JSBool
1253 proxy_fix(JSContext *cx, uintN argc, Value *vp)
1254 {
1255     if (argc < 1) {
1256         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1257                              "fix", "0", "s");
1258         return false;
1259     }
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);
1270     }
1271     return true;
1272 }
1273
1274 #endif
1275
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
1284 };
1285
1286 extern Class CallableObjectClass;
1287
1288 static const uint32 JSSLOT_CALLABLE_CALL = 0;
1289 static const uint32 JSSLOT_CALLABLE_CONSTRUCT = 1;
1290
1291 static JSBool
1292 callable_Call(JSContext *cx, uintN argc, Value *vp)
1293 {
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;
1302 }
1303
1304 static JSBool
1305 callable_Construct(JSContext *cx, uintN argc, Value *vp)
1306 {
1307     JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject());
1308     if (!thisobj)
1309         return false;
1310
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());
1318
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;
1323
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;
1330         }
1331
1332         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
1333         if (!newobj)
1334             return false;
1335
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;
1341         }
1342         if (rval.isPrimitive())
1343             vp->setObject(*newobj);
1344         else
1345             *vp = rval;
1346         return true;
1347     }
1348
1349     Value rval;
1350     bool ok = ExternalInvoke(cx, ObjectValue(*thisobj), fval, argc, vp + 2, &rval);
1351     *vp = rval;
1352     return ok;
1353 }
1354
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,
1370 };
1371
1372 JS_FRIEND_API(JSBool)
1373 FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
1374 {
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;
1381     }
1382
1383     if (OperationInProgress(cx, proxy)) {
1384         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROXY_FIX);
1385         return false;
1386     }
1387
1388     JSObject *props = NonNullObject(cx, tvr.value());
1389     if (!props)
1390         return false;
1391
1392     JSObject *proto = proxy->getProto();
1393     JSObject *parent = proxy->getParent();
1394     Class *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
1395
1396     /*
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.
1399      */
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);
1405
1406     if (clasp == &CallableObjectClass) {
1407         newborn->setSlot(JSSLOT_CALLABLE_CALL, GetCall(proxy));
1408         newborn->setSlot(JSSLOT_CALLABLE_CONSTRUCT, GetConstruct(proxy));
1409     }
1410
1411     {
1412         AutoPendingProxyOperation pending(cx, proxy);
1413         if (!js_PopulateObject(cx, newborn, props))
1414             return false;
1415     }
1416
1417     /* Trade contents between the newborn object and the proxy. */
1418     if (!proxy->swap(cx, newborn))
1419         return false;
1420
1421     /* The GC will dispose of the proxy object. */
1422
1423     *bp = true;
1424     return true;
1425 }
1426
1427 }
1428
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
1439 };
1440
1441 JS_FRIEND_API(JSObject *)
1442 js_InitProxyClass(JSContext *cx, JSObject *obj)
1443 {
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;
1450     }
1451     if (!JS_DefineFunctions(cx, module, static_methods))
1452         return NULL;
1453     return module;
1454 }