1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #ifndef vm_JSContext_inl_h
8 #define vm_JSContext_inl_h
10 #include "vm/JSContext.h"
12 #include <type_traits>
14 #include "gc/Marking.h"
16 #include "jit/JitFrames.h"
17 #include "util/DiagnosticAssertions.h"
18 #include "vm/BigIntType.h"
19 #include "vm/GlobalObject.h"
22 #include "gc/Allocator-inl.h"
23 #include "vm/Activation-inl.h" // js::Activation::hasWasmExitFP
30 JS::Realm
* realm() const { return cx
->realm(); }
31 JS::Compartment
* compartment() const { return cx
->compartment(); }
32 JS::Zone
* zone() const { return cx
->zone(); }
35 explicit ContextChecks(JSContext
* cx
) : cx(cx
) {
38 GlobalObject
* global
= realm()->unsafeUnbarrieredMaybeGlobal();
47 * Set a breakpoint here (break js::ContextChecks::fail) to debug
48 * realm/compartment/zone mismatches.
50 static void fail(JS::Realm
* r1
, JS::Realm
* r2
, int argIndex
) {
51 MOZ_CRASH_UNSAFE_PRINTF("*** Realm mismatch %p vs. %p at argument %d", r1
,
54 static void fail(JS::Compartment
* c1
, JS::Compartment
* c2
, int argIndex
) {
55 MOZ_CRASH_UNSAFE_PRINTF("*** Compartment mismatch %p vs. %p at argument %d",
58 static void fail(JS::Zone
* z1
, JS::Zone
* z2
, int argIndex
) {
59 MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d", z1
,
63 void check(JS::Realm
* r
, int argIndex
) {
64 if (r
&& r
!= realm()) {
65 fail(realm(), r
, argIndex
);
69 void check(JS::Compartment
* c
, int argIndex
) {
70 if (c
&& c
!= compartment()) {
71 fail(compartment(), c
, argIndex
);
75 void check(JS::Zone
* z
, int argIndex
) {
76 if (zone() && z
!= zone()) {
77 fail(zone(), z
, argIndex
);
81 void check(JSObject
* obj
, int argIndex
) {
84 check(obj
->compartment(), argIndex
);
88 void checkObject(JSObject
* obj
) {
89 JS::AssertObjectIsNotGray(obj
);
90 MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(obj
));
94 void checkAtom(T
* thing
, int argIndex
) {
95 static_assert(std::is_same_v
<T
, JSAtom
> || std::is_same_v
<T
, JS::Symbol
>,
96 "Should only be called with JSAtom* or JS::Symbol* argument");
99 // Atoms which move across zone boundaries need to be marked in the new
100 // zone, see JS_MarkCrossZoneId.
102 if (!cx
->runtime()->gc
.atomMarking
.atomIsMarked(zone(), thing
)) {
103 MOZ_CRASH_UNSAFE_PRINTF(
104 "*** Atom not marked for zone %p at argument %d", zone(), argIndex
);
110 void check(JSString
* str
, int argIndex
) {
111 JS::AssertCellIsNotGray(str
);
113 checkAtom(&str
->asAtom(), argIndex
);
115 check(str
->zone(), argIndex
);
119 void check(JS::Symbol
* symbol
, int argIndex
) { checkAtom(symbol
, argIndex
); }
121 void check(JS::BigInt
* bi
, int argIndex
) { check(bi
->zone(), argIndex
); }
123 void check(const js::Value
& v
, int argIndex
) {
125 check(&v
.toObject(), argIndex
);
126 } else if (v
.isString()) {
127 check(v
.toString(), argIndex
);
128 } else if (v
.isSymbol()) {
129 check(v
.toSymbol(), argIndex
);
130 } else if (v
.isBigInt()) {
131 check(v
.toBigInt(), argIndex
);
135 // Check the contents of any container class that supports the C++
136 // iteration protocol, eg GCVector<jsid>.
137 template <typename Container
>
138 std::enable_if_t
<std::is_same_v
<decltype(std::declval
<Container
>().begin()),
139 decltype(std::declval
<Container
>().end())>>
140 check(const Container
& container
, int argIndex
) {
141 for (auto i
: container
) {
146 void check(const JS::HandleValueArray
& arr
, int argIndex
) {
147 for (size_t i
= 0; i
< arr
.length(); i
++) {
148 check(arr
[i
], argIndex
);
152 void check(const CallArgs
& args
, int argIndex
) {
153 for (Value
* p
= args
.base(); p
!= args
.end(); ++p
) {
158 void check(jsid id
, int argIndex
) {
160 checkAtom(id
.toAtom(), argIndex
);
161 } else if (id
.isSymbol()) {
162 checkAtom(id
.toSymbol(), argIndex
);
164 MOZ_ASSERT(!id
.isGCThing());
168 void check(JSScript
* script
, int argIndex
) {
169 JS::AssertCellIsNotGray(script
);
171 check(script
->realm(), argIndex
);
175 void check(AbstractFramePtr frame
, int argIndex
);
177 void check(const PropertyDescriptor
& desc
, int argIndex
) {
178 if (desc
.hasGetter()) {
179 check(desc
.getter(), argIndex
);
181 if (desc
.hasSetter()) {
182 check(desc
.setter(), argIndex
);
184 if (desc
.hasValue()) {
185 check(desc
.value(), argIndex
);
189 void check(Handle
<mozilla::Maybe
<Value
>> maybe
, int argIndex
) {
190 if (maybe
.get().isSome()) {
191 check(maybe
.get().ref(), argIndex
);
195 void check(Handle
<mozilla::Maybe
<PropertyDescriptor
>> maybe
, int argIndex
) {
196 if (maybe
.get().isSome()) {
197 check(maybe
.get().ref(), argIndex
);
204 template <class... Args
>
205 inline void JSContext::checkImpl(const Args
&... args
) {
207 (..., js::ContextChecks(this).check(args
, argIndex
++));
210 template <class... Args
>
211 inline void JSContext::check(const Args
&... args
) {
212 #ifdef JS_CRASH_DIAGNOSTICS
213 if (contextChecksEnabled()) {
219 template <class... Args
>
220 inline void JSContext::releaseCheck(const Args
&... args
) {
221 if (contextChecksEnabled()) {
226 template <class... Args
>
227 MOZ_ALWAYS_INLINE
void JSContext::debugOnlyCheck(const Args
&... args
) {
228 #if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
229 if (contextChecksEnabled()) {
237 STATIC_PRECONDITION_ASSUME(ubound(args
.argv_
) >= argc
)
238 MOZ_ALWAYS_INLINE
bool CallNativeImpl(JSContext
* cx
, NativeImpl impl
,
239 const CallArgs
& args
) {
241 bool alreadyThrowing
= cx
->isExceptionPending();
244 bool ok
= impl(cx
, args
);
246 cx
->check(args
.rval());
247 MOZ_ASSERT_IF(!alreadyThrowing
, !cx
->isExceptionPending());
252 MOZ_ALWAYS_INLINE
bool CheckForInterrupt(JSContext
* cx
) {
253 MOZ_ASSERT(!cx
->isExceptionPending());
254 // Add an inline fast-path since we have to check for interrupts in some hot
255 // C++ loops of library builtins.
256 if (MOZ_UNLIKELY(cx
->hasAnyPendingInterrupt())) {
257 return cx
->handleInterrupt();
260 JS_INTERRUPT_POSSIBLY_FAIL();
267 inline void JSContext::minorGC(JS::GCReason reason
) {
268 runtime()->gc
.minorGC(reason
);
271 inline bool JSContext::runningWithTrustedPrincipals() {
275 if (!runtime()->trustedPrincipals()) {
278 return realm()->principals() == runtime()->trustedPrincipals();
281 inline void JSContext::enterRealm(JS::Realm
* realm
) {
282 // We should never enter a realm while in the atoms zone.
283 MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
289 inline void JSContext::enterAtomsZone() {
291 setZone(runtime_
->unsafeAtomsZone());
294 inline void JSContext::setZone(js::Zone
* zone
) { zone_
= zone
; }
296 inline void JSContext::enterRealmOf(JSObject
* target
) {
297 JS::AssertCellIsNotGray(target
);
298 enterRealm(target
->nonCCWRealm());
301 inline void JSContext::enterRealmOf(JSScript
* target
) {
302 JS::AssertCellIsNotGray(target
);
303 enterRealm(target
->realm());
306 inline void JSContext::enterRealmOf(js::Shape
* target
) {
307 JS::AssertCellIsNotGray(target
);
308 enterRealm(target
->realm());
311 inline void JSContext::enterNullRealm() {
312 // We should never enter a realm while in the atoms zone.
313 MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
318 inline void JSContext::leaveRealm(JS::Realm
* oldRealm
) {
319 // Only call leave() after we've setRealm()-ed away from the current realm.
320 JS::Realm
* startingRealm
= realm_
;
322 // The current realm should be marked as entered-from-C++ at this point.
323 MOZ_ASSERT_IF(startingRealm
, startingRealm
->hasBeenEnteredIgnoringJit());
328 startingRealm
->leave();
332 inline void JSContext::leaveAtomsZone(JS::Realm
* oldRealm
) {
336 inline void JSContext::setRealm(JS::Realm
* realm
) {
339 // This thread must have exclusive access to the zone.
340 MOZ_ASSERT(CurrentThreadCanAccessZone(realm
->zone()));
341 MOZ_ASSERT(!realm
->zone()->isAtomsZone());
342 setZone(realm
->zone());
348 inline void JSContext::setRealmForJitExceptionHandler(JS::Realm
* realm
) {
349 // JIT code enters (same-compartment) realms without calling realm->enter()
350 // so we don't call realm->leave() here.
351 MOZ_ASSERT(realm
->compartment() == compartment());
355 inline js::RuntimeCaches
& JSContext::caches() { return runtime()->caches(); }
357 template <typename T
, js::AllowGC allowGC
, typename
... Args
>
358 T
* JSContext::newCell(Args
&&... args
) {
359 return js::gc::CellAllocator::template NewCell
<T
, allowGC
>(
360 this, std::forward
<Args
>(args
)...);
363 #endif /* vm_JSContext_inl_h */