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

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

/*
 * Copyright 2014 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.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

/**
 * Attaches the CONST_VAR annotation to any variable that's
 * 1) Provably well-defined and assigned once in its lifetime.
 * 2) Annotated 'const'
 * 3) Declared with the 'const' keyword.
 * 4) Is constant by naming convention.
 *
 * These 3 are considered semantically equivalent. Notice that a variable
 * in a loop is never considered const.
 *
 * Note that criteria (1) is only used for normal code, not externs.
 */
class InferConsts implements CompilerPass {
  private final AbstractCompiler compiler;

  InferConsts(AbstractCompiler compiler) {
    this.compiler = compiler;
  }

  @Override
  public void process(Node externs, Node js) {
    ReferenceCollectingCallback collector =
        new ReferenceCollectingCallback(
            compiler,
            ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR,
            new SyntacticScopeCreator(compiler));
    collector.process(js);

    for (Var v : collector.getAllSymbols()) {
      considerVar(v, collector.getReferences(v));
    }

    Scope globalExternsScope = new SyntacticScopeCreator(compiler).createScope(externs, null);
    for (Var v : globalExternsScope.getAllSymbols()) {
      considerVar(v, null);
    }
  }

  private void considerVar(Var v, ReferenceCollection refCollection) {
    Node nameNode = v.getNameNode();
    JSDocInfo docInfo = v.getJSDocInfo();
    if (docInfo != null && docInfo.isConstant()) {
      nameNode.setDeclaredConstantVar(true);
    } else if (nameNode != null && nameNode.getParent().isConst()) {
      nameNode.setDeclaredConstantVar(true);
    } else if (nameNode != null
        && compiler.getCodingConvention().isConstant(nameNode.getString())) {
      nameNode.setDeclaredConstantVar(true);
    }
    if (isInferredConst(v, refCollection)) {
      nameNode.setInferredConstantVar(true);
    }
  }

  private static boolean isInferredConst(Var v, ReferenceCollection refCollection) {
    Node nameNode = v.getNameNode();
    if (nameNode == null || refCollection == null || !refCollection.isAssignedOnceInLifetime()) {
      return false;
    }
    Token declarationType = v.declarationType();

    switch (declarationType) {
      case LET:
        // Check that non-destructuring let names are actually assigned at declaration.
        return !nameNode.getParent().isLet() || nameNode.hasChildren();
      case CONST:
      case CATCH:
      case CLASS:
      case PARAM_LIST: // Parameters cannot be referenced before declaration.
      case FUNCTION: // Function hoisting means no references before declaration.
        return true;
      case IMPORT:
        // ES module exports are mutable.
        // TODO(lharker): make this check smarter if we start optimizing unrewritten modules.
        return false;
      case VAR:
        // var hoisting requires this extra work to make sure the 'declaration' is also the first
        // reference.
        return refCollection.firstReferenceIsAssigningDeclaration()
            && refCollection.isWellDefined();
      default:
        throw new IllegalStateException("Unrecognized declaration type " + declarationType);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy