Bug 1842773 - Part 32: Allow constructing growable SharedArrayBuffers. r=sfink
[gecko.git] / js / src / vm / JSContext-inl.h
blobf20c4b3c7d304445c0840481d1419b33b1e69755
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"
15 #include "gc/Zone.h"
16 #include "jit/JitFrames.h"
17 #include "util/DiagnosticAssertions.h"
18 #include "vm/BigIntType.h"
19 #include "vm/GlobalObject.h"
20 #include "vm/Realm.h"
22 #include "gc/Allocator-inl.h"
23 #include "vm/Activation-inl.h" // js::Activation::hasWasmExitFP
25 namespace js {
27 class ContextChecks {
28 JSContext* cx;
30 JS::Realm* realm() const { return cx->realm(); }
31 JS::Compartment* compartment() const { return cx->compartment(); }
32 JS::Zone* zone() const { return cx->zone(); }
34 public:
35 explicit ContextChecks(JSContext* cx) : cx(cx) {
36 #ifdef DEBUG
37 if (realm()) {
38 GlobalObject* global = realm()->unsafeUnbarrieredMaybeGlobal();
39 if (global) {
40 checkObject(global);
43 #endif
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,
52 r2, argIndex);
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",
56 c1, c2, argIndex);
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,
60 z2, argIndex);
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) {
82 if (obj) {
83 checkObject(obj);
84 check(obj->compartment(), argIndex);
88 void checkObject(JSObject* obj) {
89 JS::AssertObjectIsNotGray(obj);
90 MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(obj));
93 template <typename T>
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");
98 #ifdef DEBUG
99 // Atoms which move across zone boundaries need to be marked in the new
100 // zone, see JS_MarkCrossZoneId.
101 if (zone()) {
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);
107 #endif
110 void check(JSString* str, int argIndex) {
111 JS::AssertCellIsNotGray(str);
112 if (str->isAtom()) {
113 checkAtom(&str->asAtom(), argIndex);
114 } else {
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) {
124 if (v.isObject()) {
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) {
142 check(i, argIndex);
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) {
154 check(*p, argIndex);
158 void check(jsid id, int argIndex) {
159 if (id.isAtom()) {
160 checkAtom(id.toAtom(), argIndex);
161 } else if (id.isSymbol()) {
162 checkAtom(id.toSymbol(), argIndex);
163 } else {
164 MOZ_ASSERT(!id.isGCThing());
168 void check(JSScript* script, int argIndex) {
169 JS::AssertCellIsNotGray(script);
170 if (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);
202 } // namespace js
204 template <class... Args>
205 inline void JSContext::checkImpl(const Args&... args) {
206 int argIndex = 0;
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()) {
214 checkImpl(args...);
216 #endif
219 template <class... Args>
220 inline void JSContext::releaseCheck(const Args&... args) {
221 if (contextChecksEnabled()) {
222 checkImpl(args...);
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()) {
230 checkImpl(args...);
232 #endif
235 namespace js {
237 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
238 MOZ_ALWAYS_INLINE bool CallNativeImpl(JSContext* cx, NativeImpl impl,
239 const CallArgs& args) {
240 #ifdef DEBUG
241 bool alreadyThrowing = cx->isExceptionPending();
242 #endif
243 cx->check(args);
244 bool ok = impl(cx, args);
245 if (ok) {
246 cx->check(args.rval());
247 MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
249 return ok;
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();
262 return true;
265 } /* namespace js */
267 inline void JSContext::minorGC(JS::GCReason reason) {
268 runtime()->gc.minorGC(reason);
271 inline bool JSContext::runningWithTrustedPrincipals() {
272 if (!realm()) {
273 return true;
275 if (!runtime()->trustedPrincipals()) {
276 return false;
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());
285 realm->enter();
286 setRealm(realm);
289 inline void JSContext::enterAtomsZone() {
290 realm_ = nullptr;
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());
315 setRealm(nullptr);
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());
325 setRealm(oldRealm);
327 if (startingRealm) {
328 startingRealm->leave();
332 inline void JSContext::leaveAtomsZone(JS::Realm* oldRealm) {
333 setRealm(oldRealm);
336 inline void JSContext::setRealm(JS::Realm* realm) {
337 realm_ = realm;
338 if (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());
343 } else {
344 setZone(nullptr);
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());
352 realm_ = realm;
355 inline JSScript* JSContext::currentScript(
356 jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const {
357 if (ppc) {
358 *ppc = nullptr;
361 js::Activation* act = activation();
362 if (!act) {
363 return nullptr;
366 MOZ_ASSERT(act->cx() == this);
368 // Cross-compartment implies cross-realm.
369 if (allowCrossRealm == AllowCrossRealm::DontAllow &&
370 act->compartment() != compartment()) {
371 return nullptr;
374 JSScript* script = nullptr;
375 jsbytecode* pc = nullptr;
376 if (act->isJit()) {
377 if (act->hasWasmExitFP()) {
378 return nullptr;
380 js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc);
381 } else {
382 js::InterpreterFrame* fp = act->asInterpreter()->current();
383 MOZ_ASSERT(!fp->runningInJit());
384 script = fp->script();
385 pc = act->asInterpreter()->regs().pc;
388 MOZ_ASSERT(script->containsPC(pc));
390 if (allowCrossRealm == AllowCrossRealm::DontAllow &&
391 script->realm() != realm()) {
392 return nullptr;
395 if (ppc) {
396 *ppc = pc;
398 return script;
401 inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); }
403 template <typename T, js::AllowGC allowGC, typename... Args>
404 T* JSContext::newCell(Args&&... args) {
405 return js::gc::CellAllocator::template NewCell<T, allowGC>(
406 this, std::forward<Args>(args)...);
409 #endif /* vm_JSContext_inl_h */