File protobuf-7bff8393cab939bfbb9b5c69b3fe76b4d83c41ee.patch of Package protobuf.26315

From 7bff8393cab939bfbb9b5c69b3fe76b4d83c41ee Mon Sep 17 00:00:00 2001
From: Paul Yang <TeBoring@users.noreply.github.com>
Date: Fri, 19 Jul 2019 14:49:01 -0700
Subject: [PATCH] Down Integrate to GitHub (#6414)

* Down integrate to GitHub

* Fix broken tests
---
 Makefile.am                                                              |    2 
 java/core/src/main/java/com/google/protobuf/FieldSet.java                |  573 ++++++++--
 java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java      |  110 +
 java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java       |    3 
 java/core/src/main/java/com/google/protobuf/TextFormat.java              |  196 +++
 java/core/src/main/java/com/google/protobuf/TypeRegistry.java            |  160 ++
 java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java    |  154 ++
 java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java      |    6 
 java/core/src/test/java/com/google/protobuf/TextFormatTest.java          |  191 +++
 java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java        |   70 +
 java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto |    2 
 java/lite/pom.xml                                                        |    1 
 java/util/src/main/java/com/google/protobuf/util/JsonFormat.java         |  140 +-
 java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java     |   51 
 src/google/protobuf/compiler/java/java_context.cc                        |    1 
 src/google/protobuf/compiler/java/java_doc_comment.cc                    |   80 -
 src/google/protobuf/compiler/java/java_doc_comment.h                     |   42 
 src/google/protobuf/compiler/java/java_enum.cc                           |   11 
 src/google/protobuf/compiler/java/java_enum_field.cc                     |    4 
 src/google/protobuf/compiler/java/java_string_field_lite.cc              |    7 
 src/google/protobuf/compiler/js/js_generator.cc                          |  138 +-
 21 files changed, 1645 insertions(+), 297 deletions(-)
 create mode 100755 java/core/src/main/java/com/google/protobuf/TypeRegistry.java
 create mode 100755 java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java

--- a/Makefile.am
+++ b/Makefile.am
@@ -339,6 +339,7 @@ java_EXTRA_DIST=
   java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java               \
   java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java         \
   java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java         \
+  java/core/src/main/java/com/google/protobuf/TypeRegistry.java                    \
   java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java   \
   java/core/src/main/java/com/google/protobuf/UnknownFieldSchema.java              \
   java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java                 \
@@ -435,6 +436,7 @@ java_EXTRA_DIST=
   java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java     \
   java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java     \
   java/core/src/test/java/com/google/protobuf/TextFormatTest.java                  \
+  java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java                \
   java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java            \
   java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java             \
   java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java  \
--- a/java/core/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java
@@ -48,8 +48,7 @@ import java.util.Map;
  *
  * @author kenton@google.com Kenton Varda
  */
-final class FieldSet<
-    FieldDescriptorType extends FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
+final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> {
   /**
    * Interface for a FieldDescriptor or lite extension descriptor. This prevents FieldSet from
    * depending on {@link Descriptors.FieldDescriptor}.
@@ -72,18 +71,26 @@ final class FieldSet<
     MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from);
   }
 
-  private final SmallSortedMap<FieldDescriptorType, Object> fields;
+  private static final int DEFAULT_FIELD_MAP_ARRAY_SIZE = 16;
+
+  private final SmallSortedMap<T, Object> fields;
   private boolean isImmutable;
-  private boolean hasLazyField = false;
+  private boolean hasLazyField;
 
   /** Construct a new FieldSet. */
   private FieldSet() {
-    this.fields = SmallSortedMap.newFieldMap(16);
+    this.fields = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE);
   }
 
   /** Construct an empty FieldSet. This is only used to initialize DEFAULT_INSTANCE. */
+  @SuppressWarnings("unused")
   private FieldSet(final boolean dummy) {
-    this.fields = SmallSortedMap.newFieldMap(0);
+    this(SmallSortedMap.<T>newFieldMap(0));
+    makeImmutable();
+  }
+
+  private FieldSet(SmallSortedMap<T, Object> fields) {
+    this.fields = fields;
     makeImmutable();
   }
 
@@ -98,6 +105,11 @@ final class FieldSet<
     return DEFAULT_INSTANCE;
   }
 
+  /** Construct a new Builder. */
+  public static <T extends FieldDescriptorLite<T>> Builder<T> newBuilder() {
+    return new Builder<T>();
+  }
+
   @SuppressWarnings("rawtypes")
   private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
 
@@ -152,18 +164,16 @@ final class FieldSet<
    * @return the newly cloned FieldSet
    */
   @Override
-  public FieldSet<FieldDescriptorType> clone() {
+  public FieldSet<T> clone() {
     // We can't just call fields.clone because List objects in the map
     // should not be shared.
-    FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet();
+    FieldSet<T> clone = FieldSet.newFieldSet();
     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
-      Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
-      FieldDescriptorType descriptor = entry.getKey();
-      clone.setField(descriptor, entry.getValue());
-    }
-    for (Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
-      FieldDescriptorType descriptor = entry.getKey();
-      clone.setField(descriptor, entry.getValue());
+      Map.Entry<T, Object> entry = fields.getArrayEntryAt(i);
+      clone.setField(entry.getKey(), entry.getValue());
+    }
+    for (Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
+      clone.setField(entry.getKey(), entry.getValue());
     }
     clone.hasLazyField = hasLazyField;
     return clone;
@@ -179,15 +189,9 @@ final class FieldSet<
   }
 
   /** Get a simple map containing all the fields. */
-  public Map<FieldDescriptorType, Object> getAllFields() {
+  public Map<T, Object> getAllFields() {
     if (hasLazyField) {
-      SmallSortedMap<FieldDescriptorType, Object> result = SmallSortedMap.newFieldMap(16);
-      for (int i = 0; i < fields.getNumArrayEntries(); i++) {
-        cloneFieldEntry(result, fields.getArrayEntryAt(i));
-      }
-      for (Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
-        cloneFieldEntry(result, entry);
-      }
+      SmallSortedMap<T, Object> result = cloneAllFieldsMap(fields, /* copyList */ false);
       if (fields.isImmutable()) {
         result.makeImmutable();
       }
@@ -196,12 +200,26 @@ final class FieldSet<
     return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
   }
 
-  private void cloneFieldEntry(
-      Map<FieldDescriptorType, Object> map, Map.Entry<FieldDescriptorType, Object> entry) {
-    FieldDescriptorType key = entry.getKey();
+  private static <T extends FieldDescriptorLite<T>> SmallSortedMap<T, Object> cloneAllFieldsMap(
+      SmallSortedMap<T, Object> fields, boolean copyList) {
+    SmallSortedMap<T, Object> result = SmallSortedMap.newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE);
+    for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+      cloneFieldEntry(result, fields.getArrayEntryAt(i), copyList);
+    }
+    for (Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
+      cloneFieldEntry(result, entry, copyList);
+    }
+    return result;
+  }
+
+  private static <T extends FieldDescriptorLite<T>> void cloneFieldEntry(
+      Map<T, Object> map, Map.Entry<T, Object> entry, boolean copyList) {
+    T key = entry.getKey();
     Object value = entry.getValue();
     if (value instanceof LazyField) {
       map.put(key, ((LazyField) value).getValue());
+    } else if (copyList && value instanceof List) {
+      map.put(key, new ArrayList<>((List<?>) value));
     } else {
       map.put(key, value);
     }
@@ -211,9 +229,9 @@ final class FieldSet<
    * Get an iterator to the field map. This iterator should not be leaked out of the protobuf
    * library as it is not protected from mutation when fields is not immutable.
    */
-  public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
+  public Iterator<Map.Entry<T, Object>> iterator() {
     if (hasLazyField) {
-      return new LazyIterator<FieldDescriptorType>(fields.entrySet().iterator());
+      return new LazyIterator<T>(fields.entrySet().iterator());
     }
     return fields.entrySet().iterator();
   }
@@ -223,15 +241,15 @@ final class FieldSet<
    * should not be leaked out of the protobuf library as it is not protected from mutation when
    * fields is not immutable.
    */
-  Iterator<Map.Entry<FieldDescriptorType, Object>> descendingIterator() {
+  Iterator<Map.Entry<T, Object>> descendingIterator() {
     if (hasLazyField) {
-      return new LazyIterator<FieldDescriptorType>(fields.descendingEntrySet().iterator());
+      return new LazyIterator<T>(fields.descendingEntrySet().iterator());
     }
     return fields.descendingEntrySet().iterator();
   }
 
   /** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */
-  public boolean hasField(final FieldDescriptorType descriptor) {
+  public boolean hasField(final T descriptor) {
     if (descriptor.isRepeated()) {
       throw new IllegalArgumentException("hasField() can only be called on non-repeated fields.");
     }
@@ -244,7 +262,7 @@ final class FieldSet<
    * returns {@code null} if the field is not set; in this case it is up to the caller to fetch the
    * field's default value.
    */
-  public Object getField(final FieldDescriptorType descriptor) {
+  public Object getField(final T descriptor) {
     Object o = fields.get(descriptor);
     if (o instanceof LazyField) {
       return ((LazyField) o).getValue();
@@ -256,7 +274,7 @@ final class FieldSet<
    * Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
    */
   @SuppressWarnings({"unchecked", "rawtypes"})
-  public void setField(final FieldDescriptorType descriptor, Object value) {
+  public void setField(final T descriptor, Object value) {
     if (descriptor.isRepeated()) {
       if (!(value instanceof List)) {
         throw new IllegalArgumentException(
@@ -282,7 +300,7 @@ final class FieldSet<
   }
 
   /** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */
-  public void clearField(final FieldDescriptorType descriptor) {
+  public void clearField(final T descriptor) {
     fields.remove(descriptor);
     if (fields.isEmpty()) {
       hasLazyField = false;
@@ -290,7 +308,7 @@ final class FieldSet<
   }
 
   /** Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */
-  public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
+  public int getRepeatedFieldCount(final T descriptor) {
     if (!descriptor.isRepeated()) {
       throw new IllegalArgumentException(
           "getRepeatedField() can only be called on repeated fields.");
@@ -305,7 +323,7 @@ final class FieldSet<
   }
 
   /** Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */
-  public Object getRepeatedField(final FieldDescriptorType descriptor, final int index) {
+  public Object getRepeatedField(final T descriptor, final int index) {
     if (!descriptor.isRepeated()) {
       throw new IllegalArgumentException(
           "getRepeatedField() can only be called on repeated fields.");
@@ -325,8 +343,7 @@ final class FieldSet<
    * Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
    */
   @SuppressWarnings("unchecked")
-  public void setRepeatedField(
-      final FieldDescriptorType descriptor, final int index, final Object value) {
+  public void setRepeatedField(final T descriptor, final int index, final Object value) {
     if (!descriptor.isRepeated()) {
       throw new IllegalArgumentException(
           "getRepeatedField() can only be called on repeated fields.");
@@ -346,7 +363,7 @@ final class FieldSet<
    * Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
    */
   @SuppressWarnings("unchecked")
-  public void addRepeatedField(final FieldDescriptorType descriptor, final Object value) {
+  public void addRepeatedField(final T descriptor, final Object value) {
     if (!descriptor.isRepeated()) {
       throw new IllegalArgumentException(
           "addRepeatedField() can only be called on repeated fields.");
@@ -373,53 +390,45 @@ final class FieldSet<
    *
    * @throws IllegalArgumentException The value is not of the right type.
    */
-  private static void verifyType(final WireFormat.FieldType type, final Object value) {
-    checkNotNull(value);
+  private void verifyType(final WireFormat.FieldType type, final Object value) {
+    if (!isValidType(type, value)) {
+      // TODO(kenton):  When chaining calls to setField(), it can be hard to
+      //   tell from the stack trace which exact call failed, since the whole
+      //   chain is considered one line of code.  It would be nice to print
+      //   more information here, e.g. naming the field.  We used to do that.
+      //   But we can't now that FieldSet doesn't use descriptors.  Maybe this
+      //   isn't a big deal, though, since it would only really apply when using
+      //   reflection and generally people don't chain reflection setters.
+      throw new IllegalArgumentException(
+          "Wrong object type used with protocol message reflection.");
+    }
+  }
 
-    boolean isValid = false;
+  private static boolean isValidType(final WireFormat.FieldType type, final Object value) {
+    checkNotNull(value);
     switch (type.getJavaType()) {
       case INT:
-        isValid = value instanceof Integer;
-        break;
+        return value instanceof Integer;
       case LONG:
-        isValid = value instanceof Long;
-        break;
+        return value instanceof Long;
       case FLOAT:
-        isValid = value instanceof Float;
-        break;
+        return value instanceof Float;
       case DOUBLE:
-        isValid = value instanceof Double;
-        break;
+        return value instanceof Double;
       case BOOLEAN:
-        isValid = value instanceof Boolean;
-        break;
+        return value instanceof Boolean;
       case STRING:
-        isValid = value instanceof String;
-        break;
+        return value instanceof String;
       case BYTE_STRING:
-        isValid = value instanceof ByteString || value instanceof byte[];
-        break;
+        return value instanceof ByteString || value instanceof byte[];
       case ENUM:
         // TODO(kenton):  Caller must do type checking here, I guess.
-        isValid = (value instanceof Integer || value instanceof Internal.EnumLite);
-        break;
+        return (value instanceof Integer || value instanceof Internal.EnumLite);
       case MESSAGE:
         // TODO(kenton):  Caller must do type checking here, I guess.
-        isValid = (value instanceof MessageLite) || (value instanceof LazyField);
-        break;
-    }
-
-    if (!isValid) {
-      // TODO(kenton):  When chaining calls to setField(), it can be hard to
-      //   tell from the stack trace which exact call failed, since the whole
-      //   chain is considered one line of code.  It would be nice to print
-      //   more information here, e.g. naming the field.  We used to do that.
-      //   But we can't now that FieldSet doesn't use descriptors.  Maybe this
-      //   isn't a big deal, though, since it would only really apply when using
-      //   reflection and generally people don't chain reflection setters.
-      throw new IllegalArgumentException(
-          "Wrong object type used with protocol message reflection.");
+        return (value instanceof MessageLite) || (value instanceof LazyField);
     }
+    return false;
   }
 
   // =================================================================
@@ -436,7 +445,7 @@ final class FieldSet<
         return false;
       }
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
       if (!isInitialized(entry)) {
         return false;
       }
@@ -445,8 +454,9 @@ final class FieldSet<
   }
 
   @SuppressWarnings("unchecked")
-  private boolean isInitialized(final Map.Entry<FieldDescriptorType, Object> entry) {
-    final FieldDescriptorType descriptor = entry.getKey();
+  private static <T extends FieldDescriptorLite<T>> boolean isInitialized(
+      final Map.Entry<T, Object> entry) {
+    final T descriptor = entry.getKey();
     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
       if (descriptor.isRepeated()) {
         for (final MessageLite element : (List<MessageLite>) entry.getValue()) {
@@ -485,16 +495,16 @@ final class FieldSet<
   }
 
   /** Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}. */
-  public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
+  public void mergeFrom(final FieldSet<T> other) {
     for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
       mergeFromField(other.fields.getArrayEntryAt(i));
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : other.fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : other.fields.getOverflowEntries()) {
       mergeFromField(entry);
     }
   }
 
-  private Object cloneIfMutable(Object value) {
+  private static Object cloneIfMutable(Object value) {
     if (value instanceof byte[]) {
       byte[] bytes = (byte[]) value;
       byte[] copy = new byte[bytes.length];
@@ -506,8 +516,8 @@ final class FieldSet<
   }
 
   @SuppressWarnings({"unchecked", "rawtypes"})
-  private void mergeFromField(final Map.Entry<FieldDescriptorType, Object> entry) {
-    final FieldDescriptorType descriptor = entry.getKey();
+  private void mergeFromField(final Map.Entry<T, Object> entry) {
+    final T descriptor = entry.getKey();
     Object otherValue = entry.getValue();
     if (otherValue instanceof LazyField) {
       otherValue = ((LazyField) otherValue).getValue();
@@ -532,7 +542,6 @@ final class FieldSet<
               descriptor
                   .internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue)
                   .build();
-
         fields.put(descriptor, value);
       }
     } else {
@@ -567,10 +576,10 @@ final class FieldSet<
   /** See {@link Message#writeTo(CodedOutputStream)}. */
   public void writeTo(final CodedOutputStream output) throws IOException {
     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
-      final Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
+      final Map.Entry<T, Object> entry = fields.getArrayEntryAt(i);
       writeField(entry.getKey(), entry.getValue(), output);
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
       writeField(entry.getKey(), entry.getValue(), output);
     }
   }
@@ -580,15 +589,14 @@ final class FieldSet<
     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
       writeMessageSetTo(fields.getArrayEntryAt(i), output);
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
       writeMessageSetTo(entry, output);
     }
   }
 
-  private void writeMessageSetTo(
-      final Map.Entry<FieldDescriptorType, Object> entry, final CodedOutputStream output)
+  private void writeMessageSetTo(final Map.Entry<T, Object> entry, final CodedOutputStream output)
       throws IOException {
-    final FieldDescriptorType descriptor = entry.getKey();
+    final T descriptor = entry.getKey();
     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
         && !descriptor.isRepeated()
         && !descriptor.isPacked()) {
@@ -750,10 +758,10 @@ final class FieldSet<
   public int getSerializedSize() {
     int size = 0;
     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
-      final Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i);
+      final Map.Entry<T, Object> entry = fields.getArrayEntryAt(i);
       size += computeFieldSize(entry.getKey(), entry.getValue());
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
       size += computeFieldSize(entry.getKey(), entry.getValue());
     }
     return size;
@@ -765,14 +773,14 @@ final class FieldSet<
     for (int i = 0; i < fields.getNumArrayEntries(); i++) {
       size += getMessageSetSerializedSize(fields.getArrayEntryAt(i));
     }
-    for (final Map.Entry<FieldDescriptorType, Object> entry : fields.getOverflowEntries()) {
+    for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
       size += getMessageSetSerializedSize(entry);
     }
     return size;
   }
 
-  private int getMessageSetSerializedSize(final Map.Entry<FieldDescriptorType, Object> entry) {
-    final FieldDescriptorType descriptor = entry.getKey();
+  private int getMessageSetSerializedSize(final Map.Entry<T, Object> entry) {
+    final T descriptor = entry.getKey();
     Object value = entry.getValue();
     if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE
         && !descriptor.isRepeated()
@@ -904,4 +912,385 @@ final class FieldSet<
       return computeElementSize(type, number, value);
     }
   }
+
+  /**
+   * A FieldSet Builder that accept a {@link MessageLite.Builder} as a field value. This is useful
+   * for implementing methods in {@link MessageLite.Builder}.
+   */
+  static final class Builder<T extends FieldDescriptorLite<T>> {
+
+    private SmallSortedMap<T, Object> fields;
+    private boolean hasLazyField;
+    private boolean isMutable;
+    private boolean hasNestedBuilders;
+
+    private Builder() {
+      this(SmallSortedMap.<T>newFieldMap(DEFAULT_FIELD_MAP_ARRAY_SIZE));
+    }
+
+    private Builder(SmallSortedMap<T, Object> fields) {
+      this.fields = fields;
+      this.isMutable = true;
+    }
+
+    /** Creates the FieldSet */
+    public FieldSet<T> build() {
+      if (fields.isEmpty()) {
+        return FieldSet.emptySet();
+      }
+      isMutable = false;
+      SmallSortedMap<T, Object> fieldsForBuild = fields;
+      if (hasNestedBuilders) {
+        // Make a copy of the fields map with all Builders replaced by Message.
+        fieldsForBuild = cloneAllFieldsMap(fields, /* copyList */ false);
+        replaceBuilders(fieldsForBuild);
+      }
+      FieldSet<T> fieldSet = new FieldSet<>(fieldsForBuild);
+      fieldSet.hasLazyField = hasLazyField;
+      return fieldSet;
+    }
+
+    private static <T extends FieldDescriptorLite<T>> void replaceBuilders(
+        SmallSortedMap<T, Object> fieldMap) {
+      for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) {
+        replaceBuilders(fieldMap.getArrayEntryAt(i));
+      }
+      for (Map.Entry<T, Object> entry : fieldMap.getOverflowEntries()) {
+        replaceBuilders(entry);
+      }
+    }
+
+    private static <T extends FieldDescriptorLite<T>> void replaceBuilders(
+        Map.Entry<T, Object> entry) {
+      entry.setValue(replaceBuilders(entry.getKey(), entry.getValue()));
+    }
+
+    private static <T extends FieldDescriptorLite<T>> Object replaceBuilders(
+        T descriptor, Object value) {
+      if (value == null) {
+        return value;
+      }
+      if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+        if (descriptor.isRepeated()) {
+          if (!(value instanceof List)) {
+            throw new IllegalStateException(
+                "Repeated field should contains a List but actually contains type: "
+                    + value.getClass());
+          }
+          @SuppressWarnings("unchecked")  // We just check that value is an instance of List above.
+          List<Object> list = (List<Object>) value;
+          for (int i = 0; i < list.size(); i++) {
+            Object oldElement = list.get(i);
+            Object newElement = replaceBuilder(oldElement);
+            if (newElement != oldElement) {
+              // If the list contains a Message.Builder, then make a copy of that list and then
+              // modify the Message.Builder into a Message and return the new list. This way, the
+              // existing Message.Builder will still be able to modify the inner fields of the
+              // original FieldSet.Builder.
+              if (list == value) {
+                list = new ArrayList<>(list);
+              }
+              list.set(i, newElement);
+            }
+          }
+          return list;
+        } else {
+          return replaceBuilder(value);
+        }
+      }
+      return value;
+    }
+
+    private static Object replaceBuilder(Object value) {
+      return (value instanceof MessageLite.Builder) ? ((MessageLite.Builder) value).build() : value;
+    }
+
+    /** Returns a new Builder using the fields from {@code fieldSet}. */
+    public static <T extends FieldDescriptorLite<T>> Builder<T> fromFieldSet(FieldSet<T> fieldSet) {
+      Builder<T> builder = new Builder<T>(cloneAllFieldsMap(fieldSet.fields, /* copyList */ true));
+      builder.hasLazyField = fieldSet.hasLazyField;
+      return builder;
+    }
+
+    // =================================================================
+
+    /** Get a simple map containing all the fields. */
+    public Map<T, Object> getAllFields() {
+      if (hasLazyField) {
+        SmallSortedMap<T, Object> result = cloneAllFieldsMap(fields, /* copyList */ false);
+        if (fields.isImmutable()) {
+          result.makeImmutable();
+        } else {
+          replaceBuilders(result);
+        }
+        return result;
+      }
+      return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields);
+    }
+
+    /** Useful for implementing {@link Message#hasField(Descriptors.FieldDescriptor)}. */
+    public boolean hasField(final T descriptor) {
+      if (descriptor.isRepeated()) {
+        throw new IllegalArgumentException("hasField() can only be called on non-repeated fields.");
+      }
+
+      return fields.get(descriptor) != null;
+    }
+
+    /**
+     * Useful for implementing {@link Message#getField(Descriptors.FieldDescriptor)}. This method
+     * returns {@code null} if the field is not set; in this case it is up to the caller to fetch
+     * the field's default value.
+     */
+    public Object getField(final T descriptor) {
+      Object value = getFieldAllowBuilders(descriptor);
+      return replaceBuilders(descriptor, value);
+    }
+
+    /** Same as {@link #getField(F)}, but allow a {@link MessageLite.Builder} to be returned. */
+    Object getFieldAllowBuilders(final T descriptor) {
+      Object o = fields.get(descriptor);
+      if (o instanceof LazyField) {
+        return ((LazyField) o).getValue();
+      }
+      return o;
+    }
+
+    private void ensureIsMutable() {
+      if (!isMutable) {
+        fields = cloneAllFieldsMap(fields, /* copyList */ true);
+        isMutable = true;
+      }
+    }
+
+    /**
+     * Useful for implementing {@link Message.Builder#setField(Descriptors.FieldDescriptor,
+     * Object)}.
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public void setField(final T descriptor, Object value) {
+      ensureIsMutable();
+      if (descriptor.isRepeated()) {
+        if (!(value instanceof List)) {
+          throw new IllegalArgumentException(
+              "Wrong object type used with protocol message reflection.");
+        }
+
+        // Wrap the contents in a new list so that the caller cannot change
+        // the list's contents after setting it.
+        final List newList = new ArrayList();
+        newList.addAll((List) value);
+        for (final Object element : newList) {
+          verifyType(descriptor.getLiteType(), element);
+          hasNestedBuilders = hasNestedBuilders || element instanceof MessageLite.Builder;
+        }
+        value = newList;
+      } else {
+        verifyType(descriptor.getLiteType(), value);
+      }
+
+      if (value instanceof LazyField) {
+        hasLazyField = true;
+      }
+      hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder;
+
+      fields.put(descriptor, value);
+    }
+
+    /** Useful for implementing {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */
+    public void clearField(final T descriptor) {
+      ensureIsMutable();
+      fields.remove(descriptor);
+      if (fields.isEmpty()) {
+        hasLazyField = false;
+      }
+    }
+
+    /**
+     * Useful for implementing {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
+     */
+    public int getRepeatedFieldCount(final T descriptor) {
+      if (!descriptor.isRepeated()) {
+        throw new IllegalArgumentException(
+            "getRepeatedField() can only be called on repeated fields.");
+      }
+
+      final Object value = getField(descriptor);
+      if (value == null) {
+        return 0;
+      } else {
+        return ((List<?>) value).size();
+      }
+    }
+
+    /**
+     * Useful for implementing {@link Message#getRepeatedField(Descriptors.FieldDescriptor, int)}.
+     */
+    public Object getRepeatedField(final T descriptor, final int index) {
+      if (hasNestedBuilders) {
+        ensureIsMutable();
+      }
+      Object value = getRepeatedFieldAllowBuilders(descriptor, index);
+      return replaceBuilder(value);
+    }
+
+    /**
+     * Same as {@link #getRepeatedField(F, int)}, but allow a {@link MessageLite.Builder} to be
+     * returned.
+     */
+    Object getRepeatedFieldAllowBuilders(final T descriptor, final int index) {
+      if (!descriptor.isRepeated()) {
+        throw new IllegalArgumentException(
+            "getRepeatedField() can only be called on repeated fields.");
+      }
+
+      final Object value = getFieldAllowBuilders(descriptor);
+
+      if (value == null) {
+        throw new IndexOutOfBoundsException();
+      } else {
+        return ((List<?>) value).get(index);
+      }
+    }
+
+    /**
+     * Useful for implementing {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,
+     * int, Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public void setRepeatedField(final T descriptor, final int index, final Object value) {
+      ensureIsMutable();
+      if (!descriptor.isRepeated()) {
+        throw new IllegalArgumentException(
+            "getRepeatedField() can only be called on repeated fields.");
+      }
+
+      hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder;
+
+      final Object list = getField(descriptor);
+      if (list == null) {
+        throw new IndexOutOfBoundsException();
+      }
+
+      verifyType(descriptor.getLiteType(), value);
+      ((List<Object>) list).set(index, value);
+    }
+
+    /**
+     * Useful for implementing {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,
+     * Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public void addRepeatedField(final T descriptor, final Object value) {
+      ensureIsMutable();
+      if (!descriptor.isRepeated()) {
+        throw new IllegalArgumentException(
+            "addRepeatedField() can only be called on repeated fields.");
+      }
+
+      hasNestedBuilders = hasNestedBuilders || value instanceof MessageLite.Builder;
+
+      verifyType(descriptor.getLiteType(), value);
+
+      final Object existingValue = getField(descriptor);
+      List<Object> list;
+      if (existingValue == null) {
+        list = new ArrayList<>();
+        fields.put(descriptor, list);
+      } else {
+        list = (List<Object>) existingValue;
+      }
+
+      list.add(value);
+    }
+
+    /**
+     * Verifies that the given object is of the correct type to be a valid value for the given
+     * field. (For repeated fields, this checks if the object is the right type to be one element of
+     * the field.)
+     *
+     * @throws IllegalArgumentException The value is not of the right type.
+     */
+    private static void verifyType(final WireFormat.FieldType type, final Object value) {
+      if (!FieldSet.isValidType(type, value)) {
+        // Builder can accept Message.Builder values even though FieldSet will reject.
+        if (type.getJavaType() == WireFormat.JavaType.MESSAGE
+            && value instanceof MessageLite.Builder) {
+          return;
+        }
+        throw new IllegalArgumentException(
+            "Wrong object type used with protocol message reflection.");
+      }
+    }
+
+    /**
+     * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} itself does not have any
+     * way of knowing about required fields that aren't actually present in the set, it is up to the
+     * caller to check that all required fields are present.
+     */
+    public boolean isInitialized() {
+      for (int i = 0; i < fields.getNumArrayEntries(); i++) {
+        if (!FieldSet.isInitialized(fields.getArrayEntryAt(i))) {
+          return false;
+        }
+      }
+      for (final Map.Entry<T, Object> entry : fields.getOverflowEntries()) {
+        if (!FieldSet.isInitialized(entry)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another {@link FieldSet}.
+     */
+    public void mergeFrom(final FieldSet<T> other) {
+      ensureIsMutable();
+      for (int i = 0; i < other.fields.getNumArrayEntries(); i++) {
+        mergeFromField(other.fields.getArrayEntryAt(i));
+      }
+      for (final Map.Entry<T, Object> entry : other.fields.getOverflowEntries()) {
+        mergeFromField(entry);
+      }
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private void mergeFromField(final Map.Entry<T, Object> entry) {
+      final T descriptor = entry.getKey();
+      Object otherValue = entry.getValue();
+      if (otherValue instanceof LazyField) {
+        otherValue = ((LazyField) otherValue).getValue();
+      }
+
+      if (descriptor.isRepeated()) {
+        Object value = getField(descriptor);
+        if (value == null) {
+          value = new ArrayList();
+        }
+        for (Object element : (List) otherValue) {
+          ((List) value).add(FieldSet.cloneIfMutable(element));
+        }
+        fields.put(descriptor, value);
+      } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
+        Object value = getField(descriptor);
+        if (value == null) {
+          fields.put(descriptor, FieldSet.cloneIfMutable(otherValue));
+        } else {
+          // Merge the messages.
+          if (value instanceof MessageLite.Builder) {
+            descriptor.internalMergeFrom((MessageLite.Builder) value, (MessageLite) otherValue);
+          } else {
+            value =
+                descriptor
+                    .internalMergeFrom(((MessageLite) value).toBuilder(), (MessageLite) otherValue)
+                    .build();
+            fields.put(descriptor, value);
+          }
+        }
+      } else {
+        fields.put(descriptor, cloneIfMutable(otherValue));
+      }
+    }
+  }
 }
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
@@ -1363,7 +1363,7 @@ public abstract class GeneratedMessageV3
       extends Builder<BuilderType>
       implements ExtendableMessageOrBuilder<MessageType> {
 
-    private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet();
+    private FieldSet.Builder<FieldDescriptor> extensions;
 
     protected ExtendableBuilder() {}
 
@@ -1374,18 +1374,18 @@ public abstract class GeneratedMessageV3
 
     // For immutable message conversion.
     void internalSetExtensionSet(FieldSet<FieldDescriptor> extensions) {
-      this.extensions = extensions;
+      this.extensions = FieldSet.Builder.fromFieldSet(extensions);
     }
 
     @Override
     public BuilderType clear() {
-      extensions = FieldSet.emptySet();
+      extensions = null;
       return super.clear();
     }
 
     private void ensureExtensionsIsMutable() {
-      if (extensions.isImmutable()) {
-        extensions = extensions.clone();
+      if (extensions == null) {
+        extensions = FieldSet.newBuilder();
       }
     }
 
@@ -1408,7 +1408,7 @@ public abstract class GeneratedMessageV3
       Extension<MessageType, Type> extension = checkNotLite(extensionLite);
 
       verifyExtensionContainingType(extension);
-      return extensions.hasField(extension.getDescriptor());
+      return extensions == null ? false : extensions.hasField(extension.getDescriptor());
     }
 
     /** Get the number of elements in a repeated extension. */
@@ -1419,7 +1419,7 @@ public abstract class GeneratedMessageV3
 
       verifyExtensionContainingType(extension);
       final FieldDescriptor descriptor = extension.getDescriptor();
-      return extensions.getRepeatedFieldCount(descriptor);
+      return extensions == null ? 0 : extensions.getRepeatedFieldCount(descriptor);
     }
 
     /** Get the value of an extension. */
@@ -1429,7 +1429,7 @@ public abstract class GeneratedMessageV3
 
       verifyExtensionContainingType(extension);
       FieldDescriptor descriptor = extension.getDescriptor();
-      final Object value = extensions.getField(descriptor);
+      final Object value = extensions == null ? null : extensions.getField(descriptor);
       if (value == null) {
         if (descriptor.isRepeated()) {
           return (Type) Collections.emptyList();
@@ -1453,8 +1453,11 @@ public abstract class GeneratedMessageV3
 
       verifyExtensionContainingType(extension);
       FieldDescriptor descriptor = extension.getDescriptor();
-      return (Type) extension.singularFromReflectionType(
-          extensions.getRepeatedField(descriptor, index));
+      if (extensions == null) {
+        throw new IndexOutOfBoundsException();
+      }
+      return (Type)
+          extension.singularFromReflectionType(extensions.getRepeatedField(descriptor, index));
     }
 
     /** Set the value of an extension. */
@@ -1605,7 +1608,7 @@ public abstract class GeneratedMessageV3
 
     /** Called by subclasses to check if all extensions are initialized. */
     protected boolean extensionsAreInitialized() {
-      return extensions.isInitialized();
+      return extensions == null ? true : extensions.isInitialized();
     }
 
     /**
@@ -1613,8 +1616,9 @@ public abstract class GeneratedMessageV3
      * building the message.
      */
     private FieldSet<FieldDescriptor> buildExtensions() {
-      extensions.makeImmutable();
-      return extensions;
+      return extensions == null
+          ? (FieldSet<FieldDescriptor>) FieldSet.emptySet()
+          : extensions.build();
     }
 
     @Override
@@ -1628,7 +1632,9 @@ public abstract class GeneratedMessageV3
     @Override
     public Map<FieldDescriptor, Object> getAllFields() {
       final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
-      result.putAll(extensions.getAllFields());
+      if (extensions != null) {
+        result.putAll(extensions.getAllFields());
+      }
       return Collections.unmodifiableMap(result);
     }
 
@@ -1636,7 +1642,7 @@ public abstract class GeneratedMessageV3
     public Object getField(final FieldDescriptor field) {
       if (field.isExtension()) {
         verifyContainingType(field);
-        final Object value = extensions.getField(field);
+        final Object value = extensions == null ? null : extensions.getField(field);
         if (value == null) {
           if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
             // Lacking an ExtensionRegistry, we have no way to determine the
@@ -1654,10 +1660,43 @@ public abstract class GeneratedMessageV3
     }
 
     @Override
+    public Message.Builder getFieldBuilder(final FieldDescriptor field) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+          throw new UnsupportedOperationException(
+              "getFieldBuilder() called on a non-Message type.");
+        }
+        ensureExtensionsIsMutable();
+        final Object value = extensions.getFieldAllowBuilders(field);
+        if (value == null) {
+          Message.Builder builder = DynamicMessage.newBuilder(field.getMessageType());
+          extensions.setField(field, builder);
+          onChanged();
+          return builder;
+        } else {
+          if (value instanceof Message.Builder) {
+            return (Message.Builder) value;
+          } else if (value instanceof Message) {
+            Message.Builder builder = ((Message) value).toBuilder();
+            extensions.setField(field, builder);
+            onChanged();
+            return builder;
+          } else {
+            throw new UnsupportedOperationException(
+                "getRepeatedFieldBuilder() called on a non-Message type.");
+          }
+        }
+      } else {
+        return super.getFieldBuilder(field);
+      }
+    }
+
+    @Override
     public int getRepeatedFieldCount(final FieldDescriptor field) {
       if (field.isExtension()) {
         verifyContainingType(field);
-        return extensions.getRepeatedFieldCount(field);
+        return extensions == null ? 0 : extensions.getRepeatedFieldCount(field);
       } else {
         return super.getRepeatedFieldCount(field);
       }
@@ -1668,6 +1707,9 @@ public abstract class GeneratedMessageV3
                                    final int index) {
       if (field.isExtension()) {
         verifyContainingType(field);
+        if (extensions == null) {
+          throw new IndexOutOfBoundsException();
+        }
         return extensions.getRepeatedField(field, index);
       } else {
         return super.getRepeatedField(field, index);
@@ -1675,10 +1717,36 @@ public abstract class GeneratedMessageV3
     }
 
     @Override
+    public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, final int index) {
+      if (field.isExtension()) {
+        verifyContainingType(field);
+        ensureExtensionsIsMutable();
+        if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
+          throw new UnsupportedOperationException(
+              "getRepeatedFieldBuilder() called on a non-Message type.");
+        }
+        final Object value = extensions.getRepeatedFieldAllowBuilders(field, index);
+        if (value instanceof Message.Builder) {
+          return (Message.Builder) value;
+        } else if (value instanceof Message) {
+          Message.Builder builder = ((Message) value).toBuilder();
+          extensions.setRepeatedField(field, index, builder);
+          onChanged();
+          return builder;
+        } else {
+          throw new UnsupportedOperationException(
+              "getRepeatedFieldBuilder() called on a non-Message type.");
+        }
+      } else {
+        return super.getRepeatedFieldBuilder(field, index);
+      }
+    }
+
+    @Override
     public boolean hasField(final FieldDescriptor field) {
       if (field.isExtension()) {
         verifyContainingType(field);
-        return extensions.hasField(field);
+        return extensions == null ? false : extensions.hasField(field);
       } else {
         return super.hasField(field);
       }
@@ -1749,9 +1817,11 @@ public abstract class GeneratedMessageV3
     }
 
     protected final void mergeExtensionFields(final ExtendableMessage other) {
-      ensureExtensionsIsMutable();
-      extensions.mergeFrom(other.extensions);
-      onChanged();
+      if (other.extensions != null) {
+        ensureExtensionsIsMutable();
+        extensions.mergeFrom(other.extensions);
+        onChanged();
+      }
     }
 
     private void verifyContainingType(final FieldDescriptor field) {
--- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -32,9 +32,10 @@ package com.google.protobuf;
 
 import com.google.protobuf.Internal.ProtobufList;
 import java.util.Arrays;
+import java.util.RandomAccess;
 
 /** Implements {@link ProtobufList} for non-primitive and {@link String} types. */
-final class ProtobufArrayList<E> extends AbstractProtobufList<E> {
+final class ProtobufArrayList<E> extends AbstractProtobufList<E> implements RandomAccess {
 
   private static final ProtobufArrayList<Object> EMPTY_LIST =
       new ProtobufArrayList<Object>(new Object[0], 0);
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -113,8 +113,8 @@ public final class TextFormat {
   }
 
   /**
-   * Generates a human readable form of the field, useful for debugging and other purposes, with no
-   * newline characters.
+   * Generates a human readable form of the field, useful for debugging and other purposes, with
+   * no newline characters.
    *
    * @deprecated Use {@code printer().shortDebugString(FieldDescriptor, Object)}
    */
@@ -122,10 +122,10 @@ public final class TextFormat {
   public static String shortDebugString(final FieldDescriptor field, final Object value) {
     return printer().shortDebugString(field, value);
   }
-
+  //
   /**
-   * Generates a human readable form of the unknown fields, useful for debugging and other purposes,
-   * with no newline characters.
+   * Generates a human readable form of the unknown fields, useful for debugging and other
+   * purposes, with no newline characters.
    *
    * @deprecated Use {@code printer().shortDebugString(UnknownFieldSet)}
    */
@@ -166,8 +166,8 @@ public final class TextFormat {
   }
 
   /**
-   * Same as {@code printToString()}, except that non-ASCII characters in string type fields are not
-   * escaped in backslash+octals.
+   * Same as {@code printToString()}, except that non-ASCII characters in string type fields are
+   * not escaped in backslash+octals.
    *
    * @deprecated Use {@code printer().escapingNonAscii(false).printToString(UnknownFieldSet)}
    */
@@ -175,20 +175,21 @@ public final class TextFormat {
   public static String printToUnicodeString(final UnknownFieldSet fields) {
     return printer().escapingNonAscii(false).printToString(fields);
   }
-
+  //
   /** @deprecated Use {@code printer().printField(FieldDescriptor, Object, Appendable)} */
   @Deprecated
   public static void printField(
-      final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+      final FieldDescriptor field, final Object value, final Appendable output)
+      throws IOException {
     printer().printField(field, value, output);
   }
-
+  //
   /** @deprecated Use {@code printer().printFieldToString(FieldDescriptor, Object)} */
   @Deprecated
   public static String printFieldToString(final FieldDescriptor field, final Object value) {
     return printer().printFieldToString(field, value);
   }
-
+  //
   /**
    * Outputs a unicode textual representation of the value of given field value.
    *
@@ -205,7 +206,8 @@ public final class TextFormat {
    */
   @Deprecated
   public static void printUnicodeFieldValue(
-      final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+      final FieldDescriptor field, final Object value, final Appendable output)
+      throws IOException {
     printer().escapingNonAscii(false).printFieldValue(field, value, output);
   }
 
@@ -285,13 +287,16 @@ public final class TextFormat {
   public static final class Printer {
 
     // Printer instance which escapes non-ASCII characters.
-    private static final Printer DEFAULT = new Printer(true);
+    private static final Printer DEFAULT = new Printer(true, TypeRegistry.getEmptyTypeRegistry());
 
     /** Whether to escape non ASCII characters with backslash and octal. */
     private final boolean escapeNonAscii;
 
-    private Printer(boolean escapeNonAscii) {
+    private final TypeRegistry typeRegistry;
+
+    private Printer(boolean escapeNonAscii, TypeRegistry typeRegistry) {
       this.escapeNonAscii = escapeNonAscii;
+      this.typeRegistry = typeRegistry;
     }
 
     /**
@@ -304,7 +309,20 @@ public final class TextFormat {
      *     with the escape mode set to the given parameter.
      */
     public Printer escapingNonAscii(boolean escapeNonAscii) {
-      return new Printer(escapeNonAscii);
+      return new Printer(escapeNonAscii, typeRegistry);
+    }
+
+    /**
+     * Creates a new {@link Printer} using the given typeRegistry. The new Printer clones all other
+     * configurations from the current {@link Printer}.
+     *
+     * @throws IllegalArgumentException if a registry is already set.
+     */
+    public Printer usingTypeRegistry(TypeRegistry typeRegistry) {
+      if (this.typeRegistry != TypeRegistry.getEmptyTypeRegistry()) {
+        throw new IllegalArgumentException("Only one typeRegistry is allowed.");
+      }
+      return new Printer(escapeNonAscii, typeRegistry);
     }
 
     /**
@@ -323,9 +341,66 @@ public final class TextFormat {
 
     private void print(final MessageOrBuilder message, final TextGenerator generator)
         throws IOException {
+      if (message.getDescriptorForType().getFullName().equals("google.protobuf.Any")
+          && printAny(message, generator)) {
+        return;
+      }
       printMessage(message, generator);
     }
 
+    /**
+     * Attempt to print the 'google.protobuf.Any' message in a human-friendly format. Returns false
+     * if the message isn't a valid 'google.protobuf.Any' message (in which case the message should
+     * be rendered just like a regular message to help debugging).
+     */
+    private boolean printAny(final MessageOrBuilder message, final TextGenerator generator)
+        throws IOException {
+      Descriptor messageType = message.getDescriptorForType();
+      FieldDescriptor typeUrlField = messageType.findFieldByNumber(1);
+      FieldDescriptor valueField = messageType.findFieldByNumber(2);
+      if (typeUrlField == null
+          || typeUrlField.getType() != FieldDescriptor.Type.STRING
+          || valueField == null
+          || valueField.getType() != FieldDescriptor.Type.BYTES) {
+        // The message may look like an Any but isn't actually an Any message (might happen if the
+        // user tries to use DynamicMessage to construct an Any from incomplete Descriptor).
+        return false;
+      }
+      String typeUrl = (String) message.getField(typeUrlField);
+      // If type_url is not set, we will not be able to decode the content of the value, so just
+      // print out the Any like a regular message.
+      if (typeUrl.isEmpty()) {
+        return false;
+      }
+      Object value = message.getField(valueField);
+
+      Message.Builder contentBuilder = null;
+      try {
+        Descriptor contentType = typeRegistry.getDescriptorForTypeUrl(typeUrl);
+        if (contentType == null) {
+          return false;
+        }
+        contentBuilder = DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
+        contentBuilder.mergeFrom((ByteString) value);
+      } catch (InvalidProtocolBufferException e) {
+        // The value of Any is malformed. We cannot print it out nicely, so fallback to printing out
+        // the type_url and value as bytes. Note that we fail open here to be consistent with
+        // text_format.cc, and also to allow a way for users to inspect the content of the broken
+        // message.
+        return false;
+      }
+      generator.print("[");
+      generator.print(typeUrl);
+      generator.print("] {");
+      generator.eol();
+      generator.indent();
+      print(contentBuilder, generator);
+      generator.outdent();
+      generator.print("}");
+      generator.eol();
+      return true;
+    }
+
     public String printFieldToString(final FieldDescriptor field, final Object value) {
       try {
         final StringBuilder text = new StringBuilder();
@@ -1382,6 +1457,7 @@ public final class TextFormat {
       FORBID_SINGULAR_OVERWRITES
     }
 
+    private final TypeRegistry typeRegistry;
     private final boolean allowUnknownFields;
     private final boolean allowUnknownEnumValues;
     private final boolean allowUnknownExtensions;
@@ -1389,11 +1465,13 @@ public final class TextFormat {
     private TextFormatParseInfoTree.Builder parseInfoTreeBuilder;
 
     private Parser(
+        TypeRegistry typeRegistry,
         boolean allowUnknownFields,
         boolean allowUnknownEnumValues,
         boolean allowUnknownExtensions,
         SingularOverwritePolicy singularOverwritePolicy,
         TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
+      this.typeRegistry = typeRegistry;
       this.allowUnknownFields = allowUnknownFields;
       this.allowUnknownEnumValues = allowUnknownEnumValues;
       this.allowUnknownExtensions = allowUnknownExtensions;
@@ -1414,6 +1492,18 @@ public final class TextFormat {
       private SingularOverwritePolicy singularOverwritePolicy =
           SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES;
       private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null;
+      private TypeRegistry typeRegistry = TypeRegistry.getEmptyTypeRegistry();
+
+      /**
+       * Sets the TypeRegistry for resolving Any. If this is not set, TextFormat will not be able to
+       * parse Any unless Any is write as bytes.
+       *
+       * @throws IllegalArgumentException if a registry is already set.
+       */
+      public Builder setTypeRegistry(TypeRegistry typeRegistry) {
+        this.typeRegistry = typeRegistry;
+        return this;
+      }
 
       /**
        * Set whether this parser will allow unknown fields. By default, an exception is thrown if an
@@ -1452,6 +1542,7 @@ public final class TextFormat {
 
       public Parser build() {
         return new Parser(
+            typeRegistry,
             allowUnknownFields,
             allowUnknownEnumValues,
             allowUnknownExtensions,
@@ -1834,6 +1925,14 @@ public final class TextFormat {
           endToken = "}";
         }
 
+        // Try to parse human readable format of Any in the form: [type_url]: { ... }
+        if (field.getMessageType().getFullName().equals("google.protobuf.Any")
+            && tokenizer.tryConsume("[")) {
+          value =
+              consumeAnyFieldValue(
+                  tokenizer, extensionRegistry, field, parseTreeBuilder, unknownFields);
+          tokenizer.consume(endToken);
+        } else {
           Message defaultInstance = (extension == null) ? null : extension.defaultInstance;
           MessageReflection.MergeTarget subField =
               target.newMergeTargetForField(field, defaultInstance);
@@ -1846,6 +1945,8 @@ public final class TextFormat {
           }
 
           value = subField.finish();
+        }
+
       } else {
         switch (field.getType()) {
           case INT32:
@@ -1951,6 +2052,71 @@ public final class TextFormat {
       }
     }
 
+    private Object consumeAnyFieldValue(
+        final Tokenizer tokenizer,
+        final ExtensionRegistry extensionRegistry,
+        final FieldDescriptor field,
+        final TextFormatParseInfoTree.Builder parseTreeBuilder,
+        List<UnknownField> unknownFields)
+        throws ParseException {
+      // Try to parse human readable format of Any in the form: [type_url]: { ... }
+      StringBuilder typeUrlBuilder = new StringBuilder();
+      // Parse the type_url inside [].
+      while (true) {
+        typeUrlBuilder.append(tokenizer.consumeIdentifier());
+        if (tokenizer.tryConsume("]")) {
+          break;
+        }
+        if (tokenizer.tryConsume("/")) {
+          typeUrlBuilder.append("/");
+        } else if (tokenizer.tryConsume(".")) {
+          typeUrlBuilder.append(".");
+        } else {
+          throw tokenizer.parseExceptionPreviousToken("Expected a valid type URL.");
+        }
+      }
+      tokenizer.tryConsume(":");
+      final String anyEndToken;
+      if (tokenizer.tryConsume("<")) {
+        anyEndToken = ">";
+      } else {
+        tokenizer.consume("{");
+        anyEndToken = "}";
+      }
+      String typeUrl = typeUrlBuilder.toString();
+      Descriptor contentType = null;
+      try {
+        contentType = typeRegistry.getDescriptorForTypeUrl(typeUrl);
+      } catch (InvalidProtocolBufferException e) {
+        throw tokenizer.parseException("Invalid valid type URL. Found: " + typeUrl);
+      }
+      if (contentType == null) {
+        throw tokenizer.parseException(
+            "Unable to parse Any of type: "
+                + typeUrl
+                + ". Please make sure that the TypeRegistry contains the descriptors for the given"
+                + " types.");
+      }
+      Message.Builder contentBuilder =
+          DynamicMessage.getDefaultInstance(contentType).newBuilderForType();
+      MessageReflection.BuilderAdapter contentTarget =
+          new MessageReflection.BuilderAdapter(contentBuilder);
+      while (!tokenizer.tryConsume(anyEndToken)) {
+        mergeField(tokenizer, extensionRegistry, contentTarget, parseTreeBuilder, unknownFields);
+      }
+
+      // Serialize the content and put it back into an Any. Note that we can't depend on Any here
+      // because of a cyclic dependency (java_proto_library for any_java_proto depends on the
+      // protobuf_impl), so we need to construct the Any using proto reflection.
+      Descriptor anyDescriptor = field.getMessageType();
+      Message.Builder anyBuilder =
+          DynamicMessage.getDefaultInstance(anyDescriptor).newBuilderForType();
+      anyBuilder.setField(anyDescriptor.findFieldByName("type_url"), typeUrlBuilder.toString());
+      anyBuilder.setField(
+          anyDescriptor.findFieldByName("value"), contentBuilder.build().toByteString());
+
+      return anyBuilder.build();
+    }
 
     /** Skips the next field including the field's name and value. */
     private void skipField(Tokenizer tokenizer) throws ParseException {
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/TypeRegistry.java
@@ -0,0 +1,160 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * A TypeRegistry is used to resolve Any messages. You must provide a TypeRegistry containing all
+ * message types used in Any message fields.
+ */
+public class TypeRegistry {
+  private static final Logger logger = Logger.getLogger(TypeRegistry.class.getName());
+
+  private static class EmptyTypeRegistryHolder {
+    private static final TypeRegistry EMPTY =
+        new TypeRegistry(Collections.<String, Descriptor>emptyMap());
+  }
+
+  public static TypeRegistry getEmptyTypeRegistry() {
+    return EmptyTypeRegistryHolder.EMPTY;
+  }
+
+
+  public static Builder newBuilder() {
+    return new Builder();
+  }
+
+  /**
+   * Find a type by its full name. Returns null if it cannot be found in this {@link TypeRegistry}.
+   */
+  public Descriptor find(String name) {
+    return types.get(name);
+  }
+
+  /**
+   * Find a type by its typeUrl. Returns null if it cannot be found in this {@link TypeRegistry}.
+   */
+  /* @Nullable */
+  public final Descriptor getDescriptorForTypeUrl(String typeUrl)
+      throws InvalidProtocolBufferException {
+    return find(getTypeName(typeUrl));
+  }
+
+  private final Map<String, Descriptor> types;
+
+  TypeRegistry(Map<String, Descriptor> types) {
+    this.types = types;
+  }
+
+  private static String getTypeName(String typeUrl) throws InvalidProtocolBufferException {
+    String[] parts = typeUrl.split("/");
+    if (parts.length == 1) {
+      throw new InvalidProtocolBufferException("Invalid type url found: " + typeUrl);
+    }
+    return parts[parts.length - 1];
+  }
+
+  /** A Builder is used to build {@link TypeRegistry}. */
+  public static final class Builder {
+    private Builder() {}
+
+    /**
+     * Adds a message type and all types defined in the same .proto file as well as all transitively
+     * imported .proto files to this {@link Builder}.
+     */
+    public Builder add(Descriptor messageType) {
+      if (types == null) {
+        throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
+      }
+      addFile(messageType.getFile());
+      return this;
+    }
+
+    /**
+     * Adds message types and all types defined in the same .proto file as well as all transitively
+     * imported .proto files to this {@link Builder}.
+     */
+    public Builder add(Iterable<Descriptor> messageTypes) {
+      if (types == null) {
+        throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
+      }
+      for (Descriptor type : messageTypes) {
+        addFile(type.getFile());
+      }
+      return this;
+    }
+
+    /** Builds a {@link TypeRegistry}. This method can only be called once for one Builder. */
+    public TypeRegistry build() {
+      TypeRegistry result = new TypeRegistry(types);
+      // Make sure the built {@link TypeRegistry} is immutable.
+      types = null;
+      return result;
+    }
+
+    private void addFile(FileDescriptor file) {
+      // Skip the file if it's already added.
+      if (!files.add(file.getFullName())) {
+        return;
+      }
+      for (FileDescriptor dependency : file.getDependencies()) {
+        addFile(dependency);
+      }
+      for (Descriptor message : file.getMessageTypes()) {
+        addMessage(message);
+      }
+    }
+
+    private void addMessage(Descriptor message) {
+      for (Descriptor nestedType : message.getNestedTypes()) {
+        addMessage(nestedType);
+      }
+
+      if (types.containsKey(message.getFullName())) {
+        logger.warning("Type " + message.getFullName() + " is added multiple times.");
+        return;
+      }
+
+      types.put(message.getFullName(), message);
+    }
+
+    private final Set<String> files = new HashSet<>();
+    private Map<String, Descriptor> types = new HashMap<>();
+  }
+}
--- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -115,6 +115,160 @@ public class GeneratedMessageTest extend
     GeneratedMessageV3.setAlwaysUseFieldBuildersForTesting(false);
   }
 
+  public void testGetFieldBuilderForExtensionField() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    Message.Builder fieldBuilder =
+        builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+    int expected = 7432;
+    FieldDescriptor field =
+        NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+    // fieldBuilder still updates the builder after builder build() has been called.
+    expected += 100;
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+  }
+
+  public void testGetFieldBuilderWithExistingMessage() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    builder.setExtension(
+        UnittestProto.optionalNestedMessageExtension,
+        NestedMessage.newBuilder().setBb(123).build());
+    Message.Builder fieldBuilder =
+        builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+    int expected = 7432;
+    FieldDescriptor field =
+        NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+    // fieldBuilder still updates the builder after builder build() has been called.
+    expected += 100;
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+  }
+
+  public void testGetFieldBuilderWithExistingBuilder() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    NestedMessage.Builder nestedMessageBuilder = NestedMessage.newBuilder().setBb(123);
+    builder.setField(
+        UnittestProto.optionalNestedMessageExtension.getDescriptor(), nestedMessageBuilder);
+    Message.Builder fieldBuilder =
+        builder.getFieldBuilder(UnittestProto.optionalNestedMessageExtension.getDescriptor());
+    int expected = 7432;
+    FieldDescriptor field =
+        NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+    // Existing nestedMessageBuilder will also update builder.
+    expected += 100;
+    nestedMessageBuilder.setBb(expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+
+    // fieldBuilder still updates the builder.
+    expected += 100;
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.optionalNestedMessageExtension).getBb());
+  }
+
+  public void testGetRepeatedFieldBuilderForExtensionField() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    builder.addExtension(
+        UnittestProto.repeatedNestedMessageExtension,
+        NestedMessage.newBuilder().setBb(123).build());
+    Message.Builder fieldBuilder =
+        builder.getRepeatedFieldBuilder(
+            UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+    int expected = 7432;
+    FieldDescriptor field =
+        NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+    // fieldBuilder still updates the builder after builder build() has been called.
+    expected += 100;
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+  }
+
+  public void testGetRepeatedFieldBuilderForExistingBuilder() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    NestedMessage.Builder nestedMessageBuilder = NestedMessage.newBuilder().setBb(123);
+    builder.addRepeatedField(
+        UnittestProto.repeatedNestedMessageExtension.getDescriptor(), nestedMessageBuilder);
+    Message.Builder fieldBuilder =
+        builder.getRepeatedFieldBuilder(
+            UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+    int expected = 7432;
+    FieldDescriptor field =
+        NestedMessage.getDescriptor().findFieldByNumber(NestedMessage.BB_FIELD_NUMBER);
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+    // Existing nestedMessageBuilder will also update builder.
+    expected += 100;
+    nestedMessageBuilder.setBb(expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+
+    // fieldBuilder still updates the builder.
+    expected += 100;
+    fieldBuilder.setField(field, expected);
+    assertEquals(
+        expected,
+        builder.build().getExtension(UnittestProto.repeatedNestedMessageExtension, 0).getBb());
+  }
+
+  public void testGetExtensionFieldOutOfBound() {
+    TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
+    try {
+      builder.getRepeatedField(UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+      fail("Expected IndexOutOfBoundsException to be thrown");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      builder.getExtension(UnittestProto.repeatedNestedMessageExtension, 0);
+      fail("Expected IndexOutOfBoundsException to be thrown");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    TestAllExtensions extensionsMessage = builder.build();
+    try {
+      extensionsMessage.getRepeatedField(
+          UnittestProto.repeatedNestedMessageExtension.getDescriptor(), 0);
+      fail("Expected IndexOutOfBoundsException to be thrown");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+    try {
+      extensionsMessage.getExtension(UnittestProto.repeatedNestedMessageExtension, 0);
+      fail("Expected IndexOutOfBoundsException to be thrown");
+    } catch (IndexOutOfBoundsException expected) {
+    }
+  }
+
   public void testDefaultInstance() throws Exception {
     assertSame(
         TestAllTypes.getDefaultInstance(),
--- a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
+++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
@@ -30,6 +30,7 @@
 
 package com.google.protobuf;
 
+import protobuf_unittest.Engine;
 import protobuf_unittest.Vehicle;
 import protobuf_unittest.Wheel;
 import java.util.ArrayList;
@@ -64,7 +65,7 @@ public class NestedBuildersTest extends
     for (int i = 0; i < 4; i++) {
       vehicleBuilder.getWheelBuilder(i).setRadius(5).setWidth(i + 10);
     }
-    vehicleBuilder.getEngineBuilder().setLiters(20);
+    Engine.Builder engineBuilder = vehicleBuilder.getEngineBuilder().setLiters(20);
 
     vehicle = vehicleBuilder.build();
     for (int i = 0; i < 4; i++) {
@@ -74,6 +75,9 @@ public class NestedBuildersTest extends
     }
     assertEquals(20, vehicle.getEngine().getLiters());
     assertTrue(vehicle.hasEngine());
+
+    engineBuilder.setLiters(50);
+    assertEquals(50, vehicleBuilder.getEngine().getLiters());
   }
 
   public void testMessagesAreCached() {
--- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -33,9 +33,14 @@ package com.google.protobuf;
 import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
 import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
 
+import com.google.protobuf.DescriptorProtos.DescriptorProto;
+import com.google.protobuf.DescriptorProtos.FieldDescriptorProto;
+import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
 import com.google.protobuf.Descriptors.Descriptor;
 import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
 import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
+import any_test.AnyTestProto.TestAny;
 import map_test.MapTestProto.TestMap;
 import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
 import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
@@ -48,6 +53,7 @@ import protobuf_unittest.UnittestProto.T
 import protobuf_unittest.UnittestProto.TestRequired;
 import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
 import java.io.StringReader;
+import java.util.Arrays;
 import java.util.List;
 import java.util.logging.Logger;
 import junit.framework.TestCase;
@@ -506,6 +512,191 @@ public class TextFormatTest extends Test
     assertEquals(2, builder.getOptionalInt64());
   }
 
+  public void testPrintAny_customBuiltTypeRegistry() throws Exception {
+    TestAny testAny =
+        TestAny.newBuilder()
+            .setValue(
+                Any.newBuilder()
+                    .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+                    .setValue(
+                        TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+                    .build())
+            .build();
+    String actual =
+        TextFormat.printer()
+            .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+            .printToString(testAny);
+    String expected =
+        "value {\n"
+            + "  [type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+            + "    optional_int32: 12345\n"
+            + "  }\n"
+            + "}\n";
+    assertEquals(expected, actual);
+  }
+
+  private static Descriptor createDescriptorForAny(FieldDescriptorProto... fields)
+      throws Exception {
+    FileDescriptor fileDescriptor =
+        FileDescriptor.buildFrom(
+            FileDescriptorProto.newBuilder()
+                .setName("any.proto")
+                .setPackage("google.protobuf")
+                .setSyntax("proto3")
+                .addMessageType(
+                    DescriptorProto.newBuilder()
+                        .setName("Any")
+                        .addAllField(Arrays.asList(fields)))
+                .build(),
+            new FileDescriptor[0]);
+    return fileDescriptor.getMessageTypes().get(0);
+  }
+
+  public void testPrintAny_anyWithDynamicMessage() throws Exception {
+    Descriptor descriptor =
+        createDescriptorForAny(
+            FieldDescriptorProto.newBuilder()
+                .setName("type_url")
+                .setNumber(1)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_STRING)
+                .build(),
+            FieldDescriptorProto.newBuilder()
+                .setName("value")
+                .setNumber(2)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_BYTES)
+                .build());
+    DynamicMessage testAny =
+        DynamicMessage.newBuilder(descriptor)
+            .setField(
+                descriptor.findFieldByNumber(1),
+                "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+            .setField(
+                descriptor.findFieldByNumber(2),
+                TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+            .build();
+    String actual =
+        TextFormat.printer()
+            .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+            .printToString(testAny);
+    String expected =
+        "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+            + "  optional_int32: 12345\n"
+            + "}\n";
+    assertEquals(expected, actual);
+  }
+
+  public void testPrintAny_anyFromWithNoValueField() throws Exception {
+    Descriptor descriptor =
+        createDescriptorForAny(
+            FieldDescriptorProto.newBuilder()
+                .setName("type_url")
+                .setNumber(1)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_STRING)
+                .build());
+    DynamicMessage testAny =
+        DynamicMessage.newBuilder(descriptor)
+            .setField(
+                descriptor.findFieldByNumber(1),
+                "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+            .build();
+    String actual =
+        TextFormat.printer()
+            .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+            .printToString(testAny);
+    String expected = "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n";
+    assertEquals(expected, actual);
+  }
+
+  public void testPrintAny_anyFromWithNoTypeUrlField() throws Exception {
+    Descriptor descriptor =
+        createDescriptorForAny(
+            FieldDescriptorProto.newBuilder()
+                .setName("value")
+                .setNumber(2)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_BYTES)
+                .build());
+    DynamicMessage testAny =
+        DynamicMessage.newBuilder(descriptor)
+            .setField(
+                descriptor.findFieldByNumber(2),
+                TestAllTypes.newBuilder().setOptionalInt32(12345).build().toByteString())
+            .build();
+    String actual =
+        TextFormat.printer()
+            .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+            .printToString(testAny);
+    String expected = "value: \"\\b\\271`\"\n";
+    assertEquals(expected, actual);
+  }
+
+  public void testPrintAny_anyWithInvalidFieldType() throws Exception {
+    Descriptor descriptor =
+        createDescriptorForAny(
+            FieldDescriptorProto.newBuilder()
+                .setName("type_url")
+                .setNumber(1)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_STRING)
+                .build(),
+            FieldDescriptorProto.newBuilder()
+                .setName("value")
+                .setNumber(2)
+                .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+                .setType(FieldDescriptorProto.Type.TYPE_STRING)
+                .build());
+    DynamicMessage testAny =
+        DynamicMessage.newBuilder(descriptor)
+            .setField(
+                descriptor.findFieldByNumber(1),
+                "type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+            .setField(descriptor.findFieldByNumber(2), "test")
+            .build();
+    String actual =
+        TextFormat.printer()
+            .usingTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+            .printToString(testAny);
+    String expected =
+        "type_url: \"type.googleapis.com/protobuf_unittest.TestAllTypes\"\n" + "value: \"test\"\n";
+    assertEquals(expected, actual);
+  }
+
+
+  public void testMergeAny_customBuiltTypeRegistry() throws Exception {
+    TestAny.Builder builder = TestAny.newBuilder();
+    TextFormat.Parser.newBuilder()
+        .setTypeRegistry(TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build())
+        .build()
+        .merge(
+            "value: {\n"
+                + "[type.googleapis.com/protobuf_unittest.TestAllTypes] {\n"
+                + "optional_int32: 12345\n"
+                + "optional_nested_message {\n"
+                + "  bb: 123\n"
+                + "}\n"
+                + "}\n"
+                + "}",
+            builder);
+    assertEquals(
+        TestAny.newBuilder()
+            .setValue(
+                Any.newBuilder()
+                    .setTypeUrl("type.googleapis.com/" + TestAllTypes.getDescriptor().getFullName())
+                    .setValue(
+                        TestAllTypes.newBuilder()
+                            .setOptionalInt32(12345)
+                            .setOptionalNestedMessage(
+                                TestAllTypes.NestedMessage.newBuilder().setBb(123))
+                            .build()
+                            .toByteString())
+                    .build())
+            .build(),
+        builder.build());
+  }
+
 
   private void assertParseError(String error, String text) {
     // Test merge().
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java
@@ -0,0 +1,70 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import protobuf_unittest.UnittestProto;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class TypeRegistryTest {
+
+  @Test
+  public void findDescriptorByFullName() throws Exception {
+    Descriptor descriptor = UnittestProto.TestAllTypes.getDescriptor();
+    assertNull(TypeRegistry.getEmptyTypeRegistry().find(descriptor.getFullName()));
+
+    assertSame(
+        descriptor,
+        TypeRegistry.newBuilder().add(descriptor).build().find(descriptor.getFullName()));
+  }
+
+  @Test
+  public void findDescriptorByTypeUrl() throws Exception {
+    Descriptor descriptor = UnittestProto.TestAllTypes.getDescriptor();
+    assertNull(
+        TypeRegistry.getEmptyTypeRegistry()
+            .getDescriptorForTypeUrl("type.googleapis.com/" + descriptor.getFullName()));
+
+    assertSame(
+        descriptor,
+        TypeRegistry.newBuilder()
+            .add(descriptor)
+            .build()
+            .getDescriptorForTypeUrl("type.googleapis.com/" + descriptor.getFullName()));
+  }
+
+}
--- a/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_extra_interfaces.proto
@@ -36,8 +36,6 @@ package protobuf_unittest;
 message Proto1 {
   option experimental_java_message_interface =
       "com.google.protobuf.ExtraInterfaces.HasBoolValue";
-  option experimental_java_interface_extends =
-      "com.google.protobuf.ExtraInterfaces.HasByteValue";
   option experimental_java_message_interface =
       "com.google.protobuf.ExtraInterfaces.HasStringValue<Proto1>";
   option experimental_java_builder_interface =
--- a/java/lite/pom.xml
+++ b/java/lite/pom.xml
@@ -224,6 +224,7 @@
                     <exclude>TextFormatParseLocationTest.java</exclude>
                     <exclude>TextFormatTest.java</exclude>
                     <exclude>TestUtil.java</exclude>
+                    <exclude>TypeRegistryTest.java</exclude>
                     <exclude>UnknownEnumValueTest.java</exclude>
                     <exclude>UnknownFieldSetLiteTest.java</exclude>
                     <exclude>UnknownFieldSetTest.java</exclude>
--- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
+++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java
@@ -108,15 +108,22 @@ public class JsonFormat {
    */
   public static Printer printer() {
     return new Printer(
-        TypeRegistry.getEmptyTypeRegistry(), false, Collections.<FieldDescriptor>emptySet(),
-        false, false, false, false);
+        com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+        TypeRegistry.getEmptyTypeRegistry(),
+        /* alwaysOutputDefaultValueFields */ false,
+        /* includingDefaultValueFields */ Collections.<FieldDescriptor>emptySet(),
+        /* preservingProtoFieldNames */ false,
+        /* omittingInsignificantWhitespace */ false,
+        /* printingEnumsAsInts */ false,
+        /* sortingMapKeys */ false);
   }
 
   /**
    * A Printer converts protobuf message to JSON format.
    */
   public static class Printer {
-    private final TypeRegistry registry;
+    private final com.google.protobuf.TypeRegistry registry;
+    private final TypeRegistry oldRegistry;
     // NOTE: There are 3 states for these *defaultValueFields variables:
     // 1) Default - alwaysOutput is false & including is empty set. Fields only output if they are
     //    set to non-default values.
@@ -133,7 +140,8 @@ public class JsonFormat {
     private final boolean sortingMapKeys;
 
     private Printer(
-        TypeRegistry registry,
+        com.google.protobuf.TypeRegistry registry,
+        TypeRegistry oldRegistry,
         boolean alwaysOutputDefaultValueFields,
         Set<FieldDescriptor> includingDefaultValueFields,
         boolean preservingProtoFieldNames,
@@ -141,6 +149,7 @@ public class JsonFormat {
         boolean printingEnumsAsInts,
         boolean sortingMapKeys) {
       this.registry = registry;
+      this.oldRegistry = oldRegistry;
       this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
       this.includingDefaultValueFields = includingDefaultValueFields;
       this.preservingProtoFieldNames = preservingProtoFieldNames;
@@ -150,17 +159,41 @@ public class JsonFormat {
     }
 
     /**
-     * Creates a new {@link Printer} using the given registry. The new Printer
-     * clones all other configurations from the current {@link Printer}.
+     * Creates a new {@link Printer} using the given registry. The new Printer clones all other
+     * configurations from the current {@link Printer}.
      *
      * @throws IllegalArgumentException if a registry is already set.
      */
-    public Printer usingTypeRegistry(TypeRegistry registry) {
-      if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
+    public Printer usingTypeRegistry(TypeRegistry oldRegistry) {
+      if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+          || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
+        throw new IllegalArgumentException("Only one registry is allowed.");
+      }
+      return new Printer(
+          com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+          oldRegistry,
+          alwaysOutputDefaultValueFields,
+          includingDefaultValueFields,
+          preservingProtoFieldNames,
+          omittingInsignificantWhitespace,
+          printingEnumsAsInts,
+          sortingMapKeys);
+    }
+
+    /**
+     * Creates a new {@link Printer} using the given registry. The new Printer clones all other
+     * configurations from the current {@link Printer}.
+     *
+     * @throws IllegalArgumentException if a registry is already set.
+     */
+    public Printer usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
+      if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+          || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
         throw new IllegalArgumentException("Only one registry is allowed.");
       }
       return new Printer(
           registry,
+          oldRegistry,
           alwaysOutputDefaultValueFields,
           includingDefaultValueFields,
           preservingProtoFieldNames,
@@ -179,6 +212,7 @@ public class JsonFormat {
       checkUnsetIncludingDefaultValueFields();
       return new Printer(
           registry,
+          oldRegistry,
           true,
           Collections.<FieldDescriptor>emptySet(),
           preservingProtoFieldNames,
@@ -197,6 +231,7 @@ public class JsonFormat {
       checkUnsetPrintingEnumsAsInts();
       return new Printer(
           registry,
+          oldRegistry,
           alwaysOutputDefaultValueFields,
           Collections.<FieldDescriptor>emptySet(),
           preservingProtoFieldNames,
@@ -226,6 +261,7 @@ public class JsonFormat {
       checkUnsetIncludingDefaultValueFields();
       return new Printer(
           registry,
+          oldRegistry,
           false,
           Collections.unmodifiableSet(new HashSet<>(fieldsToAlwaysOutput)),
           preservingProtoFieldNames,
@@ -250,6 +286,7 @@ public class JsonFormat {
     public Printer preservingProtoFieldNames() {
       return new Printer(
           registry,
+          oldRegistry,
           alwaysOutputDefaultValueFields,
           includingDefaultValueFields,
           true,
@@ -279,6 +316,7 @@ public class JsonFormat {
     public Printer omittingInsignificantWhitespace() {
       return new Printer(
           registry,
+          oldRegistry,
           alwaysOutputDefaultValueFields,
           includingDefaultValueFields,
           preservingProtoFieldNames,
@@ -302,6 +340,7 @@ public class JsonFormat {
     public Printer sortingMapKeys() {
       return new Printer(
           registry,
+          oldRegistry,
           alwaysOutputDefaultValueFields,
           includingDefaultValueFields,
           preservingProtoFieldNames,
@@ -322,6 +361,7 @@ public class JsonFormat {
       // mobile.
       new PrinterImpl(
               registry,
+              oldRegistry,
               alwaysOutputDefaultValueFields,
               includingDefaultValueFields,
               preservingProtoFieldNames,
@@ -354,37 +394,66 @@ public class JsonFormat {
    * Creates a {@link Parser} with default configuration.
    */
   public static Parser parser() {
-    return new Parser(TypeRegistry.getEmptyTypeRegistry(), false, Parser.DEFAULT_RECURSION_LIMIT);
+    return new Parser(
+        com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+        TypeRegistry.getEmptyTypeRegistry(),
+        false,
+        Parser.DEFAULT_RECURSION_LIMIT);
   }
 
   /**
    * A Parser parses JSON to protobuf message.
    */
   public static class Parser {
-    private final TypeRegistry registry;
+    private final com.google.protobuf.TypeRegistry registry;
+    private final TypeRegistry oldRegistry;
     private final boolean ignoringUnknownFields;
     private final int recursionLimit;
 
     // The default parsing recursion limit is aligned with the proto binary parser.
     private static final int DEFAULT_RECURSION_LIMIT = 100;
 
-    private Parser(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
+    private Parser(
+        com.google.protobuf.TypeRegistry registry,
+        TypeRegistry oldRegistry,
+        boolean ignoreUnknownFields,
+        int recursionLimit) {
       this.registry = registry;
+      this.oldRegistry = oldRegistry;
       this.ignoringUnknownFields = ignoreUnknownFields;
       this.recursionLimit = recursionLimit;
     }
 
     /**
-     * Creates a new {@link Parser} using the given registry. The new Parser
-     * clones all other configurations from this Parser.
+     * Creates a new {@link Parser} using the given registry. The new Parser clones all other
+     * configurations from this Parser.
      *
      * @throws IllegalArgumentException if a registry is already set.
      */
-    public Parser usingTypeRegistry(TypeRegistry registry) {
-      if (this.registry != TypeRegistry.getEmptyTypeRegistry()) {
+    public Parser usingTypeRegistry(TypeRegistry oldRegistry) {
+      if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+          || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
         throw new IllegalArgumentException("Only one registry is allowed.");
       }
-      return new Parser(registry, ignoringUnknownFields, recursionLimit);
+      return new Parser(
+          com.google.protobuf.TypeRegistry.getEmptyTypeRegistry(),
+          oldRegistry,
+          ignoringUnknownFields,
+          recursionLimit);
+    }
+
+    /**
+     * Creates a new {@link Parser} using the given registry. The new Parser clones all other
+     * configurations from this Parser.
+     *
+     * @throws IllegalArgumentException if a registry is already set.
+     */
+    public Parser usingTypeRegistry(com.google.protobuf.TypeRegistry registry) {
+      if (this.oldRegistry != TypeRegistry.getEmptyTypeRegistry()
+          || this.registry != com.google.protobuf.TypeRegistry.getEmptyTypeRegistry()) {
+        throw new IllegalArgumentException("Only one registry is allowed.");
+      }
+      return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
     }
 
     /**
@@ -392,7 +461,7 @@ public class JsonFormat {
      * encountered. The new Parser clones all other configurations from this Parser.
      */
     public Parser ignoringUnknownFields() {
-      return new Parser(this.registry, true, recursionLimit);
+      return new Parser(this.registry, oldRegistry, true, recursionLimit);
     }
 
     /**
@@ -404,7 +473,8 @@ public class JsonFormat {
     public void merge(String json, Message.Builder builder) throws InvalidProtocolBufferException {
       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
       // mobile.
-      new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
+      new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
+          .merge(json, builder);
     }
 
     /**
@@ -417,12 +487,13 @@ public class JsonFormat {
     public void merge(Reader json, Message.Builder builder) throws IOException {
       // TODO(xiaofeng): Investigate the allocation overhead and optimize for
       // mobile.
-      new ParserImpl(registry, ignoringUnknownFields, recursionLimit).merge(json, builder);
+      new ParserImpl(registry, oldRegistry, ignoringUnknownFields, recursionLimit)
+          .merge(json, builder);
     }
 
     // For testing only.
     Parser usingRecursionLimit(int recursionLimit) {
-      return new Parser(registry, ignoringUnknownFields, recursionLimit);
+      return new Parser(registry, oldRegistry, ignoringUnknownFields, recursionLimit);
     }
   }
 
@@ -478,7 +549,7 @@ public class JsonFormat {
       @CanIgnoreReturnValue
       public Builder add(Descriptor messageType) {
         if (types == null) {
-          throw new IllegalStateException("A TypeRegistry.Builer can only be used once.");
+          throw new IllegalStateException("A TypeRegistry.Builder can only be used once.");
         }
         addFile(messageType.getFile());
         return this;
@@ -641,7 +712,8 @@ public class JsonFormat {
    * A Printer converts protobuf messages to JSON format.
    */
   private static final class PrinterImpl {
-    private final TypeRegistry registry;
+    private final com.google.protobuf.TypeRegistry registry;
+    private final TypeRegistry oldRegistry;
     private final boolean alwaysOutputDefaultValueFields;
     private final Set<FieldDescriptor> includingDefaultValueFields;
     private final boolean preservingProtoFieldNames;
@@ -658,7 +730,8 @@ public class JsonFormat {
     }
 
     PrinterImpl(
-        TypeRegistry registry,
+        com.google.protobuf.TypeRegistry registry,
+        TypeRegistry oldRegistry,
         boolean alwaysOutputDefaultValueFields,
         Set<FieldDescriptor> includingDefaultValueFields,
         boolean preservingProtoFieldNames,
@@ -667,6 +740,7 @@ public class JsonFormat {
         boolean printingEnumsAsInts,
         boolean sortingMapKeys) {
       this.registry = registry;
+      this.oldRegistry = oldRegistry;
       this.alwaysOutputDefaultValueFields = alwaysOutputDefaultValueFields;
       this.includingDefaultValueFields = includingDefaultValueFields;
       this.preservingProtoFieldNames = preservingProtoFieldNames;
@@ -807,7 +881,10 @@ public class JsonFormat {
       String typeUrl = (String) message.getField(typeUrlField);
       Descriptor type = registry.getDescriptorForTypeUrl(typeUrl);
       if (type == null) {
-        throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
+        type = oldRegistry.getDescriptorForTypeUrl(typeUrl);
+        if (type == null) {
+          throw new InvalidProtocolBufferException("Cannot find type for url: " + typeUrl);
+        }
       }
       ByteString content = (ByteString) message.getField(valueField);
       Message contentMessage =
@@ -1218,14 +1295,20 @@ public class JsonFormat {
   }
 
   private static class ParserImpl {
-    private final TypeRegistry registry;
+    private final com.google.protobuf.TypeRegistry registry;
+    private final TypeRegistry oldRegistry;
     private final JsonParser jsonParser;
     private final boolean ignoringUnknownFields;
     private final int recursionLimit;
     private int currentDepth;
 
-    ParserImpl(TypeRegistry registry, boolean ignoreUnknownFields, int recursionLimit) {
+    ParserImpl(
+        com.google.protobuf.TypeRegistry registry,
+        TypeRegistry oldRegistry,
+        boolean ignoreUnknownFields,
+        int recursionLimit) {
       this.registry = registry;
+      this.oldRegistry = oldRegistry;
       this.ignoringUnknownFields = ignoreUnknownFields;
       this.jsonParser = new JsonParser();
       this.recursionLimit = recursionLimit;
@@ -1448,7 +1531,10 @@ public class JsonFormat {
       String typeUrl = typeUrlElement.getAsString();
       Descriptor contentType = registry.getDescriptorForTypeUrl(typeUrl);
       if (contentType == null) {
-        throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
+        contentType = oldRegistry.getDescriptorForTypeUrl(typeUrl);
+        if (contentType == null) {
+          throw new InvalidProtocolBufferException("Cannot resolve type: " + typeUrl);
+        }
       }
       builder.setField(typeUrlField, typeUrl);
       Message.Builder contentBuilder =
--- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java
@@ -152,6 +152,16 @@ public class JsonFormatTest extends Test
     assertEquals(message.toString(), parsedMessage.toString());
   }
 
+  private void assertRoundTripEquals(Message message, com.google.protobuf.TypeRegistry registry)
+      throws Exception {
+    JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
+    JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry);
+    Message.Builder builder = message.newBuilderForType();
+    parser.merge(printer.print(message), builder);
+    Message parsedMessage = builder.build();
+    assertEquals(message.toString(), parsedMessage.toString());
+  }
+
   private String toJsonString(Message message) throws IOException {
     return JsonFormat.printer().print(message);
   }
@@ -850,6 +860,45 @@ public class JsonFormatTest extends Test
   }
 
 
+  public void testAnyFieldsWithCustomAddedTypeRegistry() throws Exception {
+    TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
+    TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
+
+    com.google.protobuf.TypeRegistry registry =
+        com.google.protobuf.TypeRegistry.newBuilder().add(content.getDescriptorForType()).build();
+    JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
+
+    assertEquals(
+        "{\n"
+            + "  \"anyValue\": {\n"
+            + "    \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+            + "    \"optionalInt32\": 1234\n"
+            + "  }\n"
+            + "}",
+        printer.print(message));
+    assertRoundTripEquals(message, registry);
+
+    TestAny messageWithDefaultAnyValue =
+        TestAny.newBuilder().setAnyValue(Any.getDefaultInstance()).build();
+    assertEquals("{\n" + "  \"anyValue\": {}\n" + "}", printer.print(messageWithDefaultAnyValue));
+    assertRoundTripEquals(messageWithDefaultAnyValue, registry);
+
+    // Well-known types have a special formatting when embedded in Any.
+    //
+    // 1. Any in Any.
+    Any anyMessage = Any.pack(Any.pack(content));
+    assertEquals(
+        "{\n"
+            + "  \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n"
+            + "  \"value\": {\n"
+            + "    \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+            + "    \"optionalInt32\": 1234\n"
+            + "  }\n"
+            + "}",
+        printer.print(anyMessage));
+    assertRoundTripEquals(anyMessage, registry);
+  }
+
   public void testAnyFields() throws Exception {
     TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
     TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
@@ -1136,7 +1185,7 @@ public class JsonFormatTest extends Test
       Any.Builder builder = Any.newBuilder();
       mergeFromJson(
           "{\n"
-              + "  \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n"
+              + "  \"@type\": \"type.googleapis.com/json_test.UnexpectedTypes\",\n"
               + "  \"optionalInt32\": 12345\n"
               + "}",
           builder);
--- a/src/google/protobuf/compiler/java/java_context.cc
+++ b/src/google/protobuf/compiler/java/java_context.cc
@@ -35,7 +35,6 @@
 #include <google/protobuf/compiler/java/java_name_resolver.h>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/stubs/strutil.h>
-
 #include <google/protobuf/stubs/map_util.h>
 
 namespace google {
--- a/src/google/protobuf/compiler/java/java_doc_comment.cc
+++ b/src/google/protobuf/compiler/java/java_doc_comment.cc
@@ -174,7 +174,8 @@ void WriteMessageDocComment(io::Printer*
 
 void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field) {
   // We start the comment with the main body based on the comments from the
-  // .proto file (if present). We then continue with the field declaration, e.g.:
+  // .proto file (if present). We then continue with the field declaration,
+  // e.g.:
   //   optional string foo = 5;
   // And then we end with the javadoc tags if applicable.
   // If the field is a group, the debug string might end with {.
@@ -185,17 +186,17 @@ void WriteFieldDocComment(io::Printer* p
   printer->Print(" */\n");
 }
 
-void WriteFieldAccessorDocComment(io::Printer* printer, 
+void WriteFieldAccessorDocComment(io::Printer* printer,
                                   const FieldDescriptor* field,
                                   const FieldAccessorType type,
                                   const bool builder) {
   printer->Print("/**\n");
   WriteDocCommentBody(printer, field);
-  printer->Print(" * <code>$def$</code>\n", "def", 
+  printer->Print(" * <code>$def$</code>\n", "def",
                  EscapeJavadoc(FirstLineOf(field->DebugString())));
   switch (type) {
     case HAZZER:
-      printer->Print(" * @return Whether the $name$ field is set.\n", "name", 
+      printer->Print(" * @return Whether the $name$ field is set.\n", "name",
                      field->camelcase_name());
       break;
     case GETTER:
@@ -211,16 +212,16 @@ void WriteFieldAccessorDocComment(io::Pr
       break;
     // Repeated
     case LIST_COUNT:
-      printer->Print(" * @return The number of $name$(s).\n", "name",
-                 field->camelcase_name());
+      printer->Print(" * @return The count of $name$.\n", "name",
+                     field->camelcase_name());
       break;
     case LIST_GETTER:
-      printer->Print(" * @return A list containing the $name$(s).\n", "name",
+      printer->Print(" * @return A list containing the $name$.\n", "name",
                      field->camelcase_name());
       break;
     case LIST_INDEXED_GETTER:
       printer->Print(" * @param index The index of the element to return.\n");
-      printer->Print(" * @return The $name$(s) at the given index.\n", "name",
+      printer->Print(" * @return The $name$ at the given index.\n", "name",
                      field->camelcase_name());
       break;
     case LIST_INDEXED_SETTER:
@@ -233,8 +234,8 @@ void WriteFieldAccessorDocComment(io::Pr
                      field->camelcase_name());
       break;
     case LIST_MULTI_ADDER:
-      printer->Print(" * @param values The $name$(s) to add.\n", "name",
-                 field->camelcase_name());
+      printer->Print(" * @param values The $name$ to add.\n", "name",
+                     field->camelcase_name());
       break;
   }
   if (builder) {
@@ -243,25 +244,28 @@ void WriteFieldAccessorDocComment(io::Pr
   printer->Print(" */\n");
 }
 
-void WriteFieldEnumValueAccessorDocComment(io::Printer* printer, 
+void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
                                            const FieldDescriptor* field,
                                            const FieldAccessorType type,
                                            const bool builder) {
   printer->Print("/**\n");
   WriteDocCommentBody(printer, field);
-  printer->Print(" * <code>$def$</code>\n", "def", 
+  printer->Print(" * <code>$def$</code>\n", "def",
                  EscapeJavadoc(FirstLineOf(field->DebugString())));
   switch (type) {
     case HAZZER:
       // Should never happen
       break;
     case GETTER:
-      printer->Print(" * @return The enum value for $name$.\n", "name",
-                 field->camelcase_name());
+      printer->Print(
+          " * @return The enum numeric value on the wire for $name$.\n", "name",
+          field->camelcase_name());
       break;
     case SETTER:
-      printer->Print(" * @param value The enum value for $name$ to set.\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @param value The enum numeric value on the wire for $name$ to "
+          "set.\n",
+          "name", field->camelcase_name());
       break;
     case CLEARER:
       // Print nothing
@@ -271,26 +275,36 @@ void WriteFieldEnumValueAccessorDocComme
       // Should never happen
       break;
     case LIST_GETTER:
-      printer->Print(" * @return A list containing the enum values for $name$(s).\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @return A list containing the enum numeric values on the wire "
+          "for $name$.\n",
+          "name", field->camelcase_name());
       break;
     case LIST_INDEXED_GETTER:
       printer->Print(" * @param index The index of the value to return.\n");
-      printer->Print(" * @return The enum value of the $name$ at the given index.\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @return The enum numeric value on the wire of $name$ at the "
+          "given index.\n",
+          "name", field->camelcase_name());
       break;
     case LIST_INDEXED_SETTER:
       printer->Print(" * @param index The index of the value to return.\n");
-      printer->Print(" * @param value The enum value of the $name$ to set.\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @param value The enum numeric value on the wire for $name$ to "
+          "set.\n",
+          "name", field->camelcase_name());
       break;
     case LIST_ADDER:
-      printer->Print(" * @param value The enum value of the $name$ to add.\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @param value The enum numeric value on the wire for $name$ to "
+          "add.\n",
+          "name", field->camelcase_name());
       break;
     case LIST_MULTI_ADDER:
-      printer->Print(" * @param values The enum values of the $name$(s) to add.\n",
-                     "name", field->camelcase_name());
+      printer->Print(
+          " * @param values The enum numeric values on the wire for $name$ to "
+          "add.\n",
+          "name", field->camelcase_name());
       break;
   }
   if (builder) {
@@ -299,13 +313,13 @@ void WriteFieldEnumValueAccessorDocComme
   printer->Print(" */\n");
 }
 
-void WriteFieldStringBytesAccessorDocComment(io::Printer* printer, 
+void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
                                              const FieldDescriptor* field,
                                              const FieldAccessorType type,
                                              const bool builder) {
   printer->Print("/**\n");
   WriteDocCommentBody(printer, field);
-  printer->Print(" * <code>$def$</code>\n", "def", 
+  printer->Print(" * <code>$def$</code>\n", "def",
                  EscapeJavadoc(FirstLineOf(field->DebugString())));
   switch (type) {
     case HAZZER:
@@ -313,11 +327,11 @@ void WriteFieldStringBytesAccessorDocCom
       break;
     case GETTER:
       printer->Print(" * @return The bytes for $name$.\n", "name",
-                 field->camelcase_name());
+                     field->camelcase_name());
       break;
     case SETTER:
-      printer->Print(" * @param value The bytes for $name$ to set.\n",
-                     "name", field->camelcase_name());
+      printer->Print(" * @param value The bytes for $name$ to set.\n", "name",
+                     field->camelcase_name());
       break;
     case CLEARER:
       // Print nothing
@@ -327,7 +341,7 @@ void WriteFieldStringBytesAccessorDocCom
       // Should never happen
       break;
     case LIST_GETTER:
-      printer->Print(" * @return A list containing the bytes for $name$(s).\n",
+      printer->Print(" * @return A list containing the bytes for $name$.\n",
                      "name", field->camelcase_name());
       break;
     case LIST_INDEXED_GETTER:
@@ -345,7 +359,7 @@ void WriteFieldStringBytesAccessorDocCom
                      "name", field->camelcase_name());
       break;
     case LIST_MULTI_ADDER:
-      printer->Print(" * @param values The bytes of the $name$(s) to add.\n",
+      printer->Print(" * @param values The bytes of the $name$ to add.\n",
                      "name", field->camelcase_name());
       break;
   }
--- a/src/google/protobuf/compiler/java/java_doc_comment.h
+++ b/src/google/protobuf/compiler/java/java_doc_comment.h
@@ -53,35 +53,33 @@ namespace compiler {
 namespace java {
 
 enum FieldAccessorType {
-
-    HAZZER,
-    GETTER,
-    SETTER,
-    CLEARER,
-    // Repeated
-    LIST_COUNT,
-    LIST_GETTER,
-    LIST_INDEXED_GETTER,
-    LIST_INDEXED_SETTER,
-    LIST_ADDER,
-    LIST_MULTI_ADDER
-
+  HAZZER,
+  GETTER,
+  SETTER,
+  CLEARER,
+  // Repeated
+  LIST_COUNT,
+  LIST_GETTER,
+  LIST_INDEXED_GETTER,
+  LIST_INDEXED_SETTER,
+  LIST_ADDER,
+  LIST_MULTI_ADDER
 };
 
 void WriteMessageDocComment(io::Printer* printer, const Descriptor* message);
 void WriteFieldDocComment(io::Printer* printer, const FieldDescriptor* field);
 void WriteFieldAccessorDocComment(io::Printer* printer,
-                                const FieldDescriptor* field,
-                                const FieldAccessorType type,
-                                const bool builder = false);
+                                  const FieldDescriptor* field,
+                                  const FieldAccessorType type,
+                                  const bool builder = false);
 void WriteFieldEnumValueAccessorDocComment(io::Printer* printer,
-                                const FieldDescriptor* field,
-                                const FieldAccessorType type,
-                                const bool builder = false);
+                                           const FieldDescriptor* field,
+                                           const FieldAccessorType type,
+                                           const bool builder = false);
 void WriteFieldStringBytesAccessorDocComment(io::Printer* printer,
-                                const FieldDescriptor* field,
-                                const FieldAccessorType type,
-                                const bool builder = false);
+                                             const FieldDescriptor* field,
+                                             const FieldAccessorType type,
+                                             const bool builder = false);
 void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enum_);
 void WriteEnumValueDocComment(io::Printer* printer,
                               const EnumValueDescriptor* value);
--- a/src/google/protobuf/compiler/java/java_enum.cc
+++ b/src/google/protobuf/compiler/java/java_enum.cc
@@ -44,7 +44,6 @@
 #include <google/protobuf/io/printer.h>
 #include <google/protobuf/stubs/strutil.h>
 
-
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -175,8 +174,9 @@ void EnumGenerator::Generate(io::Printer
       "}\n"
       "\n"
       "/**\n"
-      " * @param value The number of the enum to look for.\n"
-      " * @return The enum associated with the given number.\n"
+      " * @param value The numeric wire value of the corresponding enum "
+      "entry.\n"
+      " * @return The enum associated with the given numeric wire value.\n"
       " * @deprecated Use {@link #forNumber(int)} instead.\n"
       " */\n"
       "@java.lang.Deprecated\n"
@@ -184,6 +184,11 @@ void EnumGenerator::Generate(io::Printer
       "  return forNumber(value);\n"
       "}\n"
       "\n"
+      "/**\n"
+      " * @param value The numeric wire value of the corresponding enum "
+      "entry.\n"
+      " * @return The enum associated with the given numeric wire value.\n"
+      " */\n"
       "public static $classname$ forNumber(int value) {\n"
       "  switch (value) {\n",
       "classname", descriptor_->name());
--- a/src/google/protobuf/compiler/java/java_enum_field.cc
+++ b/src/google/protobuf/compiler/java/java_enum_field.cc
@@ -46,7 +46,6 @@
 #include <google/protobuf/wire_format.h>
 #include <google/protobuf/stubs/strutil.h>
 
-
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -220,7 +219,8 @@ void ImmutableEnumFieldGenerator::Genera
         "  return $name$_;\n"
         "}\n");
     printer->Annotate("{", "}", descriptor_);
-    WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER);
+    WriteFieldEnumValueAccessorDocComment(printer, descriptor_, SETTER,
+                                          /* builder */ true);
     printer->Print(variables_,
                    "$deprecation$public Builder "
                    "${$set$capitalized_name$Value$}$(int value) {\n"
--- a/src/google/protobuf/compiler/java/java_string_field_lite.cc
+++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc
@@ -47,7 +47,6 @@
 #include <google/protobuf/wire_format.h>
 #include <google/protobuf/stubs/strutil.h>
 
-
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -292,7 +291,8 @@ void ImmutableStringFieldLiteGenerator::
       "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER, true);
+  WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER,
+                                          /* builder */ true);
   printer->Print(
       variables_,
       "$deprecation$public Builder ${$set$capitalized_name$Bytes$}$(\n"
@@ -470,7 +470,8 @@ void ImmutableStringOneofFieldLiteGenera
       "}\n");
   printer->Annotate("{", "}", descriptor_);
 
-  WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER);
+  WriteFieldStringBytesAccessorDocComment(printer, descriptor_, SETTER,
+                                          /* builder */ true);
   printer->Print(
       variables_,
       "$deprecation$public Builder ${$set$capitalized_name$Bytes$}$(\n"
--- a/src/google/protobuf/compiler/js/js_generator.cc
+++ b/src/google/protobuf/compiler/js/js_generator.cc
@@ -43,7 +43,6 @@
 #include <google/protobuf/stubs/common.h>
 #include <google/protobuf/stubs/stringprintf.h>
 #include <google/protobuf/stubs/strutil.h>
-
 #include <google/protobuf/compiler/scc.h>
 #include <google/protobuf/compiler/js/well_known_types_embed.h>
 #include <google/protobuf/io/printer.h>
@@ -51,7 +50,6 @@
 #include <google/protobuf/descriptor.pb.h>
 #include <google/protobuf/descriptor.h>
 
-
 namespace google {
 namespace protobuf {
 namespace compiler {
@@ -1118,9 +1116,6 @@ std::string JSBinaryWriterMethodName(con
          JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
 }
 
-std::string JSReturnClause(const FieldDescriptor* desc) {
-  return "";
-}
 
 std::string JSTypeTag(const FieldDescriptor* desc) {
   switch (desc->type()) {
@@ -1156,10 +1151,6 @@ std::string JSTypeTag(const FieldDescrip
   return "";
 }
 
-std::string JSReturnDoc(const GeneratorOptions& options,
-                        const FieldDescriptor* desc) {
-  return "";
-}
 
 bool HasRepeatedFields(const GeneratorOptions& options,
                        const Descriptor* desc) {
@@ -1426,11 +1417,6 @@ bool HasFieldPresence(const GeneratorOpt
     return false;
   }
 
-  if (UseBrokenPresenceSemantics(options, field)) {
-    // Proto3 files with broken presence semantics have field presence.
-    return true;
-  }
-
   return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
          field->containing_oneof() != NULL ||
          field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
@@ -2393,7 +2379,7 @@ void Generator::GenerateClassFieldToObje
     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
         // Repeated fields get initialized to their default in the constructor
         // (why?), so we emit a plain getField() call for them.
-        !field->is_repeated() && !UseBrokenPresenceSemantics(options, field)) {
+        !field->is_repeated()) {
       // Proto3 puts all defaults (including implicit defaults) in toObject().
       // But for proto2 we leave the existing semantics unchanged: unset fields
       // without default are unset.
@@ -2677,30 +2663,31 @@ void Generator::GenerateClassField(const
         (field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
     printer->Annotate("gettername", field);
     printer->Print(
-        "/** @param {$optionaltype$} value$returndoc$ */\n"
+        "/**\n"
+        " * @param {$optionaltype$} value\n"
+        " * @return {!$class$} returns this\n"
+        "*/\n"
         "$class$.prototype.$settername$ = function(value) {\n"
-        "  jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
+        "  return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
         "optionaltype",
         JSFieldTypeAnnotation(options, field,
                               /* is_setter_argument = */ true,
                               /* force_present = */ false,
                               /* singular_if_not_packed = */ false),
-        "returndoc", JSReturnDoc(options, field), "class",
-        GetMessagePath(options, field->containing_type()), "settername",
-        "set" + JSGetterName(options, field), "oneoftag",
+        "class", GetMessagePath(options, field->containing_type()),
+        "settername", "set" + JSGetterName(options, field), "oneoftag",
         (field->containing_oneof() ? "Oneof" : ""), "repeatedtag",
         (field->is_repeated() ? "Repeated" : ""));
     printer->Annotate("settername", field);
 
     printer->Print(
-        "this, $index$$oneofgroup$, value);$returnvalue$\n"
+        "this, $index$$oneofgroup$, value);\n"
         "};\n"
         "\n"
         "\n",
         "index", JSFieldIndex(field), "oneofgroup",
         (field->containing_oneof() ? (", " + JSOneofArray(options, field))
-                                   : ""),
-        "returnvalue", JSReturnClause(field));
+                                   : ""));
 
     if (field->is_repeated()) {
       GenerateRepeatedMessageHelperMethods(options, printer, field);
@@ -2787,21 +2774,18 @@ void Generator::GenerateClassField(const
       GenerateBytesWrapper(options, printer, field, BYTES_U8);
     }
 
-    if (untyped) {
-      printer->Print(
-          "/**\n"
-          " * @param {*} value$returndoc$\n"
-          " */\n",
-          "returndoc", JSReturnDoc(options, field));
-    } else {
-      printer->Print(
-          "/** @param {$optionaltype$} value$returndoc$ */\n", "optionaltype",
-          JSFieldTypeAnnotation(options, field,
-                                /* is_setter_argument = */ true,
-                                /* force_present = */ false,
-                                /* singular_if_not_packed = */ false),
-          "returndoc", JSReturnDoc(options, field));
-    }
+    printer->Print(
+        "/**\n"
+        " * @param {$optionaltype$} value\n"
+        " * @return {!$class$} returns this\n"
+        " */\n",
+        "class", GetMessagePath(options, field->containing_type()),
+        "optionaltype",
+        untyped ? "*"
+                : JSFieldTypeAnnotation(options, field,
+                                        /* is_setter_argument = */ true,
+                                        /* force_present = */ false,
+                                        /* singular_if_not_packed = */ false));
 
     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
         !field->is_repeated() && !field->is_map() &&
@@ -2810,28 +2794,28 @@ void Generator::GenerateClassField(const
       // setProto3*Field function.
       printer->Print(
           "$class$.prototype.$settername$ = function(value) {\n"
-          "  jspb.Message.setProto3$typetag$Field(this, $index$, "
-          "value);$returnvalue$\n"
+          "  return jspb.Message.setProto3$typetag$Field(this, $index$, "
+          "value);"
+          "\n"
           "};\n"
           "\n"
           "\n",
           "class", GetMessagePath(options, field->containing_type()),
           "settername", "set" + JSGetterName(options, field), "typetag",
-          JSTypeTag(field), "index", JSFieldIndex(field), "returnvalue",
-          JSReturnClause(field));
+          JSTypeTag(field), "index", JSFieldIndex(field));
       printer->Annotate("settername", field);
     } else {
       // Otherwise, use the regular setField function.
       printer->Print(
           "$class$.prototype.$settername$ = function(value) {\n"
-          "  jspb.Message.set$oneoftag$Field(this, $index$",
+          "  return jspb.Message.set$oneoftag$Field(this, $index$",
           "class", GetMessagePath(options, field->containing_type()),
           "settername", "set" + JSGetterName(options, field), "oneoftag",
           (field->containing_oneof() ? "Oneof" : ""), "index",
           JSFieldIndex(field));
       printer->Annotate("settername", field);
       printer->Print(
-          "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
+          "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
           "};\n"
           "\n"
           "\n",
@@ -2840,16 +2824,16 @@ void Generator::GenerateClassField(const
           "typeclose", untyped ? ")" : "", "oneofgroup",
           (field->containing_oneof() ? (", " + JSOneofArray(options, field))
                                      : ""),
-          "returnvalue", JSReturnClause(field), "rptvalueinit",
-          (field->is_repeated() ? " || []" : ""));
+          "rptvalueinit", (field->is_repeated() ? " || []" : ""));
     }
 
     if (untyped) {
       printer->Print(
           "/**\n"
-          " * Clears the value.$returndoc$\n"
+          " * Clears the value.\n"
+          " * @return {!$class$} returns this\n"
           " */\n",
-          "returndoc", JSReturnDoc(options, field));
+          "class", GetMessagePath(options, field->containing_type()));
     }
 
     if (field->is_repeated()) {
@@ -2863,19 +2847,18 @@ void Generator::GenerateClassField(const
     // clang-format off
     printer->Print(
         "/**\n"
-        " * Clears values from the map. The map will be non-null."
-        "$returndoc$\n"
+        " * Clears values from the map. The map will be non-null.\n"
+        " * @return {!$class$} returns this\n"
         " */\n"
         "$class$.prototype.$clearername$ = function() {\n"
-        "  this.$gettername$().clear();$returnvalue$\n"
+        "  this.$gettername$().clear();\n"
+        "  return this;"
         "};\n"
         "\n"
         "\n",
-        "returndoc", JSReturnDoc(options, field),
         "class", GetMessagePath(options, field->containing_type()),
         "clearername", "clear" + JSGetterName(options, field),
-        "gettername", "get" + JSGetterName(options, field),
-        "returnvalue", JSReturnClause(field));
+        "gettername", "get" + JSGetterName(options, field));
     // clang-format on
     printer->Annotate("clearername", field);
   } else if (field->is_repeated() ||
@@ -2885,22 +2868,21 @@ void Generator::GenerateClassField(const
     // clang-format off
     printer->Print(
         "/**\n"
-        " * $jsdoc$$returndoc$\n"
+        " * $jsdoc$\n"
+        " * @return {!$class$} returns this\n"
         " */\n"
         "$class$.prototype.$clearername$ = function() {\n"
-        "  this.$settername$($clearedvalue$);$returnvalue$\n"
+        "  return this.$settername$($clearedvalue$);\n"
         "};\n"
         "\n"
         "\n",
        "jsdoc", field->is_repeated()
            ? "Clears the list making it empty but non-null."
            : "Clears the message field making it undefined.",
-        "returndoc", JSReturnDoc(options, field),
         "class", GetMessagePath(options, field->containing_type()),
         "clearername", "clear" + JSGetterName(options, field),
         "settername", "set" + JSGetterName(options, field),
-        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
-        "returnvalue", JSReturnClause(field));
+        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
     // clang-format on
     printer->Annotate("clearername", field);
   } else if (HasFieldPresence(options, field)) {
@@ -2909,12 +2891,12 @@ void Generator::GenerateClassField(const
     // clang-format off
     printer->Print(
         "/**\n"
-        " * Clears the field making it undefined.$returndoc$\n"
+        " * Clears the field making it undefined.\n"
+        " * @return {!$class$} returns this\n"
         " */\n"
         "$class$.prototype.$clearername$ = function() {\n"
-        "  jspb.Message.set$maybeoneof$Field(this, "
-        "$index$$maybeoneofgroup$, ",
-        "returndoc", JSReturnDoc(options, field),
+        "  return jspb.Message.set$maybeoneof$Field(this, "
+            "$index$$maybeoneofgroup$, ",
         "class", GetMessagePath(options, field->containing_type()),
         "clearername", "clear" + JSGetterName(options, field),
         "maybeoneof", (field->containing_oneof() ? "Oneof" : ""),
@@ -2925,12 +2907,11 @@ void Generator::GenerateClassField(const
     // clang-format on
     printer->Annotate("clearername", field);
     printer->Print(
-        "$clearedvalue$);$returnvalue$\n"
+        "$clearedvalue$);\n"
         "};\n"
         "\n"
         "\n",
-        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
-        "returnvalue", JSReturnClause(field));
+        "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
   }
 
   if (HasFieldPresence(options, field)) {
@@ -2957,10 +2938,12 @@ void Generator::GenerateRepeatedPrimitiv
   printer->Print(
       "/**\n"
       " * @param {$optionaltype$} value\n"
-      " * @param {number=} opt_index$returndoc$\n"
+      " * @param {number=} opt_index\n"
+      " * @return {!$class$} returns this\n"
       " */\n"
       "$class$.prototype.$addername$ = function(value, opt_index) {\n"
-      "  jspb.Message.addToRepeatedField(this, $index$",
+      "  return jspb.Message.addToRepeatedField(this, "
+      "$index$",
       "class", GetMessagePath(options, field->containing_type()), "addername",
       "add" + JSGetterName(options, field, BYTES_DEFAULT,
                            /* drop_list = */ true),
@@ -2972,20 +2955,18 @@ void Generator::GenerateRepeatedPrimitiv
                                 /* singular_if_not_packed = */ false,
                                 BYTES_DEFAULT,
                                 /* force_singular = */ true),
-      "index", JSFieldIndex(field),
-      "returndoc", JSReturnDoc(options, field));
+      "index", JSFieldIndex(field));
   printer->Annotate("addername", field);
   printer->Print(
       "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
-      "opt_index);$returnvalue$\n"
+      "opt_index);\n"
       "};\n"
       "\n"
       "\n",
       "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
       "typeclose", untyped ? ")" : "", "oneofgroup",
       (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
-      "rptvalueinit", "",
-      "returnvalue", JSReturnClause(field));
+      "rptvalueinit", "");
   // clang-format on
 }
 
@@ -3374,12 +3355,21 @@ void Generator::GenerateEnum(const Gener
       enumdesc->name());
   printer->Annotate("name", enumdesc);
 
+  std::set<string> used_name;
+  std::vector<int> valid_index;
   for (int i = 0; i < enumdesc->value_count(); i++) {
+    if (enumdesc->options().allow_alias() &&
+        !used_name.insert(ToEnumCase(enumdesc->value(i)->name())).second) {
+      continue;
+    }
+    valid_index.push_back(i);
+  }
+  for (auto i : valid_index) {
     const EnumValueDescriptor* value = enumdesc->value(i);
     printer->Print("  $name$: $value$$comma$\n", "name",
                    ToEnumCase(value->name()), "value",
                    StrCat(value->number()), "comma",
-                   (i == enumdesc->value_count() - 1) ? "" : ",");
+                   (i == valid_index.back()) ? "" : ",");
     printer->Annotate("name", value);
   }
 
openSUSE Build Service is sponsored by