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. This binary checks for style issues such as incorrect or missing JSDoc usage, and missing goog.require() statements. It does not do more advanced checks such as typechecking.

There is a newer version: v20200830
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 functions by
 * means of the FunctionNamer interface.  Anonymous functions are
 * named based on context.  For example, the anonymous function on the
 * RHS based on the property at the LHS of the assignment operator.
 *
 * goog.string.htmlEscape = function(str) {
 * }
 *
 */
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.
     * @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()) {
          if (parent.isAssign()) {
            // this is an assignment to a property, generally either a
            // static function or a prototype function
            // e.g. goog.string.htmlEscape = function() { } or
            //      goog.structs.Map.prototype.getCount = 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;
      case CLASS:
        // this handle functions that are class methods.
        // e.g. class A{
        //        method(){}
        //      }
        // or (var) A = class{
        //      method(){}
        //    }
        Node classMembersNode = n.getLastChild();
        nameClassMethods(classMembersNode, namer.getName(n));
        break;
      default:
        break;
    }
  }

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

      // Object literal keys may be STRING_KEY, GETTER_DEF, SETTER_DEF,
      // MEMBER_FUNCTION_DEF (Shorthand function definition) or COMPUTED_PROP.
      // Get, Set and CompProp are skipped because they can not be named.
      if (keyNode.isStringKey() || keyNode.isMemberFunctionDef()) {
        // concatenate the context and key name to get a new qualified name.
        String name = namer.getCombinedName(context, namer.getName(keyNode));

        Token type = valueNode.getToken();
        if (type == Token.FUNCTION) {
          // 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);
        }
      }
    }
  }

  private void nameClassMethods(Node classMembersNode, String className) {
    for (Node methodNode : classMembersNode.children()) {
      if (methodNode.isMemberFunctionDef()) {
        Node valueNode = methodNode.getFirstChild();
        String name = namer.getCombinedName(className, namer.getName(methodNode));
        namer.setFunctionName(name, valueNode);
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy