/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 #include "jstypes.h" #include "builtin/AtomicsObject.h" #include "ds/MemoryProtectionExceptionHandler.h" #include "gc/Statistics.h" #include "jit/ExecutableAllocator.h" #include "jit/Ion.h" #include "js/Utility.h" #if ENABLE_INTL_API #include "unicode/uclean.h" #include "unicode/utypes.h" #endif // ENABLE_INTL_API #include "vm/DateTime.h" #include "vm/HelperThreads.h" #include "vm/Runtime.h" #include "vm/Time.h" #include "vm/TraceLogging.h" #include "wasm/WasmInstance.h" using JS::detail::InitState; using JS::detail::libraryInitState; using js::FutexRuntime; 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 == '{' && isdigit(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.msg" # undef MSG_DEF } #endif /* DEBUG */ #define RETURN_IF_FAIL(code) do { if (!code) return #code " failed"; } while (0) 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?"); PRMJ_NowInit(); // 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. bool ignored; mozilla::TimeStamp::ProcessCreation(ignored); #ifdef DEBUG CheckMessageParameterCounts(); #endif using js::TlsPerThreadData; RETURN_IF_FAIL(TlsPerThreadData.init()); #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) RETURN_IF_FAIL(js::oom::InitThreadType()); js::oom::SetThreadType(js::oom::THREAD_TYPE_MAIN); #endif RETURN_IF_FAIL(js::Mutex::Init()); RETURN_IF_FAIL(js::wasm::InitInstanceStaticData()); js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works. RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory()); MOZ_ALWAYS_TRUE(js::MemoryProtectionExceptionHandler::install()); RETURN_IF_FAIL(js::jit::InitializeIon()); js::DateTimeInfo::init(); #if EXPOSE_INTL_API UErrorCode err = U_ZERO_ERROR; u_init(&err); if (U_FAILURE(err)) return "u_init() failed"; #endif // EXPOSE_INTL_API RETURN_IF_FAIL(js::CreateHelperThreadsState()); RETURN_IF_FAIL(FutexRuntime::initialize()); RETURN_IF_FAIL(js::gcstats::Statistics::initialize()); 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 FutexRuntime::destroy(); js::DestroyHelperThreadsState(); #ifdef JS_TRACE_LOGGING js::DestroyTraceLoggerThreadState(); js::DestroyTraceLoggerGraphState(); #endif js::MemoryProtectionExceptionHandler::uninstall(); js::wasm::ShutDownInstanceStaticData(); js::Mutex::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 EXPOSE_INTL_API u_cleanup(); #endif // EXPOSE_INTL_API if (!JSRuntime::hasLiveRuntimes()) js::jit::ReleaseProcessExecutableMemory(); 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 EXPOSE_INTL_API UErrorCode status = U_ZERO_ERROR; u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn, &status); return U_SUCCESS(status); #else return true; #endif }