Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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"
# if MOZ_ICU4X
# include "mozilla/intl/ICU4XGeckoDataProvider.h"
# endif
#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
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();
#if JS_HAS_INTL_API
mozilla::intl::ICU4CLibrary::Cleanup();
# if MOZ_ICU4X
mozilla::intl::CleanupDataProvider();
# endif // MOZ_ICU4X
#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;
}