tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */