@Override
    public void recordInterfaces(
        JSType type, JSType relatedType, DisambiguateProperties<JSType>.Property p) {
      ObjectType objType = ObjectType.cast(type);
      if (objType != null) {
        FunctionType constructor;
        if (objType instanceof FunctionType) {
          constructor = (FunctionType) objType;
        } else if (objType instanceof FunctionPrototypeType) {
          constructor = ((FunctionPrototypeType) objType).getOwnerFunction();
        } else {
          constructor = objType.getConstructor();
        }
        while (constructor != null) {
          for (ObjectType itype : constructor.getImplementedInterfaces()) {
            JSType top = getTypeWithProperty(p.name, itype);
            if (top != null) {
              p.addType(itype, top, relatedType);
            } else {
              recordInterfaces(itype, relatedType, p);
            }

            // If this interface invalidated this property, return now.
            if (p.skipRenaming) return;
          }
          if (constructor.isInterface() || constructor.isConstructor()) {
            constructor = constructor.getSuperClassConstructor();
          } else {
            constructor = null;
          }
        }
      }
    }
  /**
   * Infers the type of {@code this}.
   *
   * @param info The JSDocInfo for this function.
   * @param owner The node for the object whose prototype "owns" this function. For example, {@code
   *     A} in the expression {@code A.prototype.foo}. May be null to indicate that this is not a
   *     prototype property.
   */
  FunctionTypeBuilder inferThisType(JSDocInfo info, @Nullable Node owner) {
    ObjectType maybeThisType = null;
    if (info != null && info.hasThisType()) {
      maybeThisType = ObjectType.cast(info.getThisType().evaluate(scope, typeRegistry));
    }
    if (maybeThisType != null) {
      thisType = maybeThisType;
      thisType.setValidator(new ThisTypeValidator());
    } else if (owner != null && (info == null || !info.hasType())) {
      // If the function is of the form:
      // x.prototype.y = function() {}
      // then we can assume "x" is the @this type. On the other hand,
      // if it's of the form:
      // /** @type {Function} */ x.prototype.y;
      // then we should not give it a @this type.
      String ownerTypeName = owner.getQualifiedName();
      ObjectType ownerType =
          ObjectType.cast(
              typeRegistry.getForgivingType(
                  scope, ownerTypeName, sourceName, owner.getLineno(), owner.getCharno()));
      if (ownerType != null) {
        thisType = ownerType;
      }
    }

    return this;
  }
    @Override
    public boolean isInvalidatingType(JSType type) {
      if (type == null
          || invalidatingTypes.contains(type)
          || type.isUnknownType() /* unresolved types */) {
        return true;
      }

      ObjectType objType = ObjectType.cast(type);
      return objType != null && !objType.hasReferenceName();
    }
    private Set<JSType> getTypesToSkipForTypeNonUnion(JSType type) {
      Set<JSType> types = Sets.newHashSet();
      JSType skipType = type;
      while (skipType != null) {
        types.add(skipType);

        ObjectType objSkipType = skipType.toObjectType();
        if (objSkipType != null) {
          skipType = objSkipType.getImplicitPrototype();
        } else {
          break;
        }
      }
      return types;
    }
  /** Invalidates the given type, so that no properties on it will be renamed. */
  private void addInvalidatingType(JSType type) {
    type = type.restrictByNotNullOrUndefined();
    if (type instanceof UnionType) {
      for (JSType alt : ((UnionType) type).getAlternates()) {
        addInvalidatingType(alt);
      }
      return;
    }

    typeSystem.addInvalidatingType(type);
    ObjectType objType = ObjectType.cast(type);
    if (objType != null && objType.getImplicitPrototype() != null) {
      typeSystem.addInvalidatingType(objType.getImplicitPrototype());
    }
  }
 /**
  * Infers the type of {@code this}.
  *
  * @param type The type of this.
  */
 FunctionTypeBuilder inferThisType(JSDocInfo info, JSType type) {
   ObjectType objType = ObjectType.cast(type);
   if (objType != null && (info == null || !info.hasType())) {
     thisType = objType;
   }
   return this;
 }
  protected void initTypes() {
    ALL_TYPE = registry.getNativeType(JSTypeNative.ALL_TYPE);
    NO_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE);
    NO_TYPE = registry.getNativeObjectType(JSTypeNative.NO_TYPE);
    NO_RESOLVED_TYPE = registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE);
    ARRAY_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE);
    ARRAY_TYPE = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE);
    BOOLEAN_OBJECT_FUNCTION_TYPE =
        registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE);
    BOOLEAN_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
    BOOLEAN_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE);
    CHECKED_UNKNOWN_TYPE = registry.getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
    DATE_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE);
    DATE_TYPE = registry.getNativeObjectType(JSTypeNative.DATE_TYPE);
    ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE);
    ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.ERROR_TYPE);
    EVAL_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE);
    EVAL_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.EVAL_ERROR_TYPE);
    FUNCTION_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_FUNCTION_TYPE);
    FUNCTION_INSTANCE_TYPE = registry.getNativeFunctionType(JSTypeNative.FUNCTION_INSTANCE_TYPE);
    FUNCTION_PROTOTYPE = registry.getNativeObjectType(JSTypeNative.FUNCTION_PROTOTYPE);
    GREATEST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE);
    LEAST_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.LEAST_FUNCTION_TYPE);
    NULL_TYPE = registry.getNativeType(JSTypeNative.NULL_TYPE);
    NUMBER_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE);
    NUMBER_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE);
    NUMBER_STRING_BOOLEAN = registry.getNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN);
    NUMBER_TYPE = registry.getNativeType(JSTypeNative.NUMBER_TYPE);
    OBJECT_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE);
    OBJECT_NUMBER_STRING = registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING);
    OBJECT_NUMBER_STRING_BOOLEAN =
        registry.getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN);
    OBJECT_PROTOTYPE = registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE);
    OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
    RANGE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.RANGE_ERROR_FUNCTION_TYPE);
    RANGE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.RANGE_ERROR_TYPE);
    REFERENCE_ERROR_FUNCTION_TYPE =
        registry.getNativeType(JSTypeNative.REFERENCE_ERROR_FUNCTION_TYPE);
    REFERENCE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.REFERENCE_ERROR_TYPE);
    REGEXP_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE);
    REGEXP_TYPE = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE);
    STRING_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE);
    STRING_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE);
    STRING_TYPE = registry.getNativeType(JSTypeNative.STRING_TYPE);
    SYNTAX_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE);
    SYNTAX_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE);
    TYPE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE);
    TYPE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE);
    U2U_CONSTRUCTOR_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE);
    U2U_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE);
    UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
    URI_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE);
    URI_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE);
    VOID_TYPE = registry.getNativeType(JSTypeNative.VOID_TYPE);

    addNativeProperties(registry);

    NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount();
  }
 @Override
 public boolean apply(JSType type) {
   ObjectType objectType = ObjectType.cast(type);
   if (objectType == null) {
     reportWarning(EXTENDS_NON_OBJECT, fnName, type.toString());
   } else if (objectType.isUnknownType()
       &&
       // If this has a supertype that hasn't been resolved yet,
       // then we can assume this type will be ok once the super
       // type resolves.
       (objectType.getImplicitPrototype() == null
           || objectType.getImplicitPrototype().isResolved())) {
     reportWarning(RESOLVED_TAG_EMPTY, "@extends", fnName);
   } else {
     return true;
   }
   return false;
 }
 @Override
 public boolean apply(JSType type) {
   ObjectType objectType = ObjectType.cast(type);
   if (objectType == null) {
     reportError(BAD_IMPLEMENTED_TYPE, fnName);
   } else if (objectType.isUnknownType()
       &&
       // If this has a supertype that hasn't been resolved yet,
       // then we can assume this type will be ok once the super
       // type resolves.
       (objectType.getImplicitPrototype() == null
           || objectType.getImplicitPrototype().isResolved())) {
     reportWarning(RESOLVED_TAG_EMPTY, "@implements", fnName);
   } else {
     return true;
   }
   return false;
 }
 @Override
 public Iterable<JSType> getTypeAlternatives(JSType type) {
   if (type.isUnionType()) {
     return ((UnionType) type).getAlternates();
   } else {
     ObjectType objType = type.toObjectType();
     if (objType != null
         && objType.getConstructor() != null
         && objType.getConstructor().isInterface()) {
       List<JSType> list = Lists.newArrayList();
       for (FunctionType impl : registry.getDirectImplementors(objType)) {
         list.add(impl.getInstanceType());
       }
       return list;
     } else {
       return null;
     }
   }
 }
  /**
   * Infer the role of the function (whether it's a constructor or interface) and what it inherits
   * from in JSDocInfo.
   */
  FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) {
    if (info != null) {
      isConstructor = info.isConstructor();
      isInterface = info.isInterface();

      // base type
      if (info.hasBaseType()) {
        if (isConstructor || isInterface) {
          JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry);
          if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) {
            baseType = (ObjectType) maybeBaseType;
          }
        } else {
          reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName);
        }
      }

      // implemented interfaces
      if (isConstructor || isInterface) {
        implementedInterfaces = Lists.newArrayList();
        for (JSTypeExpression t : info.getImplementedInterfaces()) {
          JSType maybeInterType = t.evaluate(scope, typeRegistry);
          if (maybeInterType != null
              && maybeInterType.setValidator(new ImplementedTypeValidator())) {
            implementedInterfaces.add((ObjectType) maybeInterType);
          }
        }
        if (baseType != null) {
          JSType maybeFunctionType = baseType.getConstructor();
          if (maybeFunctionType instanceof FunctionType) {
            FunctionType functionType = baseType.getConstructor();
            Iterables.addAll(implementedInterfaces, functionType.getImplementedInterfaces());
          }
        }
      } else if (info.getImplementedInterfaceCount() > 0) {
        reportWarning(IMPLEMENTS_WITHOUT_CONSTRUCTOR, fnName);
      }
    }

    return this;
  }
  @Test
  public void canReferenceExportedTypeReferences() {
    CompilerUtil compiler = createCompiler(path("foo/bar.js"), path("foo/baz.js"));

    compiler.compile(
        createSourceFile(path("foo/bar.js"), "/** @constructor */", "exports.foo = function(){};"),
        createSourceFile(
            path("foo/baz.js"),
            "var bar = require('./bar');",
            "var foo = require('./bar').foo;",
            "/** @type {!bar.foo} */",
            "var a = new bar.foo();",
            "/** @type {!foo} */",
            "var b = new foo();"));

    assertThat(compiler.toSource())
        .startsWith(
            lines(
                "var $jscomp = {};",
                "$jscomp.scope = {};",
                "var module$foo$bar = {};",
                "module$foo$bar.foo = function() {",
                "};",
                "var module$foo$baz = {};",
                "$jscomp.scope.a = new module$foo$bar.foo;",
                "$jscomp.scope.b = new module$foo$bar.foo;"));

    ObjectType scope =
        compiler
            .getCompiler()
            .getTopScope()
            .getVar("$jscomp")
            .getType()
            .findPropertyType("scope")
            .toObjectType();

    assertEquals("module$foo$bar.foo", scope.getPropertyType("a").getDisplayName());
    assertEquals("module$foo$bar.foo", scope.getPropertyType("b").getDisplayName());
  }
  /** Gets the type of {@code this} in the current scope. */
  @Override
  public JSType getTypeOfThis() {
    if (isGlobal()) {
      return ObjectType.cast(rootNode.getJSType());
    }

    Preconditions.checkState(rootNode.isFunction());
    JSType nodeType = rootNode.getJSType();
    if (nodeType != null && nodeType.isFunctionType()) {
      return nodeType.toMaybeFunctionType().getTypeOfThis();
    } else {
      // Executed when the current scope has not been typechecked.
      return null;
    }
  }
    @Override
    public ObjectType getTypeWithProperty(String field, JSType type) {
      if (!(type instanceof ObjectType)) {
        if (type.autoboxesTo() != null) {
          type = type.autoboxesTo();
        } else {
          return null;
        }
      }

      // Ignore the prototype itself at all times.
      if ("prototype".equals(field)) {
        return null;
      }

      // We look up the prototype chain to find the highest place (if any) that
      // this appears.  This will make references to overriden properties look
      // like references to the initial property, so they are renamed alike.
      ObjectType foundType = null;
      ObjectType objType = ObjectType.cast(type);
      while (objType != null && objType.getImplicitPrototype() != objType) {
        if (objType.hasOwnProperty(field)) {
          foundType = objType;
        }
        objType = objType.getImplicitPrototype();
      }
      // If the property does not exist on the referenced type but the original
      // type is an object type, see if any subtype has the property.
      if (foundType == null) {
        ObjectType maybeType =
            ObjectType.cast(registry.getGreatestSubtypeWithProperty(type, field));
        // getGreatestSubtypeWithProperty does not guarantee that the property
        // is defined on the returned type, it just indicates that it might be,
        // so we have to double check.
        if (maybeType != null && maybeType.hasOwnProperty(field)) {
          foundType = maybeType;
        }
      }
      return foundType;
    }
  /** Adds a basic set of properties to the native types. */
  public static void addNativeProperties(JSTypeRegistry registry) {
    JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE);
    JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE);
    JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE);
    JSType unknownType = registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);

    ObjectType objectType = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE);
    ObjectType arrayType = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE);
    ObjectType dateType = registry.getNativeObjectType(JSTypeNative.DATE_TYPE);
    ObjectType regexpType = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE);
    ObjectType booleanObjectType = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE);
    ObjectType numberObjectType = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE);
    ObjectType stringObjectType = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE);

    ObjectType objectPrototype =
        registry.getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE).getPrototype();
    addMethod(registry, objectPrototype, "constructor", objectType);
    addMethod(registry, objectPrototype, "toString", stringType);
    addMethod(registry, objectPrototype, "toLocaleString", stringType);
    addMethod(registry, objectPrototype, "valueOf", unknownType);
    addMethod(registry, objectPrototype, "hasOwnProperty", booleanType);
    addMethod(registry, objectPrototype, "isPrototypeOf", booleanType);
    addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType);

    ObjectType arrayPrototype =
        registry.getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE).getPrototype();
    addMethod(registry, arrayPrototype, "constructor", arrayType);
    addMethod(registry, arrayPrototype, "toString", stringType);
    addMethod(registry, arrayPrototype, "toLocaleString", stringType);
    addMethod(registry, arrayPrototype, "concat", arrayType);
    addMethod(registry, arrayPrototype, "join", stringType);
    addMethod(registry, arrayPrototype, "pop", unknownType);
    addMethod(registry, arrayPrototype, "push", numberType);
    addMethod(registry, arrayPrototype, "reverse", arrayType);
    addMethod(registry, arrayPrototype, "shift", unknownType);
    addMethod(registry, arrayPrototype, "slice", arrayType);
    addMethod(registry, arrayPrototype, "sort", arrayType);
    addMethod(registry, arrayPrototype, "splice", arrayType);
    addMethod(registry, arrayPrototype, "unshift", numberType);
    arrayType.defineDeclaredProperty("length", numberType, true, null);

    ObjectType booleanPrototype =
        registry.getNativeFunctionType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE).getPrototype();
    addMethod(registry, booleanPrototype, "constructor", booleanObjectType);
    addMethod(registry, booleanPrototype, "toString", stringType);
    addMethod(registry, booleanPrototype, "valueOf", booleanType);

    ObjectType datePrototype =
        registry.getNativeFunctionType(JSTypeNative.DATE_FUNCTION_TYPE).getPrototype();
    addMethod(registry, datePrototype, "constructor", dateType);
    addMethod(registry, datePrototype, "toString", stringType);
    addMethod(registry, datePrototype, "toDateString", stringType);
    addMethod(registry, datePrototype, "toTimeString", stringType);
    addMethod(registry, datePrototype, "toLocaleString", stringType);
    addMethod(registry, datePrototype, "toLocaleDateString", stringType);
    addMethod(registry, datePrototype, "toLocaleTimeString", stringType);
    addMethod(registry, datePrototype, "valueOf", numberType);
    addMethod(registry, datePrototype, "getTime", numberType);
    addMethod(registry, datePrototype, "getFullYear", numberType);
    addMethod(registry, datePrototype, "getUTCFullYear", numberType);
    addMethod(registry, datePrototype, "getMonth", numberType);
    addMethod(registry, datePrototype, "getUTCMonth", numberType);
    addMethod(registry, datePrototype, "getDate", numberType);
    addMethod(registry, datePrototype, "getUTCDate", numberType);
    addMethod(registry, datePrototype, "getDay", numberType);
    addMethod(registry, datePrototype, "getUTCDay", numberType);
    addMethod(registry, datePrototype, "getHours", numberType);
    addMethod(registry, datePrototype, "getUTCHours", numberType);
    addMethod(registry, datePrototype, "getMinutes", numberType);
    addMethod(registry, datePrototype, "getUTCMinutes", numberType);
    addMethod(registry, datePrototype, "getSeconds", numberType);
    addMethod(registry, datePrototype, "getUTCSeconds", numberType);
    addMethod(registry, datePrototype, "getMilliseconds", numberType);
    addMethod(registry, datePrototype, "getUTCMilliseconds", numberType);
    addMethod(registry, datePrototype, "getTimezoneOffset", numberType);
    addMethod(registry, datePrototype, "setTime", numberType);
    addMethod(registry, datePrototype, "setMilliseconds", numberType);
    addMethod(registry, datePrototype, "setUTCMilliseconds", numberType);
    addMethod(registry, datePrototype, "setSeconds", numberType);
    addMethod(registry, datePrototype, "setUTCSeconds", numberType);
    addMethod(registry, datePrototype, "setMinutes", numberType);
    addMethod(registry, datePrototype, "setUTCMinutes", numberType);
    addMethod(registry, datePrototype, "setHours", numberType);
    addMethod(registry, datePrototype, "setUTCHours", numberType);
    addMethod(registry, datePrototype, "setDate", numberType);
    addMethod(registry, datePrototype, "setUTCDate", numberType);
    addMethod(registry, datePrototype, "setMonth", numberType);
    addMethod(registry, datePrototype, "setUTCMonth", numberType);
    addMethod(registry, datePrototype, "setFullYear", numberType);
    addMethod(registry, datePrototype, "setUTCFullYear", numberType);
    addMethod(registry, datePrototype, "toUTCString", stringType);
    addMethod(registry, datePrototype, "toGMTString", stringType);

    ObjectType numberPrototype =
        registry.getNativeFunctionType(JSTypeNative.NUMBER_OBJECT_FUNCTION_TYPE).getPrototype();
    addMethod(registry, numberPrototype, "constructor", numberObjectType);
    addMethod(registry, numberPrototype, "toString", stringType);
    addMethod(registry, numberPrototype, "toLocaleString", stringType);
    addMethod(registry, numberPrototype, "valueOf", numberType);
    addMethod(registry, numberPrototype, "toFixed", stringType);
    addMethod(registry, numberPrototype, "toExponential", stringType);
    addMethod(registry, numberPrototype, "toPrecision", stringType);

    ObjectType regexpPrototype =
        registry.getNativeFunctionType(JSTypeNative.REGEXP_FUNCTION_TYPE).getPrototype();
    addMethod(registry, regexpPrototype, "constructor", regexpType);
    addMethod(registry, regexpPrototype, "exec", registry.createNullableType(arrayType));
    addMethod(registry, regexpPrototype, "test", booleanType);
    addMethod(registry, regexpPrototype, "toString", stringType);
    regexpType.defineDeclaredProperty("source", stringType, true, null);
    regexpType.defineDeclaredProperty("global", booleanType, true, null);
    regexpType.defineDeclaredProperty("ignoreCase", booleanType, true, null);
    regexpType.defineDeclaredProperty("multiline", booleanType, true, null);
    regexpType.defineDeclaredProperty("lastIndex", numberType, true, null);

    ObjectType stringPrototype =
        registry.getNativeFunctionType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE).getPrototype();
    addMethod(registry, stringPrototype, "constructor", stringObjectType);
    addMethod(registry, stringPrototype, "toString", stringType);
    addMethod(registry, stringPrototype, "valueOf", stringType);
    addMethod(registry, stringPrototype, "charAt", stringType);
    addMethod(registry, stringPrototype, "charCodeAt", numberType);
    addMethod(registry, stringPrototype, "concat", stringType);
    addMethod(registry, stringPrototype, "indexOf", numberType);
    addMethod(registry, stringPrototype, "lastIndexOf", numberType);
    addMethod(registry, stringPrototype, "localeCompare", numberType);
    addMethod(registry, stringPrototype, "match", registry.createNullableType(arrayType));
    addMethod(registry, stringPrototype, "replace", stringType);
    addMethod(registry, stringPrototype, "search", numberType);
    addMethod(registry, stringPrototype, "slice", stringType);
    addMethod(registry, stringPrototype, "split", arrayType);
    addMethod(registry, stringPrototype, "substring", stringType);
    addMethod(registry, stringPrototype, "toLowerCase", stringType);
    addMethod(registry, stringPrototype, "toLocaleLowerCase", stringType);
    addMethod(registry, stringPrototype, "toUpperCase", stringType);
    addMethod(registry, stringPrototype, "toLocaleUpperCase", stringType);
    stringObjectType.defineDeclaredProperty("length", numberType, true, null);
  }
 private static void addMethod(
     JSTypeRegistry registry, ObjectType receivingType, String methodName, JSType returnType) {
   receivingType.defineDeclaredProperty(
       methodName, new FunctionBuilder(registry).withReturnType(returnType).build(), true, null);
 }
 @Override
 public JSType caseObjectType(ObjectType type) {
   return type.isSubtype(getNativeType(ARRAY_TYPE)) ? null : type;
 }
  /** @param fnNode A node for a function for which to generate a type annotation */
  private String getFunctionAnnotation(Node fnNode) {
    Preconditions.checkState(fnNode.isFunction());
    StringBuilder sb = new StringBuilder("/**\n");

    JSType type = fnNode.getJSType();

    if (type == null || type.isUnknownType()) {
      return "";
    }

    FunctionType funType = type.toMaybeFunctionType();

    // We need to use the child nodes of the function as the nodes for the
    // parameters of the function type do not have the real parameter names.
    // FUNCTION
    //   NAME
    //   LP
    //     NAME param1
    //     NAME param2
    if (fnNode != null) {
      Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild();

      // Param types
      for (Node n : funType.getParameters()) {
        // Bail out if the paramNode is not there.
        if (paramNode == null) {
          break;
        }
        sb.append(" * ");
        appendAnnotation(sb, "param", getParameterNodeJSDocType(n));
        sb.append(" ").append(paramNode.getString()).append("\n");
        paramNode = paramNode.getNext();
      }
    }

    // Return type
    JSType retType = funType.getReturnType();
    if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) {
      sb.append(" * ");
      appendAnnotation(sb, "return", retType.toAnnotationString());
      sb.append("\n");
    }

    // Constructor/interface
    if (funType.isConstructor() || funType.isInterface()) {

      FunctionType superConstructor = funType.getSuperClassConstructor();

      if (superConstructor != null) {
        ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType();
        if (!superInstance.toString().equals("Object")) {
          sb.append(" * ");
          appendAnnotation(sb, "extends", superInstance.toAnnotationString());
          sb.append("\n");
        }
      }

      if (funType.isInterface()) {
        for (ObjectType interfaceType : funType.getExtendedInterfaces()) {
          sb.append(" * ");
          appendAnnotation(sb, "extends", interfaceType.toAnnotationString());
          sb.append("\n");
        }
      }

      // Avoid duplicates, add implemented type to a set first
      Set<String> interfaces = Sets.newTreeSet();
      for (ObjectType interfaze : funType.getImplementedInterfaces()) {
        interfaces.add(interfaze.toAnnotationString());
      }
      for (String interfaze : interfaces) {
        sb.append(" * ");
        appendAnnotation(sb, "implements", interfaze);
        sb.append("\n");
      }

      if (funType.isConstructor()) {
        sb.append(" * @constructor\n");
      } else if (funType.isInterface()) {
        sb.append(" * @interface\n");
      }
    }

    if (fnNode != null && fnNode.getBooleanProp(Node.IS_DISPATCHER)) {
      sb.append(" * @javadispatch\n");
    }

    sb.append(" */\n");
    return sb.toString();
  }