mozjs_sys 0.67.1

System crate for the Mozilla SpiderMonkey JavaScript engine.
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/* -*- 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/. */

#ifndef jit_JitRealm_h
#define jit_JitRealm_h

#include "mozilla/Array.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryReporting.h"

#include <utility>

#include "builtin/TypedObject.h"
#include "jit/CompileInfo.h"
#include "jit/ICStubSpace.h"
#include "jit/IonCode.h"
#include "jit/IonControlFlow.h"
#include "jit/JitFrames.h"
#include "jit/shared/Assembler-shared.h"
#include "js/GCHashTable.h"
#include "js/Value.h"
#include "vm/Stack.h"

namespace js {
namespace jit {

class FrameSizeClass;
struct VMFunctionData;

enum class TailCallVMFunctionId;
enum class VMFunctionId;

struct EnterJitData {
  explicit EnterJitData(JSContext* cx)
      : jitcode(nullptr),
        osrFrame(nullptr),
        calleeToken(nullptr),
        maxArgv(nullptr),
        maxArgc(0),
        numActualArgs(0),
        osrNumStackValues(0),
        envChain(cx),
        result(cx),
        constructing(false) {}

  uint8_t* jitcode;
  InterpreterFrame* osrFrame;

  void* calleeToken;

  Value* maxArgv;
  unsigned maxArgc;
  unsigned numActualArgs;
  unsigned osrNumStackValues;

  RootedObject envChain;
  RootedValue result;

  bool constructing;
};

typedef void (*EnterJitCode)(void* code, unsigned argc, Value* argv,
                             InterpreterFrame* fp, CalleeToken calleeToken,
                             JSObject* envChain, size_t numStackValues,
                             Value* vp);

class JitcodeGlobalTable;

class JitRuntime {
 private:
  friend class JitRealm;

  // Executable allocator for all code except wasm code.
  MainThreadData<ExecutableAllocator> execAlloc_;

  MainThreadData<uint64_t> nextCompilationId_;

  // Shared exception-handler tail.
  WriteOnceData<uint32_t> exceptionTailOffset_;

  // Shared post-bailout-handler tail.
  WriteOnceData<uint32_t> bailoutTailOffset_;

  // Shared profiler exit frame tail.
  WriteOnceData<uint32_t> profilerExitFrameTailOffset_;

  // Trampoline for entering JIT code.
  WriteOnceData<uint32_t> enterJITOffset_;

  // Vector mapping frame class sizes to bailout tables.
  struct BailoutTable {
    uint32_t startOffset;
    uint32_t size;
    BailoutTable(uint32_t startOffset, uint32_t size)
        : startOffset(startOffset), size(size) {}
  };
  typedef Vector<BailoutTable, 4, SystemAllocPolicy> BailoutTableVector;
  WriteOnceData<BailoutTableVector> bailoutTables_;

  // Generic bailout table; used if the bailout table overflows.
  WriteOnceData<uint32_t> bailoutHandlerOffset_;

  // Argument-rectifying thunk, in the case of insufficient arguments passed
  // to a function call site.
  WriteOnceData<uint32_t> argumentsRectifierOffset_;
  WriteOnceData<uint32_t> argumentsRectifierReturnOffset_;

  // Thunk that invalides an (Ion compiled) caller on the Ion stack.
  WriteOnceData<uint32_t> invalidatorOffset_;

  // Thunk that calls the GC pre barrier.
  WriteOnceData<uint32_t> valuePreBarrierOffset_;
  WriteOnceData<uint32_t> stringPreBarrierOffset_;
  WriteOnceData<uint32_t> objectPreBarrierOffset_;
  WriteOnceData<uint32_t> shapePreBarrierOffset_;
  WriteOnceData<uint32_t> objectGroupPreBarrierOffset_;

  // Thunk to call malloc/free.
  WriteOnceData<uint32_t> mallocStubOffset_;
  WriteOnceData<uint32_t> freeStubOffset_;

  // Thunk called to finish compilation of an IonScript.
  WriteOnceData<uint32_t> lazyLinkStubOffset_;

  // Thunk to enter the interpreter from JIT code.
  WriteOnceData<uint32_t> interpreterStubOffset_;

  // Thunk to convert the value in R0 to int32 if it's a double.
  // Note: this stub treats -0 as +0 and may clobber R1.scratchReg().
  WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_;

  // Thunk used by the debugger for breakpoint and step mode.
  WriteOnceData<JitCode*> debugTrapHandler_;

  // Thunk used to fix up on-stack recompile of baseline scripts.
  WriteOnceData<JitCode*> baselineDebugModeOSRHandler_;
  WriteOnceData<void*> baselineDebugModeOSRHandlerNoFrameRegPopAddr_;

  // Code for trampolines and VMFunction wrappers.
  WriteOnceData<JitCode*> trampolineCode_;

  // Map VMFunction addresses to the offset of the wrapper in
  // trampolineCode_.
  using VMWrapperMap = HashMap<const VMFunction*, uint32_t, VMFunction>;
  WriteOnceData<VMWrapperMap*> functionWrappers_;

  // Maps VMFunctionId to the offset of the wrapper code in trampolineCode_.
  using VMWrapperOffsets = Vector<uint32_t, 0, SystemAllocPolicy>;
  VMWrapperOffsets functionWrapperOffsets_;

  // Maps TailCallVMFunctionId to the offset of the wrapper code in
  // trampolineCode_.
  VMWrapperOffsets tailCallFunctionWrapperOffsets_;

  // Global table of jitcode native address => bytecode address mappings.
  UnprotectedData<JitcodeGlobalTable*> jitcodeGlobalTable_;

#ifdef DEBUG
  // The number of possible bailing places encounters before forcefully bailing
  // in that place. Zero means inactive.
  MainThreadData<uint32_t> ionBailAfter_;
#endif

  // Number of Ion compilations which were finished off thread and are
  // waiting to be lazily linked. This is only set while holding the helper
  // thread state lock, but may be read from at other times.
  typedef mozilla::Atomic<size_t, mozilla::SequentiallyConsistent,
                          mozilla::recordreplay::Behavior::DontPreserve>
      NumFinishedBuildersType;
  NumFinishedBuildersType numFinishedBuilders_;

  // List of Ion compilation waiting to get linked.
  using IonBuilderList = mozilla::LinkedList<js::jit::IonBuilder>;
  MainThreadData<IonBuilderList> ionLazyLinkList_;
  MainThreadData<size_t> ionLazyLinkListSize_;

  // Counter used to help dismbiguate stubs in CacheIR
  MainThreadData<uint64_t> disambiguationId_;

 private:
  void generateLazyLinkStub(MacroAssembler& masm);
  void generateInterpreterStub(MacroAssembler& masm);
  void generateDoubleToInt32ValueStub(MacroAssembler& masm);
  void generateProfilerExitFrameTailStub(MacroAssembler& masm,
                                         Label* profilerExitTail);
  void generateExceptionTailStub(MacroAssembler& masm, void* handler,
                                 Label* profilerExitTail);
  void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
  void generateEnterJIT(JSContext* cx, MacroAssembler& masm);
  void generateArgumentsRectifier(MacroAssembler& masm);
  BailoutTable generateBailoutTable(MacroAssembler& masm, Label* bailoutTail,
                                    uint32_t frameClass);
  void generateBailoutHandler(MacroAssembler& masm, Label* bailoutTail);
  void generateInvalidator(MacroAssembler& masm, Label* bailoutTail);
  uint32_t generatePreBarrier(JSContext* cx, MacroAssembler& masm,
                              MIRType type);
  void generateMallocStub(MacroAssembler& masm);
  void generateFreeStub(MacroAssembler& masm);
  JitCode* generateDebugTrapHandler(JSContext* cx);
  JitCode* generateBaselineDebugModeOSRHandler(
      JSContext* cx, uint32_t* noFrameRegPopOffsetOut);

  bool generateVMWrapper(JSContext* cx, MacroAssembler& masm,
                         const VMFunctionData& f, void* nativeFun,
                         uint32_t* wrapperOffset);

  template <typename IdT>
  bool generateVMWrappers(JSContext* cx, MacroAssembler& masm,
                          VMWrapperOffsets& offsets);
  bool generateVMWrappers(JSContext* cx, MacroAssembler& masm);

  bool generateTLEventVM(MacroAssembler& masm, const VMFunctionData& f,
                         bool enter);

  inline bool generateTLEnterVM(MacroAssembler& masm, const VMFunctionData& f) {
    return generateTLEventVM(masm, f, /* enter = */ true);
  }
  inline bool generateTLExitVM(MacroAssembler& masm, const VMFunctionData& f) {
    return generateTLEventVM(masm, f, /* enter = */ false);
  }

  uint32_t startTrampolineCode(MacroAssembler& masm);

  TrampolinePtr trampolineCode(uint32_t offset) const {
    MOZ_ASSERT(offset > 0);
    MOZ_ASSERT(offset < trampolineCode_->instructionsSize());
    return TrampolinePtr(trampolineCode_->raw() + offset);
  }

 public:
  JitRuntime();
  ~JitRuntime();
  MOZ_MUST_USE bool initialize(JSContext* cx);

  static void Trace(JSTracer* trc, const js::AutoAccessAtomsZone& access);
  static void TraceJitcodeGlobalTableForMinorGC(JSTracer* trc);
  static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker);
  static void SweepJitcodeGlobalTable(JSRuntime* rt);

  ExecutableAllocator& execAlloc() { return execAlloc_.ref(); }

  IonCompilationId nextCompilationId() {
    return IonCompilationId(nextCompilationId_++);
  }

  TrampolinePtr getVMWrapper(const VMFunction& f) const;

  TrampolinePtr getVMWrapper(VMFunctionId funId) const {
    MOZ_ASSERT(trampolineCode_);
    return trampolineCode(functionWrapperOffsets_[size_t(funId)]);
  }
  TrampolinePtr getVMWrapper(TailCallVMFunctionId funId) const {
    MOZ_ASSERT(trampolineCode_);
    return trampolineCode(tailCallFunctionWrapperOffsets_[size_t(funId)]);
  }

  JitCode* debugTrapHandler(JSContext* cx);
  JitCode* getBaselineDebugModeOSRHandler(JSContext* cx);
  void* getBaselineDebugModeOSRHandlerAddress(JSContext* cx, bool popFrameReg);

  TrampolinePtr getGenericBailoutHandler() const {
    return trampolineCode(bailoutHandlerOffset_);
  }

  TrampolinePtr getExceptionTail() const {
    return trampolineCode(exceptionTailOffset_);
  }

  TrampolinePtr getBailoutTail() const {
    return trampolineCode(bailoutTailOffset_);
  }

  TrampolinePtr getProfilerExitFrameTail() const {
    return trampolineCode(profilerExitFrameTailOffset_);
  }

  TrampolinePtr getBailoutTable(const FrameSizeClass& frameClass) const;
  uint32_t getBailoutTableSize(const FrameSizeClass& frameClass) const;

  TrampolinePtr getArgumentsRectifier() const {
    return trampolineCode(argumentsRectifierOffset_);
  }

  TrampolinePtr getArgumentsRectifierReturnAddr() const {
    return trampolineCode(argumentsRectifierReturnOffset_);
  }

  TrampolinePtr getInvalidationThunk() const {
    return trampolineCode(invalidatorOffset_);
  }

  EnterJitCode enterJit() const {
    return JS_DATA_TO_FUNC_PTR(EnterJitCode,
                               trampolineCode(enterJITOffset_).value);
  }

  TrampolinePtr preBarrier(MIRType type) const {
    switch (type) {
      case MIRType::Value:
        return trampolineCode(valuePreBarrierOffset_);
      case MIRType::String:
        return trampolineCode(stringPreBarrierOffset_);
      case MIRType::Object:
        return trampolineCode(objectPreBarrierOffset_);
      case MIRType::Shape:
        return trampolineCode(shapePreBarrierOffset_);
      case MIRType::ObjectGroup:
        return trampolineCode(objectGroupPreBarrierOffset_);
      default:
        MOZ_CRASH();
    }
  }

  TrampolinePtr mallocStub() const { return trampolineCode(mallocStubOffset_); }

  TrampolinePtr freeStub() const { return trampolineCode(freeStubOffset_); }

  TrampolinePtr lazyLinkStub() const {
    return trampolineCode(lazyLinkStubOffset_);
  }
  TrampolinePtr interpreterStub() const {
    return trampolineCode(interpreterStubOffset_);
  }

  TrampolinePtr getDoubleToInt32ValueStub() const {
    return trampolineCode(doubleToInt32ValueStubOffset_);
  }

  bool hasJitcodeGlobalTable() const { return jitcodeGlobalTable_ != nullptr; }

  JitcodeGlobalTable* getJitcodeGlobalTable() {
    MOZ_ASSERT(hasJitcodeGlobalTable());
    return jitcodeGlobalTable_;
  }

  bool isProfilerInstrumentationEnabled(JSRuntime* rt) {
    return rt->geckoProfiler().enabled();
  }

  bool isOptimizationTrackingEnabled(JSRuntime* rt) {
    return isProfilerInstrumentationEnabled(rt);
  }

#ifdef DEBUG
  void* addressOfIonBailAfter() { return &ionBailAfter_; }

  // Set after how many bailing places we should forcefully bail.
  // Zero disables this feature.
  void setIonBailAfter(uint32_t after) { ionBailAfter_ = after; }
#endif

  size_t numFinishedBuilders() const { return numFinishedBuilders_; }
  NumFinishedBuildersType& numFinishedBuildersRef(
      const AutoLockHelperThreadState& locked) {
    return numFinishedBuilders_;
  }

  IonBuilderList& ionLazyLinkList(JSRuntime* rt);

  size_t ionLazyLinkListSize() const { return ionLazyLinkListSize_; }

  void ionLazyLinkListRemove(JSRuntime* rt, js::jit::IonBuilder* builder);
  void ionLazyLinkListAdd(JSRuntime* rt, js::jit::IonBuilder* builder);

  uint64_t nextDisambiguationId() { return disambiguationId_++; }
};

enum class CacheKind : uint8_t;
class CacheIRStubInfo;

enum class ICStubEngine : uint8_t {
  // Baseline IC, see BaselineIC.h.
  Baseline = 0,

  // Ion IC, see IonIC.h.
  IonIC
};

struct CacheIRStubKey : public DefaultHasher<CacheIRStubKey> {
  struct Lookup {
    CacheKind kind;
    ICStubEngine engine;
    const uint8_t* code;
    uint32_t length;

    Lookup(CacheKind kind, ICStubEngine engine, const uint8_t* code,
           uint32_t length)
        : kind(kind), engine(engine), code(code), length(length) {}
  };

  static HashNumber hash(const Lookup& l);
  static bool match(const CacheIRStubKey& entry, const Lookup& l);

  UniquePtr<CacheIRStubInfo, JS::FreePolicy> stubInfo;

  explicit CacheIRStubKey(CacheIRStubInfo* info) : stubInfo(info) {}
  CacheIRStubKey(CacheIRStubKey&& other)
      : stubInfo(std::move(other.stubInfo)) {}

  void operator=(CacheIRStubKey&& other) {
    stubInfo = std::move(other.stubInfo);
  }
};

template <typename Key>
struct IcStubCodeMapGCPolicy {
  static bool needsSweep(Key*, ReadBarrieredJitCode* value) {
    return IsAboutToBeFinalized(value);
  }
};

class JitZone {
  // Allocated space for optimized baseline stubs.
  OptimizedICStubSpace optimizedStubSpace_;
  // Allocated space for cached cfg.
  CFGSpace cfgSpace_;

  // Set of CacheIRStubInfo instances used by Ion stubs in this Zone.
  using IonCacheIRStubInfoSet =
      HashSet<CacheIRStubKey, CacheIRStubKey, SystemAllocPolicy>;
  IonCacheIRStubInfoSet ionCacheIRStubInfoSet_;

  // Map CacheIRStubKey to shared JitCode objects.
  using BaselineCacheIRStubCodeMap =
      GCHashMap<CacheIRStubKey, ReadBarrieredJitCode, CacheIRStubKey,
                SystemAllocPolicy, IcStubCodeMapGCPolicy<CacheIRStubKey>>;
  BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_;

 public:
  void sweep();

  void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                              size_t* jitZone, size_t* baselineStubsOptimized,
                              size_t* cachedCFG) const;

  OptimizedICStubSpace* optimizedStubSpace() { return &optimizedStubSpace_; }
  CFGSpace* cfgSpace() { return &cfgSpace_; }

  JitCode* getBaselineCacheIRStubCode(const CacheIRStubKey::Lookup& key,
                                      CacheIRStubInfo** stubInfo) {
    auto p = baselineCacheIRStubCodes_.lookup(key);
    if (p) {
      *stubInfo = p->key().stubInfo.get();
      return p->value();
    }
    *stubInfo = nullptr;
    return nullptr;
  }
  MOZ_MUST_USE bool putBaselineCacheIRStubCode(
      const CacheIRStubKey::Lookup& lookup, CacheIRStubKey& key,
      JitCode* stubCode) {
    auto p = baselineCacheIRStubCodes_.lookupForAdd(lookup);
    MOZ_ASSERT(!p);
    return baselineCacheIRStubCodes_.add(p, std::move(key), stubCode);
  }

  CacheIRStubInfo* getIonCacheIRStubInfo(const CacheIRStubKey::Lookup& key) {
    IonCacheIRStubInfoSet::Ptr p = ionCacheIRStubInfoSet_.lookup(key);
    return p ? p->stubInfo.get() : nullptr;
  }
  MOZ_MUST_USE bool putIonCacheIRStubInfo(const CacheIRStubKey::Lookup& lookup,
                                          CacheIRStubKey& key) {
    IonCacheIRStubInfoSet::AddPtr p =
        ionCacheIRStubInfoSet_.lookupForAdd(lookup);
    MOZ_ASSERT(!p);
    return ionCacheIRStubInfoSet_.add(p, std::move(key));
  }
  void purgeIonCacheIRStubInfo() { ionCacheIRStubInfoSet_.clearAndCompact(); }
};

enum class BailoutReturnStub {
  GetProp,
  GetPropSuper,
  SetProp,
  GetElem,
  GetElemSuper,
  Call,
  New,
  Count
};

class JitRealm {
  friend class JitActivation;

  // Map ICStub keys to ICStub shared code objects.
  using ICStubCodeMap =
      GCHashMap<uint32_t, ReadBarrieredJitCode, DefaultHasher<uint32_t>,
                ZoneAllocPolicy, IcStubCodeMapGCPolicy<uint32_t>>;
  ICStubCodeMap* stubCodes_;

  // Keep track of offset into various baseline stubs' code at return
  // point from called script.
  struct BailoutReturnStubInfo {
    void* addr;
    uint32_t key;

    BailoutReturnStubInfo() : addr(nullptr), key(0) {}
    BailoutReturnStubInfo(void* addr_, uint32_t key_)
        : addr(addr_), key(key_) {}
  };
  mozilla::EnumeratedArray<BailoutReturnStub, BailoutReturnStub::Count,
                           BailoutReturnStubInfo>
      bailoutReturnStubInfo_;

  // The JitRealm stores stubs to concatenate strings inline and perform RegExp
  // calls inline. These bake in zone and realm specific pointers and can't be
  // stored in JitRuntime. They also are dependent on the value of
  // 'stringsCanBeInNursery' and must be flushed when its value changes.
  //
  // These are weak pointers, but they can by accessed during off-thread Ion
  // compilation and therefore can't use the usual read barrier. Instead, we
  // record which stubs have been read and perform the appropriate barriers in
  // CodeGenerator::link().

  enum StubIndex : uint32_t {
    StringConcat = 0,
    RegExpMatcher,
    RegExpSearcher,
    RegExpTester,
    Count
  };

  mozilla::EnumeratedArray<StubIndex, StubIndex::Count, ReadBarrieredJitCode>
      stubs_;

  bool stringsCanBeInNursery;

  JitCode* generateStringConcatStub(JSContext* cx);
  JitCode* generateRegExpMatcherStub(JSContext* cx);
  JitCode* generateRegExpSearcherStub(JSContext* cx);
  JitCode* generateRegExpTesterStub(JSContext* cx);

  JitCode* getStubNoBarrier(StubIndex stub,
                            uint32_t* requiredBarriersOut) const {
    MOZ_ASSERT(CurrentThreadIsIonCompiling());
    *requiredBarriersOut |= 1 << uint32_t(stub);
    return stubs_[stub].unbarrieredGet();
  }

 public:
  JitCode* getStubCode(uint32_t key) {
    ICStubCodeMap::Ptr p = stubCodes_->lookup(key);
    if (p) {
      return p->value();
    }
    return nullptr;
  }
  MOZ_MUST_USE bool putStubCode(JSContext* cx, uint32_t key,
                                Handle<JitCode*> stubCode) {
    MOZ_ASSERT(stubCode);
    if (!stubCodes_->putNew(key, stubCode.get())) {
      ReportOutOfMemory(cx);
      return false;
    }
    return true;
  }
  void initBailoutReturnAddr(void* addr, uint32_t key, BailoutReturnStub kind) {
    MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr == nullptr);
    bailoutReturnStubInfo_[kind] = BailoutReturnStubInfo{addr, key};
  }
  void* bailoutReturnAddr(BailoutReturnStub kind) {
    MOZ_ASSERT(bailoutReturnStubInfo_[kind].addr);
    return bailoutReturnStubInfo_[kind].addr;
  }

  JitRealm();
  ~JitRealm();

  MOZ_MUST_USE bool initialize(JSContext* cx, bool zoneHasNurseryStrings);

  // Initialize code stubs only used by Ion, not Baseline.
  MOZ_MUST_USE bool ensureIonStubsExist(JSContext* cx) {
    if (stubs_[StringConcat]) {
      return true;
    }
    stubs_[StringConcat] = generateStringConcatStub(cx);
    return stubs_[StringConcat];
  }

  void sweep(JS::Realm* realm);

  void discardStubs() {
    for (ReadBarrieredJitCode& stubRef : stubs_) {
      stubRef = nullptr;
    }
  }

  bool hasStubs() const {
    for (const ReadBarrieredJitCode& stubRef : stubs_) {
      if (stubRef) {
        return true;
      }
    }
    return false;
  }

  void setStringsCanBeInNursery(bool allow) {
    MOZ_ASSERT(!hasStubs());
    stringsCanBeInNursery = allow;
  }

  JitCode* stringConcatStubNoBarrier(uint32_t* requiredBarriersOut) const {
    return getStubNoBarrier(StringConcat, requiredBarriersOut);
  }

  JitCode* regExpMatcherStubNoBarrier(uint32_t* requiredBarriersOut) const {
    return getStubNoBarrier(RegExpMatcher, requiredBarriersOut);
  }

  MOZ_MUST_USE bool ensureRegExpMatcherStubExists(JSContext* cx) {
    if (stubs_[RegExpMatcher]) {
      return true;
    }
    stubs_[RegExpMatcher] = generateRegExpMatcherStub(cx);
    return stubs_[RegExpMatcher];
  }

  JitCode* regExpSearcherStubNoBarrier(uint32_t* requiredBarriersOut) const {
    return getStubNoBarrier(RegExpSearcher, requiredBarriersOut);
  }

  MOZ_MUST_USE bool ensureRegExpSearcherStubExists(JSContext* cx) {
    if (stubs_[RegExpSearcher]) {
      return true;
    }
    stubs_[RegExpSearcher] = generateRegExpSearcherStub(cx);
    return stubs_[RegExpSearcher];
  }

  JitCode* regExpTesterStubNoBarrier(uint32_t* requiredBarriersOut) const {
    return getStubNoBarrier(RegExpTester, requiredBarriersOut);
  }

  MOZ_MUST_USE bool ensureRegExpTesterStubExists(JSContext* cx) {
    if (stubs_[RegExpTester]) {
      return true;
    }
    stubs_[RegExpTester] = generateRegExpTesterStub(cx);
    return stubs_[RegExpTester];
  }

  // Perform the necessary read barriers on stubs described by the bitmasks
  // passed in. This function can only be called from the main thread.
  //
  // The stub pointers must still be valid by the time these methods are
  // called. This is arranged by cancelling off-thread Ion compilation at the
  // start of GC and at the start of sweeping.
  void performStubReadBarriers(uint32_t stubsToBarrier) const;

  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};

// Called from Zone::discardJitCode().
void InvalidateAll(FreeOp* fop, JS::Zone* zone);
void FinishInvalidation(FreeOp* fop, JSScript* script);

// This class ensures JIT code is executable on its destruction. Creators
// must call makeWritable(), and not attempt to write to the buffer if it fails.
//
// AutoWritableJitCodeFallible may only fail to make code writable; it cannot
// fail to make JIT code executable (because the creating code has no chance to
// recover from a failed destructor).
class MOZ_RAII AutoWritableJitCodeFallible {
  JSRuntime* rt_;
  void* addr_;
  size_t size_;

 public:
  AutoWritableJitCodeFallible(JSRuntime* rt, void* addr, size_t size)
      : rt_(rt), addr_(addr), size_(size) {
    rt_->toggleAutoWritableJitCodeActive(true);
  }

  AutoWritableJitCodeFallible(void* addr, size_t size)
      : AutoWritableJitCodeFallible(TlsContext.get()->runtime(), addr, size) {}

  explicit AutoWritableJitCodeFallible(JitCode* code)
      : AutoWritableJitCodeFallible(code->runtimeFromMainThread(), code->raw(),
                                    code->bufferSize()) {}

  MOZ_MUST_USE bool makeWritable() {
    return ExecutableAllocator::makeWritable(addr_, size_);
  }

  ~AutoWritableJitCodeFallible() {
    if (!ExecutableAllocator::makeExecutable(addr_, size_)) {
      MOZ_CRASH();
    }
    rt_->toggleAutoWritableJitCodeActive(false);
  }
};

// Infallible variant of AutoWritableJitCodeFallible, ensures writable during
// construction
class MOZ_RAII AutoWritableJitCode : private AutoWritableJitCodeFallible {
 public:
  AutoWritableJitCode(JSRuntime* rt, void* addr, size_t size)
      : AutoWritableJitCodeFallible(rt, addr, size) {
    MOZ_RELEASE_ASSERT(makeWritable());
  }

  AutoWritableJitCode(void* addr, size_t size)
      : AutoWritableJitCode(TlsContext.get()->runtime(), addr, size) {}

  explicit AutoWritableJitCode(JitCode* code)
      : AutoWritableJitCode(code->runtimeFromMainThread(), code->raw(),
                            code->bufferSize()) {}
};

class MOZ_STACK_CLASS MaybeAutoWritableJitCode {
  mozilla::Maybe<AutoWritableJitCode> awjc_;

 public:
  MaybeAutoWritableJitCode(void* addr, size_t size, ReprotectCode reprotect) {
    if (reprotect) {
      awjc_.emplace(addr, size);
    }
  }
  MaybeAutoWritableJitCode(JitCode* code, ReprotectCode reprotect) {
    if (reprotect) {
      awjc_.emplace(code);
    }
  }
};

}  // namespace jit
}  // namespace js

#endif /* jit_JitRealm_h */