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);