tor-browser

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

TryEmitter.h (8056B)


      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_TryEmitter_h
      8 #define frontend_TryEmitter_h
      9 
     10 #include "mozilla/Maybe.h"  // mozilla::Maybe, mozilla::Nothing
     11 
     12 #include <stdint.h>  // uint32_t
     13 
     14 #include "frontend/BytecodeControlStructures.h"  // TryFinallyControl
     15 #include "frontend/BytecodeOffset.h"             // BytecodeOffset
     16 #include "frontend/JumpList.h"                   // JumpList, JumpTarget
     17 #include "js/UniquePtr.h"                        // js::UniquePtr
     18 
     19 namespace js {
     20 namespace frontend {
     21 
     22 struct BytecodeEmitter;
     23 
     24 // Class for emitting bytecode for blocks like try-catch-finally.
     25 //
     26 // Usage: (check for the return value is omitted for simplicity)
     27 //
     28 //   `try { try_block } catch (ex) { catch_block }`
     29 //     TryEmitter tryCatch(this, TryEmitter::Kind::TryCatch,
     30 //                         TryEmitter::ControlKind::Syntactic);
     31 //     tryCatch.emitTry();
     32 //     emit(try_block);
     33 //     tryCatch.emitCatch();
     34 //     emit(catch_block); // Pending exception is on stack
     35 //     tryCatch.emitEnd();
     36 //
     37 //   `try { try_block } finally { finally_block }`
     38 //     TryEmitter tryCatch(this, TryEmitter::Kind::TryFinally,
     39 //                         TryEmitter::ControlKind::Syntactic);
     40 //     tryCatch.emitTry();
     41 //     emit(try_block);
     42 //     // finally_pos: The "{" character's position in the source code text.
     43 //     tryCatch.emitFinally(Some(finally_pos));
     44 //     emit(finally_block);
     45 //     tryCatch.emitEnd();
     46 //
     47 //   `try { try_block } catch (ex) {catch_block} finally { finally_block }`
     48 //     TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally,
     49 //                         TryEmitter::ControlKind::Syntactic);
     50 //     tryCatch.emitTry();
     51 //     emit(try_block);
     52 //     tryCatch.emitCatch();
     53 //     emit(catch_block);
     54 //     tryCatch.emitFinally(Some(finally_pos));
     55 //     emit(finally_block);
     56 //     tryCatch.emitEnd();
     57 //
     58 class TryEmitter {
     59 public:
     60  enum class Kind { TryCatch, TryCatchFinally, TryFinally };
     61 
     62  // Syntactic try-catch-finally and internally used non-syntactic
     63  // try-catch-finally behave differently for 2 points.
     64  //
     65  // The first one is whether TryFinallyControl is used or not.
     66  // See the comment for `controlInfo_`.
     67  //
     68  // The second one is whether the catch and finally blocks handle the frame's
     69  // return value.  For syntactic try-catch-finally, the bytecode marked with
     70  // "*" are emitted to clear return value with `undefined` before the catch
     71  // block and the finally block, and also to save/restore the return value
     72  // before/after the finally block. Note that these instructions are not
     73  // emitted for noScriptRval scripts that don't track the return value.
     74  //
     75  //     JSOp::Try offsetOf(jumpToEnd)
     76  //
     77  //     try_body...
     78  //
     79  //     JSOp::Goto finally
     80  //     JSOp::JumpTarget
     81  //   jumpToEnd:
     82  //     JSOp::Goto end:
     83  //
     84  //   catch:
     85  //     JSOp::JumpTarget
     86  //   * JSOp::Undefined
     87  //   * JSOp::SetRval
     88  //
     89  //     catch_body...
     90  //
     91  //     JSOp::Goto finally
     92  //     JSOp::JumpTarget
     93  //     JSOp::Goto end
     94  //
     95  //   finally:
     96  //     JSOp::JumpTarget
     97  //   * JSOp::GetRval
     98  //   * JSOp::Undefined
     99  //   * JSOp::SetRval
    100  //
    101  //     finally_body...
    102  //
    103  //   * JSOp::SetRval
    104  //     JSOp::Nop
    105  //
    106  //   end:
    107  //     JSOp::JumpTarget
    108  //
    109  // For syntactic try-catch-finally, Syntactic should be used.
    110  // For non-syntactic try-catch-finally, NonSyntactic should be used.
    111  // For non-syntactic try-catch-finally for Explicit Resource Management
    112  // Disposal kind should be used.
    113  enum class ControlKind {
    114    Syntactic,
    115    NonSyntactic,
    116 
    117    // Disposal kind is exactly same in behaviour of Syntactic kind, it is
    118    // used enabling try-finally scope for Explicit Resource Management
    119    // Proposal. (https://arai-a.github.io/ecma262-compare/?pr=3000)
    120    Disposal,
    121  };
    122 
    123 private:
    124  BytecodeEmitter* bce_;
    125  Kind kind_;
    126  ControlKind controlKind_;
    127 
    128  // Tracks jumps to the finally block for later fixup.
    129  //
    130  // When a finally block is active, non-local jumps (including
    131  // jumps-over-catches) result in a goto being written into the bytecode
    132  // stream and fixed-up later.
    133  //
    134  // For non-syntactic try-catch-finally, all that handling is skipped.
    135  // The non-syntactic try-catch-finally must:
    136  //   * have only one catch block
    137  //   * have JSOp::Goto at the end of catch-block
    138  //   * have no non-local-jump
    139  //   * don't use finally block for normal completion of try-block and
    140  //     catch-block
    141  //
    142  // Additionally, a finally block may be emitted for non-syntactic
    143  // try-catch-finally, even if the kind is TryCatch, because JSOp::Goto is
    144  // not emitted.
    145  js::UniquePtr<TryFinallyControl> controlInfo_;
    146 
    147  // The stack depth before emitting JSOp::Try.
    148  int depth_;
    149 
    150  // The offset of the JSOp::Try op.
    151  BytecodeOffset tryOpOffset_;
    152 
    153  // JSOp::JumpTarget after the entire try-catch-finally block.
    154  JumpList catchAndFinallyJump_;
    155 
    156  // The offset of JSOp::Goto at the end of the try block.
    157  JumpTarget tryEnd_;
    158 
    159  // The offset of JSOp::JumpTarget at the beginning of the finally block.
    160  JumpTarget finallyStart_;
    161 
    162 #ifdef DEBUG
    163  // The state of this emitter.
    164  //
    165  // +-------+ emitTry +-----+   emitCatch +-------+      emitEnd  +-----+
    166  // | Start |-------->| Try |-+---------->| Catch |-+->+--------->| End |
    167  // +-------+         +-----+ |           +-------+ |  ^          +-----+
    168  //                           |                     |  |
    169  //                           |  +------------------+  +----+
    170  //                           |  |                          |
    171  //                           |  v emitFinally +---------+  |
    172  //                           +->+------------>| Finally |--+
    173  //                                            +---------+
    174  enum class State {
    175    // The initial state.
    176    Start,
    177 
    178    // After calling emitTry.
    179    Try,
    180 
    181    // After calling emitCatch.
    182    Catch,
    183 
    184    // After calling emitFinally.
    185    Finally,
    186 
    187    // After calling emitEnd.
    188    End
    189  };
    190  State state_;
    191 #endif
    192 
    193  bool hasCatch() const {
    194    return kind_ == Kind::TryCatch || kind_ == Kind::TryCatchFinally;
    195  }
    196  bool hasFinally() const {
    197    return kind_ == Kind::TryCatchFinally || kind_ == Kind::TryFinally;
    198  }
    199 
    200  bool requiresControlInfo() const {
    201    return controlKind_ == ControlKind::Syntactic ||
    202           controlKind_ == ControlKind::Disposal;
    203  }
    204 
    205  BytecodeOffset offsetAfterTryOp() const {
    206    return tryOpOffset_ + BytecodeOffsetDiff(JSOpLength_Try);
    207  }
    208 
    209  // Jump to the finally block. After the finally block executes,
    210  // fall through to the code following the finally block.
    211  [[nodiscard]] bool emitJumpToFinallyWithFallthrough();
    212 
    213 public:
    214  TryEmitter(BytecodeEmitter* bce, Kind kind, ControlKind controlKind);
    215 
    216  // Returns true if catch and finally blocks should handle the frame's
    217  // return value.
    218  bool shouldUpdateRval() const;
    219 
    220 #ifdef DEBUG
    221  bool hasControlInfo();
    222 #endif
    223 
    224  [[nodiscard]] bool emitTry();
    225 
    226  enum class ExceptionStack : bool {
    227    /**
    228     * Push only the pending exception value.
    229     */
    230    No,
    231 
    232    /**
    233     * Push the pending exception value and its stack.
    234     */
    235    Yes,
    236  };
    237 
    238  [[nodiscard]] bool emitCatch(ExceptionStack stack = ExceptionStack::No);
    239 
    240  // If `finallyPos` is specified, it's an offset of the finally block's
    241  // "{" character in the source code text, to improve line:column number in
    242  // the error reporting.
    243  // For non-syntactic try-catch-finally, `finallyPos` can be omitted.
    244  [[nodiscard]] bool emitFinally(
    245      const mozilla::Maybe<uint32_t>& finallyPos = mozilla::Nothing());
    246 
    247  [[nodiscard]] bool emitEnd();
    248 
    249 private:
    250  [[nodiscard]] bool emitTryEnd();
    251  [[nodiscard]] bool emitCatchEnd();
    252  [[nodiscard]] bool emitFinallyEnd();
    253 };
    254 
    255 } /* namespace frontend */
    256 } /* namespace js */
    257 
    258 #endif /* frontend_TryEmitter_h */