tor-browser

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

ParseNodeVisitor.h (4447B)


      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_ParseNodeVisitor_h
      8 #define frontend_ParseNodeVisitor_h
      9 
     10 #include "mozilla/Assertions.h"
     11 
     12 #include "frontend/ParseNode.h"
     13 #include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit
     14 
     15 namespace js {
     16 
     17 class FrontendContext;
     18 
     19 namespace frontend {
     20 
     21 /**
     22 * Utility class for walking a JS AST.
     23 *
     24 * Simple usage:
     25 *
     26 *     class HowTrueVisitor : public ParseNodeVisitor<HowTrueVisitor> {
     27 *     public:
     28 *       bool visitTrueExpr(BooleanLiteral* pn) {
     29 *         std::cout << "How true.\n";
     30 *         return true;
     31 *       }
     32 *       bool visitClassDecl(ClassNode* pn) {
     33 *         // The base-class implementation of each visit method
     34 *         // simply visits the node's children. So the subclass
     35 *         // gets to decide whether to descend into a subtree
     36 *         // and can do things either before or after:
     37 *         std::cout << "How classy.\n";
     38 *         return ParseNodeVisitor::visitClassDecl(pn);
     39 *       }
     40 *     };
     41 *
     42 *     HowTrueVisitor v;
     43 *     v.visit(programRootNode);  // walks the entire tree
     44 *
     45 * A ParseNodeVisitor can modify nodes, but it can't replace the current node
     46 * with a different one; for that, use a RewritingParseNodeVisitor.
     47 *
     48 * Note that the Curiously Recurring Template Pattern is used for performance,
     49 * as it eliminates the need for virtual method calls. Some rough testing shows
     50 * about a 12% speedup in the FoldConstants.cpp pass.
     51 * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
     52 */
     53 template <typename Derived>
     54 class ParseNodeVisitor {
     55 public:
     56  FrontendContext* fc_;
     57 
     58  explicit ParseNodeVisitor(FrontendContext* fc) : fc_(fc) {}
     59 
     60  [[nodiscard]] bool visit(ParseNode* pn) {
     61    AutoCheckRecursionLimit recursion(fc_);
     62    if (!recursion.check(fc_)) {
     63      return false;
     64    }
     65 
     66    switch (pn->getKind()) {
     67 #define VISIT_CASE(KIND, TYPE) \
     68  case ParseNodeKind::KIND:    \
     69    return static_cast<Derived*>(this)->visit##KIND(&pn->as<TYPE>());
     70      FOR_EACH_PARSE_NODE_KIND(VISIT_CASE)
     71 #undef VISIT_CASE
     72      default:
     73        MOZ_CRASH("invalid node kind");
     74    }
     75  }
     76 
     77  // using static_cast<Derived*> here allows plain visit() to be overridden.
     78 #define VISIT_METHOD(KIND, TYPE)                          \
     79  [[nodiscard]] bool visit##KIND(TYPE* pn) { /* NOLINT */ \
     80    return pn->accept(*static_cast<Derived*>(this));      \
     81  }
     82  FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD)
     83 #undef VISIT_METHOD
     84 };
     85 
     86 /*
     87 * Utility class for walking a JS AST that allows for replacing nodes.
     88 *
     89 * The API is the same as ParseNodeVisitor, except that visit methods take an
     90 * argument of type `ParseNode*&`, a reference to the field in the parent node
     91 * that points down to the node being visited. Methods can replace the current
     92 * node by assigning to this reference.
     93 *
     94 * All visit methods take a `ParseNode*&` rather than more specific types like
     95 * `BinaryNode*&`, to allow replacing the current node with one of a different
     96 * type. Constant folding makes use of this.
     97 */
     98 template <typename Derived>
     99 class RewritingParseNodeVisitor {
    100 public:
    101  FrontendContext* fc_;
    102 
    103  explicit RewritingParseNodeVisitor(FrontendContext* fc) : fc_(fc) {}
    104 
    105  [[nodiscard]] bool visit(ParseNode*& pn) {
    106    AutoCheckRecursionLimit recursion(fc_);
    107    if (!recursion.check(fc_)) {
    108      return false;
    109    }
    110 
    111    switch (pn->getKind()) {
    112 #define VISIT_CASE(KIND, _type) \
    113  case ParseNodeKind::KIND:     \
    114    return static_cast<Derived*>(this)->visit##KIND(pn);
    115      FOR_EACH_PARSE_NODE_KIND(VISIT_CASE)
    116 #undef VISIT_CASE
    117      default:
    118        MOZ_CRASH("invalid node kind");
    119    }
    120  }
    121 
    122  // using static_cast<Derived*> here allows plain visit() to be overridden.
    123 #define VISIT_METHOD(KIND, TYPE)                                 \
    124  [[nodiscard]] bool visit##KIND(ParseNode*& pn) {               \
    125    MOZ_ASSERT(pn->is<TYPE>(),                                   \
    126               "Node of kind " #KIND " was not of type " #TYPE); \
    127    return pn->as<TYPE>().accept(*static_cast<Derived*>(this));  \
    128  }
    129  FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD)
    130 #undef VISIT_METHOD
    131 };
    132 
    133 }  // namespace frontend
    134 }  // namespace js
    135 
    136 #endif  // frontend_ParseNodeVisitor_h