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/. */
7 #ifndef frontend_ParseNodeVisitor_h
8 #define frontend_ParseNodeVisitor_h
10 #include "mozilla/Assertions.h"
12 #include "frontend/ParseNode.h"
13 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
17 class FrontendContext
;
22 * Utility class for walking a JS AST.
26 * class HowTrueVisitor : public ParseNodeVisitor<HowTrueVisitor> {
28 * bool visitTrueExpr(BooleanLiteral* pn) {
29 * std::cout << "How true.\n";
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);
43 * v.visit(programRootNode); // walks the entire tree
45 * A ParseNodeVisitor can modify nodes, but it can't replace the current node
46 * with a different one; for that, use a RewritingParseNodeVisitor.
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
53 template <typename Derived
>
54 class ParseNodeVisitor
{
58 explicit ParseNodeVisitor(FrontendContext
* fc
) : fc_(fc
) {}
60 [[nodiscard
]] bool visit(ParseNode
* pn
) {
61 AutoCheckRecursionLimit
recursion(fc_
);
62 if (!recursion
.check(fc_
)) {
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
)
73 MOZ_CRASH("invalid node kind");
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)); \
82 FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD
)
87 * Utility class for walking a JS AST that allows for replacing nodes.
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.
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.
98 template <typename Derived
>
99 class RewritingParseNodeVisitor
{
101 FrontendContext
* fc_
;
103 explicit RewritingParseNodeVisitor(FrontendContext
* fc
) : fc_(fc
) {}
105 [[nodiscard
]] bool visit(ParseNode
*& pn
) {
106 AutoCheckRecursionLimit
recursion(fc_
);
107 if (!recursion
.check(fc_
)) {
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
)
118 MOZ_CRASH("invalid node kind");
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)); \
129 FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD
)
133 } // namespace frontend
136 #endif // frontend_ParseNodeVisitor_h