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