File: src/com/google/javascript/rhino/jstype/PrototypeObjectType.java
Issue: externExport with @typedef can generate invalid externs
What steps will reproduce the problem?
1. Create a file that has a @typedef and code referencing the type def above and below the typedef declaration.
2. Run the closure compiler and grab the externExport string stored on the last result for review.
3. I have attached both source and output files displaying the issue.

What is the expected output? What do you see instead?

The code above the @typedef references the aliased name of the @typedef as expected however the code below the @typedef tries embedding the body of the @typedef and ends up truncating it if the length is too long with a "...". This throws bad type errors when compiling against this extern. What is odd is this only seems to be the case when the parameter with the type is optional. When neither are optional it embeds the types, which is not a big deal, except when types are long; they get truncated and throw errors.

What version of the product are you using? On what operating system?

plovr built from revision 3103:d6db24beeb7f
Revision numbers for embedded Closure Tools:
Closure Library:    1374
Closure Compiler:   1559
Closure Templates:    23

Please provide any additional information below.

Buggy code:
String toStringHelper(boolean forAnnotations) {
  if (hasReferenceName()) {
    return getReferenceName();
  } else if (prettyPrint) {
    // Don't pretty print recursively.
    prettyPrint = false;

    // Use a tree set so that the properties are sorted.
    Set<String> propertyNames = Sets.newTreeSet();
    for (ObjectType current = this;
         current != null && !current.isNativeObjectType() &&
             propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES;
         current = current.getImplicitPrototype()) {
      propertyNames.addAll(current.getOwnPropertyNames());
    }

    StringBuilder sb = new StringBuilder();
    sb.append("{");

    int i = 0;
    for (String property : propertyNames) {
      if (i > 0) {
        sb.append(", ");
      }

      sb.append(property);
      sb.append(": ");
      sb.append(getPropertyType(property).toString());

      ++i;
      if (i == MAX_PRETTY_PRINTED_PROPERTIES) {
        sb.append(", ...");
        break;
      }
    }

    sb.append("}");

    prettyPrint = true;
    return sb.toString();
  } else {
    return "{...}";
  }
}

Fixed code:
String toStringHelper(boolean forAnnotations) {
  if (hasReferenceName()) {
    return getReferenceName();
  } else if (prettyPrint) {
    // Don't pretty print recursively.
    prettyPrint = false;

    // Use a tree set so that the properties are sorted.
    Set<String> propertyNames = Sets.newTreeSet();
    for (ObjectType current = this;
         current != null && !current.isNativeObjectType() &&
             propertyNames.size() <= MAX_PRETTY_PRINTED_PROPERTIES;
         current = current.getImplicitPrototype()) {
      propertyNames.addAll(current.getOwnPropertyNames());
    }

    StringBuilder sb = new StringBuilder();
    sb.append("{");

    int i = 0;
    for (String property : propertyNames) {
      if (i > 0) {
        sb.append(", ");
      }

      sb.append(property);
      sb.append(": ");
      sb.append(getPropertyType(property).toStringHelper(forAnnotations));

      ++i;
      if (!forAnnotations && i == MAX_PRETTY_PRINTED_PROPERTIES) {
        sb.append(", ...");
        break;
      }
    }

    sb.append("}");

    prettyPrint = true;
    return sb.toString();
  } else {
    return forAnnotations ? "?" : "{...}";
  }
}