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 "jsexn.h"
45 : #include "jsgc.h"
46 : #include "jsgcmark.h"
47 : #include "jsiter.h"
48 : #include "jsnum.h"
49 : #include "jswrapper.h"
50 : #include "methodjit/PolyIC.h"
51 : #include "methodjit/MonoIC.h"
52 : #ifdef JS_METHODJIT
53 : # include "assembler/jit/ExecutableAllocator.h"
54 : #endif
55 : #include "jscompartment.h"
56 :
57 : #include "jsobjinlines.h"
58 :
59 : #include "vm/RegExpObject-inl.h"
60 :
61 : using namespace js;
62 : using namespace js::gc;
63 :
64 : namespace js {
65 : int sWrapperFamily;
66 : }
67 :
68 : void *
69 0 : Wrapper::getWrapperFamily()
70 : {
71 0 : return &sWrapperFamily;
72 : }
73 :
74 : JS_FRIEND_API(JSObject *)
75 59070364 : js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
76 : {
77 59070364 : unsigned flags = 0;
78 118242904 : while (wrapped->isWrapper()) {
79 102176 : flags |= static_cast<Wrapper *>(GetProxyHandler(wrapped))->flags();
80 102176 : wrapped = GetProxyPrivate(wrapped).toObjectOrNull();
81 102176 : if (stopAtOuter && wrapped->getClass()->ext.innerObject)
82 0 : break;
83 : }
84 59070364 : if (flagsp)
85 194355 : *flagsp = flags;
86 59070364 : return wrapped;
87 : }
88 :
89 : JS_FRIEND_API(JSObject *)
90 9 : js::UnwrapObjectChecked(JSContext *cx, JSObject *obj)
91 : {
92 18 : while (obj->isWrapper()) {
93 0 : JSObject *wrapper = obj;
94 0 : Wrapper *handler = Wrapper::wrapperHandler(obj);
95 : bool rvOnFailure;
96 0 : if (!handler->enter(cx, wrapper, JSID_VOID,
97 0 : Wrapper::PUNCTURE, &rvOnFailure))
98 0 : return rvOnFailure ? obj : NULL;
99 0 : obj = Wrapper::wrappedObject(obj);
100 0 : JS_ASSERT(obj);
101 0 : handler->leave(cx, wrapper);
102 0 : if (obj->getClass()->ext.innerObject)
103 0 : break;
104 : }
105 9 : return obj;
106 : }
107 :
108 : bool
109 75046 : js::IsCrossCompartmentWrapper(const JSObject *wrapper)
110 : {
111 75046 : return wrapper->isWrapper() &&
112 75046 : !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
113 : }
114 :
115 37335 : Wrapper::Wrapper(unsigned flags) : ProxyHandler(&sWrapperFamily), mFlags(flags)
116 : {
117 37335 : }
118 :
119 37335 : Wrapper::~Wrapper()
120 : {
121 37335 : }
122 :
123 : #define CHECKED(op, act) \
124 : JS_BEGIN_MACRO \
125 : bool status; \
126 : if (!enter(cx, wrapper, id, act, &status)) \
127 : return status; \
128 : bool ok = (op); \
129 : leave(cx, wrapper); \
130 : return ok; \
131 : JS_END_MACRO
132 :
133 : #define SET(action) CHECKED(action, SET)
134 : #define GET(action) CHECKED(action, GET)
135 :
136 : bool
137 0 : Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
138 : PropertyDescriptor *desc)
139 : {
140 0 : desc->obj = NULL; // default result if we refuse to perform this action
141 0 : CHECKED(JS_GetPropertyDescriptorById(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc),
142 : set ? SET : GET);
143 : }
144 :
145 : static bool
146 36 : GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSPropertyDescriptor *desc)
147 : {
148 : // If obj is a proxy, we can do better than just guessing. This is
149 : // important for certain types of wrappers that wrap other wrappers.
150 36 : if (obj->isProxy())
151 0 : return Proxy::getOwnPropertyDescriptor(cx, obj, id, flags & JSRESOLVE_ASSIGNING, desc);
152 :
153 36 : if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc))
154 0 : return false;
155 36 : if (desc->obj != obj)
156 0 : desc->obj = NULL;
157 36 : return true;
158 : }
159 :
160 : bool
161 36 : Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, bool set,
162 : PropertyDescriptor *desc)
163 : {
164 36 : desc->obj = NULL; // default result if we refuse to perform this action
165 36 : CHECKED(GetOwnPropertyDescriptor(cx, wrappedObject(wrapper), id, JSRESOLVE_QUALIFIED, desc),
166 : set ? SET : GET);
167 : }
168 :
169 : bool
170 36 : Wrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
171 : {
172 36 : SET(JS_DefinePropertyById(cx, wrappedObject(wrapper), id, desc->value,
173 : desc->getter, desc->setter, desc->attrs));
174 : }
175 :
176 : bool
177 0 : Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
178 : {
179 : // if we refuse to perform this action, props remains empty
180 0 : jsid id = JSID_VOID;
181 0 : GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY | JSITER_HIDDEN, &props));
182 : }
183 :
184 : static bool
185 9 : ValueToBoolean(Value *vp, bool *bp)
186 : {
187 9 : *bp = js_ValueToBoolean(*vp);
188 9 : return true;
189 : }
190 :
191 : bool
192 9 : Wrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
193 : {
194 9 : *bp = true; // default result if we refuse to perform this action
195 : Value v;
196 9 : SET(JS_DeletePropertyById2(cx, wrappedObject(wrapper), id, &v) &&
197 : ValueToBoolean(&v, bp));
198 : }
199 :
200 : bool
201 0 : Wrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
202 : {
203 : // if we refuse to perform this action, props remains empty
204 0 : static jsid id = JSID_VOID;
205 0 : GET(GetPropertyNames(cx, wrappedObject(wrapper), 0, &props));
206 : }
207 :
208 : bool
209 0 : Wrapper::fix(JSContext *cx, JSObject *wrapper, Value *vp)
210 : {
211 0 : vp->setUndefined();
212 0 : return true;
213 : }
214 :
215 : static bool
216 23751 : Cond(JSBool b, bool *bp)
217 : {
218 23751 : *bp = !!b;
219 23751 : return true;
220 : }
221 :
222 : bool
223 23751 : Wrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
224 : {
225 23751 : *bp = false; // default result if we refuse to perform this action
226 : JSBool found;
227 23751 : GET(JS_HasPropertyById(cx, wrappedObject(wrapper), id, &found) &&
228 : Cond(found, bp));
229 : }
230 :
231 : bool
232 0 : Wrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
233 : {
234 0 : *bp = false; // default result if we refuse to perform this action
235 : PropertyDescriptor desc;
236 0 : JSObject *wobj = wrappedObject(wrapper);
237 0 : GET(JS_GetPropertyDescriptorById(cx, wobj, id, JSRESOLVE_QUALIFIED, &desc) &&
238 : Cond(desc.obj == wobj, bp));
239 : }
240 :
241 : bool
242 55241 : Wrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
243 : {
244 55241 : vp->setUndefined(); // default result if we refuse to perform this action
245 55241 : GET(wrappedObject(wrapper)->getGeneric(cx, receiver, id, vp));
246 : }
247 :
248 : bool
249 16790 : Wrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict,
250 : Value *vp)
251 : {
252 16790 : SET(wrappedObject(wrapper)->setGeneric(cx, id, vp, strict));
253 : }
254 :
255 : bool
256 22419 : Wrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
257 : {
258 : // if we refuse to perform this action, props remains empty
259 22419 : const jsid id = JSID_VOID;
260 22419 : GET(GetPropertyNames(cx, wrappedObject(wrapper), JSITER_OWNONLY, &props));
261 : }
262 :
263 : bool
264 45 : Wrapper::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp)
265 : {
266 45 : vp->setUndefined(); // default result if we refuse to perform this action
267 45 : const jsid id = JSID_VOID;
268 45 : GET(GetIterator(cx, wrappedObject(wrapper), flags, vp));
269 : }
270 :
271 : bool
272 20866 : Wrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp)
273 : {
274 20866 : vp->setUndefined(); // default result if we refuse to perform this action
275 20866 : const jsid id = JSID_VOID;
276 20866 : CHECKED(ProxyHandler::call(cx, wrapper, argc, vp), CALL);
277 : }
278 :
279 : bool
280 117 : Wrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *vp)
281 : {
282 117 : vp->setUndefined(); // default result if we refuse to perform this action
283 117 : const jsid id = JSID_VOID;
284 117 : GET(ProxyHandler::construct(cx, wrapper, argc, argv, vp));
285 : }
286 :
287 : bool
288 2709 : Wrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args)
289 : {
290 2709 : const jsid id = JSID_VOID;
291 2709 : CHECKED(CallJSNative(cx, native, args), CALL);
292 : }
293 :
294 : bool
295 0 : Wrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
296 : {
297 0 : *bp = false; // default result if we refuse to perform this action
298 0 : const jsid id = JSID_VOID;
299 0 : JSBool b = JS_FALSE;
300 0 : GET(JS_HasInstance(cx, wrappedObject(wrapper), *vp, &b) && Cond(b, bp));
301 : }
302 :
303 : JSType
304 234 : Wrapper::typeOf(JSContext *cx, JSObject *wrapper)
305 : {
306 234 : return TypeOfValue(cx, ObjectValue(*wrappedObject(wrapper)));
307 : }
308 :
309 : bool
310 441 : Wrapper::objectClassIs(JSObject *wrapper, ESClassValue classValue, JSContext *cx)
311 : {
312 441 : return ObjectClassIs(*wrappedObject(wrapper), classValue, cx);
313 : }
314 :
315 : JSString *
316 1944 : Wrapper::obj_toString(JSContext *cx, JSObject *wrapper)
317 : {
318 : bool status;
319 1944 : if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
320 0 : if (status) {
321 : // Perform some default behavior that doesn't leak any information.
322 0 : return JS_NewStringCopyZ(cx, "[object Object]");
323 : }
324 0 : return NULL;
325 : }
326 1944 : JSString *str = obj_toStringHelper(cx, wrappedObject(wrapper));
327 1944 : leave(cx, wrapper);
328 1944 : return str;
329 : }
330 :
331 : JSString *
332 9 : Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent)
333 : {
334 : bool status;
335 9 : if (!enter(cx, wrapper, JSID_VOID, GET, &status)) {
336 0 : if (status) {
337 : // Perform some default behavior that doesn't leak any information.
338 0 : if (wrapper->isCallable())
339 0 : return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
340 0 : js::Value v = ObjectValue(*wrapper);
341 0 : js_ReportIsNotFunction(cx, &v, 0);
342 0 : return NULL;
343 : }
344 0 : return NULL;
345 : }
346 9 : JSString *str = ProxyHandler::fun_toString(cx, wrapper, indent);
347 9 : leave(cx, wrapper);
348 9 : return str;
349 : }
350 :
351 : bool
352 54 : Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper, RegExpGuard *g)
353 : {
354 54 : return wrappedObject(wrapper)->asRegExp().getShared(cx, g);
355 : }
356 :
357 : bool
358 415 : Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
359 : {
360 415 : *vp = ObjectValue(*wrappedObject(wrapper));
361 415 : if (hint == JSTYPE_VOID)
362 73 : return ToPrimitive(cx, vp);
363 342 : return ToPrimitive(cx, hint, vp);
364 : }
365 :
366 : bool
367 54 : Wrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp)
368 : {
369 54 : if (!js_IteratorMore(cx, wrappedObject(wrapper), vp))
370 0 : return false;
371 :
372 54 : if (vp->toBoolean()) {
373 45 : *vp = cx->iterValue;
374 45 : cx->iterValue.setUndefined();
375 : } else {
376 9 : vp->setMagic(JS_NO_ITER_VALUE);
377 : }
378 54 : return true;
379 : }
380 :
381 : void
382 6 : Wrapper::trace(JSTracer *trc, JSObject *wrapper)
383 : {
384 6 : MarkSlot(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE), "wrappedObject");
385 6 : }
386 :
387 : JSObject *
388 265865 : Wrapper::wrappedObject(const JSObject *wrapper)
389 : {
390 265865 : return GetProxyPrivate(wrapper).toObjectOrNull();
391 : }
392 :
393 : Wrapper *
394 72166 : Wrapper::wrapperHandler(const JSObject *wrapper)
395 : {
396 72166 : return static_cast<Wrapper *>(GetProxyHandler(wrapper));
397 : }
398 :
399 : bool
400 143972 : Wrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp)
401 : {
402 143972 : *bp = true;
403 143972 : return true;
404 : }
405 :
406 : void
407 143972 : Wrapper::leave(JSContext *cx, JSObject *wrapper)
408 : {
409 143972 : }
410 :
411 18667 : Wrapper Wrapper::singleton((unsigned)0);
412 :
413 : JSObject *
414 34539 : Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler)
415 : {
416 34539 : JS_ASSERT(parent);
417 34539 : if (obj->isXML()) {
418 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WRAP_XML_OBJECT);
419 9 : return NULL;
420 : }
421 : return NewProxyObject(cx, handler, ObjectValue(*obj), proto, parent,
422 34530 : obj->isCallable() ? obj : NULL, NULL);
423 : }
424 :
425 : /* Compartments. */
426 :
427 : namespace js {
428 :
429 : extern JSObject *
430 34516 : TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
431 : unsigned flags)
432 : {
433 : // Allow wrapping outer window proxies.
434 34516 : JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
435 34516 : return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton);
436 : }
437 :
438 : }
439 :
440 0 : ForceFrame::ForceFrame(JSContext *cx, JSObject *target)
441 : : context(cx),
442 : target(target),
443 0 : frame(NULL)
444 : {
445 0 : }
446 :
447 0 : ForceFrame::~ForceFrame()
448 : {
449 0 : context->delete_(frame);
450 0 : }
451 :
452 : bool
453 0 : ForceFrame::enter()
454 : {
455 0 : frame = context->new_<DummyFrameGuard>();
456 0 : if (!frame)
457 0 : return false;
458 :
459 0 : JS_ASSERT(context->compartment == target->compartment());
460 0 : JSCompartment *destination = context->compartment;
461 :
462 0 : JSObject &scopeChain = target->global();
463 0 : JS_ASSERT(scopeChain.isNative());
464 :
465 0 : return context->stack.pushDummyFrame(context, destination, scopeChain, frame);
466 : }
467 :
468 215765 : AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
469 : : context(cx),
470 : origin(cx->compartment),
471 : target(target),
472 215765 : destination(target->compartment()),
473 431530 : entered(false)
474 : {
475 215765 : }
476 :
477 431530 : AutoCompartment::~AutoCompartment()
478 : {
479 215765 : if (entered)
480 51763 : leave();
481 215765 : }
482 :
483 : bool
484 215765 : AutoCompartment::enter()
485 : {
486 215765 : JS_ASSERT(!entered);
487 215765 : if (origin != destination) {
488 210004 : JSObject &scopeChain = target->global();
489 210004 : JS_ASSERT(scopeChain.isNative());
490 :
491 210004 : frame.construct();
492 210004 : if (!context->stack.pushDummyFrame(context, destination, scopeChain, &frame.ref()))
493 0 : return false;
494 :
495 210004 : if (context->isExceptionPending())
496 0 : context->wrapPendingException();
497 : }
498 215765 : entered = true;
499 215765 : return true;
500 : }
501 :
502 : void
503 215765 : AutoCompartment::leave()
504 : {
505 215765 : JS_ASSERT(entered);
506 215765 : if (origin != destination) {
507 210004 : frame.destroy();
508 210004 : context->resetCompartment();
509 : }
510 215765 : entered = false;
511 215765 : }
512 :
513 9981 : ErrorCopier::~ErrorCopier()
514 : {
515 9981 : JSContext *cx = ac.context;
516 19962 : if (cx->compartment == ac.destination &&
517 : ac.origin != ac.destination &&
518 9981 : cx->isExceptionPending())
519 : {
520 27 : Value exc = cx->getPendingException();
521 27 : if (exc.isObject() && exc.toObject().isError() && exc.toObject().getPrivate()) {
522 18 : cx->clearPendingException();
523 18 : ac.leave();
524 18 : JSObject *copyobj = js_CopyErrorObject(cx, &exc.toObject(), scope);
525 18 : if (copyobj)
526 18 : cx->setPendingException(ObjectValue(*copyobj));
527 : }
528 : }
529 9981 : }
530 :
531 : /* Cross compartment wrappers. */
532 :
533 18667 : CrossCompartmentWrapper::CrossCompartmentWrapper(unsigned flags)
534 18667 : : Wrapper(CROSS_COMPARTMENT | flags)
535 : {
536 18667 : }
537 :
538 18667 : CrossCompartmentWrapper::~CrossCompartmentWrapper()
539 : {
540 18667 : }
541 :
542 : #define PIERCE(cx, wrapper, mode, pre, op, post) \
543 : JS_BEGIN_MACRO \
544 : AutoCompartment call(cx, wrappedObject(wrapper)); \
545 : if (!call.enter()) \
546 : return false; \
547 : bool ok = (pre) && (op); \
548 : call.leave(); \
549 : return ok && (post); \
550 : JS_END_MACRO
551 :
552 : #define NOTHING (true)
553 :
554 : bool
555 0 : CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
556 : bool set, PropertyDescriptor *desc)
557 : {
558 0 : PIERCE(cx, wrapper, set ? SET : GET,
559 : call.destination->wrapId(cx, &id),
560 : Wrapper::getPropertyDescriptor(cx, wrapper, id, set, desc),
561 : call.origin->wrap(cx, desc));
562 : }
563 :
564 : bool
565 36 : CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
566 : bool set, PropertyDescriptor *desc)
567 : {
568 36 : PIERCE(cx, wrapper, set ? SET : GET,
569 : call.destination->wrapId(cx, &id),
570 : Wrapper::getOwnPropertyDescriptor(cx, wrapper, id, set, desc),
571 : call.origin->wrap(cx, desc));
572 : }
573 :
574 : bool
575 27 : CrossCompartmentWrapper::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
576 : {
577 54 : AutoPropertyDescriptorRooter desc2(cx, desc);
578 27 : PIERCE(cx, wrapper, SET,
579 : call.destination->wrapId(cx, &id) && call.destination->wrap(cx, &desc2),
580 : Wrapper::defineProperty(cx, wrapper, id, &desc2),
581 : NOTHING);
582 : }
583 :
584 : bool
585 0 : CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
586 : {
587 0 : PIERCE(cx, wrapper, GET,
588 : NOTHING,
589 : Wrapper::getOwnPropertyNames(cx, wrapper, props),
590 : call.origin->wrap(cx, props));
591 : }
592 :
593 : bool
594 9 : CrossCompartmentWrapper::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
595 : {
596 9 : PIERCE(cx, wrapper, SET,
597 : call.destination->wrapId(cx, &id),
598 : Wrapper::delete_(cx, wrapper, id, bp),
599 : NOTHING);
600 : }
601 :
602 : bool
603 0 : CrossCompartmentWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
604 : {
605 0 : PIERCE(cx, wrapper, GET,
606 : NOTHING,
607 : Wrapper::enumerate(cx, wrapper, props),
608 : call.origin->wrap(cx, props));
609 : }
610 :
611 : bool
612 23751 : CrossCompartmentWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
613 : {
614 23751 : PIERCE(cx, wrapper, GET,
615 : call.destination->wrapId(cx, &id),
616 : Wrapper::has(cx, wrapper, id, bp),
617 : NOTHING);
618 : }
619 :
620 : bool
621 0 : CrossCompartmentWrapper::hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
622 : {
623 0 : PIERCE(cx, wrapper, GET,
624 : call.destination->wrapId(cx, &id),
625 : Wrapper::hasOwn(cx, wrapper, id, bp),
626 : NOTHING);
627 : }
628 :
629 : bool
630 55223 : CrossCompartmentWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp)
631 : {
632 55223 : PIERCE(cx, wrapper, GET,
633 : call.destination->wrap(cx, &receiver) && call.destination->wrapId(cx, &id),
634 : Wrapper::get(cx, wrapper, receiver, id, vp),
635 : call.origin->wrap(cx, vp));
636 : }
637 :
638 : bool
639 16790 : CrossCompartmentWrapper::set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
640 : bool strict, Value *vp)
641 : {
642 33580 : AutoValueRooter tvr(cx, *vp);
643 16790 : PIERCE(cx, wrapper, SET,
644 : call.destination->wrap(cx, &receiver) &&
645 : call.destination->wrapId(cx, &id) &&
646 : call.destination->wrap(cx, tvr.addr()),
647 : Wrapper::set(cx, wrapper, receiver, id, strict, tvr.addr()),
648 : NOTHING);
649 : }
650 :
651 : bool
652 22419 : CrossCompartmentWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
653 : {
654 22419 : PIERCE(cx, wrapper, GET,
655 : NOTHING,
656 : Wrapper::keys(cx, wrapper, props),
657 : call.origin->wrap(cx, props));
658 : }
659 :
660 : /*
661 : * We can reify non-escaping iterator objects instead of having to wrap them. This
662 : * allows fast iteration over objects across a compartment boundary.
663 : */
664 : static bool
665 36 : CanReify(Value *vp)
666 : {
667 : JSObject *obj;
668 36 : return vp->isObject() &&
669 36 : (obj = &vp->toObject())->getClass() == &IteratorClass &&
670 72 : (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
671 : }
672 :
673 : struct AutoCloseIterator
674 : {
675 27 : AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
676 :
677 27 : ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
678 :
679 27 : void clear() { obj = NULL; }
680 :
681 : private:
682 : JSContext *cx;
683 : JSObject *obj;
684 : };
685 :
686 : static bool
687 27 : Reify(JSContext *cx, JSCompartment *origin, Value *vp)
688 : {
689 27 : JSObject *iterObj = &vp->toObject();
690 27 : NativeIterator *ni = iterObj->getNativeIterator();
691 :
692 54 : AutoCloseIterator close(cx, iterObj);
693 :
694 : /* Wrap the iteratee. */
695 27 : JSObject *obj = ni->obj;
696 27 : if (!origin->wrap(cx, &obj))
697 0 : return false;
698 :
699 : /*
700 : * Wrap the elements in the iterator's snapshot.
701 : * N.B. the order of closing/creating iterators is important due to the
702 : * implicit cx->enumerators state.
703 : */
704 27 : size_t length = ni->numKeys();
705 27 : bool isKeyIter = ni->isKeyIter();
706 54 : AutoIdVector keys(cx);
707 27 : if (length > 0) {
708 27 : if (!keys.reserve(length))
709 0 : return false;
710 9054 : for (size_t i = 0; i < length; ++i) {
711 : jsid id;
712 9027 : if (!ValueToId(cx, StringValue(ni->begin()[i]), &id))
713 0 : return false;
714 9027 : id = js_CheckForStringIndex(id);
715 9027 : keys.infallibleAppend(id);
716 9027 : if (!origin->wrapId(cx, &keys[i]))
717 0 : return false;
718 : }
719 : }
720 :
721 27 : close.clear();
722 27 : if (!CloseIterator(cx, iterObj))
723 0 : return false;
724 :
725 27 : if (isKeyIter)
726 27 : return VectorToKeyIterator(cx, obj, ni->flags, keys, vp);
727 0 : return VectorToValueIterator(cx, obj, ni->flags, keys, vp);
728 : }
729 :
730 : bool
731 45 : CrossCompartmentWrapper::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp)
732 : {
733 45 : PIERCE(cx, wrapper, GET,
734 : NOTHING,
735 : Wrapper::iterate(cx, wrapper, flags, vp),
736 : CanReify(vp) ? Reify(cx, call.origin, vp) : call.origin->wrap(cx, vp));
737 : }
738 :
739 : bool
740 20866 : CrossCompartmentWrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp)
741 : {
742 41732 : AutoCompartment call(cx, wrappedObject(wrapper));
743 20866 : if (!call.enter())
744 0 : return false;
745 :
746 20866 : vp[0] = ObjectValue(*call.target);
747 20866 : if (!call.destination->wrap(cx, &vp[1]))
748 0 : return false;
749 20866 : Value *argv = JS_ARGV(cx, vp);
750 37852 : for (size_t n = 0; n < argc; ++n) {
751 16986 : if (!call.destination->wrap(cx, &argv[n]))
752 0 : return false;
753 : }
754 20866 : if (!Wrapper::call(cx, wrapper, argc, vp))
755 2340 : return false;
756 :
757 18526 : call.leave();
758 18526 : return call.origin->wrap(cx, vp);
759 : }
760 :
761 : bool
762 108 : CrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv,
763 : Value *rval)
764 : {
765 216 : AutoCompartment call(cx, wrappedObject(wrapper));
766 108 : if (!call.enter())
767 0 : return false;
768 :
769 108 : for (size_t n = 0; n < argc; ++n) {
770 0 : if (!call.destination->wrap(cx, &argv[n]))
771 0 : return false;
772 : }
773 108 : if (!Wrapper::construct(cx, wrapper, argc, argv, rval))
774 45 : return false;
775 :
776 63 : call.leave();
777 63 : return call.origin->wrap(cx, rval);
778 : }
779 :
780 : extern JSBool
781 : js_generic_native_method_dispatcher(JSContext *cx, unsigned argc, Value *vp);
782 :
783 : bool
784 2709 : CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs srcArgs)
785 : {
786 5652 : JS_ASSERT_IF(!srcArgs.calleev().isUndefined(),
787 : srcArgs.callee().toFunction()->native() == native ||
788 5652 : srcArgs.callee().toFunction()->native() == js_generic_native_method_dispatcher);
789 2709 : JS_ASSERT(&srcArgs.thisv().toObject() == wrapper);
790 2709 : JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper());
791 :
792 2709 : JSObject *wrapped = wrappedObject(wrapper);
793 5418 : AutoCompartment call(cx, wrapped);
794 2709 : if (!call.enter())
795 0 : return false;
796 :
797 5418 : InvokeArgsGuard dstArgs;
798 2709 : if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs))
799 0 : return false;
800 :
801 2709 : Value *src = srcArgs.base();
802 2709 : Value *srcend = srcArgs.array() + srcArgs.length();
803 2709 : Value *dst = dstArgs.base();
804 8748 : for (; src != srcend; ++src, ++dst) {
805 6039 : *dst = *src;
806 6039 : if (!call.destination->wrap(cx, dst))
807 0 : return false;
808 : }
809 :
810 2709 : if (!Wrapper::nativeCall(cx, wrapper, clasp, native, dstArgs))
811 1800 : return false;
812 :
813 909 : srcArgs.rval() = dstArgs.rval();
814 909 : dstArgs.pop();
815 909 : call.leave();
816 909 : return call.origin->wrap(cx, &srcArgs.rval());
817 : }
818 :
819 : bool
820 0 : CrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
821 : {
822 0 : AutoCompartment call(cx, wrappedObject(wrapper));
823 0 : if (!call.enter())
824 0 : return false;
825 :
826 0 : Value v = *vp;
827 0 : if (!call.destination->wrap(cx, &v))
828 0 : return false;
829 0 : return Wrapper::hasInstance(cx, wrapper, &v, bp);
830 : }
831 :
832 : JSString *
833 1944 : CrossCompartmentWrapper::obj_toString(JSContext *cx, JSObject *wrapper)
834 : {
835 3888 : AutoCompartment call(cx, wrappedObject(wrapper));
836 1944 : if (!call.enter())
837 0 : return NULL;
838 :
839 1944 : JSString *str = Wrapper::obj_toString(cx, wrapper);
840 1944 : if (!str)
841 0 : return NULL;
842 :
843 1944 : call.leave();
844 1944 : if (!call.origin->wrap(cx, &str))
845 0 : return NULL;
846 1944 : return str;
847 : }
848 :
849 : JSString *
850 0 : CrossCompartmentWrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent)
851 : {
852 0 : AutoCompartment call(cx, wrappedObject(wrapper));
853 0 : if (!call.enter())
854 0 : return NULL;
855 :
856 0 : JSString *str = Wrapper::fun_toString(cx, wrapper, indent);
857 0 : if (!str)
858 0 : return NULL;
859 :
860 0 : call.leave();
861 0 : if (!call.origin->wrap(cx, &str))
862 0 : return NULL;
863 0 : return str;
864 : }
865 :
866 : bool
867 415 : CrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
868 : {
869 830 : AutoCompartment call(cx, wrappedObject(wrapper));
870 415 : if (!call.enter())
871 0 : return false;
872 :
873 415 : if (!Wrapper::defaultValue(cx, wrapper, hint, vp))
874 63 : return false;
875 :
876 352 : call.leave();
877 352 : return call.origin->wrap(cx, vp);
878 : }
879 :
880 : bool
881 54 : CrossCompartmentWrapper::iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp)
882 : {
883 54 : PIERCE(cx, wrapper, GET,
884 : NOTHING,
885 : Wrapper::iteratorNext(cx, wrapper, vp),
886 : call.origin->wrap(cx, vp));
887 : }
888 :
889 : void
890 36713 : CrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper)
891 : {
892 36713 : MarkCrossCompartmentSlot(trc, &wrapper->getReservedSlotRef(JSSLOT_PROXY_PRIVATE),
893 36713 : "wrappedObject");
894 36713 : }
895 :
896 18667 : CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
897 :
898 : /* Security wrappers. */
899 :
900 : template <class Base>
901 0 : SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
902 0 : : Base(flags)
903 0 : {}
904 :
905 : template <class Base>
906 : bool
907 0 : SecurityWrapper<Base>::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native,
908 : CallArgs args)
909 : {
910 : /*
911 : * Let this through until compartment-per-global lets us have stronger
912 : * invariants wrt document.domain (bug 714547).
913 : */
914 0 : return Base::nativeCall(cx, wrapper, clasp, native, args);
915 : }
916 :
917 : template <class Base>
918 : bool
919 0 : SecurityWrapper<Base>::objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx)
920 : {
921 : /*
922 : * Let this through until compartment-per-global lets us have stronger
923 : * invariants wrt document.domain (bug 714547).
924 : */
925 0 : return Base::objectClassIs(obj, classValue, cx);
926 : }
927 :
928 : template <class Base>
929 : bool
930 0 : SecurityWrapper<Base>::regexp_toShared(JSContext *cx, JSObject *obj, RegExpGuard *g)
931 : {
932 0 : return Base::regexp_toShared(cx, obj, g);
933 : }
934 :
935 :
936 : template class js::SecurityWrapper<Wrapper>;
937 56001 : template class js::SecurityWrapper<CrossCompartmentWrapper>;
|