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