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