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

com.google.javascript.jscomp.newtypes.EnumType 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 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.newtypes;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import java.util.Collection;

/**
 *
 * @author [email protected] (Ben Lickly)
 * @author [email protected] (Dimitris Vardoulakis)
 *
 * Represents an enumerated type.
 * Each enum declaration produces two types of interest:
 * - We represent the object literal that defined the enum as an ObjectType.
 * - We represent an element of the enum by using this class in JSType.
 */
public final class EnumType extends Namespace implements TypeWithProperties {

  private enum State {
    NOT_RESOLVED,
    DURING_RESOLUTION,
    RESOLVED
  }

  private State state;
  private JSTypeExpression typeExpr;
  // The type that accompanies the enum declaration
  private JSType declaredType;
  // The type of the enum's properties, a subtype of the previous field.
  private JSType enumPropType;
  // All properties have the same type, so we only need a set, not a map.
  private ImmutableSet props;

  private EnumType(JSTypes commonTypes, String name, Node defSite,
      JSTypeExpression typeExpr, Collection props) {
    super(commonTypes, name, defSite);
    Preconditions.checkNotNull(typeExpr);
    this.state = State.NOT_RESOLVED;
    // typeExpr is non-null iff the enum is not resolved
    this.typeExpr = typeExpr;
    this.props = ImmutableSet.copyOf(props);
  }

  public static EnumType make(JSTypes commonTypes, String name, Node defSite,
      JSTypeExpression typeExpr, Collection props) {
    return new EnumType(commonTypes, name, defSite, typeExpr, props);
  }

  public boolean isResolved() {
    return this.state == State.RESOLVED;
  }

  JSTypes getCommonTypes() {
    return this.commonTypes;
  }

  public JSType getEnumeratedType() {
    Preconditions.checkState(this.state == State.RESOLVED);
    return declaredType;
  }

  public JSType getPropType() {
    Preconditions.checkState(this.state == State.RESOLVED);
    return enumPropType;
  }

  // Returns null iff there is a type cycle
  public JSTypeExpression getTypeExpr() {
    Preconditions.checkState(this.state != State.RESOLVED);
    if (this.state == State.DURING_RESOLUTION) {
      return null;
    }
    this.state = State.DURING_RESOLUTION;
    return typeExpr;
  }

  public JSTypeExpression getTypeExprForErrorReporting() {
    Preconditions.checkState(this.state == State.DURING_RESOLUTION);
    return typeExpr;
  }

  void resolveEnum(JSType t) {
    Preconditions.checkNotNull(t);
    if (this.state == State.RESOLVED) {
      return;
    }
    Preconditions.checkState(this.state == State.DURING_RESOLUTION,
        "Expected state DURING_RESOLUTION but found %s", this.state.toString());
    this.state = State.RESOLVED;
    typeExpr = null;
    declaredType = t;
    enumPropType = JSType.fromEnum(this);
  }

  /**
   * When defining an enum such as
   *   /** @enum {number} * /
   *   var X = { ONE: 1, TWO: 2 };
   * the properties of the object literal are constant.
   */
  @Override
  protected JSType computeJSType() {
    Preconditions.checkNotNull(enumPropType);
    Preconditions.checkState(this.namespaceType == null);
    PersistentMap propMap = PersistentMap.create();
    for (String s : this.props) {
      propMap = propMap.with(s,
          Property.makeConstant(null, enumPropType, enumPropType));
    }
    return JSType.fromObjectType(ObjectType.makeObjectType(
        this.commonTypes, this.commonTypes.getLiteralObjNominalType(), propMap,
        null, this, false, ObjectKind.UNRESTRICTED));
  }

  @Override
  public JSType getProp(QualifiedName qname) {
    return declaredType.getProp(qname);
  }

  @Override
  public JSType getDeclaredProp(QualifiedName qname) {
    return declaredType.getDeclaredProp(qname);
  }

  @Override
  public boolean mayHaveProp(QualifiedName qname) {
    return declaredType.mayHaveProp(qname);
  }

  @Override
  public boolean hasProp(QualifiedName qname) {
    return declaredType.hasProp(qname);
  }

  @Override
  public boolean hasConstantProp(QualifiedName qname) {
    return declaredType.hasConstantProp(qname);
  }

  // Unlike hasProp, this method asks about the object literal in the enum
  // definition, not about the declared type of the enum.
  public boolean enumLiteralHasKey(String name) {
    return props.contains(name);
  }

  static boolean hasScalar(ImmutableSet enums) {
    for (EnumType e : enums) {
      if (e.declaredType.hasScalar()) {
        return true;
      }
    }
    return false;
  }

  static boolean hasNonScalar(ImmutableSet enums) {
    for (EnumType e : enums) {
      if (e.declaredType.hasNonScalar()) {
        return true;
      }
    }
    return false;
  }

  static ImmutableSet union(
      ImmutableSet s1, ImmutableSet s2) {
    if (s1.isEmpty()) {
      return s2;
    }
    if (s2.isEmpty() || s1.equals(s2)) {
      return s1;
    }
    return Sets.union(s1, s2).immutableCopy();
  }

  // We normalize the type so that it doesn't contain both enum {T1} and T1.
  static ImmutableSet normalizeForJoin(
      ImmutableSet newEnums, JSType joinWithoutEnums) {
    boolean recreateEnums = false;
    for (EnumType e : newEnums) {
      if (e.declaredType.isSubtypeOf(joinWithoutEnums, SubtypeCache.create())) {
        recreateEnums = true;
        break;
      }
    }
    if (!recreateEnums) {
      return newEnums;
    }
    ImmutableSet.Builder builder = ImmutableSet.builder();
    for (EnumType e : newEnums) {
      if (!e.declaredType.isSubtypeOf(joinWithoutEnums, SubtypeCache.create())) {
        builder.add(e);
      }
    }
    return builder.build();
  }

  static boolean areSubtypes(JSType t1, JSType t2, SubtypeCache subSuperMap) {
    ImmutableSet s1 = t1.getEnums();
    if (s1 == null) {
      return true;
    }
    ImmutableSet s2 = t2.getEnums();
    for (EnumType e : s1) {
      if (s2 != null && s2.contains(e)) {
        continue;
      }
      if (!e.declaredType.isSubtypeOf(t2, subSuperMap)) {
        return false;
      }
    }
    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy