Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jswrapper.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) 2010
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 "jsapi.h"
43 #include "jscntxt.h"
44 #include "jsiter.h"
45 #include "jsnum.h"
46 #include "jsregexp.h"
47 #include "jswrapper.h"
48 #include "methodjit/PolyIC.h"
49 #include "methodjit/MonoIC.h"
50 #ifdef JS_METHODJIT
51 # include "assembler/jit/ExecutableAllocator.h"
52 #endif
53 #include "jscompartment.h"
54
55 #include "jsobjinlines.h"
56
57 using namespace js;
58 using namespace js::gc;
59
60 static int sWrapperFamily;
61
62 void *
63 JSWrapper::getWrapperFamily()
64 {
65     return &sWrapperFamily;
66 }
67
68 bool
69 JSObject::isWrapper() const
70 {
71     return isProxy() && getProxyHandler()->family() == &sWrapperFamily;
72 }
73
74 JSObject *
75 JSObject::unwrap(uintN *flagsp)
76 {
77     JSObject *wrapped = this;
78     uintN flags = 0;
79     while (wrapped->isWrapper()) {
80         flags |= static_cast<JSWrapper *>(wrapped->getProxyHandler())->flags();
81         wrapped = wrapped->getProxyPrivate().toObjectOrNull();
82         if (wrapped->getClass()->ext.innerObject)
83             break;
84     }
85     if (flagsp)
86         *flagsp = flags;
87     return wrapped;
88 }
89
90 JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags)
91 {
92 }
93
94 JSWrapper::~JSWrapper()
95 {
96 }
97
98 #define CHECKED(op, act)                                                     \
99     JS_BEGIN_MACRO                                                           \
100         bool status;                                                         \
101         if (!enter(cx, wrapper, id, act, &status))                           \
102             return status;                                                   \
103         bool ok = (op);                                                      \
104         leave(cx, wrapper);                                                  \
105         return ok;                                                           \
106     JS_END_MACRO
107
108 #define SET(action) CHECKED(action, SET)
109 #define GET(action) CHECKED(action, GET)
110
111 bool
112 JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
113                                  bool set, PropertyDescriptor *desc)
114 {
115     desc->obj= NULL; // default result if we refuse to perform this action
116     CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED,
117                                          Jsvalify(desc)), set ? SET : GET);
118 }
119
120 static bool
121 GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSPropertyDescriptor *desc)
122 {
123     if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
124         return false;
125     if (desc->obj != obj)
126         desc->obj = NULL;
127     return true;
128 }
129
130 bool
131 JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
132                                     PropertyDescriptor *desc)
133 {
134     desc->obj= NULL; // default result if we refuse to perform this action
135     CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED,
136                                      Jsvalify(desc)), set ? SET : GET);
137 }
138
139 bool
140 JSWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
141                           PropertyDescriptor *desc)
142 {
143     SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, Jsvalify(desc->value),
144                               Jsvalify(desc->getter), Jsvalify(desc->setter), desc->attrs));
145 }
146
147 bool
148 JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
149 {
150     // if we refuse to perform this action, props remains empty
151     jsid id = JSID_VOID;
152     GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY | JSITER_HIDDEN, &props));
153 }
154
155 static bool
156 ValueToBoolean(Value *vp, bool *bp)
157 {
158     *bp = js_ValueToBoolean(*vp);
159     return true;
160 }
161
162 bool
163 JSWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
164 {
165     *bp = true; // default result if we refuse to perform this action
166     Value v;
167     SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, Jsvalify(&v)) &&
168         ValueToBoolean(&v, bp));
169 }
170
171 bool
172 JSWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
173 {
174     // if we refuse to perform this action, props remains empty
175     static jsid id = JSID_VOID;
176     GET(GetPropertyNames(cx, wrappedObject(wrapper), 0, &props));
177 }
178
179 bool
180 JSWrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp)
181 {
182     vp->setUndefined();
183     return true;
184 }
185
186 static bool
187 Cond(JSBool b, bool *bp)
188 {
189     *bp = !!b;
190     return true;
191 }
192
193 bool
194 JSWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
195 {
196     *bp = false; // default result if we refuse to perform this action
197     JSBool found;
198     GET(JS_HasPropertyById(cx, wrappedObject(wrapper), id, &found) &&
199         Cond(found, bp));
200 }
201
202 bool
203 JSWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
204 {
205     *bp = false; // default result if we refuse to perform this action
206     PropertyDescriptor desc;
207     JSObject *wobj = wrappedObject(wrapper);
208     GET(JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, Jsvalify(&desc)) &&
209         Cond(desc.obj == wobj, bp));
210 }
211
212 bool
213 JSWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
214 {
215     vp->setUndefined(); // default result if we refuse to perform this action
216     GET(wrappedObject(wrapper)->getProperty(cx, receiver, id, vp));
217 }
218
219 bool
220 JSWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict,
221                Value *vp)
222 {
223     // FIXME (bug 596351): Need deal with strict mode.
224     SET(wrappedObject(wrapper)->setProperty(cx, id, vp, false));
225 }
226
227 bool
228 JSWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
229 {
230     // if we refuse to perform this action, props remains empty
231     const jsid id = JSID_VOID;
232     GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY, &props));
233 }
234
235 bool
236 JSWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
237 {
238     vp->setUndefined(); // default result if we refuse to perform this action
239     const jsid id = JSID_VOID;
240     GET(GetIterator(cx, wrappedObject(wrapper), flags, vp));
241 }
242
243 bool
244 JSWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp)
245 {
246     vp->setUndefined(); // default result if we refuse to perform this action
247     const jsid id = JSID_VOID;
248     CHECKED(JSProxyHandler::call(cx, wrapper, argc, vp), CALL);
249 }
250
251 bool
252 JSWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *vp)
253 {
254     vp->setUndefined(); // default result if we refuse to perform this action
255     const jsid id = JSID_VOID;
256     GET(JSProxyHandler::construct(cx, wrapper, argc, argv, vp));
257 }
258
259 bool
260 JSWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
261 {
262     *bp = true; // default result if we refuse to perform this action
263     const jsid id = JSID_VOID;
264     JSBool b;
265     GET(JS_HasInstance(cx, wrappedObject(wrapper), Jsvalify(*vp), &b) && Cond(b, bp));
266 }
267
268 JSType
269 JSWrapper::typeOf(JSContext *cx, JSObject *wrapper)
270 {
271     return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper)));
272 }
273
274 JSString *
275 JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
276 {
277     bool status;
278     if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
279         if (status) {
280             // Perform some default behavior that doesn't leak any information.
281             return JS_NewStringCopyZ(cx, "[object Object]");
282         }
283         return NULL;
284     }
285     JSString *str = obj_toStringHelper(cx, wrappedObject(wrapper));
286     leave(cx, wrapper);
287     return str;
288 }
289
290 JSString *
291 JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
292 {
293     bool status;
294     if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
295         if (status) {
296             // Perform some default behavior that doesn't leak any information.
297             if (wrapper->isCallable())
298                 return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
299             js::Value v = ObjectValue(*wrapper);
300             js_ReportIsNotFunction(cx, &v, 0);
301             return NULL;
302         }
303         return NULL;
304     }
305     JSString *str = JSProxyHandler::fun_toString(cx, wrapper, indent);
306     leave(cx, wrapper);
307     return str;
308 }
309
310 void
311 JSWrapper::trace(JSTracer *trc, JSObject *wrapper)
312 {
313     MarkObject(trc, *wrappedObject(wrapper), "wrappedObject");
314 }
315
316 bool
317 JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
318 {
319     *bp = true;
320     return true;
321 }
322
323 void
324 JSWrapper::leave(JSContext *cx, JSObject *wrapper)
325 {
326 }
327
328 JSWrapper JSWrapper::singleton((uintN)0);
329
330 JSObject *
331 JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
332                JSWrapper *handler)
333 {
334     JS_ASSERT(parent);
335     if (obj->isXML()) {
336         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WRAP_XML_OBJECT);
337         return NULL;
338     }
339     return NewProxyObject(cx, handler, ObjectValue(*obj), proto, parent,
340                           obj->isCallable() ? obj : NULL, NULL);
341 }
342
343 /* Compartments. */
344
345 namespace js {
346
347 extern JSObject *
348 TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
349                          uintN flags)
350 {
351     // Allow wrapping outer window proxies.
352     JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
353     return JSWrapper::New(cx, obj, wrappedProto, parent, &JSCrossCompartmentWrapper::singleton);
354 }
355
356 }
357
358 AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
359     : context(cx),
360       origin(cx->compartment),
361       target(target),
362       destination(target->getCompartment()),
363       input(cx),
364       entered(false)
365 {
366 }
367
368 AutoCompartment::~AutoCompartment()
369 {
370     if (entered)
371         leave();
372 }
373
374 bool
375 AutoCompartment::enter()
376 {
377     JS_ASSERT(!entered);
378     if (origin != destination) {
379         LeaveTrace(context);
380
381         context->compartment = destination;
382         JSObject *scopeChain = target->getGlobal();
383         JS_ASSERT(scopeChain->isNative());
384
385         frame.construct();
386         if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
387             context->compartment = origin;
388             return false;
389         }
390
391         if (context->isExceptionPending())
392             context->wrapPendingException();
393     }
394     entered = true;
395     return true;
396 }
397
398 void
399 AutoCompartment::leave()
400 {
401     JS_ASSERT(entered);
402     if (origin != destination) {
403         frame.destroy();
404         context->resetCompartment();
405     }
406     entered = false;
407 }
408
409 /* Cross compartment wrappers. */
410
411 JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
412   : JSWrapper(CROSS_COMPARTMENT | flags)
413 {
414 }
415
416 JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper()
417 {
418 }
419
420 #define PIERCE(cx, wrapper, mode, pre, op, post)            \
421     JS_BEGIN_MACRO                                          \
422         AutoCompartment call(cx, wrappedObject(wrapper));   \
423         if (!call.enter())                                  \
424             return false;                                   \
425         bool ok = (pre) && (op);                            \
426         call.leave();                                       \
427         return ok && (post);                                \
428     JS_END_MACRO
429
430 #define NOTHING (true)
431
432 bool
433 JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
434                                                  bool set, PropertyDescriptor *desc)
435 {
436     PIERCE(cx, wrapper, set ? SET : GET,
437            call.destination->wrapId(cx, &id),
438            JSWrapper::getPropertyDescriptor(cx, wrapper, id, set, desc),
439            call.origin->wrap(cx, desc));
440 }
441
442 bool
443 JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
444                                                     bool set, PropertyDescriptor *desc)
445 {
446     PIERCE(cx, wrapper, set ? SET : GET,
447            call.destination->wrapId(cx, &id),
448            JSWrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc),
449            call.origin->wrap(cx, desc));
450 }
451
452 bool
453 JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
454 {
455     AutoPropertyDescriptorRooter desc2(cx, desc);
456     PIERCE(cx, wrapper, SET,
457            call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2),
458            JSWrapper::defineProperty(cx, wrapper, id, &desc2),
459            NOTHING);
460 }
461
462 bool
463 JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
464 {
465     PIERCE(cx, wrapper, GET,
466            NOTHING,
467            JSWrapper::getOwnPropertyNames(cx, wrapper, props),
468            call.origin->wrap(cx, props));
469 }
470
471 bool
472 JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
473 {
474     PIERCE(cx, wrapper, SET,
475            call.destination->wrapId(cx, &id),
476            JSWrapper::delete_(cx, wrapper, id, bp),
477            NOTHING);
478 }
479
480 bool
481 JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
482 {
483     PIERCE(cx, wrapper, GET,
484            NOTHING,
485            JSWrapper::enumerate(cx, wrapper, props),
486            call.origin->wrap(cx, props));
487 }
488
489 bool
490 JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
491 {
492     PIERCE(cx, wrapper, GET,
493            call.destination->wrapId(cx, &id),
494            JSWrapper::has(cx, wrapper, id, bp),
495            NOTHING);
496 }
497
498 bool
499 JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
500 {
501     PIERCE(cx, wrapper, GET,
502            call.destination->wrapId(cx, &id),
503            JSWrapper::hasOwn(cx, wrapper, id, bp),
504            NOTHING);
505 }
506
507 bool
508 JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
509 {
510     PIERCE(cx, wrapper, GET,
511            call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id),
512            JSWrapper::get(cx, wrapper, receiver, id, vp),
513            call.origin->wrap(cx, vp));
514 }
515
516 bool
517 JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
518                                bool strict, Value *vp)
519 {
520     AutoValueRooter tvr(cx, *vp);
521     PIERCE(cx, wrapper, SET,
522            call.destination->wrap(cx, &receiver) &&
523            call.destination->wrapId(cx, &id) &&
524            call.destination->wrap(cx, tvr.addr()),
525            JSWrapper::set(cx, wrapper, receiver, id, strict, tvr.addr()),
526            NOTHING);
527 }
528
529 bool
530 JSCrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
531 {
532     PIERCE(cx, wrapper, GET,
533            NOTHING,
534            JSWrapper::keys(cx, wrapper, props),
535            call.origin->wrap(cx, props));
536 }
537
538 /*
539  * We can reify non-escaping iterator objects instead of having to wrap them. This
540  * allows fast iteration over objects across a compartment boundary.
541  */
542 static bool
543 CanReify(Value *vp)
544 {
545     JSObject *obj;
546     return vp->isObject() &&
547            (obj = &vp->toObject())->getClass() == &js_IteratorClass &&
548            (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
549 }
550
551 struct AutoCloseIterator
552 {
553     AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
554
555     ~AutoCloseIterator() { if (obj) js_CloseIterator(cx, obj); }
556
557     void clear() { obj = NULL; }
558
559   private:
560     JSContext *cx;
561     JSObject *obj;
562 };
563
564 static bool
565 Reify(JSContext *cx, JSCompartment *origin, Value *vp)
566 {
567     JSObject *iterObj = &vp->toObject();
568     NativeIterator *ni = iterObj->getNativeIterator();
569
570     AutoCloseIterator close(cx, iterObj);
571
572     /* Wrap the iteratee. */
573     JSObject *obj = ni->obj;
574     if (!origin->wrap(cx, &obj))
575         return false;
576
577     /*
578      * Wrap the elements in the iterator's snapshot.
579      * N.B. the order of closing/creating iterators is important due to the
580      * implicit cx->enumerators state.
581      */
582     size_t length = ni->numKeys();
583     bool isKeyIter = ni->isKeyIter();
584     AutoIdVector keys(cx);
585     if (length > 0) {
586         if (!keys.resize(length))
587             return false;
588         for (size_t i = 0; i < length; ++i) {
589             keys[i] = ni->begin()[i];
590             if (!origin->wrapId(cx, &keys[i]))
591                 return false;
592         }
593     }
594
595     close.clear();
596     if (!js_CloseIterator(cx, iterObj))
597         return false;
598
599     if (isKeyIter)
600         return VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
601     return VectorToValueIterator(cx, obj, ni->flags, keys, vp); 
602 }
603
604 bool
605 JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
606 {
607     PIERCE(cx, wrapper, GET,
608            NOTHING,
609            JSWrapper::iterate(cx, wrapper, flags, vp),
610            CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp));
611 }
612
613 bool
614 JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp)
615 {
616     AutoCompartment call(cx, wrappedObject(wrapper));
617     if (!call.enter())
618         return false;
619
620     vp[0] = ObjectValue(*call.target);
621     if (!call.destination->wrap(cx, &vp[1]))
622         return false;
623     Value *argv = JS_ARGV(cx, vp);
624     for (size_t n = 0; n < argc; ++n) {
625         if (!call.destination->wrap(cx, &argv[n]))
626             return false;
627     }
628     if (!JSWrapper::call(cx, wrapper, argc, vp))
629         return false;
630
631     call.leave();
632     return call.origin->wrap(cx, vp);
633 }
634
635 bool
636 JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv,
637                                      Value *rval)
638 {
639     AutoCompartment call(cx, wrappedObject(wrapper));
640     if (!call.enter())
641         return false;
642
643     for (size_t n = 0; n < argc; ++n) {
644         if (!call.destination->wrap(cx, &argv[n]))
645             return false;
646     }
647     if (!JSWrapper::construct(cx, wrapper, argc, argv, rval))
648         return false;
649
650     call.leave();
651     return call.origin->wrap(cx, rval);
652 }
653
654 bool
655 JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
656 {
657     AutoCompartment call(cx, wrappedObject(wrapper));
658     if (!call.enter())
659         return false;
660
661     Value v = *vp;
662     if (!call.destination->wrap(cx, &v))
663         return false;
664     return JSWrapper::hasInstance(cx, wrapper, &v, bp);
665 }
666
667 JSString *
668 JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
669 {
670     AutoCompartment call(cx, wrappedObject(wrapper));
671     if (!call.enter())
672         return NULL;
673
674     JSString *str = JSWrapper::obj_toString(cx, wrapper);
675     if (!str)
676         return NULL;
677
678     call.leave();
679     if (!call.origin->wrap(cx, &str))
680         return NULL;
681     return str;
682 }
683
684 JSString *
685 JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
686 {
687     AutoCompartment call(cx, wrappedObject(wrapper));
688     if (!call.enter())
689         return NULL;
690
691     JSString *str = JSWrapper::fun_toString(cx, wrapper, indent);
692     if (!str)
693         return NULL;
694
695     call.leave();
696     if (!call.origin->wrap(cx, &str))
697         return NULL;
698     return str;
699 }
700
701 JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);