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"
49 #include "jsobjinlines.h"
53 static int sWrapperFamily
= 0;
56 JSObject::isWrapper() const
58 return isProxy() && getProxyHandler()->family() == &sWrapperFamily
;
62 JSObject::unwrap(uintN
*flagsp
)
64 JSObject
*wrapped
= this;
66 if (wrapped
->isWrapper()) {
67 flags
|= static_cast<JSWrapper
*>(wrapped
->getProxyHandler())->flags();
68 wrapped
= wrapped
->getProxyPrivate().toObjectOrNull();
75 JSWrapper::JSWrapper(uintN flags
) : JSProxyHandler(&sWrapperFamily
), mFlags(flags
)
79 JSWrapper::~JSWrapper()
83 #define CHECKED(op, set) \
85 if (!enter(cx, wrapper, id, set)) \
92 #define SET(action) CHECKED(action, true)
93 #define GET(action) CHECKED(action, false)
96 JSWrapper::getPropertyDescriptor(JSContext
*cx
, JSObject
*wrapper
, jsid id
,
97 PropertyDescriptor
*desc
)
99 GET(JS_GetPropertyDescriptorById(cx
, wrappedObject(wrapper
), id
, JSRESOLVE_QUALIFIED
,
104 GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
, JSPropertyDescriptor
*desc
)
106 if (!JS_GetPropertyDescriptorById(cx
, obj
, id
, flags
, desc
))
108 if (desc
->obj
!= obj
)
114 JSWrapper::getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*wrapper
, jsid id
,
115 PropertyDescriptor
*desc
)
117 GET(GetOwnPropertyDescriptor(cx
, wrappedObject(wrapper
), id
, JSRESOLVE_QUALIFIED
,
122 JSWrapper::defineProperty(JSContext
*cx
, JSObject
*wrapper
, jsid id
,
123 PropertyDescriptor
*desc
)
125 SET(JS_DefinePropertyById(cx
, wrappedObject(wrapper
), id
, Jsvalify(desc
->value
),
126 Jsvalify(desc
->getter
), Jsvalify(desc
->setter
), desc
->attrs
));
130 JSWrapper::getOwnPropertyNames(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
133 GET(GetPropertyNames(cx
, wrappedObject(wrapper
), JSITER_OWNONLY
| JSITER_HIDDEN
, props
));
137 ValueToBoolean(Value
*vp
, bool *bp
)
139 *bp
= js_ValueToBoolean(*vp
);
144 JSWrapper::delete_(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
147 SET(JS_DeletePropertyById2(cx
, wrappedObject(wrapper
), id
, Jsvalify(&v
)) &&
148 ValueToBoolean(&v
, bp
));
152 JSWrapper::enumerate(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
154 static jsid id
= JSID_VOID
;
155 GET(GetPropertyNames(cx
, wrappedObject(wrapper
), 0, props
));
159 JSWrapper::fix(JSContext
*cx
, JSObject
*wrapper
, Value
*vp
)
166 Cond(JSBool b
, bool *bp
)
173 JSWrapper::has(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
176 GET(JS_HasPropertyById(cx
, wrappedObject(wrapper
), id
, &found
) &&
181 JSWrapper::hasOwn(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
183 PropertyDescriptor desc
;
184 JSObject
*wobj
= wrappedObject(wrapper
);
185 GET(JS_GetPropertyDescriptorById(cx
, wobj
, id
, JSRESOLVE_QUALIFIED
, Jsvalify(&desc
)) &&
186 Cond(desc
.obj
== wobj
, bp
));
190 JSWrapper::get(JSContext
*cx
, JSObject
*wrapper
, JSObject
*receiver
, jsid id
, Value
*vp
)
192 GET(JS_GetPropertyById(cx
, wrappedObject(wrapper
), id
, Jsvalify(vp
)));
196 JSWrapper::set(JSContext
*cx
, JSObject
*wrapper
, JSObject
*receiver
, jsid id
, Value
*vp
)
198 SET(JS_SetPropertyById(cx
, wrappedObject(wrapper
), id
, Jsvalify(vp
)));
202 JSWrapper::enumerateOwn(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
204 const jsid id
= JSID_VOID
;
205 GET(GetPropertyNames(cx
, wrappedObject(wrapper
), JSITER_OWNONLY
, props
));
209 JSWrapper::iterate(JSContext
*cx
, JSObject
*wrapper
, uintN flags
, Value
*vp
)
211 const jsid id
= JSID_VOID
;
212 GET(GetIterator(cx
, wrappedObject(wrapper
), flags
, vp
));
216 JSWrapper::call(JSContext
*cx
, JSObject
*wrapper
, uintN argc
, Value
*vp
)
218 const jsid id
= JSID_VOID
;
219 GET(JSProxyHandler::call(cx
, wrapper
, argc
, vp
));
223 JSWrapper::construct(JSContext
*cx
, JSObject
*wrapper
, uintN argc
, Value
*argv
, Value
*rval
)
225 const jsid id
= JSID_VOID
;
226 GET(JSProxyHandler::construct(cx
, wrapper
, argc
, argv
, rval
));
230 JSWrapper::obj_toString(JSContext
*cx
, JSObject
*wrapper
)
233 if (!enter(cx
, wrapper
, JSID_VOID
, false))
235 str
= JSProxyHandler::obj_toString(cx
, wrapper
);
241 JSWrapper::fun_toString(JSContext
*cx
, JSObject
*wrapper
, uintN indent
)
244 if (!enter(cx
, wrapper
, JSID_VOID
, false))
246 str
= JSProxyHandler::fun_toString(cx
, wrapper
, indent
);
252 JSWrapper::trace(JSTracer
*trc
, JSObject
*wrapper
)
254 JS_CALL_OBJECT_TRACER(trc
, wrappedObject(wrapper
), "wrappedObject");
258 JSWrapper::enter(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool set
)
264 JSWrapper::leave(JSContext
*cx
, JSObject
*wrapper
)
268 JSWrapper
JSWrapper::singleton(0);
271 JSWrapper::New(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, JSObject
*parent
,
274 return NewProxyObject(cx
, handler
, ObjectValue(*obj
), proto
, parent
,
275 obj
->isCallable() ? obj
: NULL
, NULL
);
283 TransparentObjectWrapper(JSContext
*cx
, JSObject
*obj
, JSObject
*wrappedProto
, uintN flags
)
285 JS_ASSERT(!obj
->isWrapper());
286 return JSWrapper::New(cx
, obj
, wrappedProto
, NULL
, &JSCrossCompartmentWrapper::singleton
);
291 JSCompartment::JSCompartment(JSRuntime
*rt
)
292 : rt(rt
), principals(NULL
), data(NULL
), marked(false)
296 JSCompartment::~JSCompartment()
301 JSCompartment::init()
303 return crossCompartmentWrappers
.init();
307 JSCompartment::wrap(JSContext
*cx
, Value
*vp
)
309 JS_ASSERT(cx
->compartment
== this);
313 JS_CHECK_RECURSION(cx
, return false);
315 /* Only GC things have to be wrapped or copied. */
316 if (!vp
->isMarkable())
319 /* Static strings do not have to be wrapped. */
320 if (vp
->isString() && JSString::isStatic(vp
->toString()))
323 /* Unwrap incoming objects. */
324 if (vp
->isObject()) {
325 JSObject
*obj
= vp
->toObject().unwrap(&flags
);
327 /* If the wrapped object is already in this compartment, we are done. */
328 if (obj
->getCompartment(cx
) == this)
332 /* If we already have a wrapper for this value, use it. */
333 if (WrapperMap::Ptr p
= crossCompartmentWrappers
.lookup(*vp
)) {
338 if (vp
->isString()) {
340 JSString
*str
= vp
->toString();
341 JSString
*wrapped
= js_NewStringCopyN(cx
, str
->chars(), str
->length());
344 vp
->setString(wrapped
);
345 return crossCompartmentWrappers
.put(orig
, *vp
);
348 JSObject
*obj
= &vp
->toObject();
351 * Recurse to wrap the prototype. Long prototype chains will run out of
352 * stack, causing an error in CHECK_RECURSE.
354 * Wrapping the proto before creating the new wrapper and adding it to the
355 * cache helps avoid leaving a bad entry in the cache on OOM. But note that
356 * if we wrapped both proto and parent, we would get infinite recursion
357 * here (since Object.prototype->parent->proto leads to Object.prototype
360 JSObject
*proto
= obj
->getProto();
361 if (!wrap(cx
, &proto
))
365 * We hand in the original wrapped object into the wrap hook to allow
366 * the wrap hook to reason over what wrappers are currently applied
369 JSObject
*wrapper
= cx
->runtime
->wrapObjectCallback(cx
, obj
, proto
, flags
);
372 wrapper
->setProto(proto
);
373 vp
->setObject(*wrapper
);
374 if (!crossCompartmentWrappers
.put(wrapper
->getProxyPrivate(), *vp
))
378 * Wrappers should really be parented to the wrapped parent of the wrapped
379 * object, but in that case a wrapped global object would have a NULL
380 * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
381 * we parent all wrappers to the global object in their home compartment.
382 * This loses us some transparency, and is generally very cheesy.
385 cx
->hasfp() ? cx
->fp()->getScopeChain()->getGlobal() : cx
->globalObject
;
386 wrapper
->setParent(global
);
391 JSCompartment::wrap(JSContext
*cx
, JSString
**strp
)
393 AutoValueRooter
tvr(cx
, StringValue(*strp
));
394 if (!wrap(cx
, tvr
.addr()))
396 *strp
= tvr
.value().toString();
401 JSCompartment::wrap(JSContext
*cx
, JSObject
**objp
)
405 AutoValueRooter
tvr(cx
, ObjectValue(**objp
));
406 if (!wrap(cx
, tvr
.addr()))
408 *objp
= &tvr
.value().toObject();
413 JSCompartment::wrapId(JSContext
*cx
, jsid
*idp
) {
414 if (JSID_IS_INT(*idp
))
416 AutoValueRooter
tvr(cx
, IdToValue(*idp
));
417 if (!wrap(cx
, tvr
.addr()))
419 return ValueToId(cx
, tvr
.value(), idp
);
423 JSCompartment::wrap(JSContext
*cx
, PropertyOp
*propp
)
425 Value v
= CastAsObjectJsval(*propp
);
428 *propp
= CastAsPropertyOp(v
.toObjectOrNull());
433 JSCompartment::wrap(JSContext
*cx
, PropertyDescriptor
*desc
) {
434 return wrap(cx
, &desc
->obj
) &&
435 (!(desc
->attrs
& JSPROP_GETTER
) || wrap(cx
, &desc
->getter
)) &&
436 (!(desc
->attrs
& JSPROP_SETTER
) || wrap(cx
, &desc
->setter
)) &&
437 wrap(cx
, &desc
->value
);
441 JSCompartment::wrap(JSContext
*cx
, AutoIdVector
&props
) {
442 jsid
*vector
= props
.begin();
443 jsint length
= props
.length();
444 for (size_t n
= 0; n
< size_t(length
); ++n
) {
445 if (!wrapId(cx
, &vector
[n
]))
452 JSCompartment::wrapException(JSContext
*cx
) {
453 JS_ASSERT(cx
->compartment
== this);
456 AutoValueRooter
tvr(cx
, cx
->exception
);
457 cx
->throwing
= false;
458 cx
->exception
.setNull();
459 if (wrap(cx
, tvr
.addr())) {
461 cx
->exception
= tvr
.value();
469 JSCompartment::sweep(JSContext
*cx
)
471 /* Remove dead wrappers from the table. */
472 for (WrapperMap::Enum
e(crossCompartmentWrappers
); !e
.empty(); e
.popFront()) {
473 if (js_IsAboutToBeFinalized(e
.front().value
.asGCThing()))
478 AutoCompartment::AutoCompartment(JSContext
*cx
, JSObject
*target
)
480 origin(cx
->compartment
),
482 destination(target
->getCompartment(cx
)),
489 AutoCompartment::~AutoCompartment()
496 AutoCompartment::enter()
499 if (origin
!= destination
) {
501 context
->compartment
= destination
;
502 JSObject
*scopeChain
= target
->getGlobal();
504 if (!context
->stack().pushDummyFrame(context
, frame
.ref(), regs
, scopeChain
)) {
506 context
->compartment
= origin
;
509 js_SaveAndClearRegExpStatics(context
, &statics
, &input
);
516 AutoCompartment::leave()
519 if (origin
!= destination
) {
520 js_RestoreRegExpStatics(context
, &statics
);
522 context
->compartment
= origin
;
523 origin
->wrapException(context
);
528 /* Cross compartment wrappers. */
530 JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags
) : JSWrapper(flags
)
534 JSCrossCompartmentWrapper::~JSCrossCompartmentWrapper()
539 JSCrossCompartmentWrapper::isCrossCompartmentWrapper(JSObject
*obj
)
541 return obj
->isProxy() && obj
->getProxyHandler() == &JSCrossCompartmentWrapper::singleton
;
544 #define PIERCE(cx, wrapper, mode, pre, op, post) \
546 AutoCompartment call(cx, wrappedObject(wrapper)); \
549 bool ok = (pre) && (op); \
551 return ok && (post); \
554 #define NOTHING (true)
557 JSCrossCompartmentWrapper::getPropertyDescriptor(JSContext
*cx
, JSObject
*wrapper
, jsid id
, PropertyDescriptor
*desc
)
559 PIERCE(cx
, wrapper
, GET
,
560 call
.destination
->wrapId(cx
, &id
),
561 JSWrapper::getPropertyDescriptor(cx
, wrapper
, id
, desc
),
562 call
.origin
->wrap(cx
, desc
));
566 JSCrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext
*cx
, JSObject
*wrapper
, jsid id
, PropertyDescriptor
*desc
)
568 PIERCE(cx
, wrapper
, GET
,
569 call
.destination
->wrapId(cx
, &id
),
570 JSWrapper::getOwnPropertyDescriptor(cx
, wrapper
, id
, desc
),
571 call
.origin
->wrap(cx
, desc
));
575 JSCrossCompartmentWrapper::defineProperty(JSContext
*cx
, JSObject
*wrapper
, jsid id
, PropertyDescriptor
*desc
)
577 AutoPropertyDescriptorRooter
desc2(cx
, desc
);
578 PIERCE(cx
, wrapper
, SET
,
579 call
.destination
->wrapId(cx
, &id
) && call
.destination
->wrap(cx
, &desc2
),
580 JSWrapper::defineProperty(cx
, wrapper
, id
, &desc2
),
585 JSCrossCompartmentWrapper::getOwnPropertyNames(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
587 PIERCE(cx
, wrapper
, GET
,
589 JSWrapper::getOwnPropertyNames(cx
, wrapper
, props
),
590 call
.origin
->wrap(cx
, props
));
594 JSCrossCompartmentWrapper::delete_(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
596 PIERCE(cx
, wrapper
, SET
,
597 call
.destination
->wrapId(cx
, &id
),
598 JSWrapper::delete_(cx
, wrapper
, id
, bp
),
603 JSCrossCompartmentWrapper::enumerate(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
605 PIERCE(cx
, wrapper
, GET
,
607 JSWrapper::enumerate(cx
, wrapper
, props
),
608 call
.origin
->wrap(cx
, props
));
612 JSCrossCompartmentWrapper::has(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
614 PIERCE(cx
, wrapper
, GET
,
615 call
.destination
->wrapId(cx
, &id
),
616 JSWrapper::has(cx
, wrapper
, id
, bp
),
621 JSCrossCompartmentWrapper::hasOwn(JSContext
*cx
, JSObject
*wrapper
, jsid id
, bool *bp
)
623 PIERCE(cx
, wrapper
, GET
,
624 call
.destination
->wrapId(cx
, &id
),
625 JSWrapper::hasOwn(cx
, wrapper
, id
, bp
),
630 JSCrossCompartmentWrapper::get(JSContext
*cx
, JSObject
*wrapper
, JSObject
*receiver
, jsid id
, Value
*vp
)
632 PIERCE(cx
, wrapper
, GET
,
633 call
.destination
->wrap(cx
, &receiver
) && call
.destination
->wrapId(cx
, &id
),
634 JSWrapper::get(cx
, wrapper
, receiver
, id
, vp
),
635 call
.origin
->wrap(cx
, vp
));
639 JSCrossCompartmentWrapper::set(JSContext
*cx
, JSObject
*wrapper
, JSObject
*receiver
, jsid id
, Value
*vp
)
641 AutoValueRooter
tvr(cx
, *vp
);
642 PIERCE(cx
, wrapper
, SET
,
643 call
.destination
->wrap(cx
, &receiver
) && call
.destination
->wrapId(cx
, &id
) && call
.destination
->wrap(cx
, tvr
.addr()),
644 JSWrapper::set(cx
, wrapper
, receiver
, id
, tvr
.addr()),
649 JSCrossCompartmentWrapper::enumerateOwn(JSContext
*cx
, JSObject
*wrapper
, AutoIdVector
&props
)
651 PIERCE(cx
, wrapper
, GET
,
653 JSWrapper::enumerateOwn(cx
, wrapper
, props
),
654 call
.origin
->wrap(cx
, props
));
658 * We can reify non-escaping iterator objects instead of having to wrap them. This
659 * allows fast iteration over objects across a compartment boundary.
665 return vp
->isObject() &&
666 (obj
= &vp
->toObject())->getClass() == &js_IteratorClass
&&
667 (obj
->getNativeIterator()->flags
& JSITER_ENUMERATE
);
671 Reify(JSContext
*cx
, JSCompartment
*origin
, Value
*vp
)
673 JSObject
*iterObj
= &vp
->toObject();
674 NativeIterator
*ni
= iterObj
->getNativeIterator();
676 /* Wrap the iteratee. */
677 JSObject
*obj
= ni
->obj
;
678 if (!origin
->wrap(cx
, &obj
))
682 * Wrap the elements in the iterator's snapshot.
683 * N.B. the order of closing/creating iterators is important due to the
684 * implicit cx->enumerators state.
687 if (ni
->isKeyIter()) {
688 size_t length
= ni
->numKeys();
689 AutoIdVector
keys(cx
);
691 if (!keys
.resize(length
))
693 for (size_t i
= 0; i
< length
; ++i
) {
694 keys
[i
] = ni
->beginKey()[i
];
695 if (!origin
->wrapId(cx
, &keys
[i
]))
700 return js_CloseIterator(cx
, iterObj
) &&
701 VectorToKeyIterator(cx
, obj
, ni
->flags
, keys
, vp
);
704 size_t length
= ni
->numValues();
705 AutoValueVector
vals(cx
);
707 if (!vals
.resize(length
))
709 for (size_t i
= 0; i
< length
; ++i
) {
710 vals
[i
] = ni
->beginValue()[i
];
711 if (!origin
->wrap(cx
, &vals
[i
]))
717 return js_CloseIterator(cx
, iterObj
) &&
718 VectorToValueIterator(cx
, obj
, ni
->flags
, vals
, vp
);
722 JSCrossCompartmentWrapper::iterate(JSContext
*cx
, JSObject
*wrapper
, uintN flags
, Value
*vp
)
724 PIERCE(cx
, wrapper
, GET
,
726 JSWrapper::iterate(cx
, wrapper
, flags
, vp
),
727 CanReify(vp
) ? Reify(cx
, call
.origin
, vp
) : call
.origin
->wrap(cx
, vp
));
731 JSCrossCompartmentWrapper::call(JSContext
*cx
, JSObject
*wrapper
, uintN argc
, Value
*vp
)
733 AutoCompartment
call(cx
, wrappedObject(wrapper
));
737 vp
[0] = ObjectValue(*call
.target
);
738 if (!call
.destination
->wrap(cx
, &vp
[1]))
740 Value
*argv
= JS_ARGV(cx
, vp
);
741 for (size_t n
= 0; n
< argc
; ++n
) {
742 if (!call
.destination
->wrap(cx
, &argv
[n
]))
745 if (!JSWrapper::call(cx
, wrapper
, argc
, vp
))
749 return call
.origin
->wrap(cx
, vp
);
753 JSCrossCompartmentWrapper::construct(JSContext
*cx
, JSObject
*wrapper
, uintN argc
, Value
*argv
,
756 AutoCompartment
call(cx
, wrappedObject(wrapper
));
760 for (size_t n
= 0; n
< argc
; ++n
) {
761 if (!call
.destination
->wrap(cx
, &argv
[n
]))
764 if (!JSWrapper::construct(cx
, wrapper
, argc
, argv
, rval
))
768 return call
.origin
->wrap(cx
, rval
) &&
769 call
.origin
->wrapException(cx
);
773 JSCrossCompartmentWrapper::obj_toString(JSContext
*cx
, JSObject
*wrapper
)
775 AutoCompartment
call(cx
, wrappedObject(wrapper
));
779 JSString
*str
= JSWrapper::obj_toString(cx
, wrapper
);
784 if (!call
.origin
->wrap(cx
, &str
))
790 JSCrossCompartmentWrapper::fun_toString(JSContext
*cx
, JSObject
*wrapper
, uintN indent
)
792 AutoCompartment
call(cx
, wrappedObject(wrapper
));
796 JSString
*str
= JSWrapper::fun_toString(cx
, wrapper
, indent
);
801 if (!call
.origin
->wrap(cx
, &str
))
806 JSCrossCompartmentWrapper
JSCrossCompartmentWrapper::singleton(0);