Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / js / src / jswrapper.cpp
blob8e4e0c87579054dff36c2d778ceb3714375dbdcc
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) 2010
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 "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"
55 #include "jsobjinlines.h"
57 using namespace js;
58 using namespace js::gc;
60 static int sWrapperFamily;
62 void *
63 JSWrapper::getWrapperFamily()
65 return &sWrapperFamily;
68 bool
69 JSObject::isWrapper() const
71 return isProxy() && getProxyHandler()->family() == &sWrapperFamily;
74 JSObject *
75 JSObject::unwrap(uintN *flagsp)
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;
85 if (flagsp)
86 *flagsp = flags;
87 return wrapped;
90 JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags)
94 JSWrapper::~JSWrapper()
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
108 #define SET(action) CHECKED(action, SET)
109 #define GET(action) CHECKED(action, GET)
111 bool
112 JSWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
113 bool set, PropertyDescriptor *desc)
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);
120 static bool
121 GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSPropertyDescriptor *desc)
123 if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
124 return false;
125 if (desc->obj != obj)
126 desc->obj = NULL;
127 return true;
130 bool
131 JSWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
132 PropertyDescriptor *desc)
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);
139 bool
140 JSWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
141 PropertyDescriptor *desc)
143 SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, Jsvalify(desc->value),
144 Jsvalify(desc->getter), Jsvalify(desc->setter), desc->attrs));
147 bool
148 JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
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));
155 static bool
156 ValueToBoolean(Value *vp, bool *bp)
158 *bp = js_ValueToBoolean(*vp);
159 return true;
162 bool
163 JSWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
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));
171 bool
172 JSWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
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));
179 bool
180 JSWrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp)
182 vp->setUndefined();
183 return true;
186 static bool
187 Cond(JSBool b, bool *bp)
189 *bp = !!b;
190 return true;
193 bool
194 JSWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
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));
202 bool
203 JSWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
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));
212 bool
213 JSWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
215 vp->setUndefined(); // default result if we refuse to perform this action
216 GET(wrappedObject(wrapper)->getProperty(cx, receiver, id, vp));
219 bool
220 JSWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict,
221 Value *vp)
223 // FIXME (bug 596351): Need deal with strict mode.
224 SET(wrappedObject(wrapper)->setProperty(cx, id, vp, false));
227 bool
228 JSWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
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));
235 bool
236 JSWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
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));
243 bool
244 JSWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp)
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);
251 bool
252 JSWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv, Value *vp)
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));
259 bool
260 JSWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
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));
268 JSType
269 JSWrapper::typeOf(JSContext *cx, JSObject *wrapper)
271 return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper)));
274 JSString *
275 JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
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]");
283 return NULL;
285 JSString *str = obj_toStringHelper(cx, wrappedObject(wrapper));
286 leave(cx, wrapper);
287 return str;
290 JSString *
291 JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
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;
303 return NULL;
305 JSString *str = JSProxyHandler::fun_toString(cx, wrapper, indent);
306 leave(cx, wrapper);
307 return str;
310 void
311 JSWrapper::trace(JSTracer *trc, JSObject *wrapper)
313 MarkObject(trc, *wrappedObject(wrapper), "wrappedObject");
316 bool
317 JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
319 *bp = true;
320 return true;
323 void
324 JSWrapper::leave(JSContext *cx, JSObject *wrapper)
328 JSWrapper JSWrapper::singleton((uintN)0);
330 JSObject *
331 JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
332 JSWrapper *handler)
334 JS_ASSERT(parent);
335 if (obj->isXML()) {
336 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WRAP_XML_OBJECT);
337 return NULL;
339 return NewProxyObject(cx, handler, ObjectValue(*obj), proto, parent,
340 obj->isCallable() ? obj : NULL, NULL);
343 /* Compartments. */
345 namespace js {
347 extern JSObject *
348 TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
349 uintN flags)
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);
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)
368 AutoCompartment::~AutoCompartment()
370 if (entered)
371 leave();
374 bool
375 AutoCompartment::enter()
377 JS_ASSERT(!entered);
378 if (origin != destination) {
379 LeaveTrace(context);
381 context->compartment = destination;
382 JSObject *scopeChain = target->getGlobal();
383 JS_ASSERT(scopeChain->isNative());
385 frame.construct();
386 if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
387 context->compartment = origin;
388 return false;
391 if (context->isExceptionPending())
392 context->wrapPendingException();
394 entered = true;
395 return true;
398 void
399 AutoCompartment::leave()
401 JS_ASSERT(entered);
402 if (origin != destination) {
403 frame.destroy();
404 context->resetCompartment();
406 entered = false;
409 /* Cross compartment wrappers. */
411 JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
412 : JSWrapper(CROSS_COMPARTMENT | flags)
416 JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper()
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
430 #define NOTHING (true)
432 bool
433 JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
434 bool set, PropertyDescriptor *desc)
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));
442 bool
443 JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
444 bool set, PropertyDescriptor *desc)
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));
452 bool
453 JSCrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
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);
462 bool
463 JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
465 PIERCE(cx, wrapper, GET,
466 NOTHING,
467 JSWrapper::getOwnPropertyNames(cx, wrapper, props),
468 call.origin->wrap(cx, props));
471 bool
472 JSCrossCompartmentWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
474 PIERCE(cx, wrapper, SET,
475 call.destination->wrapId(cx, &id),
476 JSWrapper::delete_(cx, wrapper, id, bp),
477 NOTHING);
480 bool
481 JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
483 PIERCE(cx, wrapper, GET,
484 NOTHING,
485 JSWrapper::enumerate(cx, wrapper, props),
486 call.origin->wrap(cx, props));
489 bool
490 JSCrossCompartmentWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
492 PIERCE(cx, wrapper, GET,
493 call.destination->wrapId(cx, &id),
494 JSWrapper::has(cx, wrapper, id, bp),
495 NOTHING);
498 bool
499 JSCrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
501 PIERCE(cx, wrapper, GET,
502 call.destination->wrapId(cx, &id),
503 JSWrapper::hasOwn(cx, wrapper, id, bp),
504 NOTHING);
507 bool
508 JSCrossCompartmentWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
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));
516 bool
517 JSCrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
518 bool strict, Value *vp)
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);
529 bool
530 JSCrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
532 PIERCE(cx, wrapper, GET,
533 NOTHING,
534 JSWrapper::keys(cx, wrapper, props),
535 call.origin->wrap(cx, props));
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.
542 static bool
543 CanReify(Value *vp)
545 JSObject *obj;
546 return vp->isObject() &&
547 (obj = &vp->toObject())->getClass() == &js_IteratorClass &&
548 (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
551 struct AutoCloseIterator
553 AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
555 ~AutoCloseIterator() { if (obj) js_CloseIterator(cx, obj); }
557 void clear() { obj = NULL; }
559 private:
560 JSContext *cx;
561 JSObject *obj;
564 static bool
565 Reify(JSContext *cx, JSCompartment *origin, Value *vp)
567 JSObject *iterObj = &vp->toObject();
568 NativeIterator *ni = iterObj->getNativeIterator();
570 AutoCloseIterator close(cx, iterObj);
572 /* Wrap the iteratee. */
573 JSObject *obj = ni->obj;
574 if (!origin->wrap(cx, &obj))
575 return false;
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.
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;
595 close.clear();
596 if (!js_CloseIterator(cx, iterObj))
597 return false;
599 if (isKeyIter)
600 return VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
601 return VectorToValueIterator(cx, obj, ni->flags, keys, vp);
604 bool
605 JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
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));
613 bool
614 JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp)
616 AutoCompartment call(cx, wrappedObject(wrapper));
617 if (!call.enter())
618 return false;
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;
628 if (!JSWrapper::call(cx, wrapper, argc, vp))
629 return false;
631 call.leave();
632 return call.origin->wrap(cx, vp);
635 bool
636 JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv,
637 Value *rval)
639 AutoCompartment call(cx, wrappedObject(wrapper));
640 if (!call.enter())
641 return false;
643 for (size_t n = 0; n < argc; ++n) {
644 if (!call.destination->wrap(cx, &argv[n]))
645 return false;
647 if (!JSWrapper::construct(cx, wrapper, argc, argv, rval))
648 return false;
650 call.leave();
651 return call.origin->wrap(cx, rval);
654 bool
655 JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
657 AutoCompartment call(cx, wrappedObject(wrapper));
658 if (!call.enter())
659 return false;
661 Value v = *vp;
662 if (!call.destination->wrap(cx, &v))
663 return false;
664 return JSWrapper::hasInstance(cx, wrapper, &v, bp);
667 JSString *
668 JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
670 AutoCompartment call(cx, wrappedObject(wrapper));
671 if (!call.enter())
672 return NULL;
674 JSString *str = JSWrapper::obj_toString(cx, wrapper);
675 if (!str)
676 return NULL;
678 call.leave();
679 if (!call.origin->wrap(cx, &str))
680 return NULL;
681 return str;
684 JSString *
685 JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
687 AutoCompartment call(cx, wrappedObject(wrapper));
688 if (!call.enter())
689 return NULL;
691 JSString *str = JSWrapper::fun_toString(cx, wrapper, indent);
692 if (!str)
693 return NULL;
695 call.leave();
696 if (!call.origin->wrap(cx, &str))
697 return NULL;
698 return str;
701 JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);