tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Initialization.cpp (9737B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /* SpiderMonkey initialization and shutdown code. */
      8 
      9 #include "js/Initialization.h"
     10 
     11 #include "mozilla/Assertions.h"
     12 #if JS_HAS_INTL_API
     13 #  include "mozilla/intl/ICU4CLibrary.h"
     14 #endif
     15 #include "mozilla/TextUtils.h"
     16 
     17 #include "jstypes.h"
     18 
     19 #include "builtin/AtomicsObject.h"
     20 #include "builtin/TestingFunctions.h"
     21 #include "gc/Statistics.h"
     22 #include "jit/Assembler.h"
     23 #include "jit/Ion.h"
     24 #include "jit/JitOptions.h"
     25 #include "jit/Simulator.h"
     26 #include "js/Utility.h"
     27 #include "threading/ProtectedData.h"  // js::AutoNoteSingleThreadedRegion
     28 #include "util/Poison.h"
     29 #include "vm/ArrayBufferObject.h"
     30 #include "vm/DateTime.h"
     31 #include "vm/HelperThreads.h"
     32 #include "vm/Runtime.h"
     33 #include "vm/Time.h"
     34 #ifdef MOZ_VTUNE
     35 #  include "vtune/VTuneWrapper.h"
     36 #endif
     37 #include "wasm/WasmProcess.h"
     38 
     39 using js::FutexThread;
     40 using JS::detail::InitState;
     41 using JS::detail::libraryInitState;
     42 
     43 InitState JS::detail::libraryInitState;
     44 
     45 #ifdef DEBUG
     46 static unsigned MessageParameterCount(const char* format) {
     47  unsigned numfmtspecs = 0;
     48  for (const char* fmt = format; *fmt != '\0'; fmt++) {
     49    if (*fmt == '{' && mozilla::IsAsciiDigit(fmt[1])) {
     50      ++numfmtspecs;
     51    }
     52  }
     53  return numfmtspecs;
     54 }
     55 
     56 static void CheckMessageParameterCounts() {
     57  // Assert that each message format has the correct number of braced
     58  // parameters.
     59 #  define MSG_DEF(name, count, exception, format) \
     60    MOZ_ASSERT(MessageParameterCount(format) == count);
     61 #  include "js/friend/ErrorNumbers.msg"
     62 #  undef MSG_DEF
     63 }
     64 #endif /* DEBUG */
     65 
     66 #if defined(JS_RUNTIME_CANONICAL_NAN)
     67 namespace JS::detail {
     68 uint64_t CanonicalizedNaNBits;
     69 }  // namespace JS::detail
     70 #endif
     71 
     72 static void SetupCanonicalNaN() {
     73  // Compute the standard NaN value that the hardware generates.
     74  volatile double infinity = mozilla::PositiveInfinity<double>();
     75  volatile double hardwareNaN = infinity - infinity;
     76  uint64_t hardwareNaNBits = mozilla::BitwiseCast<uint64_t>(hardwareNaN);
     77  hardwareNaNBits &= ~mozilla::FloatingPoint<double>::kSignBit;
     78 
     79 #if defined(JS_NONCANONICAL_HARDWARE_NAN)
     80  // If the NaN generated by hardware operations is not compatible
     81  // with our canonical NaN, we must canonicalize every double. This
     82  // is implemented for C++ code in Value::bitsFromDouble, but is not
     83  // implemented for JIT code.
     84 #  if !defined(JS_CODEGEN_NONE)
     85 #    error "No JIT support for non-canonical hardware NaN"
     86 #  endif
     87 
     88  (void)hardwareNaNBits;
     89 #elif defined(JS_RUNTIME_CANONICAL_NAN)
     90  // Determine canonical NaN at startup. It must still match the ValueIsDouble
     91  // requirements.
     92  MOZ_RELEASE_ASSERT(JS::detail::ValueIsDouble(hardwareNaNBits));
     93  JS::detail::CanonicalizedNaNBits = hardwareNaNBits;
     94 #else
     95  // Assert that the NaN generated by hardware operations is
     96  // compatible with the canonical NaN we use for JS::Value. This is
     97  // true for all of our supported platforms, but not for SPARC.
     98  MOZ_RELEASE_ASSERT(hardwareNaNBits == JS::detail::CanonicalizedNaNBits,
     99                     "Unexpected default hardware NaN value");
    100 #endif
    101 }
    102 
    103 #define RETURN_IF_FAIL(code)           \
    104  do {                                 \
    105    if (!code) return #code " failed"; \
    106  } while (0)
    107 
    108 extern "C" void install_rust_hooks();
    109 
    110 JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic(
    111    bool isDebugBuild, FrontendOnly frontendOnly /* = FrontendOnly::No */) {
    112  // Verify that our DEBUG setting matches the caller's.
    113 #ifdef DEBUG
    114  MOZ_RELEASE_ASSERT(isDebugBuild);
    115 #else
    116  MOZ_RELEASE_ASSERT(!isDebugBuild);
    117 #endif
    118 
    119  MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
    120             "must call JS_Init once before any JSAPI operation except "
    121             "JS_SetICUMemoryFunctions");
    122  MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
    123             "how do we have live runtimes before JS_Init?");
    124 
    125  libraryInitState = InitState::Initializing;
    126 
    127 #ifdef JS_STANDALONE
    128  // The rust hooks are initialized by Gecko on non-standalone builds.
    129  install_rust_hooks();
    130 #endif
    131 
    132  if (frontendOnly == FrontendOnly::No) {
    133    // The first invocation of `ProcessCreation` creates a temporary thread
    134    // and crashes if that fails, i.e. because we're out of memory. To prevent
    135    // that from happening at some later time, get it out of the way during
    136    // startup.
    137    mozilla::TimeStamp::ProcessCreation();
    138  }
    139 
    140 #ifdef DEBUG
    141  CheckMessageParameterCounts();
    142 #endif
    143 
    144  SetupCanonicalNaN();
    145 
    146  if (frontendOnly == FrontendOnly::No) {
    147    RETURN_IF_FAIL(js::TlsContext.init());
    148  }
    149 
    150 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
    151  RETURN_IF_FAIL(js::oom::InitThreadType());
    152 #endif
    153 
    154 #if defined(FUZZING)
    155  js::oom::InitLargeAllocLimit();
    156 #endif
    157 
    158  js::InitMallocAllocator();
    159 
    160  RETURN_IF_FAIL(js::Mutex::Init());
    161 
    162  js::gc::InitMemorySubsystem();  // Ensure gc::SystemPageSize() works.
    163 
    164  RETURN_IF_FAIL(js::wasm::Init());
    165 
    166  js::coverage::InitLCov();
    167 
    168  if (frontendOnly == FrontendOnly::No) {
    169    RETURN_IF_FAIL(js::jit::InitializeJit());
    170  }
    171 
    172  RETURN_IF_FAIL(js::InitDateTimeState());
    173 
    174  if (frontendOnly == FrontendOnly::No) {
    175 #ifdef MOZ_VTUNE
    176    RETURN_IF_FAIL(js::vtune::Initialize());
    177 #endif
    178  }
    179 
    180 #if JS_HAS_INTL_API
    181  if (mozilla::intl::ICU4CLibrary::Initialize().isErr()) {
    182    return "ICU4CLibrary::Initialize() failed";
    183  }
    184 #endif  // JS_HAS_INTL_API
    185 
    186  if (frontendOnly == FrontendOnly::No) {
    187    RETURN_IF_FAIL(js::CreateHelperThreadsState());
    188    RETURN_IF_FAIL(FutexThread::initialize());
    189    RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
    190    RETURN_IF_FAIL(js::InitTestingFunctions());
    191  }
    192 
    193  RETURN_IF_FAIL(js::SharedImmutableStringsCache::initSingleton());
    194  RETURN_IF_FAIL(js::frontend::WellKnownParserAtoms::initSingleton());
    195 
    196  if (frontendOnly == FrontendOnly::No) {
    197 #ifdef JS_SIMULATOR
    198    RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
    199 #endif
    200 
    201 #ifndef JS_CODEGEN_NONE
    202    // This is forced by InitializeJit.
    203    MOZ_ASSERT(js::jit::CPUFlagsHaveBeenComputed());
    204 #endif
    205  }
    206 
    207  libraryInitState = InitState::Running;
    208  return nullptr;
    209 }
    210 
    211 #undef RETURN_IF_FAIL
    212 
    213 JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx, SelfHostedCache cache,
    214                                          SelfHostedWriter writer) {
    215  MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
    216                     "JS::InitSelfHostedCode() called more than once");
    217 
    218  js::AutoNoteSingleThreadedRegion anstr;
    219 
    220  JSRuntime* rt = cx->runtime();
    221 
    222  if (!rt->initSelfHostingStencil(cx, cache, writer)) {
    223    return false;
    224  }
    225 
    226  if (!rt->initializeAtoms(cx)) {
    227    return false;
    228  }
    229 
    230  if (!rt->initSelfHostingFromStencil(cx)) {
    231    return false;
    232  }
    233 
    234  if (js::jit::HasJitBackend()) {
    235    if (!rt->createJitRuntime(cx)) {
    236      return false;
    237    }
    238  }
    239 
    240  return true;
    241 }
    242 
    243 static void ShutdownImpl(JS::detail::FrontendOnly frontendOnly) {
    244  using FrontendOnly = JS::detail::FrontendOnly;
    245 
    246  MOZ_ASSERT(
    247      libraryInitState == InitState::Running,
    248      "JS_ShutDown must only be called after JS_Init and can't race with it");
    249 #ifdef DEBUG
    250  if (JSRuntime::hasLiveRuntimes()) {
    251    // Gecko is too buggy to assert this just yet.
    252    fprintf(stderr,
    253            "WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime "
    254            "and everything alive inside it, that is) AT JS_ShutDown "
    255            "TIME.  FIX THIS!\n");
    256  }
    257 #endif
    258 
    259  js::frontend::WellKnownParserAtoms::freeSingleton();
    260  js::SharedImmutableStringsCache::freeSingleton();
    261 
    262  if (frontendOnly == FrontendOnly::No) {
    263    FutexThread::destroy();
    264 
    265    js::DestroyHelperThreadsState();
    266 
    267 #ifdef JS_SIMULATOR
    268    js::jit::SimulatorProcess::destroy();
    269 #endif
    270  }
    271 
    272  js::wasm::ShutDown();
    273 
    274 #if JS_HAS_INTL_API
    275  mozilla::intl::ICU4CLibrary::Cleanup();
    276 #endif  // JS_HAS_INTL_API
    277 
    278  if (frontendOnly == FrontendOnly::No) {
    279 #ifdef MOZ_VTUNE
    280    js::vtune::Shutdown();
    281 #endif  // MOZ_VTUNE
    282  }
    283 
    284  js::FinishDateTimeState();
    285 
    286  if (frontendOnly == FrontendOnly::No) {
    287    js::jit::ShutdownJit();
    288  }
    289 
    290  MOZ_ASSERT_IF(!JSRuntime::hasLiveRuntimes(), !js::WasmReservedBytes());
    291 
    292  js::ShutDownMallocAllocator();
    293 
    294  if (!JSRuntime::hasLiveRuntimes()) {
    295    js::gc::CheckMemorySubsystemOnShutDown();
    296  }
    297 
    298  libraryInitState = InitState::ShutDown;
    299 }
    300 
    301 JS_PUBLIC_API void JS_ShutDown(void) {
    302  ShutdownImpl(JS::detail::FrontendOnly::No);
    303 }
    304 
    305 JS_PUBLIC_API void JS_FrontendOnlyShutDown(void) {
    306  ShutdownImpl(JS::detail::FrontendOnly::Yes);
    307 }
    308 
    309 JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
    310                                            JS_ICUReallocFn reallocFn,
    311                                            JS_ICUFreeFn freeFn) {
    312  MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
    313             "must call JS_SetICUMemoryFunctions before any other JSAPI "
    314             "operation (including JS_Init)");
    315 
    316 #if JS_HAS_INTL_API
    317  return mozilla::intl::ICU4CLibrary::SetMemoryFunctions(
    318             {allocFn, reallocFn, freeFn})
    319      .isOk();
    320 #else
    321  return true;
    322 #endif
    323 }
    324 
    325 #if defined(ENABLE_WASM_SIMD) && \
    326    (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86))
    327 void JS::SetAVXEnabled(bool enabled) {
    328  if (enabled) {
    329    js::jit::CPUInfo::SetAVXEnabled();
    330  } else {
    331    js::jit::CPUInfo::SetAVXDisabled();
    332  }
    333 }
    334 #endif
    335 
    336 JS_PUBLIC_API void JS::DisableJitBackend() {
    337  MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
    338             "DisableJitBackend must be called before JS_Init");
    339  MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
    340             "DisableJitBackend must be called before creating a JSContext");
    341  js::jit::JitOptions.disableJitBackend = true;
    342 }