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 vm_JSContext_inl_h
8 : #define vm_JSContext_inl_h
9 :
10 : #include "vm/JSContext.h"
11 :
12 : #include "builtin/Object.h"
13 : #include "jit/JitFrames.h"
14 : #include "proxy/Proxy.h"
15 : #include "vm/HelperThreads.h"
16 : #include "vm/Interpreter.h"
17 : #include "vm/Iteration.h"
18 : #include "vm/Realm.h"
19 : #include "vm/SymbolType.h"
20 :
21 : namespace js {
22 :
23 : class CompartmentChecker
24 : {
25 : JS::Compartment* compartment;
26 :
27 : public:
28 : explicit CompartmentChecker(JSContext* cx)
29 0 : : compartment(cx->compartment())
30 : {
31 : }
32 :
33 : /*
34 : * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
35 : * compartment mismatches.
36 : */
37 0 : static void fail(JS::Compartment* c1, JS::Compartment* c2) {
38 0 : printf("*** Compartment mismatch %p vs. %p\n", (void*) c1, (void*) c2);
39 0 : MOZ_CRASH();
40 : }
41 :
42 0 : static void fail(JS::Zone* z1, JS::Zone* z2) {
43 0 : printf("*** Zone mismatch %p vs. %p\n", (void*) z1, (void*) z2);
44 0 : MOZ_CRASH();
45 : }
46 :
47 : static void check(JS::Compartment* c1, JS::Compartment* c2) {
48 : if (c1 != c2)
49 : fail(c1, c2);
50 : }
51 :
52 : void check(JS::Compartment* c) {
53 0 : if (c && c != compartment)
54 0 : fail(compartment, c);
55 : }
56 :
57 0 : void checkZone(JS::Zone* z) {
58 0 : if (compartment && z != compartment->zone())
59 0 : fail(compartment->zone(), z);
60 0 : }
61 :
62 0 : void check(JSObject* obj) {
63 0 : if (obj) {
64 0 : MOZ_ASSERT(JS::ObjectIsNotGray(obj));
65 0 : MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj));
66 0 : check(obj->compartment());
67 : }
68 0 : }
69 :
70 : template<typename T>
71 : void check(const Rooted<T>& rooted) {
72 0 : check(rooted.get());
73 : }
74 :
75 : template<typename T>
76 : void check(Handle<T> handle) {
77 0 : check(handle.get());
78 : }
79 :
80 : template<typename T>
81 : void check(MutableHandle<T> handle) {
82 0 : check(handle.get());
83 : }
84 :
85 : template <typename T>
86 0 : void checkAtom(T* thing) {
87 : static_assert(mozilla::IsSame<T, JSAtom>::value ||
88 : mozilla::IsSame<T, JS::Symbol>::value,
89 : "Should only be called with JSAtom* or JS::Symbol* argument");
90 :
91 : #ifdef DEBUG
92 : // Atoms which move across zone boundaries need to be marked in the new
93 : // zone, see JS_MarkCrossZoneId.
94 0 : if (compartment) {
95 0 : JSRuntime* rt = compartment->runtimeFromAnyThread();
96 0 : MOZ_ASSERT(rt->gc.atomMarking.atomIsMarked(compartment->zone(), thing));
97 : }
98 : #endif
99 0 : }
100 :
101 0 : void check(JSString* str) {
102 0 : MOZ_ASSERT(JS::CellIsNotGray(str));
103 0 : if (str->isAtom()) {
104 0 : checkAtom(&str->asAtom());
105 : } else {
106 0 : checkZone(str->zone());
107 : }
108 0 : }
109 :
110 : void check(JS::Symbol* symbol) {
111 0 : checkAtom(symbol);
112 : }
113 :
114 0 : void check(const js::Value& v) {
115 0 : if (v.isObject())
116 0 : check(&v.toObject());
117 0 : else if (v.isString())
118 0 : check(v.toString());
119 0 : else if (v.isSymbol())
120 0 : check(v.toSymbol());
121 0 : }
122 :
123 : // Check the contents of any container class that supports the C++
124 : // iteration protocol, eg GCVector<jsid>.
125 : template <typename Container>
126 : typename mozilla::EnableIf<
127 : mozilla::IsSame<
128 : decltype(((Container*)nullptr)->begin()),
129 : decltype(((Container*)nullptr)->end())
130 : >::value
131 : >::Type
132 0 : check(const Container& container) {
133 0 : for (auto i : container)
134 0 : check(i);
135 0 : }
136 :
137 0 : void check(const JS::HandleValueArray& arr) {
138 0 : for (size_t i = 0; i < arr.length(); i++)
139 0 : check(arr[i]);
140 0 : }
141 :
142 0 : void check(const CallArgs& args) {
143 0 : for (Value* p = args.base(); p != args.end(); ++p)
144 0 : check(*p);
145 0 : }
146 :
147 0 : void check(jsid id) {
148 0 : if (JSID_IS_ATOM(id))
149 0 : checkAtom(JSID_TO_ATOM(id));
150 0 : else if (JSID_IS_SYMBOL(id))
151 0 : checkAtom(JSID_TO_SYMBOL(id));
152 : else
153 : MOZ_ASSERT(!JSID_IS_GCTHING(id));
154 0 : }
155 :
156 0 : void check(JSScript* script) {
157 0 : MOZ_ASSERT(JS::CellIsNotGray(script));
158 0 : if (script)
159 0 : check(script->compartment());
160 0 : }
161 :
162 : void check(InterpreterFrame* fp);
163 : void check(AbstractFramePtr frame);
164 :
165 0 : void check(Handle<PropertyDescriptor> desc) {
166 0 : check(desc.object());
167 0 : if (desc.hasGetterObject())
168 0 : check(desc.getterObject());
169 0 : if (desc.hasSetterObject())
170 0 : check(desc.setterObject());
171 0 : check(desc.value());
172 0 : }
173 :
174 0 : void check(TypeSet::Type type) {
175 0 : check(type.maybeCompartment());
176 0 : }
177 : };
178 :
179 : /*
180 : * Don't perform these checks when called from a finalizer. The checking
181 : * depends on other objects not having been swept yet.
182 : */
183 : #define START_ASSERT_SAME_COMPARTMENT() \
184 : if (JS::RuntimeHeapIsCollecting()) \
185 : return; \
186 : CompartmentChecker c(cx)
187 :
188 : template <class T1> inline void
189 0 : releaseAssertSameCompartment(JSContext* cx, const T1& t1)
190 : {
191 0 : START_ASSERT_SAME_COMPARTMENT();
192 0 : c.check(t1);
193 : }
194 :
195 : template <class T1> inline void
196 0 : assertSameCompartment(JSContext* cx, const T1& t1)
197 : {
198 : #ifdef JS_CRASH_DIAGNOSTICS
199 0 : START_ASSERT_SAME_COMPARTMENT();
200 0 : c.check(t1);
201 : #endif
202 : }
203 :
204 : template <class T1> inline void
205 0 : assertSameCompartmentDebugOnly(JSContext* cx, const T1& t1)
206 : {
207 : #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
208 0 : START_ASSERT_SAME_COMPARTMENT();
209 0 : c.check(t1);
210 : #endif
211 : }
212 :
213 : template <class T1, class T2> inline void
214 0 : assertSameCompartment(JSContext* cx, const T1& t1, const T2& t2)
215 : {
216 : #ifdef JS_CRASH_DIAGNOSTICS
217 0 : START_ASSERT_SAME_COMPARTMENT();
218 0 : c.check(t1);
219 0 : c.check(t2);
220 : #endif
221 : }
222 :
223 : template <class T1, class T2, class T3> inline void
224 0 : assertSameCompartment(JSContext* cx, const T1& t1, const T2& t2, const T3& t3)
225 : {
226 : #ifdef JS_CRASH_DIAGNOSTICS
227 0 : START_ASSERT_SAME_COMPARTMENT();
228 0 : c.check(t1);
229 0 : c.check(t2);
230 0 : c.check(t3);
231 : #endif
232 : }
233 :
234 : template <class T1, class T2, class T3, class T4> inline void
235 0 : assertSameCompartment(JSContext* cx,
236 : const T1& t1, const T2& t2, const T3& t3, const T4& t4)
237 : {
238 : #ifdef JS_CRASH_DIAGNOSTICS
239 0 : START_ASSERT_SAME_COMPARTMENT();
240 0 : c.check(t1);
241 0 : c.check(t2);
242 0 : c.check(t3);
243 0 : c.check(t4);
244 : #endif
245 : }
246 :
247 : template <class T1, class T2, class T3, class T4, class T5> inline void
248 : assertSameCompartment(JSContext* cx,
249 : const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5)
250 : {
251 : #ifdef JS_CRASH_DIAGNOSTICS
252 : START_ASSERT_SAME_COMPARTMENT();
253 : c.check(t1);
254 : c.check(t2);
255 : c.check(t3);
256 : c.check(t4);
257 : c.check(t5);
258 : #endif
259 : }
260 :
261 : #undef START_ASSERT_SAME_COMPARTMENT
262 :
263 : STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
264 : MOZ_ALWAYS_INLINE bool
265 0 : CallJSNative(JSContext* cx, Native native, const CallArgs& args)
266 : {
267 0 : if (!CheckRecursionLimit(cx))
268 : return false;
269 :
270 : #ifdef DEBUG
271 0 : bool alreadyThrowing = cx->isExceptionPending();
272 : #endif
273 0 : assertSameCompartment(cx, args);
274 0 : bool ok = native(cx, args.length(), args.base());
275 0 : if (ok) {
276 0 : assertSameCompartment(cx, args.rval());
277 0 : MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
278 : }
279 : return ok;
280 : }
281 :
282 : STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
283 : MOZ_ALWAYS_INLINE bool
284 0 : CallNativeImpl(JSContext* cx, NativeImpl impl, const CallArgs& args)
285 : {
286 : #ifdef DEBUG
287 0 : bool alreadyThrowing = cx->isExceptionPending();
288 : #endif
289 0 : assertSameCompartment(cx, args);
290 0 : bool ok = impl(cx, args);
291 0 : if (ok) {
292 0 : assertSameCompartment(cx, args.rval());
293 0 : MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
294 : }
295 0 : return ok;
296 : }
297 :
298 : STATIC_PRECONDITION(ubound(args.argv_) >= argc)
299 : MOZ_ALWAYS_INLINE bool
300 0 : CallJSNativeConstructor(JSContext* cx, Native native, const CallArgs& args)
301 : {
302 : #ifdef DEBUG
303 0 : RootedObject callee(cx, &args.callee());
304 : #endif
305 :
306 0 : MOZ_ASSERT(args.thisv().isMagic());
307 0 : if (!CallJSNative(cx, native, args))
308 : return false;
309 :
310 : /*
311 : * Native constructors must return non-primitive values on success.
312 : * Although it is legal, if a constructor returns the callee, there is a
313 : * 99.9999% chance it is a bug. If any valid code actually wants the
314 : * constructor to return the callee, the assertion can be removed or
315 : * (another) conjunct can be added to the antecedent.
316 : *
317 : * Exceptions:
318 : *
319 : * - Proxies are exceptions to both rules: they can return primitives and
320 : * they allow content to return the callee.
321 : *
322 : * - CallOrConstructBoundFunction is an exception as well because we might
323 : * have used bind on a proxy function.
324 : *
325 : * - (new Object(Object)) returns the callee.
326 : */
327 0 : MOZ_ASSERT_IF(native != js::proxy_Construct &&
328 : (!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
329 : args.rval().isObject() && callee != &args.rval().toObject());
330 :
331 : return true;
332 : }
333 :
334 : MOZ_ALWAYS_INLINE bool
335 0 : CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id,
336 : MutableHandleValue vp)
337 : {
338 0 : if (!CheckRecursionLimit(cx))
339 : return false;
340 :
341 0 : assertSameCompartment(cx, obj, id, vp);
342 0 : bool ok = op(cx, obj, id, vp);
343 0 : if (ok)
344 0 : assertSameCompartment(cx, vp);
345 : return ok;
346 : }
347 :
348 : MOZ_ALWAYS_INLINE bool
349 0 : CallJSSetterOp(JSContext* cx, SetterOp op, HandleObject obj, HandleId id, HandleValue v,
350 : ObjectOpResult& result)
351 : {
352 0 : if (!CheckRecursionLimit(cx))
353 : return false;
354 :
355 0 : assertSameCompartment(cx, obj, id, v);
356 0 : return op(cx, obj, id, v, result);
357 : }
358 :
359 : inline bool
360 0 : CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id,
361 : HandleValue v)
362 : {
363 0 : if (!CheckRecursionLimit(cx))
364 : return false;
365 :
366 0 : assertSameCompartment(cx, obj, id, v);
367 0 : return op(cx, obj, id, v);
368 : }
369 :
370 : inline bool
371 0 : CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
372 : ObjectOpResult& result)
373 : {
374 0 : if (!CheckRecursionLimit(cx))
375 : return false;
376 :
377 0 : assertSameCompartment(cx, receiver, id);
378 0 : if (op)
379 0 : return op(cx, receiver, id, result);
380 0 : return result.succeed();
381 : }
382 :
383 : MOZ_ALWAYS_INLINE bool
384 0 : CheckForInterrupt(JSContext* cx)
385 : {
386 0 : MOZ_ASSERT(!cx->isExceptionPending());
387 : // Add an inline fast-path since we have to check for interrupts in some hot
388 : // C++ loops of library builtins.
389 0 : if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt()))
390 0 : return cx->handleInterrupt();
391 :
392 0 : JS_INTERRUPT_POSSIBLY_FAIL();
393 :
394 : return true;
395 : }
396 :
397 : } /* namespace js */
398 :
399 : inline js::LifoAlloc&
400 : JSContext::typeLifoAlloc()
401 : {
402 0 : return zone()->types.typeLifoAlloc();
403 : }
404 :
405 : inline js::Nursery&
406 : JSContext::nursery()
407 : {
408 0 : return runtime()->gc.nursery();
409 : }
410 :
411 : inline void
412 0 : JSContext::minorGC(JS::gcreason::Reason reason)
413 : {
414 0 : runtime()->gc.minorGC(reason);
415 0 : }
416 :
417 : inline void
418 0 : JSContext::setPendingException(const js::Value& v)
419 : {
420 : #if defined(NIGHTLY_BUILD)
421 : do {
422 : // Do not intercept exceptions if we are already
423 : // in the exception interceptor. That would lead
424 : // to infinite recursion.
425 0 : if (this->runtime()->errorInterception.isExecuting)
426 : break;
427 :
428 : // Check whether we have an interceptor at all.
429 0 : if (!this->runtime()->errorInterception.interceptor)
430 : break;
431 :
432 : // Make sure that we do not call the interceptor from within
433 : // the interceptor.
434 0 : this->runtime()->errorInterception.isExecuting = true;
435 :
436 : // The interceptor must be infallible.
437 0 : const mozilla::DebugOnly<bool> wasExceptionPending = this->isExceptionPending();
438 0 : this->runtime()->errorInterception.interceptor->interceptError(this, v);
439 0 : MOZ_ASSERT(wasExceptionPending == this->isExceptionPending());
440 :
441 0 : this->runtime()->errorInterception.isExecuting = false;
442 : } while (false);
443 : #endif // defined(NIGHTLY_BUILD)
444 :
445 : // overRecursed_ is set after the fact by ReportOverRecursed.
446 0 : this->overRecursed_ = false;
447 0 : this->throwing = true;
448 0 : this->unwrappedException() = v;
449 : // We don't use assertSameCompartment here to allow
450 : // js::SetPendingExceptionCrossContext to work.
451 0 : MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
452 0 : }
453 :
454 : inline bool
455 0 : JSContext::runningWithTrustedPrincipals()
456 : {
457 0 : return !realm() || realm()->principals() == runtime()->trustedPrincipals();
458 : }
459 :
460 : inline void
461 0 : JSContext::enterRealm(JS::Realm* realm)
462 : {
463 : // We should never enter a realm while in the atoms zone.
464 0 : MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
465 :
466 123346 : realm->enter();
467 0 : setRealm(realm);
468 123347 : }
469 :
470 : inline void
471 0 : JSContext::enterAtomsZone(const js::AutoLockForExclusiveAccess& lock)
472 : {
473 : // Only one thread can be in the atoms zone at a time.
474 0 : MOZ_ASSERT(runtime_->currentThreadHasExclusiveAccess());
475 :
476 48576 : realm_ = nullptr;
477 0 : zone_ = runtime_->atomsZone(lock);
478 97152 : arenas_ = &zone_->arenas;
479 0 : }
480 :
481 : template <typename T>
482 : inline void
483 115792 : JSContext::enterRealmOf(const T& target)
484 : {
485 231584 : MOZ_ASSERT(JS::CellIsNotGray(target));
486 0 : enterRealm(target->realm());
487 115793 : }
488 :
489 : inline void
490 0 : JSContext::enterNullRealm()
491 : {
492 : // We should never enter a realm while in the atoms zone.
493 0 : MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
494 :
495 1126 : setRealm(nullptr);
496 0 : }
497 :
498 : inline void
499 0 : JSContext::leaveRealm(JS::Realm* oldRealm)
500 : {
501 : // Only call leave() after we've setRealm()-ed away from the current realm.
502 0 : JS::Realm* startingRealm = realm_;
503 124466 : setRealm(oldRealm);
504 124466 : if (startingRealm)
505 0 : startingRealm->leave();
506 124466 : }
507 :
508 : inline void
509 : JSContext::leaveAtomsZone(JS::Realm* oldRealm,
510 : const js::AutoLockForExclusiveAccess& lock)
511 : {
512 48577 : setRealm(oldRealm);
513 : }
514 :
515 : inline void
516 0 : JSContext::setRealm(JS::Realm* realm)
517 : {
518 : // Both the current and the new realm should be properly marked as
519 : // entered at this point.
520 297504 : MOZ_ASSERT_IF(realm_, realm_->hasBeenEnteredIgnoringJit());
521 297504 : MOZ_ASSERT_IF(realm, realm->hasBeenEnteredIgnoringJit());
522 :
523 : // This thread must have exclusive access to the zone.
524 297504 : MOZ_ASSERT_IF(realm, CurrentThreadCanAccessZone(realm->zone()));
525 :
526 297506 : realm_ = realm;
527 0 : zone_ = realm ? realm->zone() : nullptr;
528 595013 : arenas_ = zone_ ? &zone_->arenas : nullptr;
529 297507 : }
530 :
531 : inline JSScript*
532 0 : JSContext::currentScript(jsbytecode** ppc,
533 : MaybeAllowCrossCompartment allowCrossCompartment) const
534 : {
535 0 : if (ppc)
536 27848 : *ppc = nullptr;
537 :
538 0 : js::Activation* act = activation();
539 0 : if (!act)
540 : return nullptr;
541 :
542 33941 : MOZ_ASSERT(act->cx() == this);
543 :
544 67882 : if (!allowCrossCompartment && act->compartment() != compartment())
545 : return nullptr;
546 :
547 0 : if (act->isJit()) {
548 3650 : if (act->hasWasmExitFP())
549 : return nullptr;
550 0 : JSScript* script = nullptr;
551 3650 : js::jit::GetPcScript(const_cast<JSContext*>(this), &script, ppc);
552 10950 : MOZ_ASSERT(allowCrossCompartment || script->compartment() == compartment());
553 0 : return script;
554 : }
555 :
556 10235 : MOZ_ASSERT(act->isInterpreter());
557 :
558 0 : js::InterpreterFrame* fp = act->asInterpreter()->current();
559 0 : MOZ_ASSERT(!fp->runningInJit());
560 :
561 0 : JSScript* script = fp->script();
562 0 : MOZ_ASSERT(allowCrossCompartment || script->compartment() == compartment());
563 :
564 0 : if (ppc) {
565 4617 : *ppc = act->asInterpreter()->regs().pc;
566 4617 : MOZ_ASSERT(script->containsPC(*ppc));
567 : }
568 :
569 : return script;
570 : }
571 :
572 : inline js::RuntimeCaches&
573 : JSContext::caches()
574 : {
575 0 : return runtime()->caches();
576 : }
577 :
578 : inline
579 2450 : js::AutoKeepAtoms::AutoKeepAtoms(JSContext* cx
580 2450 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
581 4900 : : cx(cx)
582 : {
583 4900 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
584 4900 : cx->zone()->keepAtoms();
585 2450 : }
586 :
587 : inline
588 : js::AutoKeepAtoms::~AutoKeepAtoms()
589 : {
590 : cx->zone()->releaseAtoms();
591 : };
592 :
593 : #endif /* vm_JSContext_inl_h */
|