diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/Initialization.cpp | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp new file mode 100644 index 0000000000..a0ff682327 --- /dev/null +++ b/js/src/vm/Initialization.cpp @@ -0,0 +1,357 @@ +/* -*- 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" +#if JS_HAS_INTL_API +# include "mozilla/intl/ICU4CLibrary.h" +#endif +#include "mozilla/TextUtils.h" + +#include "jstypes.h" + +#include "builtin/AtomicsObject.h" +#include "builtin/TestingFunctions.h" +#include "gc/Statistics.h" +#include "jit/Assembler.h" +#include "jit/Ion.h" +#include "jit/JitOptions.h" +#include "jit/Simulator.h" +#include "js/Utility.h" +#include "threading/ProtectedData.h" // js::AutoNoteSingleThreadedRegion +#include "util/Poison.h" +#include "vm/ArrayBufferObject.h" +#include "vm/DateTime.h" +#include "vm/HelperThreads.h" +#include "vm/Runtime.h" +#include "vm/Time.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 + + (void)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_hooks(); + +JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic( + bool isDebugBuild, FrontendOnly frontendOnly /* = FrontendOnly::No */) { + // 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; + +#ifdef JS_STANDALONE + // The rust hooks are initialized by Gecko on non-standalone builds. + install_rust_hooks(); +#endif + + PRMJ_NowInit(); + + if (frontendOnly == FrontendOnly::No) { + // 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(); + + if (frontendOnly == FrontendOnly::No) { + 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 + +#if defined(JS_GC_ALLOW_EXTRA_POISONING) + if (getenv("JSGC_EXTRA_POISONING")) { + js::gExtraPoisoningEnabled = true; + } +#endif + + js::InitMallocAllocator(); + + RETURN_IF_FAIL(js::Mutex::Init()); + + js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works. + + RETURN_IF_FAIL(js::wasm::Init()); + + js::coverage::InitLCov(); + + if (frontendOnly == FrontendOnly::No) { + RETURN_IF_FAIL(js::jit::InitializeJit()); + } + + RETURN_IF_FAIL(js::InitDateTimeState()); + + if (frontendOnly == FrontendOnly::No) { +#ifdef MOZ_VTUNE + RETURN_IF_FAIL(js::vtune::Initialize()); +#endif + } + +#if JS_HAS_INTL_API + if (mozilla::intl::ICU4CLibrary::Initialize().isErr()) { + return "ICU4CLibrary::Initialize() failed"; + } +#endif // JS_HAS_INTL_API + + if (frontendOnly == FrontendOnly::No) { + RETURN_IF_FAIL(js::CreateHelperThreadsState()); + RETURN_IF_FAIL(FutexThread::initialize()); + RETURN_IF_FAIL(js::gcstats::Statistics::initialize()); + RETURN_IF_FAIL(js::InitTestingFunctions()); + } + + RETURN_IF_FAIL(js::SharedImmutableStringsCache::initSingleton()); + RETURN_IF_FAIL(js::frontend::WellKnownParserAtoms::initSingleton()); + + if (frontendOnly == FrontendOnly::No) { +#ifdef JS_SIMULATOR + RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize()); +#endif + +#ifndef JS_CODEGEN_NONE + // This is forced by InitializeJit. + MOZ_ASSERT(js::jit::CPUFlagsHaveBeenComputed()); +#endif + } + + libraryInitState = InitState::Running; + return nullptr; +} + +#undef RETURN_IF_FAIL + +JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx, SelfHostedCache cache, + SelfHostedWriter writer) { + MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(), + "JS::InitSelfHostedCode() called more than once"); + + js::AutoNoteSingleThreadedRegion anstr; + + JSRuntime* rt = cx->runtime(); + + if (!rt->initSelfHostingStencil(cx, cache, writer)) { + return false; + } + + if (!rt->initializeAtoms(cx)) { + return false; + } + + if (!rt->initSelfHostingFromStencil(cx)) { + return false; + } + + if (js::jit::HasJitBackend()) { + if (!rt->createJitRuntime(cx)) { + return false; + } + } + + return true; +} + +static void ShutdownImpl(JS::detail::FrontendOnly frontendOnly) { + using FrontendOnly = JS::detail::FrontendOnly; + + 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 + + js::frontend::WellKnownParserAtoms::freeSingleton(); + js::SharedImmutableStringsCache::freeSingleton(); + + if (frontendOnly == FrontendOnly::No) { + FutexThread::destroy(); + + js::DestroyHelperThreadsState(); + +#ifdef JS_SIMULATOR + js::jit::SimulatorProcess::destroy(); +#endif + } + + 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 + mozilla::intl::ICU4CLibrary::Cleanup(); +#endif // JS_HAS_INTL_API + + if (frontendOnly == FrontendOnly::No) { +#ifdef MOZ_VTUNE + js::vtune::Shutdown(); +#endif // MOZ_VTUNE + } + + js::FinishDateTimeState(); + + if (frontendOnly == FrontendOnly::No) { + js::jit::ShutdownJit(); + } + + MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !js::WasmReservedBytes()); + + js::ShutDownMallocAllocator(); + + libraryInitState = InitState::ShutDown; +} + +JS_PUBLIC_API void JS_ShutDown(void) { + ShutdownImpl(JS::detail::FrontendOnly::No); +} + +JS_PUBLIC_API void JS_FrontendOnlyShutDown(void) { + ShutdownImpl(JS::detail::FrontendOnly::Yes); +} + +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 + return mozilla::intl::ICU4CLibrary::SetMemoryFunctions( + {allocFn, reallocFn, freeFn}) + .isOk(); +#else + return true; +#endif +} + +#if defined(ENABLE_WASM_SIMD) && \ + (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) +void JS::SetAVXEnabled(bool enabled) { + if (enabled) { + js::jit::CPUInfo::SetAVXEnabled(); + } else { + js::jit::CPUInfo::SetAVXDisabled(); + } +} +#endif + +JS_PUBLIC_API void JS::DisableJitBackend() { + MOZ_ASSERT(libraryInitState == InitState::Uninitialized, + "DisableJitBackend must be called before JS_Init"); + MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(), + "DisableJitBackend must be called before creating a JSContext"); + js::jit::JitOptions.disableJitBackend = true; +} |