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

com.google.javascript.jscomp.CodeConsumer 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 static com.google.common.base.Preconditions.checkState;

import com.google.errorprone.annotations.ForOverride;
import com.google.javascript.rhino.Node;

/**
 * Abstracted consumer of the CodeGenerator output.
 *
 * @see CodeGenerator
 * @see CodePrinter
 * @see InlineCostEstimator
 */
public abstract class CodeConsumer {

  boolean statementNeedsEnded = false;
  boolean statementStarted = false;
  boolean sawFunction = false;

  // State tracking for template literals. Remember that template literal substitutions can contain
  // additional template literals.
  private int templateLitDepth = 0;
  private int templateLitSubDepth = 0;

  /**
   * Starts the source mapping for the given
   * node at the current position.
   */
  void startSourceMapping(Node node) {
  }

  /**
   * Finishes the source mapping for the given
   * node at the current position.
   */
  void endSourceMapping(Node node) {
  }

  /**
   * Provides a means of interrupting the CodeGenerator. Derived classes should
   * return false to stop further processing.
   */
  boolean continueProcessing() {
    return true;
  }

  /**
   * Retrieve the last character of the last string sent to append.
   */
  abstract char getLastChar();

  /**
   * Appends a (possibly multiline) string to the code, keeping track of the current line length and
   * count.
   *
   * 

Clients should use this method rather than {@link #append()}. It sanitizes the input to * {@link #append()}. */ final void add(String newcode) { maybeEndStatement(); if (newcode.isEmpty()) { return; } char c = newcode.charAt(0); if ((isWordChar(c) || c == '\\') && isWordChar(getLastChar())) { // need space to separate. This is not pretty printing. // For example: "return foo;" append(" "); } else if (c == '/' && getLastChar() == '/') { // Do not allow a forward slash to appear after a DIV. // For example, // REGEXP DIV REGEXP // is valid and should print like // / // / / append(" "); } else if ((c == '"' || c == '\'') && isWordChar(getLastChar())) { maybeInsertSpace(); } // Iterate through the new code and add each contained line, followed by a break. Remember that // the string may start and end in the middle of a line. We do this rather primitively because // this method is called frequently and most invocations have only one line. // TODO(nickreid): There are other possible newline characters recognized by the JS spec. We // should also be considering them. int startOfLine = 0; int endOfLine = newcode.indexOf('\n'); while (endOfLine >= 0) { if (endOfLine > startOfLine) { // Append line only if it is non-empty. append(newcode.substring(startOfLine, endOfLine)); } // Breaking is non-optional. Newlines added this way must be preserved (e.g. newlines in // template literals). startNewLine(); startOfLine = endOfLine + 1; // Jump over the newline char. endOfLine = newcode.indexOf('\n', startOfLine); } if (newcode.length() > startOfLine) { // Append line only if it is non-empty. append(newcode.substring(startOfLine)); // Append the last or only line without breaking. } } /** * Appends a string to the code, keeping track of the current line length. * *

Clients should not call this method directly, but instead call add {@link #add()}. * *

The string must be a complete token; partial strings or partial regexes will run the risk of * being split across lines. * *

Implementations of this method need not consider newline characters in {@code str}. Such * characters should either be ignored or cause an {@link Exception}. */ @ForOverride abstract void append(String str); void addIdentifier(String identifier) { add(identifier); } void appendBlockStart() { append("{"); } void appendBlockEnd() { append("}"); } void startNewLine() { } void maybeLineBreak() { maybeCutLine(); } void maybeCutLine() { } void endLine() { } void notePreferredLineBreak() { } void beginBlock() { if (statementNeedsEnded) { append(";"); maybeLineBreak(); } appendBlockStart(); endLine(); statementNeedsEnded = false; } void endBlock() { endBlock(false); } void endBlock(boolean shouldEndLine) { appendBlockEnd(); if (shouldEndLine) { endLine(); } statementNeedsEnded = false; } void listSeparator() { add(","); maybeLineBreak(); } /** * Indicates the end of a statement and a ';' may need to be added. * But we don't add it now, in case we're at the end of a block (in which * case we don't have to add the ';'). * See maybeEndStatement() */ void endStatement() { endStatement(false); } void endStatement(boolean needSemiColon) { if (needSemiColon) { append(";"); maybeLineBreak(); statementNeedsEnded = false; } else if (statementStarted) { statementNeedsEnded = true; } } /** * This is to be called when we're in a statement. If the prev statement * needs to be ended, add a ';'. */ void maybeEndStatement() { // Add a ';' if we need to. if (statementNeedsEnded) { append(";"); maybeLineBreak(); endLine(); statementNeedsEnded = false; } statementStarted = true; } void endFunction(boolean statementContext) { sawFunction = true; if (statementContext) { endLine(); } } void endClass(boolean statementContext) { if (statementContext) { endLine(); } } void beginCaseBody() { append(":"); } void endCaseBody() { } final void beginTemplateLit() { checkState(templateLitDepth == templateLitSubDepth); maybeEndStatement(); append("`"); templateLitDepth++; } final void beginTemplateLitSub() { // This method pair exists because '$' behaves differently inside template literals. We want to // append it without sanitizing in the generic way {@link #add()} would. checkState(isInTemplateLiteral()); append("${"); templateLitSubDepth++; } final void endTemplateLitSub() { // This method pair exists because '$' behaves differently inside template literals. We want to // append it without sanitizing in the generic way {@link #add()} would. checkState(templateLitSubDepth > 0); checkState(templateLitDepth == templateLitSubDepth); append("}"); templateLitSubDepth--; } final void endTemplateLit() { checkState(templateLitDepth > 0); checkState(isInTemplateLiteral()); append("`"); templateLitDepth--; } final boolean isInTemplateLiteral() { // We're inside a template literal but not a substitution within that literal. return templateLitDepth == templateLitSubDepth + 1; } void appendOp(String op, boolean binOp) { append(op); } void addOp(String op, boolean binOp) { maybeEndStatement(); char first = op.charAt(0); char prev = getLastChar(); if ((first == '+' || first == '-') && prev == first) { // This is not pretty printing. This is to prevent misparsing of // things like "x + ++y" or "x++ + ++y" append(" "); } else if (Character.isLetter(first) && isWordChar(prev)) { // Make sure there is a space after e.g. instanceof , typeof append(" "); } else if (prev == '-' && first == '>' || prev == '<' && first == '!') { // Make sure that we don't emit "" append(" "); } // Allow formatting around the operator. appendOp(op, binOp); // Line breaking after an operator is always safe. Line breaking before an // operator on the other hand is not. We only line break after a bin op // because it looks strange. if (binOp) { maybeCutLine(); } } void addNumber(double x, Node n) { // This is not pretty printing. This is to prevent misparsing of x- -4 as // x--4 (which is a syntax error). char prev = getLastChar(); boolean negativeZero = isNegativeZero(x); if ((x < 0 || negativeZero) && prev == '-') { add(" "); } if (negativeZero) { addConstant("-0"); } else if ((long) x == x) { long value = (long) x; long mantissa = value; int exp = 0; if (Math.abs(x) >= 100) { while (mantissa / 10 * ((long) Math.pow(10, exp + 1)) == value) { mantissa /= 10; exp++; } } if (exp > 2) { addConstant(mantissa + "E" + exp); } else { long valueAbs = Math.abs(value); if (valueAbs > 1000000000000L && // Values <1E12 are shorter in decimal Long.toHexString(valueAbs).length() + 2 < Long.toString(valueAbs).length()) { addConstant((value < 0 ? "-" : "") + "0x" + Long.toHexString(valueAbs)); } else { addConstant(Long.toString(value)); } } } else { addConstant(String.valueOf(x).replace(".0E", "E").replaceFirst( "^(-?)0\\.", "$1.")); } } void addConstant(String newcode) { add(newcode); } static boolean isNegativeZero(double x) { return x == 0.0 && 1 / x < 0; } static boolean isWordChar(char ch) { return (ch == '_' || ch == '$' || Character.isLetterOrDigit(ch)); } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? Doing so can * help when pretty-printing the code, and permits putting a debugging * breakpoint on the statement inside the condition. * * @return {@boolean true} if such expressions should be wrapped */ boolean shouldPreserveExtraBlocks() { return false; } /** * Allows a consumer to insert spaces in locations where it is unnecessary * but may improve the readability of the code. This will be called in such * places as after a statement and before opening parentheses, or after the * end of a if block before the start of an else block. */ void maybeInsertSpace() {} /** * @return Whether the a line break can be added after the specified BLOCK. */ boolean breakAfterBlockFor(Node n, boolean statementContext) { return statementContext; } /** Called when we're at the end of a file. */ void endFile() {} }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy