EmitterScope.h (9536B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef frontend_EmitterScope_h 8 #define frontend_EmitterScope_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Maybe.h" 12 13 #include <stdint.h> 14 15 #include "ds/Nestable.h" 16 #include "frontend/AbstractScopePtr.h" 17 #include "frontend/NameAnalysisTypes.h" 18 #include "frontend/NameCollections.h" 19 #include "frontend/Stencil.h" 20 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 21 # include "frontend/UsingEmitter.h" 22 #endif 23 #include "vm/Opcodes.h" // JSOp 24 #include "vm/SharedStencil.h" // GCThingIndex 25 26 namespace js { 27 namespace frontend { 28 29 struct BytecodeEmitter; 30 class EvalSharedContext; 31 class FunctionBox; 32 class GlobalSharedContext; 33 class ModuleSharedContext; 34 class TaggedParserAtomIndex; 35 36 // A scope that introduces bindings. 37 class MOZ_STACK_CLASS EmitterScope : public Nestable<EmitterScope> { 38 // The cache of bound names that may be looked up in the 39 // scope. Initially populated as the set of names this scope binds. As 40 // names are looked up in enclosing scopes, they are cached on the 41 // current scope. 42 PooledMapPtr<NameLocationMap> nameCache_; 43 44 // If this scope's cache does not include free names, such as the 45 // global scope, the NameLocation to return. 46 mozilla::Maybe<NameLocation> fallbackFreeNameLocation_; 47 48 // True if there is a corresponding EnvironmentObject on the environment 49 // chain, false if all bindings are stored in frame slots on the stack. 50 bool hasEnvironment_; 51 52 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 53 mozilla::Maybe<UsingEmitter> usingEmitter_; 54 55 private: 56 BlockKind blockKind_ = BlockKind::Other; 57 #endif 58 59 // The number of enclosing environments. Used for error checking. 60 uint16_t environmentChainLength_; 61 62 // The next usable slot on the frame for not-closed over bindings. 63 // 64 // The initial frame slot when assigning slots to bindings is the 65 // enclosing scope's nextFrameSlot. For the first scope in a frame, 66 // the initial frame slot is 0. 67 uint32_t nextFrameSlot_; 68 69 // The index in the BytecodeEmitter's interned scope vector, otherwise 70 // ScopeNote::NoScopeIndex. 71 GCThingIndex scopeIndex_; 72 73 // If kind is Lexical, Catch, or With, the index in the BytecodeEmitter's 74 // block scope note list. Otherwise ScopeNote::NoScopeNote. 75 uint32_t noteIndex_; 76 77 [[nodiscard]] bool ensureCache(BytecodeEmitter* bce); 78 79 [[nodiscard]] bool checkSlotLimits(BytecodeEmitter* bce, 80 const ParserBindingIter& bi); 81 82 [[nodiscard]] bool checkEnvironmentChainLength(BytecodeEmitter* bce); 83 84 void updateFrameFixedSlots(BytecodeEmitter* bce, const ParserBindingIter& bi); 85 86 [[nodiscard]] bool putNameInCache(BytecodeEmitter* bce, 87 TaggedParserAtomIndex name, 88 NameLocation loc); 89 90 mozilla::Maybe<NameLocation> lookupInCache(BytecodeEmitter* bce, 91 TaggedParserAtomIndex name); 92 93 EmitterScope* enclosing(BytecodeEmitter** bce) const; 94 95 mozilla::Maybe<ScopeIndex> enclosingScopeIndex(BytecodeEmitter* bce) const; 96 97 static bool nameCanBeFree(BytecodeEmitter* bce, TaggedParserAtomIndex name); 98 99 NameLocation searchAndCache(BytecodeEmitter* bce, TaggedParserAtomIndex name); 100 101 [[nodiscard]] bool internEmptyGlobalScopeAsBody(BytecodeEmitter* bce); 102 103 [[nodiscard]] bool internScopeStencil(BytecodeEmitter* bce, ScopeIndex index); 104 105 [[nodiscard]] bool internBodyScopeStencil(BytecodeEmitter* bce, 106 ScopeIndex index); 107 [[nodiscard]] bool appendScopeNote(BytecodeEmitter* bce); 108 109 [[nodiscard]] bool clearFrameSlotRange(BytecodeEmitter* bce, JSOp opcode, 110 uint32_t slotStart, 111 uint32_t slotEnd) const; 112 113 [[nodiscard]] bool deadZoneFrameSlotRange(BytecodeEmitter* bce, 114 uint32_t slotStart, 115 uint32_t slotEnd) const { 116 return clearFrameSlotRange(bce, JSOp::Uninitialized, slotStart, slotEnd); 117 } 118 119 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 120 void setHasDisposables(BytecodeEmitter* bce) { 121 if (!usingEmitter_.isSome()) { 122 usingEmitter_.emplace(bce); 123 } 124 } 125 #endif 126 127 public: 128 explicit EmitterScope(BytecodeEmitter* bce); 129 130 void dump(BytecodeEmitter* bce); 131 132 [[nodiscard]] bool enterLexical(BytecodeEmitter* bce, ScopeKind kind, 133 LexicalScope::ParserData* bindings 134 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 135 , 136 BlockKind blockKind = BlockKind::Other 137 #endif 138 ); 139 [[nodiscard]] bool enterClassBody(BytecodeEmitter* bce, ScopeKind kind, 140 ClassBodyScope::ParserData* bindings); 141 [[nodiscard]] bool enterNamedLambda(BytecodeEmitter* bce, 142 FunctionBox* funbox); 143 [[nodiscard]] bool enterFunction(BytecodeEmitter* bce, FunctionBox* funbox); 144 [[nodiscard]] bool enterFunctionExtraBodyVar(BytecodeEmitter* bce, 145 FunctionBox* funbox); 146 [[nodiscard]] bool enterGlobal(BytecodeEmitter* bce, 147 GlobalSharedContext* globalsc); 148 [[nodiscard]] bool enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc); 149 [[nodiscard]] bool enterModule(BytecodeEmitter* module, 150 ModuleSharedContext* modulesc); 151 [[nodiscard]] bool enterWith(BytecodeEmitter* bce); 152 [[nodiscard]] bool deadZoneFrameSlots(BytecodeEmitter* bce) const; 153 154 [[nodiscard]] bool leave(BytecodeEmitter* bce, bool nonLocal = false); 155 156 GCThingIndex index() const { 157 MOZ_ASSERT(scopeIndex_ != ScopeNote::NoScopeIndex, 158 "Did you forget to intern a Scope?"); 159 return scopeIndex_; 160 } 161 162 uint32_t noteIndex() const { return noteIndex_; } 163 164 AbstractScopePtr scope(const BytecodeEmitter* bce) const; 165 mozilla::Maybe<ScopeIndex> scopeIndex(const BytecodeEmitter* bce) const; 166 167 bool hasEnvironment() const { return hasEnvironment_; } 168 169 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 170 private: 171 // Disposable Scope here refers to any scope 172 // with using bindings in it for now that is 173 // a lexical scope and a module scope. 174 [[nodiscard]] bool prepareForDisposableScopeBody(BytecodeEmitter* bce); 175 176 [[nodiscard]] bool emitDisposableScopeBodyEnd(BytecodeEmitter* bce); 177 178 public: 179 [[nodiscard]] bool prepareForModuleDisposableScopeBody(BytecodeEmitter* bce); 180 181 [[nodiscard]] bool emitModuleDisposableScopeBodyEnd(BytecodeEmitter* bce); 182 183 [[nodiscard]] bool prepareForDisposableAssignment(UsingHint hint); 184 185 bool hasDisposables() const { return usingEmitter_.isSome(); } 186 187 bool hasAsyncDisposables() const { 188 return hasDisposables() && usingEmitter_->hasAwaitUsing(); 189 } 190 #endif 191 192 // The first frame slot used. 193 uint32_t frameSlotStart() const { 194 if (EmitterScope* inFrame = enclosingInFrame()) { 195 return inFrame->nextFrameSlot_; 196 } 197 return 0; 198 } 199 200 // The last frame slot used + 1. 201 uint32_t frameSlotEnd() const { return nextFrameSlot_; } 202 203 EmitterScope* enclosingInFrame() const { 204 return Nestable<EmitterScope>::enclosing(); 205 } 206 207 NameLocation lookup(BytecodeEmitter* bce, TaggedParserAtomIndex name); 208 209 // Find both the slot associated with a private name and the location of the 210 // corresponding `.privateBrand` binding. 211 // 212 // Simply doing two separate lookups, one for `name` and another for 213 // `.privateBrand`, would give the wrong answer in this case: 214 // 215 // class Outer { 216 // #outerMethod() { reutrn "ok"; } 217 // 218 // test() { 219 // class Inner { 220 // #innerMethod() {} 221 // test(outer) { 222 // return outer.#outerMethod(); 223 // } 224 // } 225 // return new Inner().test(this); 226 // } 227 // } 228 // 229 // new Outer().test(); // should return "ok" 230 // 231 // At the point in Inner.test where `#outerMethod` is called, we need to 232 // check for the private brand of `Outer`, not `Inner`; but both class bodies 233 // have `.privateBrand` bindings. In a normal `lookup`, the inner binding 234 // would shadow the outer one. 235 // 236 // This method instead sets `brandLoc` to the location of the `.privateBrand` 237 // binding in the same class body as the private name `name`, ignoring 238 // shadowing. If `name` refers to a name that is actually stamped onto the 239 // target object (anything other than a non-static private method), then 240 // `brandLoc` is set to Nothing. 241 void lookupPrivate(BytecodeEmitter* bce, TaggedParserAtomIndex name, 242 NameLocation& loc, mozilla::Maybe<NameLocation>& brandLoc); 243 244 mozilla::Maybe<NameLocation> locationBoundInScope(TaggedParserAtomIndex name, 245 EmitterScope* target); 246 247 // For a given emitter scope, return the number of enclosing environments in 248 // the current compilation (this excludes environments that could enclose the 249 // compilation, like would happen for an eval copmilation). 250 static uint32_t CountEnclosingCompilationEnvironments( 251 BytecodeEmitter* bce, EmitterScope* emitterScope); 252 }; 253 254 } /* namespace frontend */ 255 } /* namespace js */ 256 257 #endif /* frontend_EmitterScope_h */