BytecodeUtil-inl.h (7987B)
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 vm_BytecodeUtil_inl_h 8 #define vm_BytecodeUtil_inl_h 9 10 #include "vm/BytecodeUtil.h" 11 12 #include "frontend/SourceNotes.h" // SrcNote, SrcNoteType, SrcNoteIterator 13 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOffset 14 #include "vm/JSScript.h" 15 16 namespace js { 17 18 static inline unsigned GetDefCount(jsbytecode* pc) { 19 /* 20 * Add an extra pushed value for Or/And opcodes, so that they are included 21 * in the pushed array of stack values for type inference. 22 */ 23 JSOp op = JSOp(*pc); 24 switch (op) { 25 case JSOp::Or: 26 case JSOp::And: 27 case JSOp::Coalesce: 28 return 1; 29 case JSOp::Pick: 30 case JSOp::Unpick: 31 /* 32 * Pick pops and pushes how deep it looks in the stack + 1 33 * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2 34 * would pop b, c, and d to rearrange the stack to |a c[0] 35 * d[1] b[2]|. 36 */ 37 return pc[1] + 1; 38 default: 39 return StackDefs(op); 40 } 41 } 42 43 static inline unsigned GetUseCount(jsbytecode* pc) { 44 JSOp op = JSOp(*pc); 45 if (op == JSOp::Pick || op == JSOp::Unpick) { 46 return pc[1] + 1; 47 } 48 49 return StackUses(op, pc); 50 } 51 52 static inline JSOp ReverseCompareOp(JSOp op) { 53 switch (op) { 54 case JSOp::Gt: 55 return JSOp::Lt; 56 case JSOp::Ge: 57 return JSOp::Le; 58 case JSOp::Lt: 59 return JSOp::Gt; 60 case JSOp::Le: 61 return JSOp::Ge; 62 case JSOp::Eq: 63 case JSOp::Ne: 64 case JSOp::StrictEq: 65 case JSOp::StrictNe: 66 return op; 67 default: 68 MOZ_CRASH("unrecognized op"); 69 } 70 } 71 72 static inline JSOp NegateCompareOp(JSOp op) { 73 switch (op) { 74 case JSOp::Gt: 75 return JSOp::Le; 76 case JSOp::Ge: 77 return JSOp::Lt; 78 case JSOp::Lt: 79 return JSOp::Ge; 80 case JSOp::Le: 81 return JSOp::Gt; 82 case JSOp::Eq: 83 return JSOp::Ne; 84 case JSOp::Ne: 85 return JSOp::Eq; 86 case JSOp::StrictNe: 87 return JSOp::StrictEq; 88 case JSOp::StrictEq: 89 return JSOp::StrictNe; 90 default: 91 MOZ_CRASH("unrecognized op"); 92 } 93 } 94 95 class BytecodeRange { 96 public: 97 BytecodeRange(JSContext* cx, JSScript* script) 98 : script(cx, script), pc(script->code()), end(pc + script->length()) {} 99 bool empty() const { return pc == end; } 100 jsbytecode* frontPC() const { return pc; } 101 JSOp frontOpcode() const { return JSOp(*pc); } 102 size_t frontOffset() const { return script->pcToOffset(pc); } 103 void popFront() { pc += GetBytecodeLength(pc); } 104 105 private: 106 RootedScript script; 107 jsbytecode* pc; 108 jsbytecode* end; 109 }; 110 111 enum class SkipPrologueOps { No, Yes }; 112 113 class BytecodeRangeWithPosition : private BytecodeRange { 114 public: 115 using BytecodeRange::empty; 116 using BytecodeRange::frontOffset; 117 using BytecodeRange::frontOpcode; 118 using BytecodeRange::frontPC; 119 120 BytecodeRangeWithPosition(JSContext* cx, JSScript* script, 121 SkipPrologueOps skipPrologueOps) 122 : BytecodeRange(cx, script), 123 initialLine(script->lineno()), 124 lineno(script->lineno()), 125 column(script->column()), 126 sn(script->notes()), 127 snEnd(script->notesEnd()), 128 snpc(script->code()), 129 mainPC(script->main()), 130 isBreakpoint(false), 131 seenStepSeparator(false) { 132 if (sn < snEnd) { 133 snpc += sn->delta(); 134 } 135 updatePosition(); 136 if (skipPrologueOps == SkipPrologueOps::Yes) { 137 while (frontPC() != mainPC) { 138 popFront(); 139 } 140 MOZ_ASSERT(entryPointState != EntryPointState::NotEntryPoint); 141 } 142 } 143 144 void popFront() { 145 BytecodeRange::popFront(); 146 if (empty()) { 147 entryPointState = EntryPointState::NotEntryPoint; 148 } else { 149 updatePosition(); 150 } 151 } 152 153 uint32_t frontLineNumber() const { return lineno; } 154 JS::LimitedColumnNumberOneOrigin frontColumnNumber() const { return column; } 155 156 // Entry points are restricted to bytecode offsets that have an 157 // explicit mention in the line table. This restriction avoids a 158 // number of failing cases caused by some instructions not having 159 // sensible (to the user) line numbers, and it is one way to 160 // implement the idea that the bytecode emitter should tell the 161 // debugger exactly which offsets represent "interesting" (to the 162 // user) places to stop. 163 bool frontIsEntryPoint() const { 164 return entryPointState == EntryPointState::EntryPoint; 165 } 166 167 // Breakable points are explicitly marked by the emitter as locations where 168 // the debugger may want to allow users to pause. 169 bool frontIsBreakablePoint() const { return isBreakpoint; } 170 171 // Breakable step points are the first breakable point after a 172 // SrcNote::StepSep note has been encountered. 173 bool frontIsBreakableStepPoint() const { 174 return isBreakpoint && seenStepSeparator; 175 } 176 177 private: 178 void updatePosition() { 179 if (isBreakpoint) { 180 isBreakpoint = false; 181 seenStepSeparator = false; 182 } 183 184 // Determine the current line number by reading all source notes up to 185 // and including the current offset. 186 jsbytecode* lastLinePC = nullptr; 187 SrcNoteIterator iter(sn, snEnd); 188 while (!iter.atEnd() && snpc <= frontPC()) { 189 auto sn = *iter; 190 191 SrcNoteType type = sn->type(); 192 if (type == SrcNoteType::ColSpan) { 193 column += SrcNote::ColSpan::getSpan(sn); 194 } else if (type == SrcNoteType::SetLine) { 195 lineno = SrcNote::SetLine::getLine(sn, initialLine); 196 column = JS::LimitedColumnNumberOneOrigin(); 197 } else if (type == SrcNoteType::SetLineColumn) { 198 lineno = SrcNote::SetLineColumn::getLine(sn, initialLine); 199 column = SrcNote::SetLineColumn::getColumn(sn); 200 } else if (type == SrcNoteType::NewLine) { 201 lineno++; 202 column = JS::LimitedColumnNumberOneOrigin(); 203 } else if (type == SrcNoteType::NewLineColumn) { 204 lineno++; 205 column = SrcNote::NewLineColumn::getColumn(sn); 206 } else if (type == SrcNoteType::Breakpoint) { 207 isBreakpoint = true; 208 } else if (type == SrcNoteType::BreakpointStepSep) { 209 isBreakpoint = true; 210 seenStepSeparator = true; 211 } 212 lastLinePC = snpc; 213 ++iter; 214 if (!iter.atEnd()) { 215 snpc += (*iter)->delta(); 216 } 217 } 218 219 sn = *iter; 220 221 // The current bytecode op is an entry point if it's the first op of the 222 // 'main' bytecode section or if it matches lastLinePC. 223 // 224 // If we're at a JSOp::JumpTarget op, we use ArtifactEntryPoint to mark the 225 // next op as entry point instead. This prevents adding breakpoints for 226 // empty statements in the JS code. 227 if (frontPC() == mainPC || frontPC() == lastLinePC || 228 entryPointState == EntryPointState::ArtifactEntryPoint) { 229 if (frontOpcode() == JSOp::JumpTarget) { 230 entryPointState = EntryPointState::ArtifactEntryPoint; 231 } else { 232 entryPointState = EntryPointState::EntryPoint; 233 } 234 } else { 235 entryPointState = EntryPointState::NotEntryPoint; 236 } 237 238 // Ops in the prologue are never entry points or breakable locations. 239 if (frontPC() < mainPC) { 240 MOZ_ASSERT(!frontIsEntryPoint()); 241 MOZ_ASSERT(!frontIsBreakablePoint()); 242 MOZ_ASSERT(!frontIsBreakableStepPoint()); 243 } 244 } 245 246 uint32_t initialLine; 247 248 // Line number (1-origin). 249 uint32_t lineno; 250 251 // Column number in UTF-16 code units. 252 JS::LimitedColumnNumberOneOrigin column; 253 254 const SrcNote* sn; 255 const SrcNote* snEnd; 256 jsbytecode* snpc; 257 jsbytecode* mainPC; 258 bool isBreakpoint; 259 bool seenStepSeparator; 260 261 enum class EntryPointState : uint8_t { 262 NotEntryPoint, 263 EntryPoint, 264 ArtifactEntryPoint 265 }; 266 EntryPointState entryPointState = EntryPointState::NotEntryPoint; 267 }; 268 269 } // namespace js 270 271 #endif /* vm_BytecodeUtil_inl_h */