summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Initialization.cpp
blob: a0ff682327659195780d93cd23e1b8dfb5451a50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* -*- 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"
#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

  PRMJ_NowInit();

  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();

  // The only difficult-to-address reason for the restriction that you can't
  // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ
  // NowInit initialization code, which uses PR_CallOnce to initialize the
  // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
  // "reset" the called-once status -- doable, but more trouble than it's
  // worth now.)  Initializing that subsystem from JS_Init eliminates the
  // problem, but initialization can take a comparatively long time (15ms or
  // so), so we really don't want to do it in JS_Init, and we really do want
  // to do it only when PRMJ_Now is eventually called.
  PRMJ_NowShutdown();

#if JS_HAS_INTL_API
  mozilla::intl::ICU4CLibrary::Cleanup();
#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;
}