All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.AnonymousFunctionNamingCallback Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2004 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
 * Visitor that performs naming operations on anonymous function by
 * means of the FunctionNamer interface.  Anonymous functions are
 * named based on context.  For example, the anonymous function below
 * would be given a name generated from goog.string.htmlEscape by the FunctionNamer.
 *
 * goog.string.htmlEscape = function(str) {
 * }
 *
 * This pass does not try to name FUNCTIONs with empty NAME nodes if doing so would violate AST
 * validity. Currently, we can never name arrow functions, which must stay anonymous, or getters,
 * setters, and member function definitions, which have a name elsewhere in the AST.
 */
class AnonymousFunctionNamingCallback
    extends AbstractPostOrderCallback {
  private final FunctionNamer namer;

  /**
   * Interface used by AnonymousFunctionNamingCallback to set the name
   * of anonymous functions.
   */
  interface FunctionNamer {
    /**
     * Generates a string representation of a node for use by
     * setFunctionName.
     */
    String getName(Node node);

    /**
     * Sets the name of an anonymous function. Will only ever be called if the fnNode can be named
     * without making the AST invalid.
     * @param fnNode The function node to update
     * @param name The name
     */
    void setFunctionName(String name, Node fnNode);

    /**
     * Generate a name by "concatenating" the output of multiple calls
     * to getName.
     */
    String getCombinedName(String lhs, String rhs);
  }

  AnonymousFunctionNamingCallback(FunctionNamer namer) {
    this.namer = namer;
  }

  @Override
  public void visit(NodeTraversal t, Node n, Node parent) {
    switch (n.getToken()) {
      case FUNCTION:
        // this handles functions that are assigned to variables or
        // properties
        // e.g. goog.string.htmlEscape = function(str) {
        // }

        // get the function name and see if it's empty
        Node functionNameNode = n.getFirstChild();
        String functionName = functionNameNode.getString();
        if (functionName.isEmpty() && !n.isArrowFunction()) {
          if (parent.isAssign() || parent.isDefaultValue()) {
            // this is an assignment to a property, generally either a
            // static function or a prototype function, or a potential assignment of
            // a default value
            // e.g. goog.string.htmlEscape = function() { }
            //      goog.structs.Map.prototype.getCount = function() { } or
            //      function f(g = function() {}) { }
            Node lhs = parent.getFirstChild();
            String name = namer.getName(lhs);
            namer.setFunctionName(name, n);
          } else if (parent.isName()) {
            // this is an assignment to a variable
            // e.g. var handler = function() {}
            String name = namer.getName(parent);
            namer.setFunctionName(name, n);
          }
        }
        break;
      case ASSIGN:
        // this handles functions that are assigned to a prototype through
        // an object literal
        // e.g. BuzzApp.prototype = {
        //        Start : function() { }
        //      }
        Node lhs = n.getFirstChild();
        Node rhs = lhs.getNext();
        if (rhs.isObjectLit()) {
          nameObjectLiteralMethods(rhs, namer.getName(lhs));
        }
        break;
      default:
        break;
    }
  }

  private void nameObjectLiteralMethods(Node objectLiteral, String context) {
    for (Node keyNode = objectLiteral.getFirstChild();
         keyNode != null;
         keyNode = keyNode.getNext()) {

      // Object literal keys may be STRING_KEY, GETTER_DEF, SETTER_DEF,
      // MEMBER_FUNCTION_DEF (Shorthand function definition) or COMPUTED_PROP.
      // Getters, setters, and member function defs are skipped because their FUNCTION nodes must
      // have empty NAME nodes (currently enforced by CodeGenerator).
      if (keyNode.isStringKey() || keyNode.isComputedProp()) {
        // concatenate the context and key name to get a new qualified name.
        String name = namer.getCombinedName(context, namer.getName(keyNode));

        // computed property has 2 children -- index expression and value expression
        Node valueNode = keyNode.isStringKey() ? keyNode.getOnlyChild() : keyNode.getLastChild();

        Token type = valueNode.getToken();
        if (type == Token.FUNCTION && !valueNode.isArrowFunction()) {
          // set name if function is anonymous
          Node functionNameNode = valueNode.getFirstChild();
          String functionName = functionNameNode.getString();
          if (functionName.isEmpty()) {
            namer.setFunctionName(name, valueNode);
          }
        } else if (type == Token.OBJECTLIT) {
          // process nested object literal
          nameObjectLiteralMethods(valueNode, name);
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy