summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/SharedContext.h
blob: ab0606527e2f66eaa9c941beabf87f5b70733778 (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
/* -*- 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 frontend_SharedContext_h
#define frontend_SharedContext_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"

#include <stddef.h>
#include <stdint.h>

#include "jstypes.h"

#include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
#include "frontend/ParserAtom.h"          // TaggedParserAtomIndex
#include "frontend/ScopeIndex.h"          // ScopeIndex
#include "frontend/ScriptIndex.h"         // ScriptIndex
#include "js/ColumnNumber.h"              // JS::LimitedColumnNumberOneOrigin
#include "vm/FunctionFlags.h"             // js::FunctionFlags
#include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
#include "vm/Scope.h"
#include "vm/ScopeKind.h"
#include "vm/SharedStencil.h"
#include "vm/StencilEnums.h"

namespace JS {
class JS_PUBLIC_API ReadOnlyCompileOptions;
struct WasmModule;
}  // namespace JS

namespace js {

class FrontendContext;

namespace frontend {

struct CompilationState;
class FunctionBox;
class FunctionNode;
class ParseContext;
class ScriptStencil;
class ScriptStencilExtra;
struct ScopeContext;

enum class StatementKind : uint8_t {
  Label,
  Block,
  If,
  Switch,
  With,
  Catch,
  Try,
  Finally,
  ForLoopLexicalHead,
  ForLoop,
  ForInLoop,
  ForOfLoop,
  DoLoop,
  WhileLoop,
  Class,

  // Used only by BytecodeEmitter.
  Spread,
  YieldStar,
};

static inline bool StatementKindIsLoop(StatementKind kind) {
  return kind == StatementKind::ForLoop || kind == StatementKind::ForInLoop ||
         kind == StatementKind::ForOfLoop || kind == StatementKind::DoLoop ||
         kind == StatementKind::WhileLoop || kind == StatementKind::Spread ||
         kind == StatementKind::YieldStar;
}

static inline bool StatementKindIsUnlabeledBreakTarget(StatementKind kind) {
  return StatementKindIsLoop(kind) || kind == StatementKind::Switch;
}

// List of directives that may be encountered in a Directive Prologue
// (ES5 15.1).
class Directives {
  bool strict_;
  bool asmJS_;

 public:
  explicit Directives(bool strict) : strict_(strict), asmJS_(false) {}
  explicit Directives(ParseContext* parent);

  void setStrict() { strict_ = true; }
  bool strict() const { return strict_; }

  void setAsmJS() { asmJS_ = true; }
  bool asmJS() const { return asmJS_; }

  Directives& operator=(Directives rhs) {
    strict_ = rhs.strict_;
    asmJS_ = rhs.asmJS_;
    return *this;
  }
  bool operator==(const Directives& rhs) const {
    return strict_ == rhs.strict_ && asmJS_ == rhs.asmJS_;
  }
  bool operator!=(const Directives& rhs) const { return !(*this == rhs); }
};

// The kind of this-binding for the current scope. Note that arrow functions
// have a lexical this-binding so their ThisBinding is the same as the
// ThisBinding of their enclosing scope and can be any value. Derived
// constructors require TDZ checks when accessing the binding.
enum class ThisBinding : uint8_t {
  Global,
  Module,
  Function,
  DerivedConstructor
};

// If Yes, the script inherits it's "this" environment and binding from the
// enclosing script. This is true for arrow-functions and eval scripts.
enum class InheritThis { No, Yes };

class GlobalSharedContext;
class EvalSharedContext;
class ModuleSharedContext;
class SuspendableContext;

#define IMMUTABLE_FLAG_GETTER_SETTER(lowerName, name) \
  GENERIC_FLAG_GETTER_SETTER(ImmutableFlags, lowerName, name)

#define IMMUTABLE_FLAG_GETTER(lowerName, name) \
  GENERIC_FLAG_GETTER(ImmutableFlags, lowerName, name)

/*
 * The struct SharedContext is part of the current parser context (see
 * ParseContext). It stores information that is reused between the parser and
 * the bytecode emitter.
 */
class SharedContext {
 public:
  FrontendContext* const fc_;

 protected:
  // See: BaseScript::immutableFlags_
  ImmutableScriptFlags immutableFlags_ = {};

  // The location of this script in the source. Note that the value here differs
  // from the final BaseScript for the case of standalone functions.
  // This field is copied to ScriptStencil, and shouldn't be modified after the
  // copy.
  SourceExtent extent_ = {};

 protected:
  // See: ThisBinding
  ThisBinding thisBinding_ = ThisBinding::Global;

  // These flags do not have corresponding script flags and may be inherited
  // from the scope chain in the case of eval and arrows.
  bool allowNewTarget_ : 1;
  bool allowSuperProperty_ : 1;
  bool allowSuperCall_ : 1;
  bool allowArguments_ : 1;
  bool inWith_ : 1;
  bool inClass_ : 1;

  // See `strict()` below.
  bool localStrict : 1;

  // True if "use strict"; appears in the body instead of being inherited.
  bool hasExplicitUseStrict_ : 1;

  // Tracks if script-related fields are already copied to ScriptStencilExtra.
  //
  // If this field is true, those fileds shouldn't be modified.
  //
  // For FunctionBox, some fields are allowed to be modified, but the
  // modification should be synced with ScriptStencilExtra by
  // FunctionBox::copyUpdated* methods.
  bool isScriptExtraFieldCopiedToStencil : 1;

  // Indicates this shared context is eligible to use JSOp::ArgumentsLength
  // when emitting the ArgumentsLength parse node.
  bool eligibleForArgumentsLength : 1;

  // End of fields.

  enum class Kind : uint8_t { FunctionBox, Global, Eval, Module };

  // Alias enum into SharedContext
  using ImmutableFlags = ImmutableScriptFlagsEnum;

  [[nodiscard]] bool hasFlag(ImmutableFlags flag) const {
    return immutableFlags_.hasFlag(flag);
  }
  void setFlag(ImmutableFlags flag, bool b = true) {
    MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil);
    immutableFlags_.setFlag(flag, b);
  }
  void clearFlag(ImmutableFlags flag) {
    MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil);
    immutableFlags_.clearFlag(flag);
  }

 public:
  SharedContext(FrontendContext* fc, Kind kind,
                const JS::ReadOnlyCompileOptions& options,
                Directives directives, SourceExtent extent);

  IMMUTABLE_FLAG_GETTER_SETTER(isForEval, IsForEval)
  IMMUTABLE_FLAG_GETTER_SETTER(isModule, IsModule)
  IMMUTABLE_FLAG_GETTER_SETTER(isFunction, IsFunction)
  IMMUTABLE_FLAG_GETTER_SETTER(selfHosted, SelfHosted)
  IMMUTABLE_FLAG_GETTER_SETTER(forceStrict, ForceStrict)
  IMMUTABLE_FLAG_GETTER_SETTER(hasNonSyntacticScope, HasNonSyntacticScope)
  IMMUTABLE_FLAG_GETTER_SETTER(noScriptRval, NoScriptRval)
  IMMUTABLE_FLAG_GETTER(treatAsRunOnce, TreatAsRunOnce)
  // Strict: custom logic below
  IMMUTABLE_FLAG_GETTER_SETTER(hasModuleGoal, HasModuleGoal)
  IMMUTABLE_FLAG_GETTER_SETTER(hasInnerFunctions, HasInnerFunctions)
  IMMUTABLE_FLAG_GETTER_SETTER(hasDirectEval, HasDirectEval)
  IMMUTABLE_FLAG_GETTER_SETTER(bindingsAccessedDynamically,
                               BindingsAccessedDynamically)
  IMMUTABLE_FLAG_GETTER_SETTER(hasCallSiteObj, HasCallSiteObj)

  const SourceExtent& extent() const { return extent_; }

  bool isFunctionBox() const { return isFunction(); }
  inline FunctionBox* asFunctionBox();
  bool isModuleContext() const { return isModule(); }
  inline ModuleSharedContext* asModuleContext();
  bool isSuspendableContext() const { return isFunction() || isModule(); }
  inline SuspendableContext* asSuspendableContext();
  bool isGlobalContext() const {
    return !(isFunction() || isModule() || isForEval());
  }
  inline GlobalSharedContext* asGlobalContext();
  bool isEvalContext() const { return isForEval(); }
  inline EvalSharedContext* asEvalContext();

  bool isTopLevelContext() const { return !isFunction(); }

  ThisBinding thisBinding() const { return thisBinding_; }
  bool hasFunctionThisBinding() const {
    return thisBinding() == ThisBinding::Function ||
           thisBinding() == ThisBinding::DerivedConstructor;
  }
  bool needsThisTDZChecks() const {
    return thisBinding() == ThisBinding::DerivedConstructor;
  }

  bool isSelfHosted() const { return selfHosted(); }
  bool allowNewTarget() const { return allowNewTarget_; }
  bool allowSuperProperty() const { return allowSuperProperty_; }
  bool allowSuperCall() const { return allowSuperCall_; }
  bool allowArguments() const { return allowArguments_; }
  bool inWith() const { return inWith_; }
  bool inClass() const { return inClass_; }

  bool hasExplicitUseStrict() const { return hasExplicitUseStrict_; }
  void setExplicitUseStrict() { hasExplicitUseStrict_ = true; }

  ImmutableScriptFlags immutableFlags() { return immutableFlags_; }

  bool allBindingsClosedOver() { return bindingsAccessedDynamically(); }

  // The ImmutableFlag tracks if the entire script is strict, while the
  // localStrict flag indicates the current region (such as class body) should
  // be treated as strict. The localStrict flag will always be reset to false
  // before the end of the script.
  bool strict() const { return hasFlag(ImmutableFlags::Strict) || localStrict; }
  void setStrictScript() { setFlag(ImmutableFlags::Strict); }
  bool setLocalStrictMode(bool strict) {
    bool retVal = localStrict;
    localStrict = strict;
    return retVal;
  }

  bool isEligibleForArgumentsLength() {
    return eligibleForArgumentsLength && !bindingsAccessedDynamically();
  }
  void setIneligibleForArgumentsLength() { eligibleForArgumentsLength = false; }

  void copyScriptExtraFields(ScriptStencilExtra& scriptExtra);
};

class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext {
  ScopeKind scopeKind_;

 public:
  GlobalScope::ParserData* bindings;

  GlobalSharedContext(FrontendContext* fc, ScopeKind scopeKind,
                      const JS::ReadOnlyCompileOptions& options,
                      Directives directives, SourceExtent extent);

  ScopeKind scopeKind() const { return scopeKind_; }
};

inline GlobalSharedContext* SharedContext::asGlobalContext() {
  MOZ_ASSERT(isGlobalContext());
  return static_cast<GlobalSharedContext*>(this);
}

class MOZ_STACK_CLASS EvalSharedContext : public SharedContext {
 public:
  EvalScope::ParserData* bindings;

  EvalSharedContext(FrontendContext* fc, CompilationState& compilationState,
                    SourceExtent extent);
};

inline EvalSharedContext* SharedContext::asEvalContext() {
  MOZ_ASSERT(isEvalContext());
  return static_cast<EvalSharedContext*>(this);
}

enum class HasHeritage { No, Yes };

class SuspendableContext : public SharedContext {
 public:
  SuspendableContext(FrontendContext* fc, Kind kind,
                     const JS::ReadOnlyCompileOptions& options,
                     Directives directives, SourceExtent extent,
                     bool isGenerator, bool isAsync);

  IMMUTABLE_FLAG_GETTER_SETTER(isAsync, IsAsync)
  IMMUTABLE_FLAG_GETTER_SETTER(isGenerator, IsGenerator)

  bool needsFinalYield() const { return isGenerator() || isAsync(); }
  bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
  bool needsClearSlotsOnExit() const { return isGenerator() || isAsync(); }
  bool needsIteratorResult() const { return isGenerator() && !isAsync(); }
  bool needsPromiseResult() const { return isAsync() && !isGenerator(); }
};

class FunctionBox : public SuspendableContext {
  friend struct GCThingList;

  CompilationState& compilationState_;

  // If this FunctionBox refers to a lazy child of the function being
  // compiled, this field holds the child's immediately enclosing scope's index.
  // Once compilation succeeds, we will store the scope pointed by this in the
  // child's BaseScript.  (Debugger may become confused if lazy scripts refer to
  // partially initialized enclosing scopes, so we must avoid storing the
  // scope in the BaseScript until compilation has completed
  // successfully.)
  // This is copied to ScriptStencil.
  // Any update after the copy should be synced to the ScriptStencil.
  mozilla::Maybe<ScopeIndex> enclosingScopeIndex_;

  // Names from the named lambda scope, if a named lambda.
  LexicalScope::ParserData* namedLambdaBindings_ = nullptr;

  // Names from the function scope.
  FunctionScope::ParserData* functionScopeBindings_ = nullptr;

  // Names from the extra 'var' scope of the function, if the parameter list
  // has expressions.
  VarScope::ParserData* extraVarScopeBindings_ = nullptr;

  // The explicit or implicit name of the function. The FunctionFlags indicate
  // the kind of name.
  // This is copied to ScriptStencil.
  // Any update after the copy should be synced to the ScriptStencil.
  TaggedParserAtomIndex atom_;

  // Index into CompilationStencil::scriptData.
  ScriptIndex funcDataIndex_ = ScriptIndex(-1);

  // See: FunctionFlags
  // This is copied to ScriptStencil.
  // Any update after the copy should be synced to the ScriptStencil.
  FunctionFlags flags_ = {};

  // See: ImmutableScriptData::funLength
  uint16_t length_ = 0;

  // JSFunction::nargs_
  // This field is copied to ScriptStencil, and shouldn't be modified after the
  // copy.
  uint16_t nargs_ = 0;

  // See: PrivateScriptData::memberInitializers_
  // This field is copied to ScriptStencil, and shouldn't be modified after the
  // copy.
  MemberInitializers memberInitializers_ = MemberInitializers::Invalid();

 public:
  // Back pointer used by asm.js for error messages.
  FunctionNode* functionNode = nullptr;

  // True if bytecode will be emitted for this function in the current
  // compilation.
  bool emitBytecode : 1;

  // This is set by the BytecodeEmitter of the enclosing script when a reference
  // to this function is generated. This is also used to determine a hoisted
  // function already is referenced by the bytecode.
  bool wasEmittedByEnclosingScript_ : 1;

  // Need to emit a synthesized Annex B assignment
  bool isAnnexB : 1;

  // Track if we saw "use asm".
  // If we successfully validated it, `flags_` is seto to `AsmJS` kind.
  bool useAsm : 1;

  // Analysis of parameter list
  bool hasParameterExprs : 1;
  bool hasDestructuringArgs : 1;
  bool hasDuplicateParameters : 1;

  // Arrow function with expression body like: `() => 1`.
  bool hasExprBody_ : 1;

  // Used to issue an early error in static class blocks.
  bool allowReturn_ : 1;

  // Tracks if function-related fields are already copied to ScriptStencil.
  // If this field is true, modification to those fields should be synced with
  // ScriptStencil by copyUpdated* methods.
  bool isFunctionFieldCopiedToStencil : 1;

  // True if this is part of initial compilation.
  // False if this is part of delazification.
  bool isInitialCompilation : 1;

  // True if this is standalone function
  // (new Function() including generator/async, or event handler).
  bool isStandalone : 1;

  // End of fields.

  FunctionBox(FrontendContext* fc, SourceExtent extent,
              CompilationState& compilationState, Directives directives,
              GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
              bool isInitialCompilation, TaggedParserAtomIndex atom,
              FunctionFlags flags, ScriptIndex index);

  ScriptStencil& functionStencil() const;
  ScriptStencilExtra& functionExtraStencil() const;

  LexicalScope::ParserData* namedLambdaBindings() {
    return namedLambdaBindings_;
  }
  void setNamedLambdaBindings(LexicalScope::ParserData* bindings) {
    namedLambdaBindings_ = bindings;
  }

  FunctionScope::ParserData* functionScopeBindings() {
    return functionScopeBindings_;
  }
  void setFunctionScopeBindings(FunctionScope::ParserData* bindings) {
    functionScopeBindings_ = bindings;
  }

  VarScope::ParserData* extraVarScopeBindings() {
    return extraVarScopeBindings_;
  }
  void setExtraVarScopeBindings(VarScope::ParserData* bindings) {
    extraVarScopeBindings_ = bindings;
  }

  void initFromLazyFunction(const ScriptStencilExtra& extra,
                            ScopeContext& scopeContext,
                            FunctionSyntaxKind kind);
  void initFromScriptStencilExtra(const ScriptStencilExtra& extra);
  void initStandalone(ScopeContext& scopeContext, FunctionSyntaxKind kind);

 private:
  void initStandaloneOrLazy(ScopeContext& scopeContext,
                            FunctionSyntaxKind kind);

 public:
  void initWithEnclosingParseContext(ParseContext* enclosing,
                                     FunctionSyntaxKind kind);

  void setEnclosingScopeForInnerLazyFunction(ScopeIndex scopeIndex);

  bool wasEmittedByEnclosingScript() const {
    return wasEmittedByEnclosingScript_;
  }
  void setWasEmittedByEnclosingScript(bool wasEmitted) {
    wasEmittedByEnclosingScript_ = wasEmitted;
    if (isFunctionFieldCopiedToStencil) {
      copyUpdatedWasEmitted();
    }
  }

  [[nodiscard]] bool setAsmJSModule(const JS::WasmModule* module);
  bool isAsmJSModule() const { return flags_.isAsmJSNative(); }

  bool hasEnclosingScopeIndex() const { return enclosingScopeIndex_.isSome(); }
  ScopeIndex getEnclosingScopeIndex() const { return *enclosingScopeIndex_; }

  IMMUTABLE_FLAG_GETTER_SETTER(isAsync, IsAsync)
  IMMUTABLE_FLAG_GETTER_SETTER(isGenerator, IsGenerator)
  IMMUTABLE_FLAG_GETTER_SETTER(funHasExtensibleScope, FunHasExtensibleScope)
  IMMUTABLE_FLAG_GETTER_SETTER(functionHasThisBinding, FunctionHasThisBinding)
  IMMUTABLE_FLAG_GETTER_SETTER(functionHasNewTargetBinding,
                               FunctionHasNewTargetBinding)
  // NeedsHomeObject: custom logic below.
  // IsDerivedClassConstructor: custom logic below.
  // IsFieldInitializer: custom logic below.
  IMMUTABLE_FLAG_GETTER(useMemberInitializers, UseMemberInitializers)
  IMMUTABLE_FLAG_GETTER_SETTER(hasRest, HasRest)
  IMMUTABLE_FLAG_GETTER_SETTER(needsFunctionEnvironmentObjects,
                               NeedsFunctionEnvironmentObjects)
  IMMUTABLE_FLAG_GETTER_SETTER(functionHasExtraBodyVarScope,
                               FunctionHasExtraBodyVarScope)
  IMMUTABLE_FLAG_GETTER_SETTER(shouldDeclareArguments, ShouldDeclareArguments)
  IMMUTABLE_FLAG_GETTER_SETTER(needsArgsObj, NeedsArgsObj)
  // HasMappedArgsObj: custom logic below.

  bool needsCallObjectRegardlessOfBindings() const {
    // Always create a CallObject if:
    // - The scope is extensible at runtime due to sloppy eval.
    // - The function is a generator or async function. (The debugger reads the
    //   generator object directly from the frame.)

    return funHasExtensibleScope() || isGenerator() || isAsync();
  }

  bool needsExtraBodyVarEnvironmentRegardlessOfBindings() const {
    MOZ_ASSERT(hasParameterExprs);
    return funHasExtensibleScope();
  }

  GeneratorKind generatorKind() const {
    return isGenerator() ? GeneratorKind::Generator
                         : GeneratorKind::NotGenerator;
  }

  FunctionAsyncKind asyncKind() const {
    return isAsync() ? FunctionAsyncKind::AsyncFunction
                     : FunctionAsyncKind::SyncFunction;
  }

  bool needsFinalYield() const { return isGenerator() || isAsync(); }
  bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
  bool needsClearSlotsOnExit() const { return isGenerator() || isAsync(); }
  bool needsIteratorResult() const { return isGenerator() && !isAsync(); }
  bool needsPromiseResult() const { return isAsync() && !isGenerator(); }

  bool isArrow() const { return flags_.isArrow(); }
  bool isLambda() const { return flags_.isLambda(); }

  bool hasExprBody() const { return hasExprBody_; }
  void setHasExprBody() {
    MOZ_ASSERT(isArrow());
    hasExprBody_ = true;
  }

  bool allowReturn() const { return allowReturn_; }

  bool isNamedLambda() const { return flags_.isNamedLambda(!!explicitName()); }
  bool isGetter() const { return flags_.isGetter(); }
  bool isSetter() const { return flags_.isSetter(); }
  bool isMethod() const { return flags_.isMethod(); }
  bool isClassConstructor() const { return flags_.isClassConstructor(); }

  bool isInterpreted() const { return flags_.hasBaseScript(); }

  FunctionFlags::FunctionKind kind() { return flags_.kind(); }

  bool hasInferredName() const { return flags_.hasInferredName(); }
  bool hasGuessedAtom() const { return flags_.hasGuessedAtom(); }

  TaggedParserAtomIndex displayAtom() const { return atom_; }
  TaggedParserAtomIndex explicitName() const {
    return (hasInferredName() || hasGuessedAtom())
               ? TaggedParserAtomIndex::null()
               : atom_;
  }

  // NOTE: We propagate to any existing functions for now. This handles both the
  // delazification case where functions already exist, and also handles
  // code-coverage which is not yet deferred.
  void setInferredName(TaggedParserAtomIndex atom) {
    atom_ = atom;
    flags_.setInferredName();
    if (isFunctionFieldCopiedToStencil) {
      copyUpdatedAtomAndFlags();
    }
  }
  void setGuessedAtom(TaggedParserAtomIndex atom) {
    atom_ = atom;
    flags_.setGuessedAtom();
    if (isFunctionFieldCopiedToStencil) {
      copyUpdatedAtomAndFlags();
    }
  }

  bool needsHomeObject() const {
    return hasFlag(ImmutableFlags::NeedsHomeObject);
  }
  void setNeedsHomeObject() {
    MOZ_ASSERT(flags_.allowSuperProperty());
    setFlag(ImmutableFlags::NeedsHomeObject);
    flags_.setIsExtended();
  }

  bool isDerivedClassConstructor() const {
    return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
  }
  void setDerivedClassConstructor() {
    MOZ_ASSERT(flags_.isClassConstructor());
    setFlag(ImmutableFlags::IsDerivedClassConstructor);
  }

  bool isSyntheticFunction() const {
    return hasFlag(ImmutableFlags::IsSyntheticFunction);
  }
  void setSyntheticFunction() {
    // Field initializer, class constructor or getter or setter
    // synthesized from accessor keyword.
    MOZ_ASSERT(flags_.isMethod() || flags_.isGetter() || flags_.isSetter());
    setFlag(ImmutableFlags::IsSyntheticFunction);
  }

  bool hasSimpleParameterList() const {
    return !hasRest() && !hasParameterExprs && !hasDestructuringArgs;
  }

  bool hasMappedArgsObj() const {
    return !strict() && hasSimpleParameterList();
  }

  // Return whether this or an enclosing function is being parsed and
  // validated as asm.js. Note: if asm.js validation fails, this will be false
  // while the function is being reparsed. This flag can be used to disable
  // certain parsing features that are necessary in general, but unnecessary
  // for validated asm.js.
  bool useAsmOrInsideUseAsm() const { return useAsm; }

  void setStart(uint32_t offset, uint32_t line,
                JS::LimitedColumnNumberOneOrigin column) {
    MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil);
    extent_.sourceStart = offset;
    extent_.lineno = line;
    extent_.column = column;
  }

  void setEnd(uint32_t end) {
    MOZ_ASSERT(!isScriptExtraFieldCopiedToStencil);
    // For all functions except class constructors, the buffer and
    // toString ending positions are the same. Class constructors override
    // the toString ending position with the end of the class definition.
    extent_.sourceEnd = end;
    extent_.toStringEnd = end;
  }

  void setCtorToStringEnd(uint32_t end) {
    extent_.toStringEnd = end;
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedExtent();
    }
  }

  void setCtorFunctionHasThisBinding() {
    immutableFlags_.setFlag(ImmutableFlags::FunctionHasThisBinding, true);
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedImmutableFlags();
    }
  }

  void setIsInlinableLargeFunction() {
    immutableFlags_.setFlag(ImmutableFlags::IsInlinableLargeFunction, true);
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedImmutableFlags();
    }
  }

  void setUsesArgumentsIntrinsics() {
    immutableFlags_.setFlag(ImmutableFlags::UsesArgumentsIntrinsics, true);
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedImmutableFlags();
    }
  }

  uint16_t length() { return length_; }
  void setLength(uint16_t length) { length_ = length; }

  void setArgCount(uint16_t args) {
    MOZ_ASSERT(!isFunctionFieldCopiedToStencil);
    nargs_ = args;
  }

  size_t nargs() { return nargs_; }

  const MemberInitializers& memberInitializers() const {
    MOZ_ASSERT(useMemberInitializers());
    return memberInitializers_;
  }
  void setMemberInitializers(MemberInitializers memberInitializers) {
    immutableFlags_.setFlag(ImmutableFlags::UseMemberInitializers, true);
    memberInitializers_ = memberInitializers;
    if (isScriptExtraFieldCopiedToStencil) {
      copyUpdatedImmutableFlags();
      copyUpdatedMemberInitializers();
    }
  }

  ScriptIndex index() { return funcDataIndex_; }

  void finishScriptFlags();
  void copyFunctionFields(ScriptStencil& script);
  void copyFunctionExtraFields(ScriptStencilExtra& scriptExtra);

  // * setCtorFunctionHasThisBinding can be called to a class constructor
  //   with a lazy function, while parsing enclosing class
  // * setIsInlinableLargeFunction can be called by BCE to update flags of the
  //   previous top-level function, but only in self-hosted mode.
  void copyUpdatedImmutableFlags();

  // * setCtorToStringEnd bcan be called to a class constructor with a lazy
  //   function, while parsing enclosing class
  void copyUpdatedExtent();

  // * setMemberInitializers can be called to a class constructor with a lazy
  //   function, while emitting enclosing script
  void copyUpdatedMemberInitializers();

  // * setEnclosingScopeForInnerLazyFunction can be called to a lazy function,
  //   while emitting enclosing script
  void copyUpdatedEnclosingScopeIndex();

  // * setInferredName can be called to a lazy function, while emitting
  //   enclosing script
  // * setGuessedAtom can be called to both lazy/non-lazy functions,
  //   while running NameFunctions
  void copyUpdatedAtomAndFlags();

  // * setWasEmitted can be called to a lazy function, while emitting
  //   enclosing script
  void copyUpdatedWasEmitted();
};

#undef FLAG_GETTER_SETTER
#undef IMMUTABLE_FLAG_GETTER_SETTER

inline FunctionBox* SharedContext::asFunctionBox() {
  MOZ_ASSERT(isFunctionBox());
  return static_cast<FunctionBox*>(this);
}

inline SuspendableContext* SharedContext::asSuspendableContext() {
  MOZ_ASSERT(isSuspendableContext());
  return static_cast<SuspendableContext*>(this);
}

}  // namespace frontend
}  // namespace js

#endif /* frontend_SharedContext_h */