summaryrefslogtreecommitdiffstats
path: root/js/src/vm/JSContext-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/JSContext-inl.h')
-rw-r--r--js/src/vm/JSContext-inl.h407
1 files changed, 407 insertions, 0 deletions
diff --git a/js/src/vm/JSContext-inl.h b/js/src/vm/JSContext-inl.h
new file mode 100644
index 0000000000..fe6d1f303f
--- /dev/null
+++ b/js/src/vm/JSContext-inl.h
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_JSContext_inl_h
+#define vm_JSContext_inl_h
+
+#include "vm/JSContext.h"
+
+#include <type_traits>
+
+#include "gc/Marking.h"
+#include "gc/Zone.h"
+#include "jit/JitFrames.h"
+#include "util/DiagnosticAssertions.h"
+#include "vm/BigIntType.h"
+#include "vm/GlobalObject.h"
+#include "vm/Realm.h"
+
+#include "vm/Activation-inl.h" // js::Activation::hasWasmExitFP
+
+namespace js {
+
+class ContextChecks {
+ JSContext* cx;
+
+ JS::Realm* realm() const { return cx->realm(); }
+ JS::Compartment* compartment() const { return cx->compartment(); }
+ JS::Zone* zone() const { return cx->zone(); }
+
+ public:
+ explicit ContextChecks(JSContext* cx) : cx(cx) {
+#ifdef DEBUG
+ if (realm()) {
+ GlobalObject* global = realm()->unsafeUnbarrieredMaybeGlobal();
+ if (global) {
+ checkObject(global);
+ }
+ }
+#endif
+ }
+
+ /*
+ * Set a breakpoint here (break js::ContextChecks::fail) to debug
+ * realm/compartment/zone mismatches.
+ */
+ static void fail(JS::Realm* r1, JS::Realm* r2, int argIndex) {
+ MOZ_CRASH_UNSAFE_PRINTF("*** Realm mismatch %p vs. %p at argument %d", r1,
+ r2, argIndex);
+ }
+ static void fail(JS::Compartment* c1, JS::Compartment* c2, int argIndex) {
+ MOZ_CRASH_UNSAFE_PRINTF("*** Compartment mismatch %p vs. %p at argument %d",
+ c1, c2, argIndex);
+ }
+ static void fail(JS::Zone* z1, JS::Zone* z2, int argIndex) {
+ MOZ_CRASH_UNSAFE_PRINTF("*** Zone mismatch %p vs. %p at argument %d", z1,
+ z2, argIndex);
+ }
+
+ void check(JS::Realm* r, int argIndex) {
+ if (r && r != realm()) {
+ fail(realm(), r, argIndex);
+ }
+ }
+
+ void check(JS::Compartment* c, int argIndex) {
+ if (c && c != compartment()) {
+ fail(compartment(), c, argIndex);
+ }
+ }
+
+ void check(JS::Zone* z, int argIndex) {
+ if (zone() && z != zone()) {
+ fail(zone(), z, argIndex);
+ }
+ }
+
+ void check(JSObject* obj, int argIndex) {
+ if (obj) {
+ checkObject(obj);
+ check(obj->compartment(), argIndex);
+ }
+ }
+
+ void checkObject(JSObject* obj) {
+ JS::AssertObjectIsNotGray(obj);
+ MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(obj));
+ }
+
+ template <typename T>
+ void checkAtom(T* thing, int argIndex) {
+ static_assert(std::is_same_v<T, JSAtom> || std::is_same_v<T, JS::Symbol>,
+ "Should only be called with JSAtom* or JS::Symbol* argument");
+
+#ifdef DEBUG
+ // Atoms which move across zone boundaries need to be marked in the new
+ // zone, see JS_MarkCrossZoneId.
+ if (zone()) {
+ if (!cx->runtime()->gc.atomMarking.atomIsMarked(zone(), thing)) {
+ MOZ_CRASH_UNSAFE_PRINTF(
+ "*** Atom not marked for zone %p at argument %d", zone(), argIndex);
+ }
+ }
+#endif
+ }
+
+ void check(JSString* str, int argIndex) {
+ JS::AssertCellIsNotGray(str);
+ if (str->isAtom()) {
+ checkAtom(&str->asAtom(), argIndex);
+ } else {
+ check(str->zone(), argIndex);
+ }
+ }
+
+ void check(JS::Symbol* symbol, int argIndex) { checkAtom(symbol, argIndex); }
+
+ void check(JS::BigInt* bi, int argIndex) { check(bi->zone(), argIndex); }
+
+ void check(const js::Value& v, int argIndex) {
+ if (v.isObject()) {
+ check(&v.toObject(), argIndex);
+ } else if (v.isString()) {
+ check(v.toString(), argIndex);
+ } else if (v.isSymbol()) {
+ check(v.toSymbol(), argIndex);
+ } else if (v.isBigInt()) {
+ check(v.toBigInt(), argIndex);
+ }
+ }
+
+ // Check the contents of any container class that supports the C++
+ // iteration protocol, eg GCVector<jsid>.
+ template <typename Container>
+ std::enable_if_t<std::is_same_v<decltype(std::declval<Container>().begin()),
+ decltype(std::declval<Container>().end())>>
+ check(const Container& container, int argIndex) {
+ for (auto i : container) {
+ check(i, argIndex);
+ }
+ }
+
+ void check(const JS::HandleValueArray& arr, int argIndex) {
+ for (size_t i = 0; i < arr.length(); i++) {
+ check(arr[i], argIndex);
+ }
+ }
+
+ void check(const CallArgs& args, int argIndex) {
+ for (Value* p = args.base(); p != args.end(); ++p) {
+ check(*p, argIndex);
+ }
+ }
+
+ void check(jsid id, int argIndex) {
+ if (id.isAtom()) {
+ checkAtom(id.toAtom(), argIndex);
+ } else if (id.isSymbol()) {
+ checkAtom(id.toSymbol(), argIndex);
+ } else {
+ MOZ_ASSERT(!id.isGCThing());
+ }
+ }
+
+ void check(JSScript* script, int argIndex) {
+ JS::AssertCellIsNotGray(script);
+ if (script) {
+ check(script->realm(), argIndex);
+ }
+ }
+
+ void check(AbstractFramePtr frame, int argIndex);
+
+ void check(const PropertyDescriptor& desc, int argIndex) {
+ if (desc.hasGetter()) {
+ check(desc.getter(), argIndex);
+ }
+ if (desc.hasSetter()) {
+ check(desc.setter(), argIndex);
+ }
+ if (desc.hasValue()) {
+ check(desc.value(), argIndex);
+ }
+ }
+
+ void check(Handle<mozilla::Maybe<Value>> maybe, int argIndex) {
+ if (maybe.get().isSome()) {
+ check(maybe.get().ref(), argIndex);
+ }
+ }
+
+ void check(Handle<mozilla::Maybe<PropertyDescriptor>> maybe, int argIndex) {
+ if (maybe.get().isSome()) {
+ check(maybe.get().ref(), argIndex);
+ }
+ }
+};
+
+} // namespace js
+
+template <class... Args>
+inline void JSContext::checkImpl(const Args&... args) {
+ int argIndex = 0;
+ (..., js::ContextChecks(this).check(args, argIndex++));
+}
+
+template <class... Args>
+inline void JSContext::check(const Args&... args) {
+#ifdef JS_CRASH_DIAGNOSTICS
+ if (contextChecksEnabled()) {
+ checkImpl(args...);
+ }
+#endif
+}
+
+template <class... Args>
+inline void JSContext::releaseCheck(const Args&... args) {
+ if (contextChecksEnabled()) {
+ checkImpl(args...);
+ }
+}
+
+template <class... Args>
+MOZ_ALWAYS_INLINE void JSContext::debugOnlyCheck(const Args&... args) {
+#if defined(DEBUG) && defined(JS_CRASH_DIAGNOSTICS)
+ if (contextChecksEnabled()) {
+ checkImpl(args...);
+ }
+#endif
+}
+
+namespace js {
+
+STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
+MOZ_ALWAYS_INLINE bool CallNativeImpl(JSContext* cx, NativeImpl impl,
+ const CallArgs& args) {
+#ifdef DEBUG
+ bool alreadyThrowing = cx->isExceptionPending();
+#endif
+ cx->check(args);
+ bool ok = impl(cx, args);
+ if (ok) {
+ cx->check(args.rval());
+ MOZ_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
+ }
+ return ok;
+}
+
+MOZ_ALWAYS_INLINE bool CheckForInterrupt(JSContext* cx) {
+ MOZ_ASSERT(!cx->isExceptionPending());
+ // Add an inline fast-path since we have to check for interrupts in some hot
+ // C++ loops of library builtins.
+ if (MOZ_UNLIKELY(cx->hasAnyPendingInterrupt())) {
+ return cx->handleInterrupt();
+ }
+
+ JS_INTERRUPT_POSSIBLY_FAIL();
+
+ return true;
+}
+
+} /* namespace js */
+
+inline js::Nursery& JSContext::nursery() { return runtime()->gc.nursery(); }
+
+inline void JSContext::minorGC(JS::GCReason reason) {
+ runtime()->gc.minorGC(reason);
+}
+
+inline bool JSContext::runningWithTrustedPrincipals() {
+ if (!realm()) {
+ return true;
+ }
+ if (!runtime()->trustedPrincipals()) {
+ return false;
+ }
+ return realm()->principals() == runtime()->trustedPrincipals();
+}
+
+inline void JSContext::enterRealm(JS::Realm* realm) {
+ // We should never enter a realm while in the atoms zone.
+ MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
+
+ realm->enter();
+ setRealm(realm);
+}
+
+inline void JSContext::enterAtomsZone() {
+ realm_ = nullptr;
+ setZone(runtime_->unsafeAtomsZone());
+}
+
+inline void JSContext::setZone(js::Zone* zone) {
+ MOZ_ASSERT(!isHelperThreadContext());
+ zone_ = zone;
+}
+
+inline void JSContext::enterRealmOf(JSObject* target) {
+ JS::AssertCellIsNotGray(target);
+ enterRealm(target->nonCCWRealm());
+}
+
+inline void JSContext::enterRealmOf(JSScript* target) {
+ JS::AssertCellIsNotGray(target);
+ enterRealm(target->realm());
+}
+
+inline void JSContext::enterRealmOf(js::Shape* target) {
+ JS::AssertCellIsNotGray(target);
+ enterRealm(target->realm());
+}
+
+inline void JSContext::enterNullRealm() {
+ // We should never enter a realm while in the atoms zone.
+ MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
+
+ setRealm(nullptr);
+}
+
+inline void JSContext::leaveRealm(JS::Realm* oldRealm) {
+ // Only call leave() after we've setRealm()-ed away from the current realm.
+ JS::Realm* startingRealm = realm_;
+
+ // The current realm should be marked as entered-from-C++ at this point.
+ MOZ_ASSERT_IF(startingRealm, startingRealm->hasBeenEnteredIgnoringJit());
+
+ setRealm(oldRealm);
+
+ if (startingRealm) {
+ startingRealm->leave();
+ }
+}
+
+inline void JSContext::leaveAtomsZone(JS::Realm* oldRealm) {
+ setRealm(oldRealm);
+}
+
+inline void JSContext::setRealm(JS::Realm* realm) {
+ realm_ = realm;
+ if (realm) {
+ // This thread must have exclusive access to the zone.
+ MOZ_ASSERT(CurrentThreadCanAccessZone(realm->zone()));
+ MOZ_ASSERT(!realm->zone()->isAtomsZone());
+ setZone(realm->zone());
+ } else {
+ setZone(nullptr);
+ }
+}
+
+inline void JSContext::setRealmForJitExceptionHandler(JS::Realm* realm) {
+ // JIT code enters (same-compartment) realms without calling realm->enter()
+ // so we don't call realm->leave() here.
+ MOZ_ASSERT(realm->compartment() == compartment());
+ realm_ = realm;
+}
+
+inline JSScript* JSContext::currentScript(
+ jsbytecode** ppc, AllowCrossRealm allowCrossRealm) const {
+ if (ppc) {
+ *ppc = nullptr;
+ }
+
+ js::Activation* act = activation();
+ if (!act) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(act->cx() == this);
+
+ // Cross-compartment implies cross-realm.
+ if (allowCrossRealm == AllowCrossRealm::DontAllow &&
+ act->compartment() != compartment()) {
+ return nullptr;
+ }
+
+ JSScript* script = nullptr;
+ jsbytecode* pc = nullptr;
+ if (act->isJit()) {
+ if (act->hasWasmExitFP()) {
+ return nullptr;
+ }
+ js::jit::GetPcScript(const_cast<JSContext*>(this), &script, &pc);
+ } else {
+ js::InterpreterFrame* fp = act->asInterpreter()->current();
+ MOZ_ASSERT(!fp->runningInJit());
+ script = fp->script();
+ pc = act->asInterpreter()->regs().pc;
+ }
+
+ MOZ_ASSERT(script->containsPC(pc));
+
+ if (allowCrossRealm == AllowCrossRealm::DontAllow &&
+ script->realm() != realm()) {
+ return nullptr;
+ }
+
+ if (ppc) {
+ *ppc = pc;
+ }
+ return script;
+}
+
+inline js::RuntimeCaches& JSContext::caches() { return runtime()->caches(); }
+
+#endif /* vm_JSContext_inl_h */