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 }