static ImmutableSet<ObjectType> withLooseObjects(Set<ObjectType> objs) {
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj : objs) {
     newObjs.add(obj.withLoose());
   }
   return newObjs.build();
 }
 static ImmutableSet<ObjectType> withoutProperty(Set<ObjectType> objs, QualifiedName qname) {
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj : objs) {
     newObjs.add(obj.withProperty(qname, null));
   }
   return newObjs.build();
 }
 static ImmutableSet<ObjectType> withPropertyRequired(Set<ObjectType> objs, String pname) {
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj : objs) {
     newObjs.add(obj.withPropertyRequired(pname));
   }
   return newObjs.build();
 }
  // We never infer properties as optional on loose objects,
  // and we don't warn about possibly inexistent properties.
  boolean isLooseSubtypeOf(ObjectType other) {
    Preconditions.checkState(isLoose || other.isLoose);
    if (other == TOP_OBJECT) {
      return true;
    }

    if (!isLoose) {
      if (!objectKind.isSubtypeOf(other.objectKind)) {
        return false;
      }
      for (String pname : other.props.keySet()) {
        QualifiedName qname = new QualifiedName(pname);
        if (!mayHaveProp(qname) || !getProp(qname).isSubtypeOf(other.getProp(qname))) {
          return false;
        }
      }
    } else { // this is loose, other may be loose
      for (String pname : props.keySet()) {
        QualifiedName qname = new QualifiedName(pname);
        if (other.mayHaveProp(qname) && !getProp(qname).isSubtypeOf(other.getProp(qname))) {
          return false;
        }
      }
    }

    if (other.fn == null) {
      return this.fn == null || other.isLoose();
    } else if (this.fn == null) {
      return isLoose;
    }
    return fn.isLooseSubtypeOf(other.fn);
  }
  static ObjectType join(ObjectType obj1, ObjectType obj2) {
    if (obj1 == TOP_OBJECT || obj2 == TOP_OBJECT) {
      return TOP_OBJECT;
    }
    NominalType nom1 = obj1.nominalType;
    NominalType nom2 = obj2.nominalType;
    Preconditions.checkState(areRelatedClasses(nom1, nom2));

    if (obj1.equals(obj2)) {
      return obj1;
    }
    boolean isLoose = obj1.isLoose || obj2.isLoose;
    FunctionType fn = FunctionType.join(obj1.fn, obj2.fn);
    PersistentMap<String, Property> props;
    if (isLoose) {
      fn = fn == null ? null : fn.withLoose();
      props = joinPropsLoosely(obj1.props, obj2.props);
    } else {
      props = joinProps(obj1.props, obj2.props, nom1, nom2);
    }
    NominalType nominal = NominalType.pickSuperclass(nom1, nom2);
    // TODO(blickly): Split TOP_OBJECT from empty object and remove this case
    if (nominal == null || !nominal.isFunction()) {
      fn = null;
    }
    return ObjectType.makeObjectType(
        nominal, props, fn, isLoose, ObjectKind.join(obj1.objectKind, obj2.objectKind));
  }
Example #6
0
 /**
  * Takes a type tag with a single bit set (including the non-scalar bit), and prints the string
  * representation of that single type.
  */
 private static String tagToString(int tag, Set<ObjectType> objs, String T) {
   switch (tag) {
     case TRUE_MASK:
     case FALSE_MASK:
       return "boolean";
     case BOTTOM_MASK:
       return "bottom";
     case STRING_MASK:
       return "string";
     case NON_SCALAR_MASK:
       Set<String> strReps = Sets.newHashSet();
       for (ObjectType obj : objs) {
         strReps.add(obj.toString());
       }
       return Joiner.on("|").join(strReps);
     case NULL_MASK:
       return "null";
     case NUMBER_MASK:
       return "number";
     case TOP_MASK:
       return "top";
     case UNDEFINED_MASK:
       return "undefined";
     case UNKNOWN_MASK:
       return "?";
     case TYPEVAR_MASK:
       return T;
     default: // Must be a union type.
       return null;
   }
 }
 static ImmutableSet<ObjectType> withDeclaredProperty(
     Set<ObjectType> objs, QualifiedName qname, JSType type, boolean isConstant) {
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj : objs) {
     newObjs.add(obj.withPropertyHelper(qname, type, true, isConstant));
   }
   return newObjs.build();
 }
Example #8
0
 public boolean hasProp(QualifiedName qname) {
   if (objs == null) {
     return false;
   }
   for (ObjectType o : objs) {
     if (!o.hasProp(qname)) {
       return false;
     }
   }
   return true;
 }
Example #9
0
 public boolean isDict() {
   if (objs == null) {
     return false;
   }
   for (ObjectType objType : objs) {
     if (objType.isDict()) {
       return true;
     }
   }
   return false;
 }
Example #10
0
 public boolean hasConstantProp(QualifiedName pname) {
   Preconditions.checkArgument(pname.isIdentifier());
   if (objs == null) {
     return false;
   }
   for (ObjectType obj : objs) {
     if (obj.hasConstantProp(pname)) {
       return true;
     }
   }
   return false;
 }
Example #11
0
 public FunctionType getFunType() {
   if (objs == null) {
     return null;
   }
   if (objs.size() == 1) { // The common case is fast
     return Iterables.getOnlyElement(objs).getFunType();
   }
   FunctionType result = FunctionType.TOP_FUNCTION;
   for (ObjectType obj : objs) {
     result = FunctionType.meet(result, obj.getFunType());
   }
   return result;
 }
Example #12
0
 // True iff there exists a value that can have this type
 public boolean isInhabitable() {
   if (isBottom()) {
     return false;
   } else if (objs == null) {
     return true;
   }
   for (ObjectType obj : objs) {
     if (!obj.isInhabitable()) {
       return false;
     }
   }
   return true;
 }
Example #13
0
 public JSType getDeclaredProp(QualifiedName qname) {
   if (isUnknown()) {
     return UNKNOWN;
   }
   Preconditions.checkState(objs != null, "Cannot get declared prop %s of type %s", qname, this);
   JSType ptype = BOTTOM;
   for (ObjectType o : objs) {
     JSType declType = o.getDeclaredProp(qname);
     if (declType != null) {
       ptype = join(ptype, declType);
     }
   }
   return ptype.isBottom() ? null : ptype;
 }
Example #14
0
 public boolean isLooseStruct() {
   if (objs == null) {
     return false;
   }
   boolean foundLooseStruct = false;
   boolean foundNonLooseStruct = false;
   for (ObjectType objType : objs) {
     if (objType.isLooseStruct()) {
       foundLooseStruct = true;
     } else if (objType.isStruct()) {
       foundNonLooseStruct = true;
     }
   }
   return foundLooseStruct && !foundNonLooseStruct;
 }
Example #15
0
 public JSType getProp(QualifiedName qname) {
   if (isBottom() || isUnknown()) {
     return UNKNOWN;
   }
   Preconditions.checkState(objs != null);
   JSType ptype = BOTTOM;
   for (ObjectType o : objs) {
     if (o.mayHaveProp(qname)) {
       ptype = join(ptype, o.getProp(qname));
     }
   }
   if (ptype.isBottom()) {
     return null;
   }
   return ptype;
 }
 private JSType getRecordTypeHelper(
     Node n, DeclaredTypeRegistry registry, ImmutableList<String> typeParameters)
     throws UnknownTypeException {
   Map<String, Property> props = new LinkedHashMap<>();
   for (Node propNode = n.getFirstFirstChild(); propNode != null; propNode = propNode.getNext()) {
     boolean isPropDeclared = propNode.getType() == Token.COLON;
     Node propNameNode = isPropDeclared ? propNode.getFirstChild() : propNode;
     String propName = propNameNode.getString();
     if (propName.startsWith("'") || propName.startsWith("\"")) {
       propName = propName.substring(1, propName.length() - 1);
     }
     JSType propType =
         !isPropDeclared
             ? JSType.UNKNOWN
             : getTypeFromCommentHelper(propNode.getLastChild(), registry, typeParameters);
     Property prop;
     if (propType.equals(JSType.UNDEFINED) || isUnionWithUndefined(propNode.getLastChild())) {
       prop = Property.makeOptional(null, propType, propType);
     } else {
       prop = Property.make(propType, propType);
     }
     props.put(propName, prop);
   }
   return JSType.fromObjectType(ObjectType.fromProperties(props));
 }
 static boolean isUnionSubtype(
     boolean keepLoosenessOfThis, Set<ObjectType> objs1, Set<ObjectType> objs2) {
   for (ObjectType obj1 : objs1) {
     boolean foundSupertype = false;
     for (ObjectType obj2 : objs2) {
       if (obj1.isSubtypeOf(keepLoosenessOfThis, obj2)) {
         foundSupertype = true;
         break;
       }
     }
     if (!foundSupertype) {
       return false;
     }
   }
   return true;
 }
 private JSType getNominalTypeHelper(
     RawNominalType rawType,
     Node n,
     DeclaredTypeRegistry registry,
     ImmutableList<String> outerTypeParameters)
     throws UnknownTypeException {
   NominalType uninstantiated = rawType.getAsNominalType();
   if (!rawType.isGeneric() && !n.hasChildren()) {
     return rawType.getInstanceWithNullability(NULLABLE_TYPES_BY_DEFAULT);
   }
   ImmutableList.Builder<JSType> typeList = ImmutableList.builder();
   if (n.hasChildren()) {
     // Compute instantiation of polymorphic class/interface.
     Preconditions.checkState(n.getFirstChild().isBlock(), n);
     for (Node child : n.getFirstChild().children()) {
       typeList.add(getTypeFromCommentHelper(child, registry, outerTypeParameters));
     }
   }
   ImmutableList<JSType> typeArguments = typeList.build();
   ImmutableList<String> typeParameters = rawType.getTypeParameters();
   int typeArgsSize = typeArguments.size();
   int typeParamsSize = typeParameters.size();
   if (typeArgsSize != typeParamsSize) {
     // We used to also warn when (typeArgsSize < typeParamsSize), but it
     // happens so often that we stopped. Array, Object and goog.Promise are
     // common culprits, but many other types as well.
     if (typeArgsSize > typeParamsSize) {
       warnings.add(
           JSError.make(
               n,
               INVALID_GENERICS_INSTANTIATION,
               uninstantiated.getName(),
               String.valueOf(typeParamsSize),
               String.valueOf(typeArgsSize)));
     }
     return maybeMakeNullable(
         JSType.fromObjectType(
             ObjectType.fromNominalType(
                 uninstantiated.instantiateGenerics(
                     fixLengthOfTypeList(typeParameters.size(), typeArguments)))));
   }
   return maybeMakeNullable(
       JSType.fromObjectType(
           ObjectType.fromNominalType(uninstantiated.instantiateGenerics(typeArguments))));
 }
 private ObjectType withPropertyRequired(String pname) {
   Property oldProp = this.props.get(pname);
   Property newProp =
       oldProp == null
           ? UNKNOWN_PROP
           : Property.make(oldProp.getType(), oldProp.getDeclaredType());
   return ObjectType.makeObjectType(
       nominalType, this.props.with(pname, newProp), fn, isLoose, this.objectKind);
 }
Example #20
0
 public JSType withProperty(QualifiedName qname, JSType type) {
   Preconditions.checkArgument(type != null);
   if (isUnknown()) {
     return this;
   }
   Preconditions.checkState(this.objs != null);
   return new JSType(
       this.mask, this.location, ObjectType.withProperty(this.objs, qname, type), typeVar);
 }
  static ImmutableSet<ObjectType> joinSets(
      ImmutableSet<ObjectType> objs1, ImmutableSet<ObjectType> objs2) {
    if (objs1.isEmpty()) {
      return objs2;
    } else if (objs2.isEmpty()) {
      return objs1;
    }
    ObjectType[] objs1Arr = objs1.toArray(new ObjectType[0]);
    ObjectType[] keptFrom1 = Arrays.copyOf(objs1Arr, objs1Arr.length);
    ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
    for (ObjectType obj2 : objs2) {
      boolean addedObj2 = false;
      for (int i = 0; i < objs1Arr.length; i++) {
        ObjectType obj1 = objs1Arr[i];
        NominalType nominalType1 = obj1.nominalType;
        NominalType nominalType2 = obj2.nominalType;
        if (areRelatedClasses(nominalType1, nominalType2)) {
          if (nominalType2 == null && nominalType1 != null && !obj1.isSubtypeOf(obj2)
              || nominalType1 == null && nominalType2 != null && !obj2.isSubtypeOf(obj1)) {
            // Don't merge other classes with record types
            break;
          }
          keptFrom1[i] = null;
          addedObj2 = true;
          // obj1 and obj2 may be in a subtype relation.
          // Even then, we want to join them because we don't want to forget
          // any extra properties in the subtype object.
          newObjs.add(join(obj1, obj2));

          break;
        }
      }
      if (!addedObj2) {
        newObjs.add(obj2);
      }
    }
    for (ObjectType o : keptFrom1) {
      if (o != null) {
        newObjs.add(o);
      }
    }
    return newObjs.build();
  }
Example #22
0
 public JSType withDeclaredProperty(QualifiedName qname, JSType type, boolean isConstant) {
   Preconditions.checkState(this.objs != null && this.location == null);
   if (type == null && isConstant) {
     type = JSType.UNKNOWN;
   }
   return new JSType(
       this.mask,
       null,
       ObjectType.withDeclaredProperty(this.objs, qname, type, isConstant),
       typeVar);
 }
 // TODO(dimvar): handle greatest lower bound of interface types.
 // If we do that, we need to normalize the output, otherwise it could contain
 // two object types that are in a subtype relation, eg, see
 // NewTypeInferenceES5OrLowerTest#testDifficultObjectSpecialization.
 static ImmutableSet<ObjectType> meetSetsHelper(
     boolean specializeObjs1, Set<ObjectType> objs1, Set<ObjectType> objs2) {
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj2 : objs2) {
     for (ObjectType obj1 : objs1) {
       if (areRelatedClasses(obj1.nominalType, obj2.nominalType)) {
         ObjectType newObj;
         if (specializeObjs1) {
           newObj = obj1.specialize(obj2);
           if (newObj == null) {
             continue;
           }
         } else {
           newObj = meet(obj1, obj2);
         }
         newObjs.add(newObj);
       }
     }
   }
   return newObjs.build();
 }
Example #24
0
 public JSType removeType(JSType other) {
   if ((isTop() || isUnknown()) && other.equals(NULL)) {
     return TOP_MINUS_NULL;
   }
   if ((isTop() || isUnknown()) && other.equals(UNDEFINED)) {
     return TOP_MINUS_UNDEF;
   }
   if (other.equals(NULL) || other.equals(UNDEFINED)) {
     return new JSType(mask & ~other.mask, location, objs, typeVar);
   }
   if (objs == null) {
     return this;
   }
   Preconditions.checkState((other.mask & ~NON_SCALAR_MASK) == 0 && other.objs.size() == 1);
   NominalType otherKlass = Iterables.getOnlyElement(other.objs).getNominalType();
   ImmutableSet.Builder<ObjectType> newObjs = ImmutableSet.builder();
   for (ObjectType obj : objs) {
     if (!Objects.equal(obj.getNominalType(), otherKlass)) {
       newObjs.add(obj);
     }
   }
   return new JSType(mask, location, newObjs.build(), typeVar);
 }
Example #25
0
 private boolean isSubtypeOfHelper(boolean keepLoosenessOfThis, JSType other) {
   if (isUnknown() || other.isUnknown() || other.isTop()) {
     return true;
   } else if ((mask | other.mask) != other.mask) {
     return false;
   } else if (!Objects.equal(this.typeVar, other.typeVar)) {
     return false;
   } else if (this.objs == null) {
     return true;
   }
   // Because of optional properties,
   //   x \le y \iff x \join y = y does not hold.
   return ObjectType.isUnionSubtype(keepLoosenessOfThis, this.objs, other.objs);
 }
Example #26
0
 public JSType substituteGenerics(Map<String, JSType> concreteTypes) {
   if (isTop() || isUnknown()) {
     return this;
   }
   ImmutableSet<ObjectType> newObjs = null;
   if (objs != null) {
     ImmutableSet.Builder<ObjectType> builder = ImmutableSet.builder();
     for (ObjectType obj : objs) {
       builder.add(obj.substituteGenerics(concreteTypes));
     }
     newObjs = builder.build();
   }
   JSType current = new JSType(mask & ~TYPEVAR_MASK, location, newObjs, null);
   if ((mask & TYPEVAR_MASK) != 0) {
     current =
         JSType.join(
             current,
             concreteTypes.containsKey(typeVar)
                 ? concreteTypes.get(typeVar)
                 : fromTypeVar(typeVar));
   }
   return current;
 }
 public void setObjectType(RawNominalType builtinObject) {
   NominalType builtinObjectNT = builtinObject.getAsNominalType();
   this.builtinObject = builtinObject;
   this.topObjectType = builtinObject.getInstanceAsJSType().getObjTypeIfSingletonObj();
   this.looseTopObject =
       ObjectType.makeObjectType(
           this,
           builtinObjectNT,
           PersistentMap.<String, Property>create(),
           null,
           null,
           true,
           ObjectKind.UNRESTRICTED);
   this.topObject = JSType.fromObjectType(this.topObjectType);
   this.topStruct =
       JSType.fromObjectType(
           ObjectType.makeObjectType(
               this,
               builtinObjectNT,
               PersistentMap.<String, Property>create(),
               null,
               null,
               false,
               ObjectKind.STRUCT));
   this.topDict =
       JSType.fromObjectType(
           ObjectType.makeObjectType(
               this,
               builtinObjectNT,
               PersistentMap.<String, Property>create(),
               null,
               null,
               false,
               ObjectKind.DICT));
   this.bottomObject = ObjectType.createBottomObject(this);
 }
Example #28
0
  /**
   * Unify the two types symmetrically, given that we have already instantiated the type variables
   * of interest in {@code t1} and {@code t2}, treating JSType.UNKNOWN as a "hole" to be filled.
   *
   * @return The unified type, or null if unification fails
   */
  static JSType unifyUnknowns(JSType t1, JSType t2) {
    if (t1.isUnknown()) {
      return t2;
    } else if (t2.isUnknown()) {
      return t1;
    } else if (t1.isTop() && t2.isTop()) {
      return TOP;
    } else if (t1.isTop() || t2.isTop()) {
      return null;
    }

    int t1Mask = promoteBoolean(t1.mask);
    int t2Mask = promoteBoolean(t2.mask);
    if (t1Mask != t2Mask || !Objects.equal(t1.typeVar, t2.typeVar)) {
      return null;
    }
    // All scalar types are equal
    if ((t1Mask & NON_SCALAR_MASK) == 0) {
      return t1;
    }
    if (t1.objs.size() != t2.objs.size()) {
      return null;
    }

    Set<ObjectType> ununified = Sets.newHashSet(t2.objs);
    Set<ObjectType> unifiedObjs = Sets.newHashSet();
    for (ObjectType objType1 : t1.objs) {
      ObjectType unified = objType1;
      boolean hasUnified = false;
      for (ObjectType objType2 : t2.objs) {
        ObjectType tmp = ObjectType.unifyUnknowns(unified, objType2);
        if (tmp != null) {
          hasUnified = true;
          ununified.remove(objType2);
          unified = tmp;
        }
      }
      if (!hasUnified) {
        return null;
      }
      unifiedObjs.add(unified);
    }
    if (!ununified.isEmpty()) {
      return null;
    }
    return new JSType(t1Mask, null, ImmutableSet.copyOf(unifiedObjs), t1.typeVar);
  }
 /**
  * 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<String, Property> 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));
 }
Example #30
0
 // Meet two types, location agnostic
 public static JSType meet(JSType lhs, JSType rhs) {
   if (lhs.isTop()) {
     return rhs;
   } else if (rhs.isTop()) {
     return lhs;
   } else if (lhs.isUnknown()) {
     return rhs;
   } else if (rhs.isUnknown()) {
     return lhs;
   }
   int newMask = lhs.mask & rhs.mask;
   String newTypevar;
   if (Objects.equal(lhs.typeVar, rhs.typeVar)) {
     newTypevar = lhs.typeVar;
   } else {
     newTypevar = null;
     newMask = newMask & ~TYPEVAR_MASK;
   }
   return new JSType(newMask, null, ObjectType.meetSets(lhs.objs, rhs.objs), newTypevar);
 }