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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
22 * Portions created by the Initial Developer are Copyright (C) 2010
23 * the Initial Developer. All Rights Reserved.
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 ***** */
47 #include "jswrapper.h"
48 #include "methodjit/PolyIC.h"
49 #include "methodjit/MonoIC.h"
51 # include "assembler/jit/ExecutableAllocator.h"
53 #include "jscompartment.h"
55 #include "jsobjinlines.h"
58 using namespace js::gc;
60 static int sWrapperFamily;
63 JSWrapper::getWrapperFamily()
65 return &sWrapperFamily;
69 JSObject::isWrapper() const
71 return isProxy() && getProxyHandler()->family() == &sWrapperFamily;
75 JSObject::unwrap(uintN *flagsp)
77 JSObject *wrapped = this;
79 while (wrapped->isWrapper()) {
80 flags |= static_cast<JSWrapper *>(wrapped->getProxyHandler())->flags();
81 wrapped = wrapped->getProxyPrivate().toObjectOrNull();
82 if (wrapped->getClass()->ext.innerObject)
90 JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags)
94 JSWrapper::~JSWrapper()
98 #define CHECKED(op, act) \
101 if (!enter(cx, wrapper, id, act, &status)) \
104 leave(cx, wrapper); \
108 #define SET(action) CHECKED(action, SET)
109 #define GET(action) CHECKED(action, GET)
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);
121 GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSPropertyDescriptor *desc)
123 if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
125 if (desc->obj != obj)
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);
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));
148 JSWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
150 // if we refuse to perform this action, props remains empty
152 GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY | JSITER_HIDDEN, &props));
156 ValueToBoolean(Value *vp, bool *bp)
158 *bp = js_ValueToBoolean(*vp);
163 JSWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
165 *bp = true; // default result if we refuse to perform this action
167 SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, Jsvalify(&v)) &&
168 ValueToBoolean(&v, bp));
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));
180 JSWrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp)
187 Cond(JSBool b, bool *bp)
194 JSWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
196 *bp = false; // default result if we refuse to perform this action
198 GET(JS_HasPropertyById(cx, wrappedObject(wrapper), id, &found) &&
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));
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));
220 JSWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict,
223 // FIXME (bug 596351): Need deal with strict mode.
224 SET(wrappedObject(wrapper)->setProperty(cx, id, vp, false));
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));
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));
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);
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));
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;
265 GET(JS_HasInstance(cx, wrappedObject(wrapper), Jsvalify(*vp), &b) && Cond(b, bp));
269 JSWrapper::typeOf(JSContext *cx, JSObject *wrapper)
271 return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper)));
275 JSWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
278 if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
280 // Perform some default behavior that doesn't leak any information.
281 return JS_NewStringCopyZ(cx, "[object Object]");
285 JSString *str = obj_toStringHelper(cx, wrappedObject(wrapper));
291 JSWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
294 if (!enter(cx, wrapper, JSID_VOID, GET, &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);
305 JSString *str = JSProxyHandler::fun_toString(cx, wrapper, indent);
311 JSWrapper::trace(JSTracer *trc, JSObject *wrapper)
313 MarkObject(trc, *wrappedObject(wrapper), "wrappedObject");
317 JSWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
324 JSWrapper::leave(JSContext *cx, JSObject *wrapper)
328 JSWrapper JSWrapper::singleton((uintN)0);
331 JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
336 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WRAP_XML_OBJECT);
339 return NewProxyObject(cx, handler, ObjectValue(*obj), proto, parent,
340 obj->isCallable() ? obj : NULL, NULL);
348 TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
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)
360 origin(cx->compartment),
362 destination(target->getCompartment()),
368 AutoCompartment::~AutoCompartment()
375 AutoCompartment::enter()
378 if (origin != destination) {
381 context->compartment = destination;
382 JSObject *scopeChain = target->getGlobal();
383 JS_ASSERT(scopeChain->isNative());
386 if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
387 context->compartment = origin;
391 if (context->isExceptionPending())
392 context->wrapPendingException();
399 AutoCompartment::leave()
402 if (origin != destination) {
404 context->resetCompartment();
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) \
422 AutoCompartment call(cx, wrappedObject(wrapper)); \
425 bool ok = (pre) && (op); \
427 return ok && (post); \
430 #define NOTHING (true)
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));
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));
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),
463 JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
465 PIERCE(cx, wrapper, GET,
467 JSWrapper::getOwnPropertyNames(cx, wrapper, props),
468 call.origin->wrap(cx, props));
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),
481 JSCrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
483 PIERCE(cx, wrapper, GET,
485 JSWrapper::enumerate(cx, wrapper, props),
486 call.origin->wrap(cx, props));
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),
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),
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));
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()),
530 JSCrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
532 PIERCE(cx, wrapper, GET,
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.
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; }
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))
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);
586 if (!keys.resize(length))
588 for (size_t i = 0; i < length; ++i) {
589 keys[i] = ni->begin()[i];
590 if (!origin->wrapId(cx, &keys[i]))
596 if (!js_CloseIterator(cx, iterObj))
600 return VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
601 return VectorToValueIterator(cx, obj, ni->flags, keys, vp);
605 JSCrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, uintN flags, Value *vp)
607 PIERCE(cx, wrapper, GET,
609 JSWrapper::iterate(cx, wrapper, flags, vp),
610 CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp));
614 JSCrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, Value *vp)
616 AutoCompartment call(cx, wrappedObject(wrapper));
620 vp[0] = ObjectValue(*call.target);
621 if (!call.destination->wrap(cx, &vp[1]))
623 Value *argv = JS_ARGV(cx, vp);
624 for (size_t n = 0; n < argc; ++n) {
625 if (!call.destination->wrap(cx, &argv[n]))
628 if (!JSWrapper::call(cx, wrapper, argc, vp))
632 return call.origin->wrap(cx, vp);
636 JSCrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, uintN argc, Value *argv,
639 AutoCompartment call(cx, wrappedObject(wrapper));
643 for (size_t n = 0; n < argc; ++n) {
644 if (!call.destination->wrap(cx, &argv[n]))
647 if (!JSWrapper::construct(cx, wrapper, argc, argv, rval))
651 return call.origin->wrap(cx, rval);
655 JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
657 AutoCompartment call(cx, wrappedObject(wrapper));
662 if (!call.destination->wrap(cx, &v))
664 return JSWrapper::hasInstance(cx, wrapper, &v, bp);
668 JSCrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
670 AutoCompartment call(cx, wrappedObject(wrapper));
674 JSString *str = JSWrapper::obj_toString(cx, wrapper);
679 if (!call.origin->wrap(cx, &str))
685 JSCrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
687 AutoCompartment call(cx, wrappedObject(wrapper));
691 JSString *str = JSWrapper::fun_toString(cx, wrapper, indent);
696 if (!call.origin->wrap(cx, &str))
701 JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);