summaryrefslogtreecommitdiffstats
path: root/js/public/CompileOptions.h
blob: 01a84b23986eb85c4cd8c18862b00f121b5badb9 (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
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
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

/*
 * Options for JavaScript compilation.
 *
 * In the most common use case, a CompileOptions instance is allocated on the
 * stack, and holds non-owning references to non-POD option values: strings,
 * principals, objects, and so on.  The code declaring the instance guarantees
 * that such option values will outlive the CompileOptions itself: objects are
 * otherwise rooted, principals have had their reference counts bumped, and
 * strings won't be freed until the CompileOptions goes out of scope.  In this
 * situation, CompileOptions only refers to things others own, so it can be
 * lightweight.
 *
 * In some cases, however, we need to hold compilation options with a
 * non-stack-like lifetime.  For example, JS::CompileOffThread needs to save
 * compilation options where a worker thread can find them, then return
 * immediately.  The worker thread will come along at some later point, and use
 * the options.
 *
 * The compiler itself just needs to be able to access a collection of options;
 * it doesn't care who owns them, or what's keeping them alive.  It does its
 * own addrefs/copies/tracing/etc.
 *
 * Furthermore, in some cases compile options are propagated from one entity to
 * another (e.g. from a script to a function defined in that script).  This
 * involves copying over some, but not all, of the options.
 *
 * So we have a class hierarchy that reflects these four use cases:
 *
 * - TransitiveCompileOptions is the common base class, representing options
 *   that should get propagated from a script to functions defined in that
 *   script.  This class is abstract and is only ever used as a subclass.
 *
 * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
 *   representing a full set of compile options.  It can be used by code that
 *   simply needs to access options set elsewhere, like the compiler.  This
 *   class too is abstract and is only ever used as a subclass.
 *
 * - The usual CompileOptions class must be stack-allocated, and holds
 *   non-owning references to the filename, element, and so on. It's derived
 *   from ReadOnlyCompileOptions, so the compiler can use it.
 *
 * - OwningCompileOptions roots / copies / reference counts of all its values,
 *   and unroots / frees / releases them when it is destructed. It too is
 *   derived from ReadOnlyCompileOptions, so the compiler accepts it.
 */

#ifndef js_CompileOptions_h
#define js_CompileOptions_h

#include "mozilla/Assertions.h"       // MOZ_ASSERT
#include "mozilla/MemoryReporting.h"  // mozilla::MallocSizeOf

#include <stddef.h>  // size_t
#include <stdint.h>  // uint8_t, uint32_t

#include "jstypes.h"  // JS_PUBLIC_API

#include "js/CharacterEncoding.h"  // JS::ConstUTF8CharsZ
#include "js/ColumnNumber.h"       // JS::ColumnNumberOneOrigin
#include "js/TypeDecls.h"          // JS::MutableHandle (fwd)

namespace js {
class FrontendContext;
}  // namespace js

namespace JS {
using FrontendContext = js::FrontendContext;

enum class AsmJSOption : uint8_t {
  Enabled,
  DisabledByAsmJSPref,
  DisabledByLinker,
  DisabledByNoWasmCompiler,
  DisabledByDebugger,
};

#define FOREACH_DELAZIFICATION_STRATEGY(_)                                     \
  /* Do not delazify anything eagerly. */                                      \
  _(OnDemandOnly)                                                              \
                                                                               \
  /*                                                                           \
   * Compare the stencil produced by concurrent depth first delazification and \
   * on-demand delazification. Any differences would crash SpiderMonkey with   \
   * an assertion.                                                             \
   */                                                                          \
  _(CheckConcurrentWithOnDemand)                                               \
                                                                               \
  /*                                                                           \
   * Delazifiy functions in a depth first traversal of the functions.          \
   */                                                                          \
  _(ConcurrentDepthFirst)                                                      \
                                                                               \
  /*                                                                           \
   * Delazifiy functions strating with the largest function first.             \
   */                                                                          \
  _(ConcurrentLargeFirst)                                                      \
                                                                               \
  /*                                                                           \
   * Parse everything eagerly, from the first parse.                           \
   *                                                                           \
   * NOTE: Either the Realm configuration or specialized VM operating modes    \
   * may disallow syntax-parse altogether. These conditions are checked in the \
   * CompileOptions constructor.                                               \
   */                                                                          \
  _(ParseEverythingEagerly)

enum class DelazificationOption : uint8_t {
#define _ENUM_ENTRY(Name) Name,
  FOREACH_DELAZIFICATION_STRATEGY(_ENUM_ENTRY)
#undef _ENUM_ENTRY
};

class JS_PUBLIC_API InstantiateOptions;
class JS_PUBLIC_API ReadOnlyDecodeOptions;

// Compilation-specific part of JS::ContextOptions which is supposed to be
// configured by user prefs.
class JS_PUBLIC_API PrefableCompileOptions {
 public:
  PrefableCompileOptions()
      : importAttributes_(false),
        importAttributesAssertSyntax_(false),
        sourcePragmas_(true),
        throwOnAsmJSValidationFailure_(false) {}

  bool importAttributes() const { return importAttributes_; }
  PrefableCompileOptions& setImportAttributes(bool enabled) {
    importAttributes_ = enabled;
    return *this;
  }
  bool importAttributesAssertSyntax() const {
    return importAttributesAssertSyntax_;
  }
  PrefableCompileOptions& setImportAttributesAssertSyntax(bool enabled) {
    importAttributesAssertSyntax_ = enabled;
    return *this;
  }

  // Enable/disable support for parsing '//(#@) source(Mapping)?URL=' pragmas.
  bool sourcePragmas() const { return sourcePragmas_; }
  PrefableCompileOptions& setSourcePragmas(bool flag) {
    sourcePragmas_ = flag;
    return *this;
  }

  AsmJSOption asmJSOption() const { return asmJSOption_; }
  PrefableCompileOptions& setAsmJS(bool flag) {
    asmJSOption_ =
        flag ? AsmJSOption::Enabled : AsmJSOption::DisabledByAsmJSPref;
    return *this;
  }
  PrefableCompileOptions& setAsmJSOption(AsmJSOption option) {
    asmJSOption_ = option;
    return *this;
  }

  bool throwOnAsmJSValidationFailure() const {
    return throwOnAsmJSValidationFailure_;
  }
  PrefableCompileOptions& setThrowOnAsmJSValidationFailure(bool flag) {
    throwOnAsmJSValidationFailure_ = flag;
    return *this;
  }
  PrefableCompileOptions& toggleThrowOnAsmJSValidationFailure() {
    throwOnAsmJSValidationFailure_ = !throwOnAsmJSValidationFailure_;
    return *this;
  }

#if defined(DEBUG) || defined(JS_JITSPEW)
  template <typename Printer>
  void dumpWith(Printer& print) const {
#  define PrintFields_(Name) print(#Name, Name)
    PrintFields_(importAttributes_);
    PrintFields_(importAttributesAssertSyntax_);
    PrintFields_(sourcePragmas_);
    PrintFields_(throwOnAsmJSValidationFailure_);
#  undef PrintFields_

    switch (asmJSOption_) {
      case AsmJSOption::Enabled:
        print("asmJSOption_", "AsmJSOption::Enabled");
        break;
      case AsmJSOption::DisabledByAsmJSPref:
        print("asmJSOption_", "AsmJSOption::DisabledByAsmJSPref");
        break;
      case AsmJSOption::DisabledByLinker:
        print("asmJSOption_", "AsmJSOption::DisabledByLinker");
        break;
      case AsmJSOption::DisabledByNoWasmCompiler:
        print("asmJSOption_", "AsmJSOption::DisabledByNoWasmCompiler");
        break;
      case AsmJSOption::DisabledByDebugger:
        print("asmJSOption_", "AsmJSOption::DisabledByDebugger");
        break;
    }
  }
#endif  // defined(DEBUG) || defined(JS_JITSPEW)

 private:
  // ==== Syntax-related options. ====
  bool importAttributes_ : 1;
  bool importAttributesAssertSyntax_ : 1;

  // The context has specified that source pragmas should be parsed.
  bool sourcePragmas_ : 1;

  // ==== asm.js options. ====
  bool throwOnAsmJSValidationFailure_ : 1;

  AsmJSOption asmJSOption_ = AsmJSOption::DisabledByAsmJSPref;
};

/**
 * The common base class for the CompileOptions hierarchy.
 *
 * Use this in code that needs to propagate compile options from one
 * compilation unit to another.
 */
class JS_PUBLIC_API TransitiveCompileOptions {
  friend class JS_PUBLIC_API ReadOnlyDecodeOptions;

 protected:
  // non-POD options:

  JS::ConstUTF8CharsZ filename_;

  JS::ConstUTF8CharsZ introducerFilename_;

  const char16_t* sourceMapURL_ = nullptr;

  // POD options:
  // WARNING: When adding new fields, don't forget to add them to
  //          copyPODTransitiveOptions.

  /**
   * The Web Platform allows scripts to be loaded from arbitrary cross-origin
   * sources. This allows an attack by which a malicious website loads a
   * sensitive file (say, a bank statement) cross-origin (using the user's
   * cookies), and sniffs the generated syntax errors (via a window.onerror
   * handler) for juicy morsels of its contents.
   *
   * To counter this attack, HTML5 specifies that script errors should be
   * sanitized ("muted") when the script is not same-origin with the global
   * for which it is loaded. Callers should set this flag for cross-origin
   * scripts, and it will be propagated appropriately to child scripts and
   * passed back in JSErrorReports.
   */
  bool mutedErrors_ = false;

  // Either the Realm configuration or the compile request may force
  // strict-mode.
  bool forceStrictMode_ = false;

  // The Realm of this script is configured to use fdlibm math library.
  bool alwaysUseFdlibm_ = false;

  // Flag used to bypass the filename validation callback.
  // See also SetFilenameValidationCallback.
  bool skipFilenameValidation_ = false;

  bool hideScriptFromDebugger_ = false;

  // If set, this script will be hidden from the debugger. The requirement
  // is that once compilation is finished, a call to UpdateDebugMetadata will
  // be made, which will update the SSO with the appropiate debug metadata,
  // and expose the script to the debugger (if hideScriptFromDebugger_ isn't
  // set)
  bool deferDebugMetadata_ = false;

  // Off-thread delazification strategy is used to tell off-thread tasks how the
  // delazification should be performed. Multiple strategies are available in
  // order to test different approaches to the concurrent delazification.
  DelazificationOption eagerDelazificationStrategy_ =
      DelazificationOption::OnDemandOnly;

  friend class JS_PUBLIC_API InstantiateOptions;

 public:
  bool selfHostingMode = false;
  bool discardSource = false;
  bool sourceIsLazy = false;
  bool allowHTMLComments = true;
  bool nonSyntacticScope = false;

  // Top-level await is enabled by default but is not supported for chrome
  // modules loaded with ChromeUtils.importModule.
  bool topLevelAwait = true;

  // When decoding from XDR into a Stencil, directly reference data in the
  // buffer (where possible) instead of copying it. This is an optional
  // performance optimization, and may also reduce memory if the buffer is going
  // remain alive anyways.
  //
  // NOTE: The XDR buffer must remain alive as long as the Stencil does. Special
  //       care must be taken that there are no addition shared references to
  //       the Stencil.
  //
  // NOTE: Instantiated GC things may still outlive the buffer as long as the
  //       Stencil was cleaned up. This is covers a typical case where a decoded
  //       Stencil is instantiated once and then thrown away.
  bool borrowBuffer = false;

  // Similar to `borrowBuffer`, but additionally the JSRuntime may directly
  // reference data in the buffer for JS bytecode. The `borrowBuffer` flag must
  // be set if this is set. This can be a memory optimization in multi-process
  // architectures where a (read-only) XDR buffer is mapped into multiple
  // processes.
  //
  // NOTE: When using this mode, the XDR buffer must live until JS_Shutdown is
  // called. There is currently no mechanism to release the data sooner.
  bool usePinnedBytecode = false;

  // De-optimize ES module's top-level `var`s, in order to define all of them
  // on the ModuleEnvironmentObject, instead of local slot.
  //
  // This is used for providing all global variables in Cu.import return value
  // (see bug 1766761 for more details), and this is temporary solution until
  // ESM-ification finishes.
  //
  // WARNING: This option will eventually be removed.
  bool deoptimizeModuleGlobalVars = false;

  PrefableCompileOptions prefableOptions_;

  /**
   * |introductionType| is a statically allocated C string. See JSScript.h
   * for more information.
   */
  const char* introductionType = nullptr;

  unsigned introductionLineno = 0;
  uint32_t introductionOffset = 0;
  bool hasIntroductionInfo = false;

  // WARNING: When adding new fields, don't forget to add them to
  //          copyPODTransitiveOptions.

 protected:
  TransitiveCompileOptions() = default;

  // Set all POD options (those not requiring reference counts, copies,
  // rooting, or other hand-holding) to their values in |rhs|.
  void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);

  bool isEagerDelazificationEqualTo(DelazificationOption val) const {
    return eagerDelazificationStrategy() == val;
  }

  template <DelazificationOption... Values>
  bool eagerDelazificationIsOneOf() const {
    return (isEagerDelazificationEqualTo(Values) || ...);
  }

 public:
  // Read-only accessors for non-POD options. The proper way to set these
  // depends on the derived type.
  bool mutedErrors() const { return mutedErrors_; }
  bool alwaysUseFdlibm() const { return alwaysUseFdlibm_; }
  bool forceFullParse() const {
    return eagerDelazificationIsOneOf<
        DelazificationOption::ParseEverythingEagerly>();
  }
  bool forceStrictMode() const { return forceStrictMode_; }
  bool consumeDelazificationCache() const {
    return eagerDelazificationIsOneOf<
        DelazificationOption::ConcurrentDepthFirst,
        DelazificationOption::ConcurrentLargeFirst>();
  }
  bool populateDelazificationCache() const {
    return eagerDelazificationIsOneOf<
        DelazificationOption::CheckConcurrentWithOnDemand,
        DelazificationOption::ConcurrentDepthFirst,
        DelazificationOption::ConcurrentLargeFirst>();
  }
  bool waitForDelazificationCache() const {
    return eagerDelazificationIsOneOf<
        DelazificationOption::CheckConcurrentWithOnDemand>();
  }
  bool checkDelazificationCache() const {
    return eagerDelazificationIsOneOf<
        DelazificationOption::CheckConcurrentWithOnDemand>();
  }
  DelazificationOption eagerDelazificationStrategy() const {
    return eagerDelazificationStrategy_;
  }

  bool importAttributes() const { return prefableOptions_.importAttributes(); }
  bool importAttributesAssertSyntax() const {
    return prefableOptions_.importAttributesAssertSyntax();
  }
  bool sourcePragmas() const { return prefableOptions_.sourcePragmas(); }
  bool throwOnAsmJSValidationFailure() const {
    return prefableOptions_.throwOnAsmJSValidationFailure();
  }
  AsmJSOption asmJSOption() const { return prefableOptions_.asmJSOption(); }
  void setAsmJSOption(AsmJSOption option) {
    prefableOptions_.setAsmJSOption(option);
  }

  JS::ConstUTF8CharsZ filename() const { return filename_; }
  JS::ConstUTF8CharsZ introducerFilename() const { return introducerFilename_; }
  const char16_t* sourceMapURL() const { return sourceMapURL_; }

  const PrefableCompileOptions& prefableOptions() const {
    return prefableOptions_;
  }

  TransitiveCompileOptions(const TransitiveCompileOptions&) = delete;
  TransitiveCompileOptions& operator=(const TransitiveCompileOptions&) = delete;

#if defined(DEBUG) || defined(JS_JITSPEW)
  template <typename Printer>
  void dumpWith(Printer& print) const {
#  define PrintFields_(Name) print(#Name, Name)
    PrintFields_(filename_);
    PrintFields_(introducerFilename_);
    PrintFields_(sourceMapURL_);
    PrintFields_(mutedErrors_);
    PrintFields_(forceStrictMode_);
    PrintFields_(alwaysUseFdlibm_);
    PrintFields_(skipFilenameValidation_);
    PrintFields_(hideScriptFromDebugger_);
    PrintFields_(deferDebugMetadata_);
    PrintFields_(eagerDelazificationStrategy_);
    PrintFields_(selfHostingMode);
    PrintFields_(discardSource);
    PrintFields_(sourceIsLazy);
    PrintFields_(allowHTMLComments);
    PrintFields_(nonSyntacticScope);
    PrintFields_(topLevelAwait);
    PrintFields_(borrowBuffer);
    PrintFields_(usePinnedBytecode);
    PrintFields_(deoptimizeModuleGlobalVars);
    PrintFields_(introductionType);
    PrintFields_(introductionLineno);
    PrintFields_(introductionOffset);
    PrintFields_(hasIntroductionInfo);
#  undef PrintFields_

    prefableOptions_.dumpWith(print);
  }
#endif  // defined(DEBUG) || defined(JS_JITSPEW)
};

/**
 * The class representing a full set of compile options.
 *
 * Use this in code that only needs to access compilation options created
 * elsewhere, like the compiler.  Don't instantiate this class (the constructor
 * is protected anyway); instead, create instances only of the derived classes:
 * CompileOptions and OwningCompileOptions.
 */
class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions {
 public:
  // POD options.

  // Line number of the first character (1-origin).
  uint32_t lineno = 1;
  // Column number of the first character in UTF-16 code units.
  JS::ColumnNumberOneOrigin column;

  // The offset within the ScriptSource's full uncompressed text of the first
  // character we're presenting for compilation with this CompileOptions.
  //
  // When we compile a lazy script, we pass the compiler only the substring of
  // the source the lazy function occupies. With chunked decompression, we may
  // not even have the complete uncompressed source present in memory. But parse
  // node positions are offsets within the ScriptSource's full text, and
  // BaseScript indicate their substring of the full source by its starting and
  // ending offsets within the full text. This scriptSourceOffset field lets the
  // frontend convert between these offsets and offsets within the substring
  // presented for compilation.
  unsigned scriptSourceOffset = 0;

  // These only apply to non-function scripts.
  bool isRunOnce = false;
  bool noScriptRval = false;

 protected:
  ReadOnlyCompileOptions() = default;

  void copyPODNonTransitiveOptions(const ReadOnlyCompileOptions& rhs);

  ReadOnlyCompileOptions(const ReadOnlyCompileOptions&) = delete;
  ReadOnlyCompileOptions& operator=(const ReadOnlyCompileOptions&) = delete;

 public:
#if defined(DEBUG) || defined(JS_JITSPEW)
  template <typename Printer>
  void dumpWith(Printer& print) const {
    this->TransitiveCompileOptions::dumpWith(print);
#  define PrintFields_(Name) print(#Name, Name)
    PrintFields_(lineno);
    print("column", column.oneOriginValue());
    PrintFields_(scriptSourceOffset);
    PrintFields_(isRunOnce);
    PrintFields_(noScriptRval);
#  undef PrintFields_
  }
#endif  // defined(DEBUG) || defined(JS_JITSPEW)
};

class JS_PUBLIC_API OwningDecodeOptions;

/**
 * Compilation options, with dynamic lifetime. An instance of this type
 * makes a copy of / holds / roots all dynamically allocated resources
 * (principals; elements; strings) that it refers to. Its destructor frees
 * / drops / unroots them. This is heavier than CompileOptions, below, but
 * unlike CompileOptions, it can outlive any given stack frame.
 *
 * Note that this *roots* any JS values it refers to - they're live
 * unconditionally. Thus, instances of this type can't be owned, directly
 * or indirectly, by a JavaScript object: if any value that this roots ever
 * comes to refer to the object that owns this, then the whole cycle, and
 * anything else it entrains, will never be freed.
 */
class JS_PUBLIC_API OwningCompileOptions final : public ReadOnlyCompileOptions {
 public:
  // A minimal constructor, for use with OwningCompileOptions::copy.
  explicit OwningCompileOptions(JSContext* cx);

  struct ForFrontendContext {};
  explicit OwningCompileOptions(const ForFrontendContext&)
      : ReadOnlyCompileOptions() {}

  ~OwningCompileOptions();

 private:
  template <typename ContextT>
  bool copyImpl(ContextT* cx, const ReadOnlyCompileOptions& rhs);

 public:
  /** Set this to a copy of |rhs|.  Return false on OOM. */
  bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
  bool copy(JS::FrontendContext* fc, const ReadOnlyCompileOptions& rhs);

  void steal(OwningCompileOptions&& rhs);
  void steal(OwningDecodeOptions&& rhs);

  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;

  OwningCompileOptions& setIsRunOnce(bool once) {
    isRunOnce = once;
    return *this;
  }

  OwningCompileOptions& setForceStrictMode() {
    forceStrictMode_ = true;
    return *this;
  }

  OwningCompileOptions& setModule() {
    // ES6 10.2.1 Module code is always strict mode code.
    setForceStrictMode();
    setIsRunOnce(true);
    allowHTMLComments = false;
    return *this;
  }

 private:
  void release();

  OwningCompileOptions(const OwningCompileOptions&) = delete;
  OwningCompileOptions& operator=(const OwningCompileOptions&) = delete;
};

/**
 * Compilation options stored on the stack. An instance of this type
 * simply holds references to dynamically allocated resources (element;
 * filename; source map URL) that are owned by something else. If you
 * create an instance of this type, it's up to you to guarantee that
 * everything you store in it will outlive it.
 */
class MOZ_STACK_CLASS JS_PUBLIC_API CompileOptions final
    : public ReadOnlyCompileOptions {
 public:
  // Default options determined using the JSContext.
  explicit CompileOptions(JSContext* cx);

  // Copy both the transitive and the non-transitive options from another
  // options object.
  CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
      : ReadOnlyCompileOptions() {
    copyPODNonTransitiveOptions(rhs);
    copyPODTransitiveOptions(rhs);

    filename_ = rhs.filename();
    introducerFilename_ = rhs.introducerFilename();
    sourceMapURL_ = rhs.sourceMapURL();
  }

  // Construct a CompileOption in the context where JSContext is not available.
  // prefableOptions should reflect the compilation-specific user prefs.
  explicit CompileOptions(const PrefableCompileOptions& prefableOptions)
      : ReadOnlyCompileOptions() {
    prefableOptions_ = prefableOptions;
  }

  CompileOptions& setFile(const char* f) {
    filename_ = JS::ConstUTF8CharsZ(f);
    return *this;
  }

  CompileOptions& setLine(uint32_t l) {
    lineno = l;
    return *this;
  }

  CompileOptions& setFileAndLine(const char* f, uint32_t l) {
    filename_ = JS::ConstUTF8CharsZ(f);
    lineno = l;
    return *this;
  }

  CompileOptions& setSourceMapURL(const char16_t* s) {
    sourceMapURL_ = s;
    return *this;
  }

  CompileOptions& setMutedErrors(bool mute) {
    mutedErrors_ = mute;
    return *this;
  }

  CompileOptions& setColumn(JS::ColumnNumberOneOrigin c) {
    column = c;
    return *this;
  }

  CompileOptions& setScriptSourceOffset(unsigned o) {
    scriptSourceOffset = o;
    return *this;
  }

  CompileOptions& setIsRunOnce(bool once) {
    isRunOnce = once;
    return *this;
  }

  CompileOptions& setNoScriptRval(bool nsr) {
    noScriptRval = nsr;
    return *this;
  }

  CompileOptions& setSkipFilenameValidation(bool b) {
    skipFilenameValidation_ = b;
    return *this;
  }

  CompileOptions& setSelfHostingMode(bool shm) {
    selfHostingMode = shm;
    return *this;
  }

  CompileOptions& setSourceIsLazy(bool l) {
    sourceIsLazy = l;
    return *this;
  }

  CompileOptions& setNonSyntacticScope(bool n) {
    nonSyntacticScope = n;
    return *this;
  }

  CompileOptions& setIntroductionType(const char* t) {
    introductionType = t;
    return *this;
  }

  CompileOptions& setDeferDebugMetadata(bool v = true) {
    deferDebugMetadata_ = v;
    return *this;
  }

  CompileOptions& setHideScriptFromDebugger(bool v = true) {
    hideScriptFromDebugger_ = v;
    return *this;
  }

  CompileOptions& setIntroductionInfo(const char* introducerFn,
                                      const char* intro, uint32_t line,
                                      uint32_t offset) {
    introducerFilename_ = JS::ConstUTF8CharsZ(introducerFn);
    introductionType = intro;
    introductionLineno = line;
    introductionOffset = offset;
    hasIntroductionInfo = true;
    return *this;
  }

  // Set introduction information according to any currently executing script.
  CompileOptions& setIntroductionInfoToCaller(
      JSContext* cx, const char* introductionType,
      JS::MutableHandle<JSScript*> introductionScript);

  CompileOptions& setDiscardSource() {
    discardSource = true;
    return *this;
  }

  CompileOptions& setForceFullParse() {
    eagerDelazificationStrategy_ = DelazificationOption::ParseEverythingEagerly;
    return *this;
  }

  CompileOptions& setEagerDelazificationStrategy(
      DelazificationOption strategy) {
    // forceFullParse is at the moment considered as a non-overridable strategy.
    MOZ_RELEASE_ASSERT(eagerDelazificationStrategy_ !=
                           DelazificationOption::ParseEverythingEagerly ||
                       strategy ==
                           DelazificationOption::ParseEverythingEagerly);
    eagerDelazificationStrategy_ = strategy;
    return *this;
  }

  CompileOptions& setForceStrictMode() {
    forceStrictMode_ = true;
    return *this;
  }

  CompileOptions& setModule() {
    // ES6 10.2.1 Module code is always strict mode code.
    setForceStrictMode();
    setIsRunOnce(true);
    allowHTMLComments = false;
    return *this;
  }

  CompileOptions(const CompileOptions& rhs) = delete;
  CompileOptions& operator=(const CompileOptions& rhs) = delete;
};

/**
 * Subset of CompileOptions fields used while instantiating Stencils.
 */
class JS_PUBLIC_API InstantiateOptions {
 public:
  bool skipFilenameValidation = false;
  bool hideScriptFromDebugger = false;
  bool deferDebugMetadata = false;

  InstantiateOptions() = default;

  explicit InstantiateOptions(const ReadOnlyCompileOptions& options)
      : skipFilenameValidation(options.skipFilenameValidation_),
        hideScriptFromDebugger(options.hideScriptFromDebugger_),
        deferDebugMetadata(options.deferDebugMetadata_) {}

  void copyTo(CompileOptions& options) const {
    options.skipFilenameValidation_ = skipFilenameValidation;
    options.hideScriptFromDebugger_ = hideScriptFromDebugger;
    options.deferDebugMetadata_ = deferDebugMetadata;
  }

  bool hideFromNewScriptInitial() const {
    return deferDebugMetadata || hideScriptFromDebugger;
  }

#ifdef DEBUG
  // Assert that all fields have default value.
  //
  // This can be used when instantiation is performed as separate step than
  // compile-to-stencil, and CompileOptions isn't available there.
  void assertDefault() const {
    MOZ_ASSERT(skipFilenameValidation == false);
    MOZ_ASSERT(hideScriptFromDebugger == false);
    MOZ_ASSERT(deferDebugMetadata == false);
  }
#endif
};

/**
 * Subset of CompileOptions fields used while decoding Stencils.
 */
class JS_PUBLIC_API ReadOnlyDecodeOptions {
 public:
  bool borrowBuffer = false;
  bool usePinnedBytecode = false;

 protected:
  JS::ConstUTF8CharsZ introducerFilename_;

 public:
  // See `TransitiveCompileOptions::introductionType` field for details.
  const char* introductionType = nullptr;

  uint32_t introductionLineno = 0;
  uint32_t introductionOffset = 0;

 protected:
  ReadOnlyDecodeOptions() = default;

  ReadOnlyDecodeOptions(const ReadOnlyDecodeOptions&) = delete;
  ReadOnlyDecodeOptions& operator=(const ReadOnlyDecodeOptions&) = delete;

  template <typename T>
  void copyPODOptionsFrom(const T& options) {
    borrowBuffer = options.borrowBuffer;
    usePinnedBytecode = options.usePinnedBytecode;
    introductionType = options.introductionType;
    introductionLineno = options.introductionLineno;
    introductionOffset = options.introductionOffset;
  }

  template <typename T>
  void copyPODOptionsTo(T& options) const {
    options.borrowBuffer = borrowBuffer;
    options.usePinnedBytecode = usePinnedBytecode;
    options.introductionType = introductionType;
    options.introductionLineno = introductionLineno;
    options.introductionOffset = introductionOffset;
  }

 public:
  void copyTo(CompileOptions& options) const {
    copyPODOptionsTo(options);
    options.introducerFilename_ = introducerFilename_;
  }

  JS::ConstUTF8CharsZ introducerFilename() const { return introducerFilename_; }
};

class MOZ_STACK_CLASS JS_PUBLIC_API DecodeOptions final
    : public ReadOnlyDecodeOptions {
 public:
  DecodeOptions() = default;

  explicit DecodeOptions(const ReadOnlyCompileOptions& options) {
    copyPODOptionsFrom(options);

    introducerFilename_ = options.introducerFilename();
  }
};

class JS_PUBLIC_API OwningDecodeOptions final : public ReadOnlyDecodeOptions {
  friend class OwningCompileOptions;

 public:
  OwningDecodeOptions() = default;

  ~OwningDecodeOptions();

  bool copy(JS::FrontendContext* maybeFc, const ReadOnlyDecodeOptions& rhs);
  void infallibleCopy(const ReadOnlyDecodeOptions& rhs);

  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;

 private:
  void release();

  OwningDecodeOptions(const OwningDecodeOptions&) = delete;
  OwningDecodeOptions& operator=(const OwningDecodeOptions&) = delete;
};

}  // namespace JS

#endif /* js_CompileOptions_h */