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

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

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2018 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.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticRef;
import com.google.javascript.rhino.StaticSlot;
import com.google.javascript.rhino.StaticSourceFile;
import com.google.javascript.rhino.Token;
import java.util.Objects;

/**
 * Used by {@code Scope} to store information about variables.
 */
public class AbstractVar, V extends AbstractVar>
    implements StaticSlot, StaticRef {

  final String name;

  /** Var node */
  final Node nameNode;

  /** Input source */
  final CompilerInput input;

  /**
   * The index at which the var is declared. e.g. if it's 0, it's the first
   * declared variable in that scope
   */
  final int index;

  /** The enclosing scope */
  final S scope;

  AbstractVar(String name, Node nameNode, S scope, int index, CompilerInput input) {
    this.name = name;
    this.nameNode = nameNode;
    this.scope = scope;
    this.index = index;
    this.input = input;
  }

  // Non-final for jsdev tests
  @Override
  public String getName() {
    return name;
  }

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

  final CompilerInput getInput() {
    return input;
  }

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

  @Override
  public final V getSymbol() {
    return thisVar();
  }

  @Override
  public final V getDeclaration() {
    return nameNode == null ? null : thisVar();
  }

  public final Node getParentNode() {
    return nameNode == null ? null : nameNode.getParent();
  }

  /**
   * Whether this is a bleeding function (an anonymous named function
   * that bleeds into the inner scope).
   */
  public boolean isBleedingFunction() {
    return NodeUtil.isFunctionExpression(getParentNode());
  }

  public final S getScope() {
    return scope;
  }

  // Non-final for jsdev tests
  public boolean isGlobal() {
    return scope.isGlobal();
  }

  public final boolean isLocal() {
    return scope.isLocal();
  }

  final boolean isExtern() {
    return input == null || input.isExtern();
  }

  /**
   * Returns {@code true} if the variable is declared as a constant,
   * based on the value reported by {@code NodeUtil}.
   */
  public final boolean isInferredConst() {
    if (nameNode == null) {
      return false;
    }

    return nameNode.getBooleanProp(Node.IS_CONSTANT_VAR)
        || nameNode.getBooleanProp(Node.IS_CONSTANT_NAME);
  }

  /**
   * Returns {@code true} if the variable is declared as a define.
   * A variable is a define if it is annotated by {@code @define}.
   */
  public final boolean isDefine() {
    JSDocInfo info = getJSDocInfo();
    return info != null && info.isDefine();
  }

  public final Node getInitialValue() {
    return NodeUtil.getRValueOfLValue(nameNode);
  }

  // Non-final for jsdev tests
  public Node getNameNode() {
    return nameNode;
  }

  // Non-final for jsdev tests
  @Override
  public JSDocInfo getJSDocInfo() {
    return nameNode == null ? null : NodeUtil.getBestJSDocInfo(nameNode);
  }

  final boolean isVar() {
    return declarationType() == Token.VAR;
  }

  final boolean isCatch() {
    return declarationType() == Token.CATCH;
  }

  final boolean isLet() {
    return declarationType() == Token.LET;
  }

  final boolean isConst() {
    return declarationType() == Token.CONST;
  }

  final boolean isClass() {
    return declarationType() == Token.CLASS;
  }

  final boolean isParam() {
    return declarationType() == Token.PARAM_LIST;
  }

  public final boolean isDefaultParam() {
    Node parent = nameNode.getParent();
    return parent.getParent().isParamList() && parent.isDefaultValue()
        && parent.getFirstChild() == nameNode;
  }

  final boolean isImport() {
    return declarationType() == Token.IMPORT;
  }

  public boolean isArguments() {
    return false;
  }

  private static final ImmutableSet DECLARATION_TYPES = Sets.immutableEnumSet(
      Token.VAR,
      Token.LET,
      Token.CONST,
      Token.FUNCTION,
      Token.CLASS,
      Token.CATCH,
      Token.IMPORT,
      Token.PARAM_LIST);

  protected Token declarationType() {
    for (Node current = nameNode; current != null;
         current = current.getParent()) {
      if (DECLARATION_TYPES.contains(current.getToken())) {
        return current.getToken();
      }
    }
    throw new IllegalStateException("The nameNode for " + this + " must be a descendant"
        + " of one of: " + DECLARATION_TYPES);
  }

  // This is safe because any concrete subclass of AbstractVar should be assignable to V.
  // While it's theoretically possible to do otherwise, such a class would be very awkward to
  // implement, and is therefore not worth worrying about.
  @SuppressWarnings("unchecked")
  private V thisVar() {
    return (V) this;
  }

  // Non-final for jsdev tests
  @Override
  public boolean equals(Object other) {
    if (!(other instanceof AbstractVar)) {
      return false;
    }
    return Objects.equals(name, ((AbstractVar) other).name)
        && Objects.equals(scope.getRootNode(), ((AbstractVar) other).scope.getRootNode());
  }

  // Non-final for jsdev tests
  @Override
  public int hashCode() {
    return Objects.hash(name, scope.getRootNode());
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy