BytecodeControlStructures.h (6560B)
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_BytecodeControlStructures_h 8 #define frontend_BytecodeControlStructures_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT 11 #include "mozilla/Attributes.h" // MOZ_STACK_CLASS 12 #include "mozilla/Maybe.h" // mozilla::Maybe 13 14 #include <stdint.h> // int32_t, uint32_t 15 16 #include "ds/Nestable.h" // Nestable 17 #include "frontend/BytecodeOffset.h" // BytecodeOffset 18 #include "frontend/JumpList.h" // JumpList, JumpTarget 19 #include "frontend/ParserAtom.h" // TaggedParserAtomIndex 20 #include "frontend/SharedContext.h" // StatementKind, StatementKindIsLoop, StatementKindIsUnlabeledBreakTarget 21 #include "frontend/TDZCheckCache.h" // TDZCheckCache 22 #include "vm/StencilEnums.h" // TryNoteKind 23 24 namespace js { 25 namespace frontend { 26 27 struct BytecodeEmitter; 28 class EmitterScope; 29 30 // This class just like Nestable is not annotated with MOZ_STACK_CLASS 31 // in order to allow specific cases where subclasses need to be 32 // be allocated on heap. 33 class NestableControl : public Nestable<NestableControl> { 34 StatementKind kind_; 35 36 // The innermost scope when this was pushed. 37 EmitterScope* emitterScope_; 38 39 protected: 40 NestableControl(BytecodeEmitter* bce, StatementKind kind); 41 42 public: 43 using Nestable<NestableControl>::enclosing; 44 using Nestable<NestableControl>::findNearest; 45 46 StatementKind kind() const { return kind_; } 47 48 EmitterScope* emitterScope() const { return emitterScope_; } 49 50 template <typename T> 51 bool is() const; 52 53 template <typename T> 54 T& as() { 55 MOZ_ASSERT(this->is<T>()); 56 return static_cast<T&>(*this); 57 } 58 }; 59 60 class MOZ_STACK_CLASS BreakableControl : public NestableControl { 61 public: 62 // Offset of the last break. 63 JumpList breaks; 64 65 BreakableControl(BytecodeEmitter* bce, StatementKind kind); 66 67 [[nodiscard]] bool patchBreaks(BytecodeEmitter* bce); 68 }; 69 template <> 70 inline bool NestableControl::is<BreakableControl>() const { 71 return StatementKindIsUnlabeledBreakTarget(kind_) || 72 kind_ == StatementKind::Label; 73 } 74 75 class MOZ_STACK_CLASS LabelControl : public BreakableControl { 76 TaggedParserAtomIndex label_; 77 78 // The code offset when this was pushed. Used for effectfulness checking. 79 BytecodeOffset startOffset_; 80 81 public: 82 LabelControl(BytecodeEmitter* bce, TaggedParserAtomIndex label, 83 BytecodeOffset startOffset); 84 85 TaggedParserAtomIndex label() const { return label_; } 86 87 BytecodeOffset startOffset() const { return startOffset_; } 88 }; 89 template <> 90 inline bool NestableControl::is<LabelControl>() const { 91 return kind_ == StatementKind::Label; 92 } 93 94 class LoopControl : public BreakableControl { 95 // Loops' children are emitted in dominance order, so they can always 96 // have a TDZCheckCache. 97 TDZCheckCache tdzCache_; 98 99 // Here's the basic structure of a loop: 100 // 101 // head: 102 // JSOp::LoopHead 103 // {loop condition/body} 104 // 105 // continueTarget: 106 // {loop update if present} 107 // 108 // # Loop end, backward jump 109 // JSOp::Goto/JSOp::JumpIfTrue head 110 // 111 // breakTarget: 112 113 // The bytecode offset of JSOp::LoopHead. 114 JumpTarget head_; 115 116 // Stack depth when this loop was pushed on the control stack. 117 int32_t stackDepth_; 118 119 // The loop nesting depth. Used as a hint to Ion. 120 uint32_t loopDepth_; 121 122 public: 123 // Offset of the last continue in the loop. 124 JumpList continues; 125 126 LoopControl(BytecodeEmitter* bce, StatementKind loopKind); 127 128 BytecodeOffset headOffset() const { return head_.offset; } 129 130 [[nodiscard]] bool emitContinueTarget(BytecodeEmitter* bce); 131 132 // `nextPos` is the offset in the source code for the character that 133 // corresponds to the next instruction after JSOp::LoopHead. 134 // Can be Nothing() if not available. 135 [[nodiscard]] bool emitLoopHead(BytecodeEmitter* bce, 136 const mozilla::Maybe<uint32_t>& nextPos); 137 138 [[nodiscard]] bool emitLoopEnd(BytecodeEmitter* bce, JSOp op, 139 TryNoteKind tryNoteKind); 140 }; 141 template <> 142 inline bool NestableControl::is<LoopControl>() const { 143 return StatementKindIsLoop(kind_); 144 } 145 146 enum class NonLocalExitKind { Continue, Break, Return }; 147 148 class TryFinallyContinuation { 149 public: 150 TryFinallyContinuation(NestableControl* target, NonLocalExitKind kind) 151 : target_(target), kind_(kind) {} 152 153 NestableControl* target_; 154 NonLocalExitKind kind_; 155 }; 156 157 // TryFinallyControl is not annotated with MOZ_STACK_CLASS in order to allow 158 // for TryEmitter to be allocated on heap which uses this class to track jumps 159 // to the finally block. 160 class TryFinallyControl : public NestableControl { 161 bool emittingSubroutine_ = false; 162 163 public: 164 // Offset of the last jump to this `finally`. 165 JumpList finallyJumps_; 166 167 js::Vector<TryFinallyContinuation, 4, SystemAllocPolicy> continuations_; 168 169 TryFinallyControl(BytecodeEmitter* bce, StatementKind kind); 170 171 void setEmittingSubroutine() { emittingSubroutine_ = true; } 172 173 bool emittingSubroutine() const { return emittingSubroutine_; } 174 175 enum SpecialContinuations { Fallthrough, Count }; 176 bool allocateContinuation(NestableControl* target, NonLocalExitKind kind, 177 uint32_t* idx); 178 bool emitContinuations(BytecodeEmitter* bce); 179 }; 180 template <> 181 inline bool NestableControl::is<TryFinallyControl>() const { 182 return kind_ == StatementKind::Try || kind_ == StatementKind::Finally; 183 } 184 185 class NonLocalExitControl { 186 BytecodeEmitter* bce_; 187 const uint32_t savedScopeNoteIndex_; 188 const int savedDepth_; 189 uint32_t openScopeNoteIndex_; 190 NonLocalExitKind kind_; 191 192 // The offset of a `JSOp::SetRval` that can be rewritten as a 193 // `JSOp::Return` if we don't generate any code for this 194 // NonLocalExitControl. 195 BytecodeOffset setRvalOffset_ = BytecodeOffset::invalidOffset(); 196 197 [[nodiscard]] bool leaveScope(EmitterScope* es); 198 199 public: 200 NonLocalExitControl(const NonLocalExitControl&) = delete; 201 NonLocalExitControl(BytecodeEmitter* bce, NonLocalExitKind kind); 202 ~NonLocalExitControl(); 203 204 [[nodiscard]] bool emitNonLocalJump(NestableControl* target, 205 NestableControl* startingAfter = nullptr); 206 [[nodiscard]] bool emitReturn(BytecodeOffset setRvalOffset); 207 }; 208 209 } /* namespace frontend */ 210 } /* namespace js */ 211 212 #endif /* frontend_BytecodeControlStructures_h */