summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/EmitterScope.h
blob: 8f985faffe684f695bfc1ff09115398db8615af2 (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
/* -*- 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_EmitterScope_h
#define frontend_EmitterScope_h

#include "mozilla/Maybe.h"

#include <stdint.h>

#include "ds/Nestable.h"
#include "frontend/AbstractScopePtr.h"
#include "frontend/NameAnalysisTypes.h"
#include "frontend/NameCollections.h"
#include "frontend/Stencil.h"
#include "vm/Opcodes.h"        // JSOp
#include "vm/SharedStencil.h"  // GCThingIndex

namespace js {
namespace frontend {

struct BytecodeEmitter;
class EvalSharedContext;
class FunctionBox;
class GlobalSharedContext;
class ModuleSharedContext;
class TaggedParserAtomIndex;

// A scope that introduces bindings.
class EmitterScope : public Nestable<EmitterScope> {
  // The cache of bound names that may be looked up in the
  // scope. Initially populated as the set of names this scope binds. As
  // names are looked up in enclosing scopes, they are cached on the
  // current scope.
  PooledMapPtr<NameLocationMap> nameCache_;

  // If this scope's cache does not include free names, such as the
  // global scope, the NameLocation to return.
  mozilla::Maybe<NameLocation> fallbackFreeNameLocation_;

  // True if there is a corresponding EnvironmentObject on the environment
  // chain, false if all bindings are stored in frame slots on the stack.
  bool hasEnvironment_;

  // The number of enclosing environments. Used for error checking.
  uint8_t environmentChainLength_;

  // The next usable slot on the frame for not-closed over bindings.
  //
  // The initial frame slot when assigning slots to bindings is the
  // enclosing scope's nextFrameSlot. For the first scope in a frame,
  // the initial frame slot is 0.
  uint32_t nextFrameSlot_;

  // The index in the BytecodeEmitter's interned scope vector, otherwise
  // ScopeNote::NoScopeIndex.
  GCThingIndex scopeIndex_;

  // If kind is Lexical, Catch, or With, the index in the BytecodeEmitter's
  // block scope note list. Otherwise ScopeNote::NoScopeNote.
  uint32_t noteIndex_;

  [[nodiscard]] bool ensureCache(BytecodeEmitter* bce);

  [[nodiscard]] bool checkSlotLimits(BytecodeEmitter* bce,
                                     const ParserBindingIter& bi);

  [[nodiscard]] bool checkEnvironmentChainLength(BytecodeEmitter* bce);

  void updateFrameFixedSlots(BytecodeEmitter* bce, const ParserBindingIter& bi);

  [[nodiscard]] bool putNameInCache(BytecodeEmitter* bce,
                                    TaggedParserAtomIndex name,
                                    NameLocation loc);

  mozilla::Maybe<NameLocation> lookupInCache(BytecodeEmitter* bce,
                                             TaggedParserAtomIndex name);

  EmitterScope* enclosing(BytecodeEmitter** bce) const;

  mozilla::Maybe<ScopeIndex> enclosingScopeIndex(BytecodeEmitter* bce) const;

  static bool nameCanBeFree(BytecodeEmitter* bce, TaggedParserAtomIndex name);

  NameLocation searchAndCache(BytecodeEmitter* bce, TaggedParserAtomIndex name);

  [[nodiscard]] bool internEmptyGlobalScopeAsBody(BytecodeEmitter* bce);

  [[nodiscard]] bool internScopeStencil(BytecodeEmitter* bce, ScopeIndex index);

  [[nodiscard]] bool internBodyScopeStencil(BytecodeEmitter* bce,
                                            ScopeIndex index);
  [[nodiscard]] bool appendScopeNote(BytecodeEmitter* bce);

  [[nodiscard]] bool clearFrameSlotRange(BytecodeEmitter* bce, JSOp opcode,
                                         uint32_t slotStart,
                                         uint32_t slotEnd) const;

  [[nodiscard]] bool deadZoneFrameSlotRange(BytecodeEmitter* bce,
                                            uint32_t slotStart,
                                            uint32_t slotEnd) const {
    return clearFrameSlotRange(bce, JSOp::Uninitialized, slotStart, slotEnd);
  }

 public:
  explicit EmitterScope(BytecodeEmitter* bce);

  void dump(BytecodeEmitter* bce);

  [[nodiscard]] bool enterLexical(BytecodeEmitter* bce, ScopeKind kind,
                                  LexicalScope::ParserData* bindings);
  [[nodiscard]] bool enterClassBody(BytecodeEmitter* bce, ScopeKind kind,
                                    ClassBodyScope::ParserData* bindings);
  [[nodiscard]] bool enterNamedLambda(BytecodeEmitter* bce,
                                      FunctionBox* funbox);
  [[nodiscard]] bool enterFunction(BytecodeEmitter* bce, FunctionBox* funbox);
  [[nodiscard]] bool enterFunctionExtraBodyVar(BytecodeEmitter* bce,
                                               FunctionBox* funbox);
  [[nodiscard]] bool enterGlobal(BytecodeEmitter* bce,
                                 GlobalSharedContext* globalsc);
  [[nodiscard]] bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc);
  [[nodiscard]] bool enterModule(BytecodeEmitter* module,
                                 ModuleSharedContext* modulesc);
  [[nodiscard]] bool enterWith(BytecodeEmitter* bce);
  [[nodiscard]] bool deadZoneFrameSlots(BytecodeEmitter* bce) const;

  [[nodiscard]] bool leave(BytecodeEmitter* bce, bool nonLocal = false);

  GCThingIndex index() const {
    MOZ_ASSERT(scopeIndex_ != ScopeNote::NoScopeIndex,
               "Did you forget to intern a Scope?");
    return scopeIndex_;
  }

  uint32_t noteIndex() const { return noteIndex_; }

  AbstractScopePtr scope(const BytecodeEmitter* bce) const;
  mozilla::Maybe<ScopeIndex> scopeIndex(const BytecodeEmitter* bce) const;

  bool hasEnvironment() const { return hasEnvironment_; }

  // The first frame slot used.
  uint32_t frameSlotStart() const {
    if (EmitterScope* inFrame = enclosingInFrame()) {
      return inFrame->nextFrameSlot_;
    }
    return 0;
  }

  // The last frame slot used + 1.
  uint32_t frameSlotEnd() const { return nextFrameSlot_; }

  EmitterScope* enclosingInFrame() const {
    return Nestable<EmitterScope>::enclosing();
  }

  NameLocation lookup(BytecodeEmitter* bce, TaggedParserAtomIndex name);

  // Find both the slot associated with a private name and the location of the
  // corresponding `.privateBrand` binding.
  //
  // Simply doing two separate lookups, one for `name` and another for
  // `.privateBrand`, would give the wrong answer in this case:
  //
  //     class Outer {
  //       #outerMethod() { reutrn "ok"; }
  //
  //       test() {
  //         class Inner {
  //           #innerMethod() {}
  //           test(outer) {
  //             return outer.#outerMethod();
  //           }
  //         }
  //         return new Inner().test(this);
  //       }
  //     }
  //
  //    new Outer().test();  // should return "ok"
  //
  // At the point in Inner.test where `#outerMethod` is called, we need to
  // check for the private brand of `Outer`, not `Inner`; but both class bodies
  // have `.privateBrand` bindings. In a normal `lookup`, the inner binding
  // would shadow the outer one.
  //
  // This method instead sets `brandLoc` to the location of the `.privateBrand`
  // binding in the same class body as the private name `name`, ignoring
  // shadowing. If `name` refers to a name that is actually stamped onto the
  // target object (anything other than a non-static private method), then
  // `brandLoc` is set to Nothing.
  void lookupPrivate(BytecodeEmitter* bce, TaggedParserAtomIndex name,
                     NameLocation& loc, mozilla::Maybe<NameLocation>& brandLoc);

  mozilla::Maybe<NameLocation> locationBoundInScope(TaggedParserAtomIndex name,
                                                    EmitterScope* target);

  // For a given emitter scope, return the number of enclosing environments in
  // the current compilation (this excludes environments that could enclose the
  // compilation, like would happen for an eval copmilation).
  static uint32_t CountEnclosingCompilationEnvironments(
      BytecodeEmitter* bce, EmitterScope* emitterScope);
};

} /* namespace frontend */
} /* namespace js */

#endif /* frontend_EmitterScope_h */