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

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

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2008 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.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticRef;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.Token;
import java.io.Serializable;

/**
 * Represents a single declaration or reference to a variable. Note that references can only be used
 * with untyped scopes and traversals.
 */
public final class Reference implements StaticRef, Serializable {

  // Favor EnumSet over ImmutableSet for performance
  private static final ImmutableSet DECLARATION_PARENTS =
      Sets.immutableEnumSet(
          Token.VAR,
          Token.LET,
          Token.CONST,
          Token.PARAM_LIST,
          Token.FUNCTION,
          Token.CLASS,
          Token.CATCH);

  private final Node nameNode;
  private final BasicBlock basicBlock;
  private final Scope scope;
  private final InputId inputId;

  Reference(Node nameNode, NodeTraversal t, BasicBlock basicBlock) {
    this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId());
  }

  private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) {
    this.nameNode = nameNode;
    this.basicBlock = basicBlock;
    this.scope = scope;
    this.inputId = inputId;
  }

  @Override
  public String toString() {
    return nameNode.toString();
  }

  /**
   * Creates a variable reference in a given script file name, used in tests.
   *
   * @return The created reference.
   */
  @VisibleForTesting
  static Reference createRefForTest(CompilerInput input) {
    return new Reference(new Node(Token.NAME), null, null, input.getInputId());
  }

  /** Makes a copy of the current reference using a new Scope instance. */
  Reference cloneWithNewScope(Scope newScope) {
    return new Reference(nameNode, basicBlock, newScope, inputId);
  }

  @Override
  public Var getSymbol() {
    return scope.getVar(nameNode.getString());
  }

  @Override
  public Node getNode() {
    return nameNode;
  }

  public InputId getInputId() {
    return inputId;
  }

  @Override
  public StaticSourceFile getSourceFile() {
    return nameNode.getStaticSourceFile();
  }

  boolean isDeclaration() {
    return isDeclarationHelper(nameNode);
  }

  private static boolean isDeclarationHelper(Node node) {
    Node parent = node.getParent();

    // Special case for class B extends A, A is not a declaration.
    if (parent.isClass() && node != parent.getFirstChild()) {
      return false;
    }

    // This condition can be true during InlineVariables.
    if (parent.getParent() == null) {
      return false;
    }

    if (NodeUtil.isNameDeclaration(parent.getParent()) && node == parent.getSecondChild()) {
      // This is the RHS of a var/let/const and thus not a declaration.
      return false;
    }

    // Special cases for destructuring patterns.
    if (parent.isDestructuringLhs()
        || parent.isDestructuringPattern()
        || parent.isRest()
        || (parent.isStringKey() && parent.getParent().isObjectPattern())
        || (parent.isComputedProp()
            && parent.getParent().isObjectPattern()
            && node == parent.getLastChild())
        || (parent.isDefaultValue() && node == parent.getFirstChild())) {
      return isDeclarationHelper(parent);
    }

    if (parent.isImport()) {
      return true;
    }

    if (parent.isImportSpec() && node == parent.getLastChild()) {
      return true;
    }

    // Special case for arrow function
    if (parent.isArrowFunction()) {
      return node == parent.getFirstChild();
    }

    return DECLARATION_PARENTS.contains(parent.getToken());
  }

  public boolean isVarDeclaration() {
    return getParent().isVar();
  }

  boolean isLetDeclaration() {
    return getParent().isLet();
  }

  public boolean isConstDeclaration() {
    return getParent().isConst();
  }

  boolean isHoistedFunction() {
    return NodeUtil.isHoistedFunctionDeclaration(getParent());
  }

  /** Determines whether the variable is initialized at the declaration. */
  public boolean isInitializingDeclaration() {
    // VAR and LET are the only types of variable declarations that may not initialize
    // their variables. Catch blocks, named functions, and parameters all do.
    return (isDeclaration() && !getParent().isVar() && !getParent().isLet())
        || nameNode.hasChildren();
  }

  /**
   * @return For an assignment, variable declaration, or function declaration return the assigned
   *     value, otherwise null.
   */
  Node getAssignedValue() {
    return NodeUtil.getRValueOfLValue(nameNode);
  }

  BasicBlock getBasicBlock() {
    return basicBlock;
  }

  Node getParent() {
    return getNode().getParent();
  }

  Node getGrandparent() {
    return getNode().getGrandparent();
  }

  private static boolean isLhsOfEnhancedForExpression(Node n) {
    Node parent = n.getParent();
    return NodeUtil.isEnhancedFor(parent) && parent.getFirstChild() == n;
  }

  public boolean isSimpleAssignmentToName() {
    Node parent = getParent();
    return parent.isAssign() && parent.getFirstChild() == nameNode;
  }

  /**
   * Returns whether the name node for this reference is an lvalue. TODO(tbreisacher): This method
   * disagrees with NodeUtil#isLValue for "var x;" and "let x;". Consider updating it to match.
   */
  public boolean isLvalue() {
    Node parent = getParent();
    Token parentType = parent.getToken();
    switch (parentType) {
      case VAR:
      case LET:
      case CONST:
        return (nameNode.hasChildren() || isLhsOfEnhancedForExpression(nameNode));
      case DEFAULT_VALUE:
        return parent.getFirstChild() == nameNode;
      case INC:
      case DEC:
      case CATCH:
      case ITER_REST:
      case OBJECT_REST:
      case PARAM_LIST:
        return true;
      case FOR:
      case FOR_IN:
      case FOR_OF:
      case FOR_AWAIT_OF:
        return NodeUtil.isEnhancedFor(parent) && parent.getFirstChild() == nameNode;
      case ARRAY_PATTERN:
      case STRING_KEY:
      case COMPUTED_PROP:
        return NodeUtil.isLhsByDestructuring(nameNode);
      default:
        return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode);
    }
  }

  Scope getScope() {
    return scope;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy