Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef frontend_ParseNodeVisitor_h
#define frontend_ParseNodeVisitor_h
#include "mozilla/Assertions.h"
#include "frontend/ParseNode.h"
#include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
namespace js {
class FrontendContext;
namespace frontend {
/**
* Utility class for walking a JS AST.
*
* Simple usage:
*
* class HowTrueVisitor : public ParseNodeVisitor<HowTrueVisitor> {
* public:
* bool visitTrueExpr(BooleanLiteral* pn) {
* std::cout << "How true.\n";
* return true;
* }
* bool visitClassDecl(ClassNode* pn) {
* // The base-class implementation of each visit method
* // simply visits the node's children. So the subclass
* // gets to decide whether to descend into a subtree
* // and can do things either before or after:
* std::cout << "How classy.\n";
* return ParseNodeVisitor::visitClassDecl(pn);
* }
* };
*
* HowTrueVisitor v;
* v.visit(programRootNode); // walks the entire tree
*
* A ParseNodeVisitor can modify nodes, but it can't replace the current node
* with a different one; for that, use a RewritingParseNodeVisitor.
*
* Note that the Curiously Recurring Template Pattern is used for performance,
* as it eliminates the need for virtual method calls. Some rough testing shows
* about a 12% speedup in the FoldConstants.cpp pass.
*/
template <typename Derived>
class ParseNodeVisitor {
public:
FrontendContext* fc_;
explicit ParseNodeVisitor(FrontendContext* fc) : fc_(fc) {}
[[nodiscard]] bool visit(ParseNode* pn) {
AutoCheckRecursionLimit recursion(fc_);
if (!recursion.check(fc_)) {
return false;
}
switch (pn->getKind()) {
#define VISIT_CASE(KIND, TYPE) \
case ParseNodeKind::KIND: \
return static_cast<Derived*>(this)->visit##KIND(&pn->as<TYPE>());
FOR_EACH_PARSE_NODE_KIND(VISIT_CASE)
#undef VISIT_CASE
default:
MOZ_CRASH("invalid node kind");
}
}
// using static_cast<Derived*> here allows plain visit() to be overridden.
#define VISIT_METHOD(KIND, TYPE) \
[[nodiscard]] bool visit##KIND(TYPE* pn) { /* NOLINT */ \
return pn->accept(*static_cast<Derived*>(this)); \
}
FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD)
#undef VISIT_METHOD
};
/*
* Utility class for walking a JS AST that allows for replacing nodes.
*
* The API is the same as ParseNodeVisitor, except that visit methods take an
* argument of type `ParseNode*&`, a reference to the field in the parent node
* that points down to the node being visited. Methods can replace the current
* node by assigning to this reference.
*
* All visit methods take a `ParseNode*&` rather than more specific types like
* `BinaryNode*&`, to allow replacing the current node with one of a different
* type. Constant folding makes use of this.
*/
template <typename Derived>
class RewritingParseNodeVisitor {
public:
FrontendContext* fc_;
explicit RewritingParseNodeVisitor(FrontendContext* fc) : fc_(fc) {}
[[nodiscard]] bool visit(ParseNode*& pn) {
AutoCheckRecursionLimit recursion(fc_);
if (!recursion.check(fc_)) {
return false;
}
switch (pn->getKind()) {
#define VISIT_CASE(KIND, _type) \
case ParseNodeKind::KIND: \
return static_cast<Derived*>(this)->visit##KIND(pn);
FOR_EACH_PARSE_NODE_KIND(VISIT_CASE)
#undef VISIT_CASE
default:
MOZ_CRASH("invalid node kind");
}
}
// using static_cast<Derived*> here allows plain visit() to be overridden.
#define VISIT_METHOD(KIND, TYPE) \
[[nodiscard]] bool visit##KIND(ParseNode*& pn) { \
MOZ_ASSERT(pn->is<TYPE>(), \
"Node of kind " #KIND " was not of type " #TYPE); \
return pn->as<TYPE>().accept(*static_cast<Derived*>(this)); \
}
FOR_EACH_PARSE_NODE_KIND(VISIT_METHOD)
#undef VISIT_METHOD
};
} // namespace frontend
} // namespace js
#endif // frontend_ParseNodeVisitor_h