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