DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
/* -*- 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 "mozilla/Attributes.h"

#include "jsfriendapi.h"

#include "frontend/ParseNode.h"

namespace js {
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.
 * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
 */
template <typename Derived>
class ParseNodeVisitor {
 public:
  JSContext* cx_;

  explicit ParseNodeVisitor(JSContext* cx) : cx_(cx) {}

  MOZ_MUST_USE bool visit(ParseNode* pn) {
    if (!CheckRecursionLimit(cx_)) {
      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)                         \
  MOZ_MUST_USE 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:
  JSContext* cx_;

  explicit RewritingParseNodeVisitor(JSContext* cx) : cx_(cx) {}

  MOZ_MUST_USE bool visit(ParseNode*& pn) {
    if (!CheckRecursionLimit(cx_)) {
      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)                                 \
  MOZ_MUST_USE 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