Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef jscntxtinlines_h
8 : #define jscntxtinlines_h
9 :
10 : #include "jscntxt.h"
11 : #include "jscompartment.h"
12 :
13 : #include "jsiter.h"
14 :
15 : #include "builtin/Object.h"
16 : #include "jit/JitFrames.h"
17 : #include "proxy/Proxy.h"
18 : #include "vm/HelperThreads.h"
19 : #include "vm/Interpreter.h"
20 : #include "vm/Symbol.h"
21 :
22 : namespace js {
23 :
24 : class CompartmentChecker
25 : {
26 : JSCompartment* compartment;
27 :
28 : public:
29 551089 : explicit CompartmentChecker(JSContext* cx)
30 551089 : : compartment(cx->compartment())
31 : {
32 551089 : }
33 :
34 : /*
35 : * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
36 : * compartment mismatches.
37 : */
38 0 : static void fail(JSCompartment* c1, JSCompartment* c2) {
39 0 : printf("*** Compartment mismatch %p vs. %p\n", (void*) c1, (void*) c2);
40 0 : MOZ_CRASH();
41 : }
42 :
43 0 : static void fail(JS::Zone* z1, JS::Zone* z2) {
44 0 : printf("*** Zone mismatch %p vs. %p\n", (void*) z1, (void*) z2);
45 0 : MOZ_CRASH();
46 : }
47 :
48 : /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
49 : static void check(JSCompartment* c1, JSCompartment* c2) {
50 : MOZ_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1));
51 : MOZ_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2));
52 : if (c1 != c2)
53 : fail(c1, c2);
54 : }
55 :
56 474240 : void check(JSCompartment* c) {
57 474240 : if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) {
58 469464 : if (!compartment)
59 0 : compartment = c;
60 469464 : else if (c != compartment)
61 0 : fail(compartment, c);
62 : }
63 474240 : }
64 :
65 14063 : void checkZone(JS::Zone* z) {
66 14063 : if (compartment && z != compartment->zone())
67 0 : fail(compartment->zone(), z);
68 14063 : }
69 :
70 469582 : void check(JSObject* obj) {
71 469582 : if (obj) {
72 436067 : MOZ_ASSERT(JS::ObjectIsNotGray(obj));
73 436068 : MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
74 436070 : check(obj->compartment());
75 : }
76 469585 : }
77 :
78 : template<typename T>
79 4200 : void check(const Rooted<T>& rooted) {
80 4200 : check(rooted.get());
81 4200 : }
82 :
83 : template<typename T>
84 343932 : void check(Handle<T> handle) {
85 343932 : check(handle.get());
86 343932 : }
87 :
88 : template<typename T>
89 80897 : void check(MutableHandle<T> handle) {
90 80897 : check(handle.get());
91 80898 : }
92 :
93 : template <typename T>
94 163383 : void checkAtom(T* thing) {
95 : static_assert(mozilla::IsSame<T, JSAtom>::value ||
96 : mozilla::IsSame<T, JS::Symbol>::value,
97 : "Should only be called with JSAtom* or JS::Symbol* argument");
98 :
99 : #ifdef DEBUG
100 : // Atoms which move across zone boundaries need to be marked in the new
101 : // zone, see JS_MarkCrossZoneId.
102 163383 : if (compartment) {
103 163383 : JSRuntime* rt = compartment->runtimeFromAnyThread();
104 163383 : MOZ_ASSERT(rt->gc.atomMarking.atomIsMarked(compartment->zone(), thing));
105 : }
106 : #endif
107 163383 : }
108 :
109 61336 : void check(JSString* str) {
110 61336 : MOZ_ASSERT(JS::CellIsNotGray(str));
111 61336 : if (str->isAtom()) {
112 47273 : checkAtom(&str->asAtom());
113 : } else {
114 14063 : checkZone(str->zone());
115 : }
116 61336 : }
117 :
118 620 : void check(JS::Symbol* symbol) {
119 620 : checkAtom(symbol);
120 620 : }
121 :
122 447318 : void check(const js::Value& v) {
123 447318 : if (v.isObject())
124 271885 : check(&v.toObject());
125 175436 : else if (v.isString())
126 53990 : check(v.toString());
127 121446 : else if (v.isSymbol())
128 620 : check(v.toSymbol());
129 447322 : }
130 :
131 : // Check the contents of any container class that supports the C++
132 : // iteration protocol, eg GCVector<jsid>.
133 : template <typename Container>
134 : typename mozilla::EnableIf<
135 : mozilla::IsSame<
136 : decltype(((Container*)nullptr)->begin()),
137 : decltype(((Container*)nullptr)->end())
138 : >::value
139 : >::Type
140 19 : check(const Container& container) {
141 19 : for (auto i : container)
142 0 : check(i);
143 19 : }
144 :
145 : void check(const ValueArray& arr) {
146 : for (size_t i = 0; i < arr.length; i++)
147 : check(arr.array[i]);
148 : }
149 :
150 272 : void check(const JSValueArray& arr) {
151 816 : for (size_t i = 0; i < arr.length; i++)
152 544 : check(arr.array[i]);
153 272 : }
154 :
155 1937 : void check(const JS::HandleValueArray& arr) {
156 4083 : for (size_t i = 0; i < arr.length(); i++)
157 2146 : check(arr[i]);
158 1937 : }
159 :
160 32451 : void check(const CallArgs& args) {
161 139770 : for (Value* p = args.base(); p != args.end(); ++p)
162 107319 : check(*p);
163 32451 : }
164 :
165 118833 : void check(jsid id) {
166 118833 : if (JSID_IS_ATOM(id))
167 112188 : checkAtom(JSID_TO_ATOM(id));
168 6645 : else if (JSID_IS_SYMBOL(id))
169 3265 : checkAtom(JSID_TO_SYMBOL(id));
170 : else
171 3380 : MOZ_ASSERT(!JSID_IS_GCTHING(id));
172 118833 : }
173 :
174 13164 : void check(JSScript* script) {
175 13164 : MOZ_ASSERT(JS::CellIsNotGray(script));
176 13164 : if (script)
177 13164 : check(script->compartment());
178 13164 : }
179 :
180 : void check(InterpreterFrame* fp);
181 : void check(AbstractFramePtr frame);
182 : void check(SavedStacks* stacks);
183 :
184 316 : void check(Handle<PropertyDescriptor> desc) {
185 316 : check(desc.object());
186 316 : if (desc.hasGetterObject())
187 131 : check(desc.getterObject());
188 316 : if (desc.hasSetterObject())
189 67 : check(desc.setterObject());
190 316 : check(desc.value());
191 316 : }
192 :
193 12543 : void check(TypeSet::Type type) {
194 12543 : check(type.maybeCompartment());
195 12543 : }
196 : };
197 :
198 : /*
199 : * Don't perform these checks when called from a finalizer. The checking
200 : * depends on other objects not having been swept yet.
201 : */
202 : #define START_ASSERT_SAME_COMPARTMENT() \
203 : if (cx->heapState != JS::HeapState::Idle) \
204 : return; \
205 : CompartmentChecker c(cx)
206 :
207 : template <class T1> inline void
208 2261 : releaseAssertSameCompartment(JSContext* cx, const T1& t1)
209 : {
210 2261 : START_ASSERT_SAME_COMPARTMENT();
211 2261 : c.check(t1);
212 : }
213 :
214 : template <class T1> inline void
215 250572 : assertSameCompartment(JSContext* cx, const T1& t1)
216 : {
217 : #ifdef JS_CRASH_DIAGNOSTICS
218 250572 : START_ASSERT_SAME_COMPARTMENT();
219 250572 : c.check(t1);
220 : #endif
221 : }
222 :
223 : template <class T1> inline void
224 193882 : assertSameCompartmentDebugOnly(JSContext* cx, const T1& t1)
225 : {
226 : #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
227 193882 : START_ASSERT_SAME_COMPARTMENT();
228 193882 : c.check(t1);
229 : #endif
230 : }
231 :
232 : template <class T1, class T2> inline void
233 57477 : assertSameCompartment(JSContext* cx, const T1& t1, const T2& t2)
234 : {
235 : #ifdef JS_CRASH_DIAGNOSTICS
236 57477 : START_ASSERT_SAME_COMPARTMENT();
237 57477 : c.check(t1);
238 57477 : c.check(t2);
239 : #endif
240 : }
241 :
242 : template <class T1, class T2, class T3> inline void
243 23494 : assertSameCompartment(JSContext* cx, const T1& t1, const T2& t2, const T3& t3)
244 : {
245 : #ifdef JS_CRASH_DIAGNOSTICS
246 23494 : START_ASSERT_SAME_COMPARTMENT();
247 23494 : c.check(t1);
248 23494 : c.check(t2);
249 23494 : c.check(t3);
250 : #endif
251 : }
252 :
253 : template <class T1, class T2, class T3, class T4> inline void
254 0 : assertSameCompartment(JSContext* cx,
255 : const T1& t1, const T2& t2, const T3& t3, const T4& t4)
256 : {
257 : #ifdef JS_CRASH_DIAGNOSTICS
258 0 : START_ASSERT_SAME_COMPARTMENT();
259 0 : c.check(t1);
260 0 : c.check(t2);
261 0 : c.check(t3);
262 0 : c.check(t4);
263 : #endif
264 : }
265 :
266 : template <class T1, class T2, class T3, class T4, class T5> inline void
267 23351 : assertSameCompartment(JSContext* cx,
268 : const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5)
269 : {
270 : #ifdef JS_CRASH_DIAGNOSTICS
271 23351 : START_ASSERT_SAME_COMPARTMENT();
272 23351 : c.check(t1);
273 23351 : c.check(t2);
274 23351 : c.check(t3);
275 23351 : c.check(t4);
276 23351 : c.check(t5);
277 : #endif
278 : }
279 :
280 : #undef START_ASSERT_SAME_COMPARTMENT
281 :
282 : STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
283 : MOZ_ALWAYS_INLINE bool
284 32451 : CallJSNative(JSContext* cx, Native native, const CallArgs& args)
285 : {
286 32451 : if (!CheckRecursionLimit(cx))
287 0 : return false;
288 :
289 : #ifdef DEBUG
290 32451 : bool alreadyThrowing = cx->isExceptionPending();
291 : #endif
292 32451 : assertSameCompartment(cx, args);
293 32451 : bool ok = native(cx, args.length(), args.base());
294 32444 : if (ok) {
295 32265 : assertSameCompartment(cx, args.rval());
296 32266 : MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
297 : }
298 32445 : return ok;
299 : }
300 :
301 : STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
302 : MOZ_ALWAYS_INLINE bool
303 0 : CallNativeImpl(JSContext* cx, NativeImpl impl, const CallArgs& args)
304 : {
305 : #ifdef DEBUG
306 0 : bool alreadyThrowing = cx->isExceptionPending();
307 : #endif
308 0 : assertSameCompartment(cx, args);
309 0 : bool ok = impl(cx, args);
310 0 : if (ok) {
311 0 : assertSameCompartment(cx, args.rval());
312 0 : MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
313 : }
314 0 : return ok;
315 : }
316 :
317 : STATIC_PRECONDITION(ubound(args.argv_) >= argc)
318 : MOZ_ALWAYS_INLINE bool
319 749 : CallJSNativeConstructor(JSContext* cx, Native native, const CallArgs& args)
320 : {
321 : #ifdef DEBUG
322 1498 : RootedObject callee(cx, &args.callee());
323 : #endif
324 :
325 749 : MOZ_ASSERT(args.thisv().isMagic());
326 749 : if (!CallJSNative(cx, native, args))
327 11 : return false;
328 :
329 : /*
330 : * Native constructors must return non-primitive values on success.
331 : * Although it is legal, if a constructor returns the callee, there is a
332 : * 99.9999% chance it is a bug. If any valid code actually wants the
333 : * constructor to return the callee, the assertion can be removed or
334 : * (another) conjunct can be added to the antecedent.
335 : *
336 : * Exceptions:
337 : *
338 : * - Proxies are exceptions to both rules: they can return primitives and
339 : * they allow content to return the callee.
340 : *
341 : * - CallOrConstructBoundFunction is an exception as well because we might
342 : * have used bind on a proxy function.
343 : *
344 : * - new Iterator(x) is user-hookable; it returns x.__iterator__() which
345 : * could be any object.
346 : *
347 : * - (new Object(Object)) returns the callee.
348 : */
349 738 : MOZ_ASSERT_IF(native != js::proxy_Construct &&
350 : native != js::IteratorConstructor &&
351 : (!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
352 : args.rval().isObject() && callee != &args.rval().toObject());
353 :
354 738 : return true;
355 : }
356 :
357 : MOZ_ALWAYS_INLINE bool
358 738 : CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id,
359 : MutableHandleValue vp)
360 : {
361 738 : if (!CheckRecursionLimit(cx))
362 0 : return false;
363 :
364 738 : assertSameCompartment(cx, obj, id, vp);
365 738 : bool ok = op(cx, obj, id, vp);
366 738 : if (ok)
367 738 : assertSameCompartment(cx, vp);
368 738 : return ok;
369 : }
370 :
371 : MOZ_ALWAYS_INLINE bool
372 72 : CallJSSetterOp(JSContext* cx, SetterOp op, HandleObject obj, HandleId id, MutableHandleValue vp,
373 : ObjectOpResult& result)
374 : {
375 72 : if (!CheckRecursionLimit(cx))
376 0 : return false;
377 :
378 72 : assertSameCompartment(cx, obj, id, vp);
379 72 : return op(cx, obj, id, vp, result);
380 : }
381 :
382 : inline bool
383 13614 : CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id,
384 : HandleValue v)
385 : {
386 13614 : if (!CheckRecursionLimit(cx))
387 0 : return false;
388 :
389 13614 : assertSameCompartment(cx, obj, id, v);
390 13614 : return op(cx, obj, id, v);
391 : }
392 :
393 : inline bool
394 477 : CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
395 : ObjectOpResult& result)
396 : {
397 477 : if (!CheckRecursionLimit(cx))
398 0 : return false;
399 :
400 477 : assertSameCompartment(cx, receiver, id);
401 477 : if (op)
402 17 : return op(cx, receiver, id, result);
403 460 : return result.succeed();
404 : }
405 :
406 : MOZ_ALWAYS_INLINE bool
407 18615 : CheckForInterrupt(JSContext* cx)
408 : {
409 18615 : MOZ_ASSERT(!cx->isExceptionPending());
410 : // Add an inline fast-path since we have to check for interrupts in some hot
411 : // C++ loops of library builtins.
412 18615 : if (MOZ_UNLIKELY(cx->hasPendingInterrupt()))
413 15 : return cx->handleInterrupt();
414 18600 : return true;
415 : }
416 :
417 : } /* namespace js */
418 :
419 : inline js::LifoAlloc&
420 84887 : JSContext::typeLifoAlloc()
421 : {
422 84887 : return zone()->types.typeLifoAlloc();
423 : }
424 :
425 : inline js::Nursery&
426 212230 : JSContext::nursery()
427 : {
428 212230 : return runtime()->gc.nursery();
429 : }
430 :
431 : inline void
432 0 : JSContext::minorGC(JS::gcreason::Reason reason)
433 : {
434 0 : runtime()->gc.minorGC(reason);
435 0 : }
436 :
437 : inline void
438 4142 : JSContext::setPendingException(const js::Value& v)
439 : {
440 : // overRecursed_ is set after the fact by ReportOverRecursed.
441 4142 : this->overRecursed_ = false;
442 4142 : this->throwing = true;
443 4142 : this->unwrappedException() = v;
444 : // We don't use assertSameCompartment here to allow
445 : // js::SetPendingExceptionCrossContext to work.
446 4142 : MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
447 4142 : }
448 :
449 : inline bool
450 797 : JSContext::runningWithTrustedPrincipals()
451 : {
452 797 : return !compartment() || compartment()->principals() == runtime()->trustedPrincipals();
453 : }
454 :
455 : inline void
456 83424 : JSContext::enterNonAtomsCompartment(JSCompartment* c)
457 : {
458 83424 : enterCompartmentDepth_++;
459 :
460 83424 : MOZ_ASSERT(!c->zone()->isAtomsZone());
461 83424 : enterZoneGroup(c->zone()->group());
462 :
463 83424 : c->enter();
464 83424 : setCompartment(c, nullptr);
465 83424 : }
466 :
467 : inline void
468 62625 : JSContext::enterAtomsCompartment(JSCompartment* c,
469 : const js::AutoLockForExclusiveAccess& lock)
470 : {
471 62625 : enterCompartmentDepth_++;
472 :
473 62625 : MOZ_ASSERT(c->zone()->isAtomsZone());
474 :
475 62625 : c->enter();
476 62625 : setCompartment(c, &lock);
477 62625 : }
478 :
479 : template <typename T>
480 : inline void
481 70309 : JSContext::enterCompartmentOf(const T& target)
482 : {
483 70309 : MOZ_ASSERT(JS::CellIsNotGray(target));
484 70309 : enterNonAtomsCompartment(target->compartment());
485 70309 : }
486 :
487 : inline void
488 1656 : JSContext::enterNullCompartment()
489 : {
490 1656 : enterCompartmentDepth_++;
491 1656 : setCompartment(nullptr);
492 1656 : }
493 :
494 : inline void
495 147691 : JSContext::leaveCompartment(
496 : JSCompartment* oldCompartment,
497 : const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
498 : {
499 147691 : MOZ_ASSERT(hasEnteredCompartment());
500 147692 : enterCompartmentDepth_--;
501 :
502 : // Only call leave() after we've setCompartment()-ed away from the current
503 : // compartment.
504 147693 : JSCompartment* startingCompartment = compartment_;
505 147693 : setCompartment(oldCompartment, maybeLock);
506 147694 : if (startingCompartment) {
507 146038 : startingCompartment->leave();
508 146038 : if (!startingCompartment->zone()->isAtomsZone())
509 83413 : leaveZoneGroup(startingCompartment->zone()->group());
510 : }
511 147694 : }
512 :
513 : inline void
514 295387 : JSContext::setCompartment(JSCompartment* comp,
515 : const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
516 : {
517 : // Only one thread can be in the atoms compartment at a time.
518 295387 : MOZ_ASSERT_IF(runtime_->isAtomsCompartment(comp), maybeLock != nullptr);
519 295387 : MOZ_ASSERT_IF(runtime_->isAtomsCompartment(comp) || runtime_->isAtomsCompartment(compartment_),
520 : runtime_->currentThreadHasExclusiveAccess());
521 :
522 : // Make sure that the atoms compartment has its own zone.
523 295393 : MOZ_ASSERT_IF(comp && !runtime_->isAtomsCompartment(comp),
524 : !comp->zone()->isAtomsZone());
525 :
526 : // Both the current and the new compartment should be properly marked as
527 : // entered at this point.
528 295394 : MOZ_ASSERT_IF(compartment_, compartment_->hasBeenEntered());
529 295394 : MOZ_ASSERT_IF(comp, comp->hasBeenEntered());
530 :
531 : // This context must have exclusive access to the zone's group.
532 295394 : MOZ_ASSERT_IF(comp && !comp->zone()->isAtomsZone(),
533 : comp->zone()->group()->ownedByCurrentThread());
534 :
535 295394 : compartment_ = comp;
536 295394 : zone_ = comp ? comp->zone() : nullptr;
537 295394 : arenas_ = zone_ ? &zone_->arenas : nullptr;
538 295398 : }
539 :
540 : inline void
541 83424 : JSContext::enterZoneGroup(js::ZoneGroup* group)
542 : {
543 83424 : MOZ_ASSERT(this == js::TlsContext.get());
544 83424 : group->enter(this);
545 83424 : }
546 :
547 : inline void
548 83413 : JSContext::leaveZoneGroup(js::ZoneGroup* group)
549 : {
550 83413 : MOZ_ASSERT(this == js::TlsContext.get());
551 83413 : group->leave();
552 83413 : }
553 :
554 : inline JSScript*
555 35902 : JSContext::currentScript(jsbytecode** ppc,
556 : MaybeAllowCrossCompartment allowCrossCompartment) const
557 : {
558 35902 : if (ppc)
559 6936 : *ppc = nullptr;
560 :
561 35902 : js::Activation* act = activation();
562 35902 : while (act && act->isJit() && !act->asJit()->isActive())
563 0 : act = act->prev();
564 :
565 35902 : if (!act)
566 4508 : return nullptr;
567 :
568 31394 : MOZ_ASSERT(act->cx() == this);
569 :
570 31394 : if (!allowCrossCompartment && act->compartment() != compartment())
571 1333 : return nullptr;
572 :
573 30061 : if (act->isJit()) {
574 4072 : JSScript* script = nullptr;
575 4072 : js::jit::GetPcScript(const_cast<JSContext*>(this), &script, ppc);
576 4072 : MOZ_ASSERT(allowCrossCompartment || script->compartment() == compartment());
577 4072 : return script;
578 : }
579 :
580 25989 : if (act->isWasm())
581 0 : return nullptr;
582 :
583 25989 : MOZ_ASSERT(act->isInterpreter());
584 :
585 25989 : js::InterpreterFrame* fp = act->asInterpreter()->current();
586 25989 : MOZ_ASSERT(!fp->runningInJit());
587 :
588 25989 : JSScript* script = fp->script();
589 25989 : MOZ_ASSERT(allowCrossCompartment || script->compartment() == compartment());
590 :
591 25989 : if (ppc) {
592 4634 : *ppc = act->asInterpreter()->regs().pc;
593 4634 : MOZ_ASSERT(script->containsPC(*ppc));
594 : }
595 :
596 25989 : return script;
597 : }
598 :
599 : inline js::RuntimeCaches&
600 41731 : JSContext::caches()
601 : {
602 41731 : return runtime()->caches();
603 : }
604 :
605 : #endif /* jscntxtinlines_h */
|