CForEmitter.h (5410B)
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_CForEmitter_h 8 #define frontend_CForEmitter_h 9 10 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS 11 #include "mozilla/Maybe.h" // mozilla::Maybe 12 13 #include <stdint.h> // uint32_t 14 15 #include "frontend/BytecodeControlStructures.h" // LoopControl 16 #include "frontend/TDZCheckCache.h" // TDZCheckCache 17 18 namespace js { 19 namespace frontend { 20 21 struct BytecodeEmitter; 22 class EmitterScope; 23 24 // Class for emitting bytecode for c-style for block. 25 // 26 // Usage: (check for the return value is omitted for simplicity) 27 // 28 // `for (init; cond; update) body` 29 // CForEmitter cfor(this, headLexicalEmitterScopeForLet or nullptr); 30 // cfor.emitInit(Some(offset_of_init)); 31 // emit(init); // without pushing value 32 // cfor.emitCond(Some(offset_of_cond)); 33 // emit(cond); 34 // cfor.emitBody(CForEmitter::Cond::Present); 35 // emit(body); 36 // cfor.emitUpdate(CForEmitter::Update::Present, Some(offset_of_update))); 37 // emit(update); 38 // cfor.emitEnd(offset_of_for); 39 // 40 // `for (;;) body` 41 // CForEmitter cfor(this, nullptr); 42 // cfor.emitInit(Nothing()); 43 // cfor.emitCond(Nothing()); 44 // cfor.emitBody(CForEmitter::Cond::Missing); 45 // emit(body); 46 // cfor.emitUpdate(CForEmitter::Update::Missing, Nothing()); 47 // cfor.emitEnd(offset_of_for); 48 // 49 class MOZ_STACK_CLASS CForEmitter { 50 // Basic structure of the bytecode (not complete). 51 // 52 // If `cond` is not empty: 53 // {init} 54 // loop: 55 // JSOp::LoopHead 56 // {cond} 57 // JSOp::JumpIfFalse break 58 // {body} 59 // continue: 60 // {update} 61 // JSOp::Goto loop 62 // break: 63 // 64 // If `cond` is empty: 65 // {init} 66 // loop: 67 // JSOp::LoopHead 68 // {body} 69 // continue: 70 // {update} 71 // JSOp::Goto loop 72 // break: 73 // 74 public: 75 enum class Cond { Missing, Present }; 76 enum class Update { Missing, Present }; 77 78 private: 79 BytecodeEmitter* bce_; 80 81 // Whether the c-style for loop has `cond` and `update`. 82 Cond cond_ = Cond::Missing; 83 Update update_ = Update::Missing; 84 85 mozilla::Maybe<LoopControl> loopInfo_; 86 87 // The lexical scope to be freshened for each iteration. 88 // See the comment in `emitCond` for more details. 89 // 90 // ### Scope freshening 91 // 92 // Each iteration of a `for (let V...)` loop creates a fresh loop variable 93 // binding for V, even if the loop is a C-style `for(;;)` loop: 94 // 95 // var funcs = []; 96 // for (let i = 0; i < 2; i++) 97 // funcs.push(function() { return i; }); 98 // assertEq(funcs[0](), 0); // the two closures capture... 99 // assertEq(funcs[1](), 1); // ...two different `i` bindings 100 // 101 // This is implemented by "freshening" the implicit block -- changing the 102 // scope chain to a fresh clone of the instantaneous block object -- each 103 // iteration, just before evaluating the "update" in for(;;) loops. 104 // 105 // ECMAScript doesn't freshen in `for (const ...;;)`. Lack of freshening 106 // isn't directly observable in-language because `const`s can't be mutated, 107 // but it *can* be observed in the Debugger API. 108 const EmitterScope* headLexicalEmitterScopeForLet_; 109 110 mozilla::Maybe<TDZCheckCache> tdzCache_; 111 112 #ifdef DEBUG 113 // The state of this emitter. 114 // 115 // +-------+ emitInit +------+ emitCond +------+ emitBody +------+ 116 // | Start |--------->| Init |--------->| Cond |--------->| Body |-+ 117 // +-------+ +------+ +------+ +------+ | 118 // | 119 // +-------------------------------------+ 120 // | 121 // | emitUpdate +--------+ emitEnd +-----+ 122 // +----------->| Update |-------->| End | 123 // +--------+ +-----+ 124 enum class State { 125 // The initial state. 126 Start, 127 128 // After calling emitInit. 129 Init, 130 131 // After calling emitCond. 132 Cond, 133 134 // After calling emitBody. 135 Body, 136 137 // After calling emitUpdate. 138 Update, 139 140 // After calling emitEnd. 141 End 142 }; 143 State state_ = State::Start; 144 #endif 145 146 public: 147 CForEmitter(BytecodeEmitter* bce, 148 const EmitterScope* headLexicalEmitterScopeForLet); 149 150 // Parameters are the offset in the source code for each character below: 151 // 152 // for ( x = 10 ; x < 20 ; x ++ ) { f(x); } 153 // ^ ^ ^ ^ 154 // | | | | 155 // | | | updatePos 156 // | | | 157 // | | condPos 158 // | | 159 // | initPos 160 // | 161 // forPos 162 // 163 // Can be Nothing() if not available. 164 [[nodiscard]] bool emitInit(const mozilla::Maybe<uint32_t>& initPos); 165 [[nodiscard]] bool emitCond(const mozilla::Maybe<uint32_t>& condPos); 166 [[nodiscard]] bool emitBody(Cond cond); 167 [[nodiscard]] bool emitUpdate(Update update, 168 const mozilla::Maybe<uint32_t>& updatePos); 169 [[nodiscard]] bool emitEnd(uint32_t forPos); 170 }; 171 172 } /* namespace frontend */ 173 } /* namespace js */ 174 175 #endif /* frontend_CForEmitter_h */