diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/Initialization.cpp | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp new file mode 100644 index 0000000000..323aab1dfd --- /dev/null +++ b/js/src/vm/Initialization.cpp @@ -0,0 +1,301 @@ +/* -*- 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/. */ + +/* SpiderMonkey initialization and shutdown code. */ + +#include "js/Initialization.h" + +#include "mozilla/Assertions.h" +#include "mozilla/TextUtils.h" +#include "mozilla/Unused.h" + +#include "jstypes.h" + +#include "builtin/AtomicsObject.h" +#include "builtin/TestingFunctions.h" +#include "ds/MemoryProtectionExceptionHandler.h" +#include "gc/Statistics.h" +#include "jit/AtomicOperations.h" +#include "jit/ExecutableAllocator.h" +#include "jit/Ion.h" +#include "jit/JitCommon.h" +#include "js/Utility.h" +#if JS_HAS_INTL_API +# include "unicode/putil.h" +# include "unicode/uclean.h" +# include "unicode/utypes.h" +#endif // JS_HAS_INTL_API +#include "util/Poison.h" +#include "vm/ArrayBufferObject.h" +#include "vm/BigIntType.h" +#include "vm/DateTime.h" +#include "vm/HelperThreads.h" +#include "vm/Runtime.h" +#include "vm/Time.h" +#include "vm/TraceLogging.h" +#ifdef MOZ_VTUNE +# include "vtune/VTuneWrapper.h" +#endif +#include "wasm/WasmProcess.h" + +using js::FutexThread; +using JS::detail::InitState; +using JS::detail::libraryInitState; + +InitState JS::detail::libraryInitState; + +#ifdef DEBUG +static unsigned MessageParameterCount(const char* format) { + unsigned numfmtspecs = 0; + for (const char* fmt = format; *fmt != '\0'; fmt++) { + if (*fmt == '{' && mozilla::IsAsciiDigit(fmt[1])) { + ++numfmtspecs; + } + } + return numfmtspecs; +} + +static void CheckMessageParameterCounts() { + // Assert that each message format has the correct number of braced + // parameters. +# define MSG_DEF(name, count, exception, format) \ + MOZ_ASSERT(MessageParameterCount(format) == count); +# include "js/friend/ErrorNumbers.msg" +# undef MSG_DEF +} +#endif /* DEBUG */ + +#if defined(JS_RUNTIME_CANONICAL_NAN) +namespace JS::detail { +uint64_t CanonicalizedNaNBits; +} // namespace JS::detail +#endif + +static void SetupCanonicalNaN() { + // Compute the standard NaN value that the hardware generates. + volatile double infinity = mozilla::PositiveInfinity<double>(); + volatile double hardwareNaN = infinity - infinity; + uint64_t hardwareNaNBits = mozilla::BitwiseCast<uint64_t>(hardwareNaN); + hardwareNaNBits &= ~mozilla::FloatingPoint<double>::kSignBit; + +#if defined(JS_NONCANONICAL_HARDWARE_NAN) + // If the NaN generated by hardware operations is not compatible + // with our canonical NaN, we must canonicalize every double. This + // is implemented for C++ code in Value::bitsFromDouble, but is not + // implemented for JIT code. +# if !defined(JS_CODEGEN_NONE) +# error "No JIT support for non-canonical hardware NaN" +# endif + + mozilla::Unused << hardwareNaNBits; +#elif defined(JS_RUNTIME_CANONICAL_NAN) + // Determine canonical NaN at startup. It must still match the ValueIsDouble + // requirements. + MOZ_RELEASE_ASSERT(JS::detail::ValueIsDouble(hardwareNaNBits)); + JS::detail::CanonicalizedNaNBits = hardwareNaNBits; +#else + // Assert that the NaN generated by hardware operations is + // compatible with the canonical NaN we use for JS::Value. This is + // true for all of our supported platforms, but not for SPARC. + MOZ_RELEASE_ASSERT(hardwareNaNBits == JS::detail::CanonicalizedNaNBits, + "Unexpected default hardware NaN value"); +#endif +} + +#define RETURN_IF_FAIL(code) \ + do { \ + if (!code) return #code " failed"; \ + } while (0) + +extern "C" void install_rust_panic_hook(); + +JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic( + bool isDebugBuild) { + // Verify that our DEBUG setting matches the caller's. +#ifdef DEBUG + MOZ_RELEASE_ASSERT(isDebugBuild); +#else + MOZ_RELEASE_ASSERT(!isDebugBuild); +#endif + + MOZ_ASSERT(libraryInitState == InitState::Uninitialized, + "must call JS_Init once before any JSAPI operation except " + "JS_SetICUMemoryFunctions"); + MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(), + "how do we have live runtimes before JS_Init?"); + + libraryInitState = InitState::Initializing; + +#ifndef NO_RUST_PANIC_HOOK + install_rust_panic_hook(); +#endif + + PRMJ_NowInit(); + + js::SliceBudget::Init(); + + // The first invocation of `ProcessCreation` creates a temporary thread + // and crashes if that fails, i.e. because we're out of memory. To prevent + // that from happening at some later time, get it out of the way during + // startup. + mozilla::TimeStamp::ProcessCreation(); + +#ifdef DEBUG + CheckMessageParameterCounts(); +#endif + + SetupCanonicalNaN(); + + RETURN_IF_FAIL(js::TlsContext.init()); + +#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) + RETURN_IF_FAIL(js::oom::InitThreadType()); +#endif + +#if defined(FUZZING) + js::oom::InitLargeAllocLimit(); +#endif + + js::gDisablePoisoning = bool(getenv("JSGC_DISABLE_POISONING")); + + js::InitMallocAllocator(); + + RETURN_IF_FAIL(js::Mutex::Init()); + + js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works. + + RETURN_IF_FAIL(js::wasm::Init()); + + js::coverage::InitLCov(); + + RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory()); + + RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install()); + + RETURN_IF_FAIL(js::jit::InitializeJit()); + + RETURN_IF_FAIL(js::InitDateTimeState()); + +#ifdef MOZ_VTUNE + RETURN_IF_FAIL(js::vtune::Initialize()); +#endif + + RETURN_IF_FAIL(js::jit::AtomicOperations::Initialize()); + +#if JS_HAS_INTL_API +# if !MOZ_SYSTEM_ICU + // Explicitly set the data directory to its default value, but only when we're + // sure that we use our in-tree ICU copy. See bug 1527879 and ICU bug + // report <https://unicode-org.atlassian.net/browse/ICU-20491>. + u_setDataDirectory(""); +# endif + + UErrorCode err = U_ZERO_ERROR; + u_init(&err); + if (U_FAILURE(err)) { + return "u_init() failed"; + } +#endif // JS_HAS_INTL_API + + RETURN_IF_FAIL(js::CreateHelperThreadsState()); + RETURN_IF_FAIL(FutexThread::initialize()); + RETURN_IF_FAIL(js::gcstats::Statistics::initialize()); + RETURN_IF_FAIL(js::InitTestingFunctions()); + +#ifdef JS_SIMULATOR + RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize()); +#endif + +#ifdef JS_TRACE_LOGGING + RETURN_IF_FAIL(JS::InitTraceLogger()); +#endif + + libraryInitState = InitState::Running; + return nullptr; +} + +#undef RETURN_IF_FAIL + +JS_PUBLIC_API void JS_ShutDown(void) { + MOZ_ASSERT( + libraryInitState == InitState::Running, + "JS_ShutDown must only be called after JS_Init and can't race with it"); +#ifdef DEBUG + if (JSRuntime::hasLiveRuntimes()) { + // Gecko is too buggy to assert this just yet. + fprintf(stderr, + "WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime " + "and everything alive inside it, that is) AT JS_ShutDown " + "TIME. FIX THIS!\n"); + } +#endif + + FutexThread::destroy(); + + js::DestroyHelperThreadsState(); + +#ifdef JS_SIMULATOR + js::jit::SimulatorProcess::destroy(); +#endif + + js::jit::AtomicOperations::ShutDown(); + +#ifdef JS_TRACE_LOGGING + js::DestroyTraceLoggerThreadState(); + js::DestroyTraceLoggerGraphState(); +#endif + + js::MemoryProtectionExceptionHandler::uninstall(); + + js::wasm::ShutDown(); + + // The only difficult-to-address reason for the restriction that you can't + // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ + // NowInit initialization code, which uses PR_CallOnce to initialize the + // PRMJ_Now subsystem. (For reinitialization to be permitted, we'd need to + // "reset" the called-once status -- doable, but more trouble than it's + // worth now.) Initializing that subsystem from JS_Init eliminates the + // problem, but initialization can take a comparatively long time (15ms or + // so), so we really don't want to do it in JS_Init, and we really do want + // to do it only when PRMJ_Now is eventually called. + PRMJ_NowShutdown(); + +#if JS_HAS_INTL_API + u_cleanup(); +#endif // JS_HAS_INTL_API + +#ifdef MOZ_VTUNE + js::vtune::Shutdown(); +#endif // MOZ_VTUNE + + js::FinishDateTimeState(); + + if (!JSRuntime::hasLiveRuntimes()) { + js::jit::ReleaseProcessExecutableMemory(); + MOZ_ASSERT(!js::LiveMappedBufferCount()); + } + + js::ShutDownMallocAllocator(); + + libraryInitState = InitState::ShutDown; +} + +JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn, + JS_ICUReallocFn reallocFn, + JS_ICUFreeFn freeFn) { + MOZ_ASSERT(libraryInitState == InitState::Uninitialized, + "must call JS_SetICUMemoryFunctions before any other JSAPI " + "operation (including JS_Init)"); + +#if JS_HAS_INTL_API + UErrorCode status = U_ZERO_ERROR; + u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn, + &status); + return U_SUCCESS(status); +#else + return true; +#endif +} |